wasmtime_wasi_http/
http_impl.rs

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