From c50ee22e95efb47bf2bae09b911ef9c62791a10d Mon Sep 17 00:00:00 2001 From: Eduardo Rodrigues Date: Fri, 18 Aug 2023 02:21:25 +0200 Subject: [PATCH 1/5] feat(wasi): update wit definitions to latest --- wasi/wit/deps.toml | 2 +- wasi/wit/deps/cli/command.wit | 33 +++ .../{wasi-cli-base => cli}/environment.wit | 6 +- wasi/wit/deps/cli/exit.wit | 4 + wasi/wit/deps/cli/run.wit | 4 + wasi/wit/deps/cli/stdio.wit | 17 ++ wasi/wit/deps/cli/terminal.wit | 59 ++++ wasi/wit/deps/clocks/monotonic-clock.wit | 6 +- wasi/wit/deps/clocks/timezone.wit | 6 +- wasi/wit/deps/clocks/wall-clock.wit | 4 +- wasi/wit/deps/filesystem/preopens.wit | 6 + .../filesystem/{filesystem.wit => types.wit} | 139 +++++++--- wasi/wit/deps/filesystem/world.wit | 6 + wasi/wit/deps/http/incoming-handler.wit | 4 +- wasi/wit/deps/http/outgoing-handler.wit | 4 +- wasi/wit/deps/http/proxy.wit | 34 +++ wasi/wit/deps/http/types.wit | 28 +- wasi/wit/deps/io/streams.wit | 93 +++++-- .../deps/logging/{handler.wit => logging.wit} | 7 +- wasi/wit/deps/poll/poll.wit | 14 +- wasi/wit/deps/preview/command-extended.wit | 58 ++-- wasi/wit/deps/preview/command.wit | 21 -- wasi/wit/deps/preview/main.wit | 33 +++ wasi/wit/deps/preview/proxy.wit | 6 - wasi/wit/deps/preview/reactor.wit | 21 -- wasi/wit/deps/preview/test.wit | 28 ++ wasi/wit/deps/random/insecure-seed.wit | 24 ++ wasi/wit/deps/random/insecure.wit | 21 ++ wasi/wit/deps/random/random.wit | 23 +- wasi/wit/deps/sockets/instance-network.wit | 4 +- wasi/wit/deps/sockets/ip-name-lookup.wit | 66 +++-- wasi/wit/deps/sockets/network.wit | 147 +++++++++- wasi/wit/deps/sockets/tcp-create-socket.wit | 26 +- wasi/wit/deps/sockets/tcp.wit | 257 +++++++++++------- wasi/wit/deps/sockets/udp-create-socket.wit | 26 +- wasi/wit/deps/sockets/udp.wit | 199 +++++++++----- wasi/wit/deps/wasi-cli-base/exit.wit | 4 - wasi/wit/deps/wasi-cli-base/preopens.wit | 17 -- wasi/wit/reactor.wit | 53 ++-- 39 files changed, 1052 insertions(+), 458 deletions(-) create mode 100644 wasi/wit/deps/cli/command.wit rename wasi/wit/deps/{wasi-cli-base => cli}/environment.wit (71%) create mode 100644 wasi/wit/deps/cli/exit.wit create mode 100644 wasi/wit/deps/cli/run.wit create mode 100644 wasi/wit/deps/cli/stdio.wit create mode 100644 wasi/wit/deps/cli/terminal.wit create mode 100644 wasi/wit/deps/filesystem/preopens.wit rename wasi/wit/deps/filesystem/{filesystem.wit => types.wit} (86%) create mode 100644 wasi/wit/deps/filesystem/world.wit create mode 100644 wasi/wit/deps/http/proxy.wit rename wasi/wit/deps/logging/{handler.wit => logging.wit} (90%) delete mode 100644 wasi/wit/deps/preview/command.wit create mode 100644 wasi/wit/deps/preview/main.wit delete mode 100644 wasi/wit/deps/preview/proxy.wit delete mode 100644 wasi/wit/deps/preview/reactor.wit create mode 100644 wasi/wit/deps/preview/test.wit create mode 100644 wasi/wit/deps/random/insecure-seed.wit create mode 100644 wasi/wit/deps/random/insecure.wit delete mode 100644 wasi/wit/deps/wasi-cli-base/exit.wit delete mode 100644 wasi/wit/deps/wasi-cli-base/preopens.wit diff --git a/wasi/wit/deps.toml b/wasi/wit/deps.toml index 830eb3da..3628fa8f 100644 --- a/wasi/wit/deps.toml +++ b/wasi/wit/deps.toml @@ -1 +1 @@ -preview = "../../wit" +preview = "https://gitpkg.now.sh/eduardomourar/wasmtime/crates/wasi?ca265efb874bd4dc49b9034d840e5013c34fd722" diff --git a/wasi/wit/deps/cli/command.wit b/wasi/wit/deps/cli/command.wit new file mode 100644 index 00000000..3cd17bea --- /dev/null +++ b/wasi/wit/deps/cli/command.wit @@ -0,0 +1,33 @@ +package wasi:cli + +world command { + import wasi:clocks/wall-clock + import wasi:clocks/monotonic-clock + import wasi:clocks/timezone + import wasi:filesystem/types + import wasi:filesystem/preopens + import wasi:sockets/instance-network + import wasi:sockets/ip-name-lookup + import wasi:sockets/network + import wasi:sockets/tcp-create-socket + import wasi:sockets/tcp + import wasi:sockets/udp-create-socket + import wasi:sockets/udp + import wasi:random/random + import wasi:random/insecure + import wasi:random/insecure-seed + import wasi:poll/poll + import wasi:io/streams + + import environment + import exit + import stdin + import stdout + import stderr + import terminal-input + import terminal-output + import terminal-stdin + import terminal-stdout + import terminal-stderr + export run +} diff --git a/wasi/wit/deps/wasi-cli-base/environment.wit b/wasi/wit/deps/cli/environment.wit similarity index 71% rename from wasi/wit/deps/wasi-cli-base/environment.wit rename to wasi/wit/deps/cli/environment.wit index 876ea3a0..36790fe7 100644 --- a/wasi/wit/deps/wasi-cli-base/environment.wit +++ b/wasi/wit/deps/cli/environment.wit @@ -1,4 +1,4 @@ -default interface environment { +interface environment { /// Get the POSIX-style environment variables. /// /// Each environment variable is provided as a pair of string variable names @@ -11,4 +11,8 @@ default interface environment { /// Get the POSIX-style arguments to the program. get-arguments: func() -> list + + /// Return a path that programs should use as their initial current working + /// directory, interpreting `.` as shorthand for this. + initial-cwd: func() -> option } diff --git a/wasi/wit/deps/cli/exit.wit b/wasi/wit/deps/cli/exit.wit new file mode 100644 index 00000000..4831d507 --- /dev/null +++ b/wasi/wit/deps/cli/exit.wit @@ -0,0 +1,4 @@ +interface exit { + /// Exit the current instance and any linked instances. + exit: func(status: result) +} diff --git a/wasi/wit/deps/cli/run.wit b/wasi/wit/deps/cli/run.wit new file mode 100644 index 00000000..45a1ca53 --- /dev/null +++ b/wasi/wit/deps/cli/run.wit @@ -0,0 +1,4 @@ +interface run { + /// Run the program. + run: func() -> result +} diff --git a/wasi/wit/deps/cli/stdio.wit b/wasi/wit/deps/cli/stdio.wit new file mode 100644 index 00000000..6c9d4a41 --- /dev/null +++ b/wasi/wit/deps/cli/stdio.wit @@ -0,0 +1,17 @@ +interface stdin { + use wasi:io/streams.{input-stream} + + get-stdin: func() -> input-stream +} + +interface stdout { + use wasi:io/streams.{output-stream} + + get-stdout: func() -> output-stream +} + +interface stderr { + use wasi:io/streams.{output-stream} + + get-stderr: func() -> output-stream +} diff --git a/wasi/wit/deps/cli/terminal.wit b/wasi/wit/deps/cli/terminal.wit new file mode 100644 index 00000000..f32e7443 --- /dev/null +++ b/wasi/wit/deps/cli/terminal.wit @@ -0,0 +1,59 @@ +interface terminal-input { + /// The input side of a terminal. + /// + /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). + type terminal-input = u32 + + // In the future, this may include functions for disabling echoing, + // disabling input buffering so that keyboard events are sent through + // immediately, querying supported features, and so on. + + /// Dispose of the specified terminal-input after which it may no longer + /// be used. + drop-terminal-input: func(this: terminal-input) +} + +interface terminal-output { + /// The output side of a terminal. + /// + /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). + type terminal-output = u32 + + // In the future, this may include functions for querying the terminal + // size, being notified of terminal size changes, querying supported + // features, and so on. + + /// Dispose of the specified terminal-output, after which it may no longer + /// be used. + drop-terminal-output: func(this: terminal-output) +} + +/// An interface providing an optional `terminal-input` for stdin as a +/// link-time authority. +interface terminal-stdin { + use terminal-input.{terminal-input} + + /// If stdin is connected to a terminal, return a `terminal-input` handle + /// allowing further interaction with it. + get-terminal-stdin: func() -> option +} + +/// An interface providing an optional `terminal-output` for stdout as a +/// link-time authority. +interface terminal-stdout { + use terminal-output.{terminal-output} + + /// If stdout is connected to a terminal, return a `terminal-output` handle + /// allowing further interaction with it. + get-terminal-stdout: func() -> option +} + +/// An interface providing an optional `terminal-output` for stderr as a +/// link-time authority. +interface terminal-stderr { + use terminal-output.{terminal-output} + + /// If stderr is connected to a terminal, return a `terminal-output` handle + /// allowing further interaction with it. + get-terminal-stderr: func() -> option +} diff --git a/wasi/wit/deps/clocks/monotonic-clock.wit b/wasi/wit/deps/clocks/monotonic-clock.wit index 42e2981f..50eb4de1 100644 --- a/wasi/wit/deps/clocks/monotonic-clock.wit +++ b/wasi/wit/deps/clocks/monotonic-clock.wit @@ -1,3 +1,5 @@ +package wasi:clocks + /// WASI Monotonic Clock is a clock API intended to let users measure elapsed /// time. /// @@ -8,8 +10,8 @@ /// successive reads of the clock will produce non-decreasing values. /// /// It is intended for measuring elapsed time. -default interface monotonic-clock { - use poll.poll.{pollable} +interface monotonic-clock { + use wasi:poll/poll.{pollable} /// A timestamp in nanoseconds. type instant = u64 diff --git a/wasi/wit/deps/clocks/timezone.wit b/wasi/wit/deps/clocks/timezone.wit index 63f99cc4..2b685566 100644 --- a/wasi/wit/deps/clocks/timezone.wit +++ b/wasi/wit/deps/clocks/timezone.wit @@ -1,5 +1,7 @@ -default interface timezone { - use pkg.wall-clock.{datetime} +package wasi:clocks + +interface timezone { + use wall-clock.{datetime} /// A timezone. /// diff --git a/wasi/wit/deps/clocks/wall-clock.wit b/wasi/wit/deps/clocks/wall-clock.wit index 89c5a75d..6137724f 100644 --- a/wasi/wit/deps/clocks/wall-clock.wit +++ b/wasi/wit/deps/clocks/wall-clock.wit @@ -1,3 +1,5 @@ +package wasi:clocks + /// WASI Wall Clock is a clock API intended to let users query the current /// time. The name "wall" makes an analogy to a "clock on the wall", which /// is not necessarily monotonic as it may be reset. @@ -12,7 +14,7 @@ /// monotonic, making it unsuitable for measuring elapsed time. /// /// It is intended for reporting the current date and time for humans. -default interface wall-clock { +interface wall-clock { /// A time and date in seconds plus nanoseconds. record datetime { seconds: u64, diff --git a/wasi/wit/deps/filesystem/preopens.wit b/wasi/wit/deps/filesystem/preopens.wit new file mode 100644 index 00000000..f45661b8 --- /dev/null +++ b/wasi/wit/deps/filesystem/preopens.wit @@ -0,0 +1,6 @@ +interface preopens { + use types.{descriptor} + + /// Return the set of preopened directories, and their path. + get-directories: func() -> list> +} diff --git a/wasi/wit/deps/filesystem/filesystem.wit b/wasi/wit/deps/filesystem/types.wit similarity index 86% rename from wasi/wit/deps/filesystem/filesystem.wit rename to wasi/wit/deps/filesystem/types.wit index 234ef39a..e72a742d 100644 --- a/wasi/wit/deps/filesystem/filesystem.wit +++ b/wasi/wit/deps/filesystem/types.wit @@ -17,9 +17,9 @@ /// `..` and symbolic link steps, reaches a directory outside of the base /// directory, or reaches a symlink to an absolute or rooted path in the /// underlying filesystem, the function fails with `error-code::not-permitted`. -default interface filesystem { - use io.streams.{input-stream, output-stream} - use clocks.wall-clock.{datetime} +interface types { + use wasi:io/streams.{input-stream, output-stream} + use wasi:clocks/wall-clock.{datetime} /// File size or length of a region within a file. type filesize = u64 @@ -94,10 +94,6 @@ default interface filesystem { /// /// Note: This was called `filestat` in earlier versions of WASI. record descriptor-stat { - /// Device ID of device containing the file. - device: device, - /// File serial number. - inode: inode, /// File type. %type: descriptor-type, /// Number of hard links to the file. @@ -138,24 +134,25 @@ default interface filesystem { /// True if the resource is considered readable by the containing /// filesystem. readable, - /// True if the resource is considered writeable by the containing + /// True if the resource is considered writable by the containing /// filesystem. - writeable, + writable, /// True if the resource is considered executable by the containing /// filesystem. This does not apply to directories. executable, } - /// Number of hard links to an inode. - type link-count = u64 + /// Access type used by `access-at`. + variant access-type { + /// Test for readability, writeability, or executability. + access(modes), - /// Identifier for a device containing a file system. Can be used in - /// combination with `inode` to uniquely identify a file or directory in - /// the filesystem. - type device = u64 + /// Test whether the path exists. + exists, + } - /// Filesystem object serial number that is unique within its file system. - type inode = u64 + /// Number of hard links to an inode. + type link-count = u64 /// When setting a timestamp, this gives the value to set it to. variant new-timestamp { @@ -170,14 +167,6 @@ default interface filesystem { /// A directory entry. record directory-entry { - /// The serial number of the object referred to by this directory entry. - /// May be none if the inode value is not known. - /// - /// When this is none, libc implementations might do an extra `stat-at` - /// call to retrieve the inode number to fill their `d_ino` fields, so - /// implementations which can set this to a non-none value should do so. - inode: option, - /// The type of the file referred to by this directory entry. %type: descriptor-type, @@ -295,7 +284,18 @@ default interface filesystem { /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). type descriptor = u32 - /// Return a stream for reading from a file. + /// A 128-bit hash value, split into parts because wasm doesn't have a + /// 128-bit integer type. + record metadata-hash-value { + /// 64 bits of a 128-bit hash value. + lower: u64, + /// Another 64 bits of a 128-bit hash value. + upper: u64, + } + + /// Return a stream for reading from a file, if available. + /// + /// May fail with an error-code describing why the file cannot be read. /// /// Multiple read, write, and append streams may be active on the same open /// file and they do not interfere with each other. @@ -305,9 +305,11 @@ default interface filesystem { this: descriptor, /// The offset within the file at which to start reading. offset: filesize, - ) -> input-stream + ) -> result - /// Return a stream for writing to a file. + /// Return a stream for writing to a file, if available. + /// + /// May fail with an error-code describing why the file cannot be written. /// /// Note: This allows using `write-stream`, which is similar to `write` in /// POSIX. @@ -315,15 +317,17 @@ default interface filesystem { this: descriptor, /// The offset within the file at which to start writing. offset: filesize, - ) -> output-stream + ) -> result - /// Return a stream for appending to a file. + /// Return a stream for appending to a file, if available. + /// + /// May fail with an error-code describing why the file cannot be appended. /// /// Note: This allows using `write-stream`, which is similar to `write` with /// `O_APPEND` in in POSIX. append-via-stream: func( this: descriptor, - ) -> output-stream + ) -> result /// Provide file advisory information on a descriptor. /// @@ -453,14 +457,20 @@ default interface filesystem { /// Return the attributes of an open file or directory. /// - /// Note: This is similar to `fstat` in POSIX. + /// Note: This is similar to `fstat` in POSIX, except that it does not return + /// device and inode information. For testing whether two descriptors refer to + /// the same underlying filesystem object, use `is-same-object`. To obtain + /// additional data that can be used do determine whether a file has been + /// modified, use `metadata-hash`. /// /// Note: This was called `fd_filestat_get` in earlier versions of WASI. stat: func(this: descriptor) -> result /// Return the attributes of a file or directory. /// - /// Note: This is similar to `fstatat` in POSIX. + /// Note: This is similar to `fstatat` in POSIX, except that it does not + /// return device and inode information. See the `stat` description for a + /// discussion of alternatives. /// /// Note: This was called `path_filestat_get` in earlier versions of WASI. stat-at: func( @@ -586,6 +596,26 @@ default interface filesystem { new-path: string, ) -> result<_, error-code> + /// Check accessibility of a filesystem path. + /// + /// Check whether the given filesystem path names an object which is + /// readable, writable, or executable, or whether it exists. + /// + /// This does not a guarantee that subsequent accesses will succeed, as + /// filesystem permissions may be modified asynchronously by external + /// entities. + /// + /// Note: This is similar to `faccessat` with the `AT_EACCESS` flag in POSIX. + access-at: func( + this: descriptor, + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path to check. + path: string, + /// The type of check to perform. + %type: access-type + ) -> result<_, error-code> + /// Unlink a filesystem object that is not a directory. /// /// Return `error-code::is-directory` if the path refers to a directory. @@ -748,4 +778,47 @@ default interface filesystem { /// Dispose of the specified `directory-entry-stream`, after which it may no longer /// be used. drop-directory-entry-stream: func(this: directory-entry-stream) + + /// Test whether two descriptors refer to the same filesystem object. + /// + /// In POSIX, this corresponds to testing whether the two descriptors have the + /// same device (`st_dev`) and inode (`st_ino` or `d_ino`) numbers. + /// wasi-filesystem does not expose device and inode numbers, so this function + /// may be used instead. + is-same-object: func(this: descriptor, other: descriptor) -> bool + + /// Return a hash of the metadata associated with a filesystem object referred + /// to by a descriptor. + /// + /// This returns a hash of the last-modification timestamp and file size, and + /// may also include the inode number, device number, birth timestamp, and + /// other metadata fields that may change when the file is modified or + /// replaced. It may also include a secret value chosen by the + /// implementation and not otherwise exposed. + /// + /// Implementations are encourated to provide the following properties: + /// + /// - If the file is not modified or replaced, the computed hash value should + /// usually not change. + /// - If the object is modified or replaced, the computed hash value should + /// usually change. + /// - The inputs to the hash should not be easily computable from the + /// computed hash. + /// + /// However, none of these is required. + metadata-hash: func( + this: descriptor, + ) -> result + + /// Return a hash of the metadata associated with a filesystem object referred + /// to by a directory descriptor and a relative path. + /// + /// This performs the same hash computation as `metadata-hash`. + metadata-hash-at: func( + this: descriptor, + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path of the file or directory to inspect. + path: string, + ) -> result } diff --git a/wasi/wit/deps/filesystem/world.wit b/wasi/wit/deps/filesystem/world.wit new file mode 100644 index 00000000..b51f484f --- /dev/null +++ b/wasi/wit/deps/filesystem/world.wit @@ -0,0 +1,6 @@ +package wasi:filesystem + +world example-world { + import types + import preopens +} diff --git a/wasi/wit/deps/http/incoming-handler.wit b/wasi/wit/deps/http/incoming-handler.wit index 1ecff0aa..d0e27046 100644 --- a/wasi/wit/deps/http/incoming-handler.wit +++ b/wasi/wit/deps/http/incoming-handler.wit @@ -6,8 +6,8 @@ // `wasi:http/outgoing-handler` into a single `wasi:http/handler` interface // that takes a `request` parameter and returns a `response` result. // -default interface incoming-handler { - use pkg.types.{incoming-request, response-outparam} +interface incoming-handler { + use types.{incoming-request, response-outparam} // The `handle` function takes an outparam instead of returning its response // so that the component may stream its response while streaming any other diff --git a/wasi/wit/deps/http/outgoing-handler.wit b/wasi/wit/deps/http/outgoing-handler.wit index abe812ff..06c8e469 100644 --- a/wasi/wit/deps/http/outgoing-handler.wit +++ b/wasi/wit/deps/http/outgoing-handler.wit @@ -5,8 +5,8 @@ // `wasi:http/outgoing-handler` into a single `wasi:http/handler` interface // that takes a `request` parameter and returns a `response` result. // -default interface outgoing-handler { - use pkg.types.{outgoing-request, request-options, future-incoming-response} +interface outgoing-handler { + use types.{outgoing-request, request-options, future-incoming-response} // The parameter and result types of the `handle` function allow the caller // to concurrently stream the bodies of the outgoing request and the incoming diff --git a/wasi/wit/deps/http/proxy.wit b/wasi/wit/deps/http/proxy.wit new file mode 100644 index 00000000..162ab32b --- /dev/null +++ b/wasi/wit/deps/http/proxy.wit @@ -0,0 +1,34 @@ +package wasi:http + +// The `wasi:http/proxy` world captures a widely-implementable intersection of +// hosts that includes HTTP forward and reverse proxies. Components targeting +// this world may concurrently stream in and out any number of incoming and +// outgoing HTTP requests. +world proxy { + // HTTP proxies have access to time and randomness. + import wasi:clocks/wall-clock + import wasi:clocks/monotonic-clock + import wasi:clocks/timezone + import wasi:random/random + + // Proxies have standard output and error streams which are expected to + // terminate in a developer-facing console provided by the host. + import wasi:cli/stdout + import wasi:cli/stderr + + // TODO: this is a temporary workaround until component tooling is able to + // gracefully handle the absence of stdin. Hosts must return an eof stream + // for this import, which is what wasi-libc + tooling will do automatically + // when this import is properly removed. + import wasi:cli/stdin + + // This is the default handler to use when user code simply wants to make an + // HTTP request (e.g., via `fetch()`). + import outgoing-handler + + // The host delivers incoming HTTP requests to a component by calling the + // `handle` function of this exported interface. A host may arbitrarily reuse + // or not reuse component instance when delivering incoming HTTP requests and + // thus a component must be able to handle 0..N calls to `handle`. + export incoming-handler +} diff --git a/wasi/wit/deps/http/types.wit b/wasi/wit/deps/http/types.wit index bdcf7973..7b7b0155 100644 --- a/wasi/wit/deps/http/types.wit +++ b/wasi/wit/deps/http/types.wit @@ -1,10 +1,10 @@ // The `wasi:http/types` interface is meant to be imported by components to // define the HTTP resource types and operations used by the component's // imported and exported interfaces. -default interface types { - use io.streams.{input-stream, output-stream} - use poll.poll.{pollable} - +interface types { + use wasi:io/streams.{input-stream, output-stream} + use wasi:poll/poll.{pollable} + // This type corresponds to HTTP standard Methods. variant method { get, @@ -43,11 +43,11 @@ default interface types { type fields = u32 drop-fields: func(fields: fields) new-fields: func(entries: list>) -> fields - fields-get: func(fields: fields, name: string) -> list - fields-set: func(fields: fields, name: string, value: list) + fields-get: func(fields: fields, name: string) -> list> + fields-set: func(fields: fields, name: string, value: list>) fields-delete: func(fields: fields, name: string) - fields-append: func(fields: fields, name: string, value: string) - fields-entries: func(fields: fields) -> list> + fields-append: func(fields: fields, name: string, value: list) + fields-entries: func(fields: fields) -> list>> fields-clone: func(fields: fields) -> fields type headers = fields @@ -78,18 +78,16 @@ default interface types { drop-incoming-request: func(request: incoming-request) drop-outgoing-request: func(request: outgoing-request) incoming-request-method: func(request: incoming-request) -> method - incoming-request-path: func(request: incoming-request) -> string - incoming-request-query: func(request: incoming-request) -> string + incoming-request-path-with-query: func(request: incoming-request) -> option incoming-request-scheme: func(request: incoming-request) -> option - incoming-request-authority: func(request: incoming-request) -> string + incoming-request-authority: func(request: incoming-request) -> option incoming-request-headers: func(request: incoming-request) -> headers incoming-request-consume: func(request: incoming-request) -> result new-outgoing-request: func( method: method, - path: string, - query: string, + path-with-query: option, scheme: option, - authority: string, + authority: option, headers: headers ) -> outgoing-request outgoing-request-write: func(request: outgoing-request) -> result @@ -118,7 +116,7 @@ default interface types { // simply return a `stream`). type response-outparam = u32 drop-response-outparam: func(response: response-outparam) - set-response-outparam: func(response: result) -> result + set-response-outparam: func(param: response-outparam, response: result) -> result // This type corresponds to the HTTP standard Status Code. type status-code = u16 diff --git a/wasi/wit/deps/io/streams.wit b/wasi/wit/deps/io/streams.wit index c1567fd4..011bde0d 100644 --- a/wasi/wit/deps/io/streams.wit +++ b/wasi/wit/deps/io/streams.wit @@ -1,14 +1,38 @@ +package wasi:io + /// WASI I/O is an I/O abstraction API which is currently focused on providing /// stream types. /// /// In the future, the component model is expected to add built-in stream types; /// when it does, they are expected to subsume this API. -default interface streams { - use poll.poll.{pollable} - - /// An error type returned from a stream operation. Currently this - /// doesn't provide any additional information. - record stream-error {} +interface streams { + use wasi:poll/poll.{pollable} + + /// An error type returned from a stream operation. + /// + /// TODO: need to figure out the actual contents of this error. Used to be + /// an empty record but that's no longer allowed. The `dummy` field is + /// only here to have this be a valid in the component model by being + /// non-empty. + record stream-error { + dummy: u32, + } + + /// Streams provide a sequence of data and then end; once they end, they + /// no longer provide any further data. + /// + /// For example, a stream reading from a file ends when the stream reaches + /// the end of the file. For another example, a stream reading from a + /// socket ends when the socket is closed. + enum stream-status { + /// The stream is open and may produce further data. + open, + /// When reading, this indicates that the stream will not produce + /// further data. + /// When writing, this indicates that the stream will no longer be read. + /// Further writes are still permitted. + ended, + } /// An input bytestream. In the future, this will be replaced by handle /// types. @@ -29,12 +53,12 @@ default interface streams { /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). type input-stream = u32 - /// Read bytes from a stream. + /// Perform a non-blocking read from the stream. /// /// This function returns a list of bytes containing the data that was - /// read, along with a bool which, when true, indicates that the end of the - /// stream was reached. The returned list will contain up to `len` bytes; it - /// may return fewer than requested, but not more. + /// read, along with a `stream-status` which, indicates whether further + /// reads are expected to produce data. The returned list will contain up to + /// `len` bytes; it may return fewer than requested, but not more. /// /// Once a stream has reached the end, subsequent calls to read or /// `skip` will always report end-of-stream rather than producing more @@ -47,11 +71,17 @@ default interface streams { /// The len here is a `u64`, but some callees may not be able to allocate /// a buffer as large as that would imply. /// FIXME: describe what happens if allocation fails. + /// + /// When the returned `stream-status` is `open`, the length of the returned + /// value may be less than `len`. When an empty list is returned, this + /// indicates that no more bytes were available from the stream at that + /// time. In that case the subscribe-to-input-stream pollable will indicate + /// when additional bytes are available for reading. read: func( this: input-stream, /// The maximum number of bytes to read len: u64 - ) -> result, bool>, stream-error> + ) -> result, stream-status>, stream-error> /// Read bytes from a stream, with blocking. /// @@ -61,7 +91,7 @@ default interface streams { this: input-stream, /// The maximum number of bytes to read len: u64 - ) -> result, bool>, stream-error> + ) -> result, stream-status>, stream-error> /// Skip bytes from a stream. /// @@ -79,7 +109,7 @@ default interface streams { this: input-stream, /// The maximum number of bytes to skip. len: u64, - ) -> result, stream-error> + ) -> result, stream-error> /// Skip bytes from a stream, with blocking. /// @@ -89,7 +119,7 @@ default interface streams { this: input-stream, /// The maximum number of bytes to skip. len: u64, - ) -> result, stream-error> + ) -> result, stream-error> /// Create a `pollable` which will resolve once either the specified stream /// has bytes available to read or the other end of the stream has been @@ -111,7 +141,7 @@ default interface streams { /// always return promptly, after the number of bytes that can be written /// promptly, which could even be zero. To wait for the stream to be ready to /// accept data, the `subscribe-to-output-stream` function to obtain a - /// `pollable` which can be polled for using `wasi_poll`. + /// `pollable` which can be polled for using `wasi:poll`. /// /// And at present, it is a `u32` instead of being an actual handle, until /// the wit-bindgen implementation of handles and resources is ready. @@ -119,15 +149,25 @@ default interface streams { /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). type output-stream = u32 - /// Write bytes to a stream. + /// Perform a non-blocking write of bytes to a stream. /// - /// This function returns a `u64` indicating the number of bytes from - /// `buf` that were written; it may be less than the full list. + /// This function returns a `u64` and a `stream-status`. The `u64` indicates + /// the number of bytes from `buf` that were written, which may be less than + /// the length of `buf`. The `stream-status` indicates if further writes to + /// the stream are expected to be read. + /// + /// When the returned `stream-status` is `open`, the `u64` return value may + /// be less than the length of `buf`. This indicates that no more bytes may + /// be written to the stream promptly. In that case the + /// subscribe-to-output-stream pollable will indicate when additional bytes + /// may be promptly written. + /// + /// TODO: document what happens when an empty list is written write: func( this: output-stream, /// Data to write buf: list - ) -> result + ) -> result, stream-error> /// Write bytes to a stream, with blocking. /// @@ -137,7 +177,7 @@ default interface streams { this: output-stream, /// Data to write buf: list - ) -> result + ) -> result, stream-error> /// Write multiple zero bytes to a stream. /// @@ -147,7 +187,7 @@ default interface streams { this: output-stream, /// The number of zero bytes to write len: u64 - ) -> result + ) -> result, stream-error> /// Write multiple zero bytes to a stream, with blocking. /// @@ -157,7 +197,7 @@ default interface streams { this: output-stream, /// The number of zero bytes to write len: u64 - ) -> result + ) -> result, stream-error> /// Read from one stream and write to another. /// @@ -172,7 +212,7 @@ default interface streams { src: input-stream, /// The number of bytes to splice len: u64, - ) -> result, stream-error> + ) -> result, stream-error> /// Read from one stream and write to another, with blocking. /// @@ -184,7 +224,7 @@ default interface streams { src: input-stream, /// The number of bytes to splice len: u64, - ) -> result, stream-error> + ) -> result, stream-error> /// Forward the entire contents of an input stream to an output stream. /// @@ -196,12 +236,13 @@ default interface streams { /// of the input stream is seen and all the data has been written to /// the output stream. /// - /// This function returns the number of bytes transferred. + /// This function returns the number of bytes transferred, and the status of + /// the output stream. forward: func( this: output-stream, /// The stream to read from src: input-stream - ) -> result + ) -> result, stream-error> /// Create a `pollable` which will resolve once either the specified stream /// is ready to accept bytes or the other end of the stream has been closed. diff --git a/wasi/wit/deps/logging/handler.wit b/wasi/wit/deps/logging/logging.wit similarity index 90% rename from wasi/wit/deps/logging/handler.wit rename to wasi/wit/deps/logging/logging.wit index c9632b9c..b0cc4514 100644 --- a/wasi/wit/deps/logging/handler.wit +++ b/wasi/wit/deps/logging/logging.wit @@ -1,6 +1,8 @@ +package wasi:logging + /// WASI Logging is a logging API intended to let users emit log messages with /// simple priority levels and context values. -default interface handler { +interface logging { /// A log level, describing a kind of message. enum level { /// Describes messages about the values of variables and the flow of @@ -20,6 +22,9 @@ default interface handler { /// Describes messages indicating serious errors. error, + + /// Describes messages indicating fatal errors. + critical, } /// Emit a log message. diff --git a/wasi/wit/deps/poll/poll.wit b/wasi/wit/deps/poll/poll.wit index 28f08e17..a6334c55 100644 --- a/wasi/wit/deps/poll/poll.wit +++ b/wasi/wit/deps/poll/poll.wit @@ -1,6 +1,8 @@ +package wasi:poll + /// A poll API intended to let users wait for I/O events on multiple handles /// at once. -default interface poll { +interface poll { /// A "pollable" handle. /// /// This is conceptually represents a `stream<_, _>`, or in other words, @@ -30,10 +32,8 @@ default interface poll { /// component model async proposal, which will include a scalable waiting /// facility. /// - /// Note that the return type would ideally be `list`, but that would - /// be more difficult to polyfill given the current state of `wit-bindgen`. - /// See - /// for details. For now, we use zero to mean "not ready" and non-zero to - /// mean "ready". - poll-oneoff: func(in: list) -> list + /// The result list is the same length as the argument + /// list, and indicates the readiness of each corresponding + /// element in that / list, with true indicating ready. + poll-oneoff: func(in: list) -> list } diff --git a/wasi/wit/deps/preview/command-extended.wit b/wasi/wit/deps/preview/command-extended.wit index 11a0444e..518fabdc 100644 --- a/wasi/wit/deps/preview/command-extended.wit +++ b/wasi/wit/deps/preview/command-extended.wit @@ -1,29 +1,39 @@ -default world command-extended { - import wall-clock: clocks.wall-clock - import monotonic-clock: clocks.monotonic-clock - import timezone: clocks.timezone - import filesystem: filesystem.filesystem - import instance-network: sockets.instance-network - import ip-name-lookup: sockets.ip-name-lookup - import network: sockets.network - import tcp-create-socket: sockets.tcp-create-socket - import tcp: sockets.tcp - import udp-create-socket: sockets.udp-create-socket - import udp: sockets.udp - import random: random.random - import poll: poll.poll - import streams: io.streams - import environment: wasi-cli-base.environment - import preopens: wasi-cli-base.preopens - import exit: wasi-cli-base.exit +// All of the same imports and exports available in the wasi:cli/command world +// with addition of HTTP proxy related imports: +world command-extended { + import wasi:clocks/wall-clock + import wasi:clocks/monotonic-clock + import wasi:clocks/timezone + import wasi:filesystem/types + import wasi:filesystem/preopens + import wasi:sockets/instance-network + import wasi:sockets/ip-name-lookup + import wasi:sockets/network + import wasi:sockets/tcp-create-socket + import wasi:sockets/tcp + import wasi:sockets/udp-create-socket + import wasi:sockets/udp + import wasi:random/random + import wasi:random/insecure + import wasi:random/insecure-seed + import wasi:poll/poll + import wasi:io/streams + import wasi:cli/environment + import wasi:cli/exit + import wasi:cli/stdin + import wasi:cli/stdout + import wasi:cli/stderr + import wasi:cli/terminal-input + import wasi:cli/terminal-output + import wasi:cli/terminal-stdin + import wasi:cli/terminal-stdout + import wasi:cli/terminal-stderr + + export wasi:cli/run // We should replace all others with `include self.command` // as soon as the unioning of worlds is available: // https://github.com/WebAssembly/component-model/issues/169 - import console: logging.handler - import default-outgoing-HTTP: http.outgoing-handler - - export run: func( - args: list, - ) -> result + import wasi:logging/logging + import wasi:http/outgoing-handler } diff --git a/wasi/wit/deps/preview/command.wit b/wasi/wit/deps/preview/command.wit deleted file mode 100644 index 3e929515..00000000 --- a/wasi/wit/deps/preview/command.wit +++ /dev/null @@ -1,21 +0,0 @@ -default world command { - import wall-clock: clocks.wall-clock - import monotonic-clock: clocks.monotonic-clock - import timezone: clocks.timezone - import filesystem: filesystem.filesystem - import instance-network: sockets.instance-network - import ip-name-lookup: sockets.ip-name-lookup - import network: sockets.network - import tcp-create-socket: sockets.tcp-create-socket - import tcp: sockets.tcp - import udp-create-socket: sockets.udp-create-socket - import udp: sockets.udp - import random: random.random - import poll: poll.poll - import streams: io.streams - import environment: wasi-cli-base.environment - import preopens: wasi-cli-base.preopens - import exit: wasi-cli-base.exit - - export run: func() -> result -} diff --git a/wasi/wit/deps/preview/main.wit b/wasi/wit/deps/preview/main.wit new file mode 100644 index 00000000..753770ad --- /dev/null +++ b/wasi/wit/deps/preview/main.wit @@ -0,0 +1,33 @@ +package wasmtime:wasi + +// All of the same imports available in the wasi:cli/command world, but no +// export required: +world preview1-adapter-reactor { + import wasi:clocks/wall-clock + import wasi:clocks/monotonic-clock + import wasi:clocks/timezone + import wasi:filesystem/types + import wasi:filesystem/preopens + import wasi:sockets/instance-network + import wasi:sockets/ip-name-lookup + import wasi:sockets/network + import wasi:sockets/tcp-create-socket + import wasi:sockets/tcp + import wasi:sockets/udp-create-socket + import wasi:sockets/udp + import wasi:random/random + import wasi:random/insecure + import wasi:random/insecure-seed + import wasi:poll/poll + import wasi:io/streams + import wasi:cli/environment + import wasi:cli/exit + import wasi:cli/stdin + import wasi:cli/stdout + import wasi:cli/stderr + import wasi:cli/terminal-input + import wasi:cli/terminal-output + import wasi:cli/terminal-stdin + import wasi:cli/terminal-stdout + import wasi:cli/terminal-stderr +} diff --git a/wasi/wit/deps/preview/proxy.wit b/wasi/wit/deps/preview/proxy.wit deleted file mode 100644 index 2f444758..00000000 --- a/wasi/wit/deps/preview/proxy.wit +++ /dev/null @@ -1,6 +0,0 @@ -default world proxy { - import random: random.random - import console: logging.handler - import default-outgoing-HTTP: http.outgoing-handler - export HTTP: http.incoming-handler -} diff --git a/wasi/wit/deps/preview/reactor.wit b/wasi/wit/deps/preview/reactor.wit deleted file mode 100644 index 2abfa466..00000000 --- a/wasi/wit/deps/preview/reactor.wit +++ /dev/null @@ -1,21 +0,0 @@ -default world reactor { - import wall-clock: clocks.wall-clock - import monotonic-clock: clocks.monotonic-clock - import timezone: clocks.timezone - import filesystem: filesystem.filesystem - import instance-network: sockets.instance-network - import ip-name-lookup: sockets.ip-name-lookup - import network: sockets.network - import tcp-create-socket: sockets.tcp-create-socket - import tcp: sockets.tcp - import udp-create-socket: sockets.udp-create-socket - import udp: sockets.udp - import random: random.random - import poll: poll.poll - import streams: io.streams - import console: logging.handler - import default-outgoing-HTTP: http.outgoing-handler - import environment: wasi-cli-base.environment - import preopens: wasi-cli-base.preopens - import exit: wasi-cli-base.exit -} diff --git a/wasi/wit/deps/preview/test.wit b/wasi/wit/deps/preview/test.wit new file mode 100644 index 00000000..447304cb --- /dev/null +++ b/wasi/wit/deps/preview/test.wit @@ -0,0 +1,28 @@ +// only used as part of `test-programs` +world test-reactor { + + import wasi:cli/environment + import wasi:io/streams + import wasi:filesystem/types + import wasi:filesystem/preopens + import wasi:cli/exit + + export add-strings: func(s: list) -> u32 + export get-strings: func() -> list + + use wasi:io/streams.{output-stream} + + export write-strings-to: func(o: output-stream) -> result + + use wasi:filesystem/types.{descriptor-stat} + export pass-an-imported-record: func(d: descriptor-stat) -> string +} + +world test-command { + import wasi:poll/poll + import wasi:io/streams + import wasi:cli/environment + import wasi:cli/stdin + import wasi:cli/stdout + import wasi:cli/stderr +} diff --git a/wasi/wit/deps/random/insecure-seed.wit b/wasi/wit/deps/random/insecure-seed.wit new file mode 100644 index 00000000..ff2ff65d --- /dev/null +++ b/wasi/wit/deps/random/insecure-seed.wit @@ -0,0 +1,24 @@ +/// The insecure-seed interface for seeding hash-map DoS resistance. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +interface insecure-seed { + /// Return a 128-bit value that may contain a pseudo-random value. + /// + /// The returned value is not required to be computed from a CSPRNG, and may + /// even be entirely deterministic. Host implementations are encouraged to + /// provide pseudo-random values to any program exposed to + /// attacker-controlled content, to enable DoS protection built into many + /// languages' hash-map implementations. + /// + /// This function is intended to only be called once, by a source language + /// to initialize Denial Of Service (DoS) protection in its hash-map + /// implementation. + /// + /// # Expected future evolution + /// + /// This will likely be changed to a value import, to prevent it from being + /// called multiple times and potentially used for purposes other than DoS + /// protection. + insecure-seed: func() -> tuple +} diff --git a/wasi/wit/deps/random/insecure.wit b/wasi/wit/deps/random/insecure.wit new file mode 100644 index 00000000..ff082682 --- /dev/null +++ b/wasi/wit/deps/random/insecure.wit @@ -0,0 +1,21 @@ +/// The insecure interface for insecure pseudo-random numbers. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +interface insecure { + /// Return `len` insecure pseudo-random bytes. + /// + /// This function is not cryptographically secure. Do not use it for + /// anything related to security. + /// + /// There are no requirements on the values of the returned bytes, however + /// implementations are encouraged to return evenly distributed values with + /// a long period. + get-insecure-random-bytes: func(len: u64) -> list + + /// Return an insecure pseudo-random `u64` value. + /// + /// This function returns the same type of pseudo-random data as + /// `get-insecure-random-bytes`, represented as a `u64`. + get-insecure-random-u64: func() -> u64 +} diff --git a/wasi/wit/deps/random/random.wit b/wasi/wit/deps/random/random.wit index 2080ddfd..f2bd6358 100644 --- a/wasi/wit/deps/random/random.wit +++ b/wasi/wit/deps/random/random.wit @@ -1,8 +1,10 @@ +package wasi:random + /// WASI Random is a random data API. /// /// It is intended to be portable at least between Unix-family platforms and /// Windows. -default interface random { +interface random { /// Return `len` cryptographically-secure pseudo-random bytes. /// /// This function must produce data from an adequately seeded @@ -20,23 +22,4 @@ default interface random { /// This function returns the same type of pseudo-random data as /// `get-random-bytes`, represented as a `u64`. get-random-u64: func() -> u64 - - /// Return a 128-bit value that may contain a pseudo-random value. - /// - /// The returned value is not required to be computed from a CSPRNG, and may - /// even be entirely deterministic. Host implementations are encouraged to - /// provide pseudo-random values to any program exposed to - /// attacker-controlled content, to enable DoS protection built into many - /// languages' hash-map implementations. - /// - /// This function is intended to only be called once, by a source language - /// to initialize Denial Of Service (DoS) protection in its hash-map - /// implementation. - /// - /// # Expected future evolution - /// - /// This will likely be changed to a value import, to prevent it from being - /// called multiple times and potentially used for purposes other than DoS - /// protection. - insecure-random: func() -> tuple } diff --git a/wasi/wit/deps/sockets/instance-network.wit b/wasi/wit/deps/sockets/instance-network.wit index b1f5c982..d911a29c 100644 --- a/wasi/wit/deps/sockets/instance-network.wit +++ b/wasi/wit/deps/sockets/instance-network.wit @@ -1,7 +1,7 @@ /// This interface provides a value-export of the default network handle.. -default interface instance-network { - use pkg.network.{network} +interface instance-network { + use network.{network} /// Get a handle to the default network. instance-network: func() -> network diff --git a/wasi/wit/deps/sockets/ip-name-lookup.wit b/wasi/wit/deps/sockets/ip-name-lookup.wit index b594598e..6c64b461 100644 --- a/wasi/wit/deps/sockets/ip-name-lookup.wit +++ b/wasi/wit/deps/sockets/ip-name-lookup.wit @@ -1,14 +1,14 @@ -default interface ip-name-lookup { - use poll.poll.{pollable} - use pkg.network.{network, error, ip-address, ip-address-family} +interface ip-name-lookup { + use wasi:poll/poll.{pollable} + use network.{network, error-code, ip-address, ip-address-family} /// Resolve an internet host name to a list of IP addresses. - /// + /// /// See the wasi-socket proposal README.md for a comparison with getaddrinfo. - /// - /// Parameters: + /// + /// # Parameters /// - `name`: The name to look up. IP addresses are not allowed. Unicode domain names are automatically converted /// to ASCII using IDNA encoding. /// - `address-family`: If provided, limit the results to addresses of this specific address family. @@ -17,54 +17,52 @@ default interface ip-name-lookup { /// systems without an active IPv6 interface. Notes: /// - Even when no public IPv6 interfaces are present or active, names like "localhost" can still resolve to an IPv6 address. /// - Whatever is "available" or "unavailable" is volatile and can change everytime a network cable is unplugged. - /// - /// This function never blocks. It either immediately returns successfully with a `resolve-address-stream` + /// + /// This function never blocks. It either immediately fails or immediately returns successfully with a `resolve-address-stream` /// that can be used to (asynchronously) fetch the results. - /// Or it immediately fails whenever `name` is: - /// - empty - /// - an IP address - /// - a syntactically invalid domain name in another way - /// - /// References: + /// + /// At the moment, the stream never completes successfully with 0 items. Ie. the first call + /// to `resolve-next-address` never returns `ok(none)`. This may change in the future. + /// + /// # Typical errors + /// - `invalid-name`: `name` is a syntactically invalid domain name. + /// - `invalid-name`: `name` is an IP address. + /// - `address-family-not-supported`: The specified `address-family` is not supported. (EAI_FAMILY) + /// + /// # References: /// - /// - - /// - resolve-addresses: func(network: network, name: string, address-family: option, include-unavailable: bool) -> result + /// - + /// - + resolve-addresses: func(network: network, name: string, address-family: option, include-unavailable: bool) -> result type resolve-address-stream = u32 /// Returns the next address from the resolver. - /// + /// /// This function should be called multiple times. On each call, it will /// return the next address in connection order preference. If all /// addresses have been exhausted, this function returns `none`. /// After which, you should release the stream with `drop-resolve-address-stream`. - /// + /// /// This function never returns IPv4-mapped IPv6 addresses. - resolve-next-address: func(this: resolve-address-stream) -> result, error> - - + /// + /// # Typical errors + /// - `name-unresolvable`: Name does not exist or has no suitable associated IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY) + /// - `temporary-resolver-failure`: A temporary failure in name resolution occurred. (EAI_AGAIN) + /// - `permanent-resolver-failure`: A permanent failure in name resolution occurred. (EAI_FAIL) + /// - `would-block`: A result is not available yet. (EWOULDBLOCK, EAGAIN) + resolve-next-address: func(this: resolve-address-stream) -> result, error-code> /// Dispose of the specified `resolve-address-stream`, after which it may no longer be used. - /// + /// /// Note: this function is scheduled to be removed when Resources are natively supported in Wit. drop-resolve-address-stream: func(this: resolve-address-stream) - /// Get/set the blocking mode of the stream. - /// - /// By default a stream is in "blocking" mode, meaning that any function blocks and waits for its completion. - /// When switched to "non-blocking" mode, operations that would block return an `again` error. After which - /// the API consumer is expected to call `subscribe` and wait for completion using the wasi-poll module. - /// - /// Note: these functions are here for WASI Preview2 only. - /// They're planned to be removed when `future` is natively supported in Preview3. - non-blocking: func(this: resolve-address-stream) -> result - set-non-blocking: func(this: resolve-address-stream, value: bool) -> result<_, error> - /// Create a `pollable` which will resolve once the stream is ready for I/O. - /// + /// /// Note: this function is here for WASI Preview2 only. /// It's planned to be removed when `future` is natively supported in Preview3. subscribe: func(this: resolve-address-stream) -> pollable diff --git a/wasi/wit/deps/sockets/network.wit b/wasi/wit/deps/sockets/network.wit index 1f3a20d6..c370214c 100644 --- a/wasi/wit/deps/sockets/network.wit +++ b/wasi/wit/deps/sockets/network.wit @@ -1,28 +1,159 @@ +package wasi:sockets -default interface network { +interface network { /// An opaque resource that represents access to (a subset of) the network. /// This enables context-based security for networking. /// There is no need for this to map 1:1 to a physical network interface. - /// + /// /// FYI, In the future this will be replaced by handle types. type network = u32 /// Dispose of the specified `network`, after which it may no longer be used. - /// + /// /// Note: this function is scheduled to be removed when Resources are natively supported in Wit. drop-network: func(this: network) + /// Error codes. + /// + /// In theory, every API can return any error code. + /// In practice, API's typically only return the errors documented per API + /// combined with a couple of errors that are always possible: + /// - `unknown` + /// - `access-denied` + /// - `not-supported` + /// - `out-of-memory` + /// + /// See each individual API for what the POSIX equivalents are. They sometimes differ per API. + enum error-code { + // ### GENERAL ERRORS ### - enum error { + /// Unknown error unknown, - again, - // TODO ... + + /// Access denied. + /// + /// POSIX equivalent: EACCES, EPERM + access-denied, + + /// The operation is not supported. + /// + /// POSIX equivalent: EOPNOTSUPP + not-supported, + + /// Not enough memory to complete the operation. + /// + /// POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY + out-of-memory, + + /// The operation timed out before it could finish completely. + timeout, + + /// This operation is incompatible with another asynchronous operation that is already in progress. + concurrency-conflict, + + /// Trying to finish an asynchronous operation that: + /// - has not been started yet, or: + /// - was already finished by a previous `finish-*` call. + /// + /// Note: this is scheduled to be removed when `future`s are natively supported. + not-in-progress, + + /// The operation has been aborted because it could not be completed immediately. + /// + /// Note: this is scheduled to be removed when `future`s are natively supported. + would-block, + + + // ### IP ERRORS ### + + /// The specified address-family is not supported. + address-family-not-supported, + + /// An IPv4 address was passed to an IPv6 resource, or vice versa. + address-family-mismatch, + + /// The socket address is not a valid remote address. E.g. the IP address is set to INADDR_ANY, or the port is set to 0. + invalid-remote-address, + + /// The operation is only supported on IPv4 resources. + ipv4-only-operation, + + /// The operation is only supported on IPv6 resources. + ipv6-only-operation, + + + + // ### TCP & UDP SOCKET ERRORS ### + + /// A new socket resource could not be created because of a system limit. + new-socket-limit, + + /// The socket is already attached to another network. + already-attached, + + /// The socket is already bound. + already-bound, + + /// The socket is already in the Connection state. + already-connected, + + /// The socket is not bound to any local address. + not-bound, + + /// The socket is not in the Connection state. + not-connected, + + /// A bind operation failed because the provided address is not an address that the `network` can bind to. + address-not-bindable, + + /// A bind operation failed because the provided address is already in use. + address-in-use, + + /// A bind operation failed because there are no ephemeral ports available. + ephemeral-ports-exhausted, + + /// The remote address is not reachable + remote-unreachable, + + + // ### TCP SOCKET ERRORS ### + + /// The socket is already in the Listener state. + already-listening, + + /// The socket is already in the Listener state. + not-listening, + + /// The connection was forcefully rejected + connection-refused, + + /// The connection was reset. + connection-reset, + + + // ### UDP SOCKET ERRORS ### + datagram-too-large, + + + // ### NAME LOOKUP ERRORS ### + + /// The provided name is a syntactically invalid domain name. + invalid-name, + + /// Name does not exist or has no suitable associated IP addresses. + name-unresolvable, + + /// A temporary failure in name resolution occurred. + temporary-resolver-failure, + + /// A permanent failure in name resolution occurred. + permanent-resolver-failure, } enum ip-address-family { /// Similar to `AF_INET` in POSIX. - ipv4, + ipv4, /// Similar to `AF_INET6` in POSIX. ipv6, @@ -53,4 +184,4 @@ default interface network { ipv6(ipv6-socket-address), } -} \ No newline at end of file +} diff --git a/wasi/wit/deps/sockets/tcp-create-socket.wit b/wasi/wit/deps/sockets/tcp-create-socket.wit index 571a0197..f467d285 100644 --- a/wasi/wit/deps/sockets/tcp-create-socket.wit +++ b/wasi/wit/deps/sockets/tcp-create-socket.wit @@ -1,19 +1,27 @@ -default interface tcp-create-socket { - use pkg.network.{network, error, ip-address-family} - use pkg.tcp.{tcp-socket} +interface tcp-create-socket { + use network.{network, error-code, ip-address-family} + use tcp.{tcp-socket} /// Create a new TCP socket. - /// + /// /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX. - /// + /// /// This function does not require a network capability handle. This is considered to be safe because /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`listen`/`connect` /// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world. - /// - /// References: + /// + /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. + /// + /// # Typical errors + /// - `not-supported`: The host does not support TCP sockets. (EOPNOTSUPP) + /// - `address-family-not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + /// + /// # References /// - /// - - /// - create-tcp-socket: func(address-family: ip-address-family) -> result + /// - + /// - + create-tcp-socket: func(address-family: ip-address-family) -> result } diff --git a/wasi/wit/deps/sockets/tcp.wit b/wasi/wit/deps/sockets/tcp.wit index b2f48336..7ed46a69 100644 --- a/wasi/wit/deps/sockets/tcp.wit +++ b/wasi/wit/deps/sockets/tcp.wit @@ -1,12 +1,12 @@ -default interface tcp { - use io.streams.{input-stream, output-stream} - use poll.poll.{pollable} - use pkg.network.{network, error, ip-socket-address, ip-address-family} +interface tcp { + use wasi:io/streams.{input-stream, output-stream} + use wasi:poll/poll.{pollable} + use network.{network, error-code, ip-socket-address, ip-address-family} /// A TCP socket handle. type tcp-socket = u32 - + enum shutdown-type { /// Similar to `SHUT_RD` in POSIX. @@ -25,164 +25,231 @@ default interface tcp { /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which /// network interface(s) to bind to. /// If the TCP/UDP port is zero, the socket will be bound to a random free port. - /// + /// /// When a socket is not explicitly bound, the first invocation to a listen or connect operation will /// implicitly bind the socket. - /// - /// Fails when: - /// - the socket is already bound. - /// - /// References + /// + /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. + /// + /// # Typical `start` errors + /// - `address-family-mismatch`: The `local-address` has the wrong address family. (EINVAL) + /// - `already-bound`: The socket is already bound. (EINVAL) + /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + /// + /// # Typical `finish` errors + /// - `ephemeral-ports-exhausted`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) + /// - `address-in-use`: Address is already in use. (EADDRINUSE) + /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) + /// - `not-in-progress`: A `bind` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # References /// - /// - - bind: func(this: tcp-socket, network: network, local-address: ip-socket-address) -> result<_, error> + /// - + /// - + start-bind: func(this: tcp-socket, network: network, local-address: ip-socket-address) -> result<_, error-code> + finish-bind: func(this: tcp-socket) -> result<_, error-code> /// Connect to a remote endpoint. - /// + /// /// On success: /// - the socket is transitioned into the Connection state /// - a pair of streams is returned that can be used to read & write to the connection - /// - /// Fails when: - /// - the socket is already bound to a different network. - /// - the provided network does not allow connections to the specified endpoint. - /// - the socket is already in the Connection or Listener state. - /// - either the remote IP address or port is 0. - /// - /// References + /// + /// # Typical `start` errors + /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows) + /// - `invalid-remote-address`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows) + /// - `already-attached`: The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. + /// - `already-connected`: The socket is already in the Connection state. (EISCONN) + /// - `already-listening`: The socket is already in the Listener state. (EOPNOTSUPP, EINVAL on Windows) + /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + /// + /// # Typical `finish` errors + /// - `timeout`: Connection timed out. (ETIMEDOUT) + /// - `connection-refused`: The connection was forcefully rejected. (ECONNREFUSED) + /// - `connection-reset`: The connection was reset. (ECONNRESET) + /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) + /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + /// - `not-in-progress`: A `connect` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # References /// - /// - - connect: func(this: tcp-socket, network: network, remote-address: ip-socket-address) -> result, error> + /// - + /// - + start-connect: func(this: tcp-socket, network: network, remote-address: ip-socket-address) -> result<_, error-code> + finish-connect: func(this: tcp-socket) -> result, error-code> /// Start listening for new connections. - /// + /// /// Transitions the socket into the Listener state. - /// - /// Fails when: - /// - the socket is already bound to a different network. - /// - the provided network does not allow listening on the specified address. - /// - the socket is already in the Connection or Listener state. /// - /// References + /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. + /// + /// # Typical `start` errors + /// - `already-attached`: The socket is already attached to a different network. The `network` passed to `listen` must be identical to the one passed to `bind`. + /// - `already-connected`: The socket is already in the Connection state. (EISCONN, EINVAL on BSD) + /// - `already-listening`: The socket is already in the Listener state. + /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EINVAL on BSD) + /// + /// # Typical `finish` errors + /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE) + /// - `not-in-progress`: A `listen` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # References /// - /// - - listen: func(this: tcp-socket, network: network) -> result<_, error> + /// - + /// - + start-listen: func(this: tcp-socket, network: network) -> result<_, error-code> + finish-listen: func(this: tcp-socket) -> result<_, error-code> /// Accept a new client socket. - /// + /// /// The returned socket is bound and in the Connection state. - /// + /// /// On success, this function returns the newly accepted client socket along with /// a pair of streams that can be used to read & write to the connection. - /// - /// Fails when this socket is not in the Listening state. - /// - /// References: + /// + /// # Typical errors + /// - `not-listening`: Socket is not in the Listener state. (EINVAL) + /// - `would-block`: No pending connections at the moment. (EWOULDBLOCK, EAGAIN) + /// + /// Host implementations must skip over transient errors returned by the native accept syscall. + /// + /// # References /// - /// - - accept: func(this: tcp-socket) -> result, error> + /// - + /// - + accept: func(this: tcp-socket) -> result, error-code> /// Get the bound local address. - /// - /// Returns an error if the socket is not bound. - /// - /// References + /// + /// # Typical errors + /// - `not-bound`: The socket is not bound to any local address. + /// + /// # References /// - /// - - local-address: func(this: tcp-socket) -> result + /// - + /// - + local-address: func(this: tcp-socket) -> result /// Get the bound remote address. - /// - /// Fails when the socket is not in the Connection state. - /// - /// References + /// + /// # Typical errors + /// - `not-connected`: The socket is not connected to a remote address. (ENOTCONN) + /// + /// # References /// - /// - - remote-address: func(this: tcp-socket) -> result + /// - + /// - + remote-address: func(this: tcp-socket) -> result /// Whether this is a IPv4 or IPv6 socket. - /// + /// /// Equivalent to the SO_DOMAIN socket option. - address-family: func(this: tcp-socket) -> result - + address-family: func(this: tcp-socket) -> ip-address-family + /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. - /// Implementations are not required to support dual-stack mode. Calling `set-ipv6-only(false)` might fail. - /// - /// Fails when called on an IPv4 socket. - /// + /// /// Equivalent to the IPV6_V6ONLY socket option. - ipv6-only: func(this: tcp-socket) -> result - set-ipv6-only: func(this: tcp-socket, value: bool) -> result<_, error> + /// + /// # Typical errors + /// - `ipv6-only-operation`: (get/set) `this` socket is an IPv4 socket. + /// - `already-bound`: (set) The socket is already bound. + /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) + /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + ipv6-only: func(this: tcp-socket) -> result + set-ipv6-only: func(this: tcp-socket, value: bool) -> result<_, error-code> /// Hints the desired listen queue size. Implementations are free to ignore this. - set-listen-backlog-size: func(this: tcp-socket, value: u64) -> result<_, error> + /// + /// # Typical errors + /// - `already-connected`: (set) The socket is already in the Connection state. + /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + set-listen-backlog-size: func(this: tcp-socket, value: u64) -> result<_, error-code> /// Equivalent to the SO_KEEPALIVE socket option. - keep-alive: func(this: tcp-socket) -> result - set-keep-alive: func(this: tcp-socket, value: bool) -> result<_, error> + /// + /// # Typical errors + /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + keep-alive: func(this: tcp-socket) -> result + set-keep-alive: func(this: tcp-socket, value: bool) -> result<_, error-code> /// Equivalent to the TCP_NODELAY socket option. - no-delay: func(this: tcp-socket) -> result - set-no-delay: func(this: tcp-socket, value: bool) -> result<_, error> - + /// + /// # Typical errors + /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + no-delay: func(this: tcp-socket) -> result + set-no-delay: func(this: tcp-socket, value: bool) -> result<_, error-code> + /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. - unicast-hop-limit: func(this: tcp-socket) -> result - set-unicast-hop-limit: func(this: tcp-socket, value: u8) -> result<_, error> + /// + /// # Typical errors + /// - `already-connected`: (set) The socket is already in the Connection state. + /// - `already-listening`: (set) The socket is already in the Listener state. + /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + unicast-hop-limit: func(this: tcp-socket) -> result + set-unicast-hop-limit: func(this: tcp-socket, value: u8) -> result<_, error-code> /// The kernel buffer space reserved for sends/receives on this socket. - /// + /// /// Note #1: an implementation may choose to cap or round the buffer size when setting the value. /// In other words, after setting a value, reading the same setting back may return a different value. - /// + /// /// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of /// actual data to be sent/received by the application, because the kernel might also use the buffer space /// for internal metadata structures. - /// - /// Fails when this socket is in the Listening state. - /// + /// /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. - receive-buffer-size: func(this: tcp-socket) -> result - set-receive-buffer-size: func(this: tcp-socket, value: u64) -> result<_, error> - send-buffer-size: func(this: tcp-socket) -> result - set-send-buffer-size: func(this: tcp-socket, value: u64) -> result<_, error> - - /// Get/set the blocking mode of the socket. - /// - /// By default a socket is in "blocking" mode, meaning that any function blocks and waits for its completion. - /// When switched to "non-blocking" mode, operations that would block return an `again` error. After which - /// the API consumer is expected to call `subscribe` and wait for completion using the wasi-poll module. - /// - /// Note: these functions are here for WASI Preview2 only. - /// They're planned to be removed when `future` is natively supported in Preview3. - non-blocking: func(this: tcp-socket) -> result - set-non-blocking: func(this: tcp-socket, value: bool) -> result<_, error> + /// + /// # Typical errors + /// - `already-connected`: (set) The socket is already in the Connection state. + /// - `already-listening`: (set) The socket is already in the Listener state. + /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + receive-buffer-size: func(this: tcp-socket) -> result + set-receive-buffer-size: func(this: tcp-socket, value: u64) -> result<_, error-code> + send-buffer-size: func(this: tcp-socket) -> result + set-send-buffer-size: func(this: tcp-socket, value: u64) -> result<_, error-code> /// Create a `pollable` which will resolve once the socket is ready for I/O. - /// + /// /// Note: this function is here for WASI Preview2 only. /// It's planned to be removed when `future` is natively supported in Preview3. subscribe: func(this: tcp-socket) -> pollable - /// Gracefully shut down the connection. - /// + /// Initiate a graceful shutdown. + /// /// - receive: the socket is not expecting to receive any more data from the peer. All subsequent read /// operations on the `input-stream` associated with this socket will return an End Of Stream indication. /// Any data still in the receive queue at time of calling `shutdown` will be discarded. /// - send: the socket is not expecting to send any more data to the peer. All subsequent write /// operations on the `output-stream` associated with this socket will return an error. /// - both: same effect as receive & send combined. - /// - /// The shutdown function does not close the socket. - /// - /// Fails when the socket is not in the Connection state. - /// - /// References + /// + /// The shutdown function does not close (drop) the socket. + /// + /// # Typical errors + /// - `not-connected`: The socket is not in the Connection state. (ENOTCONN) + /// + /// # References /// - /// - - shutdown: func(this: tcp-socket, shutdown-type: shutdown-type) -> result<_, error> + /// - + /// - + shutdown: func(this: tcp-socket, shutdown-type: shutdown-type) -> result<_, error-code> /// Dispose of the specified `tcp-socket`, after which it may no longer be used. - /// + /// + /// Similar to the POSIX `close` function. + /// /// Note: this function is scheduled to be removed when Resources are natively supported in Wit. drop-tcp-socket: func(this: tcp-socket) } diff --git a/wasi/wit/deps/sockets/udp-create-socket.wit b/wasi/wit/deps/sockets/udp-create-socket.wit index 169957c9..1cfbd7f0 100644 --- a/wasi/wit/deps/sockets/udp-create-socket.wit +++ b/wasi/wit/deps/sockets/udp-create-socket.wit @@ -1,19 +1,27 @@ -default interface udp-create-socket { - use pkg.network.{network, error, ip-address-family} - use pkg.udp.{udp-socket} +interface udp-create-socket { + use network.{network, error-code, ip-address-family} + use udp.{udp-socket} /// Create a new UDP socket. - /// + /// /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX. - /// + /// /// This function does not require a network capability handle. This is considered to be safe because /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` is called, /// the socket is effectively an in-memory configuration object, unable to communicate with the outside world. - /// - /// References: + /// + /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. + /// + /// # Typical errors + /// - `not-supported`: The host does not support UDP sockets. (EOPNOTSUPP) + /// - `address-family-not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + /// + /// # References: /// - /// - - /// - create-udp-socket: func(address-family: ip-address-family) -> result + /// - + /// - + create-udp-socket: func(address-family: ip-address-family) -> result } diff --git a/wasi/wit/deps/sockets/udp.wit b/wasi/wit/deps/sockets/udp.wit index af8f873b..9dd4573b 100644 --- a/wasi/wit/deps/sockets/udp.wit +++ b/wasi/wit/deps/sockets/udp.wit @@ -1,7 +1,7 @@ -default interface udp { - use poll.poll.{pollable} - use pkg.network.{network, error, ip-socket-address, ip-address-family} +interface udp { + use wasi:poll/poll.{pollable} + use network.{network, error-code, ip-socket-address, ip-address-family} /// A UDP socket handle. @@ -27,136 +27,185 @@ default interface udp { /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which /// network interface(s) to bind to. /// If the TCP/UDP port is zero, the socket will be bound to a random free port. - /// - /// When a socket is not explicitly bound, the first invocation to a connect, send or receive operation will - /// implicitly bind the socket. - /// - /// Fails when: - /// - the socket is already bound. - /// - /// References + /// + /// When a socket is not explicitly bound, the first invocation to connect will implicitly bind the socket. + /// + /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. + /// + /// # Typical `start` errors + /// - `address-family-mismatch`: The `local-address` has the wrong address family. (EINVAL) + /// - `already-bound`: The socket is already bound. (EINVAL) + /// - `concurrency-conflict`: Another `bind` or `connect` operation is already in progress. (EALREADY) + /// + /// # Typical `finish` errors + /// - `ephemeral-ports-exhausted`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) + /// - `address-in-use`: Address is already in use. (EADDRINUSE) + /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) + /// - `not-in-progress`: A `bind` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # References /// - /// - - bind: func(this: udp-socket, network: network, local-address: ip-socket-address) -> result<_, error> + /// - + /// - + start-bind: func(this: udp-socket, network: network, local-address: ip-socket-address) -> result<_, error-code> + finish-bind: func(this: udp-socket) -> result<_, error-code> /// Set the destination address. - /// + /// /// The local-address is updated based on the best network path to `remote-address`. - /// + /// /// When a destination address is set: /// - all receive operations will only return datagrams sent from the provided `remote-address`. /// - the `send` function can only be used to send to this destination. - /// + /// /// Note that this function does not generate any network traffic and the peer is not aware of this "connection". - /// - /// Fails when: - /// - the socket is already bound to a different network. - /// - /// References + /// + /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. + /// + /// # Typical `start` errors + /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-remote-address`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `already-attached`: The socket is already bound to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. + /// - `concurrency-conflict`: Another `bind` or `connect` operation is already in progress. (EALREADY) + /// + /// # Typical `finish` errors + /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + /// - `not-in-progress`: A `connect` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # References /// - /// - - connect: func(this: udp-socket, network: network, remote-address: ip-socket-address) -> result<_, error> + /// - + /// - + start-connect: func(this: udp-socket, network: network, remote-address: ip-socket-address) -> result<_, error-code> + finish-connect: func(this: udp-socket) -> result<_, error-code> /// Receive a message. - /// + /// /// Returns: /// - The sender address of the datagram /// - The number of bytes read. - /// - /// Fails when: - /// - the socket is not bound. - /// - /// References + /// + /// # Typical errors + /// - `not-bound`: The socket is not bound to any local address. (EINVAL) + /// - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) + /// - `would-block`: There is no pending data available to be read at the moment. (EWOULDBLOCK, EAGAIN) + /// + /// # References /// - /// - /// - - receive: func(this: udp-socket) -> result + /// - + /// - + /// - + /// - + receive: func(this: udp-socket) -> result /// Send a message to a specific destination address. - /// + /// /// The remote address option is required. To send a message to the "connected" peer, /// call `remote-address` to get their address. - /// - /// Fails when: - /// - the socket is not bound. Unlike POSIX, this function does not perform an implicit bind. - /// - the socket is in "connected" mode and the `datagram.remote-address` does not match the address passed to `connect`. - /// - /// References + /// + /// # Typical errors + /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-remote-address`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `already-connected`: The socket is in "connected" mode and the `datagram.remote-address` does not match the address passed to `connect`. (EISCONN) + /// - `not-bound`: The socket is not bound to any local address. Unlike POSIX, this function does not perform an implicit bind. + /// - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) + /// - `datagram-too-large`: The datagram is too large. (EMSGSIZE) + /// - `would-block`: The send buffer is currently full. (EWOULDBLOCK, EAGAIN) + /// + /// # References /// - /// - /// - - send: func(this: udp-socket, datagram: datagram) -> result<_, error> + /// - + /// - + /// - + /// - + send: func(this: udp-socket, datagram: datagram) -> result<_, error-code> /// Get the current bound address. - /// - /// Returns an error if the socket is not bound. - /// - /// References + /// + /// # Typical errors + /// - `not-bound`: The socket is not bound to any local address. + /// + /// # References /// - /// - - local-address: func(this: udp-socket) -> result + /// - + /// - + local-address: func(this: udp-socket) -> result /// Get the address set with `connect`. - /// - /// References + /// + /// # Typical errors + /// - `not-connected`: The socket is not connected to a remote address. (ENOTCONN) + /// + /// # References /// - /// - - remote-address: func(this: udp-socket) -> result + /// - + /// - + remote-address: func(this: udp-socket) -> result /// Whether this is a IPv4 or IPv6 socket. - /// + /// /// Equivalent to the SO_DOMAIN socket option. - address-family: func(this: udp-socket) -> result + address-family: func(this: udp-socket) -> ip-address-family /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. - /// Implementations are not required to support dual-stack mode, so calling `set-ipv6-only(false)` might fail. - /// - /// Fails when called on an IPv4 socket. - /// + /// /// Equivalent to the IPV6_V6ONLY socket option. - ipv6-only: func(this: udp-socket) -> result - set-ipv6-only: func(this: udp-socket, value: bool) -> result<_, error> + /// + /// # Typical errors + /// - `ipv6-only-operation`: (get/set) `this` socket is an IPv4 socket. + /// - `already-bound`: (set) The socket is already bound. + /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) + /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) + ipv6-only: func(this: udp-socket) -> result + set-ipv6-only: func(this: udp-socket, value: bool) -> result<_, error-code> /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. - unicast-hop-limit: func(this: udp-socket) -> result - set-unicast-hop-limit: func(this: udp-socket, value: u8) -> result<_, error> + /// + /// # Typical errors + /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) + unicast-hop-limit: func(this: udp-socket) -> result + set-unicast-hop-limit: func(this: udp-socket, value: u8) -> result<_, error-code> /// The kernel buffer space reserved for sends/receives on this socket. - /// + /// /// Note #1: an implementation may choose to cap or round the buffer size when setting the value. /// In other words, after setting a value, reading the same setting back may return a different value. - /// + /// /// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of /// actual data to be sent/received by the application, because the kernel might also use the buffer space /// for internal metadata structures. - /// + /// /// Fails when this socket is in the Listening state. - /// + /// /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. - receive-buffer-size: func(this: udp-socket) -> result - set-receive-buffer-size: func(this: udp-socket, value: u64) -> result<_, error> - send-buffer-size: func(this: udp-socket) -> result - set-send-buffer-size: func(this: udp-socket, value: u64) -> result<_, error> - - /// Get/set the blocking mode of the socket. - /// - /// By default a socket is in "blocking" mode, meaning that any function blocks and waits for its completion. - /// When switched to "non-blocking" mode, operations that would block return an `again` error. After which - /// the API consumer is expected to call `subscribe` and wait for completion using the wasi-poll module. - /// - /// Note: these functions are here for WASI Preview2 only. - /// They're planned to be removed when `future` is natively supported in Preview3. - non-blocking: func(this: udp-socket) -> result - set-non-blocking: func(this: udp-socket, value: bool) -> result<_, error> + /// + /// # Typical errors + /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) + receive-buffer-size: func(this: udp-socket) -> result + set-receive-buffer-size: func(this: udp-socket, value: u64) -> result<_, error-code> + send-buffer-size: func(this: udp-socket) -> result + set-send-buffer-size: func(this: udp-socket, value: u64) -> result<_, error-code> /// Create a `pollable` which will resolve once the socket is ready for I/O. - /// + /// /// Note: this function is here for WASI Preview2 only. /// It's planned to be removed when `future` is natively supported in Preview3. subscribe: func(this: udp-socket) -> pollable /// Dispose of the specified `udp-socket`, after which it may no longer be used. - /// + /// /// Note: this function is scheduled to be removed when Resources are natively supported in Wit. drop-udp-socket: func(this: udp-socket) } diff --git a/wasi/wit/deps/wasi-cli-base/exit.wit b/wasi/wit/deps/wasi-cli-base/exit.wit deleted file mode 100644 index 2759e9dd..00000000 --- a/wasi/wit/deps/wasi-cli-base/exit.wit +++ /dev/null @@ -1,4 +0,0 @@ -default interface wasi-exit { - /// Exit the curerent instance and any linked instances. - exit: func(status: result) -} diff --git a/wasi/wit/deps/wasi-cli-base/preopens.wit b/wasi/wit/deps/wasi-cli-base/preopens.wit deleted file mode 100644 index 52a93442..00000000 --- a/wasi/wit/deps/wasi-cli-base/preopens.wit +++ /dev/null @@ -1,17 +0,0 @@ -default interface preopens { - use filesystem.filesystem.{descriptor} - use io.streams.{input-stream, output-stream} - - /// Stdio preopens: these are the resources that provide stdin, stdout, and - /// stderr. - record stdio-preopens { - stdin: input-stream, - stdout: output-stream, - stderr: output-stream, - } - - /// Return the set of stdio preopens. - get-stdio: func() -> stdio-preopens - /// Return the set of of preopened directories, and their path. - get-directories: func() -> list> -} diff --git a/wasi/wit/reactor.wit b/wasi/wit/reactor.wit index 2abfa466..1ebba26b 100644 --- a/wasi/wit/reactor.wit +++ b/wasi/wit/reactor.wit @@ -1,21 +1,34 @@ -default world reactor { - import wall-clock: clocks.wall-clock - import monotonic-clock: clocks.monotonic-clock - import timezone: clocks.timezone - import filesystem: filesystem.filesystem - import instance-network: sockets.instance-network - import ip-name-lookup: sockets.ip-name-lookup - import network: sockets.network - import tcp-create-socket: sockets.tcp-create-socket - import tcp: sockets.tcp - import udp-create-socket: sockets.udp-create-socket - import udp: sockets.udp - import random: random.random - import poll: poll.poll - import streams: io.streams - import console: logging.handler - import default-outgoing-HTTP: http.outgoing-handler - import environment: wasi-cli-base.environment - import preopens: wasi-cli-base.preopens - import exit: wasi-cli-base.exit +package wasi:preview + +world reactor { + import wasi:clocks/wall-clock + import wasi:clocks/monotonic-clock + import wasi:clocks/timezone + import wasi:filesystem/types + import wasi:filesystem/preopens + import wasi:sockets/instance-network + import wasi:sockets/ip-name-lookup + import wasi:sockets/network + import wasi:sockets/tcp-create-socket + import wasi:sockets/tcp + import wasi:sockets/udp-create-socket + import wasi:sockets/udp + import wasi:random/random + import wasi:random/insecure + import wasi:random/insecure-seed + import wasi:poll/poll + import wasi:io/streams + import wasi:cli/environment + import wasi:cli/exit + import wasi:cli/stdin + import wasi:cli/stdout + import wasi:cli/stderr + import wasi:cli/terminal-input + import wasi:cli/terminal-output + import wasi:cli/terminal-stdin + import wasi:cli/terminal-stdout + import wasi:cli/terminal-stderr + + import wasi:logging/logging + import wasi:http/outgoing-handler } From 0852aa51f31b17c315f358014ef8347a7d12010d Mon Sep 17 00:00:00 2001 From: Eduardo Rodrigues Date: Fri, 18 Aug 2023 02:24:20 +0200 Subject: [PATCH 2/5] feat: update wasi http client to latest --- Cargo.lock | 290 ++++++++++++++++++++++++++++++++-------- Cargo.toml | 9 +- wasi/Cargo.toml | 2 +- wasi/src/http_client.rs | 137 ++++++++++--------- wasi/src/lib.rs | 3 +- 5 files changed, 317 insertions(+), 124 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 36c1c42a..7197699d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,9 +79,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.70" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" [[package]] name = "arbitrary" @@ -97,7 +97,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.27", ] [[package]] @@ -127,6 +127,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + [[package]] name = "block-buffer" version = "0.10.4" @@ -294,7 +300,7 @@ checksum = "223163f58c9a40c3b0a43e1c4b50a9ce09f007ea2cb1ec258a687945b4b7929f" dependencies = [ "anstream", "anstyle", - "bitflags", + "bitflags 1.3.2", "clap_lex", "strsim", ] @@ -308,7 +314,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.27", ] [[package]] @@ -325,7 +331,7 @@ dependencies = [ "cap-std", "getrandom", "rustix 0.37.13", - "wit-bindgen", + "wit-bindgen 0.9.0", ] [[package]] @@ -600,6 +606,12 @@ dependencies = [ "termcolor", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.3.0" @@ -720,7 +732,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" dependencies = [ "fallible-iterator", - "indexmap", + "indexmap 1.9.3", "stable_deref_trait", ] @@ -739,6 +751,12 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + [[package]] name = "heck" version = "0.4.1" @@ -828,6 +846,17 @@ dependencies = [ "serde", ] +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", + "serde", +] + [[package]] name = "instant" version = "0.1.12" @@ -1047,7 +1076,7 @@ checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" dependencies = [ "crc32fast", "hashbrown 0.13.2", - "indexmap", + "indexmap 1.9.3", "memchr", ] @@ -1112,9 +1141,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] @@ -1134,16 +1163,27 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffade02495f22453cd593159ea2f59827aae7f53fa8323f756799b670881dcf8" dependencies = [ - "bitflags", + "bitflags 1.3.2", + "memchr", + "unicase", +] + +[[package]] +name = "pulldown-cmark" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" +dependencies = [ + "bitflags 1.3.2", "memchr", "unicase", ] [[package]] name = "quote" -version = "1.0.26" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0" dependencies = [ "proc-macro2", ] @@ -1204,7 +1244,7 @@ dependencies = [ name = "reactor-tests" version = "0.1.0" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.4.0", ] [[package]] @@ -1213,7 +1253,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -1222,7 +1262,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -1286,7 +1326,7 @@ version = "0.36.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a38f9520be93aba504e8ca974197f46158de5dcaa9fa04b57c57cd6a679d658" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", @@ -1300,7 +1340,7 @@ version = "0.37.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f79bef90eb6d984c72722595b5b1348ab39275a5e5123faca6863bf07d75a4e0" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "itoa", @@ -1333,22 +1373,22 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.159" +version = "1.0.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" +checksum = "3b88756493a5bd5e5395d53baa70b194b05764ab85b59e43e4b8f4e1192fa9b1" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.159" +version = "1.0.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" +checksum = "6e5c3a298c7f978e53536f95a63bdc4c4a64550582f31a0359a9afda6aede62e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.27", ] [[package]] @@ -1422,6 +1462,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "spdx" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b19b32ed6d899ab23174302ff105c1577e45a06b08d4fe0a9dd13ce804bbbf71" +dependencies = [ + "smallvec", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -1447,9 +1496,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.13" +version = "2.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" +checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" dependencies = [ "proc-macro2", "quote", @@ -1462,7 +1511,7 @@ version = "0.25.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e1ab6a74e204b606bf397944fa991f3b01046113cc0a4ac269be3ef067cc24b" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cap-fs-ext", "cap-std", "fd-lock", @@ -1538,7 +1587,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.27", ] [[package]] @@ -1593,7 +1642,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.27", ] [[package]] @@ -1742,7 +1791,7 @@ version = "0.0.0" dependencies = [ "anyhow", "async-trait", - "bitflags", + "bitflags 1.3.2", "cap-fs-ext", "cap-rand", "cap-std", @@ -1765,7 +1814,7 @@ dependencies = [ "anyhow", "bytes", "http", - "wit-bindgen", + "wit-bindgen 0.9.0", ] [[package]] @@ -1794,6 +1843,15 @@ dependencies = [ "leb128", ] +[[package]] +name = "wasm-encoder" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2f8e9778e04cbf44f58acc301372577375a666b966c50b03ef46144f80436a8" +dependencies = [ + "leb128", +] + [[package]] name = "wasm-metadata" version = "0.3.1" @@ -1801,7 +1859,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6956efd8a1a2c48a707e9a1b2da729834a0f8e4c58117493b0d9d089cee468" dependencies = [ "anyhow", - "indexmap", + "indexmap 1.9.3", "serde", "wasm-encoder 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)", "wasmparser 0.102.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1813,19 +1871,34 @@ version = "0.3.1" source = "git+https://github.com/bytecodealliance/wasm-tools?rev=b17fc48a23152735bafd8f7ee95e3b55683d9511#b17fc48a23152735bafd8f7ee95e3b55683d9511" dependencies = [ "anyhow", - "indexmap", + "indexmap 1.9.3", "serde", "wasm-encoder 0.25.0 (git+https://github.com/bytecodealliance/wasm-tools?rev=b17fc48a23152735bafd8f7ee95e3b55683d9511)", "wasmparser 0.102.0 (git+https://github.com/bytecodealliance/wasm-tools?rev=b17fc48a23152735bafd8f7ee95e3b55683d9511)", ] +[[package]] +name = "wasm-metadata" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51db59397fc650b5f2fc778e4a5c4456cd856bed7fc1ec15f8d3e28229dc463" +dependencies = [ + "anyhow", + "indexmap 2.0.0", + "serde", + "serde_json", + "spdx", + "wasm-encoder 0.30.0", + "wasmparser 0.108.0", +] + [[package]] name = "wasmparser" version = "0.102.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48134de3d7598219ab9eaf6b91b15d8e50d31da76b8519fe4ecfcec2cf35104b" dependencies = [ - "indexmap", + "indexmap 1.9.3", "url", ] @@ -1834,7 +1907,7 @@ name = "wasmparser" version = "0.102.0" source = "git+https://github.com/bytecodealliance/wasm-tools?rev=b17fc48a23152735bafd8f7ee95e3b55683d9511#b17fc48a23152735bafd8f7ee95e3b55683d9511" dependencies = [ - "indexmap", + "indexmap 1.9.3", "url", ] @@ -1844,10 +1917,20 @@ version = "0.103.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c437373cac5ea84f1113d648d51f71751ffbe3d90c00ae67618cf20d0b5ee7b" dependencies = [ - "indexmap", + "indexmap 1.9.3", "url", ] +[[package]] +name = "wasmparser" +version = "0.108.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76c956109dcb41436a39391139d9b6e2d0a5e0b158e1293ef352ec977e5e36c5" +dependencies = [ + "indexmap 2.0.0", + "semver", +] + [[package]] name = "wasmprinter" version = "0.2.55" @@ -1868,7 +1951,7 @@ dependencies = [ "bincode", "cfg-if", "encoding_rs", - "indexmap", + "indexmap 1.9.3", "libc", "log", "object", @@ -1983,7 +2066,7 @@ dependencies = [ "anyhow", "cranelift-entity", "gimli", - "indexmap", + "indexmap 1.9.3", "log", "object", "serde", @@ -2061,7 +2144,7 @@ dependencies = [ "cc", "cfg-if", "encoding_rs", - "indexmap", + "indexmap 1.9.3", "libc", "log", "mach", @@ -2094,7 +2177,7 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", - "bitflags", + "bitflags 1.3.2", "cap-net-ext", "cap-std", "ipnet", @@ -2193,7 +2276,7 @@ source = "git+https://github.com/bytecodealliance/wasmtime?rev=299131ae2d6655c49 dependencies = [ "anyhow", "async-trait", - "bitflags", + "bitflags 1.3.2", "thiserror", "tracing", "wasmtime", @@ -2419,7 +2502,7 @@ version = "0.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c52a121f0fbf9320d5f2a9a5d82f6cb7557eda5e8b47fc3e7f359ec866ae960" dependencies = [ - "bitflags", + "bitflags 1.3.2", "io-lifetimes", "windows-sys 0.48.0", ] @@ -2430,8 +2513,18 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e7cf57f8786216c5652e1228b25203af2ff523808b5e9d3671894eee2bf7264" dependencies = [ - "bitflags", - "wit-bindgen-rust-macro", + "bitflags 1.3.2", + "wit-bindgen-rust-macro 0.4.0", +] + +[[package]] +name = "wit-bindgen" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5c3d15a04ce994fad2c5442a754b404ab1fee23c903a04a560f84f94fdf63c0" +dependencies = [ + "bitflags 2.3.3", + "wit-bindgen-rust-macro 0.9.0", ] [[package]] @@ -2445,6 +2538,17 @@ dependencies = [ "wit-parser 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "wit-bindgen-core" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9658ec54d4a3c9e2f079bc65a131093337595b595fbf82f805008469838cdea" +dependencies = [ + "anyhow", + "wit-component 0.12.0", + "wit-parser 0.9.2", +] + [[package]] name = "wit-bindgen-rust" version = "0.4.0" @@ -2453,11 +2557,24 @@ checksum = "efdf5b00935b7b52d0e56cae1960f8ac13019a285f5aa762ff6bd7139a5c28a2" dependencies = [ "heck", "wasm-metadata 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "wit-bindgen-core", - "wit-bindgen-rust-lib", + "wit-bindgen-core 0.4.0", + "wit-bindgen-rust-lib 0.4.0", "wit-component 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "wit-bindgen-rust" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21ae6a6198ba9765771b977e2af985a0d5ac71b59f999da5c4ee1c7bbd8ca8dc" +dependencies = [ + "heck", + "wasm-metadata 0.9.0", + "wit-bindgen-core 0.9.0", + "wit-bindgen-rust-lib 0.9.0", + "wit-component 0.12.0", +] + [[package]] name = "wit-bindgen-rust-lib" version = "0.4.0" @@ -2465,7 +2582,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab0a8f4b5fb1820b9d232beb122936425f72ec8fe6acb56e5d8782cfe55083da" dependencies = [ "heck", - "wit-bindgen-core", + "wit-bindgen-core 0.4.0", +] + +[[package]] +name = "wit-bindgen-rust-lib" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c31de8c6c77cac1fd4927c7584d1314cd5e838cfb40b53333d6dffc7a132dda" +dependencies = [ + "heck", + "wit-bindgen-core 0.9.0", ] [[package]] @@ -2477,11 +2604,26 @@ dependencies = [ "anyhow", "proc-macro2", "syn 1.0.109", - "wit-bindgen-core", - "wit-bindgen-rust", + "wit-bindgen-core 0.4.0", + "wit-bindgen-rust 0.4.0", "wit-component 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a2abe5c7c4c08468d01590aa96c8a684dd94fb9241a248af88eef7edac61e43" +dependencies = [ + "anyhow", + "proc-macro2", + "syn 2.0.27", + "wit-bindgen-core 0.9.0", + "wit-bindgen-rust 0.9.0", + "wit-bindgen-rust-lib 0.9.0", + "wit-component 0.12.0", +] + [[package]] name = "wit-component" version = "0.7.4" @@ -2489,8 +2631,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed04310239706efc71cc8b995cb0226089c5b5fd260c3bd800a71486bd3cec97" dependencies = [ "anyhow", - "bitflags", - "indexmap", + "bitflags 1.3.2", + "indexmap 1.9.3", "log", "url", "wasm-encoder 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2505,8 +2647,8 @@ version = "0.7.4" source = "git+https://github.com/bytecodealliance/wasm-tools?rev=b17fc48a23152735bafd8f7ee95e3b55683d9511#b17fc48a23152735bafd8f7ee95e3b55683d9511" dependencies = [ "anyhow", - "bitflags", - "indexmap", + "bitflags 1.3.2", + "indexmap 1.9.3", "log", "url", "wasm-encoder 0.25.0 (git+https://github.com/bytecodealliance/wasm-tools?rev=b17fc48a23152735bafd8f7ee95e3b55683d9511)", @@ -2515,6 +2657,22 @@ dependencies = [ "wit-parser 0.6.4 (git+https://github.com/bytecodealliance/wasm-tools?rev=b17fc48a23152735bafd8f7ee95e3b55683d9511)", ] +[[package]] +name = "wit-component" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "253bd426c532f1cae8c633c517c63719920535f3a7fada3589de40c5b734e393" +dependencies = [ + "anyhow", + "bitflags 1.3.2", + "indexmap 2.0.0", + "log", + "wasm-encoder 0.30.0", + "wasm-metadata 0.9.0", + "wasmparser 0.108.0", + "wit-parser 0.9.2", +] + [[package]] name = "wit-parser" version = "0.6.4" @@ -2523,9 +2681,9 @@ checksum = "f887c3da527a51b321076ebe6a7513026a4757b6d4d144259946552d6fc728b3" dependencies = [ "anyhow", "id-arena", - "indexmap", + "indexmap 1.9.3", "log", - "pulldown-cmark", + "pulldown-cmark 0.8.0", "unicode-xid", "url", ] @@ -2537,9 +2695,9 @@ source = "git+https://github.com/bytecodealliance/wasm-tools?rev=b17fc48a2315273 dependencies = [ "anyhow", "id-arena", - "indexmap", + "indexmap 1.9.3", "log", - "pulldown-cmark", + "pulldown-cmark 0.8.0", "unicode-xid", "url", ] @@ -2552,9 +2710,25 @@ checksum = "365273e457e2cacf762df10df4b1e0d4d9912b5332ff4cf2a172760fd67b7ec4" dependencies = [ "anyhow", "id-arena", - "indexmap", + "indexmap 1.9.3", "log", - "pulldown-cmark", + "pulldown-cmark 0.8.0", + "unicode-xid", + "url", +] + +[[package]] +name = "wit-parser" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "541efa2046e544de53a9da1e2f6299e63079840360c9e106f1f8275a97771318" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.0.0", + "log", + "pulldown-cmark 0.9.3", + "semver", "unicode-xid", "url", ] diff --git a/Cargo.toml b/Cargo.toml index e046ab70..02b3476b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ "wasi-common", "wasi-sockets", "wasi-sockets/sync", - "wasi" + "wasi", ] [workspace.package] @@ -36,8 +36,9 @@ wasmtime-wasi-sockets = { path = "wasi-sockets" } wasmtime-wasi-sockets-sync = { path = "wasi-sockets/sync" } once_cell = "1.12.0" system-interface = { version = "0.25.1", features = ["cap_std_impls"] } -wit-bindgen = { version = "0.4.0", default-features = false } +wit-bindgen = { version = "0.9.0", default-features = false } ipnet = "2" # TODO: Move to cap_std::ipnet instead, when that's released. -wasmtime = { git = "https://github.com/bytecodealliance/wasmtime", rev = "299131ae2d6655c49138bfab2c4469650763ef3b", features = ["component-model"] } +wasmtime = { git = "https://github.com/bytecodealliance/wasmtime", rev = "299131ae2d6655c49138bfab2c4469650763ef3b", features = [ + "component-model", +] } wiggle = { git = "https://github.com/bytecodealliance/wasmtime", rev = "299131ae2d6655c49138bfab2c4469650763ef3b" } - diff --git a/wasi/Cargo.toml b/wasi/Cargo.toml index 84158675..8b715ee5 100644 --- a/wasi/Cargo.toml +++ b/wasi/Cargo.toml @@ -8,7 +8,7 @@ edition.workspace = true license = "Apache-2.0 WITH LLVM-exception" [dependencies] -wit-bindgen = { workspace = true, features = ["macros", "realloc"] } +wit-bindgen = { workspace = true, features = ["macros"] } # Dependencies for HTTP feature anyhow = { version = "1.0", optional = true } diff --git a/wasi/src/http_client.rs b/wasi/src/http_client.rs index 9ba76aed..8d58ec4a 100644 --- a/wasi/src/http_client.rs +++ b/wasi/src/http_client.rs @@ -3,40 +3,43 @@ use bytes::{BufMut, Bytes, BytesMut}; use core::ops::Deref; use http::header::{HeaderName, HeaderValue}; -use crate::snapshots::preview_2::{default_outgoing_http, streams, types as http_types}; +use crate::snapshots::preview_2::wasi::http::{outgoing_handler, types as http_types}; +use crate::snapshots::preview_2::wasi::io::streams; +use crate::snapshots::preview_2::wasi::poll::poll; pub struct DefaultClient { - options: Option, + options: Option, } impl DefaultClient { - pub fn new(options: Option) -> Self { + pub fn new(options: Option) -> Self { Self { options } } pub fn handle(&self, req: http::Request) -> anyhow::Result> { - let req = Request::try_from(req) - .context("converting http request")? - .to_owned(); + let req = Request::try_from(req).context("converting http request")?; - let res = default_outgoing_http::handle(req, self.options); - http_types::drop_outgoing_request(req); + let res = outgoing_handler::handle(req.id, self.options); let response = http::Response::try_from(Response(res)).context("converting http response")?; - http_types::drop_incoming_response(res); + + streams::drop_output_stream(req.body); + http_types::drop_outgoing_request(req.id); Ok(response) } } -pub struct Request(default_outgoing_http::OutgoingRequest); - -impl Deref for Request { - type Target = default_outgoing_http::OutgoingRequest; +#[derive(Default, Debug, Clone)] +pub struct Request { + id: outgoing_handler::OutgoingRequest, + body: http_types::OutgoingStream, +} - fn deref(&self) -> &Self::Target { - &self.0 +impl Request { + pub fn new(id: outgoing_handler::OutgoingRequest, body: http_types::OutgoingStream) -> Self { + Self { id, body } } } @@ -45,70 +48,66 @@ impl TryFrom> for Request { fn try_from(value: http::Request) -> Result { let (parts, body) = value.into_parts(); - let path = parts.uri.path(); - let query = parts.uri.query(); - let method = Method::try_from(parts.method)?; + let method = Method::try_from(parts.method).context("converting request method")?; + let path_with_query = parts.uri.path_and_query(); let headers = Headers::from(&parts.headers); let scheme = match parts.uri.scheme_str().unwrap_or("") { - "http" => Some(http_types::SchemeParam::Http), - "https" => Some(http_types::SchemeParam::Https), + "http" => Some(&http_types::Scheme::Http), + "https" => Some(&http_types::Scheme::Https), _ => None, }; let request = http_types::new_outgoing_request( - method.to_owned(), - path, - query.unwrap_or(""), + &method, + path_with_query.map(|q| q.as_str()), scheme, - parts - .uri - .authority() - .map(|a| a.as_str()) - .ok_or_else(|| anyhow!("unable to extract authority"))?, + parts.uri.authority().map(|a| a.as_str()), headers.to_owned(), ); let request_body = http_types::outgoing_request_write(request) .map_err(|_| anyhow!("outgoing request write failed"))?; - let mut body_cursor = 0; if body.is_empty() { streams::write(request_body, &[]).map_err(|_| anyhow!("writing request body"))?; } else { + let output_stream_pollable = streams::subscribe_to_output_stream(request_body); + let mut body_cursor = 0; while body_cursor < body.len() { - let written = streams::write(request_body, &body[body_cursor..]) + let (written, _) = streams::write(request_body, &body[body_cursor..]) .map_err(|_| anyhow!("writing request body"))?; body_cursor += written as usize; } + poll::drop_pollable(output_stream_pollable); } - Ok(Request(request)) + Ok(Request::new(request, request_body)) } } -pub struct Method<'a>(http_types::MethodParam<'a>); +pub struct Method(http_types::Method); -impl<'a> Deref for Method<'a> { - type Target = http_types::MethodParam<'a>; +impl Deref for Method { + type Target = http_types::Method; fn deref(&self) -> &Self::Target { &self.0 } } -impl<'a> TryFrom for Method<'a> { +impl TryFrom for Method { type Error = anyhow::Error; fn try_from(method: http::Method) -> Result { Ok(Self(match method { - http::Method::GET => http_types::MethodParam::Get, - http::Method::POST => http_types::MethodParam::Post, - http::Method::PUT => http_types::MethodParam::Put, - http::Method::DELETE => http_types::MethodParam::Delete, - http::Method::PATCH => http_types::MethodParam::Patch, - http::Method::CONNECT => http_types::MethodParam::Connect, - http::Method::TRACE => http_types::MethodParam::Trace, - http::Method::HEAD => http_types::MethodParam::Head, - http::Method::OPTIONS => http_types::MethodParam::Options, + http::Method::GET => http_types::Method::Get, + http::Method::POST => http_types::Method::Post, + http::Method::PUT => http_types::Method::Put, + http::Method::DELETE => http_types::Method::Delete, + http::Method::PATCH => http_types::Method::Patch, + http::Method::CONNECT => http_types::Method::Connect, + http::Method::TRACE => http_types::Method::Trace, + http::Method::HEAD => http_types::Method::Head, + http::Method::OPTIONS => http_types::Method::Options, _ => return Err(anyhow!("failed due to unsupported method, currently supported methods are: GET, POST, PUT, DELETE, PATCH, CONNECT, TRACE, HEAD, and OPTIONS")), })) } @@ -129,45 +128,62 @@ impl TryFrom for http::Response { fn try_from(value: Response) -> Result { let future_response = value.to_owned(); - // TODO: we could create a pollable from the future_response and - // poll on it here to test that its available immediately - // poll::drop_pollable(future_response); - let incoming_response = http_types::future_incoming_response_get(future_response) - .ok_or_else(|| anyhow!("incoming response is available immediately"))? - .map_err(|e| anyhow!("incoming response error: {e:?}"))?; + let incoming_response = match http_types::future_incoming_response_get(future_response) { + Some(result) => result, + None => { + let pollable = http_types::listen_to_future_incoming_response(future_response); + let _ = poll::poll_oneoff(&[pollable]); + http_types::future_incoming_response_get(future_response) + .expect("incoming response available") + } + } + .map_err(|e| anyhow!("incoming response error: {e:?}"))?; + http_types::drop_future_incoming_response(future_response); let status = http_types::incoming_response_status(incoming_response); - let headers_handle = http_types::incoming_response_headers(incoming_response); let body_stream = http_types::incoming_response_consume(incoming_response) .map_err(|_| anyhow!("consuming incoming response"))?; + let input_stream_pollable = streams::subscribe_to_input_stream(body_stream); let mut body = BytesMut::new(); - let mut eof = false; - while !eof { - let (body_chunk, stream_ended) = streams::read(body_stream, u64::MAX) - .map_err(|_| anyhow!("reading response body"))?; - eof = stream_ended; + let mut eof = streams::StreamStatus::Open; + while eof != streams::StreamStatus::Ended { + let (body_chunk, stream_status) = streams::read(body_stream, u64::MAX) + .map_err(|e| anyhow!("reading response body: {e:?}"))?; + eof = if body_chunk.is_empty() { + streams::StreamStatus::Ended + } else { + stream_status + }; body.put(body_chunk.as_slice()); } + poll::drop_pollable(input_stream_pollable); + let mut res = http::Response::builder() .status(status) .body(body.freeze()) .map_err(|_| anyhow!("building http response"))?; + streams::drop_input_stream(body_stream); + + let headers_handle = http_types::incoming_response_headers(incoming_response); if headers_handle > 0 { let headers_map = res.headers_mut(); for (name, value) in http_types::fields_entries(headers_handle) { headers_map.insert( HeaderName::from_bytes(name.as_bytes()) .map_err(|_| anyhow!("converting response header name"))?, - HeaderValue::from_str(value.as_str()) + HeaderValue::from_bytes(value.as_slice()) .map_err(|_| anyhow!("converting response header value"))?, ); } } + http_types::drop_fields(headers_handle); + + http_types::drop_incoming_response(incoming_response); Ok(res) } @@ -186,10 +202,11 @@ impl Deref for Headers { impl<'a> From<&'a http::HeaderMap> for Headers { fn from(headers: &'a http::HeaderMap) -> Self { Self(http_types::new_fields( - &headers + headers .iter() - .map(|(name, value)| (name.as_str(), value.to_str().unwrap())) - .collect::>(), + .map(|(name, value)| (name.to_string(), value.to_str().unwrap().to_string())) + .collect::>() + .as_slice(), )) } } diff --git a/wasi/src/lib.rs b/wasi/src/lib.rs index a3fea711..1ab21340 100644 --- a/wasi/src/lib.rs +++ b/wasi/src/lib.rs @@ -4,8 +4,9 @@ pub mod http_client; pub mod snapshots { pub mod preview_2 { wit_bindgen::generate!({ - world: "reactor", + world: "wasi:preview/reactor", std_feature, + ownership: Owning, }); } } From e7c716ed74be86437d305a12dd0cb9291f212c49 Mon Sep 17 00:00:00 2001 From: Eduardo Rodrigues Date: Fri, 18 Aug 2023 17:17:37 +0200 Subject: [PATCH 3/5] chore: update to latest wit definitions --- wasi/wit/deps.toml | 2 +- wasi/wit/deps/io/streams.wit | 127 +++++++++++++++++------------------ 2 files changed, 64 insertions(+), 65 deletions(-) diff --git a/wasi/wit/deps.toml b/wasi/wit/deps.toml index 3628fa8f..9aef68ec 100644 --- a/wasi/wit/deps.toml +++ b/wasi/wit/deps.toml @@ -1 +1 @@ -preview = "https://gitpkg.now.sh/eduardomourar/wasmtime/crates/wasi?ca265efb874bd4dc49b9034d840e5013c34fd722" +preview = "https://gitpkg.now.sh/bytecodealliance/wasmtime/crates/wasi?e250334b8ebfba9359802ab7f61bdd7c6085d87a" diff --git a/wasi/wit/deps/io/streams.wit b/wasi/wit/deps/io/streams.wit index 011bde0d..98df181c 100644 --- a/wasi/wit/deps/io/streams.wit +++ b/wasi/wit/deps/io/streams.wit @@ -8,16 +8,6 @@ package wasi:io interface streams { use wasi:poll/poll.{pollable} - /// An error type returned from a stream operation. - /// - /// TODO: need to figure out the actual contents of this error. Used to be - /// an empty record but that's no longer allowed. The `dummy` field is - /// only here to have this be a valid in the component model by being - /// non-empty. - record stream-error { - dummy: u32, - } - /// Streams provide a sequence of data and then end; once they end, they /// no longer provide any further data. /// @@ -37,15 +27,12 @@ interface streams { /// An input bytestream. In the future, this will be replaced by handle /// types. /// - /// This conceptually represents a `stream`. It's temporary - /// scaffolding until component-model's async features are ready. - /// /// `input-stream`s are *non-blocking* to the extent practical on underlying /// platforms. I/O operations always return promptly; if fewer bytes are /// promptly available than requested, they return the number of bytes promptly /// available, which could even be zero. To wait for data to be available, /// use the `subscribe-to-input-stream` function to obtain a `pollable` which - /// can be polled for using `wasi_poll`. + /// can be polled for using `wasi:poll/poll.poll_oneoff`. /// /// And at present, it is a `u32` instead of being an actual handle, until /// the wit-bindgen implementation of handles and resources is ready. @@ -58,40 +45,36 @@ interface streams { /// This function returns a list of bytes containing the data that was /// read, along with a `stream-status` which, indicates whether further /// reads are expected to produce data. The returned list will contain up to - /// `len` bytes; it may return fewer than requested, but not more. - /// - /// Once a stream has reached the end, subsequent calls to read or - /// `skip` will always report end-of-stream rather than producing more + /// `len` bytes; it may return fewer than requested, but not more. An + /// empty list and `stream-status:open` indicates no more data is + /// available at this time, and that the pollable given by + /// `subscribe-to-input-stream` will be ready when more data is available. + /// + /// Once a stream has reached the end, subsequent calls to `read` or + /// `skip` will always report `stream-status:ended` rather than producing more /// data. /// - /// If `len` is 0, it represents a request to read 0 bytes, which should - /// always succeed, assuming the stream hasn't reached its end yet, and - /// return an empty list. - /// - /// The len here is a `u64`, but some callees may not be able to allocate - /// a buffer as large as that would imply. - /// FIXME: describe what happens if allocation fails. + /// When the caller gives a `len` of 0, it represents a request to read 0 + /// bytes. This read should always succeed and return an empty list and + /// the current `stream-status`. /// - /// When the returned `stream-status` is `open`, the length of the returned - /// value may be less than `len`. When an empty list is returned, this - /// indicates that no more bytes were available from the stream at that - /// time. In that case the subscribe-to-input-stream pollable will indicate - /// when additional bytes are available for reading. + /// The `len` parameter is a `u64`, which could represent a list of u8 which + /// is not possible to allocate in wasm32, or not desirable to allocate as + /// as a return value by the callee. The callee may return a list of bytes + /// less than `len` in size while more bytes are available for reading. read: func( this: input-stream, /// The maximum number of bytes to read len: u64 - ) -> result, stream-status>, stream-error> + ) -> result, stream-status>> - /// Read bytes from a stream, with blocking. - /// - /// This is similar to `read`, except that it blocks until at least one - /// byte can be read. + /// Read bytes from a stream, after blocking until at least one byte can + /// be read. Except for blocking, identical to `read`. blocking-read: func( this: input-stream, /// The maximum number of bytes to read len: u64 - ) -> result, stream-status>, stream-error> + ) -> result, stream-status>> /// Skip bytes from a stream. /// @@ -102,40 +85,42 @@ interface streams { /// `skip` will always report end-of-stream rather than producing more /// data. /// - /// This function returns the number of bytes skipped, along with a bool - /// indicating whether the end of the stream was reached. The returned - /// value will be at most `len`; it may be less. + /// This function returns the number of bytes skipped, along with a + /// `stream-status` indicating whether the end of the stream was + /// reached. The returned value will be at most `len`; it may be less. skip: func( this: input-stream, /// The maximum number of bytes to skip. len: u64, - ) -> result, stream-error> + ) -> result> - /// Skip bytes from a stream, with blocking. - /// - /// This is similar to `skip`, except that it blocks until at least one - /// byte can be consumed. + /// Skip bytes from a stream, after blocking until at least one byte + /// can be skipped. Except for blocking behavior, identical to `skip`. blocking-skip: func( this: input-stream, /// The maximum number of bytes to skip. len: u64, - ) -> result, stream-error> + ) -> result> /// Create a `pollable` which will resolve once either the specified stream /// has bytes available to read or the other end of the stream has been /// closed. + /// The created `pollable` is a child resource of the `input-stream`. + /// Implementations may trap if the `input-stream` is dropped before + /// all derived `pollable`s created with this function are dropped. subscribe-to-input-stream: func(this: input-stream) -> pollable /// Dispose of the specified `input-stream`, after which it may no longer /// be used. + /// Implementations may trap if this `input-stream` is dropped while child + /// `pollable` resources are still alive. + /// After this `input-stream` is dropped, implementations may report any + /// corresponding `output-stream` has `stream-state.closed`. drop-input-stream: func(this: input-stream) /// An output bytestream. In the future, this will be replaced by handle /// types. /// - /// This conceptually represents a `stream`. It's temporary - /// scaffolding until component-model's async features are ready. - /// /// `output-stream`s are *non-blocking* to the extent practical on /// underlying platforms. Except where specified otherwise, I/O operations also /// always return promptly, after the number of bytes that can be written @@ -159,17 +144,18 @@ interface streams { /// When the returned `stream-status` is `open`, the `u64` return value may /// be less than the length of `buf`. This indicates that no more bytes may /// be written to the stream promptly. In that case the - /// subscribe-to-output-stream pollable will indicate when additional bytes + /// `subscribe-to-output-stream` pollable will indicate when additional bytes /// may be promptly written. /// - /// TODO: document what happens when an empty list is written + /// Writing an empty list must return a non-error result with `0` for the + /// `u64` return value, and the current `stream-status`. write: func( this: output-stream, /// Data to write buf: list - ) -> result, stream-error> + ) -> result> - /// Write bytes to a stream, with blocking. + /// Blocking write of bytes to a stream. /// /// This is similar to `write`, except that it blocks until at least one /// byte can be written. @@ -177,27 +163,29 @@ interface streams { this: output-stream, /// Data to write buf: list - ) -> result, stream-error> + ) -> result> - /// Write multiple zero bytes to a stream. + /// Write multiple zero-bytes to a stream. /// - /// This function returns a `u64` indicating the number of zero bytes - /// that were written; it may be less than `len`. + /// This function returns a `u64` indicating the number of zero-bytes + /// that were written; it may be less than `len`. Equivelant to a call to + /// `write` with a list of zeroes of the given length. write-zeroes: func( this: output-stream, - /// The number of zero bytes to write + /// The number of zero-bytes to write len: u64 - ) -> result, stream-error> + ) -> result> /// Write multiple zero bytes to a stream, with blocking. /// /// This is similar to `write-zeroes`, except that it blocks until at least - /// one byte can be written. + /// one byte can be written. Equivelant to a call to `blocking-write` with + /// a list of zeroes of the given length. blocking-write-zeroes: func( this: output-stream, /// The number of zero bytes to write len: u64 - ) -> result, stream-error> + ) -> result> /// Read from one stream and write to another. /// @@ -212,7 +200,7 @@ interface streams { src: input-stream, /// The number of bytes to splice len: u64, - ) -> result, stream-error> + ) -> result> /// Read from one stream and write to another, with blocking. /// @@ -224,7 +212,7 @@ interface streams { src: input-stream, /// The number of bytes to splice len: u64, - ) -> result, stream-error> + ) -> result> /// Forward the entire contents of an input stream to an output stream. /// @@ -242,13 +230,24 @@ interface streams { this: output-stream, /// The stream to read from src: input-stream - ) -> result, stream-error> + ) -> result> /// Create a `pollable` which will resolve once either the specified stream - /// is ready to accept bytes or the other end of the stream has been closed. + /// is ready to accept bytes or the `stream-state` has become closed. + /// + /// Once the stream-state is closed, this pollable is always ready + /// immediately. + /// + /// The created `pollable` is a child resource of the `output-stream`. + /// Implementations may trap if the `output-stream` is dropped before + /// all derived `pollable`s created with this function are dropped. subscribe-to-output-stream: func(this: output-stream) -> pollable /// Dispose of the specified `output-stream`, after which it may no longer /// be used. + /// Implementations may trap if this `output-stream` is dropped while + /// child `pollable` resources are still alive. + /// After this `output-stream` is dropped, implementations may report any + /// corresponding `input-stream` has `stream-state.closed`. drop-output-stream: func(this: output-stream) } From 7c9bc666b7f7c1a28b7b1f6d4ece2757de852162 Mon Sep 17 00:00:00 2001 From: Eduardo Rodrigues Date: Fri, 18 Aug 2023 17:19:23 +0200 Subject: [PATCH 4/5] chore(ci): run latest nightly rust --- .github/workflows/main.yml | 9 +-- .../Cargo.lock | 77 ++++++++----------- test-programs/build.rs | 2 +- 3 files changed, 38 insertions(+), 50 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 535760b0..576e7b9f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -70,14 +70,11 @@ jobs: os: macos-latest steps: - uses: actions/checkout@v3 - # TODO: Remove pin to 2023-03-14 once rust-lang/rust#109199 is released - # (expected in 1.71) - # TODO: Also remove it in test-programs/build.rs - - run: rustup update nightly-2023-03-14 + - run: rustup update nightly - run: rustup target add --toolchain=stable wasm32-wasi wasm32-unknown-unknown - - run: rustup target add --toolchain=nightly-2023-03-14 wasm32-wasi wasm32-unknown-unknown + - run: rustup target add --toolchain=nightly wasm32-wasi wasm32-unknown-unknown - name: Run test suite - run: cargo test -p host + run: cargo +nightly test -p host build-preview1-component-adapter: name: Build wasi-preview1-component-adapter diff --git a/crates/wasi-preview1-component-adapter/Cargo.lock b/crates/wasi-preview1-component-adapter/Cargo.lock index 4cdca9e0..88ce9e08 100644 --- a/crates/wasi-preview1-component-adapter/Cargo.lock +++ b/crates/wasi-preview1-component-adapter/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "anyhow" -version = "1.0.71" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "autocfg" @@ -24,17 +24,11 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" name = "byte-array-literals" version = "0.0.1" -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -62,9 +56,9 @@ checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -89,12 +83,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "log" -version = "0.4.17" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" @@ -104,24 +95,24 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "object" -version = "0.30.3" +version = "0.30.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" dependencies = [ "memchr", ] [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] @@ -139,31 +130,31 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.27" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] [[package]] name = "serde" -version = "1.0.163" +version = "1.0.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" +checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.163" +version = "1.0.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" +checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.29", ] [[package]] @@ -179,9 +170,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" dependencies = [ "proc-macro2", "quote", @@ -220,9 +211,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-normalization" @@ -253,9 +244,9 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "url" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", "idna", @@ -305,9 +296,9 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.26.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05d0b6fcd0aeb98adf16e7975331b3c17222aa815148f5b976370ce589d80ef" +checksum = "41763f20eafed1399fff1afb466496d3a959f58241436cfdc17e3f5ca954de16" dependencies = [ "leb128", ] @@ -346,21 +337,21 @@ dependencies = [ [[package]] name = "wast" -version = "57.0.0" +version = "62.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eb0f5ed17ac4421193c7477da05892c2edafd67f9639e3c11a82086416662dc" +checksum = "b8ae06f09dbe377b889fbd620ff8fa21e1d49d1d9d364983c0cdbf9870cb9f1f" dependencies = [ "leb128", "memchr", "unicode-width", - "wasm-encoder 0.26.0", + "wasm-encoder 0.31.1", ] [[package]] name = "wat" -version = "1.0.63" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab9ab0d87337c3be2bb6fc5cd331c4ba9fd6bcb4ee85048a0dd59ed9ecf92e53" +checksum = "842e15861d203fb4a96d314b0751cdeaf0f6f8b35e8d81d2953af2af5e44e637" dependencies = [ "wast", ] diff --git a/test-programs/build.rs b/test-programs/build.rs index 9762f203..992b4b18 100644 --- a/test-programs/build.rs +++ b/test-programs/build.rs @@ -46,7 +46,7 @@ fn main() { // wasi-tests and command-tests need require nightly for a feature in the `io-extras` crate: let mut cmd = Command::new("rustup"); cmd.arg("run") - .arg("nightly-2023-03-14") + .arg("nightly") .arg("cargo") .arg("build") .arg("--target=wasm32-wasi") From 4053f432fbe42ef8a58b7bfcb359178e4e4b177d Mon Sep 17 00:00:00 2001 From: Eduardo Rodrigues Date: Fri, 18 Aug 2023 17:48:20 +0200 Subject: [PATCH 5/5] chore: bump wasi-preview2-prototype to v0.0.2 --- Cargo.lock | 2 +- wasi/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7197699d..0524df90 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1809,7 +1809,7 @@ dependencies = [ [[package]] name = "wasi-preview2-prototype" -version = "0.0.1" +version = "0.0.2" dependencies = [ "anyhow", "bytes", diff --git a/wasi/Cargo.toml b/wasi/Cargo.toml index 8b715ee5..26ba63d3 100644 --- a/wasi/Cargo.toml +++ b/wasi/Cargo.toml @@ -1,7 +1,7 @@ [package] # name = "wasi" name = "wasi-preview2-prototype" -version = "0.0.1" +version = "0.0.2" # version = "0.12.0+wasi-snapshot-preview2" description = "Experimental WASI Preview2 API bindings for Rust" edition.workspace = true