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