wasmtime_wasi_http/p2/
http_impl.rs1use crate::WasiHttpCtxView;
4use crate::p2::{
5 HttpResult,
6 bindings::http::{
7 outgoing_handler,
8 types::{self, Scheme},
9 },
10 error::internal_error,
11 http_request_error,
12 types::{HostFutureIncomingResponse, HostOutgoingRequest},
13};
14use bytes::Bytes;
15use http_body_util::{BodyExt, Empty};
16use hyper::Method;
17use std::pin::Pin;
18use wasmtime::component::Resource;
19
20impl outgoing_handler::Host for WasiHttpCtxView<'_> {
21 fn handle(
22 &mut self,
23 request_id: Resource<HostOutgoingRequest>,
24 options: Option<Resource<types::RequestOptions>>,
25 ) -> HttpResult<Resource<HostFutureIncomingResponse>> {
26 let opts = match options {
29 Some(id) => Some(self.table.delete(id)?),
30 None => None,
31 };
32
33 let req = self.table.delete(request_id)?;
34 let mut builder = hyper::Request::builder();
35
36 builder = builder.method(match req.method {
37 types::Method::Get => Method::GET,
38 types::Method::Head => Method::HEAD,
39 types::Method::Post => Method::POST,
40 types::Method::Put => Method::PUT,
41 types::Method::Delete => Method::DELETE,
42 types::Method::Connect => Method::CONNECT,
43 types::Method::Options => Method::OPTIONS,
44 types::Method::Trace => Method::TRACE,
45 types::Method::Patch => Method::PATCH,
46 types::Method::Other(m) => match hyper::Method::from_bytes(m.as_bytes()) {
47 Ok(method) => method,
48 Err(_) => return Err(types::ErrorCode::HttpRequestMethodInvalid.into()),
49 },
50 });
51
52 let scheme = match req.scheme {
53 Some(scheme) => {
54 let scheme = match scheme {
55 Scheme::Http => http::uri::Scheme::HTTP,
56 Scheme::Https => http::uri::Scheme::HTTPS,
57 Scheme::Other(scheme) => http::uri::Scheme::try_from(scheme.as_str())
58 .map_err(|_| types::ErrorCode::HttpProtocolError)?,
59 };
60 if !self.hooks.is_supported_scheme(&scheme) {
61 return Err(types::ErrorCode::HttpProtocolError.into());
62 }
63 scheme
64 }
65 None => self
68 .hooks
69 .default_scheme()
70 .ok_or(types::ErrorCode::HttpProtocolError)?,
71 };
72
73 let authority = req.authority.unwrap_or_else(String::new);
74
75 let mut uri = http::Uri::builder()
76 .scheme(scheme)
77 .authority(authority.clone());
78
79 if let Some(path) = req.path_with_query {
80 uri = uri.path_and_query(path);
81 }
82
83 builder = builder.uri(uri.build().map_err(http_request_error)?);
84
85 if self.hooks.set_host_header() {
86 builder = builder.header(http::header::HOST, authority.as_str());
87 }
88
89 for (k, v) in req.headers.iter() {
90 builder = builder.header(k, v);
91 }
92
93 let body = req.body.unwrap_or_else(|| {
94 Empty::<Bytes>::new()
95 .map_err(|_| unreachable!("Infallible error"))
96 .boxed_unsync()
97 });
98 let body = body.map_err(Into::into).boxed_unsync();
99
100 let request = builder
101 .body(body)
102 .map_err(|err| internal_error(err.to_string()))?;
103
104 let future = self
105 .hooks
106 .send_request(request, opts, Box::new(async { Ok(()) }));
107 let future = wasmtime_wasi::runtime::spawn(async move {
108 let (res, io) = Pin::from(future).await?;
109 let io = wasmtime_wasi::runtime::spawn(async move {
110 match Pin::from(io).await {
111 Ok(()) => {}
112 Err(e) => tracing::warn!("dropping error {e}"),
115 }
116 });
117 let res = res.map(|b| b.boxed_unsync());
118 Ok((res, io))
119 });
120
121 Ok(self
122 .table
123 .push(HostFutureIncomingResponse::Pending(future))?)
124 }
125}
126
127mod named {
128 use crate::WasiHttpNamedView;
129 use crate::p2::bindings::http::types;
130 use crate::p2::bindings::named_imports::wasi::http::outgoing_handler;
131 use crate::p2::types::{HostFutureIncomingResponse, HostOutgoingRequest};
132 use crate::p2::{HttpError, HttpResult};
133 use wasmtime::component::Resource;
134 use wasmtime_wasi::{NamedId, WasiCtxNamedView};
135
136 impl<T> outgoing_handler::Host for WasiCtxNamedView<'_, T>
137 where
138 T: WasiHttpNamedView,
139 {
140 fn handle(
141 &mut self,
142 id: NamedId,
143 request_id: Resource<HostOutgoingRequest>,
144 options: Option<Resource<types::RequestOptions>>,
145 ) -> HttpResult<Resource<HostFutureIncomingResponse>> {
146 super::outgoing_handler::Host::handle(&mut self.0.http(id), request_id, options)
147 }
148
149 fn convert_error_code(&mut self, err: HttpError) -> wasmtime::Result<types::ErrorCode> {
150 err.downcast()
151 }
152 }
153}