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
61pub(crate) fn dns_error(rcode: String, info_code: u16) -> ErrorCode {
62    ErrorCode::DnsError(crate::bindings::http::types::DnsErrorPayload {
63        rcode: Some(rcode),
64        info_code: Some(info_code),
65    })
66}
67
68pub(crate) fn internal_error(msg: String) -> ErrorCode {
69    ErrorCode::InternalError(Some(msg))
70}
71
72/// Translate a [`http::Error`] to a wasi-http `ErrorCode` in the context of a request.
73pub fn http_request_error(err: http::Error) -> ErrorCode {
74    if err.is::<http::uri::InvalidUri>() {
75        return ErrorCode::HttpRequestUriInvalid;
76    }
77
78    tracing::warn!("http request error: {err:?}");
79
80    ErrorCode::HttpProtocolError
81}
82
83/// Translate a [`hyper::Error`] to a wasi-http `ErrorCode` in the context of a request.
84pub fn hyper_request_error(err: hyper::Error) -> ErrorCode {
85    // If there's a source, we might be able to extract a wasi-http error from it.
86    if let Some(cause) = err.source() {
87        if let Some(err) = cause.downcast_ref::<ErrorCode>() {
88            return err.clone();
89        }
90    }
91
92    tracing::warn!("hyper request error: {err:?}");
93
94    ErrorCode::HttpProtocolError
95}
96
97/// Translate a [`hyper::Error`] to a wasi-http `ErrorCode` in the context of a response.
98pub fn hyper_response_error(err: hyper::Error) -> ErrorCode {
99    if err.is_timeout() {
100        return ErrorCode::HttpResponseTimeout;
101    }
102
103    // If there's a source, we might be able to extract a wasi-http error from it.
104    if let Some(cause) = err.source() {
105        if let Some(err) = cause.downcast_ref::<ErrorCode>() {
106            return err.clone();
107        }
108    }
109
110    tracing::warn!("hyper response error: {err:?}");
111
112    ErrorCode::HttpProtocolError
113}