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        if let Some(s) = authority.as_ref() {
337            if let Err(_) = http::uri::Authority::from_str(s.as_str()) {
338                return Ok(Err(()));
339            }
340        }
341
342        req.authority = authority;
343
344        Ok(Ok(()))
345    }
346
347    fn headers(
348        &mut self,
349        request: wasmtime::component::Resource<types::OutgoingRequest>,
350    ) -> wasmtime::Result<wasmtime::component::Resource<FieldMap>> {
351        let req = self.table.get(&request)?;
352        let id = self.table.push(req.headers.clone())?;
353        Ok(id)
354    }
355}
356
357impl types::HostResponseOutparam for WasiHttpCtxView<'_> {
358    fn drop(&mut self, id: Resource<HostResponseOutparam>) -> wasmtime::Result<()> {
359        let _ = self.table.delete(id)?;
360        Ok(())
361    }
362    fn set(
363        &mut self,
364        id: Resource<HostResponseOutparam>,
365        resp: Result<Resource<HostOutgoingResponse>, types::ErrorCode>,
366    ) -> wasmtime::Result<()> {
367        let val = match resp {
368            Ok(resp) => Ok(self.table.delete(resp)?.try_into()?),
369            Err(e) => Err(e),
370        };
371
372        let resp = self.table.delete(id)?;
373        (resp.send)(val);
374        Ok(())
375    }
376
377    fn send_informational(
378        &mut self,
379        _id: Resource<HostResponseOutparam>,
380        _status: u16,
381        _headers: Resource<FieldMap>,
382    ) -> HttpResult<()> {
383        Err(HttpError::trap(format_err!("not implemented")))
384    }
385}
386
387impl types::HostIncomingResponse for WasiHttpCtxView<'_> {
388    fn drop(&mut self, response: Resource<HostIncomingResponse>) -> wasmtime::Result<()> {
389        let _ = self
390            .table
391            .delete(response)
392            .context("[drop_incoming_response] deleting response")?;
393        Ok(())
394    }
395
396    fn status(&mut self, response: Resource<HostIncomingResponse>) -> wasmtime::Result<StatusCode> {
397        let r = self
398            .table
399            .get(&response)
400            .context("[incoming_response_status] getting response")?;
401        Ok(r.status)
402    }
403
404    fn headers(
405        &mut self,
406        response: Resource<HostIncomingResponse>,
407    ) -> wasmtime::Result<Resource<FieldMap>> {
408        let resp = self.table.get(&response)?;
409        let id = self.table.push(resp.headers.clone())?;
410        Ok(id)
411    }
412
413    fn consume(
414        &mut self,
415        response: Resource<HostIncomingResponse>,
416    ) -> wasmtime::Result<Result<Resource<HostIncomingBody>, ()>> {
417        let r = self
418            .table
419            .get_mut(&response)
420            .context("[incoming_response_consume] getting response")?;
421
422        match r.body.take() {
423            Some(body) => {
424                let id = self.table.push(body)?;
425                Ok(Ok(id))
426            }
427
428            None => Ok(Err(())),
429        }
430    }
431}
432
433impl types::HostFutureTrailers for WasiHttpCtxView<'_> {
434    fn drop(&mut self, id: Resource<HostFutureTrailers>) -> wasmtime::Result<()> {
435        let _ = self
436            .table
437            .delete(id)
438            .context("[drop future-trailers] deleting future-trailers")?;
439        Ok(())
440    }
441
442    fn subscribe(
443        &mut self,
444        index: Resource<HostFutureTrailers>,
445    ) -> wasmtime::Result<Resource<DynPollable>> {
446        wasmtime_wasi::p2::subscribe(self.table, index)
447    }
448
449    fn get(
450        &mut self,
451        id: Resource<HostFutureTrailers>,
452    ) -> wasmtime::Result<Option<Result<Result<Option<Resource<Trailers>>, types::ErrorCode>, ()>>>
453    {
454        let trailers = self.table.get_mut(&id)?;
455        match trailers {
456            HostFutureTrailers::Waiting { .. } => return Ok(None),
457            HostFutureTrailers::Consumed => return Ok(Some(Err(()))),
458            HostFutureTrailers::Done(_) => {}
459        };
460
461        let res = match std::mem::replace(trailers, HostFutureTrailers::Consumed) {
462            HostFutureTrailers::Done(res) => res,
463            _ => unreachable!(),
464        };
465
466        let fields = match res {
467            Ok(Some(fields)) => fields,
468            Ok(None) => return Ok(Some(Ok(Ok(None)))),
469            Err(e) => {
470                let e = self.error_to_p2(e);
471                return Ok(Some(Ok(Err(e))));
472            }
473        };
474
475        let ts = self
476            .table
477            .push(FieldMap::new_immutable(self.hooks, fields))?;
478
479        Ok(Some(Ok(Ok(Some(ts)))))
480    }
481}
482
483impl types::HostIncomingBody for WasiHttpCtxView<'_> {
484    fn stream(
485        &mut self,
486        id: Resource<HostIncomingBody>,
487    ) -> wasmtime::Result<Result<Resource<DynInputStream>, ()>> {
488        let body = self.table.get_mut(&id)?;
489
490        if let Some(stream) = body.take_stream() {
491            let stream: DynInputStream = Box::new(stream);
492            let stream = self.table.push_child(stream, &id)?;
493            return Ok(Ok(stream));
494        }
495
496        Ok(Err(()))
497    }
498
499    fn finish(
500        &mut self,
501        id: Resource<HostIncomingBody>,
502    ) -> wasmtime::Result<Resource<HostFutureTrailers>> {
503        let body = self.table.delete(id)?;
504        let trailers = self.table.push(body.into_future_trailers())?;
505        Ok(trailers)
506    }
507
508    fn drop(&mut self, id: Resource<HostIncomingBody>) -> wasmtime::Result<()> {
509        let _ = self.table.delete(id)?;
510        Ok(())
511    }
512}
513
514impl types::HostOutgoingResponse for WasiHttpCtxView<'_> {
515    fn new(
516        &mut self,
517        headers: Resource<FieldMap>,
518    ) -> wasmtime::Result<Resource<HostOutgoingResponse>> {
519        let mut fields = self.table.delete(headers)?;
520        fields.set_immutable();
521
522        let id = self.table.push(HostOutgoingResponse {
523            status: http::StatusCode::OK,
524            headers: fields,
525            body: None,
526        })?;
527
528        Ok(id)
529    }
530
531    fn body(
532        &mut self,
533        id: Resource<HostOutgoingResponse>,
534    ) -> wasmtime::Result<Result<Resource<HostOutgoingBody>, ()>> {
535        let buffer_chunks = self.hooks.p2_outgoing_body_buffer_chunks();
536        let chunk_size = self.hooks.p2_outgoing_body_chunk_size();
537        let resp = self.table.get_mut(&id)?;
538
539        if resp.body.is_some() {
540            return Ok(Err(()));
541        }
542
543        let size = match get_content_length(&resp.headers) {
544            Ok(size) => size,
545            Err(..) => return Ok(Err(())),
546        };
547
548        let (host, body) =
549            HostOutgoingBody::new(StreamContext::Response, size, buffer_chunks, chunk_size);
550
551        resp.body.replace(body);
552
553        let id = self.table.push(host)?;
554
555        Ok(Ok(id))
556    }
557
558    fn status_code(
559        &mut self,
560        id: Resource<HostOutgoingResponse>,
561    ) -> wasmtime::Result<types::StatusCode> {
562        Ok(self.table.get(&id)?.status.into())
563    }
564
565    fn set_status_code(
566        &mut self,
567        id: Resource<HostOutgoingResponse>,
568        status: types::StatusCode,
569    ) -> wasmtime::Result<Result<(), ()>> {
570        let resp = self.table.get_mut(&id)?;
571
572        match http::StatusCode::from_u16(status) {
573            Ok(status) => resp.status = status,
574            Err(_) => return Ok(Err(())),
575        };
576
577        Ok(Ok(()))
578    }
579
580    fn headers(
581        &mut self,
582        id: Resource<HostOutgoingResponse>,
583    ) -> wasmtime::Result<Resource<FieldMap>> {
584        let resp = self.table.get(&id)?;
585        Ok(self.table.push(resp.headers.clone())?)
586    }
587
588    fn drop(&mut self, id: Resource<HostOutgoingResponse>) -> wasmtime::Result<()> {
589        let _ = self.table.delete(id)?;
590        Ok(())
591    }
592}
593
594impl types::HostFutureIncomingResponse for WasiHttpCtxView<'_> {
595    fn drop(&mut self, id: Resource<HostFutureIncomingResponse>) -> wasmtime::Result<()> {
596        let _ = self.table.delete(id)?;
597        Ok(())
598    }
599
600    fn get(
601        &mut self,
602        id: Resource<HostFutureIncomingResponse>,
603    ) -> wasmtime::Result<
604        Option<Result<Result<Resource<HostIncomingResponse>, types::ErrorCode>, ()>>,
605    > {
606        let resp = self.table.get_mut(&id)?;
607
608        match resp {
609            HostFutureIncomingResponse::Pending(_) => return Ok(None),
610            HostFutureIncomingResponse::Consumed => return Ok(Some(Err(()))),
611            HostFutureIncomingResponse::Ready(_) => {}
612        }
613
614        let (resp, io) =
615            match std::mem::replace(resp, HostFutureIncomingResponse::Consumed).unwrap_ready() {
616                Ok(pair) => pair,
617                Err(e) => {
618                    let e = self.error_to_p2(e);
619                    return Ok(Some(Ok(Err(e))));
620                }
621            };
622
623        let (parts, body) = resp.into_parts();
624        let headers = FieldMap::new_immutable(self.hooks, parts.headers);
625
626        let resp = self.table.push(HostIncomingResponse {
627            status: parts.status.as_u16(),
628            headers,
629            body: Some({
630                let mut body = HostIncomingBody::new(body);
631                body.retain_worker(io);
632                body
633            }),
634        })?;
635
636        Ok(Some(Ok(Ok(resp))))
637    }
638
639    fn subscribe(
640        &mut self,
641        id: Resource<HostFutureIncomingResponse>,
642    ) -> wasmtime::Result<Resource<DynPollable>> {
643        wasmtime_wasi::p2::subscribe(self.table, id)
644    }
645}
646
647impl types::HostOutgoingBody for WasiHttpCtxView<'_> {
648    fn write(
649        &mut self,
650        id: Resource<HostOutgoingBody>,
651    ) -> wasmtime::Result<Result<Resource<DynOutputStream>, ()>> {
652        let body = self.table.get_mut(&id)?;
653        if let Some(stream) = body.take_output_stream() {
654            let id = self.table.push_child(stream, &id)?;
655            Ok(Ok(id))
656        } else {
657            Ok(Err(()))
658        }
659    }
660
661    fn finish(
662        &mut self,
663        id: Resource<HostOutgoingBody>,
664        ts: Option<Resource<Trailers>>,
665    ) -> HttpResult<()> {
666        let body = self.table.delete(id)?;
667
668        let ts = if let Some(ts) = ts {
669            Some(self.table.delete(ts)?)
670        } else {
671            None
672        };
673
674        body.finish(ts)?;
675        Ok(())
676    }
677
678    fn drop(&mut self, id: Resource<HostOutgoingBody>) -> wasmtime::Result<()> {
679        self.table.delete(id)?.abort();
680        Ok(())
681    }
682}
683
684impl types::HostRequestOptions for WasiHttpCtxView<'_> {
685    fn new(&mut self) -> wasmtime::Result<Resource<types::RequestOptions>> {
686        let id = self.table.push(types::RequestOptions::default())?;
687        Ok(id)
688    }
689
690    fn connect_timeout(
691        &mut self,
692        opts: Resource<types::RequestOptions>,
693    ) -> wasmtime::Result<Option<types::Duration>> {
694        let nanos = self.table.get(&opts)?.connect_timeout.map(|d| d.as_nanos());
695
696        if let Some(nanos) = nanos {
697            Ok(Some(nanos.try_into()?))
698        } else {
699            Ok(None)
700        }
701    }
702
703    fn set_connect_timeout(
704        &mut self,
705        opts: Resource<types::RequestOptions>,
706        duration: Option<types::Duration>,
707    ) -> wasmtime::Result<Result<(), ()>> {
708        self.table.get_mut(&opts)?.connect_timeout = duration.map(std::time::Duration::from_nanos);
709        Ok(Ok(()))
710    }
711
712    fn first_byte_timeout(
713        &mut self,
714        opts: Resource<types::RequestOptions>,
715    ) -> wasmtime::Result<Option<types::Duration>> {
716        let nanos = self
717            .table
718            .get(&opts)?
719            .first_byte_timeout
720            .map(|d| d.as_nanos());
721
722        if let Some(nanos) = nanos {
723            Ok(Some(nanos.try_into()?))
724        } else {
725            Ok(None)
726        }
727    }
728
729    fn set_first_byte_timeout(
730        &mut self,
731        opts: Resource<types::RequestOptions>,
732        duration: Option<types::Duration>,
733    ) -> wasmtime::Result<Result<(), ()>> {
734        self.table.get_mut(&opts)?.first_byte_timeout =
735            duration.map(std::time::Duration::from_nanos);
736        Ok(Ok(()))
737    }
738
739    fn between_bytes_timeout(
740        &mut self,
741        opts: Resource<types::RequestOptions>,
742    ) -> wasmtime::Result<Option<types::Duration>> {
743        let nanos = self
744            .table
745            .get(&opts)?
746            .between_bytes_timeout
747            .map(|d| d.as_nanos());
748
749        if let Some(nanos) = nanos {
750            Ok(Some(nanos.try_into()?))
751        } else {
752            Ok(None)
753        }
754    }
755
756    fn set_between_bytes_timeout(
757        &mut self,
758        opts: Resource<types::RequestOptions>,
759        duration: Option<types::Duration>,
760    ) -> wasmtime::Result<Result<(), ()>> {
761        self.table.get_mut(&opts)?.between_bytes_timeout =
762            duration.map(std::time::Duration::from_nanos);
763        Ok(Ok(()))
764    }
765
766    fn drop(&mut self, rep: Resource<types::RequestOptions>) -> wasmtime::Result<()> {
767        let _ = self.table.delete(rep)?;
768        Ok(())
769    }
770}