1use super::{
2 errors::FlaskError,
3 CONTENT_LENGTH_HEADER,
4 get_http_version,
5 read_buffered_line,
6 read_header
7};
8
9use crate::combinators::*;
10
11use http::Request;
12use http::request::Builder;
13use std::io::{
14 BufReader,
15 prelude::*
16};
17use std::net::TcpStream;
18
19#[derive(PartialEq, Debug)]
20struct RequestLine<'a> {
21 method: &'a str,
22 target: &'a str,
23 version: &'a str,
24}
25
26fn parse_request_line(line: &str) -> Result<RequestLine, FlaskError> {
27 let (line, method): (&str, &str) = match http_method(line) {
28 Ok(obj) => obj,
29 Err(_) => return Err( FlaskError::BadRequest("Malformed Request Line: missing HTTP method".to_string()) )
30 };
31 let (line, _): (&str, &str) = match spaces(line) {
32 Ok(obj) => obj,
33 Err(_) => return Err( FlaskError::BadRequest("Malformed Request Line: missing space before target URL".to_string()) )
34 };
35 let (line, target): (&str, &str) = match to_space(line) {
36 Ok(obj) => obj,
37 Err(_) => return Err( FlaskError::BadRequest("Malformed Request Line: error parsing target URL".to_string()) )
38 };
39 let (line, _): (&str, &str) = match spaces(line) {
40 Ok(obj) => obj,
41 Err(_) => return Err( FlaskError::BadRequest("Malformed Request Line: missing space after target URL".to_string()) )
42 };
43 let (line, version): (&str, &str) = match http_version(line) {
44 Ok(obj) => obj,
45 Err(_) => return Err( FlaskError::BadRequest("Malformed Request Line: bad http version".to_string()) )
46 };
47 match crlf(line) {
48 Ok(_) => Ok(RequestLine {method: method, target: target, version: version}),
49 Err(_) => Err( FlaskError::BadRequest("Malformed Request Line: no terminating CRLF".to_string()) )
50 }
51}
52
53fn _read_initial_request_line(reader: &mut BufReader<TcpStream>) -> Result<Builder, FlaskError> {
54 let mut request = Request::builder();
55
56 let mut line: String = String::from("");
57 match reader.read_line(&mut line) {
58 Ok(_) => {
59 let req_line: RequestLine = match parse_request_line(line.as_str()) {
60 Ok(parsed_line) => parsed_line,
61 Err(_) => {
62 let msg = format!("Malformed first line of request");
63 return Err( FlaskError::BadRequest(msg) );
64 }
65 };
66 let version = get_http_version(req_line.version)?;
67
68 request = request
69 .method(req_line.method)
70 .uri(req_line.target)
71 .version(version);
72 },
73 Err(_) => {}
74 }
75 Ok(request)
76}
77
78
79fn _read_http_request(reader: &mut BufReader<TcpStream>) -> Result<Request<Vec<u8>>, FlaskError> {
80 let mut request = _read_initial_request_line(reader)?;
81
82 let content_length = {
83 let mut content_length_mut = 0;
84 loop {
85 let line: String = read_buffered_line(reader)?;
86 if line.as_str() == "\r\n" {
87 break;
88 }
89
90 let header_line = match read_header(line.as_str()) {
91 Ok(hl) => hl,
92 Err(err) => {
93 return Err( err );
94 }
95 };
96
97 if header_line.key.to_lowercase() == CONTENT_LENGTH_HEADER {
98 match header_line.value.parse::<usize>() {
99 Ok(val) => content_length_mut = val,
100 Err(_) => {
101 let msg = format!("Invalid Content-Length: {}", header_line.value);
102 return Err( FlaskError::BadRequest(msg) );
103 }
104 }
105 }
106 request = request.header(header_line.key, header_line.value);
107 }
108 content_length_mut
109 };
110
111 let mut body = vec![0; content_length];
112 match reader.read_exact(&mut body) {
113 Ok(_) => {
114 match request.body(body) {
115 Ok(req) => Ok(req),
116 Err(http_err) => {
117 eprintln!("ERROR reading request body from stream");
118 let msg: String = http_err.to_string();
119 let flask_err = FlaskError::ClientClosedRequest(msg);
120 Err(flask_err)
121 }
122 }
123 },
124 Err(http_err) => {
125 let msg: String = http_err.to_string();
126 let flask_err = FlaskError::BadRequest(msg);
127 Err(flask_err)
128 }
129 }
130}
131
132
133pub fn read_http_request(stream: TcpStream) -> Result<Request<Vec<u8>>, FlaskError> {
134 let mut reader: BufReader<TcpStream> = BufReader::new(stream);
135 _read_http_request(&mut reader)
136}
137
138#[cfg(test)]
142mod tests {
143 use super::*;
146
147 #[test]
148 fn test_parse_request_line_good() {
149 let line = "POST https://panthip.com HTTP/1.2\r\n";
150 let parsed_line = parse_request_line(line).unwrap();
151 assert_eq!(parsed_line.method, "POST");
152 assert_eq!(parsed_line.target, "https://panthip.com");
153 assert_eq!(parsed_line.version, "1.2");
154 }
165
166 #[test]
167 fn test_parse_request_line_bad_http_method() {
168 let line = "POS https://panthip.com HTTP/1.1\r\n";
169 let result = parse_request_line(line);
170 let flask_err = result.err().unwrap();
171 assert_eq!(flask_err.get_msg(), "Malformed Request Line: missing HTTP method");
172 }
173
174 #[test]
175 fn test_parse_request_line_bad_missing_newline() {
176 let line = "POST https://panthip.com HTTP/1.1\r";
177 let result = parse_request_line(line);
178 let flask_err = result.err().unwrap();
179 assert_eq!(flask_err.get_msg(), "Malformed Request Line: no terminating CRLF");
180 }
181
182 #[test]
183 fn test_parse_request_line_bad_missing_carriage_return() {
184 let line = "POST https://panthip.com HTTP/1.1\n";
185 let result = parse_request_line(line);
186 let flask_err = result.err().unwrap();
187 assert_eq!(flask_err.get_msg(), "Malformed Request Line: no terminating CRLF");
188 }
189
190 #[test]
191 fn test_parse_request_line_bad_missing_crlf() {
192 let line = "POST https://panthip.com HTTP/1.1";
193 let result = parse_request_line(line);
194 let flask_err = result.err().unwrap();
195 assert_eq!(flask_err.get_msg(), "Malformed Request Line: no terminating CRLF");
196 }
197}