Skip to main content

hooch_http/
request.rs

1//! HTTP Request Parsing Library
2//!
3//! This module provides a low-level HTTP request parser tailored for embedded or minimal-runtime environments.
4//! It does not depend on external HTTP parsing libraries and works directly on raw byte slices.
5//!
6//! ## Features
7//!
8//! - Parses HTTP request lines to extract the method, URI, and HTTP version.
9//! - Parses raw headers and stores them efficiently in a fixed-size array.
10//! - Extracts and returns the body as a UTF-8 string.
11//! - Supports route matching via a lightweight URI matcher that extracts named parameters.
12//! - Splits URI segments into path and query parameters, with type-safe iteration.
13//! - All data structures avoid heap allocation by design.
14//!
15//! ## Example
16//!
17//! ```rust
18//! use hooch_http::HttpRequest;
19//! let raw_request = b"GET /orders/123?status=shipped&sort=desc HTTP/1.1\r\nHost: localhost\r\n\r\n";
20//! let request = HttpRequest::from_bytes(raw_request);
21//! let uri = request.uri();
22//!
23//! // Match against a route with path parameters
24//! if let Some(mut params) = uri.is_match("/orders/{order_id}") {
25//!     for (key, value) in params.iter_path() {
26//!         println!("Path param: {} = {}", key.as_ref(), value.as_ref());
27//!     }
28//!     for (key, value) in params.iter_query() {
29//!         match value {
30//!             Some(val) => println!("Query param: {} = {}", key.as_ref(), val.as_ref()),
31//!             None => println!("Query param: {} with no value", key.as_ref()),
32//!         }
33//!     }
34//! }
35//! ```
36//!
37//! This parser is useful for implementing custom web servers, routers, or embedded HTTP interpreters.
38//! It aims for clarity and simplicity over full HTTP/1.1 compliance.
39
40use std::{
41    collections::HashMap,
42    fmt::{Debug, Display},
43    marker::PhantomData,
44};
45
46use crate::{shared::HttpVersion, HttpMethod};
47
48const CARRIAGE_RETURN_LINE_FEED: &[u8; 2] = b"\r\n";
49const CARRIAGE_RETURN_LINE_FEED_TWICE: &[u8; 4] = b"\r\n\r\n";
50const WHITESPACE_BYTE: u8 = 32;
51const COLON_BYTE: u8 = 58;
52
53const MAX_NUM_HEADERS: usize = 1000;
54
55/// Representation of a parsed HTTP request.
56#[derive(Debug)]
57pub struct HttpRequest<'a> {
58    method: HttpMethod,
59    uri: Uri<'a>,
60    version: HttpVersion,
61    headers: Headers<'a>,
62    body: &'a str,
63}
64
65/// Allow `HttpRequest` to be sent across threads.
66unsafe impl Send for HttpRequest<'_> {}
67
68impl Display for HttpRequest<'_> {
69    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70        f.write_str(&format!(
71            "Method: {:?}\nUri: {:?}\nVersion: {:?}\nHeaders: {:?}\nBody: {:?}",
72            self.method,
73            self.uri.0,
74            self.version,
75            self.headers
76                .keys
77                .iter()
78                .zip(self.headers.values.iter())
79                .take(self.headers.num)
80                .map(|(key, value)| (key.unwrap(), value.unwrap()))
81                .collect::<HashMap<&str, &str>>(),
82            self.body
83        ))
84    }
85}
86
87impl<'a> HttpRequest<'a> {
88    /// Parse an HTTP request from raw bytes.
89    pub fn from_bytes(bytes: &'a [u8]) -> Self {
90        let request_line = Self::get_request_line(bytes);
91        let http_method = Self::extract_http_method(request_line);
92        let http_version = Self::extract_http_version(request_line);
93        let uri = Self::extract_request_uri(request_line);
94        let header_bytes = Self::get_headers(bytes);
95        let headers = Self::extract_headers(header_bytes);
96        let body = Self::get_body(bytes);
97
98        Self {
99            method: http_method,
100            uri,
101            version: http_version,
102            headers,
103            body,
104        }
105    }
106
107    /// Get a reference to the URI.
108    pub fn uri(&self) -> &Uri {
109        &self.uri
110    }
111
112    /// Get the method
113    pub fn method(&self) -> HttpMethod {
114        self.method
115    }
116
117    /// Extract the request line from the HTTP request.
118    fn get_request_line(bytes: &[u8]) -> &[u8] {
119        let idx = bytes
120            .windows(CARRIAGE_RETURN_LINE_FEED.len())
121            .position(|window| window == CARRIAGE_RETURN_LINE_FEED)
122            .unwrap();
123
124        &bytes[..idx]
125    }
126
127    /// Extract header block (excluding request line and body).
128    fn get_headers(bytes: &[u8]) -> &[u8] {
129        let idx = bytes
130            .windows(CARRIAGE_RETURN_LINE_FEED.len())
131            .position(|window| window == CARRIAGE_RETURN_LINE_FEED)
132            .map(|idx| idx + CARRIAGE_RETURN_LINE_FEED.len())
133            .unwrap();
134
135        let header_bytes_idx = bytes[idx..]
136            .windows(CARRIAGE_RETURN_LINE_FEED_TWICE.len())
137            .position(|window| window == CARRIAGE_RETURN_LINE_FEED_TWICE)
138            .unwrap();
139
140        &bytes[idx..header_bytes_idx + idx]
141    }
142
143    /// Extract the body section of the HTTP request.
144    fn get_body(bytes: &[u8]) -> &str {
145        let idx = bytes
146            .windows(CARRIAGE_RETURN_LINE_FEED_TWICE.len())
147            .position(|window| window == CARRIAGE_RETURN_LINE_FEED_TWICE)
148            .map(|idx| idx + CARRIAGE_RETURN_LINE_FEED_TWICE.len())
149            .unwrap();
150
151        std::str::from_utf8(&bytes[idx..]).unwrap()
152    }
153
154    /// Extract HTTP method from request line.
155    fn extract_http_method(bytes: &[u8]) -> HttpMethod {
156        bytes
157            .split(|b| *b == WHITESPACE_BYTE)
158            .next()
159            .unwrap()
160            .into()
161    }
162
163    /// Extract URI from request line.
164    fn extract_request_uri(bytes: &[u8]) -> Uri {
165        let mut uri_bytes_split = bytes.split(|b| *b == WHITESPACE_BYTE);
166        uri_bytes_split.next().unwrap();
167        let uri_bytes = uri_bytes_split.next().unwrap();
168        Uri(std::str::from_utf8(uri_bytes).unwrap())
169    }
170
171    /// Extract HTTP version from request line.
172    fn extract_http_version(bytes: &[u8]) -> HttpVersion {
173        let mut http_version_bytes_split = bytes.split(|b| *b == WHITESPACE_BYTE);
174        http_version_bytes_split.next().unwrap();
175        http_version_bytes_split.next().unwrap();
176        http_version_bytes_split.next().unwrap().into()
177    }
178
179    /// Extract headers from raw header bytes.
180    fn extract_headers(bytes: &[u8]) -> Headers {
181        let mut headers = Headers::new();
182        let mut start_idx = 0;
183
184        loop {
185            let Some(carriage_return_idx) = bytes[start_idx..]
186                .windows(CARRIAGE_RETURN_LINE_FEED.len())
187                .position(|window| window == CARRIAGE_RETURN_LINE_FEED)
188            else {
189                break;
190            };
191
192            let (key, value) = HttpRequest::get_header_key_and_value(
193                &bytes[start_idx..carriage_return_idx + start_idx],
194            );
195
196            headers.add_key_value(key, value);
197
198            start_idx += carriage_return_idx + CARRIAGE_RETURN_LINE_FEED.len();
199        }
200
201        let (key, value) = HttpRequest::get_header_key_and_value(&bytes[start_idx..]);
202        headers.add_key_value(key, value);
203
204        headers
205    }
206
207    /// Extract a single header's key and value.
208    fn get_header_key_and_value(bytes: &'a [u8]) -> (&'a str, &'a str) {
209        let colon_idx = bytes.iter().position(|byte| *byte == COLON_BYTE).unwrap();
210        let key = std::str::from_utf8(&bytes[..colon_idx]);
211        let whitespace_offset = (bytes[colon_idx + 1] == WHITESPACE_BYTE) as usize;
212        let value = std::str::from_utf8(&bytes[colon_idx + 1 + whitespace_offset..]);
213        (key.unwrap(), value.unwrap())
214    }
215}
216
217/// A simple cursor used to track positions while parsing strings or byte slices.
218struct Cursor {
219    /// Current index position.
220    idx: usize,
221    /// Marker index used to track start of a span (e.g., beginning of a token).
222    start_idx: usize,
223}
224
225impl Cursor {
226    /// Advance the cursor by one position.
227    fn increment(&mut self) {
228        self.idx += 1;
229    }
230
231    /// Get the current index.
232    fn get(&self) -> usize {
233        self.idx
234    }
235
236    /// Get the last recorded start index.
237    fn get_start(&self) -> usize {
238        self.start_idx
239    }
240
241    /// Update the start index to match the current index.
242    fn set_start_to_idx(&mut self) {
243        self.start_idx = self.idx;
244    }
245}
246
247/// Marker type representing a URI path parameter segment (e.g., `{id}`).
248#[derive(Debug, Copy, Clone, PartialEq, Eq)]
249pub struct PathSegment;
250
251/// Marker type representing a URI query parameter (e.g., `?id=123`).
252#[derive(Debug, Copy, Clone, PartialEq, Eq)]
253pub struct QuerySegment;
254
255/// Holds both path and query parameters extracted from a URI.
256#[derive(Debug, Copy, Clone, PartialEq, Eq)]
257pub struct Params<'a> {
258    path_segment: Segment<'a, PathSegment>,
259    query_fragment: Segment<'a, QuerySegment>,
260}
261
262impl Default for Params<'_> {
263    fn default() -> Self {
264        Self::new()
265    }
266}
267
268impl<'a> Params<'a> {
269    /// Create a new, empty `Params` container.
270    pub fn new() -> Self {
271        Self {
272            path_segment: Segment::<PathSegment>::new(),
273            query_fragment: Segment::<QuerySegment>::new(),
274        }
275    }
276
277    /// Reset and return a mutable iterator over path parameters.
278    pub fn iter_path(&mut self) -> &mut Segment<'a, PathSegment> {
279        self.path_segment.iter()
280    }
281
282    /// Reset and return a mutable iterator over query parameters.
283    pub fn iter_query(&mut self) -> &mut Segment<'a, QuerySegment> {
284        self.query_fragment.iter()
285    }
286
287    /// Return a reference to the path parameter segment (without resetting iteration).
288    pub fn path_segment(&self) -> &Segment<'_, PathSegment> {
289        &self.path_segment
290    }
291
292    /// Return a reference to the query parameter segment (without resetting iteration).
293    pub fn query_segment(&self) -> &Segment<'_, QuerySegment> {
294        &self.query_fragment
295    }
296}
297
298/// Generic container for key-value string pairs (e.g., `("id", "123")`),
299/// parameterized over segment type (path or query).
300#[derive(Debug, Copy, Clone, PartialEq, Eq)]
301pub struct Segment<'a, T>
302where
303    T: Copy + Debug + PartialEq + Eq + Clone,
304{
305    /// Key entries, e.g., `id`, `user`, etc.
306    key: [Option<&'a str>; 1024],
307    /// Corresponding values, may be `None` for query segments with no value.
308    value: [Option<&'a str>; 1024],
309    /// Number of stored key-value pairs.
310    num: usize,
311    /// Index for iteration state.
312    iter_cnt: usize,
313    /// Phantom data to differentiate between PathSegment and QuerySegment.
314    marker: PhantomData<T>,
315}
316
317impl<'a, T> Segment<'a, T>
318where
319    T: Copy + Debug + PartialEq + Eq + Clone,
320{
321    /// Create a new empty segment container.
322    pub fn new() -> Self {
323        Self {
324            key: [None; 1024],
325            value: [None; 1024],
326            num: 0,
327            iter_cnt: 0,
328            marker: PhantomData,
329        }
330    }
331
332    /// Insert both key and optional value.
333    pub fn insert_key_value(&mut self, key: &'a str, value: Option<&'a str>) {
334        self.key[self.num] = Some(key);
335        self.value[self.num] = value;
336        self.num += 1;
337    }
338
339    /// Insert just the key (used when parsing path params before resolving value).
340    pub fn insert_key(&mut self, key: &'a str) {
341        self.key[self.num] = Some(key);
342    }
343
344    /// Insert a value (used after the key has already been inserted).
345    pub fn insert_value(&mut self, value: Option<&'a str>) {
346        self.value[self.num] = value;
347        self.num += 1;
348    }
349
350    /// Return the number of key-value pairs.
351    pub fn size(&self) -> usize {
352        self.num
353    }
354
355    /// Reset internal iteration counter.
356    pub fn iter(&mut self) -> &mut Self {
357        self.iter_cnt = 0;
358        self
359    }
360}
361
362/// Iterator implementation for path segments where all values are guaranteed to exist.
363impl<'a> Iterator for Segment<'a, PathSegment> {
364    type Item = (Key<'a>, Value<'a>);
365
366    fn next(&mut self) -> Option<Self::Item> {
367        if self.iter_cnt >= self.num {
368            return None;
369        }
370        let items = (
371            Key(self.key[self.iter_cnt].unwrap()),
372            Value(self.value[self.iter_cnt].unwrap()),
373        );
374        self.iter_cnt += 1;
375        Some(items)
376    }
377}
378
379/// Iterator implementation for query segments where values may be optional.
380impl<'a> Iterator for Segment<'a, QuerySegment> {
381    type Item = (Key<'a>, Option<Value<'a>>);
382
383    fn next(&mut self) -> Option<Self::Item> {
384        if self.iter_cnt >= self.num {
385            return None;
386        }
387        let items = (
388            Key(self.key[self.iter_cnt].unwrap()),
389            self.value[self.iter_cnt].map(Value),
390        );
391        self.iter_cnt += 1;
392        Some(items)
393    }
394}
395
396/// Lightweight wrapper for a borrowed string key (e.g., URI parameter name).
397#[derive(Debug, PartialEq)]
398pub struct Key<'a>(&'a str);
399
400impl AsRef<str> for Key<'_> {
401    fn as_ref(&self) -> &str {
402        self.0
403    }
404}
405
406/// Lightweight wrapper for a borrowed string value (e.g., URI or query value).
407#[derive(Debug, PartialEq)]
408pub struct Value<'a>(&'a str);
409
410impl AsRef<str> for Value<'_> {
411    fn as_ref(&self) -> &str {
412        self.0
413    }
414}
415
416/// Struct representing a parsed URI, which may include path and query segments.
417#[derive(Debug, Copy, Clone, PartialEq, Eq)]
418pub struct Uri<'a>(&'a str);
419
420impl AsRef<str> for Uri<'_> {
421    fn as_ref(&self) -> &str {
422        self.0
423    }
424}
425
426impl<'a> Uri<'a> {
427    /// Match the current URI against a parameterized pattern, extracting both path and query parameters.
428    pub fn is_match(&self, cmp_uri: &'a str) -> Option<Params<'a>> {
429        // TODO, find a better way, this is to handle the case both paths are home, i.e. cmp_uri =
430        // '/' && self.0 = '/'
431        // These are some sanity check
432        // ------------------------------
433        if self.0 == cmp_uri {
434            return Some(Params {
435                path_segment: Segment::<PathSegment>::new(),
436                query_fragment: Segment::<QuerySegment>::new(),
437            });
438        }
439        if self.0.split('/').count() != cmp_uri.split('/').count() {
440            return None;
441        }
442        // ------------------------------
443
444        let mut path_segment = Segment::<PathSegment>::new();
445
446        // Flags and indices for tracking parsing state
447        let mut start_idx = 0;
448        let mut bracket_hit = false;
449        let mut bracket_hit_idx = 0;
450        let mut record_uri_char = false;
451        let mut end_with_value = false;
452
453        // Cursors to track positions in the original URI and the comparison pattern
454        let mut cursor_uri = Cursor {
455            idx: 0,
456            start_idx: 0,
457        };
458        let mut cursor_cmp_uri = Cursor {
459            idx: 0,
460            start_idx: 0,
461        };
462
463        // Walk through the characters in the comparison URI (e.g., "/orders/{id}")
464        for (idx, c) in cmp_uri.char_indices() {
465            if record_uri_char {
466                // We are recording the value for a previously matched parameter
467                end_with_value = false;
468                cursor_cmp_uri.set_start_to_idx();
469
470                let start_idx_uri = cursor_uri.get();
471                let mut c_iter = self.0.chars().skip(cursor_uri.get());
472                let Some(mut c_uri) = c_iter.next() else {
473                    break;
474                };
475
476                while c_uri != c {
477                    let Some(c_uri_next) = c_iter.next() else {
478                        break;
479                    };
480                    c_uri = c_uri_next;
481                    cursor_uri.increment();
482                }
483
484                let end_idx_uri = cursor_uri.get();
485                path_segment.insert_value(Some(&self.0[start_idx_uri..end_idx_uri]));
486                cursor_uri.set_start_to_idx();
487                record_uri_char = false;
488            }
489
490            if c == '{' {
491                // We've encountered the start of a parameter segment
492                if cmp_uri[cursor_cmp_uri.get_start()..cursor_cmp_uri.get()]
493                    != self.0[cursor_uri.get_start()..cursor_uri.get()]
494                {
495                    return None;
496                } else {
497                    bracket_hit = true;
498                    bracket_hit_idx = idx;
499                }
500            }
501
502            if bracket_hit {
503                if c == '}' {
504                    // Parameter name ends
505                    bracket_hit = false;
506                    start_idx = idx + 1;
507                    record_uri_char = true;
508                    end_with_value = true;
509                    path_segment.insert_key(&cmp_uri[bracket_hit_idx + 1..idx]);
510                    cursor_cmp_uri.set_start_to_idx();
511                }
512                cursor_cmp_uri.increment();
513                continue;
514            }
515
516            // Advance both cursors normally if we're not within a bracketed param
517            cursor_uri.increment();
518            if !bracket_hit {
519                cursor_cmp_uri.increment();
520            }
521        }
522
523        let mut params: Option<Params> = None;
524
525        // Handle query string (if any)
526        if self.0.contains('?') {
527            if let Some(query_fragment) = self.0.split('?').last() {
528                let query_segment = Uri::parse_segment(query_fragment);
529                params.get_or_insert_default().query_fragment = query_segment;
530            }
531        }
532
533        // If URI ended while parsing a parameter, store the final value
534        if end_with_value {
535            let mut end_idx = self.0.len();
536            for (idx, char) in self.0.char_indices() {
537                if char == '?' {
538                    end_idx = idx;
539                    break;
540                }
541            }
542            path_segment.insert_value(Some(&self.0[cursor_uri.get()..end_idx]));
543        }
544
545        // Store the path segment into params if any values were parsed
546        if path_segment.num > 0 {
547            params.get_or_insert_default().path_segment = path_segment;
548        }
549
550        // Final segment check — we matched everything except trailing static segments
551        if start_idx >= cmp_uri.len() {
552            return params;
553        }
554
555        // Ensure the remainder of both URIs match exactly
556        if self.0[cursor_uri.get_start()..] != cmp_uri[cursor_cmp_uri.get_start()..] {
557            return params;
558        }
559
560        // All done; return extracted parameters
561        if params.is_none() {
562            params = Some(Params::default());
563        }
564        params
565    }
566
567    /// Parse the query portion of a URI (e.g., `?a=1&b`) into a Segment structure.
568    fn parse_segment(segment_part: &'a str) -> Segment<'a, QuerySegment> {
569        let mut segment: Segment<'a, QuerySegment> = Segment::new();
570
571        segment_part.split('&').for_each(|inner_split| {
572            let mut iter = inner_split.splitn(2, '=');
573            let Some(key) = iter.next() else { return };
574            let value = iter.next();
575            segment.insert_key_value(key, value);
576        });
577
578        segment
579    }
580}
581
582/// Fixed-capacity container for HTTP headers.
583#[derive(Debug, Copy, Clone, PartialEq, Eq)]
584pub struct Headers<'a> {
585    keys: [Option<&'a str>; MAX_NUM_HEADERS],
586    values: [Option<&'a str>; MAX_NUM_HEADERS],
587    /// Number of stored headers.
588    num: usize,
589}
590
591impl Default for Headers<'_> {
592    fn default() -> Self {
593        Self::new()
594    }
595}
596
597impl<'a> Headers<'a> {
598    /// Create an empty `Headers` collection.
599    pub fn new() -> Self {
600        Self {
601            keys: [None; MAX_NUM_HEADERS],
602            values: [None; MAX_NUM_HEADERS],
603            num: 0,
604        }
605    }
606
607    /// Add a header key-value pair to the collection.
608    fn add_key_value(&mut self, key: &'a str, value: &'a str) {
609        self.keys[self.num] = Some(key);
610        self.values[self.num] = Some(value);
611        self.num += 1;
612    }
613}
614
615#[cfg(test)]
616mod tests {
617    use super::*;
618
619    fn get_test_post_request() -> &'static [u8] {
620        b"POST /user HTTP/1.1\r\n\
621     Host: localhost:8080\r\n\
622     User-Agent: curl/7.81.0\r\n\
623     Accept: */*\r\n\
624     Content-Type: application/json\r\n\
625     Content-Length: 26\r\n\
626     \r\n\
627     {\"message\": \"hello world\"}"
628    }
629
630    fn get_test_post_request_no_headers() -> &'static [u8] {
631        b"POST /user HTTP/1.1\r\n\
632        \r\n\
633     {\"message\": \"hello world\"}"
634    }
635
636    #[test]
637    fn get_request_line() {
638        let request = get_test_post_request();
639        // Bytes representing request line
640        let expected = [
641            80, 79, 83, 84, 32, 47, 117, 115, 101, 114, 32, 72, 84, 84, 80, 47, 49, 46, 49,
642        ];
643        let actual = HttpRequest::get_request_line(request);
644
645        assert_eq!(actual, expected);
646    }
647
648    #[test]
649    fn extract_http_method() {
650        let request_line = [
651            80, 79, 83, 84, 32, 47, 117, 115, 101, 114, 32, 72, 84, 84, 80, 47, 49, 46, 49,
652        ];
653
654        let expected = HttpMethod::POST;
655
656        let actual = HttpRequest::extract_http_method(&request_line);
657
658        assert_eq!(actual, expected);
659    }
660
661    #[test]
662    fn bytes_to_http_method() {
663        let get = [71, 69, 84];
664        let actual: HttpMethod = get.as_slice().into();
665
666        assert_eq!(actual, HttpMethod::GET);
667
668        let head = [72, 69, 65, 68];
669        let actual: HttpMethod = head.as_slice().into();
670        assert_eq!(actual, HttpMethod::HEAD);
671
672        let options = [79, 80, 84, 73, 79, 78, 83];
673        let actual: HttpMethod = options.as_slice().into();
674        assert_eq!(actual, HttpMethod::OPTIONS);
675
676        let post = [80, 79, 83, 84];
677        let actual: HttpMethod = post.as_slice().into();
678        assert_eq!(actual, HttpMethod::POST);
679
680        let put = [80, 85, 84];
681        let actual: HttpMethod = put.as_slice().into();
682        assert_eq!(actual, HttpMethod::PUT);
683
684        let patch = [80, 65, 84, 67, 72];
685        let actual: HttpMethod = patch.as_slice().into();
686        assert_eq!(actual, HttpMethod::PATCH);
687
688        let delete = [68, 69, 76, 69, 84, 69];
689        let actual: HttpMethod = delete.as_slice().into();
690        assert_eq!(actual, HttpMethod::DELETE);
691    }
692
693    #[test]
694    fn extract_request_uri() {
695        let request_line = [
696            80, 79, 83, 84, 32, 47, 117, 115, 101, 114, 32, 72, 84, 84, 80, 47, 49, 46, 49,
697        ];
698
699        let actual = HttpRequest::extract_request_uri(&request_line);
700        let expected = Uri("/user");
701
702        assert_eq!(actual, expected);
703    }
704
705    #[test]
706    fn extract_http_version() {
707        let request_line = [
708            80, 79, 83, 84, 32, 47, 117, 115, 101, 114, 32, 72, 84, 84, 80, 47, 49, 46, 49,
709        ];
710
711        let expected = HttpVersion::OnePointOne;
712
713        let actual = HttpRequest::extract_http_version(&request_line);
714
715        assert_eq!(actual, expected);
716    }
717
718    #[test]
719    fn get_headers() {
720        let request = get_test_post_request();
721
722        let actual = HttpRequest::get_headers(request);
723
724        let expected = b"Host: localhost:8080\r\n\
725     User-Agent: curl/7.81.0\r\n\
726     Accept: */*\r\n\
727     Content-Type: application/json\r\n\
728     Content-Length: 26";
729
730        assert_eq!(actual, expected);
731    }
732
733    #[test]
734    fn extract_headers() {
735        let headers = b"Host: localhost:8080\r\n\
736     User-Agent: curl/7.81.0\r\n\
737     Accept:*/*\r\n\
738     Content-Type:application/json\r\n\
739     Content-Length: 26";
740
741        let actual = HttpRequest::extract_headers(headers);
742
743        let mut expected_keys = [None; MAX_NUM_HEADERS];
744        let mut expected_values = [None; MAX_NUM_HEADERS];
745        let expected_num_headers = 5;
746
747        expected_keys[0] = Some("Host");
748        expected_keys[1] = Some("User-Agent");
749        expected_keys[2] = Some("Accept");
750        expected_keys[3] = Some("Content-Type");
751        expected_keys[4] = Some("Content-Length");
752
753        expected_values[0] = Some("localhost:8080");
754        expected_values[1] = Some("curl/7.81.0");
755        expected_values[2] = Some("*/*");
756        expected_values[3] = Some("application/json");
757        expected_values[4] = Some("26");
758
759        let expected = Headers {
760            keys: expected_keys,
761            values: expected_values,
762            num: expected_num_headers,
763        };
764
765        assert_eq!(actual, expected);
766    }
767
768    #[test]
769    fn get_body() {
770        let request = get_test_post_request();
771
772        let actual = HttpRequest::get_body(request);
773
774        let expected = "{\"message\": \"hello world\"}";
775
776        assert_eq!(actual, expected);
777
778        let request = get_test_post_request_no_headers();
779
780        let actual = HttpRequest::get_body(request);
781
782        let expected = "{\"message\": \"hello world\"}";
783
784        assert_eq!(actual, expected);
785    }
786
787    #[test]
788    fn uri_is_match_home_path() {
789        let uri = Uri("/");
790
791        let expected_params = Params {
792            path_segment: Segment::<PathSegment>::new(),
793            query_fragment: Segment::<QuerySegment>::new(),
794        };
795
796        assert_eq!(uri.is_match("/"), Some(expected_params))
797    }
798
799    #[test]
800    fn uri_is_match_home_path_no_parameters() {
801        let uri = Uri("/");
802
803        let actual = uri.is_match("/{something}");
804
805        let mut path_segment = Segment::<PathSegment>::new();
806        path_segment.key[0] = Some("something");
807        path_segment.value[0] = Some("");
808        path_segment.num = 1;
809
810        let expected_params = Params {
811            path_segment,
812            query_fragment: Segment::<QuerySegment>::new(),
813        };
814
815        assert_eq!(actual, Some(expected_params))
816    }
817
818    #[test]
819    fn uri_is_match_no_parameterized() {
820        let uri = Uri("/uri");
821
822        let expected_params = Params {
823            path_segment: Segment::<PathSegment>::new(),
824            query_fragment: Segment::<QuerySegment>::new(),
825        };
826
827        assert_eq!(uri.is_match("/uri"), Some(expected_params))
828    }
829
830    #[test]
831    fn uri_is_match_parameterized_single_arg() {
832        let uri = Uri("/1244r2");
833
834        let mut path_segment = Segment::<PathSegment>::new();
835        path_segment.key[0] = Some("id");
836        path_segment.value[0] = Some("1244r2");
837        path_segment.num = 1;
838
839        let expected_params = Params {
840            path_segment,
841            query_fragment: Segment::<QuerySegment>::new(),
842        };
843        assert_eq!(uri.is_match("/{id}"), Some(expected_params))
844    }
845
846    #[test]
847    fn uri_is_match_parameterized_invalid_comparison() {
848        let uri = Uri("/");
849        assert!(uri.is_match("/what/{mate}").is_none())
850    }
851
852    #[test]
853    fn uri_is_match_parameterized_and_query() {
854        let uri = Uri("/orders/123?status=hello_matey&include=details");
855        let mut path_segment = Segment::<PathSegment>::new();
856
857        path_segment.key[0] = Some("orders_param");
858        path_segment.value[0] = Some("orders");
859        path_segment.num = 1;
860
861        let mut query_segment = Segment::<QuerySegment>::new();
862        query_segment.key[0] = Some("status");
863        query_segment.value[0] = Some("hello_matey");
864        query_segment.key[1] = Some("include");
865        query_segment.value[1] = Some("details");
866        query_segment.num = 2;
867
868        let params = Params {
869            path_segment,
870            query_fragment: query_segment,
871        };
872
873        assert_eq!(uri.is_match("/{orders_param}/123"), Some(params))
874    }
875
876    #[test]
877    fn uri_is_match_parameterized_multi_args() {
878        let uri = Uri("/orders/status/123?status=shipped&include=details");
879
880        let mut path_segment = Segment::<PathSegment>::new();
881        path_segment.key[0] = Some("orders_param");
882        path_segment.value[0] = Some("orders");
883        path_segment.key[1] = Some("field");
884        path_segment.value[1] = Some("status");
885        path_segment.num = 2;
886
887        let mut query_segment = Segment::<QuerySegment>::new();
888        query_segment.key[0] = Some("status");
889        query_segment.value[0] = Some("shipped");
890        query_segment.key[1] = Some("include");
891        query_segment.value[1] = Some("details");
892        query_segment.num = 2;
893
894        assert_eq!(
895            uri.is_match("/{orders_param}/{field}/123"),
896            Some(Params {
897                path_segment,
898                query_fragment: query_segment,
899            })
900        )
901    }
902
903    #[test]
904    fn uri_is_match_parameterized_multi_args2() {
905        let uri = Uri("/what/hello?this=value&is");
906
907        let mut path_segment = Segment::<PathSegment>::new();
908        path_segment.key[0] = Some("mate");
909        path_segment.value[0] = Some("hello");
910        path_segment.num = 1;
911
912        let mut query_segment = Segment::<QuerySegment>::new();
913        query_segment.key[0] = Some("this");
914        query_segment.value[0] = Some("value");
915        query_segment.key[1] = Some("is");
916        query_segment.value[1] = None;
917        query_segment.num = 2;
918
919        assert_eq!(
920            uri.is_match("/what/{mate}"),
921            Some(Params {
922                path_segment,
923                query_fragment: query_segment,
924            })
925        )
926    }
927
928    #[test]
929    fn uri_query_segment_params_parsing() {
930        let segment = "q=rust&&limit=10&debug&sort=asc";
931
932        let actual = Uri::parse_segment(segment);
933
934        let mut expected = Segment::new();
935        expected.key[0] = Some("q");
936        expected.key[1] = Some("");
937        expected.key[2] = Some("limit");
938        expected.key[3] = Some("debug");
939        expected.key[4] = Some("sort");
940        expected.value[0] = Some("rust");
941        expected.value[1] = None;
942        expected.value[2] = Some("10");
943        expected.value[3] = None;
944        expected.value[4] = Some("asc");
945        expected.num = 5;
946
947        assert_eq!(actual, expected)
948    }
949
950    #[test]
951    fn path_segment_iter() {
952        let mut path_segment = Segment::<PathSegment>::new();
953        path_segment.key[0] = Some("id");
954        path_segment.value[0] = Some("123");
955        path_segment.key[1] = Some("name");
956        path_segment.value[1] = Some("hello");
957        path_segment.num = 2;
958
959        let iter = path_segment.iter();
960
961        assert_eq!(iter.next(), Some((Key("id"), Value("123"))));
962        assert_eq!(iter.next(), Some((Key("name"), Value("hello"))));
963        assert_eq!(iter.next(), None);
964    }
965
966    #[test]
967    fn query_segment_iter() {
968        let mut query_segment = Segment::<QuerySegment>::new();
969        query_segment.key[0] = Some("id");
970        query_segment.value[0] = Some("123");
971        query_segment.key[1] = Some("name");
972        query_segment.value[1] = None;
973        query_segment.num = 2;
974
975        let iter = query_segment.iter();
976        assert_eq!(iter.next(), Some((Key("id"), Some(Value("123")))));
977        assert_eq!(iter.next(), Some((Key("name"), None)));
978        assert_eq!(iter.next(), None);
979    }
980}