diff --git a/dapps/build.rs b/dapps/build.rs
index 2bef33059..0776c03ec 100644
--- a/dapps/build.rs
+++ b/dapps/build.rs
@@ -25,8 +25,8 @@ mod inner {
pub fn main() {
let out_dir = env::var_os("OUT_DIR").unwrap();
- let src = Path::new("./src/api/mod.rs.in");
- let dst = Path::new(&out_dir).join("mod.rs");
+ let src = Path::new("./src/api/types.rs.in");
+ let dst = Path::new(&out_dir).join("types.rs");
let mut registry = syntex::Registry::new();
diff --git a/dapps/src/api/api.rs b/dapps/src/api/api.rs
index 95b01d442..ab3632074 100644
--- a/dapps/src/api/api.rs
+++ b/dapps/src/api/api.rs
@@ -15,38 +15,16 @@
// along with Parity. If not, see .
use std::sync::Arc;
-use endpoint::{Endpoint, Endpoints, EndpointInfo, Handler, EndpointPath};
-
-use api::response::as_json;
+use endpoint::{Endpoint, Endpoints, Handler, EndpointPath};
+use api::types::{App, ApiError};
+use api::response::{as_json, as_json_error};
+use hyper::{server, net, Decoder, Encoder, Next};
+#[derive(Clone)]
pub struct RestApi {
endpoints: Arc,
}
-#[derive(Debug, PartialEq, Serialize, Deserialize)]
-pub struct App {
- pub id: String,
- pub name: String,
- pub description: String,
- pub version: String,
- pub author: String,
- #[serde(rename="iconUrl")]
- pub icon_url: String,
-}
-
-impl App {
- fn from_info(id: &str, info: &EndpointInfo) -> Self {
- App {
- id: id.to_owned(),
- name: info.name.to_owned(),
- description: info.description.to_owned(),
- version: info.version.to_owned(),
- author: info.author.to_owned(),
- icon_url: info.icon_url.to_owned(),
- }
- }
-}
-
impl RestApi {
pub fn new(endpoints: Arc) -> Box {
Box::new(RestApi {
@@ -63,7 +41,39 @@ impl RestApi {
impl Endpoint for RestApi {
fn to_handler(&self, _path: EndpointPath) -> Box {
- as_json(&self.list_apps())
+ Box::new(RestApiRouter {
+ api: self.clone(),
+ handler: as_json_error(&ApiError {
+ code: "404".into(),
+ title: "Not Found".into(),
+ detail: "Resource you requested has not been found.".into(),
+ }),
+ })
}
}
+struct RestApiRouter {
+ api: RestApi,
+ handler: Box,
+}
+
+impl server::Handler for RestApiRouter {
+
+ fn on_request(&mut self, _request: server::Request) -> Next {
+ self.handler = as_json(&self.api.list_apps());
+ Next::write()
+ }
+
+ fn on_request_readable(&mut self, decoder: &mut Decoder) -> Next {
+ self.handler.on_request_readable(decoder)
+ }
+
+ fn on_response(&mut self, res: &mut server::Response) -> Next {
+ self.handler.on_response(res)
+ }
+
+ fn on_response_writable(&mut self, encoder: &mut Encoder) -> Next {
+ self.handler.on_response_writable(encoder)
+ }
+
+}
diff --git a/dapps/src/api/mod.rs b/dapps/src/api/mod.rs
index 088d7f6b2..402e84257 100644
--- a/dapps/src/api/mod.rs
+++ b/dapps/src/api/mod.rs
@@ -16,13 +16,12 @@
//! REST API
-#![warn(missing_docs)]
#![cfg_attr(feature="nightly", feature(custom_derive, custom_attribute, plugin))]
#![cfg_attr(feature="nightly", plugin(serde_macros, clippy))]
-#[cfg(feature = "serde_macros")]
-include!("mod.rs.in");
-
-#[cfg(not(feature = "serde_macros"))]
-include!(concat!(env!("OUT_DIR"), "/mod.rs"));
+mod api;
+mod response;
+mod types;
+pub use self::api::RestApi;
+pub use self::types::App;
diff --git a/dapps/src/api/response.rs b/dapps/src/api/response.rs
index 345b8a6ee..ba0922e7a 100644
--- a/dapps/src/api/response.rs
+++ b/dapps/src/api/response.rs
@@ -16,8 +16,13 @@
use serde::Serialize;
use serde_json;
-use endpoint::{ContentHandler, Handler};
+use endpoint::Handler;
+use handlers::ContentHandler;
pub fn as_json(val: &T) -> Box {
- Box::new(ContentHandler::new(serde_json::to_string(val).unwrap(), "application/json".to_owned()))
+ Box::new(ContentHandler::ok(serde_json::to_string(val).unwrap(), "application/json".to_owned()))
+}
+
+pub fn as_json_error(val: &T) -> Box {
+ Box::new(ContentHandler::not_found(serde_json::to_string(val).unwrap(), "application/json".to_owned()))
}
diff --git a/dapps/src/api/mod.rs.in b/dapps/src/api/types.rs
similarity index 82%
rename from dapps/src/api/mod.rs.in
rename to dapps/src/api/types.rs
index a069c06b0..d99d767eb 100644
--- a/dapps/src/api/mod.rs.in
+++ b/dapps/src/api/types.rs
@@ -14,8 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
-mod api;
-mod response;
+#[cfg(feature = "serde_macros")]
+include!("types.rs.in");
+
+#[cfg(not(feature = "serde_macros"))]
+include!(concat!(env!("OUT_DIR"), "/types.rs"));
+
-pub use self::api::RestApi;
-pub use self::api::App;
diff --git a/dapps/src/api/types.rs.in b/dapps/src/api/types.rs.in
new file mode 100644
index 000000000..72a8cd221
--- /dev/null
+++ b/dapps/src/api/types.rs.in
@@ -0,0 +1,51 @@
+// Copyright 2015, 2016 Ethcore (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+use endpoint::EndpointInfo;
+
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+pub struct App {
+ pub id: String,
+ pub name: String,
+ pub description: String,
+ pub version: String,
+ pub author: String,
+ #[serde(rename="iconUrl")]
+ pub icon_url: String,
+}
+
+impl App {
+ /// Creates `App` instance from `EndpointInfo` and `id`.
+ pub fn from_info(id: &str, info: &EndpointInfo) -> Self {
+ App {
+ id: id.to_owned(),
+ name: info.name.to_owned(),
+ description: info.description.to_owned(),
+ version: info.version.to_owned(),
+ author: info.author.to_owned(),
+ icon_url: info.icon_url.to_owned(),
+ }
+ }
+}
+
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+pub struct ApiError {
+ pub code: String,
+ pub title: String,
+ pub detail: String,
+}
+
+
diff --git a/dapps/src/endpoint.rs b/dapps/src/endpoint.rs
index 075211e58..ba2116e1c 100644
--- a/dapps/src/endpoint.rs
+++ b/dapps/src/endpoint.rs
@@ -16,11 +16,7 @@
//! URL Endpoint traits
-use hyper::status::StatusCode;
-use hyper::{header, server, Decoder, Encoder, Next};
-use hyper::net::HttpStream;
-
-use std::io::Write;
+use hyper::{server, net};
use std::collections::BTreeMap;
#[derive(Debug, PartialEq, Default, Clone)]
@@ -42,58 +38,8 @@ pub struct EndpointInfo {
pub trait Endpoint : Send + Sync {
fn info(&self) -> Option<&EndpointInfo> { None }
- fn to_handler(&self, path: EndpointPath) -> Box + Send>;
+ fn to_handler(&self, path: EndpointPath) -> Box + Send>;
}
pub type Endpoints = BTreeMap>;
-pub type Handler = server::Handler + Send;
-
-pub struct ContentHandler {
- content: String,
- mimetype: String,
- write_pos: usize,
-}
-
-impl ContentHandler {
- pub fn new(content: String, mimetype: String) -> Self {
- ContentHandler {
- content: content,
- mimetype: mimetype,
- write_pos: 0
- }
- }
-}
-
-impl server::Handler for ContentHandler {
- fn on_request(&mut self, _request: server::Request) -> Next {
- Next::write()
- }
-
- fn on_request_readable(&mut self, _decoder: &mut Decoder) -> Next {
- Next::write()
- }
-
- fn on_response(&mut self, res: &mut server::Response) -> Next {
- res.set_status(StatusCode::Ok);
- res.headers_mut().set(header::ContentType(self.mimetype.parse().unwrap()));
- Next::write()
- }
-
- fn on_response_writable(&mut self, encoder: &mut Encoder) -> Next {
- let bytes = self.content.as_bytes();
- if self.write_pos == bytes.len() {
- return Next::end();
- }
-
- match encoder.write(&bytes[self.write_pos..]) {
- Ok(bytes) => {
- self.write_pos += bytes;
- Next::write()
- },
- Err(e) => match e.kind() {
- ::std::io::ErrorKind::WouldBlock => Next::write(),
- _ => Next::end()
- },
- }
- }
-}
+pub type Handler = server::Handler + Send;
diff --git a/dapps/src/handlers/auth.rs b/dapps/src/handlers/auth.rs
new file mode 100644
index 000000000..7f72f7cf8
--- /dev/null
+++ b/dapps/src/handlers/auth.rs
@@ -0,0 +1,44 @@
+// Copyright 2015, 2016 Ethcore (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+//! Authorization Handlers
+
+use hyper::{server, Decoder, Encoder, Next};
+use hyper::net::HttpStream;
+use hyper::status::StatusCode;
+
+pub struct AuthRequiredHandler;
+
+impl server::Handler for AuthRequiredHandler {
+ fn on_request(&mut self, _request: server::Request) -> Next {
+ Next::write()
+ }
+
+ fn on_request_readable(&mut self, _decoder: &mut Decoder) -> Next {
+ Next::write()
+ }
+
+ fn on_response(&mut self, res: &mut server::Response) -> Next {
+ res.set_status(StatusCode::Unauthorized);
+ res.headers_mut().set_raw("WWW-Authenticate", vec![b"Basic realm=\"Parity\"".to_vec()]);
+ Next::write()
+ }
+
+ fn on_response_writable(&mut self, _encoder: &mut Encoder) -> Next {
+ Next::end()
+ }
+}
+
diff --git a/dapps/src/handlers/content.rs b/dapps/src/handlers/content.rs
new file mode 100644
index 000000000..a589e5492
--- /dev/null
+++ b/dapps/src/handlers/content.rs
@@ -0,0 +1,92 @@
+// Copyright 2015, 2016 Ethcore (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+//! Simple Content Handler
+
+use std::io::Write;
+use hyper::{header, server, Decoder, Encoder, Next};
+use hyper::net::HttpStream;
+use hyper::status::StatusCode;
+
+pub struct ContentHandler {
+ code: StatusCode,
+ content: String,
+ mimetype: String,
+ write_pos: usize,
+}
+
+impl ContentHandler {
+ pub fn ok(content: String, mimetype: String) -> Self {
+ ContentHandler {
+ code: StatusCode::Ok,
+ content: content,
+ mimetype: mimetype,
+ write_pos: 0
+ }
+ }
+
+ pub fn not_found(content: String, mimetype: String) -> Self {
+ ContentHandler {
+ code: StatusCode::NotFound,
+ content: content,
+ mimetype: mimetype,
+ write_pos: 0
+ }
+ }
+
+ pub fn new(code: StatusCode, content: String, mimetype: String) -> Self {
+ ContentHandler {
+ code: code,
+ content: content,
+ mimetype: mimetype,
+ write_pos: 0,
+ }
+ }
+}
+
+impl server::Handler for ContentHandler {
+ fn on_request(&mut self, _request: server::Request) -> Next {
+ Next::write()
+ }
+
+ fn on_request_readable(&mut self, _decoder: &mut Decoder) -> Next {
+ Next::write()
+ }
+
+ fn on_response(&mut self, res: &mut server::Response) -> Next {
+ res.set_status(self.code);
+ res.headers_mut().set(header::ContentType(self.mimetype.parse().unwrap()));
+ Next::write()
+ }
+
+ fn on_response_writable(&mut self, encoder: &mut Encoder) -> Next {
+ let bytes = self.content.as_bytes();
+ if self.write_pos == bytes.len() {
+ return Next::end();
+ }
+
+ match encoder.write(&bytes[self.write_pos..]) {
+ Ok(bytes) => {
+ self.write_pos += bytes;
+ Next::write()
+ },
+ Err(e) => match e.kind() {
+ ::std::io::ErrorKind::WouldBlock => Next::write(),
+ _ => Next::end()
+ },
+ }
+ }
+}
diff --git a/dapps/src/handlers/mod.rs b/dapps/src/handlers/mod.rs
new file mode 100644
index 000000000..933538655
--- /dev/null
+++ b/dapps/src/handlers/mod.rs
@@ -0,0 +1,25 @@
+// Copyright 2015, 2016 Ethcore (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+//! Hyper handlers implementations.
+
+mod auth;
+mod content;
+mod redirect;
+
+pub use self::auth::AuthRequiredHandler;
+pub use self::content::ContentHandler;
+pub use self::redirect::Redirection;
diff --git a/dapps/src/router/redirect.rs b/dapps/src/handlers/redirect.rs
similarity index 100%
rename from dapps/src/router/redirect.rs
rename to dapps/src/handlers/redirect.rs
diff --git a/dapps/src/lib.rs b/dapps/src/lib.rs
index 22b075afd..1c550fb07 100644
--- a/dapps/src/lib.rs
+++ b/dapps/src/lib.rs
@@ -59,6 +59,7 @@ mod endpoint;
mod apps;
mod page;
mod router;
+mod handlers;
mod rpc;
mod api;
mod proxypac;
diff --git a/dapps/src/proxypac.rs b/dapps/src/proxypac.rs
index 9d91d58f0..aaa68dc0c 100644
--- a/dapps/src/proxypac.rs
+++ b/dapps/src/proxypac.rs
@@ -16,7 +16,8 @@
//! Serving ProxyPac file
-use endpoint::{Endpoint, Handler, ContentHandler, EndpointPath};
+use endpoint::{Endpoint, Handler, EndpointPath};
+use handlers::ContentHandler;
use apps::DAPPS_DOMAIN;
pub struct ProxyPac;
@@ -41,7 +42,7 @@ function FindProxyForURL(url, host) {{
}}
"#,
DAPPS_DOMAIN, path.host, path.port);
- Box::new(ContentHandler::new(content, "application/javascript".to_owned()))
+ Box::new(ContentHandler::ok(content, "application/javascript".to_owned()))
}
}
diff --git a/dapps/src/router/auth.rs b/dapps/src/router/auth.rs
index 9459d0719..d18424a00 100644
--- a/dapps/src/router/auth.rs
+++ b/dapps/src/router/auth.rs
@@ -16,24 +16,23 @@
//! HTTP Authorization implementations
-use std::io::Write;
use std::collections::HashMap;
-use hyper::{header, server, Decoder, Encoder, Next};
-use hyper::net::HttpStream;
-use hyper::status::StatusCode;
+use hyper::{server, net, header, status};
+use endpoint::Handler;
+use handlers::{AuthRequiredHandler, ContentHandler};
/// Authorization result
pub enum Authorized {
/// Authorization was successful.
Yes,
/// Unsuccessful authorization. Handler for further work is returned.
- No(Box + Send>),
+ No(Box),
}
/// Authorization interface
pub trait Authorization : Send + Sync {
/// Checks if authorization is valid.
- fn is_authorized(&self, req: &server::Request)-> Authorized;
+ fn is_authorized(&self, req: &server::Request)-> Authorized;
}
/// HTTP Basic Authorization handler
@@ -45,18 +44,22 @@ pub struct HttpBasicAuth {
pub struct NoAuth;
impl Authorization for NoAuth {
- fn is_authorized(&self, _req: &server::Request)-> Authorized {
+ fn is_authorized(&self, _req: &server::Request)-> Authorized {
Authorized::Yes
}
}
impl Authorization for HttpBasicAuth {
- fn is_authorized(&self, req: &server::Request) -> Authorized {
+ fn is_authorized(&self, req: &server::Request) -> Authorized {
let auth = self.check_auth(&req);
match auth {
Access::Denied => {
- Authorized::No(Box::new(UnauthorizedHandler { write_pos: 0 }))
+ Authorized::No(Box::new(ContentHandler::new(
+ status::StatusCode::Unauthorized,
+ "Unauthorized
".into(),
+ "text/html".into(),
+ )))
},
Access::AuthRequired => {
Authorized::No(Box::new(AuthRequiredHandler))
@@ -89,7 +92,7 @@ impl HttpBasicAuth {
self.users.get(&username.to_owned()).map_or(false, |pass| pass == password)
}
- fn check_auth(&self, req: &server::Request) -> Access {
+ fn check_auth(&self, req: &server::Request) -> Access {
match req.headers().get::>() {
Some(&header::Authorization(
header::Basic { ref username, password: Some(ref password) }
@@ -99,63 +102,3 @@ impl HttpBasicAuth {
}
}
}
-
-pub struct UnauthorizedHandler {
- write_pos: usize,
-}
-
-impl server::Handler for UnauthorizedHandler {
- fn on_request(&mut self, _request: server::Request) -> Next {
- Next::write()
- }
-
- fn on_request_readable(&mut self, _decoder: &mut Decoder) -> Next {
- Next::write()
- }
-
- fn on_response(&mut self, res: &mut server::Response) -> Next {
- res.set_status(StatusCode::Unauthorized);
- Next::write()
- }
-
- fn on_response_writable(&mut self, encoder: &mut Encoder) -> Next {
- let response = "Unauthorized".as_bytes();
-
- if self.write_pos == response.len() {
- return Next::end();
- }
-
- match encoder.write(&response[self.write_pos..]) {
- Ok(bytes) => {
- self.write_pos += bytes;
- Next::write()
- },
- Err(e) => match e.kind() {
- ::std::io::ErrorKind::WouldBlock => Next::write(),
- _ => Next::end()
- },
- }
- }
-}
-
-pub struct AuthRequiredHandler;
-
-impl server::Handler for AuthRequiredHandler {
- fn on_request(&mut self, _request: server::Request) -> Next {
- Next::write()
- }
-
- fn on_request_readable(&mut self, _decoder: &mut Decoder) -> Next {
- Next::write()
- }
-
- fn on_response(&mut self, res: &mut server::Response) -> Next {
- res.set_status(StatusCode::Unauthorized);
- res.headers_mut().set_raw("WWW-Authenticate", vec![b"Basic realm=\"Parity\"".to_vec()]);
- Next::write()
- }
-
- fn on_response_writable(&mut self, _encoder: &mut Encoder) -> Next {
- Next::end()
- }
-}
diff --git a/dapps/src/router/mod.rs b/dapps/src/router/mod.rs
index 0cb0d38d0..f04c8b614 100644
--- a/dapps/src/router/mod.rs
+++ b/dapps/src/router/mod.rs
@@ -18,7 +18,6 @@
//! Processes request handling authorization and dispatching it to proper application.
mod url;
-mod redirect;
pub mod auth;
use DAPPS_DOMAIN;
@@ -33,7 +32,7 @@ use apps;
use endpoint::{Endpoint, Endpoints, EndpointPath};
use self::url::Url;
use self::auth::{Authorization, Authorized};
-use self::redirect::Redirection;
+use handlers::Redirection;
/// Special endpoints are accessible on every domain (every dapp)
#[derive(Debug, PartialEq, Hash, Eq)]