volo_http/
request.rs

1//! Request types and utils.
2
3use std::str::FromStr;
4
5use http::{
6    header::{self, HeaderMap, HeaderName},
7    request::Parts,
8    uri::{Scheme, Uri},
9};
10use url::Url;
11
12use crate::body::Body;
13
14/// [`Request`](http::request::Request) with [`Body`] as default body.
15///
16/// [`Body`]: crate::body::Body
17pub type Request<B = Body> = http::request::Request<B>;
18
19/// HTTP header [`X-Forwarded-For`][mdn].
20///
21/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For
22pub const X_FORWARDED_FOR: HeaderName = HeaderName::from_static("x-forwarded-for");
23
24/// HTTP header `X-Real-IP`.
25pub const X_REAL_IP: HeaderName = HeaderName::from_static("x-real-ip");
26
27/// Utilities of [`http::request::Parts`] and [`http::Request`].
28pub trait RequestPartsExt: sealed::SealedRequestPartsExt {
29    /// Get host name of the request URI from header `Host`.
30    fn host(&self) -> Option<&str>;
31    /// Get URL of the request URI.
32    fn url(&self) -> Option<url::Url>;
33}
34
35mod sealed {
36    pub trait SealedRequestPartsExt {
37        fn headers(&self) -> &http::header::HeaderMap;
38        fn uri(&self) -> &http::uri::Uri;
39        fn extensions(&self) -> &http::Extensions;
40    }
41}
42
43impl sealed::SealedRequestPartsExt for Parts {
44    fn headers(&self) -> &HeaderMap {
45        &self.headers
46    }
47    fn uri(&self) -> &Uri {
48        &self.uri
49    }
50    fn extensions(&self) -> &http::Extensions {
51        &self.extensions
52    }
53}
54
55impl<B> sealed::SealedRequestPartsExt for Request<B> {
56    fn headers(&self) -> &HeaderMap {
57        self.headers()
58    }
59    fn uri(&self) -> &Uri {
60        self.uri()
61    }
62    fn extensions(&self) -> &http::Extensions {
63        self.extensions()
64    }
65}
66
67impl<T> RequestPartsExt for T
68where
69    T: sealed::SealedRequestPartsExt,
70{
71    fn host(&self) -> Option<&str> {
72        simdutf8::basic::from_utf8(self.headers().get(header::HOST)?.as_bytes()).ok()
73    }
74
75    fn url(&self) -> Option<Url> {
76        let host = self.host()?;
77        let uri = self.uri();
78        let path = uri.path();
79        let scheme = if let Some(scheme) = uri.scheme() {
80            scheme
81        } else if let Some(scheme) = self.extensions().get::<Scheme>() {
82            scheme
83        } else {
84            &Scheme::HTTP
85        };
86
87        Url::from_str(&format!("{scheme}://{host}{path}")).ok()
88    }
89}