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

Commit

Permalink
Merge pull request #2033 from ethcore/dapps-sync
Browse files Browse the repository at this point in the history
Nice error pages for Dapps & Signer
  • Loading branch information
debris authored Sep 5, 2016
2 parents 4e466f0 + e2377dd commit 9655920
Show file tree
Hide file tree
Showing 30 changed files with 539 additions and 167 deletions.
12 changes: 7 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions dapps/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ serde_macros = { version = "0.8", optional = true }
zip = { version = "0.1", default-features = false }
ethabi = "0.2.2"
linked-hash-map = "0.3"
ethcore-devtools = { path = "../devtools" }
ethcore-rpc = { path = "../rpc" }
ethcore-util = { path = "../util" }
https-fetch = { path = "../util/https-fetch" }
Expand Down
89 changes: 59 additions & 30 deletions dapps/src/apps/fetcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use hyper;
use hyper::status::StatusCode;

use random_filename;
use SyncStatus;
use util::{Mutex, H256};
use util::sha3::sha3;
use page::LocalPageEndpoint;
Expand All @@ -44,6 +45,7 @@ const MAX_CACHED_DAPPS: usize = 10;
pub struct AppFetcher<R: URLHint = URLHintContract> {
dapps_path: PathBuf,
resolver: R,
sync: Arc<SyncStatus>,
dapps: Arc<Mutex<ContentCache>>,
}

