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