extern crate libc;
extern crate picohttpparser_sys;

use libc::{c_char, c_int, size_t};
use std::mem;
use std::ptr;
use std::slice;
use picohttpparser_sys::*;

fn slice_from_raw<'a>(pointer: *const c_char, len: size_t) -> &'a [u8] {
    unsafe { mem::transmute(slice::from_raw_parts(pointer, len)) }
}

struct ParsedResponse {
    headers: [phr_header; 4],
    num_headers: size_t,
    msg: *const c_char,
    msg_len: size_t,
    status: c_int,
    version: c_int,
    return_code: c_int,
}

impl ParsedResponse {
    fn new() -> Self {
        ParsedResponse {
            headers: [phr_header::default(); 4],
            num_headers: 4,
            msg: ptr::null_mut(),
            msg_len: 0,
            status: 0,
            version: -1,
            return_code: -3,
        }
    }

    fn msg_bytes(&self) -> &[u8] {
        slice_from_raw(self.msg, self.msg_len)
    }
}

fn header_name_bytes<'a>(header: phr_header) -> &'a [u8] {
    slice_from_raw(header.name, header.name_len)
}

fn header_value_bytes<'a>(header: phr_header) -> &'a [u8] {
    slice_from_raw(header.value, header.value_len)
}

fn test_response(buf: &[u8], last_len: size_t) -> ParsedResponse {
    let mut parsed = ParsedResponse::new();

    unsafe {
        parsed.return_code = phr_parse_response(buf.as_ptr() as *const c_char,
                                                buf.len(),
                                                &mut parsed.version,
                                                &mut parsed.status,
                                                &mut parsed.msg,
                                                &mut parsed.msg_len,
                                                parsed.headers.as_mut_ptr(),
                                                &mut parsed.num_headers,
                                                last_len);
    }

    parsed
}

#[test]
fn simple() {
    let buf = b"HTTP/1.0 200 OK\r\n\r\n";
    let parsed = test_response(buf, 0);
    assert_eq!(buf.len() as c_int, parsed.return_code);
    assert_eq!(0, parsed.num_headers);
    assert_eq!(0, parsed.version);
    assert_eq!(200, parsed.status);
    assert_eq!(b"OK", parsed.msg_bytes());
}

#[test]
fn partial() {
    let parsed = test_response(b"HTTP/1.0 200 OK\r\n\r", 0);
    assert_eq!(-2, parsed.return_code);
}

#[test]
fn parse_headers() {
    let buf = b"HTTP/1.1 200 OK\r\nHost: example.com\r\nCookie: \r\n\r\n";
    let parsed = test_response(buf, 0);
    assert_eq!(buf.len() as c_int, parsed.return_code);
    assert_eq!(2, parsed.num_headers);
    assert_eq!(1, parsed.version);
    assert_eq!(200, parsed.status);
    assert_eq!(b"OK", parsed.msg_bytes());
    assert_eq!(b"Host", header_name_bytes(parsed.headers[0]));
    assert_eq!(b"example.com", header_value_bytes(parsed.headers[0]));
    assert_eq!(b"Cookie", header_name_bytes(parsed.headers[1]));
    assert_eq!(b"", header_value_bytes(parsed.headers[1]));
}

#[test]
fn parse_multiline() {
    let buf = b"HTTP/1.0 200 OK\r\nfoo: \r\nfoo: b\r\n  \tc\r\n\r\n";
    let parsed = test_response(buf, 0);
    assert_eq!(buf.len() as c_int, parsed.return_code);
    assert_eq!(3, parsed.num_headers);
    assert_eq!(0, parsed.version);
    assert_eq!(200, parsed.status);
    assert_eq!(b"OK", parsed.msg_bytes());
    assert_eq!(b"foo", header_name_bytes(parsed.headers[0]));
    assert_eq!(b"", header_value_bytes(parsed.headers[0]));
    assert_eq!(b"foo", header_name_bytes(parsed.headers[1]));
    assert_eq!(b"b", header_value_bytes(parsed.headers[1]));
    assert_eq!(ptr::null(), parsed.headers[2].name);
    assert_eq!(b"  \tc", header_value_bytes(parsed.headers[2]));
}

