diff --git a/dapps/src/apps/fetcher.rs b/dapps/src/apps/fetcher.rs index e31aae55d..6223fb5e8 100644 --- a/dapps/src/apps/fetcher.rs +++ b/dapps/src/apps/fetcher.rs @@ -98,13 +98,11 @@ impl AppFetcher { }, // App is already being fetched Some(&mut ContentStatus::Fetching(_)) => { - (None, Box::new(ContentHandler::html( + (None, Box::new(ContentHandler::error_with_refresh( StatusCode::ServiceUnavailable, - format!( - "{}{}", - "", - "

This dapp is already being downloaded.

Please wait...

", - ) + "Download In Progress", + "This dapp is already being downloaded. Please wait...", + None, )) as Box) }, // We need to start fetching app diff --git a/dapps/src/error_tpl.html b/dapps/src/error_tpl.html new file mode 100644 index 000000000..227764b9c --- /dev/null +++ b/dapps/src/error_tpl.html @@ -0,0 +1,22 @@ + + + + + + {meta} + {title} + + + +
+
+
+

{title}

+

{message}

+

{details}

+
+
+ {version} +
+ + diff --git a/dapps/src/handlers/content.rs b/dapps/src/handlers/content.rs index 092c417ac..f283fbb6a 100644 --- a/dapps/src/handlers/content.rs +++ b/dapps/src/handlers/content.rs @@ -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, @@ -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, @@ -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="", + message=message, + details=details.unwrap_or_else(|| ""), + version=version(), + )) + } + pub fn new(code: StatusCode, content: String, mimetype: String) -> Self { ContentHandler { code: code, diff --git a/dapps/src/handlers/fetch.rs b/dapps/src/handlers/fetch.rs index d4919562a..7cbb2537b 100644 --- a/dapps/src/handlers/fetch.rs +++ b/dapps/src/handlers/fetch.rs @@ -130,16 +130,20 @@ impl server::Handler 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!("

Error starting dapp download.

{}
", e), + "Unable To Start Dapp Download", + "Could not initialize download of the dapp. It might be a problem with remote server.", + Some(&format!("{}", e)), )), } }, // or return error - _ => FetchState::Error(ContentHandler::html( + _ => FetchState::Error(ContentHandler::error( StatusCode::MethodNotAllowed, - "

Only GET requests are allowed.

".into(), + "Method Not Allowed", + "Only GET requests are allowed.", + None, )), }) } else { None }; @@ -156,10 +160,12 @@ impl server::Handler 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!("

Could not fetch app bundle within {} seconds.

", 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()) }, @@ -175,9 +181,11 @@ impl server::Handler 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!("

Downloaded bundle does not contain valid app.

{:?}
", e), + "Invalid Dapp", + "Downloaded bundle does not contain a valid dapp.", + Some(&format!("{:?}", e)) )) }, Ok(manifest) => FetchState::Done(manifest) @@ -188,9 +196,11 @@ impl server::Handler for ContentFetcherHandler< }, Ok(Err(e)) => { warn!(target: "dapps", "Unable to fetch new dapp: {:?}", e); - let error = ContentHandler::html( + let error = ContentHandler::error( StatusCode::BadGateway, - "

There was an error when fetching the dapp.

".into(), + "Download Error", + "There was an error when fetching the dapp.", + Some(&format!("{:?}", e)), ); (Some(FetchState::Error(error)), Next::write()) }, diff --git a/dapps/src/router/auth.rs b/dapps/src/router/auth.rs index d18424a00..596796eed 100644 --- a/dapps/src/router/auth.rs +++ b/dapps/src/router/auth.rs @@ -55,10 +55,9 @@ impl Authorization for HttpBasicAuth { match auth { Access::Denied => { - Authorized::No(Box::new(ContentHandler::new( + Authorized::No(Box::new(ContentHandler::error( status::StatusCode::Unauthorized, - "

Unauthorized

".into(), - "text/html".into(), + "Unauthorized", "You need to provide valid credentials to access this page.", None ))) }, Access::AuthRequired => { diff --git a/dapps/src/router/host_validation.rs b/dapps/src/router/host_validation.rs index e0f974482..d1d651c5d 100644 --- a/dapps/src/router/host_validation.rs +++ b/dapps/src/router/host_validation.rs @@ -16,7 +16,7 @@ use DAPPS_DOMAIN; -use hyper::{server, header}; +use hyper::{server, header, StatusCode}; use hyper::net::HttpStream; use jsonrpc_http_server::{is_host_header_valid}; @@ -38,11 +38,9 @@ pub fn is_valid(request: &server::Request, allowed_hosts: &[String], } pub fn host_invalid_response() -> Box + Send> { - Box::new(ContentHandler::forbidden( - r#" -

Request with disallowed Host header has been blocked.

-

Check the URL in your browser address bar.

- "#.into(), - "text/html".into() + Box::new(ContentHandler::error(StatusCode::Forbidden, + "Current host is disallowed", + "You are trying to access your node using incorrect address.", + Some("Use allowed URL or specify different hosts CLI options.") )) } diff --git a/signer/src/ws_server/session.rs b/signer/src/ws_server/session.rs index e7abea833..34097c24d 100644 --- a/signer/src/ws_server/session.rs +++ b/signer/src/ws_server/session.rs @@ -22,7 +22,7 @@ use std::path::{PathBuf, Path}; use std::sync::Arc; use std::str::FromStr; use jsonrpc_core::IoHandler; -use util::H256; +use util::{H256, version}; #[cfg(feature = "ui")] mod signer { @@ -112,7 +112,12 @@ impl ws::Handler for Session { if !self.skip_origin_validation { if !origin_is_allowed(&self.self_origin, origin) && !(origin.is_none() && origin_is_allowed(&self.self_origin, host)) { warn!(target: "signer", "Blocked connection to Signer API from untrusted origin."); - return Ok(ws::Response::forbidden(format!("You are not allowed to access system ui. Use: http://{}", self.self_origin))); + return Ok(error( + ErrorType::Forbidden, + "URL Blocked", + "You are not allowed to access Trusted Signer using this URL.", + Some(&format!("Use: http://{}", self.self_origin)), + )); } } @@ -121,7 +126,7 @@ impl ws::Handler for Session { // Check authorization if !auth_is_valid(&self.authcodes_path, req.protocols()) { info!(target: "signer", "Unauthorized connection to Signer API blocked."); - return Ok(ws::Response::forbidden("You are not authorized.".into())); + return Ok(error(ErrorType::Forbidden, "Not Authorized", "Request to this API was not authorized.", None)); } let protocols = req.protocols().expect("Existence checked by authorization."); @@ -137,7 +142,7 @@ impl ws::Handler for Session { Ok(signer::handle(req.resource()) .map_or_else( // return 404 not found - || add_headers(ws::Response::not_found("Not found".into()), "text/plain"), + || error(ErrorType::NotFound, "Not found", "Requested file was not found.", None), // or serve the file |f| add_headers(ws::Response::ok(f.content.into()), &f.mime) )) @@ -185,3 +190,24 @@ impl ws::Factory for Factory { } } } + +enum ErrorType { + NotFound, + Forbidden, +} + +fn error(error: ErrorType, title: &str, message: &str, details: Option<&str>) -> ws::Response { + let content = format!( + include_str!("../../../dapps/src/error_tpl.html"), + title=title, + meta="", + message=message, + details=details.unwrap_or(""), + version=version(), + ); + let res = match error { + ErrorType::NotFound => ws::Response::not_found(content), + ErrorType::Forbidden => ws::Response::forbidden(content), + }; + add_headers(res, "text/html") +}