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.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}