1use crate::FieldMap;
4use crate::get_content_length;
5use crate::p2::bindings::http::types::{self, Method, Scheme, StatusCode, Trailers};
6use crate::p2::body::{HostFutureTrailers, HostIncomingBody, HostOutgoingBody, StreamContext};
7use crate::p2::types::{
8 HostFutureIncomingResponse, HostIncomingRequest, HostIncomingResponse, HostOutgoingRequest,
9 HostOutgoingResponse, HostResponseOutparam, remove_forbidden_headers,
10};
11use crate::p2::{HeaderError, HeaderResult, HttpError, HttpResult, WasiHttpCtxView};
12use http::{HeaderName, HeaderValue};
13use std::str::FromStr;
14use wasmtime::component::Resource;
15use wasmtime::{error::Context as _, format_err};
16use wasmtime_wasi::p2::{DynInputStream, DynOutputStream, DynPollable};
17
18impl types::Host for WasiHttpCtxView<'_> {
19 fn convert_error_code(&mut self, err: HttpError) -> wasmtime::Result<types::ErrorCode> {
20 err.downcast()
21 }
22
23 fn convert_header_error(&mut self, err: HeaderError) -> wasmtime::Result<types::HeaderError> {
24 err.downcast()
25 }
26
27 fn http_error_code(
28 &mut self,
29 err: wasmtime::component::Resource<types::IoError>,
30 ) -> wasmtime::Result<Option<types::ErrorCode>> {
31 let e = self.table.get(&err)?;
32 Ok(e.downcast_ref::<types::ErrorCode>().cloned())
33 }
34}
35
36impl types::HostFields for WasiHttpCtxView<'_> {
37 fn new(&mut self) -> wasmtime::Result<Resource<FieldMap>> {
38 let limit = self.ctx.field_size_limit;
39 let id = self
40 .table
41 .push(FieldMap::new_mutable(limit))
42 .context("[new_fields] pushing fields")?;
43
44 Ok(id)
45 }
46
47 fn from_list(&mut self, entries: Vec<(String, Vec<u8>)>) -> HeaderResult<Resource<FieldMap>> {
48 let mut fields = FieldMap::new_mutable(self.ctx.field_size_limit);
49
50 for (header, value) in entries {
51 let header = HeaderName::from_bytes(header.as_bytes())?;
52 if self.hooks.is_forbidden_header(&header) {
53 return Err(types::HeaderError::Forbidden.into());
54 }
55 let value = HeaderValue::from_bytes(&value)?;
56 fields.append(header, value)?;
57 }
58
59 Ok(self.table.push(fields)?)
60 }
61
62 fn drop(&mut self, fields: Resource<FieldMap>) -> wasmtime::Result<()> {
63 self.table
64 .delete(fields)
65 .context("[drop_fields] deleting fields")?;
66 Ok(())
67 }
68
69 fn get(&mut self, fields: Resource<FieldMap>, name: String) -> wasmtime::Result<Vec<Vec<u8>>> {
70 let fields = self.table.get(&fields)?;
71
72 let header = match HeaderName::from_bytes(name.as_bytes()) {
73 Ok(header) => header,
74 Err(_) => return Ok(vec![]),
75 };
76
77 if !fields.contains_key(&header) {
78 return Ok(vec![]);
79 }
80
81 let res = fields
82 .get_all(&header)
83 .into_iter()
84 .map(|val| val.as_bytes().to_owned())
85 .collect();
86 Ok(res)
87 }
88
89 fn has(&mut self, fields: Resource<FieldMap>, name: String) -> wasmtime::Result<bool> {
90 let fields = self.table.get(&fields)?;
91
92 match HeaderName::from_bytes(name.as_bytes()) {
93 Ok(header) => Ok(fields.contains_key(&header)),
94 Err(_) => Ok(false),
95 }
96 }
97
98 fn set(
99 &mut self,
100 fields: Resource<FieldMap>,
101 name: String,
102 byte_values: Vec<Vec<u8>>,
103 ) -> HeaderResult<()> {
104 let header = HeaderName::from_bytes(name.as_bytes())?;
105
106 if self.hooks.is_forbidden_header(&header) {
107 return Err(types::HeaderError::Forbidden.into());
108 }
109
110 let mut values = Vec::with_capacity(byte_values.len());
111 for value in byte_values {
112 values.push(HeaderValue::from_bytes(&value)?);
113 }
114
115 let fields = self.table.get_mut(&fields)?;
116 fields.set(header, values)?;
117 Ok(())
118 }
119
120 fn delete(&mut self, fields: Resource<FieldMap>, name: String) -> HeaderResult<()> {
121 let header = HeaderName::from_bytes(name.as_bytes())?;
122
123 if self.hooks.is_forbidden_header(&header) {
124 return Err(types::HeaderError::Forbidden.into());
125 }
126
127 let fields = self.table.get_mut(&fields)?;
128 fields.remove_all(header)?;
129 Ok(())
130 }
131
132 fn append(
133 &mut self,
134 fields: Resource<FieldMap>,
135 name: String,
136 value: Vec<u8>,
137 ) -> HeaderResult<()> {
138 let header = HeaderName::from_bytes(name.as_bytes())?;
139
140 if self.hooks.is_forbidden_header(&header) {
141 return Err(types::HeaderError::Forbidden.into());
142 }
143
144 let value = HeaderValue::from_bytes(&value)?;
145
146 let fields = self.table.get_mut(&fields)?;
147 fields.append(header, value)?;
148 Ok(())
149 }
150
151 fn entries(&mut self, fields: Resource<FieldMap>) -> wasmtime::Result<Vec<(String, Vec<u8>)>> {
152 Ok(self
153 .table
154 .get(&fields)?
155 .iter()
156 .map(|(name, value)| (name.as_str().to_owned(), value.as_bytes().to_owned()))
157 .collect())
158 }
159
160 fn clone(&mut self, fields: Resource<FieldMap>) -> wasmtime::Result<Resource<FieldMap>> {
161 let mut fields = self.table.get(&fields)?.clone();
162 fields.set_mutable(self.ctx.field_size_limit);
163 let id = self.table.push(fields)?;
164 Ok(id)
165 }
166}
167
168impl types::HostIncomingRequest for WasiHttpCtxView<'_> {
169 fn method(&mut self, id: Resource<HostIncomingRequest>) -> wasmtime::Result<Method> {
170 let method = self.table.get(&id)?.method.clone();
171 Ok(method.into())
172 }
173 fn path_with_query(
174 &mut self,
175 id: Resource<HostIncomingRequest>,
176 ) -> wasmtime::Result<Option<String>> {
177 let req = self.table.get(&id)?;
178 Ok(req
179 .uri
180 .path_and_query()
181 .map(|path_and_query| path_and_query.as_str().to_owned()))
182 }
183 fn scheme(&mut self, id: Resource<HostIncomingRequest>) -> wasmtime::Result<Option<Scheme>> {
184 let req = self.table.get(&id)?;
185 Ok(Some(req.scheme.clone()))
186 }
187 fn authority(&mut self, id: Resource<HostIncomingRequest>) -> wasmtime::Result<Option<String>> {
188 let req = self.table.get(&id)?;
189 Ok(Some(req.authority.clone()))
190 }
191
192 fn headers(
193 &mut self,
194 id: Resource<HostIncomingRequest>,
195 ) -> wasmtime::Result<Resource<FieldMap>> {
196 let req = self.table.get(&id)?;
197 Ok(self.table.push(req.headers.clone())?)
198 }
199
200 fn consume(
201 &mut self,
202 id: Resource<HostIncomingRequest>,
203 ) -> wasmtime::Result<Result<Resource<HostIncomingBody>, ()>> {
204 let req = self.table.get_mut(&id)?;
205 match req.body.take() {
206 Some(body) => {
207 let id = self.table.push(body)?;
208 Ok(Ok(id))
209 }
210
211 None => Ok(Err(())),
212 }
213 }
214
215 fn drop(&mut self, id: Resource<HostIncomingRequest>) -> wasmtime::Result<()> {
216 let _ = self.table.delete(id)?;
217 Ok(())
218 }
219}
220
221impl types::HostOutgoingRequest for WasiHttpCtxView<'_> {
222 fn new(
223 &mut self,
224 headers: Resource<FieldMap>,
225 ) -> wasmtime::Result<Resource<HostOutgoingRequest>> {
226 let mut headers = self.table.delete(headers)?;
227 headers.set_immutable();
228
229 self.table
230 .push(HostOutgoingRequest {
231 path_with_query: None,
232 authority: None,
233 method: types::Method::Get,
234 headers,
235 scheme: None,
236 body: None,
237 })
238 .context("[new_outgoing_request] pushing request")
239 }
240
241 fn body(
242 &mut self,
243 request: Resource<HostOutgoingRequest>,
244 ) -> wasmtime::Result<Result<Resource<HostOutgoingBody>, ()>> {
245 let buffer_chunks = self.hooks.outgoing_body_buffer_chunks();
246 let chunk_size = self.hooks.outgoing_body_chunk_size();
247 let req = self
248 .table
249 .get_mut(&request)
250 .context("[outgoing_request_write] getting request")?;
251
252 if req.body.is_some() {
253 return Ok(Err(()));
254 }
255
256 let size = match get_content_length(&req.headers) {
257 Ok(size) => size,
258 Err(..) => return Ok(Err(())),
259 };
260
261 let (host_body, hyper_body) =
262 HostOutgoingBody::new(StreamContext::Request, size, buffer_chunks, chunk_size);
263
264 req.body = Some(hyper_body);
265
266 let outgoing_body = self.table.push(host_body)?;
269
270 Ok(Ok(outgoing_body))
271 }
272
273 fn drop(&mut self, request: Resource<HostOutgoingRequest>) -> wasmtime::Result<()> {
274 let _ = self.table.delete(request)?;
275 Ok(())
276 }
277
278 fn method(
279 &mut self,
280 request: wasmtime::component::Resource<types::OutgoingRequest>,
281 ) -> wasmtime::Result<Method> {
282 Ok(self.table.get(&request)?.method.clone())
283 }
284
285 fn set_method(
286 &mut self,
287 request: wasmtime::component::Resource<types::OutgoingRequest>,
288 method: Method,
289 ) -> wasmtime::Result<Result<(), ()>> {
290 let req = self.table.get_mut(&request)?;
291
292 if let Method::Other(s) = &method {
293 if let Err(_) = http::Method::from_str(s) {
294 return Ok(Err(()));
295 }
296 }
297
298 req.method = method;
299
300 Ok(Ok(()))
301 }
302
303 fn path_with_query(
304 &mut self,
305 request: wasmtime::component::Resource<types::OutgoingRequest>,
306 ) -> wasmtime::Result<Option<String>> {
307 Ok(self.table.get(&request)?.path_with_query.clone())
308 }
309
310 fn set_path_with_query(
311 &mut self,
312 request: wasmtime::component::Resource<types::OutgoingRequest>,
313 path_with_query: Option<String>,
314 ) -> wasmtime::Result<Result<(), ()>> {
315 let req = self.table.get_mut(&request)?;
316
317 if let Some(s) = path_with_query.as_ref() {
318 if let Err(_) = http::uri::PathAndQuery::from_str(s) {
319 return Ok(Err(()));
320 }
321 }
322
323 req.path_with_query = path_with_query;
324
325 Ok(Ok(()))
326 }
327
328 fn scheme(
329 &mut self,
330 request: wasmtime::component::Resource<types::OutgoingRequest>,
331 ) -> wasmtime::Result<Option<Scheme>> {
332 Ok(self.table.get(&request)?.scheme.clone())
333 }
334
335 fn set_scheme(
336 &mut self,
337 request: wasmtime::component::Resource<types::OutgoingRequest>,
338 scheme: Option<Scheme>,
339 ) -> wasmtime::Result<Result<(), ()>> {
340 let req = self.table.get_mut(&request)?;
341
342 if let Some(types::Scheme::Other(s)) = scheme.as_ref() {
343 if let Err(_) = http::uri::Scheme::from_str(s.as_str()) {
344 return Ok(Err(()));
345 }
346 }
347
348 req.scheme = scheme;
349
350 Ok(Ok(()))
351 }
352
353 fn authority(
354 &mut self,
355 request: wasmtime::component::Resource<types::OutgoingRequest>,
356 ) -> wasmtime::Result<Option<String>> {
357 Ok(self.table.get(&request)?.authority.clone())
358 }
359
360 fn set_authority(
361 &mut self,
362 request: wasmtime::component::Resource<types::OutgoingRequest>,
363 authority: Option<String>,
364 ) -> wasmtime::Result<Result<(), ()>> {
365 let req = self.table.get_mut(&request)?;
366
367 if let Some(s) = authority.as_ref() {
368 if let Err(_) = http::uri::Authority::from_str(s.as_str()) {
369 return Ok(Err(()));
370 }
371 }
372
373 req.authority = authority;
374
375 Ok(Ok(()))
376 }
377
378 fn headers(
379 &mut self,
380 request: wasmtime::component::Resource<types::OutgoingRequest>,
381 ) -> wasmtime::Result<wasmtime::component::Resource<FieldMap>> {
382 let req = self.table.get(&request)?;
383 let id = self.table.push(req.headers.clone())?;
384 Ok(id)
385 }
386}
387
388impl types::HostResponseOutparam for WasiHttpCtxView<'_> {
389 fn drop(&mut self, id: Resource<HostResponseOutparam>) -> wasmtime::Result<()> {
390 let _ = self.table.delete(id)?;
391 Ok(())
392 }
393 fn set(
394 &mut self,
395 id: Resource<HostResponseOutparam>,
396 resp: Result<Resource<HostOutgoingResponse>, types::ErrorCode>,
397 ) -> wasmtime::Result<()> {
398 let val = match resp {
399 Ok(resp) => Ok(self.table.delete(resp)?.try_into()?),
400 Err(e) => Err(e),
401 };
402
403 let resp = self.table.delete(id)?;
404 let _ = resp.result.send(val);
409 Ok(())
410 }
411
412 fn send_informational(
413 &mut self,
414 _id: Resource<HostResponseOutparam>,
415 _status: u16,
416 _headers: Resource<FieldMap>,
417 ) -> HttpResult<()> {
418 Err(HttpError::trap(format_err!("not implemented")))
419 }
420}
421
422impl types::HostIncomingResponse for WasiHttpCtxView<'_> {
423 fn drop(&mut self, response: Resource<HostIncomingResponse>) -> wasmtime::Result<()> {
424 let _ = self
425 .table
426 .delete(response)
427 .context("[drop_incoming_response] deleting response")?;
428 Ok(())
429 }
430
431 fn status(&mut self, response: Resource<HostIncomingResponse>) -> wasmtime::Result<StatusCode> {
432 let r = self
433 .table
434 .get(&response)
435 .context("[incoming_response_status] getting response")?;
436 Ok(r.status)
437 }
438
439 fn headers(
440 &mut self,
441 response: Resource<HostIncomingResponse>,
442 ) -> wasmtime::Result<Resource<FieldMap>> {
443 let resp = self.table.get(&response)?;
444 let id = self.table.push(resp.headers.clone())?;
445 Ok(id)
446 }
447
448 fn consume(
449 &mut self,
450 response: Resource<HostIncomingResponse>,
451 ) -> wasmtime::Result<Result<Resource<HostIncomingBody>, ()>> {
452 let r = self
453 .table
454 .get_mut(&response)
455 .context("[incoming_response_consume] getting response")?;
456
457 match r.body.take() {
458 Some(body) => {
459 let id = self.table.push(body)?;
460 Ok(Ok(id))
461 }
462
463 None => Ok(Err(())),
464 }
465 }
466}
467
468impl types::HostFutureTrailers for WasiHttpCtxView<'_> {
469 fn drop(&mut self, id: Resource<HostFutureTrailers>) -> wasmtime::Result<()> {
470 let _ = self
471 .table
472 .delete(id)
473 .context("[drop future-trailers] deleting future-trailers")?;
474 Ok(())
475 }
476
477 fn subscribe(
478 &mut self,
479 index: Resource<HostFutureTrailers>,
480 ) -> wasmtime::Result<Resource<DynPollable>> {
481 wasmtime_wasi::p2::subscribe(self.table, index)
482 }
483
484 fn get(
485 &mut self,
486 id: Resource<HostFutureTrailers>,
487 ) -> wasmtime::Result<Option<Result<Result<Option<Resource<Trailers>>, types::ErrorCode>, ()>>>
488 {
489 let trailers = self.table.get_mut(&id)?;
490 match trailers {
491 HostFutureTrailers::Waiting { .. } => return Ok(None),
492 HostFutureTrailers::Consumed => return Ok(Some(Err(()))),
493 HostFutureTrailers::Done(_) => {}
494 };
495
496 let res = match std::mem::replace(trailers, HostFutureTrailers::Consumed) {
497 HostFutureTrailers::Done(res) => res,
498 _ => unreachable!(),
499 };
500
501 let mut fields = match res {
502 Ok(Some(fields)) => fields,
503 Ok(None) => return Ok(Some(Ok(Ok(None)))),
504 Err(e) => return Ok(Some(Ok(Err(e)))),
505 };
506
507 remove_forbidden_headers(self.hooks, &mut fields);
508
509 let ts = self.table.push(FieldMap::new_immutable(fields))?;
510
511 Ok(Some(Ok(Ok(Some(ts)))))
512 }
513}
514
515impl types::HostIncomingBody for WasiHttpCtxView<'_> {
516 fn stream(
517 &mut self,
518 id: Resource<HostIncomingBody>,
519 ) -> wasmtime::Result<Result<Resource<DynInputStream>, ()>> {
520 let body = self.table.get_mut(&id)?;
521
522 if let Some(stream) = body.take_stream() {
523 let stream: DynInputStream = Box::new(stream);
524 let stream = self.table.push_child(stream, &id)?;
525 return Ok(Ok(stream));
526 }
527
528 Ok(Err(()))
529 }
530
531 fn finish(
532 &mut self,
533 id: Resource<HostIncomingBody>,
534 ) -> wasmtime::Result<Resource<HostFutureTrailers>> {
535 let body = self.table.delete(id)?;
536 let trailers = self.table.push(body.into_future_trailers())?;
537 Ok(trailers)
538 }
539
540 fn drop(&mut self, id: Resource<HostIncomingBody>) -> wasmtime::Result<()> {
541 let _ = self.table.delete(id)?;
542 Ok(())
543 }
544}
545
546impl types::HostOutgoingResponse for WasiHttpCtxView<'_> {
547 fn new(
548 &mut self,
549 headers: Resource<FieldMap>,
550 ) -> wasmtime::Result<Resource<HostOutgoingResponse>> {
551 let mut fields = self.table.delete(headers)?;
552 fields.set_immutable();
553
554 let id = self.table.push(HostOutgoingResponse {
555 status: http::StatusCode::OK,
556 headers: fields,
557 body: None,
558 })?;
559
560 Ok(id)
561 }
562
563 fn body(
564 &mut self,
565 id: Resource<HostOutgoingResponse>,
566 ) -> wasmtime::Result<Result<Resource<HostOutgoingBody>, ()>> {
567 let buffer_chunks = self.hooks.outgoing_body_buffer_chunks();
568 let chunk_size = self.hooks.outgoing_body_chunk_size();
569 let resp = self.table.get_mut(&id)?;
570
571 if resp.body.is_some() {
572 return Ok(Err(()));
573 }
574
575 let size = match get_content_length(&resp.headers) {
576 Ok(size) => size,
577 Err(..) => return Ok(Err(())),
578 };
579
580 let (host, body) =
581 HostOutgoingBody::new(StreamContext::Response, size, buffer_chunks, chunk_size);
582
583 resp.body.replace(body);
584
585 let id = self.table.push(host)?;
586
587 Ok(Ok(id))
588 }
589
590 fn status_code(
591 &mut self,
592 id: Resource<HostOutgoingResponse>,
593 ) -> wasmtime::Result<types::StatusCode> {
594 Ok(self.table.get(&id)?.status.into())
595 }
596
597 fn set_status_code(
598 &mut self,
599 id: Resource<HostOutgoingResponse>,
600 status: types::StatusCode,
601 ) -> wasmtime::Result<Result<(), ()>> {
602 let resp = self.table.get_mut(&id)?;
603
604 match http::StatusCode::from_u16(status) {
605 Ok(status) => resp.status = status,
606 Err(_) => return Ok(Err(())),
607 };
608
609 Ok(Ok(()))
610 }
611
612 fn headers(
613 &mut self,
614 id: Resource<HostOutgoingResponse>,
615 ) -> wasmtime::Result<Resource<FieldMap>> {
616 let resp = self.table.get(&id)?;
617 Ok(self.table.push(resp.headers.clone())?)
618 }
619
620 fn drop(&mut self, id: Resource<HostOutgoingResponse>) -> wasmtime::Result<()> {
621 let _ = self.table.delete(id)?;
622 Ok(())
623 }
624}
625
626impl types::HostFutureIncomingResponse for WasiHttpCtxView<'_> {
627 fn drop(&mut self, id: Resource<HostFutureIncomingResponse>) -> wasmtime::Result<()> {
628 let _ = self.table.delete(id)?;
629 Ok(())
630 }
631
632 fn get(
633 &mut self,
634 id: Resource<HostFutureIncomingResponse>,
635 ) -> wasmtime::Result<
636 Option<Result<Result<Resource<HostIncomingResponse>, types::ErrorCode>, ()>>,
637 > {
638 let resp = self.table.get_mut(&id)?;
639
640 match resp {
641 HostFutureIncomingResponse::Pending(_) => return Ok(None),
642 HostFutureIncomingResponse::Consumed => return Ok(Some(Err(()))),
643 HostFutureIncomingResponse::Ready(_) => {}
644 }
645
646 let resp =
647 match std::mem::replace(resp, HostFutureIncomingResponse::Consumed).unwrap_ready() {
648 Err(e) => {
649 let e = e.downcast::<types::ErrorCode>()?;
651 return Ok(Some(Ok(Err(e))));
652 }
653
654 Ok(Ok(resp)) => resp,
655 Ok(Err(e)) => return Ok(Some(Ok(Err(e)))),
656 };
657
658 let (mut parts, body) = resp.resp.into_parts();
659 remove_forbidden_headers(self.hooks, &mut parts.headers);
660 let headers = FieldMap::new_immutable(parts.headers);
661
662 let resp = self.table.push(HostIncomingResponse {
663 status: parts.status.as_u16(),
664 headers,
665 body: Some({
666 let mut body = HostIncomingBody::new(body, resp.between_bytes_timeout);
667 if let Some(worker) = resp.worker {
668 body.retain_worker(worker);
669 }
670 body
671 }),
672 })?;
673
674 Ok(Some(Ok(Ok(resp))))
675 }
676
677 fn subscribe(
678 &mut self,
679 id: Resource<HostFutureIncomingResponse>,
680 ) -> wasmtime::Result<Resource<DynPollable>> {
681 wasmtime_wasi::p2::subscribe(self.table, id)
682 }
683}
684
685impl types::HostOutgoingBody for WasiHttpCtxView<'_> {
686 fn write(
687 &mut self,
688 id: Resource<HostOutgoingBody>,
689 ) -> wasmtime::Result<Result<Resource<DynOutputStream>, ()>> {
690 let body = self.table.get_mut(&id)?;
691 if let Some(stream) = body.take_output_stream() {
692 let id = self.table.push_child(stream, &id)?;
693 Ok(Ok(id))
694 } else {
695 Ok(Err(()))
696 }
697 }
698
699 fn finish(
700 &mut self,
701 id: Resource<HostOutgoingBody>,
702 ts: Option<Resource<Trailers>>,
703 ) -> HttpResult<()> {
704 let body = self.table.delete(id)?;
705
706 let ts = if let Some(ts) = ts {
707 Some(self.table.delete(ts)?)
708 } else {
709 None
710 };
711
712 body.finish(ts)?;
713 Ok(())
714 }
715
716 fn drop(&mut self, id: Resource<HostOutgoingBody>) -> wasmtime::Result<()> {
717 self.table.delete(id)?.abort();
718 Ok(())
719 }
720}
721
722impl types::HostRequestOptions for WasiHttpCtxView<'_> {
723 fn new(&mut self) -> wasmtime::Result<Resource<types::RequestOptions>> {
724 let id = self.table.push(types::RequestOptions::default())?;
725 Ok(id)
726 }
727
728 fn connect_timeout(
729 &mut self,
730 opts: Resource<types::RequestOptions>,
731 ) -> wasmtime::Result<Option<types::Duration>> {
732 let nanos = self.table.get(&opts)?.connect_timeout.map(|d| d.as_nanos());
733
734 if let Some(nanos) = nanos {
735 Ok(Some(nanos.try_into()?))
736 } else {
737 Ok(None)
738 }
739 }
740
741 fn set_connect_timeout(
742 &mut self,
743 opts: Resource<types::RequestOptions>,
744 duration: Option<types::Duration>,
745 ) -> wasmtime::Result<Result<(), ()>> {
746 self.table.get_mut(&opts)?.connect_timeout = duration.map(std::time::Duration::from_nanos);
747 Ok(Ok(()))
748 }
749
750 fn first_byte_timeout(
751 &mut self,
752 opts: Resource<types::RequestOptions>,
753 ) -> wasmtime::Result<Option<types::Duration>> {
754 let nanos = self
755 .table
756 .get(&opts)?
757 .first_byte_timeout
758 .map(|d| d.as_nanos());
759
760 if let Some(nanos) = nanos {
761 Ok(Some(nanos.try_into()?))
762 } else {
763 Ok(None)
764 }
765 }
766
767 fn set_first_byte_timeout(
768 &mut self,
769 opts: Resource<types::RequestOptions>,
770 duration: Option<types::Duration>,
771 ) -> wasmtime::Result<Result<(), ()>> {
772 self.table.get_mut(&opts)?.first_byte_timeout =
773 duration.map(std::time::Duration::from_nanos);
774 Ok(Ok(()))
775 }
776
777 fn between_bytes_timeout(
778 &mut self,
779 opts: Resource<types::RequestOptions>,
780 ) -> wasmtime::Result<Option<types::Duration>> {
781 let nanos = self
782 .table
783 .get(&opts)?
784 .between_bytes_timeout
785 .map(|d| d.as_nanos());
786
787 if let Some(nanos) = nanos {
788 Ok(Some(nanos.try_into()?))
789 } else {
790 Ok(None)
791 }
792 }
793
794 fn set_between_bytes_timeout(
795 &mut self,
796 opts: Resource<types::RequestOptions>,
797 duration: Option<types::Duration>,
798 ) -> wasmtime::Result<Result<(), ()>> {
799 self.table.get_mut(&opts)?.between_bytes_timeout =
800 duration.map(std::time::Duration::from_nanos);
801 Ok(Ok(()))
802 }
803
804 fn drop(&mut self, rep: Resource<types::RequestOptions>) -> wasmtime::Result<()> {
805 let _ = self.table.delete(rep)?;
806 Ok(())
807 }
808}