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