wasmtime_wasi_http/
error.rs

1use crate::bindings::http::types::ErrorCode;
2use std::error::Error;
3use std::fmt;
4use wasmtime::component::ResourceTableError;
5
6/// A [`Result`] type where the error type defaults to [`HttpError`].
7pub type HttpResult<T, E = HttpError> = Result<T, E>;
8
9/// A `wasi:http`-specific error type used to represent either a trap or an
10/// [`ErrorCode`].
11///
12/// Modeled after [`TrappableError`](wasmtime_wasi::TrappableError).
13#[repr(transparent)]
14pub struct HttpError {
15    err: anyhow::Error,
16}
17
18impl HttpError {
19    /// Create a new `HttpError` that represents a trap.
20    pub fn trap(err: impl Into<anyhow::Error>) -> HttpError {
21        HttpError { err: err.into() }
22    }
23
24    /// Downcast this error to an [`ErrorCode`].
25    pub fn downcast(self) -> anyhow::Result<ErrorCode> {
26        self.err.downcast()
27    }
28
29    /// Downcast this error to a reference to an [`ErrorCode`]
30    pub fn downcast_ref(&self) -> Option<&ErrorCode> {
31        self.err.downcast_ref()
32    }
33}
34
35impl From<ErrorCode> for HttpError {
36    fn from(error: ErrorCode) -> Self {
37        Self { err: error.into() }
38    }
39}
40
41impl From<ResourceTableError> for HttpError {
42    fn from(error: ResourceTableError) -> Self {
43        HttpError::trap(error)
44    }
45}
46
47impl fmt::Debug for HttpError {
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        self.err.fmt(f)
50    }
51}
52
53impl fmt::Display for HttpError {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        self.err.fmt(f)
56    }
57}
58
59impl Error for HttpError {}
60
61#[cfg(feature = "default-send-request")]
62pub(crate) fn dns_error(rcode: String, info_code: u16) -> ErrorCode {
63    ErrorCode::DnsError(crate::bindings::http::types::DnsErrorPayload {
64        rcode: Some(rcode),
65        info_code: Some(info_code),
66    })
67}
68
69pub(crate) fn internal_error(msg: String) -> ErrorCode {
70    ErrorCode::InternalError(Some(msg))
71}
72
73/// Translate a [`http::Error`] to a wasi-http `ErrorCode` in the context of a request.
74pub fn http_request_error(err: http::Error) -> ErrorCode {
75    if err.is::<http::uri::InvalidUri>() {
76        return ErrorCode::HttpRequestUriInvalid;
77    }
78
79    tracing::warn!("http request error: {err:?}");
80
81    ErrorCode::HttpProtocolError
82}
83
84/// Translate a [`hyper::Error`] to a wasi-http `ErrorCode` in the context of a request.
85pub fn hyper_request_error(err: hyper::Error) -> ErrorCode {
86    // If there's a source, we might be able to extract a wasi-http error from it.
87    if let Some(cause) = err.source() {
88        if let Some(err) = cause.downcast_ref::<ErrorCode>() {
89            return err.clone();
90        }
91    }
92
93    tracing::warn!("hyper request error: {err:?}");
94
95    ErrorCode::HttpProtocolError
96}
97
98/// Translate a [`hyper::Error`] to a wasi-http `ErrorCode` in the context of a response.
99pub fn hyper_response_error(err: hyper::Error) -> ErrorCode {
100    if err.is_timeout() {
101        return ErrorCode::HttpResponseTimeout;
102    }
103
104    // If there's a source, we might be able to extract a wasi-http error from it.
105    if let Some(cause) = err.source() {
106        if let Some(err) = cause.downcast_ref::<ErrorCode>() {
107            return err.clone();
108        }
109    }
110
111    tracing::warn!("hyper response error: {err:?}");
112
113    ErrorCode::HttpProtocolError
114}