1use crate::p2::bindings::http::types::{self, ErrorCode};
2use crate::{Error, FieldMapError, WasiHttpCtxView};
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::HttpRequestHeaderSize {
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 WasiHttpCtxView<'_> {
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 }
286
287 self.hooks.p2_error_from_hyper(&err)
288 }
289 Error::Connect(err) => {
290 if err.kind() == ErrorKind::AddrNotAvailable {
291 return ErrorCode::DnsError(types::DnsErrorPayload {
292 rcode: Some("address not available".to_string()),
293 info_code: None,
294 });
295 }
296
297 if err
298 .to_string()
299 .starts_with("failed to lookup address information")
300 {
301 return ErrorCode::DnsError(types::DnsErrorPayload {
302 rcode: Some("address not available".to_string()),
303 info_code: None,
304 });
305 }
306
307 self.hooks.p2_error_from_connect(&err)
308 }
309 Error::Tls(err) => self.hooks.p2_error_from_tls(&err),
310 #[cfg(feature = "default-send-request")]
311 Error::InvalidDnsNameError(err) => self.hooks.p2_error_from_dns(&err),
312 Error::DnsTimeout => ErrorCode::DnsTimeout,
313 Error::DnsError { rcode, info_code } => {
314 ErrorCode::DnsError(types::DnsErrorPayload { rcode, info_code })
315 }
316 Error::DestinationNotFound => ErrorCode::DestinationNotFound,
317 Error::DestinationUnavailable => ErrorCode::DestinationUnavailable,
318 Error::DestinationIpProhibited => ErrorCode::DestinationIpProhibited,
319 Error::DestinationIpUnroutable => ErrorCode::DestinationIpUnroutable,
320 Error::ConnectionRefused => ErrorCode::ConnectionRefused,
321 Error::ConnectionTerminated => ErrorCode::ConnectionTerminated,
322 Error::ConnectionTimeout => ErrorCode::ConnectionTimeout,
323 Error::ConnectionReadTimeout => ErrorCode::ConnectionReadTimeout,
324 Error::ConnectionWriteTimeout => ErrorCode::ConnectionWriteTimeout,
325 Error::ConnectionLimitReached => ErrorCode::ConnectionLimitReached,
326 Error::TlsProtocolError => ErrorCode::TlsProtocolError,
327 Error::TlsCertificateError => ErrorCode::TlsCertificateError,
328 Error::TlsAlertReceived {
329 alert_id,
330 alert_message,
331 } => ErrorCode::TlsAlertReceived(types::TlsAlertReceivedPayload {
332 alert_id,
333 alert_message,
334 }),
335 Error::HttpRequestDenied => ErrorCode::HttpRequestDenied,
336 Error::HttpRequestLengthRequired => ErrorCode::HttpRequestLengthRequired,
337 Error::HttpRequestBodySize(payload) => ErrorCode::HttpRequestBodySize(payload),
338 Error::HttpRequestMethodInvalid => ErrorCode::HttpRequestMethodInvalid,
339 Error::HttpRequestUriInvalid => ErrorCode::HttpRequestUriInvalid,
340 Error::HttpRequestUriTooLong => ErrorCode::HttpRequestUriTooLong,
341 Error::HttpRequestHeaderSectionSize(payload) => {
342 ErrorCode::HttpRequestHeaderSectionSize(payload)
343 }
344 Error::HttpRequestHeaderSize {
345 field_name,
346 field_size,
347 } => ErrorCode::HttpRequestHeaderSize(Some(types::FieldSizePayload {
348 field_name,
349 field_size,
350 })),
351 Error::HttpRequestTrailerSectionSize(payload) => {
352 ErrorCode::HttpRequestTrailerSectionSize(payload)
353 }
354 Error::HttpRequestTrailerSize {
355 field_name,
356 field_size,
357 } => ErrorCode::HttpRequestTrailerSize(types::FieldSizePayload {
358 field_name,
359 field_size,
360 }),
361 Error::HttpResponseIncomplete => ErrorCode::HttpResponseIncomplete,
362 Error::HttpResponseHeaderSectionSize(payload) => {
363 ErrorCode::HttpResponseHeaderSectionSize(payload)
364 }
365 Error::HttpResponseHeaderSize {
366 field_name,
367 field_size,
368 } => ErrorCode::HttpResponseHeaderSize(types::FieldSizePayload {
369 field_name,
370 field_size,
371 }),
372 Error::HttpResponseBodySize(payload) => ErrorCode::HttpResponseBodySize(payload),
373 Error::HttpResponseTrailerSectionSize(payload) => {
374 ErrorCode::HttpResponseTrailerSectionSize(payload)
375 }
376 Error::HttpResponseTrailerSize {
377 field_name,
378 field_size,
379 } => ErrorCode::HttpResponseTrailerSize(types::FieldSizePayload {
380 field_name,
381 field_size,
382 }),
383 Error::HttpResponseTransferCoding(payload) => {
384 ErrorCode::HttpResponseTransferCoding(payload)
385 }
386 Error::HttpResponseContentCoding(payload) => {
387 ErrorCode::HttpResponseContentCoding(payload)
388 }
389 Error::HttpResponseTimeout => ErrorCode::HttpResponseTimeout,
390 Error::HttpUpgradeFailed => ErrorCode::HttpUpgradeFailed,
391 Error::HttpProtocolError => ErrorCode::HttpProtocolError,
392 Error::LoopDetected => ErrorCode::LoopDetected,
393 Error::ConfigurationError => ErrorCode::ConfigurationError,
394 Error::InternalError(payload) => ErrorCode::InternalError(payload),
395 }
396 }
397}