async_httplib/
status.rs

1use std::fmt::{self, Display};
2use std::cmp::Ordering;
3use std::convert::TryFrom;
4use std::io::{Error, ErrorKind};
5use std::str::FromStr;
6
7/// HTTP response status codes.
8///
9/// As defined by [rfc7231 section 6](https://tools.ietf.org/html/rfc7231#section-6).
10/// [Read more](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status)
11#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
12pub enum Status {
13
14    /// 100 Continue
15    ///
16    /// This interim response indicates that everything so far is OK and that the client should
17    /// continue the request, or ignore the response if the request is already finished.
18    Continue = 100,
19
20    /// 101 Switching Protocols
21    ///
22    /// This code is sent in response to an Upgrade request header from the client, and
23    /// indicates the protocol the server is switching to.
24    SwitchingProtocols = 101,
25
26    /// 103 Early Hints
27    ///
28    /// This status code is primarily intended to be used with the Link header, letting the
29    /// user agent start preloading resources while the server prepares a response.
30    EarlyHints = 103,
31
32    /// 200 Ok
33    ///
34    /// The request has succeeded
35    Ok = 200,
36
37    /// 201 Created
38    ///
39    /// The request has succeeded and a new resource has been created as a result. This is
40    /// typically the response sent after POST requests, or some PUT requests.
41    Created = 201,
42
43    /// 202 Accepted
44    ///
45    /// The request has been received but not yet acted upon. It is noncommittal, since there
46    /// is no way in HTTP to later send an asynchronous response indicating the outcome of the
47    /// request. It is intended for cases where another process or server handles the request,
48    /// or for batch processing.
49    Accepted = 202,
50
51    /// 203 Non Authoritative Information
52    ///
53    /// This response code means the returned meta-information is not exactly the same as is
54    /// available from the origin server, but is collected from a local or a third-party copy.
55    /// This is mostly used for mirrors or backups of another resource. Except for that
56    /// specific case, the "200 OK" response is preferred to this status.
57    NonAuthoritativeInformation = 203,
58
59    /// 204 No Content
60    ///
61    /// There is no content to send for this request, but the headers may be useful. The
62    /// user-agent may update its cached headers for this resource with the new ones.
63    NoContent = 204,
64
65    /// 205 Reset Content
66    ///
67    /// Tells the user-agent to reset the document which sent this request.
68    ResetContent = 205,
69
70    /// 206 Partial Content
71    ///
72    /// This response code is used when the Range header is sent from the client to request
73    /// only part of a resource.
74    PartialContent = 206,
75
76    /// 226 Im Used
77    ///
78    /// The server has fulfilled a GET request for the resource, and the response is a
79    /// representation of the result of one or more instance-manipulations applied to the
80    /// current instance.
81    ImUsed = 226,
82
83    /// 300 Multiple Choice
84    ///
85    /// The request has more than one possible response. The user-agent or user should choose one
86    /// of them. (There is no standardized way of choosing one of the responses, but HTML links to
87    /// the possibilities are recommended so the user can pick.)
88    MultipleChoice = 300,
89
90    /// 301 Moved Permanently
91    ///
92    /// The URL of the requested resource has been changed permanently. The new URL is given in the
93    /// response.
94    MovedPermanently = 301,
95
96    /// 302 Found
97    ///
98    /// This response code means that the URI of requested resource has been changed temporarily.
99    /// Further changes in the URI might be made in the future. Therefore, this same URI should be
100    /// used by the client in future requests.
101    Found = 302,
102
103    /// 303 See Other
104    ///
105    /// The server sent this response to direct the client to get the requested resource at another
106    /// URI with a GET request.
107    SeeOther = 303,
108
109    /// 304 Not Modified
110    ///
111    /// This is used for caching purposes. It tells the client that the response has not been
112    /// modified, so the client can continue to use the same cached version of the response.
113    NotModified = 304,
114
115    /// 307 Temporary Redirect
116    ///
117    /// The server sends this response to direct the client to get the requested resource at
118    /// another URI with same method that was used in the prior request. This has the same
119    /// semantics as the 302 Found HTTP response code, with the exception that the user agent must
120    /// not change the HTTP method used: If a POST was used in the first request, a POST must be
121    /// used in the second request.
122    TemporaryRedirect = 307,
123
124    /// 308 Permanent Redirect
125    ///
126    /// This means that the resource is now permanently located at another URI, specified by the
127    /// Location: HTTP Response header. This has the same semantics as the 301 Moved Permanently
128    /// HTTP response code, with the exception that the user agent must not change the HTTP method
129    /// used: If a POST was used in the first request, a POST must be used in the second request.
130    PermanentRedirect = 308,
131
132    /// 400 Bad Request
133    ///
134    /// The server could not understand the request due to invalid syntax.
135    ///
136    /// Although the HTTP standard specifies "unauthorized", semantically this response means
137    /// "unauthenticated". That is, the client must authenticate itself to get the requested
138    /// response.
139    BadRequest = 400,
140
141    /// 401 Unauthorized
142    ///
143    /// This response code is reserved for future use. The initial aim for creating this code was
144    /// using it for digital payment systems, however this status code is used very rarely and no
145    /// standard convention exists.
146    Unauthorized = 401,
147
148    /// 402 Payment Required
149    ///
150    /// The client does not have access rights to the content; that is, it is unauthorized, so the
151    /// server is refusing to give the requested resource. Unlike 401, the client's identity is
152    /// known to the server.
153    PaymentRequired = 402,
154
155    /// 403 Forbidden
156    ///
157    /// The server can not find requested resource. In the browser, this means the URL is not
158    /// recognized. In an API, this can also mean that the endpoint is valid but the resource
159    /// itself does not exist. Servers may also send this response instead of 403 to hide the
160    /// existence of a resource from an unauthorized client. This response code is probably the
161    /// most famous one due to its frequent occurrence on the web.
162    Forbidden = 403,
163
164    /// 404 Not Found
165    /// The server can not find requested resource. In the browser, this means the URL is not
166    /// recognized. In an API, this can also mean that the endpoint is valid but the resource
167    /// itself does not exist. Servers may also send this response instead of 403 to hide the
168    /// existence of a resource from an unauthorized client. This response code is probably the
169    /// most famous one due to its frequent occurrence on the web.
170    NotFound = 404,
171
172    /// 405 Method Not Allowed
173    ///
174    /// The request method is known by the server but has been disabled and cannot be used. For
175    /// example, an API may forbid DELETE-ing a resource. The two mandatory methods, GET and HEAD,
176    /// must never be disabled and should not return this error code.
177    MethodNotAllowed = 405,
178
179    /// 406 Not Acceptable
180    ///
181    /// This response is sent when the web server, after performing server-driven content
182    /// negotiation, doesn't find any content that conforms to the criteria given by the user
183    /// agent.
184    NotAcceptable = 406,
185
186    /// 407 Proxy Authentication Required
187    ///
188    /// This is similar to 401 but authentication is needed to be done by a proxy.
189    ProxyAuthenticationRequired = 407,
190
191    /// 408 Request Timeout
192    ///
193    /// This response is sent on an idle connection by some servers, even without any previous
194    /// request by the client. It means that the server would like to shut down this unused
195    /// connection. This response is used much more since some browsers, like Chrome, Firefox 27+,
196    /// or IE9, use HTTP pre-connection mechanisms to speed up surfing. Also note that some servers
197    /// merely shut down the connection without sending this message.
198    RequestTimeout = 408,
199
200    /// 409 Conflict
201    ///
202    /// This response is sent when a request conflicts with the current state of the server.
203    Conflict = 409,
204
205    /// 410 Gone
206    ///
207    /// This response is sent when the requested content has been permanently deleted from server,
208    /// with no forwarding address. Clients are expected to remove their caches and links to the
209    /// resource. The HTTP specification intends this status code to be used for "limited-time,
210    /// promotional services". APIs should not feel compelled to indicate resources that have been
211    /// deleted with this status code.
212    Gone = 410,
213
214    /// 411 Length Required
215    ///
216    /// Server rejected the request because the Content-Length header field is not defined and the
217    /// server requires it.
218    LengthRequired = 411,
219
220    /// 412 Precondition Failed
221    ///
222    /// The client has indicated preconditions in its headers which the server does not meet.
223    PreconditionFailed = 412,
224
225    /// 413 Payload Too Large
226    ///
227    /// Request entity is larger than limits defined by server; the server might close the
228    /// connection or return an Retry-After header field.
229    PayloadTooLarge = 413,
230
231    /// 414 URI Too Long
232    ///
233    /// The URI requested by the client is longer than the server is willing to interpret.
234    UriTooLong = 414,
235
236    /// 415 Unsupported Media Type
237    ///
238    /// The media format of the requested data is not supported by the server, so the server is
239    /// rejecting the request.
240    UnsupportedMediaType = 415,
241
242    /// 416 Requested Range Not Satisfiable
243    ///
244    /// The range specified by the Range header field in the request can't be fulfilled; it's
245    /// possible that the range is outside the size of the target URI's data.
246    RequestedRangeNotSatisfiable = 416,
247
248    /// 417 Expectation Failed
249    ///
250    /// This response code means the expectation indicated by the Expect request header field can't
251    /// be met by the server.
252    ///
253    ExpectationFailed = 417,
254    ///
255    /// 418 I'm a teapot
256    ///
257    /// The server refuses the attempt to brew coffee with a teapot.
258    ImATeapot = 418,
259
260    /// 421 Misdirected Request
261    ///
262    /// The request was directed at a server that is not able to produce a response. This can be
263    /// sent by a server that is not configured to produce responses for the combination of scheme
264    /// and authority that are included in the request URI.
265    MisdirectedRequest = 421,
266
267    /// 425 Too Early
268    ///
269    /// Indicates that the server is unwilling to risk processing a request that might be replayed.
270    TooEarly = 425,
271
272    /// 426 Upgrade Required
273    ///
274    /// The server refuses to perform the request using the current protocol but might be willing
275    /// to do so after the client upgrades to a different protocol. The server sends an Upgrade
276    /// header in a 426 response to indicate the required protocol(s).
277    UpgradeRequired = 426,
278
279    /// 428 Precondition Required
280    ///
281    /// The origin server requires the request to be conditional. This response is intended to
282    /// prevent the 'lost update' problem, where a client GETs a resource's state, modifies it, and
283    /// PUTs it back to the server, when meanwhile a third party has modified the state on the
284    /// server, leading to a conflict.
285    PreconditionRequired = 428,
286
287    /// 429 Too Many Requests
288    ///
289    /// The user has sent too many requests in a given amount of time ("rate limiting").
290    TooManyRequests = 429,
291
292    /// 431 Request Header Fields Too Large
293    ///
294    /// The server is unwilling to process the request because its header fields are too large. The
295    /// request may be resubmitted after reducing the size of the request header fields.
296    RequestHeaderFieldsTooLarge = 431,
297
298    /// 451 Unavailable For Legal Reasons
299    ///
300    /// The user-agent requested a resource that cannot legally be provided, such as a web page
301    /// censored by a government.
302    UnavailableForLegalReasons = 451,
303
304    /// 500 Internal Server Error
305    ///
306    /// The server has encountered a situation it doesn't know how to handle.
307    InternalServerError = 500,
308
309    /// 501 Not Implemented
310    ///
311    /// The request method is not supported by the server and cannot be handled. The only methods
312    /// that servers are required to support (and therefore that must not return this code) are GET
313    /// and HEAD.
314    NotImplemented = 501,
315
316    /// 502 Bad Gateway
317    ///
318    /// This error response means that the server, while working as a gateway to get a response
319    /// needed to handle the request, got an invalid response.
320    BadGateway = 502,
321
322    /// 503 Service Unavailable
323    ///
324    /// The server is not ready to handle the request. Common causes are a server that is down for
325    /// maintenance or that is overloaded. Note that together with this response, a user-friendly
326    /// page explaining the problem should be sent. This responses should be used for temporary
327    /// conditions and the Retry-After: HTTP header should, if possible, contain the estimated time
328    /// before the recovery of the service. The webmaster must also take care about the
329    /// caching-related headers that are sent along with this response, as these temporary
330    /// condition responses should usually not be cached.
331    ServiceUnavailable = 503,
332
333    /// 504 Gateway Timeout
334    ///
335    /// This error response is given when the server is acting as a gateway and cannot get a
336    /// response in time.
337    GatewayTimeout = 504,
338
339    /// 505 HTTP Version Not Supported
340    ///
341    /// The HTTP version used in the request is not supported by the server.
342    HttpVersionNotSupported = 505,
343
344    /// 506 Variant Also Negotiates
345    ///
346    /// The server has an internal configuration error: the chosen variant resource is configured
347    /// to engage in transparent content negotiation itself, and is therefore not a proper end
348    /// point in the negotiation process.
349    VariantAlsoNegotiates = 506,
350
351    /// 510 Not Extended
352    ///
353    /// Further extensions to the request are required for the server to fulfil it.
354    NotExtended = 510,
355
356    /// 511 Network Authentication Required
357    ///
358    /// The 511 status code indicates that the client needs to authenticate to gain network access.
359    NetworkAuthenticationRequired = 511,
360}
361
362impl Status {
363
364    /// Returns `true` if the status code is `1xx` range.
365    ///
366    /// If this returns `true` it indicates that the request was received, continuing process.
367    pub fn is_informational(&self) -> bool {
368        let num: u16 = self.clone().into();
369        num >= 100 && num < 200
370    }
371
372    /// Returns `true` if the status code is the `2xx` range.
373    ///
374    /// If this returns `true` it indicates that the request was successfully received, understood,
375    /// and accepted.
376    pub fn is_success(&self) -> bool {
377        let num: u16 = self.clone().into();
378        num >= 200 && num < 300
379    }
380
381    /// Returns `true` if the status code is the `3xx` range.
382    ///
383    /// If this returns `true` it indicates that further action needs to be taken in order to
384    /// complete the request.
385    pub fn is_redirection(&self) -> bool {
386        let num: u16 = self.clone().into();
387        num >= 300 && num < 400
388    }
389
390    /// Returns `true` if the status code is the `4xx` range.
391    ///
392    /// If this returns `true` it indicates that the request contains bad syntax or cannot be
393    /// fulfilled.
394    pub fn is_client_error(&self) -> bool {
395        let num: u16 = self.clone().into();
396        num >= 400 && num < 500
397    }
398
399    /// Returns `true` if the status code is the `5xx` range.
400    ///
401    /// If this returns `true` it indicates that the server failed to fulfill an apparently valid
402    /// request.
403    pub fn is_server_error(&self) -> bool {
404        let num: u16 = self.clone().into();
405        num >= 500 && num < 600
406    }
407
408    /// Status code
409    pub fn code(&self) -> u16 {
410        *self as u16
411    }
412
413    /// The canonical reason for a given status code
414    pub fn reason(&self) -> &'static str {
415        match self {
416            Status::Continue => "Continue",
417            Status::SwitchingProtocols => "Switching Protocols",
418            Status::EarlyHints => "Early Hints",
419            Status::Ok => "OK",
420            Status::Created => "Created",
421            Status::Accepted => "Accepted",
422            Status::NonAuthoritativeInformation => "Non Authoritative Information",
423            Status::NoContent => "No Content",
424            Status::ResetContent => "Reset Content",
425            Status::PartialContent => "Partial Content",
426            Status::ImUsed => "Im Used",
427            Status::MultipleChoice => "Multiple Choice",
428            Status::MovedPermanently => "Moved Permanently",
429            Status::Found => "Found",
430            Status::SeeOther => "See Other",
431            Status::NotModified => "Modified",
432            Status::TemporaryRedirect => "Temporary Redirect",
433            Status::PermanentRedirect => "Permanent Redirect",
434            Status::BadRequest => "Bad Request",
435            Status::Unauthorized => "Unauthorized",
436            Status::PaymentRequired => "Payment Required",
437            Status::Forbidden => "Forbidden",
438            Status::NotFound => "Not Found",
439            Status::MethodNotAllowed => "Method Not Allowed",
440            Status::NotAcceptable => "Not Acceptable",
441            Status::ProxyAuthenticationRequired => "Proxy Authentication Required",
442            Status::RequestTimeout => "Request Timeout",
443            Status::Conflict => "Conflict",
444            Status::Gone => "Gone",
445            Status::LengthRequired => "Length Required",
446            Status::PreconditionFailed => "Precondition Failed",
447            Status::PayloadTooLarge => "Payload Too Large",
448            Status::UriTooLong => "URI Too Long",
449            Status::UnsupportedMediaType => "Unsupported Media Type",
450            Status::RequestedRangeNotSatisfiable => "Requested Range Not Satisfiable",
451            Status::ExpectationFailed => "Expectation Failed",
452            Status::ImATeapot => "I'm a teapot",
453            Status::MisdirectedRequest => "Misdirected Request",
454            Status::TooEarly => "Too Early",
455            Status::UpgradeRequired => "Upgrade Required",
456            Status::PreconditionRequired => "Precondition Required",
457            Status::TooManyRequests => "Too Many Requests",
458            Status::RequestHeaderFieldsTooLarge => "Request Header Fields Too Large",
459            Status::UnavailableForLegalReasons => "Unavailable For Legal Reasons",
460            Status::InternalServerError => "Internal Server Error",
461            Status::NotImplemented => "Not Implemented",
462            Status::BadGateway => "Bad Gateway",
463            Status::ServiceUnavailable => "Service Unavailable",
464            Status::GatewayTimeout => "Gateway Timeout",
465            Status::HttpVersionNotSupported => "HTTP Version Not Supported",
466            Status::VariantAlsoNegotiates => "Variant Also Negotiates",
467            Status::NotExtended => "Not Extended",
468            Status::NetworkAuthenticationRequired => "Network Authentication Required",
469        }
470    }
471}
472
473impl From<Status> for u16 {
474    fn from(code: Status) -> u16 {
475        code as u16
476    }
477}
478
479impl std::convert::TryFrom<u16> for Status {
480    type Error = Error;
481
482    fn try_from(num: u16) -> Result<Self, Self::Error> {
483        match num {
484            100 => Ok(Status::Continue),
485            101 => Ok(Status::SwitchingProtocols),
486            103 => Ok(Status::EarlyHints),
487            200 => Ok(Status::Ok),
488            201 => Ok(Status::Created),
489            202 => Ok(Status::Accepted),
490            203 => Ok(Status::NonAuthoritativeInformation),
491            204 => Ok(Status::NoContent),
492            205 => Ok(Status::ResetContent),
493            206 => Ok(Status::PartialContent),
494            226 => Ok(Status::ImUsed),
495            300 => Ok(Status::MultipleChoice),
496            301 => Ok(Status::MovedPermanently),
497            302 => Ok(Status::Found),
498            303 => Ok(Status::SeeOther),
499            304 => Ok(Status::NotModified),
500            307 => Ok(Status::TemporaryRedirect),
501            308 => Ok(Status::PermanentRedirect),
502            400 => Ok(Status::BadRequest),
503            401 => Ok(Status::Unauthorized),
504            402 => Ok(Status::PaymentRequired),
505            403 => Ok(Status::Forbidden),
506            404 => Ok(Status::NotFound),
507            405 => Ok(Status::MethodNotAllowed),
508            406 => Ok(Status::NotAcceptable),
509            407 => Ok(Status::ProxyAuthenticationRequired),
510            408 => Ok(Status::RequestTimeout),
511            409 => Ok(Status::Conflict),
512            410 => Ok(Status::Gone),
513            411 => Ok(Status::LengthRequired),
514            412 => Ok(Status::PreconditionFailed),
515            413 => Ok(Status::PayloadTooLarge),
516            414 => Ok(Status::UriTooLong),
517            415 => Ok(Status::UnsupportedMediaType),
518            416 => Ok(Status::RequestedRangeNotSatisfiable),
519            417 => Ok(Status::ExpectationFailed),
520            418 => Ok(Status::ImATeapot),
521            421 => Ok(Status::MisdirectedRequest),
522            425 => Ok(Status::TooEarly),
523            426 => Ok(Status::UpgradeRequired),
524            428 => Ok(Status::PreconditionRequired),
525            429 => Ok(Status::TooManyRequests),
526            431 => Ok(Status::RequestHeaderFieldsTooLarge),
527            451 => Ok(Status::UnavailableForLegalReasons),
528            500 => Ok(Status::InternalServerError),
529            501 => Ok(Status::NotImplemented),
530            502 => Ok(Status::BadGateway),
531            503 => Ok(Status::ServiceUnavailable),
532            504 => Ok(Status::GatewayTimeout),
533            505 => Ok(Status::HttpVersionNotSupported),
534            506 => Ok(Status::VariantAlsoNegotiates),
535            510 => Ok(Status::NotExtended),
536            511 => Ok(Status::NetworkAuthenticationRequired),
537            c => Err(Error::new(ErrorKind::InvalidInput, format!("The status code `{}` is invalid.", c))),
538        }
539    }
540}
541
542impl<'a> std::convert::TryFrom<&[u8]> for Status {
543    type Error = Error;
544
545    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
546        match String::from_utf8(bytes.to_vec()) {
547            Ok(txt) => Self::from_str(&txt),
548            Err(e) => Err(Error::new(ErrorKind::InvalidInput, e.to_string())),
549        }
550    }
551}
552
553impl FromStr for Status {
554    type Err = Error;
555
556    fn from_str(s: &str) -> Result<Self, Self::Err> {
557        match s.parse::<u16>() {
558            Ok(num) => Status::try_from(num),
559            Err(e) => Err(Error::new(ErrorKind::InvalidInput, e.to_string())),
560        }
561    }
562}
563
564impl PartialOrd for Status {
565    fn partial_cmp(&self, other: &Status) -> Option<Ordering> {
566        Some(self.cmp(other))
567    }
568}
569
570impl Ord for Status {
571    fn cmp(&self, other: &Status) -> Ordering {
572        (*self as usize).cmp(&(*other as usize))
573    }
574}
575
576impl PartialEq<Status> for u16 {
577    fn eq(&self, other: &Status) -> bool {
578        *self == *other as u16
579    }
580}
581
582impl PartialEq<u16> for Status {
583    fn eq(&self, other: &u16) -> bool {
584        *self as u16 == *other
585    }
586}
587
588impl Display for Status {
589    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
590        write!(f, "{}", *self as u16)
591    }
592}
593
594#[cfg(test)]
595mod tests {
596    use super::*;
597
598    #[test]
599    fn implements_data() {
600        assert_eq!(Status::Ok.code(), 200);
601        assert_eq!(Status::Ok.reason(), "OK");
602    }
603
604    #[test]
605    fn implements_try_from() {
606        assert_eq!(Status::try_from(200).unwrap(), Status::Ok);
607        assert_eq!(Status::try_from("200".as_bytes()).unwrap(), Status::Ok);
608    }
609
610    #[test]
611    fn implements_from_str() {
612        assert_eq!(Status::from_str("200").unwrap(), Status::Ok);
613    }
614
615    #[test]
616    fn implements_to_string() {
617        let status = Status::from_str("200").unwrap();
618        assert_eq!(status.to_string(), "200");
619    }
620
621    #[test]
622    fn implements_ordering() {
623        assert!(Status::Ok < Status::Created);
624        assert!(Status::Ok == Status::Ok);
625    }
626}