Adding webapps router
This commit is contained in:
parent
5d6ca1498e
commit
ad37b7fd2a
15
Cargo.lock
generated
15
Cargo.lock
generated
@ -321,6 +321,8 @@ dependencies = [
|
|||||||
"jsonrpc-core 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonrpc-core 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"jsonrpc-http-server 4.0.0 (git+https://github.com/tomusdrw/jsonrpc-http-server.git)",
|
"jsonrpc-http-server 4.0.0 (git+https://github.com/tomusdrw/jsonrpc-http-server.git)",
|
||||||
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"parity-wallet 0.1.0 (git+https://github.com/tomusdrw/parity-wallet.git)",
|
||||||
|
"parity-webapp 0.1.0 (git+https://github.com/tomusdrw/parity-webapp.git)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -701,6 +703,19 @@ name = "odds"
|
|||||||
version = "0.2.12"
|
version = "0.2.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parity-wallet"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/tomusdrw/parity-wallet.git#9b0253f5cb88b31417450ca8be708cab2e437dfc"
|
||||||
|
dependencies = [
|
||||||
|
"parity-webapp 0.1.0 (git+https://github.com/tomusdrw/parity-webapp.git)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parity-webapp"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/tomusdrw/parity-webapp.git#a24297256bae0ae0712c6478cd1ad681828b3800"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "plugin"
|
name = "plugin"
|
||||||
version = "0.2.6"
|
version = "0.2.6"
|
||||||
|
@ -132,7 +132,7 @@ API and Console Options:
|
|||||||
interface. APIS is a comma-delimited list of API
|
interface. APIS is a comma-delimited list of API
|
||||||
name. Possible name are web3, eth and net.
|
name. Possible name are web3, eth and net.
|
||||||
[default: web3,eth,net,personal].
|
[default: web3,eth,net,personal].
|
||||||
-w --webap Enable the web applications server (e.g. status page).
|
-w --webapp Enable the web applications server (e.g. status page).
|
||||||
--webapp-port PORT Specify the port portion of the WebApps server
|
--webapp-port PORT Specify the port portion of the WebApps server
|
||||||
[default: 8080].
|
[default: 8080].
|
||||||
|
|
||||||
@ -305,7 +305,10 @@ fn setup_rpc_server(
|
|||||||
fn setup_webapp_server(
|
fn setup_webapp_server(
|
||||||
url: &str
|
url: &str
|
||||||
) -> Option<Arc<PanicHandler>> {
|
) -> Option<Arc<PanicHandler>> {
|
||||||
|
use rpc::v1::*;
|
||||||
|
|
||||||
let server = webapp::WebappServer::new();
|
let server = webapp::WebappServer::new();
|
||||||
|
server.add_delegate(Web3Client::new().to_delegate());
|
||||||
Some(server.start_http(url, ::num_cpus::get()))
|
Some(server.start_http(url, ::num_cpus::get()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,11 @@ hyper = { version = "0.8", default-features = false }
|
|||||||
iron = { version = "0.3" }
|
iron = { version = "0.3" }
|
||||||
ethcore-rpc = { path = "../rpc" }
|
ethcore-rpc = { path = "../rpc" }
|
||||||
ethcore-util = { path = "../util" }
|
ethcore-util = { path = "../util" }
|
||||||
|
parity-webapp = { git = "https://github.com/tomusdrw/parity-webapp.git" }
|
||||||
|
# List of apps
|
||||||
|
parity-wallet = { git = "https://github.com/tomusdrw/parity-wallet.git", optional = true }
|
||||||
clippy = { version = "0.0.61", optional = true}
|
clippy = { version = "0.0.61", optional = true}
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
default = ["parity-wallet"]
|
||||||
dev = ["clippy", "ethcore-rpc/dev", "ethcore-util/dev"]
|
dev = ["clippy", "ethcore-rpc/dev", "ethcore-util/dev"]
|
||||||
|
37
webapp/src/apps.rs
Normal file
37
webapp/src/apps.rs
Normal 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) {}
|
||||||
|
|
@ -26,16 +26,17 @@ extern crate jsonrpc_core;
|
|||||||
extern crate jsonrpc_http_server;
|
extern crate jsonrpc_http_server;
|
||||||
extern crate ethcore_rpc as rpc;
|
extern crate ethcore_rpc as rpc;
|
||||||
extern crate ethcore_util as util;
|
extern crate ethcore_util as util;
|
||||||
|
extern crate parity_webapp;
|
||||||
|
|
||||||
use rpc::v1::*;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::thread;
|
|
||||||
|
|
||||||
use util::panics::PanicHandler;
|
use util::panics::PanicHandler;
|
||||||
use iron::request::Url;
|
|
||||||
use self::jsonrpc_core::{IoHandler, IoDelegate};
|
use self::jsonrpc_core::{IoHandler, IoDelegate};
|
||||||
use jsonrpc_http_server::ServerHandler;
|
use jsonrpc_http_server::ServerHandler;
|
||||||
|
|
||||||
|
mod apps;
|
||||||
|
mod page;
|
||||||
|
mod router;
|
||||||
|
|
||||||
/// Http server.
|
/// Http server.
|
||||||
pub struct WebappServer {
|
pub struct WebappServer {
|
||||||
handler: Arc<IoHandler>,
|
handler: Arc<IoHandler>,
|
||||||
@ -44,117 +45,32 @@ pub struct WebappServer {
|
|||||||
impl WebappServer {
|
impl WebappServer {
|
||||||
/// Construct new http server object
|
/// Construct new http server object
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let server = WebappServer {
|
WebappServer {
|
||||||
handler: Arc::new(IoHandler::new()),
|
handler: Arc::new(IoHandler::new()),
|
||||||
};
|
}
|
||||||
// TODO add more
|
|
||||||
server.add_delegate(Web3Client::new().to_delegate());
|
|
||||||
|
|
||||||
server
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add io delegate.
|
/// 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);
|
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> {
|
pub fn start_http(&self, addr: &str, threads: usize) -> Arc<PanicHandler> {
|
||||||
let addr = addr.to_owned();
|
let addr = addr.to_owned();
|
||||||
let panic_handler = PanicHandler::new_in_arc();
|
let panic_handler = PanicHandler::new_in_arc();
|
||||||
let ph = panic_handler.clone();
|
|
||||||
let handler = self.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 cors_domain = jsonrpc_http_server::AccessControlAllowOrigin::Null;
|
let rpc = ServerHandler::new(handler, cors_domain);
|
||||||
let rpc = ServerHandler::new(handler, cors_domain);
|
let router = router::Router::new(rpc, apps::all_pages());
|
||||||
let router = Router::new(rpc);
|
|
||||||
|
|
||||||
ph.catch_panic(move || {
|
panic_handler.catch_panic(move || {
|
||||||
hyper::Server::http(addr.as_ref() as &str).unwrap()
|
hyper::Server::http(addr.as_ref() as &str).unwrap()
|
||||||
.handle_threads(router, threads)
|
.handle_threads(router, threads)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}).unwrap()
|
}).unwrap();
|
||||||
}).expect("Error while creating jsonrpc http thread");
|
|
||||||
|
|
||||||
panic_handler
|
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
67
webapp/src/page/mod.rs
Normal 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
89
webapp/src/router/mod.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user