2017-01-25 18:51:41 +01:00
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
2016-04-21 13:12:43 +02:00
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// 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.
// Parity is distributed in the hope that it will be useful,
// 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
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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-04-03 10:27:37 +02:00
use dapps ;
2017-05-24 12:24:07 +02:00
use dir ::default_data_path ;
use helpers ::{ parity_ipc_path , replace_home } ;
2017-05-23 12:26:39 +02:00
use jsonrpc_core ::MetaIoHandler ;
2017-03-13 15:49:52 +01:00
use parity_reactor ::TokioRemote ;
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 } ;
pub use parity_rpc ::ws ::Server as WsServer ;
2017-07-11 12:22:19 +02:00
pub use parity_rpc ::informant ::CpuPool ;
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 {
2016-04-21 13:57:27 +02:00
pub enabled : bool ,
pub interface : String ,
pub port : u16 ,
2016-07-25 16:09:47 +02:00
pub apis : ApiSet ,
2016-07-20 12:34:17 +02:00
pub cors : Option < Vec < String > > ,
pub hosts : Option < Vec < String > > ,
2017-10-05 12:35:01 +02:00
pub server_threads : usize ,
2017-07-11 12:22:19 +02:00
pub processing_threads : usize ,
2016-04-21 13:57:27 +02:00
}
2017-05-24 12:24:07 +02:00
impl HttpConfiguration {
2017-09-21 14:52:44 +02:00
pub fn address ( & self ) -> Option < rpc ::Host > {
address ( self . enabled , & self . interface , self . port , & self . hosts )
2017-05-24 12:24:07 +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 ,
cors : None ,
hosts : Some ( Vec ::new ( ) ) ,
2017-10-05 12:35:01 +02:00
server_threads : 1 ,
2017-11-01 11:23:18 +01:00
processing_threads : 4 ,
2016-07-25 16:09:47 +02:00
}
}
}
2017-05-24 12:24:07 +02:00
#[ derive(Debug, PartialEq, Clone) ]
pub struct UiConfiguration {
pub enabled : bool ,
pub interface : String ,
pub port : u16 ,
pub hosts : Option < Vec < String > > ,
}
impl UiConfiguration {
2017-09-21 14:52:44 +02:00
pub fn address ( & self ) -> Option < rpc ::Host > {
address ( self . enabled , & self . interface , self . port , & self . hosts )
}
pub fn redirection_address ( & self ) -> Option < ( String , u16 ) > {
self . address ( ) . map ( | host | {
let mut it = host . split ( ':' ) ;
let hostname : Option < String > = it . next ( ) . map ( | s | s . to_owned ( ) ) ;
let port : Option < u16 > = it . next ( ) . and_then ( | s | s . parse ( ) . ok ( ) ) ;
( hostname . unwrap_or_else ( | | " localhost " . into ( ) ) , port . unwrap_or ( 8180 ) )
} )
2017-05-24 12:24:07 +02:00
}
}
impl From < UiConfiguration > for HttpConfiguration {
fn from ( conf : UiConfiguration ) -> Self {
HttpConfiguration {
enabled : conf . enabled ,
interface : conf . interface ,
port : conf . port ,
2017-08-18 15:58:45 +02:00
apis : rpc_apis ::ApiSet ::UnsafeContext ,
2017-05-24 12:24:07 +02:00
cors : None ,
hosts : conf . hosts ,
2017-10-05 12:35:01 +02:00
server_threads : 1 ,
2017-07-11 12:22:19 +02:00
processing_threads : 0 ,
2017-05-24 12:24:07 +02:00
}
}
}
impl Default for UiConfiguration {
fn default ( ) -> Self {
UiConfiguration {
2017-06-09 12:26:57 +02:00
enabled : true & & cfg! ( feature = " ui-enabled " ) ,
2017-05-24 12:24:07 +02:00
port : 8180 ,
interface : " 127.0.0.1 " . into ( ) ,
hosts : Some ( vec! [ ] ) ,
}
}
}
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 ,
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-09-21 14:52:44 +02:00
pub ui_address : Option < rpc ::Host > ,
2017-10-18 16:28:45 +02:00
pub dapps_address : Option < rpc ::Host > ,
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 ,
2017-08-13 17:41:50 +02:00
origins : Some ( vec! [ " 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 ( ) ,
2017-06-12 15:57:16 +02:00
support_token_api : true ,
2017-09-21 14:52:44 +02:00
ui_address : Some ( " 127.0.0.1:8180 " . into ( ) ) ,
2017-10-18 16:28:45 +02:00
dapps_address : Some ( " 127.0.0.1:8545 " . into ( ) ) ,
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 > ,
pub remote : TokioRemote ,
pub stats : Arc < RpcStats > ,
2017-07-11 12:22:19 +02:00
pub pool : Option < CpuPool > ,
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
2017-09-21 14:52:44 +02:00
let full_handler = setup_apis ( rpc_apis ::ApiSet ::SafeContext , deps ) ;
2017-05-24 12:24:07 +02:00
let handler = {
let mut handler = MetaIoHandler ::with_middleware ( (
rpc ::WsDispatcher ::new ( full_handler ) ,
2017-09-21 14:52:44 +02:00
Middleware ::new ( deps . stats . clone ( ) , deps . apis . activity_notifier ( ) , deps . pool . clone ( ) )
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
} ;
2017-04-13 16:32:07 +02:00
let remote = deps . remote . clone ( ) ;
2017-05-24 12:24:07 +02:00
let ui_address = conf . ui_address . clone ( ) ;
2017-10-18 16:28:45 +02:00
let allowed_origins = into_domains ( with_domain ( conf . origins , domain , & ui_address , & conf . dapps_address ) ) ;
let allowed_hosts = into_domains ( with_domain ( conf . hosts , domain , & Some ( url . clone ( ) . into ( ) ) , & None ) ) ;
2017-04-13 16:32:07 +02:00
2017-06-12 15:57:16 +02:00
let signer_path ;
let path = match conf . support_token_api & & conf . ui_address . is_some ( ) {
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 ,
2017-05-06 13:24:18 +02:00
remote . clone ( ) ,
2017-04-13 16:32:07 +02:00
allowed_origins ,
allowed_hosts ,
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 ) ) ,
2017-11-14 11:38:17 +01:00
Err ( rpc ::ws ::Error ( rpc ::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 > ,
2017-05-24 12:24:07 +02:00
middleware : Option < dapps ::Middleware > ,
2017-04-03 12:44:52 +02:00
) -> 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
let remote = deps . remote . clone ( ) ;
2017-04-13 16:32:07 +02:00
let cors_domains = into_domains ( conf . cors ) ;
2017-10-18 16:28:45 +02:00
let allowed_hosts = into_domains ( with_domain ( conf . hosts , domain , & Some ( url . clone ( ) . into ( ) ) , & None ) ) ;
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 ,
remote ,
2017-05-24 12:24:07 +02:00
rpc ::RpcExtractor ,
2017-10-05 12:35:01 +02:00
middleware ,
conf . server_threads ,
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-03-13 15:49:52 +01:00
let remote = dependencies . remote . clone ( ) ;
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
}
2017-05-24 12:24:07 +02:00
match rpc ::start_ipc ( & conf . socket_addr , handler , remote , 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
2017-10-18 16:28:45 +02:00
fn with_domain ( items : Option < Vec < String > > , domain : & str , ui_address : & Option < rpc ::Host > , 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 ( ui_address ) ;
add_hosts ( dapps_address ) ;
2017-05-24 12:24:07 +02:00
}
items . into_iter ( ) . collect ( )
} )
}
2017-04-03 10:27:37 +02:00
2017-09-21 14:52:44 +02:00
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 (
2017-09-21 14:52:44 +02:00
Middleware ::new ( deps . stats . clone ( ) , deps . apis . activity_notifier ( ) , deps . pool . clone ( ) )
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 ( ) ) ) ;
}
}