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 ;
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-04-03 10:27:37 +02:00
pub threads : Option < usize > ,
2016-04-21 13:57:27 +02:00
}
2017-05-24 12:24:07 +02:00
impl HttpConfiguration {
pub fn address ( & self ) -> Option < ( String , u16 ) > {
match self . enabled {
true = > Some ( ( self . interface . clone ( ) , self . port ) ) ,
false = > None ,
}
}
}
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-04-03 10:27:37 +02:00
threads : None ,
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 {
pub fn address ( & self ) -> Option < ( String , u16 ) > {
match self . enabled {
true = > Some ( ( self . interface . clone ( ) , self . port ) ) ,
false = > None ,
}
}
}
impl From < UiConfiguration > for HttpConfiguration {
fn from ( conf : UiConfiguration ) -> Self {
HttpConfiguration {
enabled : conf . enabled ,
interface : conf . interface ,
port : conf . port ,
apis : rpc_apis ::ApiSet ::SafeContext ,
cors : None ,
hosts : conf . hosts ,
threads : None ,
}
}
}
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 ,
pub ui_address : Option < ( String , u16 ) > ,
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-05-24 12:24:07 +02:00
origins : Some ( vec! [ " chrome-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 ( ) ,
ui_address : Some ( ( " 127.0.0.1 " . to_owned ( ) , 8180 ) ) ,
2016-06-13 18:55:24 +02:00
}
}
}
2017-04-13 16:32:07 +02:00
2017-05-24 12:24:07 +02:00
impl WsConfiguration {
pub fn address ( & self ) -> Option < ( String , u16 ) > {
match self . enabled {
true = > Some ( ( self . interface . clone ( ) , self . port ) ) ,
false = > None ,
}
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-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 ;
let ws_address = ( conf . interface , conf . port ) ;
let url = format! ( " {} : {} " , ws_address . 0 , ws_address . 1 ) ;
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
let full_handler = setup_apis ( rpc_apis ::ApiSet ::SafeContext , deps ) ;
let handler = {
let mut handler = MetaIoHandler ::with_middleware ( (
rpc ::WsDispatcher ::new ( full_handler ) ,
Middleware ::new ( deps . stats . clone ( ) , deps . apis . activity_notifier ( ) )
) ) ;
let apis = conf . apis . list_apis ( ) . into_iter ( ) . collect ::< Vec < _ > > ( ) ;
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 ( ) ;
let allowed_origins = into_domains ( with_domain ( conf . origins , domain , & [ ui_address ] ) ) ;
let allowed_hosts = into_domains ( with_domain ( conf . hosts , domain , & [ Some ( ws_address ) ] ) ) ;
2017-04-13 16:32:07 +02:00
2017-05-24 12:24:07 +02:00
let signer_path = conf . signer_path ;
let signer_path = conf . ui_address . map ( move | _ | ::signer ::codes_path ( & signer_path ) ) ;
let path = signer_path . as_ref ( ) . map ( | p | p . as_path ( ) ) ;
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 ) ) ,
Err ( rpc ::ws ::Error ::Io ( ref err ) ) if err . kind ( ) = = io ::ErrorKind ::AddrInUse = > Err (
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 ;
let http_address = ( conf . interface , conf . port ) ;
let url = format! ( " {} : {} " , http_address . 0 , http_address . 1 ) ;
let addr = url . parse ( ) . map_err ( | _ | format! ( " Invalid {} listen host/port given: {} " , id , url ) ) ? ;
2017-04-03 10:27:37 +02:00
let handler = setup_apis ( conf . apis , deps ) ;
let remote = deps . remote . clone ( ) ;
2017-04-13 16:32:07 +02:00
let cors_domains = into_domains ( conf . cors ) ;
2017-05-24 12:24:07 +02:00
let allowed_hosts = into_domains ( with_domain ( conf . hosts , domain , & [ Some ( http_address ) ] ) ) ;
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-04-03 10:27:37 +02:00
match ( conf . threads , middleware ) {
( Some ( threads ) , None ) = > rpc ::HttpSettings ::Threads ( threads ) ,
( None , middleware ) = > rpc ::HttpSettings ::Dapps ( middleware ) ,
( Some ( _ ) , Some ( _ ) ) = > {
return Err ( " Dapps and fast multi-threaded RPC server cannot be enabled at the same time. " . into ( ) )
} ,
}
) ;
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-05-24 12:24:07 +02:00
Err ( rpc ::HttpServerError ::Io ( ref err ) ) if err . kind ( ) = = io ::ErrorKind ::AddrInUse = > Err (
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-04-03 10:27:37 +02:00
let handler = setup_apis ( conf . apis , dependencies ) ;
2017-03-13 15:49:52 +01:00
let remote = dependencies . remote . clone ( ) ;
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-05-24 12:24:07 +02:00
fn with_domain ( items : Option < Vec < String > > , domain : & str , addresses : & [ Option < ( String , u16 ) > ] ) -> Option < Vec < String > > {
items . map ( move | items | {
let mut items = items . into_iter ( ) . collect ::< HashSet < _ > > ( ) ;
for address in addresses {
if let Some ( ( host , port ) ) = address . clone ( ) {
items . insert ( format! ( " {} : {} " , host , port ) ) ;
items . insert ( format! ( " {} : {} " , host . replace ( " 127.0.0.1 " , " localhost " ) , port ) ) ;
items . insert ( format! ( " http://*. {} : {} " , domain , port ) ) ;
items . insert ( format! ( " http://*. {} " , domain ) ) ; //proxypac
}
}
items . into_iter ( ) . collect ( )
} )
}
2017-04-03 10:27:37 +02:00
2017-05-24 12:24:07 +02:00
fn setup_apis < D > ( apis : ApiSet , deps : & Dependencies < D > ) -> MetaIoHandler < Metadata , Middleware < D ::Notifier > >
where D : rpc_apis ::Dependencies
{
let mut handler = MetaIoHandler ::with_middleware (
Middleware ::new ( deps . stats . clone ( ) , deps . apis . activity_notifier ( ) )
) ;
let apis = apis . list_apis ( ) . into_iter ( ) . collect ::< Vec < _ > > ( ) ;
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
}