/// This interface defines all of the types and methods for implementing /// HTTP Requests and Responses, both incoming and outgoing, as well as /// their headers, trailers, and bodies. interface types { use wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10.{duration}; use wasi:io/streams@0.2.0-rc-2023-11-10.{input-stream, output-stream}; use wasi:io/error@0.2.0-rc-2023-11-10.{error as io-error}; use wasi:io/poll@0.2.0-rc-2023-11-10.{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) } /// These cases are inspired by the IANA HTTP Proxy Error Types: /// https://www.iana.org/assignments/http-proxy-status/http-proxy-status.xhtml#table-http-proxy-error-types variant error-code { DNS-timeout, DNS-error(DNS-error-payload), destination-not-found, destination-unavailable, destination-IP-prohibited, destination-IP-unroutable, connection-refused, connection-terminated, connection-timeout, connection-read-timeout, connection-write-timeout, connection-limit-reached, TLS-protocol-error, TLS-certificate-error, TLS-alert-received(TLS-alert-received-payload), HTTP-request-denied, HTTP-request-length-required, HTTP-request-body-size(option), HTTP-request-method-invalid, HTTP-request-URI-invalid, HTTP-request-URI-too-long, HTTP-request-header-section-size(option), HTTP-request-header-size(option), HTTP-request-trailer-section-size(option), HTTP-request-trailer-size(field-size-payload), HTTP-response-incomplete, HTTP-response-header-section-size(option), HTTP-response-header-size(field-size-payload), HTTP-response-body-size(option), HTTP-response-trailer-section-size(option), HTTP-response-trailer-size(field-size-payload), HTTP-response-transfer-coding(option), HTTP-response-content-coding(option), HTTP-response-timeout, HTTP-upgrade-failed, HTTP-protocol-error, loop-detected, configuration-error, /// This is a catch-all error for anything that doesn't fit cleanly into a /// more specific case. It also includes an optional string for an /// unstructured description of the error. Users should not depend on the /// string for diagnosing errors, as it's not required to be consistent /// between implementations. internal-error(option) } /// Defines the case payload type for `DNS-error` above: record DNS-error-payload { rcode: option, info-code: option } /// Defines the case payload type for `TLS-alert-received` above: record TLS-alert-received-payload { alert-id: option, alert-message: option } /// Defines the case payload type for `HTTP-response-{header,trailer}-size` above: record field-size-payload { field-name: option, field-size: option } /// Attempts to extract a http-related `error` from the wasi:io `error` /// provided. /// /// Stream operations which return /// `wasi:io/stream/stream-error::last-operation-failed` have a payload of /// type `wasi:io/error/error` with more information about the operation /// that failed. This payload can be passed through to this function to see /// if there's http-related information about the error to return. /// /// Note that this function is fallible because not all io-errors are /// http-related errors. http-error-code: func(err: borrow) -> option; /// This type enumerates the different kinds of errors that may occur when /// setting or appending to a `fields` resource. variant header-error { /// This error indicates that a `field-key` or `field-value` was /// syntactically invalid when used with an operation that sets headers in a /// `fields`. invalid-syntax, /// This error indicates that a forbidden `field-key` was used when trying /// to set a header in a `fields`. forbidden, /// This error indicates that the operation on the `fields` was not /// permitted because the fields are immutable. immutable, } /// Field keys are always strings. type field-key = string; /// Field values should always be ASCII strings. However, in /// reality, HTTP implementations often have to interpret malformed values, /// so they are provided as a list of bytes. type field-value = list; /// This following block defines the `fields` resource which corresponds to /// HTTP standard Fields. Fields are a common representation used for both /// Headers and Trailers. /// /// A `fields` may be mutable or immutable. A `fields` created using the /// constructor, `from-list`, or `clone` will be mutable, but a `fields` /// resource given by other means (including, but not limited to, /// `incoming-request.headers`, `outgoing-request.headers`) might be be /// immutable. In an immutable fields, the `set`, `append`, and `delete` /// operations will fail with `header-error.immutable`. resource fields { /// Construct an empty HTTP Fields. /// /// The resulting `fields` is mutable. constructor(); /// Construct an HTTP Fields. /// /// The resulting `fields` is mutable. /// /// The list represents each key-value pair in the Fields. Keys /// which have multiple values are represented by multiple entries in this /// list with the same key. /// /// The tuple is a pair of the field key, represented as a string, and /// Value, represented as a list of bytes. In a valid Fields, all keys /// and values are valid UTF-8 strings. However, values are not always /// well-formed, so they are represented as a raw list of bytes. /// /// An error result will be returned if any header or value was /// syntactically invalid, or if a header was forbidden. from-list: static func( entries: list> ) -> result; /// Get all of the values corresponding to a key. get: func(name: field-key) -> list; /// Set all of the values for a key. Clears any existing values for that /// key, if they have been set. /// /// Fails with `header-error.immutable` if the `fields` are immutable. set: func(name: field-key, value: list) -> result<_, header-error>; /// Delete all values for a key. Does nothing if no values for the key /// exist. /// /// Fails with `header-error.immutable` if the `fields` are immutable. delete: func(name: field-key) -> result<_, header-error>; /// Append a value for a key. Does not change or delete any existing /// values for that key. /// /// Fails with `header-error.immutable` if the `fields` are immutable. append: func(name: field-key, value: field-value) -> result<_, header-error>; /// Retrieve the full set of keys and values in the Fields. Like the /// constructor, the list represents each key-value pair. /// /// The outer list represents each key-value pair in the Fields. Keys /// which have multiple values are represented by multiple entries in this /// list with the same key. entries: func() -> list>; /// Make a deep copy of the Fields. Equivelant in behavior to calling the /// `fields` constructor on the return value of `entries`. The resulting /// `fields` is mutable. clone: func() -> fields; } /// Headers is an alias for Fields. type headers = fields; /// Trailers is an alias for Fields. type trailers = fields; /// Represents an incoming HTTP Request. resource incoming-request { /// Returns the method of the incoming request. method: func() -> method; /// Returns the path with query parameters from the request, as a string. path-with-query: func() -> option; /// Returns the protocol scheme from the request. scheme: func() -> option; /// Returns the authority from the request, if it was present. authority: func() -> option; /// Get the `headers` associated with the request. /// /// The returned `headers` resource is immutable: `set`, `append`, and /// `delete` operations will fail with `header-error.immutable`. /// /// The `headers` returned are a child resource: it must be dropped before /// the parent `incoming-request` is dropped. Dropping this /// `incoming-request` before all children are dropped will trap. headers: func() -> headers; /// Gives the `incoming-body` associated with this request. Will only /// return success at most once, and subsequent calls will return error. consume: func() -> result; } /// Represents an outgoing HTTP Request. resource outgoing-request { /// Construct a new `outgoing-request` with a default `method` of `GET`, and /// `none` values for `path-with-query`, `scheme`, and `authority`. /// /// * `headers` is the HTTP Headers for the Request. /// /// It is possible to construct, or manipulate with the accessor functions /// below, an `outgoing-request` with an invalid combination of `scheme` /// and `authority`, or `headers` which are not permitted to be sent. /// It is the obligation of the `outgoing-handler.handle` implementation /// to reject invalid constructions of `outgoing-request`. constructor( headers: headers ); /// Returns the resource corresponding to the outgoing Body for this /// Request. /// /// Returns success on the first call: the `outgoing-body` resource for /// this `outgoing-request` can be retrieved at most once. Subsequent /// calls will return error. body: func() -> result; /// Get the Method for the Request. method: func() -> method; /// Set the Method for the Request. Fails if the string present in a /// `method.other` argument is not a syntactically valid method. set-method: func(method: method) -> result; /// Get the combination of the HTTP Path and Query for the Request. /// When `none`, this represents an empty Path and empty Query. path-with-query: func() -> option; /// Set the combination of the HTTP Path and Query for the Request. /// When `none`, this represents an empty Path and empty Query. Fails is the /// string given is not a syntactically valid path and query uri component. set-path-with-query: func(path-with-query: option) -> result; /// Get the HTTP Related Scheme for the Request. When `none`, the /// implementation may choose an appropriate default scheme. scheme: func() -> option; /// Set the HTTP Related Scheme for the Request. When `none`, the /// implementation may choose an appropriate default scheme. Fails if the /// string given is not a syntactically valid uri scheme. set-scheme: func(scheme: option) -> result; /// Get the HTTP Authority for the Request. A value of `none` may be used /// with Related Schemes which do not require an Authority. The HTTP and /// HTTPS schemes always require an authority. authority: func() -> option; /// Set the HTTP Authority for the Request. A value of `none` may be used /// with Related Schemes which do not require an Authority. The HTTP and /// HTTPS schemes always require an authority. Fails if the string given is /// not a syntactically valid uri authority. set-authority: func(authority: option) -> result; /// Get the headers associated with the Request. /// /// 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 /// `outgoing-request` is dropped, or its ownership is transfered to /// another component by e.g. `outgoing-handler.handle`. headers: func() -> headers; } /// Parameters for making an HTTP Request. Each of these parameters is an /// optional timeout, with the unit in milliseconds, applicable to the /// transport layer of the HTTP protocol. /// /// These timeouts are separate from any the user may use to bound a /// blocking call to `wasi:io/poll.poll`. resource request-options { /// Construct a default `request-options` value. constructor(); /// The timeout for the initial connect to the HTTP Server. connect-timeout-ms: func() -> option; /// Set the timeout for the initial connect to the HTTP Server. An error /// return value indicates that this timeout is not supported. set-connect-timeout-ms: func(ms: option) -> result; /// The timeout for receiving the first byte of the Response body. first-byte-timeout-ms: func() -> option; /// Set the timeout for receiving the first byte of the Response body. An /// error return value indicates that this timeout is not supported. set-first-byte-timeout-ms: func(ms: option) -> result; /// The timeout for receiving subsequent chunks of bytes in the Response /// body stream. between-bytes-timeout-ms: func() -> option; /// Set the timeout for receiving subsequent chunks of bytes in the Response /// body stream. An error return value indicates that this timeout is not /// supported. set-between-bytes-timeout-ms: func(ms: option) -> result; } /// Represents the ability to send an HTTP Response. /// /// This resource is used by the `wasi:http/incoming-handler` interface to /// allow a Response to be sent corresponding to the Request provided as the /// other argument to `incoming-handler.handle`. resource response-outparam { /// Set the value of the `response-outparam` to either send a response, /// or indicate an error. /// /// This method consumes the `response-outparam` to ensure that it is /// called at most once. If it is never called, the implementation /// will respond with an error. /// /// The user may provide an `error` to `response` to allow the /// implementation determine how to respond with an HTTP error response. set: static func( param: response-outparam, response: result, ); } /// This type corresponds to the HTTP standard Status Code. type status-code = u16; /// Represents an incoming HTTP Response. resource incoming-response { /// Returns the status code from the incoming response. status: func() -> status-code; /// Returns the headers from the incoming response. /// /// 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 /// `incoming-response` is dropped. headers: func() -> headers; /// Returns the incoming body. May be called at most once. Returns error /// if called additional times. consume: func() -> result; } /// Represents an incoming 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 /// an `input-stream` and the delivery of trailers as a `future-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 incoming-body { /// Returns the contents of the body, as a stream of bytes. /// /// Returns success on first call: the stream representing the contents /// can be retrieved at most once. Subsequent calls will return error. /// /// The returned `input-stream` resource is a child: it must be dropped /// before the parent `incoming-body` is dropped, or consumed by /// `incoming-body.finish`. /// /// This invariant ensures that the implementation can determine whether /// the user is consuming the contents of the body, waiting on the /// `future-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. %stream: func() -> result; /// Takes ownership of `incoming-body`, and returns a `future-trailers`. /// This function will trap if the `input-stream` child is still alive. finish: static func(this: incoming-body) -> future-trailers; } /// Represents a future which may eventaully return trailers, or an error. /// /// In the case that the incoming HTTP Request or Response did not have any /// trailers, this future will resolve to the empty set of trailers once the /// complete Request or Response body has been received. resource future-trailers { /// Returns a pollable which becomes ready when either the trailers have /// been received, or an error has occured. When this pollable is ready, /// the `get` method will return `some`. subscribe: func() -> pollable; /// Returns the contents of the trailers, or an error which occured, /// once the future is ready. /// /// The outer `option` represents future readiness. Users can wait on this /// `option` to become `some` using the `subscribe` method. /// /// The `result` represents that either the HTTP Request or Response body, /// as well as any trailers, were received successfully, or that an error /// occured receiving them. The optional `trailers` indicates whether or not /// trailers were present in the body. /// /// When some `trailers` are returned by this method, the `trailers` /// resource is immutable, and a child. Use of the `set`, `append`, or /// `delete` methods will return an error, and the resource must be /// dropped before the parent `future-trailers` is dropped. get: func() -> option, error-code>>; } /// Represents an outgoing HTTP Response. resource outgoing-response { /// Construct an `outgoing-response`, with a default `status-code` of `200`. /// If a different `status-code` is needed, it must be set via the /// `set-status-code` method. /// /// * `headers` is the HTTP Headers for the Response. constructor(headers: headers); /// Get the HTTP Status Code for the Response. status-code: func() -> status-code; /// Set the HTTP Status Code for the Response. Fails if the status-code /// given is not a valid http status code. set-status-code: func(status-code: status-code) -> result; /// Get the headers associated with the Request. /// /// 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 /// `outgoing-request` is dropped, or its ownership is transfered to /// another component by e.g. `outgoing-handler.handle`. headers: func() -> headers; /// Returns the resource corresponding to the outgoing Body for this Response. /// /// Returns success on the first call: the `outgoing-body` resource for /// this `outgoing-response` can be retrieved at most once. Subsequent /// calls will return error. body: func() -> result; } /// Represents an outgoing HTTP Request or Response's Body. /// /// A body has both its contents - a stream of bytes - and a (possibly /// empty) set of trailers, inducating the full contents of the body /// have been sent. This resource represents the contents as an /// `output-stream` child resource, and the completion of the body (with /// optional trailers) with a static function that consumes the /// `outgoing-body` resource, and ensures that the user of this interface /// may not write to the body contents after the body has been finished. /// /// If the user code drops this resource, as opposed to calling the static /// method `finish`, the implementation should treat the body as incomplete, /// and that an error has occured. The implementation should propogate this /// error to the HTTP protocol by whatever means it has available, /// including: corrupting the body on the wire, aborting the associated /// Request, or sending a late status code for the Response. resource outgoing-body { /// Returns a stream for writing the body contents. /// /// The returned `output-stream` is a child resource: it must be dropped /// before the parent `outgoing-body` resource is dropped (or finished), /// otherwise the `outgoing-body` drop or `finish` will trap. /// /// Returns success on the first call: the `output-stream` resource for /// this `outgoing-body` may be retrieved at most once. Subsequent calls /// will return error. write: func() -> result; /// Finalize an outgoing body, optionally providing trailers. This must be /// called to signal that the response is complete. If the `outgoing-body` /// is dropped without calling `outgoing-body.finalize`, the implementation /// should treat the body as corrupted. /// /// Fails if the body's `outgoing-request` or `outgoing-response` was /// constructed with a Content-Length header, and the contents written /// to the body (via `write`) does not match the value given in the /// Content-Length. finish: static func( this: outgoing-body, trailers: option ) -> result<_, error-code>; } /// Represents a future which may eventaully return an incoming HTTP /// Response, or an error. /// /// This resource is returned by the `wasi:http/outgoing-handler` interface to /// provide the HTTP Response corresponding to the sent Request. resource future-incoming-response { /// Returns a pollable which becomes ready when either the Response has /// been received, or an error has occured. When this pollable is ready, /// the `get` method will return `some`. subscribe: func() -> pollable; /// Returns the incoming HTTP Response, or an error, once one is ready. /// /// The outer `option` represents future readiness. Users can wait on this /// `option` to become `some` using the `subscribe` method. /// /// The outer `result` is used to retrieve the response or error at most /// once. It will be success on the first call in which the outer option /// is `some`, and error on subsequent calls. /// /// The inner `result` represents that either the incoming HTTP Response /// status and headers have recieved successfully, or that an error /// occured. Errors may also occur while consuming the response body, /// but those will be reported by the `incoming-body` and its /// `output-stream` child. get: func() -> option>>; } }