shs/
status_code.rs

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