Skip to content
This repository was archived by the owner on Oct 13, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
c3af8fd
feat: add wasi http experimental
eduardomourar Feb 9, 2023
e707e13
feat: add bindings crate
eduardomourar Feb 10, 2023
c5da583
feat: return monadic type for http request
eduardomourar Feb 12, 2023
6b7faa4
chore: rename main http function to send
eduardomourar Feb 12, 2023
8c6043e
chore: update wit-bindgen in bindings crate
eduardomourar Feb 13, 2023
9dc2233
chore: change http method to enum
eduardomourar Feb 14, 2023
72116e3
chore: remove http status error
eduardomourar Feb 14, 2023
b2382e6
Merge branch 'main' into feat/wasi-http-experimental
eduardomourar Feb 19, 2023
bae5545
feat: use new wasi-http wit definition
eduardomourar Feb 23, 2023
922f4a7
Merge remote-tracking branch 'origin/main' into feat/wasi-http-experi…
eduardomourar Feb 23, 2023
dd74ffc
Merge remote-tracking branch 'origin/main' into feat/wasi-http-experi…
eduardomourar Mar 13, 2023
1e38e23
chore: add basic scaffolding for wasi http
eduardomourar Mar 14, 2023
e8cbfcd
Merge remote-tracking branch 'origin/main' into feat/wasi-http-experi…
eduardomourar Mar 14, 2023
0bb1f0e
chore: revert rename for http types
eduardomourar Mar 17, 2023
533894a
chore: changes based on review feedback
eduardomourar Mar 17, 2023
0933bc4
chore: add modules to verify
eduardomourar Mar 17, 2023
09b0c8d
chore: include features wasi bindings
eduardomourar Mar 17, 2023
7f41229
chore: update lock file
eduardomourar Mar 17, 2023
bce17d1
chore: fix formatting
eduardomourar Mar 17, 2023
cdac89d
chore: fix casing for http imported names
eduardomourar Mar 17, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
511 changes: 511 additions & 0 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@ tracing = { workspace = true }
wasmtime = { git = "https://github.com/bytecodealliance/wasmtime", rev = "8d3a881b524d56498dfc71b65818b2abbeb1da44", features = ["component-model"] }
wasi-common = { path = "../wasi-common" }
wasi-cap-std-sync = { path = "../wasi-common/cap-std-sync" }
reqwest = { version = "0.11.14", optional = true }

[dev-dependencies]
test-programs-macros = { path = "../test-programs/macros" }
tracing-subscriber = { version = "0.3", default-features = false, features = ["env-filter", "fmt" ]}
test-log = { version = "0.2", default-features = false, features = ["trace"] }
tempfile = "3.3.0"

[features]
http = ["dep:reqwest"]
42 changes: 42 additions & 0 deletions host/src/default_outgoing_http.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use crate::{
wasi,
wasi::http_types::{
Error as HttpError, FutureIncomingResponse as Response, Method, OutgoingRequest as Request,
RequestOptions,
},
WasiCtx,
};

#[async_trait::async_trait]
impl wasi::default_outgoing_http::Host for WasiCtx {
async fn handle<'a>(
&'a mut self,
_req: Request,
_options: Option<RequestOptions>,
) -> wasmtime::Result<Response> {
todo!()
}
}

impl From<reqwest::Error> for HttpError {
fn from(e: reqwest::Error) -> Self {
Self::UnexpectedError(e.to_string())
}
}

impl From<Method> for reqwest::Method {
fn from(method: Method) -> Self {
match method {
Method::Get => reqwest::Method::GET,
Method::Post => reqwest::Method::POST,
Method::Put => reqwest::Method::PUT,
Method::Delete => reqwest::Method::DELETE,
Method::Patch => reqwest::Method::PATCH,
Method::Connect => reqwest::Method::CONNECT,
Method::Trace => reqwest::Method::TRACE,
Method::Head => reqwest::Method::HEAD,
Method::Options => reqwest::Method::OPTIONS,
_ => panic!("failed due to unsupported method, currently supported methods are: GET, POST, PUT, DELETE, PATCH, CONNECT, TRACE, HEAD, and OPTIONS"),
}
}
}
12 changes: 12 additions & 0 deletions host/src/http.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use crate::{
wasi,
wasi::http_types::{IncomingRequest as Request, ResponseOutparam as Response},
WasiCtx,
};

