1use std::{
2 any::{Any, TypeId},
3 collections::HashMap,
4 fmt,
5 str::FromStr,
6 sync::Arc,
7};
8
9use crate::{Error, IntoResponse, RequestStream, Response};
10
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct Method(MethodRepr);
13
14#[derive(Debug, Clone, PartialEq, Eq)]
15enum MethodRepr {
16 Connect,
17 Delete,
18 Get,
19 Head,
20 Options,
21 Patch,
22 Post,
23 Put,
24 Trace,
25 Other(Box<str>),
26}
27
28impl Method {
29 pub const CONNECT: Self = Self(MethodRepr::Connect);
30 pub const DELETE: Self = Self(MethodRepr::Delete);
31 pub const GET: Self = Self(MethodRepr::Get);
32 pub const HEAD: Self = Self(MethodRepr::Head);
33 pub const OPTIONS: Self = Self(MethodRepr::Options);
34 pub const PATCH: Self = Self(MethodRepr::Patch);
35 pub const POST: Self = Self(MethodRepr::Post);
36 pub const PUT: Self = Self(MethodRepr::Put);
37 pub const TRACE: Self = Self(MethodRepr::Trace);
38
39 pub fn from_bytes(value: &[u8]) -> Result<Self, InvalidMethod> {
40 if !valid_method(value) {
41 return Err(InvalidMethod);
42 }
43
44 let value = std::str::from_utf8(value).map_err(|_| InvalidMethod)?;
45 Ok(Self::standard(value).unwrap_or_else(|| Self(MethodRepr::Other(value.into()))))
46 }
47
48 pub fn as_str(&self) -> &str {
49 match &self.0 {
50 MethodRepr::Connect => "CONNECT",
51 MethodRepr::Delete => "DELETE",
52 MethodRepr::Get => "GET",
53 MethodRepr::Head => "HEAD",
54 MethodRepr::Options => "OPTIONS",
55 MethodRepr::Patch => "PATCH",
56 MethodRepr::Post => "POST",
57 MethodRepr::Put => "PUT",
58 MethodRepr::Trace => "TRACE",
59 MethodRepr::Other(method) => method,
60 }
61 }
62
63 pub(crate) fn is_openapi_operation(&self) -> bool {
64 !matches!(self.0, MethodRepr::Connect | MethodRepr::Other(_))
65 }
66
67 fn standard(value: &str) -> Option<Self> {
68 match value {
69 "CONNECT" => Some(Self::CONNECT),
70 "DELETE" => Some(Self::DELETE),
71 "GET" => Some(Self::GET),
72 "HEAD" => Some(Self::HEAD),
73 "OPTIONS" => Some(Self::OPTIONS),
74 "PATCH" => Some(Self::PATCH),
75 "POST" => Some(Self::POST),
76 "PUT" => Some(Self::PUT),
77 "TRACE" => Some(Self::TRACE),
78 _ => None,
79 }
80 }
81}
82
83impl FromStr for Method {
84 type Err = InvalidMethod;
85
86 fn from_str(value: &str) -> Result<Self, Self::Err> {
87 Self::from_bytes(value.as_bytes())
88 }
89}
90
91impl TryFrom<&str> for Method {
92 type Error = InvalidMethod;
93
94 fn try_from(value: &str) -> Result<Self, Self::Error> {
95 Self::from_bytes(value.as_bytes())
96 }
97}
98
99impl TryFrom<String> for Method {
100 type Error = InvalidMethod;
101
102 fn try_from(value: String) -> Result<Self, Self::Error> {
103 if !valid_method(value.as_bytes()) {
104 return Err(InvalidMethod);
105 }
106
107 Ok(Self::standard(&value)
108 .unwrap_or_else(|| Self(MethodRepr::Other(value.into_boxed_str()))))
109 }
110}
111
112impl AsRef<str> for Method {
113 fn as_ref(&self) -> &str {
114 self.as_str()
115 }
116}
117
118impl fmt::Display for Method {
119 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
120 formatter.write_str(self.as_str())
121 }
122}
123
124#[derive(Debug, Clone, Copy, PartialEq, Eq)]
125pub struct InvalidMethod;
126
127impl InvalidMethod {
128 pub fn message(&self) -> &'static str {
129 "invalid HTTP method"
130 }
131}
132
133impl fmt::Display for InvalidMethod {
134 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
135 formatter.write_str(self.message())
136 }
137}
138
139impl std::error::Error for InvalidMethod {}
140
141fn valid_method(value: &[u8]) -> bool {
142 !value.is_empty()
143 && value.iter().all(|byte| {
144 byte.is_ascii_alphanumeric()
145 || matches!(
146 byte,
147 b'!' | b'#'
148 | b'$'
149 | b'%'
150 | b'&'
151 | b'\''
152 | b'*'
153 | b'+'
154 | b'-'
155 | b'.'
156 | b'^'
157 | b'_'
158 | b'`'
159 | b'|'
160 | b'~'
161 )
162 })
163}
164
165#[cfg(test)]
166mod method_tests {
167 use super::Method;
168
169 #[test]
170 fn exposes_standard_methods_as_constants() {
171 let methods = [
172 (Method::CONNECT, "CONNECT"),
173 (Method::DELETE, "DELETE"),
174 (Method::GET, "GET"),
175 (Method::HEAD, "HEAD"),
176 (Method::OPTIONS, "OPTIONS"),
177 (Method::PATCH, "PATCH"),
178 (Method::POST, "POST"),
179 (Method::PUT, "PUT"),
180 (Method::TRACE, "TRACE"),
181 ];
182
183 for (method, name) in methods {
184 assert_eq!(method.as_str(), name);
185 assert_eq!(Method::from_bytes(name.as_bytes()).unwrap(), method);
186 }
187 }
188
189 #[test]
190 fn preserves_other_methods() {
191 let method = Method::from_bytes(b"PROPFIND").unwrap();
192
193 assert_eq!(method.as_str(), "PROPFIND");
194 }
195
196 #[test]
197 fn rejects_invalid_method_tokens() {
198 for method in [b"".as_slice(), b"NOT VALID", b"GET/POST", b"m\xc3\xa9thod"] {
199 assert!(Method::from_bytes(method).is_err());
200 }
201 }
202}
203
204#[derive(Debug, Default)]
205pub struct Headers {
206 entries: Vec<(String, Vec<u8>)>,
207}
208
209impl Headers {
210 pub fn new() -> Self {
211 Self::default()
212 }
213
214 pub fn get(&self, name: &str) -> Option<&[u8]> {
215 self.entries
216 .iter()
217 .find(|(header, _)| header.eq_ignore_ascii_case(name))
218 .map(|(_, value)| value.as_slice())
219 }
220
221 pub fn get_all<'headers>(
222 &'headers self,
223 name: &'headers str,
224 ) -> impl Iterator<Item = &'headers [u8]> + 'headers {
225 self.entries
226 .iter()
227 .filter(move |(header, _)| header.eq_ignore_ascii_case(name))
228 .map(|(_, value)| value.as_slice())
229 }
230
231 pub fn contains(&self, name: &str) -> bool {
232 self.entries
233 .iter()
234 .any(|(header, _)| header.eq_ignore_ascii_case(name))
235 }
236
237 pub fn iter(&self) -> impl Iterator<Item = (&str, &[u8])> {
238 self.entries
239 .iter()
240 .map(|(name, value)| (name.as_str(), value.as_slice()))
241 }
242
243 pub fn is_empty(&self) -> bool {
244 self.entries.is_empty()
245 }
246
247 pub fn len(&self) -> usize {
248 self.entries.len()
249 }
250
251 pub fn set(
252 &mut self,
253 name: impl Into<String>,
254 value: impl Into<Vec<u8>>,
255 ) -> Result<(), InvalidHeader> {
256 let name = name.into();
257 let value = value.into();
258
259 validate_header(&name, &value)?;
260 self.remove(&name);
261 self.entries.push((name, value));
262
263 Ok(())
264 }
265
266 pub fn append(
267 &mut self,
268 name: impl Into<String>,
269 value: impl Into<Vec<u8>>,
270 ) -> Result<(), InvalidHeader> {
271 let name = name.into();
272 let value = value.into();
273
274 validate_header(&name, &value)?;
275 self.entries.push((name, value));
276
277 Ok(())
278 }
279
280 pub fn remove(&mut self, name: &str) {
281 self.entries
282 .retain(|(header, _)| !header.eq_ignore_ascii_case(name));
283 }
284
285 pub(crate) fn append_unchecked(&mut self, name: impl Into<String>, value: impl Into<Vec<u8>>) {
286 self.entries.push((name.into(), value.into()));
287 }
288
289 pub(crate) fn set_unchecked(&mut self, name: impl Into<String>, value: impl Into<Vec<u8>>) {
290 let name = name.into();
291 self.remove(&name);
292 self.entries.push((name, value.into()));
293 }
294
295 pub(crate) fn merge_from(&mut self, headers: Self) {
296 self.entries.retain(|(existing, _)| {
297 !headers
298 .entries
299 .iter()
300 .any(|(incoming, _)| existing.eq_ignore_ascii_case(incoming))
301 });
302 self.entries.extend(headers.entries);
303 }
304}
305
306#[derive(Debug, Clone, PartialEq, Eq)]
307pub struct InvalidHeader {
308 message: &'static str,
309}
310
311impl InvalidHeader {
312 pub fn message(&self) -> &str {
313 self.message
314 }
315}
316
317impl fmt::Display for InvalidHeader {
318 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
319 formatter.write_str(self.message)
320 }
321}
322
323impl IntoResponse for InvalidHeader {
324 fn into_response(self) -> Response {
325 Error::new(500, "response.header.invalid", self.message).into_response()
326 }
327}
328
329fn validate_header(name: &str, value: &[u8]) -> Result<(), InvalidHeader> {
330 if name.is_empty()
331 || !name.bytes().all(|byte| {
332 byte.is_ascii_alphanumeric()
333 || matches!(
334 byte,
335 b'!' | b'#'
336 | b'$'
337 | b'%'
338 | b'&'
339 | b'\''
340 | b'*'
341 | b'+'
342 | b'-'
343 | b'.'
344 | b'^'
345 | b'_'
346 | b'`'
347 | b'|'
348 | b'~'
349 )
350 })
351 {
352 return Err(InvalidHeader {
353 message: "invalid HTTP header name",
354 });
355 }
356
357 if value
358 .iter()
359 .any(|byte| matches!(byte, b'\0' | b'\r' | b'\n'))
360 {
361 return Err(InvalidHeader {
362 message: "invalid HTTP header value",
363 });
364 }
365
366 Ok(())
367}
368
369pub struct Request {
370 pub method: Method,
371 pub path: String,
372 pub query: Option<String>,
373 pub headers: Headers,
374 params: Vec<(String, String)>,
375 extensions: HashMap<TypeId, Box<dyn Any>>,
376 states: HashMap<TypeId, Arc<dyn Any + Send + Sync>>,
377 body_limit: Option<usize>,
378 pub(crate) body: Box<dyn RequestStream>,
379}
380
381impl Request {
382 pub fn from_parts(
383 method: Method,
384 path: impl Into<String>,
385 query: Option<String>,
386 headers: Headers,
387 body: Box<dyn RequestStream>,
388 ) -> Self {
389 Self {
390 method,
391 path: path.into(),
392 query,
393 headers,
394 params: Vec::new(),
395 extensions: HashMap::new(),
396 states: HashMap::new(),
397 body_limit: None,
398 body,
399 }
400 }
401
402 pub(crate) fn params(&self) -> &[(String, String)] {
403 &self.params
404 }
405
406 pub(crate) fn set_params(&mut self, params: Vec<(String, String)>) {
407 self.params = params;
408 }
409
410 pub fn insert_extension<T: 'static>(&mut self, value: T) {
411 self.extensions.insert(TypeId::of::<T>(), Box::new(value));
412 }
413
414 pub fn extension<T: 'static>(&self) -> Option<&T> {
415 self.extensions
416 .get(&TypeId::of::<T>())
417 .and_then(|value| value.downcast_ref())
418 }
419
420 pub(crate) fn set_states(&mut self, states: HashMap<TypeId, Arc<dyn Any + Send + Sync>>) {
421 self.states = states;
422 }
423
424 pub(crate) fn state<T: Send + Sync + 'static>(&self) -> Option<Arc<T>> {
425 self.states
426 .get(&TypeId::of::<T>())
427 .cloned()
428 .and_then(|state| state.downcast().ok())
429 }
430
431 pub(crate) fn set_body_limit(&mut self, limit: Option<usize>) {
432 self.body_limit = limit;
433 }
434
435 pub(crate) fn body_limit(&self) -> Option<usize> {
436 self.body_limit
437 }
438}
439
440impl fmt::Debug for Request {
441 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
442 formatter
443 .debug_struct("Request")
444 .field("method", &self.method)
445 .field("path", &self.path)
446 .field("query", &self.query)
447 .field("headers", &self.headers)
448 .field("params", &self.params)
449 .field("body_limit", &self.body_limit)
450 .finish_non_exhaustive()
451 }
452}