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-01-11 20:02:27 +01:00
2017-04-03 10:27:37 +02:00
use dapps ;
2017-01-11 20:02:27 +01:00
use dir ::default_data_path ;
2017-04-13 16:32:07 +02:00
use parity_rpc ::informant ::{ RpcStats , Middleware } ;
use parity_rpc ::{ self as rpc , HttpServerError , Metadata , Origin , DomainsValidation } ;
2017-01-11 20:02:27 +01:00
use helpers ::parity_ipc_path ;
2017-05-06 13:24:18 +02:00
use jsonrpc_core ::{ futures , MetaIoHandler } ;
2017-03-13 15:49:52 +01:00
use parity_reactor ::TokioRemote ;
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-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
}
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
}
}
}
#[ 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 {
2016-12-13 23:38:29 +01:00
let data_dir = default_data_path ( ) ;
2016-07-25 16:09:47 +02:00
IpcConfiguration {
enabled : true ,
2016-12-15 21:56:45 +01:00
socket_addr : parity_ipc_path ( & data_dir , " $BASE/jsonrpc.ipc " ) ,
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-04-13 16:32:07 +02:00
#[ derive(Debug, PartialEq) ]
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 > > ,
}
impl Default for WsConfiguration {
fn default ( ) -> Self {
WsConfiguration {
enabled : true ,
interface : " 127.0.0.1 " . into ( ) ,
port : 8546 ,
apis : ApiSet ::UnsafeContext ,
origins : Some ( Vec ::new ( ) ) ,
hosts : Some ( Vec ::new ( ) ) ,
2016-06-13 18:55:24 +02:00
}
}
}
2017-03-22 20:14:40 +01:00
pub struct Dependencies < D : rpc_apis ::Dependencies > {
pub apis : Arc < D > ,
2017-03-13 15:49:52 +01:00
pub remote : TokioRemote ,
2017-02-04 22:18:19 +01:00
pub stats : Arc < RpcStats > ,
2016-04-21 13:57:27 +02:00
}
2017-02-14 22:45:43 +01:00
pub struct RpcExtractor ;
2017-04-03 10:27:37 +02:00
impl rpc ::HttpMetaExtractor for RpcExtractor {
type Metadata = Metadata ;
fn read_metadata ( & self , origin : String , dapps_origin : Option < String > ) -> Metadata {
2017-02-14 22:45:43 +01:00
let mut metadata = Metadata ::default ( ) ;
2017-04-03 10:27:37 +02:00
metadata . origin = match ( origin . as_str ( ) , dapps_origin ) {
( " null " , Some ( dapp ) ) = > Origin ::Dapps ( dapp . into ( ) ) ,
_ = > Origin ::Rpc ( origin ) ,
} ;
2017-02-14 22:45:43 +01:00
metadata
}
}
2017-03-16 12:48:51 +01:00
impl rpc ::IpcMetaExtractor < Metadata > for RpcExtractor {
fn extract ( & self , _req : & rpc ::IpcRequestContext ) -> Metadata {
let mut metadata = Metadata ::default ( ) ;
// TODO [ToDr] Extract proper session id when it's available in context.
metadata . origin = Origin ::Ipc ( 1. into ( ) ) ;
metadata
}
}
2017-05-06 13:24:18 +02:00
struct Sender ( rpc ::ws ::ws ::Sender , futures ::sync ::mpsc ::Receiver < String > ) ;
impl futures ::Future for Sender {
type Item = ( ) ;
type Error = ( ) ;
fn poll ( & mut self ) -> futures ::Poll < Self ::Item , Self ::Error > {
use self ::futures ::Stream ;
let item = self . 1. poll ( ) ? ;
match item {
futures ::Async ::NotReady = > {
Ok ( futures ::Async ::NotReady )
} ,
futures ::Async ::Ready ( None ) = > {
Ok ( futures ::Async ::Ready ( ( ) ) )
} ,
futures ::Async ::Ready ( Some ( val ) ) = > {
if let Err ( e ) = self . 0. send ( val ) {
warn! ( " Error sending a subscription update: {:?} " , e ) ;
}
self . poll ( )
} ,
}
}
}
struct WsRpcExtractor {
remote : TokioRemote ,
}
impl WsRpcExtractor {
fn wrap_out ( & self , out : rpc ::ws ::ws ::Sender ) -> futures ::sync ::mpsc ::Sender < String > {
let ( sender , receiver ) = futures ::sync ::mpsc ::channel ( 8 ) ;
self . remote . spawn ( move | _ | Sender ( out , receiver ) ) ;
sender
}
}
impl rpc ::ws ::MetaExtractor < Metadata > for WsRpcExtractor {
2017-04-13 16:32:07 +02:00
fn extract ( & self , req : & rpc ::ws ::RequestContext ) -> Metadata {
let mut metadata = Metadata ::default ( ) ;
let id = req . session_id as u64 ;
metadata . origin = Origin ::Ws ( id . into ( ) ) ;
2017-05-06 13:24:18 +02:00
metadata . session = Some ( Arc ::new ( rpc ::PubSubSession ::new (
self . wrap_out ( req . out . clone ( ) )
) ) ) ;
2017-04-13 16:32:07 +02:00
metadata
}
}
struct WsStats {
stats : Arc < RpcStats > ,
}
impl rpc ::ws ::SessionStats for WsStats {
fn open_session ( & self , _id : rpc ::ws ::SessionId ) {
self . stats . open_session ( )
}
fn close_session ( & self , _id : rpc ::ws ::SessionId ) {
self . stats . close_session ( )
}
}
2017-04-03 12:44:52 +02:00
fn setup_apis < D > ( apis : ApiSet , deps : & Dependencies < D > ) -> MetaIoHandler < Metadata , Middleware < D ::Notifier > >
2017-03-22 20:14:40 +01:00
where D : rpc_apis ::Dependencies
{
2017-04-03 12:44:52 +02:00
rpc_apis ::setup_rpc ( deps . stats . clone ( ) , & * deps . apis , apis )
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 ) ;
}
let url = format! ( " {} : {} " , conf . interface , conf . port ) ;
let addr = url . parse ( ) . map_err ( | _ | format! ( " Invalid WebSockets listen host/port given: {} " , url ) ) ? ;
let handler = setup_apis ( conf . apis , deps ) ;
let remote = deps . remote . clone ( ) ;
let allowed_origins = into_domains ( conf . origins ) ;
let allowed_hosts = into_domains ( conf . hosts ) ;
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-06 13:24:18 +02:00
WsRpcExtractor {
remote : remote ,
} ,
2017-04-13 16:32:07 +02:00
WsStats {
stats : deps . stats . clone ( ) ,
} ,
) ;
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 > (
conf : HttpConfiguration ,
deps : & Dependencies < D > ,
middleware : Option < dapps ::Middleware >
) -> 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
}
2016-06-24 12:10:36 +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 HTTP JSON-RPC listen host/port given: {} " , 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 ) ;
let allowed_hosts = into_domains ( conf . hosts ) ;
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 ,
RpcExtractor ,
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-04-13 16:32:07 +02:00
Err ( HttpServerError ::Io ( ref err ) ) if err . kind ( ) = = io ::ErrorKind ::AddrInUse = > Err (
format! ( " HTTP address {} is already in use, make sure that another instance of an Ethereum client is not running or change the address using the --jsonrpc-port and --jsonrpc-interface options. " , url )
) ,
Err ( e ) = > Err ( format! ( " HTTP error: {:?} " , e ) ) ,
2016-04-21 13:12:43 +02:00
}
}
2016-06-13 18:55:24 +02:00
2017-04-13 16:32: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 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-06 13:24:18 +02:00
let ipc = rpc ::start_ipc (
& conf . socket_addr ,
handler ,
remote ,
RpcExtractor ,
) ;
match ipc {
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
}
}
#[ cfg(test) ]
mod tests {
use super ::RpcExtractor ;
2017-04-13 16:32:07 +02:00
use parity_rpc ::{ HttpMetaExtractor , Origin } ;
2017-04-03 10:27:37 +02:00
#[ test ]
fn should_extract_rpc_origin ( ) {
// given
let extractor = RpcExtractor ;
// when
let meta = extractor . read_metadata ( " http://parity.io " . into ( ) , None ) ;
let meta1 = extractor . read_metadata ( " http://parity.io " . into ( ) , Some ( " ignored " . into ( ) ) ) ;
// then
assert_eq! ( meta . origin , Origin ::Rpc ( " http://parity.io " . into ( ) ) ) ;
assert_eq! ( meta1 . origin , Origin ::Rpc ( " http://parity.io " . into ( ) ) ) ;
}
#[ test ]
fn should_dapps_origin ( ) {
// given
let extractor = RpcExtractor ;
let dapp = " https://wallet.ethereum.org " . to_owned ( ) ;
// when
let meta = extractor . read_metadata ( " null " . into ( ) , Some ( dapp . clone ( ) ) ) ;
// then
assert_eq! ( meta . origin , Origin ::Dapps ( dapp . into ( ) ) ) ;
2016-05-04 15:37:09 +02:00
}
}