#[test]
fn internal_server_error() {
    let buf = b"HTTP/1.0 500 Internal Server Error\r\n\r\n";
    let parsed = test_response(buf, 0);
    assert_eq!(buf.len() as c_int, parsed.return_code);
    assert_eq!(0, parsed.num_headers);
    assert_eq!(0, parsed.version);
    assert_eq!(500, parsed.status);
    assert_eq!(b"Internal Server Error", parsed.msg_bytes());
    assert_eq!(b"Internal Server Error".len(), parsed.msg_len);
}

#[test]
fn incomplete_1() {
    let parsed = test_response(b"H", 0);
    assert_eq!(-2, parsed.return_code);
}

#[test]
fn incomplete_2() {
    let parsed = test_response(b"HTTP/1.", 0);
    assert_eq!(-2, parsed.return_code);
}

#[test]
fn incomplete_3() {
    let parsed = test_response(b"HTTP/1.1", 0);
    assert_eq!(-2, parsed.return_code);
    assert_eq!(-1, parsed.version);
}

#[test]
fn incomplete_4() {
    let parsed = test_response(b"HTTP/1.1 ", 0);
    assert_eq!(-2, parsed.return_code);
    assert_eq!(1, parsed.version);
}

#[test]
fn incomplete_5() {
    let parsed = test_response(b"HTTP/1.1 2", 0);
    assert_eq!(-2, parsed.return_code);
    assert_eq!(1, parsed.version);
}

#[test]
fn incomplete_6() {
    let parsed = test_response(b"HTTP/1.1 200", 0);
    assert_eq!(-2, parsed.return_code);
    assert_eq!(0, parsed.status);
}

#[test]
fn incomplete_7() {
    let parsed = test_response(b"HTTP/1.1 200 ", 0);
    assert_eq!(-2, parsed.return_code);
    assert_eq!(200, parsed.status);
}

#[test]
fn incomplete_8() {
    let parsed = test_response(b"HTTP/1.1 200 O", 0);
    assert_eq!(-2, parsed.return_code);
    assert_eq!(ptr::null(), parsed.msg);
}

#[test]
fn incomplete_9() {
    let parsed = test_response(b"HTTP/1.1 200 OK\r", 0);
    assert_eq!(-2, parsed.return_code);
    assert_eq!(ptr::null(), parsed.msg);
}

#[test]
fn incomplete_10() {
    let parsed = test_response(b"HTTP/1.1 200 OK\r\n", 0);
    assert_eq!(-2, parsed.return_code);
    assert_eq!(b"OK", parsed.msg_bytes());
}

#[test]
fn incomplete_11() {
    let parsed = test_response(b"HTTP/1.1 200 OK\n", 0);
    assert_eq!(-2, parsed.return_code);
    assert_eq!(b"OK", parsed.msg_bytes());
}

#[test]
fn incomplete_12() {
    let parsed = test_response(b"HTTP/1.1 200 OK\r\nA: 1\r", 0);
    assert_eq!(-2, parsed.return_code);
    assert_eq!(0, parsed.num_headers);
}

#[test]
fn incomplete_13() {
    let parsed = test_response(b"HTTP/1.1 200 OK\r\nA: 1\r\n", 0);
    assert_eq!(-2, parsed.return_code);
    assert_eq!(1, parsed.num_headers);
    assert_eq!(b"A", header_name_bytes(parsed.headers[0]));
    assert_eq!(b"1", header_value_bytes(parsed.headers[0]));
}

#[test]
fn slowloris_incomplete() {
    let buf = b"HTTP/1.0 200 OK \r\n\r";
    let parsed = test_response(buf, buf.len() - 1);
    assert_eq!(-2, parsed.return_code);
}

#[test]
fn slowloris_complete() {
    let buf = b"HTTP/1.0 200 OK \r\n\r\n";
    let parsed = test_response(buf, buf.len() - 1);
    assert_eq!(buf.len() as c_int, parsed.return_code);
}

#[test]
fn invalid_http_version() {
    let parsed = test_response(b"HTTP/1. 200 OK\r\n\r\n", 0);
    assert_eq!(-1, parsed.return_code);
}

#[test]
fn invalid_http_version_2() {
    let parsed = test_response(b"HTTP/1.2z 200 OK\r\n\r\n", 0);
    assert_eq!(-1, parsed.return_code);
}

#[test]
fn no_status_code() {
    let parsed = test_response(b"HTTP/1.1 OK\r\n\r\n", 0);
    assert_eq!(-1, parsed.return_code);
}