Skip to content

Commit 422b235

Browse files
committed
Address PR comments
Signed-off-by: Brian Hardock <[email protected]>
1 parent 4142f96 commit 422b235

File tree

16 files changed

+153
-57
lines changed

16 files changed

+153
-57
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ postgres_range = { version = "0.11.1", optional = true }
2525
rust_decimal = { version = "1.37.2", default-features = false, optional = true }
2626
spin-executor = { version = "5.0.0", path = "crates/executor" }
2727
spin-macro = { version = "5.0.0", path = "crates/macro" }
28-
spin-wasip3-http = { version = "5.0.0", path = "crates/spin-wasip3-http" }
29-
spin-wasip3-http-macro = { version = "5.0.0", path = "crates/spin-wasip3-http-macro" }
28+
spin-wasip3-http = { version = "5.0.0", path = "crates/spin-wasip3-http", optional = true }
29+
spin-wasip3-http-macro = { version = "5.0.0", path = "crates/spin-wasip3-http-macro", optional = true }
3030
thiserror = { workspace = true }
3131
uuid = { version = "1.18.0", optional = true }
3232
wit-bindgen = { workspace = true }
@@ -44,6 +44,7 @@ default = ["export-sdk-language", "json", "postgres4-types"]
4444
export-sdk-language = []
4545
json = ["dep:serde", "dep:serde_json"]
4646
postgres4-types = ["dep:rust_decimal", "dep:uuid", "dep:postgres_range", "json"]
47+
wasip3-unstable = ["dep:spin-wasip3-http", "dep:spin-wasip3-http-macro"]
4748

4849
[workspace]
4950
resolver = "2"
@@ -112,4 +113,3 @@ once_cell = "1.18.0"
112113
thiserror = "2.0.17"
113114
# Pin to the last version that targeted WASI 0.2.0
114115
wasi = "=0.13.1"
115-
wasip3 = "0.2.0"

crates/spin-wasip3-http-macro/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ repository.workspace = true
88
rust-version.workspace = true
99
homepage.workspace = true
1010
description = """
11-
Rust procedural macros for Spin and associated WIT files
11+
Rust procedural macros for Spin and WASIp3
1212
"""
1313

1414
[lib]

