1 use crate::FieldMapError;
2 use crate::p2::bindings::http::types::{self, ErrorCode};
3 use std::error::Error;
4 use std::fmt;
5 use wasmtime::component::ResourceTableError;
6 
7 /// A [`Result`] type where the error type defaults to [`HttpError`].
8 pub type HttpResult<T, E = HttpError> = Result<T, E>;
9 
10 /// A `wasi:http`-specific error type used to represent either a trap or an
11 /// [`ErrorCode`].
12 ///
13 /// Modeled after [`TrappableError`](wasmtime_wasi::TrappableError).
14 #[repr(transparent)]
15 pub struct HttpError {
16     err: wasmtime::Error,
17 }
18 
19 impl HttpError {
20     /// Create a new `HttpError` that represents a trap.
trap(err: impl Into<wasmtime::Error>) -> HttpError21     pub fn trap(err: impl Into<wasmtime::Error>) -> HttpError {
22         HttpError { err: err.into() }
23     }
24 
25     /// Downcast this error to an [`ErrorCode`].
downcast(self) -> wasmtime::Result<ErrorCode>26     pub fn downcast(self) -> wasmtime::Result<ErrorCode> {
27         self.err.downcast()
28     }
29 
30     /// Downcast this error to a reference to an [`ErrorCode`]
downcast_ref(&self) -> Option<&ErrorCode>31     pub fn downcast_ref(&self) -> Option<&ErrorCode> {
32         self.err.downcast_ref()
33     }
34 }
35 
36 impl From<ErrorCode> for HttpError {
from(error: ErrorCode) -> Self37     fn from(error: ErrorCode) -> Self {
38         Self { err: error.into() }
39     }
40 }
41 
42 impl From<ResourceTableError> for HttpError {
from(error: ResourceTableError) -> Self43     fn from(error: ResourceTableError) -> Self {
44         HttpError::trap(error)
45     }
46 }
47 
48 impl fmt::Debug for HttpError {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result49     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50         self.err.fmt(f)
51     }
52 }
53 
54 impl fmt::Display for HttpError {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result55     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56         self.err.fmt(f)
57     }
58 }
59 
60 impl Error for HttpError {}
61 
62 /// A [`Result`] type where the error type defaults to [`HeaderError`].
63 pub type HeaderResult<T, E = HeaderError> = Result<T, E>;
64 
65 /// A `wasi:http`-specific error type used to represent either a trap or an
66 /// [`types::HeaderError`].
67 ///
68 /// Modeled after [`TrappableError`](wasmtime_wasi::TrappableError).
69 #[repr(transparent)]
70 pub struct HeaderError {
71     err: wasmtime::Error,
72 }
73 
74 impl HeaderError {
75     /// Create a new `HeaderError` that represents a trap.
trap(err: impl Into<wasmtime::Error>) -> HeaderError76     pub fn trap(err: impl Into<wasmtime::Error>) -> HeaderError {
77         HeaderError { err: err.into() }
78     }
79 
80     /// Downcast this error to an [`ErrorCode`].
downcast(self) -> wasmtime::Result<types::HeaderError>81     pub fn downcast(self) -> wasmtime::Result<types::HeaderError> {
82         self.err.downcast()
83     }
84 
85     /// Downcast this error to a reference to an [`ErrorCode`]
downcast_ref(&self) -> Option<&types::HeaderError>86     pub fn downcast_ref(&self) -> Option<&types::HeaderError> {
87         self.err.downcast_ref()
88     }
89 }
90 
91 impl From<types::HeaderError> for HeaderError {
from(error: types::HeaderError) -> Self92     fn from(error: types::HeaderError) -> Self {
93         Self { err: error.into() }
94     }
95 }
96 
97 impl From<ResourceTableError> for HeaderError {
from(error: ResourceTableError) -> Self98     fn from(error: ResourceTableError) -> Self {
99         HeaderError::trap(error)
100     }
101 }
102 
103 impl From<http::header::InvalidHeaderName> for HeaderError {
from(_: http::header::InvalidHeaderName) -> Self104     fn from(_: http::header::InvalidHeaderName) -> Self {
105         HeaderError::from(types::HeaderError::InvalidSyntax)
106     }
107 }
108 
109 impl From<http::header::InvalidHeaderValue> for HeaderError {
from(_: http::header::InvalidHeaderValue) -> Self110     fn from(_: http::header::InvalidHeaderValue) -> Self {
111         HeaderError::from(types::HeaderError::InvalidSyntax)
112     }
113 }
114 
115 impl From<FieldMapError> for HeaderError {
from(err: FieldMapError) -> Self116     fn from(err: FieldMapError) -> Self {
117         match err {
118             FieldMapError::Immutable => types::HeaderError::Immutable.into(),
119             FieldMapError::InvalidHeaderName => types::HeaderError::InvalidSyntax.into(),
120             FieldMapError::TooManyFields | FieldMapError::TotalSizeTooBig => HeaderError::trap(err),
121         }
122     }
123 }
124 
125 impl fmt::Debug for HeaderError {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result126     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127         self.err.fmt(f)
128     }
129 }
130 
131 impl fmt::Display for HeaderError {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result132     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133         self.err.fmt(f)
134     }
135 }
136 
137 #[cfg(feature = "default-send-request")]
dns_error(rcode: String, info_code: u16) -> ErrorCode138 pub(crate) fn dns_error(rcode: String, info_code: u16) -> ErrorCode {
139     ErrorCode::DnsError(crate::p2::bindings::http::types::DnsErrorPayload {
140         rcode: Some(rcode),
141         info_code: Some(info_code),
142     })
143 }
144 
internal_error(msg: String) -> ErrorCode145 pub(crate) fn internal_error(msg: String) -> ErrorCode {
146     ErrorCode::InternalError(Some(msg))
147 }
148 
149 /// Translate a [`http::Error`] to a wasi-http `ErrorCode` in the context of a request.
http_request_error(err: http::Error) -> ErrorCode150 pub fn http_request_error(err: http::Error) -> ErrorCode {
151     if err.is::<http::uri::InvalidUri>() {
152         return ErrorCode::HttpRequestUriInvalid;
153     }
154 
155     tracing::warn!("http request error: {err:?}");
156 
157     ErrorCode::HttpProtocolError
158 }
159 
160 /// Translate a [`hyper::Error`] to a wasi-http `ErrorCode` in the context of a request.
hyper_request_error(err: hyper::Error) -> ErrorCode161 pub fn hyper_request_error(err: hyper::Error) -> ErrorCode {
162     // If there's a source, we might be able to extract a wasi-http error from it.
163     if let Some(cause) = err.source() {
164         if let Some(err) = cause.downcast_ref::<ErrorCode>() {
165             return err.clone();
166         }
167     }
168 
169     tracing::warn!("hyper request error: {err:?}");
170 
171     ErrorCode::HttpProtocolError
172 }
173 
174 /// Translate a [`hyper::Error`] to a wasi-http `ErrorCode` in the context of a response.
hyper_response_error(err: hyper::Error) -> ErrorCode175 pub fn hyper_response_error(err: hyper::Error) -> ErrorCode {
176     if err.is_timeout() {
177         return ErrorCode::HttpResponseTimeout;
178     }
179 
180     // If there's a source, we might be able to extract a wasi-http error from it.
181     if let Some(cause) = err.source() {
182         if let Some(err) = cause.downcast_ref::<ErrorCode>() {
183             return err.clone();
184         }
185     }
186 
187     tracing::warn!("hyper response error: {err:?}");
188 
189     ErrorCode::HttpProtocolError
190 }
191 
192 impl From<hyper::Error> for ErrorCode {
from(err: hyper::Error) -> Self193     fn from(err: hyper::Error) -> Self {
194         hyper_response_error(err)
195     }
196 }
197