Skip to content

Conversation

@sunfishcode
Copy link
Member

Add a wstd API for creating proxy applications, wrapping the WASI proxy world trait and macro.

Compiling the example with RUSTFLAGS="-Clink-arg=--wasi-adapter=proxy" produces a program that runs in wasmtime serve.

Marking as a draft for now, as it's still experimental. Feedback on the approach or "is this even a reasonable idea" welcome 😄.

@yoshuawuyts
Copy link
Member

Thank you for submitting this! In general, I'm a big fan of making people's lives easier, and I believe that the general thrust of what you're filing here is the right thing to do. So, yes - I believe this is reasonable!

On a first pass, this is what stood out to me:

  1. I quite enjoy the pattern Rust has for main being the main entry point of the application. While I know Components blur the boundary between libraries and binaries since everything is virtualizable, I feel like it would be especially nice here if fn main for HTTP proxies could Just Work? How would you feel about changing that?
  2. There is a fair bit of mapping code inside of the macro happening. Could we turn those into conversion trait impls on the types instead? That keeps as much code out of the macros as possible.
  3. Is fn main(req) -> res the right pattern we want to implement? I believe that might lead to some trouble handling HTTP trailers?

@sunfishcode
Copy link
Member Author

sunfishcode commented Dec 5, 2024

1. I quite enjoy the pattern Rust has for `main` being the main entry point of the application. While I know Components blur the boundary between libraries and binaries since everything is virtualizable, I feel like it would be especially nice here if `fn main` for HTTP proxies could Just Work? How would you feel about changing that?

Do you still like calling it main if it can be called multiple times on the same instance?

2. There is a fair bit of mapping code inside of the macro happening. Could we turn those into conversion trait impls on the types instead? That keeps as much code out of the macros as possible.

Yeah, I'll work on factoring that out.

3. Is `fn main(req) -> res` the right pattern we want to implement? I believe that might lead to some trouble handling HTTP trailers?

I think it'll lead to some trouble handling bodies too :-}. I need to revamp the Response/Request types, and maybe even have separate types for the incoming-request side of things.

@pchickey
Copy link
Contributor

pchickey commented Dec 28, 2024

I was delayed a bit by starting a new job, but I've rewritten the reactor and streams abstractions in #35 #37. Hopefully that makes some aspects of this a bit simpler. I also added a bunch of common code to translate between the wasi-http types and the http crate types, so a bunch of the boilerplate in here can hopefully go away.

I'd like to avoid forcing users to ever use/know about RUSTFLAGS to compile applications. We can produce a HTTP Proxy world application with an ordinary cargo build --target wasm32-wasip2 of a bin with a wasi::http::proxy::export!(Wrapper) long as there is a "dummy" fn main() {} for the linker to export as the cli world run, so lets make #[wstd::proxy] async fn main(_: Request) -> Response {...} work like that. I think in this framework we should err on the side of letting the common case be straightforward - the user wants to write a proxy, and doesn't care that it also happens to impl the CLI world's export in a vacuous way - rather than enabling sophisticated behavior, like the user that has both a CLI world run export and a proxy export. Sophisticated users can use other methods to get sophisticated behavior, such as using the wasi crate directly and passing RUSTFLAGS if they want.

sunfishcode added a commit to sunfishcode/wstd that referenced this pull request Jan 2, 2025
Since as of bytecodealliance#19, wstd is using the http crate's types for `HeaderMap`,
`Method`, and `Uri`, switch it to use the http crate's types for `Request`,
`Response`, and `StatusCode` too. These types have a few more features,
and this change makes wstd more interoperable with code that uses the http
crate.

This also helps with bytecodealliance#34, as the new `Request` and `Response` types here
are independent of being incoming or outgoing.
sunfishcode added a commit to sunfishcode/wstd that referenced this pull request Jan 2, 2025
Move `IncomingBody` and related code out of the `response` module and into
the `body` module, and prepare it to be used for incoming requests as well
as incoming responses.

This is in preparation for bytecodealliance#34.
@sunfishcode
Copy link
Member Author

sunfishcode commented Jan 2, 2025

I've now overhauled the API here.

  • It now uses fn main syntax for the entrypoint.
  • All the conversion code is now factored out of the macro.
  • I switched from the response return type to a Resonder argument, which allows it to support trailers as well as streaming bodies.
  • I added a placeholder fn main to make the crate compile with wasm32-wasip2 with no special RUSTFLAGS.

Here's a simple hello-world example:

#[wstd::proxy]
async fn main(request: Request<IncomingBody>, responder: Responder) -> Finished {
    responder
        .respond(Response::new(b"Hello, wasi:http/proxy world!\n"))
        .await
}

For more interesting examples, including a streaming body, see the examples/http_server.rs in the PR.

think in this framework we should err on the side of letting the common case be straightforward - the user wants to write a proxy, and doesn't care that it also happens to impl the CLI world's export in a vacuous way - rather than enabling sophisticated behavior, like the user that has both a CLI world run export and a proxy export.

I've now implemented this. That said, a downside is that it's not just the export, it's also the imports, and it means that the resulting proxy components don't run in plain wasmtime serve; they need wasmtime serve -S cli.

@sunfishcode sunfishcode changed the title Implement a proxy framework. Implement an HTTP server framework. Jan 3, 2025
@sunfishcode sunfishcode force-pushed the sunfishcode/proxy branch 3 times, most recently from 54bafd3 to dea0b98 Compare January 6, 2025 22:05
@sunfishcode sunfishcode force-pushed the sunfishcode/proxy branch 2 times, most recently from e705d38 to db2b1d0 Compare January 8, 2025 23:29
@sunfishcode sunfishcode changed the title Implement an HTTP server framework. WIP of HTTP server, client, and trailer support Jan 8, 2025
@sunfishcode sunfishcode changed the title WIP of HTTP server, client, and trailer support HTTP server, stream client, and trailer support Jan 8, 2025
@sunfishcode sunfishcode force-pushed the sunfishcode/proxy branch 2 times, most recently from 6e0dca1 to 853da25 Compare January 10, 2025 20:55
@sunfishcode sunfishcode changed the title HTTP server, stream client, and trailer support HTTP streaming client, and trailer support Jan 15, 2025
@sunfishcode sunfishcode force-pushed the sunfishcode/proxy branch 2 times, most recently from 309be4a to 9e8f48f Compare January 15, 2025 17:17
@sunfishcode sunfishcode marked this pull request as ready for review January 15, 2025 17:17
@sunfishcode
Copy link
Member Author

With other features factored out into separate PRs that have now landed, this PR now contains a series of patches adding output-streaming client and client trailers support, and a nice example.

/// to wait for the response.
pub struct FutureIncomingResponse(WasiFutureIncomingResponse);

impl FutureIncomingResponse {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've now updated it to use impl Future.

Co-authored-by: Pat Hickey <[email protected]>
@pchickey pchickey merged commit da8d299 into bytecodealliance:main Jan 15, 2025
4 checks passed
@sunfishcode sunfishcode deleted the sunfishcode/proxy branch January 15, 2025 18:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants