1use 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 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 {
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}