1use crate::eth::EthRpcModule;
30use cfx_rpc_middlewares::CorsDomainError;
31use std::{
32 collections::HashSet,
33 io::{self, ErrorKind},
34 net::SocketAddr,
35};
36
37#[derive(Debug, PartialEq, Eq, Copy, Clone)]
39pub enum ServerKind {
40 Http(SocketAddr),
42 WS(SocketAddr),
44 WsHttp(SocketAddr),
46 Auth(SocketAddr),
48}
49
50impl ServerKind {
51 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#[derive(Debug, thiserror::Error)]
75pub enum RpcError<T = EthRpcModule> {
76 #[error("Failed to start {kind} server: {error}")]
78 ServerError {
79 kind: ServerKind,
81 error: io::Error,
83 },
84 #[error("address {kind} is already in use (os error 98). Choose a different port using {}", kind.flags())]
86 AddressAlreadyInUse {
87 kind: ServerKind,
89 error: io::Error,
91 },
92 #[error(transparent)]
94 Cors(#[from] CorsDomainError),
95 #[error(transparent)]
98 WsHttpSamePortError(#[from] WsHttpSamePortError<T>),
99 #[error("{0}")]
104 Custom(String),
105}
106
107impl<T> RpcError<T> {
108 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#[derive(Debug)]
125pub struct ConflictingModules<T = EthRpcModule> {
126 pub overlap: HashSet<T>,
128 pub http_not_ws: HashSet<T>,
130 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#[derive(Debug, thiserror::Error)]
150pub enum WsHttpSamePortError<T = EthRpcModule> {
151 #[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: Option<String>,
160 ws_cors_domains: Option<String>,
162 },
163 #[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}