gotham/helpers/http/
response.rs

1//! Helpers for HTTP response generation
2
3use hyper::header::{CONTENT_TYPE, LOCATION};
4use hyper::{Body, Method, Response, StatusCode};
5use mime::Mime;
6use std::borrow::Cow;
7
8use crate::helpers::http::header::X_REQUEST_ID;
9use crate::state::{request_id, FromState, State};
10
11/// Creates a `Response` object and populates it with a set of default headers that help to improve
12/// security and conformance to best practice.
13///
14/// `create_response` utilises `extend_response`, which delegates to `set_headers` for setting
15/// security headers. See `set_headers` for information about the headers which are populated.
16///
17/// # Examples
18///
19/// ```rust
20/// # extern crate gotham;
21/// # extern crate hyper;
22/// # extern crate mime;
23/// #
24/// # use hyper::{Body, Response, StatusCode};
25/// # use hyper::header::{CONTENT_LENGTH, CONTENT_TYPE};
26/// # use gotham::state::State;
27/// # use gotham::helpers::http::header::X_REQUEST_ID;
28/// # use gotham::helpers::http::response::create_response;
29/// # use gotham::test::TestServer;
30/// #
31/// static BODY: &'static [u8] = b"Hello, world!";
32///
33/// fn handler(state: State) -> (State, Response<Body>) {
34///     let response = create_response(&state, StatusCode::OK, mime::TEXT_PLAIN, BODY);
35///
36///     (state, response)
37/// }
38/// #
39/// # fn main() {
40/// #     let test_server = TestServer::new(|| Ok(handler)).unwrap();
41/// #     let response = test_server
42/// #         .client()
43/// #         .get("http://example.com/")
44/// #         .perform()
45/// #         .unwrap();
46/// #
47/// #     assert_eq!(response.status(), StatusCode::OK);
48/// #     assert!(response.headers().get(X_REQUEST_ID).is_some());
49/// #
50/// #     assert_eq!(
51/// #         *response.headers().get(CONTENT_TYPE).unwrap(),
52/// #         mime::TEXT_PLAIN.to_string()
53/// #     );
54/// #
55/// #     assert_eq!(
56/// #         *response.headers().get(CONTENT_LENGTH).unwrap(),
57/// #         format!("{}", BODY.len() as u64)
58/// #     );
59/// # }
60/// ```
61pub fn create_response<B>(state: &State, status: StatusCode, mime: Mime, body: B) -> Response<Body>
62where
63    B: Into<Body>,
64{
65    // use the basic empty response as a base
66    let mut res = create_empty_response(state, status);
67
68    // insert the content type header
69    res.headers_mut()
70        .insert(CONTENT_TYPE, mime.as_ref().parse().unwrap());
71
72    // add the body on non-HEAD requests
73    if Method::borrow_from(state) != Method::HEAD {
74        *res.body_mut() = body.into();
75    }
76
77    res
78}
79
80/// Produces a simple empty `Response` with a provided status.
81///
82/// # Examples
83///
84/// ```rust
85/// # extern crate gotham;
86/// # extern crate hyper;
87/// #
88/// # use hyper::{Body, Response, StatusCode};
89/// # use gotham::state::State;
90/// # use gotham::helpers::http::response::create_empty_response;
91/// # use gotham::test::TestServer;
92/// fn handler(state: State) -> (State, Response<Body>) {
93///     let resp = create_empty_response(&state, StatusCode::NO_CONTENT);
94///
95///     (state, resp)
96/// }
97/// # fn main() {
98/// #     let test_server = TestServer::new(|| Ok(handler)).unwrap();
99/// #     let response = test_server
100/// #         .client()
101/// #         .get("http://example.com/")
102/// #         .perform()
103/// #         .unwrap();
104/// #
105/// #     assert_eq!(response.status(), StatusCode::NO_CONTENT);
106/// # }
107/// ```
108pub fn create_empty_response(state: &State, status: StatusCode) -> Response<Body> {
109    // new builder for the response
110    let built = Response::builder()
111        // always add status and req-id
112        .status(status)
113        .header(X_REQUEST_ID, request_id(state))
114        // attach an empty body by default
115        .body(Body::empty());
116
117    // this expect should be safe due to generic bounds
118    built.expect("Response built from a compatible type")
119}
120
121/// Produces a simple empty `Response` with a `Location` header and a 308
122/// status.
123///
124/// # Examples
125///
126/// ```rust
127/// # extern crate gotham;
128/// # extern crate hyper;
129/// #
130/// # use hyper::{Body, Response, StatusCode};
131/// # use gotham::state::State;
132/// # use gotham::helpers::http::response::create_permanent_redirect;
133/// # use gotham::test::TestServer;
134/// # use hyper::header::LOCATION;
135/// fn handler(state: State) -> (State, Response<Body>) {
136///     let resp = create_permanent_redirect(&state, "/over-there");
137///
138///     (state, resp)
139/// }
140/// # fn main() {
141/// #     let test_server = TestServer::new(|| Ok(handler)).unwrap();
142/// #     let response = test_server
143/// #         .client()
144/// #         .get("http://example.com/")
145/// #         .perform()
146/// #         .unwrap();
147/// #
148/// #     assert_eq!(response.status(), StatusCode::PERMANENT_REDIRECT);
149/// #     assert_eq!(
150/// #         response.headers().get(LOCATION).unwrap(),
151/// #         "/over-there"
152/// #     );
153/// # }
154/// ```
155pub fn create_permanent_redirect<L: Into<Cow<'static, str>>>(
156    state: &State,
157    location: L,
158) -> Response<Body> {
159    let mut res = create_empty_response(state, StatusCode::PERMANENT_REDIRECT);
160    res.headers_mut()
161        .insert(LOCATION, location.into().to_string().parse().unwrap());
162    res
163}
164
165/// Produces a simple empty `Response` with a `Location` header and a 307
166/// status.
167///
168/// # Examples
169///
170/// ```rust
171/// # extern crate gotham;
172/// # extern crate hyper;
173/// #
174/// # use hyper::{Body, Response, StatusCode};
175/// # use gotham::state::State;
176/// # use gotham::helpers::http::response::create_temporary_redirect;
177/// # use gotham::test::TestServer;
178/// # use hyper::header::LOCATION;
179/// fn handler(state: State) -> (State, Response<Body>) {
180///     let resp = create_temporary_redirect(&state, "/quick-detour");
181///
182///     (state, resp)
183/// }
184/// # fn main() {
185/// #     let test_server = TestServer::new(|| Ok(handler)).unwrap();
186/// #     let response = test_server
187/// #         .client()
188/// #         .get("http://example.com/")
189/// #         .perform()
190/// #         .unwrap();
191/// #
192/// #     assert_eq!(response.status(), StatusCode::TEMPORARY_REDIRECT);
193/// #     assert_eq!(
194/// #         response.headers().get(LOCATION).unwrap(),
195/// #         "/quick-detour"
196/// #     );
197/// # }
198/// ```
199pub fn create_temporary_redirect<L: Into<Cow<'static, str>>>(
200    state: &State,
201    location: L,
202) -> Response<Body> {
203    let mut res = create_empty_response(state, StatusCode::TEMPORARY_REDIRECT);
204    res.headers_mut()
205        .insert(LOCATION, location.into().to_string().parse().unwrap());
206    res
207}