From a6db8c201a2f45cf52b335ed3b3772ca94527174 Mon Sep 17 00:00:00 2001 From: markfisher Date: Sat, 27 Jun 2026 20:54:58 -0400 Subject: [PATCH] streaming http-client 0.2.0 over wasi 0.3.0 Signed-off-by: markfisher --- components/Cargo.lock | 112 ++- components/Cargo.toml | 2 +- components/http/client/Cargo.toml | 4 +- components/http/client/src/lib.rs | 235 +++--- .../http/wit/deps/wasi-cli-0.2.6/package.wit | 20 - .../http/wit/deps/wasi-cli-0.3.0/package.wit | 28 + .../wit/deps/wasi-clocks-0.2.6/package.wit | 29 - .../wit/deps/wasi-clocks-0.3.0/package.wit | 43 + .../http/wit/deps/wasi-http-0.2.6/package.wit | 733 ------------------ .../http/wit/deps/wasi-http-0.3.0/package.wit | 509 ++++++++++++ .../http/wit/deps/wasi-io-0.2.6/package.wit | 48 -- .../wit/deps/wasi-random-0.2.6/package.wit | 8 - .../wit/deps/wasi-random-0.3.0/package.wit | 18 + components/http/wit/package.wit | 56 +- components/http/wkg.lock | 6 +- 15 files changed, 821 insertions(+), 1030 deletions(-) delete mode 100644 components/http/wit/deps/wasi-cli-0.2.6/package.wit create mode 100644 components/http/wit/deps/wasi-cli-0.3.0/package.wit delete mode 100644 components/http/wit/deps/wasi-clocks-0.2.6/package.wit create mode 100644 components/http/wit/deps/wasi-clocks-0.3.0/package.wit delete mode 100644 components/http/wit/deps/wasi-http-0.2.6/package.wit create mode 100644 components/http/wit/deps/wasi-http-0.3.0/package.wit delete mode 100644 components/http/wit/deps/wasi-io-0.2.6/package.wit delete mode 100644 components/http/wit/deps/wasi-random-0.2.6/package.wit create mode 100644 components/http/wit/deps/wasi-random-0.3.0/package.wit diff --git a/components/Cargo.lock b/components/Cargo.lock index e605f93..3a7d39d 100644 --- a/components/Cargo.lock +++ b/components/Cargo.lock @@ -46,6 +46,67 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-sink", + "futures-task", + "pin-project-lite", + "slab", +] + [[package]] name = "hashbrown" version = "0.17.0" @@ -63,7 +124,7 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "http-client" -version = "0.1.0" +version = "0.2.0" dependencies = [ "url", "wit-bindgen", @@ -237,6 +298,12 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + [[package]] name = "potential_utf" version = "0.1.5" @@ -322,6 +389,12 @@ dependencies = [ "zmij", ] +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + [[package]] name = "smallvec" version = "1.15.1" @@ -398,9 +471,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "wasm-encoder" -version = "0.247.0" +version = "0.251.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b6733b8b91d010a6ac5b0fb237dc46a19650bc4c67db66857e2e787d437204" +checksum = "5a879a421bd17c528b74721b2abf4c62e8f1d1889c2ba8c3c50d02deaf2ce395" dependencies = [ "leb128fmt", "wasmparser", @@ -408,9 +481,9 @@ dependencies = [ [[package]] name = "wasm-metadata" -version = "0.247.0" +version = "0.251.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "665fe59e56cc9b419ca6fcca56673e3421d1a5011e3b65caf6b726fd9e041d10" +checksum = "5f998ccc6e012f7b86865eb2a106c8a0422017a1a88977ce01a69f2244be2e57" dependencies = [ "anyhow", "indexmap", @@ -420,9 +493,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.247.0" +version = "0.251.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e6fb4c2bee46c5ea4d40f8cdb5c131725cd976718ec56f1c8e82fbde5fa2a80" +checksum = "437970b35b1a85cfde9c74b2398352d8d653f3bd8e3a3db0c063ea8f5b4b36ff" dependencies = [ "bitflags", "hashbrown", @@ -432,19 +505,20 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.57.1" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" +checksum = "a43552cfa071f246cfd99e5dbb23710dfe7336b3259e09339818483359470749" dependencies = [ "bitflags", + "futures", "wit-bindgen-rust-macro", ] [[package]] name = "wit-bindgen-core" -version = "0.57.1" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02dee27a2dc20d1008016c742ec9fc6ea498492994ba3750be7454cbc97ff04c" +checksum = "4738d1c9a78e97bc7f664bfafd5d8e67d7bb26faa5c41e6d628e8bbdad3ec351" dependencies = [ "anyhow", "heck", @@ -453,9 +527,9 @@ dependencies = [ [[package]] name = "wit-bindgen-rust" -version = "0.57.1" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5007dae772945b7a5003d69d90a3a4a78929d41f19d004e980c4259a6af4484" +checksum = "1130ce1f531bc9f9a75922244aa773bf5e2117fda1ef4a86b9f98d6b8135eb46" dependencies = [ "anyhow", "heck", @@ -469,9 +543,9 @@ dependencies = [ [[package]] name = "wit-bindgen-rust-macro" -version = "0.57.1" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af9237d678e3513ad24e96fe98beacdc0db6405284ba2a2400418cf0d42caa89" +checksum = "07296369e4d598e7e79b64eef66f724d83324ea671bcf677d78fc5cf92604ae5" dependencies = [ "anyhow", "macro-string", @@ -485,9 +559,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.247.0" +version = "0.251.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d567162a6b9843080e5e0053f696623ff694bae8ae017c9ec536d1873bbe3d8" +checksum = "83a5e60173c413659c689f0581b0cf5d1a2404077568f9ffdce748a9eb2fc913" dependencies = [ "anyhow", "bitflags", @@ -504,9 +578,9 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.247.0" +version = "0.251.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ffe4064318cdf3c08cb99343b44c039fcefe61ccdf58aa9975285f13d74d1fc" +checksum = "e960732e824fab95099971a09e638979347c94ca48568d3c854c945729196947" dependencies = [ "anyhow", "hashbrown", diff --git a/components/Cargo.toml b/components/Cargo.toml index 579523c..079e0f6 100644 --- a/components/Cargo.toml +++ b/components/Cargo.toml @@ -5,4 +5,4 @@ members = [ resolver = "3" [workspace.dependencies] -wit-bindgen = "0.57" +wit-bindgen = "0.58" diff --git a/components/http/client/Cargo.toml b/components/http/client/Cargo.toml index 733d5d3..10e5fb9 100644 --- a/components/http/client/Cargo.toml +++ b/components/http/client/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "http-client" -version = "0.1.0" +version = "0.2.0" edition = "2024" description = "HTTP client component implementing the composable:http/client interface" homepage = "https://github.com/modulewise/composable-runtime/tree/main/components/http" @@ -12,4 +12,4 @@ crate-type = ["cdylib"] [dependencies] url = "2.5" -wit-bindgen = { workspace = true } +wit-bindgen = { workspace = true, features = ["async-spawn"] } diff --git a/components/http/client/src/lib.rs b/components/http/client/src/lib.rs index 84f9c30..deef355 100644 --- a/components/http/client/src/lib.rs +++ b/components/http/client/src/lib.rs @@ -1,35 +1,36 @@ use url::Url; -use wasi::http::types::{ - Headers, IncomingBody, Method as WasiMethod, OutgoingBody, OutgoingRequest, - RequestOptions as WasiRequestOptions, Scheme, -}; -use wasi::io::streams::StreamError; wit_bindgen::generate!({ path: "../wit", - generate_all + generate_all, }); use exports::composable::http::client::{Guest, HttpResponse, Method, RequestOptions}; +use wasi::http::types::{ + ErrorCode, Fields, Method as WasiMethod, Request as WasiRequest, + RequestOptions as WasiRequestOptions, Response as WasiResponse, Scheme, +}; +use wit_bindgen::rt::async_support::{FutureReader, StreamReader}; + struct HttpClient; impl HttpClient { - fn request( - method: &WasiMethod, - url: &str, + async fn request( + method: WasiMethod, + url: String, headers: Vec<(String, String)>, - body: Option>, + body: StreamReader, options: Option, ) -> Result { - let request_headers = Headers::new(); - for (name, value) in headers { + let request_headers = Fields::new(); + for (name, value) in &headers { request_headers - .append(&name, value.as_bytes()) - .map_err(|e| format!("Failed to set header {name}: {e:?}"))?; + .append(name, value.as_bytes()) + .map_err(|e| format!("Invalid request header {name:?}: {e:?}"))?; } - let parsed = Url::parse(url).map_err(|e| format!("Invalid URL: {e}"))?; + let parsed = Url::parse(&url).map_err(|e| format!("Invalid URL: {e}"))?; let scheme = match parsed.scheme() { "http" => Scheme::Http, "https" => Scheme::Https, @@ -47,9 +48,14 @@ impl HttpClient { None => parsed.path().to_string(), }; - let request = OutgoingRequest::new(request_headers); + let (trailers_tx, trailers_rx) = wit_future::new(|| Ok(None)); + trailers_tx.write(Ok(None)); + + let wasi_options = options.map(wasi_request_options).transpose()?; + let (request, send_result) = + WasiRequest::new(request_headers, Some(body), trailers_rx, wasi_options); request - .set_method(method) + .set_method(&method) .map_err(|()| "Failed to set request method".to_string())?; request .set_scheme(Some(&scheme)) @@ -61,119 +67,60 @@ impl HttpClient { .set_path_with_query(Some(&path_with_query)) .map_err(|()| "Failed to set request path".to_string())?; - if let Some(body_data) = body { - let outgoing_body = request - .body() - .map_err(|()| "Failed to get request body".to_string())?; - let output_stream = outgoing_body - .write() - .map_err(|()| "Failed to get output stream".to_string())?; - output_stream - .blocking_write_and_flush(&body_data) - .map_err(|e| format!("Failed to write request body: {e:?}"))?; - drop(output_stream); - OutgoingBody::finish(outgoing_body, None) - .map_err(|e| format!("wasi:http error: {e:?}"))?; - } - - let max_response_body_bytes = options.as_ref().and_then(|o| o.max_response_body_bytes); - let wasi_options = options.map(wasi_request_options).transpose()?; - let response = wasi::http::outgoing_handler::handle(request, wasi_options) - .map_err(|_| "Failed to send HTTP request".to_string())?; - response.subscribe().block(); - let response = response - .get() - .ok_or("Failed to get response".to_string())? - .map_err(|()| "Request failed".to_string())? - .map_err(|e| format!("wasi:http error: {e:?}"))?; + let response = wasi::http::client::send(request) + .await + .map_err(|e| format!("HTTP request failed: {e:?}"))?; - let status = response.status(); + let status = response.get_status_code(); + let headers = read_fields(&response.get_headers()); - let headers: Vec<(String, String)> = response - .headers() - .entries() - .into_iter() - .map(|(k, v)| (k, v.into_iter().map(|b| b as char).collect())) - .collect(); - - if let Some(max) = max_response_body_bytes - && let Some(content_length) = headers - .iter() - .find(|(k, _)| k.eq_ignore_ascii_case("content-length")) - .and_then(|(_, v)| v.parse::().ok()) - && content_length > max - { - return Err(format!( - "Response Content-Length {content_length} exceeds max size of {max} bytes" - )); - } - - let body = response - .consume() - .map_err(|()| "Failed to consume response body".to_string())?; - let stream = body - .stream() - .map_err(|()| "Failed to get response stream".to_string())?; - let mut body_bytes = Vec::new(); - loop { - match stream.blocking_read(8192) { - Ok(chunk) if chunk.is_empty() => break, - Ok(chunk) => { - body_bytes.extend_from_slice(&chunk); - if let Some(max) = max_response_body_bytes - && body_bytes.len() as u64 > max - { - return Err(format!("Response body exceeded max size of {max} bytes")); - } - } - Err(StreamError::Closed) => break, - Err(StreamError::LastOperationFailed(io_error)) => { - let detail = wasi::http::types::http_error_code(&io_error) - .map(|code| format!("wasi:http error: {code:?}")) - .unwrap_or_else(|| format!("stream error: {io_error:?}")); - return Err(format!("Failed to read response body: {detail}")); - } - } - } - drop(stream); - - let trailers = IncomingBody::finish(body); - trailers.subscribe().block(); - let trailers: Vec<(String, String)> = trailers - .get() - .ok_or("Failed to get trailers".to_string())? - .map_err(|()| "Trailers already consumed".to_string())? - .map_err(|e| format!("wasi:http error: {e:?}"))? - .map(|t| { - t.entries() - .into_iter() - .map(|(k, v)| (k, v.into_iter().map(|b| b as char).collect())) - .collect() - }) - .unwrap_or_default(); + let (body_stream, wasi_trailers) = WasiResponse::consume_body(response, send_result); Ok(HttpResponse { status, headers, - body: body_bytes, - trailers, + body: body_stream, + trailers: map_trailers(wasi_trailers), }) } } +fn read_fields(fields: &Fields) -> Vec<(String, String)> { + fields + .copy_all() + .into_iter() + .map(|(k, v)| (k, v.into_iter().map(|b| b as char).collect())) + .collect() +} + +fn map_trailers( + wasi: FutureReader, ErrorCode>>, +) -> FutureReader, String>> { + let (tx, rx) = wit_future::new(|| Ok(Vec::new())); + wit_bindgen::rt::async_support::spawn_local(async move { + let resolved = match wasi.await { + Ok(Some(t)) => Ok(read_fields(&t)), + Ok(None) => Ok(Vec::new()), + Err(e) => Err(format!("wasi:http error: {e:?}")), + }; + tx.write(resolved); + }); + rx +} + fn wasi_request_options(opts: RequestOptions) -> Result { let r = WasiRequestOptions::new(); if let Some(ms) = opts.connect_timeout_ms { r.set_connect_timeout(Some(ms_to_ns(ms))) - .map_err(|()| "connect-timeout not supported by host".to_string())?; + .map_err(|e| format!("connect-timeout: {e:?}"))?; } if let Some(ms) = opts.first_byte_timeout_ms { r.set_first_byte_timeout(Some(ms_to_ns(ms))) - .map_err(|()| "first-byte-timeout not supported by host".to_string())?; + .map_err(|e| format!("first-byte-timeout: {e:?}"))?; } if let Some(ms) = opts.between_bytes_timeout_ms { r.set_between_bytes_timeout(Some(ms_to_ns(ms))) - .map_err(|()| "between-bytes-timeout not supported by host".to_string())?; + .map_err(|e| format!("between-bytes-timeout: {e:?}"))?; } Ok(r) } @@ -182,84 +129,92 @@ fn ms_to_ns(ms: u32) -> u64 { u64::from(ms) * 1_000_000 } +fn to_wasi_method(method: Method) -> WasiMethod { + match method { + Method::Get => WasiMethod::Get, + Method::Post => WasiMethod::Post, + Method::Put => WasiMethod::Put, + Method::Delete => WasiMethod::Delete, + Method::Patch => WasiMethod::Patch, + Method::Head => WasiMethod::Head, + Method::Options => WasiMethod::Options, + } +} + +fn empty_body() -> StreamReader { + let (tx, rx) = wit_stream::new::(); + drop(tx); + rx +} + impl Guest for HttpClient { - fn request( + async fn request( method: Method, url: String, headers: Vec<(String, String)>, - body: Vec, + body: StreamReader, options: Option, ) -> Result { - let wasi_method = match method { - Method::Get => WasiMethod::Get, - Method::Post => WasiMethod::Post, - Method::Put => WasiMethod::Put, - Method::Delete => WasiMethod::Delete, - Method::Patch => WasiMethod::Patch, - Method::Head => WasiMethod::Head, - Method::Options => WasiMethod::Options, - }; - let body = if body.is_empty() { None } else { Some(body) }; - Self::request(&wasi_method, &url, headers, body, options) + Self::request(to_wasi_method(method), url, headers, body, options).await } - fn get( + async fn get( url: String, headers: Vec<(String, String)>, options: Option, ) -> Result { - Self::request(&WasiMethod::Get, &url, headers, None, options) + Self::request(WasiMethod::Get, url, headers, empty_body(), options).await } - fn post( + async fn post( url: String, headers: Vec<(String, String)>, - body: Vec, + body: StreamReader, options: Option, ) -> Result { - Self::request(&WasiMethod::Post, &url, headers, Some(body), options) + Self::request(WasiMethod::Post, url, headers, body, options).await } - fn put( + async fn put( url: String, headers: Vec<(String, String)>, - body: Vec, + body: StreamReader, options: Option, ) -> Result { - Self::request(&WasiMethod::Put, &url, headers, Some(body), options) + Self::request(WasiMethod::Put, url, headers, body, options).await } - fn delete( + async fn delete( url: String, headers: Vec<(String, String)>, options: Option, ) -> Result { - Self::request(&WasiMethod::Delete, &url, headers, None, options) + Self::request(WasiMethod::Delete, url, headers, empty_body(), options).await } - fn patch( + async fn patch( url: String, headers: Vec<(String, String)>, - body: Vec, + body: StreamReader, options: Option, ) -> Result { - Self::request(&WasiMethod::Patch, &url, headers, Some(body), options) + Self::request(WasiMethod::Patch, url, headers, body, options).await } - fn head( + async fn head( url: String, headers: Vec<(String, String)>, options: Option, ) -> Result { - Self::request(&WasiMethod::Head, &url, headers, None, options) + Self::request(WasiMethod::Head, url, headers, empty_body(), options).await } - fn options( + async fn options( url: String, headers: Vec<(String, String)>, options: Option, ) -> Result { - Self::request(&WasiMethod::Options, &url, headers, None, options) + Self::request(WasiMethod::Options, url, headers, empty_body(), options).await } } diff --git a/components/http/wit/deps/wasi-cli-0.2.6/package.wit b/components/http/wit/deps/wasi-cli-0.2.6/package.wit deleted file mode 100644 index 81e1df3..0000000 --- a/components/http/wit/deps/wasi-cli-0.2.6/package.wit +++ /dev/null @@ -1,20 +0,0 @@ -package wasi:cli@0.2.6; - -interface stdout { - use wasi:io/streams@0.2.6.{output-stream}; - - get-stdout: func() -> output-stream; -} - -interface stderr { - use wasi:io/streams@0.2.6.{output-stream}; - - get-stderr: func() -> output-stream; -} - -interface stdin { - use wasi:io/streams@0.2.6.{input-stream}; - - get-stdin: func() -> input-stream; -} - diff --git a/components/http/wit/deps/wasi-cli-0.3.0/package.wit b/components/http/wit/deps/wasi-cli-0.3.0/package.wit new file mode 100644 index 0000000..d0b02bb --- /dev/null +++ b/components/http/wit/deps/wasi-cli-0.3.0/package.wit @@ -0,0 +1,28 @@ +package wasi:cli@0.3.0; + +interface types { + enum error-code { + io, + illegal-byte-sequence, + pipe, + } +} + +interface stdout { + use types.{error-code}; + + write-via-stream: func(data: stream) -> future>; +} + +interface stderr { + use types.{error-code}; + + write-via-stream: func(data: stream) -> future>; +} + +interface stdin { + use types.{error-code}; + + read-via-stream: func() -> tuple, future>>; +} + diff --git a/components/http/wit/deps/wasi-clocks-0.2.6/package.wit b/components/http/wit/deps/wasi-clocks-0.2.6/package.wit deleted file mode 100644 index 27fd3c1..0000000 --- a/components/http/wit/deps/wasi-clocks-0.2.6/package.wit +++ /dev/null @@ -1,29 +0,0 @@ -package wasi:clocks@0.2.6; - -interface monotonic-clock { - use wasi:io/poll@0.2.6.{pollable}; - - type instant = u64; - - type duration = u64; - - now: func() -> instant; - - resolution: func() -> duration; - - subscribe-instant: func(when: instant) -> pollable; - - subscribe-duration: func(when: duration) -> pollable; -} - -interface wall-clock { - record datetime { - seconds: u64, - nanoseconds: u32, - } - - now: func() -> datetime; - - resolution: func() -> datetime; -} - diff --git a/components/http/wit/deps/wasi-clocks-0.3.0/package.wit b/components/http/wit/deps/wasi-clocks-0.3.0/package.wit new file mode 100644 index 0000000..871adf0 --- /dev/null +++ b/components/http/wit/deps/wasi-clocks-0.3.0/package.wit @@ -0,0 +1,43 @@ +package wasi:clocks@0.3.0; + +interface types { + type duration = u64; +} + +interface monotonic-clock { + use types.{duration}; + + type mark = u64; + + now: func() -> mark; + + get-resolution: func() -> duration; + + wait-until: async func(when: mark); + + wait-for: async func(how-long: duration); +} + +interface system-clock { + use types.{duration}; + + record instant { + seconds: s64, + nanoseconds: u32, + } + + now: func() -> instant; + + get-resolution: func() -> duration; +} + +interface timezone { + use system-clock.{instant}; + + iana-id: func() -> option; + + utc-offset: func(when: instant) -> option; + + to-debug-string: func() -> string; +} + diff --git a/components/http/wit/deps/wasi-http-0.2.6/package.wit b/components/http/wit/deps/wasi-http-0.2.6/package.wit deleted file mode 100644 index eb1b25f..0000000 --- a/components/http/wit/deps/wasi-http-0.2.6/package.wit +++ /dev/null @@ -1,733 +0,0 @@ -package wasi:http@0.2.6; - -/// This interface defines all of the types and methods for implementing -/// HTTP Requests and Responses, both incoming and outgoing, as well as -/// their headers, trailers, and bodies. -@since(version = 0.2.0) -interface types { - @since(version = 0.2.0) - use wasi:clocks/monotonic-clock@0.2.6.{duration}; - @since(version = 0.2.0) - use wasi:io/streams@0.2.6.{input-stream, output-stream}; - @since(version = 0.2.0) - use wasi:io/error@0.2.6.{error as io-error}; - @since(version = 0.2.0) - use wasi:io/poll@0.2.6.{pollable}; - - /// This type corresponds to HTTP standard Methods. - @since(version = 0.2.0) - variant method { - get, - head, - post, - put, - delete, - connect, - options, - trace, - patch, - other(string), - } - - /// This type corresponds to HTTP standard Related Schemes. - @since(version = 0.2.0) - variant scheme { - HTTP, - HTTPS, - other(string), - } - - /// Defines the case payload type for `DNS-error` above: - @since(version = 0.2.0) - record DNS-error-payload { - rcode: option, - info-code: option, - } - - /// Defines the case payload type for `TLS-alert-received` above: - @since(version = 0.2.0) - record TLS-alert-received-payload { - alert-id: option, - alert-message: option, - } - - /// Defines the case payload type for `HTTP-response-{header,trailer}-size` above: - @since(version = 0.2.0) - record field-size-payload { - field-name: option, - field-size: option, - } - - /// These cases are inspired by the IANA HTTP Proxy Error Types: - /// - @since(version = 0.2.0) - variant error-code { - DNS-timeout, - DNS-error(DNS-error-payload), - destination-not-found, - destination-unavailable, - destination-IP-prohibited, - destination-IP-unroutable, - connection-refused, - connection-terminated, - connection-timeout, - connection-read-timeout, - connection-write-timeout, - connection-limit-reached, - TLS-protocol-error, - TLS-certificate-error, - TLS-alert-received(TLS-alert-received-payload), - HTTP-request-denied, - HTTP-request-length-required, - HTTP-request-body-size(option), - HTTP-request-method-invalid, - HTTP-request-URI-invalid, - HTTP-request-URI-too-long, - HTTP-request-header-section-size(option), - HTTP-request-header-size(option), - HTTP-request-trailer-section-size(option), - HTTP-request-trailer-size(field-size-payload), - HTTP-response-incomplete, - HTTP-response-header-section-size(option), - HTTP-response-header-size(field-size-payload), - HTTP-response-body-size(option), - HTTP-response-trailer-section-size(option), - HTTP-response-trailer-size(field-size-payload), - HTTP-response-transfer-coding(option), - HTTP-response-content-coding(option), - HTTP-response-timeout, - HTTP-upgrade-failed, - HTTP-protocol-error, - loop-detected, - configuration-error, - /// This is a catch-all error for anything that doesn't fit cleanly into a - /// more specific case. It also includes an optional string for an - /// unstructured description of the error. Users should not depend on the - /// string for diagnosing errors, as it's not required to be consistent - /// between implementations. - internal-error(option), - } - - /// This type enumerates the different kinds of errors that may occur when - /// setting or appending to a `fields` resource. - @since(version = 0.2.0) - variant header-error { - /// This error indicates that a `field-name` or `field-value` was - /// syntactically invalid when used with an operation that sets headers in a - /// `fields`. - invalid-syntax, - /// This error indicates that a forbidden `field-name` was used when trying - /// to set a header in a `fields`. - forbidden, - /// This error indicates that the operation on the `fields` was not - /// permitted because the fields are immutable. - immutable, - } - - /// Field keys are always strings. - /// - /// Field keys should always be treated as case insensitive by the `fields` - /// resource for the purposes of equality checking. - /// - /// # Deprecation - /// - /// This type has been deprecated in favor of the `field-name` type. - @since(version = 0.2.0) - @deprecated(version = 0.2.2) - type field-key = string; - - /// Field names are always strings. - /// - /// Field names should always be treated as case insensitive by the `fields` - /// resource for the purposes of equality checking. - @since(version = 0.2.1) - type field-name = field-key; - - /// Field values should always be ASCII strings. However, in - /// reality, HTTP implementations often have to interpret malformed values, - /// so they are provided as a list of bytes. - @since(version = 0.2.0) - type field-value = list; - - /// This following block defines the `fields` resource which corresponds to - /// HTTP standard Fields. Fields are a common representation used for both - /// Headers and Trailers. - /// - /// A `fields` may be mutable or immutable. A `fields` created using the - /// constructor, `from-list`, or `clone` will be mutable, but a `fields` - /// resource given by other means (including, but not limited to, - /// `incoming-request.headers`, `outgoing-request.headers`) might be - /// immutable. In an immutable fields, the `set`, `append`, and `delete` - /// operations will fail with `header-error.immutable`. - @since(version = 0.2.0) - resource fields { - /// Construct an empty HTTP Fields. - /// - /// The resulting `fields` is mutable. - @since(version = 0.2.0) - constructor(); - /// Construct an HTTP Fields. - /// - /// The resulting `fields` is mutable. - /// - /// The list represents each name-value pair in the Fields. Names - /// which have multiple values are represented by multiple entries in this - /// list with the same name. - /// - /// The tuple is a pair of the field name, represented as a string, and - /// Value, represented as a list of bytes. - /// - /// An error result will be returned if any `field-name` or `field-value` is - /// syntactically invalid, or if a field is forbidden. - @since(version = 0.2.0) - from-list: static func(entries: list>) -> result; - /// Get all of the values corresponding to a name. If the name is not present - /// in this `fields` or is syntactically invalid, an empty list is returned. - /// However, if the name is present but empty, this is represented by a list - /// with one or more empty field-values present. - @since(version = 0.2.0) - get: func(name: field-name) -> list; - /// Returns `true` when the name is present in this `fields`. If the name is - /// syntactically invalid, `false` is returned. - @since(version = 0.2.0) - has: func(name: field-name) -> bool; - /// Set all of the values for a name. Clears any existing values for that - /// name, if they have been set. - /// - /// Fails with `header-error.immutable` if the `fields` are immutable. - /// - /// Fails with `header-error.invalid-syntax` if the `field-name` or any of - /// the `field-value`s are syntactically invalid. - @since(version = 0.2.0) - set: func(name: field-name, value: list) -> result<_, header-error>; - /// Delete all values for a name. Does nothing if no values for the name - /// exist. - /// - /// Fails with `header-error.immutable` if the `fields` are immutable. - /// - /// Fails with `header-error.invalid-syntax` if the `field-name` is - /// syntactically invalid. - @since(version = 0.2.0) - delete: func(name: field-name) -> result<_, header-error>; - /// Append a value for a name. Does not change or delete any existing - /// values for that name. - /// - /// Fails with `header-error.immutable` if the `fields` are immutable. - /// - /// Fails with `header-error.invalid-syntax` if the `field-name` or - /// `field-value` are syntactically invalid. - @since(version = 0.2.0) - append: func(name: field-name, value: field-value) -> result<_, header-error>; - /// Retrieve the full set of names and values in the Fields. Like the - /// constructor, the list represents each name-value pair. - /// - /// The outer list represents each name-value pair in the Fields. Names - /// which have multiple values are represented by multiple entries in this - /// list with the same name. - /// - /// The names and values are always returned in the original casing and in - /// the order in which they will be serialized for transport. - @since(version = 0.2.0) - entries: func() -> list>; - /// Make a deep copy of the Fields. Equivalent in behavior to calling the - /// `fields` constructor on the return value of `entries`. The resulting - /// `fields` is mutable. - @since(version = 0.2.0) - clone: func() -> fields; - } - - /// Headers is an alias for Fields. - @since(version = 0.2.0) - type headers = fields; - - /// Trailers is an alias for Fields. - @since(version = 0.2.0) - type trailers = fields; - - /// Represents an incoming HTTP Request. - @since(version = 0.2.0) - resource incoming-request { - /// Returns the method of the incoming request. - @since(version = 0.2.0) - method: func() -> method; - /// Returns the path with query parameters from the request, as a string. - @since(version = 0.2.0) - path-with-query: func() -> option; - /// Returns the protocol scheme from the request. - @since(version = 0.2.0) - scheme: func() -> option; - /// Returns the authority of the Request's target URI, if present. - @since(version = 0.2.0) - authority: func() -> option; - /// Get the `headers` associated with the request. - /// - /// The returned `headers` resource is immutable: `set`, `append`, and - /// `delete` operations will fail with `header-error.immutable`. - /// - /// The `headers` returned are a child resource: it must be dropped before - /// the parent `incoming-request` is dropped. Dropping this - /// `incoming-request` before all children are dropped will trap. - @since(version = 0.2.0) - headers: func() -> headers; - /// Gives the `incoming-body` associated with this request. Will only - /// return success at most once, and subsequent calls will return error. - @since(version = 0.2.0) - consume: func() -> result; - } - - /// Represents an outgoing HTTP Request. - @since(version = 0.2.0) - resource outgoing-request { - /// Construct a new `outgoing-request` with a default `method` of `GET`, and - /// `none` values for `path-with-query`, `scheme`, and `authority`. - /// - /// * `headers` is the HTTP Headers for the Request. - /// - /// It is possible to construct, or manipulate with the accessor functions - /// below, an `outgoing-request` with an invalid combination of `scheme` - /// and `authority`, or `headers` which are not permitted to be sent. - /// It is the obligation of the `outgoing-handler.handle` implementation - /// to reject invalid constructions of `outgoing-request`. - @since(version = 0.2.0) - constructor(headers: headers); - /// Returns the resource corresponding to the outgoing Body for this - /// Request. - /// - /// Returns success on the first call: the `outgoing-body` resource for - /// this `outgoing-request` can be retrieved at most once. Subsequent - /// calls will return error. - @since(version = 0.2.0) - body: func() -> result; - /// Get the Method for the Request. - @since(version = 0.2.0) - method: func() -> method; - /// Set the Method for the Request. Fails if the string present in a - /// `method.other` argument is not a syntactically valid method. - @since(version = 0.2.0) - set-method: func(method: method) -> result; - /// Get the combination of the HTTP Path and Query for the Request. - /// When `none`, this represents an empty Path and empty Query. - @since(version = 0.2.0) - path-with-query: func() -> option; - /// Set the combination of the HTTP Path and Query for the Request. - /// When `none`, this represents an empty Path and empty Query. Fails is the - /// string given is not a syntactically valid path and query uri component. - @since(version = 0.2.0) - set-path-with-query: func(path-with-query: option) -> result; - /// Get the HTTP Related Scheme for the Request. When `none`, the - /// implementation may choose an appropriate default scheme. - @since(version = 0.2.0) - scheme: func() -> option; - /// Set the HTTP Related Scheme for the Request. When `none`, the - /// implementation may choose an appropriate default scheme. Fails if the - /// string given is not a syntactically valid uri scheme. - @since(version = 0.2.0) - set-scheme: func(scheme: option) -> result; - /// Get the authority of the Request's target URI. A value of `none` may be used - /// with Related Schemes which do not require an authority. The HTTP and - /// HTTPS schemes always require an authority. - @since(version = 0.2.0) - authority: func() -> option; - /// Set the authority of the Request's target URI. A value of `none` may be used - /// with Related Schemes which do not require an authority. The HTTP and - /// HTTPS schemes always require an authority. Fails if the string given is - /// not a syntactically valid URI authority. - @since(version = 0.2.0) - set-authority: func(authority: option) -> result; - /// Get the headers associated with the Request. - /// - /// The returned `headers` resource is immutable: `set`, `append`, and - /// `delete` operations will fail with `header-error.immutable`. - /// - /// This headers resource is a child: it must be dropped before the parent - /// `outgoing-request` is dropped, or its ownership is transferred to - /// another component by e.g. `outgoing-handler.handle`. - @since(version = 0.2.0) - headers: func() -> headers; - } - - /// 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`. - @since(version = 0.2.0) - resource request-options { - /// Construct a default `request-options` value. - @since(version = 0.2.0) - constructor(); - /// The timeout for the initial connect to the HTTP Server. - @since(version = 0.2.0) - connect-timeout: func() -> option; - /// Set the timeout for the initial connect to the HTTP Server. An error - /// return value indicates that this timeout is not supported. - @since(version = 0.2.0) - set-connect-timeout: func(duration: option) -> result; - /// The timeout for receiving the first byte of the Response body. - @since(version = 0.2.0) - 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. - @since(version = 0.2.0) - set-first-byte-timeout: func(duration: option) -> result; - /// The timeout for receiving subsequent chunks of bytes in the Response - /// body stream. - @since(version = 0.2.0) - between-bytes-timeout: func() -> option; - /// 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. - @since(version = 0.2.0) - set-between-bytes-timeout: func(duration: option) -> result; - } - - /// Represents the ability to send an HTTP Response. - /// - /// This resource is used by the `wasi:http/incoming-handler` interface to - /// allow a Response to be sent corresponding to the Request provided as the - /// other argument to `incoming-handler.handle`. - @since(version = 0.2.0) - resource response-outparam { - /// Send an HTTP 1xx response. - /// - /// Unlike `response-outparam.set`, this does not consume the - /// `response-outparam`, allowing the guest to send an arbitrary number of - /// informational responses before sending the final response using - /// `response-outparam.set`. - /// - /// This will return an `HTTP-protocol-error` if `status` is not in the - /// range [100-199], or an `internal-error` if the implementation does not - /// support informational responses. - @unstable(feature = informational-outbound-responses) - send-informational: func(status: u16, headers: headers) -> result<_, error-code>; - /// Set the value of the `response-outparam` to either send a response, - /// or indicate an error. - /// - /// This method consumes the `response-outparam` to ensure that it is - /// called at most once. If it is never called, the implementation - /// will respond with an error. - /// - /// The user may provide an `error` to `response` to allow the - /// implementation determine how to respond with an HTTP error response. - @since(version = 0.2.0) - set: static func(param: response-outparam, response: result); - } - - /// This type corresponds to the HTTP standard Status Code. - @since(version = 0.2.0) - type status-code = u16; - - /// Represents an incoming HTTP Response. - @since(version = 0.2.0) - resource incoming-response { - /// Returns the status code from the incoming response. - @since(version = 0.2.0) - status: func() -> status-code; - /// Returns the headers from the incoming response. - /// - /// The returned `headers` resource is immutable: `set`, `append`, and - /// `delete` operations will fail with `header-error.immutable`. - /// - /// This headers resource is a child: it must be dropped before the parent - /// `incoming-response` is dropped. - @since(version = 0.2.0) - headers: func() -> headers; - /// Returns the incoming body. May be called at most once. Returns error - /// if called additional times. - @since(version = 0.2.0) - consume: func() -> result; - } - - /// Represents an incoming HTTP Request or Response's Body. - /// - /// A body has both its contents - a stream of bytes - and a (possibly - /// empty) set of trailers, indicating that the full contents of the - /// body have been received. This resource represents the contents as - /// an `input-stream` and the delivery of trailers as a `future-trailers`, - /// and ensures that the user of this interface may only be consuming either - /// the body contents or waiting on trailers at any given time. - @since(version = 0.2.0) - resource incoming-body { - /// Returns the contents of the body, as a stream of bytes. - /// - /// Returns success on first call: the stream representing the contents - /// can be retrieved at most once. Subsequent calls will return error. - /// - /// The returned `input-stream` resource is a child: it must be dropped - /// before the parent `incoming-body` is dropped, or consumed by - /// `incoming-body.finish`. - /// - /// This invariant ensures that the implementation can determine whether - /// the user is consuming the contents of the body, waiting on the - /// `future-trailers` to be ready, or neither. This allows for network - /// backpressure is to be applied when the user is consuming the body, - /// and for that backpressure to not inhibit delivery of the trailers if - /// the user does not read the entire body. - @since(version = 0.2.0) - %stream: func() -> result; - /// Takes ownership of `incoming-body`, and returns a `future-trailers`. - /// This function will trap if the `input-stream` child is still alive. - @since(version = 0.2.0) - finish: static func(this: incoming-body) -> future-trailers; - } - - /// Represents a future which may eventually return trailers, or an error. - /// - /// In the case that the incoming HTTP Request or Response did not have any - /// trailers, this future will resolve to the empty set of trailers once the - /// complete Request or Response body has been received. - @since(version = 0.2.0) - resource future-trailers { - /// Returns a pollable which becomes ready when either the trailers have - /// been received, or an error has occurred. When this pollable is ready, - /// the `get` method will return `some`. - @since(version = 0.2.0) - subscribe: func() -> pollable; - /// Returns the contents of the trailers, or an error which occurred, - /// once the future is ready. - /// - /// The outer `option` represents future readiness. Users can wait on this - /// `option` to become `some` using the `subscribe` method. - /// - /// The outer `result` is used to retrieve the trailers or error at most - /// once. It will be success on the first call in which the outer option - /// is `some`, and error on subsequent calls. - /// - /// The inner `result` represents that either the HTTP Request or Response - /// body, as well as any trailers, were received successfully, or that an - /// error occurred receiving them. The optional `trailers` indicates whether - /// or not trailers were present in the body. - /// - /// When some `trailers` are returned by this method, the `trailers` - /// resource is immutable, and a child. Use of the `set`, `append`, or - /// `delete` methods will return an error, and the resource must be - /// dropped before the parent `future-trailers` is dropped. - @since(version = 0.2.0) - get: func() -> option, error-code>>>; - } - - /// Represents an outgoing HTTP Response. - @since(version = 0.2.0) - resource outgoing-response { - /// Construct an `outgoing-response`, with a default `status-code` of `200`. - /// If a different `status-code` is needed, it must be set via the - /// `set-status-code` method. - /// - /// * `headers` is the HTTP Headers for the Response. - @since(version = 0.2.0) - constructor(headers: headers); - /// Get the HTTP Status Code for the Response. - @since(version = 0.2.0) - status-code: func() -> status-code; - /// Set the HTTP Status Code for the Response. Fails if the status-code - /// given is not a valid http status code. - @since(version = 0.2.0) - set-status-code: func(status-code: status-code) -> result; - /// Get the headers associated with the Request. - /// - /// The returned `headers` resource is immutable: `set`, `append`, and - /// `delete` operations will fail with `header-error.immutable`. - /// - /// This headers resource is a child: it must be dropped before the parent - /// `outgoing-request` is dropped, or its ownership is transferred to - /// another component by e.g. `outgoing-handler.handle`. - @since(version = 0.2.0) - headers: func() -> headers; - /// Returns the resource corresponding to the outgoing Body for this Response. - /// - /// Returns success on the first call: the `outgoing-body` resource for - /// this `outgoing-response` can be retrieved at most once. Subsequent - /// calls will return error. - @since(version = 0.2.0) - body: func() -> result; - } - - /// Represents an outgoing HTTP Request or Response's Body. - /// - /// A body has both its contents - a stream of bytes - and a (possibly - /// empty) set of trailers, inducating the full contents of the body - /// have been sent. This resource represents the contents as an - /// `output-stream` child resource, and the completion of the body (with - /// optional trailers) with a static function that consumes the - /// `outgoing-body` resource, and ensures that the user of this interface - /// may not write to the body contents after the body has been finished. - /// - /// If the user code drops this resource, as opposed to calling the static - /// method `finish`, the implementation should treat the body as incomplete, - /// and that an error has occurred. The implementation should propagate this - /// error to the HTTP protocol by whatever means it has available, - /// including: corrupting the body on the wire, aborting the associated - /// Request, or sending a late status code for the Response. - @since(version = 0.2.0) - resource outgoing-body { - /// Returns a stream for writing the body contents. - /// - /// The returned `output-stream` is a child resource: it must be dropped - /// before the parent `outgoing-body` resource is dropped (or finished), - /// otherwise the `outgoing-body` drop or `finish` will trap. - /// - /// Returns success on the first call: the `output-stream` resource for - /// this `outgoing-body` may be retrieved at most once. Subsequent calls - /// will return error. - @since(version = 0.2.0) - write: func() -> result; - /// Finalize an outgoing body, optionally providing trailers. This must be - /// called to signal that the response is complete. If the `outgoing-body` - /// is dropped without calling `outgoing-body.finalize`, the implementation - /// should treat the body as corrupted. - /// - /// Fails if the body's `outgoing-request` or `outgoing-response` was - /// constructed with a Content-Length header, and the contents written - /// to the body (via `write`) does not match the value given in the - /// Content-Length. - @since(version = 0.2.0) - finish: static func(this: outgoing-body, trailers: option) -> result<_, error-code>; - } - - /// Represents a future which may eventually return an incoming HTTP - /// Response, or an error. - /// - /// This resource is returned by the `wasi:http/outgoing-handler` interface to - /// provide the HTTP Response corresponding to the sent Request. - @since(version = 0.2.0) - resource future-incoming-response { - /// Returns a pollable which becomes ready when either the Response has - /// been received, or an error has occurred. When this pollable is ready, - /// the `get` method will return `some`. - @since(version = 0.2.0) - subscribe: func() -> pollable; - /// Returns the incoming HTTP Response, or an error, once one is ready. - /// - /// The outer `option` represents future readiness. Users can wait on this - /// `option` to become `some` using the `subscribe` method. - /// - /// The outer `result` is used to retrieve the response or error at most - /// once. It will be success on the first call in which the outer option - /// is `some`, and error on subsequent calls. - /// - /// The inner `result` represents that either the incoming HTTP Response - /// status and headers have received successfully, or that an error - /// occurred. Errors may also occur while consuming the response body, - /// but those will be reported by the `incoming-body` and its - /// `output-stream` child. - @since(version = 0.2.0) - get: func() -> option>>; - } - - /// Attempts to extract a http-related `error` from the wasi:io `error` - /// provided. - /// - /// Stream operations which return - /// `wasi:io/stream/stream-error::last-operation-failed` have a payload of - /// type `wasi:io/error/error` with more information about the operation - /// that failed. This payload can be passed through to this function to see - /// if there's http-related information about the error to return. - /// - /// Note that this function is fallible because not all io-errors are - /// http-related errors. - @since(version = 0.2.0) - http-error-code: func(err: borrow) -> option; -} - -/// This interface defines a handler of incoming HTTP Requests. It should -/// be exported by components which can respond to HTTP Requests. -@since(version = 0.2.0) -interface incoming-handler { - @since(version = 0.2.0) - use types.{incoming-request, response-outparam}; - - /// This function is invoked with an incoming HTTP Request, and a resource - /// `response-outparam` which provides the capability to reply with an HTTP - /// Response. The response is sent by calling the `response-outparam.set` - /// method, which allows execution to continue after the response has been - /// sent. This enables both streaming to the response body, and performing other - /// work. - /// - /// The implementor of this function must write a response to the - /// `response-outparam` before returning, or else the caller will respond - /// with an error on its behalf. - @since(version = 0.2.0) - handle: func(request: incoming-request, response-out: response-outparam); -} - -/// This interface defines a handler of outgoing HTTP Requests. It should be -/// imported by components which wish to make HTTP Requests. -@since(version = 0.2.0) -interface outgoing-handler { - @since(version = 0.2.0) - use types.{outgoing-request, request-options, future-incoming-response, error-code}; - - /// This function is invoked with an outgoing HTTP Request, and it returns - /// a resource `future-incoming-response` which represents an HTTP Response - /// which may arrive in the future. - /// - /// The `options` argument accepts optional parameters for the HTTP - /// protocol's transport layer. - /// - /// This function may return an error if the `outgoing-request` is invalid - /// or not allowed to be made. Otherwise, protocol errors are reported - /// through the `future-incoming-response`. - @since(version = 0.2.0) - handle: func(request: outgoing-request, options: option) -> result; -} - -/// The `wasi:http/imports` world imports all the APIs for HTTP proxies. -/// It is intended to be `include`d in other worlds. -@since(version = 0.2.0) -world imports { - @since(version = 0.2.0) - import wasi:io/poll@0.2.6; - @since(version = 0.2.0) - import wasi:clocks/monotonic-clock@0.2.6; - @since(version = 0.2.0) - import wasi:clocks/wall-clock@0.2.6; - @since(version = 0.2.0) - import wasi:random/random@0.2.6; - @since(version = 0.2.0) - import wasi:io/error@0.2.6; - @since(version = 0.2.0) - import wasi:io/streams@0.2.6; - @since(version = 0.2.0) - import wasi:cli/stdout@0.2.6; - @since(version = 0.2.0) - import wasi:cli/stderr@0.2.6; - @since(version = 0.2.0) - import wasi:cli/stdin@0.2.6; - @since(version = 0.2.0) - import types; - @since(version = 0.2.0) - import outgoing-handler; -} -/// The `wasi:http/proxy` world captures a widely-implementable intersection of -/// hosts that includes HTTP forward and reverse proxies. Components targeting -/// this world may concurrently stream in and out any number of incoming and -/// outgoing HTTP requests. -@since(version = 0.2.0) -world proxy { - @since(version = 0.2.0) - import wasi:io/poll@0.2.6; - @since(version = 0.2.0) - import wasi:clocks/monotonic-clock@0.2.6; - @since(version = 0.2.0) - import wasi:clocks/wall-clock@0.2.6; - @since(version = 0.2.0) - import wasi:random/random@0.2.6; - @since(version = 0.2.0) - import wasi:io/error@0.2.6; - @since(version = 0.2.0) - import wasi:io/streams@0.2.6; - @since(version = 0.2.0) - import wasi:cli/stdout@0.2.6; - @since(version = 0.2.0) - import wasi:cli/stderr@0.2.6; - @since(version = 0.2.0) - import wasi:cli/stdin@0.2.6; - @since(version = 0.2.0) - import types; - @since(version = 0.2.0) - import outgoing-handler; - - @since(version = 0.2.0) - export incoming-handler; -} diff --git a/components/http/wit/deps/wasi-http-0.3.0/package.wit b/components/http/wit/deps/wasi-http-0.3.0/package.wit new file mode 100644 index 0000000..08458f7 --- /dev/null +++ b/components/http/wit/deps/wasi-http-0.3.0/package.wit @@ -0,0 +1,509 @@ +package wasi:http@0.3.0; + +/// This interface defines all of the types and methods for implementing HTTP +/// Requests and Responses, as well as their headers, trailers, and bodies. +@since(version = 0.3.0) +interface types { + use wasi:clocks/types@0.3.0.{duration}; + + /// This type corresponds to HTTP standard Methods. + @since(version = 0.3.0) + variant method { + get, + head, + post, + put, + delete, + connect, + options, + trace, + patch, + other(string), + } + + /// This type corresponds to HTTP standard Related Schemes. + @since(version = 0.3.0) + variant scheme { + HTTP, + HTTPS, + other(string), + } + + /// Defines the case payload type for `DNS-error` above: + @since(version = 0.3.0) + record DNS-error-payload { + rcode: option, + info-code: option, + } + + /// Defines the case payload type for `TLS-alert-received` above: + @since(version = 0.3.0) + record TLS-alert-received-payload { + alert-id: option, + alert-message: option, + } + + /// Defines the case payload type for `HTTP-response-{header,trailer}-size` above: + @since(version = 0.3.0) + record field-size-payload { + field-name: option, + field-size: option, + } + + /// These cases are inspired by the IANA HTTP Proxy Error Types: + /// + @since(version = 0.3.0) + variant error-code { + DNS-timeout, + DNS-error(DNS-error-payload), + destination-not-found, + destination-unavailable, + destination-IP-prohibited, + destination-IP-unroutable, + connection-refused, + connection-terminated, + connection-timeout, + connection-read-timeout, + connection-write-timeout, + connection-limit-reached, + TLS-protocol-error, + TLS-certificate-error, + TLS-alert-received(TLS-alert-received-payload), + HTTP-request-denied, + HTTP-request-length-required, + HTTP-request-body-size(option), + HTTP-request-method-invalid, + HTTP-request-URI-invalid, + HTTP-request-URI-too-long, + HTTP-request-header-section-size(option), + HTTP-request-header-size(option), + HTTP-request-trailer-section-size(option), + HTTP-request-trailer-size(field-size-payload), + HTTP-response-incomplete, + HTTP-response-header-section-size(option), + HTTP-response-header-size(field-size-payload), + HTTP-response-body-size(option), + HTTP-response-trailer-section-size(option), + HTTP-response-trailer-size(field-size-payload), + HTTP-response-transfer-coding(option), + HTTP-response-content-coding(option), + HTTP-response-timeout, + HTTP-upgrade-failed, + HTTP-protocol-error, + loop-detected, + configuration-error, + /// This is a catch-all error for anything that doesn't fit cleanly into a + /// more specific case. It also includes an optional string for an + /// unstructured description of the error. Users should not depend on the + /// string for diagnosing errors, as it's not required to be consistent + /// between implementations. + internal-error(option), + } + + /// This type enumerates the different kinds of errors that may occur when + /// setting or appending to a `fields` resource. + @since(version = 0.3.0) + variant header-error { + /// This error indicates that a `field-name` or `field-value` was + /// syntactically invalid when used with an operation that sets headers in a + /// `fields`. + invalid-syntax, + /// This error indicates that a forbidden `field-name` was used when trying + /// to set a header in a `fields`. + forbidden, + /// This error indicates that the operation on the `fields` was not + /// permitted because the fields are immutable. + immutable, + /// This error indicates that the operation would exceed an + /// implementation-defined limit on field sizes. This may apply to + /// an individual `field-value`, a single `field-name` plus all its + /// values, or the total aggregate size of all fields. + size-exceeded, + /// This is a catch-all error for anything that doesn't fit cleanly into a + /// more specific case. Implementations can use this to extend the error + /// type without breaking existing code. It also includes an optional + /// string for an unstructured description of the error. Users should not + /// depend on the string for diagnosing errors, as it's not required to be + /// consistent between implementations. + other(option), + } + + /// This type enumerates the different kinds of errors that may occur when + /// setting fields of a `request-options` resource. + @since(version = 0.3.0) + variant request-options-error { + /// Indicates the specified field is not supported by this implementation. + not-supported, + /// Indicates that the operation on the `request-options` was not permitted + /// because it is immutable. + immutable, + /// This is a catch-all error for anything that doesn't fit cleanly into a + /// more specific case. Implementations can use this to extend the error + /// type without breaking existing code. It also includes an optional + /// string for an unstructured description of the error. Users should not + /// depend on the string for diagnosing errors, as it's not required to be + /// consistent between implementations. + other(option), + } + + /// Field names are always strings. + /// + /// Field names should always be treated as case insensitive by the `fields` + /// resource for the purposes of equality checking. + @since(version = 0.3.0) + type field-name = string; + + /// Field values should always be ASCII strings. However, in + /// reality, HTTP implementations often have to interpret malformed values, + /// so they are provided as a list of bytes. + @since(version = 0.3.0) + type field-value = list; + + /// This following block defines the `fields` resource which corresponds to + /// HTTP standard Fields. Fields are a common representation used for both + /// Headers and Trailers. + /// + /// A `fields` may be mutable or immutable. A `fields` created using the + /// constructor, `from-list`, or `clone` will be mutable, but a `fields` + /// resource given by other means (including, but not limited to, + /// `request.headers`) might be be immutable. In an immutable fields, the + /// `set`, `append`, and `delete` operations will fail with + /// `header-error.immutable`. + /// + /// A `fields` resource should store `field-name`s and `field-value`s in their + /// original casing used to construct or mutate the `fields` resource. The `fields` + /// resource should use that original casing when serializing the fields for + /// transport or when returning them from a method. + /// + /// Implementations may impose limits on individual field values and on total + /// aggregate field section size. Operations that would exceed these limits + /// fail with `header-error.size-exceeded` + @since(version = 0.3.0) + resource fields { + /// Construct an empty HTTP Fields. + /// + /// The resulting `fields` is mutable. + constructor(); + /// Construct an HTTP Fields. + /// + /// The resulting `fields` is mutable. + /// + /// The list represents each name-value pair in the Fields. Names + /// which have multiple values are represented by multiple entries in this + /// list with the same name. + /// + /// The tuple is a pair of the field name, represented as a string, and + /// Value, represented as a list of bytes. In a valid Fields, all names + /// and values are valid UTF-8 strings. However, values are not always + /// well-formed, so they are represented as a raw list of bytes. + /// + /// An error result will be returned if any header or value was + /// syntactically invalid, if a header was forbidden, or if the + /// entries would exceed an implementation size limit. + from-list: static func(entries: list>) -> result; + /// Get all of the values corresponding to a name. If the name is not present + /// in this `fields`, an empty list is returned. However, if the name is + /// present but empty, this is represented by a list with one or more + /// empty field-values present. + get: func(name: field-name) -> list; + /// Returns `true` when the name is present in this `fields`. If the name is + /// syntactically invalid, `false` is returned. + has: func(name: field-name) -> bool; + /// Set all of the values for a name. Clears any existing values for that + /// name, if they have been set. + /// + /// Fails with `header-error.immutable` if the `fields` are immutable. + /// + /// Fails with `header-error.size-exceeded` if the name or values would + /// exceed an implementation-defined size limit. + set: func(name: field-name, value: list) -> result<_, header-error>; + /// Delete all values for a name. Does nothing if no values for the name + /// exist. + /// + /// Fails with `header-error.immutable` if the `fields` are immutable. + delete: func(name: field-name) -> result<_, header-error>; + /// Delete all values for a name. Does nothing if no values for the name + /// exist. + /// + /// Returns all values previously corresponding to the name, if any. + /// + /// Fails with `header-error.immutable` if the `fields` are immutable. + get-and-delete: func(name: field-name) -> result, header-error>; + /// Append a value for a name. Does not change or delete any existing + /// values for that name. + /// + /// Fails with `header-error.immutable` if the `fields` are immutable. + /// + /// Fails with `header-error.size-exceeded` if the value would exceed + /// an implementation-defined size limit. + append: func(name: field-name, value: field-value) -> result<_, header-error>; + /// Retrieve the full set of names and values in the Fields. Like the + /// constructor, the list represents each name-value pair. + /// + /// The outer list represents each name-value pair in the Fields. Names + /// which have multiple values are represented by multiple entries in this + /// list with the same name. + /// + /// The names and values are always returned in the original casing and in + /// the order in which they will be serialized for transport. + copy-all: func() -> list>; + /// Make a deep copy of the Fields. Equivalent in behavior to calling the + /// `fields` constructor on the return value of `copy-all`. The resulting + /// `fields` is mutable. + clone: func() -> fields; + } + + /// Headers is an alias for Fields. + @since(version = 0.3.0) + type headers = fields; + + /// Trailers is an alias for Fields. + @since(version = 0.3.0) + type trailers = fields; + + /// Represents an HTTP Request. + @since(version = 0.3.0) + resource request { + /// Construct a new `request` with a default `method` of `GET`, and + /// `none` values for `path-with-query`, `scheme`, and `authority`. + /// + /// `headers` is the HTTP Headers for the Request. + /// + /// `contents` is the optional body content stream with `none` + /// representing a zero-length content stream. + /// Once it is closed, `trailers` future must resolve to a result. + /// If `trailers` resolves to an error, underlying connection + /// will be closed immediately. + /// + /// `options` is optional `request-options` resource to be used + /// if the request is sent over a network connection. + /// + /// It is possible to construct, or manipulate with the accessor functions + /// below, a `request` with an invalid combination of `scheme` + /// and `authority`, or `headers` which are not permitted to be sent. + /// It is the obligation of the `handler.handle` implementation + /// to reject invalid constructions of `request`. + /// + /// The returned future resolves to result of transmission of this request. + new: static func(headers: headers, contents: option>, trailers: future, error-code>>, options: option) -> tuple>>; + /// Get the Method for the Request. + get-method: func() -> method; + /// Set the Method for the Request. Fails if the string present in a + /// `method.other` argument is not a syntactically valid method. + set-method: func(method: method) -> result; + /// Get the combination of the HTTP Path and Query for the Request. When + /// `none`, this represents an empty Path and empty Query. + get-path-with-query: func() -> option; + /// Set the combination of the HTTP Path and Query for the Request. When + /// `none`, this represents an empty Path and empty Query. Fails is the + /// string given is not a syntactically valid path and query uri component. + set-path-with-query: func(path-with-query: option) -> result; + /// Get the HTTP Related Scheme for the Request. When `none`, the + /// implementation may choose an appropriate default scheme. + get-scheme: func() -> option; + /// Set the HTTP Related Scheme for the Request. When `none`, the + /// implementation may choose an appropriate default scheme. Fails if the + /// string given is not a syntactically valid uri scheme. + set-scheme: func(scheme: option) -> result; + /// Get the authority of the Request's target URI. A value of `none` may be used + /// with Related Schemes which do not require an authority. The HTTP and + /// HTTPS schemes always require an authority. + get-authority: func() -> option; + /// Set the authority of the Request's target URI. A value of `none` may be used + /// with Related Schemes which do not require an authority. The HTTP and + /// HTTPS schemes always require an authority. Fails if the string given is + /// not a syntactically valid URI authority. + set-authority: func(authority: option) -> result; + /// Get the `request-options` to be associated with this request + /// + /// The returned `request-options` resource is immutable: `set-*` operations + /// will fail if invoked. + /// + /// This `request-options` resource is a child: it must be dropped before + /// the parent `request` is dropped, or its ownership is transferred to + /// another component by e.g. `handler.handle`. + get-options: func() -> option; + /// Get the headers associated with the Request. + /// + /// The returned `headers` resource is immutable: `set`, `append`, and + /// `delete` operations will fail with `header-error.immutable`. + get-headers: func() -> headers; + /// Get body of the Request. + /// + /// Stream returned by this method represents the contents of the body. + /// Once the stream is reported as closed, callers should await the returned + /// future to determine whether the body was received successfully. + /// The future will only resolve after the stream is reported as closed. + /// + /// This function takes a `res` future as a parameter, which can be used to + /// communicate an error in handling of the request. + /// + /// Note that function will move the `request`, but references to headers or + /// request options acquired from it previously will remain valid. + consume-body: static func(this: request, res: future>) -> tuple, future, error-code>>>; + } + + /// 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 an + /// asynchronous call. + @since(version = 0.3.0) + resource request-options { + /// Construct a default `request-options` value. + constructor(); + /// The timeout for the initial connect to the HTTP Server. + get-connect-timeout: func() -> option; + /// Set the timeout for the initial connect to the HTTP Server. An error + /// return value indicates that this timeout is not supported or that this + /// handle is immutable. + set-connect-timeout: func(duration: option) -> result<_, request-options-error>; + /// The timeout for receiving the first byte of the Response body. + get-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 or that + /// this handle is immutable. + set-first-byte-timeout: func(duration: option) -> result<_, request-options-error>; + /// The timeout for receiving subsequent chunks of bytes in the Response + /// body stream. + get-between-bytes-timeout: func() -> option; + /// 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 or that this handle is immutable. + set-between-bytes-timeout: func(duration: option) -> result<_, request-options-error>; + /// Make a deep copy of the `request-options`. + /// The resulting `request-options` is mutable. + clone: func() -> request-options; + } + + /// This type corresponds to the HTTP standard Status Code. + @since(version = 0.3.0) + type status-code = u16; + + /// Represents an HTTP Response. + @since(version = 0.3.0) + resource response { + /// Construct a new `response`, with a default `status-code` of `200`. + /// If a different `status-code` is needed, it must be set via the + /// `set-status-code` method. + /// + /// `headers` is the HTTP Headers for the Response. + /// + /// `contents` is the optional body content stream with `none` + /// representing a zero-length content stream. + /// Once it is closed, `trailers` future must resolve to a result. + /// If `trailers` resolves to an error, underlying connection + /// will be closed immediately. + /// + /// The returned future resolves to result of transmission of this response. + new: static func(headers: headers, contents: option>, trailers: future, error-code>>) -> tuple>>; + /// Get the HTTP Status Code for the Response. + get-status-code: func() -> status-code; + /// Set the HTTP Status Code for the Response. Fails if the status-code + /// given is not a valid http status code. + set-status-code: func(status-code: status-code) -> result; + /// Get the headers associated with the Response. + /// + /// The returned `headers` resource is immutable: `set`, `append`, and + /// `delete` operations will fail with `header-error.immutable`. + get-headers: func() -> headers; + /// Get body of the Response. + /// + /// Stream returned by this method represents the contents of the body. + /// Once the stream is reported as closed, callers should await the returned + /// future to determine whether the body was received successfully. + /// The future will only resolve after the stream is reported as closed. + /// + /// This function takes a `res` future as a parameter, which can be used to + /// communicate an error in handling of the response. + /// + /// Note that function will move the `response`, but references to headers + /// acquired from it previously will remain valid. + consume-body: static func(this: response, res: future>) -> tuple, future, error-code>>>; + } +} + +/// This interface defines a handler of HTTP Requests. +/// +/// In a `wasi:http/service` this interface is exported to respond to an +/// incoming HTTP Request with a Response. +/// +/// In `wasi:http/middleware` this interface is both exported and imported as +/// the "downstream" and "upstream" directions of the middleware chain. +@since(version = 0.3.0) +interface handler { + use types.{request, response, error-code}; + + /// This function may be called with either an incoming request read from the + /// network or a request synthesized or forwarded by another component. + handle: async func(request: request) -> result; +} + +/// This interface defines an HTTP client for sending "outgoing" requests. +/// +/// Most components are expected to import this interface to provide the +/// capability to send HTTP requests to arbitrary destinations on a network. +/// +/// The type signature of `client.send` is the same as `handler.handle`. This +/// duplication is currently necessary because some Component Model tooling +/// (including WIT itself) is unable to represent a component importing two +/// instances of the same interface. A `client.send` import may be linked +/// directly to a `handler.handle` export to bypass the network. +@since(version = 0.3.0) +interface client { + use types.{request, response, error-code}; + + /// This function may be used to either send an outgoing request over the + /// network or to forward it to another component. + send: async func(request: request) -> result; +} + +/// The `wasi:http/service` world captures a broad category of HTTP services +/// including web applications, API servers, and proxies. It may be `include`d +/// in more specific worlds such as `wasi:http/middleware`. +@since(version = 0.3.0) +world service { + import wasi:cli/types@0.3.0; + import wasi:cli/stdout@0.3.0; + import wasi:cli/stderr@0.3.0; + import wasi:cli/stdin@0.3.0; + import wasi:clocks/types@0.3.0; + import types; + import client; + import wasi:clocks/monotonic-clock@0.3.0; + import wasi:clocks/system-clock@0.3.0; + @unstable(feature = clocks-timezone) + import wasi:clocks/timezone@0.3.0; + import wasi:random/random@0.3.0; + import wasi:random/insecure@0.3.0; + import wasi:random/insecure-seed@0.3.0; + + export handler; +} +/// The `wasi:http/middleware` world captures HTTP services that forward HTTP +/// Requests to another handler. +/// +/// Components may implement this world to allow them to participate in handler +/// "chains" where a `request` flows through handlers on its way to some terminal +/// `service` and corresponding `response` flows in the opposite direction. +@since(version = 0.3.0) +world middleware { + import wasi:clocks/types@0.3.0; + import types; + import handler; + import wasi:cli/types@0.3.0; + import wasi:cli/stdout@0.3.0; + import wasi:cli/stderr@0.3.0; + import wasi:cli/stdin@0.3.0; + import client; + import wasi:clocks/monotonic-clock@0.3.0; + import wasi:clocks/system-clock@0.3.0; + @unstable(feature = clocks-timezone) + import wasi:clocks/timezone@0.3.0; + import wasi:random/random@0.3.0; + import wasi:random/insecure@0.3.0; + import wasi:random/insecure-seed@0.3.0; + + export handler; +} diff --git a/components/http/wit/deps/wasi-io-0.2.6/package.wit b/components/http/wit/deps/wasi-io-0.2.6/package.wit deleted file mode 100644 index c66abac..0000000 --- a/components/http/wit/deps/wasi-io-0.2.6/package.wit +++ /dev/null @@ -1,48 +0,0 @@ -package wasi:io@0.2.6; - -interface poll { - resource pollable { - ready: func() -> bool; - block: func(); - } - - poll: func(in: list>) -> list; -} - -interface error { - resource error { - to-debug-string: func() -> string; - } -} - -interface streams { - use error.{error}; - use poll.{pollable}; - - variant stream-error { - last-operation-failed(error), - closed, - } - - resource input-stream { - read: func(len: u64) -> result, stream-error>; - blocking-read: func(len: u64) -> result, stream-error>; - skip: func(len: u64) -> result; - blocking-skip: func(len: u64) -> result; - subscribe: func() -> pollable; - } - - resource output-stream { - check-write: func() -> result; - write: func(contents: list) -> result<_, stream-error>; - blocking-write-and-flush: func(contents: list) -> result<_, stream-error>; - flush: func() -> result<_, stream-error>; - blocking-flush: func() -> result<_, stream-error>; - subscribe: func() -> pollable; - write-zeroes: func(len: u64) -> result<_, stream-error>; - blocking-write-zeroes-and-flush: func(len: u64) -> result<_, stream-error>; - splice: func(src: borrow, len: u64) -> result; - blocking-splice: func(src: borrow, len: u64) -> result; - } -} - diff --git a/components/http/wit/deps/wasi-random-0.2.6/package.wit b/components/http/wit/deps/wasi-random-0.2.6/package.wit deleted file mode 100644 index dfb249c..0000000 --- a/components/http/wit/deps/wasi-random-0.2.6/package.wit +++ /dev/null @@ -1,8 +0,0 @@ -package wasi:random@0.2.6; - -interface random { - get-random-bytes: func(len: u64) -> list; - - get-random-u64: func() -> u64; -} - diff --git a/components/http/wit/deps/wasi-random-0.3.0/package.wit b/components/http/wit/deps/wasi-random-0.3.0/package.wit new file mode 100644 index 0000000..f6cfb81 --- /dev/null +++ b/components/http/wit/deps/wasi-random-0.3.0/package.wit @@ -0,0 +1,18 @@ +package wasi:random@0.3.0; + +interface random { + get-random-bytes: func(max-len: u64) -> list; + + get-random-u64: func() -> u64; +} + +interface insecure { + get-insecure-random-bytes: func(max-len: u64) -> list; + + get-insecure-random-u64: func() -> u64; +} + +interface insecure-seed { + get-insecure-seed: func() -> tuple; +} + diff --git a/components/http/wit/package.wit b/components/http/wit/package.wit index 17eb7cd..228b0d8 100644 --- a/components/http/wit/package.wit +++ b/components/http/wit/package.wit @@ -1,4 +1,4 @@ -package composable:http@0.1.0; +package composable:http@0.2.0; interface client { enum method { @@ -11,48 +11,50 @@ interface client { options, } - record http-response { - status: u16, - headers: list>, - body: list, - trailers: list>, - } - /// Per-request options. None fields fall through to host defaults. record request-options { connect-timeout-ms: option, first-byte-timeout-ms: option, between-bytes-timeout-ms: option, - /// Maximum response body size in bytes. None means no cap. - max-response-body-bytes: option, } - /// HTTP request with explicit method - request: func(method: method, url: string, headers: list>, body: list, options: option) -> result; + /// A streaming HTTP response. The status and headers are available + /// immediately; the body streams as `body`, and trailers (if any) resolve + /// via `trailers` once the body stream is fully consumed. + record http-response { + status: u16, + headers: list>, + body: stream, + trailers: future>, string>>, + } + + /// Send an HTTP request with an explicit method. Both the request body and + /// the response body stream. + request: async func(method: method, url: string, headers: list>, body: stream, options: option) -> result; - /// HTTP GET request - get: func(url: string, headers: list>, options: option) -> result; + /// HTTP GET request. + get: async func(url: string, headers: list>, options: option) -> result; - /// HTTP POST request - post: func(url: string, headers: list>, body: list, options: option) -> result; + /// HTTP POST request. + post: async func(url: string, headers: list>, body: stream, options: option) -> result; - /// HTTP PUT request - put: func(url: string, headers: list>, body: list, options: option) -> result; + /// HTTP PUT request. + put: async func(url: string, headers: list>, body: stream, options: option) -> result; - /// HTTP DELETE request - delete: func(url: string, headers: list>, options: option) -> result; + /// HTTP DELETE request. + delete: async func(url: string, headers: list>, options: option) -> result; - /// HTTP PATCH request - patch: func(url: string, headers: list>, body: list, options: option) -> result; + /// HTTP PATCH request. + patch: async func(url: string, headers: list>, body: stream, options: option) -> result; - /// HTTP HEAD request - head: func(url: string, headers: list>, options: option) -> result; + /// HTTP HEAD request. + head: async func(url: string, headers: list>, options: option) -> result; - /// HTTP OPTIONS request - options: func(url: string, headers: list>, options: option) -> result; + /// HTTP OPTIONS request. + options: async func(url: string, headers: list>, options: option) -> result; } world http-client { export client; - import wasi:http/outgoing-handler@0.2.6; + import wasi:http/client@0.3.0; } diff --git a/components/http/wkg.lock b/components/http/wkg.lock index 5d15676..1c6187c 100644 --- a/components/http/wkg.lock +++ b/components/http/wkg.lock @@ -7,6 +7,6 @@ name = "wasi:http" registry = "wasi.dev" [[packages.versions]] -requirement = "=0.2.6" -version = "0.2.6" -digest = "sha256:26a636620af0485dfcfc9a0563a0606564eedcf3fcabda031ab4d4ec0e505ff1" +requirement = "=0.3.0" +version = "0.3.0" +digest = "sha256:92cd8f3730c00226dc15626a2e7b21834dd187fc221f09818720d228585bbbf7"