Adding webapps router

This commit is contained in:
Tomasz Drwięga
2016-04-07 12:10:26 +02:00
parent 5d6ca1498e
commit ad37b7fd2a
7 changed files with 233 additions and 102 deletions

37
webapp/src/apps.rs Normal file
View File

@@ -0,0 +1,37 @@
// 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 <http://www.gnu.org/licenses/>.
use std::collections::HashMap;
use page::{Page, PageHandler};
extern crate parity_wallet;
pub type Pages = HashMap<String, Box<Page>>;
pub fn all_pages() -> Pages {
let mut pages = Pages::new();
wallet_page(&mut pages);
pages
}
#[cfg(feature="parity-wallet")]
fn wallet_page(pages: &mut Pages) {
pages.insert("wallet".to_owned(), Box::new(PageHandler { app: parity_wallet::App::default() }));
}
#[cfg(not(feature="parity-wallet"))]
fn wallet_page(_pages: &mut Pages) {}

View File

@@ -26,16 +26,17 @@ extern crate jsonrpc_core;
extern crate jsonrpc_http_server;
extern crate ethcore_rpc as rpc;
extern crate ethcore_util as util;
extern crate parity_webapp;
use rpc::v1::*;
use std::sync::Arc;
use std::thread;
use util::panics::PanicHandler;
use iron::request::Url;
use self::jsonrpc_core::{IoHandler, IoDelegate};
use jsonrpc_http_server::ServerHandler;
mod apps;
mod page;
mod router;
/// Http server.
pub struct WebappServer {
handler: Arc<IoHandler>,
@@ -44,117 +45,32 @@ pub struct WebappServer {
impl WebappServer {
/// Construct new http server object
pub fn new() -> Self {
let server = WebappServer {
WebappServer {
handler: Arc::new(IoHandler::new()),
};
// TODO add more
server.add_delegate(Web3Client::new().to_delegate());
server
}
}
/// Add io delegate.
fn add_delegate<D>(&self, delegate: IoDelegate<D>) where D: Send + Sync + 'static {
pub fn add_delegate<D>(&self, delegate: IoDelegate<D>) where D: Send + Sync + 'static {
self.handler.add_delegate(delegate);
}
/// Start server asynchronously in new thread and returns panic handler.
/// Start server asynchronously and returns panic handler.
pub fn start_http(&self, addr: &str, threads: usize) -> Arc<PanicHandler> {
let addr = addr.to_owned();
let panic_handler = PanicHandler::new_in_arc();
let ph = panic_handler.clone();
let handler = self.handler.clone();
thread::Builder::new().name("jsonrpc_http".to_string()).spawn(move || {
let cors_domain = jsonrpc_http_server::AccessControlAllowOrigin::Null;
let rpc = ServerHandler::new(handler, cors_domain);
let router = Router::new(rpc);
let cors_domain = jsonrpc_http_server::AccessControlAllowOrigin::Null;
let rpc = ServerHandler::new(handler, cors_domain);
let router = router::Router::new(rpc, apps::all_pages());
ph.catch_panic(move || {
hyper::Server::http(addr.as_ref() as &str).unwrap()
.handle_threads(router, threads)
.unwrap();
}).unwrap()
}).expect("Error while creating jsonrpc http thread");
panic_handler.catch_panic(move || {
hyper::Server::http(addr.as_ref() as &str).unwrap()
.handle_threads(router, threads)
.unwrap();
}).unwrap();
panic_handler
}
}
struct Router {
rpc: ServerHandler,
// admin: Page<AdminApp>,
// wallet: Page<WalletApp>,
// mist: Page<MistApp>,
}
impl Router {
pub fn new(rpc: ServerHandler) -> Self {
Router {
rpc: rpc,
// admin: Page { app: AdminApp::default() },
// wallet: Page { app: WalletApp::default() },
// mist: Page { app: MistApp::default() },
}
}
fn extract_url(req: &hyper::server::Request) -> Option<Url> {
match req.uri {
hyper::uri::RequestUri::AbsoluteUri(ref url) => {
match Url::from_generic_url(url.clone()) {
Ok(url) => Some(url),
_ => None,
}
},
hyper::uri::RequestUri::AbsolutePath(ref path) => {
// Attempt to prepend the Host header (mandatory in HTTP/1.1)
let url_string = match req.headers.get::<hyper::header::Host>() {
Some(ref host) => {
format!("http://{}:{}{}", host.hostname, host.port.unwrap_or(80), path)
},
None => return None
};
match Url::parse(&url_string) {
Ok(url) => Some(url),
_ => None,
}
}
_ => None,
}
}
fn extract_request_path<'a, 'b>(mut req: hyper::server::Request<'a, 'b>) -> (Option<String>, hyper::server::Request<'a, 'b>) {
let url = Router::extract_url(&req);
match url {
Some(url) => {
let part = url.path[0].clone();
let url = url.path[1..].join("/");
req.uri = hyper::uri::RequestUri::AbsolutePath(url);
(Some(part), req)
},
None => {
(None, req)
}
}
}
}
impl hyper::server::Handler for Router {
fn handle<'b, 'a>(&'a self, req: hyper::server::Request<'a, 'b>, res: hyper::server::Response<'a>) {
let (path, req) = Router::extract_request_path(req);
match path {
// Some(ref url) if url == "admin" => {
// self.admin.handle(req, res);
// },
// Some(ref url) if url == "wallet" => {
// self.wallet.handle(req, res);
// },
// Some(ref url) if url == "mist" => {
// self.mist.handle(req, res);
// },
_ => self.rpc.handle(req, res),
}
}
}

67
webapp/src/page/mod.rs Normal file
View File

@@ -0,0 +1,67 @@
// 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 <http://www.gnu.org/licenses/>.
use std::io::Write;
use hyper::uri::RequestUri;
use hyper::server;
use hyper::header;
use hyper::status::StatusCode;
use parity_webapp::WebApp;
pub trait Page : Send + Sync {
fn serve_file(&self, mut path: &str, mut res: server::Response);
}
pub struct PageHandler<T : WebApp> {
pub app: T
}
impl<T: WebApp> Page for PageHandler<T> {
fn serve_file(&self, mut path: &str, mut res: server::Response) {
// Support index file
if path == "" {
path = "index.html"
}
let file = self.app.file(path);
if let Some(f) = file {
*res.status_mut() = StatusCode::Ok;
res.headers_mut().set(header::ContentType(f.content_type.parse().unwrap()));
let _ = match res.start() {
Ok(mut raw_res) => {
for chunk in f.content.chunks(1024 * 20) {
let _ = raw_res.write(chunk);
}
raw_res.end()
},
Err(_) => {
println!("Error while writing response.");
Ok(())
}
};
}
}
}
impl server::Handler for Page {
fn handle(&self, req: server::Request, mut res: server::Response) {
*res.status_mut() = StatusCode::NotFound;
if let RequestUri::AbsolutePath(ref path) = req.uri {
self.serve_file(path, res);
}
}
}

89
webapp/src/router/mod.rs Normal file
View File

@@ -0,0 +1,89 @@
// 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 <http://www.gnu.org/licenses/>.
//! Router implementation
use hyper;
use apps::Pages;
use iron::request::Url;
use jsonrpc_http_server::ServerHandler;
pub struct Router {
rpc: ServerHandler,
pages: Pages,
}
impl hyper::server::Handler for Router {
fn handle<'b, 'a>(&'a self, req: hyper::server::Request<'a, 'b>, res: hyper::server::Response<'a>) {
let (path, req) = Router::extract_request_path(req);
match path {
Some(ref url) if self.pages.contains_key(url) => {
self.pages.get(url).unwrap().handle(req, res);
}
_ => self.rpc.handle(req, res),
}
}
}
impl Router {
pub fn new(rpc: ServerHandler, pages: Pages) -> Self {
Router {
rpc: rpc,
pages: pages,
}
}
fn extract_url(req: &hyper::server::Request) -> Option<Url> {
match req.uri {
hyper::uri::RequestUri::AbsoluteUri(ref url) => {
match Url::from_generic_url(url.clone()) {
Ok(url) => Some(url),
_ => None,
}
},
hyper::uri::RequestUri::AbsolutePath(ref path) => {
// Attempt to prepend the Host header (mandatory in HTTP/1.1)
let url_string = match req.headers.get::<hyper::header::Host>() {
Some(ref host) => {
format!("http://{}:{}{}", host.hostname, host.port.unwrap_or(80), path)
},
None => return None
};
match Url::parse(&url_string) {
Ok(url) => Some(url),
_ => None,
}
}
_ => None,
}
}
fn extract_request_path<'a, 'b>(mut req: hyper::server::Request<'a, 'b>) -> (Option<String>, hyper::server::Request<'a, 'b>) {
let url = Router::extract_url(&req);
match url {
Some(url) => {
let part = url.path[0].clone();
let url = url.path[1..].join("/");
req.uri = hyper::uri::RequestUri::AbsolutePath(url);
(Some(part), req)
},
None => {
(None, req)
}
}
}
}