Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Commit

Permalink
New jsonrpc-core with futures and metadata support (#3859)
Browse files Browse the repository at this point in the history
* Bumping serde & serde_json

* Super-initial usage of new jsonrpc

* Single event loop for jsonrpc

* Metadata

* Supporting metadata extraction for eth_accounts

* Fixing Cargo.lock

* Removing uneccessary clones

* Fixing unused import

* Unused import

* Fixing test
  • Loading branch information
tomusdrw authored and gavofyork committed Jan 11, 2017
1 parent c4d96a6 commit 41da1a0
Show file tree
Hide file tree
Showing 45 changed files with 705 additions and 641 deletions.
202 changes: 96 additions & 106 deletions Cargo.lock

Large diffs are not rendered by default.

15 changes: 8 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,13 @@ lazy_static = "0.2"
regex = "0.1"
isatty = "0.1"
toml = "0.2"
serde = "0.8.0"
serde_json = "0.8.0"
serde = "0.8"
serde_json = "0.8"
app_dirs = "1.1.1"
fdlimit = "0.1"
hyper = { version = "0.9", default-features = false }
ctrlc = { git = "https://github.com/ethcore/rust-ctrlc.git" }
fdlimit = "0.1"
clippy = { version = "0.0.103", optional = true}
rlp = { path = "util/rlp" }
jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git" }
ethsync = { path = "sync" }
ethcore = { path = "ethcore" }
ethcore-util = { path = "util" }
Expand All @@ -44,15 +43,17 @@ ethcore-signer = { path = "signer" }
ethcore-ipc = { path = "ipc/rpc" }
ethcore-ipc-nano = { path = "ipc/nano" }
ethcore-ipc-hypervisor = { path = "ipc/hypervisor" }
ethcore-light = { path = "ethcore/light" }
ethcore-logger = { path = "logger" }
ethcore-stratum = { path = "stratum" }
ethcore-dapps = { path = "dapps", optional = true }
rlp = { path = "util/rlp" }
rpc-cli = { path = "rpc_cli" }
parity-rpc-client = { path = "rpc_client" }
ethcore-light = { path = "ethcore/light" }
parity-hash-fetch = { path = "hash-fetch" }
parity-updater = { path = "updater" }
parity-reactor = { path = "util/reactor" }
ethcore-dapps = { path = "dapps", optional = true }
clippy = { version = "0.0.103", optional = true}

[target.'cfg(windows)'.dependencies]
winapi = "0.2"
Expand Down
98 changes: 39 additions & 59 deletions dapps/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,6 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.

//! Ethcore Webapplications for Parity
//! ```
//! extern crate jsonrpc_core;
//! extern crate ethcore_dapps;
//!
//! use std::sync::Arc;
//! use jsonrpc_core::IoHandler;
//! use ethcore_dapps::*;
//!
//! struct SayHello;
//! impl MethodCommand for SayHello {
//! fn execute(&self, _params: Params) -> Result<Value, Error> {
//! Ok(Value::String("hello".to_string()))
//! }
//! }
//!
//! fn main() {
//! let io = IoHandler::new();
//! io.add_method("say_hello", SayHello);
//! let _server = Server::start_unsecure_http(
//! &"127.0.0.1:3030".parse().unwrap(),
//! Arc::new(io)
//! );
//! }
//! ```
//!
#![warn(missing_docs)]
#![cfg_attr(feature="nightly", plugin(clippy))]
Expand Down Expand Up @@ -93,11 +69,11 @@ use std::sync::{Arc, Mutex};
use std::net::SocketAddr;
use std::collections::HashMap;

use hash_fetch::urlhint::ContractClient;
use ethcore_rpc::Metadata;
use fetch::{Fetch, Client as FetchClient};
use jsonrpc_core::{IoHandler, IoDelegate};
use hash_fetch::urlhint::ContractClient;
use jsonrpc_core::reactor::RpcHandler;
use router::auth::{Authorization, NoAuth, HttpBasicAuth};
use ethcore_rpc::Extendable;
use parity_reactor::Remote;

use self::apps::{HOME_PAGE, DAPPS_DOMAIN};
Expand Down Expand Up @@ -126,32 +102,26 @@ impl<F> WebProxyTokens for F where F: Fn(String) -> bool + Send + Sync {
pub struct ServerBuilder<T: Fetch = FetchClient> {
dapps_path: PathBuf,
extra_dapps: Vec<PathBuf>,
handler: Arc<IoHandler>,
registrar: Arc<ContractClient>,
sync_status: Arc<SyncStatus>,
web_proxy_tokens: Arc<WebProxyTokens>,
signer_address: Option<(String, u16)>,
allowed_hosts: Option<Vec<String>>,
remote: Remote,
fetch: Option<T>,
}

impl<T: Fetch> Extendable for ServerBuilder<T> {
fn add_delegate<D: Send + Sync + 'static>(&self, delegate: IoDelegate<D>) {
self.handler.add_delegate(delegate);
}
}

impl ServerBuilder {
/// Construct new dapps server
pub fn new<P: AsRef<Path>>(dapps_path: P, registrar: Arc<ContractClient>, remote: Remote) -> Self {
ServerBuilder {
dapps_path: dapps_path.as_ref().to_owned(),
extra_dapps: vec![],
handler: Arc::new(IoHandler::new()),
registrar: registrar,
sync_status: Arc::new(|| false),
web_proxy_tokens: Arc::new(|_| false),
signer_address: None,
allowed_hosts: Some(vec![]),
remote: remote,
fetch: None,
}
Expand All @@ -164,11 +134,11 @@ impl<T: Fetch> ServerBuilder<T> {
ServerBuilder {
dapps_path: self.dapps_path,
extra_dapps: vec![],
handler: self.handler,
registrar: self.registrar,
sync_status: self.sync_status,
web_proxy_tokens: self.web_proxy_tokens,
signer_address: self.signer_address,
allowed_hosts: self.allowed_hosts,
remote: self.remote,
fetch: Some(fetch),
}
Expand All @@ -192,6 +162,14 @@ impl<T: Fetch> ServerBuilder<T> {
self
}

/// Change allowed hosts.
/// `None` - All hosts are allowed
/// `Some(whitelist)` - Allow only whitelisted hosts (+ listen address)
pub fn allowed_hosts(mut self, allowed_hosts: Option<Vec<String>>) -> Self {
self.allowed_hosts = allowed_hosts;
self
}

/// Change extra dapps paths (apart from `dapps_path`)
pub fn extra_dapps<P: AsRef<Path>>(mut self, extra_dapps: &[P]) -> Self {
self.extra_dapps = extra_dapps.iter().map(|p| p.as_ref().to_owned()).collect();
Expand All @@ -200,39 +178,41 @@ impl<T: Fetch> ServerBuilder<T> {

/// Asynchronously start server with no authentication,
/// returns result with `Server` handle on success or an error.
pub fn start_unsecured_http(self, addr: &SocketAddr, hosts: Option<Vec<String>>) -> Result<Server, ServerError> {
pub fn start_unsecured_http(self, addr: &SocketAddr, handler: RpcHandler<Metadata>) -> Result<Server, ServerError> {
let fetch = self.fetch_client()?;
Server::start_http(
addr,
hosts,
self.allowed_hosts,
NoAuth,
self.handler.clone(),
self.dapps_path.clone(),
self.extra_dapps.clone(),
self.signer_address.clone(),
self.registrar.clone(),
self.sync_status.clone(),
self.web_proxy_tokens.clone(),
self.remote.clone(),
self.fetch_client()?,
handler,
self.dapps_path,
self.extra_dapps,
self.signer_address,
self.registrar,
self.sync_status,
self.web_proxy_tokens,
self.remote,
fetch,
)
}

/// Asynchronously start server with `HTTP Basic Authentication`,
/// return result with `Server` handle on success or an error.
pub fn start_basic_auth_http(self, addr: &SocketAddr, hosts: Option<Vec<String>>, username: &str, password: &str) -> Result<Server, ServerError> {
pub fn start_basic_auth_http(self, addr: &SocketAddr, username: &str, password: &str, handler: RpcHandler<Metadata>) -> Result<Server, ServerError> {
let fetch = self.fetch_client()?;
Server::start_http(
addr,
hosts,
self.allowed_hosts,
HttpBasicAuth::single_user(username, password),
self.handler.clone(),
self.dapps_path.clone(),
self.extra_dapps.clone(),
self.signer_address.clone(),
self.registrar.clone(),
self.sync_status.clone(),
self.web_proxy_tokens.clone(),
self.remote.clone(),
self.fetch_client()?,
handler,
self.dapps_path,
self.extra_dapps,
self.signer_address,
self.registrar,
self.sync_status,
self.web_proxy_tokens,
self.remote,
fetch,
)
}

Expand Down Expand Up @@ -281,7 +261,7 @@ impl Server {
addr: &SocketAddr,
hosts: Option<Vec<String>>,
authorization: A,
handler: Arc<IoHandler>,
handler: RpcHandler<Metadata>,
dapps_path: PathBuf,
extra_dapps: Vec<PathBuf>,
signer_address: Option<(String, u16)>,
Expand Down
99 changes: 18 additions & 81 deletions dapps/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@
use std::sync::{Arc, Mutex};
use hyper;

use jsonrpc_core::{IoHandler, ResponseHandler, Request, Response};
use jsonrpc_http_server::{ServerHandler, PanicHandler, AccessControlAllowOrigin, RpcHandler};
use ethcore_rpc::{Metadata, Origin};
use jsonrpc_core::reactor::RpcHandler;
use jsonrpc_http_server::{Rpc, ServerHandler, PanicHandler, AccessControlAllowOrigin, HttpMetaExtractor};
use endpoint::{Endpoint, EndpointPath, Handler};

pub fn rpc(handler: Arc<IoHandler>, panic_handler: Arc<Mutex<Option<Box<Fn() -> () + Send>>>>) -> Box<Endpoint> {
pub fn rpc(handler: RpcHandler<Metadata>, panic_handler: Arc<Mutex<Option<Box<Fn() -> () + Send>>>>) -> Box<Endpoint> {
Box::new(RpcEndpoint {
handler: Arc::new(RpcMiddleware::new(handler)),
handler: handler,
meta_extractor: Arc::new(MetadataExtractor),
panic_handler: panic_handler,
cors_domain: None,
// NOTE [ToDr] We don't need to do any hosts validation here. It's already done in router.
Expand All @@ -32,7 +34,8 @@ pub fn rpc(handler: Arc<IoHandler>, panic_handler: Arc<Mutex<Option<Box<Fn() ->
}

struct RpcEndpoint {
handler: Arc<RpcMiddleware>,
handler: RpcHandler<Metadata>,
meta_extractor: Arc<HttpMetaExtractor<Metadata>>,
panic_handler: Arc<Mutex<Option<Box<Fn() -> () + Send>>>>,
cors_domain: Option<Vec<AccessControlAllowOrigin>>,
allowed_hosts: Option<Vec<String>>,
Expand All @@ -42,7 +45,7 @@ impl Endpoint for RpcEndpoint {
fn to_async_handler(&self, _path: EndpointPath, control: hyper::Control) -> Box<Handler> {
let panic_handler = PanicHandler { handler: self.panic_handler.clone() };
Box::new(ServerHandler::new(
self.handler.clone(),
Rpc::new(self.handler.clone(), self.meta_extractor.clone()),
self.cors_domain.clone(),
self.allowed_hosts.clone(),
panic_handler,
Expand All @@ -51,85 +54,19 @@ impl Endpoint for RpcEndpoint {
}
}

struct RpcMiddleware {
handler: Arc<IoHandler>,
methods: Vec<String>,
}

impl RpcMiddleware {
fn new(handler: Arc<IoHandler>) -> Self {
RpcMiddleware {
handler: handler,
methods: vec!["eth_accounts".into(), "parity_accountsInfo".into()],
}
}

/// Appends additional parameter for specific calls.
fn augment_request(&self, request: &mut Request, meta: Option<Meta>) {
use jsonrpc_core::{Call, Params, to_value};

fn augment_call(call: &mut Call, meta: Option<&Meta>, methods: &Vec<String>) {
match (call, meta) {
(&mut Call::MethodCall(ref mut method_call), Some(meta)) if methods.contains(&method_call.method) => {
let session = to_value(&meta.app_id);

let params = match method_call.params {
Some(Params::Array(ref vec)) if vec.len() == 0 => Some(Params::Array(vec![session])),
// invalid params otherwise
_ => None,
};

method_call.params = params;
},
_ => {}
}
}

match *request {
Request::Single(ref mut call) => augment_call(call, meta.as_ref(), &self.methods),
Request::Batch(ref mut vec) => {
for mut call in vec {
augment_call(call, meta.as_ref(), &self.methods)
}
},
}
}
}

#[derive(Debug)]
struct Meta {
app_id: String,
}

impl RpcHandler for RpcMiddleware {
type Metadata = Meta;

fn read_metadata(&self, request: &hyper::server::Request<hyper::net::HttpStream>) -> Option<Self::Metadata> {
request.headers().get::<hyper::header::Referer>()
struct MetadataExtractor;
impl HttpMetaExtractor<Metadata> for MetadataExtractor {
fn read_metadata(&self, request: &hyper::server::Request<hyper::net::HttpStream>) -> Metadata {
let dapp_id = request.headers().get::<hyper::header::Referer>()
.and_then(|referer| hyper::Url::parse(referer).ok())
.and_then(|url| {
url.path_segments()
.and_then(|mut split| split.next())
.map(|app_id| Meta {
app_id: app_id.to_owned(),
})
})
}

fn handle_request<H>(&self, request_str: &str, response_handler: H, meta: Option<Self::Metadata>) where
H: ResponseHandler<Option<String>, Option<String>> + 'static
{
let handler = IoHandler::convert_handler(response_handler);
let request = IoHandler::read_request(request_str);
trace!(target: "rpc", "Request metadata: {:?}", meta);

match request {
Ok(mut request) => {
self.augment_request(&mut request, meta);
self.handler.request_handler().handle_request(request, handler, None)
},
Err(error) => handler.send(Some(Response::from(error))),
.map(|app_id| app_id.to_owned())
});
Metadata {
dapp_id: dapp_id,
origin: Origin::Dapps,
}
}
}

Loading

0 comments on commit 41da1a0

Please sign in to comment.