reth_rpc_builder/
error.rs

1use crate::{cors::CorsDomainError, RethRpcModule};
2use reth_ipc::server::IpcServerStartError;
3use std::{
4    collections::HashSet,
5    io::{self, ErrorKind},
6    net::SocketAddr,
7};
8
9/// Rpc server kind.
10#[derive(Debug, PartialEq, Eq, Copy, Clone)]
11pub enum ServerKind {
12    /// Http.
13    Http(SocketAddr),
14    /// Websocket.
15    WS(SocketAddr),
16    /// WS and http on the same port
17    WsHttp(SocketAddr),
18    /// Auth.
19    Auth(SocketAddr),
20}
21
22impl ServerKind {
23    /// Returns the appropriate flags for each variant.
24    pub const fn flags(&self) -> &'static str {
25        match self {
26            Self::Http(_) => "--http.port",
27            Self::WS(_) => "--ws.port",
28            Self::WsHttp(_) => "--ws.port and --http.port",
29            Self::Auth(_) => "--authrpc.port",
30        }
31    }
32}
33
34impl std::fmt::Display for ServerKind {
35    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36        match self {
37            Self::Http(addr) => write!(f, "{addr} (HTTP-RPC server)"),
38            Self::WS(addr) => write!(f, "{addr} (WS-RPC server)"),
39            Self::WsHttp(addr) => write!(f, "{addr} (WS-HTTP-RPC server)"),
40            Self::Auth(addr) => write!(f, "{addr} (AUTH server)"),
41        }
42    }
43}
44
45/// Rpc Server related errors
46#[derive(Debug, thiserror::Error)]
47pub enum RpcError {
48    /// Thrown during server start.
49    #[error("Failed to start {kind} server: {error}")]
50    ServerError {
51        /// Server kind.
52        kind: ServerKind,
53        /// IO error.
54        error: io::Error,
55    },
56    /// Address already in use.
57    #[error("address {kind} is already in use (os error 98). Choose a different port using {}", kind.flags())]
58    AddressAlreadyInUse {
59        /// Server kind.
60        kind: ServerKind,
61        /// IO error.
62        error: io::Error,
63    },
64    /// Cors parsing error.
65    #[error(transparent)]
66    Cors(#[from] CorsDomainError),
67    /// Http and WS server configured on the same port but with conflicting settings.
68    #[error(transparent)]
69    WsHttpSamePortError(#[from] WsHttpSamePortError),
70    /// Thrown when IPC server fails to start.
71    #[error(transparent)]
72    IpcServerError(#[from] IpcServerStartError),
73    /// Custom error.
74    #[error("{0}")]
75    Custom(String),
76}
77
78impl RpcError {
79    /// Converts an [`io::Error`] to a more descriptive `RpcError`.
80    pub fn server_error(io_error: io::Error, kind: ServerKind) -> Self {
81        if io_error.kind() == ErrorKind::AddrInUse {
82            return Self::AddressAlreadyInUse { kind, error: io_error }
83        }
84        Self::ServerError { kind, error: io_error }
85    }
86}
87
88/// Conflicting modules between http and ws servers.
89#[derive(Debug)]
90pub struct ConflictingModules {
91    /// Modules present in both http and ws.
92    pub overlap: HashSet<RethRpcModule>,
93    /// Modules present in http but not in ws.
94    pub http_not_ws: HashSet<RethRpcModule>,
95    /// Modules present in ws but not in http.
96    pub ws_not_http: HashSet<RethRpcModule>,
97}
98
99impl std::fmt::Display for ConflictingModules {
100    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101        write!(
102            f,
103            "different API modules for HTTP and WS on the same port is currently not supported: \
104            Overlap: {:?}, \
105            HTTP modules not present in WS: {:?} \
106            WS modules not present in HTTP: {:?}
107            ",
108            self.overlap, self.http_not_ws, self.ws_not_http
109        )
110    }
111}
112
113/// Errors when trying to launch ws and http server on the same port.
114#[derive(Debug, thiserror::Error)]
115pub enum WsHttpSamePortError {
116    /// Ws and http server configured on same port but with different cors domains.
117    #[error(
118        "CORS domains for HTTP and WS are different, but they are on the same port: \
119         HTTP: {http_cors_domains:?}, WS: {ws_cors_domains:?}"
120    )]
121    ConflictingCorsDomains {
122        /// Http cors domains.
123        http_cors_domains: Option<String>,
124        /// Ws cors domains.
125        ws_cors_domains: Option<String>,
126    },
127    /// Ws and http server configured on same port but with different modules.
128    #[error("{0}")]
129    ConflictingModules(Box<ConflictingModules>),
130}
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135    use std::net::{Ipv4Addr, SocketAddrV4};
136    #[test]
137    fn test_address_in_use_message() {
138        let addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 1234));
139        let kinds = [
140            ServerKind::Http(addr),
141            ServerKind::WS(addr),
142            ServerKind::WsHttp(addr),
143            ServerKind::Auth(addr),
144        ];
145
146        for kind in &kinds {
147            let err = RpcError::AddressAlreadyInUse {
148                kind: *kind,
149                error: io::Error::from(ErrorKind::AddrInUse),
150            };
151
152            assert!(err.to_string().contains(kind.flags()));
153        }
154    }
155}