1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
mod util;

use http::Request;
use http::Uri;

/// Parses a buffer and returns a Request whose body is a byte slice, if everything goes successful.
/// An error is returned in case the buffer content doesn't conform with HTTP message structure.
/// # Examples
///
/// ```
/// use saf_httparser::request_from_bytes;
///
/// let buffer = b"GET /somepath HTTP/1.1\r\nHost: www.awesomehost.com\r\n\r\nRequest body";
/// let request = request_from_bytes(buffer).unwrap();
/// ```
pub fn request_from_bytes(buffer: &[u8]) -> Result<Request<&[u8]>, String> {
  let parts = util::parse_first_line(buffer);
  let (method, version, path, end) = match parts {
    (Some(method), Some(version), Some(path), end) => (method, version, path, end),
    _ => return Err(String::from("Error processing request line"))
  };
  println!("Request line finished at {}", end);
  let (headers, end) = match util::parse_headers(buffer, end) {
    (Some(headers), end) => (headers, end),
    _ => return Err(String::from("Error processig headers"))
  };
  println!("Headers finished at {}", end);

  let mut request = Request::builder()
    .method(method)
    .version(version);
  println!("Headers: {:?}", headers);
  let uri  = Uri::builder()
    .scheme("http")
    .authority(headers.get("Host").unwrap().as_bytes())
    .path_and_query(path).build();
  
  let uri = match uri {
    Ok(u) => u,
    Err(err) => return Err(err.to_string())
  };

  for (name, value) in headers {
    match name {
      Some(name) => {
        request = request.header(name, value);
      },
      None => return Err(String::from("Error processing header"))
    }
  };

  let body = util::parse_body(buffer, end);

  match request.uri(uri).body::<&[u8]>(body) {
    Ok(req) => Ok(req),
    Err(_) => Err(String::from("Error building request body"))
  }
}

/// Same as `request_from_bytes`, except that this function takes the message content as a string slice. More convenient if you
/// don't want to call it like `request_from_bytes(buffer.as_bytes())`.
pub fn request_from_str(buffer: &str) -> Result<Request<&[u8]>, String> {
  request_from_bytes(buffer.as_bytes())
}

#[cfg(test)]
mod tests {

  use http::Version;

  #[test]
  fn parse_request() {
    let example_request = b"GET /bora/is HTTP/1.1\r\n\
                                    Host:www.bora-is-awesome.com\r\n\
                                    Content-Type:text/plain\r\n\r\nbora is awesome";
    let request = super::request_from_bytes(example_request);
    let request = match request {
      Ok(req) => req,
      Err(err) => panic!(err)
    };

    assert_eq!(request.method().as_str(), "GET");
    assert_eq!(request.uri().path(), "/bora/is");
    assert_eq!(request.version(), Version::HTTP_11);
    assert_eq!(request.headers().get("host").unwrap(), &"www.bora-is-awesome.com");
    assert_eq!(request.headers().get("content-type").unwrap(), &"text/plain");
    assert_eq!(request.body(), b"bora is awesome");
  }
}