1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! Request library for micro HTTP server.
16 //!
17 //! This library implements the basic parts of Request Message from
18 //! (RFC 5322)[ https://www.rfc-editor.org/rfc/rfc5322.html] "HTTP
19 //! Message Format."
20 //!
21 //! This library is only used for serving the netsim client and is not
22 //! meant to implement all aspects of RFC 5322. In particular,
23 //! this library does not implement the following:
24 //! * header field body with multiple lines (section 3.2.2)
25 //! * limits on the lengths of the header section or header field
26 //!
27 //! The main function is `HttpRequest::parse` which can be called
28 //! repeatedly.
29 
30 use http::Request;
31 use http::Version;
32 use std::io::BufRead;
33 use std::io::BufReader;
34 use std::io::Read;
35 
36 #[allow(clippy::read_zero_byte_vec)]
parse_http_request<T: std::io::Read>( reader: &mut BufReader<T>, ) -> Result<Request<Vec<u8>>, String>37 pub fn parse_http_request<T: std::io::Read>(
38     reader: &mut BufReader<T>,
39 ) -> Result<Request<Vec<u8>>, String> {
40     let mut line = String::new();
41     reader.read_line(&mut line).map_err(|e| format!("Failed to read request line: {e}"))?;
42     let mut parts = line.split_whitespace();
43     let method = parts.next().ok_or("Invalid request line, missing method")?;
44     let uri = parts.next().ok_or("Invalid request line, missing uri")?;
45     let version_str = parts.next().ok_or("Invalid request line, missing version")?;
46     let version = match version_str {
47         "HTTP/0.9" => Version::HTTP_09,
48         "HTTP/1.0" => Version::HTTP_10,
49         "HTTP/1.1" => Version::HTTP_11,
50         "HTTP/2.0" => Version::HTTP_2,
51         "HTTP/3.0" => Version::HTTP_3,
52         _ => return Err("Invalid HTTP version".to_string()),
53     };
54 
55     let mut headers = Vec::new();
56     for line in reader.lines() {
57         let line = line.map_err(|e| format!("Failed to parse headers: {e}"))?;
58         if let Some((name, value)) = line.split_once(':') {
59             headers.push((name.to_string(), value.trim().to_string()));
60         } else if line.len() > 1 {
61             // no colon in a header line
62             return Err(format!("Invalid header line: {line}"));
63         } else {
64             // empty line marks the end of headers
65             break;
66         }
67     }
68 
69     let mut builder = Request::builder().method(method).uri(uri).version(version);
70     let mut body_length: Option<usize> = None;
71     for (key, value) in headers {
72         builder = builder.header(key.clone(), value.clone());
73         if key == "Content-Length" {
74             body_length = match value.parse() {
75                 Ok(size) => Some(size),
76                 Err(err) => return Err(format!("{err:?}")),
77             }
78         }
79     }
80     let mut body = Vec::new();
81     if let Some(len) = body_length {
82         body.resize(len, 0);
83         reader.read_exact(&mut body).map_err(|e| format!("Failed to read body: {e}"))?;
84     }
85     match builder.body(body) {
86         Ok(request) => Ok(request),
87         Err(err) => Err(format!("{err:?}")),
88     }
89 }
90 
91 #[cfg(test)]
92 mod tests {
93     use super::*;
94 
95     #[test]
test_parse()96     fn test_parse() {
97         let request = concat!(
98             "GET /index.html HTTP/1.1\r\n",
99             "Host: example.com\r\nContent-Length: 13\r\n\r\n",
100             "Hello World\r\n"
101         );
102         let mut reader = BufReader::new(request.as_bytes());
103         let http_request = parse_http_request::<&[u8]>(&mut reader).unwrap();
104         assert_eq!(http_request.method(), "GET");
105         assert_eq!(http_request.uri().to_string(), "/index.html");
106         assert_eq!(http_request.version(), Version::HTTP_11);
107         let mut headers = http::HeaderMap::new();
108         headers.insert("Host", http::HeaderValue::from_static("example.com"));
109         headers.insert("Content-Length", http::HeaderValue::from_static("13"));
110         assert_eq!(http_request.headers().to_owned(), headers);
111         assert_eq!(http_request.body().to_owned(), b"Hello World\r\n".to_vec());
112     }
113 
114     #[test]
test_parse_without_body()115     fn test_parse_without_body() {
116         let request = concat!("GET /index.html HTTP/1.1\r\n", "Host: example.com\r\n\r\n");
117         let mut reader = BufReader::new(request.as_bytes());
118         let http_request = parse_http_request::<&[u8]>(&mut reader).unwrap();
119         assert_eq!(http_request.method(), "GET");
120         assert_eq!(http_request.uri().to_string(), "/index.html");
121         assert_eq!(http_request.version(), Version::HTTP_11);
122         let mut headers = http::HeaderMap::new();
123         headers.insert("Host", http::HeaderValue::from_static("example.com"));
124         assert_eq!(http_request.headers().to_owned(), headers);
125         assert_eq!(http_request.body().to_owned(), Vec::<u8>::new());
126     }
127 
128     #[test]
test_parse_without_content_length()129     fn test_parse_without_content_length() {
130         let request =
131             concat!("GET /index.html HTTP/1.1\r\n", "Host: example.com\r\n\r\n", "Hello World\r\n");
132         let mut reader = BufReader::new(request.as_bytes());
133         let http_request = parse_http_request::<&[u8]>(&mut reader).unwrap();
134         assert_eq!(http_request.method(), "GET");
135         assert_eq!(http_request.uri(), "/index.html");
136         assert_eq!(http_request.version(), Version::HTTP_11);
137         let mut headers = http::HeaderMap::new();
138         headers.insert("Host", http::HeaderValue::from_static("example.com"));
139         assert_eq!(http_request.headers().to_owned(), headers);
140         assert_eq!(http_request.body().to_owned(), Vec::<u8>::new());
141     }
142 }
143