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
37#[derive(Clone, Debug)]
38pub struct Response<B = EmptyBody> {
39    pub metadata: ResponseMetadata,
40    pub body: B,
41}
42
43impl<B> Response<B> {
44    pub fn new(status: Status, body: B) -> Self {
45        Self {
46            metadata: ResponseMetadata::new(status),
47            body,
48        }
49    }
50    pub fn from_parts(metadata: ResponseMetadata, body: B) -> Self {
51        Self { metadata, body }
52    }
53    pub fn into_parts(self) -> (ResponseMetadata, B) {
54        (self.metadata, self.body)
55    }
56    /// Move the body through `map` without cloning or rebuilding the metadata.
57    pub fn map_body<T>(self, map: impl FnOnce(B) -> T) -> Response<T> {
58        Response {
59            metadata: self.metadata,
60            body: map(self.body),
61        }
62    }
63    /// Replace and drop the previous body. This does not drain it or establish
64    /// asynchronous exchange cleanup; an I/O driver retains that responsibility.
65    pub fn with_body<T>(self, body: T) -> Response<T> {
66        self.map_body(|_| body)
67    }
68}
69
70impl Response<EmptyBody> {
71    pub fn empty(status: Status) -> Self {
72        Self::new(status, EmptyBody)
73    }
74}
75
76impl<B> Deref for Response<B> {
77    type Target = ResponseMetadata;
78    fn deref(&self) -> &Self::Target {
79        &self.metadata
80    }
81}
82impl<B> DerefMut for Response<B> {
83    fn deref_mut(&mut self) -> &mut Self::Target {
84        &mut self.metadata
85    }
86}
87
88impl<B: crate::body::ReadableBody> Response<B> {
89    /// Read an owned body frame. Received fields stay in the frame; the caller
90    /// can forward them or explicitly append them with `HttpHeaders::append_trailers`.
91    pub async fn read(
92        &mut self,
93        cancellation: &base_scheduler::Cancellation,
94    ) -> Result<crate::body::BodyFrame<B::Data>, B::Error> {
95        self.body.read(cancellation).await
96    }
97}