hyper_old_types/
status.rs

1//! HTTP status codes
2use std::fmt;
3use std::cmp::Ordering;
4
5#[cfg(feature = "compat")]
6use http;
7
8/// An HTTP status code (`status-code` in RFC 7230 et al.).
9///
10/// This enum contains all common status codes and an Unregistered
11/// extension variant. It allows status codes in the range [0, 65535], as any
12/// `u16` integer may be used as a status code for XHR requests. It is
13/// recommended to only use values between [100, 599], since only these are
14/// defined as valid status codes with a status class by HTTP.
15///
16/// IANA maintain the [Hypertext Transfer Protocol (HTTP) Status Code
17/// Registry](http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml) which is
18/// the source for this enum (with one exception, 418 I'm a teapot, which is
19/// inexplicably not in the register).
20#[derive(Debug, Hash)]
21pub enum StatusCode {
22    /// 100 Continue
23    /// [[RFC7231, Section 6.2.1](https://tools.ietf.org/html/rfc7231#section-6.2.1)]
24    Continue,
25    /// 101 Switching Protocols
26    /// [[RFC7231, Section 6.2.2](https://tools.ietf.org/html/rfc7231#section-6.2.2)]
27    SwitchingProtocols,
28    /// 102 Processing
29    /// [[RFC2518](https://tools.ietf.org/html/rfc2518)]
30    Processing,
31
32    /// 200 OK
33    /// [[RFC7231, Section 6.3.1](https://tools.ietf.org/html/rfc7231#section-6.3.1)]
34    Ok,
35    /// 201 Created
36    /// [[RFC7231, Section 6.3.2](https://tools.ietf.org/html/rfc7231#section-6.3.2)]
37    Created,
38    /// 202 Accepted
39    /// [[RFC7231, Section 6.3.3](https://tools.ietf.org/html/rfc7231#section-6.3.3)]
40    Accepted,
41    /// 203 Non-Authoritative Information
42    /// [[RFC7231, Section 6.3.4](https://tools.ietf.org/html/rfc7231#section-6.3.4)]
43    NonAuthoritativeInformation,
44    /// 204 No Content
45    /// [[RFC7231, Section 6.3.5](https://tools.ietf.org/html/rfc7231#section-6.3.5)]
46    NoContent,
47    /// 205 Reset Content
48    /// [[RFC7231, Section 6.3.6](https://tools.ietf.org/html/rfc7231#section-6.3.6)]
49    ResetContent,
50    /// 206 Partial Content
51    /// [[RFC7233, Section 4.1](https://tools.ietf.org/html/rfc7233#section-4.1)]
52    PartialContent,
53    /// 207 Multi-Status
54    /// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
55    MultiStatus,
56    /// 208 Already Reported
57    /// [[RFC5842](https://tools.ietf.org/html/rfc5842)]
58    AlreadyReported,
59
60    /// 226 IM Used
61    /// [[RFC3229](https://tools.ietf.org/html/rfc3229)]
62    ImUsed,
63
64    /// 300 Multiple Choices
65    /// [[RFC7231, Section 6.4.1](https://tools.ietf.org/html/rfc7231#section-6.4.1)]
66    MultipleChoices,
67    /// 301 Moved Permanently
68    /// [[RFC7231, Section 6.4.2](https://tools.ietf.org/html/rfc7231#section-6.4.2)]
69    MovedPermanently,
70    /// 302 Found
71    /// [[RFC7231, Section 6.4.3](https://tools.ietf.org/html/rfc7231#section-6.4.3)]
72    Found,
73    /// 303 See Other
74    /// [[RFC7231, Section 6.4.4](https://tools.ietf.org/html/rfc7231#section-6.4.4)]
75    SeeOther,
76    /// 304 Not Modified
77    /// [[RFC7232, Section 4.1](https://tools.ietf.org/html/rfc7232#section-4.1)]
78    NotModified,
79    /// 305 Use Proxy
80    /// [[RFC7231, Section 6.4.5](https://tools.ietf.org/html/rfc7231#section-6.4.5)]
81    UseProxy,
82    /// 307 Temporary Redirect
83    /// [[RFC7231, Section 6.4.7](https://tools.ietf.org/html/rfc7231#section-6.4.7)]
84    TemporaryRedirect,
85    /// 308 Permanent Redirect
86    /// [[RFC7238](https://tools.ietf.org/html/rfc7238)]
87    PermanentRedirect,
88
89    /// 400 Bad Request
90    /// [[RFC7231, Section 6.5.1](https://tools.ietf.org/html/rfc7231#section-6.5.1)]
91    BadRequest,
92    /// 401 Unauthorized
93    /// [[RFC7235, Section 3.1](https://tools.ietf.org/html/rfc7235#section-3.1)]
94    Unauthorized,
95    /// 402 Payment Required
96    /// [[RFC7231, Section 6.5.2](https://tools.ietf.org/html/rfc7231#section-6.5.2)]
97    PaymentRequired,
98    /// 403 Forbidden
99    /// [[RFC7231, Section 6.5.3](https://tools.ietf.org/html/rfc7231#section-6.5.3)]
100    Forbidden,
101    /// 404 Not Found
102    /// [[RFC7231, Section 6.5.4](https://tools.ietf.org/html/rfc7231#section-6.5.4)]
103    NotFound,
104    /// 405 Method Not Allowed
105    /// [[RFC7231, Section 6.5.5](https://tools.ietf.org/html/rfc7231#section-6.5.5)]
106    MethodNotAllowed,
107    /// 406 Not Acceptable
108    /// [[RFC7231, Section 6.5.6](https://tools.ietf.org/html/rfc7231#section-6.5.6)]
109    NotAcceptable,
110    /// 407 Proxy Authentication Required
111    /// [[RFC7235, Section 3.2](https://tools.ietf.org/html/rfc7235#section-3.2)]
112    ProxyAuthenticationRequired,
113    /// 408 Request Timeout
114    /// [[RFC7231, Section 6.5.7](https://tools.ietf.org/html/rfc7231#section-6.5.7)]
115    RequestTimeout,
116    /// 409 Conflict
117    /// [[RFC7231, Section 6.5.8](https://tools.ietf.org/html/rfc7231#section-6.5.8)]
118    Conflict,
119    /// 410 Gone
120    /// [[RFC7231, Section 6.5.9](https://tools.ietf.org/html/rfc7231#section-6.5.9)]
121    Gone,
122    /// 411 Length Required
123    /// [[RFC7231, Section 6.5.10](https://tools.ietf.org/html/rfc7231#section-6.5.10)]
124    LengthRequired,
125    /// 412 Precondition Failed
126    /// [[RFC7232, Section 4.2](https://tools.ietf.org/html/rfc7232#section-4.2)]
127    PreconditionFailed,
128    /// 413 Payload Too Large
129    /// [[RFC7231, Section 6.5.11](https://tools.ietf.org/html/rfc7231#section-6.5.11)]
130    PayloadTooLarge,
131    /// 414 URI Too Long
132    /// [[RFC7231, Section 6.5.12](https://tools.ietf.org/html/rfc7231#section-6.5.12)]
133    UriTooLong,
134    /// 415 Unsupported Media Type
135    /// [[RFC7231, Section 6.5.13](https://tools.ietf.org/html/rfc7231#section-6.5.13)]
136    UnsupportedMediaType,
137    /// 416 Range Not Satisfiable
138    /// [[RFC7233, Section 4.4](https://tools.ietf.org/html/rfc7233#section-4.4)]
139    RangeNotSatisfiable,
140    /// 417 Expectation Failed
141    /// [[RFC7231, Section 6.5.14](https://tools.ietf.org/html/rfc7231#section-6.5.14)]
142    ExpectationFailed,
143    /// 418 I'm a teapot
144    /// [curiously, not registered by IANA, but [RFC2324](https://tools.ietf.org/html/rfc2324)]
145    ImATeapot,
146
147    /// 421 Misdirected Request
148    /// [RFC7540, Section 9.1.2](http://tools.ietf.org/html/rfc7540#section-9.1.2)
149    MisdirectedRequest,
150    /// 422 Unprocessable Entity
151    /// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
152    UnprocessableEntity,
153    /// 423 Locked
154    /// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
155    Locked,
156    /// 424 Failed Dependency
157    /// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
158    FailedDependency,
159
160    /// 426 Upgrade Required
161    /// [[RFC7231, Section 6.5.15](https://tools.ietf.org/html/rfc7231#section-6.5.15)]
162    UpgradeRequired,
163
164    /// 428 Precondition Required
165    /// [[RFC6585](https://tools.ietf.org/html/rfc6585)]
166    PreconditionRequired,
167    /// 429 Too Many Requests
168    /// [[RFC6585](https://tools.ietf.org/html/rfc6585)]
169    TooManyRequests,
170
171    /// 431 Request Header Fields Too Large
172    /// [[RFC6585](https://tools.ietf.org/html/rfc6585)]
173    RequestHeaderFieldsTooLarge,
174
175    /// 451 Unavailable For Legal Reasons
176    /// [[RFC7725](http://tools.ietf.org/html/rfc7725)]
177    UnavailableForLegalReasons,
178
179    /// 500 Internal Server Error
180    /// [[RFC7231, Section 6.6.1](https://tools.ietf.org/html/rfc7231#section-6.6.1)]
181    InternalServerError,
182    /// 501 Not Implemented
183    /// [[RFC7231, Section 6.6.2](https://tools.ietf.org/html/rfc7231#section-6.6.2)]
184    NotImplemented,
185    /// 502 Bad Gateway
186    /// [[RFC7231, Section 6.6.3](https://tools.ietf.org/html/rfc7231#section-6.6.3)]
187    BadGateway,
188    /// 503 Service Unavailable
189    /// [[RFC7231, Section 6.6.4](https://tools.ietf.org/html/rfc7231#section-6.6.4)]
190    ServiceUnavailable,
191    /// 504 Gateway Timeout
192    /// [[RFC7231, Section 6.6.5](https://tools.ietf.org/html/rfc7231#section-6.6.5)]
193    GatewayTimeout,
194    /// 505 HTTP Version Not Supported
195    /// [[RFC7231, Section 6.6.6](https://tools.ietf.org/html/rfc7231#section-6.6.6)]
196    HttpVersionNotSupported,
197    /// 506 Variant Also Negotiates
198    /// [[RFC2295](https://tools.ietf.org/html/rfc2295)]
199    VariantAlsoNegotiates,
200    /// 507 Insufficient Storage
201    /// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
202    InsufficientStorage,
203    /// 508 Loop Detected
204    /// [[RFC5842](https://tools.ietf.org/html/rfc5842)]
205    LoopDetected,
206
207    /// 510 Not Extended
208    /// [[RFC2774](https://tools.ietf.org/html/rfc2774)]
209    NotExtended,
210    /// 511 Network Authentication Required
211    /// [[RFC6585](https://tools.ietf.org/html/rfc6585)]
212    NetworkAuthenticationRequired,
213
214    /// A status code not in the IANA HTTP status code registry or very well known
215    // `ImATeapot` is not registered.
216    Unregistered(u16),
217}
218
219#[derive(Debug)]
220pub struct InvalidStatusCode {
221    _inner: (),
222}
223
224impl StatusCode {
225
226    /// Try to convert a `u16` into a `StatusCode`.
227    ///
228    /// # Errors
229    ///
230    /// This will return an error if the provided argument is not within the
231    /// range `100...599`.
232    ///
233    /// # Note
234    ///
235    /// This function is temporary. When the `TryFrom` trait becomes stable,
236    /// this will be deprecated and replaced by `TryFrom<u16>`.
237    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    /// Get the `u16` code from this `StatusCode`.
314    ///
315    /// Also available as `From`/`Into<u16>`.
316    ///
317    /// # Example
318    ///
319    /// ```
320    /// use hyper::StatusCode;
321    ///
322    /// let status = StatusCode::Ok;
323    /// assert_eq!(status.as_u16(), 200);
324    ///
325    /// // Into
326    /// let num: u16 = status.into();
327    /// assert_eq!(num, 200);
328    ///
329    /// // From
330    /// let other = u16::from(status);
331    /// assert_eq!(num, other);
332    /// ```
333    #[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    /// Get the standardised `reason-phrase` for this status code.
401    ///
402    /// This is mostly here for servers writing responses, but could potentially have application
403    /// at other times.
404    ///
405    /// The reason phrase is defined as being exclusively for human readers. You should avoid
406    /// deriving any meaning from it at all costs.
407    ///
408    /// Bear in mind also that in HTTP/2.0 the reason phrase is abolished from transmission, and so
409    /// this canonical reason phrase really is the only reason phrase you’ll find.
410    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    /// Check if this `StatusCode` is within 100-199.
489    #[inline]
490    pub fn is_informational(&self) -> bool {
491        self.class() == StatusClass::Informational
492    }
493
494    /// Check if this `StatusCode` is within 200-299.
495    #[inline]
496    pub fn is_success(&self) -> bool {
497        self.class() == StatusClass::Success
498    }
499
500    /// Check if this `StatusCode` is within 300-399.
501    #[inline]
502    pub fn is_redirection(&self) -> bool {
503        self.class() == StatusClass::Redirection
504    }
505
506    /// Check if this `StatusCode` is within 400-499.
507    #[inline]
508    pub fn is_client_error(&self) -> bool {
509        self.class() == StatusClass::ClientError
510    }
511
512    /// Check if this `StatusCode` is within 500-599.
513    #[inline]
514    pub fn is_server_error(&self) -> bool {
515        self.class() == StatusClass::ServerError
516    }
517
518    /// Check if this `StatusCode` is not within 100-599.
519    #[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
538/// Formats the status code, *including* the canonical reason.
539///
540/// ```rust
541/// # use hyper::StatusCode::{ImATeapot, Unregistered};
542/// assert_eq!(format!("{}", ImATeapot), "418 I'm a teapot");
543/// assert_eq!(format!("{}", Unregistered(123)),
544///            "123 <unknown status code>");
545/// ```
546impl 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/// The class of an HTTP `status-code`.
619///
620/// [RFC 7231, section 6 (Response Status Codes)](https://tools.ietf.org/html/rfc7231#section-6):
621///
622/// > The first digit of the status-code defines the class of response.
623/// > The last two digits do not have any categorization role.
624///
625/// And:
626///
627/// > HTTP status codes are extensible.  HTTP clients are not required to
628/// > understand the meaning of all registered status codes, though such
629/// > understanding is obviously desirable.  However, a client MUST
630/// > understand the class of any status code, as indicated by the first
631/// > digit, and treat an unrecognized status code as being equivalent to
632/// > the x00 status code of that class, with the exception that a
633/// > recipient MUST NOT cache a response with an unrecognized status code.
634/// >
635/// > For example, if an unrecognized status code of 471 is received by a
636/// > client, the client can assume that there was something wrong with its
637/// > request and treat the response as if it had received a 400 (Bad
638/// > Request) status code.  The response message will usually contain a
639/// > representation that explains the status.
640///
641/// This can be used in cases where a status code’s meaning is unknown, also,
642/// to get the appropriate *category* of status.
643#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Copy)]
644enum StatusClass {
645    /// 1xx (Informational): The request was received, continuing process
646    Informational,
647
648    /// 2xx (Success): The request was successfully received, understood, and accepted
649    Success,
650
651    /// 3xx (Redirection): Further action needs to be taken in order to complete the request
652    Redirection,
653
654    /// 4xx (Client Error): The request contains bad syntax or cannot be fulfilled
655    ClientError,
656
657    /// 5xx (Server Error): The server failed to fulfill an apparently valid request
658    ServerError,
659
660    /// A status code lower than 100 or higher than 599. These codes do no belong to any class.
661    NoClass,
662}
663
664#[cfg(test)]
665mod tests {
666    use super::*;
667    use super::StatusCode::*;
668
669    // Check that the following entities are properly inter-connected:
670    //   - numerical code
671    //   - status code
672    //   - default code (for the given status code)
673    //   - canonical reason
674    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.class().default_code(), default_code);
678        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}