sark-core 0.8.7

the L7 layer for dope
Documentation
use sark_core::http::codec::Parse;

#[test]
fn empty_input() {
    let raw = b"";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_ok());
    assert!(result.unwrap().is_none());
}

#[test]
fn single_byte_input() {
    let raw = b"H";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_ok());
    assert!(result.unwrap().is_none());
}

#[test]
fn headers_only_no_body_separator() {
    let raw = b"HTTP/1.1 200 OK\r\nContent-Length: 5";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_ok());
    assert!(result.unwrap().is_none());
}

#[test]
fn content_length_zero_vs_absent() {
    let raw_zero = b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n";
    let resp_zero = Parse::response(raw_zero, sark_core::http::codec::DecodeMode::Response)
        .unwrap()
        .unwrap();
    assert!(resp_zero.body().is_empty());

    let raw_absent = b"HTTP/1.1 200 OK\r\n\r\n";
    let result_absent =
        Parse::response(raw_absent, sark_core::http::codec::DecodeMode::Response).unwrap();
    assert!(result_absent.is_none());
}

#[test]
fn content_length_larger_than_actual_body() {
    let raw = b"HTTP/1.1 200 OK\r\nContent-Length: 100\r\n\r\nhello";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_ok());
    assert!(result.unwrap().is_none());
}

#[test]
fn content_length_non_numeric() {
    let raw = b"HTTP/1.1 200 OK\r\nContent-Length: abc\r\n\r\nhello";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_err());
}

#[test]
fn content_length_negative() {
    let raw = b"HTTP/1.1 200 OK\r\nContent-Length: -5\r\n\r\nhello";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_err());
}

#[test]
fn content_length_overflow() {
    let raw = b"HTTP/1.1 200 OK\r\nContent-Length: 99999999999999999999999999999\r\n\r\nhello";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_err());
}

#[test]
fn multiple_content_length_headers() {
    let raw = b"HTTP/1.1 200 OK\r\nContent-Length: 5\r\nContent-Length: 10\r\n\r\nhello";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_err());
}

#[test]
fn chunked_invalid_hex_size() {
    let raw = b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\nGGG\r\nhello\r\n0\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_err());
}

#[test]
fn chunked_missing_terminator() {
    let raw = b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nhello\r\n0";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_ok());
    assert!(result.unwrap().is_none());
}

#[test]
fn chunked_extremely_large_chunk_size() {
    let raw = b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\nFFFFFFFFFFFFFFFF\r\nhello\r\n0\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_err());
}

#[test]
fn mixed_content_length_and_transfer_encoding() {
    let raw = b"HTTP/1.1 200 OK\r\nContent-Length: 100\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nhello\r\n0\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_err());
}

#[test]
fn status_line_missing_status_code() {
    let raw = b"HTTP/1.1  OK\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    let _ = result;
}

#[test]
fn status_line_invalid_status_code() {
    let raw = b"HTTP/1.1 999 Custom\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_ok());
}

#[test]
fn header_with_empty_name() {
    let raw = b"HTTP/1.1 200 OK\r\n: value\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_err());
}

#[test]
fn header_with_empty_value() {
    let raw = b"HTTP/1.1 200 OK\r\nX-Empty:\r\nContent-Length: 0\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_ok());
    let resp = result.unwrap().unwrap();
    assert_eq!(resp.headers().get("x-empty").unwrap(), "");
}

#[test]
fn very_long_header_value() {
    let long_value = "x".repeat(10240);
    let raw = format!(
        "HTTP/1.1 200 OK\r\nX-Long: {}\r\nContent-Length: 0\r\n\r\n",
        long_value
    );
    let result = Parse::response(raw.as_bytes(), sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_ok());
    let resp = result.unwrap().unwrap();
    assert_eq!(resp.headers().get("x-long").unwrap().len(), 10240);
}

#[test]
fn binary_data_in_header_value() {
    let raw = b"HTTP/1.1 200 OK\r\nX-Binary: \x00\x01\x02\xFF\r\nContent-Length: 0\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_err());
}

#[test]
fn response_with_100_headers() {
    let mut raw = String::from("HTTP/1.1 200 OK\r\n");
    for i in 0..99 {
        raw.push_str(&format!("X-Header-{}: value\r\n", i));
    }
    raw.push_str("Content-Length: 0\r\n\r\n");

    let result = Parse::response(raw.as_bytes(), sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_ok());
    let resp = result.unwrap().unwrap();
    assert_eq!(resp.headers().len(), 100);
}

