1use crate::p3::bindings::clocks::monotonic_clock::Duration;
2use crate::p3::bindings::http::types::{
3 ErrorCode, FieldName, FieldValue, Fields, HeaderError, Headers, Host, HostFields, HostRequest,
4 HostRequestOptions, HostRequestWithStore, HostResponse, HostResponseWithStore, Method, Request,
5 RequestOptions, RequestOptionsError, Response, Scheme, StatusCode, Trailers,
6};
7use crate::p3::body::{Body, HostBodyStreamProducer};
8use crate::p3::{HeaderResult, HttpError, RequestOptionsResult, WasiHttp, WasiHttpCtxView};
9use anyhow::Context as _;
10use core::mem;
11use core::pin::Pin;
12use core::task::{Context, Poll, ready};
13use http::header::CONTENT_LENGTH;
14use std::sync::Arc;
15use tokio::sync::oneshot;
16use wasmtime::component::{
17 Access, Accessor, FutureProducer, FutureReader, Resource, ResourceTable, StreamReader,
18};
19use wasmtime::{AsContextMut, StoreContextMut};
20
21fn get_fields<'a>(
22 table: &'a ResourceTable,
23 fields: &Resource<Fields>,
24) -> wasmtime::Result<&'a Fields> {
25 table
26 .get(&fields)
27 .context("failed to get fields from table")
28}
29
30fn get_fields_mut<'a>(
31 table: &'a mut ResourceTable,
32 fields: &Resource<Fields>,
33) -> HeaderResult<&'a mut Fields> {
34 table
35 .get_mut(&fields)
36 .context("failed to get fields from table")
37 .map_err(crate::p3::HeaderError::trap)
38}
39
40fn push_fields(table: &mut ResourceTable, fields: Fields) -> wasmtime::Result<Resource<Fields>> {
41 table.push(fields).context("failed to push fields to table")
42}
43
44fn delete_fields(table: &mut ResourceTable, fields: Resource<Fields>) -> wasmtime::Result<Fields> {
45 table
46 .delete(fields)
47 .context("failed to delete fields from table")
48}
49
50fn get_request<'a>(
51 table: &'a ResourceTable,
52 req: &Resource<Request>,
53) -> wasmtime::Result<&'a Request> {
54 table.get(req).context("failed to get request from table")
55}
56
57fn get_request_mut<'a>(
58 table: &'a mut ResourceTable,
59 req: &Resource<Request>,
60) -> wasmtime::Result<&'a mut Request> {
61 table
62 .get_mut(req)
63 .context("failed to get request from table")
64}
65
66fn get_response<'a>(
67 table: &'a ResourceTable,
68 res: &Resource<Response>,
69) -> wasmtime::Result<&'a Response> {
70 table.get(res).context("failed to get response from table")
71}
72
73fn get_response_mut<'a>(
74 table: &'a mut ResourceTable,
75 res: &Resource<Response>,
76) -> wasmtime::Result<&'a mut Response> {
77 table
78 .get_mut(res)
79 .context("failed to get response from table")
80}
81
82fn get_request_options<'a>(
83 table: &'a ResourceTable,
84 opts: &Resource<RequestOptions>,
85) -> wasmtime::Result<&'a RequestOptions> {
86 table
87 .get(opts)
88 .context("failed to get request options from table")
89}
90
91fn get_request_options_mut<'a>(
92 table: &'a mut ResourceTable,
93 opts: &Resource<RequestOptions>,
94) -> RequestOptionsResult<&'a mut RequestOptions> {
95 table
96 .get_mut(opts)
97 .context("failed to get request options from table")
98 .map_err(crate::p3::RequestOptionsError::trap)
99}
100
101fn push_request_options(
102 table: &mut ResourceTable,
103 opts: RequestOptions,
104) -> wasmtime::Result<Resource<RequestOptions>> {
105 table
106 .push(opts)
107 .context("failed to push request options to table")
108}
109
110fn delete_request_options(
111 table: &mut ResourceTable,
112 opts: Resource<RequestOptions>,
113) -> wasmtime::Result<RequestOptions> {
114 table
115 .delete(opts)
116 .context("failed to delete request options from table")
117}
118
119fn parse_header_value(
120 name: &http::HeaderName,
121 value: impl AsRef<[u8]>,
122) -> Result<http::HeaderValue, HeaderError> {
123 if name == CONTENT_LENGTH {
124 let s = str::from_utf8(value.as_ref()).or(Err(HeaderError::InvalidSyntax))?;
125 let v: u64 = s.parse().or(Err(HeaderError::InvalidSyntax))?;
126 Ok(v.into())
127 } else {
128 http::HeaderValue::from_bytes(value.as_ref()).or(Err(HeaderError::InvalidSyntax))
129 }
130}
131
132enum GuestBodyResultProducer {
133 Receiver(oneshot::Receiver<Box<dyn Future<Output = Result<(), ErrorCode>> + Send>>),
134 Future(Pin<Box<dyn Future<Output = Result<(), ErrorCode>> + Send>>),
135}
136
137fn poll_future<T>(
138 cx: &mut Context<'_>,
139 fut: Pin<&mut (impl Future<Output = T> + ?Sized)>,
140 finish: bool,
141) -> Poll<Option<T>> {
142 match fut.poll(cx) {
143 Poll::Ready(v) => Poll::Ready(Some(v)),
144 Poll::Pending if finish => Poll::Ready(None),
145 Poll::Pending => Poll::Pending,
146 }
147}
148
149impl<D> FutureProducer<D> for GuestBodyResultProducer {
150 type Item = Result<(), ErrorCode>;
151
152 fn poll_produce(
153 mut self: Pin<&mut Self>,
154 cx: &mut Context<'_>,
155 _: StoreContextMut<D>,
156 finish: bool,
157 ) -> Poll<wasmtime::Result<Option<Self::Item>>> {
158 match &mut *self {
159 Self::Receiver(rx) => {
160 match ready!(poll_future(cx, Pin::new(rx), finish)) {
161 Some(Ok(fut)) => {
162 let mut fut = Box::into_pin(fut);
163 let res = poll_future(cx, fut.as_mut(), finish);
165 *self = Self::Future(fut);
166 res.map(Ok)
167 }
168 Some(Err(..)) => {
169 Poll::Ready(Ok(Some(Ok(()))))
171 }
172 None => Poll::Ready(Ok(None)),
173 }
174 }
175 Self::Future(fut) => poll_future(cx, fut.as_mut(), finish).map(Ok),
176 }
177 }
178}
179
180impl HostFields for WasiHttpCtxView<'_> {
181 fn new(&mut self) -> wasmtime::Result<Resource<Fields>> {
182 push_fields(self.table, Fields::new_mutable_default())
183 }
184
185 fn from_list(
186 &mut self,
187 entries: Vec<(FieldName, FieldValue)>,
188 ) -> HeaderResult<Resource<Fields>> {
189 let mut fields = http::HeaderMap::default();
190 for (name, value) in entries {
191 let name = name.parse().or(Err(HeaderError::InvalidSyntax))?;
192 if self.ctx.is_forbidden_header(&name) {
193 return Err(HeaderError::Forbidden.into());
194 }
195 let value = parse_header_value(&name, value)?;
196 fields.append(name, value);
197 }
198 let fields = push_fields(self.table, Fields::new_mutable(fields))
199 .map_err(crate::p3::HeaderError::trap)?;
200 Ok(fields)
201 }
202
203 fn get(
204 &mut self,
205 fields: Resource<Fields>,
206 name: FieldName,
207 ) -> wasmtime::Result<Vec<FieldValue>> {
208 let fields = get_fields(self.table, &fields)?;
209 Ok(fields
210 .get_all(name)
211 .into_iter()
212 .map(|val| val.as_bytes().into())
213 .collect())
214 }
215
216 fn has(&mut self, fields: Resource<Fields>, name: FieldName) -> wasmtime::Result<bool> {
217 let fields = get_fields(self.table, &fields)?;
218 Ok(fields.contains_key(name))
219 }
220
221 fn set(
222 &mut self,
223 fields: Resource<Fields>,
224 name: FieldName,
225 value: Vec<FieldValue>,
226 ) -> HeaderResult<()> {
227 let name = name.parse().or(Err(HeaderError::InvalidSyntax))?;
228 if self.ctx.is_forbidden_header(&name) {
229 return Err(HeaderError::Forbidden.into());
230 }
231 let mut values = Vec::with_capacity(value.len());
232 for value in value {
233 let value = parse_header_value(&name, value)?;
234 values.push(value);
235 }
236 let fields = get_fields_mut(self.table, &fields)?;
237 let fields = fields.get_mut().ok_or(HeaderError::Immutable)?;
238 fields.remove(&name);
239 for value in values {
240 fields.append(&name, value);
241 }
242 Ok(())
243 }
244
245 fn delete(&mut self, fields: Resource<Fields>, name: FieldName) -> HeaderResult<()> {
246 let name = name.parse().or(Err(HeaderError::InvalidSyntax))?;
247 if self.ctx.is_forbidden_header(&name) {
248 return Err(HeaderError::Forbidden.into());
249 }
250 let fields = get_fields_mut(self.table, &fields)?;
251 let fields = fields.get_mut().ok_or(HeaderError::Immutable)?;
252 fields.remove(&name);
253 Ok(())
254 }
255
256 fn get_and_delete(
257 &mut self,
258 fields: Resource<Fields>,
259 name: FieldName,
260 ) -> HeaderResult<Vec<FieldValue>> {
261 let name = name.parse().or(Err(HeaderError::InvalidSyntax))?;
262 if self.ctx.is_forbidden_header(&name) {
263 return Err(HeaderError::Forbidden.into());
264 }
265 let fields = get_fields_mut(self.table, &fields)?;
266 let fields = fields.get_mut().ok_or(HeaderError::Immutable)?;
267 let http::header::Entry::Occupied(entry) = fields.entry(name) else {
268 return Ok(Vec::default());
269 };
270 let (.., values) = entry.remove_entry_mult();
271 Ok(values.map(|value| value.as_bytes().into()).collect())
272 }
273
274 fn append(
275 &mut self,
276 fields: Resource<Fields>,
277 name: FieldName,
278 value: FieldValue,
279 ) -> HeaderResult<()> {
280 let name = name.parse().or(Err(HeaderError::InvalidSyntax))?;
281 if self.ctx.is_forbidden_header(&name) {
282 return Err(HeaderError::Forbidden.into());
283 }
284 let value = parse_header_value(&name, value)?;
285 let fields = get_fields_mut(self.table, &fields)?;
286 let fields = fields.get_mut().ok_or(HeaderError::Immutable)?;
287 fields.append(name, value);
288 Ok(())
289 }
290
291 fn copy_all(
292 &mut self,
293 fields: Resource<Fields>,
294 ) -> wasmtime::Result<Vec<(FieldName, FieldValue)>> {
295 let fields = get_fields(self.table, &fields)?;
296 let fields = fields
297 .iter()
298 .map(|(name, value)| (name.as_str().into(), value.as_bytes().into()))
299 .collect();
300 Ok(fields)
301 }
302
303 fn clone(&mut self, fields: Resource<Fields>) -> wasmtime::Result<Resource<Fields>> {
304 let fields = get_fields(self.table, &fields)?;
305 push_fields(self.table, Fields::new_mutable(Arc::clone(fields)))
306 }
307
308 fn drop(&mut self, fields: Resource<Fields>) -> wasmtime::Result<()> {
309 delete_fields(self.table, fields)?;
310 Ok(())
311 }
312}
313
314impl HostRequestWithStore for WasiHttp {
315 async fn new<T>(
316 store: &Accessor<T, Self>,
317 headers: Resource<Headers>,
318 contents: Option<StreamReader<u8>>,
319 trailers: FutureReader<Result<Option<Resource<Trailers>>, ErrorCode>>,
320 options: Option<Resource<RequestOptions>>,
321 ) -> wasmtime::Result<(Resource<Request>, FutureReader<Result<(), ErrorCode>>)> {
322 store.with(|mut store| {
323 let (result_tx, result_rx) = oneshot::channel();
324 let body = match contents
325 .map(|rx| rx.try_into::<HostBodyStreamProducer<T>>(store.as_context_mut()))
326 {
327 Some(Ok(mut producer)) => Body::Host {
328 body: mem::take(&mut producer.body),
329 result_tx,
330 },
331 Some(Err(rx)) => Body::Guest {
332 contents_rx: Some(rx),
333 trailers_rx: trailers,
334 result_tx,
335 },
336 None => Body::Guest {
337 contents_rx: None,
338 trailers_rx: trailers,
339 result_tx,
340 },
341 };
342 let WasiHttpCtxView { table, .. } = store.get();
343 let headers = delete_fields(table, headers)?;
344 let options = options
345 .map(|options| delete_request_options(table, options))
346 .transpose()?;
347 let req = Request {
348 method: http::Method::GET,
349 scheme: None,
350 authority: None,
351 path_with_query: None,
352 headers: headers.into(),
353 options: options.map(Into::into),
354 body,
355 };
356 let req = table.push(req).context("failed to push request to table")?;
357 Ok((
358 req,
359 FutureReader::new(&mut store, GuestBodyResultProducer::Receiver(result_rx)),
360 ))
361 })
362 }
363
364 async fn consume_body<T>(
365 store: &Accessor<T, Self>,
366 req: Resource<Request>,
367 fut: FutureReader<Result<(), ErrorCode>>,
368 ) -> wasmtime::Result<(
369 StreamReader<u8>,
370 FutureReader<Result<Option<Resource<Trailers>>, ErrorCode>>,
371 )> {
372 let getter = store.getter();
373 store.with(|mut store| {
374 let Request { body, .. } = store
375 .get()
376 .table
377 .delete(req)
378 .context("failed to delete request from table")?;
379 Ok(body.consume(store, fut, getter))
380 })
381 }
382
383 fn drop<T>(mut store: Access<'_, T, Self>, req: Resource<Request>) -> wasmtime::Result<()> {
384 let Request { body, .. } = store
385 .get()
386 .table
387 .delete(req)
388 .context("failed to delete request from table")?;
389 body.drop(store);
390 Ok(())
391 }
392}
393
394impl HostRequest for WasiHttpCtxView<'_> {
395 fn get_method(&mut self, req: Resource<Request>) -> wasmtime::Result<Method> {
396 let Request { method, .. } = get_request(self.table, &req)?;
397 Ok(method.into())
398 }
399
400 fn set_method(
401 &mut self,
402 req: Resource<Request>,
403 method: Method,
404 ) -> wasmtime::Result<Result<(), ()>> {
405 let req = get_request_mut(self.table, &req)?;
406 let Ok(method) = method.try_into() else {
407 return Ok(Err(()));
408 };
409 req.method = method;
410 Ok(Ok(()))
411 }
412
413 fn get_path_with_query(&mut self, req: Resource<Request>) -> wasmtime::Result<Option<String>> {
414 let Request {
415 path_with_query, ..
416 } = get_request(self.table, &req)?;
417 Ok(path_with_query.as_ref().map(|pq| pq.as_str().into()))
418 }
419
420 fn set_path_with_query(
421 &mut self,
422 req: Resource<Request>,
423 path_with_query: Option<String>,
424 ) -> wasmtime::Result<Result<(), ()>> {
425 let req = get_request_mut(self.table, &req)?;
426 let Some(path_with_query) = path_with_query else {
427 req.path_with_query = None;
428 return Ok(Ok(()));
429 };
430 let Ok(path_with_query) = path_with_query.try_into() else {
431 return Ok(Err(()));
432 };
433 req.path_with_query = Some(path_with_query);
434 Ok(Ok(()))
435 }
436
437 fn get_scheme(&mut self, req: Resource<Request>) -> wasmtime::Result<Option<Scheme>> {
438 let Request { scheme, .. } = get_request(self.table, &req)?;
439 Ok(scheme.as_ref().map(Into::into))
440 }
441
442 fn set_scheme(
443 &mut self,
444 req: Resource<Request>,
445 scheme: Option<Scheme>,
446 ) -> wasmtime::Result<Result<(), ()>> {
447 let req = get_request_mut(self.table, &req)?;
448 let Some(scheme) = scheme else {
449 req.scheme = None;
450 return Ok(Ok(()));
451 };
452 let Ok(scheme) = scheme.try_into() else {
453 return Ok(Err(()));
454 };
455 req.scheme = Some(scheme);
456 Ok(Ok(()))
457 }
458
459 fn get_authority(&mut self, req: Resource<Request>) -> wasmtime::Result<Option<String>> {
460 let Request { authority, .. } = get_request(self.table, &req)?;
461 Ok(authority.as_ref().map(|auth| auth.as_str().into()))
462 }
463
464 fn set_authority(
465 &mut self,
466 req: Resource<Request>,
467 authority: Option<String>,
468 ) -> wasmtime::Result<Result<(), ()>> {
469 let req = get_request_mut(self.table, &req)?;
470 let Some(authority) = authority else {
471 req.authority = None;
472 return Ok(Ok(()));
473 };
474 let has_port = authority.contains(':');
475 let Ok(authority) = http::uri::Authority::try_from(authority) else {
476 return Ok(Err(()));
477 };
478 if has_port && authority.port_u16().is_none() {
479 return Ok(Err(()));
480 }
481 req.authority = Some(authority);
482 Ok(Ok(()))
483 }
484
485 fn get_options(
486 &mut self,
487 req: Resource<Request>,
488 ) -> wasmtime::Result<Option<Resource<RequestOptions>>> {
489 let Request { options, .. } = get_request(self.table, &req)?;
490 if let Some(options) = options {
491 let options = push_request_options(
492 self.table,
493 RequestOptions::new_immutable(Arc::clone(options)),
494 )?;
495 Ok(Some(options))
496 } else {
497 Ok(None)
498 }
499 }
500
501 fn get_headers(&mut self, req: Resource<Request>) -> wasmtime::Result<Resource<Headers>> {
502 let Request { headers, .. } = get_request(self.table, &req)?;
503 push_fields(self.table, Fields::new_immutable(Arc::clone(headers)))
504 }
505}
506
507impl HostRequestOptions for WasiHttpCtxView<'_> {
508 fn new(&mut self) -> wasmtime::Result<Resource<RequestOptions>> {
509 push_request_options(self.table, RequestOptions::new_mutable_default())
510 }
511
512 fn get_connect_timeout(
513 &mut self,
514 opts: Resource<RequestOptions>,
515 ) -> wasmtime::Result<Option<Duration>> {
516 let opts = get_request_options(self.table, &opts)?;
517 let Some(connect_timeout) = opts.connect_timeout else {
518 return Ok(None);
519 };
520 let ns = connect_timeout.as_nanos();
521 let ns = ns
522 .try_into()
523 .context("connect timeout duration nanoseconds do not fit in u64")?;
524 Ok(Some(ns))
525 }
526
527 fn set_connect_timeout(
528 &mut self,
529 opts: Resource<RequestOptions>,
530 duration: Option<Duration>,
531 ) -> RequestOptionsResult<()> {
532 let opts = get_request_options_mut(self.table, &opts)?;
533 let opts = opts.get_mut().ok_or(RequestOptionsError::Immutable)?;
534 opts.connect_timeout = duration.map(core::time::Duration::from_nanos);
535 Ok(())
536 }
537
538 fn get_first_byte_timeout(
539 &mut self,
540 opts: Resource<RequestOptions>,
541 ) -> wasmtime::Result<Option<Duration>> {
542 let opts = get_request_options(self.table, &opts)?;
543 let Some(first_byte_timeout) = opts.first_byte_timeout else {
544 return Ok(None);
545 };
546 let ns = first_byte_timeout.as_nanos();
547 let ns = ns
548 .try_into()
549 .context("first byte timeout duration nanoseconds do not fit in u64")?;
550 Ok(Some(ns))
551 }
552
553 fn set_first_byte_timeout(
554 &mut self,
555 opts: Resource<RequestOptions>,
556 duration: Option<Duration>,
557 ) -> RequestOptionsResult<()> {
558 let opts = get_request_options_mut(self.table, &opts)?;
559 let opts = opts.get_mut().ok_or(RequestOptionsError::Immutable)?;
560 opts.first_byte_timeout = duration.map(core::time::Duration::from_nanos);
561 Ok(())
562 }
563
564 fn get_between_bytes_timeout(
565 &mut self,
566 opts: Resource<RequestOptions>,
567 ) -> wasmtime::Result<Option<Duration>> {
568 let opts = get_request_options(self.table, &opts)?;
569 let Some(between_bytes_timeout) = opts.between_bytes_timeout else {
570 return Ok(None);
571 };
572 let ns = between_bytes_timeout.as_nanos();
573 let ns = ns
574 .try_into()
575 .context("between bytes timeout duration nanoseconds do not fit in u64")?;
576 Ok(Some(ns))
577 }
578
579 fn set_between_bytes_timeout(
580 &mut self,
581 opts: Resource<RequestOptions>,
582 duration: Option<Duration>,
583 ) -> RequestOptionsResult<()> {
584 let opts = get_request_options_mut(self.table, &opts)?;
585 let opts = opts.get_mut().ok_or(RequestOptionsError::Immutable)?;
586 opts.between_bytes_timeout = duration.map(core::time::Duration::from_nanos);
587 Ok(())
588 }
589
590 fn clone(
591 &mut self,
592 opts: Resource<RequestOptions>,
593 ) -> wasmtime::Result<Resource<RequestOptions>> {
594 let opts = get_request_options(self.table, &opts)?;
595 push_request_options(self.table, RequestOptions::new_mutable(Arc::clone(opts)))
596 }
597
598 fn drop(&mut self, opts: Resource<RequestOptions>) -> wasmtime::Result<()> {
599 delete_request_options(self.table, opts)?;
600 Ok(())
601 }
602}
603
604impl HostResponseWithStore for WasiHttp {
605 async fn new<T>(
606 store: &Accessor<T, Self>,
607 headers: Resource<Headers>,
608 contents: Option<StreamReader<u8>>,
609 trailers: FutureReader<Result<Option<Resource<Trailers>>, ErrorCode>>,
610 ) -> wasmtime::Result<(Resource<Response>, FutureReader<Result<(), ErrorCode>>)> {
611 store.with(|mut store| {
612 let (result_tx, result_rx) = oneshot::channel();
613 let body = match contents
614 .map(|rx| rx.try_into::<HostBodyStreamProducer<T>>(store.as_context_mut()))
615 {
616 Some(Ok(mut producer)) => Body::Host {
617 body: mem::take(&mut producer.body),
618 result_tx,
619 },
620 Some(Err(rx)) => Body::Guest {
621 contents_rx: Some(rx),
622 trailers_rx: trailers,
623 result_tx,
624 },
625 None => Body::Guest {
626 contents_rx: None,
627 trailers_rx: trailers,
628 result_tx,
629 },
630 };
631 let WasiHttpCtxView { table, .. } = store.get();
632 let headers = delete_fields(table, headers)?;
633 let res = Response {
634 status: http::StatusCode::OK,
635 headers: headers.into(),
636 body,
637 };
638 let res = table
639 .push(res)
640 .context("failed to push response to table")?;
641 Ok((
642 res,
643 FutureReader::new(&mut store, GuestBodyResultProducer::Receiver(result_rx)),
644 ))
645 })
646 }
647
648 async fn consume_body<T>(
649 store: &Accessor<T, Self>,
650 res: Resource<Response>,
651 fut: FutureReader<Result<(), ErrorCode>>,
652 ) -> wasmtime::Result<(
653 StreamReader<u8>,
654 FutureReader<Result<Option<Resource<Trailers>>, ErrorCode>>,
655 )> {
656 let getter = store.getter();
657 store.with(|mut store| {
658 let Response { body, .. } = store
659 .get()
660 .table
661 .delete(res)
662 .context("failed to delete response from table")?;
663 Ok(body.consume(store, fut, getter))
664 })
665 }
666
667 fn drop<T>(mut store: Access<'_, T, Self>, res: Resource<Response>) -> wasmtime::Result<()> {
668 let Response { body, .. } = store
669 .get()
670 .table
671 .delete(res)
672 .context("failed to delete response from table")?;
673 body.drop(store);
674 Ok(())
675 }
676}
677
678impl HostResponse for WasiHttpCtxView<'_> {
679 fn get_status_code(&mut self, res: Resource<Response>) -> wasmtime::Result<StatusCode> {
680 let res = get_response(self.table, &res)?;
681 Ok(res.status.into())
682 }
683
684 fn set_status_code(
685 &mut self,
686 res: Resource<Response>,
687 status_code: StatusCode,
688 ) -> wasmtime::Result<Result<(), ()>> {
689 let res = get_response_mut(self.table, &res)?;
690 let Ok(status) = http::StatusCode::from_u16(status_code) else {
691 return Ok(Err(()));
692 };
693 res.status = status;
694 Ok(Ok(()))
695 }
696
697 fn get_headers(&mut self, res: Resource<Response>) -> wasmtime::Result<Resource<Headers>> {
698 let Response { headers, .. } = get_response(self.table, &res)?;
699 push_fields(self.table, Fields::new_immutable(Arc::clone(headers)))
700 }
701}
702
703impl Host for WasiHttpCtxView<'_> {
704 fn convert_error_code(&mut self, error: HttpError) -> wasmtime::Result<ErrorCode> {
705 error.downcast()
706 }
707
708 fn convert_header_error(
709 &mut self,
710 error: crate::p3::HeaderError,
711 ) -> wasmtime::Result<HeaderError> {
712 error.downcast()
713 }
714
715 fn convert_request_options_error(
716 &mut self,
717 error: crate::p3::RequestOptionsError,
718 ) -> wasmtime::Result<RequestOptionsError> {
719 error.downcast()
720 }
721}