Fetching dispatcher (HTTP, HTTPS)

This commit is contained in:
Tomasz Drwięga
2016-08-30 11:40:59 +02:00
parent 25fc919913
commit 61879ef144
8 changed files with 244 additions and 79 deletions

View File

@@ -24,6 +24,7 @@ zip = { version = "0.1", default-features = false }
ethabi = "0.2.1"
ethcore-rpc = { path = "../rpc" }
ethcore-util = { path = "../util" }
https-fetch = { path = "../util/https-fetch" }
parity-dapps = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4" }
# List of apps
parity-dapps-status = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4" }
@@ -36,7 +37,7 @@ clippy = { version = "0.0.85", optional = true}
serde_codegen = { version = "0.7.0", optional = true }
[features]
default = ["serde_codegen", "extra-dapps"]
default = ["serde_codegen", "extra-dapps", "https-fetch/ca-github-only"]
extra-dapps = ["parity-dapps-wallet"]
nightly = ["serde_macros"]
dev = ["clippy", "ethcore-rpc/dev", "ethcore-util/dev"]

View File

@@ -27,6 +27,8 @@ use hyper::client::{Request, Response, DefaultTransport as HttpStream};
use hyper::header::Connection;
use hyper::{self, Decoder, Encoder, Next};
use super::FetchError;
#[derive(Debug)]
pub enum Error {
NotStarted,
@@ -35,7 +37,7 @@ pub enum Error {
HyperError(hyper::Error),
}
pub type FetchResult = Result<PathBuf, Error>;
pub type FetchResult = Result<PathBuf, FetchError>;
pub type OnDone = Box<Fn() + Send>;
pub struct Fetch {
@@ -54,7 +56,7 @@ impl fmt::Debug for Fetch {
impl Drop for Fetch {
fn drop(&mut self) {
let res = self.result.take().unwrap_or(Err(Error::NotStarted));
let res = self.result.take().unwrap_or(Err(Error::NotStarted.into()));
// Remove file if there was an error
if res.is_err() {
if let Some(file) = self.file.take() {
@@ -98,7 +100,7 @@ impl hyper::client::Handler<HttpStream> for Fetch {
fn on_response(&mut self, res: Response) -> Next {
if *res.status() != StatusCode::Ok {
self.result = Some(Err(Error::UnexpectedStatus(*res.status())));
self.result = Some(Err(Error::UnexpectedStatus(*res.status()).into()));
return Next::end();
}
@@ -110,7 +112,7 @@ impl hyper::client::Handler<HttpStream> for Fetch {
read()
},
Err(err) => {
self.result = Some(Err(Error::IoError(err)));
self.result = Some(Err(Error::IoError(err).into()));
Next::end()
},
}
@@ -123,7 +125,7 @@ impl hyper::client::Handler<HttpStream> for Fetch {
Err(e) => match e.kind() {
io::ErrorKind::WouldBlock => Next::read(),
_ => {
self.result = Some(Err(Error::IoError(e)));
self.result = Some(Err(Error::IoError(e).into()));
Next::end()
}
}
@@ -131,7 +133,7 @@ impl hyper::client::Handler<HttpStream> for Fetch {
}
fn on_error(&mut self, err: hyper::Error) -> Next {
self.result = Some(Err(Error::HyperError(err)));
self.result = Some(Err(Error::HyperError(err).into()));
Next::remove()
}
}

View File

@@ -16,7 +16,97 @@
//! Hyper Client Handlers
mod fetch_file;
pub mod fetch_file;
use std::env;
use std::sync::mpsc;
use std::path::PathBuf;
use hyper;
use https_fetch as https;
use random_filename;
use self::fetch_file::{Fetch, Error as HttpFetchError};
pub type FetchResult = Result<PathBuf, FetchError>;
#[derive(Debug)]
pub enum FetchError {
InvalidUrl,
Http(HttpFetchError),
Https(https::FetchError),
Other(String),
}
impl From<HttpFetchError> for FetchError {
fn from(e: HttpFetchError) -> Self {
FetchError::Http(e)
}
}
pub struct Client {
http_client: hyper::Client<Fetch>,
https_client: https::Client,
}
impl Client {
pub fn new() -> Self {
Client {
http_client: hyper::Client::new().expect("Unable to initialize http client."),
https_client: https::Client::new().expect("Unable to initialize https client."),
}
}
pub fn close(mut self) {
self.http_client.close();
self.https_client.close();
}
pub fn request(&mut self, url: String, on_done: Box<Fn() + Send>) -> Result<mpsc::Receiver<FetchResult>, FetchError> {
let is_https = url.starts_with("https://");
let url = try!(url.parse().map_err(|_| FetchError::InvalidUrl));
trace!(target: "dapps", "Fetching from: {:?}", url);
if is_https {
let url = try!(Self::convert_url(url));
let (tx, rx) = mpsc::channel();
let temp_path = Self::temp_path();
let res = self.https_client.fetch_to_file(url, temp_path.clone(), move |result| {
let res = tx.send(
result.map(|_| temp_path).map_err(FetchError::Https)
);
if let Err(_) = res {
warn!("Fetch finished, but no one was listening");
}
on_done();
});
match res {
Ok(_) => Ok(rx),
Err(e) => Err(FetchError::Other(format!("{:?}", e))),
}
} else {
let (tx, rx) = mpsc::channel();
let res = self.http_client.request(url, Fetch::new(tx, on_done));
match res {
Ok(_) => Ok(rx),
Err(e) => Err(FetchError::Other(format!("{:?}", e))),
}
}
}
fn convert_url(url: hyper::Url) -> Result<https::Url, FetchError> {
let host = format!("{}", try!(url.host().ok_or(FetchError::InvalidUrl)));
let port = try!(url.port_or_known_default().ok_or(FetchError::InvalidUrl));
https::Url::new(&host, port, url.path()).map_err(|_| FetchError::InvalidUrl)
}
fn temp_path() -> PathBuf {
let mut dir = env::temp_dir();
dir.push(random_filename());
dir
}
}
pub use self::fetch_file::{Fetch, FetchResult, OnDone};

View File

@@ -21,12 +21,12 @@ use std::path::PathBuf;
use std::sync::mpsc;
use std::time::{Instant, Duration};
use hyper::{header, server, Decoder, Encoder, Next, Method, Control, Client};
use hyper::{header, server, Decoder, Encoder, Next, Method, Control};
use hyper::net::HttpStream;
use hyper::status::StatusCode;
use handlers::ContentHandler;
use handlers::client::{Fetch, FetchResult};
use handlers::client::{Client, FetchResult};
use apps::redirection_address;
use apps::urlhint::GithubApp;
use apps::manifest::Manifest;
@@ -53,7 +53,7 @@ pub trait DappHandler {
pub struct AppFetcherHandler<H: DappHandler> {
control: Option<Control>,
status: FetchState,
client: Option<Client<Fetch>>,
client: Option<Client>,
using_dapps_domains: bool,
dapp: H,
}
@@ -76,7 +76,7 @@ impl<H: DappHandler> AppFetcherHandler<H> {
using_dapps_domains: bool,
handler: H) -> Self {
let client = Client::new().expect("Failed to create a Client");
let client = Client::new();
AppFetcherHandler {
control: Some(control),
client: Some(client),
@@ -86,7 +86,7 @@ impl<H: DappHandler> AppFetcherHandler<H> {
}
}
fn close_client(client: &mut Option<Client<Fetch>>) {
fn close_client(client: &mut Option<Client>) {
client.take()
.expect("After client is closed we are going into write, hence we can never close it again")
.close();
@@ -94,20 +94,12 @@ impl<H: DappHandler> AppFetcherHandler<H> {
// TODO [todr] https support
fn fetch_app(client: &mut Client<Fetch>, app: &GithubApp, control: Control) -> Result<mpsc::Receiver<FetchResult>, String> {
let url = try!(app.url().parse().map_err(|e| format!("{:?}", e)));
trace!(target: "dapps", "Fetching from: {:?}", url);
let (tx, rx) = mpsc::channel();
let res = client.request(url, Fetch::new(tx, Box::new(move || {
fn fetch_app(client: &mut Client, app: &GithubApp, control: Control) -> Result<mpsc::Receiver<FetchResult>, String> {
client.request(app.url(), Box::new(move || {
trace!(target: "dapps", "Fetching finished.");
// Ignoring control errors
let _ = control.ready(Next::read());
})));
match res {
Ok(_) => Ok(rx),
Err(e) => Err(format!("{:?}", e)),
}
})).map_err(|e| format!("{:?}", e))
}
}

View File

@@ -58,6 +58,7 @@ extern crate jsonrpc_http_server;
extern crate mime_guess;
extern crate rustc_serialize;
extern crate parity_dapps;
extern crate https_fetch;
extern crate ethcore_rpc;
extern crate ethcore_util as util;