Skip to content

Commit

Permalink
Add fields.has to query if a header is present in a fields (#7625) (
Browse files Browse the repository at this point in the history
#7641)

* Make `fields.get` return an option

* Add `fields.has`

* Sync with the upstream wasi-http types.wit

* Revert changes to rename duration to ms
  • Loading branch information
elliottt authored Dec 6, 2023
1 parent 494e2b8 commit debce60
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 13 deletions.
4 changes: 2 additions & 2 deletions crates/test-programs/src/bin/api_proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ 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"
);

assert!(req_hdrs.delete(&header).is_err());
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"
);

Expand Down
20 changes: 17 additions & 3 deletions crates/wasi-http/src/types_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,20 +145,34 @@ impl<T: WasiHttpView> crate::bindings::http::types::HostFields for T {
fields: Resource<HostFields>,
name: String,
) -> wasmtime::Result<Vec<Vec<u8>>> {
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<HostFields>, name: String) -> wasmtime::Result<bool> {
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<HostFields>,
Expand Down
16 changes: 12 additions & 4 deletions crates/wasi-http/wit/deps/http/types.wit
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,16 @@ interface types {
entries: list<tuple<field-key,field-value>>
) -> result<fields, header-error>;

/// 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<field-value>;

/// 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.
///
Expand All @@ -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
Expand Down Expand Up @@ -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`.
Expand Down
16 changes: 12 additions & 4 deletions crates/wasi/wit/deps/http/types.wit
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,16 @@ interface types {
entries: list<tuple<field-key,field-value>>
) -> result<fields, header-error>;

/// 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<field-value>;

/// 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.
///
Expand All @@ -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
Expand Down Expand Up @@ -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`.
Expand Down

0 comments on commit debce60

Please sign in to comment.