diff --git a/Cargo.toml b/Cargo.toml index 22826d9900..501d09bdcf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,10 @@ smallvec = { version = "1.12", features = ["const_generics", "const_new"], optio tracing = { version = "0.1", default-features = false, features = ["std"], optional = true } want = { version = "0.3", optional = true } +[patch.crates-io] +http = { git = "https://github.com/TimvdLippe/rust-http/", branch = "whitespace-characters" } +httparse = { git = "https://github.com/TimvdLippe/rust-httparse", branch = "whitespace-characters" } + [dev-dependencies] form_urlencoded = "1" futures-channel = { version = "0.3", features = ["sink"] } diff --git a/src/proto/h1/role.rs b/src/proto/h1/role.rs index 29bcd44b0d..0579e09581 100644 --- a/src/proto/h1/role.rs +++ b/src/proto/h1/role.rs @@ -47,13 +47,10 @@ macro_rules! header_name { macro_rules! header_value { ($bytes:expr) => {{ { - // unsafe used because of the call of `HeaderValue::from_maybe_shared_unchecked`. - // SAFETY: - // 1. The input `$bytes` must be a valid header value as per RFC 7230. - // 2. Specifically, it must not contain any prohibited characters (like `\r`, `\n`, or non-visible ASCII characters outside of allowed ranges). - // 3. This is safe because the caller is responsible for ensuring the byte content - // has been validated or is known to be a constant/static valid header value. - unsafe { HeaderValue::from_maybe_shared_unchecked($bytes) } + match HeaderValue::from_bytes($bytes) { + Ok(name) => name, + Err(e) => maybe_panic!(e), + } } }}; } @@ -263,7 +260,7 @@ impl Http1Transaction for Server { // SAFETY: array is valid up to `headers_len` let header = unsafe { header.assume_init_ref() }; let name = header_name!(&slice[header.name.0..header.name.1]); - let value = header_value!(slice.slice(header.value.0..header.value.1)); + let value = header_value!(&slice.slice(header.value.0..header.value.1)); match name { header::TRANSFER_ENCODING => { @@ -1120,7 +1117,7 @@ impl Http1Transaction for Client { // SAFETY: array is valid up to `headers_len` let header = unsafe { header.assume_init_ref() }; let name = header_name!(&slice[header.name.0..header.name.1]); - let value = header_value!(slice.slice(header.value.0..header.value.1)); + let value = header_value!(&slice.slice(header.value.0..header.value.1)); if let header::CONNECTION = name { // keep_alive was previously set to default for Version diff --git a/tests/client.rs b/tests/client.rs index b512260cc5..f74cd59180 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -1558,6 +1558,35 @@ test! { body: None, } +test! { + name: client_handle_whitespace_characters, + + server: + expected: "\ + GET / HTTP/1.1\r\n\ + host: {addr}\r\n\ + \r\n\ + ", + reply: "\ + HTTP/1.1 200 OK\r\n\ + Content-Length: 0\r\n\ + Content-Security-Policy: img-src\x0B'none'; default-src\x0C'none';\r\n\ + \r\n\ + ", + + client: + request: { + method: GET, + url: "http://{addr}/", + }, + response: + status: OK, + headers: { + "content-security-policy" => "img-src\x0B'none'; default-src\x0C'none';", + }, + body: None, +} + mod conn { use std::error::Error; use std::io::{self, Read, Write};