1 use {
2     flate2::{
3         Compression,
4         write::{DeflateDecoder, DeflateEncoder},
5     },
6     std::{io::Write, mem},
7     test_programs::p3::{
8         wasi::http::{
9             handler,
10             types::{ErrorCode, Headers, Request, Response},
11         },
12         wit_future, wit_stream,
13     },
14     wit_bindgen::StreamResult,
15 };
16 
17 wit_bindgen::generate!({
18     path: "../wasi-http/src/p3/wit",
19     world: "wasi:http/middleware",
20     with: {
21         "wasi:http/handler@0.3.0-rc-2026-02-09": test_programs::p3::wasi::http::handler,
22         "wasi:http/types@0.3.0-rc-2026-02-09": test_programs::p3::wasi::http::types,
23         "wasi:http/client@0.3.0-rc-2026-02-09": test_programs::p3::wasi::http::client,
24         "wasi:random/random@0.3.0-rc-2026-02-09": test_programs::p3::wasi::random::random,
25         "wasi:random/insecure@0.3.0-rc-2026-02-09": test_programs::p3::wasi::random::insecure,
26         "wasi:random/insecure-seed@0.3.0-rc-2026-02-09": test_programs::p3::wasi::random::insecure_seed,
27         "wasi:cli/stdout@0.3.0-rc-2026-02-09": test_programs::p3::wasi::cli::stdout,
28         "wasi:cli/stderr@0.3.0-rc-2026-02-09": test_programs::p3::wasi::cli::stderr,
29         "wasi:cli/stdin@0.3.0-rc-2026-02-09": test_programs::p3::wasi::cli::stdin,
30         "wasi:cli/types@0.3.0-rc-2026-02-09": test_programs::p3::wasi::cli::types,
31         "wasi:clocks/monotonic-clock@0.3.0-rc-2026-02-09": test_programs::p3::wasi::clocks::monotonic_clock,
32         "wasi:clocks/system-clock@0.3.0-rc-2026-02-09": test_programs::p3::wasi::clocks::system_clock,
33         "wasi:clocks/types@0.3.0-rc-2026-02-09": test_programs::p3::wasi::clocks::types,
34     },
35 });
36 
37 struct Component;
38 
39 export!(Component);
40 
41 impl exports::wasi::http::handler::Guest for Component {
42     /// Forward the specified request to the imported `wasi:http/handler`, transparently decoding the request body
43     /// if it is `deflate`d and then encoding the response body if the client has provided an `accept-encoding:
44     /// deflate` header.
45     async fn handle(request: Request) -> Result<Response, ErrorCode> {
46         // First, extract the parts of the request and check for (and remove) headers pertaining to body encodings.
47         let method = request.get_method();
48         let scheme = request.get_scheme();
49         let path_with_query = request.get_path_with_query();
50         let authority = request.get_authority();
51         let mut accept_deflated = false;
52         let mut content_deflated = false;
53         let headers = request.get_headers();
54         let mut headers = headers.copy_all();
55         headers.retain(|(k, v)| match (k.as_str(), v.as_slice()) {
56             ("accept-encoding", value)
57                 if std::str::from_utf8(value)
58                     .map(|v| v.contains("deflate"))
59                     .unwrap_or(false) =>
60             {
61                 accept_deflated = true;
62                 false
63             }
64             ("content-encoding", b"deflate") => {
65                 content_deflated = true;
66                 false
67             }
68             _ => true,
69         });
70         let (_, result_rx) = wit_future::new(|| Ok(()));
71         let (mut body, trailers) = Request::consume_body(request, result_rx);
72 
73         let (body, trailers) = if content_deflated {
74             // Next, spawn a task to pipe and decode the original request body and trailers into a new request
75             // we'll create below.  This will run concurrently with any code in the imported `wasi:http/handler`.
76             let (trailers_tx, trailers_rx) = wit_future::new(|| todo!());
77             let (mut pipe_tx, pipe_rx) = wit_stream::new();
78 
79             wit_bindgen::spawn(async move {
80                 {
81                     let mut decoder = DeflateDecoder::new(Vec::new());
82                     let mut status = StreamResult::Complete(0);
83                     let mut chunk = Vec::with_capacity(64 * 1024);
84 
85                     while let StreamResult::Complete(_) = status {
86                         (status, chunk) = body.read(chunk).await;
87                         decoder.write_all(&chunk).unwrap();
88                         let remaining = pipe_tx.write_all(mem::take(decoder.get_mut())).await;
89                         assert!(remaining.is_empty());
90                         *decoder.get_mut() = remaining;
91                         chunk.clear();
92                     }
93 
94                     let remaining = pipe_tx.write_all(decoder.finish().unwrap()).await;
95                     assert!(remaining.is_empty());
96 
97                     drop(pipe_tx);
98                 }
99 
100                 trailers_tx.write(trailers.await).await.unwrap();
101             });
102 
103             (pipe_rx, trailers_rx)
104         } else {
105             (body, trailers)
106         };
107 
108         // While the above task (if any) is running, synthesize a request from the parts collected above and pass
109         // it to the imported `wasi:http/handler`.
110         let (my_request, _request_complete) = Request::new(
111             Headers::from_list(&headers).unwrap(),
112             Some(body),
113             trailers,
114             None,
115         );
116         my_request.set_method(&method).unwrap();
117         my_request.set_scheme(scheme.as_ref()).unwrap();
118         my_request
119             .set_path_with_query(path_with_query.as_deref())
120             .unwrap();
121         my_request.set_authority(authority.as_deref()).unwrap();
122 
123         let response = handler::handle(my_request).await?;
124 
125         // Now that we have the response, extract the parts, adding an extra header if we'll be encoding the body.
126         let status_code = response.get_status_code();
127         let mut headers = response.get_headers().copy_all();
128         if accept_deflated {
129             headers.push(("content-encoding".into(), b"deflate".into()));
130         }
131 
132         let (_, result_rx) = wit_future::new(|| Ok(()));
133         let (mut body, trailers) = Response::consume_body(response, result_rx);
134         let (body, trailers) = if accept_deflated {
135             headers.retain(|(name, _value)| name != "content-length");
136 
137             // Spawn another task; this one is to pipe and encode the original response body and trailers into a
138             // new response we'll create below.  This will run concurrently with the caller's code (i.e. it won't
139             // necessarily complete before we return a value).
140             let (trailers_tx, trailers_rx) = wit_future::new(|| todo!());
141             let (mut pipe_tx, pipe_rx) = wit_stream::new();
142 
143             wit_bindgen::spawn(async move {
144                 {
145                     let mut encoder = DeflateEncoder::new(Vec::new(), Compression::fast());
146                     let mut status = StreamResult::Complete(0);
147                     let mut chunk = Vec::with_capacity(64 * 1024);
148 
149                     while let StreamResult::Complete(_) = status {
150                         (status, chunk) = body.read(chunk).await;
151                         encoder.write_all(&chunk).unwrap();
152                         let remaining = pipe_tx.write_all(mem::take(encoder.get_mut())).await;
153                         assert!(remaining.is_empty());
154                         *encoder.get_mut() = remaining;
155                         chunk.clear();
156                     }
157 
158                     let remaining = pipe_tx.write_all(encoder.finish().unwrap()).await;
159                     assert!(remaining.is_empty());
160 
161                     drop(pipe_tx);
162                 }
163 
164                 trailers_tx.write(trailers.await).await.unwrap();
165             });
166 
167             (pipe_rx, trailers_rx)
168         } else {
169             (body, trailers)
170         };
171 
172         // While the above tasks (if any) are running, synthesize a response from the parts collected above and
173         // return it.
174         let (my_response, _response_complete) =
175             Response::new(Headers::from_list(&headers).unwrap(), Some(body), trailers);
176         my_response.set_status_code(status_code).unwrap();
177 
178         Ok(my_response)
179     }
180 }
181 
182 // Unused function; required since this file is built as a `bin`:
183 fn main() {}
184