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