cfx_rpc_builder/
error.rs

1// Copyright 2023-2024 Paradigm.xyz
2// This file is part of reth.
3// Reth is a modular, contributor-friendly and blazing-fast implementation of
4// the Ethereum protocol
5
6// Permission is hereby granted, free of charge, to any
7// person obtaining a copy of this software and associated
8// documentation files (the "Software"), to deal in the
9// Software without restriction, including without
10// limitation the rights to use, copy, modify, merge,
11// publish, distribute, sublicense, and/or sell copies of
12// the Software, and to permit persons to whom the Software
13// is furnished to do so, subject to the following
14// conditions:
15
16// The above copyright notice and this permission notice
17// shall be included in all copies or substantial portions
18// of the Software.
19
20// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
21// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
22// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
23// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
24// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
25// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
27// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28// DEALINGS IN THE SOFTWARE.
29use crate::EthRpcModule;
30use std::{
31    collections::HashSet,
32    io::{self, ErrorKind},
33    net::SocketAddr,
34};
35
36/// Rpc server kind.
37#[derive(Debug, PartialEq, Eq, Copy, Clone)]
38pub enum ServerKind {
39    /// Http.
40    Http(SocketAddr),
41    /// Websocket.
42    WS(SocketAddr),
43    /// WS and http on the same port
44    WsHttp(SocketAddr),
45    /// Auth.
46    Auth(SocketAddr),
47}
48
49impl ServerKind {
50    /// Returns the appropriate flags for each variant.
51    pub const fn flags(&self) -> &'static str {
52        match self {
53            Self::Http(_) => "--http.port",
54            Self::WS(_) => "--ws.port",
55            Self::WsHttp(_) => "--ws.port and --http.port",
56            Self::Auth(_) => "--authrpc.port",
57        }
58    }
59}
60
61impl std::fmt::Display for ServerKind {
62    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63        match self {
64            Self::Http(addr) => write!(f, "{addr} (HTTP-RPC server)"),
65            Self::WS(addr) => write!(f, "{addr} (WS-RPC server)"),
66            Self::WsHttp(addr) => write!(f, "{addr} (WS-HTTP-RPC server)"),
67            Self::Auth(addr) => write!(f, "{addr} (AUTH server)"),
68        }
69    }
70}
71
72/// Rpc Server related errors
73#[derive(Debug, thiserror::Error)]
74pub enum RpcError {
75    /// Thrown during server start.
76    #[error("Failed to start {kind} server: {error}")]
77    ServerError {
78        /// Server kind.
79        kind: ServerKind,
80        /// IO error.
81        error: io::Error,
82    },
83    /// Address already in use.
84    #[error("address {kind} is already in use (os error 98). Choose a different port using {}", kind.flags())]
85    AddressAlreadyInUse {
86        /// Server kind.
87        kind: ServerKind,
88        /// IO error.
89        error: io::Error,
90    },
91    /// Cors parsing error.
92    // #[error(transparent)]
93    // Cors(#[from] CorsDomainError),
94    /// Http and WS server configured on the same port but with conflicting
95    /// settings.
96    #[error(transparent)]
97    WsHttpSamePortError(#[from] WsHttpSamePortError),
98    /// Thrown when IPC server fails to start.
99    // #[error(transparent)]
100    // IpcServerError(#[from] IpcServerStartError),
101    /// Custom error.
102    #[error("{0}")]
103    Custom(String),
104}
105
106impl RpcError {
107    /// Converts an [`io::Error`] to a more descriptive `RpcError`.
108    pub fn server_error(io_error: io::Error, kind: ServerKind) -> Self {
109        if io_error.kind() == ErrorKind::AddrInUse {
110            return Self::AddressAlreadyInUse {
111                kind,
112                error: io_error,
113            };
114        }
115        Self::ServerError {
116            kind,
117            error: io_error,
118        }
119    }
120}
121
122/// Conflicting modules between http and ws servers.
123#[derive(Debug)]
124pub struct ConflictingModules {
125    /// Modules present in both http and ws.
126    pub overlap: HashSet<EthRpcModule>,
127    /// Modules present in http but not in ws.
128    pub http_not_ws: HashSet<EthRpcModule>,
129    /// Modules present in ws but not in http.
130    pub ws_not_http: HashSet<EthRpcModule>,
131}
132
133impl std::fmt::Display for ConflictingModules {
134    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135        write!(
136            f,
137            "different API modules for HTTP and WS on the same port is currently not supported: \
138            Overlap: {:?}, \
139            HTTP modules not present in WS: {:?} \
140            WS modules not present in HTTP: {:?}
141            ",
142            self.overlap, self.http_not_ws, self.ws_not_http
143        )
144    }
145}
146
147/// Errors when trying to launch ws and http server on the same port.
148#[derive(Debug, thiserror::Error)]
149pub enum WsHttpSamePortError {
150    /// Ws and http server configured on same port but with different cors
151    /// domains.
152    #[error(
153        "CORS domains for HTTP and WS are different, but they are on the same port: \
154         HTTP: {http_cors_domains:?}, WS: {ws_cors_domains:?}"
155    )]
156    ConflictingCorsDomains {
157        /// Http cors domains.
158        http_cors_domains: Option<String>,
159        /// Ws cors domains.
160        ws_cors_domains: Option<String>,
161    },
162    /// Ws and http server configured on same port but with different modules.
163    #[error("{0}")]
164    ConflictingModules(Box<ConflictingModules>),
165}
166
167#[cfg(test)]
168mod tests {
169    use super::*;
170    use std::net::{Ipv4Addr, SocketAddrV4};
171    #[test]
172    fn test_address_in_use_message() {
173        let addr = SocketAddr::V4(SocketAddrV4::new(
174            Ipv4Addr::new(127, 0, 0, 1),
175            1234,
176        ));
177        let kinds = [
178            ServerKind::Http(addr),
179            ServerKind::WS(addr),
180            ServerKind::WsHttp(addr),
181            ServerKind::Auth(addr),
182        ];
183
184        for kind in &kinds {
185            let err = RpcError::AddressAlreadyInUse {
186                kind: *kind,
187                error: io::Error::from(ErrorKind::AddrInUse),
188            };
189
190            assert!(err.to_string().contains(kind.flags()));
191        }
192    }
193}