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