#[test]
fn response_exceeding_max_headers() {
    let mut raw = String::from("HTTP/1.1 200 OK\r\n");
    for i in 0..101 {
        raw.push_str(&format!("X-Header-{}: value\r\n", i));
    }
    raw.push_str("\r\n");

    let result = Parse::response(raw.as_bytes(), sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_err());
}

#[test]
fn null_byte_in_status_line() {
    let raw = b"HTTP/1.1 200\x00OK\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    if let Ok(Some(resp)) = result {
        assert_eq!(resp.status().as_u16(), 200);
    }
}

#[test]
fn null_byte_in_header_name() {
    let raw = b"HTTP/1.1 200 OK\r\nX-Nu\x00ll: value\r\nContent-Length: 0\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_err());
}

#[test]
fn null_byte_in_header_value() {
    let raw = b"HTTP/1.1 200 OK\r\nX-Test: val\x00ue\r\nContent-Length: 0\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_err());
}

#[test]
fn non_ascii_in_status_reason() {
    let raw = "HTTP/1.1 200 OKâ„¢\r\nContent-Length: 0\r\n\r\n".as_bytes();
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_ok());
}

#[test]
fn http_0_9_style_response() {
    let raw = b"hello world";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    let _ = result;
}

#[test]
fn pipelined_responses_extra_data() {
    let raw = b"HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nhelloHTTP/1.1 200 OK\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_ok());
    let resp = result.unwrap().unwrap();
    assert_eq!(resp.body_str(), Some("hello"));
}

#[test]
fn chunked_with_whitespace_in_size() {
    let raw = b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n  5  \r\nhello\r\n0\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_err());
}

#[test]
fn chunked_zero_size_without_terminal_crlf() {
    let raw = b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nhello\r\n0\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_ok());
    assert!(result.unwrap().is_none());
}

#[test]
fn content_length_with_leading_zeros() {
    let raw = b"HTTP/1.1 200 OK\r\nContent-Length: 0005\r\n\r\nhello";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_ok());
    let resp = result.unwrap().unwrap();
    assert_eq!(resp.body_str(), Some("hello"));
}

#[test]
fn content_length_with_whitespace() {
    let raw = b"HTTP/1.1 200 OK\r\nContent-Length:  5  \r\n\r\nhello";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_ok());
    let resp = result.unwrap().unwrap();
    assert_eq!(resp.body_str(), Some("hello"));
}

#[test]
fn decode_after_eof_with_no_headers() {
    let raw = b"HTTP/1.1 200 OK\r\n\r\n";
    let result = Parse::response_after_eof(raw);
    assert!(result.is_ok());
    let resp = result.unwrap();
    assert!(resp.body().is_empty());
}

#[test]
fn decode_after_eof_chunked_incomplete() {
    let raw = b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nhel";
    let result = Parse::response_after_eof(raw);
    assert!(result.is_err());
}

#[test]
fn chunked_with_multiple_extensions() {
    let raw = b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n5;ext1=val1;ext2=val2\r\nhello\r\n0\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_ok());
    let resp = result.unwrap().unwrap();
    assert_eq!(resp.body_str(), Some("hello"));
}

#[test]
fn chunked_lowercase_hex() {
    let raw =
        b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\nf\r\n012345678901234\r\n0\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_ok());
    let resp = result.unwrap().unwrap();
    assert_eq!(resp.body_str(), Some("012345678901234"));
}

#[test]
fn status_code_out_of_range() {
    let raw = b"HTTP/1.1 1000 Custom\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_err());
}

#[test]
fn header_continuation_line() {
    let raw = b"HTTP/1.1 200 OK\r\nX-Long: first\r\n second\r\nContent-Length: 0\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    let _ = result;
}

#[test]
fn crlf_in_chunk_data() {
    let raw = b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n7\r\nab\r\ncd\r\n0\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    match result {
        Ok(Some(resp)) => assert_eq!(resp.body_str(), Some("ab\r\ncd")),
        Ok(None) => {}
        Err(_) => {}
    }
}

#[test]
fn transfer_encoding_case_insensitive() {
    let raw = b"HTTP/1.1 200 OK\r\nTransfer-Encoding: CHUNKED\r\n\r\n5\r\nhello\r\n0\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_ok());
    let resp = result.unwrap().unwrap();
    assert_eq!(resp.body_str(), Some("hello"));
}

#[test]
fn content_length_case_insensitive() {
    let raw = b"HTTP/1.1 200 OK\r\nCONTENT-LENGTH: 5\r\n\r\nhello";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_ok());
    let resp = result.unwrap().unwrap();
    assert_eq!(resp.body_str(), Some("hello"));
}