Skip to content
This repository was archived by the owner on Oct 13, 2023. It is now read-only.

Commit ecfc05a

Browse files
feat(wasi): sync wit definition with wasmtime v13 (#177)
This will update the WIT definitions related to WASI to match wasmtime v13. * feat(wasi): sync wit definition with wasmtime v13 * chore: update definition * feat(wasi): update http client code * chore: use wasmtime v13 commit hash * chore: bump wasi-preview2-prototype to v0.0.3
1 parent 1af2a12 commit ecfc05a

File tree

12 files changed

+296
-180
lines changed

12 files changed

+296
-180
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

wasi/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
# name = "wasi"
33
name = "wasi-preview2-prototype"
4-
version = "0.0.2"
4+
version = "0.0.3"
55
# version = "0.12.0+wasi-snapshot-preview2"
66
description = "Experimental WASI Preview2 API bindings for Rust"
77
edition.workspace = true

wasi/src/http_client.rs

Lines changed: 54 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ use crate::snapshots::preview_2::wasi::http::{outgoing_handler, types as http_ty
77
use crate::snapshots::preview_2::wasi::io::streams;
88
use crate::snapshots::preview_2::wasi::poll::poll;
99

10+
struct DropPollable {
11+
pollable: poll::Pollable,
12+
}
13+
14+
impl Drop for DropPollable {
15+
fn drop(&mut self) {
16+
poll::drop_pollable(self.pollable);
17+
}
18+
}
19+
1020
pub struct DefaultClient {
1121
options: Option<outgoing_handler::RequestOptions>,
1222
}
@@ -68,16 +78,36 @@ impl TryFrom<http::Request<Bytes>> for Request {
6878
.map_err(|_| anyhow!("outgoing request write failed"))?;
6979

7080
if body.is_empty() {
71-
streams::write(request_body, &[]).map_err(|_| anyhow!("writing request body"))?;
72-
} else {
73-
let output_stream_pollable = streams::subscribe_to_output_stream(request_body);
74-
let mut body_cursor = 0;
75-
while body_cursor < body.len() {
76-
let (written, _) = streams::write(request_body, &body[body_cursor..])
77-
.map_err(|_| anyhow!("writing request body"))?;
78-
body_cursor += written as usize;
81+
let sub = DropPollable {
82+
pollable: streams::subscribe_to_output_stream(request_body),
83+
};
84+
let mut buf = body.as_ref();
85+
while !buf.is_empty() {
86+
poll::poll_oneoff(&[sub.pollable]);
87+
88+
let permit = match streams::check_write(request_body) {
89+
Ok(n) => usize::try_from(n)?,
90+
Err(_) => anyhow::bail!("output stream error"),
91+
};
92+
93+
let len = buf.len().min(permit);
94+
let (chunk, rest) = buf.split_at(len);
95+
buf = rest;
96+
97+
if streams::write(request_body, chunk).is_err() {
98+
anyhow::bail!("output stream error")
99+
}
100+
}
101+
102+
if streams::flush(request_body).is_err() {
103+
anyhow::bail!("output stream error")
104+
}
105+
106+
poll::poll_oneoff(&[sub.pollable]);
107+
108+
if streams::check_write(request_body).is_err() {
109+
anyhow::bail!("output stream error")
79110
}
80-
poll::drop_pollable(output_stream_pollable);
81111
}
82112

83113
Ok(Request::new(request, request_body))
@@ -146,21 +176,25 @@ impl TryFrom<Response> for http::Response<Bytes> {
146176

147177
let body_stream = http_types::incoming_response_consume(incoming_response)
148178
.map_err(|_| anyhow!("consuming incoming response"))?;
149-
let input_stream_pollable = streams::subscribe_to_input_stream(body_stream);
150179

151180
let mut body = BytesMut::new();
152-
let mut eof = streams::StreamStatus::Open;
153-
while eof != streams::StreamStatus::Ended {
154-
let (body_chunk, stream_status) = streams::read(body_stream, u64::MAX)
155-
.map_err(|e| anyhow!("reading response body: {e:?}"))?;
156-
eof = if body_chunk.is_empty() {
157-
streams::StreamStatus::Ended
158-
} else {
159-
stream_status
181+
{
182+
let sub = DropPollable {
183+
pollable: streams::subscribe_to_input_stream(body_stream),
160184
};
161-
body.put(body_chunk.as_slice());
185+
poll::poll_oneoff(&[sub.pollable]);
186+
let mut eof = streams::StreamStatus::Open;
187+
while eof != streams::StreamStatus::Ended {
188+
let (body_chunk, stream_status) = streams::read(body_stream, u64::MAX)
189+
.map_err(|e| anyhow!("reading response body: {e:?}"))?;
190+
eof = if body_chunk.is_empty() {
191+
streams::StreamStatus::Ended
192+
} else {
193+
stream_status
194+
};
195+
body.put(body_chunk.as_slice());
196+
}
162197
}
163-
poll::drop_pollable(input_stream_pollable);
164198

165199
let mut res = http::Response::builder()
166200
.status(status)

wasi/wit/deps.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
preview = "https://gitpkg.now.sh/bytecodealliance/wasmtime/crates/wasi?e250334b8ebfba9359802ab7f61bdd7c6085d87a"
1+
preview = "https://gitpkg.now.sh/bytecodealliance/wasmtime/crates/wasi?aec4b25b8f62f409175a3cc6c4a4ed18b446d3ae"

wasi/wit/deps/io/streams.wit

Lines changed: 95 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -134,58 +134,115 @@ interface streams {
134134
/// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources).
135135
type output-stream = u32
136136

137-
/// Perform a non-blocking write of bytes to a stream.
137+
/// An error for output-stream operations.
138138
///
139-
/// This function returns a `u64` and a `stream-status`. The `u64` indicates
140-
/// the number of bytes from `buf` that were written, which may be less than
141-
/// the length of `buf`. The `stream-status` indicates if further writes to
142-
/// the stream are expected to be read.
139+
/// Contrary to input-streams, a closed output-stream is reported using
140+
/// an error.
141+
enum write-error {
142+
/// The last operation (a write or flush) failed before completion.
143+
last-operation-failed,
144+
/// The stream is closed: no more input will be accepted by the
145+
/// stream. A closed output-stream will return this error on all
146+
/// future operations.
147+
closed
148+
}
149+
/// Check readiness for writing. This function never blocks.
150+
///
151+
/// Returns the number of bytes permitted for the next call to `write`,
152+
/// or an error. Calling `write` with more bytes than this function has
153+
/// permitted will trap.
154+
///
155+
/// When this function returns 0 bytes, the `subscribe-to-output-stream`
156+
/// pollable will become ready when this function will report at least
157+
/// 1 byte, or an error.
158+
check-write: func(
159+
this: output-stream
160+
) -> result<u64, write-error>
161+
162+
/// Perform a write. This function never blocks.
143163
///
144-
/// When the returned `stream-status` is `open`, the `u64` return value may
145-
/// be less than the length of `buf`. This indicates that no more bytes may
146-
/// be written to the stream promptly. In that case the
147-
/// `subscribe-to-output-stream` pollable will indicate when additional bytes
148-
/// may be promptly written.
164+
/// Precondition: check-write gave permit of Ok(n) and contents has a
165+
/// length of less than or equal to n. Otherwise, this function will trap.
149166
///
150-
/// Writing an empty list must return a non-error result with `0` for the
151-
/// `u64` return value, and the current `stream-status`.
167+
/// returns Err(closed) without writing if the stream has closed since
168+
/// the last call to check-write provided a permit.
152169
write: func(
153170
this: output-stream,
154-
/// Data to write
155-
buf: list<u8>
156-
) -> result<tuple<u64, stream-status>>
171+
contents: list<u8>
172+
) -> result<_, write-error>
157173

158-
/// Blocking write of bytes to a stream.
174+
/// Perform a write of up to 4096 bytes, and then flush the stream. Block
175+
/// until all of these operations are complete, or an error occurs.
159176
///
160-
/// This is similar to `write`, except that it blocks until at least one
161-
/// byte can be written.
162-
blocking-write: func(
163-
this: output-stream,
164-
/// Data to write
165-
buf: list<u8>
166-
) -> result<tuple<u64, stream-status>>
177+
/// This is a convenience wrapper around the use of `check-write`,
178+
/// `subscribe-to-output-stream`, `write`, and `flush`, and is implemented
179+
/// with the following pseudo-code:
180+
///
181+
/// ```text
182+
/// let pollable = subscribe-to-output-stream(this);
183+
/// while !contents.is_empty() {
184+
/// // Wait for the stream to become writable
185+
/// poll-oneoff(pollable);
186+
/// let Ok(n) = check-write(this); // eliding error handling
187+
/// let len = min(n, contents.len());
188+
/// let (chunk, rest) = contents.split_at(len);
189+
/// write(this, chunk); // eliding error handling
190+
/// contents = rest;
191+
/// }
192+
/// flush(this);
193+
/// // Wait for completion of `flush`
194+
/// poll-oneoff(pollable);
195+
/// // Check for any errors that arose during `flush`
196+
/// let _ = check-write(this); // eliding error handling
197+
/// ```
198+
blocking-write-and-flush: func(
199+
this: output-stream,
200+
contents: list<u8>
201+
) -> result<_, write-error>
167202

168-
/// Write multiple zero-bytes to a stream.
203+
/// Request to flush buffered output. This function never blocks.
169204
///
170-
/// This function returns a `u64` indicating the number of zero-bytes
171-
/// that were written; it may be less than `len`. Equivelant to a call to
172-
/// `write` with a list of zeroes of the given length.
173-
write-zeroes: func(
205+
/// This tells the output-stream that the caller intends any buffered
206+
/// output to be flushed. the output which is expected to be flushed
207+
/// is all that has been passed to `write` prior to this call.
208+
///
209+
/// Upon calling this function, the `output-stream` will not accept any
210+
/// writes (`check-write` will return `ok(0)`) until the flush has
211+
/// completed. The `subscribe-to-output-stream` pollable will become ready
212+
/// when the flush has completed and the stream can accept more writes.
213+
flush: func(
174214
this: output-stream,
175-
/// The number of zero-bytes to write
176-
len: u64
177-
) -> result<tuple<u64, stream-status>>
215+
) -> result<_, write-error>
216+
217+
/// Request to flush buffered output, and block until flush completes
218+
/// and stream is ready for writing again.
219+
blocking-flush: func(
220+
this: output-stream,
221+
) -> result<_, write-error>
222+
223+
/// Create a `pollable` which will resolve once the output-stream
224+
/// is ready for more writing, or an error has occured. When this
225+
/// pollable is ready, `check-write` will return `ok(n)` with n>0, or an
226+
/// error.
227+
///
228+
/// If the stream is closed, this pollable is always ready immediately.
229+
///
230+
/// The created `pollable` is a child resource of the `output-stream`.
231+
/// Implementations may trap if the `output-stream` is dropped before
232+
/// all derived `pollable`s created with this function are dropped.
233+
subscribe-to-output-stream: func(this: output-stream) -> pollable
178234

179-
/// Write multiple zero bytes to a stream, with blocking.
235+
/// Write zeroes to a stream.
180236
///
181-
/// This is similar to `write-zeroes`, except that it blocks until at least
182-
/// one byte can be written. Equivelant to a call to `blocking-write` with
183-
/// a list of zeroes of the given length.
184-
blocking-write-zeroes: func(
237+
/// this should be used precisely like `write` with the exact same
238+
/// preconditions (must use check-write first), but instead of
239+
/// passing a list of bytes, you simply pass the number of zero-bytes
240+
/// that should be written.
241+
write-zeroes: func(
185242
this: output-stream,
186-
/// The number of zero bytes to write
243+
/// The number of zero-bytes to write
187244
len: u64
188-
) -> result<tuple<u64, stream-status>>
245+
) -> result<_, write-error>
189246

190247
/// Read from one stream and write to another.
191248
///
@@ -232,16 +289,6 @@ interface streams {
232289
src: input-stream
233290
) -> result<tuple<u64, stream-status>>
234291

235-
/// Create a `pollable` which will resolve once either the specified stream
236-
/// is ready to accept bytes or the `stream-state` has become closed.
237-
///
238-
/// Once the stream-state is closed, this pollable is always ready
239-
/// immediately.
240-
///
241-
/// The created `pollable` is a child resource of the `output-stream`.
242-
/// Implementations may trap if the `output-stream` is dropped before
243-
/// all derived `pollable`s created with this function are dropped.
244-
subscribe-to-output-stream: func(this: output-stream) -> pollable
245292

246293
/// Dispose of the specified `output-stream`, after which it may no longer
247294
/// be used.

wasi/wit/deps/preview/test.wit

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,16 @@ world test-command {
2626
import wasi:cli/stdout
2727
import wasi:cli/stderr
2828
}
29+
30+
world test-command-with-sockets {
31+
import wasi:poll/poll
32+
import wasi:io/streams
33+
import wasi:cli/environment
34+
import wasi:cli/stdin
35+
import wasi:cli/stdout
36+
import wasi:cli/stderr
37+
import wasi:sockets/tcp
38+
import wasi:sockets/tcp-create-socket
39+
import wasi:sockets/network
40+
import wasi:sockets/instance-network
41+
}

wasi/wit/deps/sockets/ip-name-lookup.wit

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ interface ip-name-lookup {
55

66

77
/// Resolve an internet host name to a list of IP addresses.
8-
///
8+
///
99
/// See the wasi-socket proposal README.md for a comparison with getaddrinfo.
10-
///
10+
///
1111
/// # Parameters
1212
/// - `name`: The name to look up. IP addresses are not allowed. Unicode domain names are automatically converted
1313
/// to ASCII using IDNA encoding.
@@ -17,18 +17,18 @@ interface ip-name-lookup {
1717
/// systems without an active IPv6 interface. Notes:
1818
/// - Even when no public IPv6 interfaces are present or active, names like "localhost" can still resolve to an IPv6 address.
1919
/// - Whatever is "available" or "unavailable" is volatile and can change everytime a network cable is unplugged.
20-
///
20+
///
2121
/// This function never blocks. It either immediately fails or immediately returns successfully with a `resolve-address-stream`
2222
/// that can be used to (asynchronously) fetch the results.
23-
///
23+
///
2424
/// At the moment, the stream never completes successfully with 0 items. Ie. the first call
2525
/// to `resolve-next-address` never returns `ok(none)`. This may change in the future.
26-
///
26+
///
2727
/// # Typical errors
2828
/// - `invalid-name`: `name` is a syntactically invalid domain name.
2929
/// - `invalid-name`: `name` is an IP address.
3030
/// - `address-family-not-supported`: The specified `address-family` is not supported. (EAI_FAMILY)
31-
///
31+
///
3232
/// # References:
3333
/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getaddrinfo.html>
3434
/// - <https://man7.org/linux/man-pages/man3/getaddrinfo.3.html>
@@ -41,14 +41,14 @@ interface ip-name-lookup {
4141
type resolve-address-stream = u32
4242

4343
/// Returns the next address from the resolver.
44-
///
44+
///
4545
/// This function should be called multiple times. On each call, it will
4646
/// return the next address in connection order preference. If all
4747
/// addresses have been exhausted, this function returns `none`.
4848
/// After which, you should release the stream with `drop-resolve-address-stream`.
49-
///
49+
///
5050
/// This function never returns IPv4-mapped IPv6 addresses.
51-
///
51+
///
5252
/// # Typical errors
5353
/// - `name-unresolvable`: Name does not exist or has no suitable associated IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY)
5454
/// - `temporary-resolver-failure`: A temporary failure in name resolution occurred. (EAI_AGAIN)
@@ -57,12 +57,12 @@ interface ip-name-lookup {
5757
resolve-next-address: func(this: resolve-address-stream) -> result<option<ip-address>, error-code>
5858

5959
/// Dispose of the specified `resolve-address-stream`, after which it may no longer be used.
60-
///
60+
///
6161
/// Note: this function is scheduled to be removed when Resources are natively supported in Wit.
6262
drop-resolve-address-stream: func(this: resolve-address-stream)
6363

6464
/// Create a `pollable` which will resolve once the stream is ready for I/O.
65-
///
65+
///
6666
/// Note: this function is here for WASI Preview2 only.
6767
/// It's planned to be removed when `future` is natively supported in Preview3.
6868
subscribe: func(this: resolve-address-stream) -> pollable

0 commit comments

Comments
 (0)