wasmtime_wasi_http/
types_impl.rs

1//! Implementation for the `wasi:http/types` interface.
2
3use crate::{
4    HttpError, HttpResult, WasiHttpImpl, WasiHttpView,
5    bindings::http::types::{self, Headers, Method, Scheme, StatusCode, Trailers},
6    body::{HostFutureTrailers, HostIncomingBody, HostOutgoingBody, StreamContext},
7    types::{
8        FieldMap, HostFields, HostFutureIncomingResponse, HostIncomingRequest,
9        HostIncomingResponse, HostOutgoingRequest, HostOutgoingResponse, HostResponseOutparam,
10        is_forbidden_header, remove_forbidden_headers,
11    },
12};
13use anyhow::{Context, anyhow};
14use std::any::Any;
15use std::str::FromStr;
16use wasmtime::component::{Resource, ResourceTable, ResourceTableError};
17use wasmtime_wasi::p2::{DynInputStream, DynOutputStream, DynPollable, IoView};
18
19impl<T> crate::bindings::http::types::Host for WasiHttpImpl<T>
20where
21    T: WasiHttpView,
22{
23    fn convert_error_code(&mut self, err: crate::HttpError) -> wasmtime::Result<types::ErrorCode> {
24        err.downcast()
25    }
26
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/// Extract the `Content-Length` header value from a [`FieldMap`], returning `None` if it's not
37/// present. This function will return `Err` if it's not possible to parse the `Content-Length`
38/// header.
39fn get_content_length(fields: &FieldMap) -> Result<Option<u64>, ()> {
40    let header_val = match fields.get(hyper::header::CONTENT_LENGTH) {
41        Some(val) => val,
42        None => return Ok(None),
43    };
44
45    let header_str = match header_val.to_str() {
46        Ok(val) => val,
47        Err(_) => return Err(()),
48    };
49
50    match header_str.parse() {
51        Ok(len) => Ok(Some(len)),
52        Err(_) => Err(()),
53    }
54}
55
56/// Take ownership of the underlying [`FieldMap`] associated with this fields resource. If the
57/// fields resource references another fields, the returned [`FieldMap`] will be cloned.
58fn move_fields(
59    table: &mut ResourceTable,
60    id: Resource<HostFields>,
61) -> Result<FieldMap, ResourceTableError> {
62    match table.delete(id)? {
63        HostFields::Ref { parent, get_fields } => {
64            let entry = table.get_any_mut(parent)?;
65            Ok(get_fields(entry).clone())
66        }
67
68        HostFields::Owned { fields } => Ok(fields),
69    }
70}
71
72fn get_fields<'a>(
73    table: &'a mut ResourceTable,
74    id: &Resource<HostFields>,
75) -> wasmtime::Result<&'a FieldMap> {
76    let fields = table.get(&id)?;
77    if let HostFields::Ref { parent, get_fields } = *fields {
78        let entry = table.get_any_mut(parent)?;
79        return Ok(get_fields(entry));
80    }
81
82    match table.get_mut(&id)? {
83        HostFields::Owned { fields } => Ok(fields),
84        // NB: ideally the `if let` above would go here instead. That makes
85        // the borrow-checker unhappy. Unclear why. If you, dear reader, can
86        // refactor this to remove the `unreachable!` please do.
87        HostFields::Ref { .. } => unreachable!(),
88    }
89}
90
91fn get_fields_mut<'a>(
92    table: &'a mut ResourceTable,
93    id: &Resource<HostFields>,
94) -> wasmtime::Result<Result<&'a mut FieldMap, types::HeaderError>> {
95    match table.get_mut(&id)? {
96        HostFields::Owned { fields } => Ok(Ok(fields)),
97        HostFields::Ref { .. } => Ok(Err(types::HeaderError::Immutable)),
98    }
99}
100
101impl<T> crate::bindings::http::types::HostFields for WasiHttpImpl<T>
102where
103    T: WasiHttpView,
104{
105    fn new(&mut self) -> wasmtime::Result<Resource<HostFields>> {
106        let id = self
107            .table()
108            .push(HostFields::Owned {
109                fields: hyper::HeaderMap::new(),
110            })
111            .context("[new_fields] pushing fields")?;
112
113        Ok(id)
114    }
115
116    fn from_list(
117        &mut self,
118        entries: Vec<(String, Vec<u8>)>,
119    ) -> wasmtime::Result<Result<Resource<HostFields>, types::HeaderError>> {
120        let mut fields = hyper::HeaderMap::new();
121
122        for (header, value) in entries {
123            let header = match hyper::header::HeaderName::from_bytes(header.as_bytes()) {
124                Ok(header) => header,
125                Err(_) => return Ok(Err(types::HeaderError::InvalidSyntax)),
126            };
127
128            if is_forbidden_header(self, &header) {
129                return Ok(Err(types::HeaderError::Forbidden));
130            }
131
132            let value = match hyper::header::HeaderValue::from_bytes(&value) {
133                Ok(value) => value,
134                Err(_) => return Ok(Err(types::HeaderError::InvalidSyntax)),
135            };
136
137            fields.append(header, value);
138        }
139
140        let id = self
141            .table()
142            .push(HostFields::Owned { fields })
143            .context("[new_fields] pushing fields")?;
144
145        Ok(Ok(id))
146    }
147
148    fn drop(&mut self, fields: Resource<HostFields>) -> wasmtime::Result<()> {
149        self.table()
150            .delete(fields)
151            .context("[drop_fields] deleting fields")?;
152        Ok(())
153    }
154
155    fn get(
156        &mut self,
157        fields: Resource<HostFields>,
158        name: String,
159    ) -> wasmtime::Result<Vec<Vec<u8>>> {
160        let fields = get_fields(self.table(), &fields).context("[fields_get] getting fields")?;
161
162        let header = match hyper::header::HeaderName::from_bytes(name.as_bytes()) {
163            Ok(header) => header,
164            Err(_) => return Ok(vec![]),
165        };
166
167        if !fields.contains_key(&header) {
168            return Ok(vec![]);
169        }
170
171        let res = fields
172            .get_all(&header)
173            .into_iter()
174            .map(|val| val.as_bytes().to_owned())
175            .collect();
176        Ok(res)
177    }
178
179    fn has(&mut self, fields: Resource<HostFields>, name: String) -> wasmtime::Result<bool> {
180        let fields = get_fields(self.table(), &fields).context("[fields_get] getting fields")?;
181
182        match hyper::header::HeaderName::from_bytes(name.as_bytes()) {
183            Ok(header) => Ok(fields.contains_key(&header)),
184            Err(_) => Ok(false),
185        }
186    }
187
188    fn set(
189        &mut self,
190        fields: Resource<HostFields>,
191        name: String,
192        byte_values: Vec<Vec<u8>>,
193    ) -> wasmtime::Result<Result<(), types::HeaderError>> {
194        let header = match hyper::header::HeaderName::from_bytes(name.as_bytes()) {
195            Ok(header) => header,
196            Err(_) => return Ok(Err(types::HeaderError::InvalidSyntax)),
197        };
198
199        if is_forbidden_header(self, &header) {
200            return Ok(Err(types::HeaderError::Forbidden));
201        }
202
203        let mut values = Vec::with_capacity(byte_values.len());
204        for value in byte_values {
205            match hyper::header::HeaderValue::from_bytes(&value) {
206                Ok(value) => values.push(value),
207                Err(_) => return Ok(Err(types::HeaderError::InvalidSyntax)),
208            }
209        }
210
211        Ok(get_fields_mut(self.table(), &fields)
212            .context("[fields_set] getting mutable fields")?
213            .map(|fields| {
214                fields.remove(&header);
215                for value in values {
216                    fields.append(&header, value);
217                }
218            }))
219    }
220
221    fn delete(
222        &mut self,
223        fields: Resource<HostFields>,
224        name: String,
225    ) -> wasmtime::Result<Result<(), types::HeaderError>> {
226        let header = match hyper::header::HeaderName::from_bytes(name.as_bytes()) {
227            Ok(header) => header,
228            Err(_) => return Ok(Err(types::HeaderError::InvalidSyntax)),
229        };
230
231        if is_forbidden_header(self, &header) {
232            return Ok(Err(types::HeaderError::Forbidden));
233        }
234
235        Ok(get_fields_mut(self.table(), &fields)?.map(|fields| {
236            fields.remove(header);
237        }))
238    }
239
240    fn append(
241        &mut self,
242        fields: Resource<HostFields>,
243        name: String,
244        value: Vec<u8>,
245    ) -> wasmtime::Result<Result<(), types::HeaderError>> {
246        let header = match hyper::header::HeaderName::from_bytes(name.as_bytes()) {
247            Ok(header) => header,
248            Err(_) => return Ok(Err(types::HeaderError::InvalidSyntax)),
249        };
250
251        if is_forbidden_header(self, &header) {
252            return Ok(Err(types::HeaderError::Forbidden));
253        }
254
255        let value = match hyper::header::HeaderValue::from_bytes(&value) {
256            Ok(value) => value,
257            Err(_) => return Ok(Err(types::HeaderError::InvalidSyntax)),
258        };
259
260        Ok(get_fields_mut(self.table(), &fields)
261            .context("[fields_append] getting mutable fields")?
262            .map(|fields| {
263                fields.append(header, value);
264            }))
265    }
266
267    fn entries(
268        &mut self,
269        fields: Resource<HostFields>,
270    ) -> wasmtime::Result<Vec<(String, Vec<u8>)>> {
271        Ok(get_fields(self.table(), &fields)?
272            .iter()
273            .map(|(name, value)| (name.as_str().to_owned(), value.as_bytes().to_owned()))
274            .collect())
275    }
276
277    fn clone(&mut self, fields: Resource<HostFields>) -> wasmtime::Result<Resource<HostFields>> {
278        let fields = get_fields(self.table(), &fields)
279            .context("[fields_clone] getting fields")?
280            .clone();
281
282        let id = self
283            .table()
284            .push(HostFields::Owned { fields })
285            .context("[fields_clone] pushing fields")?;
286
287        Ok(id)
288    }
289}
290
291impl<T> crate::bindings::http::types::HostIncomingRequest for WasiHttpImpl<T>
292where
293    T: WasiHttpView,
294{
295    fn method(&mut self, id: Resource<HostIncomingRequest>) -> wasmtime::Result<Method> {
296        let method = self.table().get(&id)?.parts.method.clone();
297        Ok(method.into())
298    }
299    fn path_with_query(
300        &mut self,
301        id: Resource<HostIncomingRequest>,
302    ) -> wasmtime::Result<Option<String>> {
303        let req = self.table().get(&id)?;
304        Ok(req
305            .parts
306            .uri
307            .path_and_query()
308            .map(|path_and_query| path_and_query.as_str().to_owned()))
309    }
310    fn scheme(&mut self, id: Resource<HostIncomingRequest>) -> wasmtime::Result<Option<Scheme>> {
311        let req = self.table().get(&id)?;
312        Ok(Some(req.scheme.clone()))
313    }
314    fn authority(&mut self, id: Resource<HostIncomingRequest>) -> wasmtime::Result<Option<String>> {
315        let req = self.table().get(&id)?;
316        Ok(Some(req.authority.clone()))
317    }
318
319    fn headers(
320        &mut self,
321        id: Resource<HostIncomingRequest>,
322    ) -> wasmtime::Result<Resource<Headers>> {
323        let _ = self.table().get(&id)?;
324
325        fn get_fields(elem: &mut dyn Any) -> &mut FieldMap {
326            &mut elem
327                .downcast_mut::<HostIncomingRequest>()
328                .unwrap()
329                .parts
330                .headers
331        }
332
333        let headers = self.table().push_child(
334            HostFields::Ref {
335                parent: id.rep(),
336                get_fields,
337            },
338            &id,
339        )?;
340
341        Ok(headers)
342    }
343
344    fn consume(
345        &mut self,
346        id: Resource<HostIncomingRequest>,
347    ) -> wasmtime::Result<Result<Resource<HostIncomingBody>, ()>> {
348        let req = self.table().get_mut(&id)?;
349        match req.body.take() {
350            Some(body) => {
351                let id = self.table().push(body)?;
352                Ok(Ok(id))
353            }
354
355            None => Ok(Err(())),
356        }
357    }
358
359    fn drop(&mut self, id: Resource<HostIncomingRequest>) -> wasmtime::Result<()> {
360        let _ = self.table().delete(id)?;
361        Ok(())
362    }
363}
364
365impl<T> crate::bindings::http::types::HostOutgoingRequest for WasiHttpImpl<T>
366where
367    T: WasiHttpView,
368{
369    fn new(
370        &mut self,
371        headers: Resource<Headers>,
372    ) -> wasmtime::Result<Resource<HostOutgoingRequest>> {
373        let headers = move_fields(self.table(), headers)?;
374
375        self.table()
376            .push(HostOutgoingRequest {
377                path_with_query: None,
378                authority: None,
379                method: types::Method::Get,
380                headers,
381                scheme: None,
382                body: None,
383            })
384            .context("[new_outgoing_request] pushing request")
385    }
386
387    fn body(
388        &mut self,
389        request: Resource<HostOutgoingRequest>,
390    ) -> wasmtime::Result<Result<Resource<HostOutgoingBody>, ()>> {
391        let buffer_chunks = self.outgoing_body_buffer_chunks();
392        let chunk_size = self.outgoing_body_chunk_size();
393        let req = self
394            .table()
395            .get_mut(&request)
396            .context("[outgoing_request_write] getting request")?;
397
398        if req.body.is_some() {
399            return Ok(Err(()));
400        }
401
402        let size = match get_content_length(&req.headers) {
403            Ok(size) => size,
404            Err(e) => return Ok(Err(e)),
405        };
406
407        let (host_body, hyper_body) =
408            HostOutgoingBody::new(StreamContext::Request, size, buffer_chunks, chunk_size);
409
410        req.body = Some(hyper_body);
411
412        // The output stream will necessarily outlive the request, because we could be still
413        // writing to the stream after `outgoing-handler.handle` is called.
414        let outgoing_body = self.table().push(host_body)?;
415
416        Ok(Ok(outgoing_body))
417    }
418
419    fn drop(&mut self, request: Resource<HostOutgoingRequest>) -> wasmtime::Result<()> {
420        let _ = self.table().delete(request)?;
421        Ok(())
422    }
423
424    fn method(
425        &mut self,
426        request: wasmtime::component::Resource<types::OutgoingRequest>,
427    ) -> wasmtime::Result<Method> {
428        Ok(self.table().get(&request)?.method.clone())
429    }
430
431    fn set_method(
432        &mut self,
433        request: wasmtime::component::Resource<types::OutgoingRequest>,
434        method: Method,
435    ) -> wasmtime::Result<Result<(), ()>> {
436        let req = self.table().get_mut(&request)?;
437
438        if let Method::Other(s) = &method {
439            if let Err(_) = http::Method::from_str(s) {
440                return Ok(Err(()));
441            }
442        }
443
444        req.method = method;
445
446        Ok(Ok(()))
447    }
448
449    fn path_with_query(
450        &mut self,
451        request: wasmtime::component::Resource<types::OutgoingRequest>,
452    ) -> wasmtime::Result<Option<String>> {
453        Ok(self.table().get(&request)?.path_with_query.clone())
454    }
455
456    fn set_path_with_query(
457        &mut self,
458        request: wasmtime::component::Resource<types::OutgoingRequest>,
459        path_with_query: Option<String>,
460    ) -> wasmtime::Result<Result<(), ()>> {
461        let req = self.table().get_mut(&request)?;
462
463        if let Some(s) = path_with_query.as_ref() {
464            if let Err(_) = http::uri::PathAndQuery::from_str(s) {
465                return Ok(Err(()));
466            }
467        }
468
469        req.path_with_query = path_with_query;
470
471        Ok(Ok(()))
472    }
473
474    fn scheme(
475        &mut self,
476        request: wasmtime::component::Resource<types::OutgoingRequest>,
477    ) -> wasmtime::Result<Option<Scheme>> {
478        Ok(self.table().get(&request)?.scheme.clone())
479    }
480
481    fn set_scheme(
482        &mut self,
483        request: wasmtime::component::Resource<types::OutgoingRequest>,
484        scheme: Option<Scheme>,
485    ) -> wasmtime::Result<Result<(), ()>> {
486        let req = self.table().get_mut(&request)?;
487
488        if let Some(types::Scheme::Other(s)) = scheme.as_ref() {
489            if let Err(_) = http::uri::Scheme::from_str(s.as_str()) {
490                return Ok(Err(()));
491            }
492        }
493
494        req.scheme = scheme;
495
496        Ok(Ok(()))
497    }
498
499    fn authority(
500        &mut self,
501        request: wasmtime::component::Resource<types::OutgoingRequest>,
502    ) -> wasmtime::Result<Option<String>> {
503        Ok(self.table().get(&request)?.authority.clone())
504    }
505
506    fn set_authority(
507        &mut self,
508        request: wasmtime::component::Resource<types::OutgoingRequest>,
509        authority: Option<String>,
510    ) -> wasmtime::Result<Result<(), ()>> {
511        let req = self.table().get_mut(&request)?;
512
513        if let Some(s) = authority.as_ref() {
514            if let Err(_) = http::uri::Authority::from_str(s.as_str()) {
515                return Ok(Err(()));
516            }
517        }
518
519        req.authority = authority;
520
521        Ok(Ok(()))
522    }
523
524    fn headers(
525        &mut self,
526        request: wasmtime::component::Resource<types::OutgoingRequest>,
527    ) -> wasmtime::Result<wasmtime::component::Resource<Headers>> {
528        let _ = self
529            .table()
530            .get(&request)
531            .context("[outgoing_request_headers] getting request")?;
532
533        fn get_fields(elem: &mut dyn Any) -> &mut FieldMap {
534            &mut elem
535                .downcast_mut::<types::OutgoingRequest>()
536                .unwrap()
537                .headers
538        }
539
540        let id = self.table().push_child(
541            HostFields::Ref {
542                parent: request.rep(),
543                get_fields,
544            },
545            &request,
546        )?;
547
548        Ok(id)
549    }
550}
551
552impl<T> crate::bindings::http::types::HostResponseOutparam for WasiHttpImpl<T>
553where
554    T: WasiHttpView,
555{
556    fn drop(&mut self, id: Resource<HostResponseOutparam>) -> wasmtime::Result<()> {
557        let _ = self.table().delete(id)?;
558        Ok(())
559    }
560    fn set(
561        &mut self,
562        id: Resource<HostResponseOutparam>,
563        resp: Result<Resource<HostOutgoingResponse>, types::ErrorCode>,
564    ) -> wasmtime::Result<()> {
565        let val = match resp {
566            Ok(resp) => Ok(self.table().delete(resp)?.try_into()?),
567            Err(e) => Err(e),
568        };
569
570        let resp = self.table().delete(id)?;
571        // Giving the API doesn't return any error, it's probably
572        // better to ignore the error than trap the guest, in case of
573        // host timeout and dropped the receiver side of the channel.
574        // See also: #10784
575        let _ = resp.result.send(val);
576        Ok(())
577    }
578
579    fn send_informational(
580        &mut self,
581        _id: Resource<HostResponseOutparam>,
582        _status: u16,
583        _headers: Resource<Headers>,
584    ) -> HttpResult<()> {
585        Err(HttpError::trap(anyhow!("not implemented")))
586    }
587}
588
589impl<T> crate::bindings::http::types::HostIncomingResponse for WasiHttpImpl<T>
590where
591    T: WasiHttpView,
592{
593    fn drop(&mut self, response: Resource<HostIncomingResponse>) -> wasmtime::Result<()> {
594        let _ = self
595            .table()
596            .delete(response)
597            .context("[drop_incoming_response] deleting response")?;
598        Ok(())
599    }
600
601    fn status(&mut self, response: Resource<HostIncomingResponse>) -> wasmtime::Result<StatusCode> {
602        let r = self
603            .table()
604            .get(&response)
605            .context("[incoming_response_status] getting response")?;
606        Ok(r.status)
607    }
608
609    fn headers(
610        &mut self,
611        response: Resource<HostIncomingResponse>,
612    ) -> wasmtime::Result<Resource<Headers>> {
613        let _ = self
614            .table()
615            .get(&response)
616            .context("[incoming_response_headers] getting response")?;
617
618        fn get_fields(elem: &mut dyn Any) -> &mut FieldMap {
619            &mut elem.downcast_mut::<HostIncomingResponse>().unwrap().headers
620        }
621
622        let id = self.table().push_child(
623            HostFields::Ref {
624                parent: response.rep(),
625                get_fields,
626            },
627            &response,
628        )?;
629
630        Ok(id)
631    }
632
633    fn consume(
634        &mut self,
635        response: Resource<HostIncomingResponse>,
636    ) -> wasmtime::Result<Result<Resource<HostIncomingBody>, ()>> {
637        let table = self.table();
638        let r = table
639            .get_mut(&response)
640            .context("[incoming_response_consume] getting response")?;
641
642        match r.body.take() {
643            Some(body) => {
644                let id = self.table().push(body)?;
645                Ok(Ok(id))
646            }
647
648            None => Ok(Err(())),
649        }
650    }
651}
652
653impl<T> crate::bindings::http::types::HostFutureTrailers for WasiHttpImpl<T>
654where
655    T: WasiHttpView,
656{
657    fn drop(&mut self, id: Resource<HostFutureTrailers>) -> wasmtime::Result<()> {
658        let _ = self
659            .table()
660            .delete(id)
661            .context("[drop future-trailers] deleting future-trailers")?;
662        Ok(())
663    }
664
665    fn subscribe(
666        &mut self,
667        index: Resource<HostFutureTrailers>,
668    ) -> wasmtime::Result<Resource<DynPollable>> {
669        wasmtime_wasi::p2::subscribe(self.table(), index)
670    }
671
672    fn get(
673        &mut self,
674        id: Resource<HostFutureTrailers>,
675    ) -> wasmtime::Result<Option<Result<Result<Option<Resource<Trailers>>, types::ErrorCode>, ()>>>
676    {
677        let trailers = self.table().get_mut(&id)?;
678        match trailers {
679            HostFutureTrailers::Waiting(_) => return Ok(None),
680            HostFutureTrailers::Consumed => return Ok(Some(Err(()))),
681            HostFutureTrailers::Done(_) => {}
682        };
683
684        let res = match std::mem::replace(trailers, HostFutureTrailers::Consumed) {
685            HostFutureTrailers::Done(res) => res,
686            _ => unreachable!(),
687        };
688
689        let mut fields = match res {
690            Ok(Some(fields)) => fields,
691            Ok(None) => return Ok(Some(Ok(Ok(None)))),
692            Err(e) => return Ok(Some(Ok(Err(e)))),
693        };
694
695        remove_forbidden_headers(self, &mut fields);
696
697        let ts = self.table().push(HostFields::Owned { fields })?;
698
699        Ok(Some(Ok(Ok(Some(ts)))))
700    }
701}
702
703impl<T> crate::bindings::http::types::HostIncomingBody for WasiHttpImpl<T>
704where
705    T: WasiHttpView,
706{
707    fn stream(
708        &mut self,
709        id: Resource<HostIncomingBody>,
710    ) -> wasmtime::Result<Result<Resource<DynInputStream>, ()>> {
711        let body = self.table().get_mut(&id)?;
712
713        if let Some(stream) = body.take_stream() {
714            let stream: DynInputStream = Box::new(stream);
715            let stream = self.table().push_child(stream, &id)?;
716            return Ok(Ok(stream));
717        }
718
719        Ok(Err(()))
720    }
721
722    fn finish(
723        &mut self,
724        id: Resource<HostIncomingBody>,
725    ) -> wasmtime::Result<Resource<HostFutureTrailers>> {
726        let body = self.table().delete(id)?;
727        let trailers = self.table().push(body.into_future_trailers())?;
728        Ok(trailers)
729    }
730
731    fn drop(&mut self, id: Resource<HostIncomingBody>) -> wasmtime::Result<()> {
732        let _ = self.table().delete(id)?;
733        Ok(())
734    }
735}
736
737impl<T> crate::bindings::http::types::HostOutgoingResponse for WasiHttpImpl<T>
738where
739    T: WasiHttpView,
740{
741    fn new(
742        &mut self,
743        headers: Resource<Headers>,
744    ) -> wasmtime::Result<Resource<HostOutgoingResponse>> {
745        let fields = move_fields(self.table(), headers)?;
746
747        let id = self.table().push(HostOutgoingResponse {
748            status: http::StatusCode::OK,
749            headers: fields,
750            body: None,
751        })?;
752
753        Ok(id)
754    }
755
756    fn body(
757        &mut self,
758        id: Resource<HostOutgoingResponse>,
759    ) -> wasmtime::Result<Result<Resource<HostOutgoingBody>, ()>> {
760        let buffer_chunks = self.outgoing_body_buffer_chunks();
761        let chunk_size = self.outgoing_body_chunk_size();
762        let resp = self.table().get_mut(&id)?;
763
764        if resp.body.is_some() {
765            return Ok(Err(()));
766        }
767
768        let size = match get_content_length(&resp.headers) {
769            Ok(size) => size,
770            Err(e) => return Ok(Err(e)),
771        };
772
773        let (host, body) =
774            HostOutgoingBody::new(StreamContext::Response, size, buffer_chunks, chunk_size);
775
776        resp.body.replace(body);
777
778        let id = self.table().push(host)?;
779
780        Ok(Ok(id))
781    }
782
783    fn status_code(
784        &mut self,
785        id: Resource<HostOutgoingResponse>,
786    ) -> wasmtime::Result<types::StatusCode> {
787        Ok(self.table().get(&id)?.status.into())
788    }
789
790    fn set_status_code(
791        &mut self,
792        id: Resource<HostOutgoingResponse>,
793        status: types::StatusCode,
794    ) -> wasmtime::Result<Result<(), ()>> {
795        let resp = self.table().get_mut(&id)?;
796
797        match http::StatusCode::from_u16(status) {
798            Ok(status) => resp.status = status,
799            Err(_) => return Ok(Err(())),
800        };
801
802        Ok(Ok(()))
803    }
804
805    fn headers(
806        &mut self,
807        id: Resource<HostOutgoingResponse>,
808    ) -> wasmtime::Result<Resource<types::Headers>> {
809        // Trap if the outgoing-response doesn't exist.
810        let _ = self.table().get(&id)?;
811
812        fn get_fields(elem: &mut dyn Any) -> &mut FieldMap {
813            let resp = elem.downcast_mut::<HostOutgoingResponse>().unwrap();
814            &mut resp.headers
815        }
816
817        Ok(self.table().push_child(
818            HostFields::Ref {
819                parent: id.rep(),
820                get_fields,
821            },
822            &id,
823        )?)
824    }
825
826    fn drop(&mut self, id: Resource<HostOutgoingResponse>) -> wasmtime::Result<()> {
827        let _ = self.table().delete(id)?;
828        Ok(())
829    }
830}
831
832impl<T> crate::bindings::http::types::HostFutureIncomingResponse for WasiHttpImpl<T>
833where
834    T: WasiHttpView,
835{
836    fn drop(&mut self, id: Resource<HostFutureIncomingResponse>) -> wasmtime::Result<()> {
837        let _ = self.table().delete(id)?;
838        Ok(())
839    }
840
841    fn get(
842        &mut self,
843        id: Resource<HostFutureIncomingResponse>,
844    ) -> wasmtime::Result<
845        Option<Result<Result<Resource<HostIncomingResponse>, types::ErrorCode>, ()>>,
846    > {
847        let resp = self.table().get_mut(&id)?;
848
849        match resp {
850            HostFutureIncomingResponse::Pending(_) => return Ok(None),
851            HostFutureIncomingResponse::Consumed => return Ok(Some(Err(()))),
852            HostFutureIncomingResponse::Ready(_) => {}
853        }
854
855        let resp =
856            match std::mem::replace(resp, HostFutureIncomingResponse::Consumed).unwrap_ready() {
857                Err(e) => {
858                    // Trapping if it's not possible to downcast to an wasi-http error
859                    let e = e.downcast::<types::ErrorCode>()?;
860                    return Ok(Some(Ok(Err(e))));
861                }
862
863                Ok(Ok(resp)) => resp,
864                Ok(Err(e)) => return Ok(Some(Ok(Err(e)))),
865            };
866
867        let (mut parts, body) = resp.resp.into_parts();
868
869        remove_forbidden_headers(self, &mut parts.headers);
870
871        let resp = self.table().push(HostIncomingResponse {
872            status: parts.status.as_u16(),
873            headers: parts.headers,
874            body: Some({
875                let mut body = HostIncomingBody::new(body, resp.between_bytes_timeout);
876                if let Some(worker) = resp.worker {
877                    body.retain_worker(worker);
878                }
879                body
880            }),
881        })?;
882
883        Ok(Some(Ok(Ok(resp))))
884    }
885
886    fn subscribe(
887        &mut self,
888        id: Resource<HostFutureIncomingResponse>,
889    ) -> wasmtime::Result<Resource<DynPollable>> {
890        wasmtime_wasi::p2::subscribe(self.table(), id)
891    }
892}
893
894impl<T> crate::bindings::http::types::HostOutgoingBody for WasiHttpImpl<T>
895where
896    T: WasiHttpView,
897{
898    fn write(
899        &mut self,
900        id: Resource<HostOutgoingBody>,
901    ) -> wasmtime::Result<Result<Resource<DynOutputStream>, ()>> {
902        let body = self.table().get_mut(&id)?;
903        if let Some(stream) = body.take_output_stream() {
904            let id = self.table().push_child(stream, &id)?;
905            Ok(Ok(id))
906        } else {
907            Ok(Err(()))
908        }
909    }
910
911    fn finish(
912        &mut self,
913        id: Resource<HostOutgoingBody>,
914        ts: Option<Resource<Trailers>>,
915    ) -> crate::HttpResult<()> {
916        let body = self.table().delete(id)?;
917
918        let ts = if let Some(ts) = ts {
919            Some(move_fields(self.table(), ts)?)
920        } else {
921            None
922        };
923
924        body.finish(ts)?;
925        Ok(())
926    }
927
928    fn drop(&mut self, id: Resource<HostOutgoingBody>) -> wasmtime::Result<()> {
929        self.table().delete(id)?.abort();
930        Ok(())
931    }
932}
933
934impl<T> crate::bindings::http::types::HostRequestOptions for WasiHttpImpl<T>
935where
936    T: WasiHttpView,
937{
938    fn new(&mut self) -> wasmtime::Result<Resource<types::RequestOptions>> {
939        let id = self.table().push(types::RequestOptions::default())?;
940        Ok(id)
941    }
942
943    fn connect_timeout(
944        &mut self,
945        opts: Resource<types::RequestOptions>,
946    ) -> wasmtime::Result<Option<types::Duration>> {
947        let nanos = self
948            .table()
949            .get(&opts)?
950            .connect_timeout
951            .map(|d| d.as_nanos());
952
953        if let Some(nanos) = nanos {
954            Ok(Some(nanos.try_into()?))
955        } else {
956            Ok(None)
957        }
958    }
959
960    fn set_connect_timeout(
961        &mut self,
962        opts: Resource<types::RequestOptions>,
963        duration: Option<types::Duration>,
964    ) -> wasmtime::Result<Result<(), ()>> {
965        self.table().get_mut(&opts)?.connect_timeout =
966            duration.map(std::time::Duration::from_nanos);
967        Ok(Ok(()))
968    }
969
970    fn first_byte_timeout(
971        &mut self,
972        opts: Resource<types::RequestOptions>,
973    ) -> wasmtime::Result<Option<types::Duration>> {
974        let nanos = self
975            .table()
976            .get(&opts)?
977            .first_byte_timeout
978            .map(|d| d.as_nanos());
979
980        if let Some(nanos) = nanos {
981            Ok(Some(nanos.try_into()?))
982        } else {
983            Ok(None)
984        }
985    }
986
987    fn set_first_byte_timeout(
988        &mut self,
989        opts: Resource<types::RequestOptions>,
990        duration: Option<types::Duration>,
991    ) -> wasmtime::Result<Result<(), ()>> {
992        self.table().get_mut(&opts)?.first_byte_timeout =
993            duration.map(std::time::Duration::from_nanos);
994        Ok(Ok(()))
995    }
996
997    fn between_bytes_timeout(
998        &mut self,
999        opts: Resource<types::RequestOptions>,
1000    ) -> wasmtime::Result<Option<types::Duration>> {
1001        let nanos = self
1002            .table()
1003            .get(&opts)?
1004            .between_bytes_timeout
1005            .map(|d| d.as_nanos());
1006
1007        if let Some(nanos) = nanos {
1008            Ok(Some(nanos.try_into()?))
1009        } else {
1010            Ok(None)
1011        }
1012    }
1013
1014    fn set_between_bytes_timeout(
1015        &mut self,
1016        opts: Resource<types::RequestOptions>,
1017        duration: Option<types::Duration>,
1018    ) -> wasmtime::Result<Result<(), ()>> {
1019        self.table().get_mut(&opts)?.between_bytes_timeout =
1020            duration.map(std::time::Duration::from_nanos);
1021        Ok(Ok(()))
1022    }
1023
1024    fn drop(&mut self, rep: Resource<types::RequestOptions>) -> wasmtime::Result<()> {
1025        let _ = self.table().delete(rep)?;
1026        Ok(())
1027    }
1028}