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
|
||||
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
|
||||
|
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::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="<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,
|
||||
|
@ -130,16 +130,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 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 };
|
||||
@ -156,9 +160,11 @@ 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())
|
||||
@ -175,9 +181,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)
|
||||
@ -188,9 +196,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())
|
||||
},
|
||||
|
@ -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,
|
||||
"<h1>Unauthorized</h1>".into(),
|
||||
"text/html".into(),
|
||||
"Unauthorized", "You need to provide valid credentials to access this page.", None
|
||||
)))
|
||||
},
|
||||
Access::AuthRequired => {
|
||||
|
@ -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<HttpStream>, allowed_hosts: &[String],
|
||||
}
|
||||
|
||||
pub fn host_invalid_response() -> Box<server::Handler<HttpStream> + Send> {
|
||||
Box::new(ContentHandler::forbidden(
|
||||
r#"
|
||||
<h1>Request with disallowed <code>Host</code> header has been blocked.</h1>
|
||||
<p>Check the URL in your browser address bar.</p>
|
||||
"#.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 <code>hosts</code> CLI options.")
|
||||
))
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user