Skip to main content

serverkit/
request.rs

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}