diff --git a/core/codegen/src/http_codegen.rs b/core/codegen/src/http_codegen.rs
index 675accd5..a479a527 100644
--- a/core/codegen/src/http_codegen.rs
+++ b/core/codegen/src/http_codegen.rs
@@ -26,14 +26,14 @@ impl FromMeta for Status {
return Err(meta.value_span().error("status must be in range [100, 599]"));
}
- Ok(Status(http::Status::raw(num as u16)))
+ Ok(Status(http::Status::new(num as u16)))
}
}
impl ToTokens for Status {
fn to_tokens(&self, tokens: &mut TokenStream) {
- let (code, reason) = (self.0.code, self.0.reason);
- tokens.extend(quote!(rocket::http::Status { code: #code, reason: #reason }));
+ let code = self.0.code;
+ tokens.extend(quote!(rocket::http::Status { code: #code }));
}
}
diff --git a/core/codegen/src/lib.rs b/core/codegen/src/lib.rs
index a59cb647..efcef005 100644
--- a/core/codegen/src/lib.rs
+++ b/core/codegen/src/lib.rs
@@ -303,7 +303,7 @@ route_attribute!(options => Method::Options);
///
/// #[catch(default)]
/// fn default(status: Status, req: &Request) -> String {
-/// format!("{} - {} ({})", status.code, status.reason, req.uri())
+/// format!("{} ({})", status, req.uri())
/// }
/// ```
///
diff --git a/core/codegen/tests/responder.rs b/core/codegen/tests/responder.rs
index e4aa84d2..98b0e241 100644
--- a/core/codegen/tests/responder.rs
+++ b/core/codegen/tests/responder.rs
@@ -53,7 +53,7 @@ async fn responder_foo() {
.respond_to(req)
.expect("response okay");
- assert_eq!(r.status(), Status::raw(105));
+ assert_eq!(r.status().code, 105);
assert_eq!(r.content_type(), Some(ContentType::JSON));
assert_eq!(r.body_mut().to_string().await.unwrap(), "goodbye");
}
diff --git a/core/http/src/status.rs b/core/http/src/status.rs
index 5da1e2ab..5104f04c 100644
--- a/core/http/src/status.rs
+++ b/core/http/src/status.rs
@@ -42,20 +42,43 @@ impl StatusClass {
class_check_fn!(is_unknown, "`Unknown`.", Unknown);
}
-/// Structure representing an HTTP status: an integer code and a reason phrase.
+/// Structure representing an HTTP status: an integer code.
///
-/// # Usage
+/// A `Status` should rarely be created directly. Instead, an associated
+/// constant should be used; one is declared for every status defined in the
+/// HTTP standard. If a custom status code _must_ be created, note that it is
+/// not possible to set a custom reason phrase.
///
-/// Status classes should rarely be created directly. Instead, an associated
-/// constant should be used; one is declared for every status defined
-/// in the HTTP standard.
+/// ```rust
+/// # extern crate rocket;
+/// use rocket::http::Status;
+///
+/// // Create a status from a known constant.
+/// let ok = Status::Ok;
+/// assert_eq!(ok.code, 200);
+/// assert_eq!(ok.reason(), Some("OK"));
+///
+/// let not_found = Status::NotFound;
+/// assert_eq!(not_found.code, 404);
+/// assert_eq!(not_found.reason(), Some("Not Found"));
+///
+/// // Or from a status code: `reason()` returns the phrase when known.
+/// let gone = Status::new(410);
+/// assert_eq!(gone.code, 410);
+/// assert_eq!(gone.reason(), Some("Gone"));
+///
+/// // `reason()` returns `None` when unknown.
+/// let custom = Status::new(599);
+/// assert_eq!(custom.code, 599);
+/// assert_eq!(custom.reason(), None);
+/// ```
///
/// # Responding
///
/// To set a custom `Status` on a response, use a [`response::status`]
-/// responder. Alternatively, respond with `(Status, T)` where `T: Responder`, but
-/// note that the response may require additional headers to be valid as
-/// enforced by the types in [`response::status`].
+/// responder, which enforces correct status-based responses. Alternatively,
+/// respond with `(Status, T)` where `T: Responder`, but beware that the
+/// response may be invalid if it requires additional headers.
///
/// ```rust
/// # extern crate rocket;
@@ -69,47 +92,10 @@ impl StatusClass {
/// ```
///
/// [`response::status`]: ../response/status/index.html
-///
-/// ## Example
-///
-/// A status of `200 OK` can be instantiated via the `Ok` constant:
-///
-/// ```rust
-/// # extern crate rocket;
-/// use rocket::http::Status;
-///
-/// # #[allow(unused_variables)]
-/// let ok = Status::Ok;
-/// ```
-///
-/// A status of `404 Not Found` can be instantiated via the `NotFound` constant:
-///
-/// ```rust
-/// # extern crate rocket;
-/// use rocket::http::Status;
-///
-/// # #[allow(unused_variables)]
-/// let not_found = Status::NotFound;
-/// ```
-///
-/// The code and phrase can be retrieved directly:
-///
-/// ```rust
-/// # extern crate rocket;
-/// use rocket::http::Status;
-///
-/// let not_found = Status::NotFound;
-///
-/// assert_eq!(not_found.code, 404);
-/// assert_eq!(not_found.reason, "Not Found");
-/// assert_eq!(not_found.to_string(), "404 Not Found".to_string());
-/// ```
#[derive(Debug, Clone, Copy)]
pub struct Status {
/// The HTTP status code associated with this status.
pub code: u16,
- /// The HTTP reason phrase associated with this status.
- pub reason: &'static str
}
impl Default for Status {
@@ -123,12 +109,66 @@ macro_rules! ctrs {
$(
#[doc="[`Status`] with code "]
#[doc=$code_str]
- #[doc=" and reason "]
- #[doc=$reason]
- #[doc="."]
+ #[doc="."]
#[allow(non_upper_case_globals)]
- pub const $name: Status = Status { code: $code, reason: $reason };
- )+
+ pub const $name: Status = Status { code: $code };
+ )+
+
+ /// Creates a new `Status` with `code`. This should be used _only_ to
+ /// construct non-standard HTTP statuses. Use an associated constant for
+ /// standard statuses.
+ ///
+ /// # Example
+ ///
+ /// Create a custom `299` status:
+ ///
+ /// ```rust
+ /// # extern crate rocket;
+ /// use rocket::http::Status;
+ ///
+ /// let custom = Status::new(299);
+ /// assert_eq!(custom.code, 299);
+ /// ```
+ pub const fn new(code: u16) -> Status {
+ Status { code }
+ }
+
+ /// Returns the class of a given status.
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # extern crate rocket;
+ /// use rocket::http::{Status, StatusClass};
+ ///
+ /// let processing = Status::Processing;
+ /// assert_eq!(processing.class(), StatusClass::Informational);
+ ///
+ /// let ok = Status::Ok;
+ /// assert_eq!(ok.class(), StatusClass::Success);
+ ///
+ /// let see_other = Status::SeeOther;
+ /// assert_eq!(see_other.class(), StatusClass::Redirection);
+ ///
+ /// let not_found = Status::NotFound;
+ /// assert_eq!(not_found.class(), StatusClass::ClientError);
+ ///
+ /// let internal_error = Status::InternalServerError;
+ /// assert_eq!(internal_error.class(), StatusClass::ServerError);
+ ///
+ /// let custom = Status::new(600);
+ /// assert_eq!(custom.class(), StatusClass::Unknown);
+ /// ```
+ pub const fn class(self) -> StatusClass {
+ match self.code / 100 {
+ 1 => StatusClass::Informational,
+ 2 => StatusClass::Success,
+ 3 => StatusClass::Redirection,
+ 4 => StatusClass::ClientError,
+ 5 => StatusClass::ServerError,
+ _ => StatusClass::Unknown
+ }
+ }
/// Returns a Status given a standard status code `code`. If `code` is
/// not a known status code, `None` is returned.
@@ -151,88 +191,78 @@ macro_rules! ctrs {
/// # extern crate rocket;
/// use rocket::http::Status;
///
- /// let not_found = Status::from_code(600);
- /// assert!(not_found.is_none());
+ /// let unknown = Status::from_code(600);
+ /// assert!(unknown.is_none());
/// ```
- pub fn from_code(code: u16) -> Option {
+ pub const fn from_code(code: u16) -> Option {
match code {
$($code => Some(Status::$name),)+
_ => None
}
}
+
+ /// Returns the canonical reason phrase if `self` corresponds to a
+ /// canonical, known status code. Otherwise, returns `None`.
+ ///
+ /// # Example
+ ///
+ /// Reason phrase from a known `code`:
+ ///
+ /// ```rust
+ /// # extern crate rocket;
+ /// use rocket::http::Status;
+ ///
+ /// assert_eq!(Status::Created.reason(), Some("Created"));
+ /// assert_eq!(Status::new(200).reason(), Some("OK"));
+ /// ```
+ ///
+ /// Absent phrase from an unknown `code`:
+ ///
+ /// ```rust
+ /// # extern crate rocket;
+ /// use rocket::http::Status;
+ ///
+ /// assert_eq!(Status::new(499).reason(), None);
+ /// ```
+ pub const fn reason(&self) -> Option<&'static str> {
+ match self.code {
+ $($code => Some($reason),)+
+ _ => None
+ }
+ }
+
+ /// Returns the canonical reason phrase if `self` corresponds to a
+ /// canonical, known status code, or an unspecified but relevant reason
+ /// phrase otherwise.
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # extern crate rocket;
+ /// use rocket::http::Status;
+ ///
+ /// assert_eq!(Status::NotFound.reason_lossy(), "Not Found");
+ /// assert_eq!(Status::new(100).reason_lossy(), "Continue");
+ /// assert!(!Status::new(699).reason_lossy().is_empty());
+ /// ```
+ pub const fn reason_lossy(&self) -> &'static str {
+ if let Some(lossless) = self.reason() {
+ return lossless;
+ }
+
+ match self.class() {
+ StatusClass::Informational => "Informational",
+ StatusClass::Success => "Success",
+ StatusClass::Redirection => "Redirection",
+ StatusClass::ClientError => "Client Error",
+ StatusClass::ServerError => "Server Error",
+ StatusClass::Unknown => "Unknown"
+ }
+ }
};
}
impl Status {
- /// Creates a new `Status` with `code` and `reason`. This should be used _only_
- /// to construct non-standard HTTP statuses. Use an associated constant for
- /// standard statuses.
- ///
- /// # Example
- ///
- /// Create a custom `299 Somewhat Successful` status:
- ///
- /// ```rust
- /// # extern crate rocket;
- /// use rocket::http::Status;
- ///
- /// let custom = Status::new(299, "Somewhat Successful");
- /// assert_eq!(custom.to_string(), "299 Somewhat Successful".to_string());
- /// ```
- #[inline(always)]
- pub const fn new(code: u16, reason: &'static str) -> Status {
- Status { code, reason }
- }
-
- /// Returns the class of a given status.
- ///
- /// # Example
- ///
- /// ```rust
- /// # extern crate rocket;
- /// use rocket::http::{Status, StatusClass};
- ///
- /// let processing = Status::Processing;
- /// assert_eq!(processing.class(), StatusClass::Informational);
- ///
- /// let ok = Status::Ok;
- /// assert_eq!(ok.class(), StatusClass::Success);
- ///
- /// let see_other = Status::SeeOther;
- /// assert_eq!(see_other.class(), StatusClass::Redirection);
- ///
- /// let not_found = Status::NotFound;
- /// assert_eq!(not_found.class(), StatusClass::ClientError);
- ///
- /// let internal_error = Status::InternalServerError;
- /// assert_eq!(internal_error.class(), StatusClass::ServerError);
- ///
- /// let custom = Status::new(600, "Bizarre");
- /// assert_eq!(custom.class(), StatusClass::Unknown);
- /// ```
- pub fn class(self) -> StatusClass {
- match self.code / 100 {
- 1 => StatusClass::Informational,
- 2 => StatusClass::Success,
- 3 => StatusClass::Redirection,
- 4 => StatusClass::ClientError,
- 5 => StatusClass::ServerError,
- _ => StatusClass::Unknown
- }
- }
-
- /// Returns a status from a given status code. If the status code is a
- /// standard code, then the reason phrase is populated accordingly.
- /// Otherwise the reason phrase is set to "".
- #[inline]
- #[doc(hidden)]
- pub fn raw(code: u16) -> Status {
- match Status::from_code(code) {
- Some(status) => status,
- None => Status::new(code, "")
- }
- }
-
ctrs! {
100, "100", Continue => "Continue",
101, "101", SwitchingProtocols => "Switching Protocols",
@@ -300,7 +330,7 @@ impl Status {
impl fmt::Display for Status {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{} {}", self.code, self.reason)
+ write!(f, "{} {}", self.code, self.reason_lossy())
}
}
diff --git a/core/lib/src/response/response.rs b/core/lib/src/response/response.rs
index e580973c..fdba8864 100644
--- a/core/lib/src/response/response.rs
+++ b/core/lib/src/response/response.rs
@@ -106,27 +106,6 @@ impl<'r> Builder<'r> {
self
}
- /// Sets the status of the `Response` being built to a custom status
- /// constructed from the `code` and `reason` phrase.
- ///
- /// # Example
- ///
- /// ```rust
- /// use rocket::Response;
- /// use rocket::http::Status;
- ///
- /// let response = Response::build()
- /// .raw_status(699, "Alien Encounter")
- /// .finalize();
- ///
- /// assert_eq!(response.status(), Status::new(699, "Alien Encounter"));
- /// ```
- #[inline(always)]
- pub fn raw_status(&mut self, code: u16, reason: &'static str) -> &mut Builder<'r> {
- self.response.set_raw_status(code, reason);
- self
- }
-
/// Adds `header` to the `Response`, replacing any header with the same name
/// that already exists in the response. If multiple headers with
/// the same name exist, they are all removed, and only the new header and
@@ -544,25 +523,6 @@ impl<'r> Response<'r> {
self.headers().get_one("Content-Type").and_then(|v| v.parse().ok())
}
- /// Sets the status of `self` to a custom `status` with status code `code`
- /// and reason phrase `reason`. This method should be used sparingly; prefer
- /// to use [set_status](#method.set_status) instead.
- ///
- /// # Example
- ///
- /// ```rust
- /// use rocket::Response;
- /// use rocket::http::Status;
- ///
- /// let mut response = Response::new();
- /// response.set_raw_status(699, "Tripped a Wire");
- /// assert_eq!(response.status(), Status::new(699, "Tripped a Wire"));
- /// ```
- #[inline(always)]
- pub fn set_raw_status(&mut self, code: u16, reason: &'static str) {
- self.status = Some(Status::new(code, reason));
- }
-
/// Returns an iterator over the cookies in `self` as identified by the
/// `Set-Cookie` header. Malformed cookies are skipped.
///
diff --git a/examples/error-handling/src/main.rs b/examples/error-handling/src/main.rs
index 85f4ec96..a82b73f3 100644
--- a/examples/error-handling/src/main.rs
+++ b/examples/error-handling/src/main.rs
@@ -13,7 +13,7 @@ fn hello(name: &str, age: i8) -> String {
#[get("/")]
fn forced_error(code: u16) -> Status {
- Status::raw(code)
+ Status::new(code)
}
#[catch(404)]
@@ -39,7 +39,7 @@ fn sergio_error() -> &'static str {
#[catch(default)]
fn default_catcher(status: Status, req: &Request<'_>) -> status::Custom {
- let msg = format!("{} - {} ({})", status.code, status.reason, req.uri());
+ let msg = format!("{} ({})", status, req.uri());
status::Custom(status, msg)
}
diff --git a/examples/error-handling/src/tests.rs b/examples/error-handling/src/tests.rs
index 3c5195f4..db5ac18d 100644
--- a/examples/error-handling/src/tests.rs
+++ b/examples/error-handling/src/tests.rs
@@ -30,9 +30,9 @@ fn forced_error() {
assert_eq!(response.into_string().unwrap(), expected.1);
let request = client.get("/533");
- let expected = super::default_catcher(Status::raw(533), request.inner());
+ let expected = super::default_catcher(Status::new(533), request.inner());
let response = request.dispatch();
- assert_eq!(response.status(), Status::raw(533));
+ assert_eq!(response.status(), Status::new(533));
assert_eq!(response.into_string().unwrap(), expected.1);
let request = client.get("/700");