Replace tokio_core with tokio (ring -> 0.13) (#9657)
				
					
				
			* Replace `tokio_core` with `tokio`.
* Remove `tokio-core` and replace with `tokio` in
    - `ethcore/stratum`
    - `secret_store`
    - `util/fetch`
    - `util/reactor`
* Bump hyper to 0.12 in
    - `miner`
    - `util/fake-fetch`
    - `util/fetch`
    - `secret_store`
* Bump `jsonrpc-***` to 0.9 in
    - `parity`
    - `ethcore/stratum`
    - `ipfs`
    - `rpc`
    - `rpc_client`
    - `whisper`
* Bump `ring` to 0.13
* Use a more graceful shutdown process in `secret_store` tests.
* Convert some mutexes to rwlocks in `secret_store`.
* Consolidate Tokio Runtime use, remove `CpuPool`.
* Rename and move the `tokio_reactor` crate (`util/reactor`) to
  `tokio_runtime` (`util/runtime`).
* Rename `EventLoop` to `Runtime`.
    - Rename `EventLoop::spawn` to `Runtime::with_default_thread_count`.
    - Add the `Runtime::with_thread_count` method.
    - Rename `Remote` to `Executor`.
* Remove uses of `CpuPool` and spawn all tasks via the `Runtime` executor
  instead.
* Other changes related to `CpuPool` removal:
    - Remove `Reservations::with_pool`. `::new` now takes an `Executor` as an argument.
    - Remove `SenderReservations::with_pool`. `::new` now takes an `Executor` as an argument.
			
			
This commit is contained in:
		
							parent
							
								
									b8da38f4e4
								
							
						
					
					
						commit
						68ca8df22f
					
				
							
								
								
									
										1179
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1179
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										10
									
								
								Cargo.toml
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								Cargo.toml
									
									
									
									
									
								
							@ -28,10 +28,9 @@ serde = "1.0"
 | 
			
		||||
serde_json = "1.0"
 | 
			
		||||
serde_derive = "1.0"
 | 
			
		||||
futures = "0.1"
 | 
			
		||||
futures-cpupool = "0.1"
 | 
			
		||||
fdlimit = "0.1"
 | 
			
		||||
ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" }
 | 
			
		||||
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" }
 | 
			
		||||
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-2.2" }
 | 
			
		||||
ethcore = { path = "ethcore", features = ["parity"] }
 | 
			
		||||
parity-bytes = "0.1"
 | 
			
		||||
ethcore-io = { path = "util/io" }
 | 
			
		||||
@ -51,7 +50,7 @@ rpc-cli = { path = "rpc_cli" }
 | 
			
		||||
parity-hash-fetch = { path = "hash-fetch" }
 | 
			
		||||
parity-ipfs-api = { path = "ipfs" }
 | 
			
		||||
parity-local-store = { path = "local-store" }
 | 
			
		||||
parity-reactor = { path = "util/reactor" }
 | 
			
		||||
parity-runtime = { path = "util/runtime" }
 | 
			
		||||
parity-rpc = { path = "rpc" }
 | 
			
		||||
parity-rpc-client = { path = "rpc_client" }
 | 
			
		||||
parity-updater = { path = "updater" }
 | 
			
		||||
@ -137,7 +136,4 @@ members = [
 | 
			
		||||
	"util/keccak-hasher",
 | 
			
		||||
	"util/patricia-trie-ethereum",
 | 
			
		||||
	"util/fastmap",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[patch.crates-io]
 | 
			
		||||
ring = { git = "https://github.com/paritytech/ring" }
 | 
			
		||||
]
 | 
			
		||||
@ -20,7 +20,7 @@ hashdb = "0.3.0"
 | 
			
		||||
memorydb = "0.3.0"
 | 
			
		||||
patricia-trie = "0.3.0"
 | 
			
		||||
patricia-trie-ethereum = { path = "../util/patricia-trie-ethereum" }
 | 
			
		||||
parity-crypto = "0.1"
 | 
			
		||||
parity-crypto = "0.2"
 | 
			
		||||
error-chain = { version = "0.12", default-features = false }
 | 
			
		||||
ethcore-io = { path = "../util/io" }
 | 
			
		||||
ethcore-logger = { path = "../logger" }
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@ ethabi-derive = "6.0"
 | 
			
		||||
ethabi-contract = "6.0"
 | 
			
		||||
ethcore = { path = ".." }
 | 
			
		||||
parity-bytes = "0.1"
 | 
			
		||||
parity-crypto = "0.1"
 | 
			
		||||
parity-crypto = "0.2"
 | 
			
		||||
ethcore-io = { path = "../../util/io" }
 | 
			
		||||
ethcore-logger = { path = "../../logger" }
 | 
			
		||||
ethcore-miner = { path = "../../miner" }
 | 
			
		||||
 | 
			
		||||
@ -125,9 +125,9 @@ impl SecretStoreEncryptor {
 | 
			
		||||
 | 
			
		||||
		// send HTTP request
 | 
			
		||||
		let method = if use_post {
 | 
			
		||||
			Method::Post
 | 
			
		||||
			Method::POST
 | 
			
		||||
		} else {
 | 
			
		||||
			Method::Get
 | 
			
		||||
			Method::GET
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		let url = Url::from_str(&url).map_err(|e| ErrorKind::Encrypt(e.to_string()))?;
 | 
			
		||||
 | 
			
		||||
@ -8,14 +8,14 @@ authors = ["Parity Technologies <admin@parity.io>"]
 | 
			
		||||
[dependencies]
 | 
			
		||||
ethereum-types = "0.4"
 | 
			
		||||
keccak-hash = "0.1"
 | 
			
		||||
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" }
 | 
			
		||||
jsonrpc-macros = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" }
 | 
			
		||||
jsonrpc-tcp-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" }
 | 
			
		||||
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-2.2" }
 | 
			
		||||
jsonrpc-macros = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-2.2" }
 | 
			
		||||
jsonrpc-tcp-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-2.2" }
 | 
			
		||||
log = "0.4"
 | 
			
		||||
parking_lot = "0.6"
 | 
			
		||||
 | 
			
		||||
[dev-dependencies]
 | 
			
		||||
env_logger = "0.5"
 | 
			
		||||
tokio-core = "0.1"
 | 
			
		||||
tokio = "0.1"
 | 
			
		||||
tokio-io = "0.1"
 | 
			
		||||
ethcore-logger = { path = "../../logger" }
 | 
			
		||||
 | 
			
		||||
@ -25,7 +25,7 @@ extern crate parking_lot;
 | 
			
		||||
 | 
			
		||||
#[macro_use] extern crate log;
 | 
			
		||||
 | 
			
		||||
#[cfg(test)] extern crate tokio_core;
 | 
			
		||||
#[cfg(test)] extern crate tokio;
 | 
			
		||||
#[cfg(test)] extern crate tokio_io;
 | 
			
		||||
#[cfg(test)] extern crate ethcore_logger;
 | 
			
		||||
 | 
			
		||||
@ -323,12 +323,10 @@ impl MetaExtractor<SocketMetadata> for PeerMetaExtractor {
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
	use super::*;
 | 
			
		||||
	use std::net::SocketAddr;
 | 
			
		||||
	use std::net::{SocketAddr, Shutdown};
 | 
			
		||||
	use std::sync::Arc;
 | 
			
		||||
 | 
			
		||||
	use tokio_core::reactor::{Core, Timeout};
 | 
			
		||||
	use tokio_core::net::TcpStream;
 | 
			
		||||
	use tokio_io::io;
 | 
			
		||||
	use tokio::{io, runtime::Runtime, timer::timeout::{self, Timeout}, net::TcpStream};
 | 
			
		||||
	use jsonrpc_core::futures::{Future, future};
 | 
			
		||||
 | 
			
		||||
	use ethcore_logger::init_log;
 | 
			
		||||
@ -342,23 +340,23 @@ mod tests {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fn dummy_request(addr: &SocketAddr, data: &str) -> Vec<u8> {
 | 
			
		||||
		let mut core = Core::new().expect("Tokio Core should be created with no errors");
 | 
			
		||||
		let mut buffer = vec![0u8; 2048];
 | 
			
		||||
		let mut runtime = Runtime::new().expect("Tokio Runtime should be created with no errors");
 | 
			
		||||
 | 
			
		||||
		let mut data_vec = data.as_bytes().to_vec();
 | 
			
		||||
		data_vec.extend(b"\n");
 | 
			
		||||
 | 
			
		||||
		let stream = TcpStream::connect(addr, &core.handle())
 | 
			
		||||
			.and_then(|stream| {
 | 
			
		||||
				io::write_all(stream, &data_vec)
 | 
			
		||||
		let stream = TcpStream::connect(addr)
 | 
			
		||||
			.and_then(move |stream| {
 | 
			
		||||
				io::write_all(stream, data_vec)
 | 
			
		||||
			})
 | 
			
		||||
			.and_then(|(stream, _)| {
 | 
			
		||||
				io::read(stream, &mut buffer)
 | 
			
		||||
				stream.shutdown(Shutdown::Write).unwrap();
 | 
			
		||||
				io::read_to_end(stream, Vec::with_capacity(2048))
 | 
			
		||||
			})
 | 
			
		||||
			.and_then(|(_, read_buf, len)| {
 | 
			
		||||
				future::ok(read_buf[0..len].to_vec())
 | 
			
		||||
			.and_then(|(_stream, read_buf)| {
 | 
			
		||||
				future::ok(read_buf)
 | 
			
		||||
			});
 | 
			
		||||
			let result = core.run(stream).expect("Core should run with no errors");
 | 
			
		||||
			let result = runtime.block_on(stream).expect("Runtime should run with no errors");
 | 
			
		||||
 | 
			
		||||
			result
 | 
			
		||||
	}
 | 
			
		||||
@ -417,7 +415,7 @@ mod tests {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	#[test]
 | 
			
		||||
	fn receives_initial_paylaod() {
 | 
			
		||||
	fn receives_initial_payload() {
 | 
			
		||||
		let addr = "127.0.0.1:19975".parse().unwrap();
 | 
			
		||||
		let _stratum = Stratum::start(&addr, DummyManager::new(), None).expect("There should be no error starting stratum");
 | 
			
		||||
		let request = r#"{"jsonrpc": "2.0", "method": "mining.subscribe", "params": [], "id": 2}"#;
 | 
			
		||||
@ -460,40 +458,43 @@ mod tests {
 | 
			
		||||
			.to_vec();
 | 
			
		||||
		auth_request.extend(b"\n");
 | 
			
		||||
 | 
			
		||||
		let mut core = Core::new().expect("Tokio Core should be created with no errors");
 | 
			
		||||
		let timeout1 = Timeout::new(::std::time::Duration::from_millis(100), &core.handle())
 | 
			
		||||
			.expect("There should be a timeout produced in message test");
 | 
			
		||||
		let timeout2 = Timeout::new(::std::time::Duration::from_millis(100), &core.handle())
 | 
			
		||||
			.expect("There should be a timeout produced in message test");
 | 
			
		||||
		let mut buffer = vec![0u8; 2048];
 | 
			
		||||
		let mut buffer2 = vec![0u8; 2048];
 | 
			
		||||
		let stream = TcpStream::connect(&addr, &core.handle())
 | 
			
		||||
			.and_then(|stream| {
 | 
			
		||||
				io::write_all(stream, &auth_request)
 | 
			
		||||
		let auth_response = "{\"jsonrpc\":\"2.0\",\"result\":true,\"id\":1}\n";
 | 
			
		||||
 | 
			
		||||
		let mut runtime = Runtime::new().expect("Tokio Runtime should be created with no errors");
 | 
			
		||||
		let read_buf0 = vec![0u8; auth_response.len()];
 | 
			
		||||
		let read_buf1 = Vec::with_capacity(2048);
 | 
			
		||||
		let stream = TcpStream::connect(&addr)
 | 
			
		||||
			.and_then(move |stream| {
 | 
			
		||||
				io::write_all(stream, auth_request)
 | 
			
		||||
			})
 | 
			
		||||
			.and_then(|(stream, _)| {
 | 
			
		||||
				io::read(stream, &mut buffer)
 | 
			
		||||
				io::read_exact(stream, read_buf0)
 | 
			
		||||
			})
 | 
			
		||||
			.and_then(|(stream, _, _)| {
 | 
			
		||||
			.map_err(|err| panic!("{:?}", err))
 | 
			
		||||
			.and_then(move |(stream, read_buf0)| {
 | 
			
		||||
				assert_eq!(String::from_utf8(read_buf0).unwrap(), auth_response);
 | 
			
		||||
				trace!(target: "stratum", "Received authorization confirmation");
 | 
			
		||||
				timeout1.join(future::ok(stream))
 | 
			
		||||
				Timeout::new(future::ok(stream), ::std::time::Duration::from_millis(100))
 | 
			
		||||
			})
 | 
			
		||||
			.and_then(|(_, stream)| {
 | 
			
		||||
			.map_err(|err: timeout::Error<()>| panic!("Timeout: {:?}", err))
 | 
			
		||||
			.and_then(move |stream| {
 | 
			
		||||
				trace!(target: "stratum", "Pusing work to peers");
 | 
			
		||||
				stratum.push_work_all(r#"{ "00040008", "100500" }"#.to_owned())
 | 
			
		||||
					.expect("Pushing work should produce no errors");
 | 
			
		||||
				timeout2.join(future::ok(stream))
 | 
			
		||||
				Timeout::new(future::ok(stream), ::std::time::Duration::from_millis(100))
 | 
			
		||||
			})
 | 
			
		||||
			.and_then(|(_, stream)| {
 | 
			
		||||
			.map_err(|err: timeout::Error<()>| panic!("Timeout: {:?}", err))
 | 
			
		||||
			.and_then(|stream| {
 | 
			
		||||
				trace!(target: "stratum", "Ready to read work from server");
 | 
			
		||||
				io::read(stream, &mut buffer2)
 | 
			
		||||
				stream.shutdown(Shutdown::Write).unwrap();
 | 
			
		||||
				io::read_to_end(stream, read_buf1)
 | 
			
		||||
			})
 | 
			
		||||
			.and_then(|(_, read_buf, len)| {
 | 
			
		||||
			.and_then(|(_, read_buf1)| {
 | 
			
		||||
				trace!(target: "stratum", "Received work from server");
 | 
			
		||||
				future::ok(read_buf[0..len].to_vec())
 | 
			
		||||
				future::ok(read_buf1)
 | 
			
		||||
			});
 | 
			
		||||
		let response = String::from_utf8(
 | 
			
		||||
			core.run(stream).expect("Core should run with no errors")
 | 
			
		||||
			runtime.block_on(stream).expect("Runtime should run with no errors")
 | 
			
		||||
		).expect("Response should be utf-8");
 | 
			
		||||
 | 
			
		||||
		assert_eq!(
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
 | 
			
		||||
[dependencies]
 | 
			
		||||
byteorder = "1.0"
 | 
			
		||||
edit-distance = "2.0"
 | 
			
		||||
parity-crypto = "0.1"
 | 
			
		||||
parity-crypto = "0.2"
 | 
			
		||||
eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1" }
 | 
			
		||||
ethereum-types = "0.4"
 | 
			
		||||
lazy_static = "1.0"
 | 
			
		||||
 | 
			
		||||
@ -16,7 +16,7 @@ tiny-keccak = "1.4"
 | 
			
		||||
time = "0.1.34"
 | 
			
		||||
itertools = "0.5"
 | 
			
		||||
parking_lot = "0.6"
 | 
			
		||||
parity-crypto = "0.1"
 | 
			
		||||
parity-crypto = "0.2"
 | 
			
		||||
ethereum-types = "0.4"
 | 
			
		||||
dir = { path = "../util/dir" }
 | 
			
		||||
smallvec = "0.6"
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,6 @@ authors = ["Parity Technologies <admin@parity.io>"]
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
futures = "0.1"
 | 
			
		||||
futures-cpupool = "0.1"
 | 
			
		||||
log = "0.4"
 | 
			
		||||
mime = "0.3"
 | 
			
		||||
mime_guess = "2.0.0-alpha.2"
 | 
			
		||||
@ -17,7 +16,7 @@ rustc-hex = "1.0"
 | 
			
		||||
fetch = { path = "../util/fetch" }
 | 
			
		||||
parity-bytes = "0.1"
 | 
			
		||||
ethereum-types = "0.4"
 | 
			
		||||
parity-reactor = { path = "../util/reactor" }
 | 
			
		||||
parity-runtime = { path = "../util/runtime" }
 | 
			
		||||
keccak-hash = "0.1"
 | 
			
		||||
registrar = { path = "../registrar" }
 | 
			
		||||
 | 
			
		||||
@ -26,6 +25,5 @@ ethabi-derive = "6.0"
 | 
			
		||||
ethabi-contract = "6.0"
 | 
			
		||||
 | 
			
		||||
[dev-dependencies]
 | 
			
		||||
hyper = "0.11"
 | 
			
		||||
parking_lot = "0.6"
 | 
			
		||||
fake-fetch = { path = "../util/fake-fetch" }
 | 
			
		||||
 | 
			
		||||
@ -23,9 +23,8 @@ use std::path::PathBuf;
 | 
			
		||||
 | 
			
		||||
use hash::keccak_buffer;
 | 
			
		||||
use fetch::{self, Fetch};
 | 
			
		||||
use futures_cpupool::CpuPool;
 | 
			
		||||
use futures::{Future, IntoFuture};
 | 
			
		||||
use parity_reactor::Remote;
 | 
			
		||||
use parity_runtime::Executor;
 | 
			
		||||
use urlhint::{URLHintContract, URLHint, URLHintResult};
 | 
			
		||||
use registrar::{RegistrarClient, Asynchronous};
 | 
			
		||||
use ethereum_types::H256;
 | 
			
		||||
@ -109,21 +108,19 @@ fn validate_hash(path: PathBuf, hash: H256, body: fetch::BodyReader) -> Result<P
 | 
			
		||||
 | 
			
		||||
/// Default Hash-fetching client using on-chain contract to resolve hashes to URLs.
 | 
			
		||||
pub struct Client<F: Fetch + 'static = fetch::Client> {
 | 
			
		||||
	pool: CpuPool,
 | 
			
		||||
	contract: URLHintContract,
 | 
			
		||||
	fetch: F,
 | 
			
		||||
	remote: Remote,
 | 
			
		||||
	executor: Executor,
 | 
			
		||||
	random_path: Arc<Fn() -> PathBuf + Sync + Send>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<F: Fetch + 'static> Client<F> {
 | 
			
		||||
	/// Creates new instance of the `Client` given on-chain contract client, fetch service and task runner.
 | 
			
		||||
	pub fn with_fetch(contract: Arc<RegistrarClient<Call=Asynchronous>>, pool: CpuPool, fetch: F, remote: Remote) -> Self {
 | 
			
		||||
	pub fn with_fetch(contract: Arc<RegistrarClient<Call=Asynchronous>>, fetch: F, executor: Executor) -> Self {
 | 
			
		||||
		Client {
 | 
			
		||||
			pool,
 | 
			
		||||
			contract: URLHintContract::new(contract),
 | 
			
		||||
			fetch: fetch,
 | 
			
		||||
			remote: remote,
 | 
			
		||||
			executor: executor,
 | 
			
		||||
			random_path: Arc::new(random_temp_path),
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@ -135,7 +132,6 @@ impl<F: Fetch + 'static> HashFetch for Client<F> {
 | 
			
		||||
 | 
			
		||||
		let random_path = self.random_path.clone();
 | 
			
		||||
		let remote_fetch = self.fetch.clone();
 | 
			
		||||
		let pool = self.pool.clone();
 | 
			
		||||
		let future = self.contract.resolve(hash)
 | 
			
		||||
			.map_err(|e| { warn!("Error resolving URL: {}", e); Error::NoResolution })
 | 
			
		||||
			.and_then(|maybe_url| maybe_url.ok_or(Error::NoResolution))
 | 
			
		||||
@ -162,7 +158,7 @@ impl<F: Fetch + 'static> HashFetch for Client<F> {
 | 
			
		||||
					Ok(response)
 | 
			
		||||
				}
 | 
			
		||||
			})
 | 
			
		||||
			.and_then(move |response| pool.spawn_fn(move || {
 | 
			
		||||
			.and_then(move |response| {
 | 
			
		||||
				debug!(target: "fetch", "Content fetched, validating hash ({:?})", hash);
 | 
			
		||||
				let path = random_path();
 | 
			
		||||
				let res = validate_hash(path.clone(), hash, fetch::BodyReader::new(response));
 | 
			
		||||
@ -172,10 +168,10 @@ impl<F: Fetch + 'static> HashFetch for Client<F> {
 | 
			
		||||
					let _ = fs::remove_file(&path);
 | 
			
		||||
				}
 | 
			
		||||
				res
 | 
			
		||||
			}))
 | 
			
		||||
			})
 | 
			
		||||
			.then(move |res| { on_done(res); Ok(()) as Result<(), ()> });
 | 
			
		||||
 | 
			
		||||
		self.remote.spawn(future);
 | 
			
		||||
		self.executor.spawn(future);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -197,8 +193,7 @@ mod tests {
 | 
			
		||||
	use rustc_hex::FromHex;
 | 
			
		||||
	use std::sync::{Arc, mpsc};
 | 
			
		||||
	use parking_lot::Mutex;
 | 
			
		||||
	use futures_cpupool::CpuPool;
 | 
			
		||||
	use parity_reactor::Remote;
 | 
			
		||||
	use parity_runtime::Executor;
 | 
			
		||||
	use urlhint::tests::{FakeRegistrar, URLHINT};
 | 
			
		||||
	use super::{Error, Client, HashFetch, random_temp_path};
 | 
			
		||||
 | 
			
		||||
@ -216,7 +211,7 @@ mod tests {
 | 
			
		||||
		// given
 | 
			
		||||
		let contract = Arc::new(FakeRegistrar::new());
 | 
			
		||||
		let fetch = FakeFetch::new(None::<usize>);
 | 
			
		||||
		let client = Client::with_fetch(contract.clone(), CpuPool::new(1), fetch, Remote::new_sync());
 | 
			
		||||
		let client = Client::with_fetch(contract.clone(), fetch, Executor::new_sync());
 | 
			
		||||
 | 
			
		||||
		// when
 | 
			
		||||
		let (tx, rx) = mpsc::channel();
 | 
			
		||||
@ -234,7 +229,7 @@ mod tests {
 | 
			
		||||
		// given
 | 
			
		||||
		let registrar = Arc::new(registrar());
 | 
			
		||||
		let fetch = FakeFetch::new(None::<usize>);
 | 
			
		||||
		let client = Client::with_fetch(registrar.clone(), CpuPool::new(1), fetch, Remote::new_sync());
 | 
			
		||||
		let client = Client::with_fetch(registrar.clone(), fetch, Executor::new_sync());
 | 
			
		||||
 | 
			
		||||
		// when
 | 
			
		||||
		let (tx, rx) = mpsc::channel();
 | 
			
		||||
@ -252,7 +247,7 @@ mod tests {
 | 
			
		||||
		// given
 | 
			
		||||
		let registrar = Arc::new(registrar());
 | 
			
		||||
		let fetch = FakeFetch::new(Some(1));
 | 
			
		||||
		let mut client = Client::with_fetch(registrar.clone(), CpuPool::new(1), fetch, Remote::new_sync());
 | 
			
		||||
		let mut client = Client::with_fetch(registrar.clone(), fetch, Executor::new_sync());
 | 
			
		||||
		let path = random_temp_path();
 | 
			
		||||
		let path2 = path.clone();
 | 
			
		||||
		client.random_path = Arc::new(move || path2.clone());
 | 
			
		||||
@ -275,7 +270,7 @@ mod tests {
 | 
			
		||||
		// given
 | 
			
		||||
		let registrar = Arc::new(registrar());
 | 
			
		||||
		let fetch = FakeFetch::new(Some(1));
 | 
			
		||||
		let client = Client::with_fetch(registrar.clone(), CpuPool::new(1), fetch, Remote::new_sync());
 | 
			
		||||
		let client = Client::with_fetch(registrar.clone(), fetch, Executor::new_sync());
 | 
			
		||||
 | 
			
		||||
		// when
 | 
			
		||||
		let (tx, rx) = mpsc::channel();
 | 
			
		||||
 | 
			
		||||
@ -25,11 +25,10 @@ extern crate ethabi;
 | 
			
		||||
extern crate parity_bytes as bytes;
 | 
			
		||||
extern crate ethereum_types;
 | 
			
		||||
extern crate futures;
 | 
			
		||||
extern crate futures_cpupool;
 | 
			
		||||
extern crate keccak_hash as hash;
 | 
			
		||||
extern crate mime;
 | 
			
		||||
extern crate mime_guess;
 | 
			
		||||
extern crate parity_reactor;
 | 
			
		||||
extern crate parity_runtime;
 | 
			
		||||
extern crate rand;
 | 
			
		||||
extern crate rustc_hex;
 | 
			
		||||
extern crate registrar;
 | 
			
		||||
@ -43,8 +42,6 @@ extern crate ethabi_contract;
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
extern crate parking_lot;
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
extern crate hyper;
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
extern crate fake_fetch;
 | 
			
		||||
 | 
			
		||||
mod client;
 | 
			
		||||
 | 
			
		||||
@ -9,11 +9,11 @@ authors = ["Parity Technologies <admin@parity.io>"]
 | 
			
		||||
ethcore = { path = "../ethcore" }
 | 
			
		||||
parity-bytes = "0.1"
 | 
			
		||||
ethereum-types = "0.4"
 | 
			
		||||
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" }
 | 
			
		||||
jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" }
 | 
			
		||||
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-2.2" }
 | 
			
		||||
jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-2.2" }
 | 
			
		||||
rlp = { version = "0.3.0", features = ["ethereum"] }
 | 
			
		||||
cid = "0.2"
 | 
			
		||||
multihash = "0.7"
 | 
			
		||||
cid = "0.3"
 | 
			
		||||
multihash = "0.8"
 | 
			
		||||
unicase = "2.0"
 | 
			
		||||
 | 
			
		||||
[dev-dependencies]
 | 
			
		||||
 | 
			
		||||
@ -30,6 +30,42 @@ pub enum ServerError {
 | 
			
		||||
	InvalidInterface
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Handle IO errors (ports taken when starting the server).
 | 
			
		||||
impl From<::std::io::Error> for ServerError {
 | 
			
		||||
	fn from(err: ::std::io::Error) -> ServerError {
 | 
			
		||||
		ServerError::IoError(err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<http::hyper::error::Error> for ServerError {
 | 
			
		||||
	fn from(err: http::hyper::error::Error) -> ServerError {
 | 
			
		||||
		ServerError::Other(err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<ServerError> for String {
 | 
			
		||||
	fn from(err: ServerError) -> String {
 | 
			
		||||
		match err {
 | 
			
		||||
			ServerError::IoError(err) => err.to_string(),
 | 
			
		||||
			ServerError::Other(err) => err.to_string(),
 | 
			
		||||
			ServerError::InvalidInterface => "Invalid --ipfs-api-interface parameter".into(),
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ::std::fmt::Display for ServerError {
 | 
			
		||||
	fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
        	ServerError::IoError(err) => write!(f, "Io Error: {}", err),
 | 
			
		||||
        	ServerError::Other(err) => write!(f, "Other error: {}", err),
 | 
			
		||||
        	ServerError::InvalidInterface => write!(f, "Invalid interface"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ::std::error::Error for ServerError {}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, PartialEq)]
 | 
			
		||||
pub enum Error {
 | 
			
		||||
	CidParsingFailed,
 | 
			
		||||
@ -71,27 +107,4 @@ impl From<multihash::Error> for Error {
 | 
			
		||||
	fn from(_: multihash::Error) -> Error {
 | 
			
		||||
		Error::CidParsingFailed
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Handle IO errors (ports taken when starting the server).
 | 
			
		||||
impl From<::std::io::Error> for ServerError {
 | 
			
		||||
	fn from(err: ::std::io::Error) -> ServerError {
 | 
			
		||||
		ServerError::IoError(err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<http::hyper::error::Error> for ServerError {
 | 
			
		||||
	fn from(err: http::hyper::error::Error) -> ServerError {
 | 
			
		||||
		ServerError::Other(err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<ServerError> for String {
 | 
			
		||||
	fn from(err: ServerError) -> String {
 | 
			
		||||
		match err {
 | 
			
		||||
			ServerError::IoError(err) => err.to_string(),
 | 
			
		||||
			ServerError::Other(err) => err.to_string(),
 | 
			
		||||
			ServerError::InvalidInterface => "Invalid --ipfs-api-interface parameter".into(),
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@ -35,10 +35,9 @@ use std::net::{SocketAddr, IpAddr};
 | 
			
		||||
use core::futures::future::{self, FutureResult};
 | 
			
		||||
use core::futures::{self, Future};
 | 
			
		||||
use ethcore::client::BlockChainClient;
 | 
			
		||||
use http::hyper::header::{self, Vary, ContentType};
 | 
			
		||||
use http::hyper::{Method, StatusCode};
 | 
			
		||||
use http::hyper::{self, server};
 | 
			
		||||
use unicase::Ascii;
 | 
			
		||||
use http::hyper::{self, server, Method, StatusCode, Body,
 | 
			
		||||
	header::{self, HeaderValue},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use error::ServerError;
 | 
			
		||||
use route::Out;
 | 
			
		||||
@ -67,9 +66,9 @@ impl IpfsHandler {
 | 
			
		||||
			client: client,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	pub fn on_request(&self, req: hyper::Request) -> (Option<header::AccessControlAllowOrigin>, Out) {
 | 
			
		||||
	pub fn on_request(&self, req: hyper::Request<Body>) -> (Option<HeaderValue>, Out) {
 | 
			
		||||
		match *req.method() {
 | 
			
		||||
			Method::Get | Method::Post => {},
 | 
			
		||||
			Method::GET | Method::POST => {},
 | 
			
		||||
			_ => return (None, Out::Bad("Invalid Request")),
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -77,8 +76,8 @@ impl IpfsHandler {
 | 
			
		||||
			return (None, Out::Bad("Disallowed Host header"));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		let cors_header = http::cors_header(&req, &self.cors_domains);
 | 
			
		||||
		if cors_header == http::CorsHeader::Invalid {
 | 
			
		||||
		let cors_header = http::cors_allow_origin(&req, &self.cors_domains);
 | 
			
		||||
		if cors_header == http::AllowCors::Invalid {
 | 
			
		||||
			return (None, Out::Bad("Disallowed Origin header"));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -88,39 +87,39 @@ impl IpfsHandler {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl server::Service for IpfsHandler {
 | 
			
		||||
	type Request = hyper::Request;
 | 
			
		||||
	type Response = hyper::Response;
 | 
			
		||||
impl hyper::service::Service for IpfsHandler {
 | 
			
		||||
	type ReqBody = Body;
 | 
			
		||||
	type ResBody = Body;
 | 
			
		||||
	type Error = hyper::Error;
 | 
			
		||||
	type Future = FutureResult<hyper::Response, hyper::Error>;
 | 
			
		||||
	type Future = FutureResult<hyper::Response<Body>, Self::Error>;
 | 
			
		||||
 | 
			
		||||
	fn call(&self, request: Self::Request) -> Self::Future {
 | 
			
		||||
	fn call(&mut self, request: hyper::Request<Self::ReqBody>) -> Self::Future {
 | 
			
		||||
		let (cors_header, out) = self.on_request(request);
 | 
			
		||||
 | 
			
		||||
		let mut res = match out {
 | 
			
		||||
			Out::OctetStream(bytes) => {
 | 
			
		||||
				hyper::Response::new()
 | 
			
		||||
					.with_status(StatusCode::Ok)
 | 
			
		||||
					.with_header(ContentType::octet_stream())
 | 
			
		||||
					.with_body(bytes)
 | 
			
		||||
				hyper::Response::builder()
 | 
			
		||||
					.status(StatusCode::OK)
 | 
			
		||||
					.header("content-type", HeaderValue::from_static("application/octet-stream"))
 | 
			
		||||
					.body(bytes.into())
 | 
			
		||||
			},
 | 
			
		||||
			Out::NotFound(reason) => {
 | 
			
		||||
				hyper::Response::new()
 | 
			
		||||
					.with_status(StatusCode::NotFound)
 | 
			
		||||
					.with_header(ContentType::plaintext())
 | 
			
		||||
					.with_body(reason)
 | 
			
		||||
				hyper::Response::builder()
 | 
			
		||||
					.status(StatusCode::NOT_FOUND)
 | 
			
		||||
					.header("content-type", HeaderValue::from_static("text/plain; charset=utf-8"))
 | 
			
		||||
					.body(reason.into())
 | 
			
		||||
			},
 | 
			
		||||
			Out::Bad(reason) => {
 | 
			
		||||
				hyper::Response::new()
 | 
			
		||||
					.with_status(StatusCode::BadRequest)
 | 
			
		||||
					.with_header(ContentType::plaintext())
 | 
			
		||||
					.with_body(reason)
 | 
			
		||||
				hyper::Response::builder()
 | 
			
		||||
					.status(StatusCode::BAD_REQUEST)
 | 
			
		||||
					.header("content-type", HeaderValue::from_static("text/plain; charset=utf-8"))
 | 
			
		||||
					.body(reason.into())
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
		}.expect("Response builder: Parsing 'content-type' header name will not fail; qed");
 | 
			
		||||
 | 
			
		||||
		if let Some(cors_header) = cors_header {
 | 
			
		||||
			res.headers_mut().set(cors_header);
 | 
			
		||||
			res.headers_mut().set(Vary::Items(vec![Ascii::new("Origin".into())]));
 | 
			
		||||
			res.headers_mut().append(header::ACCESS_CONTROL_ALLOW_ORIGIN, cors_header);
 | 
			
		||||
			res.headers_mut().append(header::VARY, HeaderValue::from_static("origin"));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		future::ok(res)
 | 
			
		||||
@ -164,23 +163,32 @@ pub fn start_server(
 | 
			
		||||
	let hosts: DomainsValidation<_> = hosts.map(move |hosts| include_current_interface(hosts, interface, port)).into();
 | 
			
		||||
 | 
			
		||||
	let (close, shutdown_signal) = futures::sync::oneshot::channel::<()>();
 | 
			
		||||
	let (tx, rx) = mpsc::sync_channel(1);
 | 
			
		||||
	let (tx, rx) = mpsc::sync_channel::<Result<(), ServerError>>(1);
 | 
			
		||||
	let thread = thread::spawn(move || {
 | 
			
		||||
		let send = |res| tx.send(res).expect("rx end is never dropped; qed");
 | 
			
		||||
		let server = match server::Http::new().bind(&addr, move || {
 | 
			
		||||
			Ok(IpfsHandler::new(cors.clone(), hosts.clone(), client.clone()))
 | 
			
		||||
		}) {
 | 
			
		||||
			Ok(server) => {
 | 
			
		||||
				send(Ok(()));
 | 
			
		||||
				server
 | 
			
		||||
			},
 | 
			
		||||
 | 
			
		||||
		let server_bldr = match server::Server::try_bind(&addr) {
 | 
			
		||||
			Ok(s) => s,
 | 
			
		||||
			Err(err) => {
 | 
			
		||||
				send(Err(err));
 | 
			
		||||
				send(Err(ServerError::from(err)));
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		let _ = server.run_until(shutdown_signal.map_err(|_| {}));
 | 
			
		||||
		let new_service = move || {
 | 
			
		||||
			Ok::<_, ServerError>(
 | 
			
		||||
				IpfsHandler::new(cors.clone(), hosts.clone(), client.clone())
 | 
			
		||||
			)
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		let server = server_bldr
 | 
			
		||||
	        .serve(new_service)
 | 
			
		||||
	        .map_err(|_| ())
 | 
			
		||||
	        .select(shutdown_signal.map_err(|_| ()))
 | 
			
		||||
	        .then(|_| Ok(()));
 | 
			
		||||
 | 
			
		||||
	    hyper::rt::run(server);
 | 
			
		||||
		send(Ok(()));
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	// Wait for server to start successfuly.
 | 
			
		||||
 | 
			
		||||
@ -10,8 +10,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
 | 
			
		||||
# Only work_notify, consider a separate crate
 | 
			
		||||
ethash = { path = "../ethash", optional = true }
 | 
			
		||||
fetch = { path = "../util/fetch", optional = true }
 | 
			
		||||
hyper = { version = "0.11", optional = true }
 | 
			
		||||
parity-reactor = { path = "../util/reactor", optional = true }
 | 
			
		||||
hyper = { version = "0.12", optional = true }
 | 
			
		||||
url = { version = "1", optional = true }
 | 
			
		||||
 | 
			
		||||
# Miner
 | 
			
		||||
@ -20,7 +19,7 @@ error-chain = "0.12"
 | 
			
		||||
ethcore-transaction = { path = "../ethcore/transaction" }
 | 
			
		||||
ethereum-types = "0.4"
 | 
			
		||||
futures = "0.1"
 | 
			
		||||
futures-cpupool = "0.1"
 | 
			
		||||
parity-runtime = { path = "../util/runtime" }
 | 
			
		||||
heapsize = "0.4"
 | 
			
		||||
keccak-hash = "0.1"
 | 
			
		||||
linked-hash-map = "0.5"
 | 
			
		||||
@ -37,4 +36,4 @@ ethkey = { path = "../ethkey" }
 | 
			
		||||
rustc-hex = "1.0"
 | 
			
		||||
 | 
			
		||||
[features]
 | 
			
		||||
work-notify = ["ethash", "fetch", "hyper", "parity-reactor", "url"]
 | 
			
		||||
work-notify = ["ethash", "fetch", "hyper", "url"]
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,7 @@ use std::time::{Instant, Duration};
 | 
			
		||||
 | 
			
		||||
use ansi_term::Colour;
 | 
			
		||||
use ethereum_types::U256;
 | 
			
		||||
use futures_cpupool::CpuPool;
 | 
			
		||||
use parity_runtime::Executor;
 | 
			
		||||
use price_info::{Client as PriceInfoClient, PriceInfo};
 | 
			
		||||
use price_info::fetch::Client as FetchClient;
 | 
			
		||||
 | 
			
		||||
@ -43,7 +43,7 @@ pub struct GasPriceCalibrator {
 | 
			
		||||
 | 
			
		||||
impl GasPriceCalibrator {
 | 
			
		||||
	/// Create a new gas price calibrator.
 | 
			
		||||
	pub fn new(options: GasPriceCalibratorOptions, fetch: FetchClient, p: CpuPool) -> GasPriceCalibrator {
 | 
			
		||||
	pub fn new(options: GasPriceCalibratorOptions, fetch: FetchClient, p: Executor) -> GasPriceCalibrator {
 | 
			
		||||
		GasPriceCalibrator {
 | 
			
		||||
			options: options,
 | 
			
		||||
			next_calibration: Instant::now(),
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,7 @@ extern crate ansi_term;
 | 
			
		||||
extern crate ethcore_transaction as transaction;
 | 
			
		||||
extern crate ethereum_types;
 | 
			
		||||
extern crate futures;
 | 
			
		||||
extern crate futures_cpupool;
 | 
			
		||||
extern crate parity_runtime;
 | 
			
		||||
extern crate heapsize;
 | 
			
		||||
extern crate keccak_hash as hash;
 | 
			
		||||
extern crate linked_hash_map;
 | 
			
		||||
 | 
			
		||||
@ -18,18 +18,19 @@
 | 
			
		||||
 | 
			
		||||
extern crate ethash;
 | 
			
		||||
extern crate fetch;
 | 
			
		||||
extern crate parity_reactor;
 | 
			
		||||
extern crate parity_runtime;
 | 
			
		||||
extern crate url;
 | 
			
		||||
extern crate hyper;
 | 
			
		||||
 | 
			
		||||
use self::fetch::{Fetch, Request, Client as FetchClient, Method};
 | 
			
		||||
use self::parity_reactor::Remote;
 | 
			
		||||
use self::parity_runtime::Executor;
 | 
			
		||||
use self::ethash::SeedHashCompute;
 | 
			
		||||
use self::url::Url;
 | 
			
		||||
use self::hyper::header::ContentType;
 | 
			
		||||
use self::hyper::header::{self, HeaderValue};
 | 
			
		||||
 | 
			
		||||
use ethereum_types::{H256, U256};
 | 
			
		||||
use parking_lot::Mutex;
 | 
			
		||||
 | 
			
		||||
use futures::Future;
 | 
			
		||||
 | 
			
		||||
/// Trait for notifying about new mining work
 | 
			
		||||
@ -42,13 +43,13 @@ pub trait NotifyWork : Send + Sync {
 | 
			
		||||
pub struct WorkPoster {
 | 
			
		||||
	urls: Vec<Url>,
 | 
			
		||||
	client: FetchClient,
 | 
			
		||||
	remote: Remote,
 | 
			
		||||
	executor: Executor,
 | 
			
		||||
	seed_compute: Mutex<SeedHashCompute>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl WorkPoster {
 | 
			
		||||
	/// Create new `WorkPoster`.
 | 
			
		||||
	pub fn new(urls: &[String], fetch: FetchClient, remote: Remote) -> Self {
 | 
			
		||||
	pub fn new(urls: &[String], fetch: FetchClient, executor: Executor) -> Self {
 | 
			
		||||
		let urls = urls.into_iter().filter_map(|u| {
 | 
			
		||||
			match Url::parse(u) {
 | 
			
		||||
				Ok(url) => Some(url),
 | 
			
		||||
@ -60,7 +61,7 @@ impl WorkPoster {
 | 
			
		||||
		}).collect();
 | 
			
		||||
		WorkPoster {
 | 
			
		||||
			client: fetch,
 | 
			
		||||
			remote: remote,
 | 
			
		||||
			executor: executor,
 | 
			
		||||
			urls: urls,
 | 
			
		||||
			seed_compute: Mutex::new(SeedHashCompute::default()),
 | 
			
		||||
		}
 | 
			
		||||
@ -80,9 +81,9 @@ impl NotifyWork for WorkPoster {
 | 
			
		||||
 | 
			
		||||
		for u in &self.urls {
 | 
			
		||||
			let u = u.clone();
 | 
			
		||||
			self.remote.spawn(self.client.fetch(
 | 
			
		||||
				Request::new(u.clone(), Method::Post)
 | 
			
		||||
					.with_header(ContentType::json())
 | 
			
		||||
			self.executor.spawn(self.client.fetch(
 | 
			
		||||
				Request::new(u.clone(), Method::POST)
 | 
			
		||||
					.with_header(header::CONTENT_TYPE, HeaderValue::from_static("application/json"))
 | 
			
		||||
					.with_body(body.clone()), Default::default()
 | 
			
		||||
			).map_err(move |e| {
 | 
			
		||||
				warn!("Error sending HTTP notification to {} : {}, retrying", u, e);
 | 
			
		||||
 | 
			
		||||
@ -25,7 +25,6 @@ extern crate clap;
 | 
			
		||||
extern crate dir;
 | 
			
		||||
extern crate env_logger;
 | 
			
		||||
extern crate futures;
 | 
			
		||||
extern crate futures_cpupool;
 | 
			
		||||
extern crate atty;
 | 
			
		||||
extern crate jsonrpc_core;
 | 
			
		||||
extern crate num_cpus;
 | 
			
		||||
@ -60,7 +59,7 @@ extern crate kvdb;
 | 
			
		||||
extern crate parity_hash_fetch as hash_fetch;
 | 
			
		||||
extern crate parity_ipfs_api;
 | 
			
		||||
extern crate parity_local_store as local_store;
 | 
			
		||||
extern crate parity_reactor;
 | 
			
		||||
extern crate parity_runtime;
 | 
			
		||||
extern crate parity_rpc;
 | 
			
		||||
extern crate parity_updater as updater;
 | 
			
		||||
extern crate parity_version;
 | 
			
		||||
 | 
			
		||||
@ -29,7 +29,7 @@ use light::TransactionQueue;
 | 
			
		||||
 | 
			
		||||
use futures::{future, Future};
 | 
			
		||||
 | 
			
		||||
use parity_reactor::Remote;
 | 
			
		||||
use parity_runtime::Executor;
 | 
			
		||||
 | 
			
		||||
use parking_lot::RwLock;
 | 
			
		||||
 | 
			
		||||
@ -50,8 +50,8 @@ pub struct QueueCull<T> {
 | 
			
		||||
	pub on_demand: Arc<OnDemand>,
 | 
			
		||||
	/// The transaction queue.
 | 
			
		||||
	pub txq: Arc<RwLock<TransactionQueue>>,
 | 
			
		||||
	/// Event loop remote.
 | 
			
		||||
	pub remote: Remote,
 | 
			
		||||
	/// Event loop executor.
 | 
			
		||||
	pub executor: Executor,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: LightChainClient + 'static> IoHandler<ClientIoMessage> for QueueCull<T> {
 | 
			
		||||
@ -70,7 +70,7 @@ impl<T: LightChainClient + 'static> IoHandler<ClientIoMessage> for QueueCull<T>
 | 
			
		||||
		let start_nonce = self.client.engine().account_start_nonce(best_header.number());
 | 
			
		||||
 | 
			
		||||
		info!(target: "cull", "Attempting to cull queued transactions from {} senders.", senders.len());
 | 
			
		||||
		self.remote.spawn_with_timeout(move |_| {
 | 
			
		||||
		self.executor.spawn_with_timeout(move || {
 | 
			
		||||
			let maybe_fetching = sync.with_context(move |ctx| {
 | 
			
		||||
				// fetch the nonce of each sender in the queue.
 | 
			
		||||
				let nonce_reqs = senders.iter()
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,7 @@ use ethcore::client::Mode;
 | 
			
		||||
use ethcore::ethereum;
 | 
			
		||||
use ethcore::spec::{Spec, SpecParams};
 | 
			
		||||
use ethereum_types::{U256, Address};
 | 
			
		||||
use futures_cpupool::CpuPool;
 | 
			
		||||
use parity_runtime::Executor;
 | 
			
		||||
use hash_fetch::fetch::Client as FetchClient;
 | 
			
		||||
use journaldb::Algorithm;
 | 
			
		||||
use miner::gas_pricer::GasPricer;
 | 
			
		||||
@ -256,7 +256,7 @@ impl Default for GasPricerConfig {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl GasPricerConfig {
 | 
			
		||||
	pub fn to_gas_pricer(&self, fetch: FetchClient, p: CpuPool) -> GasPricer {
 | 
			
		||||
	pub fn to_gas_pricer(&self, fetch: FetchClient, p: Executor) -> GasPricer {
 | 
			
		||||
		match *self {
 | 
			
		||||
			GasPricerConfig::Fixed(u) => GasPricer::Fixed(u),
 | 
			
		||||
			GasPricerConfig::Calibrated { usd_per_tx, recalibration_period, .. } => {
 | 
			
		||||
 | 
			
		||||
@ -23,14 +23,13 @@ use dir::default_data_path;
 | 
			
		||||
use dir::helpers::replace_home;
 | 
			
		||||
use helpers::parity_ipc_path;
 | 
			
		||||
use jsonrpc_core::MetaIoHandler;
 | 
			
		||||
use parity_reactor::TokioRemote;
 | 
			
		||||
use parity_runtime::Executor;
 | 
			
		||||
use parity_rpc::informant::{RpcStats, Middleware};
 | 
			
		||||
use parity_rpc::{self as rpc, Metadata, DomainsValidation};
 | 
			
		||||
use rpc_apis::{self, ApiSet};
 | 
			
		||||
 | 
			
		||||
pub use parity_rpc::{IpcServer, HttpServer, RequestMiddleware};
 | 
			
		||||
pub use parity_rpc::ws::Server as WsServer;
 | 
			
		||||
pub use parity_rpc::informant::CpuPool;
 | 
			
		||||
 | 
			
		||||
pub const DAPPS_DOMAIN: &'static str = "web3.site";
 | 
			
		||||
 | 
			
		||||
@ -134,9 +133,8 @@ fn address(enabled: bool, bind_iface: &str, bind_port: u16, hosts: &Option<Vec<S
 | 
			
		||||
 | 
			
		||||
pub struct Dependencies<D: rpc_apis::Dependencies> {
 | 
			
		||||
	pub apis: Arc<D>,
 | 
			
		||||
	pub remote: TokioRemote,
 | 
			
		||||
	pub executor: Executor,
 | 
			
		||||
	pub stats: Arc<RpcStats>,
 | 
			
		||||
	pub pool: Option<CpuPool>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn new_ws<D: rpc_apis::Dependencies>(
 | 
			
		||||
@ -155,7 +153,7 @@ pub fn new_ws<D: rpc_apis::Dependencies>(
 | 
			
		||||
	let handler = {
 | 
			
		||||
		let mut handler = MetaIoHandler::with_middleware((
 | 
			
		||||
			rpc::WsDispatcher::new(full_handler),
 | 
			
		||||
			Middleware::new(deps.stats.clone(), deps.apis.activity_notifier(), deps.pool.clone())
 | 
			
		||||
			Middleware::new(deps.stats.clone(), deps.apis.activity_notifier())
 | 
			
		||||
		));
 | 
			
		||||
		let apis = conf.apis.list_apis();
 | 
			
		||||
		deps.apis.extend_with_set(&mut handler, &apis);
 | 
			
		||||
@ -163,7 +161,6 @@ pub fn new_ws<D: rpc_apis::Dependencies>(
 | 
			
		||||
		handler
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	let remote = deps.remote.clone();
 | 
			
		||||
	let allowed_origins = into_domains(with_domain(conf.origins, domain, &None));
 | 
			
		||||
	let allowed_hosts = into_domains(with_domain(conf.hosts, domain, &Some(url.clone().into())));
 | 
			
		||||
 | 
			
		||||
@ -178,7 +175,6 @@ pub fn new_ws<D: rpc_apis::Dependencies>(
 | 
			
		||||
	let start_result = rpc::start_ws(
 | 
			
		||||
		&addr,
 | 
			
		||||
		handler,
 | 
			
		||||
		remote.clone(),
 | 
			
		||||
		allowed_origins,
 | 
			
		||||
		allowed_hosts,
 | 
			
		||||
		conf.max_connections,
 | 
			
		||||
@ -210,7 +206,6 @@ pub fn new_http<D: rpc_apis::Dependencies>(
 | 
			
		||||
	let url = format!("{}:{}", conf.interface, conf.port);
 | 
			
		||||
	let addr = url.parse().map_err(|_| format!("Invalid {} listen host/port given: {}", id, url))?;
 | 
			
		||||
	let handler = setup_apis(conf.apis, deps);
 | 
			
		||||
	let remote = deps.remote.clone();
 | 
			
		||||
 | 
			
		||||
	let cors_domains = into_domains(conf.cors);
 | 
			
		||||
	let allowed_hosts = into_domains(with_domain(conf.hosts, domain, &Some(url.clone().into())));
 | 
			
		||||
@ -220,7 +215,6 @@ pub fn new_http<D: rpc_apis::Dependencies>(
 | 
			
		||||
		cors_domains,
 | 
			
		||||
		allowed_hosts,
 | 
			
		||||
		handler,
 | 
			
		||||
		remote,
 | 
			
		||||
		rpc::RpcExtractor,
 | 
			
		||||
		conf.server_threads,
 | 
			
		||||
		conf.max_payload,
 | 
			
		||||
@ -244,7 +238,6 @@ pub fn new_ipc<D: rpc_apis::Dependencies>(
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	let handler = setup_apis(conf.apis, dependencies);
 | 
			
		||||
	let remote = dependencies.remote.clone();
 | 
			
		||||
	let path = PathBuf::from(&conf.socket_addr);
 | 
			
		||||
	// Make sure socket file can be created on unix-like OS.
 | 
			
		||||
	// Windows pipe paths are not on the FS.
 | 
			
		||||
@ -255,7 +248,7 @@ pub fn new_ipc<D: rpc_apis::Dependencies>(
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	match rpc::start_ipc(&conf.socket_addr, handler, remote, rpc::RpcExtractor) {
 | 
			
		||||
	match rpc::start_ipc(&conf.socket_addr, handler, rpc::RpcExtractor) {
 | 
			
		||||
		Ok(server) => Ok(Some(server)),
 | 
			
		||||
		Err(io_error) => Err(format!("IPC error: {}", io_error)),
 | 
			
		||||
	}
 | 
			
		||||
@ -294,7 +287,7 @@ pub fn setup_apis<D>(apis: ApiSet, deps: &Dependencies<D>) -> MetaIoHandler<Meta
 | 
			
		||||
	where D: rpc_apis::Dependencies
 | 
			
		||||
{
 | 
			
		||||
	let mut handler = MetaIoHandler::with_middleware(
 | 
			
		||||
		Middleware::new(deps.stats.clone(), deps.apis.activity_notifier(), deps.pool.clone())
 | 
			
		||||
		Middleware::new(deps.stats.clone(), deps.apis.activity_notifier())
 | 
			
		||||
	);
 | 
			
		||||
	let apis = apis.list_apis();
 | 
			
		||||
	deps.apis.extend_with_set(&mut handler, &apis);
 | 
			
		||||
 | 
			
		||||
@ -28,13 +28,12 @@ use ethcore::miner::Miner;
 | 
			
		||||
use ethcore::snapshot::SnapshotService;
 | 
			
		||||
use ethcore_logger::RotatingLogger;
 | 
			
		||||
use sync::{ManageNetwork, SyncProvider, LightSync};
 | 
			
		||||
use futures_cpupool::CpuPool;
 | 
			
		||||
use hash_fetch::fetch::Client as FetchClient;
 | 
			
		||||
use jsonrpc_core::{self as core, MetaIoHandler};
 | 
			
		||||
use light::client::LightChainClient;
 | 
			
		||||
use light::{TransactionQueue as LightTransactionQueue, Cache as LightDataCache};
 | 
			
		||||
use miner::external::ExternalMiner;
 | 
			
		||||
use parity_reactor;
 | 
			
		||||
use parity_runtime::Executor;
 | 
			
		||||
use parity_rpc::dispatch::{FullDispatcher, LightDispatcher};
 | 
			
		||||
use parity_rpc::informant::{ActivityNotifier, ClientNotifier};
 | 
			
		||||
use parity_rpc::{Metadata, NetworkSettings, Host};
 | 
			
		||||
@ -231,8 +230,7 @@ pub struct FullDependencies {
 | 
			
		||||
	pub geth_compatibility: bool,
 | 
			
		||||
	pub ws_address: Option<Host>,
 | 
			
		||||
	pub fetch: FetchClient,
 | 
			
		||||
	pub pool: CpuPool,
 | 
			
		||||
	pub remote: parity_reactor::Remote,
 | 
			
		||||
	pub executor: Executor,
 | 
			
		||||
	pub whisper_rpc: Option<::whisper::RpcFactory>,
 | 
			
		||||
	pub gas_price_percentile: usize,
 | 
			
		||||
	pub poll_lifetime: u32,
 | 
			
		||||
@ -253,7 +251,7 @@ impl FullDependencies {
 | 
			
		||||
					let deps = &$deps;
 | 
			
		||||
					let dispatcher = FullDispatcher::new(deps.client.clone(), deps.miner.clone(), $nonces, deps.gas_price_percentile);
 | 
			
		||||
					if deps.signer_service.is_enabled() {
 | 
			
		||||
						$handler.extend_with($namespace::to_delegate(SigningQueueClient::new(&deps.signer_service, dispatcher, deps.remote.clone(), &deps.secret_store)))
 | 
			
		||||
						$handler.extend_with($namespace::to_delegate(SigningQueueClient::new(&deps.signer_service, dispatcher, deps.executor.clone(), &deps.secret_store)))
 | 
			
		||||
					} else {
 | 
			
		||||
						$handler.extend_with($namespace::to_delegate(SigningUnsafeClient::new(&deps.secret_store, dispatcher)))
 | 
			
		||||
					}
 | 
			
		||||
@ -261,7 +259,7 @@ impl FullDependencies {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		let nonces = Arc::new(Mutex::new(dispatch::Reservations::with_pool(self.pool.clone())));
 | 
			
		||||
		let nonces = Arc::new(Mutex::new(dispatch::Reservations::new(self.executor.clone())));
 | 
			
		||||
		let dispatcher = FullDispatcher::new(
 | 
			
		||||
			self.client.clone(),
 | 
			
		||||
			self.miner.clone(),
 | 
			
		||||
@ -306,7 +304,7 @@ impl FullDependencies {
 | 
			
		||||
				},
 | 
			
		||||
				Api::EthPubSub => {
 | 
			
		||||
					if !for_generic_pubsub {
 | 
			
		||||
						let client = EthPubSubClient::new(self.client.clone(), self.remote.clone());
 | 
			
		||||
						let client = EthPubSubClient::new(self.client.clone(), self.executor.clone());
 | 
			
		||||
						let h = client.handler();
 | 
			
		||||
						self.miner.add_transactions_listener(Box::new(move |hashes| if let Some(h) = h.upgrade() {
 | 
			
		||||
							h.notify_new_transactions(hashes);
 | 
			
		||||
@ -322,7 +320,7 @@ impl FullDependencies {
 | 
			
		||||
					handler.extend_with(PersonalClient::new(&self.secret_store, dispatcher.clone(), self.geth_compatibility).to_delegate());
 | 
			
		||||
				},
 | 
			
		||||
				Api::Signer => {
 | 
			
		||||
					handler.extend_with(SignerClient::new(&self.secret_store, dispatcher.clone(), &self.signer_service, self.remote.clone()).to_delegate());
 | 
			
		||||
					handler.extend_with(SignerClient::new(&self.secret_store, dispatcher.clone(), &self.signer_service, self.executor.clone()).to_delegate());
 | 
			
		||||
				},
 | 
			
		||||
				Api::Parity => {
 | 
			
		||||
					let signer = match self.signer_service.is_enabled() {
 | 
			
		||||
@ -351,7 +349,7 @@ impl FullDependencies {
 | 
			
		||||
						let mut rpc = MetaIoHandler::default();
 | 
			
		||||
						let apis = ApiSet::List(apis.clone()).retain(ApiSet::PubSub).list_apis();
 | 
			
		||||
						self.extend_api(&mut rpc, &apis, true);
 | 
			
		||||
						handler.extend_with(PubSubClient::new(rpc, self.remote.clone()).to_delegate());
 | 
			
		||||
						handler.extend_with(PubSubClient::new(rpc, self.executor.clone()).to_delegate());
 | 
			
		||||
					}
 | 
			
		||||
				},
 | 
			
		||||
				Api::ParityAccounts => {
 | 
			
		||||
@ -364,7 +362,6 @@ impl FullDependencies {
 | 
			
		||||
						&self.updater,
 | 
			
		||||
						&self.net_service,
 | 
			
		||||
						self.fetch.clone(),
 | 
			
		||||
						self.pool.clone(),
 | 
			
		||||
					).to_delegate())
 | 
			
		||||
				},
 | 
			
		||||
				Api::Traces => {
 | 
			
		||||
@ -440,9 +437,8 @@ pub struct LightDependencies<T> {
 | 
			
		||||
	pub transaction_queue: Arc<RwLock<LightTransactionQueue>>,
 | 
			
		||||
	pub ws_address: Option<Host>,
 | 
			
		||||
	pub fetch: FetchClient,
 | 
			
		||||
	pub pool: CpuPool,
 | 
			
		||||
	pub geth_compatibility: bool,
 | 
			
		||||
	pub remote: parity_reactor::Remote,
 | 
			
		||||
	pub executor: Executor,
 | 
			
		||||
	pub whisper_rpc: Option<::whisper::RpcFactory>,
 | 
			
		||||
	pub private_tx_service: Option<Arc<PrivateTransactionManager>>,
 | 
			
		||||
	pub gas_price_percentile: usize,
 | 
			
		||||
@ -464,7 +460,7 @@ impl<C: LightChainClient + 'static> LightDependencies<C> {
 | 
			
		||||
			self.on_demand.clone(),
 | 
			
		||||
			self.cache.clone(),
 | 
			
		||||
			self.transaction_queue.clone(),
 | 
			
		||||
			Arc::new(Mutex::new(dispatch::Reservations::with_pool(self.pool.clone()))),
 | 
			
		||||
			Arc::new(Mutex::new(dispatch::Reservations::new(self.executor.clone()))),
 | 
			
		||||
			self.gas_price_percentile,
 | 
			
		||||
		);
 | 
			
		||||
 | 
			
		||||
@ -476,7 +472,7 @@ impl<C: LightChainClient + 'static> LightDependencies<C> {
 | 
			
		||||
					let secret_store = deps.secret_store.clone();
 | 
			
		||||
					if deps.signer_service.is_enabled() {
 | 
			
		||||
						$handler.extend_with($namespace::to_delegate(
 | 
			
		||||
							SigningQueueClient::new(&deps.signer_service, dispatcher, deps.remote.clone(), &secret_store)
 | 
			
		||||
							SigningQueueClient::new(&deps.signer_service, dispatcher, deps.executor.clone(), &secret_store)
 | 
			
		||||
						))
 | 
			
		||||
					} else {
 | 
			
		||||
						$handler.extend_with(
 | 
			
		||||
@ -522,7 +518,7 @@ impl<C: LightChainClient + 'static> LightDependencies<C> {
 | 
			
		||||
						self.on_demand.clone(),
 | 
			
		||||
						self.sync.clone(),
 | 
			
		||||
						self.cache.clone(),
 | 
			
		||||
						self.remote.clone(),
 | 
			
		||||
						self.executor.clone(),
 | 
			
		||||
						self.gas_price_percentile,
 | 
			
		||||
					);
 | 
			
		||||
					self.client.add_listener(client.handler() as Weak<_>);
 | 
			
		||||
@ -538,7 +534,7 @@ impl<C: LightChainClient + 'static> LightDependencies<C> {
 | 
			
		||||
					handler.extend_with(PersonalClient::new(&self.secret_store, dispatcher.clone(), self.geth_compatibility).to_delegate());
 | 
			
		||||
				},
 | 
			
		||||
				Api::Signer => {
 | 
			
		||||
					handler.extend_with(SignerClient::new(&self.secret_store, dispatcher.clone(), &self.signer_service, self.remote.clone()).to_delegate());
 | 
			
		||||
					handler.extend_with(SignerClient::new(&self.secret_store, dispatcher.clone(), &self.signer_service, self.executor.clone()).to_delegate());
 | 
			
		||||
				},
 | 
			
		||||
				Api::Parity => {
 | 
			
		||||
					let signer = match self.signer_service.is_enabled() {
 | 
			
		||||
@ -565,7 +561,7 @@ impl<C: LightChainClient + 'static> LightDependencies<C> {
 | 
			
		||||
						let mut rpc = MetaIoHandler::default();
 | 
			
		||||
						let apis = ApiSet::List(apis.clone()).retain(ApiSet::PubSub).list_apis();
 | 
			
		||||
						self.extend_api(&mut rpc, &apis, true);
 | 
			
		||||
						handler.extend_with(PubSubClient::new(rpc, self.remote.clone()).to_delegate());
 | 
			
		||||
						handler.extend_with(PubSubClient::new(rpc, self.executor.clone()).to_delegate());
 | 
			
		||||
					}
 | 
			
		||||
				},
 | 
			
		||||
				Api::ParityAccounts => {
 | 
			
		||||
@ -575,7 +571,6 @@ impl<C: LightChainClient + 'static> LightDependencies<C> {
 | 
			
		||||
					handler.extend_with(light::ParitySetClient::new(
 | 
			
		||||
						self.sync.clone(),
 | 
			
		||||
						self.fetch.clone(),
 | 
			
		||||
						self.pool.clone(),
 | 
			
		||||
					).to_delegate())
 | 
			
		||||
				},
 | 
			
		||||
				Api::Traces => {
 | 
			
		||||
 | 
			
		||||
@ -34,14 +34,13 @@ use ethereum_types::Address;
 | 
			
		||||
use sync::{self, SyncConfig};
 | 
			
		||||
use miner::work_notify::WorkPoster;
 | 
			
		||||
use futures::IntoFuture;
 | 
			
		||||
use futures_cpupool::CpuPool;
 | 
			
		||||
use hash_fetch::{self, fetch};
 | 
			
		||||
use informant::{Informant, LightNodeInformantData, FullNodeInformantData};
 | 
			
		||||
use journaldb::Algorithm;
 | 
			
		||||
use light::Cache as LightDataCache;
 | 
			
		||||
use miner::external::ExternalMiner;
 | 
			
		||||
use node_filter::NodeFilter;
 | 
			
		||||
use parity_reactor::EventLoop;
 | 
			
		||||
use parity_runtime::Runtime;
 | 
			
		||||
use parity_rpc::{Origin, Metadata, NetworkSettings, informant, is_major_importing};
 | 
			
		||||
use updater::{UpdatePolicy, Updater};
 | 
			
		||||
use parity_version::version;
 | 
			
		||||
@ -270,7 +269,7 @@ fn execute_light_impl(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<Runnin
 | 
			
		||||
	*sync_handle.write() = Arc::downgrade(&light_sync);
 | 
			
		||||
 | 
			
		||||
	// spin up event loop
 | 
			
		||||
	let event_loop = EventLoop::spawn();
 | 
			
		||||
	let runtime = Runtime::with_default_thread_count();
 | 
			
		||||
 | 
			
		||||
	// queue cull service.
 | 
			
		||||
	let queue_cull = Arc::new(::light_helpers::QueueCull {
 | 
			
		||||
@ -278,7 +277,7 @@ fn execute_light_impl(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<Runnin
 | 
			
		||||
		sync: light_sync.clone(),
 | 
			
		||||
		on_demand: on_demand.clone(),
 | 
			
		||||
		txq: txq.clone(),
 | 
			
		||||
		remote: event_loop.remote(),
 | 
			
		||||
		executor: runtime.executor(),
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	service.register_handler(queue_cull).map_err(|e| format!("Error attaching service: {:?}", e))?;
 | 
			
		||||
@ -286,8 +285,6 @@ fn execute_light_impl(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<Runnin
 | 
			
		||||
	// start the network.
 | 
			
		||||
	light_sync.start_network();
 | 
			
		||||
 | 
			
		||||
	let cpu_pool = CpuPool::new(4);
 | 
			
		||||
 | 
			
		||||
	// fetch service
 | 
			
		||||
	let fetch = fetch::Client::new(FETCH_LIGHT_NUM_DNS_THREADS).map_err(|e| format!("Error starting fetch client: {:?}", e))?;
 | 
			
		||||
	let passwords = passwords_from_files(&cmd.acc_conf.password_files)?;
 | 
			
		||||
@ -313,9 +310,8 @@ fn execute_light_impl(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<Runnin
 | 
			
		||||
		transaction_queue: txq,
 | 
			
		||||
		ws_address: cmd.ws_conf.address(),
 | 
			
		||||
		fetch: fetch,
 | 
			
		||||
		pool: cpu_pool.clone(),
 | 
			
		||||
		geth_compatibility: cmd.geth_compatibility,
 | 
			
		||||
		remote: event_loop.remote(),
 | 
			
		||||
		executor: runtime.executor(),
 | 
			
		||||
		whisper_rpc: whisper_factory,
 | 
			
		||||
		private_tx_service: None, //TODO: add this to client.
 | 
			
		||||
		gas_price_percentile: cmd.gas_price_percentile,
 | 
			
		||||
@ -324,13 +320,8 @@ fn execute_light_impl(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<Runnin
 | 
			
		||||
 | 
			
		||||
	let dependencies = rpc::Dependencies {
 | 
			
		||||
		apis: deps_for_rpc_apis.clone(),
 | 
			
		||||
		remote: event_loop.raw_remote(),
 | 
			
		||||
		executor: runtime.executor(),
 | 
			
		||||
		stats: rpc_stats.clone(),
 | 
			
		||||
		pool: if cmd.http_conf.processing_threads > 0 {
 | 
			
		||||
			Some(rpc::CpuPool::new(cmd.http_conf.processing_threads))
 | 
			
		||||
		} else {
 | 
			
		||||
			None
 | 
			
		||||
		},
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// start rpc servers
 | 
			
		||||
@ -358,7 +349,7 @@ fn execute_light_impl(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<Runnin
 | 
			
		||||
			rpc: rpc_direct,
 | 
			
		||||
			informant,
 | 
			
		||||
			client,
 | 
			
		||||
			keep_alive: Box::new((event_loop, service, ws_server, http_server, ipc_server)),
 | 
			
		||||
			keep_alive: Box::new((runtime, service, ws_server, http_server, ipc_server)),
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
@ -477,10 +468,8 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
 | 
			
		||||
	// prepare account provider
 | 
			
		||||
	let account_provider = Arc::new(prepare_account_provider(&cmd.spec, &cmd.dirs, &spec.data_dir, cmd.acc_conf, &passwords)?);
 | 
			
		||||
 | 
			
		||||
	let cpu_pool = CpuPool::new(4);
 | 
			
		||||
 | 
			
		||||
	// spin up event loop
 | 
			
		||||
	let event_loop = EventLoop::spawn();
 | 
			
		||||
	let runtime = Runtime::with_default_thread_count();
 | 
			
		||||
 | 
			
		||||
	// fetch service
 | 
			
		||||
	let fetch = fetch::Client::new(FETCH_FULL_NUM_DNS_THREADS).map_err(|e| format!("Error starting fetch client: {:?}", e))?;
 | 
			
		||||
@ -489,7 +478,7 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
 | 
			
		||||
	// create miner
 | 
			
		||||
	let miner = Arc::new(Miner::new(
 | 
			
		||||
		cmd.miner_options,
 | 
			
		||||
		cmd.gas_pricer_conf.to_gas_pricer(fetch.clone(), cpu_pool.clone()),
 | 
			
		||||
		cmd.gas_pricer_conf.to_gas_pricer(fetch.clone(), runtime.executor()),
 | 
			
		||||
		&spec,
 | 
			
		||||
		Some(account_provider.clone()),
 | 
			
		||||
 | 
			
		||||
@ -500,7 +489,7 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
 | 
			
		||||
 | 
			
		||||
	if !cmd.miner_extras.work_notify.is_empty() {
 | 
			
		||||
		miner.add_work_listener(Box::new(
 | 
			
		||||
			WorkPoster::new(&cmd.miner_extras.work_notify, fetch.clone(), event_loop.remote())
 | 
			
		||||
			WorkPoster::new(&cmd.miner_extras.work_notify, fetch.clone(), runtime.executor())
 | 
			
		||||
		));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -698,7 +687,7 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
 | 
			
		||||
		&Arc::downgrade(&(service.client() as Arc<BlockChainClient>)),
 | 
			
		||||
		&Arc::downgrade(&sync_provider),
 | 
			
		||||
		update_policy,
 | 
			
		||||
		hash_fetch::Client::with_fetch(contract_client.clone(), cpu_pool.clone(), updater_fetch, event_loop.remote())
 | 
			
		||||
		hash_fetch::Client::with_fetch(contract_client.clone(), updater_fetch, runtime.executor())
 | 
			
		||||
	);
 | 
			
		||||
	service.add_notify(updater.clone());
 | 
			
		||||
 | 
			
		||||
@ -723,8 +712,7 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
 | 
			
		||||
		geth_compatibility: cmd.geth_compatibility,
 | 
			
		||||
		ws_address: cmd.ws_conf.address(),
 | 
			
		||||
		fetch: fetch.clone(),
 | 
			
		||||
		pool: cpu_pool.clone(),
 | 
			
		||||
		remote: event_loop.remote(),
 | 
			
		||||
		executor: runtime.executor(),
 | 
			
		||||
		whisper_rpc: whisper_factory,
 | 
			
		||||
		private_tx_service: Some(private_tx_service.clone()),
 | 
			
		||||
		gas_price_percentile: cmd.gas_price_percentile,
 | 
			
		||||
@ -733,14 +721,8 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
 | 
			
		||||
 | 
			
		||||
	let dependencies = rpc::Dependencies {
 | 
			
		||||
		apis: deps_for_rpc_apis.clone(),
 | 
			
		||||
		remote: event_loop.raw_remote(),
 | 
			
		||||
		executor: runtime.executor(),
 | 
			
		||||
		stats: rpc_stats.clone(),
 | 
			
		||||
		pool: if cmd.http_conf.processing_threads > 0 {
 | 
			
		||||
			Some(rpc::CpuPool::new(cmd.http_conf.processing_threads))
 | 
			
		||||
		} else {
 | 
			
		||||
			None
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// start rpc servers
 | 
			
		||||
@ -820,7 +802,7 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
 | 
			
		||||
			informant,
 | 
			
		||||
			client,
 | 
			
		||||
			client_service: Arc::new(service),
 | 
			
		||||
			keep_alive: Box::new((watcher, updater, ws_server, http_server, ipc_server, secretstore_key_server, ipfs_server, event_loop)),
 | 
			
		||||
			keep_alive: Box::new((watcher, updater, ws_server, http_server, ipc_server, secretstore_key_server, ipfs_server, runtime)),
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -9,11 +9,10 @@ authors = ["Parity Technologies <admin@parity.io>"]
 | 
			
		||||
[dependencies]
 | 
			
		||||
fetch = { path = "../util/fetch" }
 | 
			
		||||
futures = "0.1"
 | 
			
		||||
futures-cpupool = "0.1"
 | 
			
		||||
parity-runtime = { path = "../util/runtime" }
 | 
			
		||||
log = "0.4"
 | 
			
		||||
serde_json = "1.0"
 | 
			
		||||
 | 
			
		||||
[dev-dependencies]
 | 
			
		||||
hyper = "0.11"
 | 
			
		||||
parking_lot = "0.6"
 | 
			
		||||
fake-fetch = { path = "../util/fake-fetch" }
 | 
			
		||||
 | 
			
		||||
@ -19,8 +19,8 @@
 | 
			
		||||
//! A simple client to get the current ETH price using an external API.
 | 
			
		||||
 | 
			
		||||
extern crate futures;
 | 
			
		||||
extern crate futures_cpupool;
 | 
			
		||||
extern crate serde_json;
 | 
			
		||||
extern crate parity_runtime;
 | 
			
		||||
 | 
			
		||||
#[macro_use]
 | 
			
		||||
extern crate log;
 | 
			
		||||
@ -38,8 +38,8 @@ use std::str;
 | 
			
		||||
use fetch::{Client as FetchClient, Fetch};
 | 
			
		||||
use futures::{Future, Stream};
 | 
			
		||||
use futures::future::{self, Either};
 | 
			
		||||
use futures_cpupool::CpuPool;
 | 
			
		||||
use serde_json::Value;
 | 
			
		||||
use parity_runtime::Executor;
 | 
			
		||||
 | 
			
		||||
/// Current ETH price information.
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
@ -71,7 +71,7 @@ impl From<fetch::Error> for Error {
 | 
			
		||||
 | 
			
		||||
/// A client to get the current ETH price using an external API.
 | 
			
		||||
pub struct Client<F = FetchClient> {
 | 
			
		||||
	pool: CpuPool,
 | 
			
		||||
	pool: Executor,
 | 
			
		||||
	api_endpoint: String,
 | 
			
		||||
	fetch: F,
 | 
			
		||||
}
 | 
			
		||||
@ -92,7 +92,7 @@ impl<F> cmp::PartialEq for Client<F> {
 | 
			
		||||
 | 
			
		||||
impl<F: Fetch> Client<F> {
 | 
			
		||||
	/// Creates a new instance of the `Client` given a `fetch::Client`.
 | 
			
		||||
	pub fn new(fetch: F, pool: CpuPool) -> Client<F> {
 | 
			
		||||
	pub fn new(fetch: F, pool: Executor) -> Client<F> {
 | 
			
		||||
		let api_endpoint = "https://api.etherscan.io/api?module=stats&action=ethprice".to_owned();
 | 
			
		||||
		Client { pool, api_endpoint, fetch }
 | 
			
		||||
	}
 | 
			
		||||
@ -108,7 +108,7 @@ impl<F: Fetch> Client<F> {
 | 
			
		||||
				}
 | 
			
		||||
				Either::B(response.concat2().from_err())
 | 
			
		||||
			})
 | 
			
		||||
			.map(move |body| {
 | 
			
		||||
			.and_then(move |body| {
 | 
			
		||||
				let body_str = str::from_utf8(&body).ok();
 | 
			
		||||
				let value: Option<Value> = body_str.and_then(|s| serde_json::from_str(s).ok());
 | 
			
		||||
 | 
			
		||||
@ -128,30 +128,31 @@ impl<F: Fetch> Client<F> {
 | 
			
		||||
			})
 | 
			
		||||
			.map_err(|err| {
 | 
			
		||||
				warn!("Failed to auto-update latest ETH price: {:?}", err);
 | 
			
		||||
				err
 | 
			
		||||
			});
 | 
			
		||||
		self.pool.spawn(future).forget()
 | 
			
		||||
		self.pool.spawn(future)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
	use std::sync::Arc;
 | 
			
		||||
	use futures_cpupool::CpuPool;
 | 
			
		||||
	use parity_runtime::{Runtime, Executor};
 | 
			
		||||
	use Client;
 | 
			
		||||
	use std::sync::atomic::{AtomicBool, Ordering};
 | 
			
		||||
	use fake_fetch::FakeFetch;
 | 
			
		||||
 | 
			
		||||
	fn price_info_ok(response: &str) -> Client<FakeFetch<String>> {
 | 
			
		||||
		Client::new(FakeFetch::new(Some(response.to_owned())), CpuPool::new(1))
 | 
			
		||||
	fn price_info_ok(response: &str, executor: Executor) -> Client<FakeFetch<String>> {
 | 
			
		||||
		Client::new(FakeFetch::new(Some(response.to_owned())), executor)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fn price_info_not_found() -> Client<FakeFetch<String>> {
 | 
			
		||||
		Client::new(FakeFetch::new(None::<String>), CpuPool::new(1))
 | 
			
		||||
	fn price_info_not_found(executor: Executor) -> Client<FakeFetch<String>> {
 | 
			
		||||
		Client::new(FakeFetch::new(None::<String>), executor)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	#[test]
 | 
			
		||||
	fn should_get_price_info() {
 | 
			
		||||
		let runtime = Runtime::with_thread_count(1);
 | 
			
		||||
 | 
			
		||||
		// given
 | 
			
		||||
		let response = r#"{
 | 
			
		||||
			"status": "1",
 | 
			
		||||
@ -164,7 +165,7 @@ mod test {
 | 
			
		||||
			}
 | 
			
		||||
		}"#;
 | 
			
		||||
 | 
			
		||||
		let price_info = price_info_ok(response);
 | 
			
		||||
		let price_info = price_info_ok(response, runtime.executor());
 | 
			
		||||
 | 
			
		||||
		// when
 | 
			
		||||
		price_info.get(|price| {
 | 
			
		||||
@ -176,10 +177,12 @@ mod test {
 | 
			
		||||
 | 
			
		||||
	#[test]
 | 
			
		||||
	fn should_not_call_set_price_if_response_is_malformed() {
 | 
			
		||||
		let runtime = Runtime::with_thread_count(1);
 | 
			
		||||
 | 
			
		||||
		// given
 | 
			
		||||
		let response = "{}";
 | 
			
		||||
 | 
			
		||||
		let price_info = price_info_ok(response);
 | 
			
		||||
		let price_info = price_info_ok(response, runtime.executor());
 | 
			
		||||
		let b = Arc::new(AtomicBool::new(false));
 | 
			
		||||
 | 
			
		||||
		// when
 | 
			
		||||
@ -194,8 +197,10 @@ mod test {
 | 
			
		||||
 | 
			
		||||
	#[test]
 | 
			
		||||
	fn should_not_call_set_price_if_response_is_invalid() {
 | 
			
		||||
		let runtime = Runtime::with_thread_count(1);
 | 
			
		||||
 | 
			
		||||
		// given
 | 
			
		||||
		let price_info = price_info_not_found();
 | 
			
		||||
		let price_info = price_info_not_found(runtime.executor());
 | 
			
		||||
		let b = Arc::new(AtomicBool::new(false));
 | 
			
		||||
 | 
			
		||||
		// when
 | 
			
		||||
 | 
			
		||||
@ -9,11 +9,10 @@ authors = ["Parity Technologies <admin@parity.io>"]
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
ansi_term = "0.10"
 | 
			
		||||
cid = "0.2"
 | 
			
		||||
cid = "0.3"
 | 
			
		||||
futures = "0.1.6"
 | 
			
		||||
futures-cpupool = "0.1"
 | 
			
		||||
log = "0.4"
 | 
			
		||||
multihash ="0.7"
 | 
			
		||||
multihash = "0.8"
 | 
			
		||||
order-stat = "0.1"
 | 
			
		||||
parking_lot = "0.6"
 | 
			
		||||
rand = "0.4"
 | 
			
		||||
@ -28,17 +27,17 @@ tokio-timer = "0.1"
 | 
			
		||||
transient-hashmap = "0.4"
 | 
			
		||||
itertools = "0.5"
 | 
			
		||||
 | 
			
		||||
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" }
 | 
			
		||||
jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" }
 | 
			
		||||
jsonrpc-ws-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" }
 | 
			
		||||
jsonrpc-ipc-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" }
 | 
			
		||||
jsonrpc-macros = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" }
 | 
			
		||||
jsonrpc-pubsub = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" }
 | 
			
		||||
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-2.2" }
 | 
			
		||||
jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-2.2" }
 | 
			
		||||
jsonrpc-ws-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-2.2" }
 | 
			
		||||
jsonrpc-ipc-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-2.2" }
 | 
			
		||||
jsonrpc-macros = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-2.2" }
 | 
			
		||||
jsonrpc-pubsub = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-2.2" }
 | 
			
		||||
 | 
			
		||||
ethash = { path = "../ethash" }
 | 
			
		||||
ethcore = { path = "../ethcore", features = ["test-helpers"] }
 | 
			
		||||
parity-bytes = "0.1"
 | 
			
		||||
parity-crypto = "0.1"
 | 
			
		||||
parity-crypto = "0.2"
 | 
			
		||||
fastmap = { path = "../util/fastmap" }
 | 
			
		||||
ethcore-devtools = { path = "../devtools" }
 | 
			
		||||
ethcore-io = { path = "../util/io" }
 | 
			
		||||
@ -55,7 +54,7 @@ ethkey = { path = "../ethkey" }
 | 
			
		||||
ethstore = { path = "../ethstore" }
 | 
			
		||||
fetch = { path = "../util/fetch" }
 | 
			
		||||
keccak-hash = "0.1.2"
 | 
			
		||||
parity-reactor = { path = "../util/reactor" }
 | 
			
		||||
parity-runtime = { path = "../util/runtime" }
 | 
			
		||||
parity-updater = { path = "../updater" }
 | 
			
		||||
parity-version = { path = "../util/version" }
 | 
			
		||||
patricia-trie = "0.3.0"
 | 
			
		||||
 | 
			
		||||
@ -42,13 +42,13 @@ impl<M, T> http::MetaExtractor<M> for MetaExtractor<T> where
 | 
			
		||||
	T: HttpMetaExtractor<Metadata = M>,
 | 
			
		||||
	M: jsonrpc_core::Metadata,
 | 
			
		||||
{
 | 
			
		||||
	fn read_metadata(&self, req: &hyper::server::Request) -> M {
 | 
			
		||||
		let as_string = |header: Option<&hyper::header::Raw>| header
 | 
			
		||||
			.and_then(|raw| raw.one())
 | 
			
		||||
			.map(|raw| String::from_utf8_lossy(raw).into_owned());
 | 
			
		||||
	fn read_metadata(&self, req: &hyper::Request<hyper::Body>) -> M {
 | 
			
		||||
		let as_string = |header: Option<&hyper::header::HeaderValue>| {
 | 
			
		||||
			header.and_then(|val| val.to_str().ok().map(|s| s.to_owned()))
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		let origin = as_string(req.headers().get_raw("origin"));
 | 
			
		||||
		let user_agent = as_string(req.headers().get_raw("user-agent"));
 | 
			
		||||
		let origin = as_string(req.headers().get("origin"));
 | 
			
		||||
		let user_agent = as_string(req.headers().get("user-agent"));
 | 
			
		||||
		self.extractor.read_metadata(origin, user_agent)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,6 @@ extern crate futures;
 | 
			
		||||
 | 
			
		||||
extern crate ansi_term;
 | 
			
		||||
extern crate cid;
 | 
			
		||||
extern crate futures_cpupool;
 | 
			
		||||
extern crate itertools;
 | 
			
		||||
extern crate multihash;
 | 
			
		||||
extern crate order_stat;
 | 
			
		||||
@ -60,7 +59,7 @@ extern crate ethkey;
 | 
			
		||||
extern crate ethstore;
 | 
			
		||||
extern crate fetch;
 | 
			
		||||
extern crate keccak_hash as hash;
 | 
			
		||||
extern crate parity_reactor;
 | 
			
		||||
extern crate parity_runtime;
 | 
			
		||||
extern crate parity_updater as updater;
 | 
			
		||||
extern crate parity_version as version;
 | 
			
		||||
extern crate patricia_trie as trie;
 | 
			
		||||
@ -124,7 +123,6 @@ pub use authcodes::{AuthCodes, TimeProvider};
 | 
			
		||||
pub use http_common::HttpMetaExtractor;
 | 
			
		||||
 | 
			
		||||
use std::net::SocketAddr;
 | 
			
		||||
use http::tokio_core;
 | 
			
		||||
 | 
			
		||||
/// RPC HTTP Server instance
 | 
			
		||||
pub type HttpServer = http::Server;
 | 
			
		||||
@ -135,7 +133,6 @@ pub fn start_http<M, S, H, T>(
 | 
			
		||||
	cors_domains: http::DomainsValidation<http::AccessControlAllowOrigin>,
 | 
			
		||||
	allowed_hosts: http::DomainsValidation<http::Host>,
 | 
			
		||||
	handler: H,
 | 
			
		||||
	remote: tokio_core::reactor::Remote,
 | 
			
		||||
	extractor: T,
 | 
			
		||||
	threads: usize,
 | 
			
		||||
	max_payload: usize,
 | 
			
		||||
@ -148,7 +145,6 @@ pub fn start_http<M, S, H, T>(
 | 
			
		||||
	let extractor = http_common::MetaExtractor::new(extractor);
 | 
			
		||||
	Ok(http::ServerBuilder::with_meta_extractor(handler, extractor)
 | 
			
		||||
		.threads(threads)
 | 
			
		||||
		.event_loop_remote(remote)
 | 
			
		||||
		.cors(cors_domains.into())
 | 
			
		||||
		.allowed_hosts(allowed_hosts.into())
 | 
			
		||||
		.max_request_body_size(max_payload * 1024 * 1024)
 | 
			
		||||
@ -162,7 +158,6 @@ pub fn start_http_with_middleware<M, S, H, T, R>(
 | 
			
		||||
	cors_domains: http::DomainsValidation<http::AccessControlAllowOrigin>,
 | 
			
		||||
	allowed_hosts: http::DomainsValidation<http::Host>,
 | 
			
		||||
	handler: H,
 | 
			
		||||
	remote: tokio_core::reactor::Remote,
 | 
			
		||||
	extractor: T,
 | 
			
		||||
	middleware: R,
 | 
			
		||||
	threads: usize,
 | 
			
		||||
@ -177,7 +172,6 @@ pub fn start_http_with_middleware<M, S, H, T, R>(
 | 
			
		||||
	let extractor = http_common::MetaExtractor::new(extractor);
 | 
			
		||||
	Ok(http::ServerBuilder::with_meta_extractor(handler, extractor)
 | 
			
		||||
		.threads(threads)
 | 
			
		||||
		.event_loop_remote(remote)
 | 
			
		||||
		.cors(cors_domains.into())
 | 
			
		||||
		.allowed_hosts(allowed_hosts.into())
 | 
			
		||||
		.max_request_body_size(max_payload * 1024 * 1024)
 | 
			
		||||
@ -189,7 +183,6 @@ pub fn start_http_with_middleware<M, S, H, T, R>(
 | 
			
		||||
pub fn start_ipc<M, S, H, T>(
 | 
			
		||||
	addr: &str,
 | 
			
		||||
	handler: H,
 | 
			
		||||
	remote: tokio_core::reactor::Remote,
 | 
			
		||||
	extractor: T,
 | 
			
		||||
) -> ::std::io::Result<ipc::Server> where
 | 
			
		||||
	M: jsonrpc_core::Metadata,
 | 
			
		||||
@ -198,7 +191,6 @@ pub fn start_ipc<M, S, H, T>(
 | 
			
		||||
	T: IpcMetaExtractor<M>,
 | 
			
		||||
{
 | 
			
		||||
	ipc::ServerBuilder::with_meta_extractor(handler, extractor)
 | 
			
		||||
		.event_loop_remote(remote)
 | 
			
		||||
		.start(addr)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -206,7 +198,6 @@ pub fn start_ipc<M, S, H, T>(
 | 
			
		||||
pub fn start_ws<M, S, H, T, U, V>(
 | 
			
		||||
	addr: &SocketAddr,
 | 
			
		||||
	handler: H,
 | 
			
		||||
	remote: tokio_core::reactor::Remote,
 | 
			
		||||
	allowed_origins: ws::DomainsValidation<ws::Origin>,
 | 
			
		||||
	allowed_hosts: ws::DomainsValidation<ws::Host>,
 | 
			
		||||
	max_connections: usize,
 | 
			
		||||
@ -222,7 +213,6 @@ pub fn start_ws<M, S, H, T, U, V>(
 | 
			
		||||
	V: ws::RequestMiddleware,
 | 
			
		||||
{
 | 
			
		||||
	ws::ServerBuilder::with_meta_extractor(handler, extractor)
 | 
			
		||||
		.event_loop_remote(remote)
 | 
			
		||||
		.request_middleware(middleware)
 | 
			
		||||
		.allowed_origins(allowed_origins)
 | 
			
		||||
		.allowed_hosts(allowed_hosts)
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,7 @@ use std::ops::{Deref, DerefMut};
 | 
			
		||||
use std::path::PathBuf;
 | 
			
		||||
use tempdir::TempDir;
 | 
			
		||||
 | 
			
		||||
use parity_reactor::{EventLoop, TokioRemote};
 | 
			
		||||
use parity_runtime::{Runtime, TaskExecutor};
 | 
			
		||||
 | 
			
		||||
use authcodes::AuthCodes;
 | 
			
		||||
 | 
			
		||||
@ -27,15 +27,15 @@ pub struct Server<T> {
 | 
			
		||||
	/// Server
 | 
			
		||||
	pub server: T,
 | 
			
		||||
	/// RPC Event Loop
 | 
			
		||||
	pub event_loop: EventLoop,
 | 
			
		||||
	pub event_loop: Runtime,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T> Server<T> {
 | 
			
		||||
	pub fn new<F>(f: F) -> Server<T> where
 | 
			
		||||
		F: FnOnce(TokioRemote) -> T,
 | 
			
		||||
		F: FnOnce(TaskExecutor) -> T,
 | 
			
		||||
	{
 | 
			
		||||
		let event_loop = EventLoop::spawn();
 | 
			
		||||
		let remote = event_loop.raw_remote();
 | 
			
		||||
		let event_loop = Runtime::with_thread_count(1);
 | 
			
		||||
		let remote = event_loop.raw_executor();
 | 
			
		||||
 | 
			
		||||
		Server {
 | 
			
		||||
			server: f(remote),
 | 
			
		||||
 | 
			
		||||
@ -26,14 +26,13 @@ fn serve(handler: Option<MetaIoHandler<Metadata>>) -> Server<HttpServer> {
 | 
			
		||||
	let address = "127.0.0.1:0".parse().unwrap();
 | 
			
		||||
	let handler = handler.unwrap_or_default();
 | 
			
		||||
 | 
			
		||||
	Server::new(|remote| ::start_http_with_middleware(
 | 
			
		||||
	Server::new(|_remote| ::start_http_with_middleware(
 | 
			
		||||
		&address,
 | 
			
		||||
		http::DomainsValidation::Disabled,
 | 
			
		||||
		http::DomainsValidation::Disabled,
 | 
			
		||||
		handler,
 | 
			
		||||
		remote,
 | 
			
		||||
		extractors::RpcExtractor,
 | 
			
		||||
		|request: hyper::Request| {
 | 
			
		||||
		|request: hyper::Request<hyper::Body>| {
 | 
			
		||||
			http::RequestMiddlewareAction::Proceed {
 | 
			
		||||
				should_continue_on_invalid_cors: false,
 | 
			
		||||
				request,
 | 
			
		||||
@ -50,7 +49,7 @@ fn request(server: Server<HttpServer>, request: &str) -> http_client::Response {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod testsing {
 | 
			
		||||
mod tests {
 | 
			
		||||
	use jsonrpc_core::{MetaIoHandler, Value};
 | 
			
		||||
	use v1::Metadata;
 | 
			
		||||
	use super::{request, Server};
 | 
			
		||||
@ -73,7 +72,7 @@ mod testsing {
 | 
			
		||||
 | 
			
		||||
		// when
 | 
			
		||||
		let req = r#"{"method":"hello","params":[],"jsonrpc":"2.0","id":1}"#;
 | 
			
		||||
		let expected = "4B\n{\"jsonrpc\":\"2.0\",\"result\":\"unknown origin / unknown agent via RPC\",\"id\":1}\n\n0\n\n";
 | 
			
		||||
		let expected = "{\"jsonrpc\":\"2.0\",\"result\":\"unknown origin / unknown agent via RPC\",\"id\":1}\n";
 | 
			
		||||
		let res = request(server,
 | 
			
		||||
			&format!("\
 | 
			
		||||
				POST / HTTP/1.1\r\n\
 | 
			
		||||
@ -98,7 +97,7 @@ mod testsing {
 | 
			
		||||
 | 
			
		||||
		// when
 | 
			
		||||
		let req = r#"{"method":"hello","params":[],"jsonrpc":"2.0","id":1}"#;
 | 
			
		||||
		let expected = "49\n{\"jsonrpc\":\"2.0\",\"result\":\"unknown origin / curl/7.16.3 via RPC\",\"id\":1}\n\n0\n\n";
 | 
			
		||||
		let expected = "{\"jsonrpc\":\"2.0\",\"result\":\"unknown origin / curl/7.16.3 via RPC\",\"id\":1}\n";
 | 
			
		||||
		let res = request(server,
 | 
			
		||||
			&format!("\
 | 
			
		||||
				POST / HTTP/1.1\r\n\
 | 
			
		||||
 | 
			
		||||
@ -34,10 +34,9 @@ pub fn serve() -> (Server<ws::Server>, usize, GuardedAuthCodes) {
 | 
			
		||||
	let authcodes = GuardedAuthCodes::new();
 | 
			
		||||
	let stats = Arc::new(informant::RpcStats::default());
 | 
			
		||||
 | 
			
		||||
	let res = Server::new(|remote| ::start_ws(
 | 
			
		||||
	let res = Server::new(|_| ::start_ws(
 | 
			
		||||
		&address,
 | 
			
		||||
		io,
 | 
			
		||||
		remote,
 | 
			
		||||
		ws::DomainsValidation::Disabled,
 | 
			
		||||
		ws::DomainsValidation::Disabled,
 | 
			
		||||
		5,
 | 
			
		||||
 | 
			
		||||
@ -23,6 +23,7 @@ use authcodes;
 | 
			
		||||
use http_common::HttpMetaExtractor;
 | 
			
		||||
use ipc;
 | 
			
		||||
use jsonrpc_core as core;
 | 
			
		||||
use jsonrpc_core::futures::future::Either;
 | 
			
		||||
use jsonrpc_pubsub::Session;
 | 
			
		||||
use ws;
 | 
			
		||||
use ethereum_types::H256;
 | 
			
		||||
@ -216,26 +217,26 @@ impl<M: core::Middleware<Metadata>> WsDispatcher<M> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<M: core::Middleware<Metadata>> core::Middleware<Metadata> for WsDispatcher<M> {
 | 
			
		||||
	type Future = core::futures::future::Either<
 | 
			
		||||
		M::Future,
 | 
			
		||||
	type Future = Either<
 | 
			
		||||
		core::FutureRpcResult<M::Future>,
 | 
			
		||||
		core::FutureResponse,
 | 
			
		||||
	>;
 | 
			
		||||
 | 
			
		||||
	fn on_request<F, X>(&self, request: core::Request, meta: Metadata, process: F) -> Self::Future where
 | 
			
		||||
	fn on_request<F, X>(&self, request: core::Request, meta: Metadata, process: F)
 | 
			
		||||
		-> Either<Self::Future, X>
 | 
			
		||||
	where
 | 
			
		||||
		F: FnOnce(core::Request, Metadata) -> X,
 | 
			
		||||
		X: core::futures::Future<Item=Option<core::Response>, Error=()> + Send + 'static,
 | 
			
		||||
	{
 | 
			
		||||
		use self::core::futures::future::Either::{A, B};
 | 
			
		||||
 | 
			
		||||
		let use_full = match &meta.origin {
 | 
			
		||||
			&Origin::Signer { .. } => true,
 | 
			
		||||
			_ => false,
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		if use_full {
 | 
			
		||||
			A(self.full_handler.handle_rpc_request(request, meta))
 | 
			
		||||
			Either::A(Either::A(self.full_handler.handle_rpc_request(request, meta)))
 | 
			
		||||
		} else {
 | 
			
		||||
			B(Box::new(process(request, meta)))
 | 
			
		||||
			Either::B(process(request, meta))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -23,31 +23,25 @@ use ethereum_types::{U256, Address};
 | 
			
		||||
use futures::{Future, future, Poll, Async};
 | 
			
		||||
use futures::future::Either;
 | 
			
		||||
use futures::sync::oneshot;
 | 
			
		||||
use futures_cpupool::CpuPool;
 | 
			
		||||
use parity_runtime::Executor;
 | 
			
		||||
 | 
			
		||||
/// Manages currently reserved and prospective nonces
 | 
			
		||||
/// for multiple senders.
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct Reservations {
 | 
			
		||||
	nonces: HashMap<Address, SenderReservations>,
 | 
			
		||||
	pool: CpuPool,
 | 
			
		||||
	executor: Executor,
 | 
			
		||||
}
 | 
			
		||||
impl Reservations {
 | 
			
		||||
	/// A maximal number of reserved nonces in the hashmap
 | 
			
		||||
	/// before we start clearing the unused ones.
 | 
			
		||||
	const CLEAN_AT: usize = 512;
 | 
			
		||||
 | 
			
		||||
	/// Create new nonces manager and spawn a single-threaded cpu pool
 | 
			
		||||
	/// for progressing execution of dropped nonces.
 | 
			
		||||
	pub fn new() -> Self {
 | 
			
		||||
		Self::with_pool(CpuPool::new(1))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Create new nonces manager with given cpupool.
 | 
			
		||||
	pub fn with_pool(pool: CpuPool) -> Self {
 | 
			
		||||
	/// Create new nonces manager with given executor.
 | 
			
		||||
	pub fn new(executor: Executor) -> Self {
 | 
			
		||||
		Reservations {
 | 
			
		||||
			nonces: Default::default(),
 | 
			
		||||
			pool,
 | 
			
		||||
			executor,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -59,9 +53,9 @@ impl Reservations {
 | 
			
		||||
			self.nonces.retain(|_, v| !v.is_empty());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		let pool = &self.pool;
 | 
			
		||||
		let executor = &self.executor;
 | 
			
		||||
		self.nonces.entry(sender)
 | 
			
		||||
			.or_insert_with(move || SenderReservations::with_pool(pool.clone()))
 | 
			
		||||
			.or_insert_with(move || SenderReservations::new(executor.clone()))
 | 
			
		||||
			.reserve_nonce(minimal)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -71,25 +65,18 @@ impl Reservations {
 | 
			
		||||
pub struct SenderReservations {
 | 
			
		||||
	previous: Option<oneshot::Receiver<U256>>,
 | 
			
		||||
	previous_ready: Arc<AtomicBool>,
 | 
			
		||||
	pool: CpuPool,
 | 
			
		||||
	executor: Executor,
 | 
			
		||||
	prospective_value: U256,
 | 
			
		||||
	dropped: Arc<AtomicUsize>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl SenderReservations {
 | 
			
		||||
	/// Create new nonces manager and spawn a single-threaded cpu pool
 | 
			
		||||
	/// for progressing execution of dropped nonces.
 | 
			
		||||
	#[cfg(test)]
 | 
			
		||||
	pub fn new() -> Self {
 | 
			
		||||
		Self::with_pool(CpuPool::new(1))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Create new nonces manager with given cpu pool.
 | 
			
		||||
	pub fn with_pool(pool: CpuPool) -> Self {
 | 
			
		||||
	/// Create new nonces manager with given executor.
 | 
			
		||||
	pub fn new(executor: Executor) -> Self {
 | 
			
		||||
		SenderReservations {
 | 
			
		||||
			previous: None,
 | 
			
		||||
			previous_ready: Arc::new(AtomicBool::new(true)),
 | 
			
		||||
			pool,
 | 
			
		||||
			executor,
 | 
			
		||||
			prospective_value: Default::default(),
 | 
			
		||||
			dropped: Default::default(),
 | 
			
		||||
		}
 | 
			
		||||
@ -110,7 +97,7 @@ impl SenderReservations {
 | 
			
		||||
		let (next, rx) = oneshot::channel();
 | 
			
		||||
		let next = Some(next);
 | 
			
		||||
		let next_sent = Arc::new(AtomicBool::default());
 | 
			
		||||
		let pool = self.pool.clone();
 | 
			
		||||
		let executor = self.executor.clone();
 | 
			
		||||
		let dropped = self.dropped.clone();
 | 
			
		||||
		self.previous_ready = next_sent.clone();
 | 
			
		||||
		match mem::replace(&mut self.previous, Some(rx)) {
 | 
			
		||||
@ -120,7 +107,7 @@ impl SenderReservations {
 | 
			
		||||
				next_sent,
 | 
			
		||||
				minimal,
 | 
			
		||||
				prospective_value,
 | 
			
		||||
				pool,
 | 
			
		||||
				executor,
 | 
			
		||||
				dropped,
 | 
			
		||||
			},
 | 
			
		||||
			None => Reserved {
 | 
			
		||||
@ -129,7 +116,7 @@ impl SenderReservations {
 | 
			
		||||
				next_sent,
 | 
			
		||||
				minimal,
 | 
			
		||||
				prospective_value,
 | 
			
		||||
				pool,
 | 
			
		||||
				executor,
 | 
			
		||||
				dropped,
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
@ -152,7 +139,7 @@ pub struct Reserved {
 | 
			
		||||
	next_sent: Arc<AtomicBool>,
 | 
			
		||||
	minimal: U256,
 | 
			
		||||
	prospective_value: U256,
 | 
			
		||||
	pool: CpuPool,
 | 
			
		||||
	executor: Executor,
 | 
			
		||||
	dropped: Arc<AtomicUsize>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -196,10 +183,14 @@ impl Drop for Reserved {
 | 
			
		||||
			self.dropped.fetch_add(1, atomic::Ordering::SeqCst);
 | 
			
		||||
			// If Reserved is dropped just pipe previous and next together.
 | 
			
		||||
			let previous = mem::replace(&mut self.previous, Either::B(future::ok(U256::default())));
 | 
			
		||||
			self.pool.spawn(previous.map(move |nonce| {
 | 
			
		||||
				next_sent.store(true, atomic::Ordering::SeqCst);
 | 
			
		||||
				next.send(nonce).expect(Ready::RECV_PROOF)
 | 
			
		||||
			})).forget()
 | 
			
		||||
			self.executor.spawn(
 | 
			
		||||
				previous
 | 
			
		||||
				.map(move |nonce| {
 | 
			
		||||
					next_sent.store(true, atomic::Ordering::SeqCst);
 | 
			
		||||
					next.send(nonce).expect(Ready::RECV_PROOF)
 | 
			
		||||
				})
 | 
			
		||||
				.map_err(|err| error!("Error dropping `Reserved`: {:?}", err))
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -253,10 +244,12 @@ impl Drop for Ready {
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use parity_runtime::Runtime;
 | 
			
		||||
 | 
			
		||||
	#[test]
 | 
			
		||||
	fn should_reserve_a_set_of_nonces_and_resolve_them() {
 | 
			
		||||
		let mut nonces = SenderReservations::new();
 | 
			
		||||
		let runtime = Runtime::with_thread_count(1);
 | 
			
		||||
		let mut nonces = SenderReservations::new(runtime.executor());
 | 
			
		||||
 | 
			
		||||
		assert!(nonces.is_empty());
 | 
			
		||||
		let n1 = nonces.reserve_nonce(5.into());
 | 
			
		||||
@ -303,7 +296,8 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
	#[test]
 | 
			
		||||
	fn should_return_prospective_nonce() {
 | 
			
		||||
		let mut nonces = SenderReservations::new();
 | 
			
		||||
		let runtime = Runtime::with_thread_count(1);
 | 
			
		||||
		let mut nonces = SenderReservations::new(runtime.executor());
 | 
			
		||||
 | 
			
		||||
		let n1 = nonces.reserve_nonce(5.into());
 | 
			
		||||
		let n2 = nonces.reserve_nonce(5.into());
 | 
			
		||||
 | 
			
		||||
@ -95,7 +95,7 @@ impl<S: core::Middleware<Metadata>> GenericPollManager<S> {
 | 
			
		||||
				jsonrpc: Some(core::Version::V2),
 | 
			
		||||
				id: core::Id::Str(id.as_string()),
 | 
			
		||||
				method: subscription.method.clone(),
 | 
			
		||||
				params: Some(subscription.params.clone()),
 | 
			
		||||
				params: subscription.params.clone(),
 | 
			
		||||
			};
 | 
			
		||||
			trace!(target: "pubsub", "Polling method: {:?}", call);
 | 
			
		||||
			let result = self.rpc.handle_call(call.into(), subscription.metadata.clone());
 | 
			
		||||
@ -141,7 +141,7 @@ mod tests {
 | 
			
		||||
	use jsonrpc_core::{MetaIoHandler, NoopMiddleware, Value, Params};
 | 
			
		||||
	use jsonrpc_core::futures::{Future, Stream};
 | 
			
		||||
	use jsonrpc_pubsub::SubscriptionId;
 | 
			
		||||
	use http::tokio_core::reactor;
 | 
			
		||||
	use http::tokio::runtime::Runtime;
 | 
			
		||||
 | 
			
		||||
	use super::GenericPollManager;
 | 
			
		||||
 | 
			
		||||
@ -162,25 +162,25 @@ mod tests {
 | 
			
		||||
	#[test]
 | 
			
		||||
	fn should_poll_subscribed_method() {
 | 
			
		||||
		// given
 | 
			
		||||
		let mut el = reactor::Core::new().unwrap();
 | 
			
		||||
		let mut el = Runtime::new().unwrap();
 | 
			
		||||
		let mut poll_manager = poll_manager();
 | 
			
		||||
		let (id, rx) = poll_manager.subscribe(Default::default(), "hello".into(), Params::None);
 | 
			
		||||
		assert_eq!(id, SubscriptionId::String("0x416d77337e24399d".into()));
 | 
			
		||||
 | 
			
		||||
		// then
 | 
			
		||||
		poll_manager.tick().wait().unwrap();
 | 
			
		||||
		let (res, rx) = el.run(rx.into_future()).unwrap();
 | 
			
		||||
		let (res, rx) = el.block_on(rx.into_future()).unwrap();
 | 
			
		||||
		assert_eq!(res, Some(Ok(Value::String("hello".into()))));
 | 
			
		||||
 | 
			
		||||
		// retrieve second item
 | 
			
		||||
		poll_manager.tick().wait().unwrap();
 | 
			
		||||
		let (res, rx) = el.run(rx.into_future()).unwrap();
 | 
			
		||||
		let (res, rx) = el.block_on(rx.into_future()).unwrap();
 | 
			
		||||
		assert_eq!(res, Some(Ok(Value::String("world".into()))));
 | 
			
		||||
 | 
			
		||||
		// and no more notifications
 | 
			
		||||
		poll_manager.tick().wait().unwrap();
 | 
			
		||||
		// we need to unsubscribe otherwise the future will never finish.
 | 
			
		||||
		poll_manager.unsubscribe(&id);
 | 
			
		||||
		assert_eq!(el.run(rx.into_future()).unwrap().0, None);
 | 
			
		||||
		assert_eq!(el.block_on(rx.into_future()).unwrap().0, None);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -39,7 +39,7 @@ use sync::LightSync;
 | 
			
		||||
use light::cache::Cache;
 | 
			
		||||
use light::on_demand::OnDemand;
 | 
			
		||||
use light::client::{LightChainClient, LightChainNotify};
 | 
			
		||||
use parity_reactor::Remote;
 | 
			
		||||
use parity_runtime::Executor;
 | 
			
		||||
use ethereum_types::H256;
 | 
			
		||||
use bytes::Bytes;
 | 
			
		||||
use parking_lot::{RwLock, Mutex};
 | 
			
		||||
@ -56,7 +56,7 @@ pub struct EthPubSubClient<C> {
 | 
			
		||||
 | 
			
		||||
impl<C> EthPubSubClient<C> {
 | 
			
		||||
	/// Creates new `EthPubSubClient`.
 | 
			
		||||
	pub fn new(client: Arc<C>, remote: Remote) -> Self {
 | 
			
		||||
	pub fn new(client: Arc<C>, executor: Executor) -> Self {
 | 
			
		||||
		let heads_subscribers = Arc::new(RwLock::new(Subscribers::default()));
 | 
			
		||||
		let logs_subscribers = Arc::new(RwLock::new(Subscribers::default()));
 | 
			
		||||
		let transactions_subscribers = Arc::new(RwLock::new(Subscribers::default()));
 | 
			
		||||
@ -64,7 +64,7 @@ impl<C> EthPubSubClient<C> {
 | 
			
		||||
		EthPubSubClient {
 | 
			
		||||
			handler: Arc::new(ChainNotificationHandler {
 | 
			
		||||
				client,
 | 
			
		||||
				remote,
 | 
			
		||||
				executor,
 | 
			
		||||
				heads_subscribers: heads_subscribers.clone(),
 | 
			
		||||
				logs_subscribers: logs_subscribers.clone(),
 | 
			
		||||
				transactions_subscribers: transactions_subscribers.clone(),
 | 
			
		||||
@ -77,8 +77,8 @@ impl<C> EthPubSubClient<C> {
 | 
			
		||||
 | 
			
		||||
	/// Creates new `EthPubSubCient` with deterministic subscription ids.
 | 
			
		||||
	#[cfg(test)]
 | 
			
		||||
	pub fn new_test(client: Arc<C>, remote: Remote) -> Self {
 | 
			
		||||
		let client = Self::new(client, remote);
 | 
			
		||||
	pub fn new_test(client: Arc<C>, executor: Executor) -> Self {
 | 
			
		||||
		let client = Self::new(client, executor);
 | 
			
		||||
		*client.heads_subscribers.write() = Subscribers::new_test();
 | 
			
		||||
		*client.logs_subscribers.write() = Subscribers::new_test();
 | 
			
		||||
		*client.transactions_subscribers.write() = Subscribers::new_test();
 | 
			
		||||
@ -98,7 +98,7 @@ impl EthPubSubClient<LightFetch> {
 | 
			
		||||
		on_demand: Arc<OnDemand>,
 | 
			
		||||
		sync: Arc<LightSync>,
 | 
			
		||||
		cache: Arc<Mutex<Cache>>,
 | 
			
		||||
		remote: Remote,
 | 
			
		||||
		executor: Executor,
 | 
			
		||||
		gas_price_percentile: usize,
 | 
			
		||||
	) -> Self {
 | 
			
		||||
		let fetch = LightFetch {
 | 
			
		||||
@ -108,22 +108,22 @@ impl EthPubSubClient<LightFetch> {
 | 
			
		||||
			cache,
 | 
			
		||||
			gas_price_percentile,
 | 
			
		||||
		};
 | 
			
		||||
		EthPubSubClient::new(Arc::new(fetch), remote)
 | 
			
		||||
		EthPubSubClient::new(Arc::new(fetch), executor)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// PubSub Notification handler.
 | 
			
		||||
pub struct ChainNotificationHandler<C> {
 | 
			
		||||
	client: Arc<C>,
 | 
			
		||||
	remote: Remote,
 | 
			
		||||
	executor: Executor,
 | 
			
		||||
	heads_subscribers: Arc<RwLock<Subscribers<Client>>>,
 | 
			
		||||
	logs_subscribers: Arc<RwLock<Subscribers<(Client, EthFilter)>>>,
 | 
			
		||||
	transactions_subscribers: Arc<RwLock<Subscribers<Client>>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<C> ChainNotificationHandler<C> {
 | 
			
		||||
	fn notify(remote: &Remote, subscriber: &Client, result: pubsub::Result) {
 | 
			
		||||
		remote.spawn(subscriber
 | 
			
		||||
	fn notify(executor: &Executor, subscriber: &Client, result: pubsub::Result) {
 | 
			
		||||
		executor.spawn(subscriber
 | 
			
		||||
			.notify(Ok(result))
 | 
			
		||||
			.map(|_| ())
 | 
			
		||||
			.map_err(|e| warn!(target: "rpc", "Unable to send notification: {}", e))
 | 
			
		||||
@ -133,7 +133,7 @@ impl<C> ChainNotificationHandler<C> {
 | 
			
		||||
	fn notify_heads(&self, headers: &[(encoded::Header, BTreeMap<String, String>)]) {
 | 
			
		||||
		for subscriber in self.heads_subscribers.read().values() {
 | 
			
		||||
			for &(ref header, ref extra_info) in headers {
 | 
			
		||||
				Self::notify(&self.remote, subscriber, pubsub::Result::Header(RichHeader {
 | 
			
		||||
				Self::notify(&self.executor, subscriber, pubsub::Result::Header(RichHeader {
 | 
			
		||||
					inner: header.into(),
 | 
			
		||||
					extra_info: extra_info.clone(),
 | 
			
		||||
				}));
 | 
			
		||||
@ -159,14 +159,14 @@ impl<C> ChainNotificationHandler<C> {
 | 
			
		||||
				.collect::<Vec<_>>()
 | 
			
		||||
			);
 | 
			
		||||
			let limit = filter.limit;
 | 
			
		||||
			let remote = self.remote.clone();
 | 
			
		||||
			let executor = self.executor.clone();
 | 
			
		||||
			let subscriber = subscriber.clone();
 | 
			
		||||
			self.remote.spawn(logs
 | 
			
		||||
			self.executor.spawn(logs
 | 
			
		||||
				.map(move |logs| {
 | 
			
		||||
					let logs = logs.into_iter().flat_map(|log| log).collect();
 | 
			
		||||
 | 
			
		||||
					for log in limit_logs(logs, limit) {
 | 
			
		||||
						Self::notify(&remote, &subscriber, pubsub::Result::Log(log))
 | 
			
		||||
						Self::notify(&executor, &subscriber, pubsub::Result::Log(log))
 | 
			
		||||
					}
 | 
			
		||||
				})
 | 
			
		||||
				.map_err(|e| warn!("Unable to fetch latest logs: {:?}", e))
 | 
			
		||||
@ -178,7 +178,7 @@ impl<C> ChainNotificationHandler<C> {
 | 
			
		||||
	pub fn notify_new_transactions(&self, hashes: &[H256]) {
 | 
			
		||||
		for subscriber in self.transactions_subscribers.read().values() {
 | 
			
		||||
			for hash in hashes {
 | 
			
		||||
				Self::notify(&self.remote, subscriber, pubsub::Result::TransactionHash((*hash).into()));
 | 
			
		||||
				Self::notify(&self.executor, subscriber, pubsub::Result::TransactionHash((*hash).into()));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,6 @@ use std::sync::Arc;
 | 
			
		||||
 | 
			
		||||
use sync::ManageNetwork;
 | 
			
		||||
use fetch::{self, Fetch};
 | 
			
		||||
use futures_cpupool::CpuPool;
 | 
			
		||||
use hash::keccak_buffer;
 | 
			
		||||
 | 
			
		||||
use jsonrpc_core::{Result, BoxFuture};
 | 
			
		||||
@ -35,16 +34,14 @@ use v1::types::{Bytes, H160, H256, U256, ReleaseInfo, Transaction};
 | 
			
		||||
pub struct ParitySetClient<F> {
 | 
			
		||||
	net: Arc<ManageNetwork>,
 | 
			
		||||
	fetch: F,
 | 
			
		||||
	pool: CpuPool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<F: Fetch> ParitySetClient<F> {
 | 
			
		||||
	/// Creates new `ParitySetClient` with given `Fetch`.
 | 
			
		||||
	pub fn new(net: Arc<ManageNetwork>, fetch: F, p: CpuPool) -> Self {
 | 
			
		||||
	pub fn new(net: Arc<ManageNetwork>, fetch: F) -> Self {
 | 
			
		||||
		ParitySetClient {
 | 
			
		||||
			net: net,
 | 
			
		||||
			fetch: fetch,
 | 
			
		||||
			pool: p,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -134,7 +131,7 @@ impl<F: Fetch> ParitySet for ParitySetClient<F> {
 | 
			
		||||
				})
 | 
			
		||||
				.map(Into::into)
 | 
			
		||||
		});
 | 
			
		||||
		Box::new(self.pool.spawn(future))
 | 
			
		||||
		Box::new(future)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fn upgrade_ready(&self) -> Result<Option<ReleaseInfo>> {
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,6 @@ use ethcore::client::{BlockChainClient, Mode};
 | 
			
		||||
use ethcore::miner::MinerService;
 | 
			
		||||
use sync::ManageNetwork;
 | 
			
		||||
use fetch::{self, Fetch};
 | 
			
		||||
use futures_cpupool::CpuPool;
 | 
			
		||||
use hash::keccak_buffer;
 | 
			
		||||
use updater::{Service as UpdateService};
 | 
			
		||||
 | 
			
		||||
@ -40,7 +39,6 @@ pub struct ParitySetClient<C, M, U, F = fetch::Client> {
 | 
			
		||||
	updater: Arc<U>,
 | 
			
		||||
	net: Arc<ManageNetwork>,
 | 
			
		||||
	fetch: F,
 | 
			
		||||
	pool: CpuPool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<C, M, U, F> ParitySetClient<C, M, U, F>
 | 
			
		||||
@ -53,7 +51,6 @@ impl<C, M, U, F> ParitySetClient<C, M, U, F>
 | 
			
		||||
		updater: &Arc<U>,
 | 
			
		||||
		net: &Arc<ManageNetwork>,
 | 
			
		||||
		fetch: F,
 | 
			
		||||
		pool: CpuPool,
 | 
			
		||||
	) -> Self {
 | 
			
		||||
		ParitySetClient {
 | 
			
		||||
			client: client.clone(),
 | 
			
		||||
@ -61,7 +58,6 @@ impl<C, M, U, F> ParitySetClient<C, M, U, F>
 | 
			
		||||
			updater: updater.clone(),
 | 
			
		||||
			net: net.clone(),
 | 
			
		||||
			fetch: fetch,
 | 
			
		||||
			pool: pool,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -177,7 +173,7 @@ impl<C, M, U, F> ParitySet for ParitySetClient<C, M, U, F> where
 | 
			
		||||
				})
 | 
			
		||||
				.map(Into::into)
 | 
			
		||||
		});
 | 
			
		||||
		Box::new(self.pool.spawn(future))
 | 
			
		||||
		Box::new(future)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fn upgrade_ready(&self) -> Result<Option<ReleaseInfo>> {
 | 
			
		||||
 | 
			
		||||
@ -27,7 +27,7 @@ use jsonrpc_macros::pubsub::Subscriber;
 | 
			
		||||
use jsonrpc_pubsub::SubscriptionId;
 | 
			
		||||
use tokio_timer;
 | 
			
		||||
 | 
			
		||||
use parity_reactor::Remote;
 | 
			
		||||
use parity_runtime::Executor;
 | 
			
		||||
use v1::helpers::GenericPollManager;
 | 
			
		||||
use v1::metadata::Metadata;
 | 
			
		||||
use v1::traits::PubSub;
 | 
			
		||||
@ -35,12 +35,12 @@ use v1::traits::PubSub;
 | 
			
		||||
/// Parity PubSub implementation.
 | 
			
		||||
pub struct PubSubClient<S: core::Middleware<Metadata>> {
 | 
			
		||||
	poll_manager: Arc<RwLock<GenericPollManager<S>>>,
 | 
			
		||||
	remote: Remote,
 | 
			
		||||
	executor: Executor,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<S: core::Middleware<Metadata>> PubSubClient<S> {
 | 
			
		||||
	/// Creates new `PubSubClient`.
 | 
			
		||||
	pub fn new(rpc: MetaIoHandler<Metadata, S>, remote: Remote) -> Self {
 | 
			
		||||
	pub fn new(rpc: MetaIoHandler<Metadata, S>, executor: Executor) -> Self {
 | 
			
		||||
		let poll_manager = Arc::new(RwLock::new(GenericPollManager::new(rpc)));
 | 
			
		||||
		let pm2 = poll_manager.clone();
 | 
			
		||||
 | 
			
		||||
@ -50,14 +50,14 @@ impl<S: core::Middleware<Metadata>> PubSubClient<S> {
 | 
			
		||||
 | 
			
		||||
		// Start ticking
 | 
			
		||||
		let interval = timer.interval(Duration::from_millis(1000));
 | 
			
		||||
		remote.spawn(interval
 | 
			
		||||
		executor.spawn(interval
 | 
			
		||||
			.map_err(|e| warn!("Polling timer error: {:?}", e))
 | 
			
		||||
			.for_each(move |_| pm2.read().tick())
 | 
			
		||||
		);
 | 
			
		||||
 | 
			
		||||
		PubSubClient {
 | 
			
		||||
			poll_manager,
 | 
			
		||||
			remote,
 | 
			
		||||
			executor,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -65,8 +65,8 @@ impl<S: core::Middleware<Metadata>> PubSubClient<S> {
 | 
			
		||||
impl PubSubClient<core::NoopMiddleware> {
 | 
			
		||||
	/// Creates new `PubSubClient` with deterministic ids.
 | 
			
		||||
	#[cfg(test)]
 | 
			
		||||
	pub fn new_test(rpc: MetaIoHandler<Metadata, core::NoopMiddleware>, remote: Remote) -> Self {
 | 
			
		||||
		let client = Self::new(MetaIoHandler::with_middleware(Default::default()), remote);
 | 
			
		||||
	pub fn new_test(rpc: MetaIoHandler<Metadata, core::NoopMiddleware>, executor: Executor) -> Self {
 | 
			
		||||
		let client = Self::new(MetaIoHandler::with_middleware(Default::default()), executor);
 | 
			
		||||
		*client.poll_manager.write() = GenericPollManager::new_test(rpc);
 | 
			
		||||
		client
 | 
			
		||||
	}
 | 
			
		||||
@ -84,7 +84,7 @@ impl<S: core::Middleware<Metadata>> PubSub for PubSubClient<S> {
 | 
			
		||||
		let (id, receiver) = poll_manager.subscribe(meta, method, params);
 | 
			
		||||
		match subscriber.assign_id(id.clone()) {
 | 
			
		||||
			Ok(sink) => {
 | 
			
		||||
				self.remote.spawn(receiver.forward(sink.sink_map_err(|e| {
 | 
			
		||||
				self.executor.spawn(receiver.forward(sink.sink_map_err(|e| {
 | 
			
		||||
					warn!("Cannot send notification: {:?}", e);
 | 
			
		||||
				})).map(|_| ()));
 | 
			
		||||
			},
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,7 @@ use std::sync::Arc;
 | 
			
		||||
 | 
			
		||||
use ethcore::account_provider::AccountProvider;
 | 
			
		||||
use ethkey;
 | 
			
		||||
use parity_reactor::Remote;
 | 
			
		||||
use parity_runtime::Executor;
 | 
			
		||||
use parking_lot::Mutex;
 | 
			
		||||
use rlp::Rlp;
 | 
			
		||||
use transaction::{SignedTransaction, PendingTransaction};
 | 
			
		||||
@ -50,7 +50,7 @@ impl<D: Dispatcher + 'static> SignerClient<D> {
 | 
			
		||||
		store: &Arc<AccountProvider>,
 | 
			
		||||
		dispatcher: D,
 | 
			
		||||
		signer: &Arc<SignerService>,
 | 
			
		||||
		remote: Remote,
 | 
			
		||||
		executor: Executor,
 | 
			
		||||
	) -> Self {
 | 
			
		||||
		let subscribers = Arc::new(Mutex::new(Subscribers::default()));
 | 
			
		||||
		let subs = Arc::downgrade(&subscribers);
 | 
			
		||||
@ -60,7 +60,7 @@ impl<D: Dispatcher + 'static> SignerClient<D> {
 | 
			
		||||
				let requests = s.requests().into_iter().map(Into::into).collect::<Vec<ConfirmationRequest>>();
 | 
			
		||||
				for subscription in subs.lock().values() {
 | 
			
		||||
					let subscription: &Sink<_> = subscription;
 | 
			
		||||
					remote.spawn(subscription
 | 
			
		||||
					executor.spawn(subscription
 | 
			
		||||
						.notify(Ok(requests.clone()))
 | 
			
		||||
						.map(|_| ())
 | 
			
		||||
						.map_err(|e| warn!(target: "rpc", "Unable to send notification: {}", e))
 | 
			
		||||
 | 
			
		||||
@ -44,7 +44,7 @@ use v1::types::{
 | 
			
		||||
	Origin,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use parity_reactor::Remote;
 | 
			
		||||
use parity_runtime::Executor;
 | 
			
		||||
 | 
			
		||||
/// After 60s entries that are not queried with `check_request` will get garbage collected.
 | 
			
		||||
const MAX_PENDING_DURATION_SEC: u32 = 60;
 | 
			
		||||
@ -67,7 +67,7 @@ impl Future for DispatchResult {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn schedule(remote: Remote,
 | 
			
		||||
fn schedule(executor: Executor,
 | 
			
		||||
	confirmations: Arc<Mutex<TransientHashMap<U256, Option<RpcConfirmationResult>>>>,
 | 
			
		||||
	id: U256,
 | 
			
		||||
	future: RpcConfirmationReceiver) {
 | 
			
		||||
@ -83,7 +83,7 @@ fn schedule(remote: Remote,
 | 
			
		||||
		confirmations.insert(id, Some(result));
 | 
			
		||||
		Ok(())
 | 
			
		||||
	});
 | 
			
		||||
	remote.spawn(future);
 | 
			
		||||
	executor.spawn(future);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Implementation of functions that require signing when no trusted signer is used.
 | 
			
		||||
@ -91,19 +91,19 @@ pub struct SigningQueueClient<D> {
 | 
			
		||||
	signer: Arc<SignerService>,
 | 
			
		||||
	accounts: Arc<AccountProvider>,
 | 
			
		||||
	dispatcher: D,
 | 
			
		||||
	remote: Remote,
 | 
			
		||||
	executor: Executor,
 | 
			
		||||
	// None here means that the request hasn't yet been confirmed
 | 
			
		||||
	confirmations: Arc<Mutex<TransientHashMap<U256, Option<RpcConfirmationResult>>>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<D: Dispatcher + 'static> SigningQueueClient<D> {
 | 
			
		||||
	/// Creates a new signing queue client given shared signing queue.
 | 
			
		||||
	pub fn new(signer: &Arc<SignerService>, dispatcher: D, remote: Remote, accounts: &Arc<AccountProvider>) -> Self {
 | 
			
		||||
	pub fn new(signer: &Arc<SignerService>, dispatcher: D, executor: Executor, accounts: &Arc<AccountProvider>) -> Self {
 | 
			
		||||
		SigningQueueClient {
 | 
			
		||||
			signer: signer.clone(),
 | 
			
		||||
			accounts: accounts.clone(),
 | 
			
		||||
			dispatcher,
 | 
			
		||||
			remote,
 | 
			
		||||
			executor,
 | 
			
		||||
			confirmations: Arc::new(Mutex::new(TransientHashMap::new(MAX_PENDING_DURATION_SEC))),
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@ -143,7 +143,7 @@ impl<D: Dispatcher + 'static> ParitySigning for SigningQueueClient<D> {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fn post_sign(&self, meta: Metadata, address: RpcH160, data: RpcBytes) -> BoxFuture<RpcEither<RpcU256, RpcConfirmationResponse>> {
 | 
			
		||||
		let remote = self.remote.clone();
 | 
			
		||||
		let executor = self.executor.clone();
 | 
			
		||||
		let confirmations = self.confirmations.clone();
 | 
			
		||||
 | 
			
		||||
		Box::new(self.dispatch(
 | 
			
		||||
@ -153,21 +153,21 @@ impl<D: Dispatcher + 'static> ParitySigning for SigningQueueClient<D> {
 | 
			
		||||
		).map(move |result| match result {
 | 
			
		||||
			DispatchResult::Value(v) => RpcEither::Or(v),
 | 
			
		||||
			DispatchResult::Future(id, future) => {
 | 
			
		||||
				schedule(remote, confirmations, id, future);
 | 
			
		||||
				schedule(executor, confirmations, id, future);
 | 
			
		||||
				RpcEither::Either(id.into())
 | 
			
		||||
			},
 | 
			
		||||
		}))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fn post_transaction(&self, meta: Metadata, request: RpcTransactionRequest) -> BoxFuture<RpcEither<RpcU256, RpcConfirmationResponse>> {
 | 
			
		||||
		let remote = self.remote.clone();
 | 
			
		||||
		let executor = self.executor.clone();
 | 
			
		||||
		let confirmations = self.confirmations.clone();
 | 
			
		||||
 | 
			
		||||
		Box::new(self.dispatch(RpcConfirmationPayload::SendTransaction(request), DefaultAccount::Provided(self.accounts.default_account().ok().unwrap_or_default()), meta.origin)
 | 
			
		||||
			.map(|result| match result {
 | 
			
		||||
				DispatchResult::Value(v) => RpcEither::Or(v),
 | 
			
		||||
				DispatchResult::Future(id, future) => {
 | 
			
		||||
					schedule(remote, confirmations, id, future);
 | 
			
		||||
					schedule(executor, confirmations, id, future);
 | 
			
		||||
					RpcEither::Either(id.into())
 | 
			
		||||
				},
 | 
			
		||||
			}))
 | 
			
		||||
 | 
			
		||||
@ -20,12 +20,13 @@ use std::fmt;
 | 
			
		||||
use std::sync::Arc;
 | 
			
		||||
use std::sync::atomic::{self, AtomicUsize};
 | 
			
		||||
use std::time;
 | 
			
		||||
use futures_cpupool as pool;
 | 
			
		||||
use jsonrpc_core as rpc;
 | 
			
		||||
use parity_runtime;
 | 
			
		||||
use jsonrpc_core as core;
 | 
			
		||||
use jsonrpc_core::futures::future::Either;
 | 
			
		||||
use order_stat;
 | 
			
		||||
use parking_lot::RwLock;
 | 
			
		||||
 | 
			
		||||
pub use self::pool::CpuPool;
 | 
			
		||||
pub use self::parity_runtime::Executor;
 | 
			
		||||
 | 
			
		||||
const RATE_SECONDS: usize = 10;
 | 
			
		||||
const STATS_SAMPLES: usize = 60;
 | 
			
		||||
@ -186,16 +187,14 @@ pub trait ActivityNotifier: Send + Sync + 'static {
 | 
			
		||||
pub struct Middleware<T: ActivityNotifier = ClientNotifier> {
 | 
			
		||||
	stats: Arc<RpcStats>,
 | 
			
		||||
	notifier: T,
 | 
			
		||||
	pool: Option<CpuPool>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: ActivityNotifier> Middleware<T> {
 | 
			
		||||
	/// Create new Middleware with stats counter and activity notifier.
 | 
			
		||||
	pub fn new(stats: Arc<RpcStats>, notifier: T, pool: Option<CpuPool>) -> Self {
 | 
			
		||||
	pub fn new(stats: Arc<RpcStats>, notifier: T) -> Self {
 | 
			
		||||
		Middleware {
 | 
			
		||||
			stats,
 | 
			
		||||
			notifier,
 | 
			
		||||
			pool,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -204,28 +203,24 @@ impl<T: ActivityNotifier> Middleware<T> {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<M: rpc::Metadata, T: ActivityNotifier> rpc::Middleware<M> for Middleware<T> {
 | 
			
		||||
	type Future = rpc::futures::future::Either<
 | 
			
		||||
		pool::CpuFuture<Option<rpc::Response>, ()>,
 | 
			
		||||
		rpc::FutureResponse,
 | 
			
		||||
	>;
 | 
			
		||||
impl<M: core::Metadata, T: ActivityNotifier> core::Middleware<M> for Middleware<T> {
 | 
			
		||||
	type Future = core::FutureResponse;
 | 
			
		||||
 | 
			
		||||
	fn on_request<F, X>(&self, request: rpc::Request, meta: M, process: F) -> Self::Future where
 | 
			
		||||
		F: FnOnce(rpc::Request, M) -> X,
 | 
			
		||||
		X: rpc::futures::Future<Item=Option<rpc::Response>, Error=()> + Send + 'static,
 | 
			
		||||
	fn on_request<F, X>(&self, request: core::Request, meta: M, process: F) -> Either<Self::Future, X> where
 | 
			
		||||
		F: FnOnce(core::Request, M) -> X,
 | 
			
		||||
		X: core::futures::Future<Item=Option<core::Response>, Error=()> + Send + 'static,
 | 
			
		||||
	{
 | 
			
		||||
		use self::rpc::futures::future::Either::{A, B};
 | 
			
		||||
 | 
			
		||||
		let start = time::Instant::now();
 | 
			
		||||
 | 
			
		||||
		self.notifier.active();
 | 
			
		||||
		self.stats.count_request();
 | 
			
		||||
 | 
			
		||||
		let id = match request {
 | 
			
		||||
			rpc::Request::Single(rpc::Call::MethodCall(ref call)) => Some(call.id.clone()),
 | 
			
		||||
			core::Request::Single(core::Call::MethodCall(ref call)) => Some(call.id.clone()),
 | 
			
		||||
			_ => None,
 | 
			
		||||
		};
 | 
			
		||||
		let stats = self.stats.clone();
 | 
			
		||||
 | 
			
		||||
		let future = process(request, meta).map(move |res| {
 | 
			
		||||
			let time = Self::as_micro(start.elapsed());
 | 
			
		||||
			if time > 10_000 {
 | 
			
		||||
@ -235,10 +230,7 @@ impl<M: rpc::Metadata, T: ActivityNotifier> rpc::Middleware<M> for Middleware<T>
 | 
			
		||||
			res
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		match self.pool {
 | 
			
		||||
			Some(ref pool) => A(pool.spawn(future)),
 | 
			
		||||
			None => B(Box::new(future)),
 | 
			
		||||
		}
 | 
			
		||||
		Either::A(Box::new(future))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -32,6 +32,7 @@ use ethjson::spec::ForkSpec;
 | 
			
		||||
use io::IoChannel;
 | 
			
		||||
use miner::external::ExternalMiner;
 | 
			
		||||
use parking_lot::Mutex;
 | 
			
		||||
use parity_runtime::Runtime;
 | 
			
		||||
 | 
			
		||||
use jsonrpc_core::IoHandler;
 | 
			
		||||
use v1::helpers::dispatch::FullDispatcher;
 | 
			
		||||
@ -73,6 +74,7 @@ fn make_spec(chain: &BlockChain) -> Spec {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct EthTester {
 | 
			
		||||
	_runtime: Runtime,
 | 
			
		||||
	client: Arc<Client>,
 | 
			
		||||
	_miner: Arc<Miner>,
 | 
			
		||||
	_snapshot: Arc<TestSnapshotService>,
 | 
			
		||||
@ -99,6 +101,7 @@ impl EthTester {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fn from_spec(spec: Spec) -> Self {
 | 
			
		||||
		let runtime = Runtime::with_thread_count(1);
 | 
			
		||||
		let account_provider = account_provider();
 | 
			
		||||
		let opt_account_provider = account_provider.clone();
 | 
			
		||||
		let miner_service = miner_service(&spec, account_provider.clone());
 | 
			
		||||
@ -124,7 +127,7 @@ impl EthTester {
 | 
			
		||||
			Default::default(),
 | 
			
		||||
		);
 | 
			
		||||
 | 
			
		||||
		let reservations = Arc::new(Mutex::new(nonce::Reservations::new()));
 | 
			
		||||
		let reservations = Arc::new(Mutex::new(nonce::Reservations::new(runtime.executor())));
 | 
			
		||||
 | 
			
		||||
		let dispatcher = FullDispatcher::new(client.clone(), miner_service.clone(), reservations, 50);
 | 
			
		||||
		let eth_sign = SigningUnsafeClient::new(
 | 
			
		||||
@ -137,6 +140,7 @@ impl EthTester {
 | 
			
		||||
		handler.extend_with(eth_sign.to_delegate());
 | 
			
		||||
 | 
			
		||||
		EthTester {
 | 
			
		||||
			_runtime: runtime,
 | 
			
		||||
			_miner: miner_service,
 | 
			
		||||
			_snapshot: snapshot_service,
 | 
			
		||||
			client: client,
 | 
			
		||||
 | 
			
		||||
@ -32,6 +32,7 @@ use miner::external::ExternalMiner;
 | 
			
		||||
use rlp;
 | 
			
		||||
use rustc_hex::{FromHex, ToHex};
 | 
			
		||||
use transaction::{Transaction, Action};
 | 
			
		||||
use parity_runtime::Runtime;
 | 
			
		||||
 | 
			
		||||
use jsonrpc_core::IoHandler;
 | 
			
		||||
use v1::{Eth, EthClient, EthClientOptions, EthFilter, EthFilterClient, EthSigning, SigningUnsafeClient};
 | 
			
		||||
@ -65,6 +66,7 @@ fn snapshot_service() -> Arc<TestSnapshotService> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct EthTester {
 | 
			
		||||
	pub runtime: Runtime,
 | 
			
		||||
	pub client: Arc<TestBlockChainClient>,
 | 
			
		||||
	pub sync: Arc<TestSyncProvider>,
 | 
			
		||||
	pub accounts_provider: Arc<AccountProvider>,
 | 
			
		||||
@ -82,6 +84,7 @@ impl Default for EthTester {
 | 
			
		||||
 | 
			
		||||
impl EthTester {
 | 
			
		||||
	pub fn new_with_options(options: EthClientOptions) -> Self {
 | 
			
		||||
		let runtime = Runtime::with_thread_count(1);
 | 
			
		||||
		let client = blockchain_client();
 | 
			
		||||
		let sync = sync_provider();
 | 
			
		||||
		let ap = accounts_provider();
 | 
			
		||||
@ -94,7 +97,7 @@ impl EthTester {
 | 
			
		||||
		let poll_lifetime = options.poll_lifetime;
 | 
			
		||||
		let eth = EthClient::new(&client, &snapshot, &sync, &opt_ap, &miner, &external_miner, options).to_delegate();
 | 
			
		||||
		let filter = EthFilterClient::new(client.clone(), miner.clone(), poll_lifetime).to_delegate();
 | 
			
		||||
		let reservations = Arc::new(Mutex::new(nonce::Reservations::new()));
 | 
			
		||||
		let reservations = Arc::new(Mutex::new(nonce::Reservations::new(runtime.executor())));
 | 
			
		||||
 | 
			
		||||
		let dispatcher = FullDispatcher::new(client.clone(), miner.clone(), reservations, gas_price_percentile);
 | 
			
		||||
		let sign = SigningUnsafeClient::new(&opt_ap, dispatcher).to_delegate();
 | 
			
		||||
@ -104,6 +107,7 @@ impl EthTester {
 | 
			
		||||
		io.extend_with(filter);
 | 
			
		||||
 | 
			
		||||
		EthTester {
 | 
			
		||||
			runtime,
 | 
			
		||||
			client: client,
 | 
			
		||||
			sync: sync,
 | 
			
		||||
			accounts_provider: ap,
 | 
			
		||||
 | 
			
		||||
@ -25,14 +25,14 @@ use std::time::Duration;
 | 
			
		||||
use v1::{EthPubSub, EthPubSubClient, Metadata};
 | 
			
		||||
 | 
			
		||||
use ethcore::client::{TestBlockChainClient, EachBlockWith, ChainNotify, ChainRoute, ChainRouteType};
 | 
			
		||||
use parity_reactor::EventLoop;
 | 
			
		||||
use parity_runtime::Runtime;
 | 
			
		||||
 | 
			
		||||
const DURATION_ZERO: Duration = Duration::from_millis(0);
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn should_subscribe_to_new_heads() {
 | 
			
		||||
	// given
 | 
			
		||||
	let el = EventLoop::spawn();
 | 
			
		||||
	let el = Runtime::with_thread_count(1);
 | 
			
		||||
	let mut client = TestBlockChainClient::new();
 | 
			
		||||
	// Insert some blocks
 | 
			
		||||
	client.add_blocks(3, EachBlockWith::Nothing);
 | 
			
		||||
@ -40,7 +40,7 @@ fn should_subscribe_to_new_heads() {
 | 
			
		||||
	let h2 = client.block_hash_delta_minus(2);
 | 
			
		||||
	let h1 = client.block_hash_delta_minus(3);
 | 
			
		||||
 | 
			
		||||
	let pubsub = EthPubSubClient::new_test(Arc::new(client), el.remote());
 | 
			
		||||
	let pubsub = EthPubSubClient::new_test(Arc::new(client), el.executor());
 | 
			
		||||
	let handler = pubsub.handler().upgrade().unwrap();
 | 
			
		||||
	let pubsub = pubsub.to_delegate();
 | 
			
		||||
 | 
			
		||||
@ -89,7 +89,7 @@ fn should_subscribe_to_logs() {
 | 
			
		||||
	use ethcore::client::BlockInfo;
 | 
			
		||||
 | 
			
		||||
	// given
 | 
			
		||||
	let el = EventLoop::spawn();
 | 
			
		||||
	let el = Runtime::with_thread_count(1);
 | 
			
		||||
	let mut client = TestBlockChainClient::new();
 | 
			
		||||
	// Insert some blocks
 | 
			
		||||
	client.add_blocks(1, EachBlockWith::Transaction);
 | 
			
		||||
@ -112,7 +112,7 @@ fn should_subscribe_to_logs() {
 | 
			
		||||
		}
 | 
			
		||||
	]);
 | 
			
		||||
 | 
			
		||||
	let pubsub = EthPubSubClient::new_test(Arc::new(client), el.remote());
 | 
			
		||||
	let pubsub = EthPubSubClient::new_test(Arc::new(client), el.executor());
 | 
			
		||||
	let handler = pubsub.handler().upgrade().unwrap();
 | 
			
		||||
	let pubsub = pubsub.to_delegate();
 | 
			
		||||
 | 
			
		||||
@ -156,10 +156,10 @@ fn should_subscribe_to_logs() {
 | 
			
		||||
#[test]
 | 
			
		||||
fn should_subscribe_to_pending_transactions() {
 | 
			
		||||
	// given
 | 
			
		||||
	let el = EventLoop::spawn();
 | 
			
		||||
	let el = Runtime::with_thread_count(1);
 | 
			
		||||
	let client = TestBlockChainClient::new();
 | 
			
		||||
 | 
			
		||||
	let pubsub = EthPubSubClient::new_test(Arc::new(client), el.remote());
 | 
			
		||||
	let pubsub = EthPubSubClient::new_test(Arc::new(client), el.executor());
 | 
			
		||||
	let handler = pubsub.handler().upgrade().unwrap();
 | 
			
		||||
	let pubsub = pubsub.to_delegate();
 | 
			
		||||
 | 
			
		||||
@ -203,9 +203,9 @@ fn should_subscribe_to_pending_transactions() {
 | 
			
		||||
#[test]
 | 
			
		||||
fn should_return_unimplemented() {
 | 
			
		||||
	// given
 | 
			
		||||
	let el = EventLoop::spawn();
 | 
			
		||||
	let el = Runtime::with_thread_count(1);
 | 
			
		||||
	let client = TestBlockChainClient::new();
 | 
			
		||||
	let pubsub = EthPubSubClient::new_test(Arc::new(client), el.remote());
 | 
			
		||||
	let pubsub = EthPubSubClient::new_test(Arc::new(client), el.executor());
 | 
			
		||||
	let pubsub = pubsub.to_delegate();
 | 
			
		||||
 | 
			
		||||
	let mut io = MetaIoHandler::default();
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,6 @@ use ethereum_types::{U256, Address};
 | 
			
		||||
use ethcore::miner::MinerService;
 | 
			
		||||
use ethcore::client::TestBlockChainClient;
 | 
			
		||||
use sync::ManageNetwork;
 | 
			
		||||
use futures_cpupool::CpuPool;
 | 
			
		||||
 | 
			
		||||
use jsonrpc_core::IoHandler;
 | 
			
		||||
use v1::{ParitySet, ParitySetClient};
 | 
			
		||||
@ -55,8 +54,7 @@ fn parity_set_client(
 | 
			
		||||
	updater: &Arc<TestUpdater>,
 | 
			
		||||
	net: &Arc<TestManageNetwork>,
 | 
			
		||||
) -> TestParitySetClient {
 | 
			
		||||
	let pool = CpuPool::new(1);
 | 
			
		||||
	ParitySetClient::new(client, miner, updater, &(net.clone() as Arc<ManageNetwork>), FakeFetch::new(Some(1)), pool)
 | 
			
		||||
	ParitySetClient::new(client, miner, updater, &(net.clone() as Arc<ManageNetwork>), FakeFetch::new(Some(1)))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,7 @@ use ethcore::client::TestBlockChainClient;
 | 
			
		||||
use jsonrpc_core::IoHandler;
 | 
			
		||||
use parking_lot::Mutex;
 | 
			
		||||
use transaction::{Action, Transaction};
 | 
			
		||||
use parity_runtime::Runtime;
 | 
			
		||||
 | 
			
		||||
use v1::{PersonalClient, Personal, Metadata};
 | 
			
		||||
use v1::helpers::nonce;
 | 
			
		||||
@ -32,6 +33,7 @@ use v1::tests::helpers::TestMinerService;
 | 
			
		||||
use v1::types::H520;
 | 
			
		||||
 | 
			
		||||
struct PersonalTester {
 | 
			
		||||
	_runtime: Runtime,
 | 
			
		||||
	accounts: Arc<AccountProvider>,
 | 
			
		||||
	io: IoHandler<Metadata>,
 | 
			
		||||
	miner: Arc<TestMinerService>,
 | 
			
		||||
@ -51,10 +53,11 @@ fn miner_service() -> Arc<TestMinerService> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn setup() -> PersonalTester {
 | 
			
		||||
	let runtime = Runtime::with_thread_count(1);
 | 
			
		||||
	let accounts = accounts_provider();
 | 
			
		||||
	let client = blockchain_client();
 | 
			
		||||
	let miner = miner_service();
 | 
			
		||||
	let reservations = Arc::new(Mutex::new(nonce::Reservations::new()));
 | 
			
		||||
	let reservations = Arc::new(Mutex::new(nonce::Reservations::new(runtime.executor())));
 | 
			
		||||
 | 
			
		||||
	let dispatcher = FullDispatcher::new(client, miner.clone(), reservations, 50);
 | 
			
		||||
	let personal = PersonalClient::new(&accounts, dispatcher, false);
 | 
			
		||||
@ -63,6 +66,7 @@ fn setup() -> PersonalTester {
 | 
			
		||||
	io.extend_with(personal.to_delegate());
 | 
			
		||||
 | 
			
		||||
	let tester = PersonalTester {
 | 
			
		||||
		_runtime: runtime,
 | 
			
		||||
		accounts: accounts,
 | 
			
		||||
		io: io,
 | 
			
		||||
		miner: miner,
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,7 @@ use jsonrpc_core::{self as core, MetaIoHandler};
 | 
			
		||||
use jsonrpc_core::futures::{self, Stream, Future};
 | 
			
		||||
use jsonrpc_pubsub::Session;
 | 
			
		||||
 | 
			
		||||
use parity_reactor::EventLoop;
 | 
			
		||||
use parity_runtime::Runtime;
 | 
			
		||||
use v1::{PubSub, PubSubClient, Metadata};
 | 
			
		||||
 | 
			
		||||
fn rpc() -> MetaIoHandler<Metadata, core::NoopMiddleware> {
 | 
			
		||||
@ -40,9 +40,9 @@ fn rpc() -> MetaIoHandler<Metadata, core::NoopMiddleware> {
 | 
			
		||||
#[test]
 | 
			
		||||
fn should_subscribe_to_a_method() {
 | 
			
		||||
	// given
 | 
			
		||||
	let el = EventLoop::spawn();
 | 
			
		||||
	let el = Runtime::with_thread_count(1);
 | 
			
		||||
	let rpc = rpc();
 | 
			
		||||
	let pubsub = PubSubClient::new_test(rpc, el.remote()).to_delegate();
 | 
			
		||||
	let pubsub = PubSubClient::new_test(rpc, el.executor()).to_delegate();
 | 
			
		||||
 | 
			
		||||
	let mut io = MetaIoHandler::default();
 | 
			
		||||
	io.extend_with(pubsub);
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,7 @@ use bytes::ToPretty;
 | 
			
		||||
 | 
			
		||||
use ethcore::account_provider::AccountProvider;
 | 
			
		||||
use ethcore::client::TestBlockChainClient;
 | 
			
		||||
use parity_reactor::EventLoop;
 | 
			
		||||
use parity_runtime::Runtime;
 | 
			
		||||
use parking_lot::Mutex;
 | 
			
		||||
use rlp::encode;
 | 
			
		||||
use transaction::{Transaction, Action, SignedTransaction};
 | 
			
		||||
@ -36,6 +36,7 @@ use v1::helpers::{nonce, SigningQueue, SignerService, FilledTransactionRequest,
 | 
			
		||||
use v1::helpers::dispatch::{FullDispatcher, eth_data_hash};
 | 
			
		||||
 | 
			
		||||
struct SignerTester {
 | 
			
		||||
	_runtime: Runtime,
 | 
			
		||||
	signer: Arc<SignerService>,
 | 
			
		||||
	accounts: Arc<AccountProvider>,
 | 
			
		||||
	io: IoHandler<Metadata>,
 | 
			
		||||
@ -56,18 +57,19 @@ fn miner_service() -> Arc<TestMinerService> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn signer_tester() -> SignerTester {
 | 
			
		||||
	let runtime = Runtime::with_thread_count(1);
 | 
			
		||||
	let signer = Arc::new(SignerService::new_test(false));
 | 
			
		||||
	let accounts = accounts_provider();
 | 
			
		||||
	let client = blockchain_client();
 | 
			
		||||
	let miner = miner_service();
 | 
			
		||||
	let reservations = Arc::new(Mutex::new(nonce::Reservations::new()));
 | 
			
		||||
	let event_loop = EventLoop::spawn();
 | 
			
		||||
	let reservations = Arc::new(Mutex::new(nonce::Reservations::new(runtime.executor())));
 | 
			
		||||
 | 
			
		||||
	let dispatcher = FullDispatcher::new(client, miner.clone(), reservations, 50);
 | 
			
		||||
	let mut io = IoHandler::default();
 | 
			
		||||
	io.extend_with(SignerClient::new(&accounts, dispatcher, &signer, event_loop.remote()).to_delegate());
 | 
			
		||||
	io.extend_with(SignerClient::new(&accounts, dispatcher, &signer, runtime.executor()).to_delegate());
 | 
			
		||||
 | 
			
		||||
	SignerTester {
 | 
			
		||||
		_runtime: runtime,
 | 
			
		||||
		signer: signer,
 | 
			
		||||
		accounts: accounts,
 | 
			
		||||
		io: io,
 | 
			
		||||
 | 
			
		||||
@ -39,10 +39,10 @@ use ethstore::ethkey::{Generator, Random};
 | 
			
		||||
use parking_lot::Mutex;
 | 
			
		||||
use serde_json;
 | 
			
		||||
use transaction::{Transaction, Action, SignedTransaction};
 | 
			
		||||
 | 
			
		||||
use parity_reactor::Remote;
 | 
			
		||||
use parity_runtime::{Runtime, Executor};
 | 
			
		||||
 | 
			
		||||
struct SigningTester {
 | 
			
		||||
	pub runtime: Runtime,
 | 
			
		||||
	pub signer: Arc<SignerService>,
 | 
			
		||||
	pub client: Arc<TestBlockChainClient>,
 | 
			
		||||
	pub miner: Arc<TestMinerService>,
 | 
			
		||||
@ -52,23 +52,25 @@ struct SigningTester {
 | 
			
		||||
 | 
			
		||||
impl Default for SigningTester {
 | 
			
		||||
	fn default() -> Self {
 | 
			
		||||
		let runtime = Runtime::with_thread_count(1);
 | 
			
		||||
		let signer = Arc::new(SignerService::new_test(false));
 | 
			
		||||
		let client = Arc::new(TestBlockChainClient::default());
 | 
			
		||||
		let miner = Arc::new(TestMinerService::default());
 | 
			
		||||
		let accounts = Arc::new(AccountProvider::transient_provider());
 | 
			
		||||
		let reservations = Arc::new(Mutex::new(nonce::Reservations::new()));
 | 
			
		||||
		let reservations = Arc::new(Mutex::new(nonce::Reservations::new(runtime.executor())));
 | 
			
		||||
		let mut io = IoHandler::default();
 | 
			
		||||
 | 
			
		||||
		let dispatcher = FullDispatcher::new(client.clone(), miner.clone(), reservations, 50);
 | 
			
		||||
 | 
			
		||||
		let remote = Remote::new_thread_per_future();
 | 
			
		||||
		let executor = Executor::new_thread_per_future();
 | 
			
		||||
 | 
			
		||||
		let rpc = SigningQueueClient::new(&signer, dispatcher.clone(), remote.clone(), &accounts);
 | 
			
		||||
		let rpc = SigningQueueClient::new(&signer, dispatcher.clone(), executor.clone(), &accounts);
 | 
			
		||||
		io.extend_with(EthSigning::to_delegate(rpc));
 | 
			
		||||
		let rpc = SigningQueueClient::new(&signer, dispatcher, remote, &accounts);
 | 
			
		||||
		let rpc = SigningQueueClient::new(&signer, dispatcher, executor, &accounts);
 | 
			
		||||
		io.extend_with(ParitySigning::to_delegate(rpc));
 | 
			
		||||
 | 
			
		||||
		SigningTester {
 | 
			
		||||
			runtime,
 | 
			
		||||
			signer: signer,
 | 
			
		||||
			client: client,
 | 
			
		||||
			miner: miner,
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,7 @@ serde_json = "1.0"
 | 
			
		||||
url = "1.2.0"
 | 
			
		||||
matches = "0.1"
 | 
			
		||||
parking_lot = "0.6"
 | 
			
		||||
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" }
 | 
			
		||||
jsonrpc-ws-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" }
 | 
			
		||||
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-2.2" }
 | 
			
		||||
jsonrpc-ws-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-2.2" }
 | 
			
		||||
parity-rpc = { path = "../rpc" }
 | 
			
		||||
keccak-hash = "0.1"
 | 
			
		||||
 | 
			
		||||
@ -274,7 +274,7 @@ impl Rpc {
 | 
			
		||||
		let request = MethodCall {
 | 
			
		||||
			jsonrpc: Some(Version::V2),
 | 
			
		||||
			method: method.to_owned(),
 | 
			
		||||
			params: Some(Params::Array(params)),
 | 
			
		||||
			params: Params::Array(params),
 | 
			
		||||
			id: Id::Num(id as u64),
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -9,23 +9,20 @@ authors = ["Parity Technologies <admin@parity.io>"]
 | 
			
		||||
byteorder = "1.0"
 | 
			
		||||
log = "0.4"
 | 
			
		||||
parking_lot = "0.6"
 | 
			
		||||
hyper = { version = "0.11", default-features = false }
 | 
			
		||||
hyper = { version = "0.12", default-features = false }
 | 
			
		||||
serde = "1.0"
 | 
			
		||||
serde_json = "1.0"
 | 
			
		||||
serde_derive = "1.0"
 | 
			
		||||
futures = "0.1"
 | 
			
		||||
futures-cpupool = "0.1"
 | 
			
		||||
rustc-hex = "1.0"
 | 
			
		||||
tiny-keccak = "1.4"
 | 
			
		||||
tokio = "0.1"
 | 
			
		||||
tokio-core = "0.1"
 | 
			
		||||
tokio = "~0.1.11"
 | 
			
		||||
tokio-io = "0.1"
 | 
			
		||||
tokio-service = "0.1"
 | 
			
		||||
tokio-proto = "0.1"
 | 
			
		||||
url = "1.0"
 | 
			
		||||
ethcore = { path = "../ethcore" }
 | 
			
		||||
parity-bytes = "0.1"
 | 
			
		||||
parity-crypto = "0.1"
 | 
			
		||||
parity-crypto = "0.2"
 | 
			
		||||
ethcore-logger = { path = "../logger" }
 | 
			
		||||
ethcore-sync = { path = "../ethcore/sync" }
 | 
			
		||||
ethcore-transaction = { path = "../ethcore/transaction" }
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,7 @@ use std::sync::Arc;
 | 
			
		||||
use std::sync::mpsc;
 | 
			
		||||
use futures::{self, Future};
 | 
			
		||||
use parking_lot::Mutex;
 | 
			
		||||
use tokio_core::reactor::Core;
 | 
			
		||||
use tokio::runtime;
 | 
			
		||||
use crypto::DEFAULT_MAC;
 | 
			
		||||
use ethkey::crypto;
 | 
			
		||||
use super::acl_storage::AclStorage;
 | 
			
		||||
@ -191,7 +191,11 @@ impl KeyServerCore {
 | 
			
		||||
		let (stop, stopped) = futures::oneshot();
 | 
			
		||||
		let (tx, rx) = mpsc::channel();
 | 
			
		||||
		let handle = thread::Builder::new().name("KeyServerLoop".into()).spawn(move || {
 | 
			
		||||
			let mut el = match Core::new() {
 | 
			
		||||
			let runtime_res = runtime::Builder::new()
 | 
			
		||||
				.core_threads(config.threads)
 | 
			
		||||
				.build();
 | 
			
		||||
 | 
			
		||||
			let mut el = match runtime_res {
 | 
			
		||||
				Ok(el) => el,
 | 
			
		||||
				Err(e) => {
 | 
			
		||||
					tx.send(Err(Error::Internal(format!("error initializing event loop: {}", e)))).expect("Rx is blocking upper thread.");
 | 
			
		||||
@ -199,10 +203,10 @@ impl KeyServerCore {
 | 
			
		||||
				},
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
			let cluster = ClusterCore::new(el.handle(), config);
 | 
			
		||||
			let cluster = ClusterCore::new(el.executor(), config);
 | 
			
		||||
			let cluster_client = cluster.and_then(|c| c.run().map(|_| c.client()));
 | 
			
		||||
			tx.send(cluster_client.map_err(Into::into)).expect("Rx is blocking upper thread.");
 | 
			
		||||
			let _ = el.run(futures::empty().select(stopped));
 | 
			
		||||
			let _ = el.block_on(futures::empty().select(stopped));
 | 
			
		||||
 | 
			
		||||
			trace!(target: "secretstore_net", "{}: KeyServerLoop thread stopped", self_key_pair.public());
 | 
			
		||||
		}).map_err(|e| Error::Internal(format!("{}", e)))?;
 | 
			
		||||
 | 
			
		||||
@ -701,7 +701,7 @@ impl SessionImpl {
 | 
			
		||||
		let threshold = data.threshold.expect("threshold is filled in initialization phase; KV phase follows initialization phase; qed");
 | 
			
		||||
		let is_zero = data.is_zero.expect("is_zero is filled in initialization phase; KV phase follows initialization phase; qed");
 | 
			
		||||
		let self_public_share = {
 | 
			
		||||
			if !is_zero { 
 | 
			
		||||
			if !is_zero {
 | 
			
		||||
				let derived_point = data.derived_point.clone().expect("derived point generated on initialization phase; KV phase follows initialization phase; qed");
 | 
			
		||||
				let number_id = data.nodes[self.node()].id_number.clone();
 | 
			
		||||
				for (_	, node_data) in data.nodes.iter_mut().filter(|&(node_id, _)| node_id != self.node()) {
 | 
			
		||||
@ -942,12 +942,12 @@ pub mod tests {
 | 
			
		||||
	use std::sync::Arc;
 | 
			
		||||
	use std::collections::{BTreeSet, BTreeMap, VecDeque};
 | 
			
		||||
	use std::time::Duration;
 | 
			
		||||
	use tokio_core::reactor::Core;
 | 
			
		||||
	use ethereum_types::Address;
 | 
			
		||||
	use ethkey::{Random, Generator, KeyPair};
 | 
			
		||||
	use key_server_cluster::{NodeId, SessionId, Error, KeyStorage, DummyKeyStorage};
 | 
			
		||||
	use key_server_cluster::message::{self, Message, GenerationMessage};
 | 
			
		||||
	use key_server_cluster::cluster::tests::{DummyCluster, make_clusters, run_clusters, loop_until, all_connections_established};
 | 
			
		||||
	use key_server_cluster::cluster::tests::{DummyCluster, make_clusters, run_clusters, loop_until,
 | 
			
		||||
		all_connections_established, new_runtime};
 | 
			
		||||
	use key_server_cluster::cluster_sessions::ClusterSession;
 | 
			
		||||
	use key_server_cluster::generation_session::{SessionImpl, SessionState, SessionParams};
 | 
			
		||||
	use key_server_cluster::math;
 | 
			
		||||
@ -1357,19 +1357,22 @@ pub mod tests {
 | 
			
		||||
 | 
			
		||||
		let test_cases = [(1, 3)];
 | 
			
		||||
		for &(threshold, num_nodes) in &test_cases {
 | 
			
		||||
			let mut core = Core::new().unwrap();
 | 
			
		||||
			let mut core = new_runtime();
 | 
			
		||||
 | 
			
		||||
			// prepare cluster objects for each node
 | 
			
		||||
			let clusters = make_clusters(&core, 6031, num_nodes);
 | 
			
		||||
			run_clusters(&clusters);
 | 
			
		||||
 | 
			
		||||
			// `clusters` contains `Arc<ClusterCore>` and clones will refer to the same cores.
 | 
			
		||||
			let clusters_clone = clusters.clone();
 | 
			
		||||
 | 
			
		||||
			// establish connections
 | 
			
		||||
			loop_until(&mut core, CONN_TIMEOUT, || clusters.iter().all(all_connections_established));
 | 
			
		||||
			loop_until(&mut core, CONN_TIMEOUT, move || clusters_clone.iter().all(all_connections_established));
 | 
			
		||||
 | 
			
		||||
			// run session to completion
 | 
			
		||||
			let session_id = SessionId::default();
 | 
			
		||||
			let session = clusters[0].client().new_generation_session(session_id, Default::default(), Default::default(), threshold).unwrap();
 | 
			
		||||
			loop_until(&mut core, SESSION_TIMEOUT, || session.joint_public_and_secret().is_some());
 | 
			
		||||
			loop_until(&mut core, SESSION_TIMEOUT, move || session.joint_public_and_secret().is_some());
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -17,15 +17,16 @@
 | 
			
		||||
use std::io;
 | 
			
		||||
use std::time::{Duration, Instant};
 | 
			
		||||
use std::sync::Arc;
 | 
			
		||||
use std::sync::atomic::{AtomicBool, Ordering};
 | 
			
		||||
use std::collections::{BTreeMap, BTreeSet};
 | 
			
		||||
use std::collections::btree_map::Entry;
 | 
			
		||||
use std::net::{SocketAddr, IpAddr};
 | 
			
		||||
use futures::{finished, failed, Future, Stream};
 | 
			
		||||
use futures_cpupool::CpuPool;
 | 
			
		||||
use parking_lot::{RwLock, Mutex};
 | 
			
		||||
use futures::{future, Future, Stream};
 | 
			
		||||
use parking_lot::{Mutex, RwLock};
 | 
			
		||||
use tokio_io::IoFuture;
 | 
			
		||||
use tokio_core::reactor::{Handle, Remote, Interval};
 | 
			
		||||
use tokio_core::net::{TcpListener, TcpStream};
 | 
			
		||||
use tokio::runtime::TaskExecutor;
 | 
			
		||||
use tokio::timer::{Interval, timeout::Error as TimeoutError};
 | 
			
		||||
use tokio::net::{TcpListener, TcpStream};
 | 
			
		||||
use ethkey::{Public, KeyPair, Signature, Random, Generator};
 | 
			
		||||
use ethereum_types::{Address, H256};
 | 
			
		||||
use key_server_cluster::{Error, NodeId, SessionId, Requester, AclStorage, KeyStorage, KeyServerSet, NodeKeyPair};
 | 
			
		||||
@ -136,8 +137,9 @@ pub struct ClusterConfiguration {
 | 
			
		||||
	pub acl_storage: Arc<AclStorage>,
 | 
			
		||||
	/// Administrator public key.
 | 
			
		||||
	pub admin_public: Option<Public>,
 | 
			
		||||
	/// Should key servers set change session should be started when servers set changes.
 | 
			
		||||
	/// This will only work when servers set is configured using KeyServerSet contract.
 | 
			
		||||
	/// Should key servers set change session when servers set changes? This
 | 
			
		||||
	/// will only work when servers set is configured using KeyServerSet
 | 
			
		||||
	/// contract.
 | 
			
		||||
	pub auto_migrate_enabled: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -149,8 +151,6 @@ pub struct ClusterState {
 | 
			
		||||
 | 
			
		||||
/// Network cluster implementation.
 | 
			
		||||
pub struct ClusterCore {
 | 
			
		||||
	/// Handle to the event loop.
 | 
			
		||||
	handle: Handle,
 | 
			
		||||
	/// Listen address.
 | 
			
		||||
	listen_address: SocketAddr,
 | 
			
		||||
	/// Cluster data.
 | 
			
		||||
@ -165,7 +165,7 @@ pub struct ClusterClientImpl {
 | 
			
		||||
 | 
			
		||||
/// Network cluster view. It is a communication channel, required in single session.
 | 
			
		||||
pub struct ClusterView {
 | 
			
		||||
	core: Arc<Mutex<ClusterViewCore>>,
 | 
			
		||||
	core: Arc<RwLock<ClusterViewCore>>,
 | 
			
		||||
	configured_nodes_count: usize,
 | 
			
		||||
	connected_nodes_count: usize,
 | 
			
		||||
}
 | 
			
		||||
@ -175,15 +175,15 @@ pub struct ClusterData {
 | 
			
		||||
	/// Cluster configuration.
 | 
			
		||||
	pub config: ClusterConfiguration,
 | 
			
		||||
	/// Handle to the event loop.
 | 
			
		||||
	pub handle: Remote,
 | 
			
		||||
	/// Handle to the cpu thread pool.
 | 
			
		||||
	pub pool: CpuPool,
 | 
			
		||||
	pub executor: TaskExecutor,
 | 
			
		||||
	/// KeyPair this node holds.
 | 
			
		||||
	pub self_key_pair: Arc<NodeKeyPair>,
 | 
			
		||||
	/// Connections data.
 | 
			
		||||
	pub connections: ClusterConnections,
 | 
			
		||||
	/// Active sessions data.
 | 
			
		||||
	pub sessions: ClusterSessions,
 | 
			
		||||
	/// Shutdown flag:
 | 
			
		||||
	pub is_shutdown: Arc<AtomicBool>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Connections that are forming the cluster. Lock order: trigger.lock() -> data.lock().
 | 
			
		||||
@ -231,19 +231,18 @@ pub struct Connection {
 | 
			
		||||
	/// Connection key.
 | 
			
		||||
	key: KeyPair,
 | 
			
		||||
	/// Last message time.
 | 
			
		||||
	last_message_time: Mutex<Instant>,
 | 
			
		||||
	last_message_time: RwLock<Instant>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ClusterCore {
 | 
			
		||||
	pub fn new(handle: Handle, config: ClusterConfiguration) -> Result<Arc<Self>, Error> {
 | 
			
		||||
	pub fn new(executor: TaskExecutor, config: ClusterConfiguration) -> Result<Arc<Self>, Error> {
 | 
			
		||||
		let listen_address = make_socket_address(&config.listen_address.0, config.listen_address.1)?;
 | 
			
		||||
		let connections = ClusterConnections::new(&config)?;
 | 
			
		||||
		let servers_set_change_creator_connector = connections.connector.clone();
 | 
			
		||||
		let sessions = ClusterSessions::new(&config, servers_set_change_creator_connector);
 | 
			
		||||
		let data = ClusterData::new(&handle, config, connections, sessions);
 | 
			
		||||
		let data = ClusterData::new(&executor, config, connections, sessions);
 | 
			
		||||
 | 
			
		||||
		Ok(Arc::new(ClusterCore {
 | 
			
		||||
			handle: handle,
 | 
			
		||||
			listen_address: listen_address,
 | 
			
		||||
			data: data,
 | 
			
		||||
		}))
 | 
			
		||||
@ -272,7 +271,7 @@ impl ClusterCore {
 | 
			
		||||
			.and_then(|_| self.run_connections())?;
 | 
			
		||||
 | 
			
		||||
		// schedule maintain procedures
 | 
			
		||||
		ClusterCore::schedule_maintain(&self.handle, self.data.clone());
 | 
			
		||||
		ClusterCore::schedule_maintain(self.data.clone());
 | 
			
		||||
 | 
			
		||||
		Ok(())
 | 
			
		||||
	}
 | 
			
		||||
@ -280,7 +279,7 @@ impl ClusterCore {
 | 
			
		||||
	/// Start listening for incoming connections.
 | 
			
		||||
	pub fn run_listener(&self) -> Result<(), Error> {
 | 
			
		||||
		// start listeining for incoming connections
 | 
			
		||||
		self.handle.spawn(ClusterCore::listen(&self.handle, self.data.clone(), self.listen_address.clone())?);
 | 
			
		||||
		self.data.spawn(ClusterCore::listen(self.data.clone(), self.listen_address.clone())?);
 | 
			
		||||
		Ok(())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -293,53 +292,49 @@ impl ClusterCore {
 | 
			
		||||
 | 
			
		||||
	/// Connect to peer.
 | 
			
		||||
	fn connect(data: Arc<ClusterData>, node_address: SocketAddr) {
 | 
			
		||||
		data.handle.clone().spawn(move |handle| {
 | 
			
		||||
			data.pool.clone().spawn(ClusterCore::connect_future(handle, data, node_address))
 | 
			
		||||
		})
 | 
			
		||||
		data.clone().spawn(ClusterCore::connect_future(data, node_address));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Connect to socket using given context and handle.
 | 
			
		||||
	fn connect_future(handle: &Handle, data: Arc<ClusterData>, node_address: SocketAddr) -> BoxedEmptyFuture {
 | 
			
		||||
	/// Connect to socket using given context and executor.
 | 
			
		||||
	fn connect_future(data: Arc<ClusterData>, node_address: SocketAddr) -> BoxedEmptyFuture {
 | 
			
		||||
		let disconnected_nodes = data.connections.disconnected_nodes().keys().cloned().collect();
 | 
			
		||||
		Box::new(net_connect(&node_address, handle, data.self_key_pair.clone(), disconnected_nodes)
 | 
			
		||||
		Box::new(net_connect(&node_address, data.self_key_pair.clone(), disconnected_nodes)
 | 
			
		||||
			.then(move |result| ClusterCore::process_connection_result(data, Some(node_address), result))
 | 
			
		||||
			.then(|_| finished(())))
 | 
			
		||||
			.then(|_| future::ok(())))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Start listening for incoming connections.
 | 
			
		||||
	fn listen(handle: &Handle, data: Arc<ClusterData>, listen_address: SocketAddr) -> Result<BoxedEmptyFuture, Error> {
 | 
			
		||||
		Ok(Box::new(TcpListener::bind(&listen_address, &handle)?
 | 
			
		||||
	fn listen(data: Arc<ClusterData>, listen_address: SocketAddr) -> Result<BoxedEmptyFuture, Error> {
 | 
			
		||||
		Ok(Box::new(TcpListener::bind(&listen_address)?
 | 
			
		||||
			.incoming()
 | 
			
		||||
			.and_then(move |(stream, node_address)| {
 | 
			
		||||
				ClusterCore::accept_connection(data.clone(), stream, node_address);
 | 
			
		||||
			.and_then(move |stream| {
 | 
			
		||||
				ClusterCore::accept_connection(data.clone(), stream);
 | 
			
		||||
				Ok(())
 | 
			
		||||
			})
 | 
			
		||||
			.for_each(|_| Ok(()))
 | 
			
		||||
			.then(|_| finished(()))))
 | 
			
		||||
			.then(|_| future::ok(()))))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Accept connection.
 | 
			
		||||
	fn accept_connection(data: Arc<ClusterData>, stream: TcpStream, node_address: SocketAddr) {
 | 
			
		||||
		data.handle.clone().spawn(move |handle| {
 | 
			
		||||
			data.pool.clone().spawn(ClusterCore::accept_connection_future(handle, data, stream, node_address))
 | 
			
		||||
		})
 | 
			
		||||
	fn accept_connection(data: Arc<ClusterData>, stream: TcpStream) {
 | 
			
		||||
		data.clone().spawn(ClusterCore::accept_connection_future(data, stream))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Accept connection future.
 | 
			
		||||
	fn accept_connection_future(handle: &Handle, data: Arc<ClusterData>, stream: TcpStream, node_address: SocketAddr) -> BoxedEmptyFuture {
 | 
			
		||||
		Box::new(net_accept_connection(node_address, stream, handle, data.self_key_pair.clone())
 | 
			
		||||
	fn accept_connection_future(data: Arc<ClusterData>, stream: TcpStream) -> BoxedEmptyFuture {
 | 
			
		||||
		Box::new(net_accept_connection(stream, data.self_key_pair.clone())
 | 
			
		||||
			.then(move |result| ClusterCore::process_connection_result(data, None, result))
 | 
			
		||||
			.then(|_| finished(())))
 | 
			
		||||
			.then(|_| future::ok(())))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Schedule mainatain procedures.
 | 
			
		||||
	fn schedule_maintain(handle: &Handle, data: Arc<ClusterData>) {
 | 
			
		||||
	fn schedule_maintain(data: Arc<ClusterData>) {
 | 
			
		||||
		let d = data.clone();
 | 
			
		||||
		let interval: BoxedEmptyFuture = Box::new(Interval::new(Duration::new(MAINTAIN_INTERVAL, 0), handle)
 | 
			
		||||
			.expect("failed to create interval")
 | 
			
		||||
 | 
			
		||||
		let interval = Interval::new_interval(Duration::new(MAINTAIN_INTERVAL, 0))
 | 
			
		||||
			.and_then(move |_| Ok(ClusterCore::maintain(data.clone())))
 | 
			
		||||
			.for_each(|_| Ok(()))
 | 
			
		||||
			.then(|_| finished(())));
 | 
			
		||||
			.then(|_| future::ok(()));
 | 
			
		||||
 | 
			
		||||
		d.spawn(interval);
 | 
			
		||||
	}
 | 
			
		||||
@ -362,20 +357,20 @@ impl ClusterCore {
 | 
			
		||||
					Ok((_, Ok(message))) => {
 | 
			
		||||
						ClusterCore::process_connection_message(data.clone(), connection.clone(), message);
 | 
			
		||||
						// continue serving connection
 | 
			
		||||
						data.spawn(ClusterCore::process_connection_messages(data.clone(), connection));
 | 
			
		||||
						Box::new(finished(Ok(())))
 | 
			
		||||
						data.spawn(ClusterCore::process_connection_messages(data.clone(), connection).then(|_| Ok(())));
 | 
			
		||||
						Box::new(future::ok(Ok(())))
 | 
			
		||||
					},
 | 
			
		||||
					Ok((_, Err(err))) => {
 | 
			
		||||
						warn!(target: "secretstore_net", "{}: protocol error '{}' when reading message from node {}", data.self_key_pair.public(), err, connection.node_id());
 | 
			
		||||
						// continue serving connection
 | 
			
		||||
						data.spawn(ClusterCore::process_connection_messages(data.clone(), connection));
 | 
			
		||||
						Box::new(finished(Err(err)))
 | 
			
		||||
						data.spawn(ClusterCore::process_connection_messages(data.clone(), connection).then(|_| Ok(())));
 | 
			
		||||
						Box::new(future::ok(Err(err)))
 | 
			
		||||
					},
 | 
			
		||||
					Err(err) => {
 | 
			
		||||
						warn!(target: "secretstore_net", "{}: network error '{}' when reading message from node {}", data.self_key_pair.public(), err, connection.node_id());
 | 
			
		||||
						// close connection
 | 
			
		||||
						data.connections.remove(data.clone(), connection.node_id(), connection.is_inbound());
 | 
			
		||||
						Box::new(failed(err))
 | 
			
		||||
						Box::new(future::err(err))
 | 
			
		||||
					},
 | 
			
		||||
				}
 | 
			
		||||
			))
 | 
			
		||||
@ -394,7 +389,7 @@ impl ClusterCore {
 | 
			
		||||
				data.sessions.on_connection_timeout(connection.node_id());
 | 
			
		||||
			}
 | 
			
		||||
			else if last_message_diff > KEEP_ALIVE_SEND_INTERVAL {
 | 
			
		||||
				data.spawn(connection.send_message(Message::Cluster(ClusterMessage::KeepAlive(message::KeepAlive {}))));
 | 
			
		||||
				data.spawn(connection.send_message(Message::Cluster(ClusterMessage::KeepAlive(message::KeepAlive {}))).then(|_| Ok(())));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@ -415,33 +410,35 @@ impl ClusterCore {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Process connection future result.
 | 
			
		||||
	fn process_connection_result(data: Arc<ClusterData>, outbound_addr: Option<SocketAddr>, result: Result<DeadlineStatus<Result<NetConnection, Error>>, io::Error>) -> IoFuture<Result<(), Error>> {
 | 
			
		||||
	fn process_connection_result(data: Arc<ClusterData>, outbound_addr: Option<SocketAddr>,
 | 
			
		||||
		result: Result<DeadlineStatus<Result<NetConnection, Error>>, TimeoutError<io::Error>>) -> IoFuture<Result<(), Error>>
 | 
			
		||||
	{
 | 
			
		||||
		match result {
 | 
			
		||||
			Ok(DeadlineStatus::Meet(Ok(connection))) => {
 | 
			
		||||
				let connection = Connection::new(outbound_addr.is_none(), connection);
 | 
			
		||||
				if data.connections.insert(data.clone(), connection.clone()) {
 | 
			
		||||
					ClusterCore::process_connection_messages(data.clone(), connection)
 | 
			
		||||
				} else {
 | 
			
		||||
					Box::new(finished(Ok(())))
 | 
			
		||||
					Box::new(future::ok(Ok(())))
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			Ok(DeadlineStatus::Meet(Err(err))) => {
 | 
			
		||||
				warn!(target: "secretstore_net", "{}: protocol error '{}' when establishing {} connection{}",
 | 
			
		||||
					data.self_key_pair.public(), err, if outbound_addr.is_some() { "outbound" } else { "inbound" },
 | 
			
		||||
					outbound_addr.map(|a| format!(" with {}", a)).unwrap_or_default());
 | 
			
		||||
				Box::new(finished(Ok(())))
 | 
			
		||||
				Box::new(future::ok(Ok(())))
 | 
			
		||||
			},
 | 
			
		||||
			Ok(DeadlineStatus::Timeout) => {
 | 
			
		||||
				warn!(target: "secretstore_net", "{}: timeout when establishing {} connection{}",
 | 
			
		||||
					data.self_key_pair.public(), if outbound_addr.is_some() { "outbound" } else { "inbound" },
 | 
			
		||||
					outbound_addr.map(|a| format!(" with {}", a)).unwrap_or_default());
 | 
			
		||||
				Box::new(finished(Ok(())))
 | 
			
		||||
				Box::new(future::ok(Ok(())))
 | 
			
		||||
			},
 | 
			
		||||
			Err(err) => {
 | 
			
		||||
				warn!(target: "secretstore_net", "{}: network error '{}' when establishing {} connection{}",
 | 
			
		||||
					data.self_key_pair.public(), err, if outbound_addr.is_some() { "outbound" } else { "inbound" },
 | 
			
		||||
					outbound_addr.map(|a| format!(" with {}", a)).unwrap_or_default());
 | 
			
		||||
				Box::new(finished(Ok(())))
 | 
			
		||||
				Box::new(future::ok(Ok(())))
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@ -595,7 +592,7 @@ impl ClusterCore {
 | 
			
		||||
				if !message.is_error_message() {
 | 
			
		||||
					let session_id = message.into_session_id().expect("session_id only fails for cluster messages; only session messages are passed to process_message; qed");
 | 
			
		||||
					let session_nonce = message.session_nonce().expect("session_nonce only fails for cluster messages; only session messages are passed to process_message; qed");
 | 
			
		||||
					data.spawn(connection.send_message(SC::make_error_message(session_id, session_nonce, error)));
 | 
			
		||||
					data.spawn(connection.send_message(SC::make_error_message(session_id, session_nonce, error)).then(|_| Ok(())));
 | 
			
		||||
				}
 | 
			
		||||
				return None;
 | 
			
		||||
			},
 | 
			
		||||
@ -648,13 +645,19 @@ impl ClusterCore {
 | 
			
		||||
		match message {
 | 
			
		||||
			ClusterMessage::KeepAlive(_) => data.spawn(connection.send_message(Message::Cluster(ClusterMessage::KeepAliveResponse(message::KeepAliveResponse {
 | 
			
		||||
				session_id: None,
 | 
			
		||||
			})))),
 | 
			
		||||
			}))).then(|_| Ok(()))),
 | 
			
		||||
			ClusterMessage::KeepAliveResponse(msg) => if let Some(session_id) = msg.session_id {
 | 
			
		||||
				data.sessions.on_session_keep_alive(connection.node_id(), session_id.into());
 | 
			
		||||
			},
 | 
			
		||||
			_ => warn!(target: "secretstore_net", "{}: received unexpected message {} from node {} at {}", data.self_key_pair.public(), message, connection.node_id(), connection.node_address()),
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Prevents new tasks from being spawned.
 | 
			
		||||
	#[cfg(test)]
 | 
			
		||||
	pub fn shutdown(&self) {
 | 
			
		||||
		self.data.shutdown()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ClusterConnections {
 | 
			
		||||
@ -787,14 +790,14 @@ impl ClusterConnections {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ClusterData {
 | 
			
		||||
	pub fn new(handle: &Handle, config: ClusterConfiguration, connections: ClusterConnections, sessions: ClusterSessions) -> Arc<Self> {
 | 
			
		||||
	pub fn new(executor: &TaskExecutor, config: ClusterConfiguration, connections: ClusterConnections, sessions: ClusterSessions) -> Arc<Self> {
 | 
			
		||||
		Arc::new(ClusterData {
 | 
			
		||||
			handle: handle.remote().clone(),
 | 
			
		||||
			pool: CpuPool::new(config.threads),
 | 
			
		||||
			executor: executor.clone(),
 | 
			
		||||
			self_key_pair: config.self_key_pair.clone(),
 | 
			
		||||
			connections: connections,
 | 
			
		||||
			sessions: sessions,
 | 
			
		||||
			config: config,
 | 
			
		||||
			is_shutdown: Arc::new(AtomicBool::new(false)),
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -803,12 +806,28 @@ impl ClusterData {
 | 
			
		||||
		self.connections.get(node)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Spawns a future using thread pool and schedules execution of it with event loop handle.
 | 
			
		||||
	pub fn spawn<F>(&self, f: F) where F: Future + Send + 'static, F::Item: Send + 'static, F::Error: Send + 'static {
 | 
			
		||||
		let pool_work = self.pool.spawn(f);
 | 
			
		||||
		self.handle.spawn(move |_handle| {
 | 
			
		||||
			pool_work.then(|_| finished(()))
 | 
			
		||||
		})
 | 
			
		||||
	/// Spawns a future on the runtime.
 | 
			
		||||
	//
 | 
			
		||||
	// TODO: Consider implementing a more graceful shutdown process using an
 | 
			
		||||
	// `AtomicBool`, etc. which would prevent tasks from being spawned after a
 | 
			
		||||
	// shutdown signal is given. (Recursive calls, in
 | 
			
		||||
	// `process_connection_messages` for example, appear to continue
 | 
			
		||||
	// indefinitely.)
 | 
			
		||||
	pub fn spawn<F>(&self, f: F) where F: Future<Item = (), Error = ()> + Send + 'static {
 | 
			
		||||
		if self.is_shutdown.load(Ordering::Acquire) == false {
 | 
			
		||||
			if let Err(err) = future::Executor::execute(&self.executor, Box::new(f)) {
 | 
			
		||||
				error!("Secret store runtime unable to spawn task. Runtime is shutting down. ({:?})", err);
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			error!("Secret store runtime unable to spawn task. Shutdown has been started.");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Sets the `is_shutdown` flag which prevents future tasks from being
 | 
			
		||||
	/// spawned via `::spawn`.
 | 
			
		||||
	#[cfg(test)]
 | 
			
		||||
	pub fn shutdown(&self) {
 | 
			
		||||
		self.is_shutdown.store(true, Ordering::Release);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -820,7 +839,7 @@ impl Connection {
 | 
			
		||||
			is_inbound: is_inbound,
 | 
			
		||||
			stream: connection.stream,
 | 
			
		||||
			key: connection.key,
 | 
			
		||||
			last_message_time: Mutex::new(Instant::now()),
 | 
			
		||||
			last_message_time: RwLock::new(Instant::now()),
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -833,11 +852,11 @@ impl Connection {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pub fn last_message_time(&self) -> Instant {
 | 
			
		||||
		*self.last_message_time.lock()
 | 
			
		||||
		*self.last_message_time.read()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pub fn set_last_message_time(&self, last_message_time: Instant) {
 | 
			
		||||
		*self.last_message_time.lock() = last_message_time;
 | 
			
		||||
		*self.last_message_time.write() = last_message_time;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pub fn node_address(&self) -> &SocketAddr {
 | 
			
		||||
@ -858,7 +877,7 @@ impl ClusterView {
 | 
			
		||||
		ClusterView {
 | 
			
		||||
			configured_nodes_count: configured_nodes_count,
 | 
			
		||||
			connected_nodes_count: nodes.len(),
 | 
			
		||||
			core: Arc::new(Mutex::new(ClusterViewCore {
 | 
			
		||||
			core: Arc::new(RwLock::new(ClusterViewCore {
 | 
			
		||||
				cluster: cluster,
 | 
			
		||||
				nodes: nodes,
 | 
			
		||||
			})),
 | 
			
		||||
@ -868,29 +887,29 @@ impl ClusterView {
 | 
			
		||||
 | 
			
		||||
impl Cluster for ClusterView {
 | 
			
		||||
	fn broadcast(&self, message: Message) -> Result<(), Error> {
 | 
			
		||||
		let core = self.core.lock();
 | 
			
		||||
		let core = self.core.read();
 | 
			
		||||
		for node in core.nodes.iter().filter(|n| *n != core.cluster.self_key_pair.public()) {
 | 
			
		||||
			trace!(target: "secretstore_net", "{}: sent message {} to {}", core.cluster.self_key_pair.public(), message, node);
 | 
			
		||||
			let connection = core.cluster.connection(node).ok_or(Error::NodeDisconnected)?;
 | 
			
		||||
			core.cluster.spawn(connection.send_message(message.clone()))
 | 
			
		||||
			core.cluster.spawn(connection.send_message(message.clone()).then(|_| Ok(())))
 | 
			
		||||
		}
 | 
			
		||||
		Ok(())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fn send(&self, to: &NodeId, message: Message) -> Result<(), Error> {
 | 
			
		||||
		let core = self.core.lock();
 | 
			
		||||
		let core = self.core.read();
 | 
			
		||||
		trace!(target: "secretstore_net", "{}: sent message {} to {}", core.cluster.self_key_pair.public(), message, to);
 | 
			
		||||
		let connection = core.cluster.connection(to).ok_or(Error::NodeDisconnected)?;
 | 
			
		||||
		core.cluster.spawn(connection.send_message(message));
 | 
			
		||||
		core.cluster.spawn(connection.send_message(message).then(|_| Ok(())));
 | 
			
		||||
		Ok(())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fn is_connected(&self, node: &NodeId) -> bool {
 | 
			
		||||
		self.core.lock().nodes.contains(node)
 | 
			
		||||
		self.core.read().nodes.contains(node)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fn nodes(&self) -> BTreeSet<NodeId> {
 | 
			
		||||
		self.core.lock().nodes.clone()
 | 
			
		||||
		self.core.read().nodes.clone()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fn configured_nodes_count(&self) -> usize {
 | 
			
		||||
@ -1118,8 +1137,11 @@ pub mod tests {
 | 
			
		||||
	use std::sync::atomic::{AtomicUsize, Ordering};
 | 
			
		||||
	use std::time::{Duration, Instant};
 | 
			
		||||
	use std::collections::{BTreeSet, VecDeque};
 | 
			
		||||
	use parking_lot::Mutex;
 | 
			
		||||
	use tokio_core::reactor::Core;
 | 
			
		||||
	use parking_lot::RwLock;
 | 
			
		||||
	use tokio::{
 | 
			
		||||
		runtime::{Runtime, Builder as RuntimeBuilder},
 | 
			
		||||
		prelude::{future, Future},
 | 
			
		||||
	};
 | 
			
		||||
	use ethereum_types::{Address, H256};
 | 
			
		||||
	use ethkey::{Random, Generator, Public, Signature, sign};
 | 
			
		||||
	use key_server_cluster::{NodeId, SessionId, Requester, Error, DummyAclStorage, DummyKeyStorage,
 | 
			
		||||
@ -1135,7 +1157,7 @@ pub mod tests {
 | 
			
		||||
	use key_server_cluster::key_version_negotiation_session::{SessionImpl as KeyVersionNegotiationSession,
 | 
			
		||||
		IsolatedSessionTransport as KeyVersionNegotiationSessionTransport};
 | 
			
		||||
 | 
			
		||||
	const TIMEOUT: Duration = Duration::from_millis(300);
 | 
			
		||||
	const TIMEOUT: Duration = Duration::from_millis(1000);
 | 
			
		||||
 | 
			
		||||
	#[derive(Default)]
 | 
			
		||||
	pub struct DummyClusterClient {
 | 
			
		||||
@ -1145,7 +1167,7 @@ pub mod tests {
 | 
			
		||||
	#[derive(Debug)]
 | 
			
		||||
	pub struct DummyCluster {
 | 
			
		||||
		id: NodeId,
 | 
			
		||||
		data: Mutex<DummyClusterData>,
 | 
			
		||||
		data: RwLock<DummyClusterData>,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	#[derive(Debug, Default)]
 | 
			
		||||
@ -1182,7 +1204,7 @@ pub mod tests {
 | 
			
		||||
		pub fn new(id: NodeId) -> Self {
 | 
			
		||||
			DummyCluster {
 | 
			
		||||
				id: id,
 | 
			
		||||
				data: Mutex::new(DummyClusterData::default())
 | 
			
		||||
				data: RwLock::new(DummyClusterData::default())
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -1191,25 +1213,25 @@ pub mod tests {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		pub fn add_node(&self, node: NodeId) {
 | 
			
		||||
			self.data.lock().nodes.insert(node);
 | 
			
		||||
			self.data.write().nodes.insert(node);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		pub fn add_nodes<I: Iterator<Item=NodeId>>(&self, nodes: I) {
 | 
			
		||||
			self.data.lock().nodes.extend(nodes)
 | 
			
		||||
			self.data.write().nodes.extend(nodes)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		pub fn remove_node(&self, node: &NodeId) {
 | 
			
		||||
			self.data.lock().nodes.remove(node);
 | 
			
		||||
			self.data.write().nodes.remove(node);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		pub fn take_message(&self) -> Option<(NodeId, Message)> {
 | 
			
		||||
			self.data.lock().messages.pop_front()
 | 
			
		||||
			self.data.write().messages.pop_front()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	impl Cluster for DummyCluster {
 | 
			
		||||
		fn broadcast(&self, message: Message) -> Result<(), Error> {
 | 
			
		||||
			let mut data = self.data.lock();
 | 
			
		||||
			let mut data = self.data.write();
 | 
			
		||||
			let all_nodes: Vec<_> = data.nodes.iter().cloned().filter(|n| n != &self.id).collect();
 | 
			
		||||
			for node in all_nodes {
 | 
			
		||||
				data.messages.push_back((node, message.clone()));
 | 
			
		||||
@ -1219,40 +1241,49 @@ pub mod tests {
 | 
			
		||||
 | 
			
		||||
		fn send(&self, to: &NodeId, message: Message) -> Result<(), Error> {
 | 
			
		||||
			debug_assert!(&self.id != to);
 | 
			
		||||
			self.data.lock().messages.push_back((to.clone(), message));
 | 
			
		||||
			self.data.write().messages.push_back((to.clone(), message));
 | 
			
		||||
			Ok(())
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		fn is_connected(&self, node: &NodeId) -> bool {
 | 
			
		||||
			let data = self.data.lock();
 | 
			
		||||
			let data = self.data.read();
 | 
			
		||||
			&self.id == node || data.nodes.contains(node)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		fn nodes(&self) -> BTreeSet<NodeId> {
 | 
			
		||||
			self.data.lock().nodes.iter().cloned().collect()
 | 
			
		||||
			self.data.read().nodes.iter().cloned().collect()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		fn configured_nodes_count(&self) -> usize {
 | 
			
		||||
			self.data.lock().nodes.len()
 | 
			
		||||
			self.data.read().nodes.len()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		fn connected_nodes_count(&self) -> usize {
 | 
			
		||||
			self.data.lock().nodes.len()
 | 
			
		||||
			self.data.read().nodes.len()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pub fn loop_until<F>(core: &mut Core, timeout: Duration, predicate: F) where F: Fn() -> bool {
 | 
			
		||||
		let start = Instant::now();
 | 
			
		||||
		loop {
 | 
			
		||||
			core.turn(Some(Duration::from_millis(1)));
 | 
			
		||||
			if predicate() {
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
	/// Loops until `predicate` returns `true` or `timeout` has elapsed.
 | 
			
		||||
	pub fn loop_until<F>(runtime: &mut Runtime, timeout: Duration, predicate: F)
 | 
			
		||||
		where F: Send + 'static + Fn() -> bool
 | 
			
		||||
	{
 | 
			
		||||
		use futures::Stream;
 | 
			
		||||
		use tokio::timer::Interval;
 | 
			
		||||
 | 
			
		||||
			if Instant::now() - start > timeout {
 | 
			
		||||
				panic!("no result in {:?}", timeout);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		let start = Instant::now();
 | 
			
		||||
 | 
			
		||||
		runtime.block_on(Interval::new_interval(Duration::from_millis(1))
 | 
			
		||||
			.and_then(move |_| {
 | 
			
		||||
				if Instant::now() - start > timeout {
 | 
			
		||||
					panic!("no result in {:?}", timeout);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				Ok(())
 | 
			
		||||
			})
 | 
			
		||||
			.take_while(move |_| future::ok(!predicate()))
 | 
			
		||||
			.for_each(|_| Ok(()))
 | 
			
		||||
			.then(|_| future::ok::<(), ()>(()))
 | 
			
		||||
		).unwrap();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pub fn all_connections_established(cluster: &Arc<ClusterCore>) -> bool {
 | 
			
		||||
@ -1261,7 +1292,7 @@ pub mod tests {
 | 
			
		||||
			.all(|p| cluster.connection(p).is_some())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pub fn make_clusters(core: &Core, ports_begin: u16, num_nodes: usize) -> Vec<Arc<ClusterCore>> {
 | 
			
		||||
	pub fn make_clusters(runtime: &Runtime, ports_begin: u16, num_nodes: usize) -> Vec<Arc<ClusterCore>> {
 | 
			
		||||
		let key_pairs: Vec<_> = (0..num_nodes).map(|_| Random.generate().unwrap()).collect();
 | 
			
		||||
		let cluster_params: Vec<_> = (0..num_nodes).map(|i| ClusterConfiguration {
 | 
			
		||||
			threads: 1,
 | 
			
		||||
@ -1277,7 +1308,7 @@ pub mod tests {
 | 
			
		||||
			auto_migrate_enabled: false,
 | 
			
		||||
		}).collect();
 | 
			
		||||
		let clusters: Vec<_> = cluster_params.into_iter().enumerate()
 | 
			
		||||
			.map(|(_, params)| ClusterCore::new(core.handle(), params).unwrap())
 | 
			
		||||
			.map(|(_, params)| ClusterCore::new(runtime.executor(), params).unwrap())
 | 
			
		||||
			.collect();
 | 
			
		||||
 | 
			
		||||
		clusters
 | 
			
		||||
@ -1292,97 +1323,134 @@ pub mod tests {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pub fn shutdown_clusters(clusters: &[Arc<ClusterCore>]) {
 | 
			
		||||
		for cluster in clusters {
 | 
			
		||||
			cluster.shutdown()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Returns a new runtime with a static number of threads.
 | 
			
		||||
	pub fn new_runtime() -> Runtime {
 | 
			
		||||
		RuntimeBuilder::new()
 | 
			
		||||
			.core_threads(4)
 | 
			
		||||
			.build()
 | 
			
		||||
			.expect("Unable to create tokio runtime")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	#[test]
 | 
			
		||||
	fn cluster_connects_to_other_nodes() {
 | 
			
		||||
		let mut core = Core::new().unwrap();
 | 
			
		||||
		let clusters = make_clusters(&core, 6010, 3);
 | 
			
		||||
		let mut runtime = new_runtime();
 | 
			
		||||
		let clusters = make_clusters(&runtime, 6010, 3);
 | 
			
		||||
		run_clusters(&clusters);
 | 
			
		||||
		loop_until(&mut core, TIMEOUT, || clusters.iter().all(all_connections_established));
 | 
			
		||||
		let clusters_clone = clusters.clone();
 | 
			
		||||
		loop_until(&mut runtime, TIMEOUT, move || clusters_clone.iter().all(all_connections_established));
 | 
			
		||||
		shutdown_clusters(&clusters);
 | 
			
		||||
		runtime.shutdown_now().wait().unwrap();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	#[test]
 | 
			
		||||
	fn cluster_wont_start_generation_session_if_not_fully_connected() {
 | 
			
		||||
		let core = Core::new().unwrap();
 | 
			
		||||
		let clusters = make_clusters(&core, 6013, 3);
 | 
			
		||||
		let runtime = new_runtime();
 | 
			
		||||
		let clusters = make_clusters(&runtime, 6013, 3);
 | 
			
		||||
		clusters[0].run().unwrap();
 | 
			
		||||
		match clusters[0].client().new_generation_session(SessionId::default(), Default::default(), Default::default(), 1) {
 | 
			
		||||
			Err(Error::NodeDisconnected) => (),
 | 
			
		||||
			Err(e) => panic!("unexpected error {:?}", e),
 | 
			
		||||
			_ => panic!("unexpected success"),
 | 
			
		||||
		}
 | 
			
		||||
		shutdown_clusters(&clusters);
 | 
			
		||||
		runtime.shutdown_now().wait().unwrap();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	#[test]
 | 
			
		||||
	fn error_in_generation_session_broadcasted_to_all_other_nodes() {
 | 
			
		||||
		//::logger::init_log();
 | 
			
		||||
		let mut core = Core::new().unwrap();
 | 
			
		||||
		let clusters = make_clusters(&core, 6016, 3);
 | 
			
		||||
		let mut runtime = new_runtime();
 | 
			
		||||
		let clusters = make_clusters(&runtime, 6016, 3);
 | 
			
		||||
		run_clusters(&clusters);
 | 
			
		||||
		loop_until(&mut core, TIMEOUT, || clusters.iter().all(all_connections_established));
 | 
			
		||||
		let clusters_clone = clusters.clone();
 | 
			
		||||
		loop_until(&mut runtime, TIMEOUT, move || clusters_clone.iter().all(all_connections_established));
 | 
			
		||||
 | 
			
		||||
		// ask one of nodes to produce faulty generation sessions
 | 
			
		||||
		clusters[1].client().make_faulty_generation_sessions();
 | 
			
		||||
 | 
			
		||||
		// start && wait for generation session to fail
 | 
			
		||||
		let session = clusters[0].client().new_generation_session(SessionId::default(), Default::default(), Default::default(), 1).unwrap();
 | 
			
		||||
		loop_until(&mut core, TIMEOUT, || session.joint_public_and_secret().is_some()
 | 
			
		||||
			&& clusters[0].client().generation_session(&SessionId::default()).is_none());
 | 
			
		||||
		let session_clone = session.clone();
 | 
			
		||||
		let clusters_clone = clusters.clone();
 | 
			
		||||
		loop_until(&mut runtime, TIMEOUT, move || session_clone.joint_public_and_secret().is_some()
 | 
			
		||||
			&& clusters_clone[0].client().generation_session(&SessionId::default()).is_none());
 | 
			
		||||
		assert!(session.joint_public_and_secret().unwrap().is_err());
 | 
			
		||||
 | 
			
		||||
		// check that faulty session is either removed from all nodes, or nonexistent (already removed)
 | 
			
		||||
		for i in 1..3 {
 | 
			
		||||
			if let Some(session) = clusters[i].client().generation_session(&SessionId::default()) {
 | 
			
		||||
				let session_clone = session.clone();
 | 
			
		||||
				let clusters_clone = clusters.clone();
 | 
			
		||||
				// wait for both session completion && session removal (session completion event is fired
 | 
			
		||||
				// before session is removed from its own container by cluster)
 | 
			
		||||
				loop_until(&mut core, TIMEOUT, || session.joint_public_and_secret().is_some()
 | 
			
		||||
					&& clusters[i].client().generation_session(&SessionId::default()).is_none());
 | 
			
		||||
				loop_until(&mut runtime, TIMEOUT, move || session_clone.joint_public_and_secret().is_some()
 | 
			
		||||
					&& clusters_clone[i].client().generation_session(&SessionId::default()).is_none());
 | 
			
		||||
				assert!(session.joint_public_and_secret().unwrap().is_err());
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		shutdown_clusters(&clusters);
 | 
			
		||||
		runtime.shutdown_now().wait().unwrap();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	#[test]
 | 
			
		||||
	fn generation_session_completion_signalled_if_failed_on_master() {
 | 
			
		||||
		//::logger::init_log();
 | 
			
		||||
		let mut core = Core::new().unwrap();
 | 
			
		||||
		let clusters = make_clusters(&core, 6025, 3);
 | 
			
		||||
		let mut runtime = new_runtime();
 | 
			
		||||
 | 
			
		||||
		let clusters = make_clusters(&runtime, 6025, 3);
 | 
			
		||||
		run_clusters(&clusters);
 | 
			
		||||
		loop_until(&mut core, TIMEOUT, || clusters.iter().all(all_connections_established));
 | 
			
		||||
		let clusters_clone = clusters.clone();
 | 
			
		||||
		loop_until(&mut runtime, TIMEOUT, move || clusters_clone.iter().all(all_connections_established));
 | 
			
		||||
 | 
			
		||||
		// ask one of nodes to produce faulty generation sessions
 | 
			
		||||
		clusters[0].client().make_faulty_generation_sessions();
 | 
			
		||||
 | 
			
		||||
		// start && wait for generation session to fail
 | 
			
		||||
		let session = clusters[0].client().new_generation_session(SessionId::default(), Default::default(), Default::default(), 1).unwrap();
 | 
			
		||||
		loop_until(&mut core, TIMEOUT, || session.joint_public_and_secret().is_some()
 | 
			
		||||
			&& clusters[0].client().generation_session(&SessionId::default()).is_none());
 | 
			
		||||
		let session_clone = session.clone();
 | 
			
		||||
		let clusters_clone = clusters.clone();
 | 
			
		||||
		loop_until(&mut runtime, TIMEOUT, move || session_clone.joint_public_and_secret().is_some()
 | 
			
		||||
			&& clusters_clone[0].client().generation_session(&SessionId::default()).is_none());
 | 
			
		||||
		assert!(session.joint_public_and_secret().unwrap().is_err());
 | 
			
		||||
 | 
			
		||||
		// check that faulty session is either removed from all nodes, or nonexistent (already removed)
 | 
			
		||||
		for i in 1..3 {
 | 
			
		||||
			if let Some(session) = clusters[i].client().generation_session(&SessionId::default()) {
 | 
			
		||||
				let session_clone = session.clone();
 | 
			
		||||
				let clusters_clone = clusters.clone();
 | 
			
		||||
				// wait for both session completion && session removal (session completion event is fired
 | 
			
		||||
				// before session is removed from its own container by cluster)
 | 
			
		||||
				loop_until(&mut core, TIMEOUT, || session.joint_public_and_secret().is_some()
 | 
			
		||||
					&& clusters[i].client().generation_session(&SessionId::default()).is_none());
 | 
			
		||||
				loop_until(&mut runtime, TIMEOUT, move || session_clone.joint_public_and_secret().is_some()
 | 
			
		||||
					&& clusters_clone[i].client().generation_session(&SessionId::default()).is_none());
 | 
			
		||||
				assert!(session.joint_public_and_secret().unwrap().is_err());
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		shutdown_clusters(&clusters);
 | 
			
		||||
		runtime.shutdown_now().wait().unwrap();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	#[test]
 | 
			
		||||
	fn generation_session_is_removed_when_succeeded() {
 | 
			
		||||
		//::logger::init_log();
 | 
			
		||||
		let mut core = Core::new().unwrap();
 | 
			
		||||
		let clusters = make_clusters(&core, 6019, 3);
 | 
			
		||||
		let mut runtime = new_runtime();
 | 
			
		||||
		let clusters = make_clusters(&runtime, 6019, 3);
 | 
			
		||||
		run_clusters(&clusters);
 | 
			
		||||
		loop_until(&mut core, TIMEOUT, || clusters.iter().all(all_connections_established));
 | 
			
		||||
		let clusters_clone = clusters.clone();
 | 
			
		||||
		loop_until(&mut runtime, TIMEOUT, move || clusters_clone.iter().all(all_connections_established));
 | 
			
		||||
 | 
			
		||||
		// start && wait for generation session to complete
 | 
			
		||||
		let session = clusters[0].client().new_generation_session(SessionId::default(), Default::default(), Default::default(), 1).unwrap();
 | 
			
		||||
		loop_until(&mut core, TIMEOUT, || (session.state() == GenerationSessionState::Finished
 | 
			
		||||
			|| session.state() == GenerationSessionState::Failed)
 | 
			
		||||
			&& clusters[0].client().generation_session(&SessionId::default()).is_none());
 | 
			
		||||
		let session_clone = session.clone();
 | 
			
		||||
		let clusters_clone = clusters.clone();
 | 
			
		||||
		loop_until(&mut runtime, TIMEOUT, move || (session_clone.state() == GenerationSessionState::Finished
 | 
			
		||||
			|| session_clone.state() == GenerationSessionState::Failed)
 | 
			
		||||
			&& clusters_clone[0].client().generation_session(&SessionId::default()).is_none());
 | 
			
		||||
		assert!(session.joint_public_and_secret().unwrap().is_ok());
 | 
			
		||||
 | 
			
		||||
		// check that on non-master nodes session is either:
 | 
			
		||||
@ -1392,19 +1460,24 @@ pub mod tests {
 | 
			
		||||
			if let Some(session) = clusters[i].client().generation_session(&SessionId::default()) {
 | 
			
		||||
				// run to completion if completion message is still on the way
 | 
			
		||||
				// AND check that it is actually removed from cluster sessions
 | 
			
		||||
				loop_until(&mut core, TIMEOUT, || (session.state() == GenerationSessionState::Finished
 | 
			
		||||
					|| session.state() == GenerationSessionState::Failed)
 | 
			
		||||
					&& clusters[i].client().generation_session(&SessionId::default()).is_none());
 | 
			
		||||
				let session_clone = session.clone();
 | 
			
		||||
				let clusters_clone = clusters.clone();
 | 
			
		||||
				loop_until(&mut runtime, TIMEOUT, move || (session_clone.state() == GenerationSessionState::Finished
 | 
			
		||||
					|| session_clone.state() == GenerationSessionState::Failed)
 | 
			
		||||
					&& clusters_clone[i].client().generation_session(&SessionId::default()).is_none());
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		shutdown_clusters(&clusters);
 | 
			
		||||
		runtime.shutdown_now().wait().unwrap();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	#[test]
 | 
			
		||||
	fn sessions_are_removed_when_initialization_fails() {
 | 
			
		||||
		let mut core = Core::new().unwrap();
 | 
			
		||||
		let clusters = make_clusters(&core, 6022, 3);
 | 
			
		||||
		let mut runtime = new_runtime();
 | 
			
		||||
		let clusters = make_clusters(&runtime, 6022, 3);
 | 
			
		||||
		run_clusters(&clusters);
 | 
			
		||||
		loop_until(&mut core, TIMEOUT, || clusters.iter().all(all_connections_established));
 | 
			
		||||
		let clusters_clone = clusters.clone();
 | 
			
		||||
		loop_until(&mut runtime, TIMEOUT, move || clusters_clone.iter().all(all_connections_established));
 | 
			
		||||
 | 
			
		||||
		// generation session
 | 
			
		||||
		{
 | 
			
		||||
@ -1432,6 +1505,8 @@ pub mod tests {
 | 
			
		||||
			assert!(clusters[0].data.sessions.decryption_sessions.is_empty());
 | 
			
		||||
			assert!(clusters[0].data.sessions.negotiation_sessions.is_empty());
 | 
			
		||||
		}
 | 
			
		||||
		shutdown_clusters(&clusters);
 | 
			
		||||
		runtime.shutdown_now().wait().unwrap();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// test ignored because of
 | 
			
		||||
@ -1441,16 +1516,19 @@ pub mod tests {
 | 
			
		||||
	#[ignore]
 | 
			
		||||
	fn schnorr_signing_session_completes_if_node_does_not_have_a_share() {
 | 
			
		||||
		//::logger::init_log();
 | 
			
		||||
		let mut core = Core::new().unwrap();
 | 
			
		||||
		let clusters = make_clusters(&core, 6028, 3);
 | 
			
		||||
		let mut runtime = new_runtime();
 | 
			
		||||
		let clusters = make_clusters(&runtime, 6028, 3);
 | 
			
		||||
		run_clusters(&clusters);
 | 
			
		||||
		loop_until(&mut core, TIMEOUT, || clusters.iter().all(all_connections_established));
 | 
			
		||||
		let clusters_clone = clusters.clone();
 | 
			
		||||
		loop_until(&mut runtime, TIMEOUT, move || clusters_clone.iter().all(all_connections_established));
 | 
			
		||||
 | 
			
		||||
		// start && wait for generation session to complete
 | 
			
		||||
		let session = clusters[0].client().new_generation_session(SessionId::default(), Default::default(), Default::default(), 1).unwrap();
 | 
			
		||||
		loop_until(&mut core, TIMEOUT, || (session.state() == GenerationSessionState::Finished
 | 
			
		||||
			|| session.state() == GenerationSessionState::Failed)
 | 
			
		||||
			&& clusters[0].client().generation_session(&SessionId::default()).is_none());
 | 
			
		||||
		let session_clone = session.clone();
 | 
			
		||||
		let clusters_clone = clusters.clone();
 | 
			
		||||
		loop_until(&mut runtime, TIMEOUT, move || (session_clone.state() == GenerationSessionState::Finished
 | 
			
		||||
			|| session_clone.state() == GenerationSessionState::Failed)
 | 
			
		||||
			&& clusters_clone[0].client().generation_session(&SessionId::default()).is_none());
 | 
			
		||||
		assert!(session.joint_public_and_secret().unwrap().is_ok());
 | 
			
		||||
 | 
			
		||||
		// now remove share from node2
 | 
			
		||||
@ -1462,8 +1540,10 @@ pub mod tests {
 | 
			
		||||
		let session0 = clusters[0].client().new_schnorr_signing_session(Default::default(), signature.into(), None, Default::default()).unwrap();
 | 
			
		||||
		let session = clusters[0].data.sessions.schnorr_signing_sessions.first().unwrap();
 | 
			
		||||
 | 
			
		||||
		loop_until(&mut core, TIMEOUT, || session.is_finished() && (0..3).all(|i|
 | 
			
		||||
			clusters[i].data.sessions.schnorr_signing_sessions.is_empty()));
 | 
			
		||||
		let session_clone = session.clone();
 | 
			
		||||
		let clusters_clone = clusters.clone();
 | 
			
		||||
		loop_until(&mut runtime, TIMEOUT, move || session_clone.is_finished() && (0..3).all(|i|
 | 
			
		||||
			clusters_clone[i].data.sessions.schnorr_signing_sessions.is_empty()));
 | 
			
		||||
		session0.wait().unwrap();
 | 
			
		||||
 | 
			
		||||
		// and try to sign message with generated key using node that has no key share
 | 
			
		||||
@ -1471,8 +1551,10 @@ pub mod tests {
 | 
			
		||||
		let session2 = clusters[2].client().new_schnorr_signing_session(Default::default(), signature.into(), None, Default::default()).unwrap();
 | 
			
		||||
		let session = clusters[2].data.sessions.schnorr_signing_sessions.first().unwrap();
 | 
			
		||||
 | 
			
		||||
		loop_until(&mut core, TIMEOUT, || session.is_finished()  && (0..3).all(|i|
 | 
			
		||||
			clusters[i].data.sessions.schnorr_signing_sessions.is_empty()));
 | 
			
		||||
		let session_clone = session.clone();
 | 
			
		||||
		let clusters_clone = clusters.clone();
 | 
			
		||||
		loop_until(&mut runtime, TIMEOUT, move || session_clone.is_finished()  && (0..3).all(|i|
 | 
			
		||||
			clusters_clone[i].data.sessions.schnorr_signing_sessions.is_empty()));
 | 
			
		||||
		session2.wait().unwrap();
 | 
			
		||||
 | 
			
		||||
		// now remove share from node1
 | 
			
		||||
@ -1483,8 +1565,11 @@ pub mod tests {
 | 
			
		||||
		let session1 = clusters[0].client().new_schnorr_signing_session(Default::default(), signature.into(), None, Default::default()).unwrap();
 | 
			
		||||
		let session = clusters[0].data.sessions.schnorr_signing_sessions.first().unwrap();
 | 
			
		||||
 | 
			
		||||
		loop_until(&mut core, TIMEOUT, || session.is_finished());
 | 
			
		||||
		let session = session.clone();
 | 
			
		||||
		loop_until(&mut runtime, TIMEOUT, move || session.is_finished());
 | 
			
		||||
		session1.wait().unwrap_err();
 | 
			
		||||
		shutdown_clusters(&clusters);
 | 
			
		||||
		runtime.shutdown_now().wait().unwrap();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// test ignored because of
 | 
			
		||||
@ -1494,16 +1579,19 @@ pub mod tests {
 | 
			
		||||
	#[ignore]
 | 
			
		||||
	fn ecdsa_signing_session_completes_if_node_does_not_have_a_share() {
 | 
			
		||||
		//::logger::init_log();
 | 
			
		||||
		let mut core = Core::new().unwrap();
 | 
			
		||||
		let clusters = make_clusters(&core, 6041, 4);
 | 
			
		||||
		let mut runtime = new_runtime();
 | 
			
		||||
		let clusters = make_clusters(&runtime, 6041, 4);
 | 
			
		||||
		run_clusters(&clusters);
 | 
			
		||||
		loop_until(&mut core, TIMEOUT, || clusters.iter().all(all_connections_established));
 | 
			
		||||
		let clusters_clone = clusters.clone();
 | 
			
		||||
		loop_until(&mut runtime, TIMEOUT, move || clusters_clone.iter().all(all_connections_established));
 | 
			
		||||
 | 
			
		||||
		// start && wait for generation session to complete
 | 
			
		||||
		let session = clusters[0].client().new_generation_session(SessionId::default(), Default::default(), Default::default(), 1).unwrap();
 | 
			
		||||
		loop_until(&mut core, TIMEOUT, || (session.state() == GenerationSessionState::Finished
 | 
			
		||||
			|| session.state() == GenerationSessionState::Failed)
 | 
			
		||||
			&& clusters[0].client().generation_session(&SessionId::default()).is_none());
 | 
			
		||||
		let session_clone = session.clone();
 | 
			
		||||
		let clusters_clone = clusters.clone();
 | 
			
		||||
		loop_until(&mut runtime, TIMEOUT, move || (session_clone.state() == GenerationSessionState::Finished
 | 
			
		||||
			|| session_clone.state() == GenerationSessionState::Failed)
 | 
			
		||||
			&& clusters_clone[0].client().generation_session(&SessionId::default()).is_none());
 | 
			
		||||
		assert!(session.joint_public_and_secret().unwrap().is_ok());
 | 
			
		||||
 | 
			
		||||
		// now remove share from node2
 | 
			
		||||
@ -1515,16 +1603,20 @@ pub mod tests {
 | 
			
		||||
		let session0 = clusters[0].client().new_ecdsa_signing_session(Default::default(), signature.into(), None, H256::random()).unwrap();
 | 
			
		||||
		let session = clusters[0].data.sessions.ecdsa_signing_sessions.first().unwrap();
 | 
			
		||||
 | 
			
		||||
		loop_until(&mut core, Duration::from_millis(1000), || session.is_finished() && (0..3).all(|i|
 | 
			
		||||
			clusters[i].data.sessions.ecdsa_signing_sessions.is_empty()));
 | 
			
		||||
		let session_clone = session.clone();
 | 
			
		||||
		let clusters_clone = clusters.clone();
 | 
			
		||||
		loop_until(&mut runtime, Duration::from_millis(1000), move || session_clone.is_finished() && (0..3).all(|i|
 | 
			
		||||
			clusters_clone[i].data.sessions.ecdsa_signing_sessions.is_empty()));
 | 
			
		||||
		session0.wait().unwrap();
 | 
			
		||||
 | 
			
		||||
		// and try to sign message with generated key using node that has no key share
 | 
			
		||||
		let signature = sign(Random.generate().unwrap().secret(), &Default::default()).unwrap();
 | 
			
		||||
		let session2 = clusters[2].client().new_ecdsa_signing_session(Default::default(), signature.into(), None, H256::random()).unwrap();
 | 
			
		||||
		let session = clusters[2].data.sessions.ecdsa_signing_sessions.first().unwrap();
 | 
			
		||||
		loop_until(&mut core, Duration::from_millis(1000), || session.is_finished()  && (0..3).all(|i|
 | 
			
		||||
			clusters[i].data.sessions.ecdsa_signing_sessions.is_empty()));
 | 
			
		||||
		let session_clone = session.clone();
 | 
			
		||||
		let clusters_clone = clusters.clone();
 | 
			
		||||
		loop_until(&mut runtime, Duration::from_millis(1000), move || session_clone.is_finished()  && (0..3).all(|i|
 | 
			
		||||
			clusters_clone[i].data.sessions.ecdsa_signing_sessions.is_empty()));
 | 
			
		||||
		session2.wait().unwrap();
 | 
			
		||||
 | 
			
		||||
		// now remove share from node1
 | 
			
		||||
@ -1534,7 +1626,9 @@ pub mod tests {
 | 
			
		||||
		let signature = sign(Random.generate().unwrap().secret(), &Default::default()).unwrap();
 | 
			
		||||
		let session1 = clusters[0].client().new_ecdsa_signing_session(Default::default(), signature.into(), None, H256::random()).unwrap();
 | 
			
		||||
		let session = clusters[0].data.sessions.ecdsa_signing_sessions.first().unwrap();
 | 
			
		||||
		loop_until(&mut core, Duration::from_millis(1000), || session.is_finished());
 | 
			
		||||
		loop_until(&mut runtime, Duration::from_millis(1000), move || session.is_finished());
 | 
			
		||||
		session1.wait().unwrap_err();
 | 
			
		||||
		shutdown_clusters(&clusters);
 | 
			
		||||
		runtime.shutdown_now().wait().unwrap();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -16,18 +16,34 @@
 | 
			
		||||
 | 
			
		||||
use std::io;
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
use futures::{Future, Select, Poll, Async};
 | 
			
		||||
use tokio_core::reactor::{Handle, Timeout};
 | 
			
		||||
use futures::{Future, Poll};
 | 
			
		||||
use tokio::timer::timeout::{Timeout, Error as TimeoutError};
 | 
			
		||||
 | 
			
		||||
type DeadlineBox<F> = Box<Future<Item = DeadlineStatus<<F as Future>::Item>, Error = <F as Future>::Error> + Send>;
 | 
			
		||||
type DeadlineBox<F> = Box<Future<
 | 
			
		||||
	Item = DeadlineStatus<<F as Future>::Item>,
 | 
			
		||||
	Error = TimeoutError<<F as Future>::Error>
 | 
			
		||||
> + Send>;
 | 
			
		||||
 | 
			
		||||
/// Complete a passed future or fail if it is not completed within timeout.
 | 
			
		||||
pub fn deadline<F, T>(duration: Duration, handle: &Handle, future: F) -> Result<Deadline<F>, io::Error>
 | 
			
		||||
	where F: Future<Item = T, Error = io::Error> + Send + 'static, T: 'static {
 | 
			
		||||
	let timeout: DeadlineBox<F> = Box::new(Timeout::new(duration, handle)?.map(|_| DeadlineStatus::Timeout));
 | 
			
		||||
	let future: DeadlineBox<F> = Box::new(future.map(DeadlineStatus::Meet));
 | 
			
		||||
pub fn deadline<F, T>(duration: Duration, future: F) -> Result<Deadline<F>, io::Error>
 | 
			
		||||
	where F: Future<Item = T, Error = io::Error> + Send + 'static, T: Send + 'static
 | 
			
		||||
{
 | 
			
		||||
	let timeout = Box::new(Timeout::new(future, duration)
 | 
			
		||||
		.then(|res| {
 | 
			
		||||
			match res {
 | 
			
		||||
				Ok(fut) => Ok(DeadlineStatus::Meet(fut)),
 | 
			
		||||
				Err(err) => {
 | 
			
		||||
					if err.is_elapsed() {
 | 
			
		||||
						Ok(DeadlineStatus::Timeout)
 | 
			
		||||
					} else {
 | 
			
		||||
						Err(err)
 | 
			
		||||
					}
 | 
			
		||||
				},
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	);
 | 
			
		||||
	let deadline = Deadline {
 | 
			
		||||
		future: timeout.select(future),
 | 
			
		||||
		future: timeout,
 | 
			
		||||
	};
 | 
			
		||||
	Ok(deadline)
 | 
			
		||||
}
 | 
			
		||||
@ -43,19 +59,15 @@ pub enum DeadlineStatus<T> {
 | 
			
		||||
 | 
			
		||||
/// Future, which waits for passed future completion within given period, or fails with timeout.
 | 
			
		||||
pub struct Deadline<F> where F: Future {
 | 
			
		||||
	future: Select<DeadlineBox<F>, DeadlineBox<F>>,
 | 
			
		||||
	future: DeadlineBox<F>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<F, T> Future for Deadline<F> where F: Future<Item = T, Error = io::Error> {
 | 
			
		||||
	type Item = DeadlineStatus<T>;
 | 
			
		||||
	type Error = io::Error;
 | 
			
		||||
	type Error = TimeoutError<io::Error>;
 | 
			
		||||
 | 
			
		||||
	fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
 | 
			
		||||
		match self.future.poll() {
 | 
			
		||||
			Ok(Async::Ready((result, _other))) => Ok(Async::Ready(result)),
 | 
			
		||||
			Ok(Async::NotReady) => Ok(Async::NotReady),
 | 
			
		||||
			Err((err, _other)) => Err(err),
 | 
			
		||||
		}
 | 
			
		||||
		self.future.poll()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -63,14 +75,14 @@ impl<F, T> Future for Deadline<F> where F: Future<Item = T, Error = io::Error> {
 | 
			
		||||
mod tests {
 | 
			
		||||
	use std::time::Duration;
 | 
			
		||||
	use futures::{Future, done};
 | 
			
		||||
	use tokio_core::reactor::Core;
 | 
			
		||||
	use tokio::reactor::Reactor;
 | 
			
		||||
	use super::{deadline, DeadlineStatus};
 | 
			
		||||
 | 
			
		||||
	#[test]
 | 
			
		||||
	fn deadline_result_works() {
 | 
			
		||||
		let mut core = Core::new().unwrap();
 | 
			
		||||
		let deadline = deadline(Duration::from_millis(1000), &core.handle(), done(Ok(()))).unwrap();
 | 
			
		||||
		core.turn(Some(Duration::from_millis(3)));
 | 
			
		||||
		let mut reactor = Reactor::new().unwrap();
 | 
			
		||||
		let deadline = deadline(Duration::from_millis(1000), done(Ok(()))).unwrap();
 | 
			
		||||
		reactor.turn(Some(Duration::from_millis(3))).unwrap();
 | 
			
		||||
		assert_eq!(deadline.wait().unwrap(), DeadlineStatus::Meet(()));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -19,7 +19,7 @@ use std::net::Shutdown;
 | 
			
		||||
use std::io::{Read, Write, Error};
 | 
			
		||||
use futures::Poll;
 | 
			
		||||
use tokio_io::{AsyncRead, AsyncWrite};
 | 
			
		||||
use tokio_core::net::TcpStream;
 | 
			
		||||
use tokio::net::TcpStream;
 | 
			
		||||
 | 
			
		||||
/// Read+Write implementation for Arc<TcpStream>.
 | 
			
		||||
pub struct SharedTcpStream {
 | 
			
		||||
 | 
			
		||||
@ -19,20 +19,23 @@ use std::sync::Arc;
 | 
			
		||||
use std::net::SocketAddr;
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
use futures::{Future, Poll};
 | 
			
		||||
use tokio_core::reactor::Handle;
 | 
			
		||||
use tokio_core::net::TcpStream;
 | 
			
		||||
use tokio::net::TcpStream;
 | 
			
		||||
use key_server_cluster::{Error, NodeKeyPair};
 | 
			
		||||
use key_server_cluster::io::{accept_handshake, Handshake, Deadline, deadline};
 | 
			
		||||
use key_server_cluster::net::Connection;
 | 
			
		||||
 | 
			
		||||
/// Create future for accepting incoming connection.
 | 
			
		||||
pub fn accept_connection(address: SocketAddr, stream: TcpStream, handle: &Handle, self_key_pair: Arc<NodeKeyPair>) -> Deadline<AcceptConnection> {
 | 
			
		||||
pub fn accept_connection(stream: TcpStream, self_key_pair: Arc<NodeKeyPair>) -> Deadline<AcceptConnection> {
 | 
			
		||||
	// TODO: This could fail so it would be better either to accept the
 | 
			
		||||
	// address as a separate argument or return a result.
 | 
			
		||||
	let address = stream.peer_addr().expect("Unable to determine tcp peer address");
 | 
			
		||||
 | 
			
		||||
	let accept = AcceptConnection {
 | 
			
		||||
		handshake: accept_handshake(stream, self_key_pair),
 | 
			
		||||
		address: address,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	deadline(Duration::new(5, 0), handle, accept).expect("Failed to create timeout")
 | 
			
		||||
	deadline(Duration::new(5, 0), accept).expect("Failed to create timeout")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Future for accepting incoming connection.
 | 
			
		||||
 | 
			
		||||
@ -20,26 +20,25 @@ use std::io;
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
use std::net::SocketAddr;
 | 
			
		||||
use futures::{Future, Poll, Async};
 | 
			
		||||
use tokio_core::reactor::Handle;
 | 
			
		||||
use tokio_core::net::{TcpStream, TcpStreamNew};
 | 
			
		||||
use tokio::net::{TcpStream, tcp::ConnectFuture};
 | 
			
		||||
use key_server_cluster::{Error, NodeId, NodeKeyPair};
 | 
			
		||||
use key_server_cluster::io::{handshake, Handshake, Deadline, deadline};
 | 
			
		||||
use key_server_cluster::net::Connection;
 | 
			
		||||
 | 
			
		||||
/// Create future for connecting to other node.
 | 
			
		||||
pub fn connect(address: &SocketAddr, handle: &Handle, self_key_pair: Arc<NodeKeyPair>, trusted_nodes: BTreeSet<NodeId>) -> Deadline<Connect> {
 | 
			
		||||
pub fn connect(address: &SocketAddr, self_key_pair: Arc<NodeKeyPair>, trusted_nodes: BTreeSet<NodeId>) -> Deadline<Connect> {
 | 
			
		||||
	let connect = Connect {
 | 
			
		||||
		state: ConnectState::TcpConnect(TcpStream::connect(address, handle)),
 | 
			
		||||
		state: ConnectState::TcpConnect(TcpStream::connect(address)),
 | 
			
		||||
		address: address.clone(),
 | 
			
		||||
		self_key_pair: self_key_pair,
 | 
			
		||||
		trusted_nodes: trusted_nodes,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	deadline(Duration::new(5, 0), handle, connect).expect("Failed to create timeout")
 | 
			
		||||
	deadline(Duration::new(5, 0), connect).expect("Failed to create timeout")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum ConnectState {
 | 
			
		||||
	TcpConnect(TcpStreamNew),
 | 
			
		||||
	TcpConnect(ConnectFuture),
 | 
			
		||||
	Handshake(Handshake<TcpStream>),
 | 
			
		||||
	Connected,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -24,7 +24,6 @@ extern crate ethcore_sync as sync;
 | 
			
		||||
extern crate ethcore_transaction as transaction;
 | 
			
		||||
extern crate ethereum_types;
 | 
			
		||||
extern crate ethkey;
 | 
			
		||||
extern crate futures_cpupool;
 | 
			
		||||
extern crate hyper;
 | 
			
		||||
extern crate keccak_hash as hash;
 | 
			
		||||
extern crate kvdb;
 | 
			
		||||
@ -34,9 +33,7 @@ extern crate serde;
 | 
			
		||||
extern crate serde_json;
 | 
			
		||||
extern crate tiny_keccak;
 | 
			
		||||
extern crate tokio;
 | 
			
		||||
extern crate tokio_core;
 | 
			
		||||
extern crate tokio_io;
 | 
			
		||||
extern crate tokio_proto;
 | 
			
		||||
extern crate tokio_service;
 | 
			
		||||
extern crate url;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -16,14 +16,17 @@
 | 
			
		||||
 | 
			
		||||
use std::collections::BTreeSet;
 | 
			
		||||
use std::sync::{Arc, Weak};
 | 
			
		||||
use hyper::{self, header, Chunk, Uri, Request as HttpRequest, Response as HttpResponse, Method as HttpMethod, StatusCode as HttpStatusCode};
 | 
			
		||||
use hyper::server::Http;
 | 
			
		||||
use hyper::{self, Uri, Request as HttpRequest, Response as HttpResponse, Method as HttpMethod,
 | 
			
		||||
	StatusCode as HttpStatusCode, Body,
 | 
			
		||||
	header::{self, HeaderValue},
 | 
			
		||||
	server::conn::Http,
 | 
			
		||||
	service::Service,
 | 
			
		||||
};
 | 
			
		||||
use serde::Serialize;
 | 
			
		||||
use serde_json;
 | 
			
		||||
use tokio;
 | 
			
		||||
use tokio::net::TcpListener;
 | 
			
		||||
use tokio::runtime::Runtime;
 | 
			
		||||
use tokio_service::Service;
 | 
			
		||||
use tokio::runtime::{Runtime, Builder as RuntimeBuilder};
 | 
			
		||||
use futures::{future, Future, Stream};
 | 
			
		||||
use url::percent_encoding::percent_decode;
 | 
			
		||||
 | 
			
		||||
@ -88,7 +91,10 @@ impl KeyServerHttpListener {
 | 
			
		||||
			key_server: key_server,
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		let mut runtime = Runtime::new()?;
 | 
			
		||||
		let mut runtime = RuntimeBuilder::new()
 | 
			
		||||
			// TODO: Add config option/arg?
 | 
			
		||||
			.core_threads(2)
 | 
			
		||||
			.build()?;
 | 
			
		||||
		let listener_address = format!("{}:{}", listener_address.address, listener_address.port).parse()?;
 | 
			
		||||
		let listener = TcpListener::bind(&listener_address)?;
 | 
			
		||||
 | 
			
		||||
@ -97,10 +103,10 @@ impl KeyServerHttpListener {
 | 
			
		||||
		let server = listener.incoming()
 | 
			
		||||
			.map_err(|e| warn!("Key server listener error: {:?}", e))
 | 
			
		||||
			.for_each(move |socket| {
 | 
			
		||||
				let http: Http<Chunk> = Http::new();
 | 
			
		||||
				let serve = http.serve_connection(socket, KeyServerHttpHandler {
 | 
			
		||||
					handler: shared_handler2.clone(),
 | 
			
		||||
				}).map(|_| ()).map_err(|e| {
 | 
			
		||||
				let http = Http::new();
 | 
			
		||||
				let serve = http.serve_connection(socket,
 | 
			
		||||
					KeyServerHttpHandler { handler: shared_handler2.clone() }
 | 
			
		||||
				).map(|_| ()).map_err(|e| {
 | 
			
		||||
					warn!("Key server handler error: {:?}", e);
 | 
			
		||||
				});
 | 
			
		||||
 | 
			
		||||
@ -119,7 +125,7 @@ impl KeyServerHttpListener {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl KeyServerHttpHandler {
 | 
			
		||||
	fn process(self, req_method: HttpMethod, req_uri: Uri, path: &str, req_body: &[u8]) -> HttpResponse {
 | 
			
		||||
	fn process(self, req_method: HttpMethod, req_uri: Uri, path: &str, req_body: &[u8]) -> HttpResponse<Body> {
 | 
			
		||||
		match parse_request(&req_method, &path, &req_body) {
 | 
			
		||||
			Request::GenerateServerKey(document, signature, threshold) => {
 | 
			
		||||
				return_server_public_key(&req_uri, self.handler.key_server.upgrade()
 | 
			
		||||
@ -195,22 +201,28 @@ impl KeyServerHttpHandler {
 | 
			
		||||
				},
 | 
			
		||||
			Request::Invalid => {
 | 
			
		||||
				warn!(target: "secretstore", "Ignoring invalid {}-request {}", req_method, req_uri);
 | 
			
		||||
				HttpResponse::new().with_status(HttpStatusCode::BadRequest)
 | 
			
		||||
				HttpResponse::builder()
 | 
			
		||||
					.status(HttpStatusCode::BAD_REQUEST)
 | 
			
		||||
					.body(Body::empty())
 | 
			
		||||
					.expect("Nothing to parse, cannot fail; qed")
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Service for KeyServerHttpHandler {
 | 
			
		||||
	type Request = HttpRequest;
 | 
			
		||||
	type Response = HttpResponse;
 | 
			
		||||
	type ReqBody = Body;
 | 
			
		||||
	type ResBody = Body;
 | 
			
		||||
	type Error = hyper::Error;
 | 
			
		||||
	type Future = Box<Future<Item=Self::Response, Error=Self::Error> + Send>;
 | 
			
		||||
	type Future = Box<Future<Item = HttpResponse<Self::ResBody>, Error=Self::Error> + Send>;
 | 
			
		||||
 | 
			
		||||
	fn call(&self, req: HttpRequest) -> Self::Future {
 | 
			
		||||
		if req.headers().has::<header::Origin>() {
 | 
			
		||||
	fn call(&mut self, req: HttpRequest<Body>) -> Self::Future {
 | 
			
		||||
		if req.headers().contains_key(header::ORIGIN) {
 | 
			
		||||
			warn!(target: "secretstore", "Ignoring {}-request {} with Origin header", req.method(), req.uri());
 | 
			
		||||
			return Box::new(future::ok(HttpResponse::new().with_status(HttpStatusCode::NotFound)));
 | 
			
		||||
			return Box::new(future::ok(HttpResponse::builder()
 | 
			
		||||
					.status(HttpStatusCode::NOT_FOUND)
 | 
			
		||||
					.body(Body::empty())
 | 
			
		||||
					.expect("Nothing to parse, cannot fail; qed")))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		let req_method = req.method().clone();
 | 
			
		||||
@ -218,35 +230,40 @@ impl Service for KeyServerHttpHandler {
 | 
			
		||||
		// We cannot consume Self because of the Service trait requirement.
 | 
			
		||||
		let this = self.clone();
 | 
			
		||||
 | 
			
		||||
		Box::new(req.body().concat2().map(move |body| {
 | 
			
		||||
		Box::new(req.into_body().concat2().map(move |body| {
 | 
			
		||||
			let path = req_uri.path().to_string();
 | 
			
		||||
			if path.starts_with("/") {
 | 
			
		||||
				this.process(req_method, req_uri, &path, &body)
 | 
			
		||||
			} else {
 | 
			
		||||
				warn!(target: "secretstore", "Ignoring invalid {}-request {}", req_method, req_uri);
 | 
			
		||||
				HttpResponse::new().with_status(HttpStatusCode::NotFound)
 | 
			
		||||
				HttpResponse::builder()
 | 
			
		||||
					.status(HttpStatusCode::NOT_FOUND)
 | 
			
		||||
					.body(Body::empty())
 | 
			
		||||
					.expect("Nothing to parse, cannot fail; qed")
 | 
			
		||||
			}
 | 
			
		||||
		}))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn return_empty(req_uri: &Uri, empty: Result<(), Error>) -> HttpResponse {
 | 
			
		||||
fn return_empty(req_uri: &Uri, empty: Result<(), Error>) -> HttpResponse<Body> {
 | 
			
		||||
	return_bytes::<i32>(req_uri, empty.map(|_| None))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn return_server_public_key(req_uri: &Uri, server_public: Result<Public, Error>) -> HttpResponse {
 | 
			
		||||
fn return_server_public_key(req_uri: &Uri, server_public: Result<Public, Error>) -> HttpResponse<Body> {
 | 
			
		||||
	return_bytes(req_uri, server_public.map(|k| Some(SerializablePublic(k))))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn return_message_signature(req_uri: &Uri, signature: Result<EncryptedDocumentKey, Error>) -> HttpResponse {
 | 
			
		||||
fn return_message_signature(req_uri: &Uri, signature: Result<EncryptedDocumentKey, Error>) -> HttpResponse<Body> {
 | 
			
		||||
	return_bytes(req_uri, signature.map(|s| Some(SerializableBytes(s))))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn return_document_key(req_uri: &Uri, document_key: Result<EncryptedDocumentKey, Error>) -> HttpResponse {
 | 
			
		||||
fn return_document_key(req_uri: &Uri, document_key: Result<EncryptedDocumentKey, Error>) -> HttpResponse<Body> {
 | 
			
		||||
	return_bytes(req_uri, document_key.map(|k| Some(SerializableBytes(k))))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn return_document_key_shadow(req_uri: &Uri, document_key_shadow: Result<EncryptedDocumentKeyShadow, Error>) -> HttpResponse {
 | 
			
		||||
fn return_document_key_shadow(req_uri: &Uri, document_key_shadow: Result<EncryptedDocumentKeyShadow, Error>)
 | 
			
		||||
	-> HttpResponse<Body>
 | 
			
		||||
{
 | 
			
		||||
	return_bytes(req_uri, document_key_shadow.map(|k| Some(SerializableEncryptedDocumentKeyShadow {
 | 
			
		||||
		decrypted_secret: k.decrypted_secret.into(),
 | 
			
		||||
		common_point: k.common_point.expect("always filled when requesting document_key_shadow; qed").into(),
 | 
			
		||||
@ -254,42 +271,65 @@ fn return_document_key_shadow(req_uri: &Uri, document_key_shadow: Result<Encrypt
 | 
			
		||||
	})))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn return_bytes<T: Serialize>(req_uri: &Uri, result: Result<Option<T>, Error>) -> HttpResponse {
 | 
			
		||||
fn return_bytes<T: Serialize>(req_uri: &Uri, result: Result<Option<T>, Error>) -> HttpResponse<Body> {
 | 
			
		||||
	match result {
 | 
			
		||||
		Ok(Some(result)) => match serde_json::to_vec(&result) {
 | 
			
		||||
			Ok(result) => HttpResponse::new()
 | 
			
		||||
				.with_header(header::ContentType::json())
 | 
			
		||||
				.with_body(result),
 | 
			
		||||
			Ok(result) => {
 | 
			
		||||
				let body: Body = result.into();
 | 
			
		||||
				HttpResponse::builder()
 | 
			
		||||
					.header(header::CONTENT_TYPE, HeaderValue::from_static("application/json; charset=utf-8"))
 | 
			
		||||
					.body(body)
 | 
			
		||||
					.expect("Error creating http response")
 | 
			
		||||
			},
 | 
			
		||||
			Err(err) => {
 | 
			
		||||
				warn!(target: "secretstore", "response to request {} has failed with: {}", req_uri, err);
 | 
			
		||||
				HttpResponse::new().with_status(HttpStatusCode::InternalServerError)
 | 
			
		||||
				HttpResponse::builder()
 | 
			
		||||
					.status(HttpStatusCode::INTERNAL_SERVER_ERROR)
 | 
			
		||||
					.body(Body::empty())
 | 
			
		||||
					.expect("Nothing to parse, cannot fail; qed")
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		Ok(None) => HttpResponse::new().with_status(HttpStatusCode::Ok),
 | 
			
		||||
		Ok(None) => {
 | 
			
		||||
			HttpResponse::builder()
 | 
			
		||||
					.status(HttpStatusCode::OK)
 | 
			
		||||
					.body(Body::empty())
 | 
			
		||||
					.expect("Nothing to parse, cannot fail; qed")
 | 
			
		||||
		},
 | 
			
		||||
		Err(err) => return_error(err),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn return_error(err: Error) -> HttpResponse {
 | 
			
		||||
	let mut res = HttpResponse::new().with_status(match err {
 | 
			
		||||
		Error::AccessDenied | Error::ConsensusUnreachable | Error::ConsensusTemporaryUnreachable =>
 | 
			
		||||
			HttpStatusCode::Forbidden,
 | 
			
		||||
		Error::ServerKeyIsNotFound | Error::DocumentKeyIsNotFound =>
 | 
			
		||||
			HttpStatusCode::NotFound,
 | 
			
		||||
		Error::InsufficientRequesterData(_) | Error::Hyper(_) | Error::Serde(_)
 | 
			
		||||
			| Error::DocumentKeyAlreadyStored | Error::ServerKeyAlreadyGenerated =>
 | 
			
		||||
			HttpStatusCode::BadRequest,
 | 
			
		||||
		_ => HttpStatusCode::InternalServerError,
 | 
			
		||||
	});
 | 
			
		||||
fn return_error(err: Error) -> HttpResponse<Body> {
 | 
			
		||||
	let status = match err {
 | 
			
		||||
		| Error::AccessDenied
 | 
			
		||||
		| Error::ConsensusUnreachable
 | 
			
		||||
		| Error::ConsensusTemporaryUnreachable =>
 | 
			
		||||
			HttpStatusCode::FORBIDDEN,
 | 
			
		||||
		| Error::ServerKeyIsNotFound
 | 
			
		||||
		| Error::DocumentKeyIsNotFound =>
 | 
			
		||||
			HttpStatusCode::NOT_FOUND,
 | 
			
		||||
		| Error::InsufficientRequesterData(_)
 | 
			
		||||
		| Error::Hyper(_)
 | 
			
		||||
		| Error::Serde(_)
 | 
			
		||||
		| Error::DocumentKeyAlreadyStored
 | 
			
		||||
		| Error::ServerKeyAlreadyGenerated =>
 | 
			
		||||
			HttpStatusCode::BAD_REQUEST,
 | 
			
		||||
		_ => HttpStatusCode::INTERNAL_SERVER_ERROR,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	let mut res = HttpResponse::builder();
 | 
			
		||||
	res.status(status);
 | 
			
		||||
 | 
			
		||||
	// return error text. ignore errors when returning error
 | 
			
		||||
	let error_text = format!("\"{}\"", err);
 | 
			
		||||
	if let Ok(error_text) = serde_json::to_vec(&error_text) {
 | 
			
		||||
		res.headers_mut().set(header::ContentType::json());
 | 
			
		||||
		res.set_body(error_text);
 | 
			
		||||
		res.header(header::CONTENT_TYPE, HeaderValue::from_static("application/json; charset=utf-8"));
 | 
			
		||||
		res.body(error_text.into())
 | 
			
		||||
			.expect("`error_text` is a formatted string, parsing cannot fail; qed")
 | 
			
		||||
	} else {
 | 
			
		||||
		res.body(Body::empty())
 | 
			
		||||
			.expect("Nothing to parse, cannot fail; qed")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn parse_request(method: &HttpMethod, uri_path: &str, body: &[u8]) -> Request {
 | 
			
		||||
@ -328,19 +368,19 @@ fn parse_request(method: &HttpMethod, uri_path: &str, body: &[u8]) -> Request {
 | 
			
		||||
	let common_point = path.get(args_offset + 2).map(|v| v.parse());
 | 
			
		||||
	let encrypted_key = path.get(args_offset + 3).map(|v| v.parse());
 | 
			
		||||
	match (prefix, args_count, method, threshold, message_hash, common_point, encrypted_key) {
 | 
			
		||||
		("shadow", 3, &HttpMethod::Post, Some(Ok(threshold)), _, _, _) =>
 | 
			
		||||
		("shadow", 3, &HttpMethod::POST, Some(Ok(threshold)), _, _, _) =>
 | 
			
		||||
			Request::GenerateServerKey(document, signature, threshold),
 | 
			
		||||
		("shadow", 4, &HttpMethod::Post, _, _, Some(Ok(common_point)), Some(Ok(encrypted_key))) =>
 | 
			
		||||
		("shadow", 4, &HttpMethod::POST, _, _, Some(Ok(common_point)), Some(Ok(encrypted_key))) =>
 | 
			
		||||
			Request::StoreDocumentKey(document, signature, common_point, encrypted_key),
 | 
			
		||||
		("", 3, &HttpMethod::Post, Some(Ok(threshold)), _, _, _) =>
 | 
			
		||||
		("", 3, &HttpMethod::POST, Some(Ok(threshold)), _, _, _) =>
 | 
			
		||||
			Request::GenerateDocumentKey(document, signature, threshold),
 | 
			
		||||
		("", 2, &HttpMethod::Get, _, _, _, _) =>
 | 
			
		||||
		("", 2, &HttpMethod::GET, _, _, _, _) =>
 | 
			
		||||
			Request::GetDocumentKey(document, signature),
 | 
			
		||||
		("shadow", 2, &HttpMethod::Get, _, _, _, _) =>
 | 
			
		||||
		("shadow", 2, &HttpMethod::GET, _, _, _, _) =>
 | 
			
		||||
			Request::GetDocumentKeyShadow(document, signature),
 | 
			
		||||
		("schnorr", 3, &HttpMethod::Get, _, Some(Ok(message_hash)), _, _) =>
 | 
			
		||||
		("schnorr", 3, &HttpMethod::GET, _, Some(Ok(message_hash)), _, _) =>
 | 
			
		||||
			Request::SchnorrSignMessage(document, signature, message_hash),
 | 
			
		||||
		("ecdsa", 3, &HttpMethod::Get, _, Some(Ok(message_hash)), _, _) =>
 | 
			
		||||
		("ecdsa", 3, &HttpMethod::GET, _, Some(Ok(message_hash)), _, _) =>
 | 
			
		||||
			Request::EcdsaSignMessage(document, signature, message_hash),
 | 
			
		||||
		_ => Request::Invalid,
 | 
			
		||||
	}
 | 
			
		||||
@ -348,7 +388,7 @@ fn parse_request(method: &HttpMethod, uri_path: &str, body: &[u8]) -> Request {
 | 
			
		||||
 | 
			
		||||
fn parse_admin_request(method: &HttpMethod, path: Vec<String>, body: &[u8]) -> Request {
 | 
			
		||||
	let args_count = path.len();
 | 
			
		||||
	if *method != HttpMethod::Post || args_count != 4 || path[1] != "servers_set_change" {
 | 
			
		||||
	if *method != HttpMethod::POST || args_count != 4 || path[1] != "servers_set_change" {
 | 
			
		||||
		return Request::Invalid;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -392,39 +432,39 @@ mod tests {
 | 
			
		||||
	#[test]
 | 
			
		||||
	fn parse_request_successful() {
 | 
			
		||||
		// POST		/shadow/{server_key_id}/{signature}/{threshold}						=> generate server key
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::Post, "/shadow/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/2", Default::default()),
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::POST, "/shadow/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/2", Default::default()),
 | 
			
		||||
			Request::GenerateServerKey("0000000000000000000000000000000000000000000000000000000000000001".into(),
 | 
			
		||||
				"a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(),
 | 
			
		||||
				2));
 | 
			
		||||
		// POST		/shadow/{server_key_id}/{signature}/{common_point}/{encrypted_key}	=> store encrypted document key
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::Post, "/shadow/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8/1395568277679f7f583ab7c0992da35f26cde57149ee70e524e49bdae62db3e18eb96122501e7cbb798b784395d7bb5a499edead0706638ad056d886e56cf8fb", Default::default()),
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::POST, "/shadow/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8/1395568277679f7f583ab7c0992da35f26cde57149ee70e524e49bdae62db3e18eb96122501e7cbb798b784395d7bb5a499edead0706638ad056d886e56cf8fb", Default::default()),
 | 
			
		||||
			Request::StoreDocumentKey("0000000000000000000000000000000000000000000000000000000000000001".into(),
 | 
			
		||||
				"a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(),
 | 
			
		||||
				"b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8".parse().unwrap(),
 | 
			
		||||
				"1395568277679f7f583ab7c0992da35f26cde57149ee70e524e49bdae62db3e18eb96122501e7cbb798b784395d7bb5a499edead0706638ad056d886e56cf8fb".parse().unwrap()));
 | 
			
		||||
		// POST		/{server_key_id}/{signature}/{threshold}							=> generate server && document key
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::Post, "/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/2", Default::default()),
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::POST, "/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/2", Default::default()),
 | 
			
		||||
			Request::GenerateDocumentKey("0000000000000000000000000000000000000000000000000000000000000001".into(),
 | 
			
		||||
				"a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(),
 | 
			
		||||
				2));
 | 
			
		||||
		// GET		/{server_key_id}/{signature}										=> get document key
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::Get, "/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", Default::default()),
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::GET, "/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", Default::default()),
 | 
			
		||||
			Request::GetDocumentKey("0000000000000000000000000000000000000000000000000000000000000001".into(),
 | 
			
		||||
				"a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap()));
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::Get, "/%30000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", Default::default()),
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::GET, "/%30000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", Default::default()),
 | 
			
		||||
			Request::GetDocumentKey("0000000000000000000000000000000000000000000000000000000000000001".into(),
 | 
			
		||||
				"a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap()));
 | 
			
		||||
		// GET		/shadow/{server_key_id}/{signature}									=> get document key shadow
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::Get, "/shadow/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", Default::default()),
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::GET, "/shadow/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", Default::default()),
 | 
			
		||||
			Request::GetDocumentKeyShadow("0000000000000000000000000000000000000000000000000000000000000001".into(),
 | 
			
		||||
				"a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap()));
 | 
			
		||||
		// GET		/schnorr/{server_key_id}/{signature}/{message_hash}					=> schnorr-sign message with server key
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::Get, "/schnorr/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c", Default::default()),
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::GET, "/schnorr/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c", Default::default()),
 | 
			
		||||
			Request::SchnorrSignMessage("0000000000000000000000000000000000000000000000000000000000000001".into(),
 | 
			
		||||
				"a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(),
 | 
			
		||||
				"281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c".parse().unwrap()));
 | 
			
		||||
		// GET		/ecdsa/{server_key_id}/{signature}/{message_hash}					=> ecdsa-sign message with server key
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::Get, "/ecdsa/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c", Default::default()),
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::GET, "/ecdsa/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c", Default::default()),
 | 
			
		||||
			Request::EcdsaSignMessage("0000000000000000000000000000000000000000000000000000000000000001".into(),
 | 
			
		||||
				"a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(),
 | 
			
		||||
				"281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c".parse().unwrap()));
 | 
			
		||||
@ -432,7 +472,7 @@ mod tests {
 | 
			
		||||
		let node1: Public = "843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91".parse().unwrap();
 | 
			
		||||
		let node2: Public = "07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3".parse().unwrap();
 | 
			
		||||
		let nodes = vec![node1, node2].into_iter().collect();
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::Post, "/admin/servers_set_change/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/b199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01",
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::POST, "/admin/servers_set_change/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/b199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01",
 | 
			
		||||
			&r#"["0x843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91",
 | 
			
		||||
				"0x07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3"]"#.as_bytes()),
 | 
			
		||||
			Request::ChangeServersSet(
 | 
			
		||||
@ -444,20 +484,20 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
	#[test]
 | 
			
		||||
	fn parse_request_failed() {
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::Get, "", Default::default()), Request::Invalid);
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::Get, "/shadow", Default::default()), Request::Invalid);
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::Get, "///2", Default::default()), Request::Invalid);
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::Get, "/shadow///2", Default::default()), Request::Invalid);
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::Get, "/0000000000000000000000000000000000000000000000000000000000000001", Default::default()), Request::Invalid);
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::Get, "/0000000000000000000000000000000000000000000000000000000000000001/", Default::default()), Request::Invalid);
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::Get, "/a/b", Default::default()), Request::Invalid);
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::Get, "/schnorr/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/0000000000000000000000000000000000000000000000000000000000000002/0000000000000000000000000000000000000000000000000000000000000002", Default::default()), Request::Invalid);
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::Get, "/ecdsa/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/0000000000000000000000000000000000000000000000000000000000000002/0000000000000000000000000000000000000000000000000000000000000002", Default::default()), Request::Invalid);
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::Post, "/admin/servers_set_change/xxx/yyy",
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::GET, "", Default::default()), Request::Invalid);
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::GET, "/shadow", Default::default()), Request::Invalid);
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::GET, "///2", Default::default()), Request::Invalid);
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::GET, "/shadow///2", Default::default()), Request::Invalid);
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::GET, "/0000000000000000000000000000000000000000000000000000000000000001", Default::default()), Request::Invalid);
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::GET, "/0000000000000000000000000000000000000000000000000000000000000001/", Default::default()), Request::Invalid);
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::GET, "/a/b", Default::default()), Request::Invalid);
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::GET, "/schnorr/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/0000000000000000000000000000000000000000000000000000000000000002/0000000000000000000000000000000000000000000000000000000000000002", Default::default()), Request::Invalid);
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::GET, "/ecdsa/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/0000000000000000000000000000000000000000000000000000000000000002/0000000000000000000000000000000000000000000000000000000000000002", Default::default()), Request::Invalid);
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::POST, "/admin/servers_set_change/xxx/yyy",
 | 
			
		||||
			&r#"["0x843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91",
 | 
			
		||||
				"0x07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3"]"#.as_bytes()),
 | 
			
		||||
			Request::Invalid);
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::Post, "/admin/servers_set_change/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", "".as_bytes()),
 | 
			
		||||
		assert_eq!(parse_request(&HttpMethod::POST, "/admin/servers_set_change/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", "".as_bytes()),
 | 
			
		||||
			Request::Invalid);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -8,4 +8,4 @@ authors = ["Parity Technologies <admin@parity.io>"]
 | 
			
		||||
[dependencies]
 | 
			
		||||
fetch = { path = "../fetch" }
 | 
			
		||||
futures = "0.1"
 | 
			
		||||
hyper = "0.11"
 | 
			
		||||
hyper = "0.12"
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,7 @@ extern crate fetch;
 | 
			
		||||
extern crate hyper;
 | 
			
		||||
extern crate futures;
 | 
			
		||||
 | 
			
		||||
use hyper::StatusCode;
 | 
			
		||||
use hyper::{StatusCode, Body};
 | 
			
		||||
use futures::{future, future::FutureResult};
 | 
			
		||||
use fetch::{Fetch, Url, Request};
 | 
			
		||||
 | 
			
		||||
@ -39,10 +39,13 @@ impl<T: 'static> Fetch for FakeFetch<T> where T: Clone + Send+ Sync {
 | 
			
		||||
	fn fetch(&self, request: Request, abort: fetch::Abort) -> Self::Result {
 | 
			
		||||
		let u = request.url().clone();
 | 
			
		||||
		future::ok(if self.val.is_some() {
 | 
			
		||||
			let r = hyper::Response::new().with_body(&b"Some content"[..]);
 | 
			
		||||
			let r = hyper::Response::new("Some content".into());
 | 
			
		||||
			fetch::client::Response::new(u, r, abort)
 | 
			
		||||
		} else {
 | 
			
		||||
			fetch::client::Response::new(u, hyper::Response::new().with_status(StatusCode::NotFound), abort)
 | 
			
		||||
			let r = hyper::Response::builder()
 | 
			
		||||
				.status(StatusCode::NOT_FOUND)
 | 
			
		||||
				.body(Body::empty()).expect("Nothing to parse, can not fail; qed");
 | 
			
		||||
			fetch::client::Response::new(u, r, abort)
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -8,11 +8,11 @@ authors = ["Parity Technologies <admin@parity.io>"]
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
futures = "0.1"
 | 
			
		||||
hyper = "0.11"
 | 
			
		||||
hyper-rustls = "0.11"
 | 
			
		||||
hyper = "~0.12.9"
 | 
			
		||||
hyper-rustls = "0.14"
 | 
			
		||||
http = "0.1"
 | 
			
		||||
log = "0.4"
 | 
			
		||||
tokio-core = "0.1"
 | 
			
		||||
tokio-timer = "0.1"
 | 
			
		||||
tokio = "~0.1.8"
 | 
			
		||||
url = "1"
 | 
			
		||||
bytes = "0.4"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -17,8 +17,7 @@
 | 
			
		||||
use futures::future::{self, Loop};
 | 
			
		||||
use futures::sync::{mpsc, oneshot};
 | 
			
		||||
use futures::{self, Future, Async, Sink, Stream};
 | 
			
		||||
use hyper::header::{UserAgent, Location, ContentLength, ContentType};
 | 
			
		||||
use hyper::mime::Mime;
 | 
			
		||||
use hyper::header::{self, HeaderMap, HeaderValue, IntoHeaderName};
 | 
			
		||||
use hyper::{self, Method, StatusCode};
 | 
			
		||||
use hyper_rustls;
 | 
			
		||||
use std;
 | 
			
		||||
@ -29,8 +28,7 @@ use std::sync::mpsc::RecvTimeoutError;
 | 
			
		||||
use std::thread;
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
use std::{io, fmt};
 | 
			
		||||
use tokio_core::reactor;
 | 
			
		||||
use tokio_timer::{self, Timer};
 | 
			
		||||
use tokio::{self, util::FutureExt};
 | 
			
		||||
use url::{self, Url};
 | 
			
		||||
use bytes::Bytes;
 | 
			
		||||
 | 
			
		||||
@ -118,7 +116,7 @@ impl Abort {
 | 
			
		||||
/// Types which retrieve content from some URL.
 | 
			
		||||
pub trait Fetch: Clone + Send + Sync + 'static {
 | 
			
		||||
	/// The result future.
 | 
			
		||||
	type Result: Future<Item=Response, Error=Error> + Send + 'static;
 | 
			
		||||
	type Result: Future<Item = Response, Error = Error> + Send + 'static;
 | 
			
		||||
 | 
			
		||||
	/// Make a request to given URL
 | 
			
		||||
	fn fetch(&self, request: Request, abort: Abort) -> Self::Result;
 | 
			
		||||
@ -131,7 +129,7 @@ pub trait Fetch: Clone + Send + Sync + 'static {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type TxResponse = oneshot::Sender<Result<Response, Error>>;
 | 
			
		||||
type TxStartup = std::sync::mpsc::SyncSender<Result<(), io::Error>>;
 | 
			
		||||
type TxStartup = std::sync::mpsc::SyncSender<Result<(), tokio::io::Error>>;
 | 
			
		||||
type ChanItem = Option<(Request, Abort, TxResponse)>;
 | 
			
		||||
 | 
			
		||||
/// An implementation of `Fetch` using a `hyper` client.
 | 
			
		||||
@ -140,9 +138,8 @@ type ChanItem = Option<(Request, Abort, TxResponse)>;
 | 
			
		||||
// not implement `Send` currently.
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct Client {
 | 
			
		||||
	core: mpsc::Sender<ChanItem>,
 | 
			
		||||
	runtime: mpsc::Sender<ChanItem>,
 | 
			
		||||
	refs: Arc<AtomicUsize>,
 | 
			
		||||
	timer: Timer,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// When cloning a client we increment the internal reference counter.
 | 
			
		||||
@ -150,9 +147,8 @@ impl Clone for Client {
 | 
			
		||||
	fn clone(&self) -> Client {
 | 
			
		||||
		self.refs.fetch_add(1, Ordering::SeqCst);
 | 
			
		||||
		Client {
 | 
			
		||||
			core: self.core.clone(),
 | 
			
		||||
			runtime: self.runtime.clone(),
 | 
			
		||||
			refs: self.refs.clone(),
 | 
			
		||||
			timer: self.timer.clone(),
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -163,7 +159,7 @@ impl Drop for Client {
 | 
			
		||||
	fn drop(&mut self) {
 | 
			
		||||
		if self.refs.fetch_sub(1, Ordering::SeqCst) == 1 {
 | 
			
		||||
			// ignore send error as it means the background thread is gone already
 | 
			
		||||
			let _ = self.core.clone().send(None).wait();
 | 
			
		||||
			let _ = self.runtime.clone().send(None).wait();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -193,23 +189,20 @@ impl Client {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Ok(Client {
 | 
			
		||||
			core: tx_proto,
 | 
			
		||||
			runtime: tx_proto,
 | 
			
		||||
			refs: Arc::new(AtomicUsize::new(1)),
 | 
			
		||||
			timer: Timer::default(),
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fn background_thread(tx_start: TxStartup, rx_proto: mpsc::Receiver<ChanItem>, num_dns_threads: usize) -> io::Result<thread::JoinHandle<()>> {
 | 
			
		||||
		thread::Builder::new().name("fetch".into()).spawn(move || {
 | 
			
		||||
			let mut core = match reactor::Core::new() {
 | 
			
		||||
			let mut runtime = match tokio::runtime::current_thread::Runtime::new() {
 | 
			
		||||
				Ok(c) => c,
 | 
			
		||||
				Err(e) => return tx_start.send(Err(e)).unwrap_or(())
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
			let handle = core.handle();
 | 
			
		||||
			let hyper = hyper::Client::configure()
 | 
			
		||||
				.connector(hyper_rustls::HttpsConnector::new(num_dns_threads, &core.handle()))
 | 
			
		||||
				.build(&core.handle());
 | 
			
		||||
			let hyper = hyper::Client::builder()
 | 
			
		||||
				.build(hyper_rustls::HttpsConnector::new(num_dns_threads));
 | 
			
		||||
 | 
			
		||||
			let future = rx_proto.take_while(|item| Ok(item.is_some()))
 | 
			
		||||
				.map(|item| item.expect("`take_while` is only passing on channel items != None; qed"))
 | 
			
		||||
@ -241,13 +234,18 @@ impl Client {
 | 
			
		||||
									request2.set_url(next_url);
 | 
			
		||||
									request2
 | 
			
		||||
								} else {
 | 
			
		||||
									Request::new(next_url, Method::Get)
 | 
			
		||||
									Request::new(next_url, Method::GET)
 | 
			
		||||
								};
 | 
			
		||||
								Ok(Loop::Continue((client, request, abort, redirects + 1)))
 | 
			
		||||
							} else {
 | 
			
		||||
								let content_len = resp.headers.get::<ContentLength>().cloned();
 | 
			
		||||
								if content_len.map(|n| *n > abort.max_size() as u64).unwrap_or(false) {
 | 
			
		||||
									return Err(Error::SizeLimit)
 | 
			
		||||
								if let Some(ref h_val) = resp.headers.get(header::CONTENT_LENGTH) {
 | 
			
		||||
									let content_len = h_val
 | 
			
		||||
										.to_str()?
 | 
			
		||||
										.parse::<u64>()?;
 | 
			
		||||
 | 
			
		||||
									if content_len > abort.max_size() as u64 {
 | 
			
		||||
										return Err(Error::SizeLimit)
 | 
			
		||||
									}
 | 
			
		||||
								}
 | 
			
		||||
								Ok(Loop::Break(resp))
 | 
			
		||||
							}
 | 
			
		||||
@ -256,7 +254,7 @@ impl Client {
 | 
			
		||||
					.then(|result| {
 | 
			
		||||
						future::ok(sender.send(result).unwrap_or(()))
 | 
			
		||||
					});
 | 
			
		||||
				handle.spawn(fut);
 | 
			
		||||
				tokio::spawn(fut);
 | 
			
		||||
				trace!(target: "fetch", "waiting for next request ...");
 | 
			
		||||
				future::ok(())
 | 
			
		||||
			});
 | 
			
		||||
@ -264,7 +262,7 @@ impl Client {
 | 
			
		||||
			tx_start.send(Ok(())).unwrap_or(());
 | 
			
		||||
 | 
			
		||||
			debug!(target: "fetch", "processing requests ...");
 | 
			
		||||
			if let Err(()) = core.run(future) {
 | 
			
		||||
			if let Err(()) = runtime.block_on(future) {
 | 
			
		||||
				error!(target: "fetch", "error while executing future")
 | 
			
		||||
			}
 | 
			
		||||
			debug!(target: "fetch", "fetch background thread finished")
 | 
			
		||||
@ -273,7 +271,7 @@ impl Client {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Fetch for Client {
 | 
			
		||||
	type Result = Box<Future<Item=Response, Error=Error> + Send>;
 | 
			
		||||
	type Result = Box<Future<Item=Response, Error=Error> + Send + 'static>;
 | 
			
		||||
 | 
			
		||||
	fn fetch(&self, request: Request, abort: Abort) -> Self::Result {
 | 
			
		||||
		debug!(target: "fetch", "fetching: {:?}", request.url());
 | 
			
		||||
@ -282,7 +280,7 @@ impl Fetch for Client {
 | 
			
		||||
		}
 | 
			
		||||
		let (tx_res, rx_res) = oneshot::channel();
 | 
			
		||||
		let maxdur = abort.max_duration();
 | 
			
		||||
		let sender = self.core.clone();
 | 
			
		||||
		let sender = self.runtime.clone();
 | 
			
		||||
		let future = sender.send(Some((request, abort, tx_res)))
 | 
			
		||||
			.map_err(|e| {
 | 
			
		||||
				error!(target: "fetch", "failed to schedule request: {}", e);
 | 
			
		||||
@ -291,7 +289,15 @@ impl Fetch for Client {
 | 
			
		||||
			.and_then(|_| rx_res.map_err(|oneshot::Canceled| Error::BackgroundThreadDead))
 | 
			
		||||
			.and_then(future::result);
 | 
			
		||||
 | 
			
		||||
		Box::new(self.timer.timeout(future, maxdur))
 | 
			
		||||
		Box::new(future.timeout(maxdur)
 | 
			
		||||
			.map_err(|err| {
 | 
			
		||||
				if err.is_inner() {
 | 
			
		||||
					Error::from(err.into_inner().unwrap())
 | 
			
		||||
				} else {
 | 
			
		||||
					Error::from(err)
 | 
			
		||||
				}
 | 
			
		||||
			})
 | 
			
		||||
		)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Get content from some URL.
 | 
			
		||||
@ -315,22 +321,21 @@ impl Fetch for Client {
 | 
			
		||||
 | 
			
		||||
// Extract redirect location from response. The second return value indicate whether the original method should be preserved.
 | 
			
		||||
fn redirect_location(u: Url, r: &Response) -> Option<(Url, bool)> {
 | 
			
		||||
	use hyper::StatusCode::*;
 | 
			
		||||
	let preserve_method = match r.status() {
 | 
			
		||||
		TemporaryRedirect | PermanentRedirect => true,
 | 
			
		||||
		StatusCode::TEMPORARY_REDIRECT | StatusCode::PERMANENT_REDIRECT => true,
 | 
			
		||||
		_ => false,
 | 
			
		||||
	};
 | 
			
		||||
	match r.status() {
 | 
			
		||||
		MovedPermanently
 | 
			
		||||
		| PermanentRedirect
 | 
			
		||||
		| TemporaryRedirect
 | 
			
		||||
		| Found
 | 
			
		||||
		| SeeOther => {
 | 
			
		||||
			if let Some(loc) = r.headers.get::<Location>() {
 | 
			
		||||
				u.join(loc).ok().map(|url| (url, preserve_method))
 | 
			
		||||
			} else {
 | 
			
		||||
				None
 | 
			
		||||
			}
 | 
			
		||||
		StatusCode::MOVED_PERMANENTLY
 | 
			
		||||
		| StatusCode::PERMANENT_REDIRECT
 | 
			
		||||
		| StatusCode::TEMPORARY_REDIRECT
 | 
			
		||||
		| StatusCode::FOUND
 | 
			
		||||
		| StatusCode::SEE_OTHER => {
 | 
			
		||||
			r.headers.get(header::LOCATION).and_then(|loc| {
 | 
			
		||||
				loc.to_str().ok().and_then(|loc_s| {
 | 
			
		||||
					u.join(loc_s).ok().map(|url| (url, preserve_method))
 | 
			
		||||
				})
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
		_ => None
 | 
			
		||||
	}
 | 
			
		||||
@ -341,7 +346,7 @@ fn redirect_location(u: Url, r: &Response) -> Option<(Url, bool)> {
 | 
			
		||||
pub struct Request {
 | 
			
		||||
	url: Url,
 | 
			
		||||
	method: Method,
 | 
			
		||||
	headers: hyper::Headers,
 | 
			
		||||
	headers: HeaderMap,
 | 
			
		||||
	body: Bytes,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -350,19 +355,19 @@ impl Request {
 | 
			
		||||
	pub fn new(url: Url, method: Method) -> Request {
 | 
			
		||||
		Request {
 | 
			
		||||
			url, method,
 | 
			
		||||
			headers: hyper::Headers::new(),
 | 
			
		||||
			headers: HeaderMap::new(),
 | 
			
		||||
			body: Default::default(),
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Create a new GET request.
 | 
			
		||||
	pub fn get(url: Url) -> Request {
 | 
			
		||||
		Request::new(url, Method::Get)
 | 
			
		||||
		Request::new(url, Method::GET)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Create a new empty POST request.
 | 
			
		||||
	pub fn post(url: Url) -> Request {
 | 
			
		||||
		Request::new(url, Method::Post)
 | 
			
		||||
		Request::new(url, Method::POST)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Read the url.
 | 
			
		||||
@ -371,12 +376,12 @@ impl Request {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Read the request headers.
 | 
			
		||||
	pub fn headers(&self) -> &hyper::Headers {
 | 
			
		||||
	pub fn headers(&self) -> &HeaderMap {
 | 
			
		||||
		&self.headers
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Get a mutable reference to the headers.
 | 
			
		||||
	pub fn headers_mut(&mut self) -> &mut hyper::Headers {
 | 
			
		||||
	pub fn headers_mut(&mut self) -> &mut HeaderMap {
 | 
			
		||||
		&mut self.headers
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -391,8 +396,10 @@ impl Request {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Consume self, and return it with the added given header.
 | 
			
		||||
	pub fn with_header<H: hyper::header::Header>(mut self, value: H) -> Self {
 | 
			
		||||
		self.headers_mut().set(value);
 | 
			
		||||
	pub fn with_header<K>(mut self, key: K, val: HeaderValue) -> Self
 | 
			
		||||
		where K: IntoHeaderName,
 | 
			
		||||
	{
 | 
			
		||||
		self.headers_mut().append(key, val);
 | 
			
		||||
		self
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -403,16 +410,15 @@ impl Request {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Into<hyper::Request> for Request {
 | 
			
		||||
	fn into(mut self) -> hyper::Request {
 | 
			
		||||
		let uri = self.url.as_ref().parse().expect("Every valid URLis also a URI.");
 | 
			
		||||
		let mut req = hyper::Request::new(self.method, uri);
 | 
			
		||||
 | 
			
		||||
		self.headers.set(UserAgent::new("Parity Fetch Neo"));
 | 
			
		||||
		*req.headers_mut() = self.headers;
 | 
			
		||||
		req.set_body(self.body);
 | 
			
		||||
 | 
			
		||||
		req
 | 
			
		||||
impl From<Request> for hyper::Request<hyper::Body> {
 | 
			
		||||
	fn from(req: Request) -> hyper::Request<hyper::Body> {
 | 
			
		||||
		let uri: hyper::Uri = req.url.as_ref().parse().expect("Every valid URLis also a URI.");
 | 
			
		||||
		hyper::Request::builder()
 | 
			
		||||
			.method(req.method)
 | 
			
		||||
			.uri(uri)
 | 
			
		||||
			.header(header::USER_AGENT, HeaderValue::from_static("Parity Fetch Neo"))
 | 
			
		||||
			.body(req.body.into())
 | 
			
		||||
			.expect("Header, uri, method, and body are already valid and can not fail to parse; qed")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -421,7 +427,7 @@ impl Into<hyper::Request> for Request {
 | 
			
		||||
pub struct Response {
 | 
			
		||||
	url: Url,
 | 
			
		||||
	status: StatusCode,
 | 
			
		||||
	headers: hyper::Headers,
 | 
			
		||||
	headers: HeaderMap,
 | 
			
		||||
	body: hyper::Body,
 | 
			
		||||
	abort: Abort,
 | 
			
		||||
	nread: usize,
 | 
			
		||||
@ -429,12 +435,12 @@ pub struct Response {
 | 
			
		||||
 | 
			
		||||
impl Response {
 | 
			
		||||
	/// Create a new response, wrapping a hyper response.
 | 
			
		||||
	pub fn new(u: Url, r: hyper::Response, a: Abort) -> Response {
 | 
			
		||||
	pub fn new(u: Url, r: hyper::Response<hyper::Body>, a: Abort) -> Response {
 | 
			
		||||
		Response {
 | 
			
		||||
			url: u,
 | 
			
		||||
			status: r.status(),
 | 
			
		||||
			headers: r.headers().clone(),
 | 
			
		||||
			body: r.body(),
 | 
			
		||||
			body: r.into_body(),
 | 
			
		||||
			abort: a,
 | 
			
		||||
			nread: 0,
 | 
			
		||||
		}
 | 
			
		||||
@ -447,26 +453,21 @@ impl Response {
 | 
			
		||||
 | 
			
		||||
	/// Status code == OK (200)?
 | 
			
		||||
	pub fn is_success(&self) -> bool {
 | 
			
		||||
		self.status() == StatusCode::Ok
 | 
			
		||||
		self.status() == StatusCode::OK
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Status code == 404.
 | 
			
		||||
	pub fn is_not_found(&self) -> bool {
 | 
			
		||||
		self.status() == StatusCode::NotFound
 | 
			
		||||
		self.status() == StatusCode::NOT_FOUND
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Is the content-type text/html?
 | 
			
		||||
	pub fn is_html(&self) -> bool {
 | 
			
		||||
		if let Some(ref mime) = self.content_type() {
 | 
			
		||||
			mime.type_() == "text" && mime.subtype() == "html"
 | 
			
		||||
		} else {
 | 
			
		||||
			false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// The conten-type header value.
 | 
			
		||||
	pub fn content_type(&self) -> Option<Mime> {
 | 
			
		||||
		self.headers.get::<ContentType>().map(|ct| ct.0.clone())
 | 
			
		||||
		self.headers.get(header::CONTENT_TYPE).and_then(|ct_val| {
 | 
			
		||||
			ct_val.to_str().ok().map(|ct_str| {
 | 
			
		||||
				ct_str.contains("text") && ct_str.contains("html")
 | 
			
		||||
			})
 | 
			
		||||
		}).unwrap_or(false)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -562,6 +563,10 @@ impl io::Read for BodyReader {
 | 
			
		||||
pub enum Error {
 | 
			
		||||
	/// Hyper gave us an error.
 | 
			
		||||
	Hyper(hyper::Error),
 | 
			
		||||
	/// A hyper header conversion error.
 | 
			
		||||
	HyperHeaderToStrError(hyper::header::ToStrError),
 | 
			
		||||
	/// An integer parsing error.
 | 
			
		||||
	ParseInt(std::num::ParseIntError),
 | 
			
		||||
	/// Some I/O error occured.
 | 
			
		||||
	Io(io::Error),
 | 
			
		||||
	/// Invalid URLs where attempted to parse.
 | 
			
		||||
@ -570,8 +575,10 @@ pub enum Error {
 | 
			
		||||
	Aborted,
 | 
			
		||||
	/// Too many redirects have been encountered.
 | 
			
		||||
	TooManyRedirects,
 | 
			
		||||
	/// tokio-timer inner future gave us an error.
 | 
			
		||||
	TokioTimeoutInnerVal(String),
 | 
			
		||||
	/// tokio-timer gave us an error.
 | 
			
		||||
	Timer(tokio_timer::TimerError),
 | 
			
		||||
	TokioTimer(Option<tokio::timer::Error>),
 | 
			
		||||
	/// The maximum duration was reached.
 | 
			
		||||
	Timeout,
 | 
			
		||||
	/// The response body is too large.
 | 
			
		||||
@ -585,23 +592,43 @@ impl fmt::Display for Error {
 | 
			
		||||
		match *self {
 | 
			
		||||
			Error::Aborted => write!(fmt, "The request has been aborted."),
 | 
			
		||||
			Error::Hyper(ref e) => write!(fmt, "{}", e),
 | 
			
		||||
			Error::HyperHeaderToStrError(ref e) => write!(fmt, "{}", e),
 | 
			
		||||
			Error::ParseInt(ref e) => write!(fmt, "{}", e),
 | 
			
		||||
			Error::Url(ref e) => write!(fmt, "{}", e),
 | 
			
		||||
			Error::Io(ref e) => write!(fmt, "{}", e),
 | 
			
		||||
			Error::BackgroundThreadDead => write!(fmt, "background thread gond"),
 | 
			
		||||
			Error::TooManyRedirects => write!(fmt, "too many redirects"),
 | 
			
		||||
			Error::Timer(ref e) => write!(fmt, "{}", e),
 | 
			
		||||
			Error::TokioTimeoutInnerVal(ref s) => write!(fmt, "tokio timer inner value error: {:?}", s),
 | 
			
		||||
			Error::TokioTimer(ref e) => write!(fmt, "tokio timer error: {:?}", e),
 | 
			
		||||
			Error::Timeout => write!(fmt, "request timed out"),
 | 
			
		||||
			Error::SizeLimit => write!(fmt, "size limit reached"),
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ::std::error::Error for Error {
 | 
			
		||||
	fn description(&self) -> &str { "Fetch client error" }
 | 
			
		||||
	fn cause(&self) -> Option<&::std::error::Error> { None }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<hyper::Error> for Error {
 | 
			
		||||
	fn from(e: hyper::Error) -> Self {
 | 
			
		||||
		Error::Hyper(e)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<hyper::header::ToStrError> for Error {
 | 
			
		||||
	fn from(e: hyper::header::ToStrError) -> Self {
 | 
			
		||||
		Error::HyperHeaderToStrError(e)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<std::num::ParseIntError> for Error {
 | 
			
		||||
	fn from(e: std::num::ParseIntError) -> Self {
 | 
			
		||||
		Error::ParseInt(e)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<io::Error> for Error {
 | 
			
		||||
	fn from(e: io::Error) -> Self {
 | 
			
		||||
		Error::Io(e)
 | 
			
		||||
@ -614,24 +641,35 @@ impl From<url::ParseError> for Error {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<F> From<tokio_timer::TimeoutError<F>> for Error {
 | 
			
		||||
	fn from(e: tokio_timer::TimeoutError<F>) -> Self {
 | 
			
		||||
		match e {
 | 
			
		||||
			tokio_timer::TimeoutError::Timer(_, e) => Error::Timer(e),
 | 
			
		||||
			tokio_timer::TimeoutError::TimedOut(_) => Error::Timeout,
 | 
			
		||||
impl<T: std::fmt::Debug> From<tokio::timer::timeout::Error<T>> for Error {
 | 
			
		||||
	fn from(e: tokio::timer::timeout::Error<T>) -> Self {
 | 
			
		||||
		if e.is_inner() {
 | 
			
		||||
			Error::TokioTimeoutInnerVal(format!("{:?}", e.into_inner().unwrap()))
 | 
			
		||||
		} else if e.is_elapsed() {
 | 
			
		||||
			Error::Timeout
 | 
			
		||||
		} else {
 | 
			
		||||
			Error::TokioTimer(e.into_timer())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<tokio::timer::Error> for Error {
 | 
			
		||||
	fn from(e: tokio::timer::Error) -> Self {
 | 
			
		||||
		Error::TokioTimer(Some(e))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
	use super::*;
 | 
			
		||||
	use futures::future;
 | 
			
		||||
	use futures::sync::mpsc;
 | 
			
		||||
	use hyper::StatusCode;
 | 
			
		||||
	use hyper::server::{Http, Request, Response, Service};
 | 
			
		||||
	use tokio_timer::Timer;
 | 
			
		||||
	use std;
 | 
			
		||||
	use futures::sync::oneshot;
 | 
			
		||||
	use hyper::{
 | 
			
		||||
		StatusCode,
 | 
			
		||||
		service::Service,
 | 
			
		||||
	};
 | 
			
		||||
	use tokio::timer::Delay;
 | 
			
		||||
	use tokio::runtime::current_thread::Runtime;
 | 
			
		||||
	use std::io::Read;
 | 
			
		||||
	use std::net::SocketAddr;
 | 
			
		||||
 | 
			
		||||
@ -641,139 +679,238 @@ mod test {
 | 
			
		||||
	fn it_should_fetch() {
 | 
			
		||||
		let server = TestServer::run();
 | 
			
		||||
		let client = Client::new(4).unwrap();
 | 
			
		||||
		let future = client.get(&format!("http://{}?123", server.addr()), Default::default());
 | 
			
		||||
		let resp = future.wait().unwrap();
 | 
			
		||||
		assert!(resp.is_success());
 | 
			
		||||
		let body = resp.concat2().wait().unwrap();
 | 
			
		||||
		assert_eq!(&body[..], b"123")
 | 
			
		||||
		let mut runtime = Runtime::new().unwrap();
 | 
			
		||||
 | 
			
		||||
		let future = client.get(&format!("http://{}?123", server.addr()), Abort::default())
 | 
			
		||||
			.map(|resp| {
 | 
			
		||||
				assert!(resp.is_success());
 | 
			
		||||
				resp
 | 
			
		||||
			})
 | 
			
		||||
			.map(|resp| resp.concat2())
 | 
			
		||||
			.flatten()
 | 
			
		||||
			.map(|body| assert_eq!(&body[..], b"123"))
 | 
			
		||||
			.map_err(|err| panic!(err));
 | 
			
		||||
 | 
			
		||||
		runtime.block_on(future).unwrap();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	#[test]
 | 
			
		||||
	fn it_should_fetch_in_light_mode() {
 | 
			
		||||
		let server = TestServer::run();
 | 
			
		||||
		let client = Client::new(1).unwrap();
 | 
			
		||||
		let future = client.get(&format!("http://{}?123", server.addr()), Default::default());
 | 
			
		||||
		let resp = future.wait().unwrap();
 | 
			
		||||
		assert!(resp.is_success());
 | 
			
		||||
		let body = resp.concat2().wait().unwrap();
 | 
			
		||||
		assert_eq!(&body[..], b"123")
 | 
			
		||||
		let mut runtime = Runtime::new().unwrap();
 | 
			
		||||
 | 
			
		||||
		let future = client.get(&format!("http://{}?123", server.addr()), Abort::default())
 | 
			
		||||
			.map(|resp| {
 | 
			
		||||
				assert!(resp.is_success());
 | 
			
		||||
				resp
 | 
			
		||||
			})
 | 
			
		||||
			.map(|resp| resp.concat2())
 | 
			
		||||
			.flatten()
 | 
			
		||||
			.map(|body| assert_eq!(&body[..], b"123"))
 | 
			
		||||
			.map_err(|err| panic!(err));
 | 
			
		||||
 | 
			
		||||
		runtime.block_on(future).unwrap();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	#[test]
 | 
			
		||||
	fn it_should_timeout() {
 | 
			
		||||
		let server = TestServer::run();
 | 
			
		||||
		let client = Client::new(4).unwrap();
 | 
			
		||||
		let mut runtime = Runtime::new().unwrap();
 | 
			
		||||
 | 
			
		||||
		let abort = Abort::default().with_max_duration(Duration::from_secs(1));
 | 
			
		||||
		match client.get(&format!("http://{}/delay?3", server.addr()), abort).wait() {
 | 
			
		||||
			Err(Error::Timeout) => {}
 | 
			
		||||
			other => panic!("expected timeout, got {:?}", other)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		let future = client.get(&format!("http://{}/delay?3", server.addr()), abort)
 | 
			
		||||
			.then(|res| {
 | 
			
		||||
				match res {
 | 
			
		||||
					Err(Error::Timeout) => Ok::<_, ()>(()),
 | 
			
		||||
					other => panic!("expected timeout, got {:?}", other),
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
		runtime.block_on(future).unwrap();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	#[test]
 | 
			
		||||
	fn it_should_follow_redirects() {
 | 
			
		||||
		let server = TestServer::run();
 | 
			
		||||
		let client = Client::new(4).unwrap();
 | 
			
		||||
		let mut runtime = Runtime::new().unwrap();
 | 
			
		||||
 | 
			
		||||
		let abort = Abort::default();
 | 
			
		||||
		let future = client.get(&format!("http://{}/redirect?http://{}/", server.addr(), server.addr()), abort);
 | 
			
		||||
		assert!(future.wait().unwrap().is_success())
 | 
			
		||||
 | 
			
		||||
		let future = client.get(&format!("http://{}/redirect?http://{}/", server.addr(), server.addr()), abort)
 | 
			
		||||
			.and_then(|resp| {
 | 
			
		||||
				if resp.is_success() { Ok(()) } else { panic!("Response unsuccessful") }
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
		runtime.block_on(future).unwrap();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	#[test]
 | 
			
		||||
	fn it_should_follow_relative_redirects() {
 | 
			
		||||
		let server = TestServer::run();
 | 
			
		||||
		let client = Client::new(4).unwrap();
 | 
			
		||||
		let mut runtime = Runtime::new().unwrap();
 | 
			
		||||
 | 
			
		||||
		let abort = Abort::default().with_max_redirects(4);
 | 
			
		||||
		let future = client.get(&format!("http://{}/redirect?/", server.addr()), abort);
 | 
			
		||||
		assert!(future.wait().unwrap().is_success())
 | 
			
		||||
		let future = client.get(&format!("http://{}/redirect?/", server.addr()), abort)
 | 
			
		||||
			.and_then(|resp| {
 | 
			
		||||
				if resp.is_success() { Ok(()) } else { panic!("Response unsuccessful") }
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
		runtime.block_on(future).unwrap();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	#[test]
 | 
			
		||||
	fn it_should_not_follow_too_many_redirects() {
 | 
			
		||||
		let server = TestServer::run();
 | 
			
		||||
		let client = Client::new(4).unwrap();
 | 
			
		||||
		let mut runtime = Runtime::new().unwrap();
 | 
			
		||||
 | 
			
		||||
		let abort = Abort::default().with_max_redirects(3);
 | 
			
		||||
		match client.get(&format!("http://{}/loop", server.addr()), abort).wait() {
 | 
			
		||||
			Err(Error::TooManyRedirects) => {}
 | 
			
		||||
			other => panic!("expected too many redirects error, got {:?}", other)
 | 
			
		||||
		}
 | 
			
		||||
		let future = client.get(&format!("http://{}/loop", server.addr()), abort)
 | 
			
		||||
			.then(|res| {
 | 
			
		||||
				match res {
 | 
			
		||||
					Err(Error::TooManyRedirects) => Ok::<_, ()>(()),
 | 
			
		||||
					other => panic!("expected too many redirects error, got {:?}", other)
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
		runtime.block_on(future).unwrap();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	#[test]
 | 
			
		||||
	fn it_should_read_data() {
 | 
			
		||||
		let server = TestServer::run();
 | 
			
		||||
		let client = Client::new(4).unwrap();
 | 
			
		||||
		let mut runtime = Runtime::new().unwrap();
 | 
			
		||||
 | 
			
		||||
		let abort = Abort::default();
 | 
			
		||||
		let future = client.get(&format!("http://{}?abcdefghijklmnopqrstuvwxyz", server.addr()), abort);
 | 
			
		||||
		let resp = future.wait().unwrap();
 | 
			
		||||
		assert!(resp.is_success());
 | 
			
		||||
		assert_eq!(&resp.concat2().wait().unwrap()[..], b"abcdefghijklmnopqrstuvwxyz")
 | 
			
		||||
		let future = client.get(&format!("http://{}?abcdefghijklmnopqrstuvwxyz", server.addr()), abort)
 | 
			
		||||
			.and_then(|resp| {
 | 
			
		||||
				if resp.is_success() { Ok(resp) } else { panic!("Response unsuccessful") }
 | 
			
		||||
			})
 | 
			
		||||
			.map(|resp| resp.concat2())
 | 
			
		||||
			.flatten()
 | 
			
		||||
			.map(|body| assert_eq!(&body[..], b"abcdefghijklmnopqrstuvwxyz"));
 | 
			
		||||
 | 
			
		||||
		runtime.block_on(future).unwrap();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	#[test]
 | 
			
		||||
	fn it_should_not_read_too_much_data() {
 | 
			
		||||
		let server = TestServer::run();
 | 
			
		||||
		let client = Client::new(4).unwrap();
 | 
			
		||||
		let mut runtime = Runtime::new().unwrap();
 | 
			
		||||
 | 
			
		||||
		let abort = Abort::default().with_max_size(3);
 | 
			
		||||
		let resp = client.get(&format!("http://{}/?1234", server.addr()), abort).wait().unwrap();
 | 
			
		||||
		assert!(resp.is_success());
 | 
			
		||||
		match resp.concat2().wait() {
 | 
			
		||||
			Err(Error::SizeLimit) => {}
 | 
			
		||||
			other => panic!("expected size limit error, got {:?}", other)
 | 
			
		||||
		}
 | 
			
		||||
		let future = client.get(&format!("http://{}/?1234", server.addr()), abort)
 | 
			
		||||
			.and_then(|resp| {
 | 
			
		||||
				if resp.is_success() { Ok(resp) } else { panic!("Response unsuccessful") }
 | 
			
		||||
			})
 | 
			
		||||
			.map(|resp| resp.concat2())
 | 
			
		||||
			.flatten()
 | 
			
		||||
			.then(|body| {
 | 
			
		||||
				match body {
 | 
			
		||||
					Err(Error::SizeLimit) => Ok::<_, ()>(()),
 | 
			
		||||
					other => panic!("expected size limit error, got {:?}", other),
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
		runtime.block_on(future).unwrap();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	#[test]
 | 
			
		||||
	fn it_should_not_read_too_much_data_sync() {
 | 
			
		||||
		let server = TestServer::run();
 | 
			
		||||
		let client = Client::new(4).unwrap();
 | 
			
		||||
		let mut runtime = Runtime::new().unwrap();
 | 
			
		||||
 | 
			
		||||
		// let abort = Abort::default().with_max_size(3);
 | 
			
		||||
		// let resp = client.get(&format!("http://{}/?1234", server.addr()), abort).wait().unwrap();
 | 
			
		||||
		// assert!(resp.is_success());
 | 
			
		||||
		// let mut buffer = Vec::new();
 | 
			
		||||
		// let mut reader = BodyReader::new(resp);
 | 
			
		||||
		// match reader.read_to_end(&mut buffer) {
 | 
			
		||||
		// 	Err(ref e) if e.kind() == io::ErrorKind::PermissionDenied => {}
 | 
			
		||||
		// 	other => panic!("expected size limit error, got {:?}", other)
 | 
			
		||||
		// }
 | 
			
		||||
 | 
			
		||||
		// FIXME (c0gent): The prior version of this test (pre-hyper-0.12,
 | 
			
		||||
		// commented out above) is not possible to recreate. It relied on an
 | 
			
		||||
		// apparent bug in `Client::background_thread` which suppressed the
 | 
			
		||||
		// `SizeLimit` error from occurring. This is due to the headers
 | 
			
		||||
		// collection not returning a value for content length when queried.
 | 
			
		||||
		// The precise reason why this was happening is unclear.
 | 
			
		||||
 | 
			
		||||
		let abort = Abort::default().with_max_size(3);
 | 
			
		||||
		let resp = client.get(&format!("http://{}/?1234", server.addr()), abort).wait().unwrap();
 | 
			
		||||
		assert!(resp.is_success());
 | 
			
		||||
		let mut buffer = Vec::new();
 | 
			
		||||
		let mut reader = BodyReader::new(resp);
 | 
			
		||||
		match reader.read_to_end(&mut buffer) {
 | 
			
		||||
			Err(ref e) if e.kind() == io::ErrorKind::PermissionDenied => {}
 | 
			
		||||
			other => panic!("expected size limit error, got {:?}", other)
 | 
			
		||||
		let future = client.get(&format!("http://{}/?1234", server.addr()), abort)
 | 
			
		||||
			.and_then(|resp| {
 | 
			
		||||
				assert_eq!(true, false, "Unreachable. (see FIXME note)");
 | 
			
		||||
				assert!(resp.is_success());
 | 
			
		||||
				let mut buffer = Vec::new();
 | 
			
		||||
				let mut reader = BodyReader::new(resp);
 | 
			
		||||
				match reader.read_to_end(&mut buffer) {
 | 
			
		||||
					Err(ref e) if e.kind() == io::ErrorKind::PermissionDenied => Ok(()),
 | 
			
		||||
					other => panic!("expected size limit error, got {:?}", other)
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
		// FIXME: This simply demonstrates the above point.
 | 
			
		||||
		match runtime.block_on(future) {
 | 
			
		||||
			Err(Error::SizeLimit) => {},
 | 
			
		||||
			other => panic!("Expected `Error::SizeLimit`, got: {:?}", other),
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	struct TestServer(Timer);
 | 
			
		||||
	struct TestServer;
 | 
			
		||||
 | 
			
		||||
	impl Service for TestServer {
 | 
			
		||||
		type Request = Request;
 | 
			
		||||
		type Response = Response;
 | 
			
		||||
		type Error = hyper::Error;
 | 
			
		||||
		type Future = Box<Future<Item=Self::Response, Error=Self::Error>>;
 | 
			
		||||
		type ReqBody = hyper::Body;
 | 
			
		||||
		type ResBody = hyper::Body;
 | 
			
		||||
		type Error = Error;
 | 
			
		||||
		type Future = Box<Future<Item=hyper::Response<Self::ResBody>, Error=Self::Error> + Send + 'static>;
 | 
			
		||||
 | 
			
		||||
		fn call(&self, req: Request) -> Self::Future {
 | 
			
		||||
		fn call(&mut self, req: hyper::Request<hyper::Body>) -> Self::Future {
 | 
			
		||||
			match req.uri().path() {
 | 
			
		||||
				"/" => {
 | 
			
		||||
					let body = req.uri().query().unwrap_or("").to_string();
 | 
			
		||||
					let req = Response::new().with_body(body);
 | 
			
		||||
					Box::new(future::ok(req))
 | 
			
		||||
					let res = hyper::Response::new(body.into());
 | 
			
		||||
					Box::new(future::ok(res))
 | 
			
		||||
				}
 | 
			
		||||
				"/redirect" => {
 | 
			
		||||
					let loc = Location::new(req.uri().query().unwrap_or("/").to_string());
 | 
			
		||||
					let req = Response::new()
 | 
			
		||||
						.with_status(StatusCode::MovedPermanently)
 | 
			
		||||
						.with_header(loc);
 | 
			
		||||
					Box::new(future::ok(req))
 | 
			
		||||
					let loc = req.uri().query().unwrap_or("/").to_string();
 | 
			
		||||
					let res = hyper::Response::builder()
 | 
			
		||||
						.status(StatusCode::MOVED_PERMANENTLY)
 | 
			
		||||
						.header(hyper::header::LOCATION, loc)
 | 
			
		||||
						.body(hyper::Body::empty())
 | 
			
		||||
						.expect("Unable to create response");
 | 
			
		||||
					Box::new(future::ok(res))
 | 
			
		||||
				}
 | 
			
		||||
				"/loop" => {
 | 
			
		||||
					let req = Response::new()
 | 
			
		||||
						.with_status(StatusCode::MovedPermanently)
 | 
			
		||||
						.with_header(Location::new("/loop".to_string()));
 | 
			
		||||
					Box::new(future::ok(req))
 | 
			
		||||
					let res = hyper::Response::builder()
 | 
			
		||||
						.status(StatusCode::MOVED_PERMANENTLY)
 | 
			
		||||
						.header(hyper::header::LOCATION, "/loop")
 | 
			
		||||
						.body(hyper::Body::empty())
 | 
			
		||||
						.expect("Unable to create response");
 | 
			
		||||
					Box::new(future::ok(res))
 | 
			
		||||
				}
 | 
			
		||||
				"/delay" => {
 | 
			
		||||
					let d = Duration::from_secs(req.uri().query().unwrap_or("0").parse().unwrap());
 | 
			
		||||
					Box::new(self.0.sleep(d)
 | 
			
		||||
							 .map_err(|_| return io::Error::new(io::ErrorKind::Other, "timer error"))
 | 
			
		||||
							 .from_err()
 | 
			
		||||
							 .map(|_| Response::new()))
 | 
			
		||||
					let dur = Duration::from_secs(req.uri().query().unwrap_or("0").parse().unwrap());
 | 
			
		||||
					let delayed_res = Delay::new(std::time::Instant::now() + dur)
 | 
			
		||||
						.and_then(|_| Ok::<_, _>(hyper::Response::new(hyper::Body::empty())))
 | 
			
		||||
						.from_err();
 | 
			
		||||
					Box::new(delayed_res)
 | 
			
		||||
				}
 | 
			
		||||
				_ => {
 | 
			
		||||
					let res = hyper::Response::builder()
 | 
			
		||||
						.status(StatusCode::NOT_FOUND)
 | 
			
		||||
						.body(hyper::Body::empty())
 | 
			
		||||
						.expect("Unable to create response");
 | 
			
		||||
					Box::new(future::ok(res))
 | 
			
		||||
				}
 | 
			
		||||
				_ => Box::new(future::ok(Response::new().with_status(StatusCode::NotFound)))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@ -781,19 +918,27 @@ mod test {
 | 
			
		||||
	impl TestServer {
 | 
			
		||||
		fn run() -> Handle {
 | 
			
		||||
			let (tx_start, rx_start) = std::sync::mpsc::sync_channel(1);
 | 
			
		||||
			let (tx_end, rx_end) = mpsc::channel(0);
 | 
			
		||||
			let rx_end_fut = rx_end.into_future().map(|_| ()).map_err(|_| ());
 | 
			
		||||
			let (tx_end, rx_end) = oneshot::channel();
 | 
			
		||||
			let rx_end_fut = rx_end.map(|_| ()).map_err(|_| ());
 | 
			
		||||
			thread::spawn(move || {
 | 
			
		||||
				let addr = ADDRESS.parse().unwrap();
 | 
			
		||||
				let server = Http::new().bind(&addr, || Ok(TestServer(Timer::default()))).unwrap();
 | 
			
		||||
				tx_start.send(server.local_addr().unwrap()).unwrap_or(());
 | 
			
		||||
				server.run_until(rx_end_fut).unwrap();
 | 
			
		||||
 | 
			
		||||
				let server = hyper::server::Server::bind(&addr)
 | 
			
		||||
					.serve(|| future::ok::<_, hyper::Error>(TestServer));
 | 
			
		||||
 | 
			
		||||
				tx_start.send(server.local_addr()).unwrap_or(());
 | 
			
		||||
 | 
			
		||||
				tokio::run(
 | 
			
		||||
					server.with_graceful_shutdown(rx_end_fut)
 | 
			
		||||
						.map_err(|e| panic!("server error: {}", e))
 | 
			
		||||
				);
 | 
			
		||||
			});
 | 
			
		||||
			Handle(rx_start.recv().unwrap(), tx_end)
 | 
			
		||||
 | 
			
		||||
			Handle(rx_start.recv().unwrap(), Some(tx_end))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	struct Handle(SocketAddr, mpsc::Sender<()>);
 | 
			
		||||
	struct Handle(SocketAddr, Option<oneshot::Sender<()>>);
 | 
			
		||||
 | 
			
		||||
	impl Handle {
 | 
			
		||||
		fn addr(&self) -> SocketAddr {
 | 
			
		||||
@ -803,7 +948,7 @@ mod test {
 | 
			
		||||
 | 
			
		||||
	impl Drop for Handle {
 | 
			
		||||
		fn drop(&mut self) {
 | 
			
		||||
			self.1.clone().send(()).wait().unwrap();
 | 
			
		||||
			self.1.take().unwrap().send(()).unwrap();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -26,9 +26,9 @@ extern crate futures;
 | 
			
		||||
 | 
			
		||||
extern crate hyper;
 | 
			
		||||
extern crate hyper_rustls;
 | 
			
		||||
extern crate http;
 | 
			
		||||
 | 
			
		||||
extern crate tokio_core;
 | 
			
		||||
extern crate tokio_timer;
 | 
			
		||||
extern crate tokio;
 | 
			
		||||
extern crate url;
 | 
			
		||||
extern crate bytes;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,7 @@ ansi_term = "0.10"
 | 
			
		||||
rustc-hex = "1.0"
 | 
			
		||||
ethcore-io = { path = "../io", features = ["mio"] }
 | 
			
		||||
parity-bytes = "0.1"
 | 
			
		||||
parity-crypto = "0.1"
 | 
			
		||||
parity-crypto = "0.2"
 | 
			
		||||
ethcore-logger = { path ="../../logger" }
 | 
			
		||||
ethcore-network = { path = "../network" }
 | 
			
		||||
ethereum-types = "0.4"
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
error-chain = { version = "0.12", default-features = false }
 | 
			
		||||
parity-crypto = "0.1"
 | 
			
		||||
parity-crypto = "0.2"
 | 
			
		||||
ethcore-io = { path = "../io" }
 | 
			
		||||
ethereum-types = "0.4"
 | 
			
		||||
ethkey = { path = "../../ethkey" }
 | 
			
		||||
 | 
			
		||||
@ -1,238 +0,0 @@
 | 
			
		||||
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
 | 
			
		||||
// 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/>.
 | 
			
		||||
 | 
			
		||||
//! Tokio Core Reactor wrapper.
 | 
			
		||||
 | 
			
		||||
extern crate futures;
 | 
			
		||||
extern crate tokio_core;
 | 
			
		||||
 | 
			
		||||
use std::{fmt, thread};
 | 
			
		||||
use std::sync::mpsc;
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
use futures::{Future, IntoFuture};
 | 
			
		||||
pub use tokio_core::reactor::{Remote as TokioRemote, Handle, Timeout};
 | 
			
		||||
 | 
			
		||||
/// Event Loop for futures.
 | 
			
		||||
/// Wrapper around `tokio::reactor::Core`.
 | 
			
		||||
/// Runs in a separate thread.
 | 
			
		||||
pub struct EventLoop {
 | 
			
		||||
	remote: Remote,
 | 
			
		||||
	handle: EventLoopHandle,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl EventLoop {
 | 
			
		||||
	/// Spawns a new thread with `EventLoop` with given handler.
 | 
			
		||||
	pub fn spawn() -> Self {
 | 
			
		||||
		let (stop, stopped) = futures::oneshot();
 | 
			
		||||
		let (tx, rx) = mpsc::channel();
 | 
			
		||||
		let handle = thread::spawn(move || {
 | 
			
		||||
			let mut el = tokio_core::reactor::Core::new().expect("Creating an event loop should not fail.");
 | 
			
		||||
			tx.send(el.remote()).expect("Rx is blocking upper thread.");
 | 
			
		||||
			let _ = el.run(futures::empty().select(stopped));
 | 
			
		||||
		});
 | 
			
		||||
		let remote = rx.recv().expect("tx is transfered to a newly spawned thread.");
 | 
			
		||||
 | 
			
		||||
		EventLoop {
 | 
			
		||||
			remote: Remote {
 | 
			
		||||
				inner: Mode::Tokio(remote),
 | 
			
		||||
			},
 | 
			
		||||
			handle: EventLoopHandle {
 | 
			
		||||
				close: Some(stop),
 | 
			
		||||
				handle: Some(handle),
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Returns this event loop raw remote.
 | 
			
		||||
	///
 | 
			
		||||
	/// Deprecated: Exists only to connect with current JSONRPC implementation.
 | 
			
		||||
	pub fn raw_remote(&self) -> TokioRemote {
 | 
			
		||||
		if let Mode::Tokio(ref remote) = self.remote.inner {
 | 
			
		||||
			remote.clone()
 | 
			
		||||
		} else {
 | 
			
		||||
			panic!("Event loop is never initialized in other mode then Tokio.")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Returns event loop remote.
 | 
			
		||||
	pub fn remote(&self) -> Remote {
 | 
			
		||||
		self.remote.clone()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
enum Mode {
 | 
			
		||||
	Tokio(TokioRemote),
 | 
			
		||||
	Sync,
 | 
			
		||||
	ThreadPerFuture,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Debug for Mode {
 | 
			
		||||
	fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
 | 
			
		||||
		use self::Mode::*;
 | 
			
		||||
 | 
			
		||||
		match *self {
 | 
			
		||||
			Tokio(_) => write!(fmt, "tokio"),
 | 
			
		||||
			Sync => write!(fmt, "synchronous"),
 | 
			
		||||
			ThreadPerFuture => write!(fmt, "thread per future"),
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub struct Remote {
 | 
			
		||||
	inner: Mode,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Remote {
 | 
			
		||||
	/// Remote for existing event loop.
 | 
			
		||||
	///
 | 
			
		||||
	/// Deprecated: Exists only to connect with current JSONRPC implementation.
 | 
			
		||||
	pub fn new(remote: TokioRemote) -> Self {
 | 
			
		||||
		Remote {
 | 
			
		||||
			inner: Mode::Tokio(remote),
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Synchronous remote, used mostly for tests.
 | 
			
		||||
	pub fn new_sync() -> Self {
 | 
			
		||||
		Remote {
 | 
			
		||||
			inner: Mode::Sync,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Spawns a new thread for each future (use only for tests).
 | 
			
		||||
	pub fn new_thread_per_future() -> Self {
 | 
			
		||||
		Remote {
 | 
			
		||||
			inner: Mode::ThreadPerFuture,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Spawn a future to this event loop
 | 
			
		||||
	pub fn spawn<R>(&self, r: R) where
 | 
			
		||||
        R: IntoFuture<Item=(), Error=()> + Send + 'static,
 | 
			
		||||
        R::Future: 'static,
 | 
			
		||||
	{
 | 
			
		||||
		match self.inner {
 | 
			
		||||
			Mode::Tokio(ref remote) => remote.spawn(move |_| r),
 | 
			
		||||
			Mode::Sync => {
 | 
			
		||||
				let _= r.into_future().wait();
 | 
			
		||||
			},
 | 
			
		||||
			Mode::ThreadPerFuture => {
 | 
			
		||||
				thread::spawn(move || {
 | 
			
		||||
					let _= r.into_future().wait();
 | 
			
		||||
				});
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Spawn a new future returned by given closure.
 | 
			
		||||
	pub fn spawn_fn<F, R>(&self, f: F) where
 | 
			
		||||
		F: FnOnce(&Handle) -> R + Send + 'static,
 | 
			
		||||
        R: IntoFuture<Item=(), Error=()>,
 | 
			
		||||
        R::Future: 'static,
 | 
			
		||||
	{
 | 
			
		||||
		match self.inner {
 | 
			
		||||
			Mode::Tokio(ref remote) => remote.spawn(move |handle| f(handle)),
 | 
			
		||||
			Mode::Sync => {
 | 
			
		||||
				let mut core = tokio_core::reactor::Core::new().expect("Creating an event loop should not fail.");
 | 
			
		||||
				let handle = core.handle();
 | 
			
		||||
				let _ = core.run(f(&handle).into_future());
 | 
			
		||||
			},
 | 
			
		||||
			Mode::ThreadPerFuture => {
 | 
			
		||||
				thread::spawn(move || {
 | 
			
		||||
					let mut core = tokio_core::reactor::Core::new().expect("Creating an event loop should not fail.");
 | 
			
		||||
					let handle = core.handle();
 | 
			
		||||
					let _ = core.run(f(&handle).into_future());
 | 
			
		||||
				});
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Spawn a new future and wait for it or for a timeout to occur.
 | 
			
		||||
	pub fn spawn_with_timeout<F, R, T>(&self, f: F, duration: Duration, on_timeout: T) where
 | 
			
		||||
		T: FnOnce() -> () + Send + 'static,
 | 
			
		||||
		F: FnOnce(&Handle) -> R + Send + 'static,
 | 
			
		||||
		R: IntoFuture<Item=(), Error=()>,
 | 
			
		||||
		R::Future: 'static,
 | 
			
		||||
	{
 | 
			
		||||
		match self.inner {
 | 
			
		||||
			Mode::Tokio(ref remote) => remote.spawn(move |handle| {
 | 
			
		||||
				let future = f(handle).into_future();
 | 
			
		||||
				let timeout = Timeout::new(duration, handle).expect("Event loop is still up.");
 | 
			
		||||
				future.select(timeout.then(move |_| {
 | 
			
		||||
					on_timeout();
 | 
			
		||||
					Ok(())
 | 
			
		||||
				})).then(|_| Ok(()))
 | 
			
		||||
			}),
 | 
			
		||||
			Mode::Sync => {
 | 
			
		||||
				let mut core = tokio_core::reactor::Core::new().expect("Creating an event loop should not fail.");
 | 
			
		||||
				let handle = core.handle();
 | 
			
		||||
				let future = f(&handle).into_future();
 | 
			
		||||
				let timeout = Timeout::new(duration, &handle).expect("Event loop is still up.");
 | 
			
		||||
				let _: Result<(), ()> = core.run(future.select(timeout.then(move |_| {
 | 
			
		||||
					on_timeout();
 | 
			
		||||
					Ok(())
 | 
			
		||||
				})).then(|_| Ok(())));
 | 
			
		||||
			},
 | 
			
		||||
			Mode::ThreadPerFuture => {
 | 
			
		||||
				thread::spawn(move || {
 | 
			
		||||
					let mut core = tokio_core::reactor::Core::new().expect("Creating an event loop should not fail.");
 | 
			
		||||
					let handle = core.handle();
 | 
			
		||||
					let future = f(&handle).into_future();
 | 
			
		||||
					let timeout = Timeout::new(duration, &handle).expect("Event loop is still up.");
 | 
			
		||||
					let _: Result<(), ()> = core.run(future.select(timeout.then(move |_| {
 | 
			
		||||
						on_timeout();
 | 
			
		||||
						Ok(())
 | 
			
		||||
					})).then(|_| Ok(())));
 | 
			
		||||
				});
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A handle to running event loop. Dropping the handle will cause event loop to finish.
 | 
			
		||||
pub struct EventLoopHandle {
 | 
			
		||||
	close: Option<futures::Complete<()>>,
 | 
			
		||||
	handle: Option<thread::JoinHandle<()>>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<EventLoop> for EventLoopHandle {
 | 
			
		||||
	fn from(el: EventLoop) -> Self {
 | 
			
		||||
		el.handle
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Drop for EventLoopHandle {
 | 
			
		||||
	fn drop(&mut self) {
 | 
			
		||||
		self.close.take().map(|v| v.send(()));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl EventLoopHandle {
 | 
			
		||||
	/// Blocks current thread and waits until the event loop is finished.
 | 
			
		||||
	pub fn wait(mut self) -> thread::Result<()> {
 | 
			
		||||
		self.handle.take()
 | 
			
		||||
			.expect("Handle is taken only in `wait`, `wait` is consuming; qed").join()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Finishes this event loop.
 | 
			
		||||
	pub fn close(mut self) {
 | 
			
		||||
		let _ = self.close.take()
 | 
			
		||||
			.expect("Close is taken only in `close` and `drop`. `close` is consuming; qed")
 | 
			
		||||
			.send(());
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,11 +1,11 @@
 | 
			
		||||
[package]
 | 
			
		||||
description = "Parity Reactor"
 | 
			
		||||
description = "Parity Runtime"
 | 
			
		||||
homepage = "http://parity.io"
 | 
			
		||||
license = "GPL-3.0"
 | 
			
		||||
name = "parity-reactor"
 | 
			
		||||
name = "parity-runtime"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
authors = ["Parity Technologies <admin@parity.io>"]
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
futures = "0.1"
 | 
			
		||||
tokio-core = "0.1"
 | 
			
		||||
tokio = "~0.1.9"
 | 
			
		||||
							
								
								
									
										256
									
								
								util/runtime/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										256
									
								
								util/runtime/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,256 @@
 | 
			
		||||
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
 | 
			
		||||
// 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/>.
 | 
			
		||||
 | 
			
		||||
//! Tokio Runtime wrapper.
 | 
			
		||||
 | 
			
		||||
extern crate futures;
 | 
			
		||||
extern crate tokio;
 | 
			
		||||
 | 
			
		||||
use std::{fmt, thread};
 | 
			
		||||
use std::sync::mpsc;
 | 
			
		||||
use std::time::{Duration, Instant};
 | 
			
		||||
use futures::{future, Future, IntoFuture};
 | 
			
		||||
pub use tokio::timer::Delay;
 | 
			
		||||
pub use tokio::runtime::{Runtime as TokioRuntime, Builder as TokioRuntimeBuilder, TaskExecutor};
 | 
			
		||||
 | 
			
		||||
/// Runtime for futures.
 | 
			
		||||
///
 | 
			
		||||
/// Runs in a separate thread.
 | 
			
		||||
pub struct Runtime {
 | 
			
		||||
	executor: Executor,
 | 
			
		||||
	handle: RuntimeHandle,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Runtime {
 | 
			
		||||
	fn new(runtime_bldr: &mut TokioRuntimeBuilder) -> Self {
 | 
			
		||||
		let mut runtime = runtime_bldr
 | 
			
		||||
			.build()
 | 
			
		||||
			.expect("Building a Tokio runtime will only fail when mio components \
 | 
			
		||||
				cannot be initialized (catastrophic)");
 | 
			
		||||
		let (stop, stopped) = futures::oneshot();
 | 
			
		||||
		let (tx, rx) = mpsc::channel();
 | 
			
		||||
		let handle = thread::spawn(move || {
 | 
			
		||||
			tx.send(runtime.executor()).expect("Rx is blocking upper thread.");
 | 
			
		||||
			runtime.block_on(futures::empty().select(stopped).map(|_| ()).map_err(|_| ()))
 | 
			
		||||
				.expect("Tokio runtime should not have unhandled errors.");
 | 
			
		||||
		});
 | 
			
		||||
		let executor = rx.recv().expect("tx is transfered to a newly spawned thread.");
 | 
			
		||||
 | 
			
		||||
		Runtime {
 | 
			
		||||
			executor: Executor {
 | 
			
		||||
				inner: Mode::Tokio(executor),
 | 
			
		||||
			},
 | 
			
		||||
			handle: RuntimeHandle {
 | 
			
		||||
				close: Some(stop),
 | 
			
		||||
				handle: Some(handle),
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Spawns a new tokio runtime with a default thread count on a background
 | 
			
		||||
	/// thread and returns a `Runtime` which can be used to spawn tasks via
 | 
			
		||||
	/// its executor.
 | 
			
		||||
	pub fn with_default_thread_count() -> Self {
 | 
			
		||||
		let mut runtime_bldr = TokioRuntimeBuilder::new();
 | 
			
		||||
		Self::new(&mut runtime_bldr)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Spawns a new tokio runtime with a the specified thread count on a
 | 
			
		||||
	/// background thread and returns a `Runtime` which can be used to spawn
 | 
			
		||||
	/// tasks via its executor.
 | 
			
		||||
	pub fn with_thread_count(thread_count: usize) -> Self {
 | 
			
		||||
		let mut runtime_bldr = TokioRuntimeBuilder::new();
 | 
			
		||||
		runtime_bldr.core_threads(thread_count);
 | 
			
		||||
 | 
			
		||||
		Self::new(&mut runtime_bldr)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Returns this runtime raw executor.
 | 
			
		||||
	///
 | 
			
		||||
	/// Deprecated: Exists only to connect with current JSONRPC implementation.
 | 
			
		||||
	pub fn raw_executor(&self) -> TaskExecutor {
 | 
			
		||||
		if let Mode::Tokio(ref executor) = self.executor.inner {
 | 
			
		||||
			executor.clone()
 | 
			
		||||
		} else {
 | 
			
		||||
			panic!("Runtime is not initialized in Tokio mode.")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Returns runtime executor.
 | 
			
		||||
	pub fn executor(&self) -> Executor {
 | 
			
		||||
		self.executor.clone()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
enum Mode {
 | 
			
		||||
	Tokio(TaskExecutor),
 | 
			
		||||
	Sync,
 | 
			
		||||
	ThreadPerFuture,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Debug for Mode {
 | 
			
		||||
	fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
 | 
			
		||||
		use self::Mode::*;
 | 
			
		||||
 | 
			
		||||
		match *self {
 | 
			
		||||
			Tokio(_) => write!(fmt, "tokio"),
 | 
			
		||||
			Sync => write!(fmt, "synchronous"),
 | 
			
		||||
			ThreadPerFuture => write!(fmt, "thread per future"),
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Returns a future which runs `f` until `duration` has elapsed, at which
 | 
			
		||||
/// time `on_timeout` is run and the future resolves.
 | 
			
		||||
fn timeout<F, R, T>(f: F, duration: Duration, on_timeout: T)
 | 
			
		||||
	-> impl Future<Item = (), Error = ()> + Send + 'static
 | 
			
		||||
where
 | 
			
		||||
	T: FnOnce() -> () + Send + 'static,
 | 
			
		||||
	F: FnOnce() -> R + Send + 'static,
 | 
			
		||||
	R: IntoFuture<Item=(), Error=()> + Send + 'static,
 | 
			
		||||
	R::Future: Send + 'static,
 | 
			
		||||
{
 | 
			
		||||
	let future = future::lazy(f);
 | 
			
		||||
	let timeout = Delay::new(Instant::now() + duration)
 | 
			
		||||
		.then(move |_| {
 | 
			
		||||
			on_timeout();
 | 
			
		||||
			Ok(())
 | 
			
		||||
		});
 | 
			
		||||
	future.select(timeout).then(|_| Ok(()))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub struct Executor {
 | 
			
		||||
	inner: Mode,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Executor {
 | 
			
		||||
	/// Executor for existing runtime.
 | 
			
		||||
	///
 | 
			
		||||
	/// Deprecated: Exists only to connect with current JSONRPC implementation.
 | 
			
		||||
	pub fn new(executor: TaskExecutor) -> Self {
 | 
			
		||||
		Executor {
 | 
			
		||||
			inner: Mode::Tokio(executor),
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Synchronous executor, used mostly for tests.
 | 
			
		||||
	pub fn new_sync() -> Self {
 | 
			
		||||
		Executor {
 | 
			
		||||
			inner: Mode::Sync,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Spawns a new thread for each future (use only for tests).
 | 
			
		||||
	pub fn new_thread_per_future() -> Self {
 | 
			
		||||
		Executor {
 | 
			
		||||
			inner: Mode::ThreadPerFuture,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Spawn a future to this runtime
 | 
			
		||||
	pub fn spawn<R>(&self, r: R) where
 | 
			
		||||
		R: IntoFuture<Item=(), Error=()> + Send + 'static,
 | 
			
		||||
		R::Future: Send + 'static,
 | 
			
		||||
	{
 | 
			
		||||
		match self.inner {
 | 
			
		||||
			Mode::Tokio(ref executor) => executor.spawn(r.into_future()),
 | 
			
		||||
			Mode::Sync => {
 | 
			
		||||
				let _= r.into_future().wait();
 | 
			
		||||
			},
 | 
			
		||||
			Mode::ThreadPerFuture => {
 | 
			
		||||
				thread::spawn(move || {
 | 
			
		||||
					let _= r.into_future().wait();
 | 
			
		||||
				});
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Spawn a new future returned by given closure.
 | 
			
		||||
	pub fn spawn_fn<F, R>(&self, f: F) where
 | 
			
		||||
		F: FnOnce() -> R + Send + 'static,
 | 
			
		||||
		R: IntoFuture<Item=(), Error=()> + Send + 'static,
 | 
			
		||||
		R::Future: Send + 'static,
 | 
			
		||||
	{
 | 
			
		||||
		match self.inner {
 | 
			
		||||
			Mode::Tokio(ref executor) => executor.spawn(future::lazy(f)),
 | 
			
		||||
			Mode::Sync => {
 | 
			
		||||
				let _ = future::lazy(f).wait();
 | 
			
		||||
			},
 | 
			
		||||
			Mode::ThreadPerFuture => {
 | 
			
		||||
				thread::spawn(move || {
 | 
			
		||||
					let _= f().into_future().wait();
 | 
			
		||||
				});
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Spawn a new future and wait for it or for a timeout to occur.
 | 
			
		||||
	pub fn spawn_with_timeout<F, R, T>(&self, f: F, duration: Duration, on_timeout: T) where
 | 
			
		||||
		T: FnOnce() -> () + Send + 'static,
 | 
			
		||||
		F: FnOnce() -> R + Send + 'static,
 | 
			
		||||
		R: IntoFuture<Item=(), Error=()> + Send + 'static,
 | 
			
		||||
		R::Future: Send + 'static,
 | 
			
		||||
	{
 | 
			
		||||
		match self.inner {
 | 
			
		||||
			Mode::Tokio(ref executor) => {
 | 
			
		||||
				executor.spawn(timeout(f, duration, on_timeout))
 | 
			
		||||
			},
 | 
			
		||||
			Mode::Sync => {
 | 
			
		||||
				let _ = timeout(f, duration, on_timeout).wait();
 | 
			
		||||
			},
 | 
			
		||||
			Mode::ThreadPerFuture => {
 | 
			
		||||
				thread::spawn(move || {
 | 
			
		||||
					let _ = timeout(f, duration, on_timeout).wait();
 | 
			
		||||
				});
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A handle to a runtime. Dropping the handle will cause runtime to shutdown.
 | 
			
		||||
pub struct RuntimeHandle {
 | 
			
		||||
	close: Option<futures::Complete<()>>,
 | 
			
		||||
	handle: Option<thread::JoinHandle<()>>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<Runtime> for RuntimeHandle {
 | 
			
		||||
	fn from(el: Runtime) -> Self {
 | 
			
		||||
		el.handle
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Drop for RuntimeHandle {
 | 
			
		||||
	fn drop(&mut self) {
 | 
			
		||||
		self.close.take().map(|v| v.send(()));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl RuntimeHandle {
 | 
			
		||||
	/// Blocks current thread and waits until the runtime is finished.
 | 
			
		||||
	pub fn wait(mut self) -> thread::Result<()> {
 | 
			
		||||
		self.handle.take()
 | 
			
		||||
			.expect("Handle is taken only in `wait`, `wait` is consuming; qed").join()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Finishes this runtime.
 | 
			
		||||
	pub fn close(mut self) {
 | 
			
		||||
		let _ = self.close.take()
 | 
			
		||||
			.expect("Close is taken only in `close` and `drop`. `close` is consuming; qed")
 | 
			
		||||
			.send(());
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -9,7 +9,7 @@ bitflags = "0.9"
 | 
			
		||||
byteorder = "1.0.0"
 | 
			
		||||
ethereum-types = "0.4"
 | 
			
		||||
ethcore-network = { path = "../util/network" }
 | 
			
		||||
parity-crypto = "0.1"
 | 
			
		||||
parity-crypto = "0.2"
 | 
			
		||||
ethkey = { path = "../ethkey" }
 | 
			
		||||
hex = "0.2"
 | 
			
		||||
log = "0.4"
 | 
			
		||||
@ -25,6 +25,6 @@ slab = "0.3"
 | 
			
		||||
smallvec = "0.6"
 | 
			
		||||
tiny-keccak = "1.4"
 | 
			
		||||
 | 
			
		||||
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" }
 | 
			
		||||
jsonrpc-macros = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" }
 | 
			
		||||
jsonrpc-pubsub = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" }
 | 
			
		||||
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-2.2" }
 | 
			
		||||
jsonrpc-macros = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-2.2" }
 | 
			
		||||
jsonrpc-pubsub = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-2.2" }
 | 
			
		||||
 | 
			
		||||
@ -14,9 +14,9 @@ docopt = "0.8"
 | 
			
		||||
serde = "1.0"
 | 
			
		||||
serde_derive = "1.0"
 | 
			
		||||
panic_hook = { path = "../../util/panic_hook" }
 | 
			
		||||
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" }
 | 
			
		||||
jsonrpc-pubsub = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" }
 | 
			
		||||
jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" }
 | 
			
		||||
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-2.2" }
 | 
			
		||||
jsonrpc-pubsub = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-2.2" }
 | 
			
		||||
jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-2.2" }
 | 
			
		||||
log = "0.4"
 | 
			
		||||
 | 
			
		||||
[[bin]]
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user