gatewayd: strict status lines and chunk lines; no as casts (M4a review, findings 1 and 3)
A status line splits on single spaces only, and every chunk line must end in CRLF. The bounded `as` casts in http.rs, handshake.rs and proto's sha1.rs become try_from and from. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
//! The HTTP reader is strict where it can be: a status line with anything but single spaces, and a
|
||||
//! chunk line not ended by CRLF, are protocol errors (M4a review, finding 3).
|
||||
|
||||
use std::io::Cursor;
|
||||
|
||||
use gatewayd::http::{HttpError, read_body, read_head};
|
||||
|
||||
fn read(response: &[u8]) -> Result<Vec<u8>, HttpError> {
|
||||
let mut c = Cursor::new(response.to_vec());
|
||||
let head = read_head(&mut c)?;
|
||||
read_body(&mut c, &head)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_status_line_has_single_spaces() {
|
||||
for line in [
|
||||
"HTTP/1.1 200 OK",
|
||||
"HTTP/1.1 200 OK",
|
||||
" HTTP/1.1 200 OK",
|
||||
"HTTP/1.1\t200 OK",
|
||||
] {
|
||||
let response = format!("{line}\r\nContent-Length: 0\r\n\r\n");
|
||||
let got = read(response.as_bytes());
|
||||
if line == "HTTP/1.1 200 OK" {
|
||||
// The reason phrase is free text: two spaces inside it are allowed.
|
||||
assert!(got.is_ok(), "{line:?}: {got:?}");
|
||||
} else {
|
||||
assert!(
|
||||
matches!(got, Err(HttpError::Protocol(_))),
|
||||
"{line:?}: {got:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
assert!(
|
||||
read(b"HTTP/1.1 204\r\n\r\n").is_ok(),
|
||||
"a status line without a reason phrase"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chunk_lines_end_in_crlf() {
|
||||
let head = "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n";
|
||||
let good = format!("{head}2\r\nab\r\n0\r\n\r\n");
|
||||
assert_eq!(read(good.as_bytes()).unwrap(), b"ab");
|
||||
for body in [
|
||||
"2\nab\r\n0\r\n\r\n",
|
||||
"2\r\nab\n0\r\n\r\n",
|
||||
"2\r\nab\r\n0\n\r\n",
|
||||
"2\r\nab\r\n0\r\n\n",
|
||||
] {
|
||||
let got = read(format!("{head}{body}").as_bytes());
|
||||
assert!(
|
||||
matches!(got, Err(HttpError::Protocol(_))),
|
||||
"{body:?}: {got:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user