1use std::fmt;
3use std::cmp::Ordering;
4
5#[cfg(feature = "compat")]
6use http;
7
8#[derive(Debug, Hash)]
21pub enum StatusCode {
22 Continue,
25 SwitchingProtocols,
28 Processing,
31
32 Ok,
35 Created,
38 Accepted,
41 NonAuthoritativeInformation,
44 NoContent,
47 ResetContent,
50 PartialContent,
53 MultiStatus,
56 AlreadyReported,
59
60 ImUsed,
63
64 MultipleChoices,
67 MovedPermanently,
70 Found,
73 SeeOther,
76 NotModified,
79 UseProxy,
82 TemporaryRedirect,
85 PermanentRedirect,
88
89 BadRequest,
92 Unauthorized,
95 PaymentRequired,
98 Forbidden,
101 NotFound,
104 MethodNotAllowed,
107 NotAcceptable,
110 ProxyAuthenticationRequired,
113 RequestTimeout,
116 Conflict,
119 Gone,
122 LengthRequired,
125 PreconditionFailed,
128 PayloadTooLarge,
131 UriTooLong,
134 UnsupportedMediaType,
137 RangeNotSatisfiable,
140 ExpectationFailed,
143 ImATeapot,
146
147 MisdirectedRequest,
150 UnprocessableEntity,
153 Locked,
156 FailedDependency,
159
160 UpgradeRequired,
163
164 PreconditionRequired,
167 TooManyRequests,
170
171 RequestHeaderFieldsTooLarge,
174
175 UnavailableForLegalReasons,
178
179 InternalServerError,
182 NotImplemented,
185 BadGateway,
188 ServiceUnavailable,
191 GatewayTimeout,
194 HttpVersionNotSupported,
197 VariantAlsoNegotiates,
200 InsufficientStorage,
203 LoopDetected,
206
207 NotExtended,
210 NetworkAuthenticationRequired,
213
214 Unregistered(u16),
217}
218
219#[derive(Debug)]
220pub struct InvalidStatusCode {
221 _inner: (),
222}
223
224impl StatusCode {
225
226 pub fn try_from(n: u16) -> Result<StatusCode, InvalidStatusCode> {
238 if n < 100 || n > 599 {
239 Err(InvalidStatusCode {
240 _inner: (),
241 })
242 } else {
243 Ok(StatusCode::from_u16(n))
244 }
245 }
246
247 fn from_u16(n: u16) -> StatusCode {
248 match n {
249 100 => StatusCode::Continue,
250 101 => StatusCode::SwitchingProtocols,
251 102 => StatusCode::Processing,
252 200 => StatusCode::Ok,
253 201 => StatusCode::Created,
254 202 => StatusCode::Accepted,
255 203 => StatusCode::NonAuthoritativeInformation,
256 204 => StatusCode::NoContent,
257 205 => StatusCode::ResetContent,
258 206 => StatusCode::PartialContent,
259 207 => StatusCode::MultiStatus,
260 208 => StatusCode::AlreadyReported,
261 226 => StatusCode::ImUsed,
262 300 => StatusCode::MultipleChoices,
263 301 => StatusCode::MovedPermanently,
264 302 => StatusCode::Found,
265 303 => StatusCode::SeeOther,
266 304 => StatusCode::NotModified,
267 305 => StatusCode::UseProxy,
268 307 => StatusCode::TemporaryRedirect,
269 308 => StatusCode::PermanentRedirect,
270 400 => StatusCode::BadRequest,
271 401 => StatusCode::Unauthorized,
272 402 => StatusCode::PaymentRequired,
273 403 => StatusCode::Forbidden,
274 404 => StatusCode::NotFound,
275 405 => StatusCode::MethodNotAllowed,
276 406 => StatusCode::NotAcceptable,
277 407 => StatusCode::ProxyAuthenticationRequired,
278 408 => StatusCode::RequestTimeout,
279 409 => StatusCode::Conflict,
280 410 => StatusCode::Gone,
281 411 => StatusCode::LengthRequired,
282 412 => StatusCode::PreconditionFailed,
283 413 => StatusCode::PayloadTooLarge,
284 414 => StatusCode::UriTooLong,
285 415 => StatusCode::UnsupportedMediaType,
286 416 => StatusCode::RangeNotSatisfiable,
287 417 => StatusCode::ExpectationFailed,
288 418 => StatusCode::ImATeapot,
289 421 => StatusCode::MisdirectedRequest,
290 422 => StatusCode::UnprocessableEntity,
291 423 => StatusCode::Locked,
292 424 => StatusCode::FailedDependency,
293 426 => StatusCode::UpgradeRequired,
294 428 => StatusCode::PreconditionRequired,
295 429 => StatusCode::TooManyRequests,
296 431 => StatusCode::RequestHeaderFieldsTooLarge,
297 451 => StatusCode::UnavailableForLegalReasons,
298 500 => StatusCode::InternalServerError,
299 501 => StatusCode::NotImplemented,
300 502 => StatusCode::BadGateway,
301 503 => StatusCode::ServiceUnavailable,
302 504 => StatusCode::GatewayTimeout,
303 505 => StatusCode::HttpVersionNotSupported,
304 506 => StatusCode::VariantAlsoNegotiates,
305 507 => StatusCode::InsufficientStorage,
306 508 => StatusCode::LoopDetected,
307 510 => StatusCode::NotExtended,
308 511 => StatusCode::NetworkAuthenticationRequired,
309 _ => StatusCode::Unregistered(n),
310 }
311 }
312
313 #[inline]
334 pub fn as_u16(&self) -> u16 {
335 match *self {
336 StatusCode::Continue => 100,
337 StatusCode::SwitchingProtocols => 101,
338 StatusCode::Processing => 102,
339 StatusCode::Ok => 200,
340 StatusCode::Created => 201,
341 StatusCode::Accepted => 202,
342 StatusCode::NonAuthoritativeInformation => 203,
343 StatusCode::NoContent => 204,
344 StatusCode::ResetContent => 205,
345 StatusCode::PartialContent => 206,
346 StatusCode::MultiStatus => 207,
347 StatusCode::AlreadyReported => 208,
348 StatusCode::ImUsed => 226,
349 StatusCode::MultipleChoices => 300,
350 StatusCode::MovedPermanently => 301,
351 StatusCode::Found => 302,
352 StatusCode::SeeOther => 303,
353 StatusCode::NotModified => 304,
354 StatusCode::UseProxy => 305,
355 StatusCode::TemporaryRedirect => 307,
356 StatusCode::PermanentRedirect => 308,
357 StatusCode::BadRequest => 400,
358 StatusCode::Unauthorized => 401,
359 StatusCode::PaymentRequired => 402,
360 StatusCode::Forbidden => 403,
361 StatusCode::NotFound => 404,
362 StatusCode::MethodNotAllowed => 405,
363 StatusCode::NotAcceptable => 406,
364 StatusCode::ProxyAuthenticationRequired => 407,
365 StatusCode::RequestTimeout => 408,
366 StatusCode::Conflict => 409,
367 StatusCode::Gone => 410,
368 StatusCode::LengthRequired => 411,
369 StatusCode::PreconditionFailed => 412,
370 StatusCode::PayloadTooLarge => 413,
371 StatusCode::UriTooLong => 414,
372 StatusCode::UnsupportedMediaType => 415,
373 StatusCode::RangeNotSatisfiable => 416,
374 StatusCode::ExpectationFailed => 417,
375 StatusCode::ImATeapot => 418,
376 StatusCode::MisdirectedRequest => 421,
377 StatusCode::UnprocessableEntity => 422,
378 StatusCode::Locked => 423,
379 StatusCode::FailedDependency => 424,
380 StatusCode::UpgradeRequired => 426,
381 StatusCode::PreconditionRequired => 428,
382 StatusCode::TooManyRequests => 429,
383 StatusCode::RequestHeaderFieldsTooLarge => 431,
384 StatusCode::UnavailableForLegalReasons => 451,
385 StatusCode::InternalServerError => 500,
386 StatusCode::NotImplemented => 501,
387 StatusCode::BadGateway => 502,
388 StatusCode::ServiceUnavailable => 503,
389 StatusCode::GatewayTimeout => 504,
390 StatusCode::HttpVersionNotSupported => 505,
391 StatusCode::VariantAlsoNegotiates => 506,
392 StatusCode::InsufficientStorage => 507,
393 StatusCode::LoopDetected => 508,
394 StatusCode::NotExtended => 510,
395 StatusCode::NetworkAuthenticationRequired => 511,
396 StatusCode::Unregistered(n) => n,
397 }
398 }
399
400 pub fn canonical_reason(&self) -> Option<&'static str> {
411 match *self {
412 StatusCode::Continue => Some("Continue"),
413 StatusCode::SwitchingProtocols => Some("Switching Protocols"),
414 StatusCode::Processing => Some("Processing"),
415
416 StatusCode::Ok => Some("OK"),
417 StatusCode::Created => Some("Created"),
418 StatusCode::Accepted => Some("Accepted"),
419 StatusCode::NonAuthoritativeInformation => Some("Non-Authoritative Information"),
420 StatusCode::NoContent => Some("No Content"),
421 StatusCode::ResetContent => Some("Reset Content"),
422 StatusCode::PartialContent => Some("Partial Content"),
423 StatusCode::MultiStatus => Some("Multi-Status"),
424 StatusCode::AlreadyReported => Some("Already Reported"),
425
426 StatusCode::ImUsed => Some("IM Used"),
427
428 StatusCode::MultipleChoices => Some("Multiple Choices"),
429 StatusCode::MovedPermanently => Some("Moved Permanently"),
430 StatusCode::Found => Some("Found"),
431 StatusCode::SeeOther => Some("See Other"),
432 StatusCode::NotModified => Some("Not Modified"),
433 StatusCode::UseProxy => Some("Use Proxy"),
434
435 StatusCode::TemporaryRedirect => Some("Temporary Redirect"),
436 StatusCode::PermanentRedirect => Some("Permanent Redirect"),
437
438 StatusCode::BadRequest => Some("Bad Request"),
439 StatusCode::Unauthorized => Some("Unauthorized"),
440 StatusCode::PaymentRequired => Some("Payment Required"),
441 StatusCode::Forbidden => Some("Forbidden"),
442 StatusCode::NotFound => Some("Not Found"),
443 StatusCode::MethodNotAllowed => Some("Method Not Allowed"),
444 StatusCode::NotAcceptable => Some("Not Acceptable"),
445 StatusCode::ProxyAuthenticationRequired => Some("Proxy Authentication Required"),
446 StatusCode::RequestTimeout => Some("Request Timeout"),
447 StatusCode::Conflict => Some("Conflict"),
448 StatusCode::Gone => Some("Gone"),
449 StatusCode::LengthRequired => Some("Length Required"),
450 StatusCode::PreconditionFailed => Some("Precondition Failed"),
451 StatusCode::PayloadTooLarge => Some("Payload Too Large"),
452 StatusCode::UriTooLong => Some("URI Too Long"),
453 StatusCode::UnsupportedMediaType => Some("Unsupported Media Type"),
454 StatusCode::RangeNotSatisfiable => Some("Range Not Satisfiable"),
455 StatusCode::ExpectationFailed => Some("Expectation Failed"),
456 StatusCode::ImATeapot => Some("I'm a teapot"),
457
458 StatusCode::MisdirectedRequest => Some("Misdirected Request"),
459 StatusCode::UnprocessableEntity => Some("Unprocessable Entity"),
460 StatusCode::Locked => Some("Locked"),
461 StatusCode::FailedDependency => Some("Failed Dependency"),
462
463 StatusCode::UpgradeRequired => Some("Upgrade Required"),
464
465 StatusCode::PreconditionRequired => Some("Precondition Required"),
466 StatusCode::TooManyRequests => Some("Too Many Requests"),
467
468 StatusCode::RequestHeaderFieldsTooLarge => Some("Request Header Fields Too Large"),
469
470 StatusCode::UnavailableForLegalReasons => Some("Unavailable For Legal Reasons"),
471
472 StatusCode::InternalServerError => Some("Internal Server Error"),
473 StatusCode::NotImplemented => Some("Not Implemented"),
474 StatusCode::BadGateway => Some("Bad Gateway"),
475 StatusCode::ServiceUnavailable => Some("Service Unavailable"),
476 StatusCode::GatewayTimeout => Some("Gateway Timeout"),
477 StatusCode::HttpVersionNotSupported => Some("HTTP Version Not Supported"),
478 StatusCode::VariantAlsoNegotiates => Some("Variant Also Negotiates"),
479 StatusCode::InsufficientStorage => Some("Insufficient Storage"),
480 StatusCode::LoopDetected => Some("Loop Detected"),
481
482 StatusCode::NotExtended => Some("Not Extended"),
483 StatusCode::NetworkAuthenticationRequired => Some("Network Authentication Required"),
484 StatusCode::Unregistered(..) => None
485 }
486 }
487
488 #[inline]
490 pub fn is_informational(&self) -> bool {
491 self.class() == StatusClass::Informational
492 }
493
494 #[inline]
496 pub fn is_success(&self) -> bool {
497 self.class() == StatusClass::Success
498 }
499
500 #[inline]
502 pub fn is_redirection(&self) -> bool {
503 self.class() == StatusClass::Redirection
504 }
505
506 #[inline]
508 pub fn is_client_error(&self) -> bool {
509 self.class() == StatusClass::ClientError
510 }
511
512 #[inline]
514 pub fn is_server_error(&self) -> bool {
515 self.class() == StatusClass::ServerError
516 }
517
518 #[inline]
520 pub fn is_strange_status(&self) -> bool {
521 self.class() == StatusClass::NoClass
522 }
523
524 fn class(&self) -> StatusClass {
525 match self.as_u16() {
526 100...199 => StatusClass::Informational,
527 200...299 => StatusClass::Success,
528 300...399 => StatusClass::Redirection,
529 400...499 => StatusClass::ClientError,
530 500...599 => StatusClass::ServerError,
531 _ => StatusClass::NoClass,
532 }
533 }
534}
535
536impl Copy for StatusCode {}
537
538impl fmt::Display for StatusCode {
547 #[inline]
548 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
549 write!(f, "{} {}", self.as_u16(),
550 self.canonical_reason().unwrap_or("<unknown status code>"))
551 }
552}
553
554impl PartialEq for StatusCode {
555 #[inline]
556 fn eq(&self, other: &StatusCode) -> bool {
557 self.as_u16() == other.as_u16()
558 }
559}
560
561impl Eq for StatusCode {}
562
563impl Clone for StatusCode {
564 #[inline]
565 fn clone(&self) -> StatusCode {
566 *self
567 }
568}
569
570impl PartialOrd for StatusCode {
571 #[inline]
572 fn partial_cmp(&self, other: &StatusCode) -> Option<Ordering> {
573 self.as_u16().partial_cmp(&(other.as_u16()))
574 }
575}
576
577impl Ord for StatusCode {
578 #[inline]
579 fn cmp(&self, other: &StatusCode) -> Ordering {
580 if *self < *other {
581 Ordering::Less
582 } else if *self > *other {
583 Ordering::Greater
584 } else {
585 Ordering::Equal
586 }
587 }
588}
589
590impl Default for StatusCode {
591 fn default() -> StatusCode {
592 StatusCode::Ok
593 }
594}
595
596impl From<StatusCode> for u16 {
597 fn from(code: StatusCode) -> u16 {
598 code.as_u16()
599 }
600}
601
602#[cfg(feature = "compat")]
603impl From<http::StatusCode> for StatusCode {
604 fn from(status: http::StatusCode) -> StatusCode {
605 StatusCode::try_from(status.as_u16())
606 .expect("attempted to convert invalid status code")
607 }
608}
609
610#[cfg(feature = "compat")]
611impl From<StatusCode> for http::StatusCode {
612 fn from(status: StatusCode) -> http::StatusCode {
613 http::StatusCode::from_u16(status.as_u16())
614 .expect("attempted to convert invalid status code")
615 }
616}
617
618#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Copy)]
644enum StatusClass {
645 Informational,
647
648 Success,
650
651 Redirection,
653
654 ClientError,
656
657 ServerError,
659
660 NoClass,
662}
663
664#[cfg(test)]
665mod tests {
666 use super::*;
667 use super::StatusCode::*;
668
669 fn validate(num: u16, status_code: StatusCode, _default_code: StatusCode, reason: Option<&str>) {
675 assert_eq!(StatusCode::from_u16(num), status_code);
676 assert_eq!(status_code.as_u16(), num);
677 assert_eq!(status_code.canonical_reason(), reason);
679 }
680
681 #[test]
682 fn test_status_code() {
683 validate(99, Unregistered(99), Ok, None);
684
685 validate(100, Continue, Continue, Some("Continue"));
686 validate(101, SwitchingProtocols, Continue, Some("Switching Protocols"));
687 validate(102, Processing, Continue, Some("Processing"));
688
689 validate(200, Ok, Ok, Some("OK"));
690 validate(201, Created, Ok, Some("Created"));
691 validate(202, Accepted, Ok, Some("Accepted"));
692 validate(203, NonAuthoritativeInformation, Ok, Some("Non-Authoritative Information"));
693 validate(204, NoContent, Ok, Some("No Content"));
694 validate(205, ResetContent, Ok, Some("Reset Content"));
695 validate(206, PartialContent, Ok, Some("Partial Content"));
696 validate(207, MultiStatus, Ok, Some("Multi-Status"));
697 validate(208, AlreadyReported, Ok, Some("Already Reported"));
698 validate(226, ImUsed, Ok, Some("IM Used"));
699
700 validate(300, MultipleChoices, MultipleChoices, Some("Multiple Choices"));
701 validate(301, MovedPermanently, MultipleChoices, Some("Moved Permanently"));
702 validate(302, Found, MultipleChoices, Some("Found"));
703 validate(303, SeeOther, MultipleChoices, Some("See Other"));
704 validate(304, NotModified, MultipleChoices, Some("Not Modified"));
705 validate(305, UseProxy, MultipleChoices, Some("Use Proxy"));
706 validate(307, TemporaryRedirect, MultipleChoices, Some("Temporary Redirect"));
707 validate(308, PermanentRedirect, MultipleChoices, Some("Permanent Redirect"));
708
709 validate(400, BadRequest, BadRequest, Some("Bad Request"));
710 validate(401, Unauthorized, BadRequest, Some("Unauthorized"));
711 validate(402, PaymentRequired, BadRequest, Some("Payment Required"));
712 validate(403, Forbidden, BadRequest, Some("Forbidden"));
713 validate(404, NotFound, BadRequest, Some("Not Found"));
714 validate(405, MethodNotAllowed, BadRequest, Some("Method Not Allowed"));
715 validate(406, NotAcceptable, BadRequest, Some("Not Acceptable"));
716 validate(407, ProxyAuthenticationRequired, BadRequest,
717 Some("Proxy Authentication Required"));
718 validate(408, RequestTimeout, BadRequest, Some("Request Timeout"));
719 validate(409, Conflict, BadRequest, Some("Conflict"));
720 validate(410, Gone, BadRequest, Some("Gone"));
721 validate(411, LengthRequired, BadRequest, Some("Length Required"));
722 validate(412, PreconditionFailed, BadRequest, Some("Precondition Failed"));
723 validate(413, PayloadTooLarge, BadRequest, Some("Payload Too Large"));
724 validate(414, UriTooLong, BadRequest, Some("URI Too Long"));
725 validate(415, UnsupportedMediaType, BadRequest, Some("Unsupported Media Type"));
726 validate(416, RangeNotSatisfiable, BadRequest, Some("Range Not Satisfiable"));
727 validate(417, ExpectationFailed, BadRequest, Some("Expectation Failed"));
728 validate(418, ImATeapot, BadRequest, Some("I'm a teapot"));
729 validate(421, MisdirectedRequest, BadRequest, Some("Misdirected Request"));
730 validate(422, UnprocessableEntity, BadRequest, Some("Unprocessable Entity"));
731 validate(423, Locked, BadRequest, Some("Locked"));
732 validate(424, FailedDependency, BadRequest, Some("Failed Dependency"));
733 validate(426, UpgradeRequired, BadRequest, Some("Upgrade Required"));
734 validate(428, PreconditionRequired, BadRequest, Some("Precondition Required"));
735 validate(429, TooManyRequests, BadRequest, Some("Too Many Requests"));
736 validate(431, RequestHeaderFieldsTooLarge, BadRequest,
737 Some("Request Header Fields Too Large"));
738 validate(451, UnavailableForLegalReasons, BadRequest,
739 Some("Unavailable For Legal Reasons"));
740
741 validate(500, InternalServerError, InternalServerError, Some("Internal Server Error"));
742 validate(501, NotImplemented, InternalServerError, Some("Not Implemented"));
743 validate(502, BadGateway, InternalServerError, Some("Bad Gateway"));
744 validate(503, ServiceUnavailable, InternalServerError, Some("Service Unavailable"));
745 validate(504, GatewayTimeout, InternalServerError, Some("Gateway Timeout"));
746 validate(505, HttpVersionNotSupported, InternalServerError,
747 Some("HTTP Version Not Supported"));
748 validate(506, VariantAlsoNegotiates, InternalServerError, Some("Variant Also Negotiates"));
749 validate(507, InsufficientStorage, InternalServerError, Some("Insufficient Storage"));
750 validate(508, LoopDetected, InternalServerError, Some("Loop Detected"));
751 validate(510, NotExtended, InternalServerError, Some("Not Extended"));
752 validate(511, NetworkAuthenticationRequired, InternalServerError,
753 Some("Network Authentication Required"));
754
755 }
756
757 #[test]
758 fn try_from() {
759 StatusCode::try_from(100).unwrap();
760 StatusCode::try_from(599).unwrap();
761 StatusCode::try_from(200).unwrap();
762
763 StatusCode::try_from(99).unwrap_err();
764 StatusCode::try_from(600).unwrap_err();
765 StatusCode::try_from(0).unwrap_err();
766 StatusCode::try_from(1000).unwrap_err();
767 }
768
769 #[test]
770 #[cfg(feature = "compat")]
771 fn test_compat() {
772 use http::{self, HttpTryFrom};
773 for i in 100..600 {
774 let orig_hyper_status = StatusCode::try_from(i).unwrap();
775 let orig_http_status = http::StatusCode::try_from(i).unwrap();
776 let conv_hyper_status: StatusCode = orig_http_status.into();
777 let conv_http_status: http::StatusCode = orig_hyper_status.into();
778 assert_eq!(orig_hyper_status, conv_hyper_status);
779 assert_eq!(orig_http_status, conv_http_status);
780 }
781 }
782}