From cc3f7134c17d84386d8a9ca026a7c3435e4e774d Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Mon, 13 Nov 2023 14:04:20 -0800 Subject: [PATCH 1/5] wit-deps update --- wit/deps.lock | 24 +- wit/deps/cli/command.wit | 2 +- wit/deps/cli/reactor.wit | 35 +- wit/deps/cli/stdio.wit | 6 +- wit/deps/clocks/monotonic-clock.wit | 3 +- wit/deps/clocks/timezone.wit | 48 --- wit/deps/clocks/wall-clock.wit | 1 + wit/deps/clocks/world.wit | 3 +- wit/deps/filesystem/preopens.wit | 2 + wit/deps/filesystem/types.wit | 83 +--- wit/deps/filesystem/world.wit | 2 +- wit/deps/io/error.wit | 34 ++ wit/deps/io/poll.wit | 2 +- wit/deps/io/streams.wit | 23 +- wit/deps/io/world.wit | 2 +- wit/deps/random/insecure-seed.wit | 1 + wit/deps/random/insecure.wit | 1 + wit/deps/random/random.wit | 1 + wit/deps/random/world.wit | 2 +- wit/deps/sockets/instance-network.wit | 6 +- wit/deps/sockets/ip-name-lookup.wit | 98 ++--- wit/deps/sockets/network.wit | 243 +++++------ wit/deps/sockets/tcp-create-socket.wit | 45 +- wit/deps/sockets/tcp.wit | 564 ++++++++++++++----------- wit/deps/sockets/udp-create-socket.wit | 45 +- wit/deps/sockets/udp.wit | 485 ++++++++++++--------- wit/deps/sockets/world.wit | 2 +- 27 files changed, 875 insertions(+), 888 deletions(-) delete mode 100644 wit/deps/clocks/timezone.wit create mode 100644 wit/deps/io/error.wit diff --git a/wit/deps.lock b/wit/deps.lock index f55de7c..661a351 100644 --- a/wit/deps.lock +++ b/wit/deps.lock @@ -1,29 +1,29 @@ [cli] url = "https://github.com/WebAssembly/wasi-cli/archive/main.tar.gz" -sha256 = "bf57bb59e137f1dea6521409137e46dbdcd0a78d419ac2313b59fb21332718cd" -sha512 = "36a793525eba4921f0bd55bc445465e86fc7ff8fcf3e5bb61108d35e4d791950b107b456ccadf65897a0a5cd201a5955741c9c1fecb533fca99e8bae478a2dbf" +sha256 = "fb029d0f9468fcb404a079a58fafd9265ef99c0ee1350835348da7b6e105c597" +sha512 = "8602e881281adc67b1ac5a4eb0888636d6f50d15bd14e36dcc446a51551f3f9bb3e9eabb776d723bb113bf1e26a702c5042de095e66e897c3d3cf689e0b7d4f9" [clocks] url = "https://github.com/WebAssembly/wasi-clocks/archive/main.tar.gz" -sha256 = "8d6b9f7a8bf9466bdc68043c33e054878fdf09c1cc69c19c99eeadd3bb257a90" -sha512 = "21b65d911930c4512bb3caa08459283fc70b1ccc5159313092334cffd6662fb92cfe90577b51829ef363e2d02530802c88f2a1f82db43964d1f8bff7ecbc794b" +sha256 = "89da8eca4cd195516574c89c5b3c24a7b5af3ff2565c16753d20d3bdbc5fc60f" +sha512 = "244079b3f592d58478a97adbd0bee8d49ae9dd1a3e435651ee40997b50da9fe62cfaba7e3ec7f7406d7d0288d278a43a3a0bc5150226ba40ce0f8ac6d33f7ddb" [filesystem] url = "https://github.com/WebAssembly/wasi-filesystem/archive/main.tar.gz" -sha256 = "c05155f44cf5798d15a16eaf9cdfb065d05914ed4710421a7448bb61c6decf3a" -sha512 = "fb30ea13678d3f3d2002b2cc1f6dae99ee6a06eae7c408c0d558f21e5039979dd80c8ced46b1a7629ce8b050820d81462093f7b4a733ff706d0258bf5dea5657" +sha256 = "05952bbc98895aa3aeda6c765a3e521016de59f993f3b60394c724640935c09c" +sha512 = "2c242489801a75466986fe014d730fb3aa7b5c6e56a230c8735e6672711b58bcbe92ba78a341b78fc3ac69339e55d3859d8bb14410230f0371ee30dbd83add64" [io] url = "https://github.com/WebAssembly/wasi-io/archive/main.tar.gz" -sha256 = "fb76f4449eea54d06b56fc6a7ca988da51bd84a54d2021cf18da67b5e2c7ebcf" -sha512 = "c005e2a91522958a9537827a49ae344e1cb39d66e85492901a86bcc7e322ba8d0a7f1a02c9b9f840c123b4ad97e297355fac98d4822536d1426d1096dd1d73ac" +sha256 = "f2e6127b235c37c06be675a904d6acf08db953ea688d78c42892c6ad3bd194e4" +sha512 = "32feefbc115c34bf6968cb6e9dc15e755698ee90648e5a5d84448917c36a318bd61b401195eb64330e2475e1d098bfb8dee1440d594a68e0797748762bd84ae5" [random] url = "https://github.com/WebAssembly/wasi-random/archive/main.tar.gz" -sha256 = "7d7c882d50baeb054a754b5766d46f5eed35a4470d887fc8b45121bfc7ac8701" -sha512 = "4b8aad54da50aa44d35f6e5eea87c67bbcedd610650500ffe350c8c8d26f7ee14c49c4c4cc51ebd0ae607459095092625f27a451e52232c575b460334b5a0606" +sha256 = "11afcbff9920f5f1f72b6764d01e59a5faa2c671f0c59f0c9b405778f3708745" +sha512 = "cc4fa3d178559a89d9d6a376e3359b892158d1e73317c5db1f797ebc6b0b57abf2422797648d5b2b494c50cf9360720bc451cc27e15def7d278ba875805ccbf5" [sockets] url = "https://github.com/WebAssembly/wasi-sockets/archive/main.tar.gz" -sha256 = "e9cc9bf3e809c17f7a100f9498056e435eea133bcb0f1abd8833261f3e5ff067" -sha512 = "1917e06807dc5f9f0993fbffbb9a6c8c36ab25a14ba775ffee939aca458ecd52ee796764708381f7f4166665cfcf11a5d47dc756a1e38f291b270678455b3a02" +sha256 = "b5c2e9cc87cefbaef06bbe9978f9bc336da9feee2d51747bc28e10164fc46c39" +sha512 = "3aea6fe0c768b27d5c5cb3adab5e60dc936198f8b677c2cf6c4d57a0460db87eb779e0b577f1240fb2a6bf3ade49919fbffe39b0137bce3242343e6091cc7510" diff --git a/wit/deps/cli/command.wit b/wit/deps/cli/command.wit index 86c9c73..74811d3 100644 --- a/wit/deps/cli/command.wit +++ b/wit/deps/cli/command.wit @@ -1,4 +1,4 @@ -package wasi:cli; +package wasi:cli@0.2.0-rc-2023-11-10; world command { include reactor; diff --git a/wit/deps/cli/reactor.wit b/wit/deps/cli/reactor.wit index 6bd780e..eafa2fd 100644 --- a/wit/deps/cli/reactor.wit +++ b/wit/deps/cli/reactor.wit @@ -1,23 +1,22 @@ -package wasi:cli; +package wasi:cli@0.2.0-rc-2023-11-10; 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:io/poll; - import wasi:io/streams; + import wasi:clocks/wall-clock@0.2.0-rc-2023-11-10; + import wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10; + import wasi:filesystem/types@0.2.0-rc-2023-11-10; + import wasi:filesystem/preopens@0.2.0-rc-2023-11-10; + import wasi:sockets/instance-network@0.2.0-rc-2023-11-10; + import wasi:sockets/ip-name-lookup@0.2.0-rc-2023-11-10; + import wasi:sockets/network@0.2.0-rc-2023-11-10; + import wasi:sockets/tcp-create-socket@0.2.0-rc-2023-11-10; + import wasi:sockets/tcp@0.2.0-rc-2023-11-10; + import wasi:sockets/udp-create-socket@0.2.0-rc-2023-11-10; + import wasi:sockets/udp@0.2.0-rc-2023-11-10; + import wasi:random/random@0.2.0-rc-2023-11-10; + import wasi:random/insecure@0.2.0-rc-2023-11-10; + import wasi:random/insecure-seed@0.2.0-rc-2023-11-10; + import wasi:io/poll@0.2.0-rc-2023-11-10; + import wasi:io/streams@0.2.0-rc-2023-11-10; import environment; import exit; diff --git a/wit/deps/cli/stdio.wit b/wit/deps/cli/stdio.wit index 1bb6c55..1b653b6 100644 --- a/wit/deps/cli/stdio.wit +++ b/wit/deps/cli/stdio.wit @@ -1,17 +1,17 @@ interface stdin { - use wasi:io/streams.{input-stream}; + use wasi:io/streams@0.2.0-rc-2023-11-10.{input-stream}; get-stdin: func() -> input-stream; } interface stdout { - use wasi:io/streams.{output-stream}; + use wasi:io/streams@0.2.0-rc-2023-11-10.{output-stream}; get-stdout: func() -> output-stream; } interface stderr { - use wasi:io/streams.{output-stream}; + use wasi:io/streams@0.2.0-rc-2023-11-10.{output-stream}; get-stderr: func() -> output-stream; } diff --git a/wit/deps/clocks/monotonic-clock.wit b/wit/deps/clocks/monotonic-clock.wit index afacdbb..09ef32c 100644 --- a/wit/deps/clocks/monotonic-clock.wit +++ b/wit/deps/clocks/monotonic-clock.wit @@ -1,3 +1,4 @@ +package wasi:clocks@0.2.0-rc-2023-11-10; /// WASI Monotonic Clock is a clock API intended to let users measure elapsed /// time. /// @@ -9,7 +10,7 @@ /// /// It is intended for measuring elapsed time. interface monotonic-clock { - use wasi:io/poll.{pollable}; + use wasi:io/poll@0.2.0-rc-2023-11-10.{pollable}; /// An instant in time, in nanoseconds. An instant is relative to an /// unspecified initial value, and can only be compared to instances from diff --git a/wit/deps/clocks/timezone.wit b/wit/deps/clocks/timezone.wit deleted file mode 100644 index e717e7b..0000000 --- a/wit/deps/clocks/timezone.wit +++ /dev/null @@ -1,48 +0,0 @@ -interface timezone { - use wall-clock.{datetime}; - - /// Return information needed to display the given `datetime`. This includes - /// the UTC offset, the time zone name, and a flag indicating whether - /// daylight saving time is active. - /// - /// If the timezone cannot be determined for the given `datetime`, return a - /// `timezone-display` for `UTC` with a `utc-offset` of 0 and no daylight - /// saving time. - display: func(when: datetime) -> timezone-display; - - /// The same as `display`, but only return the UTC offset. - utc-offset: func(when: datetime) -> s32; - - /// Information useful for displaying the timezone of a specific `datetime`. - /// - /// This information may vary within a single `timezone` to reflect daylight - /// saving time adjustments. - record timezone-display { - /// The number of seconds difference between UTC time and the local - /// time of the timezone. - /// - /// The returned value will always be less than 86400 which is the - /// number of seconds in a day (24*60*60). - /// - /// In implementations that do not expose an actual time zone, this - /// should return 0. - utc-offset: s32, - - /// The abbreviated name of the timezone to display to a user. The name - /// `UTC` indicates Coordinated Universal Time. Otherwise, this should - /// reference local standards for the name of the time zone. - /// - /// In implementations that do not expose an actual time zone, this - /// should be the string `UTC`. - /// - /// In time zones that do not have an applicable name, a formatted - /// representation of the UTC offset may be returned, such as `-04:00`. - name: string, - - /// Whether daylight saving time is active. - /// - /// In implementations that do not expose an actual time zone, this - /// should return false. - in-daylight-saving-time: bool, - } -} diff --git a/wit/deps/clocks/wall-clock.wit b/wit/deps/clocks/wall-clock.wit index c395649..8abb9a0 100644 --- a/wit/deps/clocks/wall-clock.wit +++ b/wit/deps/clocks/wall-clock.wit @@ -1,3 +1,4 @@ +package wasi:clocks@0.2.0-rc-2023-11-10; /// 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. diff --git a/wit/deps/clocks/world.wit b/wit/deps/clocks/world.wit index 3295ba8..8fa080f 100644 --- a/wit/deps/clocks/world.wit +++ b/wit/deps/clocks/world.wit @@ -1,7 +1,6 @@ -package wasi:clocks; +package wasi:clocks@0.2.0-rc-2023-11-10; world imports { import monotonic-clock; import wall-clock; - import timezone; } diff --git a/wit/deps/filesystem/preopens.wit b/wit/deps/filesystem/preopens.wit index 3f787ac..95ec678 100644 --- a/wit/deps/filesystem/preopens.wit +++ b/wit/deps/filesystem/preopens.wit @@ -1,3 +1,5 @@ +package wasi:filesystem@0.2.0-rc-2023-11-10; + interface preopens { use types.{descriptor}; diff --git a/wit/deps/filesystem/types.wit b/wit/deps/filesystem/types.wit index 7ac147f..059722a 100644 --- a/wit/deps/filesystem/types.wit +++ b/wit/deps/filesystem/types.wit @@ -1,3 +1,4 @@ +package wasi:filesystem@0.2.0-rc-2023-11-10; /// WASI filesystem is a filesystem API primarily intended to let users run WASI /// programs that access their files on their existing filesystems, without /// significant overhead. @@ -23,8 +24,8 @@ /// /// [WASI filesystem path resolution]: https://github.com/WebAssembly/wasi-filesystem/blob/main/path-resolution.md interface types { - use wasi:io/streams.{input-stream, output-stream, error}; - use wasi:clocks/wall-clock.{datetime}; + use wasi:io/streams@0.2.0-rc-2023-11-10.{input-stream, output-stream, error}; + use wasi:clocks/wall-clock@0.2.0-rc-2023-11-10.{datetime}; /// File size or length of a region within a file. type filesize = u64; @@ -142,29 +143,6 @@ interface types { truncate, } - /// Permissions mode used by `open-at`, `change-file-permissions-at`, and - /// similar. - flags modes { - /// True if the resource is considered readable by the containing - /// filesystem. - readable, - /// True if the resource is considered writable by the containing - /// filesystem. - writable, - /// True if the resource is considered executable by the containing - /// filesystem. This does not apply to directories. - executable, - } - - /// Access type used by `access-at`. - variant access-type { - /// Test for readability, writeability, or executability. - access(modes), - - /// Test whether the path exists. - exists, - } - /// Number of hard links to an inode. type link-count = u64; @@ -538,8 +516,6 @@ interface types { open-flags: open-flags, /// Flags to use for the resulting descriptor. %flags: descriptor-flags, - /// Permissions to use when creating a new file. - modes: modes ) -> result; /// Read the contents of a symbolic link. @@ -588,25 +564,6 @@ interface types { 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( - /// 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. @@ -616,40 +573,6 @@ interface types { path: string, ) -> result<_, error-code>; - /// Change the permissions of a filesystem object that is not a directory. - /// - /// Note that the ultimate meanings of these permissions is - /// filesystem-specific. - /// - /// Note: This is similar to `fchmodat` in POSIX. - change-file-permissions-at: func( - /// Flags determining the method of how the path is resolved. - path-flags: path-flags, - /// The relative path to operate on. - path: string, - /// The new permissions for the filesystem object. - modes: modes, - ) -> result<_, error-code>; - - /// Change the permissions of a directory. - /// - /// Note that the ultimate meanings of these permissions is - /// filesystem-specific. - /// - /// Unlike in POSIX, the `executable` flag is not reinterpreted as a "search" - /// flag. `read` on a directory implies readability and searchability, and - /// `execute` is not valid for directories. - /// - /// Note: This is similar to `fchmodat` in POSIX. - change-directory-permissions-at: func( - /// Flags determining the method of how the path is resolved. - path-flags: path-flags, - /// The relative path to operate on. - path: string, - /// The new permissions for the directory. - modes: modes, - ) -> result<_, error-code>; - /// Test whether two descriptors refer to the same filesystem object. /// /// In POSIX, this corresponds to testing whether the two descriptors have the diff --git a/wit/deps/filesystem/world.wit b/wit/deps/filesystem/world.wit index bd47294..285e0ba 100644 --- a/wit/deps/filesystem/world.wit +++ b/wit/deps/filesystem/world.wit @@ -1,4 +1,4 @@ -package wasi:filesystem; +package wasi:filesystem@0.2.0-rc-2023-11-10; world imports { import types; diff --git a/wit/deps/io/error.wit b/wit/deps/io/error.wit new file mode 100644 index 0000000..31918ac --- /dev/null +++ b/wit/deps/io/error.wit @@ -0,0 +1,34 @@ +package wasi:io@0.2.0-rc-2023-11-10; + + +interface error { + /// A resource which represents some error information. + /// + /// The only method provided by this resource is `to-debug-string`, + /// which provides some human-readable information about the error. + /// + /// In the `wasi:io` package, this resource is returned through the + /// `wasi:io/streams/stream-error` type. + /// + /// To provide more specific error information, other interfaces may + /// provide functions to further "downcast" this error into more specific + /// error information. For example, `error`s returned in streams derived + /// from filesystem types to be described using the filesystem's own + /// error-code type, using the function + /// `wasi:filesystem/types/filesystem-error-code`, which takes a parameter + /// `borrow` and returns + /// `option`. + /// + /// The set of functions which can "downcast" an `error` into a more + /// concrete type is open. + resource error { + /// Returns a string that is suitable to assist humans in debugging + /// this error. + /// + /// WARNING: The returned string should not be consumed mechanically! + /// It may change across platforms, hosts, or other implementation + /// details. Parsing this string is a major platform-compatibility + /// hazard. + to-debug-string: func() -> string; + } +} diff --git a/wit/deps/io/poll.wit b/wit/deps/io/poll.wit index 0829a7d..bddde3c 100644 --- a/wit/deps/io/poll.wit +++ b/wit/deps/io/poll.wit @@ -1,4 +1,4 @@ -package wasi:io; +package wasi:io@0.2.0-rc-2023-11-10; /// A poll API intended to let users wait for I/O events on multiple handles /// at once. diff --git a/wit/deps/io/streams.wit b/wit/deps/io/streams.wit index 8999b28..e7e1b68 100644 --- a/wit/deps/io/streams.wit +++ b/wit/deps/io/streams.wit @@ -1,4 +1,4 @@ -package wasi:io; +package wasi:io@0.2.0-rc-2023-11-10; /// WASI I/O is an I/O abstraction API which is currently focused on providing /// stream types. @@ -6,6 +6,7 @@ package wasi:io; /// In the future, the component model is expected to add built-in stream types; /// when it does, they are expected to subsume this API. interface streams { + use error.{error}; use poll.{pollable}; /// An error for input-stream and output-stream operations. @@ -20,26 +21,6 @@ interface streams { closed } - /// Contextual error information about the last failure that happened on - /// a read, write, or flush from an `input-stream` or `output-stream`. - /// - /// This type is returned through the `stream-error` type whenever an - /// operation on a stream directly fails or an error is discovered - /// after-the-fact, for example when a write's failure shows up through a - /// later `flush` or `check-write`. - /// - /// Interfaces such as `wasi:filesystem/types` provide functionality to - /// further "downcast" this error into interface-specific error information. - resource error { - /// Returns a string that's suitable to assist humans in debugging this - /// error. - /// - /// The returned string will change across platforms and hosts which - /// means that parsing it, for example, would be a - /// platform-compatibility hazard. - to-debug-string: func() -> string; - } - /// An input bytestream. /// /// `input-stream`s are *non-blocking* to the extent practical on underlying diff --git a/wit/deps/io/world.wit b/wit/deps/io/world.wit index 05244a9..8243da2 100644 --- a/wit/deps/io/world.wit +++ b/wit/deps/io/world.wit @@ -1,4 +1,4 @@ -package wasi:io; +package wasi:io@0.2.0-rc-2023-11-10; world imports { import streams; diff --git a/wit/deps/random/insecure-seed.wit b/wit/deps/random/insecure-seed.wit index 139aed1..f76e87d 100644 --- a/wit/deps/random/insecure-seed.wit +++ b/wit/deps/random/insecure-seed.wit @@ -1,3 +1,4 @@ +package wasi:random@0.2.0-rc-2023-11-10; /// The insecure-seed interface for seeding hash-map DoS resistance. /// /// It is intended to be portable at least between Unix-family platforms and diff --git a/wit/deps/random/insecure.wit b/wit/deps/random/insecure.wit index 2ffd223..ec7b997 100644 --- a/wit/deps/random/insecure.wit +++ b/wit/deps/random/insecure.wit @@ -1,3 +1,4 @@ +package wasi:random@0.2.0-rc-2023-11-10; /// The insecure interface for insecure pseudo-random numbers. /// /// It is intended to be portable at least between Unix-family platforms and diff --git a/wit/deps/random/random.wit b/wit/deps/random/random.wit index 2c3c6a8..7a7dfa2 100644 --- a/wit/deps/random/random.wit +++ b/wit/deps/random/random.wit @@ -1,3 +1,4 @@ +package wasi:random@0.2.0-rc-2023-11-10; /// WASI Random is a random data API. /// /// It is intended to be portable at least between Unix-family platforms and diff --git a/wit/deps/random/world.wit b/wit/deps/random/world.wit index bb1dd7b..49e5743 100644 --- a/wit/deps/random/world.wit +++ b/wit/deps/random/world.wit @@ -1,4 +1,4 @@ -package wasi:random; +package wasi:random@0.2.0-rc-2023-11-10; world imports { import random; diff --git a/wit/deps/sockets/instance-network.wit b/wit/deps/sockets/instance-network.wit index 14e4479..e455d0f 100644 --- a/wit/deps/sockets/instance-network.wit +++ b/wit/deps/sockets/instance-network.wit @@ -1,9 +1,9 @@ /// This interface provides a value-export of the default network handle.. interface instance-network { - use network.{network}; + use network.{network}; - /// Get a handle to the default network. - instance-network: func() -> network; + /// Get a handle to the default network. + instance-network: func() -> network; } diff --git a/wit/deps/sockets/ip-name-lookup.wit b/wit/deps/sockets/ip-name-lookup.wit index b51fe05..931ccf7 100644 --- a/wit/deps/sockets/ip-name-lookup.wit +++ b/wit/deps/sockets/ip-name-lookup.wit @@ -1,61 +1,51 @@ interface ip-name-lookup { - use wasi:io/poll.{pollable}; - use network.{network, error-code, ip-address, ip-address-family}; + use wasi:io/poll@0.2.0-rc-2023-11-10.{pollable}; + use network.{network, error-code, ip-address}; - /// Resolve an internet host name to a list of IP addresses. - /// - /// See the wasi-socket proposal README.md for a comparison with getaddrinfo. - /// - /// # 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. - /// - `include-unavailable`: When set to true, this function will also return addresses of which the runtime - /// thinks (or knows) can't be connected to at the moment. For example, this will return IPv6 addresses on - /// 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 fails or immediately returns successfully with a `resolve-address-stream` - /// that can be used to (asynchronously) fetch the results. - /// - /// 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: borrow, name: string, address-family: option, include-unavailable: bool) -> result; + /// Resolve an internet host name to a list of IP addresses. + /// + /// Unicode domain names are automatically converted to ASCII using IDNA encoding. + /// If the input is an IP address string, the address is parsed and returned + /// as-is without making any external requests. + /// + /// See the wasi-socket proposal README.md for a comparison with getaddrinfo. + /// + /// 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. + /// + /// # Typical errors + /// - `invalid-argument`: `name` is a syntactically invalid domain name or IP address. + /// + /// # References: + /// - + /// - + /// - + /// - + resolve-addresses: func(network: borrow, name: string) -> result; - resource resolve-address-stream { - /// 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`. - /// - /// This function never returns IPv4-mapped IPv6 addresses. - /// - /// # 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() -> result, error-code>; + resource resolve-address-stream { + /// 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`. + /// + /// This function never returns IPv4-mapped IPv6 addresses. + /// + /// # 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() -> result, error-code>; - /// 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() -> pollable; - } + /// 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() -> pollable; + } } diff --git a/wit/deps/sockets/network.wit b/wit/deps/sockets/network.wit index e269595..6bb07cd 100644 --- a/wit/deps/sockets/network.wit +++ b/wit/deps/sockets/network.wit @@ -1,178 +1,147 @@ 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. - resource 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. + resource 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 ### + /// 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` + /// - `concurrency-conflict` + /// + /// See each individual API for what the POSIX equivalents are. They sometimes differ per API. + enum error-code { + // ### GENERAL ERRORS ### - /// Unknown error - unknown, + /// Unknown error + unknown, - /// Access denied. - /// - /// POSIX equivalent: EACCES, EPERM - access-denied, + /// Access denied. + /// + /// POSIX equivalent: EACCES, EPERM + access-denied, - /// The operation is not supported. - /// - /// POSIX equivalent: EOPNOTSUPP - not-supported, + /// 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, + /// One of the arguments is invalid. + /// + /// POSIX equivalent: EINVAL + invalid-argument, - /// The operation timed out before it could finish completely. - timeout, + /// Not enough memory to complete the operation. + /// + /// POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY + out-of-memory, - /// This operation is incompatible with another asynchronous operation that is already in progress. - concurrency-conflict, + /// The operation timed out before it could finish completely. + timeout, - /// 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, + /// This operation is incompatible with another asynchronous operation that is already in progress. + /// + /// POSIX equivalent: EALREADY + concurrency-conflict, - /// 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, + /// 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, + // ### TCP & UDP SOCKET ERRORS ### - /// 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 not valid in the socket's current state. + invalid-state, - /// The operation is only supported on IPv4 resources. - ipv4-only-operation, + /// A new socket resource could not be created because of a system limit. + new-socket-limit, - /// The operation is only supported on IPv6 resources. - ipv6-only-operation, + /// 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 or because there are no ephemeral ports available. + address-in-use, + /// The remote address is not reachable + remote-unreachable, - // ### 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, + // ### TCP SOCKET ERRORS ### - /// The socket is already bound. - already-bound, + /// The connection was forcefully rejected + connection-refused, - /// The socket is already in the Connection state. - already-connected, + /// The connection was reset. + connection-reset, - /// The socket is not bound to any local address. - not-bound, + /// A connection was aborted. + connection-aborted, - /// 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, + // ### UDP SOCKET ERRORS ### + datagram-too-large, - /// 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, + // ### NAME LOOKUP ERRORS ### - /// The remote address is not reachable - remote-unreachable, - + /// Name does not exist or has no suitable associated IP addresses. + name-unresolvable, - // ### TCP SOCKET ERRORS ### - - /// The socket is already in the Listener state. - already-listening, + /// A temporary failure in name resolution occurred. + temporary-resolver-failure, - /// The socket is already in the Listener state. - not-listening, + /// A permanent failure in name resolution occurred. + permanent-resolver-failure, + } - /// The connection was forcefully rejected - connection-refused, + enum ip-address-family { + /// Similar to `AF_INET` in POSIX. + ipv4, - /// The connection was reset. - connection-reset, - + /// Similar to `AF_INET6` in POSIX. + ipv6, + } - // ### UDP SOCKET ERRORS ### - datagram-too-large, + type ipv4-address = tuple; + type ipv6-address = tuple; + variant ip-address { + ipv4(ipv4-address), + ipv6(ipv6-address), + } - // ### NAME LOOKUP ERRORS ### - - /// The provided name is a syntactically invalid domain name. - invalid-name, + record ipv4-socket-address { + port: u16, // sin_port + address: ipv4-address, // sin_addr + } - /// Name does not exist or has no suitable associated IP addresses. - name-unresolvable, + record ipv6-socket-address { + port: u16, // sin6_port + flow-info: u32, // sin6_flowinfo + address: ipv6-address, // sin6_addr + scope-id: u32, // sin6_scope_id + } - /// 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, - - /// Similar to `AF_INET6` in POSIX. - ipv6, - } - - type ipv4-address = tuple; - type ipv6-address = tuple; - - variant ip-address { - ipv4(ipv4-address), - ipv6(ipv6-address), - } - - record ipv4-socket-address { - port: u16, // sin_port - address: ipv4-address, // sin_addr - } - - record ipv6-socket-address { - port: u16, // sin6_port - flow-info: u32, // sin6_flowinfo - address: ipv6-address, // sin6_addr - scope-id: u32, // sin6_scope_id - } - - variant ip-socket-address { - ipv4(ipv4-socket-address), - ipv6(ipv6-socket-address), - } + variant ip-socket-address { + ipv4(ipv4-socket-address), + ipv6(ipv6-socket-address), + } } diff --git a/wit/deps/sockets/tcp-create-socket.wit b/wit/deps/sockets/tcp-create-socket.wit index 056bbef..768a07c 100644 --- a/wit/deps/sockets/tcp-create-socket.wit +++ b/wit/deps/sockets/tcp-create-socket.wit @@ -1,27 +1,26 @@ interface tcp-create-socket { - use network.{network, error-code, ip-address-family}; - use tcp.{tcp-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. - /// - /// 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 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. + /// + /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. + /// + /// # Typical errors + /// - `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; } diff --git a/wit/deps/sockets/tcp.wit b/wit/deps/sockets/tcp.wit index fea4438..b01b65e 100644 --- a/wit/deps/sockets/tcp.wit +++ b/wit/deps/sockets/tcp.wit @@ -1,249 +1,321 @@ interface tcp { - use wasi:io/streams.{input-stream, output-stream}; - use wasi:io/poll.{pollable}; - use network.{network, error-code, ip-socket-address, ip-address-family}; - - enum shutdown-type { - /// Similar to `SHUT_RD` in POSIX. - receive, - - /// Similar to `SHUT_WR` in POSIX. - send, - - /// Similar to `SHUT_RDWR` in POSIX. - both, - } - - - /// A TCP socket handle. - resource tcp-socket { - /// Bind the socket to a specific network on the provided IP address and port. - /// - /// 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. - /// - /// 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 - /// - - /// - - /// - - /// - - start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code>; - finish-bind: func() -> 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 - /// - /// # 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 - /// - - /// - - /// - - /// - - start-connect: func(network: borrow, remote-address: ip-socket-address) -> result<_, error-code>; - finish-connect: func() -> result, error-code>; - - /// Start listening for new connections. - /// - /// Transitions the socket into the Listener state. - /// - /// Unlike POSIX: - /// - this function is async. This enables interactive WASI hosts to inject permission prompts. - /// - the socket must already be explicitly bound. - /// - /// # Typical `start` errors - /// - `not-bound`: The socket is not bound to any local address. (EDESTADDRREQ) - /// - `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 - /// - - /// - - /// - - /// - - start-listen: func() -> result<_, error-code>; - finish-listen: func() -> 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. - /// - /// # 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() -> result, error-code>; - - /// Get the bound local address. - /// - /// # Typical errors - /// - `not-bound`: The socket is not bound to any local address. - /// - /// # References - /// - - /// - - /// - - /// - - local-address: func() -> result; - - /// Get the bound remote address. - /// - /// # Typical errors - /// - `not-connected`: The socket is not connected to a remote address. (ENOTCONN) - /// - /// # References - /// - - /// - - /// - - /// - - remote-address: func() -> result; - - /// Whether this is a IPv4 or IPv6 socket. - /// - /// Equivalent to the SO_DOMAIN socket option. - address-family: func() -> ip-address-family; - - /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. - /// - /// Equivalent to the IPV6_V6ONLY socket option. - /// - /// # 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() -> result; - set-ipv6-only: func(value: bool) -> result<_, error-code>; - - /// Hints the desired listen queue size. Implementations are free to ignore this. - /// - /// # 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(value: u64) -> result<_, error-code>; - - /// Equivalent to the SO_KEEPALIVE socket option. - /// - /// # Typical errors - /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - keep-alive: func() -> result; - set-keep-alive: func(value: bool) -> result<_, error-code>; - - /// Equivalent to the TCP_NODELAY socket option. - /// - /// # Typical errors - /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - no-delay: func() -> result; - set-no-delay: func(value: bool) -> result<_, error-code>; - - /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. - /// - /// # 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() -> result; - set-unicast-hop-limit: func(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. - /// - /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. - /// - /// # 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() -> result; - set-receive-buffer-size: func(value: u64) -> result<_, error-code>; - send-buffer-size: func() -> result; - set-send-buffer-size: func(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() -> pollable; - - /// 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 (drop) the socket. - /// - /// # Typical errors - /// - `not-connected`: The socket is not in the Connection state. (ENOTCONN) - /// - /// # References - /// - - /// - - /// - - /// - - shutdown: func(shutdown-type: shutdown-type) -> result<_, error-code>; - } + use wasi:io/streams@0.2.0-rc-2023-11-10.{input-stream, output-stream}; + use wasi:io/poll@0.2.0-rc-2023-11-10.{pollable}; + use wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10.{duration}; + use network.{network, error-code, ip-socket-address, ip-address-family}; + + enum shutdown-type { + /// Similar to `SHUT_RD` in POSIX. + receive, + + /// Similar to `SHUT_WR` in POSIX. + send, + + /// Similar to `SHUT_RDWR` in POSIX. + both, + } + + + /// A TCP socket handle. + resource tcp-socket { + /// Bind the socket to a specific network on the provided IP address and port. + /// + /// 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. + /// + /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. + /// + /// # Typical `start` errors + /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) + /// - `invalid-argument`: `local-address` is not a unicast address. (EINVAL) + /// - `invalid-argument`: `local-address` is an IPv4-mapped IPv6 address, but the socket has `ipv6-only` enabled. (EINVAL) + /// - `invalid-state`: The socket is already bound. (EINVAL) + /// + /// # Typical `finish` errors + /// - `address-in-use`: 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 + /// - + /// - + /// - + /// - + start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code>; + finish-bind: func() -> 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 + /// + /// POSIX mentions: + /// > If connect() fails, the state of the socket is unspecified. Conforming applications should + /// > close the file descriptor and create a new socket before attempting to reconnect. + /// + /// WASI prescribes the following behavior: + /// - If `connect` fails because an input/state validation error, the socket should remain usable. + /// - If a connection was actually attempted but failed, the socket should become unusable for further network communication. + /// Besides `drop`, any method after such a failure may return an error. + /// + /// # Typical `start` errors + /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-argument`: `remote-address` is not a unicast address. (EINVAL, ENETUNREACH on Linux, EAFNOSUPPORT on MacOS) + /// - `invalid-argument`: `remote-address` is an IPv4-mapped IPv6 address, but the socket has `ipv6-only` enabled. (EINVAL, EADDRNOTAVAIL on Illumos) + /// - `invalid-argument`: `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa) + /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows) + /// - `invalid-argument`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows) + /// - `invalid-argument`: The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. + /// - `invalid-state`: The socket is already in the Connection state. (EISCONN) + /// - `invalid-state`: The socket is already in the Listener state. (EOPNOTSUPP, EINVAL on Windows) + /// + /// # Typical `finish` errors + /// - `timeout`: Connection timed out. (ETIMEDOUT) + /// - `connection-refused`: The connection was forcefully rejected. (ECONNREFUSED) + /// - `connection-reset`: The connection was reset. (ECONNRESET) + /// - `connection-aborted`: The connection was aborted. (ECONNABORTED) + /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `address-in-use`: 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 + /// - + /// - + /// - + /// - + start-connect: func(network: borrow, remote-address: ip-socket-address) -> result<_, error-code>; + finish-connect: func() -> result, error-code>; + + /// Start listening for new connections. + /// + /// Transitions the socket into the Listener state. + /// + /// Unlike POSIX: + /// - this function is async. This enables interactive WASI hosts to inject permission prompts. + /// - the socket must already be explicitly bound. + /// + /// # Typical `start` errors + /// - `invalid-state`: The socket is not bound to any local address. (EDESTADDRREQ) + /// - `invalid-state`: The socket is already in the Connection state. (EISCONN, EINVAL on BSD) + /// - `invalid-state`: The socket is already in the Listener state. + /// + /// # Typical `finish` errors + /// - `address-in-use`: 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 + /// - + /// - + /// - + /// - + start-listen: func() -> result<_, error-code>; + finish-listen: func() -> result<_, error-code>; + + /// Accept a new client socket. + /// + /// The returned socket is bound and in the Connection state. The following properties are inherited from the listener socket: + /// - `address-family` + /// - `ipv6-only` + /// - `keep-alive-enabled` + /// - `keep-alive-idle-time` + /// - `keep-alive-interval` + /// - `keep-alive-count` + /// - `hop-limit` + /// - `receive-buffer-size` + /// - `send-buffer-size` + /// + /// 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. + /// + /// # Typical errors + /// - `invalid-state`: Socket is not in the Listener state. (EINVAL) + /// - `would-block`: No pending connections at the moment. (EWOULDBLOCK, EAGAIN) + /// - `connection-aborted`: An incoming connection was pending, but was terminated by the client before this listener could accept it. (ECONNABORTED) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + /// + /// # References + /// - + /// - + /// - + /// - + accept: func() -> result, error-code>; + + /// Get the bound local address. + /// + /// POSIX mentions: + /// > If the socket has not been bound to a local name, the value + /// > stored in the object pointed to by `address` is unspecified. + /// + /// WASI is stricter and requires `local-address` to return `invalid-state` when the socket hasn't been bound yet. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not bound to any local address. + /// + /// # References + /// - + /// - + /// - + /// - + local-address: func() -> result; + + /// Get the remote address. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + remote-address: func() -> result; + + /// Whether the socket is listening for new connections. + /// + /// Equivalent to the SO_ACCEPTCONN socket option. + is-listening: func() -> bool; + + /// Whether this is a IPv4 or IPv6 socket. + /// + /// Equivalent to the SO_DOMAIN socket option. + address-family: func() -> ip-address-family; + + /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. + /// + /// Equivalent to the IPV6_V6ONLY socket option. + /// + /// # Typical errors + /// - `invalid-state`: (set) The socket is already bound. + /// - `not-supported`: (get/set) `this` socket is an IPv4 socket. + /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) + ipv6-only: func() -> result; + set-ipv6-only: func(value: bool) -> result<_, error-code>; + + /// Hints the desired listen queue size. Implementations are free to ignore this. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// + /// # Typical errors + /// - `not-supported`: (set) The platform does not support changing the backlog size after the initial listen. + /// - `invalid-argument`: (set) The provided value was 0. + /// - `invalid-state`: (set) The socket is already in the Connection state. + set-listen-backlog-size: func(value: u64) -> result<_, error-code>; + + /// Enables or disables keepalive. + /// + /// The keepalive behavior can be adjusted using: + /// - `keep-alive-idle-time` + /// - `keep-alive-interval` + /// - `keep-alive-count` + /// These properties can be configured while `keep-alive-enabled` is false, but only come into effect when `keep-alive-enabled` is true. + /// + /// Equivalent to the SO_KEEPALIVE socket option. + keep-alive-enabled: func() -> result; + set-keep-alive-enabled: func(value: bool) -> result<_, error-code>; + + /// Amount of time the connection has to be idle before TCP starts sending keepalive packets. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the TCP_KEEPIDLE socket option. (TCP_KEEPALIVE on MacOS) + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + keep-alive-idle-time: func() -> result; + set-keep-alive-idle-time: func(value: duration) -> result<_, error-code>; + + /// The time between keepalive packets. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the TCP_KEEPINTVL socket option. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + keep-alive-interval: func() -> result; + set-keep-alive-interval: func(value: duration) -> result<_, error-code>; + + /// The maximum amount of keepalive packets TCP should send before aborting the connection. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the TCP_KEEPCNT socket option. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + keep-alive-count: func() -> result; + set-keep-alive-count: func(value: u32) -> result<_, error-code>; + + /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The TTL value must be 1 or higher. + /// - `invalid-state`: (set) The socket is already in the Connection state. + /// - `invalid-state`: (set) The socket is already in the Listener state. + hop-limit: func() -> result; + set-hop-limit: func(value: u8) -> result<_, error-code>; + + /// The kernel buffer space reserved for sends/receives on this socket. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + /// - `invalid-state`: (set) The socket is already in the Connection state. + /// - `invalid-state`: (set) The socket is already in the Listener state. + receive-buffer-size: func() -> result; + set-receive-buffer-size: func(value: u64) -> result<_, error-code>; + send-buffer-size: func() -> result; + set-send-buffer-size: func(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() -> pollable; + + /// 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 (drop) the socket. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not in the Connection state. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + shutdown: func(shutdown-type: shutdown-type) -> result<_, error-code>; + } } diff --git a/wit/deps/sockets/udp-create-socket.wit b/wit/deps/sockets/udp-create-socket.wit index 66f9482..cc58234 100644 --- a/wit/deps/sockets/udp-create-socket.wit +++ b/wit/deps/sockets/udp-create-socket.wit @@ -1,27 +1,26 @@ interface udp-create-socket { - use network.{network, error-code, ip-address-family}; - use udp.{udp-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. - /// - /// 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 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` is called, + /// the socket is effectively an in-memory configuration object, unable to communicate with the outside world. + /// + /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. + /// + /// # Typical errors + /// - `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; } diff --git a/wit/deps/sockets/udp.wit b/wit/deps/sockets/udp.wit index e20b57e..c8dafad 100644 --- a/wit/deps/sockets/udp.wit +++ b/wit/deps/sockets/udp.wit @@ -1,214 +1,277 @@ interface udp { - use wasi:io/poll.{pollable}; - use network.{network, error-code, ip-socket-address, ip-address-family}; - - - record datagram { - data: list, // Theoretical max size: ~64 KiB. In practice, typically less than 1500 bytes. - remote-address: ip-socket-address, - - /// Possible future additions: - /// local-address: ip-socket-address, // IP_PKTINFO / IP_RECVDSTADDR / IPV6_PKTINFO - /// local-interface: u32, // IP_PKTINFO / IP_RECVIF - /// ttl: u8, // IP_RECVTTL - /// dscp: u6, // IP_RECVTOS - /// ecn: u2, // IP_RECVTOS - } - - - - /// A UDP socket handle. - resource udp-socket { - /// Bind the socket to a specific network on the provided IP address and port. - /// - /// 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 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 - /// - - /// - - /// - - /// - - start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code>; - finish-bind: func() -> 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". - /// - /// 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 - /// - - /// - - /// - - /// - - start-connect: func(network: borrow, remote-address: ip-socket-address) -> result<_, error-code>; - finish-connect: func() -> result<_, error-code>; - - /// Receive messages on the socket. - /// - /// This function attempts to receive up to `max-results` datagrams on the socket without blocking. - /// The returned list may contain fewer elements than requested, but never more. - /// If `max-results` is 0, this function returns successfully with an empty list. - /// - /// # 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(max-results: u64) -> result, error-code>; - - /// Send messages on the socket. - /// - /// This function attempts to send all provided `datagrams` on the socket without blocking and - /// returns how many messages were actually sent (or queued for sending). - /// - /// This function semantically behaves the same as iterating the `datagrams` list and sequentially - /// sending each individual datagram until either the end of the list has been reached or the first error occurred. - /// If at least one datagram has been sent successfully, this function never returns an error. - /// - /// If the input list is empty, the function returns `ok(0)`. - /// - /// The remote address option is required. To send a message to the "connected" peer, - /// call `remote-address` to get their address. - /// - /// # 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(datagrams: list) -> result; - - /// Get the current bound address. - /// - /// # Typical errors - /// - `not-bound`: The socket is not bound to any local address. - /// - /// # References - /// - - /// - - /// - - /// - - local-address: func() -> result; - - /// Get the address set with `connect`. - /// - /// # Typical errors - /// - `not-connected`: The socket is not connected to a remote address. (ENOTCONN) - /// - /// # References - /// - - /// - - /// - - /// - - remote-address: func() -> result; - - /// Whether this is a IPv4 or IPv6 socket. - /// - /// Equivalent to the SO_DOMAIN socket option. - address-family: func() -> ip-address-family; - - /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. - /// - /// Equivalent to the IPV6_V6ONLY socket option. - /// - /// # 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() -> result; - set-ipv6-only: func(value: bool) -> result<_, error-code>; - - /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. - /// - /// # Typical errors - /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) - unicast-hop-limit: func() -> result; - set-unicast-hop-limit: func(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. - /// - /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. - /// - /// # Typical errors - /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) - receive-buffer-size: func() -> result; - set-receive-buffer-size: func(value: u64) -> result<_, error-code>; - send-buffer-size: func() -> result; - set-send-buffer-size: func(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() -> pollable; - } + use wasi:io/poll@0.2.0-rc-2023-11-10.{pollable}; + use network.{network, error-code, ip-socket-address, ip-address-family}; + + /// A received datagram. + record incoming-datagram { + /// The payload. + /// + /// Theoretical max size: ~64 KiB. In practice, typically less than 1500 bytes. + data: list, + + /// The source address. + /// + /// This field is guaranteed to match the remote address the stream was initialized with, if any. + /// + /// Equivalent to the `src_addr` out parameter of `recvfrom`. + remote-address: ip-socket-address, + } + + /// A datagram to be sent out. + record outgoing-datagram { + /// The payload. + data: list, + + /// The destination address. + /// + /// The requirements on this field depend on how the stream was initialized: + /// - with a remote address: this field must be None or match the stream's remote address exactly. + /// - without a remote address: this field is required. + /// + /// If this value is None, the send operation is equivalent to `send` in POSIX. Otherwise it is equivalent to `sendto`. + remote-address: option, + } + + + + /// A UDP socket handle. + resource udp-socket { + /// Bind the socket to a specific network on the provided IP address and port. + /// + /// 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 port is zero, the socket will be bound to a random free port. + /// + /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. + /// + /// # Typical `start` errors + /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) + /// - `invalid-state`: The socket is already bound. (EINVAL) + /// + /// # Typical `finish` errors + /// - `address-in-use`: 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 + /// - + /// - + /// - + /// - + start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code>; + finish-bind: func() -> result<_, error-code>; + + /// Set up inbound & outbound communication channels, optionally to a specific peer. + /// + /// This function only changes the local socket configuration and does not generate any network traffic. + /// On success, the `remote-address` of the socket is updated. The `local-address` may be updated as well, + /// based on the best network path to `remote-address`. + /// + /// When a `remote-address` is provided, the returned streams are limited to communicating with that specific peer: + /// - `send` can only be used to send to this destination. + /// - `receive` will only return datagrams sent from the provided `remote-address`. + /// + /// This method may be called multiple times on the same socket to change its association, but + /// only the most recently returned pair of streams will be operational. Implementations may trap if + /// the streams returned by a previous invocation haven't been dropped yet before calling `stream` again. + /// + /// The POSIX equivalent in pseudo-code is: + /// ```text + /// if (was previously connected) { + /// connect(s, AF_UNSPEC) + /// } + /// if (remote_address is Some) { + /// connect(s, remote_address) + /// } + /// ``` + /// + /// Unlike in POSIX, the socket must already be explicitly bound. + /// + /// # Typical errors + /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-argument`: `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa) + /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-state`: The socket is not bound. + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `connection-refused`: The connection was refused. (ECONNREFUSED) + /// + /// # References + /// - + /// - + /// - + /// - + %stream: func(remote-address: option) -> result, error-code>; + + /// Get the current bound address. + /// + /// POSIX mentions: + /// > If the socket has not been bound to a local name, the value + /// > stored in the object pointed to by `address` is unspecified. + /// + /// WASI is stricter and requires `local-address` to return `invalid-state` when the socket hasn't been bound yet. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not bound to any local address. + /// + /// # References + /// - + /// - + /// - + /// - + local-address: func() -> result; + + /// Get the address the socket is currently streaming to. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not streaming to a specific remote address. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + remote-address: func() -> result; + + /// Whether this is a IPv4 or IPv6 socket. + /// + /// Equivalent to the SO_DOMAIN socket option. + address-family: func() -> ip-address-family; + + /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. + /// + /// Equivalent to the IPV6_V6ONLY socket option. + /// + /// # Typical errors + /// - `not-supported`: (get/set) `this` socket is an IPv4 socket. + /// - `invalid-state`: (set) The socket is already bound. + /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) + ipv6-only: func() -> result; + set-ipv6-only: func(value: bool) -> result<_, error-code>; + + /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The TTL value must be 1 or higher. + unicast-hop-limit: func() -> result; + set-unicast-hop-limit: func(value: u8) -> result<_, error-code>; + + /// The kernel buffer space reserved for sends/receives on this socket. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + receive-buffer-size: func() -> result; + set-receive-buffer-size: func(value: u64) -> result<_, error-code>; + send-buffer-size: func() -> result; + set-send-buffer-size: func(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() -> pollable; + } + + resource incoming-datagram-stream { + /// Receive messages on the socket. + /// + /// This function attempts to receive up to `max-results` datagrams on the socket without blocking. + /// The returned list may contain fewer elements than requested, but never more. + /// + /// This function returns successfully with an empty list when either: + /// - `max-results` is 0, or: + /// - `max-results` is greater than 0, but no results are immediately available. + /// This function never returns `error(would-block)`. + /// + /// # Typical errors + /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `connection-refused`: The connection was refused. (ECONNREFUSED) + /// + /// # References + /// - + /// - + /// - + /// - + /// - + /// - + /// - + /// - + receive: func(max-results: u64) -> result, error-code>; + + /// Create a `pollable` which will resolve once the stream is ready to receive again. + /// + /// Note: this function is here for WASI Preview2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + subscribe: func() -> pollable; + } + + resource outgoing-datagram-stream { + /// Check readiness for sending. This function never blocks. + /// + /// Returns the number of datagrams permitted for the next call to `send`, + /// or an error. Calling `send` with more datagrams than this function has + /// permitted will trap. + /// + /// When this function returns ok(0), the `subscribe` pollable will + /// become ready when this function will report at least ok(1), or an + /// error. + /// + /// Never returns `would-block`. + check-send: func() -> result; + + /// Send messages on the socket. + /// + /// This function attempts to send all provided `datagrams` on the socket without blocking and + /// returns how many messages were actually sent (or queued for sending). This function never + /// returns `error(would-block)`. If none of the datagrams were able to be sent, `ok(0)` is returned. + /// + /// This function semantically behaves the same as iterating the `datagrams` list and sequentially + /// sending each individual datagram until either the end of the list has been reached or the first error occurred. + /// If at least one datagram has been sent successfully, this function never returns an error. + /// + /// If the input list is empty, the function returns `ok(0)`. + /// + /// Each call to `send` must be permitted by a preceding `check-send`. Implementations must trap if + /// either `check-send` was not called or `datagrams` contains more items than `check-send` permitted. + /// + /// # Typical errors + /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-argument`: `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa) + /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The socket is in "connected" mode and `remote-address` is `some` value that does not match the address passed to `stream`. (EISCONN) + /// - `invalid-argument`: The socket is not "connected" and no value for `remote-address` was provided. (EDESTADDRREQ) + /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `connection-refused`: The connection was refused. (ECONNREFUSED) + /// - `datagram-too-large`: The datagram is too large. (EMSGSIZE) + /// + /// # References + /// - + /// - + /// - + /// - + /// - + /// - + /// - + /// - + send: func(datagrams: list) -> result; + + /// Create a `pollable` which will resolve once the stream is ready to send again. + /// + /// Note: this function is here for WASI Preview2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + subscribe: func() -> pollable; + } } diff --git a/wit/deps/sockets/world.wit b/wit/deps/sockets/world.wit index 432b0dc..49ad8d3 100644 --- a/wit/deps/sockets/world.wit +++ b/wit/deps/sockets/world.wit @@ -1,4 +1,4 @@ -package wasi:sockets; +package wasi:sockets@0.2.0-rc-2023-11-10; world imports { import instance-network; From 91f7bba201d3253fae5e65dcbb66b37d26232c96 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Mon, 13 Nov 2023 14:11:15 -0800 Subject: [PATCH 2/5] update dependencies on clocks, random, cli, and io to @0.2.0-rc-2023-11-10 --- wit/proxy.wit | 13 ++++++------- wit/types.wit | 6 +++--- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/wit/proxy.wit b/wit/proxy.wit index 557ab45..770e3f8 100644 --- a/wit/proxy.wit +++ b/wit/proxy.wit @@ -6,21 +6,20 @@ package wasi:http; /// 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; + import wasi:clocks/wall-clock@0.2.0-rc-2023-11-10; + import wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10; + import wasi:random/random@0.2.0-rc-2023-11-10; /// 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; + import wasi:cli/stdout@0.2.0-rc-2023-11-10; + import wasi:cli/stderr@0.2.0-rc-2023-11-10; /// 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; + import wasi:cli/stdin@0.2.0-rc-2023-11-10; /// This is the default handler to use when user code simply wants to make an /// HTTP request (e.g., via `fetch()`). diff --git a/wit/types.wit b/wit/types.wit index c43c2e3..163d580 100644 --- a/wit/types.wit +++ b/wit/types.wit @@ -2,9 +2,9 @@ /// HTTP Requests and Responses, both incoming and outgoing, as well as /// their headers, trailers, and bodies. interface types { - use wasi:clocks/monotonic-clock.{duration}; - use wasi:io/streams.{input-stream, output-stream, error as stream-error}; - use wasi:io/poll.{pollable}; + use wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10.{duration}; + use wasi:io/streams@0.2.0-rc-2023-11-10.{input-stream, output-stream, error as stream-error}; + use wasi:io/poll@0.2.0-rc-2023-11-10.{pollable}; /// This type corresponds to HTTP standard Methods. variant method { From 7dc1e7e33e240980d9aab01318e3305771f2ac9a Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Mon, 13 Nov 2023 14:11:58 -0800 Subject: [PATCH 3/5] set wasi:http package version to 0.2.0-rc-2023-11-10 --- wit/proxy.wit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wit/proxy.wit b/wit/proxy.wit index 770e3f8..453f590 100644 --- a/wit/proxy.wit +++ b/wit/proxy.wit @@ -1,4 +1,4 @@ -package wasi:http; +package wasi:http@0.2.0-rc-2023-11-10; /// The `wasi:http/proxy` world captures a widely-implementable intersection of /// hosts that includes HTTP forward and reverse proxies. Components targeting From 0c3a7dd0fc55dcf086730867b66b5ec7c3e17fec Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Mon, 13 Nov 2023 14:20:26 -0800 Subject: [PATCH 4/5] the wasi:io/streams/error is canonically wasi:io/error/error now --- wit/types.wit | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/wit/types.wit b/wit/types.wit index 163d580..1dd4214 100644 --- a/wit/types.wit +++ b/wit/types.wit @@ -3,7 +3,8 @@ /// their headers, trailers, and bodies. interface types { use wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10.{duration}; - use wasi:io/streams@0.2.0-rc-2023-11-10.{input-stream, output-stream, error as stream-error}; + use wasi:io/streams@0.2.0-rc-2023-11-10.{input-stream, output-stream}; + use wasi:io/error@0.2.0-rc-2023-11-10.{error as io-error}; use wasi:io/poll@0.2.0-rc-2023-11-10.{pollable}; /// This type corresponds to HTTP standard Methods. @@ -94,17 +95,18 @@ interface types { field-size: option } - /// Attempts to extract a http-related `error` from the stream `error` + /// Attempts to extract a http-related `error` from the wasi:io `error` /// provided. /// - /// Stream operations which return `stream-error::last-operation-failed` have - /// a payload with more information about the operation that failed. This - /// payload can be passed through to this function to see if there's - /// http-related information about the error to return. + /// Stream operations which return + /// `wasi:io/stream/stream-error::last-operation-failed` have a payload of + /// type `wasi:io/error/error` with more information about the operation + /// that failed. This payload can be passed through to this function to see + /// if there's http-related information about the error to return. /// - /// Note that this function is fallible because not all stream-related errors - /// are http-related errors. - http-error-code: func(err: borrow) -> option; + /// Note that this function is fallible because not all io-errors are + /// http-related errors. + http-error-code: func(err: borrow) -> option; /// This type enumerates the different kinds of errors that may occur when /// setting or appending to a `fields` resource. From 9fd5db952e37be9eb199847dd3863c031865505e Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Mon, 13 Nov 2023 14:12:16 -0800 Subject: [PATCH 5/5] generate markdown --- proxy.md | 177 +++++++++++++++++++------------------------------------ 1 file changed, 61 insertions(+), 116 deletions(-) diff --git a/proxy.md b/proxy.md index 3011130..c9fd103 100644 --- a/proxy.md +++ b/proxy.md @@ -6,26 +6,26 @@ outgoing HTTP requests.

-

Import interface wasi:clocks/wall-clock

+

Import interface wasi:clocks/wall-clock@0.2.0-rc-2023-11-10

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.

@@ -66,7 +66,7 @@ also known as Unix Time.
  • datetime
  • -

    Import interface wasi:io/poll

    +

    Import interface wasi:io/poll@0.2.0-rc-2023-11-10

    A poll API intended to let users wait for I/O events on multiple handles at once.


    @@ -116,7 +116,7 @@ being reaedy for I/O.

    • list<u32>
    -

    Import interface wasi:clocks/monotonic-clock

    +

    Import interface wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10

    WASI Monotonic Clock is a clock API intended to let users measure elapsed time.

    It is intended to be portable at least between Unix-family platforms and @@ -177,72 +177,7 @@ occured.

    -

    Import interface wasi:clocks/timezone

    -
    -

    Types

    -

    type datetime

    -

    datetime

    -

    -#### `record timezone-display` -

    Information useful for displaying the timezone of a specific datetime.

    -

    This information may vary within a single timezone to reflect daylight -saving time adjustments.

    -
    Record Fields
    -
      -
    • -

      utc-offset: s32

      -

      The number of seconds difference between UTC time and the local -time of the timezone. -

      The returned value will always be less than 86400 which is the -number of seconds in a day (246060).

      -

      In implementations that do not expose an actual time zone, this -should return 0.

      -
    • -
    • -

      name: string

      -

      The abbreviated name of the timezone to display to a user. The name -`UTC` indicates Coordinated Universal Time. Otherwise, this should -reference local standards for the name of the time zone. -

      In implementations that do not expose an actual time zone, this -should be the string UTC.

      -

      In time zones that do not have an applicable name, a formatted -representation of the UTC offset may be returned, such as -04:00.

      -
    • -
    • -

      in-daylight-saving-time: bool

      -

      Whether daylight saving time is active. -

      In implementations that do not expose an actual time zone, this -should return false.

      -
    • -
    -
    -

    Functions

    -

    display: func

    -

    Return information needed to display the given datetime. This includes -the UTC offset, the time zone name, and a flag indicating whether -daylight saving time is active.

    -

    If the timezone cannot be determined for the given datetime, return a -timezone-display for UTC with a utc-offset of 0 and no daylight -saving time.

    -
    Params
    - -
    Return values
    - -

    utc-offset: func

    -

    The same as display, but only return the UTC offset.

    -
    Params
    - -
    Return values
    -
      -
    • s32
    • -
    -

    Import interface wasi:random/random

    +

    Import interface wasi:random/random@0.2.0-rc-2023-11-10

    WASI Random is a random data API.

    It is intended to be portable at least between Unix-family platforms and Windows.

    @@ -275,18 +210,41 @@ represented as a u64.

    • u64
    -

    Import interface wasi:io/streams

    +

    Import interface wasi:io/error@0.2.0-rc-2023-11-10

    +
    +

    Types

    +

    resource error

    +
    +

    Functions

    +

    [method]error.to-debug-string: func

    +

    Returns a string that is suitable to assist humans in debugging +this error.

    +

    WARNING: The returned string should not be consumed mechanically! +It may change across platforms, hosts, or other implementation +details. Parsing this string is a major platform-compatibility +hazard.

    +
    Params
    + +
    Return values
    +
      +
    • string
    • +
    +

    Import interface wasi:io/streams@0.2.0-rc-2023-11-10

    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.


    Types

    -

    type pollable

    -

    pollable

    +

    type error

    +

    error

    -#### `resource error` -

    variant stream-error

    +#### `type pollable` +[`pollable`](#pollable) +

    +#### `variant stream-error`

    An error for input-stream and output-stream operations.

    Variant Cases