Skip to main content

tower_http/set_header/
mod.rs

1//! Middleware for setting headers on requests and responses.
2//!
3//! See [request] and [response] for more details.
4
5use std::fmt;
6
7use http::{header::HeaderName, HeaderMap, HeaderValue, Request, Response};
8
9pub mod request;
10pub mod response;
11
12#[doc(inline)]
13pub use self::{
14    request::{SetRequestHeader, SetRequestHeaderLayer},
15    response::{SetResponseHeader, SetResponseHeaderLayer},
16};
17
18/// Trait for producing header values.
19///
20/// Used by [`SetRequestHeader`] and [`SetResponseHeader`].
21///
22/// This trait is implemented for closures with the correct type signature. Typically users will
23/// not have to implement this trait for their own types.
24///
25/// It is also implemented directly for [`HeaderValue`]. When a fixed header value should be added
26/// to all responses, it can be supplied directly to the middleware.
27pub trait MakeHeaderValue<T> {
28    /// Try to create a header value from the request or response.
29    fn make_header_value(&mut self, message: &T) -> Option<HeaderValue>;
30}
31
32impl<F, T> MakeHeaderValue<T> for F
33where
34    F: FnMut(&T) -> Option<HeaderValue>,
35{
36    fn make_header_value(&mut self, message: &T) -> Option<HeaderValue> {
37        self(message)
38    }
39}
40
41impl<T> MakeHeaderValue<T> for HeaderValue {
42    fn make_header_value(&mut self, _message: &T) -> Option<HeaderValue> {
43        Some(self.clone())
44    }
45}
46
47impl<T> MakeHeaderValue<T> for Option<HeaderValue> {
48    fn make_header_value(&mut self, _message: &T) -> Option<HeaderValue> {
49        self.clone()
50    }
51}
52
53#[derive(Debug, Clone, Copy)]
54enum InsertHeaderMode {
55    Override,
56    Append,
57    IfNotPresent,
58}
59
60impl InsertHeaderMode {
61    fn apply<T, M>(self, header_name: &HeaderName, target: &mut T, make: &mut M)
62    where
63        T: Headers,
64        M: MakeHeaderValue<T>,
65    {
66        match self {
67            InsertHeaderMode::Override => {
68                if let Some(value) = make.make_header_value(target) {
69                    target.headers_mut().insert(header_name.clone(), value);
70                }
71            }
72            InsertHeaderMode::IfNotPresent => {
73                if !target.headers().contains_key(header_name) {
74                    if let Some(value) = make.make_header_value(target) {
75                        target.headers_mut().insert(header_name.clone(), value);
76                    }
77                }
78            }
79            InsertHeaderMode::Append => {
80                if let Some(value) = make.make_header_value(target) {
81                    target.headers_mut().append(header_name.clone(), value);
82                }
83            }
84        }
85    }
86}
87
88trait Headers {
89    fn headers(&self) -> &HeaderMap;
90
91    fn headers_mut(&mut self) -> &mut HeaderMap;
92}
93
94impl<B> Headers for Request<B> {
95    fn headers(&self) -> &HeaderMap {
96        Request::headers(self)
97    }
98
99    fn headers_mut(&mut self) -> &mut HeaderMap {
100        Request::headers_mut(self)
101    }
102}
103
104impl<B> Headers for Response<B> {
105    fn headers(&self) -> &HeaderMap {
106        Response::headers(self)
107    }
108
109    fn headers_mut(&mut self) -> &mut HeaderMap {
110        Response::headers_mut(self)
111    }
112}
113
114/// A trait that combines MakeHeaderValue and Clone capability for trait objects.
115trait CloneableMakeHeaderValue<T>: MakeHeaderValue<T> + Send + Sync {
116    fn clone_box(&self) -> Box<dyn CloneableMakeHeaderValue<T>>;
117}
118
119impl<T, M> CloneableMakeHeaderValue<T> for M
120where
121    M: MakeHeaderValue<T> + Clone + Send + Sync + 'static,
122{
123    fn clone_box(&self) -> Box<dyn CloneableMakeHeaderValue<T>> {
124        Box::new(self.clone())
125    }
126}
127
128/// A "Bridge" struct that allows for trait object-based header value generation.
129struct BoxedMakeHeaderValue<T>(Box<dyn CloneableMakeHeaderValue<T>>);
130
131impl<T> BoxedMakeHeaderValue<T> {
132    /// Create a new BoxedMakeHeaderValue from any maker that implements MakeHeaderValue and Clone.
133    fn new<M>(maker: M) -> Self
134    where
135        M: MakeHeaderValue<T> + Clone + Send + Sync + 'static,
136    {
137        Self(Box::new(maker))
138    }
139}
140
141impl<T> Clone for BoxedMakeHeaderValue<T> {
142    fn clone(&self) -> Self {
143        Self(self.0.clone_box())
144    }
145}
146
147impl<T> MakeHeaderValue<T> for BoxedMakeHeaderValue<T> {
148    fn make_header_value(&mut self, message: &T) -> Option<HeaderValue> {
149        self.0.make_header_value(message)
150    }
151}
152
153impl<T> fmt::Debug for BoxedMakeHeaderValue<T> {
154    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155        f.debug_struct("BoxedMakeHeaderValue").finish()
156    }
157}
158
159/// Metadata describing a request or response header to be set.
160#[derive(Clone, Debug)]
161pub struct HeaderMetadata<T> {
162    /// The name of the header to set.
163    header_name: HeaderName,
164    /// The value or value factory for the header.
165    make: BoxedMakeHeaderValue<T>,
166}
167
168impl<T> HeaderMetadata<T> {
169    /// Create a new HeaderMetadata with the given header name and value factory.
170    fn new<M: MakeHeaderValue<T> + Clone + 'static + Send + Sync>(
171        header_name: HeaderName,
172        make: M,
173    ) -> Self {
174        Self {
175            header_name,
176            make: BoxedMakeHeaderValue::new(make),
177        }
178    }
179
180    /// Convert this metadata into a [`HeaderInsertionConfig`] with the given insertion mode.
181    fn build_config(self, mode: InsertHeaderMode) -> HeaderInsertionConfig<T> {
182        HeaderInsertionConfig {
183            header_name: self.header_name,
184            make: self.make,
185            mode,
186        }
187    }
188}
189
190impl<T, M> From<(HeaderName, M)> for HeaderMetadata<T>
191where
192    M: MakeHeaderValue<T> + Clone + 'static + Send + Sync,
193{
194    fn from((header_name, make): (HeaderName, M)) -> Self {
195        HeaderMetadata::new(header_name, make)
196    }
197}
198
199/// Configuration for inserting a header into a response or request.
200struct HeaderInsertionConfig<T> {
201    header_name: HeaderName,
202    make: BoxedMakeHeaderValue<T>,
203    mode: InsertHeaderMode,
204}
205
206impl<T> Clone for HeaderInsertionConfig<T>
207where
208    BoxedMakeHeaderValue<T>: Clone,
209{
210    fn clone(&self) -> Self {
211        Self {
212            header_name: self.header_name.clone(),
213            make: self.make.clone(),
214            mode: self.mode,
215        }
216    }
217}
218
219impl<T> fmt::Debug for HeaderInsertionConfig<T> {
220    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221        f.debug_struct("HeaderInsertionConfig")
222            .field("header_name", &self.header_name)
223            .field("mode", &self.mode)
224            .field("make", &"BoxedMakeHeaderValue")
225            .finish()
226    }
227}