#[async_trait::async_trait]
impl wasi::http::Host for WasiCtx {
async fn handle<'a>(&'a mut self, _req: Request, _resp: Response) -> anyhow::Result<()> {
todo!()
}
}
8 changes: 8 additions & 0 deletions host/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
mod clocks;
#[cfg(feature = "http")]
mod default_outgoing_http;
mod env;
mod exit;
mod filesystem;
#[cfg(feature = "http")]
mod http;
mod io;
mod ip_name_lookup;
mod network;
Expand Down Expand Up @@ -45,5 +49,9 @@ pub fn add_to_linker<T: Send>(
wasi::exit::add_to_linker(l, f)?;
wasi::environment::add_to_linker(l, f)?;
wasi::environment_preopens::add_to_linker(l, f)?;
#[cfg(feature = "http")]
wasi::default_outgoing_http::add_to_linker(l, f)?;
#[cfg(feature = "http")]
wasi::http::add_to_linker(l, f)?;
Ok(())
}
2 changes: 2 additions & 0 deletions wit/command.wit
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ default world command {
import random: random.random
import poll: poll.poll
import streams: io.streams
import default-outgoing-http: http.outgoing-handler
import http: http.incoming-handler
import environment: pkg.environment
import environment-preopens: pkg.environment-preopens
import exit: pkg.exit
Expand Down
157 changes: 157 additions & 0 deletions wit/deps/http/http-types.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// The `wasi:http/types` interface is meant to be imported by components to
// define the HTTP resource types and operations used by the component's
// imported and exported interfaces.
default interface http-types {
use io.streams.{input-stream, output-stream}
use poll.poll.{pollable}

// This type corresponds to HTTP standard Methods.
variant method {
get,
head,
post,
put,
delete,
connect,
options,
trace,
patch,
other(string)
}

// This type corresponds to HTTP standard Related Schemes.
variant scheme {
HTTP,
HTTPS,
other(string)
}

// TODO: perhaps better align with HTTP semantics?
// 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)
}

// 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: fields)
new-fields: func(entries: list<tuple<string,string>>) -> fields
fields-get: func(fields: fields, name: string) -> list<string>
fields-set: func(fields: fields, name: string, value: list<string>)
fields-delete: func(fields: fields, name: string)
fields-append: func(fields: fields, name: string, value: string)
fields-entries: func(fields: fields) -> list<tuple<string,string>>
fields-clone: func(fields: fields) -> fields

type headers = fields
type trailers = fields

// The following block defines stream types which corresponds to the HTTP
// standard Contents and Trailers. With Preview3, all of these fields can be
// replaced by a stream<u8, option<trailers>>. In the interim, we need to
// build on separate resource types defined by `wasi:io/streams`. The
// `finish-` functions emulate the stream's result value and MUST be called
// exactly once after the final read/write from/to the stream before dropping
// the stream.
type incoming-stream = input-stream
type outgoing-stream = output-stream
finish-incoming-stream: func(s: incoming-stream) -> option<trailers>
finish-outgoing-stream: func(s: outgoing-stream, trailers: option<trailers>)

// The following block defines the `incoming-request` and `outgoing-request`
// resource types that correspond to HTTP standard Requests. Soon, when
// resource types are added, the `u32` type aliases can be replaced by
// proper `resource` type definitions containing all the functions as
// methods. Later, Preview2 will allow both types to be merged together into
// 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
type outgoing-request = u32
drop-incoming-request: func(request: incoming-request)
drop-outgoing-request: func(request: outgoing-request)
incoming-request-method: func(request: incoming-request) -> method
incoming-request-path: func(request: incoming-request) -> string
incoming-request-query: func(request: incoming-request) -> string
incoming-request-scheme: func(request: incoming-request) -> option<scheme>
incoming-request-authority: func(request: incoming-request) -> string
incoming-request-headers: func(request: incoming-request) -> headers
incoming-request-consume: func(request: incoming-request) -> result<incoming-stream>
new-outgoing-request: func(
method: method,
path: string,
query: string,
scheme: option<scheme>,
authority: string,
headers: headers
) -> outgoing-request
outgoing-request-write: func(request: outgoing-request) -> result<outgoing-stream>

// Additional optional parameters that can be set when making a request.
record request-options {
// The following timeouts are specific to the HTTP protocol and work
// independently of the overall timeouts passed to `io.poll.poll-oneoff`.

// The timeout for the initial connect.
connect-timeout-ms: option<u32>,

// The timeout for receiving the first byte of the response body.
first-byte-timeout-ms: option<u32>,

// The timeout for receiving the next chunk of bytes in the response body
// stream.
between-bytes-timeout-ms: option<u32>
}

// The following block defines a special resource type used by the
// `wasi:http/incoming-handler` interface. When resource types are added, this
// block can be replaced by a proper `resource response-outparam { ... }`
// 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: response-outparam)
set-response-outparam: func(response: result<outgoing-response, error>) -> result

// This type corresponds to the HTTP standard Status Code.
type status-code = u16

// The following block defines the `incoming-response` and `outgoing-response`
// resource types that correspond to HTTP standard Responses. Soon, when
// resource types are added, the `u32` type aliases can be replaced by proper
// `resource` type definitions containing all the functions as methods. Later,
// 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
type outgoing-response = u32
drop-incoming-response: func(response: incoming-response)
drop-outgoing-response: func(response: outgoing-response)
incoming-response-status: func(response: incoming-response) -> status-code
incoming-response-headers: func(response: incoming-response) -> headers
incoming-response-consume: func(response: incoming-response) -> result<incoming-stream>
new-outgoing-response: func(
status-code: status-code,
headers: headers
) -> outgoing-response
outgoing-response-write: func(response: outgoing-response) -> result<outgoing-stream>

// The following block defines a special resource type used by the
// `wasi:http/outgoing-handler` interface to emulate
// `future<result<response, error>>` in advance of Preview3. Given a
// `future-incoming-response`, the client can call the non-blocking `get`
// 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
// `io.poll.poll-oneoff`.
type future-incoming-response = u32
drop-future-incoming-response: func(f: future-incoming-response)
future-incoming-response-get: func(f: future-incoming-response) -> option<result<incoming-response, error>>
listen-to-future-incoming-response: func(f: future-incoming-response) -> pollable
}
24 changes: 24 additions & 0 deletions wit/deps/http/incoming-handler.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// The `wasi:http/incoming-handler` interface is meant to be exported by
// components and called by the host in response to a new incoming HTTP
// response.
//
// NOTE: in Preview3, this interface will be merged with
// `wasi:http/outgoing-handler` into a single `wasi:http/handler` interface
// that takes a `request` parameter and returns a `response` result.
//
default interface incoming-handler {
use pkg.http-types.{incoming-request, response-outparam}

// The `handle` function takes an outparam instead of returning its response
// so that the component may stream its response while streaming any other
// request or response bodies. The callee MUST write a response to the
// `response-out` and then finish the response before returning. The `handle`
// function is allowed to continue execution after finishing the response's
// output stream. While this post-response execution is taken off the
// critical path, since there is no return value, there is no way to report
// its success or failure.
handle: func(
request: incoming-request,
response-out: response-outparam
)
}
18 changes: 18 additions & 0 deletions wit/deps/http/outgoing-handler.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// The `wasi:http/outgoing-handler` interface is meant to be imported by
// components and implemented by the host.
//
// NOTE: in Preview3, this interface will be merged with
// `wasi:http/outgoing-handler` into a single `wasi:http/handler` interface
// that takes a `request` parameter and returns a `response` result.
//
default interface outgoing-handler {
use pkg.http-types.{outgoing-request, request-options, future-incoming-response}

// The parameter and result types of the `handle` function allow the caller
// to concurrently stream the bodies of the outgoing request and the incoming
// response.
handle: func(
request: outgoing-request,
options: option<request-options>
) -> future-incoming-response
}
2 changes: 2 additions & 0 deletions wit/reactor.wit
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ default world reactor {
import random: random.random
import poll: poll.poll
import streams: io.streams
import default-outgoing-http: http.outgoing-handler
import http: http.incoming-handler
import environment: pkg.environment
import environment-preopens: pkg.environment-preopens
import exit: pkg.exit
Expand Down