Expand All @@ -56,13 +58,14 @@ impl<R: URLHint> Drop for AppFetcher<R> {

impl<R: URLHint> AppFetcher<R> {

pub fn new(resolver: R) -> Self {
pub fn new(resolver: R, sync_status: Arc<SyncStatus>) -> Self {
let mut dapps_path = env::temp_dir();
dapps_path.push(random_filename());

AppFetcher {
dapps_path: dapps_path,
resolver: resolver,
sync: sync_status,
dapps: Arc::new(Mutex::new(ContentCache::default())),
}
}
Expand All @@ -74,21 +77,36 @@ impl<R: URLHint> AppFetcher<R> {

pub fn contains(&self, app_id: &str) -> bool {
let mut dapps = self.dapps.lock();
match dapps.get(app_id) {
// Check if we already have the app
Some(_) => true,
// fallback to resolver
None => match app_id.from_hex() {
Ok(app_id) => self.resolver.resolve(app_id).is_some(),
_ => false,
},
// Check if we already have the app
if dapps.get(app_id).is_some() {
return true;
}
// fallback to resolver
if let Ok(app_id) = app_id.from_hex() {
// if app_id is valid, but we are syncing always return true.
if self.sync.is_major_syncing() {
return true;
}
// else try to resolve the app_id
self.resolver.resolve(app_id).is_some()
} else {
false
}
}

pub fn to_async_handler(&self, path: EndpointPath, control: hyper::Control) -> Box<Handler> {
let mut dapps = self.dapps.lock();
let app_id = path.app_id.clone();

if self.sync.is_major_syncing() {
return Box::new(ContentHandler::error(
StatusCode::ServiceUnavailable,
"Sync In Progress",
"Your node is still syncing. We cannot resolve any content before it's fully synced.",
Some("<a href=\"javascript:window.location.reload()\">Refresh</a>")
));
}

let (new_status, handler) = {
let status = dapps.get(&app_id);
match status {
Expand All @@ -98,32 +116,42 @@ impl<R: URLHint> AppFetcher<R> {
},
// App is already being fetched
Some(&mut ContentStatus::Fetching(_)) => {
(None, Box::new(ContentHandler::html(
(None, Box::new(ContentHandler::error_with_refresh(
StatusCode::ServiceUnavailable,
format!(
"<html><head>{}</head><body>{}</body></html>",
"<meta http-equiv=\"refresh\" content=\"1\">",
"<h1>This dapp is already being downloaded.</h1><h2>Please wait...</h2>",
)
"Download In Progress",
"This dapp is already being downloaded. Please wait...",
None,
)) as Box<Handler>)
},
// We need to start fetching app
None => {
let app_hex = app_id.from_hex().expect("to_handler is called only when `contains` returns true.");
let app = self.resolver.resolve(app_hex).expect("to_handler is called only when `contains` returns true.");
let abort = Arc::new(AtomicBool::new(false));

(Some(ContentStatus::Fetching(abort.clone())), Box::new(ContentFetcherHandler::new(
app,
abort,
control,
path.using_dapps_domains,
DappInstaller {
dapp_id: app_id.clone(),
dapps_path: self.dapps_path.clone(),
dapps: self.dapps.clone(),
}
)) as Box<Handler>)
let app = self.resolver.resolve(app_hex);

if let Some(app) = app {
let abort = Arc::new(AtomicBool::new(false));

(Some(ContentStatus::Fetching(abort.clone())), Box::new(ContentFetcherHandler::new(
app,
abort,
control,
path.using_dapps_domains,
DappInstaller {
dapp_id: app_id.clone(),
dapps_path: self.dapps_path.clone(),
dapps: self.dapps.clone(),
}
)) as Box<Handler>)
} else {
// This may happen when sync status changes in between
// `contains` and `to_handler`
(None, Box::new(ContentHandler::error(
StatusCode::NotFound,
"Resource Not Found",
"Requested resource was not found.",
None
)) as Box<Handler>)
}
},
}
};
Expand Down Expand Up @@ -294,6 +322,7 @@ impl ContentValidator for DappInstaller {
#[cfg(test)]
mod tests {
use std::env;
use std::sync::Arc;
use util::Bytes;
use endpoint::EndpointInfo;
use page::LocalPageEndpoint;
Expand All @@ -312,7 +341,7 @@ mod tests {
fn should_true_if_contains_the_app() {
// given
let path = env::temp_dir();
let fetcher = AppFetcher::new(FakeResolver);
let fetcher = AppFetcher::new(FakeResolver, Arc::new(|| false));
let handler = LocalPageEndpoint::new(path, EndpointInfo {
name: "fake".into(),
description: "".into(),
Expand Down
22 changes: 22 additions & 0 deletions dapps/src/error_tpl.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
{meta}
<title>{title}</title>
<link rel="stylesheet" href="/parity-utils/styles.css">
</head>
<body>
<div class="parity-navbar">
</div>
<div class="parity-box">
<h1>{title}</h1>
<h3>{message}</h3>
<p><code>{details}</code></p>
</div>
<div class="parity-status">
<small>{version}</small>
</div>
</body>
</html>
33 changes: 24 additions & 9 deletions dapps/src/handlers/content.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ use hyper::{header, server, Decoder, Encoder, Next};
use hyper::net::HttpStream;
use hyper::status::StatusCode;

use util::version;

pub struct ContentHandler {
code: StatusCode,
content: String,
Expand All @@ -38,15 +40,6 @@ impl ContentHandler {
}
}

pub fn forbidden(content: String, mimetype: String) -> Self {
ContentHandler {
code: StatusCode::Forbidden,
content: content,
mimetype: mimetype,
write_pos: 0
}
}

pub fn not_found(content: String, mimetype: String) -> Self {
ContentHandler {
code: StatusCode::NotFound,
Expand All @@ -60,6 +53,28 @@ impl ContentHandler {
Self::new(code, content, "text/html".into())
}

pub fn error(code: StatusCode, title: &str, message: &str, details: Option<&str>) -> Self {
Self::html(code, format!(
include_str!("../error_tpl.html"),
title=title,
meta="",
message=message,
details=details.unwrap_or_else(|| ""),
version=version(),
))
}

pub fn error_with_refresh(code: StatusCode, title: &str, message: &str, details: Option<&str>) -> Self {
Self::html(code, format!(
include_str!("../error_tpl.html"),
title=title,
meta="<meta http-equiv=\"refresh\" content=\"1\">",
message=message,
details=details.unwrap_or_else(|| ""),
version=version(),
))
}

pub fn new(code: StatusCode, content: String, mimetype: String) -> Self {
ContentHandler {
code: code,
Expand Down
32 changes: 21 additions & 11 deletions dapps/src/handlers/fetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,16 +121,20 @@ impl<H: ContentValidator> server::Handler<HttpStream> for ContentFetcherHandler<
deadline: Instant::now() + Duration::from_secs(FETCH_TIMEOUT),
receiver: receiver,
},
Err(e) => FetchState::Error(ContentHandler::html(
Err(e) => FetchState::Error(ContentHandler::error(
StatusCode::BadGateway,
format!("<h1>Error starting dapp download.</h1><pre>{}</pre>", e),
"Unable To Start Dapp Download",
"Could not initialize download of the dapp. It might be a problem with the remote server.",
Some(&format!("{}", e)),
)),
}
},
// or return error
_ => FetchState::Error(ContentHandler::html(
_ => FetchState::Error(ContentHandler::error(
StatusCode::MethodNotAllowed,
"<h1>Only <code>GET</code> requests are allowed.</h1>".into(),
"Method Not Allowed",
"Only <code>GET</code> requests are allowed.",
None,
)),
})
} else { None };
Expand All @@ -147,10 +151,12 @@ impl<H: ContentValidator> server::Handler<HttpStream> for ContentFetcherHandler<
// Request may time out
FetchState::InProgress { ref deadline, .. } if *deadline < Instant::now() => {
trace!(target: "dapps", "Fetching dapp failed because of timeout.");
let timeout = ContentHandler::html(
let timeout = ContentHandler::error(
StatusCode::GatewayTimeout,
format!("<h1>Could not fetch app bundle within {} seconds.</h1>", FETCH_TIMEOUT),
);
"Download Timeout",
&format!("Could not fetch dapp bundle within {} seconds.", FETCH_TIMEOUT),
None
);
Self::close_client(&mut self.client);
(Some(FetchState::Error(timeout)), Next::write())
},
Expand All @@ -166,9 +172,11 @@ impl<H: ContentValidator> server::Handler<HttpStream> for ContentFetcherHandler<
let state = match self.dapp.validate_and_install(path.clone()) {
Err(e) => {
trace!(target: "dapps", "Error while validating dapp: {:?}", e);
FetchState::Error(ContentHandler::html(
FetchState::Error(ContentHandler::error(
StatusCode::BadGateway,
format!("<h1>Downloaded bundle does not contain valid app.</h1><pre>{}</pre>", e),
"Invalid Dapp",
"Downloaded bundle does not contain a valid dapp.",
Some(&format!("{:?}", e))
))
},
Ok(manifest) => FetchState::Done(manifest)
Expand All @@ -180,9 +188,11 @@ impl<H: ContentValidator> server::Handler<HttpStream> for ContentFetcherHandler<
},
Ok(Err(e)) => {
warn!(target: "dapps", "Unable to fetch new dapp: {:?}", e);
let error = ContentHandler::html(
let error = ContentHandler::error(
StatusCode::BadGateway,
"<h1>There was an error when fetching the dapp.</h1>".into(),
"Download Error",
"There was an error when fetching the dapp.",
Some(&format!("{:?}", e)),
);
(Some(FetchState::Error(error)), Next::write())
},
Expand Down
Loading

0 comments on commit 9655920

Please sign in to comment.