Skip to content
This repository was archived by the owner on Nov 25, 2025. It is now read-only.
77 changes: 56 additions & 21 deletions wit-0.3.0-draft/types.wit
Original file line number Diff line number Diff line change
Expand Up @@ -265,17 +265,26 @@ interface types {

/// Returns the contents of the body, as a stream of bytes.
///
/// This function may be called multiple times as long as any `stream`s
/// returned by previous calls have been dropped first.
///
/// On success, this function returns a stream and a future, which will resolve
/// This function returns a stream and a future, which will resolve
/// to an error code if receiving data from stream fails.
/// The returned future resolves to success if body is closed.
%stream: func() -> result<tuple<stream<u8>, future<result<_, error-code>>>>;
/// The returned future resolves to success if data section of the
/// body is fully consumed.
///
/// The handles returned by this function are considered to be child
/// handles. Dropping the resource on which this function is called
/// will close the handles with an error context, if they are still open.
///
/// This function may be called multiple times.
/// If it is called while either a stream or future returned by a previous
/// call to this function is still open, those handles will be closed
/// with an error context.
Comment thread
rvolosatovs marked this conversation as resolved.
Outdated
%stream: func() -> tuple<stream<u8>, future<result<_, error-code>>>;

/// Takes ownership of `body`, and returns an unresolved optional `trailers` result.
///
/// This function will trap if a `stream` child is still alive.
/// Developers are encouraged to close any open handles returned by previous
/// calls to `stream`, if any, before calling this function,
/// but calling `finish` will close them with an error context.
finish: static func(this: body) -> future<result<option<trailers>, error-code>>;
}

Expand Down Expand Up @@ -348,21 +357,34 @@ interface types {
///
/// The returned `headers` resource is immutable: `set`, `append`, and
/// `delete` operations will fail with `header-error.immutable`.
///
/// This headers resource is a child: it must be dropped before the parent
/// `request` is dropped, or its ownership is transferred to another
/// component by e.g. `handler.handle`.
headers: func() -> headers;

/// Get the body associated with the Request, if any.
///
/// This body resource is a child: it must be dropped before the parent
/// `request` is dropped, or its ownership is transferred to another
/// component by e.g. `handler.handle`.
/// This body resource is a child: it must be dropped or consumed before
/// the parent `request` is dropped, or its ownership is transferred
/// to another component by e.g. `handler.handle`.
///
/// Only a single body resource can be active at a time.
/// If this function is called before the body resource returned by
/// a previous call to this function is dropped, the previously-returned
/// body will be transitioned into an "error state" and all operations
/// on it will fail.
///
/// In case, that a `body.finish` was called on the body returned by a previous
/// call to this function, this function returns `none`.
Comment thread
rvolosatovs marked this conversation as resolved.
Outdated
body: func() -> option<body>;

/// Takes ownership of the `request` and returns the `headers`, `body`
/// and `request-options`, if any.
///
/// The headers returned by this function are considered to be a clone of the headers
/// of the request. Changes to the `headers` returned by this function will not be reflected
/// in the immutable `headers` resources, that could have been acquired by calls to `headers`
/// prior to this function being called.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I know this is what we discussed, and it is the friendliest option but I remembered later that, when we discussed this as a group in the BA C-M impl call, the idea was to instead say: if you call into-parts when there is already an existing handle to headers, that existing headers handle is put into a state where all header operations fail (how precisely, to be defined per-method). This is symmetric to what you're describing with body and it matches more-closely how a built-in WIT child use<headers> return type would work (bindgen wouldn't know to clone). WDYT?

(and similarly in response)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It seems like we'd need to wrap fields#get, fields#has and fields#entries results in a result<T, header-error>, as well as introduce a header-error#moved or header-error#corrupted to fully cover this case.

  • /// Get all of the values corresponding to a name. If the name is not present
    /// in this `fields`, an empty list is returned. However, if the name is
    /// present but empty, this is represented by a list with one or more
    /// empty field-values present.
    get: func(name: field-name) -> list<field-value>;
    /// Returns `true` when the name is present in this `fields`. If the name is
    /// syntactically invalid, `false` is returned.
    has: func(name: field-name) -> bool;
  • /// Retrieve the full set of names and values in the Fields. Like the
    /// constructor, the list represents each name-value pair.
    ///
    /// The outer list represents each name-value pair in the Fields. Names
    /// which have multiple values are represented by multiple entries in this
    /// list with the same name.
    ///
    /// The names and values are always returned in the original casing and in
    /// the order in which they will be serialized for transport.
    entries: func() -> list<tuple<field-name,field-value>>;

Otherwise, I'm afraid this behavior would inevitably cause bugs in applications using this API.

I feel like the behavior proposed in this PR is less surprising, provide better developer experience and introduce no overhead. That said I'm happy to add a new header-error and wrap the methods with a result, if you think we should do that. Otherwise, perhaps we should discuss this in next BA C-M impl call?

///
/// In case, that a `body.finish` was called on the body returned by a previous
/// call to `body`, this function returns body as `none`.
into-parts: static func(this: request) -> tuple<headers, option<body>, option<request-options>>;
}

Expand Down Expand Up @@ -431,21 +453,34 @@ interface types {
///
/// The returned `headers` resource is immutable: `set`, `append`, and
/// `delete` operations will fail with `header-error.immutable`.
///
/// This headers resource is a child: it must be dropped before the parent
/// `response` is dropped, or its ownership is transferred to another
/// component by e.g. `handler.handle`.
headers: func() -> headers;

/// Get the body associated with the Response, if any.
///
/// This body resource is a child: it must be dropped before the parent
/// `response` is dropped, or its ownership is transferred to another
/// component by e.g. `handler.handle`.
/// This body resource is a child: it should be dropped or consumed before
/// the parent `response` is dropped, or its ownership is transferred
/// to another component by e.g. `handler.handle`.
///
/// Only a single body resource can be active at a time.
/// If this function is called before the body resource returned by
/// a previous call to this function is dropped, the previously-returned
/// body will be transitioned into an "error state" and all operations
/// on it will fail.
///
/// In case, that a `body.finish` was called on the body returned by a previous
/// call to this function, this function returns `none`.
body: func() -> option<body>;

/// Takes ownership of the `response` and returns the `headers` and `body`,
/// if any.
///
/// The headers returned by this function are considered to be a clone of the headers
/// of the response. Changes to the `headers` returned by this function will not be reflected
/// in the immutable `headers` resources, that could have been acquired by calls to `headers`
/// prior to this function being called.
///
/// In case, that a `body.finish` was called on the body returned by a previous
/// call to `body`, this function returns body as `none`.
into-parts: static func(this: response) -> tuple<headers, option<body>>;
}
}