wasmtime_wasi_http/
http_impl.rs

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