Skip to content

[v0.3] Expand documentation of "result of transmission" futures #777

Open

Description

@lann

These futures are associated with two (pairs) of functions, and the documentation is currently quite sparse:

{request,response}.new:
https://github.com/WebAssembly/wasi-http/blob/ce36dac529d5def9e5191dd624e928eb12325111/wit-0.3.0-draft/types.wit#L256

{request,response}.consume-body:
https://github.com/WebAssembly/wasi-http/blob/ce36dac529d5def9e5191dd624e928eb12325111/wit-0.3.0-draft/types.wit#L412-L413

I know I have spent a fair bit of time discussing, forgetting, and trying to remember precisely what these futures are for and how best to plumb them together, and I'm sure I'm not the only one. Let's try to get that information written down in this issue and distilled into a bit more doc commentary.

Activity

  1. lann commented on Oct 3, 2025

    @lann
    ContributorAuthor

    I'll start with my best understanding, to be corrected:

    The future returned from {request,response}.new represents the final result of abstractly "transmitting" the message body and trailers. When "transmitting" involves actual I/O this future represents the host's best understanding of the result of "flushing" the message.

    The future passed to {request,response}.consume-body is just the other end of this signal. In practice most guests will only use this to "proxy" one message into another of the same kind, e.g. request->request or response->response (not request -> response). Another example would be a component that implements wasi-http backed by wasi-sockets; in this case the future would be resolved with the result of the last send for the message.

    Conceptually this is useful for requests because handle can return immediately after the response headers have been received where in the case of full-duplex request/response streaming the request body may still be in flight. In fact it seems to be allowable (though certainly rare) for the response to be completed before the request has finished streaming. Without this future there would be no way to reliably receive error information from failed transmission.

    One example use case for this feature would be request retry middleware where a request body is buffered and held until this future resolves. It's a little harder for me to see compelling use cases on the response side, though you could always just log errors.

    Questions:

    • When there is no further "transmission", such as when the request is being handled by a terminal HTTP service, what should be done with this future? Resolve it immediately? Resolve it after "processing" the request body?
    • What kinds of errors might consumers expect to see on these futures? Clearly in the I/O-backed case you could get e.g. "connection reset". Would content-length enforcement signal an error here?
    • If handle results in an error what should happen to this future?
    • More generally, is there any guarantee attached to successful resolution of the future? Or is it just a best-effort channel for signaling failures?
  2. lukewagner commented on Oct 9, 2025

    @lukewagner
    Member

    Agreed that more prose is necessary here and thanks so much for digging in! What you're saying sounds right. Digging into the questions:

    When there is no further "transmission", such as when the request is being handled by a terminal HTTP service, what should be done with this future? Resolve it immediately? Resolve it after "processing" the request body?

    I think the contract should be that the future is only resolved when the body is closed (to communicate the final success or failure of the transmission). That leaves open the question: if a request-consumer eagerly performs a future.write (if they know there's no chance they'll generate an error) and/or a response-producer eagerly performs a future.read (just to have it "ready to go"): are these reads/writes allowed to complete before the stream<u8> is closed?

    Now, in a future where we had a stream<u8 then result<trailers, error-code>> reply-with result<_, error-code>> in WIT (I've been playing with the idea of replacing the commas in stream<T,U,V> with more readable keywords...) that was stream.split into a stream<u8>, readable future<result<trailers, error-code>> and writable future<result<_, error-code>>, then the answer would be "no": the reads/writes could be started, but there is an internal state machine that will cause them to block until the stream<u8> is closed and the result<trailers, error-code> is successfully delivered. I believe this behavior is implementable in 0.3.0 even in component-to-component cases because the common wasi:http/types implementation of {request,response}.new and {request,response}.consume-body can inject itself between the readable/writable ends to delay the rendezvous. So unless this ends up being hard to implement, then perhaps we should specify ordering constraints on the completion of reads/writes of the futures that client code (of wasi:http/types) can depend on. These ordering guarantees might be nice for understanding these futures too.

    What kinds of errors might consumers expect to see on these futures? Clearly in the I/O-backed case you could get e.g. "connection reset". Would content-length enforcement signal an error here?

    I believe wasi:http/types should enforce content-length such that callers of {request,response}.new and {request,response}.consume-body can simply assume it has been checked (and they'll get an error if the condition was violated). (Allowing wasi:http/types to enforce content-length is what prompted the whole immutable-once-attached semantics of fields.)

    If handle results in an error what should happen to this future?

    I can't think of any invariants relating handle's result and the request body futures. Especially due to the concurrency of various operations, it seems like they're all independent. E.g., streaming a request could fail concurrently with a response's headers arriving, such that handle would race returning ok(response) with error(error-code) from the request future.

    More generally, is there any guarantee attached to successful resolution of the future? Or is it just a best-effort channel for signaling failures?

    Mostly the latter, although I suppose we could assign a "that's the very end of the HTTP call, there are no more failures possible for this call" significance to a response body's futures.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P-httpProposal: wasi-http

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions