1use std::io::Read;
5
6
7use super::{read_headers, read_line, ParseError};
8use crate::header::Header;
9use crate::method;
10use crate::parse::transfer::{resolve_body, Body, MessageKind};
11
12pub const DEFAULT_MAX_HEADER_BYTES: usize = 1 << 20; pub struct ParsedRequest {
18 pub method: String,
19 pub request_uri: String,
20 pub proto: String,
21 pub proto_major: u8,
22 pub proto_minor: u8,
23 pub header: Header,
24 pub body: Body,
25 pub content_length: i64,
26 pub transfer_encoding: Vec<String>,
27 pub host: String,
28}
29
30impl std::fmt::Debug for ParsedRequest {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 f.debug_struct("ParsedRequest")
33 .field("method", &self.method)
34 .field("request_uri", &self.request_uri)
35 .finish_non_exhaustive()
36 }
37}
38
39pub fn read_request(
45 r: impl Read + Send + 'static,
46 max_header_bytes: usize,
47) -> Result<ParsedRequest, ParseError> {
48 let mut r: Box<dyn Read + Send> = Box::new(r);
49
50 let line = read_line(&mut r)?;
52 let parts: Vec<&str> = line.splitn(3, ' ').collect();
53 if parts.len() != 3 {
54 return Err(ParseError::BadRequestLine);
55 }
56 let raw_method = parts[0];
57 let request_uri = parts[1];
58 let proto = parts[2];
59
60 if !method::is_valid(raw_method) {
61 return Err(ParseError::BadRequestLine);
62 }
63
64 let (proto_major, proto_minor) = parse_proto(proto)?;
65
66 let header = read_headers(&mut r, max_header_bytes)?;
68
69 let host = header.get("Host").unwrap_or("").to_owned();
71
72 let transfer_encoding: Vec<String> = header
74 .values("Transfer-Encoding")
75 .iter()
76 .flat_map(|v| v.split(',').map(|s| s.trim().to_ascii_lowercase()))
77 .collect();
78
79 let content_length = parse_content_length(&header)?;
81
82 let body = resolve_body(r, &header, MessageKind::Request)?;
84
85 Ok(ParsedRequest {
86 method: raw_method.to_owned(),
87 request_uri: request_uri.to_owned(),
88 proto: proto.to_owned(),
89 proto_major,
90 proto_minor,
91 header,
92 body,
93 content_length,
94 transfer_encoding,
95 host,
96 })
97}
98
99fn parse_proto(proto: &str) -> Result<(u8, u8), ParseError> {
105 if !proto.starts_with("HTTP/") {
106 return Err(ParseError::BadRequestLine);
107 }
108 let ver = &proto[5..];
109 let dot = ver.find('.').ok_or(ParseError::BadRequestLine)?;
110 let major: u8 = ver[..dot].parse().map_err(|_| ParseError::BadRequestLine)?;
111 let minor: u8 = ver[dot + 1..].parse().map_err(|_| ParseError::BadRequestLine)?;
112 Ok((major, minor))
113}
114
115fn parse_content_length(h: &Header) -> Result<i64, ParseError> {
117 match h.get("Content-Length") {
118 None => Ok(-1),
119 Some(s) => s
120 .trim()
121 .parse::<i64>()
122 .map_err(|_| ParseError::InvalidContentLength),
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129 use std::io::{Cursor, Read};
130
131 fn parse(raw: &'static [u8]) -> ParsedRequest {
132 read_request(Cursor::new(raw), DEFAULT_MAX_HEADER_BYTES).unwrap()
133 }
134
135 #[test]
136 fn simple_get() {
137 let req = parse(b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n");
138 assert_eq!(req.method, "GET");
139 assert_eq!(req.request_uri, "/");
140 assert_eq!(req.proto_major, 1);
141 assert_eq!(req.proto_minor, 1);
142 assert_eq!(req.host, "example.com");
143 }
144
145 #[test]
146 fn post_with_body() {
147 let raw = b"POST /submit HTTP/1.1\r\nHost: x\r\nContent-Length: 5\r\n\r\nHello extra";
148 let mut req = parse(raw);
149 let mut body_out = Vec::new();
150 req.body.read_to_end(&mut body_out).unwrap();
151 assert_eq!(body_out, b"Hello");
152 }
153
154 #[test]
155 fn bad_method() {
156 let result = read_request(
157 Cursor::new(b"G\xc3\x89T / HTTP/1.1\r\n\r\n"),
158 DEFAULT_MAX_HEADER_BYTES,
159 );
160 assert_eq!(result.unwrap_err(), ParseError::BadRequestLine);
161 }
162
163 #[test]
164 fn bad_proto() {
165 let result = read_request(
166 Cursor::new(b"GET / NOTHTTP\r\n\r\n"),
167 DEFAULT_MAX_HEADER_BYTES,
168 );
169 assert_eq!(result.unwrap_err(), ParseError::BadRequestLine);
170 }
171}