diff --git a/crates/test-programs/src/bin/api_proxy.rs b/crates/test-programs/src/bin/api_proxy.rs index 33114699bcf2..a0b26cfd722c 100644 --- a/crates/test-programs/src/bin/api_proxy.rs +++ b/crates/test-programs/src/bin/api_proxy.rs @@ -24,7 +24,7 @@ impl bindings::exports::wasi::http::incoming_handler::Guest for T { let req_hdrs = request.headers(); assert!( - req_hdrs.get(&header).is_empty(), + !req_hdrs.has(&header), "forbidden `custom-forbidden-header` found in request" ); @@ -32,7 +32,7 @@ impl bindings::exports::wasi::http::incoming_handler::Guest for T { assert!(req_hdrs.append(&header, &b"no".to_vec()).is_err()); assert!( - req_hdrs.get(&header).is_empty(), + !req_hdrs.has(&header), "append of forbidden header succeeded" ); diff --git a/crates/wasi-http/src/types_impl.rs b/crates/wasi-http/src/types_impl.rs index 4ecb27481f2b..74ee18c2ff57 100644 --- a/crates/wasi-http/src/types_impl.rs +++ b/crates/wasi-http/src/types_impl.rs @@ -145,20 +145,34 @@ impl crate::bindings::http::types::HostFields for T { fields: Resource, name: String, ) -> wasmtime::Result>> { + let fields = get_fields(self.table(), &fields).context("[fields_get] getting fields")?; + let header = match hyper::header::HeaderName::from_bytes(name.as_bytes()) { Ok(header) => header, Err(_) => return Ok(vec![]), }; - let res = get_fields(self.table(), &fields) - .context("[fields_get] getting fields")? - .get_all(header) + if !fields.contains_key(&header) { + return Ok(vec![]); + } + + let res = fields + .get_all(&header) .into_iter() .map(|val| val.as_bytes().to_owned()) .collect(); Ok(res) } + fn has(&mut self, fields: Resource, name: String) -> wasmtime::Result { + let fields = get_fields(self.table(), &fields).context("[fields_get] getting fields")?; + + match hyper::header::HeaderName::from_bytes(name.as_bytes()) { + Ok(header) => Ok(fields.contains_key(&header)), + Err(_) => Ok(false), + } + } + fn set( &mut self, fields: Resource, diff --git a/crates/wasi-http/wit/deps/http/types.wit b/crates/wasi-http/wit/deps/http/types.wit index c42f841bf6b3..0f698e769e96 100644 --- a/crates/wasi-http/wit/deps/http/types.wit +++ b/crates/wasi-http/wit/deps/http/types.wit @@ -169,9 +169,16 @@ interface types { entries: list> ) -> result; - /// Get all of the values corresponding to a key. + /// Get all of the values corresponding to a key. If the key is not present + /// in this `fields`, an empty list is returned. However, if the key is + /// present but empty, this is represented by a list with one or more + /// empty field-values present. get: func(name: field-key) -> list; + /// Returns `true` when the key is present in this `fields`. If the key is + /// syntactically invalid, `false` is returned. + has: func(name: field-key) -> bool; + /// Set all of the values for a key. Clears any existing values for that /// key, if they have been set. /// @@ -191,7 +198,7 @@ interface types { append: func(name: field-key, value: field-value) -> result<_, header-error>; /// Retrieve the full set of keys and values in the Fields. Like the - /// constructor, the list represents each key-value pair. + /// constructor, the list represents each key-value pair. /// /// The outer list represents each key-value pair in the Fields. Keys /// which have multiple values are represented by multiple entries in this @@ -308,8 +315,9 @@ interface types { headers: func() -> headers; } - /// Parameters for making an HTTP Request. Each of these parameters is an - /// optional timeout, applicable to the transport layer of the HTTP protocol. + /// Parameters for making an HTTP Request. Each of these parameters is + /// currently an optional timeout applicable to the transport layer of the + /// HTTP protocol. /// /// These timeouts are separate from any the user may use to bound a /// blocking call to `wasi:io/poll.poll`. diff --git a/crates/wasi/wit/deps/http/types.wit b/crates/wasi/wit/deps/http/types.wit index c42f841bf6b3..0f698e769e96 100644 --- a/crates/wasi/wit/deps/http/types.wit +++ b/crates/wasi/wit/deps/http/types.wit @@ -169,9 +169,16 @@ interface types { entries: list> ) -> result; - /// Get all of the values corresponding to a key. + /// Get all of the values corresponding to a key. If the key is not present + /// in this `fields`, an empty list is returned. However, if the key is + /// present but empty, this is represented by a list with one or more + /// empty field-values present. get: func(name: field-key) -> list; + /// Returns `true` when the key is present in this `fields`. If the key is + /// syntactically invalid, `false` is returned. + has: func(name: field-key) -> bool; + /// Set all of the values for a key. Clears any existing values for that /// key, if they have been set. /// @@ -191,7 +198,7 @@ interface types { append: func(name: field-key, value: field-value) -> result<_, header-error>; /// Retrieve the full set of keys and values in the Fields. Like the - /// constructor, the list represents each key-value pair. + /// constructor, the list represents each key-value pair. /// /// The outer list represents each key-value pair in the Fields. Keys /// which have multiple values are represented by multiple entries in this @@ -308,8 +315,9 @@ interface types { headers: func() -> headers; } - /// Parameters for making an HTTP Request. Each of these parameters is an - /// optional timeout, applicable to the transport layer of the HTTP protocol. + /// Parameters for making an HTTP Request. Each of these parameters is + /// currently an optional timeout applicable to the transport layer of the + /// HTTP protocol. /// /// These timeouts are separate from any the user may use to bound a /// blocking call to `wasi:io/poll.poll`.