Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion crates/test-programs/src/bin/p3_http_api_bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,9 @@ fn new_outgoing_requests(n: usize) -> (Vec<Request>, Vec<BodyWriters>) {
let (request, writers) = new_request();
request.set_path_with_query(Some("/")).unwrap();
request.set_scheme(Some(&Scheme::Http)).unwrap();
request.set_authority(Some("p3-test")).unwrap();
request
.set_authority(Some("p3-test-immediate-response"))
.unwrap();
(request, writers)
})
.unzip()
Expand Down
35 changes: 34 additions & 1 deletion crates/test-programs/src/bin/p3_http_proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,15 @@ test_programs::p3::service::export!(Component);

impl Handler for Component {
// Forward the request body and trailers to a URL specified in a header.
//
// If `expect-transmit-error` is set, also assert that the `response.new`
// transmit future of the returned response reports an error, and then send
// a request to `http://p3-test/` to signal that the assertion has run.
async fn handle(request: Request) -> Result<Response, ErrorCode> {
let headers = request.get_headers().copy_all();
let expect_transmit_error = headers
.iter()
.any(|(k, v)| k == "expect-transmit-error" && v == b"true");
Ok(
if let Some(url) = headers.iter().find_map(|(k, v)| {
(k == "url")
Expand All @@ -42,14 +49,40 @@ impl Handler for Component {
outgoing_request
.set_authority(Some(url.authority()))
.unwrap();
client::send(outgoing_request).await?
let response = client::send(outgoing_request).await?;
if expect_transmit_error {
forward_expecting_transmit_error(response)
} else {
response
}
} else {
bad_request()
},
)
}
}

fn forward_expecting_transmit_error(upstream: Response) -> Response {
let (body, trailers) = Response::consume_body(upstream, wit_future::new(|| Ok(())).1);
let (response, transmit) = Response::new(Fields::new(), Some(body), trailers);
wit_bindgen::spawn_local(async move {
// In the background, assert that the response fails to complete.
let result = transmit.await;
assert!(
result.is_err(),
"transmitting the response should have failed, got {result:?}"
);

// Send a request to indicate that the assertion passed.
let done = Request::new(Fields::new(), None, wit_future::new(|| Ok(None)).1, None).0;
done.set_scheme(Some(&Scheme::Http)).unwrap();
done.set_authority(Some("p3-test")).unwrap();
done.set_path_with_query(Some("/")).unwrap();
client::send(done).await.unwrap();
});
response
}

fn bad_request() -> Response {
respond(400)
}
Expand Down
4 changes: 2 additions & 2 deletions crates/wasi-http/src/default_send_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ pub async fn default_send_request(
let connect = async {
let stream = TcpStream::connect(&authority)
.await
.map_err(Error::Connect)?;
.map_err(|e| Error::Connect(e.into()))?;
let stream = if use_tls {
// derived from https://github.com/rustls/rustls/blob/main/examples/src/bin/simpleclient.rs
let root_cert_store = rustls::RootCertStore {
Expand All @@ -82,7 +82,7 @@ pub async fn default_send_request(
let stream = connector
.connect(domain, stream)
.await
.map_err(Error::Tls)?;
.map_err(|e| Error::Tls(e.into()))?;
stream.boxed()
} else {
stream.boxed()
Expand Down
18 changes: 11 additions & 7 deletions crates/wasi-http/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
use std::fmt;
use std::io;
use std::sync::Arc;

/// Error type for `wasi:http` embedder interactions.
///
/// This error type is a superset of the wasip2/wasip3 error types for example
/// and is the error that the embedder interacts with for all operations.
#[derive(Debug)]
///
/// Errors from other crates which cannot be cloned are wrapped in [`Arc`] so
/// that this error can be.
#[derive(Clone, Debug)]
#[non_exhaustive]
#[expect(missing_docs, reason = "too cluttered")]
pub enum Error {
Hyper(hyper::Error),
Connect(io::Error),
Tls(io::Error),
Hyper(Arc<hyper::Error>),
Connect(Arc<io::Error>),
Tls(Arc<io::Error>),
#[cfg(feature = "default-send-request")]
InvalidDnsNameError(rustls::pki_types::InvalidDnsNameError),
InvalidDnsNameError(Arc<rustls::pki_types::InvalidDnsNameError>),
HttpRequestBodySize(Option<u64>),

DnsTimeout,
Expand Down Expand Up @@ -79,7 +83,7 @@ pub type Result<T, E = Error> = std::result::Result<T, E>;

impl From<hyper::Error> for Error {
fn from(err: hyper::Error) -> Self {
Self::Hyper(err)
Self::Hyper(Arc::new(err))
}
}

Expand All @@ -92,7 +96,7 @@ impl From<std::convert::Infallible> for Error {
#[cfg(feature = "default-send-request")]
impl From<rustls::pki_types::InvalidDnsNameError> for Error {
fn from(err: rustls::pki_types::InvalidDnsNameError) -> Self {
Self::InvalidDnsNameError(err)
Self::InvalidDnsNameError(Arc::new(err))
}
}

Expand Down
Loading
Loading