use derusted::mitm::{
http_parser::{parse_http1_request, parse_http1_response, ParseError},
logging::PiiRedactor,
};
#[test]
fn test_incomplete_http_request() {
let incomplete_request = b"GET /api/test HTTP/1.1\r\nHost: example.com\r\n";
let result = parse_http1_request(incomplete_request);
match result {
Err(ParseError::Incomplete) => {
}
_ => panic!("Expected ParseError::Incomplete for incomplete request"),
}
}
#[test]
fn test_large_headers() {
let large_value = "x".repeat(10000);
let large_header_request = format!(
"GET /api/test HTTP/1.1\r\nHost: example.com\r\nX-Large-Header: {}\r\n\r\n",
large_value
);
let result = parse_http1_request(large_header_request.as_bytes());
assert!(result.is_ok());
let request = result.unwrap();
assert_eq!(request.method, "GET");
assert_eq!(request.path, "/api/test");
assert!(request.headers.contains_key("x-large-header"));
}
#[test]
fn test_many_headers() {
let mut request_str = String::from("POST /api/data HTTP/1.1\r\nHost: example.com\r\n");
for i in 0..100 {
request_str.push_str(&format!("X-Custom-{}: value{}\r\n", i, i));
}
request_str.push_str("\r\n");
let result = parse_http1_request(request_str.as_bytes());
assert!(result.is_ok());
let request = result.unwrap();
assert_eq!(request.method, "POST");
assert_eq!(request.headers.len(), 101); }
#[test]
fn test_chunked_transfer_encoding_header() {
let chunked_request =
b"POST /api/upload HTTP/1.1\r\nHost: example.com\r\nTransfer-Encoding: chunked\r\n\r\n";
let result = parse_http1_request(chunked_request);
assert!(result.is_ok());
let request = result.unwrap();
assert_eq!(request.method, "POST");
assert_eq!(request.headers.get("transfer-encoding").unwrap(), "chunked");
assert_eq!(request.content_length, None); }
#[test]
fn test_chunked_response_header() {
let chunked_response = b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n";
let result = parse_http1_response(chunked_response);
assert!(result.is_ok());
let response = result.unwrap();
assert_eq!(response.status_code, 200);
assert_eq!(
response.headers.get("transfer-encoding").unwrap(),
"chunked"
);
assert_eq!(response.content_length, None);
}
#[test]
fn test_malformed_http_request() {
let malformed_request = b"INVALID REQUEST FORMAT\r\n\r\n";
let result = parse_http1_request(malformed_request);
assert!(result.is_err());
}
#[test]
fn test_http10_request() {
let http10_request = b"GET /index.html HTTP/1.0\r\nHost: example.com\r\n\r\n";
let result = parse_http1_request(http10_request);
assert!(result.is_ok());
let request = result.unwrap();
assert_eq!(request.method, "GET");
assert_eq!(request.version, "HTTP/1.0");
}
#[test]
fn test_http10_response() {
let http10_response = b"HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n";
let result = parse_http1_response(http10_response);
assert!(result.is_ok());
let response = result.unwrap();
assert_eq!(response.version, "HTTP/1.0");
assert_eq!(response.status_code, 200);
}
#[test]
fn test_unusual_http_methods() {
let methods = ["OPTIONS", "HEAD", "PATCH", "DELETE", "TRACE"];
for method in &methods {
let request_str = format!("{} /api/test HTTP/1.1\r\nHost: example.com\r\n\r\n", method);
let result = parse_http1_request(request_str.as_bytes());
assert!(result.is_ok());
let request = result.unwrap();
assert_eq!(request.method, *method);
}
}
#[test]
fn test_informational_response() {
let info_response = b"HTTP/1.1 100 Continue\r\n\r\n";
let result = parse_http1_response(info_response);
assert!(result.is_ok());
let response = result.unwrap();
assert_eq!(response.status_code, 100);
assert_eq!(response.reason, "Continue");
}
#[test]
fn test_redirect_response() {
let redirect_response =
b"HTTP/1.1 301 Moved Permanently\r\nLocation: https://newsite.com\r\n\r\n";
let result = parse_http1_response(redirect_response);
assert!(result.is_ok());
let response = result.unwrap();
assert_eq!(response.status_code, 301);
assert_eq!(
response.headers.get("location").unwrap(),
"https://newsite.com"
);
}
#[test]
fn test_client_error_response() {
let error_response = b"HTTP/1.1 404 Not Found\r\nContent-Type: text/html\r\n\r\n";
let result = parse_http1_response(error_response);
assert!(result.is_ok());
let response = result.unwrap();
assert_eq!(response.status_code, 404);
assert!(response.is_error());
}
#[test]
fn test_server_error_response() {
let error_response = b"HTTP/1.1 500 Internal Server Error\r\nContent-Type: text/plain\r\n\r\n";
let result = parse_http1_response(error_response);
assert!(result.is_ok());
let response = result.unwrap();
assert_eq!(response.status_code, 500);
assert!(response.is_error());
}
#[test]
fn test_request_with_root_path() {
let root_request = b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n";
let result = parse_http1_request(root_request);
assert!(result.is_ok());
let request = result.unwrap();
assert_eq!(request.path, "/");
}
#[test]
fn test_long_path() {
let long_path = format!("/api/{}/data", "segment/".repeat(100));
let request_str = format!("GET {} HTTP/1.1\r\nHost: example.com\r\n\r\n", long_path);
let result = parse_http1_request(request_str.as_bytes());
assert!(result.is_ok());
let request = result.unwrap();
assert!(request.path.starts_with("/api/"));
assert!(request.path.contains("segment"));
}
#[test]
fn test_pii_redaction_unicode() {
let path_with_unicode = "/user?name=José&email=jose@example.com&city=São_Paulo";
let redacted = PiiRedactor::redact(path_with_unicode);
assert!(redacted.contains("[REDACTED]"));
assert!(!redacted.contains("jose@example.com"));
assert!(redacted.contains("José") || redacted.contains("Jose"));
}
#[test]
fn test_pii_redaction_url_encoded() {
let path_encoded = "/search?q=test%20query&email=user%40example.com";
let redacted = PiiRedactor::redact(path_encoded);
assert!(path_encoded.contains("%40")); }
#[test]
fn test_empty_body_with_content_length() {
let request = b"POST /api/data HTTP/1.1\r\nHost: example.com\r\nContent-Length: 0\r\n\r\n";
let result = parse_http1_request(request);
assert!(result.is_ok());
let req = result.unwrap();
assert_eq!(req.content_length, Some(0));
assert!(req.body_preview.is_empty());
}
#[test]
fn test_multiple_content_length() {
let request = b"POST /api/data HTTP/1.1\r\nHost: example.com\r\nContent-Length: 10\r\nContent-Length: 20\r\n\r\n";
let result = parse_http1_request(request);
assert!(result.is_ok() || result.is_err());
}
#[test]
fn test_response_no_reason_phrase() {
let response = b"HTTP/1.1 200\r\nContent-Type: text/plain\r\n\r\n";
let result = parse_http1_response(response);
match result {
Ok(resp) => {
assert_eq!(resp.status_code, 200);
}
Err(_) => {
}
}
}
#[test]
fn test_header_case_insensitivity() {
let request = b"GET /api/test HTTP/1.1\r\nHost: example.com\r\nContent-Type: application/json\r\ncontent-length: 100\r\n\r\n";
let result = parse_http1_request(request);
assert!(result.is_ok());
let req = result.unwrap();
assert!(req.headers.contains_key("content-type"));
assert!(req.headers.contains_key("content-length"));
}
#[test]
fn test_header_value_whitespace() {
let request = b"GET /api/test HTTP/1.1\r\nHost: example.com\r\nAuthorization: Bearer token123 \r\n\r\n";
let result = parse_http1_request(request);
assert!(result.is_ok());
let req = result.unwrap();
let auth = req.headers.get("authorization").unwrap();
assert!(auth.contains("Bearer"));
assert!(auth.contains("token123"));
}