From 3c3ea4433d0b378e5c6c2af6af75f8cf851aeafc Mon Sep 17 00:00:00 2001 From: Trevor Elliott Date: Tue, 3 Oct 2023 12:38:19 -0700 Subject: [PATCH] Resourcify the wasi-http interface (#7135) --- .../wasi-http-proxy-tests/src/lib.rs | 13 +- .../test-programs/wasi-http-tests/src/lib.rs | 37 +- crates/wasi-http/src/http_impl.rs | 15 +- crates/wasi-http/src/lib.rs | 11 + crates/wasi-http/src/types.rs | 297 +---------- crates/wasi-http/src/types_impl.rs | 498 ++++++++++-------- crates/wasi-http/wit/deps/http/types.wit | 214 ++++---- crates/wasi/wit/deps/http/types.wit | 214 ++++---- 8 files changed, 554 insertions(+), 745 deletions(-) diff --git a/crates/test-programs/wasi-http-proxy-tests/src/lib.rs b/crates/test-programs/wasi-http-proxy-tests/src/lib.rs index c51be36d7f38..65c7db9168c6 100644 --- a/crates/test-programs/wasi-http-proxy-tests/src/lib.rs +++ b/crates/test-programs/wasi-http-proxy-tests/src/lib.rs @@ -16,18 +16,17 @@ struct T; impl bindings::exports::wasi::http::incoming_handler::Guest for T { fn handle(_request: IncomingRequest, outparam: ResponseOutparam) { - let hdrs = bindings::wasi::http::types::new_fields(&[]); - let resp = bindings::wasi::http::types::new_outgoing_response(200, hdrs); - let body = - bindings::wasi::http::types::outgoing_response_write(resp).expect("outgoing response"); + let hdrs = bindings::wasi::http::types::Headers::new(&[]); + let resp = bindings::wasi::http::types::OutgoingResponse::new(200, hdrs); + let body = resp.write().expect("outgoing response"); - bindings::wasi::http::types::set_response_outparam(outparam, Ok(resp)); + bindings::wasi::http::types::ResponseOutparam::set(outparam, Ok(resp)); - let out = bindings::wasi::http::types::outgoing_body_write(body).expect("outgoing stream"); + let out = body.write().expect("outgoing stream"); out.blocking_write_and_flush(b"hello, world!") .expect("writing response"); drop(out); - bindings::wasi::http::types::outgoing_body_finish(body, None); + bindings::wasi::http::types::OutgoingBody::finish(body, None); } } diff --git a/crates/test-programs/wasi-http-tests/src/lib.rs b/crates/test-programs/wasi-http-tests/src/lib.rs index d40c5c0e96ea..aae7d0fecbdf 100644 --- a/crates/test-programs/wasi-http-tests/src/lib.rs +++ b/crates/test-programs/wasi-http-tests/src/lib.rs @@ -51,7 +51,7 @@ pub fn request( fn header_val(v: &str) -> Vec { v.to_string().into_bytes() } - let headers = http_types::new_fields( + let headers = http_types::Headers::new( &[ &[ ("User-agent".to_string(), header_val("WASI-HTTP/0.0.1")), @@ -62,7 +62,7 @@ pub fn request( .concat(), ); - let request = http_types::new_outgoing_request( + let request = http_types::OutgoingRequest::new( &method, Some(path_with_query), Some(&scheme), @@ -70,11 +70,13 @@ pub fn request( headers, ); - let outgoing_body = http_types::outgoing_request_write(request) + let outgoing_body = request + .write() .map_err(|_| anyhow!("outgoing request write failed"))?; if let Some(mut buf) = body { - let request_body = http_types::outgoing_body_write(outgoing_body) + let request_body = outgoing_body + .write() .map_err(|_| anyhow!("outgoing request write failed"))?; let pollable = request_body.subscribe(); @@ -111,17 +113,15 @@ pub fn request( let future_response = outgoing_handler::handle(request, None)?; - // TODO: The current implementation requires this drop after the request is sent. - // The ownership semantics are unclear in wasi-http we should clarify exactly what is - // supposed to happen here. - http_types::outgoing_body_finish(outgoing_body, None); + http_types::OutgoingBody::finish(outgoing_body, None); - let incoming_response = match http_types::future_incoming_response_get(future_response) { + let incoming_response = match future_response.get() { Some(result) => result.map_err(|_| anyhow!("incoming response errored"))?, None => { - let pollable = http_types::listen_to_future_incoming_response(future_response); + let pollable = future_response.subscribe(); let _ = poll::poll_list(&[&pollable]); - http_types::future_incoming_response_get(future_response) + future_response + .get() .expect("incoming response available") .map_err(|_| anyhow!("incoming response errored"))? } @@ -130,18 +130,19 @@ pub fn request( // Error? anyway, just use its Debug here: .map_err(|e| anyhow!("{e:?}"))?; - http_types::drop_future_incoming_response(future_response); + drop(future_response); - let status = http_types::incoming_response_status(incoming_response); + let status = incoming_response.status(); - let headers_handle = http_types::incoming_response_headers(incoming_response); - let headers = http_types::fields_entries(headers_handle); - http_types::drop_fields(headers_handle); + let headers_handle = incoming_response.headers(); + let headers = headers_handle.entries(); + drop(headers_handle); - let incoming_body = http_types::incoming_response_consume(incoming_response) + let incoming_body = incoming_response + .consume() .map_err(|()| anyhow!("incoming response has no body stream"))?; - http_types::drop_incoming_response(incoming_response); + drop(incoming_response); let input_stream = incoming_body.stream().unwrap(); let input_stream_pollable = input_stream.subscribe(); diff --git a/crates/wasi-http/src/http_impl.rs b/crates/wasi-http/src/http_impl.rs index ae05c998d4ad..2df2213134de 100644 --- a/crates/wasi-http/src/http_impl.rs +++ b/crates/wasi-http/src/http_impl.rs @@ -1,8 +1,8 @@ use crate::bindings::http::{ outgoing_handler, - types::{FutureIncomingResponse, OutgoingRequest, RequestOptions, Scheme}, + types::{RequestOptions, Scheme}, }; -use crate::types::{self, HostFutureIncomingResponse, IncomingResponseInternal, TableHttpExt}; +use crate::types::{self, HostFutureIncomingResponse, IncomingResponseInternal}; use crate::WasiHttpView; use anyhow::Context; use bytes::Bytes; @@ -11,14 +11,17 @@ use hyper::Method; use std::time::Duration; use tokio::net::TcpStream; use tokio::time::timeout; +use types::HostOutgoingRequest; +use wasmtime::component::Resource; use wasmtime_wasi::preview2; impl outgoing_handler::Host for T { fn handle( &mut self, - request_id: OutgoingRequest, + request_id: Resource, options: Option, - ) -> wasmtime::Result> { + ) -> wasmtime::Result, outgoing_handler::Error>> + { let connect_timeout = Duration::from_millis( options .and_then(|opts| opts.connect_timeout_ms) @@ -37,7 +40,7 @@ impl outgoing_handler::Host for T { .unwrap_or(600 * 1000) as u64, ); - let req = types::OutgoingRequestLens::from(request_id).delete(self.table())?; + let req = self.table().delete_resource(request_id)?; let method = match req.method { crate::bindings::http::types::Method::Get => Method::GET, @@ -175,7 +178,7 @@ impl outgoing_handler::Host for T { let fut = self .table() - .push_future_incoming_response(HostFutureIncomingResponse::new(handle))?; + .push_resource(HostFutureIncomingResponse::new(handle))?; Ok(Ok(fut)) } diff --git a/crates/wasi-http/src/lib.rs b/crates/wasi-http/src/lib.rs index 1fc7bc3736d7..2fac15be2ca2 100644 --- a/crates/wasi-http/src/lib.rs +++ b/crates/wasi-http/src/lib.rs @@ -19,6 +19,17 @@ pub mod bindings { with: { "wasi:io/streams": wasmtime_wasi::preview2::bindings::io::streams, "wasi:io/poll": wasmtime_wasi::preview2::bindings::io::poll, + + "wasi:http/types/outgoing-body": super::body::HostOutgoingBody, + "wasi:http/types/future-incoming-response": super::types::HostFutureIncomingResponse, + "wasi:http/types/outgoing-response": super::types::HostOutgoingResponse, + "wasi:http/types/future-trailers": super::body::HostFutureTrailers, + "wasi:http/types/incoming-body": super::body::HostIncomingBody, + "wasi:http/types/incoming-response": super::types::HostIncomingResponse, + "wasi:http/types/response-outparam": super::types::HostResponseOutparam, + "wasi:http/types/outgoing-request": super::types::HostOutgoingRequest, + "wasi:http/types/incoming-request": super::types::HostIncomingRequest, + "wasi:http/types/fields": super::types::HostFields, } }); diff --git a/crates/wasi-http/src/types.rs b/crates/wasi-http/src/types.rs index 2b6ff34b1056..308b65283cb3 100644 --- a/crates/wasi-http/src/types.rs +++ b/crates/wasi-http/src/types.rs @@ -2,18 +2,12 @@ //! implementation of the wasi-http API. use crate::{ - bindings::http::types::{ - self, FutureTrailers, IncomingBody, IncomingRequest, Method, OutgoingBody, - OutgoingResponse, ResponseOutparam, Scheme, - }, - body::{ - HostFutureTrailers, HostIncomingBody, HostIncomingBodyBuilder, HostOutgoingBody, - HyperIncomingBody, HyperOutgoingBody, - }, + bindings::http::types::{self, Method, Scheme}, + body::{HostIncomingBodyBuilder, HyperIncomingBody, HyperOutgoingBody}, }; use std::any::Any; use wasmtime::component::Resource; -use wasmtime_wasi::preview2::{AbortOnDropJoinHandle, Subscribe, Table, TableError}; +use wasmtime_wasi::preview2::{AbortOnDropJoinHandle, Subscribe, Table}; /// Capture the state necessary for use in the wasi-http API implementation. pub struct WasiHttpCtx; @@ -25,21 +19,17 @@ pub trait WasiHttpView: Send { fn new_incoming_request( &mut self, req: hyper::Request, - ) -> wasmtime::Result { + ) -> wasmtime::Result> { let (parts, body) = req.into_parts(); let body = HostIncomingBodyBuilder { body, // TODO: this needs to be plumbed through between_bytes_timeout: std::time::Duration::from_millis(600 * 1000), }; - Ok(IncomingRequestLens::push( - self.table(), - HostIncomingRequest { - parts, - body: Some(body), - }, - )? - .id) + Ok(self.table().push_resource(HostIncomingRequest { + parts, + body: Some(body), + })?) } fn new_response_outparam( @@ -47,27 +37,24 @@ pub trait WasiHttpView: Send { result: tokio::sync::oneshot::Sender< Result, types::Error>, >, - ) -> wasmtime::Result { - Ok(ResponseOutparamLens::push(self.table(), HostResponseOutparam { result })?.id) + ) -> wasmtime::Result> { + let id = self + .table() + .push_resource(HostResponseOutparam { result })?; + Ok(id) } } -pub type IncomingRequestLens = TableLens; - pub struct HostIncomingRequest { pub parts: http::request::Parts, pub body: Option, } -pub type ResponseOutparamLens = TableLens; - pub struct HostResponseOutparam { pub result: tokio::sync::oneshot::Sender, types::Error>>, } -pub type OutgoingRequestLens = TableLens; - pub struct HostOutgoingRequest { pub method: Method, pub scheme: Option, @@ -84,8 +71,6 @@ pub struct HostIncomingResponse { pub worker: AbortOnDropJoinHandle>, } -pub type OutgoingResponseLens = TableLens; - pub struct HostOutgoingResponse { pub status: u16, pub headers: FieldMap, @@ -173,259 +158,3 @@ impl Subscribe for HostFutureIncomingResponse { } } } - -pub struct TableLens { - id: u32, - _unused: std::marker::PhantomData, -} - -impl TableLens { - pub fn from(id: u32) -> Self { - Self { - id, - _unused: std::marker::PhantomData {}, - } - } - - pub fn into(self) -> u32 { - self.id - } - - #[inline(always)] - pub fn push(table: &mut Table, val: T) -> Result { - let id = table.push(Box::new(val))?; - Ok(Self::from(id)) - } - - #[inline(always)] - pub fn get<'t>(&self, table: &'t Table) -> Result<&'t T, TableError> { - table.get(self.id) - } - - #[inline(always)] - pub fn get_mut<'t>(&self, table: &'t mut Table) -> Result<&'t mut T, TableError> { - table.get_mut(self.id) - } - - #[inline(always)] - pub fn delete(&self, table: &mut Table) -> Result { - table.delete(self.id) - } -} - -pub trait TableHttpExt { - fn push_outgoing_response( - &mut self, - resp: HostOutgoingResponse, - ) -> Result; - fn get_outgoing_response(&mut self, id: u32) -> Result<&mut HostOutgoingResponse, TableError>; - fn delete_outgoing_response(&mut self, id: u32) -> Result; - - fn push_incoming_response(&mut self, response: HostIncomingResponse) - -> Result; - fn get_incoming_response(&self, id: u32) -> Result<&HostIncomingResponse, TableError>; - fn get_incoming_response_mut( - &mut self, - id: u32, - ) -> Result<&mut HostIncomingResponse, TableError>; - fn delete_incoming_response(&mut self, id: u32) -> Result; - - fn push_fields(&mut self, fields: HostFields) -> Result; - fn get_fields(&mut self, id: u32) -> Result<&mut FieldMap, TableError>; - fn delete_fields(&mut self, id: u32) -> Result; - - fn push_future_incoming_response( - &mut self, - response: HostFutureIncomingResponse, - ) -> Result; - fn get_future_incoming_response( - &self, - id: u32, - ) -> Result<&HostFutureIncomingResponse, TableError>; - fn get_future_incoming_response_mut( - &mut self, - id: u32, - ) -> Result<&mut HostFutureIncomingResponse, TableError>; - fn delete_future_incoming_response( - &mut self, - id: u32, - ) -> Result; - - fn push_incoming_body( - &mut self, - body: HostIncomingBody, - ) -> Result, TableError>; - fn get_incoming_body( - &mut self, - id: &Resource, - ) -> Result<&mut HostIncomingBody, TableError>; - fn delete_incoming_body( - &mut self, - id: Resource, - ) -> Result; - - fn push_outgoing_body(&mut self, body: HostOutgoingBody) -> Result; - fn get_outgoing_body(&mut self, id: u32) -> Result<&mut HostOutgoingBody, TableError>; - fn delete_outgoing_body(&mut self, id: u32) -> Result; - - fn push_future_trailers( - &mut self, - trailers: HostFutureTrailers, - ) -> Result; - fn get_future_trailers( - &mut self, - id: FutureTrailers, - ) -> Result<&mut HostFutureTrailers, TableError>; - fn delete_future_trailers( - &mut self, - id: FutureTrailers, - ) -> Result; -} - -impl TableHttpExt for Table { - fn push_outgoing_response( - &mut self, - response: HostOutgoingResponse, - ) -> Result { - self.push(Box::new(response)) - } - - fn get_outgoing_response( - &mut self, - id: OutgoingResponse, - ) -> Result<&mut HostOutgoingResponse, TableError> { - self.get_mut(id) - } - - fn delete_outgoing_response( - &mut self, - id: OutgoingResponse, - ) -> Result { - self.delete(id) - } - - fn push_incoming_response( - &mut self, - response: HostIncomingResponse, - ) -> Result { - self.push(Box::new(response)) - } - fn get_incoming_response(&self, id: u32) -> Result<&HostIncomingResponse, TableError> { - self.get::(id) - } - fn get_incoming_response_mut( - &mut self, - id: u32, - ) -> Result<&mut HostIncomingResponse, TableError> { - self.get_mut::(id) - } - fn delete_incoming_response(&mut self, id: u32) -> Result { - let resp = self.delete::(id)?; - Ok(resp) - } - - fn push_fields(&mut self, fields: HostFields) -> Result { - match fields { - HostFields::Ref { parent, .. } => self.push_child(Box::new(fields), parent), - HostFields::Owned { .. } => self.push(Box::new(fields)), - } - } - fn get_fields(&mut self, id: u32) -> Result<&mut FieldMap, TableError> { - let fields = self.get_mut::(id)?; - if let HostFields::Ref { parent, get_fields } = *fields { - let entry = self.get_any_mut(parent)?; - return Ok(get_fields(entry)); - } - - match self.get_mut::(id)? { - HostFields::Owned { fields } => Ok(fields), - // NB: ideally the `if let` above would go here instead. That makes - // the borrow-checker unhappy. Unclear why. If you, dear reader, can - // refactor this to remove the `unreachable!` please do. - HostFields::Ref { .. } => unreachable!(), - } - } - fn delete_fields(&mut self, id: u32) -> Result { - let fields = self.delete::(id)?; - Ok(fields) - } - - fn push_future_incoming_response( - &mut self, - response: HostFutureIncomingResponse, - ) -> Result { - self.push(Box::new(response)) - } - fn get_future_incoming_response( - &self, - id: u32, - ) -> Result<&HostFutureIncomingResponse, TableError> { - self.get::(id) - } - fn get_future_incoming_response_mut( - &mut self, - id: u32, - ) -> Result<&mut HostFutureIncomingResponse, TableError> { - self.get_mut::(id) - } - fn delete_future_incoming_response( - &mut self, - id: u32, - ) -> Result { - self.delete(id) - } - - fn push_incoming_body( - &mut self, - body: HostIncomingBody, - ) -> Result, TableError> { - Ok(Resource::new_own(self.push(Box::new(body))?)) - } - - fn get_incoming_body( - &mut self, - id: &Resource, - ) -> Result<&mut HostIncomingBody, TableError> { - self.get_mut(id.rep()) - } - - fn delete_incoming_body( - &mut self, - id: Resource, - ) -> Result { - self.delete(id.rep()) - } - - fn push_outgoing_body(&mut self, body: HostOutgoingBody) -> Result { - Ok(self.push(Box::new(body))?) - } - - fn get_outgoing_body(&mut self, id: u32) -> Result<&mut HostOutgoingBody, TableError> { - self.get_mut(id) - } - - fn delete_outgoing_body(&mut self, id: u32) -> Result { - self.delete(id) - } - - fn push_future_trailers( - &mut self, - trailers: HostFutureTrailers, - ) -> Result { - self.push(Box::new(trailers)) - } - - fn get_future_trailers( - &mut self, - id: FutureTrailers, - ) -> Result<&mut HostFutureTrailers, TableError> { - self.get_mut(id) - } - - fn delete_future_trailers( - &mut self, - id: FutureTrailers, - ) -> Result { - self.delete(id) - } -} diff --git a/crates/wasi-http/src/types_impl.rs b/crates/wasi-http/src/types_impl.rs index a470ece0ef3d..bc2ae97f4ed5 100644 --- a/crates/wasi-http/src/types_impl.rs +++ b/crates/wasi-http/src/types_impl.rs @@ -1,16 +1,12 @@ -use crate::bindings::http::types::{ - Error, Fields, FutureIncomingResponse, FutureTrailers, Headers, IncomingBody, IncomingRequest, - IncomingResponse, Method, OutgoingBody, OutgoingRequest, OutgoingResponse, ResponseOutparam, - Scheme, StatusCode, Trailers, -}; +use crate::bindings::http::types::{Error, Headers, Method, Scheme, StatusCode, Trailers}; use crate::body::{FinishMessage, HostFutureTrailers, HostFutureTrailersState}; use crate::types::{HostIncomingRequest, HostOutgoingResponse}; use crate::WasiHttpView; use crate::{ - body::{HostIncomingBodyBuilder, HostOutgoingBody}, + body::{HostIncomingBody, HostIncomingBodyBuilder, HostOutgoingBody}, types::{ - self, FieldMap, HostFields, HostFutureIncomingResponse, HostIncomingResponse, - HostOutgoingRequest, TableHttpExt, + FieldMap, HostFields, HostFutureIncomingResponse, HostIncomingResponse, + HostOutgoingRequest, HostResponseOutparam, }, }; use anyhow::Context; @@ -18,17 +14,32 @@ use std::any::Any; use wasmtime::component::Resource; use wasmtime_wasi::preview2::{ bindings::io::streams::{InputStream, OutputStream}, - Pollable, + Pollable, Table, }; -impl crate::bindings::http::types::Host for T { - fn drop_fields(&mut self, fields: Fields) -> wasmtime::Result<()> { - self.table() - .delete_fields(fields) - .context("[drop_fields] deleting fields")?; - Ok(()) +impl crate::bindings::http::types::Host for T {} + +fn get_fields_mut<'a>( + table: &'a mut Table, + id: &Resource, +) -> wasmtime::Result<&'a mut FieldMap> { + let fields = table.get_resource(&id)?; + if let HostFields::Ref { parent, get_fields } = *fields { + let entry = table.get_any_mut(parent)?; + return Ok(get_fields(entry)); } - fn new_fields(&mut self, entries: Vec<(String, Vec)>) -> wasmtime::Result { + + match table.get_resource_mut(&id)? { + HostFields::Owned { fields } => Ok(fields), + // NB: ideally the `if let` above would go here instead. That makes + // the borrow-checker unhappy. Unclear why. If you, dear reader, can + // refactor this to remove the `unreachable!` please do. + HostFields::Ref { .. } => unreachable!(), + } +} + +impl crate::bindings::http::types::HostFields for T { + fn new(&mut self, entries: Vec<(String, Vec)>) -> wasmtime::Result> { let mut map = hyper::HeaderMap::new(); for (header, value) in entries { @@ -39,14 +50,25 @@ impl crate::bindings::http::types::Host for T { let id = self .table() - .push_fields(HostFields::Owned { fields: map }) + .push_resource(HostFields::Owned { fields: map }) .context("[new_fields] pushing fields")?; + Ok(id) } - fn fields_get(&mut self, fields: Fields, name: String) -> wasmtime::Result>> { - let res = self - .table() - .get_fields(fields) + + fn drop(&mut self, fields: Resource) -> wasmtime::Result<()> { + self.table() + .delete_resource(fields) + .context("[drop_fields] deleting fields")?; + Ok(()) + } + + fn get( + &mut self, + fields: Resource, + name: String, + ) -> wasmtime::Result>> { + let res = get_fields_mut(self.table(), &fields) .context("[fields_get] getting fields")? .get_all(hyper::header::HeaderName::from_bytes(name.as_bytes())?) .into_iter() @@ -54,13 +76,14 @@ impl crate::bindings::http::types::Host for T { .collect(); Ok(res) } - fn fields_set( + + fn set( &mut self, - fields: Fields, + fields: Resource, name: String, values: Vec>, ) -> wasmtime::Result<()> { - let m = self.table().get_fields(fields)?; + let m = get_fields_mut(self.table(), &fields)?; let header = hyper::header::HeaderName::from_bytes(name.as_bytes())?; @@ -72,61 +95,57 @@ impl crate::bindings::http::types::Host for T { Ok(()) } - fn fields_delete(&mut self, fields: Fields, name: String) -> wasmtime::Result<()> { - let m = self.table().get_fields(fields)?; + + fn delete(&mut self, fields: Resource, name: String) -> wasmtime::Result<()> { + let m = get_fields_mut(self.table(), &fields)?; let header = hyper::header::HeaderName::from_bytes(name.as_bytes())?; m.remove(header); Ok(()) } - fn fields_append( + + fn append( &mut self, - fields: Fields, + fields: Resource, name: String, value: Vec, ) -> wasmtime::Result<()> { - let m = self - .table() - .get_fields(fields) + let m = get_fields_mut(self.table(), &fields) .context("[fields_append] getting mutable fields")?; let header = hyper::header::HeaderName::from_bytes(name.as_bytes())?; let value = hyper::header::HeaderValue::from_bytes(&value)?; m.append(header, value); Ok(()) } - fn fields_entries(&mut self, fields: Fields) -> wasmtime::Result)>> { - let fields = self.table().get_fields(fields)?; + + fn entries( + &mut self, + fields: Resource, + ) -> wasmtime::Result)>> { + let fields = get_fields_mut(self.table(), &fields)?; let result = fields .iter() .map(|(name, value)| (name.as_str().to_owned(), value.as_bytes().to_owned())) .collect(); Ok(result) } - fn fields_clone(&mut self, fields: Fields) -> wasmtime::Result { - let fields = self - .table() - .get_fields(fields) + + fn clone(&mut self, fields: Resource) -> wasmtime::Result> { + let fields = get_fields_mut(self.table(), &fields) .context("[fields_clone] getting fields")? .clone(); + let id = self .table() - .push_fields(HostFields::Owned { fields }) + .push_resource(HostFields::Owned { fields }) .context("[fields_clone] pushing fields")?; + Ok(id) } - fn drop_incoming_request(&mut self, id: IncomingRequest) -> wasmtime::Result<()> { - let _ = types::IncomingRequestLens::from(id).delete(self.table())?; - Ok(()) - } - fn drop_outgoing_request(&mut self, request: OutgoingRequest) -> wasmtime::Result<()> { - types::OutgoingRequestLens::from(request).delete(self.table())?; - Ok(()) - } - fn incoming_request_method(&mut self, request: IncomingRequest) -> wasmtime::Result { - let method = types::IncomingRequestLens::from(request) - .get(self.table())? - .parts - .method - .as_ref(); +} + +impl crate::bindings::http::types::HostIncomingRequest for T { + fn method(&mut self, id: Resource) -> wasmtime::Result { + let method = self.table().get_resource(&id)?.parts.method.as_ref(); if method == hyper::Method::GET { Ok(Method::Get) @@ -150,19 +169,19 @@ impl crate::bindings::http::types::Host for T { Ok(Method::Other(method.to_owned())) } } - fn incoming_request_path_with_query( + fn path_with_query( &mut self, - id: IncomingRequest, + id: Resource, ) -> wasmtime::Result> { - let req = types::IncomingRequestLens::from(id).get(self.table())?; + let req = self.table().get_resource(&id)?; Ok(req .parts .uri .path_and_query() .map(|path_and_query| path_and_query.as_str().to_owned())) } - fn incoming_request_scheme(&mut self, id: IncomingRequest) -> wasmtime::Result> { - let req = types::IncomingRequestLens::from(id).get(self.table())?; + fn scheme(&mut self, id: Resource) -> wasmtime::Result> { + let req = self.table().get_resource(&id)?; Ok(req.parts.uri.scheme().map(|scheme| { if scheme == &http::uri::Scheme::HTTP { return Scheme::Http; @@ -175,19 +194,20 @@ impl crate::bindings::http::types::Host for T { Scheme::Other(req.parts.uri.scheme_str().unwrap().to_owned()) })) } - fn incoming_request_authority( - &mut self, - id: IncomingRequest, - ) -> wasmtime::Result> { - let req = types::IncomingRequestLens::from(id).get(self.table())?; + fn authority(&mut self, id: Resource) -> wasmtime::Result> { + let req = self.table().get_resource(&id)?; Ok(req .parts .uri .authority() .map(|auth| auth.as_str().to_owned())) } - fn incoming_request_headers(&mut self, id: IncomingRequest) -> wasmtime::Result { - let _ = types::IncomingRequestLens::from(id).get(self.table())?; + + fn headers( + &mut self, + id: Resource, + ) -> wasmtime::Result> { + let _ = self.table().get_resource(&id)?; fn get_fields(elem: &mut dyn Any) -> &mut FieldMap { &mut elem @@ -197,56 +217,67 @@ impl crate::bindings::http::types::Host for T { .headers } - let headers = self.table().push_fields(HostFields::Ref { - parent: id, - get_fields, - })?; + let headers = self.table().push_child_resource( + HostFields::Ref { + parent: id.rep(), + get_fields, + }, + &id, + )?; Ok(headers) } - fn incoming_request_consume( + + fn consume( &mut self, - id: IncomingRequest, - ) -> wasmtime::Result, ()>> { - let req = types::IncomingRequestLens::from(id).get_mut(self.table())?; + id: Resource, + ) -> wasmtime::Result, ()>> { + let req = self.table().get_resource_mut(&id)?; match req.body.take() { Some(builder) => { - let id = self.table().push_incoming_body(builder.build())?; + let id = self.table().push_resource(builder.build())?; Ok(Ok(id)) } None => Ok(Err(())), } } - fn new_outgoing_request( + + fn drop(&mut self, id: Resource) -> wasmtime::Result<()> { + let _ = self.table().delete_resource(id)?; + Ok(()) + } +} + +impl crate::bindings::http::types::HostOutgoingRequest for T { + fn new( &mut self, method: Method, path_with_query: Option, scheme: Option, authority: Option, - headers: Headers, - ) -> wasmtime::Result { - let headers = self.table().get_fields(headers)?.clone(); - - let req = HostOutgoingRequest { - path_with_query: path_with_query.unwrap_or("".to_string()), - authority: authority.unwrap_or("".to_string()), - method, - headers, - scheme, - body: None, - }; - let id = types::OutgoingRequestLens::push(self.table(), req) - .context("[new_outgoing_request] pushing request")? - .into(); - Ok(id) + headers: Resource, + ) -> wasmtime::Result> { + let headers = get_fields_mut(self.table(), &headers)?.clone(); + self.table() + .push_resource(HostOutgoingRequest { + path_with_query: path_with_query.unwrap_or("".to_string()), + authority: authority.unwrap_or("".to_string()), + method, + headers, + scheme, + body: None, + }) + .context("[new_outgoing_request] pushing request") } - fn outgoing_request_write( + + fn write( &mut self, - request: OutgoingRequest, - ) -> wasmtime::Result> { - let req = types::OutgoingRequestLens::from(request) - .get_mut(self.table()) + request: Resource, + ) -> wasmtime::Result, ()>> { + let req = self + .table() + .get_resource_mut(&request) .context("[outgoing_request_write] getting request")?; if req.body.is_some() { @@ -259,110 +290,122 @@ impl crate::bindings::http::types::Host for T { // The output stream will necessarily outlive the request, because we could be still // writing to the stream after `outgoing-handler.handle` is called. - let outgoing_body = self.table().push_outgoing_body(host_body)?; + let outgoing_body = self.table().push_resource(host_body)?; Ok(Ok(outgoing_body)) } - fn drop_response_outparam(&mut self, id: ResponseOutparam) -> wasmtime::Result<()> { - let _ = types::ResponseOutparamLens::from(id).delete(self.table())?; + + fn drop(&mut self, request: Resource) -> wasmtime::Result<()> { + let _ = self.table().delete_resource(request)?; Ok(()) } - fn set_response_outparam( +} + +impl crate::bindings::http::types::HostResponseOutparam for T { + fn drop(&mut self, id: Resource) -> wasmtime::Result<()> { + let _ = self.table().delete_resource(id)?; + Ok(()) + } + fn set( &mut self, - id: ResponseOutparam, - resp: Result, + id: Resource, + resp: Result, Error>, ) -> wasmtime::Result<()> { let val = match resp { - Ok(resp) => Ok(self.table().delete_outgoing_response(resp)?.try_into()?), + Ok(resp) => Ok(self.table().delete_resource(resp)?.try_into()?), Err(e) => Err(e), }; - types::ResponseOutparamLens::from(id) - .delete(self.table())? + self.table() + .delete_resource(id)? .result .send(val) .map_err(|_| anyhow::anyhow!("failed to initialize response")) } - fn drop_incoming_response(&mut self, response: IncomingResponse) -> wasmtime::Result<()> { - self.table() - .delete_incoming_response(response) +} + +impl crate::bindings::http::types::HostIncomingResponse for T { + fn drop(&mut self, response: Resource) -> wasmtime::Result<()> { + let _ = self + .table() + .delete_resource(response) .context("[drop_incoming_response] deleting response")?; Ok(()) } - fn drop_outgoing_response(&mut self, id: OutgoingResponse) -> wasmtime::Result<()> { - types::OutgoingResponseLens::from(id).delete(self.table())?; - Ok(()) - } - fn incoming_response_status( - &mut self, - response: IncomingResponse, - ) -> wasmtime::Result { + + fn status(&mut self, response: Resource) -> wasmtime::Result { let r = self .table() - .get_incoming_response(response) + .get_resource(&response) .context("[incoming_response_status] getting response")?; Ok(r.status) } - fn incoming_response_headers( + + fn headers( &mut self, - response: IncomingResponse, - ) -> wasmtime::Result { + response: Resource, + ) -> wasmtime::Result> { let _ = self .table() - .get_incoming_response_mut(response) + .get_resource(&response) .context("[incoming_response_headers] getting response")?; fn get_fields(elem: &mut dyn Any) -> &mut FieldMap { &mut elem.downcast_mut::().unwrap().headers } - let id = self.table().push_fields(HostFields::Ref { - parent: response, - get_fields, - })?; + let id = self.table().push_child_resource( + HostFields::Ref { + parent: response.rep(), + get_fields, + }, + &response, + )?; Ok(id) } - fn incoming_response_consume( + + fn consume( &mut self, - response: IncomingResponse, - ) -> wasmtime::Result, ()>> { + response: Resource, + ) -> wasmtime::Result, ()>> { let table = self.table(); let r = table - .get_incoming_response_mut(response) + .get_resource_mut(&response) .context("[incoming_response_consume] getting response")?; match r.body.take() { Some(builder) => { - let id = self.table().push_incoming_body(builder.build())?; + let id = self.table().push_resource(builder.build())?; Ok(Ok(id)) } None => Ok(Err(())), } } - fn drop_future_trailers(&mut self, id: FutureTrailers) -> wasmtime::Result<()> { - self.table() - .delete_future_trailers(id) +} + +impl crate::bindings::http::types::HostFutureTrailers for T { + fn drop(&mut self, id: Resource) -> wasmtime::Result<()> { + let _ = self + .table() + .delete_resource(id) .context("[drop future-trailers] deleting future-trailers")?; Ok(()) } - fn future_trailers_subscribe( + fn subscribe( &mut self, - index: FutureTrailers, + index: Resource, ) -> wasmtime::Result> { - wasmtime_wasi::preview2::subscribe( - self.table(), - Resource::::new_borrow(index), - ) + wasmtime_wasi::preview2::subscribe(self.table(), index) } - fn future_trailers_get( + fn get( &mut self, - id: FutureTrailers, - ) -> wasmtime::Result>> { - let trailers = self.table().get_future_trailers(id)?; + id: Resource, + ) -> wasmtime::Result, Error>>> { + let trailers = self.table().get_resource_mut(&id)?; match &trailers.state { HostFutureTrailersState::Waiting(_) => return Ok(None), HostFutureTrailersState::Done(Err(e)) => return Ok(Some(Err(e.clone()))), @@ -377,39 +420,72 @@ impl crate::bindings::http::types::Host for T { } } - let hdrs = self.table().push_fields(HostFields::Ref { - parent: id, - get_fields, - })?; + let hdrs = self.table().push_child_resource( + HostFields::Ref { + parent: id.rep(), + get_fields, + }, + &id, + )?; Ok(Some(Ok(hdrs))) } +} - fn new_outgoing_response( +impl crate::bindings::http::types::HostIncomingBody for T { + fn stream( + &mut self, + id: Resource, + ) -> wasmtime::Result, ()>> { + let body = self.table().get_resource_mut(&id)?; + + if let Some(stream) = body.stream.take() { + let stream = InputStream::Host(Box::new(stream)); + let stream = self.table().push_child_resource(stream, &id)?; + return Ok(Ok(stream)); + } + + Ok(Err(())) + } + + fn finish( + &mut self, + id: Resource, + ) -> wasmtime::Result> { + let body = self.table().delete_resource(id)?; + let trailers = self.table().push_resource(body.into_future_trailers())?; + Ok(trailers) + } + + fn drop(&mut self, id: Resource) -> wasmtime::Result<()> { + let _ = self.table().delete_resource(id)?; + Ok(()) + } +} + +impl crate::bindings::http::types::HostOutgoingResponse for T { + fn new( &mut self, status: StatusCode, - headers: Headers, - ) -> wasmtime::Result { - let fields = self.table().get_fields(headers)?.clone(); - self.table().delete_fields(headers)?; - - let id = types::OutgoingResponseLens::push( - self.table(), - HostOutgoingResponse { - status, - headers: fields, - body: None, - }, - )? - .into(); + headers: Resource, + ) -> wasmtime::Result> { + let fields = get_fields_mut(self.table(), &headers)?.clone(); + self.table().delete_resource(headers)?; + + let id = self.table().push_resource(HostOutgoingResponse { + status, + headers: fields, + body: None, + })?; Ok(id) } - fn outgoing_response_write( + + fn write( &mut self, - id: OutgoingResponse, - ) -> wasmtime::Result> { - let resp = types::OutgoingResponseLens::from(id).get_mut(self.table())?; + id: Resource, + ) -> wasmtime::Result, ()>> { + let resp = self.table().get_resource_mut(&id)?; if resp.body.is_some() { return Ok(Err(())); @@ -419,22 +495,28 @@ impl crate::bindings::http::types::Host for T { resp.body.replace(body); - let id = self.table().push_outgoing_body(host)?; + let id = self.table().push_resource(host)?; Ok(Ok(id)) } - fn drop_future_incoming_response( - &mut self, - id: FutureIncomingResponse, - ) -> wasmtime::Result<()> { - let _ = self.table().delete_future_incoming_response(id)?; + + fn drop(&mut self, id: Resource) -> wasmtime::Result<()> { + let _ = self.table().delete_resource(id)?; + Ok(()) + } +} + +impl crate::bindings::http::types::HostFutureIncomingResponse for T { + fn drop(&mut self, id: Resource) -> wasmtime::Result<()> { + let _ = self.table().delete_resource(id)?; Ok(()) } - fn future_incoming_response_get( + + fn get( &mut self, - id: FutureIncomingResponse, - ) -> wasmtime::Result, ()>>> { - let resp = self.table().get_future_incoming_response_mut(id)?; + id: Resource, + ) -> wasmtime::Result, Error>, ()>>> { + let resp = self.table().get_resource_mut(&id)?; match resp { HostFutureIncomingResponse::Pending(_) => return Ok(None), @@ -455,7 +537,7 @@ impl crate::bindings::http::types::Host for T { let (parts, body) = resp.resp.into_parts(); - let resp = self.table().push_incoming_response(HostIncomingResponse { + let resp = self.table().push_resource(HostIncomingResponse { status: parts.status.as_u16(), headers: FieldMap::from(parts.headers), body: Some(HostIncomingBodyBuilder { @@ -467,36 +549,34 @@ impl crate::bindings::http::types::Host for T { Ok(Some(Ok(Ok(resp)))) } - fn listen_to_future_incoming_response( + fn subscribe( &mut self, - id: FutureIncomingResponse, + id: Resource, ) -> wasmtime::Result> { - wasmtime_wasi::preview2::subscribe( - self.table(), - Resource::::new_borrow(id), - ) + wasmtime_wasi::preview2::subscribe(self.table(), id) } +} - fn outgoing_body_write( +impl crate::bindings::http::types::HostOutgoingBody for T { + fn write( &mut self, - id: OutgoingBody, + id: Resource, ) -> wasmtime::Result, ()>> { - let body = self.table().get_outgoing_body(id)?; + let body = self.table().get_resource_mut(&id)?; if let Some(stream) = body.body_output_stream.take() { - let dummy = Resource::::new_own(id); - let id = self.table().push_child_resource(stream, &dummy)?; + let id = self.table().push_child_resource(stream, &id)?; Ok(Ok(id)) } else { Ok(Err(())) } } - fn outgoing_body_finish( + fn finish( &mut self, - id: OutgoingBody, - ts: Option, + id: Resource, + ts: Option>, ) -> wasmtime::Result<()> { - let mut body = self.table().delete_outgoing_body(id)?; + let mut body = self.table().delete_resource(id)?; let sender = body .finish_sender @@ -504,7 +584,7 @@ impl crate::bindings::http::types::Host for T { .expect("outgoing-body trailer_sender consumed by a non-owning function"); let message = if let Some(ts) = ts { - FinishMessage::Trailers(self.table().get_fields(ts)?.clone().into()) + FinishMessage::Trailers(get_fields_mut(self.table(), &ts)?.clone().into()) } else { FinishMessage::Finished }; @@ -515,8 +595,8 @@ impl crate::bindings::http::types::Host for T { Ok(()) } - fn drop_outgoing_body(&mut self, id: OutgoingBody) -> wasmtime::Result<()> { - let mut body = self.table().delete_outgoing_body(id)?; + fn drop(&mut self, id: Resource) -> wasmtime::Result<()> { + let mut body = self.table().delete_resource(id)?; let sender = body .finish_sender @@ -529,33 +609,3 @@ impl crate::bindings::http::types::Host for T { Ok(()) } } - -impl crate::bindings::http::types::HostIncomingBody for T { - fn stream( - &mut self, - id: Resource, - ) -> wasmtime::Result, ()>> { - let body = self.table().get_incoming_body(&id)?; - - if let Some(stream) = body.stream.take() { - let stream = InputStream::Host(Box::new(stream)); - let stream = self.table().push_child_resource(stream, &id)?; - return Ok(Ok(stream)); - } - - Ok(Err(())) - } - - fn finish(&mut self, id: Resource) -> wasmtime::Result { - let body = self.table().delete_incoming_body(id)?; - let trailers = self - .table() - .push_future_trailers(body.into_future_trailers())?; - Ok(trailers) - } - - fn drop(&mut self, id: Resource) -> wasmtime::Result<()> { - let _ = self.table().delete_incoming_body(id)?; - Ok(()) - } -} diff --git a/crates/wasi-http/wit/deps/http/types.wit b/crates/wasi-http/wit/deps/http/types.wit index 1abc7a1ff2c4..aa486f3c7c7c 100644 --- a/crates/wasi-http/wit/deps/http/types.wit +++ b/crates/wasi-http/wit/deps/http/types.wit @@ -30,35 +30,38 @@ interface types { // This type enumerates the different kinds of errors that may occur when // initially returning a response. variant error { - invalid-url(string), - timeout-error(string), - protocol-error(string), - unexpected-error(string) + invalid-url(string), + timeout-error(string), + protocol-error(string), + unexpected-error(string) } // This following block defines the `fields` resource which corresponds to // HTTP standard Fields. Soon, when resource types are added, the `type // fields = u32` type alias can be replaced by a proper `resource fields` // definition containing all the functions using the method syntactic sugar. - type fields = u32 - drop-fields: func(fields: /* own */ fields) - // Multiple values for a header are multiple entries in the list with the - // same key. - new-fields: func(entries: list>>) -> fields - // Values off wire are not necessarily well formed, so they are given by - // list instead of string. - fields-get: func(fields: /* borrow */ fields, name: string) -> list> - // Values off wire are not necessarily well formed, so they are given by - // list instead of string. - fields-set: func(fields: /* borrow */ fields, name: string, value: list>) - fields-delete: func(fields: /* borrow */ fields, name: string) - fields-append: func(fields: /* borrow */ fields, name: string, value: list) - - // Values off wire are not necessarily well formed, so they are given by - // list instead of string. - fields-entries: func(fields: /* borrow */ fields) -> list>> - // Deep copy of all contents in a fields. - fields-clone: func(fields: /* borrow */ fields) -> fields + resource fields { + // Multiple values for a header are multiple entries in the list with the + // same key. + constructor(entries: list>>) + + // Values off wire are not necessarily well formed, so they are given by + // list instead of string. + get: func(name: string) -> list> + + // Values off wire are not necessarily well formed, so they are given by + // list instead of string. + set: func(name: string, value: list>) + delete: func(name: string) + append: func(name: string, value: list) + + // Values off wire are not necessarily well formed, so they are given by + // list instead of string. + entries: func() -> list>> + + // Deep copy of all contents in a fields. + clone: func() -> fields + } type headers = fields type trailers = fields @@ -71,31 +74,35 @@ interface types { // a single `request` type (that uses the single `stream` type mentioned // above). The `consume` and `write` methods may only be called once (and // return failure thereafter). - type incoming-request = u32 - drop-incoming-request: func(request: /* own */ incoming-request) - incoming-request-method: func(request: /* borrow */ incoming-request) -> method - incoming-request-path-with-query: func(request: /* borrow */ incoming-request) -> option - incoming-request-scheme: func(request: /* borrow */ incoming-request) -> option - incoming-request-authority: func(request: /* borrow */ incoming-request) -> option - - incoming-request-headers: func(request: /* borrow */ incoming-request) -> /* child */ headers - // Will return the input-stream child at most once. If called more than - // once, subsequent calls will return error. - incoming-request-consume: func(request: /* borrow */ incoming-request) -> result< /* own */ incoming-body> - - type outgoing-request = u32 - drop-outgoing-request: func(request: /* own */ outgoing-request) - new-outgoing-request: func( - method: method, - path-with-query: option, - scheme: option, - authority: option, - headers: /* borrow */ headers - ) -> outgoing-request - - // Will return the outgoing-body child at most once. If called more than - // once, subsequent calls will return error. - outgoing-request-write: func(request: /* borrow */ outgoing-request) -> result< /* child */ outgoing-body> + resource incoming-request { + method: func() -> method + + path-with-query: func() -> option + + scheme: func() -> option + + authority: func() -> option + + headers: func() -> /* child */ headers + // Will return the input-stream child at most once. If called more than + // once, subsequent calls will return error. + + consume: func() -> result< /* own */ incoming-body> + } + + resource outgoing-request { + constructor( + method: method, + path-with-query: option, + scheme: option, + authority: option, + headers: /* borrow */ headers + ) + + // Will return the outgoing-body child at most once. If called more than + // once, subsequent calls will return error. + write: func() -> result< /* child */ outgoing-body> + } // Additional optional parameters that can be set when making a request. record request-options { @@ -119,9 +126,9 @@ interface types { // definition. Later, with Preview3, the need for an outparam goes away entirely // (the `wasi:http/handler` interface used for both incoming and outgoing can // simply return a `stream`). - type response-outparam = u32 - drop-response-outparam: func(response: /* own */ response-outparam) - set-response-outparam: func(param: /* own */ response-outparam, response: result< /* own */ outgoing-response, error>) + resource response-outparam { + set: static func(param: /* own */ response-outparam, response: result< /* own */ outgoing-response, error>) + } // This type corresponds to the HTTP standard Status Code. type status-code = u16 @@ -133,58 +140,58 @@ interface types { // Preview2 will allow both types to be merged together into a single `response` // type (that uses the single `stream` type mentioned above). The `consume` and // `write` methods may only be called once (and return failure thereafter). - type incoming-response = u32 - drop-incoming-response: func(response: /* own */ incoming-response) - incoming-response-status: func(response: /* borrow */ incoming-response) -> status-code - incoming-response-headers: func(response: /* borrow */ incoming-response) -> /* child */ headers - // May be called at most once. returns error if called additional times. - // TODO: make incoming-request-consume work the same way, giving a child - // incoming-body. - incoming-response-consume: func(response: /* borrow */ incoming-response) -> result + resource incoming-response { + status: func() -> status-code + + headers: func() -> /* child */ headers + + // May be called at most once. returns error if called additional times. + // TODO: make incoming-request-consume work the same way, giving a child + // incoming-body. + consume: func() -> result + } resource incoming-body { // returned input-stream is a child - the implementation may trap if // incoming-body is dropped (or consumed by call to // incoming-body-finish) before the input-stream is dropped. // May be called at most once. returns error if called additional times. - %stream: func() -> - result + %stream: func() -> result + // takes ownership of incoming-body. this will trap if the // incoming-body-stream child is still alive! - finish: func() -> - /* transitive child of the incoming-response of incoming-body */ future-trailers + finish: static func(this: /* own */ incoming-body) -> + /* transitive child of the incoming-response of incoming-body */ future-trailers + } + + resource future-trailers { + /// Pollable that resolves when the body has been fully read, and the trailers + /// are ready to be consumed. + subscribe: func() -> /* child */ pollable + + /// Retrieve reference to trailers, if they are ready. + get: func() -> option> } - type future-trailers = u32 - drop-future-trailers: func(this: /* own */ future-trailers) - /// Pollable that resolves when the body has been fully read, and the trailers - /// are ready to be consumed. - future-trailers-subscribe: func(this: /* borrow */ future-trailers) -> /* child */ pollable - - /// Retrieve reference to trailers, if they are ready. - future-trailers-get: func(response: /* borrow */ future-trailers) -> option> - - type outgoing-response = u32 - drop-outgoing-response: func(response: /* own */ outgoing-response) - new-outgoing-response: func( - status-code: status-code, - headers: /* borrow */ headers - ) -> outgoing-response - - /// Will give the child outgoing-response at most once. subsequent calls will - /// return an error. - outgoing-response-write: func(this: /* borrow */ outgoing-response) -> result - - type outgoing-body = u32 - drop-outgoing-body: func(this: /* own */ outgoing-body) - /// Will give the child output-stream at most once. subsequent calls will - /// return an error. - outgoing-body-write: func(this: /* borrow */ outgoing-body) -> 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. - outgoing-body-finish: func(this: /* own */ outgoing-body, trailers: /* own */ option) + resource outgoing-response { + constructor(status-code: status-code, headers: /* borrow */ headers) + + /// Will give the child outgoing-response at most once. subsequent calls will + /// return an error. + write: func() -> result + } + + resource outgoing-body { + /// Will give the child output-stream at most once. subsequent calls will + /// return an error. + 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. + finish: static func(this: /* own */ outgoing-body, trailers: /* own */ option) + } /// The following block defines a special resource type used by the /// `wasi:http/outgoing-handler` interface to emulate @@ -193,14 +200,15 @@ interface types { /// method to get the result if it is available. If the result is not available, /// the client can call `listen` to get a `pollable` that can be passed to /// `wasi:io/poll.poll-list`. - type future-incoming-response = u32 - drop-future-incoming-response: func(f: /* own */ future-incoming-response) - /// option indicates readiness. - /// outer result indicates you are allowed to get the - /// incoming-response-or-error at most once. subsequent calls after ready - /// will return an error here. - /// inner result indicates whether the incoming-response was available, or an - /// error occured. - future-incoming-response-get: func(f: /* borrow */ future-incoming-response) -> option>> - listen-to-future-incoming-response: func(f: /* borrow */ future-incoming-response) -> /* child */ pollable + resource future-incoming-response { + /// option indicates readiness. + /// outer result indicates you are allowed to get the + /// incoming-response-or-error at most once. subsequent calls after ready + /// will return an error here. + /// inner result indicates whether the incoming-response was available, or an + /// error occured. + get: func() -> option>> + + subscribe: func() -> /* child */ pollable + } } diff --git a/crates/wasi/wit/deps/http/types.wit b/crates/wasi/wit/deps/http/types.wit index 1abc7a1ff2c4..aa486f3c7c7c 100644 --- a/crates/wasi/wit/deps/http/types.wit +++ b/crates/wasi/wit/deps/http/types.wit @@ -30,35 +30,38 @@ interface types { // This type enumerates the different kinds of errors that may occur when // initially returning a response. variant error { - invalid-url(string), - timeout-error(string), - protocol-error(string), - unexpected-error(string) + invalid-url(string), + timeout-error(string), + protocol-error(string), + unexpected-error(string) } // This following block defines the `fields` resource which corresponds to // HTTP standard Fields. Soon, when resource types are added, the `type // fields = u32` type alias can be replaced by a proper `resource fields` // definition containing all the functions using the method syntactic sugar. - type fields = u32 - drop-fields: func(fields: /* own */ fields) - // Multiple values for a header are multiple entries in the list with the - // same key. - new-fields: func(entries: list>>) -> fields - // Values off wire are not necessarily well formed, so they are given by - // list instead of string. - fields-get: func(fields: /* borrow */ fields, name: string) -> list> - // Values off wire are not necessarily well formed, so they are given by - // list instead of string. - fields-set: func(fields: /* borrow */ fields, name: string, value: list>) - fields-delete: func(fields: /* borrow */ fields, name: string) - fields-append: func(fields: /* borrow */ fields, name: string, value: list) - - // Values off wire are not necessarily well formed, so they are given by - // list instead of string. - fields-entries: func(fields: /* borrow */ fields) -> list>> - // Deep copy of all contents in a fields. - fields-clone: func(fields: /* borrow */ fields) -> fields + resource fields { + // Multiple values for a header are multiple entries in the list with the + // same key. + constructor(entries: list>>) + + // Values off wire are not necessarily well formed, so they are given by + // list instead of string. + get: func(name: string) -> list> + + // Values off wire are not necessarily well formed, so they are given by + // list instead of string. + set: func(name: string, value: list>) + delete: func(name: string) + append: func(name: string, value: list) + + // Values off wire are not necessarily well formed, so they are given by + // list instead of string. + entries: func() -> list>> + + // Deep copy of all contents in a fields. + clone: func() -> fields + } type headers = fields type trailers = fields @@ -71,31 +74,35 @@ interface types { // a single `request` type (that uses the single `stream` type mentioned // above). The `consume` and `write` methods may only be called once (and // return failure thereafter). - type incoming-request = u32 - drop-incoming-request: func(request: /* own */ incoming-request) - incoming-request-method: func(request: /* borrow */ incoming-request) -> method - incoming-request-path-with-query: func(request: /* borrow */ incoming-request) -> option - incoming-request-scheme: func(request: /* borrow */ incoming-request) -> option - incoming-request-authority: func(request: /* borrow */ incoming-request) -> option - - incoming-request-headers: func(request: /* borrow */ incoming-request) -> /* child */ headers - // Will return the input-stream child at most once. If called more than - // once, subsequent calls will return error. - incoming-request-consume: func(request: /* borrow */ incoming-request) -> result< /* own */ incoming-body> - - type outgoing-request = u32 - drop-outgoing-request: func(request: /* own */ outgoing-request) - new-outgoing-request: func( - method: method, - path-with-query: option, - scheme: option, - authority: option, - headers: /* borrow */ headers - ) -> outgoing-request - - // Will return the outgoing-body child at most once. If called more than - // once, subsequent calls will return error. - outgoing-request-write: func(request: /* borrow */ outgoing-request) -> result< /* child */ outgoing-body> + resource incoming-request { + method: func() -> method + + path-with-query: func() -> option + + scheme: func() -> option + + authority: func() -> option + + headers: func() -> /* child */ headers + // Will return the input-stream child at most once. If called more than + // once, subsequent calls will return error. + + consume: func() -> result< /* own */ incoming-body> + } + + resource outgoing-request { + constructor( + method: method, + path-with-query: option, + scheme: option, + authority: option, + headers: /* borrow */ headers + ) + + // Will return the outgoing-body child at most once. If called more than + // once, subsequent calls will return error. + write: func() -> result< /* child */ outgoing-body> + } // Additional optional parameters that can be set when making a request. record request-options { @@ -119,9 +126,9 @@ interface types { // definition. Later, with Preview3, the need for an outparam goes away entirely // (the `wasi:http/handler` interface used for both incoming and outgoing can // simply return a `stream`). - type response-outparam = u32 - drop-response-outparam: func(response: /* own */ response-outparam) - set-response-outparam: func(param: /* own */ response-outparam, response: result< /* own */ outgoing-response, error>) + resource response-outparam { + set: static func(param: /* own */ response-outparam, response: result< /* own */ outgoing-response, error>) + } // This type corresponds to the HTTP standard Status Code. type status-code = u16 @@ -133,58 +140,58 @@ interface types { // Preview2 will allow both types to be merged together into a single `response` // type (that uses the single `stream` type mentioned above). The `consume` and // `write` methods may only be called once (and return failure thereafter). - type incoming-response = u32 - drop-incoming-response: func(response: /* own */ incoming-response) - incoming-response-status: func(response: /* borrow */ incoming-response) -> status-code - incoming-response-headers: func(response: /* borrow */ incoming-response) -> /* child */ headers - // May be called at most once. returns error if called additional times. - // TODO: make incoming-request-consume work the same way, giving a child - // incoming-body. - incoming-response-consume: func(response: /* borrow */ incoming-response) -> result + resource incoming-response { + status: func() -> status-code + + headers: func() -> /* child */ headers + + // May be called at most once. returns error if called additional times. + // TODO: make incoming-request-consume work the same way, giving a child + // incoming-body. + consume: func() -> result + } resource incoming-body { // returned input-stream is a child - the implementation may trap if // incoming-body is dropped (or consumed by call to // incoming-body-finish) before the input-stream is dropped. // May be called at most once. returns error if called additional times. - %stream: func() -> - result + %stream: func() -> result + // takes ownership of incoming-body. this will trap if the // incoming-body-stream child is still alive! - finish: func() -> - /* transitive child of the incoming-response of incoming-body */ future-trailers + finish: static func(this: /* own */ incoming-body) -> + /* transitive child of the incoming-response of incoming-body */ future-trailers + } + + resource future-trailers { + /// Pollable that resolves when the body has been fully read, and the trailers + /// are ready to be consumed. + subscribe: func() -> /* child */ pollable + + /// Retrieve reference to trailers, if they are ready. + get: func() -> option> } - type future-trailers = u32 - drop-future-trailers: func(this: /* own */ future-trailers) - /// Pollable that resolves when the body has been fully read, and the trailers - /// are ready to be consumed. - future-trailers-subscribe: func(this: /* borrow */ future-trailers) -> /* child */ pollable - - /// Retrieve reference to trailers, if they are ready. - future-trailers-get: func(response: /* borrow */ future-trailers) -> option> - - type outgoing-response = u32 - drop-outgoing-response: func(response: /* own */ outgoing-response) - new-outgoing-response: func( - status-code: status-code, - headers: /* borrow */ headers - ) -> outgoing-response - - /// Will give the child outgoing-response at most once. subsequent calls will - /// return an error. - outgoing-response-write: func(this: /* borrow */ outgoing-response) -> result - - type outgoing-body = u32 - drop-outgoing-body: func(this: /* own */ outgoing-body) - /// Will give the child output-stream at most once. subsequent calls will - /// return an error. - outgoing-body-write: func(this: /* borrow */ outgoing-body) -> 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. - outgoing-body-finish: func(this: /* own */ outgoing-body, trailers: /* own */ option) + resource outgoing-response { + constructor(status-code: status-code, headers: /* borrow */ headers) + + /// Will give the child outgoing-response at most once. subsequent calls will + /// return an error. + write: func() -> result + } + + resource outgoing-body { + /// Will give the child output-stream at most once. subsequent calls will + /// return an error. + 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. + finish: static func(this: /* own */ outgoing-body, trailers: /* own */ option) + } /// The following block defines a special resource type used by the /// `wasi:http/outgoing-handler` interface to emulate @@ -193,14 +200,15 @@ interface types { /// method to get the result if it is available. If the result is not available, /// the client can call `listen` to get a `pollable` that can be passed to /// `wasi:io/poll.poll-list`. - type future-incoming-response = u32 - drop-future-incoming-response: func(f: /* own */ future-incoming-response) - /// option indicates readiness. - /// outer result indicates you are allowed to get the - /// incoming-response-or-error at most once. subsequent calls after ready - /// will return an error here. - /// inner result indicates whether the incoming-response was available, or an - /// error occured. - future-incoming-response-get: func(f: /* borrow */ future-incoming-response) -> option>> - listen-to-future-incoming-response: func(f: /* borrow */ future-incoming-response) -> /* child */ pollable + resource future-incoming-response { + /// option indicates readiness. + /// outer result indicates you are allowed to get the + /// incoming-response-or-error at most once. subsequent calls after ready + /// will return an error here. + /// inner result indicates whether the incoming-response was available, or an + /// error occured. + get: func() -> option>> + + subscribe: func() -> /* child */ pollable + } }