2019-01-07 11:33:07 +01:00
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
// This file is part of Parity Ethereum.
2016-04-21 13:12:43 +02:00
2019-01-07 11:33:07 +01:00
// Parity Ethereum is free software: you can redistribute it and/or modify
2016-04-21 13:12:43 +02:00
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
2019-01-07 11:33:07 +01:00
// Parity Ethereum is distributed in the hope that it will be useful,
2016-04-21 13:12:43 +02:00
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
2019-01-07 11:33:07 +01:00
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
2016-04-21 13:12:43 +02:00
2017-04-13 16:32:07 +02:00
use std ::io ;
2016-04-21 13:12:43 +02:00
use std ::sync ::Arc ;
2017-05-24 12:24:07 +02:00
use std ::path ::PathBuf ;
use std ::collections ::HashSet ;
2017-01-11 20:02:27 +01:00
2017-05-24 12:24:07 +02:00
use dir ::default_data_path ;
2017-12-24 09:34:43 +01:00
use dir ::helpers ::replace_home ;
use helpers ::parity_ipc_path ;
2017-05-23 12:26:39 +02:00
use jsonrpc_core ::MetaIoHandler ;
2018-10-22 09:40:50 +02:00
use parity_runtime ::Executor ;
2017-05-24 12:24:07 +02:00
use parity_rpc ::informant ::{ RpcStats , Middleware } ;
use parity_rpc ::{ self as rpc , Metadata , DomainsValidation } ;
2017-04-03 10:27:37 +02:00
use rpc_apis ::{ self , ApiSet } ;
2016-04-21 13:12:43 +02:00
2017-04-13 16:32:07 +02:00
pub use parity_rpc ::{ IpcServer , HttpServer , RequestMiddleware } ;
2019-07-05 10:24:24 +02:00
pub use parity_rpc ::ws ::{ Server as WsServer , ws } ;
2016-04-21 13:12:43 +02:00
2017-05-24 12:24:07 +02:00
pub const DAPPS_DOMAIN : & 'static str = " web3.site " ;
2017-04-03 10:27:37 +02:00
#[ derive(Debug, Clone, PartialEq) ]
2016-05-04 15:37:09 +02:00
pub struct HttpConfiguration {
2019-11-18 19:40:48 +01:00
/// Is RPC over HTTP enabled (default is true)?
2016-04-21 13:57:27 +02:00
pub enabled : bool ,
2019-11-18 19:40:48 +01:00
/// The IP of the network interface used (default is 127.0.0.1).
2016-04-21 13:57:27 +02:00
pub interface : String ,
2019-11-18 19:40:48 +01:00
/// The network port (default is 8545).
2016-04-21 13:57:27 +02:00
pub port : u16 ,
2019-11-18 19:40:48 +01:00
/// The categories of RPC calls enabled.
2016-07-25 16:09:47 +02:00
pub apis : ApiSet ,
2019-11-18 19:40:48 +01:00
/// CORS headers
2016-07-20 12:34:17 +02:00
pub cors : Option < Vec < String > > ,
2019-11-18 19:40:48 +01:00
/// Specify a list of valid hosts we accept requests from.
2016-07-20 12:34:17 +02:00
pub hosts : Option < Vec < String > > ,
2019-11-18 19:40:48 +01:00
/// Number of HTTP server threads to use to handle incoming requests (default is 4).
2017-10-05 12:35:01 +02:00
pub server_threads : usize ,
2019-11-18 19:40:48 +01:00
/// Sets the maximum size of a request body in megabytes (default is 5 MiB).
2018-07-02 10:23:57 +02:00
pub max_payload : usize ,
2019-11-18 19:40:48 +01:00
/// Use keepalive messages on the underlying socket: SO_KEEPALIVE as well as the TCP_KEEPALIVE
/// or TCP_KEEPIDLE options depending on your platform (default is true).
2018-11-05 15:39:51 +01:00
pub keep_alive : bool ,
2016-04-21 13:57:27 +02:00
}
2016-07-25 16:09:47 +02:00
impl Default for HttpConfiguration {
fn default ( ) -> Self {
HttpConfiguration {
enabled : true ,
interface : " 127.0.0.1 " . into ( ) ,
port : 8545 ,
apis : ApiSet ::UnsafeContext ,
2017-12-27 18:56:06 +01:00
cors : Some ( vec! [ ] ) ,
hosts : Some ( vec! [ ] ) ,
2019-11-18 19:40:48 +01:00
server_threads : 4 ,
2018-07-02 10:23:57 +02:00
max_payload : 5 ,
2018-11-05 15:39:51 +01:00
keep_alive : true ,
2016-07-25 16:09:47 +02:00
}
}
}
#[ derive(Debug, PartialEq) ]
2016-05-04 15:37:09 +02:00
pub struct IpcConfiguration {
pub enabled : bool ,
pub socket_addr : String ,
2016-07-25 16:09:47 +02:00
pub apis : ApiSet ,
}
impl Default for IpcConfiguration {
fn default ( ) -> Self {
IpcConfiguration {
enabled : true ,
2017-05-23 12:24:32 +02:00
socket_addr : if cfg! ( windows ) {
r "\\.\pipe\jsonrpc.ipc" . into ( )
} else {
let data_dir = ::dir ::default_data_path ( ) ;
parity_ipc_path ( & data_dir , " $BASE/jsonrpc.ipc " , 0 )
} ,
2016-11-04 09:58:39 +01:00
apis : ApiSet ::IpcContext ,
2016-07-25 16:09:47 +02:00
}
}
2016-05-04 15:37:09 +02:00
}
2017-05-24 12:24:07 +02:00
#[ derive(Debug, Clone, PartialEq) ]
2017-04-13 16:32:07 +02:00
pub struct WsConfiguration {
pub enabled : bool ,
pub interface : String ,
pub port : u16 ,
pub apis : ApiSet ,
2018-04-02 12:33:09 +02:00
pub max_connections : usize ,
2017-04-13 16:32:07 +02:00
pub origins : Option < Vec < String > > ,
pub hosts : Option < Vec < String > > ,
2017-05-24 12:24:07 +02:00
pub signer_path : PathBuf ,
2017-06-12 15:57:16 +02:00
pub support_token_api : bool ,
2017-04-13 16:32:07 +02:00
}
impl Default for WsConfiguration {
fn default ( ) -> Self {
2017-05-24 12:24:07 +02:00
let data_dir = default_data_path ( ) ;
2017-04-13 16:32:07 +02:00
WsConfiguration {
enabled : true ,
interface : " 127.0.0.1 " . into ( ) ,
port : 8546 ,
apis : ApiSet ::UnsafeContext ,
2018-04-02 12:33:09 +02:00
max_connections : 100 ,
2018-03-02 10:26:49 +01:00
origins : Some ( vec! [ " parity://* " . into ( ) , " chrome-extension://* " . into ( ) , " moz-extension://* " . into ( ) ] ) ,
2017-04-13 16:32:07 +02:00
hosts : Some ( Vec ::new ( ) ) ,
2017-05-24 12:24:07 +02:00
signer_path : replace_home ( & data_dir , " $BASE/signer " ) . into ( ) ,
2019-12-05 09:38:14 +01:00
support_token_api : false ,
2016-06-13 18:55:24 +02:00
}
}
}
2017-05-24 12:24:07 +02:00
impl WsConfiguration {
2017-09-21 14:52:44 +02:00
pub fn address ( & self ) -> Option < rpc ::Host > {
address ( self . enabled , & self . interface , self . port , & self . hosts )
}
}
fn address ( enabled : bool , bind_iface : & str , bind_port : u16 , hosts : & Option < Vec < String > > ) -> Option < rpc ::Host > {
if ! enabled {
return None ;
}
match * hosts {
Some ( ref hosts ) if ! hosts . is_empty ( ) = > Some ( hosts [ 0 ] . clone ( ) . into ( ) ) ,
_ = > Some ( format! ( " {} : {} " , bind_iface , bind_port ) . into ( ) ) ,
2017-04-13 16:32:07 +02:00
}
}
2017-05-24 12:24:07 +02:00
pub struct Dependencies < D : rpc_apis ::Dependencies > {
pub apis : Arc < D > ,
2018-10-22 09:40:50 +02:00
pub executor : Executor ,
2017-05-24 12:24:07 +02:00
pub stats : Arc < RpcStats > ,
2017-04-03 10:27:37 +02:00
}
2017-04-13 16:32:07 +02:00
pub fn new_ws < D : rpc_apis ::Dependencies > (
conf : WsConfiguration ,
deps : & Dependencies < D > ,
) -> Result < Option < WsServer > , String > {
if ! conf . enabled {
return Ok ( None ) ;
}
2017-05-24 12:24:07 +02:00
let domain = DAPPS_DOMAIN ;
2017-09-21 14:52:44 +02:00
let url = format! ( " {} : {} " , conf . interface , conf . port ) ;
2017-04-13 16:32:07 +02:00
let addr = url . parse ( ) . map_err ( | _ | format! ( " Invalid WebSockets listen host/port given: {} " , url ) ) ? ;
2017-05-24 12:24:07 +02:00
2019-02-08 14:11:55 +01:00
let full_handler = setup_apis ( rpc_apis ::ApiSet ::All , deps ) ;
2017-05-24 12:24:07 +02:00
let handler = {
let mut handler = MetaIoHandler ::with_middleware ( (
rpc ::WsDispatcher ::new ( full_handler ) ,
2018-10-22 09:40:50 +02:00
Middleware ::new ( deps . stats . clone ( ) , deps . apis . activity_notifier ( ) )
2017-05-24 12:24:07 +02:00
) ) ;
2017-07-10 17:42:29 +02:00
let apis = conf . apis . list_apis ( ) ;
2017-05-24 12:24:07 +02:00
deps . apis . extend_with_set ( & mut handler , & apis ) ;
handler
} ;
2018-07-11 12:19:54 +02:00
let allowed_origins = into_domains ( with_domain ( conf . origins , domain , & None ) ) ;
2018-06-06 10:05:52 +02:00
let allowed_hosts = into_domains ( with_domain ( conf . hosts , domain , & Some ( url . clone ( ) . into ( ) ) ) ) ;
2017-04-13 16:32:07 +02:00
2017-06-12 15:57:16 +02:00
let signer_path ;
2018-03-20 18:57:37 +01:00
let path = match conf . support_token_api {
2017-06-12 15:57:16 +02:00
true = > {
signer_path = ::signer ::codes_path ( & conf . signer_path ) ;
Some ( signer_path . as_path ( ) )
} ,
false = > None
} ;
2017-04-13 16:32:07 +02:00
let start_result = rpc ::start_ws (
& addr ,
handler ,
allowed_origins ,
allowed_hosts ,
2018-04-02 12:33:09 +02:00
conf . max_connections ,
2017-05-24 12:24:07 +02:00
rpc ::WsExtractor ::new ( path . clone ( ) ) ,
rpc ::WsExtractor ::new ( path . clone ( ) ) ,
rpc ::WsStats ::new ( deps . stats . clone ( ) ) ,
2017-04-13 16:32:07 +02:00
) ;
match start_result {
Ok ( server ) = > Ok ( Some ( server ) ) ,
2019-07-05 10:24:24 +02:00
Err ( rpc ::ws ::Error ::WsError ( ws ::Error {
kind : ws ::ErrorKind ::Io ( ref err ) , ..
} ) ) if err . kind ( ) = = io ::ErrorKind ::AddrInUse = > Err (
2017-04-13 16:32:07 +02:00
format! ( " WebSockets address {} is already in use, make sure that another instance of an Ethereum client is not running or change the address using the --ws-port and --ws-interface options. " , url )
) ,
Err ( e ) = > Err ( format! ( " WebSockets error: {:?} " , e ) ) ,
}
}
2017-04-03 12:44:52 +02:00
pub fn new_http < D : rpc_apis ::Dependencies > (
2017-05-24 12:24:07 +02:00
id : & str ,
options : & str ,
2017-04-03 12:44:52 +02:00
conf : HttpConfiguration ,
deps : & Dependencies < D > ,
) -> Result < Option < HttpServer > , String > {
2016-04-21 13:57:27 +02:00
if ! conf . enabled {
2016-07-25 16:09:47 +02:00
return Ok ( None ) ;
2016-04-21 13:57:27 +02:00
}
2017-05-24 12:24:07 +02:00
let domain = DAPPS_DOMAIN ;
2017-09-21 14:52:44 +02:00
let url = format! ( " {} : {} " , conf . interface , conf . port ) ;
2017-05-24 12:24:07 +02:00
let addr = url . parse ( ) . map_err ( | _ | format! ( " Invalid {} listen host/port given: {} " , id , url ) ) ? ;
2017-09-21 14:52:44 +02:00
let handler = setup_apis ( conf . apis , deps ) ;
2017-04-03 10:27:37 +02:00
2017-04-13 16:32:07 +02:00
let cors_domains = into_domains ( conf . cors ) ;
2018-06-06 10:05:52 +02:00
let allowed_hosts = into_domains ( with_domain ( conf . hosts , domain , & Some ( url . clone ( ) . into ( ) ) ) ) ;
2017-04-03 10:27:37 +02:00
let start_result = rpc ::start_http (
& addr ,
2017-04-13 16:32:07 +02:00
cors_domains ,
allowed_hosts ,
2017-04-03 10:27:37 +02:00
handler ,
2017-05-24 12:24:07 +02:00
rpc ::RpcExtractor ,
2017-10-05 12:35:01 +02:00
conf . server_threads ,
2018-07-02 10:23:57 +02:00
conf . max_payload ,
2018-11-05 15:39:51 +01:00
conf . keep_alive ,
2017-04-03 10:27:37 +02:00
) ;
2016-05-04 15:37:09 +02:00
2016-04-21 13:12:43 +02:00
match start_result {
2017-04-03 10:27:37 +02:00
Ok ( server ) = > Ok ( Some ( server ) ) ,
2017-11-14 11:38:17 +01:00
Err ( ref err ) if err . kind ( ) = = io ::ErrorKind ::AddrInUse = > Err (
2017-05-24 12:24:07 +02:00
format! ( " {} address {} is already in use, make sure that another instance of an Ethereum client is not running or change the address using the -- {} -port and -- {} -interface options. " , id , url , options , options )
2017-04-13 16:32:07 +02:00
) ,
2017-05-24 12:24:07 +02:00
Err ( e ) = > Err ( format! ( " {} error: {:?} " , id , e ) ) ,
2016-04-21 13:12:43 +02:00
}
}
2016-06-13 18:55:24 +02:00
2017-04-03 12:44:52 +02:00
pub fn new_ipc < D : rpc_apis ::Dependencies > (
conf : IpcConfiguration ,
dependencies : & Dependencies < D >
) -> Result < Option < IpcServer > , String > {
2017-04-03 10:27:37 +02:00
if ! conf . enabled {
return Ok ( None ) ;
}
2017-04-13 16:32:07 +02:00
2017-09-21 14:52:44 +02:00
let handler = setup_apis ( conf . apis , dependencies ) ;
2017-10-24 12:25:34 +02:00
let path = PathBuf ::from ( & conf . socket_addr ) ;
2017-10-30 08:05:44 +01:00
// Make sure socket file can be created on unix-like OS.
// Windows pipe paths are not on the FS.
2017-10-29 13:35:12 +01:00
if ! cfg! ( windows ) {
if let Some ( dir ) = path . parent ( ) {
::std ::fs ::create_dir_all ( & dir )
. map_err ( | err | format! ( " Unable to create IPC directory at {} : {} " , dir . display ( ) , err ) ) ? ;
}
2017-10-24 12:25:34 +02:00
}
2018-10-22 09:40:50 +02:00
match rpc ::start_ipc ( & conf . socket_addr , handler , rpc ::RpcExtractor ) {
2017-04-03 10:27:37 +02:00
Ok ( server ) = > Ok ( Some ( server ) ) ,
2017-04-13 16:32:07 +02:00
Err ( io_error ) = > Err ( format! ( " IPC error: {} " , io_error ) ) ,
2017-04-03 10:27:37 +02:00
}
}
2017-05-24 12:24:07 +02:00
fn into_domains < T : From < String > > ( items : Option < Vec < String > > ) -> DomainsValidation < T > {
items . map ( | vals | vals . into_iter ( ) . map ( T ::from ) . collect ( ) ) . into ( )
}
2017-04-03 10:27:37 +02:00
2018-06-06 10:05:52 +02:00
fn with_domain ( items : Option < Vec < String > > , domain : & str , dapps_address : & Option < rpc ::Host > ) -> Option < Vec < String > > {
2017-09-21 14:52:44 +02:00
fn extract_port ( s : & str ) -> Option < u16 > {
s . split ( ':' ) . nth ( 1 ) . and_then ( | s | s . parse ( ) . ok ( ) )
}
2017-05-24 12:24:07 +02:00
items . map ( move | items | {
let mut items = items . into_iter ( ) . collect ::< HashSet < _ > > ( ) ;
2017-10-18 16:28:45 +02:00
{
let mut add_hosts = | address : & Option < rpc ::Host > | {
if let Some ( host ) = address . clone ( ) {
items . insert ( host . to_string ( ) ) ;
items . insert ( host . replace ( " 127.0.0.1 " , " localhost " ) ) ;
items . insert ( format! ( " http://*. {} " , domain ) ) ; //proxypac
if let Some ( port ) = extract_port ( & * host ) {
items . insert ( format! ( " http://*. {} : {} " , domain , port ) ) ;
}
}
} ;
add_hosts ( dapps_address ) ;
2017-05-24 12:24:07 +02:00
}
items . into_iter ( ) . collect ( )
} )
}
2017-04-03 10:27:37 +02:00
2018-05-22 19:07:27 +02:00
pub fn setup_apis < D > ( apis : ApiSet , deps : & Dependencies < D > ) -> MetaIoHandler < Metadata , Middleware < D ::Notifier > >
2017-05-24 12:24:07 +02:00
where D : rpc_apis ::Dependencies
{
let mut handler = MetaIoHandler ::with_middleware (
2018-10-22 09:40:50 +02:00
Middleware ::new ( deps . stats . clone ( ) , deps . apis . activity_notifier ( ) )
2017-05-24 12:24:07 +02:00
) ;
2017-07-10 17:42:29 +02:00
let apis = apis . list_apis ( ) ;
2017-05-24 12:24:07 +02:00
deps . apis . extend_with_set ( & mut handler , & apis ) ;
2017-04-03 10:27:37 +02:00
2017-05-24 12:24:07 +02:00
handler
2016-05-04 15:37:09 +02:00
}
2017-09-21 14:52:44 +02:00
#[ cfg(test) ]
mod tests {
use super ::address ;
#[ test ]
fn should_return_proper_address ( ) {
assert_eq! ( address ( false , " localhost " , 8180 , & None ) , None ) ;
assert_eq! ( address ( true , " localhost " , 8180 , & None ) , Some ( " localhost:8180 " . into ( ) ) ) ;
assert_eq! ( address ( true , " localhost " , 8180 , & Some ( vec! [ " host:443 " . into ( ) ] ) ) , Some ( " host:443 " . into ( ) ) ) ;
assert_eq! ( address ( true , " localhost " , 8180 , & Some ( vec! [ " host " . into ( ) ] ) ) , Some ( " host " . into ( ) ) ) ;
}
}