Skip to content

Commit

Permalink
Add a Status::Other(n) status code to represent arbitrary values
Browse files Browse the repository at this point in the history
This replaces the `Status::Unknown` variant with a `Status::Other(n)`
variant that can be used to represent arbitrary status codes.  RFC 9110
defines a number of status codes that aren't currently specified as enum
variants, and servers may return other arbitrary codes.  This change
allows `Status` to represent any arbitrary code, rather than collapsing
unknown codes to a single value.

This does break backwards compatibility, as the `Status` variant can no
longer be trivial converted to a u16 (and it now requires more space to
store).  This also results in having more than one way to represent some
status codes.  e.g., `Status::Ok` and `Status::Other(200)` are both
treated as equal now.
  • Loading branch information
simpkins committed May 13, 2024
1 parent 516df33 commit eec90d1
Showing 1 changed file with 58 additions and 16 deletions.
74 changes: 58 additions & 16 deletions src/response/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ where
conn: &'resp mut C,
/// The method used to create the response.
method: Method,
/// The HTTP response status code, as an integer
pub code: u16,
/// The HTTP response status code.
pub status: Status,
/// The HTTP response content type.
Expand Down Expand Up @@ -83,8 +81,7 @@ where
let mut response = httparse::Response::new(&mut headers);
response.parse(&header_buf[..header_len]).unwrap();

let code = response.code.unwrap();
let status = code.into();
let status = response.code.unwrap().into();
let mut content_type = None;
let mut content_length = None;
let mut transfer_encoding = Vec::new();
Expand Down Expand Up @@ -122,7 +119,6 @@ where
Ok(Response {
conn,
method,
code,
status,
content_type,
content_length,
Expand Down Expand Up @@ -388,8 +384,9 @@ where
}

/// HTTP status types
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
#[derive(Clone, Copy, Debug, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u16)]
pub enum Status {
Ok = 200,
Created = 201,
Expand All @@ -415,34 +412,64 @@ pub enum Status {
BadGateway = 502,
ServiceUnavailable = 503,
GatewayTimeout = 504,
Unknown = 0,
Other(u16),
}

impl Status {
pub fn is_informational(&self) -> bool {
let status = *self as u16;
let status = self.as_u16();
(100..=199).contains(&status)
}

pub fn is_successful(&self) -> bool {
let status = *self as u16;
let status = self.as_u16();
(200..=299).contains(&status)
}

pub fn is_redirection(&self) -> bool {
let status = *self as u16;
let status = self.as_u16();
(300..=399).contains(&status)
}

pub fn is_client_error(&self) -> bool {
let status = *self as u16;
let status = self.as_u16();
(400..=499).contains(&status)
}

pub fn is_server_error(&self) -> bool {
let status = *self as u16;
let status = self.as_u16();
(500..=599).contains(&status)
}

fn as_u16(&self) -> u16 {
match self {
Status::Ok => 200,
Status::Created => 201,
Status::Accepted => 202,
Status::NoContent => 204,
Status::PartialContent => 206,
Status::MovedPermanently => 301,
Status::Found => 302,
Status::SeeOther => 303,
Status::NotModified => 304,
Status::TemporaryRedirect => 307,
Status::PermanentRedirect => 308,
Status::BadRequest => 400,
Status::Unauthorized => 401,
Status::Forbidden => 403,
Status::NotFound => 404,
Status::MethodNotAllowed => 405,
Status::Conflict => 409,
Status::UnsupportedMediaType => 415,
Status::RangeNotSatisfiable => 416,
Status::TooManyRequests => 429,
Status::InternalServerError => 500,
Status::BadGateway => 502,
Status::ServiceUnavailable => 503,
Status::GatewayTimeout => 504,
Status::Other(n) => *n,
}
}
}

impl From<u16> for Status {
Expand Down Expand Up @@ -472,14 +499,29 @@ impl From<u16> for Status {
502 => Status::BadGateway,
503 => Status::ServiceUnavailable,
504 => Status::GatewayTimeout,
n => {
warn!("Unknown status code: {:?}", n);
Status::Unknown
}
n => Status::Other(n),
}
}
}

impl Into<u16> for Status {
fn into(self) -> u16 {
self.as_u16()
}
}

impl PartialEq<Self> for Status {
fn eq(&self, rhs: &Self) -> bool {
self.as_u16() == rhs.as_u16()
}
}

impl PartialOrd<Self> for Status {
fn partial_cmp(&self, rhs: &Self) -> Option<core::cmp::Ordering> {
self.as_u16().partial_cmp(&rhs.as_u16())
}
}

#[cfg(test)]
mod tests {
use core::convert::Infallible;
Expand Down

0 comments on commit eec90d1

Please sign in to comment.