diff --git a/Cargo.lock b/Cargo.lock index cc58972..840c21c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -736,7 +736,7 @@ checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] @@ -1233,7 +1233,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.48.0", ] @@ -1883,7 +1883,7 @@ version = "3.1.1" dependencies = [ "futures", "once_cell", - "wit-bindgen", + "wasi 0.13.1+wasi-0.2.0", ] [[package]] @@ -1919,6 +1919,7 @@ dependencies = [ "spin-macro", "thiserror", "tokio", + "wasi 0.13.1+wasi-0.2.0", "wasmtime", "wasmtime-wasi", "wasmtime-wasi-http", @@ -2301,6 +2302,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.13.1+wasi-0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f43d1c36145feb89a3e61aa0ba3e582d976a8ab77f1474aa0adb80800fe0cf8" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasi-common" version = "18.0.1" @@ -3159,6 +3169,15 @@ dependencies = [ "wit-parser 0.13.2", ] +[[package]] +name = "wit-bindgen-rt" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0780cf7046630ed70f689a098cd8d56c5c3b22f2a7379bbdb088879963ff96" +dependencies = [ + "bitflags 2.4.2", +] + [[package]] name = "wit-bindgen-rust" version = "0.16.0" diff --git a/Cargo.toml b/Cargo.toml index e0915d4..e2cfc5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ bytes = "1" hyperium = { package = "http", version = "1.0.0" } serde_json = { version = "1.0.96", optional = true } serde = { version = "1.0.163", optional = true } +wasi = { workspace = true } [features] default = ["export-sdk-language", "json"] @@ -93,3 +94,5 @@ homepage = "https://spinframework.dev/rust-components" wit-bindgen = "0.16.0" futures = "0.3.28" once_cell = "1.18.0" +# Pin to the last version that targeted WASI 0.2.0 +wasi = "=0.13.1" diff --git a/crates/executor/Cargo.toml b/crates/executor/Cargo.toml index 210926b..8ae8d88 100644 --- a/crates/executor/Cargo.toml +++ b/crates/executor/Cargo.toml @@ -17,4 +17,4 @@ Spin SDK async executor [dependencies] once_cell = "1.18.0" futures = "0.3.28" -wit-bindgen = { workspace = true } \ No newline at end of file +wasi = { workspace = true } \ No newline at end of file diff --git a/crates/executor/io.wit b/crates/executor/io.wit deleted file mode 100644 index 1f7f774..0000000 --- a/crates/executor/io.wit +++ /dev/null @@ -1,342 +0,0 @@ -package wasi:io@0.2.0; - -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; - } -} - - -/// A poll API intended to let users wait for I/O events on multiple handles -/// at once. -interface poll { - /// `pollable` represents a single I/O event which may be ready, or not. - resource pollable { - - /// Return the readiness of a pollable. This function never blocks. - /// - /// Returns `true` when the pollable is ready, and `false` otherwise. - ready: func() -> bool; - - /// `block` returns immediately if the pollable is ready, and otherwise - /// blocks until ready. - /// - /// This function is equivalent to calling `poll.poll` on a list - /// containing only this pollable. - block: func(); - } - - /// Poll for completion on a set of pollables. - /// - /// This function takes a list of pollables, which identify I/O sources of - /// interest, and waits until one or more of the events is ready for I/O. - /// - /// The result `list` contains one or more indices of handles in the - /// argument list that is ready for I/O. - /// - /// If the list contains more elements than can be indexed with a `u32` - /// value, this function traps. - /// - /// A timeout can be implemented by adding a pollable from the - /// wasi-clocks API to the list. - /// - /// This function does not return a `result`; polling in itself does not - /// do any I/O so it doesn't fail. If any of the I/O sources identified by - /// the pollables has an error, it is indicated by marking the source as - /// being reaedy for I/O. - poll: func(in: list>) -> list; -} - - -/// 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. -interface streams { - use error.{error}; - use poll.{pollable}; - - /// An error for input-stream and output-stream operations. - variant stream-error { - /// The last operation (a write or flush) failed before completion. - /// - /// More information is available in the `error` payload. - last-operation-failed(error), - /// 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 - } - - /// An input bytestream. - /// - /// `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` function to obtain a `pollable` which can be polled - /// for using `wasi:io/poll`. - resource input-stream { - /// Perform a non-blocking read from the stream. - /// - /// When the source of a `read` is binary data, the bytes from the source - /// are returned verbatim. When the source of a `read` is known to the - /// implementation to be text, bytes containing the UTF-8 encoding of the - /// text are returned. - /// - /// This function returns a list of bytes containing the read data, - /// when successful. The returned list will contain up to `len` bytes; - /// it may return fewer than requested, but not more. The list is - /// empty when no bytes are available for reading at this time. The - /// pollable given by `subscribe` will be ready when more bytes are - /// available. - /// - /// This function fails with a `stream-error` when the operation - /// encounters an error, giving `last-operation-failed`, or when the - /// stream is closed, giving `closed`. - /// - /// When the caller gives a `len` of 0, it represents a request to - /// read 0 bytes. If the stream is still open, this call should - /// succeed and return an empty list, or otherwise fail with `closed`. - /// - /// 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( - /// The maximum number of bytes to read - len: u64 - ) -> result, stream-error>; - - /// Read bytes from a stream, after blocking until at least one byte can - /// be read. Except for blocking, behavior is identical to `read`. - blocking-read: func( - /// The maximum number of bytes to read - len: u64 - ) -> result, stream-error>; - - /// Skip bytes from a stream. Returns number of bytes skipped. - /// - /// Behaves identical to `read`, except instead of returning a list - /// of bytes, returns the number of bytes consumed from the stream. - skip: func( - /// The maximum number of bytes to skip. - len: u64, - ) -> result; - - /// 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( - /// The maximum number of bytes to skip. - len: u64, - ) -> 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: func() -> pollable; - } - - - /// An output bytestream. - /// - /// `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` function to obtain a `pollable` which can be - /// polled for using `wasi:io/poll`. - resource output-stream { - /// 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` pollable will - /// become ready when this function will report at least 1 byte, or an - /// error. - check-write: func() -> result; - - /// Perform a write. This function never blocks. - /// - /// When the destination of a `write` is binary data, the bytes from - /// `contents` are written verbatim. When the destination of a `write` is - /// known to the implementation to be text, the bytes of `contents` are - /// transcoded from UTF-8 into the encoding of the destination and then - /// written. - /// - /// 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. - /// - /// returns Err(closed) without writing if the stream has closed since - /// the last call to check-write provided a permit. - write: func( - contents: list - ) -> result<_, stream-error>; - - /// 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 a convenience wrapper around the use of `check-write`, - /// `subscribe`, `write`, and `flush`, and is implemented with the - /// following pseudo-code: - /// - /// ```text - /// let pollable = this.subscribe(); - /// while !contents.is_empty() { - /// // Wait for the stream to become writable - /// pollable.block(); - /// let Ok(n) = this.check-write(); // eliding error handling - /// let len = min(n, contents.len()); - /// let (chunk, rest) = contents.split_at(len); - /// this.write(chunk ); // eliding error handling - /// contents = rest; - /// } - /// this.flush(); - /// // Wait for completion of `flush` - /// pollable.block(); - /// // Check for any errors that arose during `flush` - /// let _ = this.check-write(); // eliding error handling - /// ``` - blocking-write-and-flush: func( - contents: list - ) -> result<_, stream-error>; - - /// Request to flush buffered output. This function never blocks. - /// - /// 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` pollable will become ready when the - /// flush has completed and the stream can accept more writes. - flush: func() -> result<_, stream-error>; - - /// Request to flush buffered output, and block until flush completes - /// and stream is ready for writing again. - blocking-flush: func() -> result<_, stream-error>; - - /// 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. - /// - /// 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: func() -> 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( - /// The number of zero-bytes to write - len: u64 - ) -> result<_, stream-error>; - - /// Perform a write of up to 4096 zeroes, and then flush the stream. - /// Block until all of these operations are complete, or an error - /// occurs. - /// - /// This is a convenience wrapper around the use of `check-write`, - /// `subscribe`, `write-zeroes`, and `flush`, and is implemented with - /// the following pseudo-code: - /// - /// ```text - /// let pollable = this.subscribe(); - /// while num_zeroes != 0 { - /// // Wait for the stream to become writable - /// pollable.block(); - /// let Ok(n) = this.check-write(); // eliding error handling - /// let len = min(n, num_zeroes); - /// this.write-zeroes(len); // eliding error handling - /// num_zeroes -= len; - /// } - /// this.flush(); - /// // Wait for completion of `flush` - /// pollable.block(); - /// // Check for any errors that arose during `flush` - /// let _ = this.check-write(); // eliding error handling - /// ``` - blocking-write-zeroes-and-flush: func( - /// The number of zero-bytes to write - len: u64 - ) -> result<_, stream-error>; - - /// Read from one stream and write to another. - /// - /// The behavior of splice is equivelant to: - /// 1. calling `check-write` on the `output-stream` - /// 2. calling `read` on the `input-stream` with the smaller of the - /// `check-write` permitted length and the `len` provided to `splice` - /// 3. calling `write` on the `output-stream` with that read data. - /// - /// Any error reported by the call to `check-write`, `read`, or - /// `write` ends the splice and reports that error. - /// - /// This function returns the number of bytes transferred; it may be less - /// than `len`. - splice: func( - /// The stream to read from - src: borrow, - /// The number of bytes to splice - len: u64, - ) -> result; - - /// Read from one stream and write to another, with blocking. - /// - /// This is similar to `splice`, except that it blocks until the - /// `output-stream` is ready for writing, and the `input-stream` - /// is ready for reading, before performing the `splice`. - blocking-splice: func( - /// The stream to read from - src: borrow, - /// The number of bytes to splice - len: u64, - ) -> result; - } -} - - -world imports { - import streams; - import poll; -} diff --git a/crates/executor/src/lib.rs b/crates/executor/src/lib.rs index 4486921..f686776 100644 --- a/crates/executor/src/lib.rs +++ b/crates/executor/src/lib.rs @@ -1,25 +1,9 @@ -use bindings::wasi::io; use std::future::Future; use std::mem; use std::ops::DerefMut; use std::sync::{Arc, Mutex}; use std::task::{Context, Poll, Wake, Waker}; - -/// Module containing the generated WIT bindings. -pub mod bindings { - wit_bindgen::generate!({ - world: "imports", - path: "io.wit", - }); -} - -impl std::fmt::Display for io::streams::Error { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(&self.to_debug_string()) - } -} - -impl std::error::Error for io::streams::Error {} +use wasi::io; type Wrapped = Arc>>; diff --git a/src/http.rs b/src/http.rs index fbd0aed..d4c2294 100644 --- a/src/http.rs +++ b/src/http.rs @@ -26,7 +26,7 @@ pub use types::{ use self::conversions::{TryFromIncomingResponse, TryIntoOutgoingRequest}; use super::wit::wasi::http0_2_0::types; use futures::SinkExt; -use spin_executor::bindings::wasi::io::streams::{self, StreamError}; +use wasi::io::streams::{self, StreamError}; /// Represents an incoming HTTP request. /// diff --git a/src/http/conversions.rs b/src/http/conversions.rs index 4753ae4..b816ec7 100644 --- a/src/http/conversions.rs +++ b/src/http/conversions.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use async_trait::async_trait; -use spin_executor::bindings::wasi::io::streams; +use wasi::io::streams; use super::{ Headers, IncomingRequest, IncomingResponse, Json, JsonBodyError, Method, OutgoingRequest, diff --git a/src/http/executor.rs b/src/http/executor.rs index 027fb84..29d1c39 100644 --- a/src/http/executor.rs +++ b/src/http/executor.rs @@ -4,8 +4,8 @@ use crate::wit::wasi::http0_2_0::types::{ OutgoingRequest, }; -use spin_executor::bindings::wasi::io; -use spin_executor::bindings::wasi::io::streams::{InputStream, OutputStream, StreamError}; +use wasi::io; +use wasi::io::streams::{InputStream, OutputStream, StreamError}; use futures::{future, sink, stream, Sink, Stream}; diff --git a/src/lib.rs b/src/lib.rs index 02d8496..48418d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,9 +27,9 @@ pub mod wit { world: "platform", path: "./wit", with: { - "wasi:io/error@0.2.0": spin_executor::bindings::wasi::io::error, - "wasi:io/streams@0.2.0": spin_executor::bindings::wasi::io::streams, - "wasi:io/poll@0.2.0": spin_executor::bindings::wasi::io::poll, + "wasi:io/error@0.2.0": ::wasi::io::error, + "wasi:io/streams@0.2.0": ::wasi::io::streams, + "wasi:io/poll@0.2.0": ::wasi::io::poll, } }); pub use fermyon::spin2_0_0 as v2;