pavex/response/response_.rs
1use bytes::Bytes;
2use http::header::CONTENT_TYPE;
3use http_body_util::Empty;
4
5use crate::http::StatusCode;
6use crate::http::{HeaderMap, Version};
7
8use super::ResponseBody;
9use super::body::TypedBody;
10use super::body::raw::RawBody;
11
12/// Represents an HTTP response.
13///
14/// ```rust
15/// use pavex::Response;
16/// use pavex::http::{HeaderValue, header::SERVER};
17///
18/// // Create a new response with:
19/// // - status code `OK`
20/// // - HTTP version `HTTP/1.1`
21/// // - the `Server` header set to `Pavex`
22/// // - the `Content-Type` header set to `text/plain; charset=utf-8`
23/// // - the body set to `Hello, world!`
24/// let response = Response::ok()
25/// .insert_header(SERVER, HeaderValue::from_static("Pavex"))
26/// .set_typed_body("Hello, world!");
27/// ```
28///
29/// The response is composed of a head ([`ResponseHead`]) and an optional body.
30///
31/// Check out [`Response::new`] for details on how to build a new [`Response`].
32/// You might also want to check out the following methods to further customize
33/// your response:
34///
35/// - [`set_status`](Response::set_status) to change the status code.
36/// - [`set_version`](Response::set_version) to change the HTTP version.
37/// - [`append_header`](Response::append_header) to append a value to a header.
38/// - [`insert_header`](Response::insert_header) to upsert a header value.
39/// - [`set_typed_body`](Response::set_typed_body) to set the body and automatically set the `Content-Type` header.
40///
41/// There are other methods available on [`Response`] that you might find useful, but the
42/// ones listed above are the most commonly used and should be enough to get you started.
43#[derive(Debug)]
44pub struct Response {
45 inner: http::Response<ResponseBody>,
46}
47
48#[non_exhaustive]
49#[derive(Debug)]
50/// All the information that is transmitted as part of an HTTP [`Response`] ahead of the body.
51///
52/// It includes the status code, the HTTP version, and the headers.
53pub struct ResponseHead {
54 status: StatusCode,
55 version: Version,
56 headers: HeaderMap,
57}
58
59impl Response {
60 /// Build a new [`Response`] with the given status code.
61 /// The HTTP version is set to HTTP 1.1, there are no headers and
62 /// the body is empty.
63 ///
64 /// # Example
65 ///
66 /// ```rust
67 /// use pavex::http::StatusCode;
68 /// use pavex::Response;
69 ///
70 /// let response = Response::new(StatusCode::OK);
71 /// ```
72 ///
73 /// # Alternatives
74 ///
75 /// Pavex's provides a set of shorthands for building a new [`Response`] using
76 /// well-known status code. For example, the following code is equivalent to the
77 /// example above:
78 ///
79 /// ```rust
80 /// use pavex::Response;
81 ///
82 /// let response = Response::ok();
83 /// ```
84 ///
85 /// Check out [`Response`]'s API documentation for a complete list of all
86 /// the supported shorthands.
87 pub fn new(status_code: StatusCode) -> Self {
88 let inner = http::Response::new(ResponseBody::new(Empty::new()));
89 Self { inner }.set_status(status_code)
90 }
91}
92
93impl Response {
94 /// Change the status code of the [`Response`].
95 ///
96 /// # Example
97 ///
98 /// ```rust
99 /// use pavex::http::StatusCode;
100 /// use pavex::Response;
101 ///
102 /// let mut response = Response::ok();
103 /// assert_eq!(response.status(), StatusCode::OK);
104 ///
105 /// // Change the status code to `CREATED`.
106 /// response = response.set_status(StatusCode::CREATED);
107 /// assert_eq!(response.status(), StatusCode::CREATED);
108 /// ```
109 pub fn set_status(mut self, status: StatusCode) -> Self {
110 *self.inner.status_mut() = status;
111 self
112 }
113
114 /// Get a mutable reference to the [`Response`] status.
115 ///
116 /// # Example
117 ///
118 /// ```rust
119 /// use pavex::http::StatusCode;
120 /// use pavex::Response;
121 /// use pavex::http::header::CONTENT_TYPE;
122 ///
123 /// let mut response = Response::ok();
124 ///
125 /// assert_eq!(response.status(), StatusCode::OK);
126 ///
127 /// // Get a mutable reference to the status.
128 /// let status = response.status_mut();
129 ///
130 /// // Change the Status
131 /// *status = StatusCode::NOT_FOUND;
132 ///
133 /// assert_eq!(response.status(), StatusCode::NOT_FOUND);
134 /// ```
135 pub fn status_mut(&mut self) -> &mut StatusCode {
136 self.inner.status_mut()
137 }
138
139 /// Change the HTTP version of the [`Response`].
140 ///
141 /// # Example
142 ///
143 /// ```rust
144 /// use pavex::http::Version;
145 /// use pavex::Response;
146 ///
147 /// let mut response = Response::ok();
148 /// // By default, the HTTP version is HTTP/1.1.
149 /// assert_eq!(response.version(), Version::HTTP_11);
150 ///
151 /// // Change the HTTP version to HTTP/2.
152 /// response = response.set_version(Version::HTTP_2);
153 /// assert_eq!(response.version(), Version::HTTP_2);
154 /// ```
155 pub fn set_version(mut self, version: Version) -> Self {
156 *self.inner.version_mut() = version;
157 self
158 }
159
160 /// Append a value to a [`Response`] header.
161 ///
162 /// If the header is not present, it is added with the given value.
163 /// If the header is present, the value is appended to the end
164 /// of the comma-separated list of existing values for that header.
165 ///
166 /// # Example
167 ///
168 /// ```rust
169 /// use pavex::http::{header::HOST, HeaderValue};
170 /// use pavex::Response;
171 ///
172 /// let mut response = Response::ok();
173 /// assert!(response.headers().get("host").is_none());
174 ///
175 /// // Append a value to the `host` header.
176 /// let value = HeaderValue::from_static("world");
177 /// response = response.append_header(HOST, value);
178 ///
179 /// let headers: Vec<_> = response.headers().get_all("host").iter().collect();
180 /// assert_eq!(headers.len(), 1);
181 /// assert_eq!(headers[0], "world");
182 ///
183 /// // Append another value to the `host` header.
184 /// let value = HeaderValue::from_static("earth");
185 /// response = response.append_header(HOST, value);
186 ///
187 /// let headers: Vec<_> = response.headers().get_all("host").iter().collect();
188 /// assert_eq!(headers.len(), 2);
189 /// assert_eq!(headers[0], "world");
190 /// assert_eq!(headers[1], "earth");
191 /// ```
192 ///
193 /// # Alternatives
194 ///
195 /// If you want to replace the value of a header instead of appending to it,
196 /// use [`insert_header`](Response::insert_header) instead.
197 pub fn append_header(
198 mut self,
199 key: crate::http::HeaderName,
200 value: crate::http::HeaderValue,
201 ) -> Self {
202 self.inner.headers_mut().append(key, value);
203 self
204 }
205
206 /// Insert a header value into the [`Response`].
207 ///
208 /// If the header key is not present, it is added with the given value.
209 /// If the header key is present, its value is replaced with the given value.
210 ///
211 /// # Example
212 ///
213 /// ```rust
214 /// use pavex::http::{header::HOST, HeaderValue};
215 /// use pavex::Response;
216 ///
217 /// let mut response = Response::ok();
218 /// assert!(response.headers().get("host").is_none());
219 ///
220 /// // Insert a value into the `host` header.
221 /// let value = HeaderValue::from_static("world");
222 /// response = response.insert_header(HOST, value);
223 ///
224 /// let headers: Vec<_> = response.headers().get_all("host").iter().collect();
225 /// assert_eq!(headers.len(), 1);
226 /// assert_eq!(headers[0], "world");
227 ///
228 /// // Insert another value into the `host` header.
229 /// let value = HeaderValue::from_static("earth");
230 /// response = response.insert_header(HOST, value);
231 ///
232 /// let headers: Vec<_> = response.headers().get_all("host").iter().collect();
233 /// assert_eq!(headers.len(), 1);
234 /// assert_eq!(headers[0], "earth");
235 /// ```
236 ///
237 /// # Alternatives
238 ///
239 /// If you want to append to the current header value instead of replacing it,
240 /// use [`append_header`](Response::append_header) instead.
241 pub fn insert_header(
242 mut self,
243 key: crate::http::HeaderName,
244 value: crate::http::HeaderValue,
245 ) -> Self {
246 self.inner.headers_mut().insert(key, value);
247 self
248 }
249
250 /// Set the [`Response`] body.
251 ///
252 /// The provided body must implement the [`TypedBody`] trait.
253 /// The `Content-Type` header is automatically set to the value returned
254 /// by [`TypedBody::content_type`].
255 ///
256 /// If a body is already set, it is replaced.
257 ///
258 /// # Example
259 ///
260 /// ```rust
261 /// use pavex::{Response, response::body::Html};
262 /// use pavex::http::header::CONTENT_TYPE;
263 ///
264 /// let typed_body = "Hello, world!";
265 /// let response = Response::ok().set_typed_body(typed_body);
266 ///
267 /// // The `Content-Type` header is set automatically
268 /// // when using `set_typed_body`.
269 /// assert_eq!(response.headers()[CONTENT_TYPE], "text/plain; charset=utf-8");
270 /// ```
271 ///
272 /// # Built-in `TypedBody` implementations
273 ///
274 /// Pavex provides several implementations of [`TypedBody`] out of the box,
275 /// to cover the most common use cases:
276 ///
277 /// - [`String`], [`&'static str`](std::primitive::str)
278 /// and [`Cow<'static, str>`](std::borrow::Cow) for `text/plain; charset=utf-8` responses.
279 /// - [`Vec<u8>`], [`&'static [u8]`](std::primitive::u8),
280 /// [`Cow<'static, [u8]>`](std::borrow::Cow) and [`Bytes`] for `application/octet-stream` responses.
281 /// - [`Json`](crate::response::body::Json) for `application/json` responses.
282 /// - [`Html`](crate::response::body::Html) for `text/html; charset=utf-8` responses.
283 ///
284 /// Check out the [`body`](super::body) sub-module for an exhaustive list.
285 ///
286 /// # Raw body
287 ///
288 /// If you don't want Pavex to automatically set the `Content-Type` header,
289 /// you might want to use [`Response::set_raw_body`] instead.
290 pub fn set_typed_body<NewBody>(self, body: NewBody) -> Response
291 where
292 NewBody: TypedBody,
293 <<NewBody as TypedBody>::Body as RawBody>::Error:
294 Into<Box<dyn std::error::Error + Send + Sync>>,
295 {
296 let (mut head, _) = self.inner.into_parts();
297 head.headers.insert(CONTENT_TYPE, body.content_type());
298 http::Response::from_parts(head, ResponseBody::new(body.body())).into()
299 }
300
301 /// Set the body of the [`Response`] to the given value, without setting
302 /// the `Content-Type` header.
303 ///
304 /// This method should only be used if you need fine-grained control over
305 /// the `Content-Type` header or the body type. In all other circumstances, use
306 /// [`set_typed_body`](Response::set_typed_body).
307 ///
308 /// # Example
309 ///
310 /// ```rust
311 /// use pavex::Response;
312 /// use pavex::response::body::raw::{Bytes, Full};
313 /// use pavex::http::header::CONTENT_TYPE;
314 ///
315 /// let raw_body: Full<Bytes> = Full::new("Hello, world!".into());
316 /// let response = Response::ok().set_raw_body(raw_body);
317 ///
318 /// // The `Content-Type` header is not set automatically
319 /// // when using `set_raw_body`.
320 /// assert_eq!(response.headers().get(CONTENT_TYPE), None);
321 /// ```
322 pub fn set_raw_body<NewBody>(self, body: NewBody) -> Response
323 where
324 NewBody: RawBody<Data = Bytes> + Send + 'static,
325 <NewBody as RawBody>::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
326 {
327 let (head, _) = self.inner.into_parts();
328 http::Response::from_parts(head, ResponseBody::new(body)).into()
329 }
330
331 /// Get a mutable reference to the [`Response`] body.
332 pub fn body_mut(&mut self) -> &mut ResponseBody {
333 self.inner.body_mut()
334 }
335
336 /// Get a mutable reference to the [`Response`] headers.
337 ///
338 /// # Example
339 ///
340 /// ```rust
341 /// use pavex::Response;
342 /// use pavex::http::{header::CONTENT_TYPE, HeaderValue};
343 /// use mime::TEXT_PLAIN_UTF_8;
344 ///
345 /// let mut response = Response::ok();
346 ///
347 /// // Get a mutable reference to the headers.
348 /// let headers = response.headers_mut();
349 ///
350 /// // Insert a header.
351 /// let value = HeaderValue::from_static(TEXT_PLAIN_UTF_8.as_ref());
352 /// headers.insert(CONTENT_TYPE, value);
353 ///
354 /// assert_eq!(headers.len(), 1);
355 ///
356 /// // Remove a header.
357 /// headers.remove(CONTENT_TYPE);
358 ///
359 /// assert!(headers.is_empty());
360 /// ```
361 pub fn headers_mut(&mut self) -> &mut crate::http::HeaderMap {
362 self.inner.headers_mut()
363 }
364}
365
366impl Response {
367 /// Get a reference to the [`Response`] status code.
368 ///
369 /// # Example
370 ///
371 /// ```rust
372 /// use pavex::{http::StatusCode, Response};
373 ///
374 /// let response = Response::bad_request();
375 /// assert_eq!(response.status(), StatusCode::BAD_REQUEST);
376 /// ```
377 ///
378 /// # Mutation
379 ///
380 /// Check out [`Response::set_status`] if you need to modify the
381 /// status code of the [`Response`].
382 pub fn status(&self) -> StatusCode {
383 self.inner.status()
384 }
385
386 /// Get a reference to the version of the HTTP protocol used by the [`Response`].
387 ///
388 /// # Example
389 ///
390 /// ```rust
391 /// use pavex::http::Version;
392 /// use pavex::Response;
393 ///
394 /// let mut response = Response::ok();
395 /// // By default, the HTTP version is HTTP/1.1.
396 /// assert_eq!(response.version(), Version::HTTP_11);
397 /// ```
398 ///
399 /// # Mutation
400 ///
401 /// Check out [`Response::set_version`] if you need to modify the
402 /// HTTP protocol version used by the [`Response`].
403 pub fn version(&self) -> crate::http::Version {
404 self.inner.version()
405 }
406
407 /// Get a reference to the [`Response`] headers.
408 ///
409 /// # Example
410 ///
411 /// ```rust
412 /// use pavex::http::{header::{HOST, SERVER}, HeaderValue};
413 /// use pavex::Response;
414 ///
415 /// let response = Response::ok()
416 /// .append_header(HOST, HeaderValue::from_static("world"))
417 /// .append_header(HOST, HeaderValue::from_static("earth"))
418 /// .insert_header(SERVER, HeaderValue::from_static("Pavex"));
419 ///
420 /// let headers = response.headers();
421 /// assert_eq!(headers.len(), 3);
422 ///
423 /// let host_values: Vec<_> = response.headers().get_all("host").iter().collect();
424 /// assert_eq!(host_values.len(), 2);
425 /// assert_eq!(host_values[0], "world");
426 /// assert_eq!(host_values[1], "earth");
427 ///
428 /// assert_eq!(headers[SERVER], "Pavex");
429 /// ```
430 ///
431 /// # Mutation
432 ///
433 /// If you need to modify the [`Response`] headers, check out:
434 ///
435 /// - [`Response::append_header`]
436 /// - [`Response::insert_header`]
437 /// - [`Response::headers_mut`]
438 pub fn headers(&self) -> &crate::http::HeaderMap {
439 self.inner.headers()
440 }
441
442 /// Get a reference to the [`Response`] body.
443 ///
444 /// # Mutation
445 ///
446 /// If you need to modify the [`Response`] body, check out:
447 ///
448 /// - [`Response::set_typed_body`]
449 /// - [`Response::set_raw_body`]
450 /// - [`Response::body_mut`]
451 pub fn body(&self) -> &ResponseBody {
452 self.inner.body()
453 }
454}
455
456impl Response {
457 /// Break down the [`Response`] into its two components: the [`ResponseHead`]
458 /// and the body.
459 ///
460 /// This method consumes the [`Response`].
461 ///
462 /// # Related
463 ///
464 /// You can use [`Response::from_parts`] to reconstruct a [`Response`] from
465 /// a [`ResponseHead`] and a body.
466 pub fn into_parts(self) -> (ResponseHead, ResponseBody) {
467 let (head, body) = self.inner.into_parts();
468 (head.into(), body)
469 }
470
471 /// Build a [`Response`] from its two components: the [`ResponseHead`]
472 /// and the body.
473 ///
474 /// # Related
475 ///
476 /// You can use [`Response::into_parts`] to decompose a [`Response`] from
477 /// a [`ResponseHead`] and a body.
478 pub fn from_parts(head: ResponseHead, body: ResponseBody) -> Self {
479 Self {
480 inner: http::Response::from_parts(head.into(), body),
481 }
482 }
483}
484
485impl<Body> From<http::Response<Body>> for Response
486where
487 Body: Send + RawBody<Data = Bytes> + 'static,
488 <Body as RawBody>::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
489{
490 fn from(inner: http::Response<Body>) -> Self {
491 let (head, body) = inner.into_parts();
492 let inner = http::Response::from_parts(head, ResponseBody::new(body));
493 Self { inner }
494 }
495}
496
497impl From<Response> for http::Response<ResponseBody> {
498 fn from(res: Response) -> Self {
499 res.inner
500 }
501}
502
503impl From<ResponseHead> for http::response::Parts {
504 fn from(head: ResponseHead) -> Self {
505 let ResponseHead {
506 status,
507 version,
508 headers,
509 } = head;
510 // Is there no better way to do create a new `Parts` instance?
511 let (mut parts, _) = http::response::Response::builder()
512 .body(Empty::<()>::new())
513 .unwrap()
514 .into_parts();
515 parts.status = status;
516 parts.version = version;
517 parts.headers = headers;
518 parts
519 }
520}
521
522impl From<http::response::Parts> for ResponseHead {
523 fn from(parts: http::response::Parts) -> Self {
524 let http::response::Parts {
525 status,
526 version,
527 headers,
528 ..
529 } = parts;
530 Self {
531 status,
532 version,
533 headers,
534 }
535 }
536}
537
538macro_rules! shorthand {
539 ($name:ident) => {
540 paste::paste! {
541 #[doc = "Start building a new [`Response`] with [`" $name "`](`StatusCode::" $name "`) as status code."]
542 pub fn [<$name:lower>]() -> Response {
543 Response::new(StatusCode::[<$name>])
544 }
545 }
546 };
547}
548
549/// Shorthand for building a new [`Response`] using a well-known status code.
550impl Response {
551 /// Start building a new [`Response`] with [`CONTINUE`](StatusCode::CONTINUE) as status code.
552 // This is special-cased because `continue` is a keyword in Rust.
553 pub fn continue_() -> Response {
554 Response::new(StatusCode::CONTINUE)
555 }
556
557 // 2xx
558 shorthand!(SWITCHING_PROTOCOLS);
559 shorthand!(PROCESSING);
560 shorthand!(OK);
561 shorthand!(CREATED);
562 shorthand!(ACCEPTED);
563 shorthand!(NON_AUTHORITATIVE_INFORMATION);
564
565 shorthand!(NO_CONTENT);
566 shorthand!(RESET_CONTENT);
567 shorthand!(PARTIAL_CONTENT);
568 shorthand!(MULTI_STATUS);
569 shorthand!(ALREADY_REPORTED);
570
571 // 3xx
572 shorthand!(MULTIPLE_CHOICES);
573 shorthand!(MOVED_PERMANENTLY);
574 shorthand!(FOUND);
575 shorthand!(SEE_OTHER);
576 shorthand!(NOT_MODIFIED);
577 shorthand!(USE_PROXY);
578 shorthand!(TEMPORARY_REDIRECT);
579 shorthand!(PERMANENT_REDIRECT);
580
581 // 4xx
582 shorthand!(BAD_REQUEST);
583 shorthand!(NOT_FOUND);
584 shorthand!(UNAUTHORIZED);
585 shorthand!(PAYMENT_REQUIRED);
586 shorthand!(FORBIDDEN);
587 shorthand!(METHOD_NOT_ALLOWED);
588 shorthand!(NOT_ACCEPTABLE);
589 shorthand!(PROXY_AUTHENTICATION_REQUIRED);
590 shorthand!(REQUEST_TIMEOUT);
591 shorthand!(CONFLICT);
592 shorthand!(GONE);
593 shorthand!(LENGTH_REQUIRED);
594 shorthand!(PRECONDITION_FAILED);
595 shorthand!(PRECONDITION_REQUIRED);
596 shorthand!(PAYLOAD_TOO_LARGE);
597 shorthand!(URI_TOO_LONG);
598 shorthand!(UNSUPPORTED_MEDIA_TYPE);
599 shorthand!(RANGE_NOT_SATISFIABLE);
600 shorthand!(EXPECTATION_FAILED);
601 shorthand!(UNPROCESSABLE_ENTITY);
602 shorthand!(TOO_MANY_REQUESTS);
603 shorthand!(REQUEST_HEADER_FIELDS_TOO_LARGE);
604 shorthand!(UNAVAILABLE_FOR_LEGAL_REASONS);
605
606 // 5xx
607 shorthand!(INTERNAL_SERVER_ERROR);
608 shorthand!(NOT_IMPLEMENTED);
609 shorthand!(BAD_GATEWAY);
610 shorthand!(SERVICE_UNAVAILABLE);
611 shorthand!(GATEWAY_TIMEOUT);
612 shorthand!(HTTP_VERSION_NOT_SUPPORTED);
613 shorthand!(VARIANT_ALSO_NEGOTIATES);
614 shorthand!(INSUFFICIENT_STORAGE);
615 shorthand!(LOOP_DETECTED);
616}