extern crate thhp;
use thhp::*;
#[cfg(test)]
mod request {
use crate::*;
macro_rules! good {
($buf:expr) => {
good!($buf, |_req| {})
};
($buf:expr, | $req:ident | $body:expr) => {{
let mut headers = Vec::<HeaderField>::with_capacity(10);
match Request::parse($buf, &mut headers) {
Ok(Complete((req, c))) => {
assert_eq!(c, $buf.len());
closure(req);
}
_ => assert!(false),
}
fn closure($req: Request) {
$body
}
}};
}
macro_rules! fail {
($buf:expr, $err:ident) => {{
let mut headers = Vec::<HeaderField>::with_capacity(1);
let r = Request::parse($buf, &mut headers);
assert!(r.is_err());
assert_eq!(r.err().unwrap(), $err);
}};
}
macro_rules! invalid_method {
($parse:expr) => {
fail!($parse, InvalidMethod)
};
}
macro_rules! invalid_path {
($parse:expr) => {
fail!($parse, InvalidPath)
};
}
macro_rules! invalid_version {
($parse:expr) => {
fail!($parse, InvalidVersion)
};
}
macro_rules! invalid_field_name {
($parse:expr) => {
fail!($parse, InvalidFieldName)
};
}
macro_rules! invalid_field_value {
($parse:expr) => {
fail!($parse, InvalidFieldValue)
};
}
macro_rules! invalid_new_line {
($parse:expr) => {
fail!($parse, InvalidNewLine)
};
}
macro_rules! out_of_capacity {
($parse:expr) => {
fail!($parse, OutOfCapacity)
};
}
macro_rules! incomplete {
($buf:expr) => {{
let mut headers = Vec::<HeaderField>::with_capacity(10);
let r = Request::parse($buf, &mut headers);
assert!(r.is_ok());
assert!(r.unwrap().is_incomplete());
}};
}
#[test]
fn simple_request() {
good!(b"GET / HTTP/1.1\r\n\r\n", |req| {
assert_eq!(req.method, "GET");
assert_eq!(req.target, "/");
assert_eq!(req.minor_version, 1);
assert_eq!(req.headers.len(), 0);
});
}
#[test]
fn simple_request_with_headers() {
good!(b"GET / HTTP/1.1\r\na:b\r\nc:d\r\n\r\n", |req| {
assert_eq!(req.method, "GET");
assert_eq!(req.target, "/");
assert_eq!(req.minor_version, 1);
assert_eq!(req.headers.len(), 2);
assert_eq!(req.headers[0].name, "a");
assert_eq!(req.headers[0].value, "b");
assert_eq!(req.headers[1].name, "c");
assert_eq!(req.headers[1].value, "d");
});
}
#[test]
fn accept_various_new_lines() {
good!(b"GET / HTTP/1.1\n\n");
good!(b"GET / HTTP/1.1\r\n\n");
good!(b"GET / HTTP/1.1\n\r\n");
good!(b"GET / HTTP/1.1\r\na:b\r\n\n");
good!(b"GET / HTTP/1.1\r\na:b\n\n");
good!(b"GET / HTTP/1.1\r\na:b\n\r\n");
}
#[test]
fn skip_front_new_lines() {
good!(b"\r\nGET / HTTP/1.1\r\na:b\n\r\n");
good!(b"\r\n\r\nGET / HTTP/1.1\r\na:b\n\r\n");
good!(b"\nGET / HTTP/1.1\r\na:b\n\r\n");
good!(b"\n\nGET / HTTP/1.1\r\na:b\n\r\n");
}
#[test]
fn bad_request() {
invalid_method!(b"G\x01ET / HTTP/1.1\r\n\r\n");
invalid_path!(b"GET /a\x01ef HTTP/1.1\r\n\r\n");
invalid_version!(b"GET / H\r\n\r\n");
invalid_version!(b"GET / HOGE\r\n\r\n");
invalid_version!(b"GET / HTTP/11.1\r\n\r\n");
invalid_version!(b"GET / HTTP/A.1\r\n\r\n");
invalid_version!(b"GET / HTTP/1.A\r\n\r\n");
invalid_version!(b"GET / HTTP/1.A\r\n\r\n");
invalid_version!(b"GET / HTTP/1.1A\r\n\r\n");
invalid_field_name!(b"GET / HTTP/1.1\r\na\x01b:xyz\r\n\r\n");
invalid_field_value!(b"GET / HTTP/1.1\r\nabc:x\x01z\r\n\r\n");
invalid_new_line!(b"GET / HTTP/1.1\r\nabc:xyz\ra\n\r\n");
invalid_new_line!(b"GET / HTTP/1.1\r\nabc:xyz\r\n\ra\n");
invalid_new_line!(b"\rGET / HTTP/1.1\r\n\r\n");
out_of_capacity!(b"GET / HTTP/1.1\r\na:b\r\nc:d\r\n\r\n");
}
#[test]
fn incomplete_request() {
incomplete!(b"");
incomplete!(b"GET");
incomplete!(b"GET ");
incomplete!(b"GET /");
incomplete!(b"GET / ");
incomplete!(b"GET / HTT");
incomplete!(b"GET / HTTP/1.");
incomplete!(b"GET / HTTP/1.1");
incomplete!(b"GET / HTTP/1.1\r");
incomplete!(b"GET / HTTP/1.1\r\n");
incomplete!(b"GET / HTTP/1.1\r\na");
incomplete!(b"GET / HTTP/1.1\r\na:");
incomplete!(b"GET / HTTP/1.1\r\na:b");
incomplete!(b"GET / HTTP/1.1\r\na:b\r");
incomplete!(b"GET / HTTP/1.1\r\na:b\r\n");
incomplete!(b"GET / HTTP/1.1\r\na:b\r\n\r");
}
}
#[cfg(test)]
mod response {
use crate::*;
macro_rules! good {
($buf:expr) => {
good!($buf, |_res| {})
};
($buf:expr, | $res:ident | $body:expr) => {{
let mut headers = Vec::<HeaderField>::with_capacity(10);
match Response::parse($buf, &mut headers) {
Ok(Complete((res, c))) => {
assert_eq!(c, $buf.len());
closure(res);
}
_ => assert!(false),
}
fn closure($res: Response) {
$body
}
}};
}
macro_rules! fail {
($buf:expr, $err:ident) => {{
let mut headers = Vec::<HeaderField>::with_capacity(1);
let r = Response::parse($buf, &mut headers);
assert!(r.is_err());
assert_eq!(r.err().unwrap(), $err);
}};
}
macro_rules! invalid_version {
($parse:expr) => {
fail!($parse, InvalidVersion)
};
}
macro_rules! invalid_status_code {
($parse:expr) => {
fail!($parse, InvalidStatusCode)
};
}
macro_rules! invalid_reason_phrase {
($parse:expr) => {
fail!($parse, InvalidReasonPhrase)
};
}
macro_rules! out_of_capacity {
($parse:expr) => {
fail!($parse, OutOfCapacity)
};
}
macro_rules! incomplete {
($buf:expr) => {{
let mut headers = Vec::<HeaderField>::with_capacity(10);
let r = Response::parse($buf, &mut headers);
assert!(r.is_ok());
assert!(r.unwrap().is_incomplete());
}};
}
#[test]
fn simple_response() {
good!(b"HTTP/1.1 200 OK\r\n\r\n", |res| {
assert_eq!(res.minor_version, 1);
assert_eq!(res.status, 200);
assert_eq!(res.reason, "OK");
assert_eq!(res.headers.len(), 0);
})
}
#[test]
fn simple_response_with_headers() {
good!(b"HTTP/1.1 200 OK\r\na:b\r\nc:d\r\n\r\n", |res| {
assert_eq!(res.minor_version, 1);
assert_eq!(res.status, 200);
assert_eq!(res.reason, "OK");
assert_eq!(res.headers.len(), 2);
assert_eq!(res.headers[0].name, "a");
assert_eq!(res.headers[0].value, "b");
assert_eq!(res.headers[1].name, "c");
assert_eq!(res.headers[1].value, "d");
})
}
#[test]
fn accept_various_new_lines() {
good!(b"HTTP/1.1 200 OK\n\n");
good!(b"HTTP/1.1 200 OK\r\n\n");
good!(b"HTTP/1.1 200 OK\n\r\n");
good!(b"HTTP/1.1 200 OK\r\na:b\r\n\n");
good!(b"HTTP/1.1 200 OK\r\na:b\n\n");
good!(b"HTTP/1.1 200 OK\r\na:b\n\r\n");
}
#[test]
fn skip_front_new_lines() {
good!(b"\r\nHTTP/1.1 200 OK\r\na:b\n\r\n");
good!(b"\r\n\r\nHTTP/1.1 200 OK\r\na:b\n\r\n");
good!(b"\nHTTP/1.1 200 OK\r\na:b\n\r\n");
good!(b"\n\r\nHTTP/1.1 200 OK\r\na:b\n\r\n");
}
#[test]
fn bad_response() {
invalid_version!(b"ABC\r\n\r\n");
invalid_version!(b"HOGE/1.1 200 OK\r\n\r\n");
invalid_version!(b"HTTP/11.1 200 OK\r\n\r\n");
invalid_version!(b"HTTP/A.1 200 OK\r\n\r\n");
invalid_version!(b"HTTP/1.A 200 OK\r\n\r\n");
invalid_version!(b"HTTP/1.11 200 OK\r\n\r\n");
invalid_status_code!(b"HTTP/1.1 20 OK\r\na:b\r\n\r\n");
invalid_status_code!(b"HTTP/1.1 2000 OK\r\na:b\r\n\r\n");
invalid_status_code!(b"HTTP/1.1 2A00 OK\r\na:b\r\n\r\n");
invalid_status_code!(b"HTTP/1.1 200OK\r\na:b\r\n\r\n");
invalid_reason_phrase!(b"HTTP/1.1 200 O\x01K\r\na:b\r\n\r\n");
invalid_reason_phrase!(b"HTTP/1.1 200 O\x01K\r\na:b\r\n\r\n");
invalid_reason_phrase!(b"HTTP/1.1 200 O\x01K\r\na\x01:b\r\n\r\n");
out_of_capacity!(b"HTTP/1.1 200 OK\r\na:b\r\nc:d\r\n\r\n");
}
#[test]
fn incomplete_response() {
incomplete!(b"");
incomplete!(b"HTT");
incomplete!(b"HTTP/");
incomplete!(b"HTTP/1");
incomplete!(b"HTTP/1.1");
incomplete!(b"HTTP/1.1 ");
incomplete!(b"HTTP/1.1 2");
incomplete!(b"HTTP/1.1 200");
incomplete!(b"HTTP/1.1 200 ");
incomplete!(b"HTTP/1.1 200 O");
incomplete!(b"HTTP/1.1 200 OK");
incomplete!(b"HTTP/1.1 200 OK\r");
incomplete!(b"HTTP/1.1 200 OK\r\n");
incomplete!(b"HTTP/1.1 200 OK\r\na");
incomplete!(b"HTTP/1.1 200 OK\r\na:");
incomplete!(b"HTTP/1.1 200 OK\r\na:b");
incomplete!(b"HTTP/1.1 200 OK\r\na:b\r");
incomplete!(b"HTTP/1.1 200 OK\r\na:b\r\n");
incomplete!(b"HTTP/1.1 200 OK\r\na:b\r\n\r");
}
}