wasmtime_wasi_http/p2/
types.rs1use crate::p2::{
5 bindings::http::types::{self, Method, Scheme},
6 body::{HostIncomingBody, HyperIncomingBody, HyperOutgoingBody},
7};
8use crate::{Error, ErrorResponse, FieldMap, WasiHttpCtxView};
9use bytes::Bytes;
10use http_body_util::BodyExt;
11use hyper::body::Body;
12use wasmtime::Result;
13use wasmtime::component::Resource;
14use wasmtime_wasi::p2::Pollable;
15use wasmtime_wasi::runtime::AbortOnDropJoinHandle;
16
17impl From<http::Method> for types::Method {
18 fn from(method: http::Method) -> Self {
19 if method == http::Method::GET {
20 types::Method::Get
21 } else if method == hyper::Method::HEAD {
22 types::Method::Head
23 } else if method == hyper::Method::POST {
24 types::Method::Post
25 } else if method == hyper::Method::PUT {
26 types::Method::Put
27 } else if method == hyper::Method::DELETE {
28 types::Method::Delete
29 } else if method == hyper::Method::CONNECT {
30 types::Method::Connect
31 } else if method == hyper::Method::OPTIONS {
32 types::Method::Options
33 } else if method == hyper::Method::TRACE {
34 types::Method::Trace
35 } else if method == hyper::Method::PATCH {
36 types::Method::Patch
37 } else {
38 types::Method::Other(method.to_string())
39 }
40 }
41}
42
43impl TryInto<http::Method> for types::Method {
44 type Error = http::method::InvalidMethod;
45
46 fn try_into(self) -> Result<http::Method, Self::Error> {
47 match self {
48 Method::Get => Ok(http::Method::GET),
49 Method::Head => Ok(http::Method::HEAD),
50 Method::Post => Ok(http::Method::POST),
51 Method::Put => Ok(http::Method::PUT),
52 Method::Delete => Ok(http::Method::DELETE),
53 Method::Connect => Ok(http::Method::CONNECT),
54 Method::Options => Ok(http::Method::OPTIONS),
55 Method::Trace => Ok(http::Method::TRACE),
56 Method::Patch => Ok(http::Method::PATCH),
57 Method::Other(s) => http::Method::from_bytes(s.as_bytes()),
58 }
59 }
60}
61
62#[derive(Debug)]
64pub struct HostIncomingRequest {
65 pub(crate) method: http::method::Method,
66 pub(crate) uri: http::uri::Uri,
67 pub(crate) headers: FieldMap,
68 pub(crate) scheme: Scheme,
69 pub(crate) authority: String,
70 pub body: Option<HostIncomingBody>,
72}
73
74impl WasiHttpCtxView<'_> {
75 pub fn new_incoming_request<B>(
77 &mut self,
78 scheme: Scheme,
79 req: hyper::Request<B>,
80 ) -> wasmtime::Result<Resource<HostIncomingRequest>>
81 where
82 B: Body<Data = Bytes> + Send + 'static,
83 B::Error: Into<Error>,
84 {
85 let (parts, body) = req.into_parts();
86 let body = body.map_err(Into::into).boxed_unsync();
87 let body = HostIncomingBody::new(body);
88 let authority = match parts.uri.authority() {
89 Some(authority) => authority.to_string(),
90 None => match parts.headers.get(http::header::HOST) {
91 Some(host) => host.to_str()?.to_string(),
92 None => {
93 return Err(wasmtime::Error::msg(
94 "invalid HTTP request missing authority in URI and host header",
95 )
96 .context(ErrorResponse::new(http::StatusCode::BAD_REQUEST)));
97 }
98 },
99 };
100
101 let headers = FieldMap::new_immutable(self.hooks, parts.headers);
102
103 let req = HostIncomingRequest {
104 method: parts.method,
105 uri: parts.uri,
106 headers,
107 authority,
108 scheme,
109 body: Some(body),
110 };
111 Ok(self.table.push(req)?)
112 }
113}
114
115pub struct HostResponseOutparam {
117 pub send:
119 Box<dyn FnOnce(Result<hyper::Response<HyperOutgoingBody>, types::ErrorCode>) + Send + Sync>,
120}
121
122impl WasiHttpCtxView<'_> {
123 pub fn new_response_outparam(
125 &mut self,
126 result: tokio::sync::oneshot::Sender<
127 Result<hyper::Response<HyperOutgoingBody>, types::ErrorCode>,
128 >,
129 ) -> wasmtime::Result<Resource<HostResponseOutparam>> {
130 let id = self.table.push(HostResponseOutparam {
131 send: Box::new(move |value| {
132 _ = result.send(value)
137 }),
138 })?;
139 Ok(id)
140 }
141
142 pub fn new_response_outparam_from_callback(
144 &mut self,
145 callback: impl FnOnce(Result<hyper::Response<HyperOutgoingBody>, types::ErrorCode>)
146 + Send
147 + Sync
148 + 'static,
149 ) -> wasmtime::Result<Resource<HostResponseOutparam>> {
150 let id = self.table.push(HostResponseOutparam {
151 send: Box::new(callback),
152 })?;
153 Ok(id)
154 }
155}
156
157pub struct HostOutgoingResponse {
159 pub status: http::StatusCode,
161 pub headers: FieldMap,
163 pub body: Option<HyperOutgoingBody>,
165}
166
167impl TryFrom<HostOutgoingResponse> for hyper::Response<HyperOutgoingBody> {
168 type Error = http::Error;
169
170 fn try_from(
171 resp: HostOutgoingResponse,
172 ) -> Result<hyper::Response<HyperOutgoingBody>, Self::Error> {
173 use http_body_util::Empty;
174
175 let mut builder = hyper::Response::builder().status(resp.status);
176
177 *builder.headers_mut().unwrap() = resp.headers.into();
178
179 match resp.body {
180 Some(body) => builder.body(body),
181 None => builder.body(
182 Empty::<bytes::Bytes>::new()
183 .map_err(|_| unreachable!("Infallible error"))
184 .boxed_unsync(),
185 ),
186 }
187 }
188}
189
190#[derive(Debug)]
192pub struct HostOutgoingRequest {
193 pub method: Method,
195 pub scheme: Option<Scheme>,
197 pub authority: Option<String>,
199 pub path_with_query: Option<String>,
201 pub headers: FieldMap,
203 pub body: Option<HyperOutgoingBody>,
205}
206
207#[derive(Debug)]
209pub struct HostIncomingResponse {
210 pub status: u16,
212 pub headers: FieldMap,
214 pub body: Option<HostIncomingBody>,
216}
217
218pub type FutureIncomingResponseHandle = AbortOnDropJoinHandle<SendRequestResult>;
220
221#[derive(Debug)]
223pub struct IncomingResponse {
224 pub resp: hyper::Response<HyperIncomingBody>,
226 pub worker: Option<AbortOnDropJoinHandle<()>>,
228}
229
230type SendRequestResult =
231 crate::Result<(http::Response<HyperIncomingBody>, AbortOnDropJoinHandle<()>)>;
232
233pub enum HostFutureIncomingResponse {
235 Pending(FutureIncomingResponseHandle),
237 Ready(SendRequestResult),
241 Consumed,
243}
244
245impl HostFutureIncomingResponse {
246 pub(crate) fn unwrap_ready(self) -> SendRequestResult {
248 match self {
249 Self::Ready(res) => res,
250 Self::Pending(_) | Self::Consumed => {
251 panic!("unwrap_ready called on a pending HostFutureIncomingResponse")
252 }
253 }
254 }
255}
256
257#[async_trait::async_trait]
258impl Pollable for HostFutureIncomingResponse {
259 async fn ready(&mut self) {
260 if let Self::Pending(handle) = self {
261 *self = Self::Ready(handle.await);
262 }
263 }
264}