From fa9a6622f363c85e1bde961ac3e451ffeb565875 Mon Sep 17 00:00:00 2001 From: Trevor Elliott Date: Fri, 1 Dec 2023 16:19:42 -0800 Subject: [PATCH 1/4] Make `fields.get` return an option --- crates/test-programs/src/bin/api_proxy.rs | 4 ++-- crates/wasi-http/src/types_impl.rs | 17 +++++++++++------ crates/wasi-http/wit/deps/http/types.wit | 6 ++++-- crates/wasi/wit/deps/http/types.wit | 6 ++++-- 4 files changed, 21 insertions(+), 12 deletions(-) diff --git a/crates/test-programs/src/bin/api_proxy.rs b/crates/test-programs/src/bin/api_proxy.rs index 33114699bcf2..daf5dfd1811f 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.get(&header).is_none(), "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.get(&header).is_none(), "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..7f7fd67cab9f 100644 --- a/crates/wasi-http/src/types_impl.rs +++ b/crates/wasi-http/src/types_impl.rs @@ -144,19 +144,24 @@ impl crate::bindings::http::types::HostFields for T { &mut self, fields: Resource, name: String, - ) -> wasmtime::Result>> { + ) -> 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![]), + Err(_) => return Ok(None), }; - let res = get_fields(self.table(), &fields) - .context("[fields_get] getting fields")? - .get_all(header) + if !fields.contains_key(&header) { + return Ok(None); + } + + let res = fields + .get_all(&header) .into_iter() .map(|val| val.as_bytes().to_owned()) .collect(); - Ok(res) + Ok(Some(res)) } fn set( diff --git a/crates/wasi-http/wit/deps/http/types.wit b/crates/wasi-http/wit/deps/http/types.wit index c42f841bf6b3..9c94623292f7 100644 --- a/crates/wasi-http/wit/deps/http/types.wit +++ b/crates/wasi-http/wit/deps/http/types.wit @@ -169,8 +169,10 @@ interface types { entries: list> ) -> result; - /// Get all of the values corresponding to a key. - get: func(name: field-key) -> list; + /// Get all of the values corresponding to a key. Returns a `none` value + /// when the key isn't present, to distinguish from the case where it's + /// present but empty. + get: func(name: field-key) -> option>; /// Set all of the values for a key. Clears any existing values for that /// key, if they have been set. diff --git a/crates/wasi/wit/deps/http/types.wit b/crates/wasi/wit/deps/http/types.wit index c42f841bf6b3..9c94623292f7 100644 --- a/crates/wasi/wit/deps/http/types.wit +++ b/crates/wasi/wit/deps/http/types.wit @@ -169,8 +169,10 @@ interface types { entries: list> ) -> result; - /// Get all of the values corresponding to a key. - get: func(name: field-key) -> list; + /// Get all of the values corresponding to a key. Returns a `none` value + /// when the key isn't present, to distinguish from the case where it's + /// present but empty. + get: func(name: field-key) -> option>; /// Set all of the values for a key. Clears any existing values for that /// key, if they have been set. From 0c8114c917e7194ec67b47ad82959e45bf5c59bc Mon Sep 17 00:00:00 2001 From: Trevor Elliott Date: Mon, 4 Dec 2023 16:05:53 -0800 Subject: [PATCH 2/4] Add `fields.has` --- crates/test-programs/src/bin/api_proxy.rs | 4 ++-- crates/wasi-http/src/types_impl.rs | 17 +++++++++++++---- crates/wasi-http/wit/deps/http/types.wit | 13 +++++++++---- crates/wasi/wit/deps/http/types.wit | 13 +++++++++---- 4 files changed, 33 insertions(+), 14 deletions(-) diff --git a/crates/test-programs/src/bin/api_proxy.rs b/crates/test-programs/src/bin/api_proxy.rs index daf5dfd1811f..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_none(), + !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_none(), + !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 7f7fd67cab9f..74ee18c2ff57 100644 --- a/crates/wasi-http/src/types_impl.rs +++ b/crates/wasi-http/src/types_impl.rs @@ -144,16 +144,16 @@ impl crate::bindings::http::types::HostFields for T { &mut self, fields: Resource, name: String, - ) -> wasmtime::Result>>> { + ) -> 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(None), + Err(_) => return Ok(vec![]), }; if !fields.contains_key(&header) { - return Ok(None); + return Ok(vec![]); } let res = fields @@ -161,7 +161,16 @@ impl crate::bindings::http::types::HostFields for T { .into_iter() .map(|val| val.as_bytes().to_owned()) .collect(); - Ok(Some(res)) + 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( diff --git a/crates/wasi-http/wit/deps/http/types.wit b/crates/wasi-http/wit/deps/http/types.wit index 9c94623292f7..ad7f9ade3d44 100644 --- a/crates/wasi-http/wit/deps/http/types.wit +++ b/crates/wasi-http/wit/deps/http/types.wit @@ -169,10 +169,15 @@ interface types { entries: list> ) -> result; - /// Get all of the values corresponding to a key. Returns a `none` value - /// when the key isn't present, to distinguish from the case where it's - /// present but empty. - get: func(name: field-key) -> option>; + /// 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. diff --git a/crates/wasi/wit/deps/http/types.wit b/crates/wasi/wit/deps/http/types.wit index 9c94623292f7..ad7f9ade3d44 100644 --- a/crates/wasi/wit/deps/http/types.wit +++ b/crates/wasi/wit/deps/http/types.wit @@ -169,10 +169,15 @@ interface types { entries: list> ) -> result; - /// Get all of the values corresponding to a key. Returns a `none` value - /// when the key isn't present, to distinguish from the case where it's - /// present but empty. - get: func(name: field-key) -> option>; + /// 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. From be2662893e31d16c5a3c32e475615f68a6744e4c Mon Sep 17 00:00:00 2001 From: Trevor Elliott Date: Mon, 4 Dec 2023 16:09:32 -0800 Subject: [PATCH 3/4] Sync with the upstream wasi-http types.wit --- crates/wasi-http/wit/deps/http/types.wit | 13 +++++++------ crates/wasi/wit/deps/http/types.wit | 13 +++++++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/crates/wasi-http/wit/deps/http/types.wit b/crates/wasi-http/wit/deps/http/types.wit index ad7f9ade3d44..00219e2c1445 100644 --- a/crates/wasi-http/wit/deps/http/types.wit +++ b/crates/wasi-http/wit/deps/http/types.wit @@ -198,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 @@ -315,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`. @@ -329,14 +330,14 @@ interface types { /// Set the timeout for the initial connect to the HTTP Server. An error /// return value indicates that this timeout is not supported. - set-connect-timeout: func(duration: option) -> result; + set-connect-timeout: func(ms: option) -> result; /// The timeout for receiving the first byte of the Response body. first-byte-timeout: func() -> option; /// Set the timeout for receiving the first byte of the Response body. An /// error return value indicates that this timeout is not supported. - set-first-byte-timeout: func(duration: option) -> result; + set-first-byte-timeout: func(ms: option) -> result; /// The timeout for receiving subsequent chunks of bytes in the Response /// body stream. @@ -345,7 +346,7 @@ interface types { /// Set the timeout for receiving subsequent chunks of bytes in the Response /// body stream. An error return value indicates that this timeout is not /// supported. - set-between-bytes-timeout: func(duration: option) -> result; + set-between-bytes-timeout: func(ms: option) -> result; } /// Represents the ability to send an HTTP Response. diff --git a/crates/wasi/wit/deps/http/types.wit b/crates/wasi/wit/deps/http/types.wit index ad7f9ade3d44..00219e2c1445 100644 --- a/crates/wasi/wit/deps/http/types.wit +++ b/crates/wasi/wit/deps/http/types.wit @@ -198,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 @@ -315,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`. @@ -329,14 +330,14 @@ interface types { /// Set the timeout for the initial connect to the HTTP Server. An error /// return value indicates that this timeout is not supported. - set-connect-timeout: func(duration: option) -> result; + set-connect-timeout: func(ms: option) -> result; /// The timeout for receiving the first byte of the Response body. first-byte-timeout: func() -> option; /// Set the timeout for receiving the first byte of the Response body. An /// error return value indicates that this timeout is not supported. - set-first-byte-timeout: func(duration: option) -> result; + set-first-byte-timeout: func(ms: option) -> result; /// The timeout for receiving subsequent chunks of bytes in the Response /// body stream. @@ -345,7 +346,7 @@ interface types { /// Set the timeout for receiving subsequent chunks of bytes in the Response /// body stream. An error return value indicates that this timeout is not /// supported. - set-between-bytes-timeout: func(duration: option) -> result; + set-between-bytes-timeout: func(ms: option) -> result; } /// Represents the ability to send an HTTP Response. From 895cc50a3cf1d26fea8b88ead74b9bdc758cef2b Mon Sep 17 00:00:00 2001 From: Trevor Elliott Date: Mon, 4 Dec 2023 23:19:40 -0800 Subject: [PATCH 4/4] Revert changes to rename duration to ms --- crates/wasi-http/wit/deps/http/types.wit | 6 +++--- crates/wasi/wit/deps/http/types.wit | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/wasi-http/wit/deps/http/types.wit b/crates/wasi-http/wit/deps/http/types.wit index 00219e2c1445..0f698e769e96 100644 --- a/crates/wasi-http/wit/deps/http/types.wit +++ b/crates/wasi-http/wit/deps/http/types.wit @@ -330,14 +330,14 @@ interface types { /// Set the timeout for the initial connect to the HTTP Server. An error /// return value indicates that this timeout is not supported. - set-connect-timeout: func(ms: option) -> result; + set-connect-timeout: func(duration: option) -> result; /// The timeout for receiving the first byte of the Response body. first-byte-timeout: func() -> option; /// Set the timeout for receiving the first byte of the Response body. An /// error return value indicates that this timeout is not supported. - set-first-byte-timeout: func(ms: option) -> result; + set-first-byte-timeout: func(duration: option) -> result; /// The timeout for receiving subsequent chunks of bytes in the Response /// body stream. @@ -346,7 +346,7 @@ interface types { /// Set the timeout for receiving subsequent chunks of bytes in the Response /// body stream. An error return value indicates that this timeout is not /// supported. - set-between-bytes-timeout: func(ms: option) -> result; + set-between-bytes-timeout: func(duration: option) -> result; } /// Represents the ability to send an HTTP Response. diff --git a/crates/wasi/wit/deps/http/types.wit b/crates/wasi/wit/deps/http/types.wit index 00219e2c1445..0f698e769e96 100644 --- a/crates/wasi/wit/deps/http/types.wit +++ b/crates/wasi/wit/deps/http/types.wit @@ -330,14 +330,14 @@ interface types { /// Set the timeout for the initial connect to the HTTP Server. An error /// return value indicates that this timeout is not supported. - set-connect-timeout: func(ms: option) -> result; + set-connect-timeout: func(duration: option) -> result; /// The timeout for receiving the first byte of the Response body. first-byte-timeout: func() -> option; /// Set the timeout for receiving the first byte of the Response body. An /// error return value indicates that this timeout is not supported. - set-first-byte-timeout: func(ms: option) -> result; + set-first-byte-timeout: func(duration: option) -> result; /// The timeout for receiving subsequent chunks of bytes in the Response /// body stream. @@ -346,7 +346,7 @@ interface types { /// Set the timeout for receiving subsequent chunks of bytes in the Response /// body stream. An error return value indicates that this timeout is not /// supported. - set-between-bytes-timeout: func(ms: option) -> result; + set-between-bytes-timeout: func(duration: option) -> result; } /// Represents the ability to send an HTTP Response.