hyper_sync/
status.rs

1//! HTTP status codes
2use std::fmt;
3use std::cmp::Ordering;
4
5// shamelessly lifted from Teepee. I tried a few schemes, this really
6// does seem like the best. Improved scheme to support arbitrary status codes.
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/// If you encounter a status code that you do not know how to deal with, you
17/// should treat it as the `x00` status code—e.g. for code 123, treat it as
18/// 100 (Continue). This can be achieved with
19/// `self.class().default_code()`:
20///
21/// ```rust
22/// # use hyper_sync::status::StatusCode;
23/// let status = StatusCode::Unregistered(123);
24/// assert_eq!(status.class().default_code(), StatusCode::Continue);
25/// ```
26///
27/// IANA maintain the [Hypertext Transfer Protocol (HTTP) Status Code
28/// Registry](http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml) which is
29/// the source for this enum (with one exception, 418 I'm a teapot, which is
30/// inexplicably not in the register).
31#[derive(Debug, Hash)]
32pub enum StatusCode {
33    /// 100 Continue
34    /// [[RFC7231, Section 6.2.1](https://tools.ietf.org/html/rfc7231#section-6.2.1)]
35    Continue,
36    /// 101 Switching Protocols
37    /// [[RFC7231, Section 6.2.2](https://tools.ietf.org/html/rfc7231#section-6.2.2)]
38    SwitchingProtocols,
39    /// 102 Processing
40    /// [[RFC2518](https://tools.ietf.org/html/rfc2518)]
41    Processing,
42
43    /// 200 OK
44    /// [[RFC7231, Section 6.3.1](https://tools.ietf.org/html/rfc7231#section-6.3.1)]
45    Ok,
46    /// 201 Created
47    /// [[RFC7231, Section 6.3.2](https://tools.ietf.org/html/rfc7231#section-6.3.2)]
48    Created,
49    /// 202 Accepted
50    /// [[RFC7231, Section 6.3.3](https://tools.ietf.org/html/rfc7231#section-6.3.3)]
51    Accepted,
52    /// 203 Non-Authoritative Information
53    /// [[RFC7231, Section 6.3.4](https://tools.ietf.org/html/rfc7231#section-6.3.4)]
54    NonAuthoritativeInformation,
55    /// 204 No Content
56    /// [[RFC7231, Section 6.3.5](https://tools.ietf.org/html/rfc7231#section-6.3.5)]
57    NoContent,
58    /// 205 Reset Content
59    /// [[RFC7231, Section 6.3.6](https://tools.ietf.org/html/rfc7231#section-6.3.6)]
60    ResetContent,
61    /// 206 Partial Content
62    /// [[RFC7233, Section 4.1](https://tools.ietf.org/html/rfc7233#section-4.1)]
63    PartialContent,
64    /// 207 Multi-Status
65    /// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
66    MultiStatus,
67    /// 208 Already Reported
68    /// [[RFC5842](https://tools.ietf.org/html/rfc5842)]
69    AlreadyReported,
70
71    /// 226 IM Used
72    /// [[RFC3229](https://tools.ietf.org/html/rfc3229)]
73    ImUsed,
74
75    /// 300 Multiple Choices
76    /// [[RFC7231, Section 6.4.1](https://tools.ietf.org/html/rfc7231#section-6.4.1)]
77    MultipleChoices,
78    /// 301 Moved Permanently
79    /// [[RFC7231, Section 6.4.2](https://tools.ietf.org/html/rfc7231#section-6.4.2)]
80    MovedPermanently,
81    /// 302 Found
82    /// [[RFC7231, Section 6.4.3](https://tools.ietf.org/html/rfc7231#section-6.4.3)]
83    Found,
84    /// 303 See Other
85    /// [[RFC7231, Section 6.4.4](https://tools.ietf.org/html/rfc7231#section-6.4.4)]
86    SeeOther,
87    /// 304 Not Modified
88    /// [[RFC7232, Section 4.1](https://tools.ietf.org/html/rfc7232#section-4.1)]
89    NotModified,
90    /// 305 Use Proxy
91    /// [[RFC7231, Section 6.4.5](https://tools.ietf.org/html/rfc7231#section-6.4.5)]
92    UseProxy,
93    /// 307 Temporary Redirect
94    /// [[RFC7231, Section 6.4.7](https://tools.ietf.org/html/rfc7231#section-6.4.7)]
95    TemporaryRedirect,
96    /// 308 Permanent Redirect
97    /// [[RFC7238](https://tools.ietf.org/html/rfc7238)]
98    PermanentRedirect,
99
100    /// 400 Bad Request
101    /// [[RFC7231, Section 6.5.1](https://tools.ietf.org/html/rfc7231#section-6.5.1)]
102    BadRequest,
103    /// 401 Unauthorized
104    /// [[RFC7235, Section 3.1](https://tools.ietf.org/html/rfc7235#section-3.1)]
105    Unauthorized,
106    /// 402 Payment Required
107    /// [[RFC7231, Section 6.5.2](https://tools.ietf.org/html/rfc7231#section-6.5.2)]
108    PaymentRequired,
109    /// 403 Forbidden
110    /// [[RFC7231, Section 6.5.3](https://tools.ietf.org/html/rfc7231#section-6.5.3)]
111    Forbidden,
112    /// 404 Not Found
113    /// [[RFC7231, Section 6.5.4](https://tools.ietf.org/html/rfc7231#section-6.5.4)]
114    NotFound,
115    /// 405 Method Not Allowed
116    /// [[RFC7231, Section 6.5.5](https://tools.ietf.org/html/rfc7231#section-6.5.5)]
117    MethodNotAllowed,
118    /// 406 Not Acceptable
119    /// [[RFC7231, Section 6.5.6](https://tools.ietf.org/html/rfc7231#section-6.5.6)]
120    NotAcceptable,
121    /// 407 Proxy Authentication Required
122    /// [[RFC7235, Section 3.2](https://tools.ietf.org/html/rfc7235#section-3.2)]
123    ProxyAuthenticationRequired,
124    /// 408 Request Timeout
125    /// [[RFC7231, Section 6.5.7](https://tools.ietf.org/html/rfc7231#section-6.5.7)]
126    RequestTimeout,
127    /// 409 Conflict
128    /// [[RFC7231, Section 6.5.8](https://tools.ietf.org/html/rfc7231#section-6.5.8)]
129    Conflict,
130    /// 410 Gone
131    /// [[RFC7231, Section 6.5.9](https://tools.ietf.org/html/rfc7231#section-6.5.9)]
132    Gone,
133    /// 411 Length Required
134    /// [[RFC7231, Section 6.5.10](https://tools.ietf.org/html/rfc7231#section-6.5.10)]
135    LengthRequired,
136    /// 412 Precondition Failed
137    /// [[RFC7232, Section 4.2](https://tools.ietf.org/html/rfc7232#section-4.2)]
138    PreconditionFailed,
139    /// 413 Payload Too Large
140    /// [[RFC7231, Section 6.5.11](https://tools.ietf.org/html/rfc7231#section-6.5.11)]
141    PayloadTooLarge,
142    /// 414 URI Too Long
143    /// [[RFC7231, Section 6.5.12](https://tools.ietf.org/html/rfc7231#section-6.5.12)]
144    UriTooLong,
145    /// 415 Unsupported Media Type
146    /// [[RFC7231, Section 6.5.13](https://tools.ietf.org/html/rfc7231#section-6.5.13)]
147    UnsupportedMediaType,
148    /// 416 Range Not Satisfiable
149    /// [[RFC7233, Section 4.4](https://tools.ietf.org/html/rfc7233#section-4.4)]
150    RangeNotSatisfiable,
151    /// 417 Expectation Failed
152    /// [[RFC7231, Section 6.5.14](https://tools.ietf.org/html/rfc7231#section-6.5.14)]
153    ExpectationFailed,
154    /// 418 I'm a teapot
155    /// [curiously, not registered by IANA, but [RFC2324](https://tools.ietf.org/html/rfc2324)]
156    ImATeapot,
157
158    /// 421 Misdirected Request
159    /// [RFC7540, Section 9.1.2](http://tools.ietf.org/html/rfc7540#section-9.1.2)
160    MisdirectedRequest,
161    /// 422 Unprocessable Entity
162    /// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
163    UnprocessableEntity,
164    /// 423 Locked
165    /// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
166    Locked,
167    /// 424 Failed Dependency
168    /// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
169    FailedDependency,
170
171    /// 426 Upgrade Required
172    /// [[RFC7231, Section 6.5.15](https://tools.ietf.org/html/rfc7231#section-6.5.15)]
173    UpgradeRequired,
174
175    /// 428 Precondition Required
176    /// [[RFC6585](https://tools.ietf.org/html/rfc6585)]
177    PreconditionRequired,
178    /// 429 Too Many Requests
179    /// [[RFC6585](https://tools.ietf.org/html/rfc6585)]
180    TooManyRequests,
181
182    /// 431 Request Header Fields Too Large
183    /// [[RFC6585](https://tools.ietf.org/html/rfc6585)]
184    RequestHeaderFieldsTooLarge,
185
186    /// 451 Unavailable For Legal Reasons
187    /// [[RFC7725](http://tools.ietf.org/html/rfc7725)]
188    UnavailableForLegalReasons,
189
190    /// 500 Internal Server Error
191    /// [[RFC7231, Section 6.6.1](https://tools.ietf.org/html/rfc7231#section-6.6.1)]
192    InternalServerError,
193    /// 501 Not Implemented
194    /// [[RFC7231, Section 6.6.2](https://tools.ietf.org/html/rfc7231#section-6.6.2)]
195    NotImplemented,
196    /// 502 Bad Gateway
197    /// [[RFC7231, Section 6.6.3](https://tools.ietf.org/html/rfc7231#section-6.6.3)]
198    BadGateway,
199    /// 503 Service Unavailable
200    /// [[RFC7231, Section 6.6.4](https://tools.ietf.org/html/rfc7231#section-6.6.4)]
201    ServiceUnavailable,
202    /// 504 Gateway Timeout
203    /// [[RFC7231, Section 6.6.5](https://tools.ietf.org/html/rfc7231#section-6.6.5)]
204    GatewayTimeout,
205    /// 505 HTTP Version Not Supported
206    /// [[RFC7231, Section 6.6.6](https://tools.ietf.org/html/rfc7231#section-6.6.6)]
207    HttpVersionNotSupported,
208    /// 506 Variant Also Negotiates
209    /// [[RFC2295](https://tools.ietf.org/html/rfc2295)]
210    VariantAlsoNegotiates,
211    /// 507 Insufficient Storage
212    /// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
213    InsufficientStorage,
214    /// 508 Loop Detected
215    /// [[RFC5842](https://tools.ietf.org/html/rfc5842)]
216    LoopDetected,
217
218    /// 510 Not Extended
219    /// [[RFC2774](https://tools.ietf.org/html/rfc2774)]
220    NotExtended,
221    /// 511 Network Authentication Required
222    /// [[RFC6585](https://tools.ietf.org/html/rfc6585)]
223    NetworkAuthenticationRequired,
224
225    /// A status code not in the IANA HTTP status code registry or very well known
226    // `ImATeapot` is not registered.
227    Unregistered(u16),
228}
229
230impl StatusCode {
231
232    #[doc(hidden)]
233    pub fn from_u16(n: u16) -> StatusCode {
234        match n {
235            100 => StatusCode::Continue,
236            101 => StatusCode::SwitchingProtocols,
237            102 => StatusCode::Processing,
238            200 => StatusCode::Ok,
239            201 => StatusCode::Created,
240            202 => StatusCode::Accepted,
241            203 => StatusCode::NonAuthoritativeInformation,
242            204 => StatusCode::NoContent,
243            205 => StatusCode::ResetContent,
244            206 => StatusCode::PartialContent,
245            207 => StatusCode::MultiStatus,
246            208 => StatusCode::AlreadyReported,
247            226 => StatusCode::ImUsed,
248            300 => StatusCode::MultipleChoices,
249            301 => StatusCode::MovedPermanently,
250            302 => StatusCode::Found,
251            303 => StatusCode::SeeOther,
252            304 => StatusCode::NotModified,
253            305 => StatusCode::UseProxy,
254            307 => StatusCode::TemporaryRedirect,
255            308 => StatusCode::PermanentRedirect,
256            400 => StatusCode::BadRequest,
257            401 => StatusCode::Unauthorized,
258            402 => StatusCode::PaymentRequired,
259            403 => StatusCode::Forbidden,
260            404 => StatusCode::NotFound,
261            405 => StatusCode::MethodNotAllowed,
262            406 => StatusCode::NotAcceptable,
263            407 => StatusCode::ProxyAuthenticationRequired,
264            408 => StatusCode::RequestTimeout,
265            409 => StatusCode::Conflict,
266            410 => StatusCode::Gone,
267            411 => StatusCode::LengthRequired,
268            412 => StatusCode::PreconditionFailed,
269            413 => StatusCode::PayloadTooLarge,
270            414 => StatusCode::UriTooLong,
271            415 => StatusCode::UnsupportedMediaType,
272            416 => StatusCode::RangeNotSatisfiable,
273            417 => StatusCode::ExpectationFailed,
274            418 => StatusCode::ImATeapot,
275            421 => StatusCode::MisdirectedRequest,
276            422 => StatusCode::UnprocessableEntity,
277            423 => StatusCode::Locked,
278            424 => StatusCode::FailedDependency,
279            426 => StatusCode::UpgradeRequired,
280            428 => StatusCode::PreconditionRequired,
281            429 => StatusCode::TooManyRequests,
282            431 => StatusCode::RequestHeaderFieldsTooLarge,
283            451 => StatusCode::UnavailableForLegalReasons,
284            500 => StatusCode::InternalServerError,
285            501 => StatusCode::NotImplemented,
286            502 => StatusCode::BadGateway,
287            503 => StatusCode::ServiceUnavailable,
288            504 => StatusCode::GatewayTimeout,
289            505 => StatusCode::HttpVersionNotSupported,
290            506 => StatusCode::VariantAlsoNegotiates,
291            507 => StatusCode::InsufficientStorage,
292            508 => StatusCode::LoopDetected,
293            510 => StatusCode::NotExtended,
294            511 => StatusCode::NetworkAuthenticationRequired,
295            _ => StatusCode::Unregistered(n),
296        }
297    }
298
299    #[doc(hidden)]
300    pub fn to_u16(&self) -> u16 {
301        match *self {
302            StatusCode::Continue => 100,
303            StatusCode::SwitchingProtocols => 101,
304            StatusCode::Processing => 102,
305            StatusCode::Ok => 200,
306            StatusCode::Created => 201,
307            StatusCode::Accepted => 202,
308            StatusCode::NonAuthoritativeInformation => 203,
309            StatusCode::NoContent => 204,
310            StatusCode::ResetContent => 205,
311            StatusCode::PartialContent => 206,
312            StatusCode::MultiStatus => 207,
313            StatusCode::AlreadyReported => 208,
314            StatusCode::ImUsed => 226,
315            StatusCode::MultipleChoices => 300,
316            StatusCode::MovedPermanently => 301,
317            StatusCode::Found => 302,
318            StatusCode::SeeOther => 303,
319            StatusCode::NotModified => 304,
320            StatusCode::UseProxy => 305,
321            StatusCode::TemporaryRedirect => 307,
322            StatusCode::PermanentRedirect => 308,
323            StatusCode::BadRequest => 400,
324            StatusCode::Unauthorized => 401,
325            StatusCode::PaymentRequired => 402,
326            StatusCode::Forbidden => 403,
327            StatusCode::NotFound => 404,
328            StatusCode::MethodNotAllowed => 405,
329            StatusCode::NotAcceptable => 406,
330            StatusCode::ProxyAuthenticationRequired => 407,
331            StatusCode::RequestTimeout => 408,
332            StatusCode::Conflict => 409,
333            StatusCode::Gone => 410,
334            StatusCode::LengthRequired => 411,
335            StatusCode::PreconditionFailed => 412,
336            StatusCode::PayloadTooLarge => 413,
337            StatusCode::UriTooLong => 414,
338            StatusCode::UnsupportedMediaType => 415,
339            StatusCode::RangeNotSatisfiable => 416,
340            StatusCode::ExpectationFailed => 417,
341            StatusCode::ImATeapot => 418,
342            StatusCode::MisdirectedRequest => 421,
343            StatusCode::UnprocessableEntity => 422,
344            StatusCode::Locked => 423,
345            StatusCode::FailedDependency => 424,
346            StatusCode::UpgradeRequired => 426,
347            StatusCode::PreconditionRequired => 428,
348            StatusCode::TooManyRequests => 429,
349            StatusCode::RequestHeaderFieldsTooLarge => 431,
350            StatusCode::UnavailableForLegalReasons => 451,
351            StatusCode::InternalServerError => 500,
352            StatusCode::NotImplemented => 501,
353            StatusCode::BadGateway => 502,
354            StatusCode::ServiceUnavailable => 503,
355            StatusCode::GatewayTimeout => 504,
356            StatusCode::HttpVersionNotSupported => 505,
357            StatusCode::VariantAlsoNegotiates => 506,
358            StatusCode::InsufficientStorage => 507,
359            StatusCode::LoopDetected => 508,
360            StatusCode::NotExtended => 510,
361            StatusCode::NetworkAuthenticationRequired => 511,
362            StatusCode::Unregistered(n) => n,
363        }
364    }
365
366    /// Get the standardised `reason-phrase` for this status code.
367    ///
368    /// This is mostly here for servers writing responses, but could potentially have application
369    /// at other times.
370    ///
371    /// The reason phrase is defined as being exclusively for human readers. You should avoid
372    /// deriving any meaning from it at all costs.
373    ///
374    /// Bear in mind also that in HTTP/2.0 the reason phrase is abolished from transmission, and so
375    /// this canonical reason phrase really is the only reason phrase you’ll find.
376    pub fn canonical_reason(&self) -> Option<&'static str> {
377        match *self {
378            StatusCode::Continue => Some("Continue"),
379            StatusCode::SwitchingProtocols => Some("Switching Protocols"),
380            StatusCode::Processing => Some("Processing"),
381
382            StatusCode::Ok => Some("OK"),
383            StatusCode::Created => Some("Created"),
384            StatusCode::Accepted => Some("Accepted"),
385            StatusCode::NonAuthoritativeInformation => Some("Non-Authoritative Information"),
386            StatusCode::NoContent => Some("No Content"),
387            StatusCode::ResetContent => Some("Reset Content"),
388            StatusCode::PartialContent => Some("Partial Content"),
389            StatusCode::MultiStatus => Some("Multi-Status"),
390            StatusCode::AlreadyReported => Some("Already Reported"),
391
392            StatusCode::ImUsed => Some("IM Used"),
393
394            StatusCode::MultipleChoices => Some("Multiple Choices"),
395            StatusCode::MovedPermanently => Some("Moved Permanently"),
396            StatusCode::Found => Some("Found"),
397            StatusCode::SeeOther => Some("See Other"),
398            StatusCode::NotModified => Some("Not Modified"),
399            StatusCode::UseProxy => Some("Use Proxy"),
400
401            StatusCode::TemporaryRedirect => Some("Temporary Redirect"),
402            StatusCode::PermanentRedirect => Some("Permanent Redirect"),
403
404            StatusCode::BadRequest => Some("Bad Request"),
405            StatusCode::Unauthorized => Some("Unauthorized"),
406            StatusCode::PaymentRequired => Some("Payment Required"),
407            StatusCode::Forbidden => Some("Forbidden"),
408            StatusCode::NotFound => Some("Not Found"),
409            StatusCode::MethodNotAllowed => Some("Method Not Allowed"),
410            StatusCode::NotAcceptable => Some("Not Acceptable"),
411            StatusCode::ProxyAuthenticationRequired => Some("Proxy Authentication Required"),
412            StatusCode::RequestTimeout => Some("Request Timeout"),
413            StatusCode::Conflict => Some("Conflict"),
414            StatusCode::Gone => Some("Gone"),
415            StatusCode::LengthRequired => Some("Length Required"),
416            StatusCode::PreconditionFailed => Some("Precondition Failed"),
417            StatusCode::PayloadTooLarge => Some("Payload Too Large"),
418            StatusCode::UriTooLong => Some("URI Too Long"),
419            StatusCode::UnsupportedMediaType => Some("Unsupported Media Type"),
420            StatusCode::RangeNotSatisfiable => Some("Range Not Satisfiable"),
421            StatusCode::ExpectationFailed => Some("Expectation Failed"),
422            StatusCode::ImATeapot => Some("I'm a teapot"),
423
424            StatusCode::MisdirectedRequest => Some("Misdirected Request"),
425            StatusCode::UnprocessableEntity => Some("Unprocessable Entity"),
426            StatusCode::Locked => Some("Locked"),
427            StatusCode::FailedDependency => Some("Failed Dependency"),
428
429            StatusCode::UpgradeRequired => Some("Upgrade Required"),
430
431            StatusCode::PreconditionRequired => Some("Precondition Required"),
432            StatusCode::TooManyRequests => Some("Too Many Requests"),
433
434            StatusCode::RequestHeaderFieldsTooLarge => Some("Request Header Fields Too Large"),
435
436            StatusCode::UnavailableForLegalReasons => Some("Unavailable For Legal Reasons"),
437
438            StatusCode::InternalServerError => Some("Internal Server Error"),
439            StatusCode::NotImplemented => Some("Not Implemented"),
440            StatusCode::BadGateway => Some("Bad Gateway"),
441            StatusCode::ServiceUnavailable => Some("Service Unavailable"),
442            StatusCode::GatewayTimeout => Some("Gateway Timeout"),
443            StatusCode::HttpVersionNotSupported => Some("HTTP Version Not Supported"),
444            StatusCode::VariantAlsoNegotiates => Some("Variant Also Negotiates"),
445            StatusCode::InsufficientStorage => Some("Insufficient Storage"),
446            StatusCode::LoopDetected => Some("Loop Detected"),
447
448            StatusCode::NotExtended => Some("Not Extended"),
449            StatusCode::NetworkAuthenticationRequired => Some("Network Authentication Required"),
450            StatusCode::Unregistered(..) => None
451        }
452    }
453
454    /// Determine the class of a status code, based on its first digit.
455    pub fn class(&self) -> StatusClass {
456        match self.to_u16() {
457            100...199 => StatusClass::Informational,
458            200...299 => StatusClass::Success,
459            300...399 => StatusClass::Redirection,
460            400...499 => StatusClass::ClientError,
461            500...599 => StatusClass::ServerError,
462            _ => StatusClass::NoClass,
463        }
464    }
465
466    /// Check if class is Informational.
467    pub fn is_informational(&self) -> bool {
468        self.class() == StatusClass::Informational
469    }
470
471    /// Check if class is Success.
472    pub fn is_success(&self) -> bool {
473        self.class() == StatusClass::Success
474    }
475
476    /// Check if class is Redirection.
477    pub fn is_redirection(&self) -> bool {
478        self.class() == StatusClass::Redirection
479    }
480
481    /// Check if class is ClientError.
482    pub fn is_client_error(&self) -> bool {
483        self.class() == StatusClass::ClientError
484    }
485
486    /// Check if class is ServerError.
487    pub fn is_server_error(&self) -> bool {
488        self.class() == StatusClass::ServerError
489    }
490
491    /// Check if class is NoClass
492    pub fn is_strange_status(&self) -> bool {
493        self.class() == StatusClass::NoClass
494    }
495}
496
497impl Copy for StatusCode {}
498
499/// Formats the status code, *including* the canonical reason.
500///
501/// ```rust
502/// # use hyper_sync::status::StatusCode::{ImATeapot, Unregistered};
503/// assert_eq!(format!("{}", ImATeapot), "418 I'm a teapot");
504/// assert_eq!(format!("{}", Unregistered(123)),
505///            "123 <unknown status code>");
506/// ```
507impl fmt::Display for StatusCode {
508    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
509        write!(f, "{} {}", self.to_u16(),
510               self.canonical_reason().unwrap_or("<unknown status code>"))
511    }
512}
513
514impl PartialEq for StatusCode {
515    #[inline]
516    fn eq(&self, other: &StatusCode) -> bool {
517        self.to_u16() == other.to_u16()
518    }
519}
520
521impl Eq for StatusCode {}
522
523impl Clone for StatusCode {
524    #[inline]
525    fn clone(&self) -> StatusCode {
526        *self
527    }
528}
529
530impl PartialOrd for StatusCode {
531    #[inline]
532    fn partial_cmp(&self, other: &StatusCode) -> Option<Ordering> {
533        self.to_u16().partial_cmp(&(other.to_u16()))
534    }
535}
536
537impl Ord for StatusCode {
538    #[inline]
539    fn cmp(&self, other: &StatusCode) -> Ordering {
540        if *self < *other {
541            Ordering::Less
542        } else if *self > *other {
543            Ordering::Greater
544        } else {
545            Ordering::Equal
546        }
547    }
548}
549
550/// The class of an HTTP `status-code`.
551///
552/// [RFC 7231, section 6 (Response Status Codes)](https://tools.ietf.org/html/rfc7231#section-6):
553///
554/// > The first digit of the status-code defines the class of response.
555/// > The last two digits do not have any categorization role.
556///
557/// And:
558///
559/// > HTTP status codes are extensible.  HTTP clients are not required to
560/// > understand the meaning of all registered status codes, though such
561/// > understanding is obviously desirable.  However, a client MUST
562/// > understand the class of any status code, as indicated by the first
563/// > digit, and treat an unrecognized status code as being equivalent to
564/// > the x00 status code of that class, with the exception that a
565/// > recipient MUST NOT cache a response with an unrecognized status code.
566/// >
567/// > For example, if an unrecognized status code of 471 is received by a
568/// > client, the client can assume that there was something wrong with its
569/// > request and treat the response as if it had received a 400 (Bad
570/// > Request) status code.  The response message will usually contain a
571/// > representation that explains the status.
572///
573/// This can be used in cases where a status code’s meaning is unknown, also,
574/// to get the appropriate *category* of status.
575#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Copy)]
576pub enum StatusClass {
577    /// 1xx (Informational): The request was received, continuing process
578    Informational,
579
580    /// 2xx (Success): The request was successfully received, understood, and accepted
581    Success,
582
583    /// 3xx (Redirection): Further action needs to be taken in order to complete the request
584    Redirection,
585
586    /// 4xx (Client Error): The request contains bad syntax or cannot be fulfilled
587    ClientError,
588
589    /// 5xx (Server Error): The server failed to fulfill an apparently valid request
590    ServerError,
591
592    /// A status code lower than 100 or higher than 599. These codes do no belong to any class.
593    NoClass,
594}
595
596impl StatusClass {
597    /// Get the default status code for the class.
598    ///
599    /// This produces the x00 status code; thus, for `ClientError` (4xx), for
600    /// example, this will produce `BadRequest` (400):
601    ///
602    /// ```rust
603    /// # use hyper_sync::status::StatusClass::ClientError;
604    /// # use hyper_sync::status::StatusCode::BadRequest;
605    /// assert_eq!(ClientError.default_code(), BadRequest);
606    /// ```
607    ///
608    /// The use for this is outlined in [RFC 7231, section 6 (Response Status
609    /// Codes)](https://tools.ietf.org/html/rfc7231#section-6):
610    ///
611    /// > HTTP status codes are extensible.  HTTP clients are not required to
612    /// > understand the meaning of all registered status codes, though such
613    /// > understanding is obviously desirable.  However, a client MUST
614    /// > understand the class of any status code, as indicated by the first
615    /// > digit, and treat an unrecognized status code as being equivalent to
616    /// > the x00 status code of that class, with the exception that a
617    /// > recipient MUST NOT cache a response with an unrecognized status code.
618    /// >
619    /// > For example, if an unrecognized status code of 471 is received by a
620    /// > client, the client can assume that there was something wrong with its
621    /// > request and treat the response as if it had received a 400 (Bad
622    /// > Request) status code.  The response message will usually contain a
623    /// > representation that explains the status.
624    ///
625    /// This is demonstrated thusly:
626    ///
627    /// ```rust
628    /// # use hyper_sync::status::StatusCode::{Unregistered, BadRequest};
629    /// // Suppose we have received this status code.
630    /// // You will never directly create an unregistered status code.
631    /// let status = Unregistered(471);
632    ///
633    /// // Uh oh! Don’t know what to do with it.
634    /// // Let’s fall back to the default:
635    /// let status = status.class().default_code();
636    ///
637    /// // And look! That is 400 Bad Request.
638    /// assert_eq!(status, BadRequest);
639    /// // So now let’s treat it as that.
640    /// ```
641    /// All status codes that do not map to an existing status class are matched
642    /// by a `NoClass`, variant that resolves to 200 (Ok) as default code.
643    /// This is a common handling for unknown status codes in major browsers.
644    pub fn default_code(&self) -> StatusCode {
645        match *self {
646            StatusClass::Informational => StatusCode::Continue,
647            StatusClass::Success => StatusCode::Ok,
648            StatusClass::Redirection => StatusCode::MultipleChoices,
649            StatusClass::ClientError => StatusCode::BadRequest,
650            StatusClass::ServerError => StatusCode::InternalServerError,
651            StatusClass::NoClass => StatusCode::Ok,
652        }
653    }
654}
655
656#[cfg(test)]
657mod tests {
658    use super::*;
659    use super::StatusCode::*;
660
661    // Check that the following entities are properly inter-connected:
662    //   - numerical code
663    //   - status code
664    //   - default code (for the given status code)
665    //   - canonical reason
666    fn validate(num: u16, status_code: StatusCode, default_code: StatusCode, reason: Option<&str>) {
667        assert_eq!(StatusCode::from_u16(num), status_code);
668        assert_eq!(status_code.to_u16(), num);
669        assert_eq!(status_code.class().default_code(), default_code);
670        assert_eq!(status_code.canonical_reason(), reason);
671    }
672
673    #[test]
674    fn test_status_code() {
675        validate(99, Unregistered(99), Ok, None);
676
677        validate(100, Continue, Continue, Some("Continue"));
678        validate(101, SwitchingProtocols, Continue, Some("Switching Protocols"));
679        validate(102, Processing, Continue, Some("Processing"));
680
681        validate(200, Ok, Ok, Some("OK"));
682        validate(201, Created, Ok, Some("Created"));
683        validate(202, Accepted, Ok, Some("Accepted"));
684        validate(203, NonAuthoritativeInformation, Ok, Some("Non-Authoritative Information"));
685        validate(204, NoContent, Ok, Some("No Content"));
686        validate(205, ResetContent, Ok, Some("Reset Content"));
687        validate(206, PartialContent, Ok, Some("Partial Content"));
688        validate(207, MultiStatus, Ok, Some("Multi-Status"));
689        validate(208, AlreadyReported, Ok, Some("Already Reported"));
690        validate(226, ImUsed, Ok, Some("IM Used"));
691
692        validate(300, MultipleChoices, MultipleChoices, Some("Multiple Choices"));
693        validate(301, MovedPermanently, MultipleChoices, Some("Moved Permanently"));
694        validate(302, Found, MultipleChoices, Some("Found"));
695        validate(303, SeeOther, MultipleChoices, Some("See Other"));
696        validate(304, NotModified, MultipleChoices, Some("Not Modified"));
697        validate(305, UseProxy, MultipleChoices, Some("Use Proxy"));
698        validate(307, TemporaryRedirect, MultipleChoices, Some("Temporary Redirect"));
699        validate(308, PermanentRedirect, MultipleChoices, Some("Permanent Redirect"));
700
701        validate(400, BadRequest, BadRequest, Some("Bad Request"));
702        validate(401, Unauthorized, BadRequest, Some("Unauthorized"));
703        validate(402, PaymentRequired, BadRequest, Some("Payment Required"));
704        validate(403, Forbidden, BadRequest, Some("Forbidden"));
705        validate(404, NotFound, BadRequest, Some("Not Found"));
706        validate(405, MethodNotAllowed, BadRequest, Some("Method Not Allowed"));
707        validate(406, NotAcceptable, BadRequest, Some("Not Acceptable"));
708        validate(407, ProxyAuthenticationRequired, BadRequest,
709            Some("Proxy Authentication Required"));
710        validate(408, RequestTimeout, BadRequest, Some("Request Timeout"));
711        validate(409, Conflict, BadRequest, Some("Conflict"));
712        validate(410, Gone, BadRequest, Some("Gone"));
713        validate(411, LengthRequired, BadRequest, Some("Length Required"));
714        validate(412, PreconditionFailed, BadRequest, Some("Precondition Failed"));
715        validate(413, PayloadTooLarge, BadRequest, Some("Payload Too Large"));
716        validate(414, UriTooLong, BadRequest, Some("URI Too Long"));
717        validate(415, UnsupportedMediaType, BadRequest, Some("Unsupported Media Type"));
718        validate(416, RangeNotSatisfiable, BadRequest, Some("Range Not Satisfiable"));
719        validate(417, ExpectationFailed, BadRequest, Some("Expectation Failed"));
720        validate(418, ImATeapot, BadRequest, Some("I'm a teapot"));
721        validate(421, MisdirectedRequest, BadRequest, Some("Misdirected Request"));
722        validate(422, UnprocessableEntity, BadRequest, Some("Unprocessable Entity"));
723        validate(423, Locked, BadRequest, Some("Locked"));
724        validate(424, FailedDependency, BadRequest, Some("Failed Dependency"));
725        validate(426, UpgradeRequired, BadRequest, Some("Upgrade Required"));
726        validate(428, PreconditionRequired, BadRequest, Some("Precondition Required"));
727        validate(429, TooManyRequests, BadRequest, Some("Too Many Requests"));
728        validate(431, RequestHeaderFieldsTooLarge, BadRequest,
729            Some("Request Header Fields Too Large"));
730        validate(451, UnavailableForLegalReasons, BadRequest,
731            Some("Unavailable For Legal Reasons"));
732
733        validate(500, InternalServerError, InternalServerError, Some("Internal Server Error"));
734        validate(501, NotImplemented, InternalServerError, Some("Not Implemented"));
735        validate(502, BadGateway, InternalServerError, Some("Bad Gateway"));
736        validate(503, ServiceUnavailable, InternalServerError, Some("Service Unavailable"));
737        validate(504, GatewayTimeout, InternalServerError, Some("Gateway Timeout"));
738        validate(505, HttpVersionNotSupported, InternalServerError,
739            Some("HTTP Version Not Supported"));
740        validate(506, VariantAlsoNegotiates, InternalServerError, Some("Variant Also Negotiates"));
741        validate(507, InsufficientStorage, InternalServerError, Some("Insufficient Storage"));
742        validate(508, LoopDetected, InternalServerError, Some("Loop Detected"));
743        validate(510, NotExtended, InternalServerError, Some("Not Extended"));
744        validate(511, NetworkAuthenticationRequired, InternalServerError,
745            Some("Network Authentication Required"));
746
747    }
748}