1pub mod http {
2 pub use http::*;
3}
4
5use anyhow::anyhow;
6pub type Result<T> = anyhow::Result<T>;
7pub type Error = anyhow::Error;
8
9mod bindings {
10 wit_bindgen::generate!({
11 path: "wit",
12 world: "http",
13 generate_all
14 });
15}
16
17#[cfg(feature = "serde")]
19pub use serde;
20
21#[cfg(feature = "serde_json")]
23pub use serde_json;
24
25pub use wrapper::*;
26
27mod wrapper {
29 use std::{
30 any::Any,
31 ops::{Deref, DerefMut},
32 };
33
34 use http::{request::Builder, Error, HeaderName, HeaderValue, Response, Uri, Version};
35
36 pub struct ByteResponse(Response<Vec<u8>>);
37
38 impl Deref for ByteResponse {
39 type Target = Response<Vec<u8>>;
40
41 fn deref(&self) -> &Self::Target {
42 &self.0
43 }
44 }
45
46 impl DerefMut for ByteResponse {
47 fn deref_mut(&mut self) -> &mut Self::Target {
48 &mut self.0
49 }
50 }
51
52 impl From<Response<Vec<u8>>> for ByteResponse {
53 fn from(response: Response<Vec<u8>>) -> Self {
54 ByteResponse(response)
55 }
56 }
57
58 impl ByteResponse {
59 pub fn text(self) -> anyhow::Result<String> {
60 Ok(String::from_utf8(self.0.into_body())?)
61 }
62
63 pub fn bytes(self) -> Vec<u8> {
64 self.0.into_body()
65 }
66
67 pub fn as_slice(&self) -> &[u8] {
68 self.0.body()
69 }
70
71 pub fn into_inner(self) -> Response<Vec<u8>> {
72 self.0
73 }
74 }
75
76 pub fn get<T>(uri: T) -> anyhow::Result<ByteResponse>
79 where
80 T: TryInto<Uri>,
81 <T as TryInto<Uri>>::Error: Into<Error>,
82 {
83 let body_helper = BodyBuilder::empty();
84 body_helper.get(uri)
85 }
86
87 pub fn json<T>(body: T) -> BodyBuilder<T> {
89 let request = crate::http::Request::builder().header("Content-Type", "application/json");
90 BodyBuilder {
91 body,
92 builder: request,
93 }
94 }
95
96 pub struct BodyBuilder<Body> {
99 body: Body,
100 builder: Builder,
101 }
102
103 impl BodyBuilder<Vec<u8>> {
104 pub fn empty() -> Self {
105 Self {
106 body: Vec::new(),
107 builder: crate::http::Request::builder(),
108 }
109 }
110 }
111
112 impl<Body> BodyBuilder<Body>
113 where
114 Body: AsRef<[u8]>,
115 {
116 pub fn body(self) -> Body {
117 self.body
118 }
119
120 pub fn body_ref(&self) -> &Body {
121 &self.body
122 }
123
124 pub fn version(self, version: Version) -> Self {
125 Self {
126 builder: self.builder.version(version),
127 body: self.body,
128 }
129 }
130
131 pub fn auth<V>(self, token: V) -> Self
133 where
134 V: TryInto<HeaderValue>,
135 <V as TryInto<HeaderValue>>::Error: Into<Error>,
136 {
137 self.header("Authorization", token)
138 }
139
140 pub fn bearer(self, token: &str) -> Self {
142 self.auth(format!("Bearer {}", token))
143 }
144
145 pub fn header<K, V>(self, key: K, value: V) -> Self
146 where
147 K: TryInto<HeaderName>,
148 <K as TryInto<HeaderName>>::Error: Into<Error>,
149 V: TryInto<HeaderValue>,
150 <V as TryInto<HeaderValue>>::Error: Into<Error>,
151 {
152 Self {
153 builder: self.builder.header(key, value),
154 body: self.body,
155 }
156 }
157
158 pub fn extension<T>(self, extension: T) -> Self
159 where
160 T: Clone + Any + Send + Sync + 'static,
161 {
162 Self {
163 builder: self.builder.extension(extension),
164 body: self.body,
165 }
166 }
167
168 pub fn post<T>(self, uri: T) -> anyhow::Result<ByteResponse>
170 where
171 T: TryInto<Uri>,
172 <T as TryInto<Uri>>::Error: Into<Error>,
173 {
174 let request = self
175 .builder
176 .uri(uri)
177 .method(http::Method::POST)
178 .body(self.body)?;
179 let response = crate::blocking::send(request)?;
180 Ok(response.into())
181 }
182
183 pub fn get<T>(self, uri: T) -> anyhow::Result<ByteResponse>
184 where
185 T: TryInto<Uri>,
186 <T as TryInto<Uri>>::Error: Into<Error>,
187 {
188 let request = self
189 .builder
190 .uri(uri)
191 .method(http::Method::GET)
192 .body(self.body)?;
193 let response = crate::blocking::send(request)?;
194 Ok(response.into())
195 }
196 }
197
198 impl<Body> Deref for BodyBuilder<Body> {
199 type Target = Builder;
200
201 fn deref(&self) -> &Self::Target {
202 &self.builder
203 }
204 }
205
206 impl<Body> DerefMut for BodyBuilder<Body> {
207 fn deref_mut(&mut self) -> &mut Self::Target {
208 &mut self.builder
209 }
210 }
211}
212
213pub mod blocking {
214 use anyhow::anyhow;
215
216 use crate::bindings::wasi::http::outgoing_handler;
217 use crate::bindings::wasi::http::types::{OutgoingBody, OutgoingRequest};
218 use crate::bindings::wasi::io::streams::StreamError;
219
220 use super::http::{Request, Response};
221 use super::Result;
222
223 pub fn send<T: AsRef<[u8]>>(request: Request<T>) -> Result<Response<Vec<u8>>> {
224 let request_wasi = OutgoingRequest::try_from(&request)?;
225
226 let request_body = request_wasi
227 .body()
228 .map_err(|_| anyhow!("outgoing request write failed"))?;
229 let output_stream = request_body
230 .write()
231 .map_err(|_| anyhow!("request has no input stream"))?;
232 output_stream.write(request.body().as_ref())?;
233 drop(output_stream);
234
235 let response_fut = outgoing_handler::handle(request_wasi, None)?;
236 OutgoingBody::finish(request_body, None)?;
237
238 let response_wasi = match response_fut.get() {
239 Some(result) => result.map_err(|_| anyhow!("response already taken"))?,
240 None => {
241 let pollable = response_fut.subscribe();
242 pollable.block();
243 response_fut
244 .get()
245 .ok_or_else(|| anyhow!("response available"))?
246 .map_err(|_| anyhow!("response already taken"))?
247 }
248 }?;
249
250 let mut response_builder = Response::builder();
251 response_builder =
252 response_builder.status(http::StatusCode::from_u16(response_wasi.status())?);
253
254 for (header, values) in response_wasi.headers().entries() {
255 response_builder = response_builder.header(header, values);
256 }
257
258 let body_wasi = response_wasi
259 .consume()
260 .map_err(|()| anyhow!("response has no body stream"))?;
261
262 let input_stream = body_wasi
263 .stream()
264 .map_err(|()| anyhow!("response body has no stream"))?;
265 let input_stream_pollable = input_stream.subscribe();
266
267 let mut body = Vec::new();
268 loop {
269 input_stream_pollable.block();
270 let mut body_chunk = match input_stream.read(1024 * 1024) {
271 Ok(c) => c,
272 Err(StreamError::Closed) => break,
273 Err(e) => Err(anyhow!("input stream read failed: {e:?}"))?,
274 };
275 if !body_chunk.is_empty() {
276 body.append(&mut body_chunk);
277 }
278 }
279 Ok(response_builder.body(body)?)
280 }
281}
282
283impl<T> TryFrom<&http::Request<T>> for bindings::wasi::http::types::OutgoingRequest {
284 type Error = Error;
285
286 fn try_from(request: &http::Request<T>) -> std::result::Result<Self, Self::Error> {
287 let headers = request.headers().try_into()?;
288 let request_wasi = Self::new(headers);
289
290 let method = request.method().into();
291 let scheme = request.uri().scheme().map(|s| s.into());
292 let authority = request.uri().authority().map(|a| a.as_str());
293 let path_and_query = request.uri().path_and_query().map(|a| a.as_str());
294
295 request_wasi
296 .set_method(&method)
297 .map_err(|_| anyhow!("invalid method"))?;
298 request_wasi
299 .set_scheme(scheme.as_ref())
300 .map_err(|_| anyhow!("invalid scheme"))?;
301 request_wasi
302 .set_authority(authority)
303 .map_err(|_| anyhow!("invalid authority"))?;
304 request_wasi
305 .set_path_with_query(path_and_query)
306 .map_err(|_| anyhow!("invalid path_and_query"))?;
307
308 Ok(request_wasi)
309 }
310}
311
312impl From<&http::Method> for bindings::wasi::http::types::Method {
313 fn from(value: &http::Method) -> Self {
314 match value.as_str() {
315 "OPTIONS" => Self::Options,
316 "GET" => Self::Get,
317 "POST" => Self::Post,
318 "PUT" => Self::Put,
319 "DELETE" => Self::Delete,
320 "HEAD" => Self::Head,
321 "TRACE" => Self::Trace,
322 "CONNECT" => Self::Connect,
323 "PATCH" => Self::Patch,
324 other => Self::Other(other.to_string()),
325 }
326 }
327}
328
329impl From<&http::uri::Scheme> for bindings::wasi::http::types::Scheme {
330 fn from(value: &http::uri::Scheme) -> Self {
331 match value.as_str() {
332 "https" | "HTTPS" => Self::Https,
333 _ => Self::Http,
334 }
335 }
336}
337
338impl TryFrom<&http::HeaderMap> for bindings::wasi::http::types::Headers {
339 type Error = bindings::wasi::http::types::HeaderError;
340
341 fn try_from(value: &http::HeaderMap) -> std::result::Result<Self, Self::Error> {
342 let headers = bindings::wasi::http::types::Headers::new();
343 for key in value.keys() {
344 let all: Vec<Vec<u8>> = value
345 .get_all(key)
346 .iter()
347 .flat_map(|v| v.to_str().ok())
348 .map(|v| v.as_bytes().to_vec())
349 .collect();
350 let key: String = key.to_string();
351 headers.set(&key, &all)?;
352 }
353 Ok(headers)
354 }
355}