Skip to main content

wasmtime_wasi_http/p2/
types_impl.rs

1//! Implementation for the `wasi:http/types` interface.
2
3use crate::p2::bindings::http::types::{self, Method, Scheme, StatusCode, Trailers};
4use crate::p2::body::{HostFutureTrailers, HostIncomingBody, HostOutgoingBody, StreamContext};
5use crate::p2::types::{
6    HostFutureIncomingResponse, HostIncomingRequest, HostIncomingResponse, HostOutgoingRequest,
7    HostOutgoingResponse, HostResponseOutparam,
8};
9use crate::p2::{HeaderError, HeaderResult, HttpError, HttpResult};
10use crate::{FieldMap, WasiHttpCtxView, get_content_length};
11use http::HeaderName;
12use std::str::FromStr;
13use wasmtime::component::Resource;
14use wasmtime::{error::Context as _, format_err};
15use wasmtime_wasi::p2::{DynInputStream, DynOutputStream, DynPollable};
16
17impl types::Host for WasiHttpCtxView<'_> {
18    fn convert_error_code(&mut self, err: HttpError) -> wasmtime::Result<types::ErrorCode> {
19        err.downcast()
20    }
21
22    fn convert_header_error(&mut self, err: HeaderError) -> wasmtime::Result<types::HeaderError> {
23        err.downcast()
24    }
25
26    fn http_error_code(
27        &mut self,
28        err: wasmtime::component::Resource<types::IoError>,
29    ) -> wasmtime::Result<Option<types::ErrorCode>> {
30        let e = self.table.get(&err)?;
31        Ok(e.downcast_ref::<types::ErrorCode>().cloned())
32    }
33}
34
35impl types::HostFields for WasiHttpCtxView<'_> {
36    fn new(&mut self) -> wasmtime::Result<Resource<FieldMap>> {
37        let limit = self.ctx.field_size_limit;
38        let id = self
39            .table
40            .push(FieldMap::new_mutable(limit))
41            .context("[new_fields] pushing fields")?;
42
43        Ok(id)
44    }
45
46    fn from_list(&mut self, entries: Vec<(String, Vec<u8>)>) -> HeaderResult<Resource<FieldMap>> {
47        let mut fields = FieldMap::new_mutable(self.ctx.field_size_limit);
48
49        for (header, value) in entries {
50            fields.append(self.hooks, header, value)?;
51        }
52
53        Ok(self.table.push(fields)?)
54    }
55
56    fn drop(&mut self, fields: Resource<FieldMap>) -> wasmtime::Result<()> {
57        self.table
58            .delete(fields)
59            .context("[drop_fields] deleting fields")?;
60        Ok(())
61    }
62
63    fn get(&mut self, fields: Resource<FieldMap>, name: String) -> wasmtime::Result<Vec<Vec<u8>>> {
64        let fields = self.table.get(&fields)?;
65
66        let header = match HeaderName::from_bytes(name.as_bytes()) {
67            Ok(header) => header,
68            Err(_) => return Ok(vec![]),
69        };
70
71        if !fields.contains_key(&header) {
72            return Ok(vec![]);
73        }
74
75        let res = fields
76            .get_all(&header)
77            .into_iter()
78            .map(|val| val.as_bytes().to_owned())
79            .collect();
80        Ok(res)
81    }
82
83    fn has(&mut self, fields: Resource<FieldMap>, name: String) -> wasmtime::Result<bool> {
84        let fields = self.table.get(&fields)?;
85
86        match HeaderName::from_bytes(name.as_bytes()) {
87            Ok(header) => Ok(fields.contains_key(&header)),
88            Err(_) => Ok(false),
89        }
90    }
91
92    fn set(
93        &mut self,
94        fields: Resource<FieldMap>,
95        name: String,
96        values: Vec<Vec<u8>>,
97    ) -> HeaderResult<()> {
98        let fields = self.table.get_mut(&fields)?;
99        fields.set(self.hooks, name, values)?;
100        Ok(())
101    }
102
103    fn delete(&mut self, fields: Resource<FieldMap>, name: String) -> HeaderResult<()> {
104        let fields = self.table.get_mut(&fields)?;
105        fields.remove_all(self.hooks, name)?;
106        Ok(())
107    }
108
109    fn append(
110        &mut self,
111        fields: Resource<FieldMap>,
112        name: String,
113        value: Vec<u8>,
114    ) -> HeaderResult<()> {
115        let fields = self.table.get_mut(&fields)?;
116        fields.append(self.hooks, name, value)?;
117        Ok(())
118    }
119
120    fn entries(&mut self, fields: Resource<FieldMap>) -> wasmtime::Result<Vec<(String, Vec<u8>)>> {
121        Ok(self
122            .table
123            .get(&fields)?
124            .iter()
125            .map(|(name, value)| (name.as_str().to_owned(), value.as_bytes().to_owned()))
126            .collect())
127    }
128
129    fn clone(&mut self, fields: Resource<FieldMap>) -> wasmtime::Result<Resource<FieldMap>> {
130        let mut fields = self.table.get(&fields)?.clone();
131        fields.set_mutable(self.ctx.field_size_limit);
132        let id = self.table.push(fields)?;
133        Ok(id)
134    }
135}
136
137impl types::HostIncomingRequest for WasiHttpCtxView<'_> {
138    fn method(&mut self, id: Resource<HostIncomingRequest>) -> wasmtime::Result<Method> {
139        let method = self.table.get(&id)?.method.clone();
140        Ok(method.into())
141    }
142    fn path_with_query(
143        &mut self,
144        id: Resource<HostIncomingRequest>,
145    ) -> wasmtime::Result<Option<String>> {
146        let req = self.table.get(&id)?;
147        Ok(req
148            .uri
149            .path_and_query()
150            .map(|path_and_query| path_and_query.as_str().to_owned()))
151    }
152    fn scheme(&mut self, id: Resource<HostIncomingRequest>) -> wasmtime::Result<Option<Scheme>> {
153        let req = self.table.get(&id)?;
154        Ok(Some(req.scheme.clone()))
155    }
156    fn authority(&mut self, id: Resource<HostIncomingRequest>) -> wasmtime::Result<Option<String>> {
157        let req = self.table.get(&id)?;
158        Ok(Some(req.authority.clone()))
159    }
160
161    fn headers(
162        &mut self,
163        id: Resource<HostIncomingRequest>,
164    ) -> wasmtime::Result<Resource<FieldMap>> {
165        let req = self.table.get(&id)?;
166        Ok(self.table.push(req.headers.clone())?)
167    }
168
169    fn consume(
170        &mut self,
171        id: Resource<HostIncomingRequest>,
172    ) -> wasmtime::Result<Result<Resource<HostIncomingBody>, ()>> {
173        let req = self.table.get_mut(&id)?;
174        match req.body.take() {
175            Some(body) => {
176                let id = self.table.push(body)?;
177                Ok(Ok(id))
178            }
179
180            None => Ok(Err(())),
181        }
182    }
183
184    fn drop(&mut self, id: Resource<HostIncomingRequest>) -> wasmtime::Result<()> {
185        let _ = self.table.delete(id)?;
186        Ok(())
187    }
188}
189
190impl types::HostOutgoingRequest for WasiHttpCtxView<'_> {
191    fn new(
192        &mut self,
193        headers: Resource<FieldMap>,
194    ) -> wasmtime::Result<Resource<HostOutgoingRequest>> {
195        let mut headers = self.table.delete(headers)?;
196        headers.set_immutable();
197
198        self.table
199            .push(HostOutgoingRequest {
200                path_with_query: None,
201                authority: None,
202                method: types::Method::Get,
203                headers,
204                scheme: None,
205                body: None,
206            })
207            .context("[new_outgoing_request] pushing request")
208    }
209
210    fn body(
211        &mut self,
212        request: Resource<HostOutgoingRequest>,
213    ) -> wasmtime::Result<Result<Resource<HostOutgoingBody>, ()>> {
214        let buffer_chunks = self.hooks.p2_outgoing_body_buffer_chunks();
215        let chunk_size = self.hooks.p2_outgoing_body_chunk_size();
216        let req = self
217            .table
218            .get_mut(&request)
219            .context("[outgoing_request_write] getting request")?;
220
221        if req.body.is_some() {
222            return Ok(Err(()));
223        }
224
225        let size = match get_content_length(&req.headers) {
226            Ok(size) => size,
227            Err(..) => return Ok(Err(())),
228        };
229
230        let (host_body, hyper_body) =
231            HostOutgoingBody::new(StreamContext::Request, size, buffer_chunks, chunk_size);
232
233        req.body = Some(hyper_body);
234
235        // The output stream will necessarily outlive the request, because we could be still
236        // writing to the stream after `outgoing-handler.handle` is called.
237        let outgoing_body = self.table.push(host_body)?;
238
239        Ok(Ok(outgoing_body))
240    }
241
242    fn drop(&mut self, request: Resource<HostOutgoingRequest>) -> wasmtime::Result<()> {
243        let _ = self.table.delete(request)?;
244        Ok(())
245    }
246
247    fn method(
248        &mut self,
249        request: wasmtime::component::Resource<types::OutgoingRequest>,
250    ) -> wasmtime::Result<Method> {
251        Ok(self.table.get(&request)?.method.clone())
252    }
253
254    fn set_method(
255        &mut self,
256        request: wasmtime::component::Resource<types::OutgoingRequest>,
257        method: Method,
258    ) -> wasmtime::Result<Result<(), ()>> {
259        let req = self.table.get_mut(&request)?;
260
261        if let Method::Other(s) = &method {
262            if let Err(_) = http::Method::from_str(s) {
263                return Ok(Err(()));
264            }
265        }
266
267        req.method = method;
268
269        Ok(Ok(()))
270    }
271
272    fn path_with_query(
273        &mut self,
274        request: wasmtime::component::Resource<types::OutgoingRequest>,
275    ) -> wasmtime::Result<Option<String>> {
276        Ok(self.table.get(&request)?.path_with_query.clone())
277    }
278
279    fn set_path_with_query(
280        &mut self,
281        request: wasmtime::component::Resource<types::OutgoingRequest>,
282        path_with_query: Option<String>,
283    ) -> wasmtime::Result<Result<(), ()>> {
284        let req = self.table.get_mut(&request)?;
285
286        if let Some(s) = path_with_query.as_ref() {
287            if let Err(_) = http::uri::PathAndQuery::from_str(s) {
288                return Ok(Err(()));
289            }
290        }
291
292        req.path_with_query = path_with_query;
293
294        Ok(Ok(()))
295    }
296
297    fn scheme(
298        &mut self,
299        request: wasmtime::component::Resource<types::OutgoingRequest>,
300    ) -> wasmtime::Result<Option<Scheme>> {
301        Ok(self.table.get(&request)?.scheme.clone())
302    }
303
304    fn set_scheme(
305        &mut self,
306        request: wasmtime::component::Resource<types::OutgoingRequest>,
307        scheme: Option<Scheme>,
308    ) -> wasmtime::Result<Result<(), ()>> {
309        let req = self.table.get_mut(&request)?;
310
311        if let Some(types::Scheme::Other(s)) = scheme.as_ref() {
312            if let Err(_) = http::uri::Scheme::from_str(s.as_str()) {
313                return Ok(Err(()));
314            }
315        }
316
317        req.scheme = scheme;
318
319        Ok(Ok(()))
320    }
321
322    fn authority(
323        &mut self,
324        request: wasmtime::component::Resource<types::OutgoingRequest>,
325    ) -> wasmtime::Result<Option<String>> {
326        Ok(self.table.get(&request)?.authority.clone())
327    }
328
329    fn set_authority(
330        &mut self,
331        request: wasmtime::component::Resource<types::OutgoingRequest>,
332        authority: Option<String>,
333    ) -> wasmtime::Result<Result<(), ()>> {
334        let req = self.table.get_mut(&request)?;
335
336        // Match p3: reject empty / non-numeric / out-of-range ports that
337        // `http::uri::Authority` alone would accept (see crate::parse_authority).
338        if let Some(s) = authority {
339            let Ok(parsed) = crate::parse_authority(s) else {
340                return Ok(Err(()));
341            };
342            req.authority = Some(parsed.as_str().into());
343        } else {
344            req.authority = None;
345        }
346
347        Ok(Ok(()))
348    }
349
350    fn headers(
351        &mut self,
352        request: wasmtime::component::Resource<types::OutgoingRequest>,
353    ) -> wasmtime::Result<wasmtime::component::Resource<FieldMap>> {
354        let req = self.table.get(&request)?;
355        let id = self.table.push(req.headers.clone())?;
356        Ok(id)
357    }
358}
359
360impl types::HostResponseOutparam for WasiHttpCtxView<'_> {
361    fn drop(&mut self, id: Resource<HostResponseOutparam>) -> wasmtime::Result<()> {
362        let _ = self.table.delete(id)?;
363        Ok(())
364    }
365    fn set(
366        &mut self,
367        id: Resource<HostResponseOutparam>,
368        resp: Result<Resource<HostOutgoingResponse>, types::ErrorCode>,
369    ) -> wasmtime::Result<()> {
370        let val = match resp {
371            Ok(resp) => Ok(self.table.delete(resp)?.try_into()?),
372            Err(e) => Err(e),
373        };
374
375        let resp = self.table.delete(id)?;
376        (resp.send)(val);
377        Ok(())
378    }
379
380    fn send_informational(
381        &mut self,
382        _id: Resource<HostResponseOutparam>,
383        _status: u16,
384        _headers: Resource<FieldMap>,
385    ) -> HttpResult<()> {
386        Err(HttpError::trap(format_err!("not implemented")))
387    }
388}
389
390impl types::HostIncomingResponse for WasiHttpCtxView<'_> {
391    fn drop(&mut self, response: Resource<HostIncomingResponse>) -> wasmtime::Result<()> {
392        let _ = self
393            .table
394            .delete(response)
395            .context("[drop_incoming_response] deleting response")?;
396        Ok(())
397    }
398
399    fn status(&mut self, response: Resource<HostIncomingResponse>) -> wasmtime::Result<StatusCode> {
400        let r = self
401            .table
402            .get(&response)
403            .context("[incoming_response_status] getting response")?;
404        Ok(r.status)
405    }
406
407    fn headers(
408        &mut self,
409        response: Resource<HostIncomingResponse>,
410    ) -> wasmtime::Result<Resource<FieldMap>> {
411        let resp = self.table.get(&response)?;
412        let id = self.table.push(resp.headers.clone())?;
413        Ok(id)
414    }
415
416    fn consume(
417        &mut self,
418        response: Resource<HostIncomingResponse>,
419    ) -> wasmtime::Result<Result<Resource<HostIncomingBody>, ()>> {
420        let r = self
421            .table
422            .get_mut(&response)
423            .context("[incoming_response_consume] getting response")?;
424
425        match r.body.take() {
426            Some(body) => {
427                let id = self.table.push(body)?;
428                Ok(Ok(id))
429            }
430
431            None => Ok(Err(())),
432        }
433    }
434}
435
436impl types::HostFutureTrailers for WasiHttpCtxView<'_> {
437    fn drop(&mut self, id: Resource<HostFutureTrailers>) -> wasmtime::Result<()> {
438        let _ = self
439            .table
440            .delete(id)
441            .context("[drop future-trailers] deleting future-trailers")?;
442        Ok(())
443    }
444
445    fn subscribe(
446        &mut self,
447        index: Resource<HostFutureTrailers>,
448    ) -> wasmtime::Result<Resource<DynPollable>> {
449        wasmtime_wasi::p2::subscribe(self.table, index)
450    }
451
452    fn get(
453        &mut self,
454        id: Resource<HostFutureTrailers>,
455    ) -> wasmtime::Result<Option<Result<Result<Option<Resource<Trailers>>, types::ErrorCode>, ()>>>
456    {
457        let trailers = self.table.get_mut(&id)?;
458        match trailers {
459            HostFutureTrailers::Waiting { .. } => return Ok(None),
460            HostFutureTrailers::Consumed => return Ok(Some(Err(()))),
461            HostFutureTrailers::Done(_) => {}
462        };
463
464        let res = match std::mem::replace(trailers, HostFutureTrailers::Consumed) {
465            HostFutureTrailers::Done(res) => res,
466            _ => unreachable!(),
467        };
468
469        let fields = match res {
470            Ok(Some(fields)) => fields,
471            Ok(None) => return Ok(Some(Ok(Ok(None)))),
472            Err(e) => {
473                let e = self.error_to_p2(e);
474                return Ok(Some(Ok(Err(e))));
475            }
476        };
477
478        let ts = self
479            .table
480            .push(FieldMap::new_immutable(self.hooks, fields))?;
481
482        Ok(Some(Ok(Ok(Some(ts)))))
483    }
484}
485
486impl types::HostIncomingBody for WasiHttpCtxView<'_> {
487    fn stream(
488        &mut self,
489        id: Resource<HostIncomingBody>,
490    ) -> wasmtime::Result<Result<Resource<DynInputStream>, ()>> {
491        let body = self.table.get_mut(&id)?;
492
493        if let Some(stream) = body.take_stream() {
494            let stream: DynInputStream = Box::new(stream);
495            let stream = self.table.push_child(stream, &id)?;
496            return Ok(Ok(stream));
497        }
498
499        Ok(Err(()))
500    }
501
502    fn finish(
503        &mut self,
504        id: Resource<HostIncomingBody>,
505    ) -> wasmtime::Result<Resource<HostFutureTrailers>> {
506        let body = self.table.delete(id)?;
507        let trailers = self.table.push(body.into_future_trailers())?;
508        Ok(trailers)
509    }
510
511    fn drop(&mut self, id: Resource<HostIncomingBody>) -> wasmtime::Result<()> {
512        let _ = self.table.delete(id)?;
513        Ok(())
514    }
515}
516
517impl types::HostOutgoingResponse for WasiHttpCtxView<'_> {
518    fn new(
519        &mut self,
520        headers: Resource<FieldMap>,
521    ) -> wasmtime::Result<Resource<HostOutgoingResponse>> {
522        let mut fields = self.table.delete(headers)?;
523        fields.set_immutable();
524
525        let id = self.table.push(HostOutgoingResponse {
526            status: http::StatusCode::OK,
527            headers: fields,
528            body: None,
529        })?;
530
531        Ok(id)
532    }
533
534    fn body(
535        &mut self,
536        id: Resource<HostOutgoingResponse>,
537    ) -> wasmtime::Result<Result<Resource<HostOutgoingBody>, ()>> {
538        let buffer_chunks = self.hooks.p2_outgoing_body_buffer_chunks();
539        let chunk_size = self.hooks.p2_outgoing_body_chunk_size();
540        let resp = self.table.get_mut(&id)?;
541
542        if resp.body.is_some() {
543            return Ok(Err(()));
544        }
545
546        let size = match get_content_length(&resp.headers) {
547            Ok(size) => size,
548            Err(..) => return Ok(Err(())),
549        };
550
551        let (host, body) =
552            HostOutgoingBody::new(StreamContext::Response, size, buffer_chunks, chunk_size);
553
554        resp.body.replace(body);
555
556        let id = self.table.push(host)?;
557
558        Ok(Ok(id))
559    }
560
561    fn status_code(
562        &mut self,
563        id: Resource<HostOutgoingResponse>,
564    ) -> wasmtime::Result<types::StatusCode> {
565        Ok(self.table.get(&id)?.status.into())
566    }
567
568    fn set_status_code(
569        &mut self,
570        id: Resource<HostOutgoingResponse>,
571        status: types::StatusCode,
572    ) -> wasmtime::Result<Result<(), ()>> {
573        let resp = self.table.get_mut(&id)?;
574
575        match http::StatusCode::from_u16(status) {
576            Ok(status) => resp.status = status,
577            Err(_) => return Ok(Err(())),
578        };
579
580        Ok(Ok(()))
581    }
582
583    fn headers(
584        &mut self,
585        id: Resource<HostOutgoingResponse>,
586    ) -> wasmtime::Result<Resource<FieldMap>> {
587        let resp = self.table.get(&id)?;
588        Ok(self.table.push(resp.headers.clone())?)
589    }
590
591    fn drop(&mut self, id: Resource<HostOutgoingResponse>) -> wasmtime::Result<()> {
592        let _ = self.table.delete(id)?;
593        Ok(())
594    }
595}
596
597impl types::HostFutureIncomingResponse for WasiHttpCtxView<'_> {
598    fn drop(&mut self, id: Resource<HostFutureIncomingResponse>) -> wasmtime::Result<()> {
599        let _ = self.table.delete(id)?;
600        Ok(())
601    }
602
603    fn get(
604        &mut self,
605        id: Resource<HostFutureIncomingResponse>,
606    ) -> wasmtime::Result<
607        Option<Result<Result<Resource<HostIncomingResponse>, types::ErrorCode>, ()>>,
608    > {
609        let resp = self.table.get_mut(&id)?;
610
611        match resp {
612            HostFutureIncomingResponse::Pending(_) => return Ok(None),
613            HostFutureIncomingResponse::Consumed => return Ok(Some(Err(()))),
614            HostFutureIncomingResponse::Ready(_) => {}
615        }
616
617        let (resp, io) =
618            match std::mem::replace(resp, HostFutureIncomingResponse::Consumed).unwrap_ready() {
619                Ok(pair) => pair,
620                Err(e) => {
621                    let e = self.error_to_p2(e);
622                    return Ok(Some(Ok(Err(e))));
623                }
624            };
625
626        let (parts, body) = resp.into_parts();
627        let headers = FieldMap::new_immutable(self.hooks, parts.headers);
628
629        let resp = self.table.push(HostIncomingResponse {
630            status: parts.status.as_u16(),
631            headers,
632            body: Some({
633                let mut body = HostIncomingBody::new(body);
634                body.retain_worker(io);
635                body
636            }),
637        })?;
638
639        Ok(Some(Ok(Ok(resp))))
640    }
641
642    fn subscribe(
643        &mut self,
644        id: Resource<HostFutureIncomingResponse>,
645    ) -> wasmtime::Result<Resource<DynPollable>> {
646        wasmtime_wasi::p2::subscribe(self.table, id)
647    }
648}
649
650impl types::HostOutgoingBody for WasiHttpCtxView<'_> {
651    fn write(
652        &mut self,
653        id: Resource<HostOutgoingBody>,
654    ) -> wasmtime::Result<Result<Resource<DynOutputStream>, ()>> {
655        let body = self.table.get_mut(&id)?;
656        if let Some(stream) = body.take_output_stream() {
657            let id = self.table.push_child(stream, &id)?;
658            Ok(Ok(id))
659        } else {
660            Ok(Err(()))
661        }
662    }
663
664    fn finish(
665        &mut self,
666        id: Resource<HostOutgoingBody>,
667        ts: Option<Resource<Trailers>>,
668    ) -> HttpResult<()> {
669        let body = self.table.delete(id)?;
670
671        let ts = if let Some(ts) = ts {
672            Some(self.table.delete(ts)?)
673        } else {
674            None
675        };
676
677        body.finish(ts)?;
678        Ok(())
679    }
680
681    fn drop(&mut self, id: Resource<HostOutgoingBody>) -> wasmtime::Result<()> {
682        self.table.delete(id)?.abort();
683        Ok(())
684    }
685}
686
687impl types::HostRequestOptions for WasiHttpCtxView<'_> {
688    fn new(&mut self) -> wasmtime::Result<Resource<types::RequestOptions>> {
689        let id = self.table.push(types::RequestOptions::default())?;
690        Ok(id)
691    }
692
693    fn connect_timeout(
694        &mut self,
695        opts: Resource<types::RequestOptions>,
696    ) -> wasmtime::Result<Option<types::Duration>> {
697        let nanos = self.table.get(&opts)?.connect_timeout.map(|d| d.as_nanos());
698
699        if let Some(nanos) = nanos {
700            Ok(Some(nanos.try_into()?))
701        } else {
702            Ok(None)
703        }
704    }
705
706    fn set_connect_timeout(
707        &mut self,
708        opts: Resource<types::RequestOptions>,
709        duration: Option<types::Duration>,
710    ) -> wasmtime::Result<Result<(), ()>> {
711        self.table.get_mut(&opts)?.connect_timeout = duration.map(std::time::Duration::from_nanos);
712        Ok(Ok(()))
713    }
714
715    fn first_byte_timeout(
716        &mut self,
717        opts: Resource<types::RequestOptions>,
718    ) -> wasmtime::Result<Option<types::Duration>> {
719        let nanos = self
720            .table
721            .get(&opts)?
722            .first_byte_timeout
723            .map(|d| d.as_nanos());
724
725        if let Some(nanos) = nanos {
726            Ok(Some(nanos.try_into()?))
727        } else {
728            Ok(None)
729        }
730    }
731
732    fn set_first_byte_timeout(
733        &mut self,
734        opts: Resource<types::RequestOptions>,
735        duration: Option<types::Duration>,
736    ) -> wasmtime::Result<Result<(), ()>> {
737        self.table.get_mut(&opts)?.first_byte_timeout =
738            duration.map(std::time::Duration::from_nanos);
739        Ok(Ok(()))
740    }
741
742    fn between_bytes_timeout(
743        &mut self,
744        opts: Resource<types::RequestOptions>,
745    ) -> wasmtime::Result<Option<types::Duration>> {
746        let nanos = self
747            .table
748            .get(&opts)?
749            .between_bytes_timeout
750            .map(|d| d.as_nanos());
751
752        if let Some(nanos) = nanos {
753            Ok(Some(nanos.try_into()?))
754        } else {
755            Ok(None)
756        }
757    }
758
759    fn set_between_bytes_timeout(
760        &mut self,
761        opts: Resource<types::RequestOptions>,
762        duration: Option<types::Duration>,
763    ) -> wasmtime::Result<Result<(), ()>> {
764        self.table.get_mut(&opts)?.between_bytes_timeout =
765            duration.map(std::time::Duration::from_nanos);
766        Ok(Ok(()))
767    }
768
769    fn drop(&mut self, rep: Resource<types::RequestOptions>) -> wasmtime::Result<()> {
770        let _ = self.table.delete(rep)?;
771        Ok(())
772    }
773}
774
775mod named {
776    use crate::p2::bindings::http::types::{self, Method, Scheme, StatusCode, Trailers};
777    use crate::p2::bindings::named_imports::wasi::http::types as named_types;
778    use crate::p2::body::{HostFutureTrailers, HostIncomingBody, HostOutgoingBody};
779    use crate::p2::types::{
780        HostFutureIncomingResponse, HostIncomingRequest, HostIncomingResponse, HostOutgoingRequest,
781        HostOutgoingResponse, HostResponseOutparam,
782    };
783    use crate::p2::{HeaderError, HeaderResult, HttpError, HttpResult};
784    use crate::{FieldMap, WasiHttpNamedView};
785    use wasmtime::component::Resource;
786    use wasmtime_wasi::p2::{DynInputStream, DynOutputStream, DynPollable};
787    use wasmtime_wasi::{NamedId, WasiCtxNamedView};
788
789    impl<T> named_types::Host for WasiCtxNamedView<'_, T>
790    where
791        T: WasiHttpNamedView,
792    {
793        fn convert_error_code(&mut self, err: HttpError) -> wasmtime::Result<types::ErrorCode> {
794            err.downcast()
795        }
796
797        fn convert_header_error(
798            &mut self,
799            err: HeaderError,
800        ) -> wasmtime::Result<types::HeaderError> {
801            err.downcast()
802        }
803
804        fn http_error_code(
805            &mut self,
806            id: NamedId,
807            err: wasmtime::component::Resource<types::IoError>,
808        ) -> wasmtime::Result<Option<types::ErrorCode>> {
809            super::types::Host::http_error_code(&mut self.0.http(id), err)
810        }
811    }
812
813    impl<T> named_types::HostFields for WasiCtxNamedView<'_, T>
814    where
815        T: WasiHttpNamedView,
816    {
817        fn new(&mut self, id: NamedId) -> wasmtime::Result<Resource<FieldMap>> {
818            super::types::HostFields::new(&mut self.0.http(id))
819        }
820
821        fn from_list(
822            &mut self,
823            id: NamedId,
824            entries: Vec<(String, Vec<u8>)>,
825        ) -> HeaderResult<Resource<FieldMap>> {
826            super::types::HostFields::from_list(&mut self.0.http(id), entries)
827        }
828
829        fn drop(&mut self, id: NamedId, fields: Resource<FieldMap>) -> wasmtime::Result<()> {
830            super::types::HostFields::drop(&mut self.0.http(id), fields)
831        }
832
833        fn get(
834            &mut self,
835            id: NamedId,
836            fields: Resource<FieldMap>,
837            name: String,
838        ) -> wasmtime::Result<Vec<Vec<u8>>> {
839            super::types::HostFields::get(&mut self.0.http(id), fields, name)
840        }
841
842        fn has(
843            &mut self,
844            id: NamedId,
845            fields: Resource<FieldMap>,
846            name: String,
847        ) -> wasmtime::Result<bool> {
848            super::types::HostFields::has(&mut self.0.http(id), fields, name)
849        }
850
851        fn set(
852            &mut self,
853            id: NamedId,
854            fields: Resource<FieldMap>,
855            name: String,
856            values: Vec<Vec<u8>>,
857        ) -> HeaderResult<()> {
858            super::types::HostFields::set(&mut self.0.http(id), fields, name, values)
859        }
860
861        fn delete(
862            &mut self,
863            id: NamedId,
864            fields: Resource<FieldMap>,
865            name: String,
866        ) -> HeaderResult<()> {
867            super::types::HostFields::delete(&mut self.0.http(id), fields, name)
868        }
869
870        fn append(
871            &mut self,
872            id: NamedId,
873            fields: Resource<FieldMap>,
874            name: String,
875            value: Vec<u8>,
876        ) -> HeaderResult<()> {
877            super::types::HostFields::append(&mut self.0.http(id), fields, name, value)
878        }
879
880        fn entries(
881            &mut self,
882            id: NamedId,
883            fields: Resource<FieldMap>,
884        ) -> wasmtime::Result<Vec<(String, Vec<u8>)>> {
885            super::types::HostFields::entries(&mut self.0.http(id), fields)
886        }
887
888        fn clone(
889            &mut self,
890            id: NamedId,
891            fields: Resource<FieldMap>,
892        ) -> wasmtime::Result<Resource<FieldMap>> {
893            super::types::HostFields::clone(&mut self.0.http(id), fields)
894        }
895    }
896
897    impl<T> named_types::HostIncomingRequest for WasiCtxNamedView<'_, T>
898    where
899        T: WasiHttpNamedView,
900    {
901        fn method(
902            &mut self,
903            id: NamedId,
904            this: Resource<HostIncomingRequest>,
905        ) -> wasmtime::Result<Method> {
906            super::types::HostIncomingRequest::method(&mut self.0.http(id), this)
907        }
908
909        fn path_with_query(
910            &mut self,
911            id: NamedId,
912            this: Resource<HostIncomingRequest>,
913        ) -> wasmtime::Result<Option<String>> {
914            super::types::HostIncomingRequest::path_with_query(&mut self.0.http(id), this)
915        }
916
917        fn scheme(
918            &mut self,
919            id: NamedId,
920            this: Resource<HostIncomingRequest>,
921        ) -> wasmtime::Result<Option<Scheme>> {
922            super::types::HostIncomingRequest::scheme(&mut self.0.http(id), this)
923        }
924
925        fn authority(
926            &mut self,
927            id: NamedId,
928            this: Resource<HostIncomingRequest>,
929        ) -> wasmtime::Result<Option<String>> {
930            super::types::HostIncomingRequest::authority(&mut self.0.http(id), this)
931        }
932
933        fn headers(
934            &mut self,
935            id: NamedId,
936            this: Resource<HostIncomingRequest>,
937        ) -> wasmtime::Result<Resource<FieldMap>> {
938            super::types::HostIncomingRequest::headers(&mut self.0.http(id), this)
939        }
940
941        fn consume(
942            &mut self,
943            id: NamedId,
944            this: Resource<HostIncomingRequest>,
945        ) -> wasmtime::Result<Result<Resource<HostIncomingBody>, ()>> {
946            super::types::HostIncomingRequest::consume(&mut self.0.http(id), this)
947        }
948
949        fn drop(
950            &mut self,
951            id: NamedId,
952            this: Resource<HostIncomingRequest>,
953        ) -> wasmtime::Result<()> {
954            super::types::HostIncomingRequest::drop(&mut self.0.http(id), this)
955        }
956    }
957
958    impl<T> named_types::HostOutgoingRequest for WasiCtxNamedView<'_, T>
959    where
960        T: WasiHttpNamedView,
961    {
962        fn new(
963            &mut self,
964            id: NamedId,
965            headers: Resource<FieldMap>,
966        ) -> wasmtime::Result<Resource<HostOutgoingRequest>> {
967            super::types::HostOutgoingRequest::new(&mut self.0.http(id), headers)
968        }
969
970        fn body(
971            &mut self,
972            id: NamedId,
973            request: Resource<HostOutgoingRequest>,
974        ) -> wasmtime::Result<Result<Resource<HostOutgoingBody>, ()>> {
975            super::types::HostOutgoingRequest::body(&mut self.0.http(id), request)
976        }
977
978        fn drop(
979            &mut self,
980            id: NamedId,
981            request: Resource<HostOutgoingRequest>,
982        ) -> wasmtime::Result<()> {
983            super::types::HostOutgoingRequest::drop(&mut self.0.http(id), request)
984        }
985
986        fn method(
987            &mut self,
988            id: NamedId,
989            request: wasmtime::component::Resource<types::OutgoingRequest>,
990        ) -> wasmtime::Result<Method> {
991            super::types::HostOutgoingRequest::method(&mut self.0.http(id), request)
992        }
993
994        fn set_method(
995            &mut self,
996            id: NamedId,
997            request: wasmtime::component::Resource<types::OutgoingRequest>,
998            method: Method,
999        ) -> wasmtime::Result<Result<(), ()>> {
1000            super::types::HostOutgoingRequest::set_method(&mut self.0.http(id), request, method)
1001        }
1002
1003        fn path_with_query(
1004            &mut self,
1005            id: NamedId,
1006            request: wasmtime::component::Resource<types::OutgoingRequest>,
1007        ) -> wasmtime::Result<Option<String>> {
1008            super::types::HostOutgoingRequest::path_with_query(&mut self.0.http(id), request)
1009        }
1010
1011        fn set_path_with_query(
1012            &mut self,
1013            id: NamedId,
1014            request: wasmtime::component::Resource<types::OutgoingRequest>,
1015            path_with_query: Option<String>,
1016        ) -> wasmtime::Result<Result<(), ()>> {
1017            super::types::HostOutgoingRequest::set_path_with_query(
1018                &mut self.0.http(id),
1019                request,
1020                path_with_query,
1021            )
1022        }
1023
1024        fn scheme(
1025            &mut self,
1026            id: NamedId,
1027            request: wasmtime::component::Resource<types::OutgoingRequest>,
1028        ) -> wasmtime::Result<Option<Scheme>> {
1029            super::types::HostOutgoingRequest::scheme(&mut self.0.http(id), request)
1030        }
1031
1032        fn set_scheme(
1033            &mut self,
1034            id: NamedId,
1035            request: wasmtime::component::Resource<types::OutgoingRequest>,
1036            scheme: Option<Scheme>,
1037        ) -> wasmtime::Result<Result<(), ()>> {
1038            super::types::HostOutgoingRequest::set_scheme(&mut self.0.http(id), request, scheme)
1039        }
1040
1041        fn authority(
1042            &mut self,
1043            id: NamedId,
1044            request: wasmtime::component::Resource<types::OutgoingRequest>,
1045        ) -> wasmtime::Result<Option<String>> {
1046            super::types::HostOutgoingRequest::authority(&mut self.0.http(id), request)
1047        }
1048
1049        fn set_authority(
1050            &mut self,
1051            id: NamedId,
1052            request: wasmtime::component::Resource<types::OutgoingRequest>,
1053            authority: Option<String>,
1054        ) -> wasmtime::Result<Result<(), ()>> {
1055            super::types::HostOutgoingRequest::set_authority(
1056                &mut self.0.http(id),
1057                request,
1058                authority,
1059            )
1060        }
1061
1062        fn headers(
1063            &mut self,
1064            id: NamedId,
1065            request: wasmtime::component::Resource<types::OutgoingRequest>,
1066        ) -> wasmtime::Result<wasmtime::component::Resource<FieldMap>> {
1067            super::types::HostOutgoingRequest::headers(&mut self.0.http(id), request)
1068        }
1069    }
1070
1071    impl<T> named_types::HostResponseOutparam for WasiCtxNamedView<'_, T>
1072    where
1073        T: WasiHttpNamedView,
1074    {
1075        fn drop(
1076            &mut self,
1077            id: NamedId,
1078            this: Resource<HostResponseOutparam>,
1079        ) -> wasmtime::Result<()> {
1080            super::types::HostResponseOutparam::drop(&mut self.0.http(id), this)
1081        }
1082
1083        fn set(
1084            &mut self,
1085            id: NamedId,
1086            this: Resource<HostResponseOutparam>,
1087            resp: Result<Resource<HostOutgoingResponse>, types::ErrorCode>,
1088        ) -> wasmtime::Result<()> {
1089            super::types::HostResponseOutparam::set(&mut self.0.http(id), this, resp)
1090        }
1091
1092        fn send_informational(
1093            &mut self,
1094            id: NamedId,
1095            _id: Resource<HostResponseOutparam>,
1096            _status: u16,
1097            _headers: Resource<FieldMap>,
1098        ) -> HttpResult<()> {
1099            super::types::HostResponseOutparam::send_informational(
1100                &mut self.0.http(id),
1101                _id,
1102                _status,
1103                _headers,
1104            )
1105        }
1106    }
1107
1108    impl<T> named_types::HostIncomingResponse for WasiCtxNamedView<'_, T>
1109    where
1110        T: WasiHttpNamedView,
1111    {
1112        fn drop(
1113            &mut self,
1114            id: NamedId,
1115            response: Resource<HostIncomingResponse>,
1116        ) -> wasmtime::Result<()> {
1117            super::types::HostIncomingResponse::drop(&mut self.0.http(id), response)
1118        }
1119
1120        fn status(
1121            &mut self,
1122            id: NamedId,
1123            response: Resource<HostIncomingResponse>,
1124        ) -> wasmtime::Result<StatusCode> {
1125            super::types::HostIncomingResponse::status(&mut self.0.http(id), response)
1126        }
1127
1128        fn headers(
1129            &mut self,
1130            id: NamedId,
1131            response: Resource<HostIncomingResponse>,
1132        ) -> wasmtime::Result<Resource<FieldMap>> {
1133            super::types::HostIncomingResponse::headers(&mut self.0.http(id), response)
1134        }
1135
1136        fn consume(
1137            &mut self,
1138            id: NamedId,
1139            response: Resource<HostIncomingResponse>,
1140        ) -> wasmtime::Result<Result<Resource<HostIncomingBody>, ()>> {
1141            super::types::HostIncomingResponse::consume(&mut self.0.http(id), response)
1142        }
1143    }
1144
1145    impl<T> named_types::HostFutureTrailers for WasiCtxNamedView<'_, T>
1146    where
1147        T: WasiHttpNamedView,
1148    {
1149        fn drop(
1150            &mut self,
1151            id: NamedId,
1152            this: Resource<HostFutureTrailers>,
1153        ) -> wasmtime::Result<()> {
1154            super::types::HostFutureTrailers::drop(&mut self.0.http(id), this)
1155        }
1156
1157        fn subscribe(
1158            &mut self,
1159            id: NamedId,
1160            index: Resource<HostFutureTrailers>,
1161        ) -> wasmtime::Result<Resource<DynPollable>> {
1162            super::types::HostFutureTrailers::subscribe(&mut self.0.http(id), index)
1163        }
1164
1165        fn get(
1166            &mut self,
1167            id: NamedId,
1168            this: Resource<HostFutureTrailers>,
1169        ) -> wasmtime::Result<
1170            Option<Result<Result<Option<Resource<Trailers>>, types::ErrorCode>, ()>>,
1171        > {
1172            super::types::HostFutureTrailers::get(&mut self.0.http(id), this)
1173        }
1174    }
1175
1176    impl<T> named_types::HostIncomingBody for WasiCtxNamedView<'_, T>
1177    where
1178        T: WasiHttpNamedView,
1179    {
1180        fn stream(
1181            &mut self,
1182            id: NamedId,
1183            this: Resource<HostIncomingBody>,
1184        ) -> wasmtime::Result<Result<Resource<DynInputStream>, ()>> {
1185            super::types::HostIncomingBody::stream(&mut self.0.http(id), this)
1186        }
1187
1188        fn finish(
1189            &mut self,
1190            id: NamedId,
1191            this: Resource<HostIncomingBody>,
1192        ) -> wasmtime::Result<Resource<HostFutureTrailers>> {
1193            super::types::HostIncomingBody::finish(&mut self.0.http(id), this)
1194        }
1195
1196        fn drop(&mut self, id: NamedId, this: Resource<HostIncomingBody>) -> wasmtime::Result<()> {
1197            super::types::HostIncomingBody::drop(&mut self.0.http(id), this)
1198        }
1199    }
1200
1201    impl<T> named_types::HostOutgoingResponse for WasiCtxNamedView<'_, T>
1202    where
1203        T: WasiHttpNamedView,
1204    {
1205        fn new(
1206            &mut self,
1207            id: NamedId,
1208            headers: Resource<FieldMap>,
1209        ) -> wasmtime::Result<Resource<HostOutgoingResponse>> {
1210            super::types::HostOutgoingResponse::new(&mut self.0.http(id), headers)
1211        }
1212
1213        fn body(
1214            &mut self,
1215            id: NamedId,
1216            this: Resource<HostOutgoingResponse>,
1217        ) -> wasmtime::Result<Result<Resource<HostOutgoingBody>, ()>> {
1218            super::types::HostOutgoingResponse::body(&mut self.0.http(id), this)
1219        }
1220
1221        fn status_code(
1222            &mut self,
1223            id: NamedId,
1224            this: Resource<HostOutgoingResponse>,
1225        ) -> wasmtime::Result<types::StatusCode> {
1226            super::types::HostOutgoingResponse::status_code(&mut self.0.http(id), this)
1227        }
1228
1229        fn set_status_code(
1230            &mut self,
1231            id: NamedId,
1232            this: Resource<HostOutgoingResponse>,
1233            status: types::StatusCode,
1234        ) -> wasmtime::Result<Result<(), ()>> {
1235            super::types::HostOutgoingResponse::set_status_code(&mut self.0.http(id), this, status)
1236        }
1237
1238        fn headers(
1239            &mut self,
1240            id: NamedId,
1241            this: Resource<HostOutgoingResponse>,
1242        ) -> wasmtime::Result<Resource<FieldMap>> {
1243            super::types::HostOutgoingResponse::headers(&mut self.0.http(id), this)
1244        }
1245
1246        fn drop(
1247            &mut self,
1248            id: NamedId,
1249            this: Resource<HostOutgoingResponse>,
1250        ) -> wasmtime::Result<()> {
1251            super::types::HostOutgoingResponse::drop(&mut self.0.http(id), this)
1252        }
1253    }
1254
1255    impl<T> named_types::HostFutureIncomingResponse for WasiCtxNamedView<'_, T>
1256    where
1257        T: WasiHttpNamedView,
1258    {
1259        fn drop(
1260            &mut self,
1261            id: NamedId,
1262            this: Resource<HostFutureIncomingResponse>,
1263        ) -> wasmtime::Result<()> {
1264            super::types::HostFutureIncomingResponse::drop(&mut self.0.http(id), this)
1265        }
1266
1267        fn get(
1268            &mut self,
1269            id: NamedId,
1270            this: Resource<HostFutureIncomingResponse>,
1271        ) -> wasmtime::Result<
1272            Option<Result<Result<Resource<HostIncomingResponse>, types::ErrorCode>, ()>>,
1273        > {
1274            super::types::HostFutureIncomingResponse::get(&mut self.0.http(id), this)
1275        }
1276
1277        fn subscribe(
1278            &mut self,
1279            id: NamedId,
1280            this: Resource<HostFutureIncomingResponse>,
1281        ) -> wasmtime::Result<Resource<DynPollable>> {
1282            super::types::HostFutureIncomingResponse::subscribe(&mut self.0.http(id), this)
1283        }
1284    }
1285
1286    impl<T> named_types::HostOutgoingBody for WasiCtxNamedView<'_, T>
1287    where
1288        T: WasiHttpNamedView,
1289    {
1290        fn write(
1291            &mut self,
1292            id: NamedId,
1293            this: Resource<HostOutgoingBody>,
1294        ) -> wasmtime::Result<Result<Resource<DynOutputStream>, ()>> {
1295            super::types::HostOutgoingBody::write(&mut self.0.http(id), this)
1296        }
1297
1298        fn finish(
1299            &mut self,
1300            id: NamedId,
1301            this: Resource<HostOutgoingBody>,
1302            ts: Option<Resource<Trailers>>,
1303        ) -> HttpResult<()> {
1304            super::types::HostOutgoingBody::finish(&mut self.0.http(id), this, ts)
1305        }
1306
1307        fn drop(&mut self, id: NamedId, this: Resource<HostOutgoingBody>) -> wasmtime::Result<()> {
1308            super::types::HostOutgoingBody::drop(&mut self.0.http(id), this)
1309        }
1310    }
1311
1312    impl<T> named_types::HostRequestOptions for WasiCtxNamedView<'_, T>
1313    where
1314        T: WasiHttpNamedView,
1315    {
1316        fn new(&mut self, id: NamedId) -> wasmtime::Result<Resource<types::RequestOptions>> {
1317            super::types::HostRequestOptions::new(&mut self.0.http(id))
1318        }
1319
1320        fn connect_timeout(
1321            &mut self,
1322            id: NamedId,
1323            opts: Resource<types::RequestOptions>,
1324        ) -> wasmtime::Result<Option<types::Duration>> {
1325            super::types::HostRequestOptions::connect_timeout(&mut self.0.http(id), opts)
1326        }
1327
1328        fn set_connect_timeout(
1329            &mut self,
1330            id: NamedId,
1331            opts: Resource<types::RequestOptions>,
1332            duration: Option<types::Duration>,
1333        ) -> wasmtime::Result<Result<(), ()>> {
1334            super::types::HostRequestOptions::set_connect_timeout(
1335                &mut self.0.http(id),
1336                opts,
1337                duration,
1338            )
1339        }
1340
1341        fn first_byte_timeout(
1342            &mut self,
1343            id: NamedId,
1344            opts: Resource<types::RequestOptions>,
1345        ) -> wasmtime::Result<Option<types::Duration>> {
1346            super::types::HostRequestOptions::first_byte_timeout(&mut self.0.http(id), opts)
1347        }
1348
1349        fn set_first_byte_timeout(
1350            &mut self,
1351            id: NamedId,
1352            opts: Resource<types::RequestOptions>,
1353            duration: Option<types::Duration>,
1354        ) -> wasmtime::Result<Result<(), ()>> {
1355            super::types::HostRequestOptions::set_first_byte_timeout(
1356                &mut self.0.http(id),
1357                opts,
1358                duration,
1359            )
1360        }
1361
1362        fn between_bytes_timeout(
1363            &mut self,
1364            id: NamedId,
1365            opts: Resource<types::RequestOptions>,
1366        ) -> wasmtime::Result<Option<types::Duration>> {
1367            super::types::HostRequestOptions::between_bytes_timeout(&mut self.0.http(id), opts)
1368        }
1369
1370        fn set_between_bytes_timeout(
1371            &mut self,
1372            id: NamedId,
1373            opts: Resource<types::RequestOptions>,
1374            duration: Option<types::Duration>,
1375        ) -> wasmtime::Result<Result<(), ()>> {
1376            super::types::HostRequestOptions::set_between_bytes_timeout(
1377                &mut self.0.http(id),
1378                opts,
1379                duration,
1380            )
1381        }
1382
1383        fn drop(
1384            &mut self,
1385            id: NamedId,
1386            rep: Resource<types::RequestOptions>,
1387        ) -> wasmtime::Result<()> {
1388            super::types::HostRequestOptions::drop(&mut self.0.http(id), rep)
1389        }
1390    }
1391}