-
Notifications
You must be signed in to change notification settings - Fork 29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[DO NOT MERGE] first draft of wasi:[email protected] #101
Conversation
This is intended to start a conversation about what we expect wasi:[email protected] to look like once we have support for `stream`s, `future`s, stream `error`s, and asynchronous lifts and lowers in the component model. In addition, I'm planning to implement a prototype of this interface using [isyswasfa](https://github.com/dicej/isyswasfa) in the near future, so I want to make sure the design is at least plausible. High level description: - The `incoming-handler` and `outgoing-handler` interfaces have been combined into a single `handler` interface. - The `incoming-` and `outgoing-` variations of `request` and `response` have been combined. Likewise for `body`. - I've added a `option<request-options>` field to `request` since it would be awkward to leave it as a parameter of `handler.handle` (e.g. what would it mean to receive such a parameter for an incoming request or for a request passed from one component to the other without any use of the network?). - I've added a `request-options-error` (analogous to `header-error`) to distinguish between unsupported fields and immutable handles. I do not intend for this to be merged into the `main` branch of the `wasi-http` repo (since we'll presumably be doing 0.2.x work there), but I _would_ like for it to live somewhere we can iterate on it. A few options come to mind: - Keep it in my fork of the `wasi-http` repo for now - Make a long-lived branch in the official repo for it - Put it in another repo under the `WebAssembly` org (e.g. `wasi-http-0.3.0`) - Other ideas? Signed-off-by: Joel Dice <[email protected]>
Very exciting to see these simplifications, even if only hypothetical atm. |
This moves the methods of that resource directly to `request` and `response`. Signed-off-by: Joel Dice <[email protected]>
Signed-off-by: Joel Dice <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, looks good so far!
Signed-off-by: Joel Dice <[email protected]>
- Rather than assume the existence of a built-in WIT `error` type, I've switched back to using `wasi:io/error/error` for the time being so I don't have to teach `wit-parser` and friends about the new built-in type (yet). The built-in type is just a vague idea at this point, so it's too early to try to use it, plus it's unlikely to be very interesting compared to `stream` and `future`. - Fix a cut-and-paste issue for `request`'s `options` method, which was both misnamed and misdocumented. Signed-off-by: Joel Dice <[email protected]>
I've just finished adding a "mock" implementation of this draft (i.e. one suitable for testing but which doesn't use the network) to isyswasfa. The service and middleware examples illustrate the structure of a non-trivial application that includes body streaming and trailers. In other words, that's roughly what I would expect a real A few observations based on the implementation experience:
|
Great work! That's really exciting.
Yes, that makes sense. I think the WIT feature we need to add here is CM/#287, so you'd be able to write: world middleware {
import a: wasi:http/handler;
import b: wasi:http/handler;
...
} which would then produce a component-type: (component
(import "a" (implements "wasi:http/handler") (instance ...))
(import "b" (implements "wasi:http/handler") (instance ...))
...
) where
So just to check my understanding: is the problem that, if you immediately call
#103 is proposes adding a
Yes, agreed. I was thinking of adding a |
Yeah, that looks great.
In the current draft, the
If I want to pass the original request to the imported
👍 |
Good point; I think you're right. This isn't clear-cut in 0.2 because you end up being forced to create a new resource anyways, but in 0.3, it seems clear.
I think the mutable-except-for-content-length design fixes this exact issue, but if we tweak it slightly (maybe I want to create new headers but use the same body), it does seem like a design flaw if you have to wait to copy over the body stream just so you can forward the trailers. I know I proposed removing the intermediate |
Yeah, that should work. If we do that and leave the |
Agreed! |
Does this look plausible? diff --git a/wit/deps/http/types.wit b/wit/deps/http/types.wit
index c859a38..9793aca 100644
--- a/wit/deps/http/types.wit
+++ b/wit/deps/http/types.wit
@@ -225,6 +225,43 @@ interface types {
/// Trailers is an alias for Fields.
type trailers = fields;
+ /// Represents an 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 a `stream<u8>` and the
+ /// delivery of trailers as a `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.
+ resource body {
+
+ /// Construct a new `body` with the specified stream and trailers.
+ constructor(
+ %stream: stream<u8>,
+ trailers: option<future<trailers>>
+ );
+
+ /// Returns the contents of the body, as a stream of bytes.
+ ///
+ /// The returned `stream` is a child: it must be dropped before the parent
+ /// `body` is dropped, or consumed by `body.finish`.
+ ///
+ /// This invariant ensures that the implementation can determine whether the
+ /// user is consuming the contents of the body, waiting on the 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.
+ ///
+ /// This function may be called multiple times as long as any `stream`s
+ /// returned by previous calls have been dropped first.
+ %stream: func() -> result<stream<u8>>;
+
+ /// Takes ownership of `body`, and returns a `trailers`. This function will
+ /// trap if a `stream` child is still alive.
+ finish: static func(this: body) -> result<option<trailers>, error-code>;
+ }
+
/// Represents an HTTP Request.
resource request {
@@ -245,8 +282,7 @@ interface types {
/// to reject invalid constructions of `request`.
constructor(
headers: headers,
- body: stream<u8>,
- trailers: option<future<trailers>>,
+ body: body,
options: option<request-options>
);
@@ -302,25 +338,8 @@ interface types {
/// component by e.g. `handler.handle`.
headers: func() -> headers;
- /// Returns the contents of the body, as a stream of bytes.
- ///
- /// The returned `stream` is a child: it must be dropped before the parent
- /// `request` is dropped, or consumed by `request.finish`.
- ///
- /// This invariant ensures that the implementation can determine whether the
- /// user is consuming the contents of the body, waiting on the 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.
- ///
- /// This function may be called multiple times as long as any `stream`s
- /// returned by previous calls have been dropped first.
- body: func() -> result<stream<u8>>;
-
- /// Takes ownership of `request`, and returns a `trailers`. This function will
- /// trap if a `stream` child is still alive.
- finish: static func(this: request) -> result<option<trailers>, error-code>;
+ /// Takes ownership of the `request` and returns the `body`.
+ consume: static func(this: request) -> body;
}
/// Parameters for making an HTTP Request. Each of these parameters is
@@ -375,8 +394,7 @@ interface types {
/// for the Response.
constructor(
headers: headers,
- body: stream<u8>,
- trailers: option<future<trailers>>
+ body: body,
);
/// Get the HTTP Status Code for the Response.
@@ -396,24 +414,7 @@ interface types {
/// component by e.g. `handler.handle`.
headers: func() -> headers;
- /// Returns the contents of the body, as a stream of bytes.
- ///
- /// The returned `stream` is a child: it must be dropped before the parent
- /// `response` is dropped, or consumed by `response.finish`.
- ///
- /// This invariant ensures that the implementation can determine whether the
- /// user is consuming the contents of the body, waiting on the 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.
- ///
- /// This function may be called multiple times as long as any `stream`s
- /// returned by previous calls have been dropped first.
- body: func() -> result<stream<u8>>;
-
- /// Takes ownership of `response`, and returns a `trailers`. This function will
- /// trap if a `stream` child is still alive.
- finish: static func(this: response) -> result<option<trailers>, error-code>;
+ /// Takes ownership of the `response` and returns the `body`.
+ consume: static func(this: response) -> body;
}
} |
@dicej Looks generally in the right direction! A few thoughts: First, the comments about the Next, for the case where I want to simply read the body bytes of a For the case where I want to extract the body from one WDYT? |
All sounds good to me. Now I'm thinking we should aim to get this merged into the repo so we can iterate on it properly (without interfering with the 0.2.x work). Should I add a |
Sounds good to me; maybe |
I've opened a new PR that puts the draft in its own directory: #106 |
This is intended to start a conversation about what we expect wasi:[email protected] to look like once we have support for
stream
s,future
s, streamerror
s, and asynchronous lifts and lowers in the component model. In addition, I'm planning to implement a prototype of this interface using isyswasfa in the near future, so I want to make sure the design is at least plausible.High level description:
incoming-handler
andoutgoing-handler
interfaces have been combined into a singlehandler
interface.incoming-
andoutgoing-
variations ofrequest
andresponse
have been combined.option<request-options>
field torequest
since it would be awkward to leave it as a parameter ofhandler.handle
(e.g. what would it mean to receive such a parameter for an incoming request or for a request passed from one component to the other without any use of the network?).request-options-error
(analogous toheader-error
) to distinguish between unsupported fields and immutable handles.I do not intend for this to be merged into the
main
branch of thewasi-http
repo (since we'll presumably be doing 0.2.x work there), but I would like for it to live somewhere we can iterate on it. A few options come to mind:wasi-http
repo for nowWebAssembly
org (e.g.wasi-http-0.3.0
)