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}