From 4be87b4e6614c10cf005f2ca9d103a3cadcea77b Mon Sep 17 00:00:00 2001 From: Erik Rose Date: Fri, 12 Sep 2025 17:47:43 -0400 Subject: [PATCH] Update to the WITs at the present tip of the `dgohman-fastly/sync-wit` branch of Viceroy. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Locally, `make serve` results in… ``` viceroy serve app.wasm 2025-09-12T21:46:06.062560Z ERROR component imports instance `wasi:cli/terminal-input@0.2.0`, but a matching implementation was not found in the linker make: *** [serve] Error 1 ``` --- .github/workflows/python-ci.yml | 2 +- wit/deps/fastly-adapter/adapter.wit | 26 +- wit/deps/fastly/compute.wit | 560 ++++++++++++++++++---------- wit/deps/http/handler.wit | 6 + wit/deps/http/types.wit | 222 ++++++++--- wit/viceroy.wit | 6 +- 6 files changed, 551 insertions(+), 271 deletions(-) diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index 1b1fab8..14ea56d 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -26,7 +26,7 @@ jobs: - name: Install dependencies run: uv sync - name: Install viceroy - run: cargo install --git https://github.com/fastly/Viceroy.git --branch sunfishcode/sync-wit viceroy + run: cargo install --git https://github.com/fastly/Viceroy.git --tag erikrose/python-sdk-checkpoint-1 viceroy - name: Build WebAssembly component run: make app.wasm - name: Install dependencies diff --git a/wit/deps/fastly-adapter/adapter.wit b/wit/deps/fastly-adapter/adapter.wit index 4ceadf3..fedb9d4 100644 --- a/wit/deps/fastly-adapter/adapter.wit +++ b/wit/deps/fastly-adapter/adapter.wit @@ -28,17 +28,17 @@ interface adapter-http-req { downstream-client-h2-fingerprint: func(max-len: u64) -> result; downstream-client-request-id: func(max-len: u64) -> result; downstream-client-oh-fingerprint: func(max-len: u64) -> result; - downstream-client-ddos-detected: func() -> result; - downstream-tls-cipher-openssl-name: func(max-len: u64) -> result, error>; - downstream-tls-protocol: func(max-len: u64) -> result, error>; - downstream-tls-client-hello: func(max-len: u64) -> result, error>; - downstream-tls-client-cert-verify-result: func() -> result; - downstream-tls-ja3-md5: func() -> result, error>; - downstream-tls-ja4: func(max-len: u64) -> result; - downstream-compliance-region: func(max-len: u64) -> result; - - /// Deprecated, because it doesn't return `error.optional-none` on an empty certificate. - downstream-tls-raw-client-certificate-deprecated: func(max-len: u64) -> result, error>; + downstream-client-ddos-detected: func() -> result; + downstream-tls-cipher-openssl-name: func(max-len: u64) -> result>, error>; + downstream-tls-protocol: func(max-len: u64) -> result>, error>; + downstream-tls-client-hello: func(max-len: u64) -> result>, error>; + downstream-tls-client-cert-verify-result: func() -> result, error>; + downstream-tls-ja3-md5: func() -> result>, error>; + downstream-tls-ja4: func(max-len: u64) -> result, error>; + downstream-compliance-region: func(max-len: u64) -> result, error>; + + /// Deprecated, because it doesn't return `none` on an empty certificate. + downstream-tls-raw-client-certificate-deprecated: func(max-len: u64) -> result>, error>; get-original-header-names: func( max-len: u64, @@ -60,11 +60,11 @@ interface adapter-http-downstream { use fastly:compute/types.{error}; use fastly:compute/http-req.{request}; - /// Deprecated, because it doesn't return `error.optional-none` on an empty certificate. + /// Deprecated, because it doesn't return `none` on an empty certificate. downstream-tls-raw-client-certificate-deprecated: func( ds-request: borrow, max-len: u64 - ) -> result, error>; + ) -> result>, error>; } /// User-agent string parsing (deprecated). diff --git a/wit/deps/fastly/compute.wit b/wit/deps/fastly/compute.wit index 0346749..5c2df02 100644 --- a/wit/deps/fastly/compute.wit +++ b/wit/deps/fastly/compute.wit @@ -316,9 +316,11 @@ interface http-downstream { /// Waits until the next request is available, and then returns the resulting /// request and body. + /// + /// Returns `ok(none)` if there are no more requests for this session. await-next-request: func( pending: request-promise, - ) -> result; + ) -> result, error>; next-request-abandon: func( pending: request-promise, @@ -376,67 +378,85 @@ interface http-downstream { /// Returns whether the request was tagged as contributing to a DDoS attack. downstream-client-ddos-detected: func( ds-request: borrow - ) -> result; + ) -> result; /// Gets the cipher suite used to secure the downstream client TLS connection. /// /// The value returned will be consistent with the [OpenSSL name] for the cipher suite. /// + /// Returns `ok(none)` if the downstream client connection is not a TLS connection. + /// /// [OpenSSL name]: https://testssl.sh/openssl-iana.mapping.html downstream-tls-cipher-openssl-name: func( ds-request: borrow, max-len: u64 - ) -> result, error>; + ) -> result>, error>; /// Gets the TLS protocol version used to secure the downstream client TLS connection. + /// + /// Returns `ok(none)` if the downstream client connection is not a TLS connection. downstream-tls-protocol: func( ds-request: borrow, max-len: u64 - ) -> result, error>; + ) -> result>, error>; /// Gets the raw bytes sent by the client in the TLS ClientHello message. /// /// See [RFC 5246] for details. /// + /// Returns `ok(none)` if the downstream client connection is not a TLS connection. + /// /// [RFC 5246]: https://www.rfc-editor.org/rfc/rfc5246#section-7.4.1.2 downstream-tls-client-hello: func( ds-request: borrow, max-len: u64 - ) -> result, error>; + ) -> result>, error>; /// Gets the raw client certificate used to secure the downstream client mTLS connection. /// /// The value returned will be based on PEM format. /// - /// Returns `error.optional-none` if the connection is not mTLS or is unavailable. + /// Returns `ok(none)` if the downstream client connection is not a TLS connection. downstream-tls-raw-client-certificate: func( ds-request: borrow, max-len: u64 - ) -> result, error>; + ) -> result>, error>; /// Returns the `client-cert-verify-result` from the downstream client mTLS handshake. /// - /// Returns `none` if not available. + /// Returns `ok(none)` if the downstream client connection is not a TLS connection. downstream-tls-client-cert-verify-result: func( ds-request: borrow - ) -> result; + ) -> result, error>; + + /// Returns the Server Name Indication from the downstream client TLS handshake. + /// + /// Returns `ok(none)` if not available. + downstream-tls-client-servername: func( + ds-request: borrow, + max-len: u64 + ) -> result, error>; /// Gets the JA3 hash of the TLS ClientHello message. + /// + /// Returns `ok(none)` if the downstream client connection is not a TLS connection. downstream-tls-ja3-md5: func( ds-request: borrow - ) -> result, error>; + ) -> result>, error>; /// Gets the JA4 hash of the TLS ClientHello message. + /// + /// Returns `ok(none)` if the downstream client connection is not a TLS connection. downstream-tls-ja4: func( ds-request: borrow, max-len: u64 - ) -> result; + ) -> result, error>; /// Gets the compliance region that the client IP address is in. downstream-compliance-region: func( ds-request: borrow, max-len: u64 - ) -> result; + ) -> result, error>; /// Returns whether or not the original client request arrived with a /// Fastly-Key belonging to a user with the rights to purge content on this @@ -453,7 +473,6 @@ interface http-req { use http-types.{http-version, content-encodings, framing-headers-mode, tls-version}; use http-resp.{response}; use http-body.{body}; - use secret-store.{secret}; use http-resp.{response-with-body}; /// Handle that can be used to wait for a sent request. @@ -843,6 +862,7 @@ interface http-req { record inspect-options { corp: option, workspace: option, + override-client-ip: option, /// Additional options may be added in the future via this resource type. extra: option>, @@ -865,37 +885,6 @@ interface http-req { upgrade-websocket: func(backend: string) -> result<_, error>; - /// Create a backend for later use - register-dynamic-backend: func( - prefix: string, - target: string, - options: dynamic-backend-options, - ) -> result<_, error>; - - /// Create a backend for later use - resource dynamic-backend-options { - constructor(); - - host-override: func(value: string); - connect-timeout: func(value: u32); - first-byte-timeout: func(value: u32); - between-bytes-timeout: func(value: u32); - use-tls: func(value: bool); - tls-min-version: func(value: tls-version); - tls-max-version: func(value: tls-version); - cert-hostname: func(value: string); - ca-cert: func(value: string); - ciphers: func(value: string); - sni-hostname: func(value: string); - client-cert: func(client-cert: string, key: borrow); - http-keepalive-time-ms: func(value: u32); - tcp-keepalive-enable: func(value: u32); - tcp-keepalive-interval-secs: func(value: u32); - tcp-keepalive-probes: func(value: u32); - tcp-keepalive-time-secs: func(value: u32); - pooling: func(value: bool); - grpc: func(value: bool); - } } /// HTTP responses. @@ -1059,9 +1048,9 @@ interface dictionary { /// Tries to look up a value in this dictionary. /// - /// If the lookup is successful, this function returns `ok(s)` containing the found string - /// `s`, or `err(error.optional-none)` if no entry with the given key was found. - get: func( + /// If the lookup is successful, this function returns `ok(some(s))` containing the found + /// string `s`, or `ok(none)` if no entry with the given key was found. + lookup: func( key: string, max-len: u64, ) -> result, error>; @@ -1154,58 +1143,6 @@ interface erl { ) -> result; } -/// Object Store (deprecated in favor of `kv-store`) -interface object-store { - - use types.{error}; - use http-body.{body}; - - /// (DEPRECATED) An Object Store. - resource store { - open: static func(name: string) -> result, error>; - - lookup: func( - key: string, - ) -> result, error>; - - lookup-async: func( - key: string, - ) -> result; - - insert: func( - key: string, - body: body, - ) -> result<_, error>; - - insert-async: func( - key: string, - body: body, - ) -> result; - - delete-async: func( - key: string, - ) -> result; - } - /// (DEPRECATED) A pending Object Store lookup. - resource pending-lookup {} - /// (DEPRECATED) A pending Object Store insert. - resource pending-insert {} - /// (DEPRECATED) A pending Object Store delete. - resource pending-delete {} - - await-pending-lookup: func( - handle: pending-lookup, - ) -> result, error>; - - await-pending-insert: func( - handle: pending-insert, - ) -> result<_, error>; - - await-pending-delete: func( - handle: pending-delete, - ) -> result<_, error>; -} - /// Interface to Fastly's [Compute KV Store]. /// /// For a high-level introduction to this feature, see this [blog post]. @@ -1386,7 +1323,7 @@ interface kv-store { /// After calling this method, this entry will no longer have a body. take-body: func() -> option; - /// Read the metadata of the KV Store item. + /// Read the metadata of the KV Store item, if present. metadata: func(max-len: u64) -> result, error>; /// Read the current generation of the KV Store item. @@ -1540,24 +1477,24 @@ interface acl { /// Performs a lookup of the given IP address in the ACL. /// - /// If no matches are found, then `ok(none)` is returned. + /// If no matches are found, then `ok(none)` is returned. This corresponds + /// to an HTTP error code of 204, “No Content”. lookup: func( ip-addr: ip-address, - ) -> result, acl-error>, error>; + ) -> result, acl-error>; } /// Errors returned on ACL lookup failure. enum acl-error { - /// The $acl_error has not been initialized. - uninitialized, - /// There was no error. - ok, - /// This will map to the api's 204 code. - /// It indicates that the request succeeded, yet returned nothing. - no-content, - /// This will map to the api's 429 code. /// Too many requests have been made. + /// + /// This corresponds to an HTTP error code of 429, “Too Many Requests”. too-many-requests, + + /// Generic error value. + /// + /// This means that some unexpected error occurred. + generic-error, } } @@ -1583,6 +1520,170 @@ interface acl { interface backend { use types.{error}; use http-types.{tls-version}; + use secret-store.{secret}; + + /// Creates a new dynamic backend. + /// + /// The arguments are the name of the new backend to use, along with a string describing the + /// backend host. The latter can be of the form: + /// + /// - "" + /// - "" + /// - ":" + /// - ":" + /// + /// The name can be whatever you would like, as long as it does not match the name of any of the + /// static service backends nor match any other dynamic backends built during this session. + /// (Names can overlap between different sessions of the same service—they will be treated as + /// completely separate entities and will not be pooled—but you cannot, for example, declare + /// a dynamic backend named “dynamic-backend” twice in the same session.) + /// + /// Dynamic backends must be enabled for the Compute service. You can determine whether or not + /// dynamic backends have been allowed for the current service by checking for the + /// `error.unsupported` error result. This error only arises when attempting to use dynamic + /// backends with a service that has not had dynamic backends enabled, or dynamic backends have + /// been administratively prohibited for the node in response to an ongoing incident. + register-dynamic-backend: func( + prefix: string, + target: string, + options: dynamic-backend-options, + ) -> result<_, error>; + + /// Options for `register-dynamic-backend`. + resource dynamic-backend-options { + /// Constructs an options resource with default values for all other possible fields for the + /// backend, which can be overridden using the other methods provided. + constructor(); + + /// Sets a host header override when contacting this backend. + /// + /// This will force the value of the “Host” header to the given string when sending out the + /// origin request. If this is not set and no header already exists, the “Host” header will + /// default to the target. + /// + /// For more information, see [the Fastly documentation on override hosts]. + /// + /// [the Fastly documentation on override hosts]: https://docs.fastly.com/en/guides/specifying-an-override-host> + override-host: func(value: string); + + /// Sets the connection timeout, in milliseconds, for this backend. + /// + /// Defaults to 1,000ms (1s). + connect-timeout: func(value: u32); + + /// Sets a timeout, in milliseconds, that applies between the time of connection and the time we + /// get the first byte back. + /// + /// Defaults to 15,000ms (15s). + first-byte-timeout: func(value: u32); + + /// Sets a timeout, in milliseconds, that applies between any two bytes we receive across the + /// wire. + /// + /// Defaults to 10,000ms (10s). + between-bytes-timeout: func(value: u32); + + /// Enables or disables TLS to connect to the backend. + /// + /// When using TLS, Fastly checks the validity of the backend’s certificate, and fails the + /// connection if the certificate is invalid. This check is not optional: an invalid + /// certificate will cause the backend connection to fail (but read on). + /// + /// By default, the validity check does not require that the certificate hostname matches the + /// hostname of your request. You can use check_certificate to request a check of the + /// certificate hostname. + /// + /// By default, certificate validity uses a set of public certificate authorities. You can + /// specify an alternative CA using ca_certificate. + use-tls: func(value: bool); + + /// Sets the minimum TLS version for connecting to the backend. + /// + /// Setting this will enable TLS for the connection as a side effect. + tls-min-version: func(value: tls-version); + + /// Sets the maximum TLS version for connecting to the backend. + /// + /// Setting this will enable TLS for the connection as a side effect. ( + tls-max-version: func(value: tls-version); + + /// Defines the hostname that the server certificate should declare, and turn on validation + /// during backend connections. + /// + /// You should enable this if you are using TLS, and setting this will enable TLS for the + /// connection as a side effect. + /// + /// If `check-certificate` is not provided (default), the server certificate’s hostname may + /// have any value. + cert-hostname: func(value: string); + + /// Sets the CA certificate to use when checking the validity of the backend. + /// + /// Setting this will enable TLS for the connection as a side effect. + /// + /// If `ca-certificate` is not provided (default), the backends’s certificate is validated + /// using a set of public root CAs. + ca-certificate: func(value: string); + + /// Sets the acceptable cipher suites to use for TLS 1.0 - 1.2 connections. + /// + /// Setting this will enable TLS for the connection as a side effect. + tls-ciphers: func(value: string); + + /// Sets the SNI hostname for the backend connection. + /// + /// Setting this will enable TLS for the connection as a side effect. + sni-hostname: func(value: string); + + /// Provides the given client certificate to the server as part of the TLS handshake. + /// + /// Setting this will enable TLS for the connection as a side effect. Both the certificate and + /// the key to use should be in standard PEM format; providing the information in another + /// format will lead to an error. We suggest that (at least the) key should be held in + /// something like the Fastly secret store for security, with the handle passed to this + /// function without unpacking it via Secret::plaintext; the certificate can be held in a less + /// secure medium. + /// + /// (If it is absolutely necessary to get the key from another source, we suggest the use of + /// `secret.from-bytes`. + client-cert: func(client-cert: string, key: borrow); + + /// Configures up to how long to allow HTTP keepalive connections to remain idle in the + /// connection pool. + http-keepalive-time-ms: func(value: u32); + + /// Configures whether or not to use TCP keepalive on the connection to the backend. + tcp-keepalive-enable: func(value: u32); + + /// Configures how long to wait in between each TCP keepalive probe sent to the backend. + tcp-keepalive-interval-secs: func(value: u32); + + /// Configures up to how many TCP keepalive probes to send to the backend before the connection + /// is considered dead. + tcp-keepalive-probes: func(value: u32); + + /// Configures how long to wait after the last sent data over the TCP connection before starting + /// to send TCP keepalive probes. + tcp-keepalive-time-secs: func(value: u32); + + /// Determines whether or not connections to the same backend should be pooled across different + /// sessions. + /// + /// Fastly considers two backends “the same” if they’re registered with the same name and + /// the exact same settings. In those cases, when pooling is enabled, if Session 1 opens a + /// connection to this backend it will be left open, and can be re-used by Session 2. This can + /// help improve backend latency, by removing the need for the initial + /// network / TLS handshake(s). + /// + /// By default, pooling is enabled for dynamic backends. + pooling: func(value: bool); + + /// Sets whether or not this backend will be used for gRPC traffic. + /// + /// Warning: Setting this for backends that will not be used with gRPC may have unpredictable + /// effects. Fastly only currently guarantees that this connection will work for gRPC traffic. + grpc: func(value: bool); + } type timeout-ms = u32; type timeout-secs = u32; @@ -1770,7 +1871,9 @@ interface purge { /// A surrogate key must contain only printable ASCII characters (those between `0x21` and `0x7E`, /// inclusive). /// - /// Never returns `error.optional-none`. + /// Returns a [JSON purge response]. + /// + /// [JSON purge response]: https://developer.fastly.com/reference/api/purging/#purge-tag purge-surrogate-key: func( surrogate-keys: string, purge-options: purge-options, @@ -1778,13 +1881,8 @@ interface purge { /// Purge a surrogate key for the current service, and return the purge id. /// - /// This is similar to `purge-surrogate-key`, but on success, returns a - /// [JSON purge response] containing an ASCII alphanumeric string identifying - /// a purging. - /// - /// Never returns `error.optional-none`. - /// - /// [JSON purge response]: https://developer.fastly.com/reference/api/purging/#purge-tag + /// This is similar to `purge-surrogate-key`, but on success, returns an + /// ASCII alphanumeric string identifying a purging. purge-surrogate-key-verbose: func( surrogate-keys: string, purge-options: purge-options, @@ -1865,11 +1963,11 @@ interface cache { get-state: func() -> result; - /// Gets the user metadata of the found object, returning `none` if no object + /// Gets the user metadata of the found object, returning `ok(none)` if no object /// was found. get-user-metadata: func(max-len: u64) -> result>, error>; - /// Gets a range of the found object body, returning the `optional-none` error if there + /// Gets a range of the found object body, returning `ok(none)` if there /// was no found object. /// /// The returned `body` must be closed before calling this function again on the same @@ -1882,25 +1980,25 @@ interface cache { options: get-body-options, ) -> result; - /// Gets the content length of the found object, returning the `error.optional-none` error if + /// Gets the content length of the found object, returning `ok(none)` if /// there was no found object, or no content length was provided. - get-length: func() -> result; + get-length: func() -> result, error>; - /// Gets the configured max age of the found object, returning the `error.optional-none` error + /// Gets the configured max age of the found object, returning `ok(none)` /// if there was no found object. - get-max-age-ns: func() -> result; + get-max-age-ns: func() -> result, error>; - /// Gets the configured stale-while-revalidate period of the found object, returning the - /// `error.optional-none` error if there was no found object. - get-stale-while-revalidate-ns: func() -> result; + /// Gets the configured stale-while-revalidate period of the found object, returning `ok(none)` + /// if there was no found object. + get-stale-while-revalidate-ns: func() -> result, error>; - /// Gets the age of the found object, returning the `error.optional-none` error if there + /// Gets the age of the found object, returning `ok(none)` if there /// was no found object. - get-age-ns: func() -> result; + get-age-ns: func() -> result, error>; - /// Gets the number of cache hits for the found object, returning the `error.optional-none` - /// error if there was no found object. - get-hits: func() -> result; + /// Gets the number of cache hits for the found object, returning `ok(none)` + /// if there was no found object. + get-hits: func() -> result, error>; /// Cancel an obligation to provide an object to the cache. /// @@ -1932,12 +2030,12 @@ interface cache { ) -> result; /// Gets the age of the existing object during replace, returning - /// `none` if there was no object. + /// `ok(none)` if there was no object. replace-get-age-ns: func( handle: borrow, ) -> result, error>; - /// Gets a range of the existing object body, returning `none` if there + /// Gets a range of the existing object body, returning `ok(none)` if there /// was no existing object. /// /// The returned `body` must be closed before calling this function @@ -1948,39 +2046,39 @@ interface cache { ) -> result, error>; /// Gets the number of cache hits for the existing object during replace, - /// returning `none` if there was no object. + /// returning `ok(none)` if there was no object. replace-get-hits: func( handle: borrow, ) -> result, error>; /// Gets the content length of the existing object during replace, - /// returning `none` if there was no object, or no content + /// returning `ok(none)` if there was no object, or no content /// length was provided. replace-get-length: func( handle: borrow, ) -> result, error>; /// Gets the configured max age of the existing object during replace, - /// returning the `error.optional-none` error if there was no object. + /// returning `ok(none)` if there was no object. replace-get-max-age-ns: func( handle: borrow, ) -> result, error>; /// Gets the configured stale-while-revalidate period of the existing - /// object during replace, returning the `error.optional-none` error if there was no + /// object during replace, returning `ok(none)` if there was no /// object. replace-get-stale-while-revalidate-ns: func( handle: borrow, ) -> result, error>; /// Gets the lookup state of the existing object during replace, returning - /// the `error.optional-none` error if there was no object. + /// `ok(none)` if there was no object. replace-get-state: func( handle: borrow, ) -> result, error>; /// Gets the user metadata of the existing object during replace, returning - /// the `error.optional-none` error if there was no object. + /// `ok(none)` if there was no object. replace-get-user-metadata: func( handle: borrow, max-len: u64, @@ -2096,7 +2194,7 @@ interface cache { /// choosing a new waiter to perform the insertion/update. /// /// This may be passed either an `entry` or a `replace-entry`. - close: func(handle: entry) -> result<_, error>; + close-entry: func(handle: entry) -> result<_, error>; /// Options for cache replace operations record replace-options { @@ -2169,7 +2267,7 @@ interface http-cache { use types.{error}; use http-body.{body}; use http-req.{request}; - use http-resp.{response}; + use http-resp.{response, response-with-body}; use cache.{lookup-state, object-length, duration-ns, cache-hit-count}; /// An HTTP Cache transaction. @@ -2278,7 +2376,7 @@ interface http-cache { response: borrow, ) -> result, error>; - /// Retrieves a stored response from the cache, returning the `error.optional-none` error if + /// Retrieves a stored response from the cache, returning `ok(none)` if /// there was no response found. /// /// If `transform-for-client` is set, the response will be adjusted according to the looked-up @@ -2286,7 +2384,7 @@ interface http-cache { /// `206 Partial Content` response with an appropriate `content-range` header. get-found-response: func( transform-for-client: u32, - ) -> result, error>; + ) -> result, error>; /// Gets the state of a cache transaction. /// @@ -2295,35 +2393,35 @@ interface http-cache { get-state: func( ) -> result; - /// Gets the length of the found response, returning the `error.optional-none` error if there + /// Gets the length of the found response, returning `ok(none)` if there /// was no response found or no length was provided. - get-length: func() -> result; + get-length: func() -> result, error>; - /// Gets the configured max age of the found response in nanoseconds, returning the - /// `error.optional-none` error if there was no response found. - get-max-age-ns: func() -> result; + /// Gets the configured max age of the found response in nanoseconds, returning `ok(none)` + /// if there was no response found. + get-max-age-ns: func() -> result, error>; /// Gets the configured stale-while-revalidate period of the found response in nanoseconds, - /// returning the `error.optional-none` error if there was no response found. + /// returning `ok(none)` if there was no response found. get-stale-while-revalidate-ns: func( - ) -> result; + ) -> result, error>; - /// Gets the age of the found response in nanoseconds, returning the `error.optional-none` error + /// Gets the age of the found response in nanoseconds, returning `ok(none)` /// if there was no response found. - get-age-ns: func() -> result; + get-age-ns: func() -> result, error>; - /// Gets the number of cache hits for the found response, returning the `error.optional-none` - /// error if there was no response found. + /// Gets the number of cache hits for the found response, returning `ok(none)` + /// if there was no response found. /// /// This figure only reflects hits for a stored response in a particular cache server /// or cluster, not the entire Fastly network. - get-hits: func() -> result; + get-hits: func() -> result, error>; - /// Gets whether a found response is marked as containing sensitive data, returning the - /// `error.optional-none` error if there was no response found. - get-sensitive-data: func() -> result; + /// Gets whether a found response is marked as containing sensitive data, returning `ok(none)` + /// if there was no response found. + get-sensitive-data: func() -> result, error>; - /// Gets the surrogate keys of the found response, returning the `error.optional-none` error if + /// Gets the surrogate keys of the found response, returning `ok(none)` if /// there was no response found. /// /// The output is a list of surrogate keys separated by spaces. @@ -2332,9 +2430,9 @@ interface http-cache { /// error is returned containing the required size. get-surrogate-keys: func( max-len: u64, - ) -> result; + ) -> result, error>; - /// Gets the vary rule of the found response, returning the `error.optional-none` error if there + /// Gets the vary rule of the found response, returning `ok(none)` if there /// was no response found. /// /// The output is a list of header names separated by spaces. @@ -2343,7 +2441,7 @@ interface http-cache { /// error is returned containing the required size. get-vary-rule: func( max-len: u64, - ) -> result; + ) -> result, error>; /// Abandons an obligation to provide a response to the cache. /// @@ -2474,7 +2572,7 @@ interface http-cache { /// If the cache handle state includes `must-insert-or-update` (and hence no insert or update /// has been performed), closing the handle cancels any request collapsing, potentially choosing /// a new waiter to perform the insertion/update. - close: func( + close-entry: func( handle: entry, ) -> result<_, error>; @@ -2514,7 +2612,7 @@ interface config-store { /// Names are case sensitive. open: static func(name: string) -> result; - /// Fetches a value from the config store, returning `none` if it doesn't exist. + /// Fetches a value from the config store, returning `ok(none)` if it doesn't exist. get: func( key: string, max-len: u64, @@ -2572,24 +2670,11 @@ interface image-optimizer { /// Extensibility for `image-optimizer-transform-options` resource extra-image-optimizer-transform-options {} - enum image-optimizer-error-tag { - uninitialized, - ok, - error, - warning, - } - - record image-optimizer-error-detail { - tag: image-optimizer-error-tag, - message: list, - } - transform-image-optimizer-request: func( origin-image-request: borrow, origin-image-request-body: option, origin-image-request-backend: string, io-transform-options: image-optimizer-transform-options, - io-error-detail: image-optimizer-error-detail, ) -> result; } @@ -2619,7 +2704,7 @@ interface compute-runtime { /// A timestamp in milliseconds. type vcpu-ms = u64; - /// Gets the amount of vCPU time that has passed since this instance was started, in milliseconds. + /// Gets the amount of vCPU time that has passed since this session was started, in milliseconds. /// /// This function returns only time spent running on a vCPU, and does not include time spent /// performing any I/O operations. However, it is based on clock time passing, and so will include @@ -2629,10 +2714,100 @@ interface compute-runtime { /// As a result, this function *should not be used in benchmarking across runs*. It can be used, /// with caution, to compare the runtime of different operations within the same session. get-vcpu-ms: func() -> vcpu-ms; + + /// A UUID generated by Fastly for each session. + /// + /// This is often a useful value to include in log messages, and also to send to upstream + /// servers as an additional custom HTTP header, allowing for straightforward correlation of + /// which WebAssembly session processed a request to requests later processed by an origin + /// server. If a session is used to process multiple downstream requests, then you may wish to + /// use the per-request UUID associated with each individual request handle instead of this + /// field. + /// + /// Equivalent to the "FASTLY_TRACE_ID" environment variable. + get-session-id: func() -> string; + + /// The hostname of the Fastly cache server which is executing the current session, for + /// example, `cache-jfk1034`. + /// + /// Equivalent to the "FASTLY_HOSTNAME" environment variable and to [`server.hostname`] in VCL. + /// + /// [`server.hostname`]: https://www.fastly.com/documentation/reference/vcl/variables/server/server-hostname/ + get-hostname: func() -> string; + + /// The three-character identifying code of the [Fastly POP] in which the current session is + /// running. + /// + /// Equivalent to the "FASTLY_POP" environment variable and to [`server.datacenter`] in VCL. + /// + /// [Fastly POP]: https://www.fastly.com/documentation/guides/concepts/pop/ + /// [`server.datacenter`]: https://www.fastly.com/documentation/reference/vcl/variables/server/server-datacenter/ + get-pop: func() -> string; + + /// A code representing the general geographic region in which the [Fastly POP] processing the + /// current Compute session resides. + /// + /// Equivalent to the "FASTLY_REGION" environment variable and to [`server.region`] in VCL, and + /// has the same possible values. + /// + /// [`server.region`]: https://www.fastly.com/documentation/reference/vcl/variables/server/server-region/ + /// [Fastly POP]: https://www.fastly.com/documentation/guides/concepts/pop/ + get-region: func() -> string; + + /// The current cache generation value for this Fastly service. + /// + /// The cache generation value is incremented by [purge-all operations]. + /// + /// Equivalent to the "FASTLY_CACHE_GENERATION" environment variable and to + /// [`req.vcl.generation`] in VCL. + /// + /// [purge-all operations]: https://www.fastly.com/documentation/guides/concepts/edge-state/cache/purging/ + /// [`req.vcl.generation`]: https://www.fastly.com/documentation/reference/vcl/variables/miscellaneous/req-vcl-generation/ + get-cache-generation: func() -> u64; + + /// The customer ID of the Fastly customer account to which the currently executing Fastly + /// service belongs. + /// + /// Equivalent to the "FASTLY_CUSTOMER_ID" environment variable and to [`req.customer_id`] in VCL. + /// + /// [`req.customer_id`]: https://www.fastly.com/documentation/reference/vcl/variables/miscellaneous/req-customer-id/ + get-customer-id: func() -> string; + + /// Whether the request is running in the Fastly service's [staging environment]. + /// + /// `false` for production or `true` for staging. + /// + /// Equivalent to the "FASTLY_IS_STAGING" environment variable and to [`fastly.is_staging`] in VCL. + /// + /// [`fastly.is_staging`]: https://www.fastly.com/documentation/reference/vcl/variables/miscellaneous/fastly-is-staging/ + /// [staging environment]: https://docs.fastly.com/products/staging + get-is-staging: func() -> bool; + + /// The identifier for the Fastly service that is processing the current request. + /// + /// Equivalent to the "FASTLY_SERVICE_ID" environment variable and to [`req.service_id`] in VCL. + /// + /// [`req.service_id`]: https://www.fastly.com/documentation/reference/vcl/variables/miscellaneous/req-service-id/ + get-service-id: func() -> string; + + /// The version number for the Fastly service that is processing the current request. + /// + /// Equivalent to the "FASTLY_SERVICE_VERSION" environment variable and to [`req.vcl.version`] + /// in VCL. + /// + /// [`req.vcl.version`]: https://www.fastly.com/documentation/reference/vcl/variables/miscellaneous/req-vcl-version/ + get-service-version: func() -> u64; + + /// This function is not suitable for general-purpose use. + get-namespace-id: func() -> string; } -/// WASI interfaces used by `fastly:compute/service`. -world wasi-imports { +/// Interfaces that a Fastly Compute service may import. +/// +/// This contains the imports used in the `service` world, factored out into a +/// separate world so that it can be used by library components. Library components +/// are components that do not export anything themselves. +world service-imports { import wasi:clocks/wall-clock@0.2.6; import wasi:clocks/monotonic-clock@0.2.6; import wasi:io/error@0.2.6; @@ -2643,10 +2818,7 @@ world wasi-imports { import wasi:cli/stdout@0.2.6; import wasi:cli/stderr@0.2.6; import wasi:cli/stdin@0.2.6; -} -/// Custom interfaces used by `fastly:compute/service`. -world custom-imports { import acl; import async-io; import backend; @@ -2665,27 +2837,11 @@ world custom-imports { import image-optimizer; import log; import kv-store; - import object-store; import purge; import secret-store; import shielding; } -world custom-exports { - // Export the `http-incoming` interface. - export http-incoming; -} - -/// Interfaces that a Fastly Compute service may import. -/// -/// This contains the imports used in the `service` world, factored out into a -/// separate world so that it can be used by library components. Library components -/// are components that do not export anything themselves. -world service-imports { - include wasi-imports; - include custom-imports; -} - /// A Fastly Compute service. /// /// This defines the set of interfaces available to, and expected of, @@ -2695,5 +2851,7 @@ world service-imports { /// `http-incoming` exports. world service { include service-imports; - include custom-exports; + + // Export the `http-incoming` interface. + export http-incoming; } diff --git a/wit/deps/http/handler.wit b/wit/deps/http/handler.wit index a34a064..6a6c629 100644 --- a/wit/deps/http/handler.wit +++ b/wit/deps/http/handler.wit @@ -1,6 +1,8 @@ /// This interface defines a handler of incoming HTTP Requests. It should /// be exported by components which can respond to HTTP Requests. +@since(version = 0.2.0) interface incoming-handler { + @since(version = 0.2.0) use types.{incoming-request, response-outparam}; /// This function is invoked with an incoming HTTP Request, and a resource @@ -13,6 +15,7 @@ interface incoming-handler { /// The implementor of this function must write a response to the /// `response-outparam` before returning, or else the caller will respond /// with an error on its behalf. + @since(version = 0.2.0) handle: func( request: incoming-request, response-out: response-outparam @@ -21,7 +24,9 @@ interface incoming-handler { /// This interface defines a handler of outgoing HTTP Requests. It should be /// imported by components which wish to make HTTP Requests. +@since(version = 0.2.0) interface outgoing-handler { + @since(version = 0.2.0) use types.{ outgoing-request, request-options, future-incoming-response, error-code }; @@ -36,6 +41,7 @@ interface outgoing-handler { /// This function may return an error if the `outgoing-request` is invalid /// or not allowed to be made. Otherwise, protocol errors are reported /// through the `future-incoming-response`. + @since(version = 0.2.0) handle: func( request: outgoing-request, options: option diff --git a/wit/deps/http/types.wit b/wit/deps/http/types.wit index e174c3d..c9f3cc4 100644 --- a/wit/deps/http/types.wit +++ b/wit/deps/http/types.wit @@ -1,13 +1,19 @@ /// 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. +@since(version = 0.2.0) interface types { + @since(version = 0.2.0) use wasi:clocks/monotonic-clock@0.2.6.{duration}; + @since(version = 0.2.0) use wasi:io/streams@0.2.6.{input-stream, output-stream}; + @since(version = 0.2.0) use wasi:io/error@0.2.6.{error as io-error}; + @since(version = 0.2.0) use wasi:io/poll@0.2.6.{pollable}; /// This type corresponds to HTTP standard Methods. + @since(version = 0.2.0) variant method { get, head, @@ -22,6 +28,7 @@ interface types { } /// This type corresponds to HTTP standard Related Schemes. + @since(version = 0.2.0) variant scheme { HTTP, HTTPS, @@ -29,7 +36,8 @@ interface types { } /// 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 + /// + @since(version = 0.2.0) variant error-code { DNS-timeout, DNS-error(DNS-error-payload), @@ -78,18 +86,21 @@ interface types { } /// Defines the case payload type for `DNS-error` above: + @since(version = 0.2.0) record DNS-error-payload { rcode: option, info-code: option } /// Defines the case payload type for `TLS-alert-received` above: + @since(version = 0.2.0) record TLS-alert-received-payload { alert-id: option, alert-message: option } /// Defines the case payload type for `HTTP-response-{header,trailer}-size` above: + @since(version = 0.2.0) record field-size-payload { field-name: option, field-size: option @@ -106,17 +117,19 @@ interface types { /// /// Note that this function is fallible because not all io-errors are /// http-related errors. + @since(version = 0.2.0) 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. + @since(version = 0.2.0) variant header-error { - /// This error indicates that a `field-key` or `field-value` was + /// This error indicates that a `field-name` 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 + /// This error indicates that a forbidden `field-name` was used when trying /// to set a header in a `fields`. forbidden, @@ -125,12 +138,29 @@ interface types { immutable, } + /// Field names are always strings. + /// + /// Field names should always be treated as case insensitive by the `fields` + /// resource for the purposes of equality checking. + @since(version = 0.2.1) + type field-name = field-key; + /// Field keys are always strings. + /// + /// Field keys should always be treated as case insensitive by the `fields` + /// resource for the purposes of equality checking. + /// + /// # Deprecation + /// + /// This type has been deprecated in favor of the `field-name` type. + @since(version = 0.2.0) + @deprecated(version = 0.2.2) 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. + @since(version = 0.2.0) type field-value = list; /// This following block defines the `fields` resource which corresponds to @@ -140,96 +170,123 @@ interface types { /// 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 + /// `incoming-request.headers`, `outgoing-request.headers`) might be /// immutable. In an immutable fields, the `set`, `append`, and `delete` /// operations will fail with `header-error.immutable`. + @since(version = 0.2.0) resource fields { /// Construct an empty HTTP Fields. /// /// The resulting `fields` is mutable. + @since(version = 0.2.0) constructor(); /// Construct an HTTP Fields. /// /// The resulting `fields` is mutable. /// - /// The list represents each key-value pair in the Fields. Keys + /// The 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 key. + /// list with the same name. /// - /// 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. + /// The tuple is a pair of the field name, represented as a string, and + /// Value, represented as a list of bytes. /// - /// An error result will be returned if any header or value was - /// syntactically invalid, or if a header was forbidden. + /// An error result will be returned if any `field-name` or `field-value` is + /// syntactically invalid, or if a field is forbidden. + @since(version = 0.2.0) from-list: static func( - entries: list> + entries: list> ) -> result; - /// Get all of the values corresponding to a key. If the key is not present - /// in this `fields`, an empty list is returned. However, if the key is - /// present but empty, this is represented by a list with one or more - /// empty field-values present. - get: func(name: field-key) -> list; + /// Get all of the values corresponding to a name. If the name is not present + /// in this `fields` or is syntactically invalid, 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. + @since(version = 0.2.0) + get: func(name: field-name) -> list; - /// Returns `true` when the key is present in this `fields`. If the key is + /// Returns `true` when the name is present in this `fields`. If the name is /// syntactically invalid, `false` is returned. - has: func(name: field-key) -> bool; + @since(version = 0.2.0) + has: func(name: field-name) -> bool; - /// Set all of the values for a key. Clears any existing values for that - /// key, if they have been set. + /// Set all of the values for a name. Clears any existing values for that + /// name, 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>; + /// + /// Fails with `header-error.invalid-syntax` if the `field-name` or any of + /// the `field-value`s are syntactically invalid. + @since(version = 0.2.0) + set: func(name: field-name, value: list) -> result<_, header-error>; - /// Delete all values for a key. Does nothing if no values for the key + /// Delete all values for a name. Does nothing if no values for the name /// exist. /// /// Fails with `header-error.immutable` if the `fields` are immutable. - delete: func(name: field-key) -> result<_, header-error>; + /// + /// Fails with `header-error.invalid-syntax` if the `field-name` is + /// syntactically invalid. + @since(version = 0.2.0) + delete: func(name: field-name) -> result<_, header-error>; - /// Append a value for a key. Does not change or delete any existing - /// values for that key. + /// Append a value for a name. Does not change or delete any existing + /// values for that name. /// /// Fails with `header-error.immutable` if the `fields` are immutable. - append: func(name: field-key, value: field-value) -> result<_, header-error>; + /// + /// Fails with `header-error.invalid-syntax` if the `field-name` or + /// `field-value` are syntactically invalid. + @since(version = 0.2.0) + append: func(name: field-name, 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. + /// 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 key-value pair in the Fields. Keys + /// 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 key. - entries: func() -> list>; + /// 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. + @since(version = 0.2.0) + entries: func() -> list>; - /// Make a deep copy of the Fields. Equivelant in behavior to calling the + /// Make a deep copy of the Fields. Equivalent in behavior to calling the /// `fields` constructor on the return value of `entries`. The resulting /// `fields` is mutable. + @since(version = 0.2.0) clone: func() -> fields; } /// Headers is an alias for Fields. + @since(version = 0.2.0) type headers = fields; /// Trailers is an alias for Fields. + @since(version = 0.2.0) type trailers = fields; /// Represents an incoming HTTP Request. + @since(version = 0.2.0) resource incoming-request { /// Returns the method of the incoming request. + @since(version = 0.2.0) method: func() -> method; /// Returns the path with query parameters from the request, as a string. + @since(version = 0.2.0) path-with-query: func() -> option; /// Returns the protocol scheme from the request. + @since(version = 0.2.0) scheme: func() -> option; - /// Returns the authority from the request, if it was present. + /// Returns the authority of the Request's target URI, if present. + @since(version = 0.2.0) authority: func() -> option; /// Get the `headers` associated with the request. @@ -240,14 +297,17 @@ interface types { /// 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. + @since(version = 0.2.0) headers: func() -> headers; /// Gives the `incoming-body` associated with this request. Will only /// return success at most once, and subsequent calls will return error. + @since(version = 0.2.0) consume: func() -> result; } /// Represents an outgoing HTTP Request. + @since(version = 0.2.0) resource outgoing-request { /// Construct a new `outgoing-request` with a default `method` of `GET`, and @@ -260,6 +320,7 @@ interface types { /// 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`. + @since(version = 0.2.0) constructor( headers: headers ); @@ -270,38 +331,47 @@ interface types { /// 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. + @since(version = 0.2.0) body: func() -> result; /// Get the Method for the Request. + @since(version = 0.2.0) 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. + @since(version = 0.2.0) 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. + @since(version = 0.2.0) 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. + @since(version = 0.2.0) 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. + @since(version = 0.2.0) 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. + @since(version = 0.2.0) 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 + /// Get the authority of the Request's target URI. 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. + @since(version = 0.2.0) 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 + /// Set the authority of the Request's target URI. 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. + /// not a syntactically valid URI authority. + @since(version = 0.2.0) set-authority: func(authority: option) -> result; /// Get the headers associated with the Request. @@ -310,8 +380,9 @@ interface types { /// `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 + /// `outgoing-request` is dropped, or its ownership is transferred to /// another component by e.g. `outgoing-handler.handle`. + @since(version = 0.2.0) headers: func() -> headers; } @@ -321,31 +392,39 @@ interface types { /// /// These timeouts are separate from any the user may use to bound a /// blocking call to `wasi:io/poll.poll`. + @since(version = 0.2.0) resource request-options { /// Construct a default `request-options` value. + @since(version = 0.2.0) constructor(); /// The timeout for the initial connect to the HTTP Server. + @since(version = 0.2.0) connect-timeout: func() -> option; /// Set the timeout for the initial connect to the HTTP Server. An error /// return value indicates that this timeout is not supported. + @since(version = 0.2.0) set-connect-timeout: func(duration: option) -> result; /// The timeout for receiving the first byte of the Response body. + @since(version = 0.2.0) first-byte-timeout: 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. + @since(version = 0.2.0) set-first-byte-timeout: func(duration: option) -> result; /// The timeout for receiving subsequent chunks of bytes in the Response /// body stream. + @since(version = 0.2.0) between-bytes-timeout: 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. + @since(version = 0.2.0) set-between-bytes-timeout: func(duration: option) -> result; } @@ -354,7 +433,23 @@ interface types { /// 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`. + @since(version = 0.2.0) resource response-outparam { + /// Send an HTTP 1xx response. + /// + /// Unlike `response-outparam.set`, this does not consume the + /// `response-outparam`, allowing the guest to send an arbitrary number of + /// informational responses before sending the final response using + /// `response-outparam.set`. + /// + /// This will return an `HTTP-protocol-error` if `status` is not in the + /// range [100-199], or an `internal-error` if the implementation does not + /// support informational responses. + @unstable(feature = informational-outbound-responses) + send-informational: func( + status: u16, + headers: headers + ) -> result<_, error-code>; /// Set the value of the `response-outparam` to either send a response, /// or indicate an error. @@ -365,6 +460,7 @@ interface types { /// /// The user may provide an `error` to `response` to allow the /// implementation determine how to respond with an HTTP error response. + @since(version = 0.2.0) set: static func( param: response-outparam, response: result, @@ -372,12 +468,15 @@ interface types { } /// This type corresponds to the HTTP standard Status Code. + @since(version = 0.2.0) type status-code = u16; /// Represents an incoming HTTP Response. + @since(version = 0.2.0) resource incoming-response { /// Returns the status code from the incoming response. + @since(version = 0.2.0) status: func() -> status-code; /// Returns the headers from the incoming response. @@ -387,10 +486,12 @@ interface types { /// /// This headers resource is a child: it must be dropped before the parent /// `incoming-response` is dropped. + @since(version = 0.2.0) headers: func() -> headers; /// Returns the incoming body. May be called at most once. Returns error /// if called additional times. + @since(version = 0.2.0) consume: func() -> result; } @@ -402,6 +503,7 @@ interface types { /// 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. + @since(version = 0.2.0) resource incoming-body { /// Returns the contents of the body, as a stream of bytes. @@ -419,26 +521,30 @@ interface types { /// 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. + @since(version = 0.2.0) %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. + @since(version = 0.2.0) finish: static func(this: incoming-body) -> future-trailers; } - /// Represents a future which may eventaully return trailers, or an error. + /// Represents a future which may eventually 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. + @since(version = 0.2.0) 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, + /// been received, or an error has occurred. When this pollable is ready, /// the `get` method will return `some`. + @since(version = 0.2.0) subscribe: func() -> pollable; - /// Returns the contents of the trailers, or an error which occured, + /// Returns the contents of the trailers, or an error which occurred, /// once the future is ready. /// /// The outer `option` represents future readiness. Users can wait on this @@ -450,17 +556,19 @@ interface types { /// /// The inner `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 + /// error occurred 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. + @since(version = 0.2.0) get: func() -> option, error-code>>>; } /// Represents an outgoing HTTP Response. + @since(version = 0.2.0) resource outgoing-response { /// Construct an `outgoing-response`, with a default `status-code` of `200`. @@ -468,13 +576,16 @@ interface types { /// `set-status-code` method. /// /// * `headers` is the HTTP Headers for the Response. + @since(version = 0.2.0) constructor(headers: headers); /// Get the HTTP Status Code for the Response. + @since(version = 0.2.0) 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. + @since(version = 0.2.0) set-status-code: func(status-code: status-code) -> result; /// Get the headers associated with the Request. @@ -483,8 +594,9 @@ interface types { /// `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 + /// `outgoing-request` is dropped, or its ownership is transferred to /// another component by e.g. `outgoing-handler.handle`. + @since(version = 0.2.0) headers: func() -> headers; /// Returns the resource corresponding to the outgoing Body for this Response. @@ -492,6 +604,7 @@ interface types { /// 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. + @since(version = 0.2.0) body: func() -> result; } @@ -507,10 +620,11 @@ interface types { /// /// 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 + /// and that an error has occurred. The implementation should propagate 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. + @since(version = 0.2.0) resource outgoing-body { /// Returns a stream for writing the body contents. @@ -522,6 +636,7 @@ interface types { /// 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. + @since(version = 0.2.0) write: func() -> result; /// Finalize an outgoing body, optionally providing trailers. This must be @@ -533,21 +648,24 @@ interface types { /// 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. + @since(version = 0.2.0) finish: static func( this: outgoing-body, trailers: option ) -> result<_, error-code>; } - /// Represents a future which may eventaully return an incoming HTTP + /// Represents a future which may eventually 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. + @since(version = 0.2.0) 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, + /// been received, or an error has occurred. When this pollable is ready, /// the `get` method will return `some`. + @since(version = 0.2.0) subscribe: func() -> pollable; /// Returns the incoming HTTP Response, or an error, once one is ready. @@ -560,11 +678,11 @@ interface types { /// 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, + /// status and headers have received successfully, or that an error + /// occurred. Errors may also occur while consuming the response body, /// but those will be reported by the `incoming-body` and its /// `output-stream` child. + @since(version = 0.2.0) get: func() -> option>>; - } } diff --git a/wit/viceroy.wit b/wit/viceroy.wit index 7c68bd5..7cbecc5 100644 --- a/wit/viceroy.wit +++ b/wit/viceroy.wit @@ -1,7 +1,5 @@ package fastly:viceroy; world viceroy { - include fastly:compute/custom-imports; - include fastly:adapter/adapter-imports; - include fastly:compute/custom-exports; -} + include fastly:compute/service; +} \ No newline at end of file