Skip to main content

base_http/
response.rs

1// Released under the MIT License.
2// Copyright, 2026, by Samuel Williams.
3
4use crate::{Fields, Protocol, Status, Version, body::EmptyBody};
5use std::ops::{Deref, DerefMut};
6
7/// Informational and final responses share the same metadata. Protocol/responder
8/// capabilities sequence heads; upgrades and CONNECT use bidirectional bodies,
9/// with resource lifecycle retained by the exchange/driver.
10#[derive(Clone, Debug)]
11pub struct ResponseMetadata {
12    pub status: Status,
13    pub version: Option<Version>,
14    pub fields: Fields,
15    /// Declared representation length, independent of whether this message has
16    /// readable data (for example HEAD and 304 responses).
17    pub content_length: Option<u64>,
18    /// Outgoing connection reuse preference. Received hop-specific decisions
19    /// are retained by the protocol exchange, rather than forwarded as fields.
20    pub persistent: Option<bool>,
21    pub protocol: Option<Protocol>,
22}
23
24impl ResponseMetadata {
25    pub fn new(status: Status) -> Self {
26        Self {
27            status,
28            version: None,
29            fields: Fields::new(),
30            content_length: None,
31            persistent: None,
32            protocol: None,
33        }
34    }
35
36    /// The declared `content_length`, unless a body of the given known length contradicts it, as after replacing the body. A codec sending this response's data uses it; a response without data, to HEAD or with 204 or 304, is described by `content_length` itself, so every protocol resolves the same length.
37    pub fn declared_length(&self, body: Option<u64>) -> Option<u64> {
38        self.content_length
39            .filter(|&declared| body.is_none_or(|body| body == declared))
40    }
41}
42
43#[derive(Clone, Debug)]
44pub struct Response<B = EmptyBody> {
45    pub metadata: ResponseMetadata,
46    pub body: B,
47}
48
49impl<B> Response<B> {
50    pub fn new(status: Status, body: B) -> Self {
51        Self {
52            metadata: ResponseMetadata::new(status),
53            body,
54        }
55    }
56    pub fn from_parts(metadata: ResponseMetadata, body: B) -> Self {
57        Self { metadata, body }
58    }
59    pub fn into_parts(self) -> (ResponseMetadata, B) {
60        (self.metadata, self.body)
61    }
62    /// Move the body through `map` without cloning or rebuilding the metadata.
63    pub fn map_body<T>(self, map: impl FnOnce(B) -> T) -> Response<T> {
64        Response {
65            metadata: self.metadata,
66            body: map(self.body),
67        }
68    }
69    /// Replace and drop the previous body. This does not drain it or establish
70    /// asynchronous exchange cleanup; an I/O driver retains that responsibility.
71    pub fn with_body<T>(self, body: T) -> Response<T> {
72        self.map_body(|_| body)
73    }
74}
75
76impl Response<EmptyBody> {
77    pub fn empty(status: Status) -> Self {
78        Self::new(status, EmptyBody)
79    }
80}
81
82impl<B> Deref for Response<B> {
83    type Target = ResponseMetadata;
84    fn deref(&self) -> &Self::Target {
85        &self.metadata
86    }
87}
88impl<B> DerefMut for Response<B> {
89    fn deref_mut(&mut self) -> &mut Self::Target {
90        &mut self.metadata
91    }
92}
93
94impl<B: crate::body::ReadableBody> Response<B> {
95    /// The length of the data this response sends: the body's own length when it knows one, because the body is what is sent, and otherwise the declaration [`ResponseMetadata::declared_length`] keeps. A response without data, to HEAD or with 204 or 304, is instead described by `content_length`, and a tunnel has no length.
96    pub fn length(&self) -> Option<u64> {
97        let body = self.body.length();
98        body.or(self.metadata.declared_length(body))
99    }
100
101    /// Read an owned body frame. Received fields stay in the frame; the caller
102    /// can forward them or explicitly append them with `HttpHeaders::append_trailers`.
103    pub async fn read(
104        &mut self,
105        cancellation: &base_scheduler::Cancellation,
106    ) -> Result<crate::body::BodyFrame<B::Data>, B::Error> {
107        self.body.read(cancellation).await
108    }
109}