Skip to main content

xitca_web/handler/types/
header.rs

1//! type extractor for header value
2
3use core::{fmt, ops::Deref};
4
5use crate::{
6    body::ResponseBody,
7    context::WebContext,
8    error::{Error, HeaderNotFound},
9    handler::{FromRequest, Responder},
10    http::{
11        WebResponse,
12        header::{self, HeaderMap, HeaderName, HeaderValue},
13    },
14};
15
16macro_rules! const_header_name {
17    ($n:expr ;) => {};
18    ($n:expr ; $i: ident $(, $rest:ident)*) => {
19        pub const $i: usize = $n;
20        const_header_name!($n + 1; $($rest),*);
21    };
22    ($($i:ident), +) => { const_header_name!(0; $($i),*); };
23}
24
25macro_rules! map_to_header_name {
26    ($($i:ident), +) => {
27        const fn map_to_header_name<const HEADER_NAME: usize>() -> header::HeaderName {
28            match HEADER_NAME  {
29            $(
30                $i => header::$i,
31            )*
32                _ => unreachable!()
33            }
34        }
35    }
36}
37
38macro_rules! const_header_name_impl {
39    ($($i:ident), +) => {
40        const_header_name!($($i), +);
41        map_to_header_name!($($i), +);
42    }
43}
44
45const_header_name_impl!(
46    // common request headers.
47    ACCEPT,
48    ACCEPT_ENCODING,
49    ACCEPT_LANGUAGE,
50    AUTHORIZATION,
51    CACHE_CONTROL,
52    CONNECTION,
53    CONTENT_TYPE,
54    CONTENT_LENGTH,
55    COOKIE,
56    DATE,
57    HOST,
58    ORIGIN,
59    REFERER,
60    USER_AGENT,
61    // conditional and partial request.
62    RANGE,
63    IF_MATCH,
64    IF_NONE_MATCH,
65    IF_MODIFIED_SINCE,
66    IF_UNMODIFIED_SINCE,
67    IF_RANGE,
68    // request body description.
69    CONTENT_DISPOSITION,
70    CONTENT_ENCODING,
71    CONTENT_LANGUAGE,
72    CONTENT_RANGE,
73    TRANSFER_ENCODING,
74    TE,
75    EXPECT,
76    // websocket upgrade.
77    UPGRADE,
78    SEC_WEBSOCKET_KEY,
79    SEC_WEBSOCKET_VERSION,
80    SEC_WEBSOCKET_PROTOCOL,
81    SEC_WEBSOCKET_EXTENSIONS,
82    // proxy, cors preflight and misc.
83    FORWARDED,
84    VIA,
85    PROXY_AUTHORIZATION,
86    MAX_FORWARDS,
87    FROM,
88    PRAGMA,
89    DNT,
90    UPGRADE_INSECURE_REQUESTS,
91    ACCESS_CONTROL_REQUEST_METHOD,
92    ACCESS_CONTROL_REQUEST_HEADERS
93);
94
95/// typed header extractor.
96///
97/// on success HeaderRef will be received in handler function where it can be dereference to
98/// [HeaderValue] type.
99///
100/// on failure [HeaderNotFound] error would be returned which would generate a "400 BadRequest"
101/// http response.
102///
103/// # Example
104/// ```rust
105/// use xitca_web::{
106///     handler::{
107///         handler_service,
108///         header::{self, HeaderRef}
109///     },
110///     App
111/// };
112/// # use xitca_web::WebContext;
113///
114/// // a handle function expecting content_type header.
115/// async fn handle(header: HeaderRef<'_, { header::CONTENT_TYPE }>) -> &'static str {
116///     // dereference HeaderRef to operate on HeaderValue.
117///     println!("{:?}", header.to_str());
118///     ""
119/// }
120///
121/// App::new()
122///     .at("/", handler_service(handle))
123///     # .at("/nah", handler_service(|_: &WebContext<'_>| async { "for type infer" }));
124/// ```
125pub struct HeaderRef<'a, const HEADER_NAME: usize>(&'a HeaderValue);
126
127impl<const HEADER_NAME: usize> fmt::Debug for HeaderRef<'_, HEADER_NAME> {
128    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129        f.debug_struct("Header")
130            .field("name", &map_to_header_name::<HEADER_NAME>())
131            .field("value", &self.0)
132            .finish()
133    }
134}
135
136impl<const HEADER_NAME: usize> Deref for HeaderRef<'_, HEADER_NAME> {
137    type Target = HeaderValue;
138
139    fn deref(&self) -> &Self::Target {
140        self.0
141    }
142}
143
144impl<'a, 'r, C, B, const HEADER_NAME: usize> FromRequest<'a, WebContext<'r, C, B>> for HeaderRef<'a, HEADER_NAME> {
145    type Type<'b> = HeaderRef<'b, HEADER_NAME>;
146    type Error = Error;
147
148    #[inline]
149    async fn from_request(ctx: &'a WebContext<'r, C, B>) -> Result<Self, Self::Error> {
150        let name = map_to_header_name::<HEADER_NAME>();
151        ctx.req()
152            .headers()
153            .get(&name)
154            .map(HeaderRef)
155            .ok_or_else(|| Error::from_service(HeaderNotFound(name)))
156    }
157}
158
159impl<'a, 'r, C, B> FromRequest<'a, WebContext<'r, C, B>> for &'a HeaderMap {
160    type Type<'b> = &'b HeaderMap;
161    type Error = Error;
162
163    #[inline]
164    async fn from_request(ctx: &'a WebContext<'r, C, B>) -> Result<Self, Self::Error> {
165        Ok(ctx.req().headers())
166    }
167}
168
169impl<'a, 'r, C, B> FromRequest<'a, WebContext<'r, C, B>> for HeaderMap {
170    type Type<'b> = HeaderMap;
171    type Error = Error;
172
173    #[inline]
174    async fn from_request(ctx: &'a WebContext<'r, C, B>) -> Result<Self, Self::Error> {
175        Ok(ctx.req().headers().clone())
176    }
177}
178
179impl<'r, C, B> Responder<WebContext<'r, C, B>> for (HeaderName, HeaderValue) {
180    type Response = WebResponse;
181    type Error = Error;
182
183    async fn respond(self, ctx: WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
184        let res = ctx.into_response(ResponseBody::empty());
185        Responder::<WebContext<'r, C, B>>::map(self, res)
186    }
187
188    fn map(self, mut res: Self::Response) -> Result<Self::Response, Self::Error> {
189        res.headers_mut().append(self.0, self.1);
190        Ok(res)
191    }
192}
193
194impl<'r, C, B, const N: usize> Responder<WebContext<'r, C, B>> for [(HeaderName, HeaderValue); N] {
195    type Response = WebResponse;
196    type Error = Error;
197
198    async fn respond(self, ctx: WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
199        let res = ctx.into_response(ResponseBody::empty());
200        Responder::<WebContext<'r, C, B>>::map(self, res)
201    }
202
203    fn map(self, mut res: Self::Response) -> Result<Self::Response, Self::Error> {
204        for (k, v) in self {
205            res.headers_mut().append(k, v);
206        }
207        Ok(res)
208    }
209}
210
211impl<'r, C, B> Responder<WebContext<'r, C, B>> for Vec<(HeaderName, HeaderValue)> {
212    type Response = WebResponse;
213    type Error = Error;
214
215    async fn respond(self, ctx: WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
216        let res = ctx.into_response(ResponseBody::empty());
217        Responder::<WebContext<'r, C, B>>::map(self, res)
218    }
219
220    fn map(self, mut res: Self::Response) -> Result<Self::Response, Self::Error> {
221        for (k, v) in self {
222            res.headers_mut().append(k, v);
223        }
224        Ok(res)
225    }
226}
227
228impl<'r, C, B> Responder<WebContext<'r, C, B>> for HeaderMap {
229    type Response = WebResponse;
230    type Error = Error;
231
232    async fn respond(self, ctx: WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
233        let res = ctx.into_response(ResponseBody::empty());
234        Responder::<WebContext<'r, C, B>>::map(self, res)
235    }
236
237    fn map(self, mut res: Self::Response) -> Result<Self::Response, Self::Error> {
238        res.headers_mut().extend(self);
239        Ok(res)
240    }
241}
242
243#[cfg(test)]
244mod test {
245    use xitca_unsafe_collection::futures::NowOrPanic;
246
247    use super::*;
248
249    #[test]
250    fn extract_header() {
251        let mut req = WebContext::new_test(());
252        let mut req = req.as_web_ctx();
253        req.req_mut()
254            .headers_mut()
255            .insert(header::HOST, header::HeaderValue::from_static("996"));
256        req.req_mut()
257            .headers_mut()
258            .insert(header::ACCEPT_ENCODING, header::HeaderValue::from_static("251"));
259
260        assert_eq!(
261            HeaderRef::<'_, { super::ACCEPT_ENCODING }>::from_request(&req)
262                .now_or_panic()
263                .unwrap()
264                .deref(),
265            &header::HeaderValue::from_static("251")
266        );
267        assert_eq!(
268            HeaderRef::<'_, { super::HOST }>::from_request(&req)
269                .now_or_panic()
270                .unwrap()
271                .deref(),
272            &header::HeaderValue::from_static("996")
273        );
274    }
275}