Rocket/core/lib/src/response/response.rs

942 lines
31 KiB
Rust
Raw Normal View History

use std::{fmt, str};
use std::borrow::Cow;
use tokio::io::{AsyncRead, AsyncSeek};
2019-06-13 01:48:02 +00:00
use crate::http::{Header, HeaderMap, Status, ContentType, Cookie};
use crate::response::Body;
/// Builder for the [`Response`] type.
2016-12-20 07:29:20 +00:00
///
/// Building a [`Response`] can be a low-level ordeal; this structure presents a
/// higher-level API that simplifies building `Response`s.
2016-12-20 07:29:20 +00:00
///
/// # Usage
///
/// `Builder` follows the builder pattern and is usually obtained by calling
/// [`Response::build()`] on `Response`. Almost all methods take the current
/// builder as a mutable reference and return the same mutable reference with
/// field(s) modified in the `Response` being built. These method calls can be
/// chained: `build.a().b()`.
2016-12-20 07:29:20 +00:00
///
/// To finish building and retrieve the built `Response`, use the
/// [`finalize()`](#method.finalize) or [`ok()`](#method.ok) methods.
2016-12-20 07:29:20 +00:00
///
/// ## Headers
///
/// When building a `Response`, headers can either be _replaced_ or _adjoined_;
/// the default behavior (using `header(..)`) is to _replace_. When a header is
/// _replaced_, any existing values for headers with the same name are removed,
/// and the new value is set. If no header exists, the header is simply added.
/// On the other hand, when a header is _adjoined_, all existing values will
2016-12-20 07:29:20 +00:00
/// remain, and the `value` of the adjoined header will be added to the set of
/// existing values, if any. Adjoining maintains order: headers adjoined first
/// will appear first in the `Response`.
///
/// ## Joining and Merging
///
/// It is often necessary to combine multiple `Response`s in some way. The
/// [merge](#method.merge) and [join](#method.join) methods facilitate this. The
/// `merge` method replaces all of the fields in `self` with those present in
/// `other`. The `join` method sets any fields not set in `self` to the value in
/// `other`. See their documentation for more details.
/// ## Example
///
/// The following example builds a `Response` with:
///
/// * **Status**: `418 I'm a teapot`
/// * **Content-Type** header: `text/plain; charset=utf-8`
/// * **X-Teapot-Make** header: `Rocket`
/// * **X-Teapot-Model** headers: `Utopia`, `Series 1`
/// * **Body**: fixed-size string `"Brewing the best coffee!"`
///
/// ```rust
/// use std::io::Cursor;
/// use rocket::response::Response;
/// use rocket::http::{Status, ContentType};
///
/// let body = "Brewing the best coffee!";
2016-12-20 07:29:20 +00:00
/// let response = Response::build()
/// .status(Status::ImATeapot)
/// .header(ContentType::Plain)
/// .raw_header("X-Teapot-Make", "Rocket")
/// .raw_header("X-Teapot-Model", "Utopia")
/// .raw_header_adjoin("X-Teapot-Model", "Series 1")
/// .sized_body(body.len(), Cursor::new(body))
/// .finalize();
2016-12-20 07:29:20 +00:00
/// ```
pub struct Builder<'r> {
response: Response<'r>,
}
impl<'r> Builder<'r> {
/// Creates a new `Builder` that will build on top of the `base`
2016-12-20 07:29:20 +00:00
/// `Response`.
///
/// # Example
///
/// ```rust
/// use rocket::response::{Builder, Response};
2016-12-20 07:29:20 +00:00
///
2017-02-02 10:16:21 +00:00
/// # #[allow(unused_variables)]
/// let builder = Builder::new(Response::new());
2016-12-20 07:29:20 +00:00
/// ```
#[inline(always)]
pub fn new(base: Response<'r>) -> Builder<'r> {
Builder {
response: base,
}
}
2016-12-20 07:29:20 +00:00
/// Sets the status of the `Response` being built to `status`.
///
/// # Example
///
/// ```rust
/// use rocket::Response;
/// use rocket::http::Status;
///
/// let response = Response::build()
/// .status(Status::NotFound)
/// .finalize();
2016-12-20 07:29:20 +00:00
/// ```
#[inline(always)]
pub fn status(&mut self, status: Status) -> &mut Builder<'r> {
self.response.set_status(status);
self
}
2016-12-20 07:29:20 +00:00
/// 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
/// value will remain.
///
/// The type of `header` can be any type that implements `Into<Header>`.
2019-06-13 01:48:02 +00:00
/// This includes `Header` itself, [`ContentType`](crate::http::ContentType) and
/// [hyper::header types](crate::http::hyper::header).
///
2016-12-20 07:29:20 +00:00
/// # Example
///
/// ```rust
/// use rocket::Response;
/// use rocket::http::ContentType;
///
/// let response = Response::build()
/// .header(ContentType::JSON)
/// .header(ContentType::HTML)
/// .finalize();
2016-12-20 07:29:20 +00:00
///
/// assert_eq!(response.headers().get("Content-Type").count(), 1);
2016-12-20 07:29:20 +00:00
/// ```
#[inline(always)]
pub fn header<'h: 'r, H>(&mut self, header: H) -> &mut Builder<'r>
where H: Into<Header<'h>>
{
self.response.set_header(header);
self
}
2016-12-20 07:29:20 +00:00
/// Adds `header` to the `Response` by adjoining the header with any
/// existing headers with the same name that already exist in the
/// `Response`. This allows for multiple headers with the same name and
2016-12-20 07:29:20 +00:00
/// potentially different values to be present in the `Response`.
///
/// The type of `header` can be any type that implements `Into<Header>`.
2019-06-13 01:48:02 +00:00
/// This includes `Header` itself, [`ContentType`](crate::http::ContentType) and
/// [hyper::header types](crate::http::hyper::header).
///
2016-12-20 07:29:20 +00:00
/// # Example
///
/// ```rust
/// use rocket::Response;
/// use rocket::http::Header;
/// use rocket::http::hyper::header::ACCEPT;
2016-12-20 07:29:20 +00:00
///
/// let response = Response::build()
/// .header_adjoin(Header::new(ACCEPT.as_str(), "application/json"))
/// .header_adjoin(Header::new(ACCEPT.as_str(), "text/plain"))
/// .finalize();
2016-12-20 07:29:20 +00:00
///
/// assert_eq!(response.headers().get("Accept").count(), 2);
2016-12-20 07:29:20 +00:00
/// ```
#[inline(always)]
pub fn header_adjoin<'h: 'r, H>(&mut self, header: H) -> &mut Builder<'r>
where H: Into<Header<'h>>
{
self.response.adjoin_header(header);
self
}
/// Adds a custom header to the `Response` with the given name and value,
2016-12-20 07:29:20 +00:00
/// 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 value will remain.
///
/// # Example
///
/// ```rust
/// use rocket::Response;
///
/// let response = Response::build()
/// .raw_header("X-Custom", "first")
/// .raw_header("X-Custom", "second")
/// .finalize();
2016-12-20 07:29:20 +00:00
///
/// assert_eq!(response.headers().get("X-Custom").count(), 1);
2016-12-20 07:29:20 +00:00
/// ```
#[inline(always)]
pub fn raw_header<'a, 'b, N, V>(&mut self, name: N, value: V) -> &mut Builder<'r>
2020-02-02 01:30:19 +00:00
where N: Into<Cow<'a, str>>, V: Into<Cow<'b, str>>, 'a: 'r, 'b: 'r
{
self.response.set_raw_header(name, value);
self
}
2016-12-20 07:29:20 +00:00
/// Adds custom header to the `Response` with the given name and value,
/// adjoining the header with any existing headers with the same name that
/// already exist in the `Response`. This allows for multiple headers with
2016-12-20 07:29:20 +00:00
/// the same name and potentially different values to be present in the
/// `Response`.
///
/// # Example
///
/// ```rust
/// use rocket::Response;
///
/// let response = Response::build()
/// .raw_header_adjoin("X-Custom", "first")
/// .raw_header_adjoin("X-Custom", "second")
/// .finalize();
2016-12-20 07:29:20 +00:00
///
/// assert_eq!(response.headers().get("X-Custom").count(), 2);
2016-12-20 07:29:20 +00:00
/// ```
#[inline(always)]
pub fn raw_header_adjoin<'a, 'b, N, V>(&mut self, name: N, value: V) -> &mut Builder<'r>
2020-02-02 01:30:19 +00:00
where N: Into<Cow<'a, str>>, V: Into<Cow<'b, str>>, 'a: 'r, 'b: 'r
{
self.response.adjoin_raw_header(name, value);
self
}
/// Sets the body of the `Response` to be the fixed-sized `body` with size
/// `size`, which may be `None`. If `size` is `None`, the body's size will
/// be computed with calls to `seek` when the response is written out.
2016-12-20 07:29:20 +00:00
///
/// # Example
///
/// ```rust
/// use std::io::Cursor;
2016-12-20 07:29:20 +00:00
/// use rocket::Response;
///
/// let body = "Hello, world!";
2016-12-20 07:29:20 +00:00
/// let response = Response::build()
/// .sized_body(body.len(), Cursor::new(body))
/// .finalize();
2016-12-20 07:29:20 +00:00
/// ```
pub fn sized_body<B, S>(&mut self, size: S, body: B) -> &mut Builder<'r>
where B: AsyncRead + AsyncSeek + Send + 'r,
S: Into<Option<usize>>
{
self.response.set_sized_body(size, body);
self
}
2016-12-20 07:29:20 +00:00
/// Sets the body of the `Response` to be the streamed `body`.
///
/// # Example
///
/// ```rust
/// use std::io::Cursor;
2016-12-20 07:29:20 +00:00
/// use rocket::Response;
///
/// let response = Response::build()
/// .streamed_body(Cursor::new("Hello, world!"))
/// .finalize();
2016-12-20 07:29:20 +00:00
/// ```
#[inline(always)]
pub fn streamed_body<B>(&mut self, body: B) -> &mut Builder<'r>
where B: AsyncRead + Send + 'r
{
self.response.set_streamed_body(body);
self
}
/// Sets the max chunk size of a body, if any, to `size`.
2016-12-20 07:29:20 +00:00
///
/// See [`Response::set_max_chunk_size()`] for notes.
///
/// # Example
///
/// ```rust
/// use std::io::Cursor;
/// use rocket::Response;
///
/// let response = Response::build()
/// .streamed_body(Cursor::new("Hello, world!"))
/// .max_chunk_size(3072)
/// .finalize();
/// ```
#[inline(always)]
pub fn max_chunk_size(&mut self, size: usize) -> &mut Builder<'r> {
self.response.set_max_chunk_size(size);
self
}
2016-12-20 07:29:20 +00:00
/// Merges the `other` `Response` into `self` by setting any fields in
/// `self` to the corresponding value in `other` if they are set in `other`.
/// Fields in `self` are unchanged if they are not set in `other`. If a
/// header is set in both `self` and `other`, the values in `other` are
/// kept. Headers set only in `self` remain.
///
/// # Example
///
/// ```rust
/// use rocket::Response;
/// use rocket::http::{Status, ContentType};
///
/// let base = Response::build()
/// .status(Status::NotFound)
/// .header(ContentType::HTML)
/// .raw_header("X-Custom", "value 1")
/// .finalize();
2016-12-20 07:29:20 +00:00
///
/// let response = Response::build()
/// .status(Status::ImATeapot)
/// .raw_header("X-Custom", "value 2")
/// .raw_header_adjoin("X-Custom", "value 3")
/// .merge(base)
/// .finalize();
2016-12-20 07:29:20 +00:00
///
/// assert_eq!(response.status(), Status::NotFound);
///
/// let ctype: Vec<_> = response.headers().get("Content-Type").collect();
2016-12-20 07:29:20 +00:00
/// assert_eq!(ctype, vec![ContentType::HTML.to_string()]);
///
/// let custom_values: Vec<_> = response.headers().get("X-Custom").collect();
2016-12-20 07:29:20 +00:00
/// assert_eq!(custom_values, vec!["value 1"]);
/// ```
#[inline(always)]
pub fn merge(&mut self, other: Response<'r>) -> &mut Builder<'r> {
self.response.merge(other);
self
}
2016-12-20 07:29:20 +00:00
/// Joins the `other` `Response` into `self` by setting any fields in `self`
/// to the corresponding value in `other` if they are set in `self`. Fields
/// in `self` are unchanged if they are already set. If a header is set in
/// both `self` and `other`, the values are adjoined, with the values in
/// `self` coming first. Headers only in `self` or `other` are set in
/// `self`.
///
/// # Example
///
/// ```rust
/// use rocket::Response;
/// use rocket::http::{Status, ContentType};
///
/// let other = Response::build()
/// .status(Status::NotFound)
/// .header(ContentType::HTML)
/// .raw_header("X-Custom", "value 1")
/// .finalize();
2016-12-20 07:29:20 +00:00
///
/// let response = Response::build()
/// .status(Status::ImATeapot)
/// .raw_header("X-Custom", "value 2")
/// .raw_header_adjoin("X-Custom", "value 3")
/// .join(other)
/// .finalize();
2016-12-20 07:29:20 +00:00
///
/// assert_eq!(response.status(), Status::ImATeapot);
///
/// let ctype: Vec<_> = response.headers().get("Content-Type").collect();
2016-12-20 07:29:20 +00:00
/// assert_eq!(ctype, vec![ContentType::HTML.to_string()]);
///
/// let custom_values: Vec<_> = response.headers().get("X-Custom").collect();
2016-12-20 07:29:20 +00:00
/// assert_eq!(custom_values, vec!["value 2", "value 3", "value 1"]);
/// ```
#[inline(always)]
pub fn join(&mut self, other: Response<'r>) -> &mut Builder<'r> {
self.response.join(other);
self
}
/// Return the `Response` structure that was being built by this builder.
/// After calling this method, `self` is cleared and must be rebuilt as if
/// from `new()`.
2016-12-20 07:29:20 +00:00
///
/// # Example
///
/// ```rust
/// use std::io::Cursor;
///
2016-12-20 07:29:20 +00:00
/// use rocket::Response;
/// use rocket::http::Status;
2016-12-20 07:29:20 +00:00
///
/// let body = "Brewing the best coffee!";
/// let response = Response::build()
/// .status(Status::ImATeapot)
/// .sized_body(body.len(), Cursor::new(body))
/// .raw_header("X-Custom", "value 2")
/// .finalize();
/// ```
pub fn finalize(&mut self) -> Response<'r> {
std::mem::replace(&mut self.response, Response::new())
}
/// Retrieve the built `Response` wrapped in `Ok`. After calling this
/// method, `self` is cleared and must be rebuilt as if from `new()`.
///
/// # Example
///
/// ```rust
/// use rocket::Response;
///
2016-12-20 07:29:20 +00:00
/// let response: Result<Response, ()> = Response::build()
/// // build the response
/// .ok();
2016-12-20 07:29:20 +00:00
///
/// assert!(response.is_ok());
/// ```
#[inline(always)]
pub fn ok<E>(&mut self) -> Result<Response<'r>, E> {
Ok(self.finalize())
}
}
/// A response, as returned by types implementing
/// [`Responder`](crate::response::Responder).
///
/// See [`Builder`] for docs on how a `Response` is typically created.
2016-12-17 17:18:30 +00:00
#[derive(Default)]
pub struct Response<'r> {
status: Option<Status>,
headers: HeaderMap<'r>,
body: Body<'r>,
}
impl<'r> Response<'r> {
/// Creates a new, empty `Response` without a status, body, or headers.
/// Because all HTTP responses must have a status, if a default `Response`
/// is written to the client without a status, the status defaults to `200
/// Ok`.
///
/// # Example
///
/// ```rust
/// use rocket::Response;
/// use rocket::http::Status;
///
/// let mut response = Response::new();
///
/// assert_eq!(response.status(), Status::Ok);
/// assert_eq!(response.headers().len(), 0);
/// assert!(response.body().is_none());
/// ```
#[inline(always)]
pub fn new() -> Response<'r> {
Response::default()
}
/// Returns a `Builder` with a base of `Response::new()`.
///
/// # Example
///
/// ```rust
/// use rocket::Response;
///
2017-02-02 10:16:21 +00:00
/// # #[allow(unused_variables)]
/// let builder = Response::build();
/// ```
#[inline(always)]
pub fn build() -> Builder<'r> {
Response::build_from(Response::new())
}
/// Returns a `Builder` with a base of `other`.
///
/// # Example
///
/// ```rust
2017-02-02 10:16:21 +00:00
/// # #![allow(unused_variables)]
/// use rocket::Response;
///
/// let other = Response::new();
/// let builder = Response::build_from(other);
/// ```
#[inline(always)]
pub fn build_from(other: Response<'r>) -> Builder<'r> {
Builder::new(other)
}
2017-06-30 09:06:03 +00:00
/// Returns the status of `self`.
///
/// # Example
///
/// ```rust
/// use rocket::Response;
/// use rocket::http::Status;
///
/// let mut response = Response::new();
/// assert_eq!(response.status(), Status::Ok);
///
/// response.set_status(Status::NotFound);
/// assert_eq!(response.status(), Status::NotFound);
/// ```
#[inline(always)]
pub fn status(&self) -> Status {
self.status.unwrap_or(Status::Ok)
}
/// Sets the status of `self` to `status`.
///
/// # Example
///
/// ```rust
/// use rocket::Response;
/// use rocket::http::Status;
///
/// let mut response = Response::new();
/// response.set_status(Status::ImATeapot);
/// assert_eq!(response.status(), Status::ImATeapot);
/// ```
#[inline(always)]
pub fn set_status(&mut self, status: Status) {
self.status = Some(status);
}
2017-04-14 21:35:22 +00:00
/// Returns the Content-Type header of `self`. If the header is not present
/// or is malformed, returns `None`.
///
/// # Example
///
/// ```rust
/// use rocket::Response;
/// use rocket::http::ContentType;
///
/// let mut response = Response::new();
/// response.set_header(ContentType::HTML);
/// assert_eq!(response.content_type(), Some(ContentType::HTML));
/// ```
#[inline(always)]
pub fn content_type(&self) -> Option<ContentType> {
self.headers().get_one("Content-Type").and_then(|v| v.parse().ok())
}
/// Returns an iterator over the cookies in `self` as identified by the
/// `Set-Cookie` header. Malformed cookies are skipped.
///
/// # Example
///
/// ```rust
/// use rocket::Response;
/// use rocket::http::Cookie;
///
/// let mut response = Response::new();
/// response.set_header(Cookie::new("hello", "world!"));
/// let cookies: Vec<_> = response.cookies().collect();
/// assert_eq!(cookies, vec![Cookie::new("hello", "world!")]);
/// ```
pub fn cookies(&self) -> impl Iterator<Item = Cookie<'_>> {
self.headers()
.get("Set-Cookie")
.filter_map(|header| Cookie::parse_encoded(header).ok())
}
/// Returns a [`HeaderMap`] of all of the headers in `self`.
///
/// # Example
///
/// ```rust
/// use rocket::Response;
/// use rocket::http::Header;
///
/// let mut response = Response::new();
/// response.adjoin_raw_header("X-Custom", "1");
/// response.adjoin_raw_header("X-Custom", "2");
///
/// let mut custom_headers = response.headers().iter();
/// assert_eq!(custom_headers.next(), Some(Header::new("X-Custom", "1")));
/// assert_eq!(custom_headers.next(), Some(Header::new("X-Custom", "2")));
/// assert_eq!(custom_headers.next(), None);
/// ```
#[inline(always)]
pub fn headers(&self) -> &HeaderMap<'r> {
&self.headers
}
/// Sets the header `header` in `self`. Any existing headers with the name
/// `header.name` will be lost, and only `header` will remain. The type of
/// `header` can be any type that implements `Into<Header>`. This includes
2019-06-13 01:48:02 +00:00
/// `Header` itself, [`ContentType`](crate::http::ContentType) and
/// [`hyper::header` types](crate::http::hyper::header).
///
/// # Example
///
/// ```rust
/// use rocket::Response;
/// use rocket::http::ContentType;
///
/// let mut response = Response::new();
///
/// response.set_header(ContentType::HTML);
/// assert_eq!(response.headers().iter().next(), Some(ContentType::HTML.into()));
/// assert_eq!(response.headers().len(), 1);
///
/// response.set_header(ContentType::JSON);
/// assert_eq!(response.headers().iter().next(), Some(ContentType::JSON.into()));
/// assert_eq!(response.headers().len(), 1);
/// ```
#[inline(always)]
pub fn set_header<'h: 'r, H: Into<Header<'h>>>(&mut self, header: H) -> bool {
self.headers.replace(header)
}
/// Sets the custom header with name `name` and value `value` in `self`. Any
/// existing headers with the same `name` will be lost, and the new custom
/// header will remain. This method should be used sparingly; prefer to use
/// [set_header](#method.set_header) instead.
///
/// # Example
///
/// ```rust
/// use rocket::Response;
/// use rocket::http::Header;
///
/// let mut response = Response::new();
///
/// response.set_raw_header("X-Custom", "1");
/// assert_eq!(response.headers().get_one("X-Custom"), Some("1"));
/// assert_eq!(response.headers().len(), 1);
///
/// response.set_raw_header("X-Custom", "2");
/// assert_eq!(response.headers().get_one("X-Custom"), Some("2"));
/// assert_eq!(response.headers().len(), 1);
/// ```
#[inline(always)]
pub fn set_raw_header<'a: 'r, 'b: 'r, N, V>(&mut self, name: N, value: V) -> bool
where N: Into<Cow<'a, str>>, V: Into<Cow<'b, str>>
{
self.set_header(Header::new(name, value))
}
/// Adds the header `header` to `self`. If `self` contains headers with the
/// name `header.name`, another header with the same name and value
/// `header.value` is added. The type of `header` can be any type that
/// implements `Into<Header>`. This includes `Header` itself,
2019-06-13 01:48:02 +00:00
/// [`ContentType`](crate::http::ContentType) and [`hyper::header`
/// types](crate::http::hyper::header).
///
/// # Example
///
/// ```rust
/// use rocket::Response;
/// use rocket::http::Header;
/// use rocket::http::hyper::header::ACCEPT;
///
/// let mut response = Response::new();
/// response.adjoin_header(Header::new(ACCEPT.as_str(), "application/json"));
/// response.adjoin_header(Header::new(ACCEPT.as_str(), "text/plain"));
///
/// let mut accept_headers = response.headers().iter();
/// assert_eq!(accept_headers.next(), Some(Header::new(ACCEPT.as_str(), "application/json")));
/// assert_eq!(accept_headers.next(), Some(Header::new(ACCEPT.as_str(), "text/plain")));
/// assert_eq!(accept_headers.next(), None);
/// ```
#[inline(always)]
pub fn adjoin_header<'h: 'r, H: Into<Header<'h>>>(&mut self, header: H) {
self.headers.add(header)
}
/// Adds a custom header with name `name` and value `value` to `self`. If
/// `self` already contains headers with the name `name`, another header
/// with the same `name` and `value` is added. The type of `header` can be
/// any type implements `Into<Header>`. This includes `Header` itself,
2019-06-13 01:48:02 +00:00
/// [`ContentType`](crate::http::ContentType) and [`hyper::header`
/// types](crate::http::hyper::header).
///
/// # Example
///
/// ```rust
/// use rocket::Response;
/// use rocket::http::Header;
///
/// let mut response = Response::new();
/// response.adjoin_raw_header("X-Custom", "one");
/// response.adjoin_raw_header("X-Custom", "two");
///
/// let mut custom_headers = response.headers().iter();
/// assert_eq!(custom_headers.next(), Some(Header::new("X-Custom", "one")));
/// assert_eq!(custom_headers.next(), Some(Header::new("X-Custom", "two")));
/// assert_eq!(custom_headers.next(), None);
/// ```
#[inline(always)]
pub fn adjoin_raw_header<'a: 'r, 'b: 'r, N, V>(&mut self, name: N, value: V)
where N: Into<Cow<'a, str>>, V: Into<Cow<'b, str>>
{
self.adjoin_header(Header::new(name, value));
}
/// Removes all headers with the name `name`.
///
/// # Example
///
/// ```rust
/// use rocket::Response;
///
/// let mut response = Response::new();
///
/// response.adjoin_raw_header("X-Custom", "one");
/// response.adjoin_raw_header("X-Custom", "two");
/// response.adjoin_raw_header("X-Other", "hi");
/// assert_eq!(response.headers().len(), 3);
///
/// response.remove_header("X-Custom");
/// assert_eq!(response.headers().len(), 1);
/// ```
#[inline(always)]
pub fn remove_header(&mut self, name: &str) {
self.headers.remove(name);
}
/// Returns an immutable borrow of the body of `self`, if there is one.
///
/// # Example
///
/// ```rust
/// use std::io::Cursor;
/// use rocket::Response;
///
/// # rocket::async_test(async {
/// let mut response = Response::new();
/// assert!(response.body().is_none());
///
/// let string = "Hello, world!";
/// response.set_sized_body(string.len(), Cursor::new(string));
/// assert!(response.body().is_some());
/// # })
/// ```
#[inline(always)]
pub fn body(&self) -> &Body<'r> {
&self.body
}
/// Returns a mutable borrow of the body of `self`, if there is one. A
/// mutable borrow allows for reading the body.
///
/// # Example
///
/// ```rust
/// use std::io::Cursor;
/// use rocket::Response;
///
/// # rocket::async_test(async {
/// let mut response = Response::new();
/// assert!(response.body().is_none());
///
/// let string = "Hello, world!";
/// response.set_sized_body(string.len(), Cursor::new(string));
/// let string = response.body_mut().to_string().await;
/// assert_eq!(string.unwrap(), "Hello, world!");
/// # })
/// ```
#[inline(always)]
pub fn body_mut(&mut self) -> &mut Body<'r> {
&mut self.body
}
// Makes the `AsyncRead`er in the body empty but leaves the size of the body
// if it exists. Meant to be used during HEAD handling.
2016-12-16 13:17:16 +00:00
#[inline(always)]
pub(crate) fn strip_body(&mut self) {
self.body.strip();
2016-12-16 13:17:16 +00:00
}
/// Sets the body of `self` to be the fixed-sized `body` with size
/// `size`, which may be `None`. If `size` is `None`, the body's size will
/// be computing with calls to `seek` just before being written out in a
/// response.
///
/// # Example
///
/// ```rust
/// use std::io;
/// use rocket::Response;
///
/// # let o: io::Result<()> = rocket::async_test(async {
/// let string = "Hello, world!";
///
/// let mut response = Response::new();
/// response.set_sized_body(string.len(), io::Cursor::new(string));
/// assert_eq!(response.body_mut().to_string().await?, "Hello, world!");
/// # Ok(())
/// # });
/// # assert!(o.is_ok());
/// ```
pub fn set_sized_body<B, S>(&mut self, size: S, body: B)
where B: AsyncRead + AsyncSeek + Send + 'r,
S: Into<Option<usize>>
{
self.body = Body::with_sized(body, size.into());
}
/// Sets the body of `self` to `body`, which will be streamed.
///
/// The max chunk size is configured via [`Response::set_max_chunk_size()`]
/// and defaults to [`Body::DEFAULT_MAX_CHUNK`].
///
/// # Example
///
/// ```rust
/// # use std::io;
/// use tokio::io::{repeat, AsyncReadExt};
/// use rocket::Response;
///
/// # let o: io::Result<()> = rocket::async_test(async {
/// let mut response = Response::new();
/// response.set_streamed_body(repeat(97).take(5));
/// assert_eq!(response.body_mut().to_string().await?, "aaaaa");
/// # Ok(())
/// # });
/// # assert!(o.is_ok());
/// ```
#[inline(always)]
pub fn set_streamed_body<B>(&mut self, body: B)
where B: AsyncRead + Send + 'r
{
self.body = Body::with_unsized(body);
}
/// Sets the body's maximum chunk size to `size` bytes.
///
/// The default max chunk size is [`Body::DEFAULT_MAX_CHUNK`]. The max chunk
/// size is a property of the body and is thus reset whenever a body is set
/// via [`Response::set_streamed_body()`], [`Response::set_sized_body()`],
/// or the corresponding builer methods.
///
/// This setting does not typically need to be changed. Configuring a high
/// value can result in high memory usage. Similarly, configuring a low
/// value can result in excessive network writes. When unsure, leave the
/// value unchanged.
///
/// # Example
///
/// ```rust
/// use tokio::io::{repeat, AsyncReadExt};
/// use rocket::Response;
///
/// # let o: Option<()> = rocket::async_test(async {
/// let mut response = Response::new();
/// response.set_streamed_body(repeat(97).take(5));
/// response.set_max_chunk_size(3072);
/// # Some(())
/// # });
/// # assert!(o.is_some());
#[inline(always)]
pub fn set_max_chunk_size(&mut self, size: usize) {
self.body_mut().set_max_chunk_size(size);
}
/// Replaces this response's status and body with that of `other`, if they
/// exist in `other`. Any headers that exist in `other` replace the ones in
/// `self`. Any in `self` that aren't in `other` remain in `self`.
///
/// # Example
///
/// ```rust
/// use rocket::Response;
/// use rocket::http::{Status, ContentType};
///
/// let base = Response::build()
/// .status(Status::NotFound)
/// .header(ContentType::HTML)
/// .raw_header("X-Custom", "value 1")
/// .finalize();
///
/// let response = Response::build()
/// .status(Status::ImATeapot)
/// .raw_header("X-Custom", "value 2")
/// .raw_header_adjoin("X-Custom", "value 3")
/// .merge(base)
/// .finalize();
///
/// assert_eq!(response.status(), Status::NotFound);
///
/// let ctype: Vec<_> = response.headers().get("Content-Type").collect();
/// assert_eq!(ctype, vec![ContentType::HTML.to_string()]);
///
/// let custom_values: Vec<_> = response.headers().get("X-Custom").collect();
/// assert_eq!(custom_values, vec!["value 1"]);
/// ```
pub fn merge(&mut self, other: Response<'r>) {
if let Some(status) = other.status {
self.status = Some(status);
}
if other.body().is_some() {
self.body = other.body;
}
for (name, values) in other.headers.into_iter_raw() {
self.headers.replace_all(name.into_cow(), values);
}
}
2017-03-23 10:53:12 +00:00
/// Sets `self`'s status and body to that of `other` if they are not already
/// set in `self`. Any headers present in both `other` and `self` are
/// adjoined.
///
/// # Example
///
/// ```rust
/// use rocket::Response;
/// use rocket::http::{Status, ContentType};
///
/// let other = Response::build()
/// .status(Status::NotFound)
/// .header(ContentType::HTML)
/// .raw_header("X-Custom", "value 1")
/// .finalize();
///
/// let response = Response::build()
/// .status(Status::ImATeapot)
/// .raw_header("X-Custom", "value 2")
/// .raw_header_adjoin("X-Custom", "value 3")
/// .join(other)
/// .finalize();
///
/// assert_eq!(response.status(), Status::ImATeapot);
///
/// let ctype: Vec<_> = response.headers().get("Content-Type").collect();
/// assert_eq!(ctype, vec![ContentType::HTML.to_string()]);
///
/// let custom_values: Vec<_> = response.headers().get("X-Custom").collect();
/// assert_eq!(custom_values, vec!["value 2", "value 3", "value 1"]);
/// ```
pub fn join(&mut self, other: Response<'r>) {
if self.status.is_none() {
self.status = other.status;
}
if self.body.is_none() {
self.body = other.body;
}
for (name, mut values) in other.headers.into_iter_raw() {
self.headers.add_all(name.into_cow(), &mut values);
}
}
}
2019-06-13 01:48:02 +00:00
impl fmt::Debug for Response<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "{}", self.status())?;
for header in self.headers().iter() {
writeln!(f, "{}", header)?;
}
self.body.fmt(f)
2016-12-16 04:57:14 +00:00
}
}