Skip to main content

base_http/
request.rs

1// Released under the MIT License.
2// Copyright, 2026, by Samuel Williams.
3
4use crate::{Authority, Fields, Method, Path, Protocol, Scheme, Version, body::EmptyBody};
5use std::ops::{Deref, DerefMut};
6
7/// Protocol-independent request metadata. Components are preserved as supplied,
8/// except that schemes and authorities are stored lowercase; drivers validate
9/// target combinations and wire syntax before transmission.
10#[derive(Clone, Debug)]
11pub struct RequestMetadata {
12    pub method: Method,
13    pub scheme: Option<Scheme>,
14    pub authority: Option<Authority>,
15    /// Path and optional query, or the supplied request-target form. `None`
16    /// represents a request such as traditional CONNECT with no path.
17    pub path: Option<Path>,
18    pub version: Option<Version>,
19    pub fields: Fields,
20    /// Declared representation length, independent of whether this message has
21    /// readable data (for example HEAD and 304 responses).
22    pub content_length: Option<u64>,
23    /// Outgoing connection reuse preference. Received hop-specific decisions
24    /// are retained by the protocol exchange, rather than forwarded as fields.
25    pub persistent: Option<bool>,
26    pub protocol: Option<Protocol>,
27}
28
29impl RequestMetadata {
30    pub fn new(method: Method) -> Self {
31        Self {
32            method,
33            scheme: None,
34            authority: None,
35            path: None,
36            version: None,
37            fields: Fields::new(),
38            content_length: None,
39            persistent: None,
40            protocol: None,
41        }
42    }
43
44    /// The declared `content_length`, unless a body of the given known length contradicts it, as after replacing the body. A codec sending this message's data uses it, so every protocol resolves the same length.
45    pub fn declared_length(&self, body: Option<u64>) -> Option<u64> {
46        self.content_length
47            .filter(|&declared| body.is_none_or(|body| body == declared))
48    }
49}
50
51/// A semantic request with an arbitrary body owner. The metadata is also accessible
52/// through dereferencing, e.g. `request.fields` and `request.method`.
53#[derive(Clone, Debug)]
54pub struct Request<B = EmptyBody> {
55    pub metadata: RequestMetadata,
56    pub body: B,
57}
58
59impl<B> Request<B> {
60    pub fn new(method: Method, path: impl Into<Path>, body: B) -> Self {
61        let mut metadata = RequestMetadata::new(method);
62        metadata.path = Some(path.into());
63        Self { metadata, body }
64    }
65
66    pub fn from_parts(metadata: RequestMetadata, body: B) -> Self {
67        Self { metadata, body }
68    }
69    pub fn into_parts(self) -> (RequestMetadata, B) {
70        (self.metadata, self.body)
71    }
72    /// Move the body through `map` without cloning or rebuilding the metadata.
73    pub fn map_body<T>(self, map: impl FnOnce(B) -> T) -> Request<T> {
74        Request {
75            metadata: self.metadata,
76            body: map(self.body),
77        }
78    }
79    /// Replace and drop the previous body. This does not drain it or establish
80    /// asynchronous exchange cleanup; an I/O driver retains that responsibility.
81    pub fn with_body<T>(self, body: T) -> Request<T> {
82        self.map_body(|_| body)
83    }
84    pub fn is_head(&self) -> bool {
85        self.method == Method::HEAD
86    }
87    pub fn is_connect(&self) -> bool {
88        self.method == Method::CONNECT
89    }
90}
91
92impl Request<EmptyBody> {
93    pub fn get(path: impl Into<Path>) -> Self {
94        Self::new(Method::GET, path, EmptyBody)
95    }
96}
97
98impl<B> Deref for Request<B> {
99    type Target = RequestMetadata;
100    fn deref(&self) -> &Self::Target {
101        &self.metadata
102    }
103}
104impl<B> DerefMut for Request<B> {
105    fn deref_mut(&mut self) -> &mut Self::Target {
106        &mut self.metadata
107    }
108}
109
110impl<B: crate::body::ReadableBody> Request<B> {
111    /// The length of the data this request sends: the body's own length when it knows one, because the body is what is sent, and otherwise the declaration [`RequestMetadata::declared_length`] keeps.
112    pub fn length(&self) -> Option<u64> {
113        let body = self.body.length();
114        body.or(self.metadata.declared_length(body))
115    }
116
117    /// Read an owned body frame. Received fields stay in the frame; the caller
118    /// can forward them or explicitly append them with `HttpHeaders::append_trailers`.
119    pub async fn read(
120        &mut self,
121        cancellation: &base_scheduler::Cancellation,
122    ) -> Result<crate::body::BodyFrame<B::Data>, B::Error> {
123        self.body.read(cancellation).await
124    }
125}