#![allow(clippy::all)]
#[cfg(test)]
mod request_line_regression_tests {
use crate::bytes::BytesMut;
use crate::codec::Decoder;
use crate::http::h1::{Http1Codec, HttpError};
#[test]
fn request_line_standard_methods() {
let test_cases = vec![
"GET /index.html HTTP/1.1\r\n\r\n",
"POST /api/users HTTP/1.1\r\n\r\n",
"PUT /resource HTTP/1.0\r\n\r\n",
"DELETE /item/123 HTTP/1.1\r\n\r\n",
"HEAD /status HTTP/1.1\r\n\r\n",
"OPTIONS * HTTP/1.1\r\n\r\n",
];
for (i, request) in test_cases.iter().enumerate() {
let mut codec = Http1Codec::new();
let mut buf = BytesMut::from(*request);
let result = codec.decode(&mut buf);
assert!(
result.is_ok(),
"Failed to parse standard request {}: {:?}",
i,
request
);
}
}
#[test]
fn request_line_extension_methods() {
let test_cases = vec![
"PATCH /resource HTTP/1.1\r\n\r\n",
"CUSTOMMETHOD /api HTTP/1.1\r\n\r\n",
"MYVERB /endpoint HTTP/1.1\r\n\r\n",
];
for request in test_cases {
let mut codec = Http1Codec::new();
let mut buf = BytesMut::from(request);
let result = codec.decode(&mut buf);
assert!(
result.is_ok(),
"Failed to parse extension method: {}",
request
);
}
}
#[test]
fn request_line_whitespace_handling() {
let test_cases = vec![
"GET /path HTTP/1.1\r\n\r\n",
"POST /api HTTP/1.0\r\n\r\n",
"GET\t/path\tHTTP/1.1\r\n\r\n",
];
for (i, request) in test_cases.iter().enumerate() {
let mut codec = Http1Codec::new();
let mut buf = BytesMut::from(*request);
let result = codec.decode(&mut buf);
if i < 2 {
assert!(
result.is_ok(),
"Failed to parse request with extra spaces: {}",
request
);
} else {
assert!(
result.is_err(),
"Tab characters should be rejected: {}",
request
);
}
}
}
#[test]
fn request_line_version_validation() {
let valid_cases = vec!["GET /test HTTP/1.0\r\n\r\n", "GET /test HTTP/1.1\r\n\r\n"];
let invalid_cases = vec![
"GET /test HTTP/2.0\r\n\r\n",
"GET /test http/1.1\r\n\r\n", "GET /test HTTP/1.2\r\n\r\n",
"GET /test HTTP\r\n\r\n", "GET /test HTTP/1.1.0\r\n\r\n", ];
for request in valid_cases {
let mut codec = Http1Codec::new();
let mut buf = BytesMut::from(request);
let result = codec.decode(&mut buf);
assert!(
result.is_ok(),
"Valid version should be accepted: {}",
request
);
}
for request in invalid_cases {
let mut codec = Http1Codec::new();
let mut buf = BytesMut::from(request);
let result = codec.decode(&mut buf);
assert!(
result.is_err(),
"Invalid version should be rejected: {}",
request
);
}
}
#[test]
fn request_line_length_limits() {
let mut codec = Http1Codec::new();
let long_path = "a".repeat(8000);
let request_ok = format!("GET /{} HTTP/1.1\r\n\r\n", long_path);
let mut buf_ok = BytesMut::from(request_ok.as_str());
let result = codec.decode(&mut buf_ok);
assert!(
result.is_ok(),
"Request under length limit should be accepted"
);
let very_long_path = "a".repeat(9000);
let request_too_long = format!("GET /{} HTTP/1.1\r\n\r\n", very_long_path);
let mut buf_too_long = BytesMut::from(request_too_long.as_str());
let result = codec.decode(&mut buf_too_long);
match result {
Err(HttpError::RequestLineTooLong) => {
}
_ => panic!("Request over length limit should return RequestLineTooLong error"),
}
}
#[test]
fn request_line_crlf_handling() {
let test_cases = vec![
("GET /test HTTP/1.1\r\n\r\n", true),
("GET /test HTTP/1.1\n\n", true),
("GET /test HTTP/1.1\r\r", false),
("GET /test HTTP/1.1\r\nHost: example.com\n\r\n", true),
];
for (request, should_succeed) in test_cases {
let mut codec = Http1Codec::new();
let mut buf = BytesMut::from(request);
let result = codec.decode(&mut buf);
if should_succeed {
assert!(
result.is_ok(),
"Request should parse successfully: {:?}",
request
);
} else {
assert!(
result.is_err(),
"Request should fail to parse: {:?}",
request
);
}
}
}
#[test]
fn request_line_invalid_bytes() {
let invalid_cases = vec![
"G\x00ET /test HTTP/1.1\r\n\r\n",
"GET /test\x01 HTTP/1.1\r\n\r\n",
"GET /test HTTP/1.1\x7F\r\n\r\n",
"GÈT /test HTTP/1.1\r\n\r\n",
];
for request in invalid_cases {
let mut codec = Http1Codec::new();
let mut buf = BytesMut::from(request);
let result = codec.decode(&mut buf);
assert!(
result.is_err(),
"Invalid bytes should be rejected: {:?}",
request.escape_debug()
);
}
}
#[test]
fn request_line_percent_encoding() {
let test_cases = vec![
"GET /path%20with%20spaces HTTP/1.1\r\n\r\n",
"GET /encoded%2Fslash HTTP/1.1\r\n\r\n",
"GET /%3Fquery%3Dvalue HTTP/1.1\r\n\r\n",
];
for request in test_cases {
let mut codec = Http1Codec::new();
let mut buf = BytesMut::from(request);
let result = codec.decode(&mut buf);
assert!(
result.is_ok(),
"Percent-encoded path should parse: {}",
request
);
}
}
#[test]
fn request_line_malformed() {
let malformed_cases = vec![
"GET\r\n\r\n",
"GET /test\r\n\r\n",
"/test HTTP/1.1\r\n\r\n",
"GET /test HTTP/1.1 EXTRA\r\n\r\n",
" /test HTTP/1.1\r\n\r\n",
"GET HTTP/1.1\r\n\r\n",
"GET /test \r\n\r\n",
];
for request in malformed_cases {
let mut codec = Http1Codec::new();
let mut buf = BytesMut::from(request);
let result = codec.decode(&mut buf);
assert!(
result.is_err(),
"Malformed request should be rejected: {:?}",
request
);
}
}
#[test]
fn request_line_exact_boundary() {
let method = "GET ";
let version = " HTTP/1.1";
let path_len = 8192 - method.len() - version.len();
let path = format!("/{}", "a".repeat(path_len - 1));
let request_line = format!("{}{}{}\r\n\r\n", method, path, version);
assert_eq!(request_line.find("\r\n").unwrap(), 8192);
let mut codec = Http1Codec::new();
let mut buf = BytesMut::from(request_line.as_str());
let result = codec.decode(&mut buf);
assert!(
result.is_ok(),
"Request line exactly at limit should be accepted"
);
}
}