Merge branch 'master' into kill_unwraps
This commit is contained in:
commit
9afbc9816d
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -283,6 +283,7 @@ dependencies = [
|
|||||||
"serde_codegen 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_codegen 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ log = "0.3"
|
|||||||
jsonrpc-core = "2.0"
|
jsonrpc-core = "2.0"
|
||||||
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc-http-server.git" }
|
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc-http-server.git" }
|
||||||
hyper = { default-features = false, git = "https://github.com/ethcore/hyper" }
|
hyper = { default-features = false, git = "https://github.com/ethcore/hyper" }
|
||||||
|
unicase = "1.3"
|
||||||
url = "1.0"
|
url = "1.0"
|
||||||
rustc-serialize = "0.3"
|
rustc-serialize = "0.3"
|
||||||
serde = "0.7.0"
|
serde = "0.7.0"
|
||||||
|
@ -15,20 +15,23 @@
|
|||||||
// 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::sync::Arc;
|
||||||
use endpoint::{Endpoint, Endpoints, Handler, EndpointPath};
|
|
||||||
use api::types::{App, ApiError};
|
|
||||||
use api::response::{as_json, as_json_error};
|
|
||||||
use hyper::{server, net, Decoder, Encoder, Next};
|
use hyper::{server, net, Decoder, Encoder, Next};
|
||||||
|
use api::types::{App, ApiError};
|
||||||
|
use api::response::{as_json, as_json_error, ping_response};
|
||||||
|
use handlers::extract_url;
|
||||||
|
use endpoint::{Endpoint, Endpoints, Handler, EndpointPath};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RestApi {
|
pub struct RestApi {
|
||||||
|
local_domain: String,
|
||||||
endpoints: Arc<Endpoints>,
|
endpoints: Arc<Endpoints>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RestApi {
|
impl RestApi {
|
||||||
pub fn new(endpoints: Arc<Endpoints>) -> Box<Endpoint> {
|
pub fn new(local_domain: String, endpoints: Arc<Endpoints>) -> Box<Endpoint> {
|
||||||
Box::new(RestApi {
|
Box::new(RestApi {
|
||||||
endpoints: endpoints
|
local_domain: local_domain,
|
||||||
|
endpoints: endpoints,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,9 +62,28 @@ struct RestApiRouter {
|
|||||||
|
|
||||||
impl server::Handler<net::HttpStream> for RestApiRouter {
|
impl server::Handler<net::HttpStream> for RestApiRouter {
|
||||||
|
|
||||||
fn on_request(&mut self, _request: server::Request<net::HttpStream>) -> Next {
|
fn on_request(&mut self, request: server::Request<net::HttpStream>) -> Next {
|
||||||
self.handler = as_json(&self.api.list_apps());
|
let url = extract_url(&request);
|
||||||
Next::write()
|
if url.is_none() {
|
||||||
|
// Just return 404 if we can't parse URL
|
||||||
|
return Next::write();
|
||||||
|
}
|
||||||
|
|
||||||
|
let url = url.expect("Check for None is above; qed");
|
||||||
|
let endpoint = url.path.get(1).map(|v| v.as_str());
|
||||||
|
|
||||||
|
let handler = endpoint.and_then(|v| match v {
|
||||||
|
"apps" => Some(as_json(&self.api.list_apps())),
|
||||||
|
"ping" => Some(ping_response(&self.api.local_domain)),
|
||||||
|
_ => None,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Overwrite default
|
||||||
|
if let Some(h) = handler {
|
||||||
|
self.handler = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.handler.on_request(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_request_readable(&mut self, decoder: &mut Decoder<net::HttpStream>) -> Next {
|
fn on_request_readable(&mut self, decoder: &mut Decoder<net::HttpStream>) -> Next {
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use endpoint::Handler;
|
use endpoint::Handler;
|
||||||
use handlers::ContentHandler;
|
use handlers::{ContentHandler, EchoHandler};
|
||||||
|
|
||||||
pub fn as_json<T : Serialize>(val: &T) -> Box<Handler> {
|
pub fn as_json<T : Serialize>(val: &T) -> Box<Handler> {
|
||||||
Box::new(ContentHandler::ok(serde_json::to_string(val).unwrap(), "application/json".to_owned()))
|
Box::new(ContentHandler::ok(serde_json::to_string(val).unwrap(), "application/json".to_owned()))
|
||||||
@ -26,3 +26,11 @@ pub fn as_json<T : Serialize>(val: &T) -> Box<Handler> {
|
|||||||
pub fn as_json_error<T : Serialize>(val: &T) -> Box<Handler> {
|
pub fn as_json_error<T : Serialize>(val: &T) -> Box<Handler> {
|
||||||
Box::new(ContentHandler::not_found(serde_json::to_string(val).unwrap(), "application/json".to_owned()))
|
Box::new(ContentHandler::not_found(serde_json::to_string(val).unwrap(), "application/json".to_owned()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ping_response(local_domain: &str) -> Box<Handler> {
|
||||||
|
Box::new(EchoHandler::cors(vec![
|
||||||
|
format!("http://{}", local_domain),
|
||||||
|
// Allow CORS calls also for localhost
|
||||||
|
format!("http://{}", local_domain.replace("127.0.0.1", "localhost")),
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
@ -19,5 +19,3 @@ include!("types.rs.in");
|
|||||||
|
|
||||||
#[cfg(not(feature = "serde_macros"))]
|
#[cfg(not(feature = "serde_macros"))]
|
||||||
include!(concat!(env!("OUT_DIR"), "/types.rs"));
|
include!(concat!(env!("OUT_DIR"), "/types.rs"));
|
||||||
|
|
||||||
|
|
||||||
|
@ -48,4 +48,3 @@ pub struct ApiError {
|
|||||||
pub detail: String,
|
pub detail: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
148
dapps/src/handlers/echo.rs
Normal file
148
dapps/src/handlers/echo.rs
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
//! Echo Handler
|
||||||
|
|
||||||
|
use std::io::Read;
|
||||||
|
use hyper::{header, server, Decoder, Encoder, Next};
|
||||||
|
use hyper::method::Method;
|
||||||
|
use hyper::net::HttpStream;
|
||||||
|
use unicase::UniCase;
|
||||||
|
use super::ContentHandler;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
/// Type of Cross-Origin request
|
||||||
|
enum Cors {
|
||||||
|
/// Not a Cross-Origin request - no headers needed
|
||||||
|
No,
|
||||||
|
/// Cross-Origin request with valid Origin
|
||||||
|
Allowed(String),
|
||||||
|
/// Cross-Origin request with invalid Origin
|
||||||
|
Forbidden,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EchoHandler {
|
||||||
|
safe_origins: Vec<String>,
|
||||||
|
content: String,
|
||||||
|
cors: Cors,
|
||||||
|
handler: Option<ContentHandler>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EchoHandler {
|
||||||
|
|
||||||
|
pub fn cors(safe_origins: Vec<String>) -> Self {
|
||||||
|
EchoHandler {
|
||||||
|
safe_origins: safe_origins,
|
||||||
|
content: String::new(),
|
||||||
|
cors: Cors::Forbidden,
|
||||||
|
handler: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cors_header(&self, origin: Option<String>) -> Cors {
|
||||||
|
fn origin_is_allowed(origin: &str, safe_origins: &[String]) -> bool {
|
||||||
|
for safe in safe_origins {
|
||||||
|
if origin.starts_with(safe) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
match origin {
|
||||||
|
Some(ref origin) if origin_is_allowed(origin, &self.safe_origins) => {
|
||||||
|
Cors::Allowed(origin.clone())
|
||||||
|
},
|
||||||
|
None => Cors::No,
|
||||||
|
_ => Cors::Forbidden,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl server::Handler<HttpStream> for EchoHandler {
|
||||||
|
fn on_request(&mut self, request: server::Request<HttpStream>) -> Next {
|
||||||
|
let origin = request.headers().get_raw("origin")
|
||||||
|
.and_then(|list| list.get(0))
|
||||||
|
.and_then(|origin| String::from_utf8(origin.clone()).ok());
|
||||||
|
|
||||||
|
self.cors = self.cors_header(origin);
|
||||||
|
|
||||||
|
// Don't even read the payload if origin is forbidden!
|
||||||
|
if let Cors::Forbidden = self.cors {
|
||||||
|
self.handler = Some(ContentHandler::ok(String::new(), "text/plain".into()));
|
||||||
|
Next::write()
|
||||||
|
} else {
|
||||||
|
Next::read()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_request_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
|
||||||
|
match decoder.read_to_string(&mut self.content) {
|
||||||
|
Ok(0) => {
|
||||||
|
self.handler = Some(ContentHandler::ok(self.content.clone(), "application/json".into()));
|
||||||
|
Next::write()
|
||||||
|
},
|
||||||
|
Ok(_) => Next::read(),
|
||||||
|
Err(e) => match e.kind() {
|
||||||
|
::std::io::ErrorKind::WouldBlock => Next::read(),
|
||||||
|
_ => Next::end(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||||
|
if let Cors::Allowed(ref domain) = self.cors {
|
||||||
|
let mut headers = res.headers_mut();
|
||||||
|
headers.set(header::Allow(vec![Method::Options, Method::Post, Method::Get]));
|
||||||
|
headers.set(header::AccessControlAllowHeaders(vec![
|
||||||
|
UniCase("origin".to_owned()),
|
||||||
|
UniCase("content-type".to_owned()),
|
||||||
|
UniCase("accept".to_owned()),
|
||||||
|
]));
|
||||||
|
headers.set(header::AccessControlAllowOrigin::Value(domain.clone()));
|
||||||
|
}
|
||||||
|
self.handler.as_mut().unwrap().on_response(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||||
|
self.handler.as_mut().unwrap().on_response_writable(encoder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_return_correct_cors_value() {
|
||||||
|
// given
|
||||||
|
let safe_origins = vec!["chrome-extension://".to_owned(), "http://localhost:8080".to_owned()];
|
||||||
|
let cut = EchoHandler {
|
||||||
|
safe_origins: safe_origins,
|
||||||
|
content: String::new(),
|
||||||
|
cors: Cors::No,
|
||||||
|
handler: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// when
|
||||||
|
let res1 = cut.cors_header(Some("http://ethcore.io".into()));
|
||||||
|
let res2 = cut.cors_header(Some("http://localhost:8080".into()));
|
||||||
|
let res3 = cut.cors_header(Some("chrome-extension://deadbeefcafe".into()));
|
||||||
|
let res4 = cut.cors_header(None);
|
||||||
|
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(res1, Cors::Forbidden);
|
||||||
|
assert_eq!(res2, Cors::Allowed("http://localhost:8080".into()));
|
||||||
|
assert_eq!(res3, Cors::Allowed("chrome-extension://deadbeefcafe".into()));
|
||||||
|
assert_eq!(res4, Cors::No);
|
||||||
|
}
|
@ -17,9 +17,41 @@
|
|||||||
//! Hyper handlers implementations.
|
//! Hyper handlers implementations.
|
||||||
|
|
||||||
mod auth;
|
mod auth;
|
||||||
|
mod echo;
|
||||||
mod content;
|
mod content;
|
||||||
mod redirect;
|
mod redirect;
|
||||||
|
|
||||||
pub use self::auth::AuthRequiredHandler;
|
pub use self::auth::AuthRequiredHandler;
|
||||||
|
pub use self::echo::EchoHandler;
|
||||||
pub use self::content::ContentHandler;
|
pub use self::content::ContentHandler;
|
||||||
pub use self::redirect::Redirection;
|
pub use self::redirect::Redirection;
|
||||||
|
|
||||||
|
use url::Url;
|
||||||
|
use hyper::{server, header, net, uri};
|
||||||
|
|
||||||
|
pub fn extract_url(req: &server::Request<net::HttpStream>) -> Option<Url> {
|
||||||
|
match *req.uri() {
|
||||||
|
uri::RequestUri::AbsoluteUri(ref url) => {
|
||||||
|
match Url::from_generic_url(url.clone()) {
|
||||||
|
Ok(url) => Some(url),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
uri::RequestUri::AbsolutePath(ref path) => {
|
||||||
|
// Attempt to prepend the Host header (mandatory in HTTP/1.1)
|
||||||
|
let url_string = match req.headers().get::<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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -45,8 +45,9 @@
|
|||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
extern crate url;
|
extern crate url as url_lib;
|
||||||
extern crate hyper;
|
extern crate hyper;
|
||||||
|
extern crate unicase;
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
extern crate jsonrpc_core;
|
extern crate jsonrpc_core;
|
||||||
@ -64,6 +65,7 @@ mod handlers;
|
|||||||
mod rpc;
|
mod rpc;
|
||||||
mod api;
|
mod api;
|
||||||
mod proxypac;
|
mod proxypac;
|
||||||
|
mod url;
|
||||||
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
@ -123,7 +125,7 @@ impl Server {
|
|||||||
let special = Arc::new({
|
let special = Arc::new({
|
||||||
let mut special = HashMap::new();
|
let mut special = HashMap::new();
|
||||||
special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, panic_handler.clone()));
|
special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, panic_handler.clone()));
|
||||||
special.insert(router::SpecialEndpoint::Api, api::RestApi::new(endpoints.clone()));
|
special.insert(router::SpecialEndpoint::Api, api::RestApi::new(format!("{}", addr), endpoints.clone()));
|
||||||
special.insert(router::SpecialEndpoint::Utils, apps::utils());
|
special.insert(router::SpecialEndpoint::Utils, apps::utils());
|
||||||
special
|
special
|
||||||
});
|
});
|
||||||
|
@ -17,22 +17,18 @@
|
|||||||
//! 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 url;
|
|
||||||
pub mod auth;
|
pub mod auth;
|
||||||
|
|
||||||
use DAPPS_DOMAIN;
|
use DAPPS_DOMAIN;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use url::Host;
|
use url::{Url, Host};
|
||||||
use hyper;
|
use hyper::{self, server, Next, Encoder, Decoder};
|
||||||
use hyper::{server, uri, header};
|
|
||||||
use hyper::{Next, Encoder, Decoder};
|
|
||||||
use hyper::net::HttpStream;
|
use hyper::net::HttpStream;
|
||||||
use apps;
|
use apps;
|
||||||
use endpoint::{Endpoint, Endpoints, EndpointPath};
|
use endpoint::{Endpoint, Endpoints, EndpointPath};
|
||||||
use self::url::Url;
|
use handlers::{Redirection, extract_url};
|
||||||
use self::auth::{Authorization, Authorized};
|
use self::auth::{Authorization, Authorized};
|
||||||
use handlers::Redirection;
|
|
||||||
|
|
||||||
/// Special endpoints are accessible on every domain (every dapp)
|
/// Special endpoints are accessible on every domain (every dapp)
|
||||||
#[derive(Debug, PartialEq, Hash, Eq)]
|
#[derive(Debug, PartialEq, Hash, Eq)]
|
||||||
@ -123,32 +119,6 @@ impl<A: Authorization> Router<A> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_url(req: &server::Request<HttpStream>) -> Option<Url> {
|
|
||||||
match *req.uri() {
|
|
||||||
uri::RequestUri::AbsoluteUri(ref url) => {
|
|
||||||
match Url::from_generic_url(url.clone()) {
|
|
||||||
Ok(url) => Some(url),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
uri::RequestUri::AbsolutePath(ref path) => {
|
|
||||||
// Attempt to prepend the Host header (mandatory in HTTP/1.1)
|
|
||||||
let url_string = match req.headers().get::<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_endpoint(url: &Option<Url>) -> (Option<EndpointPath>, SpecialEndpoint) {
|
fn extract_endpoint(url: &Option<Url>) -> (Option<EndpointPath>, SpecialEndpoint) {
|
||||||
fn special_endpoint(url: &Url) -> SpecialEndpoint {
|
fn special_endpoint(url: &Url) -> SpecialEndpoint {
|
||||||
if url.path.len() <= 1 {
|
if url.path.len() <= 1 {
|
||||||
|
@ -16,14 +16,14 @@
|
|||||||
|
|
||||||
//! HTTP/HTTPS URL type. Based on URL type from Iron library.
|
//! HTTP/HTTPS URL type. Based on URL type from Iron library.
|
||||||
|
|
||||||
use url::Host;
|
use url_lib::{self};
|
||||||
use url::{self};
|
pub use url_lib::Host;
|
||||||
|
|
||||||
/// HTTP/HTTPS URL type for Iron.
|
/// HTTP/HTTPS URL type for Iron.
|
||||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||||
pub struct Url {
|
pub struct Url {
|
||||||
/// Raw url of url
|
/// Raw url of url
|
||||||
pub raw: url::Url,
|
pub raw: url_lib::Url,
|
||||||
|
|
||||||
/// The host field of the URL, probably a domain.
|
/// The host field of the URL, probably a domain.
|
||||||
pub host: Host,
|
pub host: Host,
|
||||||
@ -62,14 +62,14 @@ impl Url {
|
|||||||
/// See: http://url.spec.whatwg.org/#special-scheme
|
/// See: http://url.spec.whatwg.org/#special-scheme
|
||||||
pub fn parse(input: &str) -> Result<Url, String> {
|
pub fn parse(input: &str) -> Result<Url, String> {
|
||||||
// Parse the string using rust-url, then convert.
|
// Parse the string using rust-url, then convert.
|
||||||
match url::Url::parse(input) {
|
match url_lib::Url::parse(input) {
|
||||||
Ok(raw_url) => Url::from_generic_url(raw_url),
|
Ok(raw_url) => Url::from_generic_url(raw_url),
|
||||||
Err(e) => Err(format!("{}", e))
|
Err(e) => Err(format!("{}", e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a `Url` from a `rust-url` `Url`.
|
/// Create a `Url` from a `rust-url` `Url`.
|
||||||
pub fn from_generic_url(raw_url: url::Url) -> Result<Url, String> {
|
pub fn from_generic_url(raw_url: url_lib::Url) -> Result<Url, String> {
|
||||||
// Map empty usernames to None.
|
// Map empty usernames to None.
|
||||||
let username = match raw_url.username() {
|
let username = match raw_url.username() {
|
||||||
"" => None,
|
"" => None,
|
@ -19,7 +19,7 @@
|
|||||||
use nanoipc;
|
use nanoipc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::{Ordering, AtomicBool};
|
use std::sync::atomic::{Ordering, AtomicBool};
|
||||||
use client::{Client, ClientConfig, RemoteClient};
|
use client::{Client, BlockChainClient, ClientConfig, RemoteClient, BlockID};
|
||||||
use tests::helpers::*;
|
use tests::helpers::*;
|
||||||
use devtools::*;
|
use devtools::*;
|
||||||
use miner::Miner;
|
use miner::Miner;
|
||||||
@ -55,3 +55,17 @@ fn can_handshake() {
|
|||||||
assert!(remote_client.handshake().is_ok());
|
assert!(remote_client.handshake().is_ok());
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_query_block() {
|
||||||
|
crossbeam::scope(|scope| {
|
||||||
|
let stop_guard = StopGuard::new();
|
||||||
|
let socket_path = "ipc:///tmp/parity-client-rpc-20.ipc";
|
||||||
|
run_test_worker(scope, stop_guard.share(), socket_path);
|
||||||
|
let remote_client = nanoipc::init_client::<RemoteClient<_>>(socket_path).unwrap();
|
||||||
|
|
||||||
|
let non_existant_block = remote_client.block_header(BlockID::Number(999));
|
||||||
|
|
||||||
|
assert!(non_existant_block.is_none());
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -707,7 +707,12 @@ fn binary_expr_variant(
|
|||||||
let buffer = &mut buffer[1..];
|
let buffer = &mut buffer[1..];
|
||||||
$write_expr
|
$write_expr
|
||||||
}),
|
}),
|
||||||
read: quote_arm!(cx, $variant_index_ident => { $read_expr } ),
|
read: quote_arm!(cx,
|
||||||
|
$variant_index_ident => {
|
||||||
|
let buffer = &buffer[1..];
|
||||||
|
$read_expr
|
||||||
|
}
|
||||||
|
),
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
ast::VariantData::Struct(ref fields, _) => {
|
ast::VariantData::Struct(ref fields, _) => {
|
||||||
@ -742,7 +747,12 @@ fn binary_expr_variant(
|
|||||||
let buffer = &mut buffer[1..];
|
let buffer = &mut buffer[1..];
|
||||||
$write_expr
|
$write_expr
|
||||||
}),
|
}),
|
||||||
read: quote_arm!(cx, $variant_index_ident => { $read_expr } ),
|
read: quote_arm!(cx,
|
||||||
|
$variant_index_ident => {
|
||||||
|
let buffer = &buffer[1..];
|
||||||
|
$read_expr
|
||||||
|
}
|
||||||
|
),
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -425,11 +425,11 @@ impl Configuration {
|
|||||||
fn geth_ipc_path(&self) -> String {
|
fn geth_ipc_path(&self) -> String {
|
||||||
if cfg!(windows) {
|
if cfg!(windows) {
|
||||||
r"\\.\pipe\geth.ipc".to_owned()
|
r"\\.\pipe\geth.ipc".to_owned()
|
||||||
}
|
} else {
|
||||||
else {
|
match self.args.flag_testnet {
|
||||||
if self.args.flag_testnet { path::ethereum::with_testnet("geth.ipc") }
|
true => path::ethereum::with_testnet("geth.ipc"),
|
||||||
else { path::ethereum::with_default("geth.ipc") }
|
false => path::ethereum::with_default("geth.ipc"),
|
||||||
.to_str().unwrap().to_owned()
|
}.to_str().unwrap().to_owned()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ use std::time::{Instant, Duration};
|
|||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use ethsync::{EthSync, SyncProvider};
|
use ethsync::{EthSync, SyncProvider};
|
||||||
use util::{Uint, RwLockable};
|
use util::{Uint, RwLockable, NetworkService};
|
||||||
use ethcore::client::*;
|
use ethcore::client::*;
|
||||||
use number_prefix::{binary_prefix, Standalone, Prefixed};
|
use number_prefix::{binary_prefix, Standalone, Prefixed};
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ impl Informant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature="dev", allow(match_bool))]
|
#[cfg_attr(feature="dev", allow(match_bool))]
|
||||||
pub fn tick(&self, client: &Client, maybe_sync: Option<&EthSync>) {
|
pub fn tick<Message>(&self, client: &Client, maybe_sync: Option<(&EthSync, &NetworkService<Message>)>) where Message: Send + Sync + Clone + 'static {
|
||||||
let elapsed = self.last_tick.unwrapped_read().elapsed();
|
let elapsed = self.last_tick.unwrapped_read().elapsed();
|
||||||
if elapsed < Duration::from_secs(5) {
|
if elapsed < Duration::from_secs(5) {
|
||||||
return;
|
return;
|
||||||
@ -110,11 +110,13 @@ impl Informant {
|
|||||||
paint(Yellow.bold(), format!("{:3}", ((report.gas_processed - last_report.gas_processed) / From::from(elapsed.as_milliseconds() * 1000)).low_u64())),
|
paint(Yellow.bold(), format!("{:3}", ((report.gas_processed - last_report.gas_processed) / From::from(elapsed.as_milliseconds() * 1000)).low_u64())),
|
||||||
|
|
||||||
match maybe_sync {
|
match maybe_sync {
|
||||||
Some(sync) => {
|
Some((sync, net)) => {
|
||||||
let sync_info = sync.status();
|
let sync_info = sync.status();
|
||||||
format!("{}/{} peers {} ",
|
let net_config = net.config();
|
||||||
|
format!("{}/{}/{} peers {} ",
|
||||||
paint(Green.bold(), format!("{:2}", sync_info.num_active_peers)),
|
paint(Green.bold(), format!("{:2}", sync_info.num_active_peers)),
|
||||||
paint(Green.bold(), format!("{:2}", sync_info.num_peers)),
|
paint(Green.bold(), format!("{:2}", sync_info.num_peers)),
|
||||||
|
paint(Green.bold(), format!("{:2}", net_config.ideal_peers)),
|
||||||
paint(Cyan.bold(), format!("{:>8}", format!("#{}", sync_info.last_imported_block_number.unwrap_or(chain_info.best_block_number)))),
|
paint(Cyan.bold(), format!("{:>8}", format!("#{}", sync_info.last_imported_block_number.unwrap_or(chain_info.best_block_number)))),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -128,7 +130,7 @@ impl Informant {
|
|||||||
paint(Purple.bold(), format!("{:>8}", Informant::format_bytes(cache_info.total()))),
|
paint(Purple.bold(), format!("{:>8}", Informant::format_bytes(cache_info.total()))),
|
||||||
paint(Purple.bold(), format!("{:>8}", Informant::format_bytes(queue_info.mem_used))),
|
paint(Purple.bold(), format!("{:>8}", Informant::format_bytes(queue_info.mem_used))),
|
||||||
match maybe_sync {
|
match maybe_sync {
|
||||||
Some(sync) => {
|
Some((sync, _)) => {
|
||||||
let sync_info = sync.status();
|
let sync_info = sync.status();
|
||||||
format!(" {} sync", paint(Purple.bold(), format!("{:>8}", Informant::format_bytes(sync_info.mem_used))))
|
format!(" {} sync", paint(Purple.bold(), format!("{:>8}", Informant::format_bytes(sync_info.mem_used))))
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,9 @@ impl IoHandler<NetSyncMessage> for ClientIoHandler {
|
|||||||
|
|
||||||
fn timeout(&self, _io: &IoContext<NetSyncMessage>, timer: TimerToken) {
|
fn timeout(&self, _io: &IoContext<NetSyncMessage>, timer: TimerToken) {
|
||||||
if let INFO_TIMER = timer {
|
if let INFO_TIMER = timer {
|
||||||
self.info.tick(&self.client, Some(&self.sync));
|
if let Some(net) = self.network.upgrade() {
|
||||||
|
self.info.tick(&self.client, Some((&self.sync, &net)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -485,7 +485,7 @@ fn execute_import(conf: Configuration) {
|
|||||||
Err(BlockImportError::Import(ImportError::AlreadyInChain)) => { trace!("Skipping block already in chain."); }
|
Err(BlockImportError::Import(ImportError::AlreadyInChain)) => { trace!("Skipping block already in chain."); }
|
||||||
Err(e) => die!("Cannot import block: {:?}", e)
|
Err(e) => die!("Cannot import block: {:?}", e)
|
||||||
}
|
}
|
||||||
informant.tick(client.deref(), None);
|
informant.tick::<&'static ()>(client.deref(), None);
|
||||||
};
|
};
|
||||||
|
|
||||||
match format {
|
match format {
|
||||||
|
@ -72,7 +72,7 @@ pub trait Lockable<T> {
|
|||||||
fn locked(&self) -> MutexGuard<T>;
|
fn locked(&self) -> MutexGuard<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Sized> Lockable<T> for Mutex<T> {
|
impl<T> Lockable<T> for Mutex<T> {
|
||||||
fn locked(&self) -> MutexGuard<T> { self.lock().unwrap() }
|
fn locked(&self) -> MutexGuard<T> { self.lock().unwrap() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ pub trait RwLockable<T> {
|
|||||||
fn unwrapped_write(&self) -> RwLockWriteGuard<T>;
|
fn unwrapped_write(&self) -> RwLockWriteGuard<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Sized> RwLockable<T> for RwLock<T> {
|
impl<T> RwLockable<T> for RwLock<T> {
|
||||||
fn unwrapped_read(&self) -> RwLockReadGuard<T> { self.read().unwrap() }
|
fn unwrapped_read(&self) -> RwLockReadGuard<T> { self.read().unwrap() }
|
||||||
fn unwrapped_write(&self) -> RwLockWriteGuard<T> { self.write().unwrap() }
|
fn unwrapped_write(&self) -> RwLockWriteGuard<T> { self.write().unwrap() }
|
||||||
}
|
}
|
||||||
|
@ -79,6 +79,11 @@ impl<Message> NetworkService<Message> where Message: Send + Sync + Clone + 'stat
|
|||||||
&self.stats
|
&self.stats
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns network configuration.
|
||||||
|
pub fn config(&self) -> &NetworkConfiguration {
|
||||||
|
&self.config
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns external url if available.
|
/// Returns external url if available.
|
||||||
pub fn external_url(&self) -> Option<String> {
|
pub fn external_url(&self) -> Option<String> {
|
||||||
let host = self.host.unwrapped_read();
|
let host = self.host.unwrapped_read();
|
||||||
|
Loading…
Reference in New Issue
Block a user