wasmer_wasix/http/
client.rs1use std::{collections::BTreeSet, ops::Deref, sync::Arc};
2
3use futures::future::BoxFuture;
4use http::{HeaderMap, Method, StatusCode};
5use url::Url;
6
7#[derive(Clone, Debug, PartialEq, Eq, Hash)]
9pub struct HttpClientCapabilityV1 {
10 pub allow_all: bool,
11 pub allowed_hosts: BTreeSet<String>,
12}
13
14impl HttpClientCapabilityV1 {
15 pub fn new() -> Self {
16 Self {
17 allow_all: false,
18 allowed_hosts: Default::default(),
19 }
20 }
21
22 pub fn new_allow_all() -> Self {
23 Self {
24 allow_all: true,
25 allowed_hosts: Default::default(),
26 }
27 }
28
29 pub fn is_deny_all(&self) -> bool {
30 !self.allow_all && self.allowed_hosts.is_empty()
31 }
32
33 pub fn can_access_domain(&self, domain: &str) -> bool {
34 self.allow_all || self.allowed_hosts.contains(domain)
35 }
36
37 pub fn update(&mut self, other: HttpClientCapabilityV1) {
38 let HttpClientCapabilityV1 {
39 allow_all,
40 allowed_hosts,
41 } = other;
42 self.allow_all |= allow_all;
43 self.allowed_hosts.extend(allowed_hosts);
44 }
45}
46
47impl Default for HttpClientCapabilityV1 {
48 fn default() -> Self {
49 Self::new()
50 }
51}
52
53#[derive(Debug, Default)]
54pub struct HttpRequestOptions {
55 pub gzip: bool,
56 pub cors_proxy: Option<String>,
57}
58
59pub struct HttpRequest {
61 pub url: Url,
62 pub method: Method,
63 pub headers: HeaderMap,
64 pub body: Option<Vec<u8>>,
65 pub options: HttpRequestOptions,
66}
67
68impl HttpRequest {
69 fn from_http_parts(parts: http::request::Parts, body: impl Into<Option<Vec<u8>>>) -> Self {
70 let http::request::Parts {
71 method,
72 uri,
73 headers,
74 ..
75 } = parts;
76
77 HttpRequest {
78 url: uri.to_string().parse().unwrap(),
79 method,
80 headers,
81 body: body.into(),
82 options: HttpRequestOptions::default(),
83 }
84 }
85}
86
87impl std::fmt::Debug for HttpRequest {
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 let HttpRequest {
90 url,
91 method,
92 headers,
93 body,
94 options,
95 } = self;
96
97 f.debug_struct("HttpRequest")
98 .field("url", &format_args!("{url}"))
99 .field("method", method)
100 .field("headers", headers)
101 .field("body", &body.as_deref().map(String::from_utf8_lossy))
102 .field("options", &options)
103 .finish()
104 }
105}
106
107impl From<http::Request<Option<Vec<u8>>>> for HttpRequest {
108 fn from(value: http::Request<Option<Vec<u8>>>) -> Self {
109 let (parts, body) = value.into_parts();
110 HttpRequest::from_http_parts(parts, body)
111 }
112}
113
114impl From<http::Request<Vec<u8>>> for HttpRequest {
115 fn from(value: http::Request<Vec<u8>>) -> Self {
116 let (parts, body) = value.into_parts();
117 HttpRequest::from_http_parts(parts, body)
118 }
119}
120
121impl From<http::Request<&str>> for HttpRequest {
122 fn from(value: http::Request<&str>) -> Self {
123 value.map(|body| body.to_string()).into()
124 }
125}
126
127impl From<http::Request<String>> for HttpRequest {
128 fn from(value: http::Request<String>) -> Self {
129 let (parts, body) = value.into_parts();
130 HttpRequest::from_http_parts(parts, body.into_bytes())
131 }
132}
133
134impl From<http::Request<()>> for HttpRequest {
135 fn from(value: http::Request<()>) -> Self {
136 let (parts, _) = value.into_parts();
137 HttpRequest::from_http_parts(parts, None)
138 }
139}
140
141pub struct HttpResponse {
143 pub body: Option<Vec<u8>>,
144 pub redirected: bool,
145 pub status: StatusCode,
146 pub headers: HeaderMap,
147}
148
149impl HttpResponse {
150 pub fn is_ok(&self) -> bool {
151 !self.status.is_client_error() && !self.status.is_server_error()
152 }
153}
154
155impl std::fmt::Debug for HttpResponse {
156 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
157 let HttpResponse {
158 body,
159 redirected,
160 status,
161 headers,
162 } = self;
163
164 f.debug_struct("HttpResponse")
165 .field("ok", &self.is_ok())
166 .field("redirected", &redirected)
167 .field("status", &status)
168 .field("headers", &headers)
169 .field("body", &body.as_deref().map(String::from_utf8_lossy))
170 .finish()
171 }
172}
173
174pub trait HttpClient: std::fmt::Debug {
175 fn request(&self, request: HttpRequest) -> BoxFuture<'_, Result<HttpResponse, anyhow::Error>>;
177}
178
179impl<D, C> HttpClient for D
180where
181 D: Deref<Target = C> + std::fmt::Debug,
182 C: HttpClient + ?Sized + 'static,
183{
184 fn request(&self, request: HttpRequest) -> BoxFuture<'_, Result<HttpResponse, anyhow::Error>> {
185 let client = &**self;
186 client.request(request)
187 }
188}
189
190pub type DynHttpClient = Arc<dyn HttpClient + Send + Sync + 'static>;