1use crate::p2::bindings::http::types::{self, ErrorCode};
2use crate::{Error, FieldMapError, WasiHttpHooks};
3use std::error::Error as _;
4use std::fmt;
5use std::io::ErrorKind;
6use wasmtime::component::ResourceTableError;
7
8pub type HttpResult<T, E = HttpError> = Result<T, E>;
10
11#[repr(transparent)]
16pub struct HttpError {
17 err: wasmtime::Error,
18}
19
20impl HttpError {
21 pub fn trap(err: impl Into<wasmtime::Error>) -> HttpError {
23 HttpError { err: err.into() }
24 }
25
26 pub fn downcast(self) -> wasmtime::Result<ErrorCode> {
28 self.err.downcast()
29 }
30
31 pub fn downcast_ref(&self) -> Option<&ErrorCode> {
33 self.err.downcast_ref()
34 }
35}
36
37impl From<ErrorCode> for HttpError {
38 fn from(error: ErrorCode) -> Self {
39 Self { err: error.into() }
40 }
41}
42
43impl From<ResourceTableError> for HttpError {
44 fn from(error: ResourceTableError) -> Self {
45 HttpError::trap(error)
46 }
47}
48
49impl fmt::Debug for HttpError {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 self.err.fmt(f)
52 }
53}
54
55impl fmt::Display for HttpError {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 self.err.fmt(f)
58 }
59}
60
61impl std::error::Error for HttpError {}
62
63pub type HeaderResult<T, E = HeaderError> = Result<T, E>;
65
66#[repr(transparent)]
71pub struct HeaderError {
72 err: wasmtime::Error,
73}
74
75impl HeaderError {
76 pub fn trap(err: impl Into<wasmtime::Error>) -> HeaderError {
78 HeaderError { err: err.into() }
79 }
80
81 pub fn downcast(self) -> wasmtime::Result<types::HeaderError> {
83 self.err.downcast()
84 }
85
86 pub fn downcast_ref(&self) -> Option<&types::HeaderError> {
88 self.err.downcast_ref()
89 }
90}
91
92impl From<types::HeaderError> for HeaderError {
93 fn from(error: types::HeaderError) -> Self {
94 Self { err: error.into() }
95 }
96}
97
98impl From<ResourceTableError> for HeaderError {
99 fn from(error: ResourceTableError) -> Self {
100 HeaderError::trap(error)
101 }
102}
103
104impl From<http::header::InvalidHeaderName> for HeaderError {
105 fn from(_: http::header::InvalidHeaderName) -> Self {
106 HeaderError::from(types::HeaderError::InvalidSyntax)
107 }
108}
109
110impl From<http::header::InvalidHeaderValue> for HeaderError {
111 fn from(_: http::header::InvalidHeaderValue) -> Self {
112 HeaderError::from(types::HeaderError::InvalidSyntax)
113 }
114}
115
116impl From<FieldMapError> for HeaderError {
117 fn from(err: FieldMapError) -> Self {
118 match err {
119 FieldMapError::Immutable => types::HeaderError::Immutable.into(),
120 FieldMapError::InvalidHeaderName | FieldMapError::InvalidHeaderValue => {
121 types::HeaderError::InvalidSyntax.into()
122 }
123 FieldMapError::TooManyFields | FieldMapError::TotalSizeTooBig => HeaderError::trap(err),
124 FieldMapError::Forbidden => types::HeaderError::Forbidden.into(),
125 }
126 }
127}
128
129impl fmt::Debug for HeaderError {
130 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131 self.err.fmt(f)
132 }
133}
134
135impl fmt::Display for HeaderError {
136 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137 self.err.fmt(f)
138 }
139}
140
141pub(crate) fn internal_error(msg: String) -> ErrorCode {
142 ErrorCode::InternalError(Some(msg))
143}
144
145pub fn http_request_error(err: http::Error) -> ErrorCode {
147 if err.is::<http::uri::InvalidUri>() {
148 return ErrorCode::HttpRequestUriInvalid;
149 }
150
151 tracing::warn!("http request error: {err:?}");
152
153 ErrorCode::HttpProtocolError
154}
155
156pub fn hyper_request_error(err: hyper::Error) -> ErrorCode {
158 if let Some(cause) = err.source() {
160 if let Some(err) = cause.downcast_ref::<ErrorCode>() {
161 return err.clone();
162 }
163 }
164
165 tracing::warn!("hyper request error: {err:?}");
166
167 ErrorCode::HttpProtocolError
168}
169
170pub fn hyper_response_error(err: hyper::Error) -> ErrorCode {
172 if err.is_timeout() {
173 return ErrorCode::HttpResponseTimeout;
174 }
175
176 if let Some(cause) = err.source() {
178 if let Some(err) = cause.downcast_ref::<ErrorCode>() {
179 return err.clone();
180 }
181 }
182
183 tracing::warn!("hyper response error: {err:?}");
184
185 ErrorCode::HttpProtocolError
186}
187
188impl From<hyper::Error> for ErrorCode {
189 fn from(err: hyper::Error) -> Self {
190 hyper_response_error(err)
191 }
192}
193
194impl From<ErrorCode> for Error {
195 fn from(e: ErrorCode) -> Self {
196 match e {
197 ErrorCode::DnsTimeout => Self::DnsTimeout,
198 ErrorCode::DnsError(payload) => Self::DnsError {
199 rcode: payload.rcode,
200 info_code: payload.info_code,
201 },
202 ErrorCode::DestinationNotFound => Self::DestinationNotFound,
203 ErrorCode::DestinationUnavailable => Self::DestinationUnavailable,
204 ErrorCode::DestinationIpProhibited => Self::DestinationIpProhibited,
205 ErrorCode::DestinationIpUnroutable => Self::DestinationIpUnroutable,
206 ErrorCode::ConnectionRefused => Self::ConnectionRefused,
207 ErrorCode::ConnectionTerminated => Self::ConnectionTerminated,
208 ErrorCode::ConnectionTimeout => Self::ConnectionTimeout,
209 ErrorCode::ConnectionReadTimeout => Self::ConnectionReadTimeout,
210 ErrorCode::ConnectionWriteTimeout => Self::ConnectionWriteTimeout,
211 ErrorCode::ConnectionLimitReached => Self::ConnectionLimitReached,
212 ErrorCode::TlsProtocolError => Self::TlsProtocolError,
213 ErrorCode::TlsCertificateError => Self::TlsCertificateError,
214 ErrorCode::TlsAlertReceived(payload) => Self::TlsAlertReceived {
215 alert_id: payload.alert_id,
216 alert_message: payload.alert_message,
217 },
218 ErrorCode::HttpRequestDenied => Self::HttpRequestDenied,
219 ErrorCode::HttpRequestLengthRequired => Self::HttpRequestLengthRequired,
220 ErrorCode::HttpRequestBodySize(payload) => Self::HttpRequestBodySize(payload),
221 ErrorCode::HttpRequestMethodInvalid => Self::HttpRequestMethodInvalid,
222 ErrorCode::HttpRequestUriInvalid => Self::HttpRequestUriInvalid,
223 ErrorCode::HttpRequestUriTooLong => Self::HttpRequestUriTooLong,
224 ErrorCode::HttpRequestHeaderSectionSize(payload) => {
225 Self::HttpRequestHeaderSectionSize(payload)
226 }
227 ErrorCode::HttpRequestHeaderSize(payload) => {
228 let (field_name, field_size) = match payload {
229 Some(p) => (p.field_name, p.field_size),
230 None => (None, None),
231 };
232 Self::HttpRequestHeaderSize {
233 field_name,
234 field_size,
235 }
236 }
237 ErrorCode::HttpRequestTrailerSectionSize(payload) => {
238 Self::HttpRequestTrailerSectionSize(payload)
239 }
240 ErrorCode::HttpRequestTrailerSize(payload) => Self::HttpRequestTrailerSize {
241 field_name: payload.field_name,
242 field_size: payload.field_size,
243 },
244 ErrorCode::HttpResponseIncomplete => Self::HttpResponseIncomplete,
245 ErrorCode::HttpResponseHeaderSectionSize(payload) => {
246 Self::HttpResponseHeaderSectionSize(payload)
247 }
248 ErrorCode::HttpResponseHeaderSize(payload) => Self::HttpResponseHeaderSize {
249 field_name: payload.field_name,
250 field_size: payload.field_size,
251 },
252 ErrorCode::HttpResponseBodySize(payload) => Self::HttpResponseBodySize(payload),
253 ErrorCode::HttpResponseTrailerSectionSize(payload) => {
254 Self::HttpResponseTrailerSectionSize(payload)
255 }
256 ErrorCode::HttpResponseTrailerSize(payload) => Self::HttpResponseTrailerSize {
257 field_name: payload.field_name,
258 field_size: payload.field_size,
259 },
260 ErrorCode::HttpResponseTransferCoding(payload) => {
261 Self::HttpResponseTransferCoding(payload)
262 }
263 ErrorCode::HttpResponseContentCoding(payload) => {
264 Self::HttpResponseContentCoding(payload)
265 }
266 ErrorCode::HttpResponseTimeout => Self::HttpResponseTimeout,
267 ErrorCode::HttpUpgradeFailed => Self::HttpUpgradeFailed,
268 ErrorCode::HttpProtocolError => Self::HttpProtocolError,
269 ErrorCode::LoopDetected => Self::LoopDetected,
270 ErrorCode::ConfigurationError => Self::ConfigurationError,
271 ErrorCode::InternalError(payload) => Self::InternalError(payload),
272 }
273 }
274}
275
276impl dyn WasiHttpHooks + '_ {
277 pub(crate) fn error_to_p2(&mut self, e: &Error) -> ErrorCode {
278 match e {
279 Error::Hyper(err) => {
280 if let Some(cause) = err.source() {
282 if let Some(err) = cause.downcast_ref::<ErrorCode>() {
283 return err.clone();
284 }
285 if let Some(err) = cause.downcast_ref::<Error>() {
286 return self.error_to_p2(err);
287 }
288 }
289
290 self.p2_error_from_hyper(err)
291 }
292 Error::Connect(err) => {
293 if err.kind() == ErrorKind::AddrNotAvailable {
294 return ErrorCode::DnsError(types::DnsErrorPayload {
295 rcode: Some("address not available".to_string()),
296 info_code: None,
297 });
298 }
299
300 if err
301 .to_string()
302 .starts_with("failed to lookup address information")
303 {
304 return ErrorCode::DnsError(types::DnsErrorPayload {
305 rcode: Some("address not available".to_string()),
306 info_code: None,
307 });
308 }
309
310 self.p2_error_from_connect(err)
311 }
312 Error::Tls(err) => self.p2_error_from_tls(err),
313 #[cfg(feature = "default-send-request")]
314 Error::InvalidDnsNameError(err) => self.p2_error_from_dns(err),
315 Error::DnsTimeout => ErrorCode::DnsTimeout,
316 Error::DnsError { rcode, info_code } => ErrorCode::DnsError(types::DnsErrorPayload {
317 rcode: rcode.clone(),
318 info_code: *info_code,
319 }),
320 Error::DestinationNotFound => ErrorCode::DestinationNotFound,
321 Error::DestinationUnavailable => ErrorCode::DestinationUnavailable,
322 Error::DestinationIpProhibited => ErrorCode::DestinationIpProhibited,
323 Error::DestinationIpUnroutable => ErrorCode::DestinationIpUnroutable,
324 Error::ConnectionRefused => ErrorCode::ConnectionRefused,
325 Error::ConnectionTerminated => ErrorCode::ConnectionTerminated,
326 Error::ConnectionTimeout => ErrorCode::ConnectionTimeout,
327 Error::ConnectionReadTimeout => ErrorCode::ConnectionReadTimeout,
328 Error::ConnectionWriteTimeout => ErrorCode::ConnectionWriteTimeout,
329 Error::ConnectionLimitReached => ErrorCode::ConnectionLimitReached,
330 Error::TlsProtocolError => ErrorCode::TlsProtocolError,
331 Error::TlsCertificateError => ErrorCode::TlsCertificateError,
332 Error::TlsAlertReceived {
333 alert_id,
334 alert_message,
335 } => ErrorCode::TlsAlertReceived(types::TlsAlertReceivedPayload {
336 alert_id: *alert_id,
337 alert_message: alert_message.clone(),
338 }),
339 Error::HttpRequestDenied => ErrorCode::HttpRequestDenied,
340 Error::HttpRequestLengthRequired => ErrorCode::HttpRequestLengthRequired,
341 Error::HttpRequestBodySize(payload) => ErrorCode::HttpRequestBodySize(*payload),
342 Error::HttpRequestMethodInvalid => ErrorCode::HttpRequestMethodInvalid,
343 Error::HttpRequestUriInvalid => ErrorCode::HttpRequestUriInvalid,
344 Error::HttpRequestUriTooLong => ErrorCode::HttpRequestUriTooLong,
345 Error::HttpRequestHeaderSectionSize(payload) => {
346 ErrorCode::HttpRequestHeaderSectionSize(*payload)
347 }
348 Error::HttpRequestHeaderSize {
349 field_name,
350 field_size,
351 } => ErrorCode::HttpRequestHeaderSize(Some(types::FieldSizePayload {
352 field_name: field_name.clone(),
353 field_size: *field_size,
354 })),
355 Error::HttpRequestTrailerSectionSize(payload) => {
356 ErrorCode::HttpRequestTrailerSectionSize(*payload)
357 }
358 Error::HttpRequestTrailerSize {
359 field_name,
360 field_size,
361 } => ErrorCode::HttpRequestTrailerSize(types::FieldSizePayload {
362 field_name: field_name.clone(),
363 field_size: *field_size,
364 }),
365 Error::HttpResponseIncomplete => ErrorCode::HttpResponseIncomplete,
366 Error::HttpResponseHeaderSectionSize(payload) => {
367 ErrorCode::HttpResponseHeaderSectionSize(*payload)
368 }
369 Error::HttpResponseHeaderSize {
370 field_name,
371 field_size,
372 } => ErrorCode::HttpResponseHeaderSize(types::FieldSizePayload {
373 field_name: field_name.clone(),
374 field_size: *field_size,
375 }),
376 Error::HttpResponseBodySize(payload) => ErrorCode::HttpResponseBodySize(*payload),
377 Error::HttpResponseTrailerSectionSize(payload) => {
378 ErrorCode::HttpResponseTrailerSectionSize(*payload)
379 }
380 Error::HttpResponseTrailerSize {
381 field_name,
382 field_size,
383 } => ErrorCode::HttpResponseTrailerSize(types::FieldSizePayload {
384 field_name: field_name.clone(),
385 field_size: *field_size,
386 }),
387 Error::HttpResponseTransferCoding(payload) => {
388 ErrorCode::HttpResponseTransferCoding(payload.clone())
389 }
390 Error::HttpResponseContentCoding(payload) => {
391 ErrorCode::HttpResponseContentCoding(payload.clone())
392 }
393 Error::HttpResponseTimeout => ErrorCode::HttpResponseTimeout,
394 Error::HttpUpgradeFailed => ErrorCode::HttpUpgradeFailed,
395 Error::HttpProtocolError => ErrorCode::HttpProtocolError,
396 Error::LoopDetected => ErrorCode::LoopDetected,
397 Error::ConfigurationError => ErrorCode::ConfigurationError,
398 Error::InternalError(payload) => ErrorCode::InternalError(payload.clone()),
399 }
400 }
401}