Use hyper 0.11 in ethcore-miner and improvements in parity-reactor (#8335)

* parity-reactor: Pass over Handle in spawning fn to allow normal tokio ops

* Allow fetch to work with arbitrary requests

* typo: Fix missing handler closure

* miner, work_notify: use fetch and parity-reactor

* Fix work_notify pushing in parity CLI
This commit is contained in:
Wei Tang 2018-04-10 19:51:29 +08:00 committed by Marek Kotewicz
parent 86446d713a
commit 692cd10d4a
20 changed files with 302 additions and 221 deletions

72
Cargo.lock generated
View File

@ -250,15 +250,6 @@ dependencies = [
"custom_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cookie"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "crossbeam"
version = "0.3.2"
@ -660,16 +651,19 @@ dependencies = [
"ethcore-transaction 0.1.0",
"ethereum-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethkey 0.3.0",
"fetch 0.1.0",
"futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
"heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.10.0-a.0 (git+https://github.com/paritytech/hyper)",
"hyper 0.11.24 (registry+https://github.com/rust-lang/crates.io-index)",
"keccak-hash 0.1.0",
"linked-hash-map 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-reactor 0.1.0",
"parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"table 0.1.0",
"transient-hashmap 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -753,6 +747,7 @@ dependencies = [
"serde_derive 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"tiny-keccak 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -1041,6 +1036,7 @@ dependencies = [
name = "fetch"
version = "0.1.0"
dependencies = [
"bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-timer 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.11.24 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1204,25 +1200,6 @@ name = "httparse"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "hyper"
version = "0.10.0-a.0"
source = "git+https://github.com/paritytech/hyper#da10f69a4924cd44e98a8d1f9562bd7534d13dcc"
dependencies = [
"cookie 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rotor 0.6.3 (git+https://github.com/tailhook/rotor)",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
"spmc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"vecio 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "hyper"
version = "0.10.13"
@ -2634,11 +2611,6 @@ dependencies = [
"quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quick-error"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "quote"
version = "0.5.1"
@ -2807,18 +2779,6 @@ dependencies = [
"snappy-sys 0.1.0 (git+https://github.com/paritytech/rust-snappy)",
]
[[package]]
name = "rotor"
version = "0.6.3"
source = "git+https://github.com/tailhook/rotor#80ce2e4cd82fdc7f88bb2d737407fa5106799790"
dependencies = [
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)",
"quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rpassword"
version = "1.0.2"
@ -3054,11 +3014,6 @@ dependencies = [
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "spmc"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "stable_deref_trait"
version = "1.0.0"
@ -3612,15 +3567,6 @@ name = "vec_map"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "vecio"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "vergen"
version = "0.1.1"
@ -3831,7 +3777,6 @@ dependencies = [
"checksum cid 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d85ee025368e69063c420cbb2ed9f852cb03a5e69b73be021e65726ce03585b6"
"checksum clap 2.29.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8f4a2b3bb7ef3c672d7c13d15613211d5a6976b6892c598b0fcb5d40765f19c2"
"checksum conv 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "78ff10625fd0ac447827aa30ea8b861fead473bb60aeb73af6c1c58caf0d1299"
"checksum cookie 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d53b80dde876f47f03cda35303e368a79b91c70b0d65ecba5fd5280944a08591"
"checksum crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24ce9782d4d5c53674646a6a4c1863a21a8fc0cb649b3c94dfc16e45071dea19"
"checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3"
"checksum crossbeam-deque 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c1bdc73742c36f7f35ebcda81dbb33a7e0d33757d03a06d9ddca762712ec5ea2"
@ -3878,7 +3823,6 @@ dependencies = [
"checksum hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6a22814455d41612f41161581c2883c0c6a1c41852729b17d5ed88f01e153aa"
"checksum hidapi 0.3.1 (git+https://github.com/paritytech/hidapi-rs)" = "<none>"
"checksum httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "af2f2dd97457e8fb1ae7c5a420db346af389926e36f43768b96f101546b04a07"
"checksum hyper 0.10.0-a.0 (git+https://github.com/paritytech/hyper)" = "<none>"
"checksum hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)" = "368cb56b2740ebf4230520e2b90ebb0461e69034d85d1945febd9b3971426db2"
"checksum hyper 0.11.24 (registry+https://github.com/rust-lang/crates.io-index)" = "df4dd5dae401458087396b6db7fabc4d6760aa456a5fa8e92bda549f39cae661"
"checksum hyper-rustls 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d6cdc1751771a14b8175764394f025e309a28c825ed9eaf97fa62bb831dc8c5"
@ -3973,7 +3917,6 @@ dependencies = [
"checksum quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18c45c4854d6d1cf5d531db97c75880feb91c958b0720f4ec1057135fec358b3"
"checksum quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9e25fa23c044c1803f43ca59c98dac608976dd04ce799411edd58ece776d4"
"checksum quasi_macros 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29cec87bc2816766d7e4168302d505dd06b0a825aed41b00633d296e922e02dd"
"checksum quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eda5fe9b71976e62bc81b781206aaa076401769b2143379d3eb2118388babac4"
"checksum quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7b0ff51282f28dc1b53fd154298feaa2e77c5ea0dba68e1fd8b03b72fbe13d2a"
"checksum rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)" = "512870020642bb8c221bf68baa1b2573da814f6ccfe5c9699b1c303047abe9b1"
"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5"
@ -3988,7 +3931,6 @@ dependencies = [
"checksum ring 0.12.1 (git+https://github.com/paritytech/ring)" = "<none>"
"checksum rocksdb 0.4.5 (git+https://github.com/paritytech/rust-rocksdb)" = "<none>"
"checksum rocksdb-sys 0.3.0 (git+https://github.com/paritytech/rust-rocksdb)" = "<none>"
"checksum rotor 0.6.3 (git+https://github.com/tailhook/rotor)" = "<none>"
"checksum rpassword 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b273c91bd242ca03ad6d71c143b6f17a48790e61f21a6c78568fa2b6774a24a4"
"checksum rprompt 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1601f32bc5858aae3cbfa1c645c96c4d820cc5c16be0194f089560c00b6eb625"
"checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a"
@ -4020,7 +3962,6 @@ dependencies = [
"checksum smallvec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8fcd03faf178110ab0334d74ca9631d77f94c8c11cc77fcb59538abf0025695d"
"checksum snappy 0.1.0 (git+https://github.com/paritytech/rust-snappy)" = "<none>"
"checksum snappy-sys 0.1.0 (git+https://github.com/paritytech/rust-snappy)" = "<none>"
"checksum spmc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cd1f11d1fb5fd41834e55ce0b85a186efbf2f2afd9fdb09e2c8d72f9bff1ad1a"
"checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b"
"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694"
"checksum subtle 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc7f6353c2ee5407358d063a14cccc1630804527090a6fb5a9489ce4924280fb"
@ -4075,7 +4016,6 @@ dependencies = [
"checksum url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eeb819346883532a271eb626deb43c4a1bb4c4dd47c519bd78137c3e72a4fe27"
"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
"checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c"
"checksum vecio 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0795a11576d29ae80525a3fda315bf7b534f8feb9d34101e5fe63fb95bb2fd24"
"checksum vergen 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c3365f36c57e5df714a34be40902b27a992eeddb9996eca52d0584611cf885d"
"checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"

View File

@ -53,7 +53,7 @@ impl NodeHealth {
let tx = Arc::new(Mutex::new(Some(tx)));
let tx2 = tx.clone();
self.remote.spawn_with_timeout(
move || time.then(move |result| {
move |_| time.then(move |result| {
let _ = tx.lock().take().expect(PROOF).send(Ok(result));
Ok(())
}),

View File

@ -19,8 +19,8 @@ use std::sync::{atomic, mpsc, Arc};
use parking_lot::Mutex;
use hyper;
use futures::{self, Future};
use fetch::{self, Fetch, Url, Method};
use futures::{self, future, Future};
use fetch::{self, Fetch, Url, Request, Abort};
pub struct FetchControl {
sender: mpsc::Sender<()>,
@ -97,9 +97,9 @@ impl FakeFetch {
impl Fetch for FakeFetch {
type Result = Box<Future<Item = fetch::Response, Error = fetch::Error> + Send>;
fn fetch(&self, url: &str, _method: Method, abort: fetch::Abort) -> Self::Result {
let u = Url::parse(url).unwrap();
self.requested.lock().push(url.into());
fn fetch(&self, request: Request, abort: fetch::Abort) -> Self::Result {
let u = request.url().clone();
self.requested.lock().push(u.as_str().into());
let manual = self.manual.clone();
let response = self.response.clone();
@ -115,4 +115,20 @@ impl Fetch for FakeFetch {
Box::new(rx.map_err(|_| fetch::Error::Aborted))
}
fn get(&self, url: &str, abort: Abort) -> Self::Result {
let url: Url = match url.parse() {
Ok(u) => u,
Err(e) => return Box::new(future::err(e.into()))
};
self.fetch(Request::get(url), abort)
}
fn post(&self, url: &str, abort: Abort) -> Self::Result {
let url: Url = match url.parse() {
Ok(u) => u,
Err(e) => return Box::new(future::err(e.into()))
};
self.fetch(Request::post(url), abort)
}
}

View File

@ -34,3 +34,4 @@ serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
tiny-keccak = "1.3"
url = "1"

View File

@ -17,6 +17,7 @@
//! Encryption providers.
use std::io::Read;
use std::str::FromStr;
use std::iter::repeat;
use std::time::{Instant, Duration};
use std::collections::HashMap;
@ -28,9 +29,10 @@ use ethjson;
use ethkey::{Signature, Public};
use ethcrypto;
use futures::Future;
use fetch::{Fetch, Client as FetchClient, Method, BodyReader};
use fetch::{Fetch, Client as FetchClient, Method, BodyReader, Request};
use bytes::{Bytes, ToPretty};
use error::{Error, ErrorKind};
use url::Url;
use super::find_account_password;
/// Initialization vector length.
@ -128,7 +130,8 @@ impl SecretStoreEncryptor {
Method::Get
};
let response = self.client.fetch(&url, method, Default::default()).wait()
let url = Url::from_str(&url).map_err(|e| ErrorKind::Encrypt(e.to_string()))?;
let response = self.client.fetch(Request::new(url, method), Default::default()).wait()
.map_err(|e| ErrorKind::Encrypt(e.to_string()))?;
if response.is_not_found() {

View File

@ -41,6 +41,7 @@ extern crate keccak_hash as hash;
extern crate parking_lot;
extern crate patricia_trie as trie;
extern crate rlp;
extern crate url;
extern crate rustc_hex;
#[macro_use]
extern crate log;
@ -673,4 +674,3 @@ impl ChainNotify for Provider {
}
}
}

View File

@ -36,7 +36,7 @@ use ethcore_miner::transaction_queue::{
TransactionOrigin,
};
use futures_cpupool::CpuPool;
use ethcore_miner::work_notify::{WorkPoster, NotifyWork};
use ethcore_miner::work_notify::NotifyWork;
use miner::service_transaction_checker::ServiceTransactionChecker;
use miner::{MinerService, MinerStatus};
use price_info::fetch::Client as FetchClient;
@ -104,8 +104,6 @@ pub enum Banning {
/// Configures the behaviour of the miner.
#[derive(Debug, PartialEq)]
pub struct MinerOptions {
/// URLs to notify when there is new work.
pub new_work_notify: Vec<String>,
/// Force the miner to reseal, even when nobody has asked for work.
pub force_sealing: bool,
/// Reseal on receipt of new external transactions.
@ -147,7 +145,6 @@ pub struct MinerOptions {
impl Default for MinerOptions {
fn default() -> Self {
MinerOptions {
new_work_notify: vec![],
force_sealing: false,
reseal_on_external_tx: false,
reseal_on_own_tx: true,
@ -305,10 +302,7 @@ impl Miner {
),
};
let notifiers: Vec<Box<NotifyWork>> = match options.new_work_notify.is_empty() {
true => Vec::new(),
false => vec![Box::new(WorkPoster::new(&options.new_work_notify))],
};
let notifiers: Vec<Box<NotifyWork>> = Vec::new();
let service_transaction_action = match options.refuse_service_transactions {
true => ServiceTransactionAction::Refuse,
@ -324,7 +318,6 @@ impl Miner {
sealing_work: Mutex::new(SealingWork{
queue: UsingQueue::new(options.work_queue_size),
enabled: options.force_sealing
|| !options.new_work_notify.is_empty()
|| spec.engine.seals_internally().is_some()
}),
gas_range_target: RwLock::new((U256::zero(), U256::zero())),
@ -1364,7 +1357,6 @@ mod tests {
fn miner() -> Miner {
Arc::try_unwrap(Miner::new(
MinerOptions {
new_work_notify: Vec::new(),
force_sealing: false,
reseal_on_external_tx: false,
reseal_on_own_tx: true,

View File

@ -199,7 +199,7 @@ mod tests {
use parking_lot::Mutex;
use futures::future;
use futures_cpupool::CpuPool;
use fetch::{self, Fetch, Url, Method};
use fetch::{self, Fetch, Url, Request};
use parity_reactor::Remote;
use urlhint::tests::{FakeRegistrar, URLHINT};
use super::{Error, Client, HashFetch, random_temp_path};
@ -214,15 +214,31 @@ mod tests {
impl Fetch for FakeFetch {
type Result = future::Ok<fetch::Response, fetch::Error>;
fn fetch(&self, url: &str, _method: Method, abort: fetch::Abort) -> Self::Result {
assert_eq!(url, "https://parity.io/assets/images/ethcore-black-horizontal.png");
let u = Url::parse(url).unwrap();
fn fetch(&self, request: Request, abort: fetch::Abort) -> Self::Result {
assert_eq!(request.url().as_str(), "https://parity.io/assets/images/ethcore-black-horizontal.png");
let u = request.url().clone();
future::ok(if self.return_success {
fetch::client::Response::new(u, hyper::Response::new().with_body(&b"result"[..]), abort)
} else {
fetch::client::Response::new(u, hyper::Response::new().with_status(StatusCode::NotFound), abort)
})
}
fn get(&self, url: &str, abort: fetch::Abort) -> Self::Result {
let url: Url = match url.parse() {
Ok(u) => u,
Err(e) => return future::err(e.into())
};
self.fetch(Request::get(url), abort)
}
fn post(&self, url: &str, abort: fetch::Abort) -> Self::Result {
let url: Url = match url.parse() {
Ok(u) => u,
Err(e) => return future::err(e.into())
};
self.fetch(Request::post(url), abort)
}
}
fn registrar() -> FakeRegistrar {
@ -311,4 +327,3 @@ mod tests {
assert!(result.is_ok(), "Should return path, got: {:?}", result);
}
}

View File

@ -7,9 +7,6 @@ version = "1.11.0"
authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
# TODO [ToDr] Rewrite using reqwest
hyper = { git = "https://github.com/paritytech/hyper", default-features = false }
common-types = { path = "../ethcore/types" }
ethabi = "5.1"
ethabi-contract = "5.0"
@ -27,3 +24,7 @@ parking_lot = "0.5"
rustc-hex = "1.0"
table = { path = "../util/table" }
transient-hashmap = "0.4"
fetch = { path = "../util/fetch" }
parity-reactor = { path = "../util/reactor" }
url = "1"
hyper = "0.11"

View File

@ -17,19 +17,20 @@
//! Sends HTTP notifications to a list of URLs every time new work is available.
extern crate ethash;
extern crate fetch;
extern crate parity_reactor;
extern crate url;
extern crate hyper;
use self::hyper::header::ContentType;
use self::hyper::method::Method;
use self::hyper::client::{Request, Response, Client};
use self::hyper::{Next, Url};
use self::hyper::net::HttpStream;
use self::fetch::{Fetch, Request, Client as FetchClient, Method};
use self::parity_reactor::Remote;
use self::ethash::SeedHashCompute;
use self::url::Url;
use self::hyper::header::ContentType;
use std::io::Write;
use ethereum_types::{H256, U256};
use parking_lot::Mutex;
use futures::Future;
/// Trait for notifying about new mining work
pub trait NotifyWork : Send + Sync {
@ -40,13 +41,14 @@ pub trait NotifyWork : Send + Sync {
/// POSTs info about new work to given urls.
pub struct WorkPoster {
urls: Vec<Url>,
client: Mutex<Client<PostHandler>>,
client: FetchClient,
remote: Remote,
seed_compute: Mutex<SeedHashCompute>,
}
impl WorkPoster {
/// Create new `WorkPoster`.
pub fn new(urls: &[String]) -> Self {
pub fn new(urls: &[String], fetch: FetchClient, remote: Remote) -> Self {
let urls = urls.into_iter().filter_map(|u| {
match Url::parse(u) {
Ok(url) => Some(url),
@ -56,20 +58,13 @@ impl WorkPoster {
}
}
}).collect();
let client = WorkPoster::create_client();
WorkPoster {
client: Mutex::new(client),
client: fetch,
remote: remote,
urls: urls,
seed_compute: Mutex::new(SeedHashCompute::new()),
}
}
fn create_client() -> Client<PostHandler> {
Client::<PostHandler>::configure()
.keep_alive(true)
.build()
.expect("Error creating HTTP client")
}
}
/// Convert an Ethash difficulty to the target boundary. Basically just `f(x) = 2^256 / x`.
@ -91,51 +86,16 @@ impl NotifyWork for WorkPoster {
r#"{{ "result": ["0x{:x}","0x{:x}","0x{:x}","0x{:x}"] }}"#,
pow_hash, seed_hash, target, number
);
let mut client = self.client.lock();
for u in &self.urls {
if let Err(e) = client.request(u.clone(), PostHandler { body: body.clone() }) {
let u = u.clone();
self.remote.spawn(self.client.fetch(
Request::new(u.clone(), Method::Post)
.with_header(ContentType::json())
.with_body(body.clone()), Default::default()
).map_err(move |e| {
warn!("Error sending HTTP notification to {} : {}, retrying", u, e);
// TODO: remove this once https://github.com/hyperium/hyper/issues/848 is fixed
*client = WorkPoster::create_client();
if let Err(e) = client.request(u.clone(), PostHandler { body: body.clone() }) {
warn!("Error sending HTTP notification to {} : {}", u, e);
}).map(|_| ()));
}
}
}
}
}
struct PostHandler {
body: String,
}
impl hyper::client::Handler<HttpStream> for PostHandler {
fn on_request(&mut self, request: &mut Request) -> Next {
request.set_method(Method::Post);
request.headers_mut().set(ContentType::json());
Next::write()
}
fn on_request_writable(&mut self, encoder: &mut hyper::Encoder<HttpStream>) -> Next {
if let Err(e) = encoder.write_all(self.body.as_bytes()) {
trace!("Error posting work data: {}", e);
}
encoder.close();
Next::read()
}
fn on_response(&mut self, _response: Response) -> Next {
Next::end()
}
fn on_response_readable(&mut self, _decoder: &mut hyper::Decoder<HttpStream>) -> Next {
Next::end()
}
fn on_error(&mut self, err: hyper::Error) -> Next {
trace!("Error posting work data: {}", err);
Next::end()
}
}

View File

@ -354,6 +354,7 @@ impl Configuration {
daemon: daemon,
logger_config: logger_config.clone(),
miner_options: self.miner_options()?,
work_notify: self.work_notify(),
gas_price_percentile: self.args.arg_gas_price_percentile,
ntp_servers: self.ntp_servers(),
ws_conf: ws_conf,
@ -537,7 +538,6 @@ impl Configuration {
let reseal = self.args.arg_reseal_on_txs.parse::<ResealPolicy>()?;
let options = MinerOptions {
new_work_notify: self.work_notify(),
force_sealing: self.args.flag_force_sealing,
reseal_on_external_tx: reseal.external,
reseal_on_own_tx: reseal.own,
@ -1516,6 +1516,7 @@ mod tests {
no_hardcoded_sync: false,
no_persistent_txqueue: false,
whisper: Default::default(),
work_notify: Vec::new(),
};
expected.secretstore_conf.enabled = cfg!(feature = "secretstore");
expected.secretstore_conf.http_enabled = cfg!(feature = "secretstore");

View File

@ -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.remote.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()

View File

@ -35,6 +35,7 @@ use ethcore::verification::queue::VerifierSettings;
use ethcore_logger::{Config as LogConfig, RotatingLogger};
use ethcore_service::ClientService;
use sync::{self, SyncConfig};
use miner::work_notify::WorkPoster;
use fdlimit::raise_fd_limit;
use futures_cpupool::CpuPool;
use hash_fetch::{self, fetch};
@ -137,6 +138,7 @@ pub struct RunCmd {
pub no_persistent_txqueue: bool,
pub whisper: ::whisper::Config,
pub no_hardcoded_sync: bool,
pub work_notify: Vec<String>,
}
pub fn open_ui(ws_conf: &rpc::WsConfiguration, ui_conf: &rpc::UiConfiguration, logger_config: &LogConfig) -> Result<(), String> {
@ -549,6 +551,9 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
let cpu_pool = CpuPool::new(4);
// spin up event loop
let event_loop = EventLoop::spawn();
// fetch service
let fetch = fetch::Client::new().map_err(|e| format!("Error starting fetch client: {:?}", e))?;
@ -561,6 +566,9 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
miner.set_extra_data(cmd.miner_extras.extra_data);
miner.set_minimal_gas_price(initial_min_gas_price);
miner.recalibrate_minimal_gas_price();
if !cmd.work_notify.is_empty() {
miner.push_notifier(Box::new(WorkPoster::new(&cmd.work_notify, fetch.clone(), event_loop.remote())));
}
let engine_signer = cmd.miner_extras.engine_signer;
if engine_signer != Default::default() {
@ -730,9 +738,6 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
chain_notify.start();
}
// spin up event loop
let event_loop = EventLoop::spawn();
let contract_client = Arc::new(::dapps::FullRegistrar::new(client.clone()));
// the updater service

View File

@ -140,7 +140,7 @@ mod test {
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use fetch;
use fetch::{Fetch, Url, Method};
use fetch::{Fetch, Url, Request};
use futures_cpupool::CpuPool;
use futures::future::{self, FutureResult};
use Client;
@ -158,9 +158,9 @@ mod test {
impl Fetch for FakeFetch {
type Result = FutureResult<fetch::Response, fetch::Error>;
fn fetch(&self, url: &str, _method: Method, abort: fetch::Abort) -> Self::Result {
assert_eq!(url, "https://api.etherscan.io/api?module=stats&action=ethprice");
let u = Url::parse(url).unwrap();
fn fetch(&self, request: Request, abort: fetch::Abort) -> Self::Result {
assert_eq!(request.url().as_str(), "https://api.etherscan.io/api?module=stats&action=ethprice");
let u = request.url().clone();
let mut val = self.1.lock();
*val = *val + 1;
if let Some(ref response) = self.0 {
@ -171,6 +171,22 @@ mod test {
future::ok(fetch::client::Response::new(u, r, abort))
}
}
fn get(&self, url: &str, abort: fetch::Abort) -> Self::Result {
let url: Url = match url.parse() {
Ok(u) => u,
Err(e) => return future::err(e.into())
};
self.fetch(Request::get(url), abort)
}
fn post(&self, url: &str, abort: fetch::Abort) -> Self::Result {
let url: Url = match url.parse() {
Ok(u) => u,
Err(e) => return future::err(e.into())
};
self.fetch(Request::post(url), abort)
}
}
fn price_info_ok(response: &str) -> Client<FakeFetch> {

View File

@ -60,7 +60,6 @@ fn sync_provider() -> Arc<TestSyncProvider> {
fn miner_service(spec: &Spec, accounts: Arc<AccountProvider>) -> Arc<Miner> {
Miner::new(
MinerOptions {
new_work_notify: vec![],
force_sealing: true,
reseal_on_external_tx: true,
reseal_on_own_tx: true,

View File

@ -17,8 +17,10 @@
//! Test implementation of fetch client.
use std::thread;
use std::boxed::Box;
use jsonrpc_core::futures::{self, Future};
use fetch::{self, Fetch, Url, Method};
use fetch::{self, Fetch, Url, Request};
use futures::future;
use hyper;
/// Test implementation of fetcher. Will always return the same file.
@ -28,8 +30,8 @@ pub struct TestFetch;
impl Fetch for TestFetch {
type Result = Box<Future<Item = fetch::Response, Error = fetch::Error> + Send + 'static>;
fn fetch(&self, url: &str, _method: Method, abort: fetch::Abort) -> Self::Result {
let u = Url::parse(url).unwrap();
fn fetch(&self, request: Request, abort: fetch::Abort) -> Self::Result {
let u = request.url().clone();
let (tx, rx) = futures::oneshot();
thread::spawn(move || {
let r = hyper::Response::new().with_body(&b"Some content"[..]);
@ -38,4 +40,20 @@ impl Fetch for TestFetch {
Box::new(rx.map_err(|_| fetch::Error::Aborted))
}
fn get(&self, url: &str, abort: fetch::Abort) -> Self::Result {
let url: Url = match url.parse() {
Ok(u) => u,
Err(e) => return Box::new(future::err(e.into()))
};
self.fetch(Request::get(url), abort)
}
fn post(&self, url: &str, abort: fetch::Abort) -> Self::Result {
let url: Url = match url.parse() {
Ok(u) => u,
Err(e) => return Box::new(future::err(e.into()))
};
self.fetch(Request::post(url), abort)
}
}

View File

@ -14,6 +14,7 @@ hyper-rustls = "0.11"
log = "0.4"
tokio-core = "0.1"
url = "1"
bytes = "0.4"
[features]
default = []

View File

@ -20,7 +20,7 @@ use futures::{self, Future, Async, Sink, Stream};
use futures_timer::FutureExt;
use hyper::header::{UserAgent, Location, ContentLength, ContentType};
use hyper::mime::Mime;
use hyper::{self, Request, Method, StatusCode};
use hyper::{self, Method, StatusCode};
use hyper_rustls;
use std;
use std::cmp::min;
@ -32,6 +32,7 @@ use std::time::Duration;
use std::{io, fmt};
use tokio_core::reactor;
use url::{self, Url};
use bytes::Bytes;
const MAX_SIZE: usize = 64 * 1024 * 1024;
const MAX_SECS: Duration = Duration::from_secs(5);
@ -120,22 +121,18 @@ pub trait Fetch: Clone + Send + Sync + 'static {
type Result: Future<Item=Response, Error=Error> + Send + 'static;
/// Make a request to given URL
fn fetch(&self, url: &str, method: Method, abort: Abort) -> Self::Result;
fn fetch(&self, request: Request, abort: Abort) -> Self::Result;
/// Get content from some URL.
fn get(&self, url: &str, abort: Abort) -> Self::Result {
self.fetch(url, Method::Get, abort)
}
fn get(&self, url: &str, abort: Abort) -> Self::Result;
/// Post content to some URL.
fn post(&self, url: &str, abort: Abort) -> Self::Result {
self.fetch(url, Method::Post, abort)
}
fn post(&self, url: &str, abort: Abort) -> Self::Result;
}
type TxResponse = oneshot::Sender<Result<Response, Error>>;
type TxStartup = std::sync::mpsc::SyncSender<Result<(), io::Error>>;
type ChanItem = Option<(Url, Method, Abort, TxResponse)>;
type ChanItem = Option<(Request, Abort, TxResponse)>;
/// An implementation of `Fetch` using a `hyper` client.
// Due to the `Send` bound of `Fetch` we spawn a background thread for
@ -213,29 +210,37 @@ impl Client {
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"))
.for_each(|(url, method, abort, sender)|
.for_each(|(request, abort, sender)|
{
trace!(target: "fetch", "new request to {}", url);
trace!(target: "fetch", "new request to {}", request.url());
if abort.is_aborted() {
return future::ok(sender.send(Err(Error::Aborted)).unwrap_or(()))
}
let ini = (hyper.clone(), url, method, abort, 0);
let fut = future::loop_fn(ini, |(client, url, method, abort, redirects)| {
let url2 = url.clone();
let ini = (hyper.clone(), request, abort, 0);
let fut = future::loop_fn(ini, |(client, request, abort, redirects)| {
let request2 = request.clone();
let url2 = request2.url().clone();
let abort2 = abort.clone();
client.request(build_request(&url, method.clone()))
client.request(request.into())
.map(move |resp| Response::new(url2, resp, abort2))
.from_err()
.and_then(move |resp| {
if abort.is_aborted() {
debug!(target: "fetch", "fetch of {} aborted", url);
debug!(target: "fetch", "fetch of {} aborted", request2.url());
return Err(Error::Aborted)
}
if let Some(next_url) = redirect_location(url, &resp) {
if let Some((next_url, preserve_method)) = redirect_location(request2.url().clone(), &resp) {
if redirects >= abort.max_redirects() {
return Err(Error::TooManyRedirects)
}
Ok(Loop::Continue((client, next_url, method, abort, redirects + 1)))
let request = if preserve_method {
let mut request2 = request2.clone();
request2.set_url(next_url);
request2
} else {
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) {
@ -267,19 +272,15 @@ impl Client {
impl Fetch for Client {
type Result = Box<Future<Item=Response, Error=Error> + Send>;
fn fetch(&self, url: &str, method: Method, abort: Abort) -> Self::Result {
debug!(target: "fetch", "fetching: {:?}", url);
fn fetch(&self, request: Request, abort: Abort) -> Self::Result {
debug!(target: "fetch", "fetching: {:?}", request.url());
if abort.is_aborted() {
return Box::new(future::err(Error::Aborted))
}
let url: Url = match url.parse() {
Ok(u) => u,
Err(e) => return Box::new(future::err(e.into()))
};
let (tx_res, rx_res) = oneshot::channel();
let maxdur = abort.max_duration();
let sender = self.core.clone();
let future = sender.send(Some((url.clone(), method, abort, tx_res)))
let future = sender.send(Some((request, abort, tx_res)))
.map_err(|e| {
error!(target: "fetch", "failed to schedule request: {}", e);
Error::BackgroundThreadDead
@ -297,11 +298,33 @@ impl Fetch for Client {
});
Box::new(future)
}
/// Get content from some URL.
fn get(&self, url: &str, abort: Abort) -> Self::Result {
let url: Url = match url.parse() {
Ok(u) => u,
Err(e) => return Box::new(future::err(e.into()))
};
self.fetch(Request::get(url), abort)
}
// Extract redirect location from response.
fn redirect_location(u: Url, r: &Response) -> Option<Url> {
/// Post content to some URL.
fn post(&self, url: &str, abort: Abort) -> Self::Result {
let url: Url = match url.parse() {
Ok(u) => u,
Err(e) => return Box::new(future::err(e.into()))
};
self.fetch(Request::post(url), abort)
}
}
// 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,
_ => false,
};
match r.status() {
MovedPermanently
| PermanentRedirect
@ -309,7 +332,7 @@ fn redirect_location(u: Url, r: &Response) -> Option<Url> {
| Found
| SeeOther => {
if let Some(loc) = r.headers.get::<Location>() {
u.join(loc).ok()
u.join(loc).ok().map(|url| (url, preserve_method))
} else {
None
}
@ -318,12 +341,84 @@ fn redirect_location(u: Url, r: &Response) -> Option<Url> {
}
}
// Build a simple request for the given Url and method
fn build_request(u: &Url, method: Method) -> hyper::Request {
let uri = u.as_ref().parse().expect("Every valid URL is aso a URI.");
let mut rq = Request::new(method, uri);
rq.headers_mut().set(UserAgent::new("Parity Fetch Neo"));
rq
/// A wrapper for hyper::Request using Url and with methods.
#[derive(Debug, Clone)]
pub struct Request {
url: Url,
method: Method,
headers: hyper::Headers,
body: Bytes,
}
impl Request {
/// Create a new request, with given url and method.
pub fn new(url: Url, method: Method) -> Request {
Request {
url, method,
headers: hyper::Headers::new(),
body: Default::default(),
}
}
/// Create a new GET request.
pub fn get(url: Url) -> Request {
Request::new(url, Method::Get)
}
/// Create a new empty POST request.
pub fn post(url: Url) -> Request {
Request::new(url, Method::Post)
}
/// Read the url.
pub fn url(&self) -> &Url {
&self.url
}
/// Read the request headers.
pub fn headers(&self) -> &hyper::Headers {
&self.headers
}
/// Get a mutable reference to the headers.
pub fn headers_mut(&mut self) -> &mut hyper::Headers {
&mut self.headers
}
/// Set the body of the request.
pub fn set_body<T: Into<Bytes>>(&mut self, body: T) {
self.body = body.into();
}
/// Set the url of the request.
pub fn set_url(&mut self, url: Url) {
self.url = url;
}
/// 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);
self
}
/// Consume self, and return it with the body.
pub fn with_body<T: Into<Bytes>>(mut self, body: T) -> Self {
self.set_body(body);
self
}
}
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
}
}
/// An HTTP response.
@ -527,7 +622,7 @@ mod test {
use futures::future;
use futures::sync::mpsc;
use futures_timer::Delay;
use hyper::{StatusCode, Method};
use hyper::StatusCode;
use hyper::server::{Http, Request, Response, Service};
use std;
use std::io::Read;
@ -539,7 +634,7 @@ mod test {
fn it_should_fetch() {
let server = TestServer::run();
let client = Client::new().unwrap();
let future = client.fetch(&format!("http://{}?123", server.addr()), Method::Get, Default::default());
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();
@ -551,7 +646,7 @@ mod test {
let server = TestServer::run();
let client = Client::new().unwrap();
let abort = Abort::default().with_max_duration(Duration::from_secs(1));
match client.fetch(&format!("http://{}/delay?3", server.addr()), Method::Get, abort).wait() {
match client.get(&format!("http://{}/delay?3", server.addr()), abort).wait() {
Err(Error::Timeout) => {}
other => panic!("expected timeout, got {:?}", other)
}
@ -562,7 +657,7 @@ mod test {
let server = TestServer::run();
let client = Client::new().unwrap();
let abort = Abort::default();
let future = client.fetch(&format!("http://{}/redirect?http://{}/", server.addr(), server.addr()), Method::Get, abort);
let future = client.get(&format!("http://{}/redirect?http://{}/", server.addr(), server.addr()), abort);
assert!(future.wait().unwrap().is_success())
}
@ -571,7 +666,7 @@ mod test {
let server = TestServer::run();
let client = Client::new().unwrap();
let abort = Abort::default().with_max_redirects(4);
let future = client.fetch(&format!("http://{}/redirect?/", server.addr()), Method::Get, abort);
let future = client.get(&format!("http://{}/redirect?/", server.addr()), abort);
assert!(future.wait().unwrap().is_success())
}
@ -580,7 +675,7 @@ mod test {
let server = TestServer::run();
let client = Client::new().unwrap();
let abort = Abort::default().with_max_redirects(3);
match client.fetch(&format!("http://{}/loop", server.addr()), Method::Get, abort).wait() {
match client.get(&format!("http://{}/loop", server.addr()), abort).wait() {
Err(Error::TooManyRedirects) => {}
other => panic!("expected too many redirects error, got {:?}", other)
}
@ -591,7 +686,7 @@ mod test {
let server = TestServer::run();
let client = Client::new().unwrap();
let abort = Abort::default();
let future = client.fetch(&format!("http://{}?abcdefghijklmnopqrstuvwxyz", server.addr()), Method::Get, abort);
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")
@ -602,7 +697,7 @@ mod test {
let server = TestServer::run();
let client = Client::new().unwrap();
let abort = Abort::default().with_max_size(3);
let resp = client.fetch(&format!("http://{}/?1234", server.addr()), Method::Get, abort).wait().unwrap();
let resp = client.get(&format!("http://{}/?1234", server.addr()), abort).wait().unwrap();
assert!(resp.is_success());
match resp.concat2().wait() {
Err(Error::SizeLimit) => {}
@ -615,7 +710,7 @@ mod test {
let server = TestServer::run();
let client = Client::new().unwrap();
let abort = Abort::default().with_max_size(3);
let resp = client.fetch(&format!("http://{}/?1234", server.addr()), Method::Get, abort).wait().unwrap();
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);

View File

@ -30,11 +30,11 @@ extern crate hyper_rustls;
extern crate tokio_core;
extern crate url;
extern crate bytes;
/// Fetch client implementation.
pub mod client;
pub use url::Url;
pub use self::client::{Client, Fetch, Error, Response, Abort, BodyReader};
pub use self::client::{Client, Fetch, Error, Response, Request, Abort, BodyReader};
pub use hyper::Method;

View File

@ -24,7 +24,7 @@ use std::{fmt, thread};
use std::sync::mpsc;
use std::time::Duration;
use futures::{Future, IntoFuture};
pub use tokio_core::reactor::{Remote as TokioRemote, Timeout};
pub use tokio_core::reactor::{Remote as TokioRemote, Handle, Timeout};
/// Event Loop for futures.
/// Wrapper around `tokio::reactor::Core`.
@ -143,18 +143,22 @@ impl Remote {
/// Spawn a new future returned by given closure.
pub fn spawn_fn<F, R>(&self, f: F) where
F: FnOnce() -> R + 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 |_| f()),
Mode::Tokio(ref remote) => remote.spawn(move |handle| f(handle)),
Mode::Sync => {
let _ = f().into_future().wait();
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 _= f().into_future().wait();
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());
});
},
}
@ -163,13 +167,13 @@ impl Remote {
/// 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,
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().into_future();
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();
@ -177,11 +181,25 @@ impl Remote {
})).then(|_| Ok(()))
}),
Mode::Sync => {
let _ = f().into_future().wait();
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 _ = f().into_future().wait();
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(())));
});
},
}