flask/httpx/
request.rs

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//#################################################################################################################
139// test cases go below here
140//#################################################################################################################
141#[cfg(test)]
142mod tests {
143  // extern crate rand;
144
145  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    // match  {
155    //   Ok(parsed_line) => {
156    //     assert_eq!(parsed_line.method, "POST");
157    //     assert_eq!(parsed_line.target, "https://panthip.com");
158    //     assert_eq!(parsed_line.version, "1.2");
159    //   },
160    //   Err(e) => {
161    //     assert_eq!(true, false);
162    //   }
163    // }
164  }
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}