Fetching dispatcher (HTTP, HTTPS)
This commit is contained in:
@@ -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"]
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user