micro_http/protocol/
request.rs1use std::convert::Into;
8
9use http::request::Parts;
10use http::{HeaderMap, Method, Request, Uri, Version};
11
12#[derive(Debug)]
20pub struct RequestHeader {
21 inner: Request<()>,
22}
23
24impl AsRef<Request<()>> for RequestHeader {
25 fn as_ref(&self) -> &Request<()> {
26 &self.inner
27 }
28}
29
30impl AsMut<Request<()>> for RequestHeader {
31 fn as_mut(&mut self) -> &mut Request<()> {
32 &mut self.inner
33 }
34}
35
36impl RequestHeader {
37 pub fn into_inner(self) -> Request<()> {
39 self.inner
40 }
41
42 pub fn body<T>(self, body: T) -> Request<T> {
46 self.inner.map(|_| body)
47 }
48
49 pub fn method(&self) -> &Method {
51 self.inner.method()
52 }
53
54 pub fn uri(&self) -> &Uri {
56 self.inner.uri()
57 }
58
59 pub fn version(&self) -> Version {
61 self.inner.version()
62 }
63
64 pub fn headers(&self) -> &HeaderMap {
66 self.inner.headers()
67 }
68
69 pub fn need_body(&self) -> bool {
78 !matches!(
79 self.method(),
80 &Method::GET | &Method::HEAD | &Method::DELETE | &Method::OPTIONS | &Method::CONNECT | &Method::TRACE | &Method::PATCH
81 )
82 }
83}
84
85impl From<Parts> for RequestHeader {
87 #[inline]
88 fn from(parts: Parts) -> Self {
89 Self { inner: Request::from_parts(parts, ()) }
90 }
91}
92
93impl From<Request<()>> for RequestHeader {
95 #[inline]
96 fn from(inner: Request<()>) -> Self {
97 Self { inner }
98 }
99}
100
101impl<'headers, 'buf> From<httparse::Request<'headers, 'buf>> for RequestHeader {
110 fn from(req: httparse::Request<'headers, 'buf>) -> Self {
111 let mut builder =
112 Request::builder().method(req.method.unwrap()).uri(req.path.unwrap()).version(U8Wrapper(req.version.unwrap()).into());
113
114 builder.headers_mut().unwrap().reserve(req.headers.len());
115 for header in req.headers.iter() {
116 builder = builder.header(header.name, header.value)
117 }
118
119 RequestHeader { inner: builder.body(()).unwrap() }
120 }
121}
122
123struct U8Wrapper(u8);
125
126impl From<U8Wrapper> for Version {
127 fn from(value: U8Wrapper) -> Self {
128 match value.0 {
129 1 => Version::HTTP_11,
130 0 => Version::HTTP_10,
131 _ => Version::HTTP_09,
133 }
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use std::mem::MaybeUninit;
140
141 use http::{HeaderValue, Method, Version};
142 use indoc::indoc;
143
144 use super::*;
145
146 #[test]
147 fn from_curl() {
148 let str = indoc! {r##"
149 GET /index.html HTTP/1.1
150 Host: 127.0.0.1:8080
151 User-Agent: curl/7.79.1
152 Accept: */*
153
154 "##};
155
156 let mut parsed_req = httparse::Request::new(&mut []);
157 let mut headers: [MaybeUninit<httparse::Header>; 4] = unsafe { MaybeUninit::uninit().assume_init() };
158
159 parsed_req.parse_with_uninit_headers(str.as_bytes(), &mut headers).unwrap();
160
161 let header: RequestHeader = parsed_req.into();
162
163 assert_eq!(header.method(), &Method::GET);
164 assert_eq!(header.version(), Version::HTTP_11);
165 assert_eq!(header.uri().host(), None);
166 assert_eq!(header.uri().path(), "/index.html");
167 assert_eq!(header.uri().scheme(), None);
168 assert_eq!(header.uri().query(), None);
169
170 assert_eq!(header.headers().len(), 3);
171
172 assert_eq!(header.headers().get(http::header::ACCEPT), Some(&HeaderValue::from_str("*/*").unwrap()));
173
174 assert_eq!(header.headers().get(http::header::HOST), Some(&HeaderValue::from_str("127.0.0.1:8080").unwrap()));
175
176 assert_eq!(header.headers().get(http::header::USER_AGENT), Some(&HeaderValue::from_str("curl/7.79.1").unwrap()));
177 }
178
179 #[test]
180 fn from_edge() {
181 let str = indoc! {r##"
182 GET /index/?a=1&b=2&a=3 HTTP/1.1
183 Host: 127.0.0.1:8080
184 Connection: keep-alive
185 Cache-Control: max-age=0
186 sec-ch-ua: "#Not_A Brand";v="99", "Microsoft Edge";v="109", "Chromium";v="109"
187 sec-ch-ua-mobile: ?0
188 sec-ch-ua-platform: "macOS"
189 Upgrade-Insecure-Requests: 1
190 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.52
191 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
192 Sec-Fetch-Site: none
193 Sec-Fetch-Mode: navigate
194 Sec-Fetch-User: ?1
195 Sec-Fetch-Dest: document
196 Accept-Encoding: gzip, deflate, br
197 Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
198
199 "##};
200
201 let mut parsed_req = httparse::Request::new(&mut []);
202 let mut headers: [MaybeUninit<httparse::Header>; 64] = unsafe { MaybeUninit::uninit().assume_init() };
203
204 parsed_req.parse_with_uninit_headers(str.as_bytes(), &mut headers).unwrap();
205
206 let header: RequestHeader = parsed_req.into();
207
208 assert_eq!(header.method(), &Method::GET);
209 assert_eq!(header.version(), Version::HTTP_11);
210 assert_eq!(header.uri().host(), None);
211 assert_eq!(header.uri().path(), "/index/");
212 assert_eq!(header.uri().scheme(), None);
213 assert_eq!(header.uri().query(), Some("a=1&b=2&a=3"));
214
215 assert_eq!(header.headers().len(), 15);
216
217 assert_eq!(header.headers().get(http::header::CONNECTION), Some(&HeaderValue::from_str("keep-alive").unwrap()));
219
220 assert_eq!(header.headers().get(http::header::CACHE_CONTROL), Some(&HeaderValue::from_str("max-age=0").unwrap()));
221
222 assert_eq!(
223 header.headers().get("sec-ch-ua"),
224 Some(&HeaderValue::from_str(r##""#Not_A Brand";v="99", "Microsoft Edge";v="109", "Chromium";v="109""##).unwrap())
225 );
226
227 assert_eq!(header.headers().get("sec-ch-ua-mobile"), Some(&HeaderValue::from_str("?0").unwrap()));
228
229 assert_eq!(header.headers().get("sec-ch-ua-platform"), Some(&HeaderValue::from_str("\"macOS\"").unwrap()));
230
231 assert_eq!(header.headers().get(http::header::UPGRADE_INSECURE_REQUESTS), Some(&HeaderValue::from_str("1").unwrap()));
232
233 assert_eq!(header.headers().get(http::header::USER_AGENT),
234 Some(&HeaderValue::from_str("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.52").unwrap()));
235
236 assert_eq!(header.headers().get("Sec-Fetch-Site"), Some(&HeaderValue::from_str("none").unwrap()));
237
238 assert_eq!(header.headers().get("Sec-Fetch-Mode"), Some(&HeaderValue::from_str("navigate").unwrap()));
239
240 assert_eq!(header.headers().get("Sec-Fetch-User"), Some(&HeaderValue::from_str("?1").unwrap()));
241
242 assert_eq!(header.headers().get("Sec-Fetch-Dest"), Some(&HeaderValue::from_str("document").unwrap()));
243
244 assert_eq!(header.headers().get(http::header::ACCEPT_ENCODING), Some(&HeaderValue::from_str("gzip, deflate, br").unwrap()));
245
246 assert_eq!(
247 header.headers().get(http::header::ACCEPT_LANGUAGE),
248 Some(&HeaderValue::from_str("zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7").unwrap())
249 );
250 }
251}