Skip to main content

wasmtime_wasi_http/p2/
http_impl.rs

1//! Implementation of the `wasi:http/outgoing-handler` interface.
2
3use crate::p2::{
4    HttpResult, WasiHttpCtxView,
5    bindings::http::{
6        outgoing_handler,
7        types::{self, Scheme},
8    },
9    error::internal_error,
10    http_request_error,
11    types::{HostFutureIncomingResponse, HostOutgoingRequest, OutgoingRequestConfig},
12};
13use bytes::Bytes;
14use http_body_util::{BodyExt, Empty};
15use hyper::Method;
16use wasmtime::component::Resource;
17
18impl outgoing_handler::Host for WasiHttpCtxView<'_> {
19    fn handle(
20        &mut self,
21        request_id: Resource<HostOutgoingRequest>,
22        options: Option<Resource<types::RequestOptions>>,
23    ) -> HttpResult<Resource<HostFutureIncomingResponse>> {
24        let opts = options.and_then(|opts| self.table.get(&opts).ok());
25
26        let connect_timeout = opts
27            .and_then(|opts| opts.connect_timeout)
28            .unwrap_or(std::time::Duration::from_secs(600));
29
30        let first_byte_timeout = opts
31            .and_then(|opts| opts.first_byte_timeout)
32            .unwrap_or(std::time::Duration::from_secs(600));
33
34        let between_bytes_timeout = opts
35            .and_then(|opts| opts.between_bytes_timeout)
36            .unwrap_or(std::time::Duration::from_secs(600));
37
38        let req = self.table.delete(request_id)?;
39        let mut builder = hyper::Request::builder();
40
41        builder = builder.method(match req.method {
42            types::Method::Get => Method::GET,
43            types::Method::Head => Method::HEAD,
44            types::Method::Post => Method::POST,
45            types::Method::Put => Method::PUT,
46            types::Method::Delete => Method::DELETE,
47            types::Method::Connect => Method::CONNECT,
48            types::Method::Options => Method::OPTIONS,
49            types::Method::Trace => Method::TRACE,
50            types::Method::Patch => Method::PATCH,
51            types::Method::Other(m) => match hyper::Method::from_bytes(m.as_bytes()) {
52                Ok(method) => method,
53                Err(_) => return Err(types::ErrorCode::HttpRequestMethodInvalid.into()),
54            },
55        });
56
57        let (use_tls, scheme) = match req.scheme.unwrap_or(Scheme::Https) {
58            Scheme::Http => (false, http::uri::Scheme::HTTP),
59            Scheme::Https => (true, http::uri::Scheme::HTTPS),
60
61            // We can only support http/https
62            Scheme::Other(_) => return Err(types::ErrorCode::HttpProtocolError.into()),
63        };
64
65        let authority = req.authority.unwrap_or_else(String::new);
66
67        let mut uri = http::Uri::builder()
68            .scheme(scheme)
69            .authority(authority.clone());
70
71        if let Some(path) = req.path_with_query {
72            uri = uri.path_and_query(path);
73        }
74
75        builder = builder.uri(uri.build().map_err(http_request_error)?);
76
77        for (k, v) in req.headers.iter() {
78            builder = builder.header(k, v);
79        }
80
81        let body = req.body.unwrap_or_else(|| {
82            Empty::<Bytes>::new()
83                .map_err(|_| unreachable!("Infallible error"))
84                .boxed_unsync()
85        });
86
87        let request = builder
88            .body(body)
89            .map_err(|err| internal_error(err.to_string()))?;
90
91        let future = self.hooks.send_request(
92            request,
93            OutgoingRequestConfig {
94                use_tls,
95                connect_timeout,
96                first_byte_timeout,
97                between_bytes_timeout,
98            },
99        )?;
100
101        Ok(self.table.push(future)?)
102    }
103}