From c2fab212e648e9a59bd0153bb5059bb1d66e50ff Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Fri, 17 Jul 2026 14:34:36 -0700 Subject: [PATCH 1/3] header map translation: do not set FORBIDDEN_HEADERS the set of FORBIDDEN_HEADERS is derived from wasmtime, with the addition of Content-Length, and Expect. --- src/http/fields.rs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/http/fields.rs b/src/http/fields.rs index de6df16..d5a07ec 100644 --- a/src/http/fields.rs +++ b/src/http/fields.rs @@ -18,10 +18,25 @@ pub(crate) fn header_map_from_wasi(wasi_fields: Fields) -> Result Result { let wasi_fields = Fields::new(); for (key, value) in header_map { - // Unwrap because `HeaderMap` has already validated the headers. - wasi_fields - .append(key.as_str(), value.as_bytes()) - .with_context(|| format!("wasi rejected header `{key}: {value:?}`"))? + if !FORBIDDEN_HEADERS.contains(key) { + wasi_fields + .append(key.as_str(), value.as_bytes()) + .with_context(|| format!("wasi rejected header `{key}: {value:?}`"))? + } } Ok(wasi_fields) } + +const FORBIDDEN_HEADERS: [HeaderName; 11] = [ + http::header::CONNECTION, + HeaderName::from_static("keep-alive"), + http::header::PROXY_AUTHENTICATE, + http::header::PROXY_AUTHORIZATION, + HeaderName::from_static("proxy-connection"), + http::header::TRANSFER_ENCODING, + http::header::UPGRADE, + http::header::HOST, + HeaderName::from_static("http2-settings"), + http::header::EXPECT, + http::header::CONTENT_LENGTH, +]; From 0b0692c178c8005e06dd9aa31e49d8f1fd3a4924 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Fri, 17 Jul 2026 14:39:23 -0700 Subject: [PATCH 2/3] server: do not add a Content-Length header --- src/http/server.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/http/server.rs b/src/http/server.rs index 9fb6ff4..c9a396c 100644 --- a/src/http/server.rs +++ b/src/http/server.rs @@ -19,7 +19,6 @@ //! [`http_server`]: crate::http_server use super::{Body, Error, Response, error::ErrorCode, fields::header_map_to_wasi}; -use http::header::CONTENT_LENGTH; use wasip2::exports::http::incoming_handler::ResponseOutparam; use wasip2::http::types::OutgoingResponse; @@ -44,14 +43,6 @@ impl Responder { // Consume the `response` and prepare to write the body. let body = response.into_body().into(); - // Automatically add a Content-Length header. - if let Some(len) = body.content_length() { - let mut buffer = itoa::Buffer::new(); - wasi_headers - .append(CONTENT_LENGTH.as_str(), buffer.format(len).as_bytes()) - .unwrap(); - } - let wasi_response = OutgoingResponse::new(wasi_headers); // Unwrap because `StatusCode` has already validated the status. From 35872be6bb4df3394a9f11d5a0ee5255879d4906 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Mon, 20 Jul 2026 15:01:42 -0700 Subject: [PATCH 3/3] lowercase search --- src/http/fields.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/http/fields.rs b/src/http/fields.rs index d5a07ec..4845f9f 100644 --- a/src/http/fields.rs +++ b/src/http/fields.rs @@ -18,7 +18,12 @@ pub(crate) fn header_map_from_wasi(wasi_fields: Fields) -> Result Result { let wasi_fields = Fields::new(); for (key, value) in header_map { - if !FORBIDDEN_HEADERS.contains(key) { + let key = key.as_str().to_ascii_lowercase(); + if FORBIDDEN_HEADERS + .iter() + .find(|k| k.as_str() == key) + .is_none() + { wasi_fields .append(key.as_str(), value.as_bytes()) .with_context(|| format!("wasi rejected header `{key}: {value:?}`"))? @@ -27,6 +32,7 @@ pub(crate) fn header_map_to_wasi(header_map: &HeaderMap) -> Result