16f6a514bSRoman Volosatovs use {
26f6a514bSRoman Volosatovs flate2::{
36f6a514bSRoman Volosatovs Compression,
46f6a514bSRoman Volosatovs write::{DeflateDecoder, DeflateEncoder},
56f6a514bSRoman Volosatovs },
66f6a514bSRoman Volosatovs std::{io::Write, mem},
76f6a514bSRoman Volosatovs test_programs::p3::{
86f6a514bSRoman Volosatovs wasi::http::{
96f6a514bSRoman Volosatovs handler,
106f6a514bSRoman Volosatovs types::{ErrorCode, Headers, Request, Response},
116f6a514bSRoman Volosatovs },
126f6a514bSRoman Volosatovs wit_future, wit_stream,
136f6a514bSRoman Volosatovs },
141047b511SAlex Crichton wit_bindgen::StreamResult,
156f6a514bSRoman Volosatovs };
166f6a514bSRoman Volosatovs
171cc0bcffSBailey Hayes wit_bindgen::generate!({
181cc0bcffSBailey Hayes path: "../wasi-http/src/p3/wit",
191cc0bcffSBailey Hayes world: "wasi:http/middleware",
201cc0bcffSBailey Hayes with: {
21*5d3627b7SBailey Hayes "wasi:http/handler@0.3.0-rc-2026-03-15": test_programs::p3::wasi::http::handler,
22*5d3627b7SBailey Hayes "wasi:http/types@0.3.0-rc-2026-03-15": test_programs::p3::wasi::http::types,
23*5d3627b7SBailey Hayes "wasi:http/client@0.3.0-rc-2026-03-15": test_programs::p3::wasi::http::client,
24*5d3627b7SBailey Hayes "wasi:random/random@0.3.0-rc-2026-03-15": test_programs::p3::wasi::random::random,
25*5d3627b7SBailey Hayes "wasi:random/insecure@0.3.0-rc-2026-03-15": test_programs::p3::wasi::random::insecure,
26*5d3627b7SBailey Hayes "wasi:random/insecure-seed@0.3.0-rc-2026-03-15": test_programs::p3::wasi::random::insecure_seed,
27*5d3627b7SBailey Hayes "wasi:cli/stdout@0.3.0-rc-2026-03-15": test_programs::p3::wasi::cli::stdout,
28*5d3627b7SBailey Hayes "wasi:cli/stderr@0.3.0-rc-2026-03-15": test_programs::p3::wasi::cli::stderr,
29*5d3627b7SBailey Hayes "wasi:cli/stdin@0.3.0-rc-2026-03-15": test_programs::p3::wasi::cli::stdin,
30*5d3627b7SBailey Hayes "wasi:cli/types@0.3.0-rc-2026-03-15": test_programs::p3::wasi::cli::types,
31*5d3627b7SBailey Hayes "wasi:clocks/monotonic-clock@0.3.0-rc-2026-03-15": test_programs::p3::wasi::clocks::monotonic_clock,
32*5d3627b7SBailey Hayes "wasi:clocks/system-clock@0.3.0-rc-2026-03-15": test_programs::p3::wasi::clocks::system_clock,
33*5d3627b7SBailey Hayes "wasi:clocks/types@0.3.0-rc-2026-03-15": test_programs::p3::wasi::clocks::types,
341cc0bcffSBailey Hayes },
351cc0bcffSBailey Hayes });
361cc0bcffSBailey Hayes
376f6a514bSRoman Volosatovs struct Component;
386f6a514bSRoman Volosatovs
391cc0bcffSBailey Hayes export!(Component);
406f6a514bSRoman Volosatovs
411cc0bcffSBailey Hayes impl exports::wasi::http::handler::Guest for Component {
426f6a514bSRoman Volosatovs /// Forward the specified request to the imported `wasi:http/handler`, transparently decoding the request body
436f6a514bSRoman Volosatovs /// if it is `deflate`d and then encoding the response body if the client has provided an `accept-encoding:
446f6a514bSRoman Volosatovs /// deflate` header.
handle(request: Request) -> Result<Response, ErrorCode>456f6a514bSRoman Volosatovs async fn handle(request: Request) -> Result<Response, ErrorCode> {
466f6a514bSRoman Volosatovs // First, extract the parts of the request and check for (and remove) headers pertaining to body encodings.
476f6a514bSRoman Volosatovs let method = request.get_method();
486f6a514bSRoman Volosatovs let scheme = request.get_scheme();
496f6a514bSRoman Volosatovs let path_with_query = request.get_path_with_query();
506f6a514bSRoman Volosatovs let authority = request.get_authority();
516f6a514bSRoman Volosatovs let mut accept_deflated = false;
526f6a514bSRoman Volosatovs let mut content_deflated = false;
536f6a514bSRoman Volosatovs let headers = request.get_headers();
546f6a514bSRoman Volosatovs let mut headers = headers.copy_all();
556f6a514bSRoman Volosatovs headers.retain(|(k, v)| match (k.as_str(), v.as_slice()) {
566f6a514bSRoman Volosatovs ("accept-encoding", value)
576f6a514bSRoman Volosatovs if std::str::from_utf8(value)
586f6a514bSRoman Volosatovs .map(|v| v.contains("deflate"))
596f6a514bSRoman Volosatovs .unwrap_or(false) =>
606f6a514bSRoman Volosatovs {
616f6a514bSRoman Volosatovs accept_deflated = true;
626f6a514bSRoman Volosatovs false
636f6a514bSRoman Volosatovs }
646f6a514bSRoman Volosatovs ("content-encoding", b"deflate") => {
656f6a514bSRoman Volosatovs content_deflated = true;
666f6a514bSRoman Volosatovs false
676f6a514bSRoman Volosatovs }
686f6a514bSRoman Volosatovs _ => true,
696f6a514bSRoman Volosatovs });
7053059995SRoman Volosatovs let (_, result_rx) = wit_future::new(|| Ok(()));
7153059995SRoman Volosatovs let (mut body, trailers) = Request::consume_body(request, result_rx);
726f6a514bSRoman Volosatovs
736f6a514bSRoman Volosatovs let (body, trailers) = if content_deflated {
746f6a514bSRoman Volosatovs // Next, spawn a task to pipe and decode the original request body and trailers into a new request
756f6a514bSRoman Volosatovs // we'll create below. This will run concurrently with any code in the imported `wasi:http/handler`.
766f6a514bSRoman Volosatovs let (trailers_tx, trailers_rx) = wit_future::new(|| todo!());
776f6a514bSRoman Volosatovs let (mut pipe_tx, pipe_rx) = wit_stream::new();
786f6a514bSRoman Volosatovs
791047b511SAlex Crichton wit_bindgen::spawn(async move {
806f6a514bSRoman Volosatovs {
816f6a514bSRoman Volosatovs let mut decoder = DeflateDecoder::new(Vec::new());
82da633618SJoel Dice let mut status = StreamResult::Complete(0);
83da633618SJoel Dice let mut chunk = Vec::with_capacity(64 * 1024);
846f6a514bSRoman Volosatovs
856f6a514bSRoman Volosatovs while let StreamResult::Complete(_) = status {
86da633618SJoel Dice (status, chunk) = body.read(chunk).await;
876f6a514bSRoman Volosatovs decoder.write_all(&chunk).unwrap();
886f6a514bSRoman Volosatovs let remaining = pipe_tx.write_all(mem::take(decoder.get_mut())).await;
896f6a514bSRoman Volosatovs assert!(remaining.is_empty());
906f6a514bSRoman Volosatovs *decoder.get_mut() = remaining;
916f6a514bSRoman Volosatovs chunk.clear();
926f6a514bSRoman Volosatovs }
936f6a514bSRoman Volosatovs
946f6a514bSRoman Volosatovs let remaining = pipe_tx.write_all(decoder.finish().unwrap()).await;
956f6a514bSRoman Volosatovs assert!(remaining.is_empty());
966f6a514bSRoman Volosatovs
976f6a514bSRoman Volosatovs drop(pipe_tx);
986f6a514bSRoman Volosatovs }
996f6a514bSRoman Volosatovs
1006f6a514bSRoman Volosatovs trailers_tx.write(trailers.await).await.unwrap();
1016f6a514bSRoman Volosatovs });
1026f6a514bSRoman Volosatovs
1036f6a514bSRoman Volosatovs (pipe_rx, trailers_rx)
1046f6a514bSRoman Volosatovs } else {
1056f6a514bSRoman Volosatovs (body, trailers)
1066f6a514bSRoman Volosatovs };
1076f6a514bSRoman Volosatovs
1086f6a514bSRoman Volosatovs // While the above task (if any) is running, synthesize a request from the parts collected above and pass
1096f6a514bSRoman Volosatovs // it to the imported `wasi:http/handler`.
1106f6a514bSRoman Volosatovs let (my_request, _request_complete) = Request::new(
1116f6a514bSRoman Volosatovs Headers::from_list(&headers).unwrap(),
1126f6a514bSRoman Volosatovs Some(body),
1136f6a514bSRoman Volosatovs trailers,
1146f6a514bSRoman Volosatovs None,
1156f6a514bSRoman Volosatovs );
1166f6a514bSRoman Volosatovs my_request.set_method(&method).unwrap();
1176f6a514bSRoman Volosatovs my_request.set_scheme(scheme.as_ref()).unwrap();
1186f6a514bSRoman Volosatovs my_request
1196f6a514bSRoman Volosatovs .set_path_with_query(path_with_query.as_deref())
1206f6a514bSRoman Volosatovs .unwrap();
1216f6a514bSRoman Volosatovs my_request.set_authority(authority.as_deref()).unwrap();
1226f6a514bSRoman Volosatovs
1236f6a514bSRoman Volosatovs let response = handler::handle(my_request).await?;
1246f6a514bSRoman Volosatovs
1256f6a514bSRoman Volosatovs // Now that we have the response, extract the parts, adding an extra header if we'll be encoding the body.
1266f6a514bSRoman Volosatovs let status_code = response.get_status_code();
1276f6a514bSRoman Volosatovs let mut headers = response.get_headers().copy_all();
1286f6a514bSRoman Volosatovs if accept_deflated {
1296f6a514bSRoman Volosatovs headers.push(("content-encoding".into(), b"deflate".into()));
1306f6a514bSRoman Volosatovs }
1316f6a514bSRoman Volosatovs
13253059995SRoman Volosatovs let (_, result_rx) = wit_future::new(|| Ok(()));
13353059995SRoman Volosatovs let (mut body, trailers) = Response::consume_body(response, result_rx);
1346f6a514bSRoman Volosatovs let (body, trailers) = if accept_deflated {
1356f6a514bSRoman Volosatovs headers.retain(|(name, _value)| name != "content-length");
1366f6a514bSRoman Volosatovs
1376f6a514bSRoman Volosatovs // Spawn another task; this one is to pipe and encode the original response body and trailers into a
1386f6a514bSRoman Volosatovs // new response we'll create below. This will run concurrently with the caller's code (i.e. it won't
1396f6a514bSRoman Volosatovs // necessarily complete before we return a value).
1406f6a514bSRoman Volosatovs let (trailers_tx, trailers_rx) = wit_future::new(|| todo!());
1416f6a514bSRoman Volosatovs let (mut pipe_tx, pipe_rx) = wit_stream::new();
1426f6a514bSRoman Volosatovs
1431047b511SAlex Crichton wit_bindgen::spawn(async move {
1446f6a514bSRoman Volosatovs {
1456f6a514bSRoman Volosatovs let mut encoder = DeflateEncoder::new(Vec::new(), Compression::fast());
146da633618SJoel Dice let mut status = StreamResult::Complete(0);
147da633618SJoel Dice let mut chunk = Vec::with_capacity(64 * 1024);
1486f6a514bSRoman Volosatovs
1496f6a514bSRoman Volosatovs while let StreamResult::Complete(_) = status {
150da633618SJoel Dice (status, chunk) = body.read(chunk).await;
1516f6a514bSRoman Volosatovs encoder.write_all(&chunk).unwrap();
1526f6a514bSRoman Volosatovs let remaining = pipe_tx.write_all(mem::take(encoder.get_mut())).await;
1536f6a514bSRoman Volosatovs assert!(remaining.is_empty());
1546f6a514bSRoman Volosatovs *encoder.get_mut() = remaining;
1556f6a514bSRoman Volosatovs chunk.clear();
1566f6a514bSRoman Volosatovs }
1576f6a514bSRoman Volosatovs
1586f6a514bSRoman Volosatovs let remaining = pipe_tx.write_all(encoder.finish().unwrap()).await;
1596f6a514bSRoman Volosatovs assert!(remaining.is_empty());
1606f6a514bSRoman Volosatovs
1616f6a514bSRoman Volosatovs drop(pipe_tx);
1626f6a514bSRoman Volosatovs }
1636f6a514bSRoman Volosatovs
1646f6a514bSRoman Volosatovs trailers_tx.write(trailers.await).await.unwrap();
1656f6a514bSRoman Volosatovs });
1666f6a514bSRoman Volosatovs
1676f6a514bSRoman Volosatovs (pipe_rx, trailers_rx)
1686f6a514bSRoman Volosatovs } else {
1696f6a514bSRoman Volosatovs (body, trailers)
1706f6a514bSRoman Volosatovs };
1716f6a514bSRoman Volosatovs
1726f6a514bSRoman Volosatovs // While the above tasks (if any) are running, synthesize a response from the parts collected above and
1736f6a514bSRoman Volosatovs // return it.
1746f6a514bSRoman Volosatovs let (my_response, _response_complete) =
1756f6a514bSRoman Volosatovs Response::new(Headers::from_list(&headers).unwrap(), Some(body), trailers);
1766f6a514bSRoman Volosatovs my_response.set_status_code(status_code).unwrap();
1776f6a514bSRoman Volosatovs
1786f6a514bSRoman Volosatovs Ok(my_response)
1796f6a514bSRoman Volosatovs }
1806f6a514bSRoman Volosatovs }
1816f6a514bSRoman Volosatovs
1826f6a514bSRoman Volosatovs // Unused function; required since this file is built as a `bin`:
main()1836f6a514bSRoman Volosatovs fn main() {}
184