crates/spin-wasip3-http-macro/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pub fn http_component(_attr: TokenStream, item: TokenStream) -> TokenStream {
2727

2828
impl ::spin_sdk::http_wasip3::wasip3::exports::http::handler::Guest for self::Spin {
2929
async fn handle(request: ::spin_sdk::http_wasip3::wasip3::http::types::Request) -> Result<::spin_sdk::http_wasip3::wasip3::http::types::Response, ::spin_sdk::http_wasip3::wasip3::http::types::ErrorCode> {
30-
let request = <::spin_sdk::http_wasip3::IncomingRequest as ::spin_sdk::http_wasip3::FromRequest>::from_request(request)?;
30+
let request = <::spin_sdk::http_wasip3::Request as ::spin_sdk::http_wasip3::FromRequest>::from_request(request)?;
3131
::spin_sdk::http_wasip3::IntoResponse::into_response(super::#func_name(request).await)
3232
}
3333
}

crates/spin-wasip3-http/Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,4 @@ bytes = { workspace = true }
1414
http-body = { workspace = true }
1515
http-body-util = { workspace = true }
1616
hyperium = { workspace = true }
17-
wasip3-http-ext = { version = "5.0.0", path = "../wasip3-http-ext" }
18-
wasip3 = { workspace = true }
17+
wasip3-http-ext = { version = "5.0.0", path = "../wasip3-http-ext" }

crates/spin-wasip3-http/src/lib.rs

Lines changed: 121 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
//! Experimental Rust SDK for wasip3 http.
1+
//! Experimental Rust SDK for WASIp3 http.
22
33
#![deny(missing_docs)]
44

55
#[doc(hidden)]
6-
pub use wasip3;
6+
pub use wasip3_http_ext::wasip3;
77

88
use hyperium as http;
99
use std::any::Any;
@@ -16,33 +16,86 @@ use wasip3_http_ext::helpers::{
1616
use wasip3_http_ext::RequestOptionsExtension;
1717
use wasip3_http_ext::{IncomingRequestBody, IncomingResponseBody};
1818

19+
/// A body type representing an empty payload.
20+
///
21+
/// This is a convenience alias for [`http_body_util::Empty<bytes::Bytes>`],
22+
/// used when constructing HTTP requests or responses with no body.
23+
///
24+
/// # Examples
25+
///
26+
/// ```rust
27+
/// use crate::EmptyBody;
28+
///
29+
/// let empty = EmptyBody::new();
30+
/// let response = http::Response::builder()
31+
/// .status(204)
32+
/// .body(empty)
33+
/// .unwrap();
34+
/// ```
35+
pub type EmptyBody = http_body_util::Empty<bytes::Bytes>;
36+
37+
/// A body type representing a complete, in-memory payload.
38+
///
39+
/// This is a convenience alias for [`http_body_util::Full<T>`], used when the
40+
/// entire body is already available as a single value of type `T`.
41+
///
42+
/// It is typically used for sending small or pre-buffered request or response
43+
/// bodies without the need for streaming.
44+
///
45+
/// # Type Parameters
46+
///
47+
/// * `T` — The data type of the full body, such as [`bytes::Bytes`] or [`String`].
48+
///
49+
/// # Examples
50+
///
51+
/// ```rust
52+
/// use crate::FullBody;
53+
/// use bytes::Bytes;
54+
///
55+
/// let body = FullBody::new(Bytes::from("hello"));
56+
/// let request = http::Request::builder()
57+
/// .method("POST")
58+
/// .uri("https://example.com")
59+
/// .body(body)
60+
/// .unwrap();
61+
/// ```
62+
pub type FullBody<T> = http_body_util::Full<T>;
63+
1964
/// A alias for [`std::result::Result`] that uses [`Error`] as the default error type.
2065
///
2166
/// This allows functions throughout the crate to return `Result<T>`
2267
/// instead of writing out `Result<T, Error>` explicitly.
2368
pub type Result<T, E = Error> = ::std::result::Result<T, E>;
2469

25-
/// An inbound HTTP request carrying an [`wasip3_http_ext::IncomingRequestBody`].
70+
/// A type alias for an HTTP request with a customizable body type.
71+
///
72+
/// This is a convenience wrapper around [`http::Request`], parameterized
73+
/// by the body type `T`. By default, it uses [`IncomingRequestBody`],
74+
/// which represents the standard incoming body used by this runtime.
75+
///
76+
/// # Type Parameters
2677
///
27-
/// This type alias specializes [`http::Request`] with the crate’s
28-
/// [`wasip3_http_ext::IncomingRequestBody`] type, representing a request received
29-
/// from the WASI HTTP runtime or an external client.
78+
/// * `T` — The request body type. Defaults to [`IncomingRequestBody`].
3079
///
3180
/// # See also
3281
/// - [`wasip3_http_ext::IncomingRequestBody`]: The body type for inbound HTTP requests.
3382
/// - [`http::Request`]: The standard HTTP request type from the `http` crate.
34-
pub type IncomingRequest = http::Request<IncomingRequestBody>;
83+
pub type Request<T = IncomingRequestBody> = http::Request<T>;
3584

36-
/// An inbound HTTP response carrying an [`wasip3_http_ext::IncomingResponseBody`].
85+
/// A type alias for an HTTP response with a customizable body type.
3786
///
38-
/// This type alias specializes [`http::Response`] with the crate’s
39-
/// [`wasip3_http_ext::IncomingResponseBody`] type, representing a response received
40-
/// from the WASI HTTP runtime or a remote endpoint.
87+
/// This is a convenience wrapper around [`http::Response`], parameterized
88+
/// by the body type `T`. By default, it uses [`IncomingResponseBody`],
89+
/// which represents the standard incoming body type used by this runtime.
90+
///
91+
/// # Type Parameters
92+
///
93+
/// * `T` — The response body type. Defaults to [`IncomingResponseBody`].
4194
///
4295
/// # See also
4396
/// - [`wasip3_http_ext::IncomingResponseBody`]: The body type for inbound HTTP responses.
4497
/// - [`http::Response`]: The standard HTTP response type from the `http` crate.
45-
pub type IncomingResponse = http::Response<IncomingResponseBody>;
98+
pub type Response<T = IncomingResponseBody> = http::Response<T>;
4699

47100
type HttpResult<T> = Result<T, types::ErrorCode>;
48101

@@ -52,10 +105,10 @@ type HttpResult<T> = Result<T, types::ErrorCode>;
52105
/// [`IntoRequest`] trait, dispatches it to the WASI HTTP handler, and awaits
53106
/// the resulting response. It provides a convenient high-level interface for
54107
/// issuing HTTP requests within a WASI environment.
55-
pub async fn send(request: impl IntoRequest) -> HttpResult<IncomingResponse> {
108+
pub async fn send(request: impl IntoRequest) -> HttpResult<Response> {
56109
let request = request.into_request()?;
57110
let response = wasip3::http::handler::handle(request).await?;
58-
IncomingResponse::from_response(response)
111+
Response::from_response(response)
59112
}
60113

61114
/// A trait for any type that can be converted into a [`wasip3::http::types::Request`].
@@ -415,3 +468,57 @@ impl FromResponse for IncomingResponseBody {
415468
Self::new(response)
416469
}
417470
}
471+
472+
/// Helpers for consuming an [`IncomingBody`].
473+
///
474+
/// This module provides extension traits and utilities for working with
475+
/// [`IncomingBody`] instances, such as streaming or collecting the entire
476+
/// body into memory.
477+
///
478+
/// These helpers make it easier to transform low-level streaming body types
479+
/// into higher-level forms (e.g., [`Bytes`]) for simplified data handling.
480+
pub mod body {
481+
use bytes::Bytes;
482+
use http_body_util::{BodyDataStream, BodyExt};
483+
use wasip3_http_ext::wasip3::http::types::ErrorCode;
484+
use wasip3_http_ext::{IncomingBody, IncomingMessage};
485+
486+
/// Extension trait providing convenient methods for consuming an [`IncomingBody`].
487+
///
488+
/// This trait defines common patterns for handling HTTP body data in
489+
/// asynchronous contexts. It allows converting the body into a stream
490+
/// or fully collecting it into memory as a [`Bytes`] buffer.
491+
#[allow(async_fn_in_trait)]
492+
pub trait IncomingBodyExt {
493+
/// Convert this [`IncomingBody`] into a [`BodyDataStream`].
494+
///
495+
/// This method enables iteration over the body’s data chunks as they
496+
/// arrive, without collecting them all into memory at once. It is
497+
/// suitable for processing large or streaming payloads efficiently.
498+
fn stream(self) -> BodyDataStream<Self>
499+
where
500+
Self: Sized;
501+
502+
/// Consume this [`IncomingBody`] and collect it into a single [`Bytes`] buffer.
503+
///
504+
/// This method reads the entire body asynchronously and returns the
505+
/// concatenated contents. It is best suited for small or bounded-size
506+
/// payloads where holding all data in memory is acceptable.
507+
async fn bytes(self) -> Result<Bytes, ErrorCode>;
508+
}
509+
510+
impl<T: IncomingMessage> IncomingBodyExt for IncomingBody<T> {
511+
/// Convert this [`IncomingBody`] into a [`BodyDataStream`].
512+
fn stream(self) -> BodyDataStream<Self>
513+
where
514+
Self: Sized,
515+
{
516+
BodyDataStream::new(self)
517+
}
518+
519+
/// Collect the [`IncomingBody`] into a single [`Bytes`] buffer.
520+
async fn bytes(self) -> Result<Bytes, ErrorCode> {
521+
self.collect().await.map(|c| c.to_bytes())
522+
}
523+
}
524+
}

crates/wasip3-http-ext/Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ homepage.workspace = true
1111
[dependencies]
1212
bytes = { workspace = true }
1313
http-body = { workspace = true }
14-
http-body-util = { workspace = true }
1514
hyperium = { workspace = true }
16-
wasip3 = { workspace = true }
15+
wasip3 = "0.2.0"
1716
thiserror = { workspace = true }

crates/wasip3-http-ext/src/body_writer.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
use std::fmt::Debug;
2-
3-
use http_body::Frame;
4-
use http_body_util::BodyExt as _;
1+
use http_body::{Body as _, Frame};
52
use hyperium::HeaderMap;
3+
use std::future::poll_fn;
4+
use std::{fmt::Debug, pin};
65
use wasip3::{
76
http::types::{ErrorCode, HeaderError, Trailers},
87
wit_bindgen::{FutureReader, FutureWriter, StreamReader, StreamWriter},
@@ -75,15 +74,18 @@ impl BodyWriter {
7574
/// trailers) is returned.
7675
///
7776
/// If there is an error it is written to to the result future.
78-
pub async fn forward_http_body<T>(mut self, body: &mut T) -> Result<u64, Error>
77+
pub async fn forward_http_body<T>(mut self, mut body: &mut T) -> Result<u64, Error>
7978
where
8079
T: http_body::Body + Unpin,
8180
T::Data: Into<Vec<u8>>,
8281
T::Error: Into<BoxError>,
8382
{
8483
let mut total_written = 0;
84+
8585
loop {
86-
match body.frame().await {
86+
let frame = poll_fn(|cx| pin::Pin::new(&mut body).poll_frame(cx)).await;
87+
88+
match frame {
8789
Some(Ok(frame)) => {
8890
let written = self.process_http_body_frame(frame).await?;
8991
total_written += written as u64;

crates/wasip3-http-ext/src/lib.rs

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ pub mod body_writer;
55
use bytes::Bytes;
66
use helpers::{fields_to_header_map, get_content_length, to_internal_error_code};
77
use http_body::SizeHint;
8-
use http_body_util::{BodyExt, BodyStream};
98
use hyperium as http;
109
use std::{
1110
pin::Pin,
@@ -17,6 +16,8 @@ use wasip3::{
1716
wit_future,
1817
};
1918

19+
pub use wasip3;
20+
2021
const READ_FRAME_SIZE: usize = 16 * 1024;
2122

2223
pub type IncomingRequestBody = IncomingBody<types::Request>;
@@ -99,16 +100,6 @@ impl<T: IncomingMessage> IncomingBody<T> {
99100
})
100101
}
101102

102-
pub async fn stream(self) -> BodyStream<Self> {
103-
BodyStream::new(self)
104-
}
105-
106-
pub async fn bytes(self) -> Result<Bytes, ErrorCode> {
107-
self.collect().await.map(|c| c.to_bytes())
108-
}
109-
110-
// TODO: pub fn take_future() -> result
111-
112103
pub fn take_unstarted(&mut self) -> Option<T> {
113104
match self.state {
114105
StartedState::Unstarted(_) => {

examples/wasip3-http-axum-router/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ crate-type = ["cdylib"]
99
[dependencies]
1010
axum = { version = "0.8.1", default-features = false, features = ["json", "macros"] }
1111
serde = { version = "1.0.163", features = ["derive"] }
12-
spin-sdk = { path = "../.." }
12+
spin-sdk = { path = "../..", features = ["wasip3-unstable"] }
1313
tower-service = "0.3.3"

0 commit comments

Comments
 (0)