Much nicer error pages
This commit is contained in:
parent
25e6a4e45f
commit
2789824a51
@ -98,13 +98,11 @@ impl<R: URLHint> AppFetcher<R> {
|
|||||||
},
|
},
|
||||||
// App is already being fetched
|
// App is already being fetched
|
||||||
Some(&mut ContentStatus::Fetching(_)) => {
|
Some(&mut ContentStatus::Fetching(_)) => {
|
||||||
(None, Box::new(ContentHandler::html(
|
(None, Box::new(ContentHandler::error_with_refresh(
|
||||||
StatusCode::ServiceUnavailable,
|
StatusCode::ServiceUnavailable,
|
||||||
format!(
|
"Download In Progress",
|
||||||
"<html><head>{}</head><body>{}</body></html>",
|
"This dapp is already being downloaded. Please wait...",
|
||||||
"<meta http-equiv=\"refresh\" content=\"1\">",
|
None,
|
||||||
"<h1>This dapp is already being downloaded.</h1><h2>Please wait...</h2>",
|
|
||||||
)
|
|
||||||
)) as Box<Handler>)
|
)) as Box<Handler>)
|
||||||
},
|
},
|
||||||
// We need to start fetching app
|
// We need to start fetching app
|
||||||
|
22
dapps/src/error_tpl.html
Normal file
22
dapps/src/error_tpl.html
Normal file
@ -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">
|
||||||
|
{version}
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -21,6 +21,8 @@ use hyper::{header, server, Decoder, Encoder, Next};
|
|||||||
use hyper::net::HttpStream;
|
use hyper::net::HttpStream;
|
||||||
use hyper::status::StatusCode;
|
use hyper::status::StatusCode;
|
||||||
|
|
||||||
|
use util::version;
|
||||||
|
|
||||||
pub struct ContentHandler {
|
pub struct ContentHandler {
|
||||||
code: StatusCode,
|
code: StatusCode,
|
||||||
content: String,
|
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 {
|
pub fn not_found(content: String, mimetype: String) -> Self {
|
||||||
ContentHandler {
|
ContentHandler {
|
||||||
code: StatusCode::NotFound,
|
code: StatusCode::NotFound,
|
||||||
@ -60,6 +53,28 @@ impl ContentHandler {
|
|||||||
Self::new(code, content, "text/html".into())
|
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 {
|
pub fn new(code: StatusCode, content: String, mimetype: String) -> Self {
|
||||||
ContentHandler {
|
ContentHandler {
|
||||||
code: code,
|
code: code,
|
||||||
|
@ -130,16 +130,20 @@ impl<H: ContentValidator> server::Handler<HttpStream> for ContentFetcherHandler<
|
|||||||
deadline: Instant::now() + Duration::from_secs(FETCH_TIMEOUT),
|
deadline: Instant::now() + Duration::from_secs(FETCH_TIMEOUT),
|
||||||
receiver: receiver,
|
receiver: receiver,
|
||||||
},
|
},
|
||||||
Err(e) => FetchState::Error(ContentHandler::html(
|
Err(e) => FetchState::Error(ContentHandler::error(
|
||||||
StatusCode::BadGateway,
|
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 remote server.",
|
||||||
|
Some(&format!("{}", e)),
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// or return error
|
// or return error
|
||||||
_ => FetchState::Error(ContentHandler::html(
|
_ => FetchState::Error(ContentHandler::error(
|
||||||
StatusCode::MethodNotAllowed,
|
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 };
|
} else { None };
|
||||||
@ -156,10 +160,12 @@ impl<H: ContentValidator> server::Handler<HttpStream> for ContentFetcherHandler<
|
|||||||
// Request may time out
|
// Request may time out
|
||||||
FetchState::InProgress { ref deadline, .. } if *deadline < Instant::now() => {
|
FetchState::InProgress { ref deadline, .. } if *deadline < Instant::now() => {
|
||||||
trace!(target: "dapps", "Fetching dapp failed because of timeout.");
|
trace!(target: "dapps", "Fetching dapp failed because of timeout.");
|
||||||
let timeout = ContentHandler::html(
|
let timeout = ContentHandler::error(
|
||||||
StatusCode::GatewayTimeout,
|
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);
|
Self::close_client(&mut self.client);
|
||||||
(Some(FetchState::Error(timeout)), Next::write())
|
(Some(FetchState::Error(timeout)), Next::write())
|
||||||
},
|
},
|
||||||
@ -175,9 +181,11 @@ impl<H: ContentValidator> server::Handler<HttpStream> for ContentFetcherHandler<
|
|||||||
let state = match self.dapp.validate_and_install(path.clone()) {
|
let state = match self.dapp.validate_and_install(path.clone()) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
trace!(target: "dapps", "Error while validating dapp: {:?}", e);
|
trace!(target: "dapps", "Error while validating dapp: {:?}", e);
|
||||||
FetchState::Error(ContentHandler::html(
|
FetchState::Error(ContentHandler::error(
|
||||||
StatusCode::BadGateway,
|
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)
|
Ok(manifest) => FetchState::Done(manifest)
|
||||||
@ -188,9 +196,11 @@ impl<H: ContentValidator> server::Handler<HttpStream> for ContentFetcherHandler<
|
|||||||
},
|
},
|
||||||
Ok(Err(e)) => {
|
Ok(Err(e)) => {
|
||||||
warn!(target: "dapps", "Unable to fetch new dapp: {:?}", e);
|
warn!(target: "dapps", "Unable to fetch new dapp: {:?}", e);
|
||||||
let error = ContentHandler::html(
|
let error = ContentHandler::error(
|
||||||
StatusCode::BadGateway,
|
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())
|
(Some(FetchState::Error(error)), Next::write())
|
||||||
},
|
},
|
||||||
|
@ -55,10 +55,9 @@ impl Authorization for HttpBasicAuth {
|
|||||||
|
|
||||||
match auth {
|
match auth {
|
||||||
Access::Denied => {
|
Access::Denied => {
|
||||||
Authorized::No(Box::new(ContentHandler::new(
|
Authorized::No(Box::new(ContentHandler::error(
|
||||||
status::StatusCode::Unauthorized,
|
status::StatusCode::Unauthorized,
|
||||||
"<h1>Unauthorized</h1>".into(),
|
"Unauthorized", "You need to provide valid credentials to access this page.", None
|
||||||
"text/html".into(),
|
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
Access::AuthRequired => {
|
Access::AuthRequired => {
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
|
|
||||||
use DAPPS_DOMAIN;
|
use DAPPS_DOMAIN;
|
||||||
use hyper::{server, header};
|
use hyper::{server, header, StatusCode};
|
||||||
use hyper::net::HttpStream;
|
use hyper::net::HttpStream;
|
||||||
|
|
||||||
use jsonrpc_http_server::{is_host_header_valid};
|
use jsonrpc_http_server::{is_host_header_valid};
|
||||||
@ -38,11 +38,9 @@ pub fn is_valid(request: &server::Request<HttpStream>, allowed_hosts: &[String],
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn host_invalid_response() -> Box<server::Handler<HttpStream> + Send> {
|
pub fn host_invalid_response() -> Box<server::Handler<HttpStream> + Send> {
|
||||||
Box::new(ContentHandler::forbidden(
|
Box::new(ContentHandler::error(StatusCode::Forbidden,
|
||||||
r#"
|
"Current host is disallowed",
|
||||||
<h1>Request with disallowed <code>Host</code> header has been blocked.</h1>
|
"You are trying to access your node using incorrect address.",
|
||||||
<p>Check the URL in your browser address bar.</p>
|
Some("Use allowed URL or specify different <code>hosts</code> CLI options.")
|
||||||
"#.into(),
|
|
||||||
"text/html".into()
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ use std::path::{PathBuf, Path};
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use jsonrpc_core::IoHandler;
|
use jsonrpc_core::IoHandler;
|
||||||
use util::H256;
|
use util::{H256, version};
|
||||||
|
|
||||||
#[cfg(feature = "ui")]
|
#[cfg(feature = "ui")]
|
||||||
mod signer {
|
mod signer {
|
||||||
@ -112,7 +112,12 @@ impl ws::Handler for Session {
|
|||||||
if !self.skip_origin_validation {
|
if !self.skip_origin_validation {
|
||||||
if !origin_is_allowed(&self.self_origin, origin) && !(origin.is_none() && origin_is_allowed(&self.self_origin, host)) {
|
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.");
|
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
|
// Check authorization
|
||||||
if !auth_is_valid(&self.authcodes_path, req.protocols()) {
|
if !auth_is_valid(&self.authcodes_path, req.protocols()) {
|
||||||
info!(target: "signer", "Unauthorized connection to Signer API blocked.");
|
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.");
|
let protocols = req.protocols().expect("Existence checked by authorization.");
|
||||||
@ -137,7 +142,7 @@ impl ws::Handler for Session {
|
|||||||
Ok(signer::handle(req.resource())
|
Ok(signer::handle(req.resource())
|
||||||
.map_or_else(
|
.map_or_else(
|
||||||
// return 404 not found
|
// 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
|
// or serve the file
|
||||||
|f| add_headers(ws::Response::ok(f.content.into()), &f.mime)
|
|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")
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user