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}