1 //! Implementation for the `wasi:http/types` interface.
2 
3 use crate::FieldMap;
4 use crate::get_content_length;
5 use crate::p2::bindings::http::types::{self, Method, Scheme, StatusCode, Trailers};
6 use crate::p2::body::{HostFutureTrailers, HostIncomingBody, HostOutgoingBody, StreamContext};
7 use crate::p2::types::{
8     HostFutureIncomingResponse, HostIncomingRequest, HostIncomingResponse, HostOutgoingRequest,
9     HostOutgoingResponse, HostResponseOutparam, remove_forbidden_headers,
10 };
11 use crate::p2::{HeaderError, HeaderResult, HttpError, HttpResult, WasiHttpCtxView};
12 use http::{HeaderName, HeaderValue};
13 use std::str::FromStr;
14 use wasmtime::component::Resource;
15 use wasmtime::{error::Context as _, format_err};
16 use wasmtime_wasi::p2::{DynInputStream, DynOutputStream, DynPollable};
17 
18 impl types::Host for WasiHttpCtxView<'_> {
convert_error_code(&mut self, err: HttpError) -> wasmtime::Result<types::ErrorCode>19     fn convert_error_code(&mut self, err: HttpError) -> wasmtime::Result<types::ErrorCode> {
20         err.downcast()
21     }
22 
convert_header_error(&mut self, err: HeaderError) -> wasmtime::Result<types::HeaderError>23     fn convert_header_error(&mut self, err: HeaderError) -> wasmtime::Result<types::HeaderError> {
24         err.downcast()
25     }
26 
http_error_code( &mut self, err: wasmtime::component::Resource<types::IoError>, ) -> wasmtime::Result<Option<types::ErrorCode>>27     fn http_error_code(
28         &mut self,
29         err: wasmtime::component::Resource<types::IoError>,
30     ) -> wasmtime::Result<Option<types::ErrorCode>> {
31         let e = self.table.get(&err)?;
32         Ok(e.downcast_ref::<types::ErrorCode>().cloned())
33     }
34 }
35 
36 impl types::HostFields for WasiHttpCtxView<'_> {
new(&mut self) -> wasmtime::Result<Resource<FieldMap>>37     fn new(&mut self) -> wasmtime::Result<Resource<FieldMap>> {
38         let limit = self.ctx.field_size_limit;
39         let id = self
40             .table
41             .push(FieldMap::new_mutable(limit))
42             .context("[new_fields] pushing fields")?;
43 
44         Ok(id)
45     }
46 
from_list(&mut self, entries: Vec<(String, Vec<u8>)>) -> HeaderResult<Resource<FieldMap>>47     fn from_list(&mut self, entries: Vec<(String, Vec<u8>)>) -> HeaderResult<Resource<FieldMap>> {
48         let mut fields = FieldMap::new_mutable(self.ctx.field_size_limit);
49 
50         for (header, value) in entries {
51             let header = HeaderName::from_bytes(header.as_bytes())?;
52             if self.hooks.is_forbidden_header(&header) {
53                 return Err(types::HeaderError::Forbidden.into());
54             }
55             let value = HeaderValue::from_bytes(&value)?;
56             fields.append(header, value)?;
57         }
58 
59         Ok(self.table.push(fields)?)
60     }
61 
drop(&mut self, fields: Resource<FieldMap>) -> wasmtime::Result<()>62     fn drop(&mut self, fields: Resource<FieldMap>) -> wasmtime::Result<()> {
63         self.table
64             .delete(fields)
65             .context("[drop_fields] deleting fields")?;
66         Ok(())
67     }
68 
get(&mut self, fields: Resource<FieldMap>, name: String) -> wasmtime::Result<Vec<Vec<u8>>>69     fn get(&mut self, fields: Resource<FieldMap>, name: String) -> wasmtime::Result<Vec<Vec<u8>>> {
70         let fields = self.table.get(&fields)?;
71 
72         let header = match HeaderName::from_bytes(name.as_bytes()) {
73             Ok(header) => header,
74             Err(_) => return Ok(vec![]),
75         };
76 
77         if !fields.contains_key(&header) {
78             return Ok(vec![]);
79         }
80 
81         let res = fields
82             .get_all(&header)
83             .into_iter()
84             .map(|val| val.as_bytes().to_owned())
85             .collect();
86         Ok(res)
87     }
88 
has(&mut self, fields: Resource<FieldMap>, name: String) -> wasmtime::Result<bool>89     fn has(&mut self, fields: Resource<FieldMap>, name: String) -> wasmtime::Result<bool> {
90         let fields = self.table.get(&fields)?;
91 
92         match HeaderName::from_bytes(name.as_bytes()) {
93             Ok(header) => Ok(fields.contains_key(&header)),
94             Err(_) => Ok(false),
95         }
96     }
97 
set( &mut self, fields: Resource<FieldMap>, name: String, byte_values: Vec<Vec<u8>>, ) -> HeaderResult<()>98     fn set(
99         &mut self,
100         fields: Resource<FieldMap>,
101         name: String,
102         byte_values: Vec<Vec<u8>>,
103     ) -> HeaderResult<()> {
104         let header = HeaderName::from_bytes(name.as_bytes())?;
105 
106         if self.hooks.is_forbidden_header(&header) {
107             return Err(types::HeaderError::Forbidden.into());
108         }
109 
110         let mut values = Vec::with_capacity(byte_values.len());
111         for value in byte_values {
112             values.push(HeaderValue::from_bytes(&value)?);
113         }
114 
115         let fields = self.table.get_mut(&fields)?;
116         fields.set(header, values)?;
117         Ok(())
118     }
119 
delete(&mut self, fields: Resource<FieldMap>, name: String) -> HeaderResult<()>120     fn delete(&mut self, fields: Resource<FieldMap>, name: String) -> HeaderResult<()> {
121         let header = HeaderName::from_bytes(name.as_bytes())?;
122 
123         if self.hooks.is_forbidden_header(&header) {
124             return Err(types::HeaderError::Forbidden.into());
125         }
126 
127         let fields = self.table.get_mut(&fields)?;
128         fields.remove_all(header)?;
129         Ok(())
130     }
131 
append( &mut self, fields: Resource<FieldMap>, name: String, value: Vec<u8>, ) -> HeaderResult<()>132     fn append(
133         &mut self,
134         fields: Resource<FieldMap>,
135         name: String,
136         value: Vec<u8>,
137     ) -> HeaderResult<()> {
138         let header = HeaderName::from_bytes(name.as_bytes())?;
139 
140         if self.hooks.is_forbidden_header(&header) {
141             return Err(types::HeaderError::Forbidden.into());
142         }
143 
144         let value = HeaderValue::from_bytes(&value)?;
145 
146         let fields = self.table.get_mut(&fields)?;
147         fields.append(header, value)?;
148         Ok(())
149     }
150 
entries(&mut self, fields: Resource<FieldMap>) -> wasmtime::Result<Vec<(String, Vec<u8>)>>151     fn entries(&mut self, fields: Resource<FieldMap>) -> wasmtime::Result<Vec<(String, Vec<u8>)>> {
152         Ok(self
153             .table
154             .get(&fields)?
155             .iter()
156             .map(|(name, value)| (name.as_str().to_owned(), value.as_bytes().to_owned()))
157             .collect())
158     }
159 
clone(&mut self, fields: Resource<FieldMap>) -> wasmtime::Result<Resource<FieldMap>>160     fn clone(&mut self, fields: Resource<FieldMap>) -> wasmtime::Result<Resource<FieldMap>> {
161         let mut fields = self.table.get(&fields)?.clone();
162         fields.set_mutable(self.ctx.field_size_limit);
163         let id = self.table.push(fields)?;
164         Ok(id)
165     }
166 }
167 
168 impl types::HostIncomingRequest for WasiHttpCtxView<'_> {
method(&mut self, id: Resource<HostIncomingRequest>) -> wasmtime::Result<Method>169     fn method(&mut self, id: Resource<HostIncomingRequest>) -> wasmtime::Result<Method> {
170         let method = self.table.get(&id)?.method.clone();
171         Ok(method.into())
172     }
path_with_query( &mut self, id: Resource<HostIncomingRequest>, ) -> wasmtime::Result<Option<String>>173     fn path_with_query(
174         &mut self,
175         id: Resource<HostIncomingRequest>,
176     ) -> wasmtime::Result<Option<String>> {
177         let req = self.table.get(&id)?;
178         Ok(req
179             .uri
180             .path_and_query()
181             .map(|path_and_query| path_and_query.as_str().to_owned()))
182     }
scheme(&mut self, id: Resource<HostIncomingRequest>) -> wasmtime::Result<Option<Scheme>>183     fn scheme(&mut self, id: Resource<HostIncomingRequest>) -> wasmtime::Result<Option<Scheme>> {
184         let req = self.table.get(&id)?;
185         Ok(Some(req.scheme.clone()))
186     }
authority(&mut self, id: Resource<HostIncomingRequest>) -> wasmtime::Result<Option<String>>187     fn authority(&mut self, id: Resource<HostIncomingRequest>) -> wasmtime::Result<Option<String>> {
188         let req = self.table.get(&id)?;
189         Ok(Some(req.authority.clone()))
190     }
191 
headers( &mut self, id: Resource<HostIncomingRequest>, ) -> wasmtime::Result<Resource<FieldMap>>192     fn headers(
193         &mut self,
194         id: Resource<HostIncomingRequest>,
195     ) -> wasmtime::Result<Resource<FieldMap>> {
196         let req = self.table.get(&id)?;
197         Ok(self.table.push(req.headers.clone())?)
198     }
199 
consume( &mut self, id: Resource<HostIncomingRequest>, ) -> wasmtime::Result<Result<Resource<HostIncomingBody>, ()>>200     fn consume(
201         &mut self,
202         id: Resource<HostIncomingRequest>,
203     ) -> wasmtime::Result<Result<Resource<HostIncomingBody>, ()>> {
204         let req = self.table.get_mut(&id)?;
205         match req.body.take() {
206             Some(body) => {
207                 let id = self.table.push(body)?;
208                 Ok(Ok(id))
209             }
210 
211             None => Ok(Err(())),
212         }
213     }
214 
drop(&mut self, id: Resource<HostIncomingRequest>) -> wasmtime::Result<()>215     fn drop(&mut self, id: Resource<HostIncomingRequest>) -> wasmtime::Result<()> {
216         let _ = self.table.delete(id)?;
217         Ok(())
218     }
219 }
220 
221 impl types::HostOutgoingRequest for WasiHttpCtxView<'_> {
new( &mut self, headers: Resource<FieldMap>, ) -> wasmtime::Result<Resource<HostOutgoingRequest>>222     fn new(
223         &mut self,
224         headers: Resource<FieldMap>,
225     ) -> wasmtime::Result<Resource<HostOutgoingRequest>> {
226         let mut headers = self.table.delete(headers)?;
227         headers.set_immutable();
228 
229         self.table
230             .push(HostOutgoingRequest {
231                 path_with_query: None,
232                 authority: None,
233                 method: types::Method::Get,
234                 headers,
235                 scheme: None,
236                 body: None,
237             })
238             .context("[new_outgoing_request] pushing request")
239     }
240 
body( &mut self, request: Resource<HostOutgoingRequest>, ) -> wasmtime::Result<Result<Resource<HostOutgoingBody>, ()>>241     fn body(
242         &mut self,
243         request: Resource<HostOutgoingRequest>,
244     ) -> wasmtime::Result<Result<Resource<HostOutgoingBody>, ()>> {
245         let buffer_chunks = self.hooks.outgoing_body_buffer_chunks();
246         let chunk_size = self.hooks.outgoing_body_chunk_size();
247         let req = self
248             .table
249             .get_mut(&request)
250             .context("[outgoing_request_write] getting request")?;
251 
252         if req.body.is_some() {
253             return Ok(Err(()));
254         }
255 
256         let size = match get_content_length(&req.headers) {
257             Ok(size) => size,
258             Err(..) => return Ok(Err(())),
259         };
260 
261         let (host_body, hyper_body) =
262             HostOutgoingBody::new(StreamContext::Request, size, buffer_chunks, chunk_size);
263 
264         req.body = Some(hyper_body);
265 
266         // The output stream will necessarily outlive the request, because we could be still
267         // writing to the stream after `outgoing-handler.handle` is called.
268         let outgoing_body = self.table.push(host_body)?;
269 
270         Ok(Ok(outgoing_body))
271     }
272 
drop(&mut self, request: Resource<HostOutgoingRequest>) -> wasmtime::Result<()>273     fn drop(&mut self, request: Resource<HostOutgoingRequest>) -> wasmtime::Result<()> {
274         let _ = self.table.delete(request)?;
275         Ok(())
276     }
277 
method( &mut self, request: wasmtime::component::Resource<types::OutgoingRequest>, ) -> wasmtime::Result<Method>278     fn method(
279         &mut self,
280         request: wasmtime::component::Resource<types::OutgoingRequest>,
281     ) -> wasmtime::Result<Method> {
282         Ok(self.table.get(&request)?.method.clone())
283     }
284 
set_method( &mut self, request: wasmtime::component::Resource<types::OutgoingRequest>, method: Method, ) -> wasmtime::Result<Result<(), ()>>285     fn set_method(
286         &mut self,
287         request: wasmtime::component::Resource<types::OutgoingRequest>,
288         method: Method,
289     ) -> wasmtime::Result<Result<(), ()>> {
290         let req = self.table.get_mut(&request)?;
291 
292         if let Method::Other(s) = &method {
293             if let Err(_) = http::Method::from_str(s) {
294                 return Ok(Err(()));
295             }
296         }
297 
298         req.method = method;
299 
300         Ok(Ok(()))
301     }
302 
path_with_query( &mut self, request: wasmtime::component::Resource<types::OutgoingRequest>, ) -> wasmtime::Result<Option<String>>303     fn path_with_query(
304         &mut self,
305         request: wasmtime::component::Resource<types::OutgoingRequest>,
306     ) -> wasmtime::Result<Option<String>> {
307         Ok(self.table.get(&request)?.path_with_query.clone())
308     }
309 
set_path_with_query( &mut self, request: wasmtime::component::Resource<types::OutgoingRequest>, path_with_query: Option<String>, ) -> wasmtime::Result<Result<(), ()>>310     fn set_path_with_query(
311         &mut self,
312         request: wasmtime::component::Resource<types::OutgoingRequest>,
313         path_with_query: Option<String>,
314     ) -> wasmtime::Result<Result<(), ()>> {
315         let req = self.table.get_mut(&request)?;
316 
317         if let Some(s) = path_with_query.as_ref() {
318             if let Err(_) = http::uri::PathAndQuery::from_str(s) {
319                 return Ok(Err(()));
320             }
321         }
322 
323         req.path_with_query = path_with_query;
324 
325         Ok(Ok(()))
326     }
327 
scheme( &mut self, request: wasmtime::component::Resource<types::OutgoingRequest>, ) -> wasmtime::Result<Option<Scheme>>328     fn scheme(
329         &mut self,
330         request: wasmtime::component::Resource<types::OutgoingRequest>,
331     ) -> wasmtime::Result<Option<Scheme>> {
332         Ok(self.table.get(&request)?.scheme.clone())
333     }
334 
set_scheme( &mut self, request: wasmtime::component::Resource<types::OutgoingRequest>, scheme: Option<Scheme>, ) -> wasmtime::Result<Result<(), ()>>335     fn set_scheme(
336         &mut self,
337         request: wasmtime::component::Resource<types::OutgoingRequest>,
338         scheme: Option<Scheme>,
339     ) -> wasmtime::Result<Result<(), ()>> {
340         let req = self.table.get_mut(&request)?;
341 
342         if let Some(types::Scheme::Other(s)) = scheme.as_ref() {
343             if let Err(_) = http::uri::Scheme::from_str(s.as_str()) {
344                 return Ok(Err(()));
345             }
346         }
347 
348         req.scheme = scheme;
349 
350         Ok(Ok(()))
351     }
352 
authority( &mut self, request: wasmtime::component::Resource<types::OutgoingRequest>, ) -> wasmtime::Result<Option<String>>353     fn authority(
354         &mut self,
355         request: wasmtime::component::Resource<types::OutgoingRequest>,
356     ) -> wasmtime::Result<Option<String>> {
357         Ok(self.table.get(&request)?.authority.clone())
358     }
359 
set_authority( &mut self, request: wasmtime::component::Resource<types::OutgoingRequest>, authority: Option<String>, ) -> wasmtime::Result<Result<(), ()>>360     fn set_authority(
361         &mut self,
362         request: wasmtime::component::Resource<types::OutgoingRequest>,
363         authority: Option<String>,
364     ) -> wasmtime::Result<Result<(), ()>> {
365         let req = self.table.get_mut(&request)?;
366 
367         if let Some(s) = authority.as_ref() {
368             if let Err(_) = http::uri::Authority::from_str(s.as_str()) {
369                 return Ok(Err(()));
370             }
371         }
372 
373         req.authority = authority;
374 
375         Ok(Ok(()))
376     }
377 
headers( &mut self, request: wasmtime::component::Resource<types::OutgoingRequest>, ) -> wasmtime::Result<wasmtime::component::Resource<FieldMap>>378     fn headers(
379         &mut self,
380         request: wasmtime::component::Resource<types::OutgoingRequest>,
381     ) -> wasmtime::Result<wasmtime::component::Resource<FieldMap>> {
382         let req = self.table.get(&request)?;
383         let id = self.table.push(req.headers.clone())?;
384         Ok(id)
385     }
386 }
387 
388 impl types::HostResponseOutparam for WasiHttpCtxView<'_> {
drop(&mut self, id: Resource<HostResponseOutparam>) -> wasmtime::Result<()>389     fn drop(&mut self, id: Resource<HostResponseOutparam>) -> wasmtime::Result<()> {
390         let _ = self.table.delete(id)?;
391         Ok(())
392     }
set( &mut self, id: Resource<HostResponseOutparam>, resp: Result<Resource<HostOutgoingResponse>, types::ErrorCode>, ) -> wasmtime::Result<()>393     fn set(
394         &mut self,
395         id: Resource<HostResponseOutparam>,
396         resp: Result<Resource<HostOutgoingResponse>, types::ErrorCode>,
397     ) -> wasmtime::Result<()> {
398         let val = match resp {
399             Ok(resp) => Ok(self.table.delete(resp)?.try_into()?),
400             Err(e) => Err(e),
401         };
402 
403         let resp = self.table.delete(id)?;
404         // Giving the API doesn't return any error, it's probably
405         // better to ignore the error than trap the guest, in case of
406         // host timeout and dropped the receiver side of the channel.
407         // See also: #10784
408         let _ = resp.result.send(val);
409         Ok(())
410     }
411 
send_informational( &mut self, _id: Resource<HostResponseOutparam>, _status: u16, _headers: Resource<FieldMap>, ) -> HttpResult<()>412     fn send_informational(
413         &mut self,
414         _id: Resource<HostResponseOutparam>,
415         _status: u16,
416         _headers: Resource<FieldMap>,
417     ) -> HttpResult<()> {
418         Err(HttpError::trap(format_err!("not implemented")))
419     }
420 }
421 
422 impl types::HostIncomingResponse for WasiHttpCtxView<'_> {
drop(&mut self, response: Resource<HostIncomingResponse>) -> wasmtime::Result<()>423     fn drop(&mut self, response: Resource<HostIncomingResponse>) -> wasmtime::Result<()> {
424         let _ = self
425             .table
426             .delete(response)
427             .context("[drop_incoming_response] deleting response")?;
428         Ok(())
429     }
430 
status(&mut self, response: Resource<HostIncomingResponse>) -> wasmtime::Result<StatusCode>431     fn status(&mut self, response: Resource<HostIncomingResponse>) -> wasmtime::Result<StatusCode> {
432         let r = self
433             .table
434             .get(&response)
435             .context("[incoming_response_status] getting response")?;
436         Ok(r.status)
437     }
438 
headers( &mut self, response: Resource<HostIncomingResponse>, ) -> wasmtime::Result<Resource<FieldMap>>439     fn headers(
440         &mut self,
441         response: Resource<HostIncomingResponse>,
442     ) -> wasmtime::Result<Resource<FieldMap>> {
443         let resp = self.table.get(&response)?;
444         let id = self.table.push(resp.headers.clone())?;
445         Ok(id)
446     }
447 
consume( &mut self, response: Resource<HostIncomingResponse>, ) -> wasmtime::Result<Result<Resource<HostIncomingBody>, ()>>448     fn consume(
449         &mut self,
450         response: Resource<HostIncomingResponse>,
451     ) -> wasmtime::Result<Result<Resource<HostIncomingBody>, ()>> {
452         let r = self
453             .table
454             .get_mut(&response)
455             .context("[incoming_response_consume] getting response")?;
456 
457         match r.body.take() {
458             Some(body) => {
459                 let id = self.table.push(body)?;
460                 Ok(Ok(id))
461             }
462 
463             None => Ok(Err(())),
464         }
465     }
466 }
467 
468 impl types::HostFutureTrailers for WasiHttpCtxView<'_> {
drop(&mut self, id: Resource<HostFutureTrailers>) -> wasmtime::Result<()>469     fn drop(&mut self, id: Resource<HostFutureTrailers>) -> wasmtime::Result<()> {
470         let _ = self
471             .table
472             .delete(id)
473             .context("[drop future-trailers] deleting future-trailers")?;
474         Ok(())
475     }
476 
subscribe( &mut self, index: Resource<HostFutureTrailers>, ) -> wasmtime::Result<Resource<DynPollable>>477     fn subscribe(
478         &mut self,
479         index: Resource<HostFutureTrailers>,
480     ) -> wasmtime::Result<Resource<DynPollable>> {
481         wasmtime_wasi::p2::subscribe(self.table, index)
482     }
483 
get( &mut self, id: Resource<HostFutureTrailers>, ) -> wasmtime::Result<Option<Result<Result<Option<Resource<Trailers>>, types::ErrorCode>, ()>>>484     fn get(
485         &mut self,
486         id: Resource<HostFutureTrailers>,
487     ) -> wasmtime::Result<Option<Result<Result<Option<Resource<Trailers>>, types::ErrorCode>, ()>>>
488     {
489         let trailers = self.table.get_mut(&id)?;
490         match trailers {
491             HostFutureTrailers::Waiting { .. } => return Ok(None),
492             HostFutureTrailers::Consumed => return Ok(Some(Err(()))),
493             HostFutureTrailers::Done(_) => {}
494         };
495 
496         let res = match std::mem::replace(trailers, HostFutureTrailers::Consumed) {
497             HostFutureTrailers::Done(res) => res,
498             _ => unreachable!(),
499         };
500 
501         let mut fields = match res {
502             Ok(Some(fields)) => fields,
503             Ok(None) => return Ok(Some(Ok(Ok(None)))),
504             Err(e) => return Ok(Some(Ok(Err(e)))),
505         };
506 
507         remove_forbidden_headers(self.hooks, &mut fields);
508 
509         let ts = self.table.push(FieldMap::new_immutable(fields))?;
510 
511         Ok(Some(Ok(Ok(Some(ts)))))
512     }
513 }
514 
515 impl types::HostIncomingBody for WasiHttpCtxView<'_> {
stream( &mut self, id: Resource<HostIncomingBody>, ) -> wasmtime::Result<Result<Resource<DynInputStream>, ()>>516     fn stream(
517         &mut self,
518         id: Resource<HostIncomingBody>,
519     ) -> wasmtime::Result<Result<Resource<DynInputStream>, ()>> {
520         let body = self.table.get_mut(&id)?;
521 
522         if let Some(stream) = body.take_stream() {
523             let stream: DynInputStream = Box::new(stream);
524             let stream = self.table.push_child(stream, &id)?;
525             return Ok(Ok(stream));
526         }
527 
528         Ok(Err(()))
529     }
530 
finish( &mut self, id: Resource<HostIncomingBody>, ) -> wasmtime::Result<Resource<HostFutureTrailers>>531     fn finish(
532         &mut self,
533         id: Resource<HostIncomingBody>,
534     ) -> wasmtime::Result<Resource<HostFutureTrailers>> {
535         let body = self.table.delete(id)?;
536         let trailers = self.table.push(body.into_future_trailers())?;
537         Ok(trailers)
538     }
539 
drop(&mut self, id: Resource<HostIncomingBody>) -> wasmtime::Result<()>540     fn drop(&mut self, id: Resource<HostIncomingBody>) -> wasmtime::Result<()> {
541         let _ = self.table.delete(id)?;
542         Ok(())
543     }
544 }
545 
546 impl types::HostOutgoingResponse for WasiHttpCtxView<'_> {
new( &mut self, headers: Resource<FieldMap>, ) -> wasmtime::Result<Resource<HostOutgoingResponse>>547     fn new(
548         &mut self,
549         headers: Resource<FieldMap>,
550     ) -> wasmtime::Result<Resource<HostOutgoingResponse>> {
551         let mut fields = self.table.delete(headers)?;
552         fields.set_immutable();
553 
554         let id = self.table.push(HostOutgoingResponse {
555             status: http::StatusCode::OK,
556             headers: fields,
557             body: None,
558         })?;
559 
560         Ok(id)
561     }
562 
body( &mut self, id: Resource<HostOutgoingResponse>, ) -> wasmtime::Result<Result<Resource<HostOutgoingBody>, ()>>563     fn body(
564         &mut self,
565         id: Resource<HostOutgoingResponse>,
566     ) -> wasmtime::Result<Result<Resource<HostOutgoingBody>, ()>> {
567         let buffer_chunks = self.hooks.outgoing_body_buffer_chunks();
568         let chunk_size = self.hooks.outgoing_body_chunk_size();
569         let resp = self.table.get_mut(&id)?;
570 
571         if resp.body.is_some() {
572             return Ok(Err(()));
573         }
574 
575         let size = match get_content_length(&resp.headers) {
576             Ok(size) => size,
577             Err(..) => return Ok(Err(())),
578         };
579 
580         let (host, body) =
581             HostOutgoingBody::new(StreamContext::Response, size, buffer_chunks, chunk_size);
582 
583         resp.body.replace(body);
584 
585         let id = self.table.push(host)?;
586 
587         Ok(Ok(id))
588     }
589 
status_code( &mut self, id: Resource<HostOutgoingResponse>, ) -> wasmtime::Result<types::StatusCode>590     fn status_code(
591         &mut self,
592         id: Resource<HostOutgoingResponse>,
593     ) -> wasmtime::Result<types::StatusCode> {
594         Ok(self.table.get(&id)?.status.into())
595     }
596 
set_status_code( &mut self, id: Resource<HostOutgoingResponse>, status: types::StatusCode, ) -> wasmtime::Result<Result<(), ()>>597     fn set_status_code(
598         &mut self,
599         id: Resource<HostOutgoingResponse>,
600         status: types::StatusCode,
601     ) -> wasmtime::Result<Result<(), ()>> {
602         let resp = self.table.get_mut(&id)?;
603 
604         match http::StatusCode::from_u16(status) {
605             Ok(status) => resp.status = status,
606             Err(_) => return Ok(Err(())),
607         };
608 
609         Ok(Ok(()))
610     }
611 
headers( &mut self, id: Resource<HostOutgoingResponse>, ) -> wasmtime::Result<Resource<FieldMap>>612     fn headers(
613         &mut self,
614         id: Resource<HostOutgoingResponse>,
615     ) -> wasmtime::Result<Resource<FieldMap>> {
616         let resp = self.table.get(&id)?;
617         Ok(self.table.push(resp.headers.clone())?)
618     }
619 
drop(&mut self, id: Resource<HostOutgoingResponse>) -> wasmtime::Result<()>620     fn drop(&mut self, id: Resource<HostOutgoingResponse>) -> wasmtime::Result<()> {
621         let _ = self.table.delete(id)?;
622         Ok(())
623     }
624 }
625 
626 impl types::HostFutureIncomingResponse for WasiHttpCtxView<'_> {
drop(&mut self, id: Resource<HostFutureIncomingResponse>) -> wasmtime::Result<()>627     fn drop(&mut self, id: Resource<HostFutureIncomingResponse>) -> wasmtime::Result<()> {
628         let _ = self.table.delete(id)?;
629         Ok(())
630     }
631 
get( &mut self, id: Resource<HostFutureIncomingResponse>, ) -> wasmtime::Result< Option<Result<Result<Resource<HostIncomingResponse>, types::ErrorCode>, ()>>, >632     fn get(
633         &mut self,
634         id: Resource<HostFutureIncomingResponse>,
635     ) -> wasmtime::Result<
636         Option<Result<Result<Resource<HostIncomingResponse>, types::ErrorCode>, ()>>,
637     > {
638         let resp = self.table.get_mut(&id)?;
639 
640         match resp {
641             HostFutureIncomingResponse::Pending(_) => return Ok(None),
642             HostFutureIncomingResponse::Consumed => return Ok(Some(Err(()))),
643             HostFutureIncomingResponse::Ready(_) => {}
644         }
645 
646         let resp =
647             match std::mem::replace(resp, HostFutureIncomingResponse::Consumed).unwrap_ready() {
648                 Err(e) => {
649                     // Trapping if it's not possible to downcast to an wasi-http error
650                     let e = e.downcast::<types::ErrorCode>()?;
651                     return Ok(Some(Ok(Err(e))));
652                 }
653 
654                 Ok(Ok(resp)) => resp,
655                 Ok(Err(e)) => return Ok(Some(Ok(Err(e)))),
656             };
657 
658         let (mut parts, body) = resp.resp.into_parts();
659         remove_forbidden_headers(self.hooks, &mut parts.headers);
660         let headers = FieldMap::new_immutable(parts.headers);
661 
662         let resp = self.table.push(HostIncomingResponse {
663             status: parts.status.as_u16(),
664             headers,
665             body: Some({
666                 let mut body = HostIncomingBody::new(body, resp.between_bytes_timeout);
667                 if let Some(worker) = resp.worker {
668                     body.retain_worker(worker);
669                 }
670                 body
671             }),
672         })?;
673 
674         Ok(Some(Ok(Ok(resp))))
675     }
676 
subscribe( &mut self, id: Resource<HostFutureIncomingResponse>, ) -> wasmtime::Result<Resource<DynPollable>>677     fn subscribe(
678         &mut self,
679         id: Resource<HostFutureIncomingResponse>,
680     ) -> wasmtime::Result<Resource<DynPollable>> {
681         wasmtime_wasi::p2::subscribe(self.table, id)
682     }
683 }
684 
685 impl types::HostOutgoingBody for WasiHttpCtxView<'_> {
write( &mut self, id: Resource<HostOutgoingBody>, ) -> wasmtime::Result<Result<Resource<DynOutputStream>, ()>>686     fn write(
687         &mut self,
688         id: Resource<HostOutgoingBody>,
689     ) -> wasmtime::Result<Result<Resource<DynOutputStream>, ()>> {
690         let body = self.table.get_mut(&id)?;
691         if let Some(stream) = body.take_output_stream() {
692             let id = self.table.push_child(stream, &id)?;
693             Ok(Ok(id))
694         } else {
695             Ok(Err(()))
696         }
697     }
698 
finish( &mut self, id: Resource<HostOutgoingBody>, ts: Option<Resource<Trailers>>, ) -> HttpResult<()>699     fn finish(
700         &mut self,
701         id: Resource<HostOutgoingBody>,
702         ts: Option<Resource<Trailers>>,
703     ) -> HttpResult<()> {
704         let body = self.table.delete(id)?;
705 
706         let ts = if let Some(ts) = ts {
707             Some(self.table.delete(ts)?)
708         } else {
709             None
710         };
711 
712         body.finish(ts)?;
713         Ok(())
714     }
715 
drop(&mut self, id: Resource<HostOutgoingBody>) -> wasmtime::Result<()>716     fn drop(&mut self, id: Resource<HostOutgoingBody>) -> wasmtime::Result<()> {
717         self.table.delete(id)?.abort();
718         Ok(())
719     }
720 }
721 
722 impl types::HostRequestOptions for WasiHttpCtxView<'_> {
new(&mut self) -> wasmtime::Result<Resource<types::RequestOptions>>723     fn new(&mut self) -> wasmtime::Result<Resource<types::RequestOptions>> {
724         let id = self.table.push(types::RequestOptions::default())?;
725         Ok(id)
726     }
727 
connect_timeout( &mut self, opts: Resource<types::RequestOptions>, ) -> wasmtime::Result<Option<types::Duration>>728     fn connect_timeout(
729         &mut self,
730         opts: Resource<types::RequestOptions>,
731     ) -> wasmtime::Result<Option<types::Duration>> {
732         let nanos = self.table.get(&opts)?.connect_timeout.map(|d| d.as_nanos());
733 
734         if let Some(nanos) = nanos {
735             Ok(Some(nanos.try_into()?))
736         } else {
737             Ok(None)
738         }
739     }
740 
set_connect_timeout( &mut self, opts: Resource<types::RequestOptions>, duration: Option<types::Duration>, ) -> wasmtime::Result<Result<(), ()>>741     fn set_connect_timeout(
742         &mut self,
743         opts: Resource<types::RequestOptions>,
744         duration: Option<types::Duration>,
745     ) -> wasmtime::Result<Result<(), ()>> {
746         self.table.get_mut(&opts)?.connect_timeout = duration.map(std::time::Duration::from_nanos);
747         Ok(Ok(()))
748     }
749 
first_byte_timeout( &mut self, opts: Resource<types::RequestOptions>, ) -> wasmtime::Result<Option<types::Duration>>750     fn first_byte_timeout(
751         &mut self,
752         opts: Resource<types::RequestOptions>,
753     ) -> wasmtime::Result<Option<types::Duration>> {
754         let nanos = self
755             .table
756             .get(&opts)?
757             .first_byte_timeout
758             .map(|d| d.as_nanos());
759 
760         if let Some(nanos) = nanos {
761             Ok(Some(nanos.try_into()?))
762         } else {
763             Ok(None)
764         }
765     }
766 
set_first_byte_timeout( &mut self, opts: Resource<types::RequestOptions>, duration: Option<types::Duration>, ) -> wasmtime::Result<Result<(), ()>>767     fn set_first_byte_timeout(
768         &mut self,
769         opts: Resource<types::RequestOptions>,
770         duration: Option<types::Duration>,
771     ) -> wasmtime::Result<Result<(), ()>> {
772         self.table.get_mut(&opts)?.first_byte_timeout =
773             duration.map(std::time::Duration::from_nanos);
774         Ok(Ok(()))
775     }
776 
between_bytes_timeout( &mut self, opts: Resource<types::RequestOptions>, ) -> wasmtime::Result<Option<types::Duration>>777     fn between_bytes_timeout(
778         &mut self,
779         opts: Resource<types::RequestOptions>,
780     ) -> wasmtime::Result<Option<types::Duration>> {
781         let nanos = self
782             .table
783             .get(&opts)?
784             .between_bytes_timeout
785             .map(|d| d.as_nanos());
786 
787         if let Some(nanos) = nanos {
788             Ok(Some(nanos.try_into()?))
789         } else {
790             Ok(None)
791         }
792     }
793 
set_between_bytes_timeout( &mut self, opts: Resource<types::RequestOptions>, duration: Option<types::Duration>, ) -> wasmtime::Result<Result<(), ()>>794     fn set_between_bytes_timeout(
795         &mut self,
796         opts: Resource<types::RequestOptions>,
797         duration: Option<types::Duration>,
798     ) -> wasmtime::Result<Result<(), ()>> {
799         self.table.get_mut(&opts)?.between_bytes_timeout =
800             duration.map(std::time::Duration::from_nanos);
801         Ok(Ok(()))
802     }
803 
drop(&mut self, rep: Resource<types::RequestOptions>) -> wasmtime::Result<()>804     fn drop(&mut self, rep: Resource<types::RequestOptions>) -> wasmtime::Result<()> {
805         let _ = self.table.delete(rep)?;
806         Ok(())
807     }
808 }
809