wasmtime_wasi_http/
types_impl.rs

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