Merge pull request #899 from ethcore/webapps2

Support for compile-time included WebApplications.
This commit is contained in:
Arkadiy Paronyan 2016-04-08 10:54:40 +02:00
commit f9f11b6f74
14 changed files with 594 additions and 5 deletions

112
Cargo.lock generated
View File

@ -11,6 +11,7 @@ dependencies = [
"ethcore-devtools 1.1.0", "ethcore-devtools 1.1.0",
"ethcore-rpc 1.1.0", "ethcore-rpc 1.1.0",
"ethcore-util 1.1.0", "ethcore-util 1.1.0",
"ethcore-webapp 1.1.0",
"ethminer 1.1.0", "ethminer 1.1.0",
"ethsync 1.1.0", "ethsync 1.1.0",
"fdlimit 0.1.0", "fdlimit 0.1.0",
@ -107,6 +108,14 @@ dependencies = [
"unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "conduit-mime-types"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "cookie" name = "cookie"
version = "0.1.21" version = "0.1.21"
@ -185,6 +194,15 @@ dependencies = [
"regex 0.1.61 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.1.61 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "error"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "eth-secp256k1" name = "eth-secp256k1"
version = "0.5.4" version = "0.5.4"
@ -291,6 +309,23 @@ dependencies = [
"vergen 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "vergen 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "ethcore-webapp"
version = "1.1.0"
dependencies = [
"clippy 0.0.61 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-rpc 1.1.0",
"ethcore-util 1.1.0",
"hyper 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"iron 0.3.0 (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?branch=old-hyper)",
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-status 0.1.4 (git+https://github.com/tomusdrw/parity-status.git)",
"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]]
name = "ethjson" name = "ethjson"
version = "0.1.0" version = "0.1.0"
@ -448,6 +483,23 @@ dependencies = [
"xmltree 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "xmltree 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "iron"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"conduit-mime-types 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
"error 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"modifier 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"plugin 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"url 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.4.11" version = "0.4.11"
@ -472,6 +524,16 @@ dependencies = [
"syntex 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", "syntex 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "jsonrpc-http-server"
version = "4.0.0"
source = "git+https://github.com/tomusdrw/jsonrpc-http-server.git?branch=old-hyper#46bd4e7cf8352e0efc940cf76d3dff99f1a3da15"
dependencies = [
"hyper 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "jsonrpc-http-server" name = "jsonrpc-http-server"
version = "5.0.0" version = "5.0.0"
@ -594,6 +656,11 @@ dependencies = [
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "modifier"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "net2" name = "net2"
version = "0.2.23" version = "0.2.23"
@ -658,6 +725,35 @@ 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-status"
version = "0.1.4"
source = "git+https://github.com/tomusdrw/parity-status.git#380d13c8aafc3847a731968a6532edb09c78f2cf"
dependencies = [
"parity-webapp 0.1.0 (git+https://github.com/tomusdrw/parity-webapp.git)",
]
[[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]]
name = "plugin"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "primal" name = "primal"
version = "0.2.3" version = "0.2.3"
@ -976,6 +1072,14 @@ name = "typeable"
version = "0.1.2" version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "typemap"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unsafe-any 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "unicase" name = "unicase"
version = "1.4.0" version = "1.4.0"
@ -1002,6 +1106,14 @@ name = "unicode-xid"
version = "0.0.3" version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unsafe-any"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "url" name = "url"
version = "0.2.38" version = "0.2.38"

View File

@ -28,6 +28,8 @@ ethsync = { path = "sync" }
ethminer = { path = "miner" } ethminer = { path = "miner" }
ethcore-devtools = { path = "devtools" } ethcore-devtools = { path = "devtools" }
ethcore-rpc = { path = "rpc", optional = true } ethcore-rpc = { path = "rpc", optional = true }
ethcore-webapp = { path = "webapp", optional = true }
[dependencies.hyper] [dependencies.hyper]
version = "0.8" version = "0.8"
@ -36,7 +38,9 @@ default-features = false
[features] [features]
default = ["rpc"] default = ["rpc"]
rpc = ["ethcore-rpc"] rpc = ["ethcore-rpc"]
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev", "ethminer/dev"] webapp = ["ethcore-webapp"]
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev", "ethminer/dev",
"ethcore-webapp/dev"]
travis-beta = ["ethcore/json-tests"] travis-beta = ["ethcore/json-tests"]
travis-nightly = ["ethcore/json-tests", "dev"] travis-nightly = ["ethcore/json-tests", "dev"]

2
cov.sh
View File

@ -23,6 +23,7 @@ cargo test \
-p ethcore-rpc \ -p ethcore-rpc \
-p parity \ -p parity \
-p ethminer \ -p ethminer \
-p ethcore-webapp \
--no-run || exit $? --no-run || exit $?
rm -rf target/coverage rm -rf target/coverage
mkdir -p target/coverage mkdir -p target/coverage
@ -33,5 +34,6 @@ kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage t
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore_util-* kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore_util-*
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethsync-* kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethsync-*
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore_rpc-* kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore_rpc-*
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore_webapp-*
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethminer-* kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethminer-*
xdg-open target/coverage/index.html xdg-open target/coverage/index.html

1
doc.sh
View File

@ -7,5 +7,6 @@ cargo doc --no-deps --verbose \
-p ethcore \ -p ethcore \
-p ethsync \ -p ethsync \
-p ethcore-rpc \ -p ethcore-rpc \
-p ethcore-webapp \
-p parity \ -p parity \
-p ethminer -p ethminer

1
fmt.sh
View File

@ -9,6 +9,7 @@ $RUSTFMT ./json/src/lib.rs
$RUSTFMT ./miner/src/lib.rs $RUSTFMT ./miner/src/lib.rs
$RUSTFMT ./parity/main.rs $RUSTFMT ./parity/main.rs
$RUSTFMT ./rpc/src/lib.rs $RUSTFMT ./rpc/src/lib.rs
$RUSTFMT ./webapp/src/lib.rs
$RUSTFMT ./sync/src/lib.rs $RUSTFMT ./sync/src/lib.rs
$RUSTFMT ./util/src/lib.rs $RUSTFMT ./util/src/lib.rs

View File

@ -7,6 +7,6 @@ echo "set -e" >> $FILE
echo "cargo build --release --features dev" >> $FILE echo "cargo build --release --features dev" >> $FILE
# Build tests # Build tests
echo "cargo test --no-run --features dev \\" >> $FILE echo "cargo test --no-run --features dev \\" >> $FILE
echo " -p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethminer" >> $FILE echo " -p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethminer -p ethcore-webapp" >> $FILE
echo "" >> $FILE echo "" >> $FILE
chmod +x $FILE chmod +x $FILE

View File

@ -42,6 +42,8 @@ extern crate rpassword;
#[cfg(feature = "rpc")] #[cfg(feature = "rpc")]
extern crate ethcore_rpc as rpc; extern crate ethcore_rpc as rpc;
#[cfg(feature = "webapp")]
extern crate ethcore_webapp as webapp;
use std::io::{BufRead, BufReader}; use std::io::{BufRead, BufReader};
use std::fs::File; use std::fs::File;
@ -65,6 +67,8 @@ use daemonize::Daemonize;
use number_prefix::{binary_prefix, Standalone, Prefixed}; use number_prefix::{binary_prefix, Standalone, Prefixed};
#[cfg(feature = "rpc")] #[cfg(feature = "rpc")]
use rpc::Server as RpcServer; use rpc::Server as RpcServer;
#[cfg(feature = "webapp")]
use webapp::Listening as WebappServer;
mod price_info; mod price_info;
@ -120,7 +124,7 @@ Networking Options:
string or input to SHA3 operation. string or input to SHA3 operation.
API and Console Options: API and Console Options:
-j --jsonrpc Enable the JSON-RPC API sever. -j --jsonrpc Enable the JSON-RPC API server.
--jsonrpc-interface IP Specify the hostname portion of the JSONRPC API --jsonrpc-interface IP Specify the hostname portion of the JSONRPC API
server, IP should be an interface's IP address, or server, IP should be an interface's IP address, or
all (all interfaces) or local [default: local]. all (all interfaces) or local [default: local].
@ -132,6 +136,13 @@ 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 --webapp Enable the web applications server (e.g. status page).
--webapp-port PORT Specify the port portion of the WebApps server
[default: 8080].
--webapp-interface IP Specify the hostname portion of the WebApps
server, IP should be an interface's IP address, or
all (all interfaces) or local [default: local].
Sealing/Mining Options: Sealing/Mining Options:
--usd-per-tx USD Amount of USD to be paid for a basic transaction --usd-per-tx USD Amount of USD to be paid for a basic transaction
@ -216,6 +227,9 @@ struct Args {
flag_jsonrpc_port: u16, flag_jsonrpc_port: u16,
flag_jsonrpc_cors: String, flag_jsonrpc_cors: String,
flag_jsonrpc_apis: String, flag_jsonrpc_apis: String,
flag_webapp: bool,
flag_webapp_port: u16,
flag_webapp_interface: String,
flag_author: String, flag_author: String,
flag_usd_per_tx: String, flag_usd_per_tx: String,
flag_usd_per_eth: String, flag_usd_per_eth: String,
@ -301,6 +315,31 @@ fn setup_rpc_server(
} }
} }
#[cfg(feature = "webapp")]
fn setup_webapp_server(
client: Arc<Client>,
sync: Arc<EthSync>,
secret_store: Arc<AccountService>,
miner: Arc<Miner>,
url: &str
) -> WebappServer {
use rpc::v1::*;
let server = webapp::WebappServer::new();
server.add_delegate(Web3Client::new().to_delegate());
server.add_delegate(NetClient::new(&sync).to_delegate());
server.add_delegate(EthClient::new(&client, &sync, &secret_store, &miner).to_delegate());
server.add_delegate(EthFilterClient::new(&client, &miner).to_delegate());
server.add_delegate(PersonalClient::new(&secret_store).to_delegate());
let start_result = server.start_http(url, ::num_cpus::get());
match start_result {
Err(webapp::WebappServerError::IoError(err)) => die_with_io_error(err),
Err(e) => die!("{:?}", e),
Ok(handle) => handle,
}
}
#[cfg(not(feature = "rpc"))] #[cfg(not(feature = "rpc"))]
struct RpcServer; struct RpcServer;
@ -317,6 +356,20 @@ fn setup_rpc_server(
die!("Your Parity version has been compiled without JSON-RPC support.") die!("Your Parity version has been compiled without JSON-RPC support.")
} }
#[cfg(not(feature = "webapp"))]
struct WebappServer;
#[cfg(not(feature = "webapp"))]
fn setup_webapp_server(
_client: Arc<Client>,
_sync: Arc<EthSync>,
_secret_store: Arc<AccountService>,
_miner: Arc<Miner>,
_url: &str
) -> ! {
die!("Your Parity version has been compiled without WebApps support.")
}
fn print_version() { fn print_version() {
println!("\ println!("\
Parity Parity
@ -621,6 +674,26 @@ impl Configuration {
None None
}; };
let webapp_server = if self.args.flag_webapp {
let url = format!("{}:{}",
match self.args.flag_webapp_interface.as_str() {
"all" => "0.0.0.0",
"local" => "127.0.0.1",
x => x,
},
self.args.flag_webapp_port
);
Some(setup_webapp_server(
service.client(),
sync.clone(),
account_service.clone(),
miner.clone(),
&url,
))
} else {
None
};
// Register IO handler // Register IO handler
let io_handler = Arc::new(ClientIoHandler { let io_handler = Arc::new(ClientIoHandler {
client: service.client(), client: service.client(),
@ -631,11 +704,11 @@ impl Configuration {
service.io().register_handler(io_handler).expect("Error registering IO handler"); service.io().register_handler(io_handler).expect("Error registering IO handler");
// Handle exit // Handle exit
wait_for_exit(panic_handler, rpc_server); wait_for_exit(panic_handler, rpc_server, webapp_server);
} }
} }
fn wait_for_exit(panic_handler: Arc<PanicHandler>, _rpc_server: Option<RpcServer>) { fn wait_for_exit(panic_handler: Arc<PanicHandler>, _rpc_server: Option<RpcServer>, _webapp_server: Option<WebappServer>) {
let exit = Arc::new(Condvar::new()); let exit = Arc::new(Condvar::new());
// Handle possible exits // Handle possible exits

View File

@ -7,6 +7,7 @@ cargo test --features ethcore/json-tests $1 \
-p ethcore \ -p ethcore \
-p ethsync \ -p ethsync \
-p ethcore-rpc \ -p ethcore-rpc \
-p ethcore-webapp \
-p parity \ -p parity \
-p ethminer \ -p ethminer \
-p bigint -p bigint

26
webapp/Cargo.toml Normal file
View File

@ -0,0 +1,26 @@
[package]
description = "Parity WebApplications crate"
name = "ethcore-webapp"
version = "1.1.0"
license = "GPL-3.0"
authors = ["Ethcore <admin@ethcore.io"]
[lib]
[dependencies]
log = "0.3"
jsonrpc-core = "2.0"
jsonrpc-http-server = { git = "https://github.com/tomusdrw/jsonrpc-http-server.git", branch="old-hyper" }
hyper = { version = "0.8", default-features = false }
iron = { version = "0.3" }
ethcore-rpc = { path = "../rpc" }
ethcore-util = { path = "../util" }
parity-webapp = { git = "https://github.com/tomusdrw/parity-webapp.git" }
# List of apps
parity-status = { git = "https://github.com/tomusdrw/parity-status.git", version = "0.1.4" }
parity-wallet = { git = "https://github.com/tomusdrw/parity-wallet.git", optional = true }
clippy = { version = "0.0.61", optional = true}
[features]
default = ["parity-wallet"]
dev = ["clippy", "ethcore-rpc/dev", "ethcore-util/dev"]

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

@ -0,0 +1,41 @@
// 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_status;
extern crate parity_wallet;
pub type Pages = HashMap<String, Box<Page>>;
pub fn main_page() -> Box<Page> {
Box::new(PageHandler { app: parity_status::App::default() })
}
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) {}

99
webapp/src/lib.rs Normal file
View File

@ -0,0 +1,99 @@
// 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/>.
//! Ethcore Webapplications for Parity
#![warn(missing_docs)]
#![cfg_attr(feature="nightly", plugin(clippy))]
#[macro_use]
extern crate log;
extern crate hyper;
extern crate iron;
extern crate jsonrpc_core;
extern crate jsonrpc_http_server;
extern crate ethcore_rpc as rpc;
extern crate parity_webapp;
use std::sync::Arc;
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>,
}
impl WebappServer {
/// Construct new http server object
pub fn new() -> Self {
WebappServer {
handler: Arc::new(IoHandler::new()),
}
}
/// Add io delegate.
pub fn add_delegate<D>(&self, delegate: IoDelegate<D>) where D: Send + Sync + 'static {
self.handler.add_delegate(delegate);
}
/// Start server asynchronously and returns result with `Listening` handle on success or an error.
pub fn start_http(&self, addr: &str, threads: usize) -> Result<Listening, WebappServerError> {
let addr = addr.to_owned();
let handler = self.handler.clone();
let cors_domain = jsonrpc_http_server::AccessControlAllowOrigin::Null;
let rpc = ServerHandler::new(handler, cors_domain);
let router = router::Router::new(rpc, apps::main_page(), apps::all_pages());
try!(hyper::Server::http(addr.as_ref() as &str))
.handle_threads(router, threads)
.map(|l| Listening { listening: l })
.map_err(WebappServerError::from)
}
}
/// Listening handle
pub struct Listening {
listening: hyper::server::Listening
}
impl Drop for Listening {
fn drop(&mut self) {
self.listening.close().unwrap();
}
}
/// Webapp Server startup error
#[derive(Debug)]
pub enum WebappServerError {
/// Wrapped `std::io::Error`
IoError(std::io::Error),
/// Other `hyper` error
Other(hyper::error::Error),
}
impl From<hyper::error::Error> for WebappServerError {
fn from(err: hyper::error::Error) -> Self {
match err {
hyper::error::Error::Io(e) => WebappServerError::IoError(e),
e => WebappServerError::Other(e),
}
}
}

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);
}
}
}

53
webapp/src/router/api.rs Normal file
View File

@ -0,0 +1,53 @@
// 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/>.
//! Simple REST API
use std::sync::Arc;
use hyper;
use hyper::status::StatusCode;
use hyper::header;
use hyper::uri::RequestUri::AbsolutePath as Path;
use apps::Pages;
pub struct RestApi {
pub pages: Arc<Pages>,
}
impl RestApi {
fn list_pages(&self) -> String {
let mut s = "[".to_owned();
for name in self.pages.keys() {
s.push_str(&format!("\"{}\",", name));
}
s.push_str("\"rpc\"");
s.push_str("]");
s
}
}
impl hyper::server::Handler for RestApi {
fn handle<'b, 'a>(&'a self, req: hyper::server::Request<'a, 'b>, mut res: hyper::server::Response<'a>) {
match req.uri {
Path(ref path) if path == "apps" => {
*res.status_mut() = StatusCode::Ok;
res.headers_mut().set(header::ContentType("application/json".parse().unwrap()));
let _ = res.send(self.list_pages().as_bytes());
},
_ => (),
}
}
}

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

@ -0,0 +1,109 @@
// 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 std::sync::Arc;
use hyper;
use page::Page;
use apps::Pages;
use iron::request::Url;
use jsonrpc_http_server::ServerHandler;
mod api;
pub struct Router {
rpc: ServerHandler,
api: api::RestApi,
main_page: Box<Page>,
pages: Arc<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);
},
Some(ref url) if url == "api" => {
self.api.handle(req, res);
},
_ if req.method == hyper::method::Method::Post => {
self.rpc.handle(req, res)
},
_ => self.main_page.handle(req, res),
}
}
}
impl Router {
pub fn new(rpc: ServerHandler, main_page: Box<Page>, pages: Pages) -> Self {
let pages = Arc::new(pages);
Router {
rpc: rpc,
api: api::RestApi { pages: pages.clone() },
main_page: main_page,
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(ref url) if url.path.len() > 1 => {
let part = url.path[0].clone();
let url = url.path[1..].join("/");
req.uri = hyper::uri::RequestUri::AbsolutePath(url);
(Some(part), req)
},
Some(url) => {
let url = url.path.join("/");
req.uri = hyper::uri::RequestUri::AbsolutePath(url);
(None, req)
},
_ => {
(None, req)
},
}
}
}