Merge pull request #957 from ethcore/webapps-mio
Using hyper-mio branch in webapps.
This commit is contained in:
commit
5547b44e5a
84
Cargo.lock
generated
84
Cargo.lock
generated
@ -132,14 +132,6 @@ 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.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cookie"
|
name = "cookie"
|
||||||
version = "0.1.21"
|
version = "0.1.21"
|
||||||
@ -218,15 +210,6 @@ dependencies = [
|
|||||||
"regex 0.1.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
"regex 0.1.65 (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.3 (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"
|
||||||
@ -370,14 +353,14 @@ dependencies = [
|
|||||||
"clippy 0.0.63 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clippy 0.0.63 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethcore-rpc 1.1.0",
|
"ethcore-rpc 1.1.0",
|
||||||
"ethcore-util 1.1.0",
|
"ethcore-util 1.1.0",
|
||||||
"hyper 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hyper 0.9.0-mio (git+https://github.com/hyperium/hyper?branch=mio)",
|
||||||
"iron 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"jsonrpc-core 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonrpc-core 2.0.2 (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)",
|
"jsonrpc-http-server 5.0.1 (git+https://github.com/debris/jsonrpc-http-server.git)",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-status 0.1.7 (git+https://github.com/tomusdrw/parity-status.git)",
|
"parity-status 0.1.7 (git+https://github.com/tomusdrw/parity-status.git)",
|
||||||
"parity-wallet 0.1.1 (git+https://github.com/tomusdrw/parity-wallet.git)",
|
"parity-wallet 0.1.1 (git+https://github.com/tomusdrw/parity-wallet.git)",
|
||||||
"parity-webapp 0.1.0 (git+https://github.com/tomusdrw/parity-webapp.git)",
|
"parity-webapp 0.1.0 (git+https://github.com/tomusdrw/parity-webapp.git)",
|
||||||
|
"url 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -537,23 +520,6 @@ 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.6 (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.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.4.12"
|
version = "0.4.12"
|
||||||
@ -578,16 +544,6 @@ dependencies = [
|
|||||||
"syntex 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syntex 0.31.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.2 (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.1"
|
version = "5.0.1"
|
||||||
@ -707,11 +663,6 @@ 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 = "nanomsg"
|
name = "nanomsg"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
@ -815,14 +766,6 @@ name = "parity-webapp"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/tomusdrw/parity-webapp.git#a24297256bae0ae0712c6478cd1ad681828b3800"
|
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"
|
||||||
@ -1128,11 +1071,6 @@ name = "traitobject"
|
|||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "traitobject"
|
|
||||||
version = "0.0.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "transient-hashmap"
|
name = "transient-hashmap"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -1146,14 +1084,6 @@ 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"
|
||||||
@ -1180,14 +1110,6 @@ 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.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "0.2.38"
|
version = "0.2.38"
|
||||||
|
@ -73,7 +73,7 @@ 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")]
|
#[cfg(feature = "webapp")]
|
||||||
use webapp::Listening as WebappServer;
|
use webapp::Server as WebappServer;
|
||||||
|
|
||||||
mod price_info;
|
mod price_info;
|
||||||
mod upgrade;
|
mod upgrade;
|
||||||
@ -346,12 +346,12 @@ fn setup_webapp_server(
|
|||||||
sync: Arc<EthSync>,
|
sync: Arc<EthSync>,
|
||||||
secret_store: Arc<AccountService>,
|
secret_store: Arc<AccountService>,
|
||||||
miner: Arc<Miner>,
|
miner: Arc<Miner>,
|
||||||
url: &str,
|
url: &SocketAddr,
|
||||||
auth: Option<(String, String)>,
|
auth: Option<(String, String)>,
|
||||||
) -> WebappServer {
|
) -> WebappServer {
|
||||||
use rpc::v1::*;
|
use rpc::v1::*;
|
||||||
|
|
||||||
let server = webapp::WebappServer::new();
|
let server = webapp::ServerBuilder::new();
|
||||||
server.add_delegate(Web3Client::new().to_delegate());
|
server.add_delegate(Web3Client::new().to_delegate());
|
||||||
server.add_delegate(NetClient::new(&sync).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(EthClient::new(&client, &sync, &secret_store, &miner).to_delegate());
|
||||||
@ -360,14 +360,14 @@ fn setup_webapp_server(
|
|||||||
server.add_delegate(EthcoreClient::new(&miner).to_delegate());
|
server.add_delegate(EthcoreClient::new(&miner).to_delegate());
|
||||||
let start_result = match auth {
|
let start_result = match auth {
|
||||||
None => {
|
None => {
|
||||||
server.start_unsecure_http(url, ::num_cpus::get())
|
server.start_unsecure_http(url)
|
||||||
},
|
},
|
||||||
Some((username, password)) => {
|
Some((username, password)) => {
|
||||||
server.start_basic_auth_http(url, ::num_cpus::get(), &username, &password)
|
server.start_basic_auth_http(url, &username, &password)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
match start_result {
|
match start_result {
|
||||||
Err(webapp::WebappServerError::IoError(err)) => die_with_io_error(err),
|
Err(webapp::ServerError::IoError(err)) => die_with_io_error(err),
|
||||||
Err(e) => die!("{:?}", e),
|
Err(e) => die!("{:?}", e),
|
||||||
Ok(handle) => handle,
|
Ok(handle) => handle,
|
||||||
}
|
}
|
||||||
@ -383,7 +383,7 @@ fn setup_rpc_server(
|
|||||||
_sync: Arc<EthSync>,
|
_sync: Arc<EthSync>,
|
||||||
_secret_store: Arc<AccountService>,
|
_secret_store: Arc<AccountService>,
|
||||||
_miner: Arc<Miner>,
|
_miner: Arc<Miner>,
|
||||||
_url: &str,
|
_url: &SocketAddr,
|
||||||
_cors_domain: Option<String>,
|
_cors_domain: Option<String>,
|
||||||
_apis: Vec<&str>,
|
_apis: Vec<&str>,
|
||||||
) -> ! {
|
) -> ! {
|
||||||
@ -399,7 +399,7 @@ fn setup_webapp_server(
|
|||||||
_sync: Arc<EthSync>,
|
_sync: Arc<EthSync>,
|
||||||
_secret_store: Arc<AccountService>,
|
_secret_store: Arc<AccountService>,
|
||||||
_miner: Arc<Miner>,
|
_miner: Arc<Miner>,
|
||||||
_url: &str,
|
_url: &SocketAddr,
|
||||||
_auth: Option<(String, String)>,
|
_auth: Option<(String, String)>,
|
||||||
) -> ! {
|
) -> ! {
|
||||||
die!("Your Parity version has been compiled without WebApps support.")
|
die!("Your Parity version has been compiled without WebApps support.")
|
||||||
@ -753,6 +753,7 @@ impl Configuration {
|
|||||||
},
|
},
|
||||||
self.args.flag_webapp_port
|
self.args.flag_webapp_port
|
||||||
);
|
);
|
||||||
|
let addr = SocketAddr::from_str(&url).unwrap_or_else(|_| die!("{}: Invalid Webapps listen host/port given.", url));
|
||||||
let auth = self.args.flag_webapp_user.as_ref().map(|username| {
|
let auth = self.args.flag_webapp_user.as_ref().map(|username| {
|
||||||
let password = self.args.flag_webapp_pass.as_ref().map_or_else(|| {
|
let password = self.args.flag_webapp_pass.as_ref().map_or_else(|| {
|
||||||
use rpassword::read_password;
|
use rpassword::read_password;
|
||||||
@ -769,7 +770,7 @@ impl Configuration {
|
|||||||
sync.clone(),
|
sync.clone(),
|
||||||
account_service.clone(),
|
account_service.clone(),
|
||||||
miner.clone(),
|
miner.clone(),
|
||||||
&url,
|
&addr,
|
||||||
auth,
|
auth,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
|
@ -10,9 +10,9 @@ authors = ["Ethcore <admin@ethcore.io"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.3"
|
log = "0.3"
|
||||||
jsonrpc-core = "2.0"
|
jsonrpc-core = "2.0"
|
||||||
jsonrpc-http-server = { git = "https://github.com/tomusdrw/jsonrpc-http-server.git", branch="old-hyper" }
|
jsonrpc-http-server = { git = "https://github.com/debris/jsonrpc-http-server.git" }
|
||||||
hyper = { version = "0.8", default-features = false }
|
hyper = { default-features = false, git = "https://github.com/hyperium/hyper", branch = "mio" }
|
||||||
iron = { version = "0.3" }
|
url = "0.5"
|
||||||
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" }
|
parity-webapp = { git = "https://github.com/tomusdrw/parity-webapp.git" }
|
||||||
|
94
webapp/src/api.rs
Normal file
94
webapp/src/api.rs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
// 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::io::Write;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use hyper::status::StatusCode;
|
||||||
|
use hyper::{header, server, Decoder, Encoder, Next};
|
||||||
|
use hyper::net::HttpStream;
|
||||||
|
use endpoint::{Endpoint, Endpoints};
|
||||||
|
|
||||||
|
pub struct RestApi {
|
||||||
|
endpoints: Arc<Endpoints>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RestApi {
|
||||||
|
pub fn new(endpoints: Arc<Endpoints>) -> Box<Endpoint> {
|
||||||
|
Box::new(RestApi {
|
||||||
|
endpoints: endpoints
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list_pages(&self) -> String {
|
||||||
|
let mut s = "[".to_owned();
|
||||||
|
for name in self.endpoints.keys() {
|
||||||
|
s.push_str(&format!("\"{}\",", name));
|
||||||
|
}
|
||||||
|
s.push_str("\"rpc\"");
|
||||||
|
s.push_str("]");
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Endpoint for RestApi {
|
||||||
|
fn to_handler(&self, _prefix: &str) -> Box<server::Handler<HttpStream>> {
|
||||||
|
Box::new(RestApiHandler {
|
||||||
|
pages: self.list_pages(),
|
||||||
|
write_pos: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RestApiHandler {
|
||||||
|
pages: String,
|
||||||
|
write_pos: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl server::Handler<HttpStream> for RestApiHandler {
|
||||||
|
fn on_request(&mut self, _request: server::Request) -> Next {
|
||||||
|
Next::write()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
|
||||||
|
Next::write()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||||
|
res.set_status(StatusCode::Ok);
|
||||||
|
res.headers_mut().set(header::ContentType("application/json".parse().unwrap()));
|
||||||
|
Next::write()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||||
|
let bytes = self.pages.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()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -14,28 +14,28 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use endpoint::Endpoints;
|
||||||
use page::{Page, PageHandler};
|
use page::PageEndpoint;
|
||||||
|
|
||||||
extern crate parity_status;
|
extern crate parity_status;
|
||||||
extern crate parity_wallet;
|
extern crate parity_wallet;
|
||||||
|
|
||||||
pub type Pages = HashMap<String, Box<Page>>;
|
|
||||||
|
|
||||||
pub fn main_page() -> Box<Page> {
|
pub fn main_page() -> &'static str {
|
||||||
Box::new(PageHandler { app: parity_status::App::default() })
|
"/status/"
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn all_pages() -> Pages {
|
pub fn all_endpoints() -> Endpoints {
|
||||||
let mut pages = Pages::new();
|
let mut pages = Endpoints::new();
|
||||||
|
pages.insert("status".to_owned(), Box::new(PageEndpoint::new(parity_status::App::default())));
|
||||||
wallet_page(&mut pages);
|
wallet_page(&mut pages);
|
||||||
pages
|
pages
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "parity-wallet")]
|
#[cfg(feature = "parity-wallet")]
|
||||||
fn wallet_page(pages: &mut Pages) {
|
fn wallet_page(pages: &mut Endpoints) {
|
||||||
pages.insert("wallet".to_owned(), Box::new(PageHandler { app: parity_wallet::App::default() }));
|
pages.insert("wallet".to_owned(), Box::new(PageEndpoint::new(parity_wallet::App::default())));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "parity-wallet"))]
|
#[cfg(not(feature = "parity-wallet"))]
|
||||||
fn wallet_page(_pages: &mut Pages) {}
|
fn wallet_page(_pages: &mut Endpoints) {}
|
||||||
|
27
webapp/src/endpoint.rs
Normal file
27
webapp/src/endpoint.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
//! URL Endpoint traits
|
||||||
|
|
||||||
|
use hyper::server;
|
||||||
|
use hyper::net::HttpStream;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub trait Endpoint : Send + Sync {
|
||||||
|
fn to_handler(&self, prefix: &str) -> Box<server::Handler<HttpStream>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Endpoints = HashMap<String, Box<Endpoint>>;
|
@ -15,38 +15,64 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! Ethcore Webapplications for Parity
|
//! Ethcore Webapplications for Parity
|
||||||
|
//! ```
|
||||||
|
//! extern crate jsonrpc_core;
|
||||||
|
//! extern crate ethcore_webapp;
|
||||||
|
//!
|
||||||
|
//! use std::sync::Arc;
|
||||||
|
//! use jsonrpc_core::IoHandler;
|
||||||
|
//! use ethcore_webapp::*;
|
||||||
|
//!
|
||||||
|
//! struct SayHello;
|
||||||
|
//! impl MethodCommand for SayHello {
|
||||||
|
//! fn execute(&self, _params: Params) -> Result<Value, Error> {
|
||||||
|
//! Ok(Value::String("hello".to_string()))
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! fn main() {
|
||||||
|
//! let io = IoHandler::new();
|
||||||
|
//! io.add_method("say_hello", SayHello);
|
||||||
|
//! let _server = Server::start_unsecure_http(
|
||||||
|
//! &"127.0.0.1:3030".parse().unwrap(),
|
||||||
|
//! Arc::new(io)
|
||||||
|
//! );
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![cfg_attr(feature="nightly", plugin(clippy))]
|
#![cfg_attr(feature="nightly", plugin(clippy))]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
extern crate url;
|
||||||
extern crate hyper;
|
extern crate hyper;
|
||||||
extern crate iron;
|
|
||||||
extern crate jsonrpc_core;
|
extern crate jsonrpc_core;
|
||||||
extern crate jsonrpc_http_server;
|
extern crate jsonrpc_http_server;
|
||||||
extern crate ethcore_rpc as rpc;
|
|
||||||
extern crate parity_webapp;
|
extern crate parity_webapp;
|
||||||
|
|
||||||
use std::sync::Arc;
|
mod endpoint;
|
||||||
use self::jsonrpc_core::{IoHandler, IoDelegate};
|
|
||||||
use jsonrpc_http_server::ServerHandler;
|
|
||||||
|
|
||||||
mod apps;
|
mod apps;
|
||||||
mod page;
|
mod page;
|
||||||
mod router;
|
mod router;
|
||||||
|
mod rpc;
|
||||||
|
mod api;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
use jsonrpc_core::{IoHandler, IoDelegate};
|
||||||
use router::auth::{Authorization, NoAuth, HttpBasicAuth};
|
use router::auth::{Authorization, NoAuth, HttpBasicAuth};
|
||||||
|
|
||||||
/// Http server.
|
/// Webapps HTTP+RPC server build.
|
||||||
pub struct WebappServer {
|
pub struct ServerBuilder {
|
||||||
handler: Arc<IoHandler>,
|
handler: Arc<IoHandler>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebappServer {
|
impl ServerBuilder {
|
||||||
/// Construct new http server object
|
/// Construct new webapps
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
WebappServer {
|
ServerBuilder {
|
||||||
handler: Arc::new(IoHandler::new()),
|
handler: Arc::new(IoHandler::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,57 +82,63 @@ impl WebappServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Asynchronously start server with no authentication,
|
/// Asynchronously start server with no authentication,
|
||||||
/// return result with `Listening` handle on success or an error.
|
/// returns result with `Server` handle on success or an error.
|
||||||
pub fn start_unsecure_http(&self, addr: &str, threads: usize) -> Result<Listening, WebappServerError> {
|
pub fn start_unsecure_http(&self, addr: &SocketAddr) -> Result<Server, ServerError> {
|
||||||
self.start_http(addr, threads, NoAuth)
|
Server::start_http(addr, NoAuth, self.handler.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Asynchronously start server with `HTTP Basic Authentication`,
|
/// Asynchronously start server with `HTTP Basic Authentication`,
|
||||||
/// return result with `Listening` handle on success or an error.
|
/// return result with `Server` handle on success or an error.
|
||||||
pub fn start_basic_auth_http(&self, addr: &str, threads: usize, username: &str, password: &str) -> Result<Listening, WebappServerError> {
|
pub fn start_basic_auth_http(&self, addr: &SocketAddr, username: &str, password: &str) -> Result<Server, ServerError> {
|
||||||
self.start_http(addr, threads, HttpBasicAuth::single_user(username, password))
|
Server::start_http(addr, HttpBasicAuth::single_user(username, password), self.handler.clone())
|
||||||
}
|
|
||||||
|
|
||||||
fn start_http<A: Authorization + 'static>(&self, addr: &str, threads: usize, authorization: A) -> 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(), authorization);
|
|
||||||
|
|
||||||
try!(hyper::Server::http(addr.as_ref() as &str))
|
|
||||||
.handle_threads(router, threads)
|
|
||||||
.map(|l| Listening { listening: l })
|
|
||||||
.map_err(WebappServerError::from)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Listening handle
|
/// Webapps HTTP server.
|
||||||
pub struct Listening {
|
pub struct Server {
|
||||||
listening: hyper::server::Listening
|
server: Option<hyper::server::Listening>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Listening {
|
impl Server {
|
||||||
|
fn start_http<A: Authorization + 'static>(addr: &SocketAddr, authorization: A, handler: Arc<IoHandler>) -> Result<Server, ServerError> {
|
||||||
|
let endpoints = Arc::new(apps::all_endpoints());
|
||||||
|
let authorization = Arc::new(authorization);
|
||||||
|
let rpc_endpoint = Arc::new(rpc::rpc(handler));
|
||||||
|
let api = Arc::new(api::RestApi::new(endpoints.clone()));
|
||||||
|
|
||||||
|
try!(hyper::Server::http(addr))
|
||||||
|
.handle(move |_| router::Router::new(
|
||||||
|
apps::main_page(),
|
||||||
|
endpoints.clone(),
|
||||||
|
rpc_endpoint.clone(),
|
||||||
|
api.clone(),
|
||||||
|
authorization.clone(),
|
||||||
|
))
|
||||||
|
.map(|l| Server { server: Some(l) })
|
||||||
|
.map_err(ServerError::from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Server {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.listening.close().unwrap();
|
self.server.take().unwrap().close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Webapp Server startup error
|
/// Webapp Server startup error
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum WebappServerError {
|
pub enum ServerError {
|
||||||
/// Wrapped `std::io::Error`
|
/// Wrapped `std::io::Error`
|
||||||
IoError(std::io::Error),
|
IoError(std::io::Error),
|
||||||
/// Other `hyper` error
|
/// Other `hyper` error
|
||||||
Other(hyper::error::Error),
|
Other(hyper::error::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<hyper::error::Error> for WebappServerError {
|
impl From<hyper::error::Error> for ServerError {
|
||||||
fn from(err: hyper::error::Error) -> Self {
|
fn from(err: hyper::error::Error) -> Self {
|
||||||
match err {
|
match err {
|
||||||
hyper::error::Error::Io(e) => WebappServerError::IoError(e),
|
hyper::error::Error::Io(e) => ServerError::IoError(e),
|
||||||
e => WebappServerError::Other(e),
|
e => ServerError::Other(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,54 +14,94 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use hyper::uri::RequestUri;
|
use hyper::uri::RequestUri;
|
||||||
use hyper::server;
|
use hyper::server;
|
||||||
use hyper::header;
|
use hyper::header;
|
||||||
use hyper::status::StatusCode;
|
use hyper::status::StatusCode;
|
||||||
|
use hyper::net::HttpStream;
|
||||||
|
use hyper::{Decoder, Encoder, Next};
|
||||||
|
use endpoint::Endpoint;
|
||||||
use parity_webapp::WebApp;
|
use parity_webapp::WebApp;
|
||||||
|
|
||||||
pub trait Page : Send + Sync {
|
pub struct PageEndpoint<T : WebApp + 'static> {
|
||||||
fn serve_file(&self, mut path: &str, mut res: server::Response);
|
pub app: Arc<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PageHandler<T : WebApp> {
|
impl<T: WebApp + 'static> PageEndpoint<T> {
|
||||||
pub app: T,
|
pub fn new(app: T) -> Self {
|
||||||
}
|
PageEndpoint {
|
||||||
|
app: Arc::new(app)
|
||||||
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() {
|
impl<T: WebApp> Endpoint for PageEndpoint<T> {
|
||||||
Ok(mut raw_res) => {
|
fn to_handler(&self, prefix: &str) -> Box<server::Handler<HttpStream>> {
|
||||||
for chunk in f.content.chunks(1024 * 20) {
|
Box::new(PageHandler {
|
||||||
let _ = raw_res.write(chunk);
|
app: self.app.clone(),
|
||||||
}
|
prefix: prefix.to_owned(),
|
||||||
raw_res.end()
|
prefix_with_slash: prefix.to_owned() + "/",
|
||||||
},
|
path: None,
|
||||||
Err(_) => {
|
write_pos: 0,
|
||||||
println!("Error while writing response.");
|
})
|
||||||
Ok(())
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
|
struct PageHandler<T: WebApp + 'static> {
|
||||||
|
app: Arc<T>,
|
||||||
|
prefix: String,
|
||||||
|
prefix_with_slash: String,
|
||||||
|
path: Option<String>,
|
||||||
|
write_pos: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: WebApp + 'static> server::Handler<HttpStream> for PageHandler<T> {
|
||||||
|
fn on_request(&mut self, req: server::Request) -> Next {
|
||||||
|
if let RequestUri::AbsolutePath(ref path) = *req.uri() {
|
||||||
|
// Index file support
|
||||||
|
self.path = match path == &self.prefix || path == &self.prefix_with_slash {
|
||||||
|
true => Some("index.html".to_owned()),
|
||||||
|
false => Some(path[self.prefix_with_slash.len()..].to_owned()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Next::write()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl server::Handler for Page {
|
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
|
||||||
fn handle(&self, req: server::Request, mut res: server::Response) {
|
Next::write()
|
||||||
*res.status_mut() = StatusCode::NotFound;
|
}
|
||||||
|
|
||||||
if let RequestUri::AbsolutePath(ref path) = req.uri {
|
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||||
self.serve_file(path, res);
|
if let Some(f) = self.path.as_ref().and_then(|f| self.app.file(f)) {
|
||||||
|
res.set_status(StatusCode::Ok);
|
||||||
|
res.headers_mut().set(header::ContentType(f.content_type.parse().unwrap()));
|
||||||
|
Next::write()
|
||||||
|
} else {
|
||||||
|
res.set_status(StatusCode::NotFound);
|
||||||
|
Next::write()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||||
|
let (wrote, res) = {
|
||||||
|
let file = self.path.as_ref().and_then(|f| self.app.file(f));
|
||||||
|
match file {
|
||||||
|
None => (None, Next::end()),
|
||||||
|
Some(f) if self.write_pos == f.content.len() => (None, Next::end()),
|
||||||
|
Some(f) => match encoder.write(&f.content[self.write_pos..]) {
|
||||||
|
Ok(bytes) => (Some(bytes), Next::write()),
|
||||||
|
Err(e) => match e.kind() {
|
||||||
|
::std::io::ErrorKind::WouldBlock => (None, Next::write()),
|
||||||
|
_ => (None, Next::end())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if let Some(bytes) = wrote {
|
||||||
|
self.write_pos += bytes;
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
// 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());
|
|
||||||
},
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -16,22 +16,24 @@
|
|||||||
|
|
||||||
//! HTTP Authorization implementations
|
//! HTTP Authorization implementations
|
||||||
|
|
||||||
|
use std::io::Write;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use hyper::{header, server};
|
use hyper::{header, server, Decoder, Encoder, Next};
|
||||||
|
use hyper::net::HttpStream;
|
||||||
use hyper::status::StatusCode;
|
use hyper::status::StatusCode;
|
||||||
|
|
||||||
/// Authorization result
|
/// Authorization result
|
||||||
pub enum Authorized<'a, 'b> where 'b : 'a {
|
pub enum Authorized {
|
||||||
/// Authorization was successful. Request and Response are returned for further processing.
|
/// Authorization was successful.
|
||||||
Yes(server::Request<'a, 'b>, server::Response<'a>),
|
Yes,
|
||||||
/// Unsuccessful authorization. Request and Response has been consumed.
|
/// Unsuccessful authorization. Handler for further work is returned.
|
||||||
No,
|
No(Box<server::Handler<HttpStream>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Authorization interface
|
/// Authorization interface
|
||||||
pub trait Authorization : Send + Sync {
|
pub trait Authorization : Send + Sync {
|
||||||
/// Handle authorization process and return `Request` and `Response` when authorization is successful.
|
/// Checks if authorization is valid.
|
||||||
fn handle<'b, 'a>(&'a self, req: server::Request<'a, 'b>, res: server::Response<'a>)-> Authorized<'a, 'b>;
|
fn is_authorized(&self, req: &server::Request)-> Authorized;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP Basic Authorization handler
|
/// HTTP Basic Authorization handler
|
||||||
@ -43,27 +45,24 @@ pub struct HttpBasicAuth {
|
|||||||
pub struct NoAuth;
|
pub struct NoAuth;
|
||||||
|
|
||||||
impl Authorization for NoAuth {
|
impl Authorization for NoAuth {
|
||||||
fn handle<'b, 'a>(&'a self, req: server::Request<'a, 'b>, res: server::Response<'a>)-> Authorized<'a, 'b> {
|
fn is_authorized(&self, _req: &server::Request)-> Authorized {
|
||||||
Authorized::Yes(req, res)
|
Authorized::Yes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Authorization for HttpBasicAuth {
|
impl Authorization for HttpBasicAuth {
|
||||||
|
fn is_authorized(&self, req: &server::Request) -> Authorized {
|
||||||
fn handle<'b, 'a>(&'a self, req: server::Request<'a, 'b>, res: server::Response<'a>)-> Authorized<'a, 'b> {
|
|
||||||
let auth = self.check_auth(&req);
|
let auth = self.check_auth(&req);
|
||||||
|
|
||||||
match auth {
|
match auth {
|
||||||
Access::Denied => {
|
Access::Denied => {
|
||||||
self.respond_with_unauthorized(res);
|
Authorized::No(Box::new(UnauthorizedHandler { write_pos: 0 }))
|
||||||
Authorized::No
|
|
||||||
},
|
},
|
||||||
Access::AuthRequired => {
|
Access::AuthRequired => {
|
||||||
self.respond_with_auth_required(res);
|
Authorized::No(Box::new(AuthRequiredHandler))
|
||||||
Authorized::No
|
|
||||||
},
|
},
|
||||||
Access::Granted => {
|
Access::Granted => {
|
||||||
Authorized::Yes(req, res)
|
Authorized::Yes
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -90,7 +89,7 @@ impl HttpBasicAuth {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn check_auth(&self, req: &server::Request) -> Access {
|
fn check_auth(&self, req: &server::Request) -> Access {
|
||||||
match req.headers.get::<header::Authorization<header::Basic>>() {
|
match req.headers().get::<header::Authorization<header::Basic>>() {
|
||||||
Some(&header::Authorization(
|
Some(&header::Authorization(
|
||||||
header::Basic { ref username, password: Some(ref password) }
|
header::Basic { ref username, password: Some(ref password) }
|
||||||
)) if self.is_authorized(username, password) => Access::Granted,
|
)) if self.is_authorized(username, password) => Access::Granted,
|
||||||
@ -98,15 +97,64 @@ impl HttpBasicAuth {
|
|||||||
None => Access::AuthRequired,
|
None => Access::AuthRequired,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn respond_with_unauthorized(&self, mut res: server::Response) {
|
pub struct UnauthorizedHandler {
|
||||||
*res.status_mut() = StatusCode::Unauthorized;
|
write_pos: usize,
|
||||||
let _ = res.send(b"Unauthorized");
|
}
|
||||||
|
|
||||||
|
impl server::Handler<HttpStream> for UnauthorizedHandler {
|
||||||
|
fn on_request(&mut self, _request: server::Request) -> Next {
|
||||||
|
Next::write()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn respond_with_auth_required(&self, mut res: server::Response) {
|
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
|
||||||
*res.status_mut() = StatusCode::Unauthorized;
|
Next::write()
|
||||||
res.headers_mut().set_raw("WWW-Authenticate", vec![b"Basic realm=\"Parity\"".to_vec()]);
|
}
|
||||||
|
|
||||||
|
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<HttpStream>) -> 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<HttpStream> for AuthRequiredHandler {
|
||||||
|
fn on_request(&mut self, _request: server::Request) -> Next {
|
||||||
|
Next::write()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> 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::end()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_response_writable(&mut self, _encoder: &mut Encoder<HttpStream>) -> Next {
|
||||||
|
Next::end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -17,66 +17,97 @@
|
|||||||
//! Router implementation
|
//! Router implementation
|
||||||
//! Processes request handling authorization and dispatching it to proper application.
|
//! Processes request handling authorization and dispatching it to proper application.
|
||||||
|
|
||||||
mod api;
|
mod url;
|
||||||
|
mod redirect;
|
||||||
pub mod auth;
|
pub mod auth;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use hyper;
|
use hyper;
|
||||||
use hyper::{server, uri, header};
|
use hyper::{server, uri, header};
|
||||||
use page::Page;
|
use hyper::{Next, Encoder, Decoder};
|
||||||
use apps::Pages;
|
use hyper::net::HttpStream;
|
||||||
use iron::request::Url;
|
use endpoint::{Endpoint, Endpoints};
|
||||||
use jsonrpc_http_server::ServerHandler;
|
use self::url::Url;
|
||||||
use self::auth::{Authorization, Authorized};
|
use self::auth::{Authorization, Authorized};
|
||||||
|
use self::redirect::Redirection;
|
||||||
|
|
||||||
pub struct Router<A: Authorization> {
|
pub struct Router<A: Authorization + 'static> {
|
||||||
authorization: A,
|
main_page: &'static str,
|
||||||
rpc: ServerHandler,
|
endpoints: Arc<Endpoints>,
|
||||||
api: api::RestApi,
|
rpc: Arc<Box<Endpoint>>,
|
||||||
main_page: Box<Page>,
|
api: Arc<Box<Endpoint>>,
|
||||||
pages: Arc<Pages>,
|
authorization: Arc<A>,
|
||||||
|
handler: Box<server::Handler<HttpStream>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: Authorization> server::Handler for Router<A> {
|
impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
|
||||||
fn handle<'b, 'a>(&'a self, req: server::Request<'a, 'b>, res: server::Response<'a>) {
|
|
||||||
let auth = self.authorization.handle(req, res);
|
|
||||||
|
|
||||||
if let Authorized::Yes(req, res) = auth {
|
fn on_request(&mut self, req: server::Request) -> Next {
|
||||||
let (path, req) = self.extract_request_path(req);
|
let auth = self.authorization.is_authorized(&req);
|
||||||
match path {
|
self.handler = match auth {
|
||||||
Some(ref url) if self.pages.contains_key(url) => {
|
Authorized::No(handler) => handler,
|
||||||
self.pages.get(url).unwrap().handle(req, res);
|
Authorized::Yes => {
|
||||||
},
|
let path = self.extract_request_path(&req);
|
||||||
Some(ref url) if url == "api" => {
|
match path {
|
||||||
self.api.handle(req, res);
|
Some(ref url) if self.endpoints.contains_key(url) => {
|
||||||
},
|
let prefix = "/".to_owned() + url;
|
||||||
_ if req.method == hyper::method::Method::Post => {
|
self.endpoints.get(url).unwrap().to_handler(&prefix)
|
||||||
self.rpc.handle(req, res)
|
},
|
||||||
},
|
Some(ref url) if url == "api" => {
|
||||||
_ => self.main_page.handle(req, res),
|
self.api.to_handler("/api")
|
||||||
|
},
|
||||||
|
_ if *req.method() == hyper::method::Method::Get => {
|
||||||
|
Redirection::new(self.main_page)
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
self.rpc.to_handler(&"/")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
self.handler.on_request(req)
|
||||||
|
// Check authorization
|
||||||
|
// Choose proper handler depending on path
|
||||||
|
// Delegate on_request to proper handler
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This event occurs each time the `Request` is ready to be read from.
|
||||||
|
fn on_request_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
|
||||||
|
self.handler.on_request_readable(decoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This event occurs after the first time this handled signals `Next::write()`.
|
||||||
|
fn on_response(&mut self, response: &mut server::Response) -> Next {
|
||||||
|
self.handler.on_response(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This event occurs each time the `Response` is ready to be written to.
|
||||||
|
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||||
|
self.handler.on_response_writable(encoder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: Authorization> Router<A> {
|
impl<A: Authorization> Router<A> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
rpc: ServerHandler,
|
main_page: &'static str,
|
||||||
main_page: Box<Page>,
|
endpoints: Arc<Endpoints>,
|
||||||
pages: Pages,
|
rpc: Arc<Box<Endpoint>>,
|
||||||
authorization: A) -> Self {
|
api: Arc<Box<Endpoint>>,
|
||||||
let pages = Arc::new(pages);
|
authorization: Arc<A>) -> Self {
|
||||||
|
|
||||||
|
let handler = rpc.to_handler(&"/");
|
||||||
Router {
|
Router {
|
||||||
authorization: authorization,
|
|
||||||
rpc: rpc,
|
|
||||||
api: api::RestApi { pages: pages.clone() },
|
|
||||||
main_page: main_page,
|
main_page: main_page,
|
||||||
pages: pages,
|
endpoints: endpoints,
|
||||||
|
rpc: rpc,
|
||||||
|
api: api,
|
||||||
|
authorization: authorization,
|
||||||
|
handler: handler,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_url(&self, req: &server::Request) -> Option<Url> {
|
fn extract_url(&self, req: &server::Request) -> Option<Url> {
|
||||||
match req.uri {
|
match *req.uri() {
|
||||||
uri::RequestUri::AbsoluteUri(ref url) => {
|
uri::RequestUri::AbsoluteUri(ref url) => {
|
||||||
match Url::from_generic_url(url.clone()) {
|
match Url::from_generic_url(url.clone()) {
|
||||||
Ok(url) => Some(url),
|
Ok(url) => Some(url),
|
||||||
@ -85,7 +116,7 @@ impl<A: Authorization> Router<A> {
|
|||||||
},
|
},
|
||||||
uri::RequestUri::AbsolutePath(ref path) => {
|
uri::RequestUri::AbsolutePath(ref path) => {
|
||||||
// Attempt to prepend the Host header (mandatory in HTTP/1.1)
|
// Attempt to prepend the Host header (mandatory in HTTP/1.1)
|
||||||
let url_string = match req.headers.get::<header::Host>() {
|
let url_string = match req.headers().get::<header::Host>() {
|
||||||
Some(ref host) => {
|
Some(ref host) => {
|
||||||
format!("http://{}:{}{}", host.hostname, host.port.unwrap_or(80), path)
|
format!("http://{}:{}{}", host.hostname, host.port.unwrap_or(80), path)
|
||||||
},
|
},
|
||||||
@ -101,22 +132,15 @@ impl<A: Authorization> Router<A> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_request_path<'a, 'b>(&self, mut req: server::Request<'a, 'b>) -> (Option<String>, server::Request<'a, 'b>) {
|
fn extract_request_path(&self, req: &server::Request) -> Option<String> {
|
||||||
let url = self.extract_url(&req);
|
let url = self.extract_url(&req);
|
||||||
match url {
|
match url {
|
||||||
Some(ref url) if url.path.len() > 1 => {
|
Some(ref url) if url.path.len() > 1 => {
|
||||||
let part = url.path[0].clone();
|
let part = url.path[0].clone();
|
||||||
let url = url.path[1..].join("/");
|
Some(part)
|
||||||
req.uri = uri::RequestUri::AbsolutePath(url);
|
|
||||||
(Some(part), req)
|
|
||||||
},
|
|
||||||
Some(url) => {
|
|
||||||
let url = url.path.join("/");
|
|
||||||
req.uri = uri::RequestUri::AbsolutePath(url);
|
|
||||||
(None, req)
|
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
(None, req)
|
None
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
56
webapp/src/router/redirect.rs
Normal file
56
webapp/src/router/redirect.rs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
//! HTTP Redirection hyper handler
|
||||||
|
|
||||||
|
use std::io::Write;
|
||||||
|
use hyper::{header, server, Decoder, Encoder, Next};
|
||||||
|
use hyper::net::HttpStream;
|
||||||
|
use hyper::status::StatusCode;
|
||||||
|
|
||||||
|
pub struct Redirection {
|
||||||
|
to_url: &'static str
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Redirection {
|
||||||
|
pub fn new(url: &'static str) -> Box<Self> {
|
||||||
|
Box::new(Redirection {
|
||||||
|
to_url: url
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl server::Handler<HttpStream> for Redirection {
|
||||||
|
fn on_request(&mut self, _request: server::Request) -> Next {
|
||||||
|
Next::write()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
|
||||||
|
Next::write()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||||
|
res.set_status(StatusCode::MovedPermanently);
|
||||||
|
res.headers_mut().set(header::Location(self.to_url.to_owned()));
|
||||||
|
Next::end()
|
||||||
|
}
|
||||||
|
fn on_response_writable(&mut self, _encoder: &mut Encoder<HttpStream>) -> Next {
|
||||||
|
Next::end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
177
webapp/src/router/url.rs
Normal file
177
webapp/src/router/url.rs
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
//! HTTP/HTTPS URL type. Based on URL type from Iron library.
|
||||||
|
|
||||||
|
use url::Host;
|
||||||
|
use url::{whatwg_scheme_type_mapper};
|
||||||
|
use url::{self, SchemeData, SchemeType};
|
||||||
|
|
||||||
|
/// HTTP/HTTPS URL type for Iron.
|
||||||
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||||
|
pub struct Url {
|
||||||
|
/// The lower-cased scheme of the URL, typically "http" or "https".
|
||||||
|
pub scheme: String,
|
||||||
|
|
||||||
|
/// The host field of the URL, probably a domain.
|
||||||
|
pub host: Host,
|
||||||
|
|
||||||
|
/// The connection port.
|
||||||
|
pub port: u16,
|
||||||
|
|
||||||
|
/// The URL path, the resource to be accessed.
|
||||||
|
///
|
||||||
|
/// A *non-empty* vector encoding the parts of the URL path.
|
||||||
|
/// Empty entries of `""` correspond to trailing slashes.
|
||||||
|
pub path: Vec<String>,
|
||||||
|
|
||||||
|
/// The URL username field, from the userinfo section of the URL.
|
||||||
|
///
|
||||||
|
/// `None` if the `@` character was not part of the input OR
|
||||||
|
/// if a blank username was provided.
|
||||||
|
/// Otherwise, a non-empty string.
|
||||||
|
pub username: Option<String>,
|
||||||
|
|
||||||
|
/// The URL password field, from the userinfo section of the URL.
|
||||||
|
///
|
||||||
|
/// `None` if the `@` character was not part of the input OR
|
||||||
|
/// if a blank password was provided.
|
||||||
|
/// Otherwise, a non-empty string.
|
||||||
|
pub password: Option<String>,
|
||||||
|
|
||||||
|
/// The URL query string.
|
||||||
|
///
|
||||||
|
/// `None` if the `?` character was not part of the input.
|
||||||
|
/// Otherwise, a possibly empty, percent encoded string.
|
||||||
|
pub query: Option<String>,
|
||||||
|
|
||||||
|
/// The URL fragment.
|
||||||
|
///
|
||||||
|
/// `None` if the `#` character was not part of the input.
|
||||||
|
/// Otherwise, a possibly empty, percent encoded string.
|
||||||
|
pub fragment: Option<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Url {
|
||||||
|
/// Create a URL from a string.
|
||||||
|
///
|
||||||
|
/// The input must be a valid URL with a special scheme for this to succeed.
|
||||||
|
///
|
||||||
|
/// HTTP and HTTPS are special schemes.
|
||||||
|
///
|
||||||
|
/// See: http://url.spec.whatwg.org/#special-scheme
|
||||||
|
pub fn parse(input: &str) -> Result<Url, String> {
|
||||||
|
// Parse the string using rust-url, then convert.
|
||||||
|
match url::Url::parse(input) {
|
||||||
|
Ok(raw_url) => Url::from_generic_url(raw_url),
|
||||||
|
Err(e) => Err(format!("{}", e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a `Url` from a `rust-url` `Url`.
|
||||||
|
pub fn from_generic_url(raw_url: url::Url) -> Result<Url, String> {
|
||||||
|
// Create an Iron URL by extracting the special scheme data.
|
||||||
|
match raw_url.scheme_data {
|
||||||
|
SchemeData::Relative(data) => {
|
||||||
|
// Extract the port as a 16-bit unsigned integer.
|
||||||
|
let port: u16 = match data.port {
|
||||||
|
// If explicitly defined, unwrap it.
|
||||||
|
Some(port) => port,
|
||||||
|
|
||||||
|
// Otherwise, use the scheme's default port.
|
||||||
|
None => {
|
||||||
|
match whatwg_scheme_type_mapper(&raw_url.scheme) {
|
||||||
|
SchemeType::Relative(port) => port,
|
||||||
|
_ => return Err(format!("Invalid special scheme: `{}`",
|
||||||
|
raw_url.scheme))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Map empty usernames to None.
|
||||||
|
let username = match &*data.username {
|
||||||
|
"" => None,
|
||||||
|
_ => Some(data.username)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Map empty passwords to None.
|
||||||
|
let password = match data.password {
|
||||||
|
None => None,
|
||||||
|
Some(ref x) if x.is_empty() => None,
|
||||||
|
Some(password) => Some(password)
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Url {
|
||||||
|
scheme: raw_url.scheme,
|
||||||
|
host: data.host,
|
||||||
|
port: port,
|
||||||
|
path: data.path,
|
||||||
|
username: username,
|
||||||
|
password: password,
|
||||||
|
query: raw_url.query,
|
||||||
|
fragment: raw_url.fragment
|
||||||
|
})
|
||||||
|
},
|
||||||
|
_ => Err(format!("Not a special scheme: `{}`", raw_url.scheme))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::Url;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_default_port() {
|
||||||
|
assert_eq!(Url::parse("http://example.com/wow").unwrap().port, 80u16);
|
||||||
|
assert_eq!(Url::parse("https://example.com/wow").unwrap().port, 443u16);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_explicit_port() {
|
||||||
|
assert_eq!(Url::parse("http://localhost:3097").unwrap().port, 3097u16);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_empty_username() {
|
||||||
|
assert!(Url::parse("http://@example.com").unwrap().username.is_none());
|
||||||
|
assert!(Url::parse("http://:password@example.com").unwrap().username.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_not_empty_username() {
|
||||||
|
let user = Url::parse("http://john:pass@example.com").unwrap().username;
|
||||||
|
assert_eq!(user.unwrap(), "john");
|
||||||
|
|
||||||
|
let user = Url::parse("http://john:@example.com").unwrap().username;
|
||||||
|
assert_eq!(user.unwrap(), "john");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_empty_password() {
|
||||||
|
assert!(Url::parse("http://michael@example.com").unwrap().password.is_none());
|
||||||
|
assert!(Url::parse("http://:@example.com").unwrap().password.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_not_empty_password() {
|
||||||
|
let pass = Url::parse("http://michael:pass@example.com").unwrap().password;
|
||||||
|
assert_eq!(pass.unwrap(), "pass");
|
||||||
|
|
||||||
|
let pass = Url::parse("http://:pass@example.com").unwrap().password;
|
||||||
|
assert_eq!(pass.unwrap(), "pass");
|
||||||
|
}
|
||||||
|
}
|
41
webapp/src/rpc.rs
Normal file
41
webapp/src/rpc.rs
Normal 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::sync::Arc;
|
||||||
|
use hyper::server;
|
||||||
|
use hyper::net::HttpStream;
|
||||||
|
use jsonrpc_core::IoHandler;
|
||||||
|
use jsonrpc_http_server::ServerHandler;
|
||||||
|
use jsonrpc_http_server::AccessControlAllowOrigin;
|
||||||
|
use endpoint::Endpoint;
|
||||||
|
|
||||||
|
pub fn rpc(handler: Arc<IoHandler>) -> Box<Endpoint> {
|
||||||
|
Box::new(RpcEndpoint {
|
||||||
|
handler: handler,
|
||||||
|
cors_domain: Some(AccessControlAllowOrigin::Null)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RpcEndpoint {
|
||||||
|
handler: Arc<IoHandler>,
|
||||||
|
cors_domain: Option<AccessControlAllowOrigin>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Endpoint for RpcEndpoint {
|
||||||
|
fn to_handler(&self, _prefix: &str) -> Box<server::Handler<HttpStream>> {
|
||||||
|
Box::new(ServerHandler::new(self.handler.clone(), self.cors_domain.clone()))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user