diff --git a/wit/deps.lock b/wit/deps.lock index 5cf8421..b55b0ae 100644 --- a/wit/deps.lock +++ b/wit/deps.lock @@ -1,34 +1,34 @@ [cli] url = "https://github.com/WebAssembly/wasi-cli/archive/main.tar.gz" -sha256 = "f21b7722b9225b7ff4f040a67daacd4e412fe2c53c982568a2c25b97fc85d2a2" -sha512 = "ceec4b906f94ba53731dd9428e7e62b964de2284d6aa7cb3436cb856ca89405b0ca8b02e8a8d4575204b9e72dac0e3b29690221ca438324b89e0084117f1700f" +sha256 = "07cb01a72e2d8a763d8e440a84b8a82a30748cf1e626048a4a14d1b8683bae82" +sha512 = "b121e2f3d0d9f93fe3db7a9b4809da459eeb802b89f988b2d4cc4275474e2ae6c4288cc29494c1ef94abede52c00186e539e4b6ab92fea5a535f3b9e6df41f17" [clocks] url = "https://github.com/WebAssembly/wasi-clocks/archive/main.tar.gz" -sha256 = "1ed7e35b3f9738663854f0dd92a95bfadc410ea07170501f5c2fec0cc24e3d57" -sha512 = "ef1e23704a8a8436fd3718593d4c4d8b6d1c64dad3595b7496c0888ca14b725046f2900109800faca1bc8c14f237cdcaca791dba8284e1ad50105ab2d036825b" +sha256 = "a1eef34337ff035526b52617336e507ac448b7114be2d0dfabd6aa789fb7f00d" +sha512 = "3039855063b7d9123e2e07517e7793bfb72c3b7bf90ed026bcef02824aee28d49857acb82fce2ca2f1ba52396c4727d63f62ef6209bdf8a8237fdafe86c21fa4" [filesystem] url = "https://github.com/WebAssembly/wasi-filesystem/archive/main.tar.gz" -sha256 = "dc170645d8aa52f2f94ab8f71093fa0c101e509ed1a07318995dc0395e9d6cf2" -sha512 = "3195a3e0f9ec52c3a91c4b4fde0547694236c7b29bceecb7f38634894fafd809c69ed1c1c9acbf225b2d5d00f5036d70371c9fed121d85028162b202035cabef" +sha256 = "15b77f6c3c15916c66d836af6511db5d3ff39c0f75b17d029ada5bca4962eb84" +sha512 = "4bf96bd9005fc9eea3e329c6014b841efba46b6f2d80ee7048c5777faa8bc43ece4dad33dd606468736f08ce6a48312f6f609a9a53069d1c62a21d63ea22ea13" [io] url = "https://github.com/WebAssembly/wasi-io/archive/main.tar.gz" -sha256 = "6e18239b0e20d1a3e6343cb961ebfd2c663ba7feb4c1aa3744b756fbdd1fb5b8" -sha512 = "53169b6e4fba0b2cf5fcf808f76e7fbb7cabb6ed66ab53f77d0966e7448312ccbe8571880ef4fc2ee86fbd6ba19bc48d46e10d22dcac6c51d217e8d7127c32db" +sha256 = "8f927d374d71c7bd79b53f94631d9b3ddbc7093ae97bed018eeb32486e849b4b" +sha512 = "63959ac670bba0f008238f77d6fe0a1fc206e77c2df6e33323c62751ef424571ee8ac5951ca6a2d0c94c4173884032b97447394a302bd4c85c23329df58b150d" [poll] url = "https://github.com/WebAssembly/wasi-poll/archive/main.tar.gz" -sha256 = "d4c27124f4c137eb538b5c92ba5858ed9042e11b24a2eef85d14becd0b7f55de" -sha512 = "422c01b273b4b1377ece6f2e4ba0dfc609ca8ef30a3e0be0e172e1303fcf7b3ca4c470f4dea6c51bdf114b0f5c871ebc4934dfe3bf217d66ea689748df2b1e55" +sha256 = "1a81838e1ae108f28ac15716cd20078db05405175ece4e1d739c252cfde6d3ae" +sha512 = "db438afd93cb4fb6bac87d7b9d18774bca14210cc82b9077836ec4f55c9679262cb2889cb98cf624346ef1befbedf02ded51bd63a4c4f42103921b70d2502ce1" [random] url = "https://github.com/WebAssembly/wasi-random/archive/main.tar.gz" -sha256 = "9b622463e597b9ca94f41e4eaae589a77be38f71b4723142b60246ffed8eaae4" -sha512 = "21f03ca1e595b80d7ced522de1a47446526b49b900e2fb26fcbf410ce6aa267dbf247aebf3fbfa8123b46fc1a828e2fd64fb1e0198b40161a3257e8d86fd4546" +sha256 = "2f718c9909fcc95dc8d7f9fc3a17a30d7da715eaa9fa70af3c89f0f9439abc8a" +sha512 = "7580d79d0f01d4fb65ff85f76d52d420fdbc7406959a34c2b1cad4398dc34d2610f8b6b4e22bcea9db2f3d70242d356e961d843bfc4d2c4956c4f5e5ca3811f9" [sockets] url = "https://github.com/WebAssembly/wasi-sockets/archive/main.tar.gz" -sha256 = "871c211b12d87a5da87c42353338b652260840897efcd37e2afba3b9290058fc" -sha512 = "e436a5ff3145ca85d702a086499c03488523483dd3addc8d71e4946e9c186355291551bb6d38b157173836fcc318182403e6dba970de4512f6cfb3374ccad6b9" +sha256 = "85b872b5af12d3010c20a5f4bf6d334a09c1a42ef7f7703c2949c0598ed8ebee" +sha512 = "238bbeea140eba0774cf5ce5abfdc93f03617a2fc30b497e104cc6a23fdf7bd496c61d3c962ca6718255ea5c96a9277bddae1fc4a6b6f7544de2be2649752eac" diff --git a/wit/deps/cli/command.wit b/wit/deps/cli/command.wit index 13675f1..d28f5f6 100644 --- a/wit/deps/cli/command.wit +++ b/wit/deps/cli/command.wit @@ -1,27 +1,7 @@ package wasi:cli world command { - import wasi:clocks/wall-clock - import wasi:clocks/monotonic-clock - import wasi:clocks/timezone - import wasi:filesystem/types - import wasi:sockets/instance-network - import wasi:sockets/ip-name-lookup - import wasi:sockets/network - import wasi:sockets/tcp-create-socket - import wasi:sockets/tcp - import wasi:sockets/udp-create-socket - import wasi:sockets/udp - import wasi:random/random - import wasi:random/insecure - import wasi:random/insecure-seed - import wasi:poll/poll - import wasi:io/streams - import environment - import preopens - import exit - import stdin - import stdout - import stderr + include reactor + export run } diff --git a/wit/deps/cli/environment.wit b/wit/deps/cli/environment.wit index 65471e8..36790fe 100644 --- a/wit/deps/cli/environment.wit +++ b/wit/deps/cli/environment.wit @@ -11,4 +11,8 @@ interface environment { /// Get the POSIX-style arguments to the program. get-arguments: func() -> list + + /// Return a path that programs should use as their initial current working + /// directory, interpreting `.` as shorthand for this. + initial-cwd: func() -> option } diff --git a/wit/deps/cli/preopens.wit b/wit/deps/cli/preopens.wit deleted file mode 100644 index 6371028..0000000 --- a/wit/deps/cli/preopens.wit +++ /dev/null @@ -1,11 +0,0 @@ -interface preopens { - use wasi:filesystem/types.{descriptor} - use wasi:io/streams.{input-stream, output-stream} - - /// Return the set of of preopened directories, and their path. - get-directories: func() -> list> - - /// Return a path that programs should use as their initial current working - /// directory, interpreting `.` as shorthand for this. - initial-cwd: func() -> option -} diff --git a/wit/deps/cli/reactor.wit b/wit/deps/cli/reactor.wit new file mode 100644 index 0000000..30ff962 --- /dev/null +++ b/wit/deps/cli/reactor.wit @@ -0,0 +1,30 @@ +world reactor { + import wasi:clocks/wall-clock + import wasi:clocks/monotonic-clock + import wasi:clocks/timezone + import wasi:filesystem/types + import wasi:filesystem/preopens + import wasi:sockets/instance-network + import wasi:sockets/ip-name-lookup + import wasi:sockets/network + import wasi:sockets/tcp-create-socket + import wasi:sockets/tcp + import wasi:sockets/udp-create-socket + import wasi:sockets/udp + import wasi:random/random + import wasi:random/insecure + import wasi:random/insecure-seed + import wasi:poll/poll + import wasi:io/streams + import environment + import exit + import stdin + import stdout + import stderr + import terminal-input + import terminal-output + import terminal-stdin + import terminal-stdout + import terminal-stderr +} + diff --git a/wit/deps/clocks/timezone.wit b/wit/deps/clocks/timezone.wit index 6628308..a872bff 100644 --- a/wit/deps/clocks/timezone.wit +++ b/wit/deps/clocks/timezone.wit @@ -1,15 +1,6 @@ interface timezone { use wall-clock.{datetime} - /// A timezone. - /// - /// In timezones that recognize daylight saving time, also known as daylight - /// time and summer time, the information returned from the functions varies - /// over time to reflect these adjustments. - /// - /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). - type timezone = u32 - /// 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. @@ -17,14 +8,10 @@ interface timezone { /// 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(this: timezone, when: datetime) -> timezone-display + display: func(when: datetime) -> timezone-display /// The same as `display`, but only return the UTC offset. - utc-offset: func(this: timezone, when: datetime) -> s32 - - /// Dispose of the specified input-stream, after which it may no longer - /// be used. - drop-timezone: func(this: timezone) + utc-offset: func(when: datetime) -> s32 /// Information useful for displaying the timezone of a specific `datetime`. /// diff --git a/wit/deps/clocks/world.wit b/wit/deps/clocks/world.wit index 291a9c7..5c2dd41 100644 --- a/wit/deps/clocks/world.wit +++ b/wit/deps/clocks/world.wit @@ -1,6 +1,6 @@ package wasi:clocks -world example-world { +world imports { import monotonic-clock import wall-clock import timezone diff --git a/wit/deps/filesystem/types.wit b/wit/deps/filesystem/types.wit index a437f22..c7e817d 100644 --- a/wit/deps/filesystem/types.wit +++ b/wit/deps/filesystem/types.wit @@ -17,6 +17,11 @@ /// `..` and symbolic link steps, reaches a directory outside of the base /// directory, or reaches a symlink to an absolute or rooted path in the /// underlying filesystem, the function fails with `error-code::not-permitted`. +/// +/// For more information about WASI path resolution and sandboxing, see +/// [WASI filesystem path resolution]. +/// +/// [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} use wasi:clocks/wall-clock.{datetime} @@ -55,14 +60,6 @@ interface types { read, /// Write mode: Data can be written to. write, - /// Requests non-blocking operation. - /// - /// When this flag is enabled, functions may return immediately with an - /// `error-code::would-block` error code in situations where they would - /// otherwise block. However, this non-blocking behavior is not - /// required. Implementations are permitted to ignore this flag and - /// block. This is similar to `O_NONBLOCK` in POSIX. - non-blocking, /// Request that writes be performed according to synchronized I/O file /// integrity completion. The data stored in the file and the file's /// metadata are synchronized. This is similar to `O_SYNC` in POSIX. @@ -110,11 +107,20 @@ interface types { /// length in bytes of the pathname contained in the symbolic link. size: filesize, /// Last data access timestamp. - data-access-timestamp: datetime, + /// + /// If the `option` is none, the platform doesn't maintain an access + /// timestamp for this file. + data-access-timestamp: option, /// Last data modification timestamp. - data-modification-timestamp: datetime, - /// Last file status change timestamp. - status-change-timestamp: datetime, + /// + /// If the `option` is none, the platform doesn't maintain a + /// modification timestamp for this file. + data-modification-timestamp: option, + /// Last file status-change timestamp. + /// + /// If the `option` is none, the platform doesn't maintain a + /// status-change timestamp for this file. + status-change-timestamp: option, } /// Flags determining the method of how paths are resolved. @@ -378,15 +384,6 @@ interface types { /// from `fdstat_get` in earlier versions of WASI. get-type: func(this: descriptor) -> result - /// Set status flags associated with a descriptor. - /// - /// This function may only change the `non-blocking` flag. - /// - /// Note: This is similar to `fcntl(fd, F_SETFL, flags)` in POSIX. - /// - /// Note: This was called `fd_fdstat_set_flags` in earlier versions of WASI. - set-flags: func(this: descriptor, %flags: descriptor-flags) -> result<_, error-code> - /// Adjust the size of an open file. If this increases the file's size, the /// extra bytes are filled with zeros. /// diff --git a/wit/deps/filesystem/world.wit b/wit/deps/filesystem/world.wit index b51f484..5fa7eaf 100644 --- a/wit/deps/filesystem/world.wit +++ b/wit/deps/filesystem/world.wit @@ -1,6 +1,6 @@ package wasi:filesystem -world example-world { +world imports { import types import preopens } diff --git a/wit/deps/io/streams.wit b/wit/deps/io/streams.wit index de8694b..e2631f6 100644 --- a/wit/deps/io/streams.wit +++ b/wit/deps/io/streams.wit @@ -1,3 +1,5 @@ +package wasi:io + /// WASI I/O is an I/O abstraction API which is currently focused on providing /// stream types. /// @@ -6,10 +8,6 @@ interface streams { use wasi:poll/poll.{pollable} - /// An error type returned from a stream operation. Currently this - /// doesn't provide any additional information. - record stream-error {} - /// Streams provide a sequence of data and then end; once they end, they /// no longer provide any further data. /// @@ -19,22 +17,22 @@ interface streams { enum stream-status { /// The stream is open and may produce further data. open, - /// The stream has ended and will not produce any further data. + /// When reading, this indicates that the stream will not produce + /// further data. + /// When writing, this indicates that the stream will no longer be read. + /// Further writes are still permitted. ended, } /// An input bytestream. In the future, this will be replaced by handle /// types. /// - /// This conceptually represents a `stream`. It's temporary - /// scaffolding until component-model's async features are ready. - /// /// `input-stream`s are *non-blocking* to the extent practical on underlying /// platforms. I/O operations always return promptly; if fewer bytes are /// promptly available than requested, they return the number of bytes promptly /// available, which could even be zero. To wait for data to be available, /// use the `subscribe-to-input-stream` function to obtain a `pollable` which - /// can be polled for using `wasi_poll`. + /// can be polled for using `wasi:poll/poll.poll_oneoff`. /// /// And at present, it is a `u32` instead of being an actual handle, until /// the wit-bindgen implementation of handles and resources is ready. @@ -42,39 +40,41 @@ interface streams { /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). type input-stream = u32 - /// Read bytes from a stream. + /// Perform a non-blocking read from the stream. /// /// This function returns a list of bytes containing the data that was - /// read, along with a `stream-status` which indicates whether the end of - /// the stream was reached. The returned list will contain up to `len` - /// bytes; it may return fewer than requested, but not more. - /// - /// Once a stream has reached the end, subsequent calls to read or - /// `skip` will always report end-of-stream rather than producing more + /// read, along with a `stream-status` which, indicates whether further + /// reads are expected to produce data. The returned list will contain up to + /// `len` bytes; it may return fewer than requested, but not more. An + /// empty list and `stream-status:open` indicates no more data is + /// available at this time, and that the pollable given by + /// `subscribe-to-input-stream` will be ready when more data is available. + /// + /// Once a stream has reached the end, subsequent calls to `read` or + /// `skip` will always report `stream-status:ended` rather than producing more /// data. /// - /// If `len` is 0, it represents a request to read 0 bytes, which should - /// always succeed, assuming the stream hasn't reached its end yet, and - /// return an empty list. + /// When the caller gives a `len` of 0, it represents a request to read 0 + /// bytes. This read should always succeed and return an empty list and + /// the current `stream-status`. /// - /// The len here is a `u64`, but some callees may not be able to allocate - /// a buffer as large as that would imply. - /// FIXME: describe what happens if allocation fails. + /// The `len` parameter is a `u64`, which could represent a list of u8 which + /// is not possible to allocate in wasm32, or not desirable to allocate as + /// as a return value by the callee. The callee may return a list of bytes + /// less than `len` in size while more bytes are available for reading. read: func( this: input-stream, /// The maximum number of bytes to read len: u64 - ) -> result, stream-status>, stream-error> + ) -> result, stream-status>> - /// Read bytes from a stream, with blocking. - /// - /// This is similar to `read`, except that it blocks until at least one - /// byte can be read. + /// Read bytes from a stream, after blocking until at least one byte can + /// be read. Except for blocking, identical to `read`. blocking-read: func( this: input-stream, /// The maximum number of bytes to read len: u64 - ) -> result, stream-status>, stream-error> + ) -> result, stream-status>> /// Skip bytes from a stream. /// @@ -92,39 +92,41 @@ interface streams { this: input-stream, /// The maximum number of bytes to skip. len: u64, - ) -> result, stream-error> + ) -> result> - /// Skip bytes from a stream, with blocking. - /// - /// This is similar to `skip`, except that it blocks until at least one - /// byte can be consumed. + /// Skip bytes from a stream, after blocking until at least one byte + /// can be skipped. Except for blocking behavior, identical to `skip`. blocking-skip: func( this: input-stream, /// The maximum number of bytes to skip. len: u64, - ) -> result, stream-error> + ) -> result> /// Create a `pollable` which will resolve once either the specified stream /// has bytes available to read or the other end of the stream has been /// closed. + /// The created `pollable` is a child resource of the `input-stream`. + /// Implementations may trap if the `input-stream` is dropped before + /// all derived `pollable`s created with this function are dropped. subscribe-to-input-stream: func(this: input-stream) -> pollable /// Dispose of the specified `input-stream`, after which it may no longer /// be used. + /// Implementations may trap if this `input-stream` is dropped while child + /// `pollable` resources are still alive. + /// After this `input-stream` is dropped, implementations may report any + /// corresponding `output-stream` has `stream-state.closed`. drop-input-stream: func(this: input-stream) /// An output bytestream. In the future, this will be replaced by handle /// types. /// - /// This conceptually represents a `stream`. It's temporary - /// scaffolding until component-model's async features are ready. - /// /// `output-stream`s are *non-blocking* to the extent practical on /// underlying platforms. Except where specified otherwise, I/O operations also /// always return promptly, after the number of bytes that can be written /// promptly, which could even be zero. To wait for the stream to be ready to /// accept data, the `subscribe-to-output-stream` function to obtain a - /// `pollable` which can be polled for using `wasi_poll`. + /// `pollable` which can be polled for using `wasi:poll`. /// /// And at present, it is a `u32` instead of being an actual handle, until /// the wit-bindgen implementation of handles and resources is ready. @@ -132,45 +134,115 @@ interface streams { /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). type output-stream = u32 - /// Write bytes to a stream. + /// An error for output-stream operations. + /// + /// Contrary to input-streams, a closed output-stream is reported using + /// an error. + enum write-error { + /// The last operation (a write or flush) failed before completion. + last-operation-failed, + /// The stream is closed: no more input will be accepted by the + /// stream. A closed output-stream will return this error on all + /// future operations. + closed + } + /// Check readiness for writing. This function never blocks. + /// + /// Returns the number of bytes permitted for the next call to `write`, + /// or an error. Calling `write` with more bytes than this function has + /// permitted will trap. + /// + /// When this function returns 0 bytes, the `subscribe-to-output-stream` + /// pollable will become ready when this function will report at least + /// 1 byte, or an error. + check-write: func( + this: output-stream + ) -> result + + /// Perform a write. This function never blocks. + /// + /// Precondition: check-write gave permit of Ok(n) and contents has a + /// length of less than or equal to n. Otherwise, this function will trap. /// - /// This function returns a `u64` indicating the number of bytes from - /// `buf` that were written; it may be less than the full list. + /// returns Err(closed) without writing if the stream has closed since + /// the last call to check-write provided a permit. write: func( this: output-stream, - /// Data to write - buf: list - ) -> result + contents: list + ) -> result<_, write-error> - /// Write bytes to a stream, with blocking. + /// Perform a write of up to 4096 bytes, and then flush the stream. Block + /// until all of these operations are complete, or an error occurs. /// - /// This is similar to `write`, except that it blocks until at least one - /// byte can be written. - blocking-write: func( - this: output-stream, - /// Data to write - buf: list - ) -> result + /// This is a convenience wrapper around the use of `check-write`, + /// `subscribe-to-output-stream`, `write`, and `flush`, and is implemented + /// with the following pseudo-code: + /// + /// ```text + /// let pollable = subscribe-to-output-stream(this); + /// while !contents.is_empty() { + /// // Wait for the stream to become writable + /// poll-oneoff(pollable); + /// let Ok(n) = check-write(this); // eliding error handling + /// let len = min(n, contents.len()); + /// let (chunk, rest) = contents.split_at(len); + /// write(this, chunk); // eliding error handling + /// contents = rest; + /// } + /// flush(this); + /// // Wait for completion of `flush` + /// poll-oneoff(pollable); + /// // Check for any errors that arose during `flush` + /// let _ = check-write(this); // eliding error handling + /// ``` + blocking-write-and-flush: func( + this: output-stream, + contents: list + ) -> result<_, write-error> - /// Write multiple zero bytes to a stream. + /// Request to flush buffered output. This function never blocks. /// - /// This function returns a `u64` indicating the number of zero bytes - /// that were written; it may be less than `len`. - write-zeroes: func( + /// This tells the output-stream that the caller intends any buffered + /// output to be flushed. the output which is expected to be flushed + /// is all that has been passed to `write` prior to this call. + /// + /// Upon calling this function, the `output-stream` will not accept any + /// writes (`check-write` will return `ok(0)`) until the flush has + /// completed. The `subscribe-to-output-stream` pollable will become ready + /// when the flush has completed and the stream can accept more writes. + flush: func( this: output-stream, - /// The number of zero bytes to write - len: u64 - ) -> result + ) -> result<_, write-error> + + /// Request to flush buffered output, and block until flush completes + /// and stream is ready for writing again. + blocking-flush: func( + this: output-stream, + ) -> result<_, write-error> - /// Write multiple zero bytes to a stream, with blocking. + /// Create a `pollable` which will resolve once the output-stream + /// is ready for more writing, or an error has occured. When this + /// pollable is ready, `check-write` will return `ok(n)` with n>0, or an + /// error. + /// + /// If the stream is closed, this pollable is always ready immediately. /// - /// This is similar to `write-zeroes`, except that it blocks until at least - /// one byte can be written. - blocking-write-zeroes: func( + /// The created `pollable` is a child resource of the `output-stream`. + /// Implementations may trap if the `output-stream` is dropped before + /// all derived `pollable`s created with this function are dropped. + subscribe-to-output-stream: func(this: output-stream) -> pollable + + /// Write zeroes to a stream. + /// + /// this should be used precisely like `write` with the exact same + /// preconditions (must use check-write first), but instead of + /// passing a list of bytes, you simply pass the number of zero-bytes + /// that should be written. + write-zeroes: func( this: output-stream, - /// The number of zero bytes to write + /// The number of zero-bytes to write len: u64 - ) -> result + ) -> result<_, write-error> /// Read from one stream and write to another. /// @@ -185,7 +257,7 @@ interface streams { src: input-stream, /// The number of bytes to splice len: u64, - ) -> result, stream-error> + ) -> result> /// Read from one stream and write to another, with blocking. /// @@ -197,7 +269,7 @@ interface streams { src: input-stream, /// The number of bytes to splice len: u64, - ) -> result, stream-error> + ) -> result> /// Forward the entire contents of an input stream to an output stream. /// @@ -209,18 +281,20 @@ interface streams { /// of the input stream is seen and all the data has been written to /// the output stream. /// - /// This function returns the number of bytes transferred. + /// This function returns the number of bytes transferred, and the status of + /// the output stream. forward: func( this: output-stream, /// The stream to read from src: input-stream - ) -> result + ) -> result> - /// Create a `pollable` which will resolve once either the specified stream - /// is ready to accept bytes or the other end of the stream has been closed. - subscribe-to-output-stream: func(this: output-stream) -> pollable /// Dispose of the specified `output-stream`, after which it may no longer /// be used. + /// Implementations may trap if this `output-stream` is dropped while + /// child `pollable` resources are still alive. + /// After this `output-stream` is dropped, implementations may report any + /// corresponding `input-stream` has `stream-state.closed`. drop-output-stream: func(this: output-stream) } diff --git a/wit/deps/io/world.wit b/wit/deps/io/world.wit index b7f6253..e9ca233 100644 --- a/wit/deps/io/world.wit +++ b/wit/deps/io/world.wit @@ -1,5 +1,5 @@ package wasi:io -world example-world { +world imports { import streams } diff --git a/wit/deps/poll/world.wit b/wit/deps/poll/world.wit index d08cadc..bea9f3a 100644 --- a/wit/deps/poll/world.wit +++ b/wit/deps/poll/world.wit @@ -1,5 +1,5 @@ package wasi:poll -world example-world { +world imports { import poll } diff --git a/wit/deps/random/world.wit b/wit/deps/random/world.wit index 3c3d165..41dc9ed 100644 --- a/wit/deps/random/world.wit +++ b/wit/deps/random/world.wit @@ -1,6 +1,6 @@ package wasi:random -world example-world { +world imports { import random import insecure import insecure-seed diff --git a/wit/deps/sockets/ip-name-lookup.wit b/wit/deps/sockets/ip-name-lookup.wit index f15d19d..f998aae 100644 --- a/wit/deps/sockets/ip-name-lookup.wit +++ b/wit/deps/sockets/ip-name-lookup.wit @@ -1,6 +1,6 @@ interface ip-name-lookup { - use wasi:poll/poll.{pollable} + use wasi:io/poll.{pollable} use network.{network, error-code, ip-address, ip-address-family} @@ -34,36 +34,28 @@ interface ip-name-lookup { /// - /// - /// - - resolve-addresses: func(network: network, name: string, address-family: option, include-unavailable: bool) -> result - - - - type resolve-address-stream = u32 - - /// Returns the next address from the resolver. - /// - /// This function should be called multiple times. On each call, it will - /// return the next address in connection order preference. If all - /// addresses have been exhausted, this function returns `none`. - /// After which, you should release the stream with `drop-resolve-address-stream`. - /// - /// This function never returns IPv4-mapped IPv6 addresses. - /// - /// # Typical errors - /// - `name-unresolvable`: Name does not exist or has no suitable associated IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY) - /// - `temporary-resolver-failure`: A temporary failure in name resolution occurred. (EAI_AGAIN) - /// - `permanent-resolver-failure`: A permanent failure in name resolution occurred. (EAI_FAIL) - /// - `would-block`: A result is not available yet. (EWOULDBLOCK, EAGAIN) - resolve-next-address: func(this: resolve-address-stream) -> result, error-code> - - /// Dispose of the specified `resolve-address-stream`, after which it may no longer be used. - /// - /// Note: this function is scheduled to be removed when Resources are natively supported in Wit. - drop-resolve-address-stream: func(this: resolve-address-stream) - - /// Create a `pollable` which will resolve once the stream is ready for I/O. - /// - /// Note: this function is here for WASI Preview2 only. - /// It's planned to be removed when `future` is natively supported in Preview3. - subscribe: func(this: resolve-address-stream) -> pollable + resolve-addresses: func(network: borrow, name: string, address-family: option, include-unavailable: bool) -> 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> + + /// 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 2d09bcb..8214eaa 100644 --- a/wit/deps/sockets/network.wit +++ b/wit/deps/sockets/network.wit @@ -1,17 +1,9 @@ interface network { - /// An opaque resource that represents access to (a subset of) the network. + /// An opaque resource that represents access to (a subset of) the network. /// This enables context-based security for networking. /// There is no need for this to map 1:1 to a physical network interface. - /// - /// FYI, In the future this will be replaced by handle types. - type network = u32 - - /// Dispose of the specified `network`, after which it may no longer be used. - /// - /// Note: this function is scheduled to be removed when Resources are natively supported in Wit. - drop-network: func(this: network) - + resource network /// Error codes. /// @@ -183,4 +175,4 @@ interface network { ipv6(ipv6-socket-address), } -} \ No newline at end of file +} diff --git a/wit/deps/sockets/tcp.wit b/wit/deps/sockets/tcp.wit index 4edb1db..175626c 100644 --- a/wit/deps/sockets/tcp.wit +++ b/wit/deps/sockets/tcp.wit @@ -1,13 +1,9 @@ interface tcp { use wasi:io/streams.{input-stream, output-stream} - use wasi:poll/poll.{pollable} + use wasi:io/poll.{pollable} use network.{network, error-code, ip-socket-address, ip-address-family} - /// A TCP socket handle. - type tcp-socket = u32 - - enum shutdown-type { /// Similar to `SHUT_RD` in POSIX. receive, @@ -20,238 +16,234 @@ interface tcp { } - /// 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(this: tcp-socket, network: network, local-address: ip-socket-address) -> result<_, error-code> - finish-bind: func(this: tcp-socket) -> result<_, error-code> - - /// Connect to a remote endpoint. - /// - /// On success: - /// - the socket is transitioned into the Connection state - /// - a pair of streams is returned that can be used to read & write to the connection - /// - /// # 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(this: tcp-socket, network: network, remote-address: ip-socket-address) -> result<_, error-code> - finish-connect: func(this: tcp-socket) -> result, error-code> - - /// Start listening for new connections. - /// - /// Transitions the socket into the Listener state. - /// - /// 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(this: tcp-socket) -> result<_, error-code> - finish-listen: func(this: tcp-socket) -> result<_, error-code> - - /// Accept a new client socket. - /// - /// The returned socket is bound and in the Connection state. - /// - /// On success, this function returns the newly accepted client socket along with - /// a pair of streams that can be used to read & write to the connection. - /// - /// # Typical errors - /// - `not-listening`: Socket is not in the Listener state. (EINVAL) - /// - `would-block`: No pending connections at the moment. (EWOULDBLOCK, EAGAIN) - /// - /// Host implementations must skip over transient errors returned by the native accept syscall. - /// - /// # References - /// - - /// - - /// - - /// - - accept: func(this: tcp-socket) -> result, error-code> - - /// Get the bound local address. - /// - /// # Typical errors - /// - `not-bound`: The socket is not bound to any local address. - /// - /// # References - /// - - /// - - /// - - /// - - local-address: func(this: tcp-socket) -> result - - /// Get the bound remote address. - /// - /// # Typical errors - /// - `not-connected`: The socket is not connected to a remote address. (ENOTCONN) - /// - /// # References - /// - - /// - - /// - - /// - - remote-address: func(this: tcp-socket) -> result - - /// Whether this is a IPv4 or IPv6 socket. - /// - /// Equivalent to the SO_DOMAIN socket option. - address-family: func(this: tcp-socket) -> ip-address-family + /// 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(this: tcp-socket) -> result - set-ipv6-only: func(this: tcp-socket, value: bool) -> result<_, error-code> - - /// Hints the desired listen queue size. Implementations are free to ignore this. - /// - /// # Typical errors - /// - `already-connected`: (set) The socket is already in the Connection state. - /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - set-listen-backlog-size: func(this: tcp-socket, value: u64) -> result<_, error-code> - - /// Equivalent to the SO_KEEPALIVE socket option. - /// - /// # Typical errors - /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - keep-alive: func(this: tcp-socket) -> result - set-keep-alive: func(this: tcp-socket, value: bool) -> result<_, error-code> - - /// Equivalent to the TCP_NODELAY socket option. - /// - /// # Typical errors - /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) - no-delay: func(this: tcp-socket) -> result - set-no-delay: func(this: tcp-socket, value: bool) -> result<_, error-code> + /// 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(this: tcp-socket) -> result - set-unicast-hop-limit: func(this: tcp-socket, value: u8) -> result<_, error-code> - - /// The kernel buffer space reserved for sends/receives on this socket. - /// - /// Note #1: an implementation may choose to cap or round the buffer size when setting the value. - /// In other words, after setting a value, reading the same setting back may return a different value. - /// - /// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of - /// actual data to be sent/received by the application, because the kernel might also use the buffer space - /// for internal metadata structures. - /// - /// 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(this: tcp-socket) -> result - set-receive-buffer-size: func(this: tcp-socket, value: u64) -> result<_, error-code> - send-buffer-size: func(this: tcp-socket) -> result - set-send-buffer-size: func(this: tcp-socket, value: u64) -> result<_, error-code> - - /// Create a `pollable` which will resolve once the socket is ready for I/O. - /// - /// Note: this function is here for WASI Preview2 only. - /// It's planned to be removed when `future` is natively supported in Preview3. - subscribe: func(this: tcp-socket) -> pollable - - /// 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(this: tcp-socket, shutdown-type: shutdown-type) -> result<_, error-code> - - /// Dispose of the specified `tcp-socket`, after which it may no longer be used. - /// - /// Similar to the POSIX `close` function. - /// - /// Note: this function is scheduled to be removed when Resources are natively supported in Wit. - drop-tcp-socket: func(this: tcp-socket) + /// 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> + } } diff --git a/wit/deps/sockets/udp.wit b/wit/deps/sockets/udp.wit index 948ed58..01e5b95 100644 --- a/wit/deps/sockets/udp.wit +++ b/wit/deps/sockets/udp.wit @@ -1,13 +1,9 @@ interface udp { - use wasi:poll/poll.{pollable} + use wasi:io/poll.{pollable} use network.{network, error-code, ip-socket-address, ip-address-family} - /// A UDP socket handle. - type udp-socket = u32 - - record datagram { data: list, // Theoretical max size: ~64 KiB. In practice, typically less than 1500 bytes. remote-address: ip-socket-address, @@ -22,201 +18,197 @@ interface udp { - /// 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(this: udp-socket, network: network, local-address: ip-socket-address) -> result<_, error-code> - finish-bind: func(this: udp-socket) -> result<_, error-code> - - /// Set the destination address. - /// - /// The local-address is updated based on the best network path to `remote-address`. - /// - /// When a destination address is set: - /// - all receive operations will only return datagrams sent from the provided `remote-address`. - /// - the `send` function can only be used to send to this destination. - /// - /// Note that this function does not generate any network traffic and the peer is not aware of this "connection". - /// - /// 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(this: udp-socket, network: network, remote-address: ip-socket-address) -> result<_, error-code> - finish-connect: func(this: udp-socket) -> 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(this: udp-socket, 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(this: udp-socket, 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(this: udp-socket) -> 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(this: udp-socket) -> result - - /// Whether this is a IPv4 or IPv6 socket. - /// - /// Equivalent to the SO_DOMAIN socket option. - address-family: func(this: udp-socket) -> 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(this: udp-socket) -> result - set-ipv6-only: func(this: udp-socket, value: bool) -> result<_, error-code> - - /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. - /// - /// # Typical errors - /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) - unicast-hop-limit: func(this: udp-socket) -> result - set-unicast-hop-limit: func(this: udp-socket, value: u8) -> result<_, error-code> - - /// The kernel buffer space reserved for sends/receives on this socket. - /// - /// Note #1: an implementation may choose to cap or round the buffer size when setting the value. - /// In other words, after setting a value, reading the same setting back may return a different value. - /// - /// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of - /// actual data to be sent/received by the application, because the kernel might also use the buffer space - /// for internal metadata structures. - /// - /// Fails when this socket is in the Listening state. - /// - /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. - /// - /// # Typical errors - /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) - receive-buffer-size: func(this: udp-socket) -> result - set-receive-buffer-size: func(this: udp-socket, value: u64) -> result<_, error-code> - send-buffer-size: func(this: udp-socket) -> result - set-send-buffer-size: func(this: udp-socket, value: u64) -> result<_, error-code> - - /// Create a `pollable` which will resolve once the socket is ready for I/O. - /// - /// Note: this function is here for WASI Preview2 only. - /// It's planned to be removed when `future` is natively supported in Preview3. - subscribe: func(this: udp-socket) -> pollable - - /// Dispose of the specified `udp-socket`, after which it may no longer be used. - /// - /// Note: this function is scheduled to be removed when Resources are natively supported in Wit. - drop-udp-socket: func(this: udp-socket) + /// 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 + } } diff --git a/wit/deps/sockets/world.wit b/wit/deps/sockets/world.wit index ca23d86..12f3c28 100644 --- a/wit/deps/sockets/world.wit +++ b/wit/deps/sockets/world.wit @@ -1,6 +1,6 @@ package wasi:sockets -world example-world { +world imports { import instance-network import network import udp diff --git a/wit/proxy.wit b/wit/proxy.wit index db28ba4..6987cb8 100644 --- a/wit/proxy.wit +++ b/wit/proxy.wit @@ -6,7 +6,7 @@ package wasi:http // outgoing HTTP requests. world proxy { // HTTP proxies have access to time and randomness. - include wasi:clocks/example-world + include wasi:clocks/imports import wasi:random/random // Proxies have standard output and error streams which are expected to