diff --git a/Cargo.lock b/Cargo.lock index db5c025df..a66dcf3b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,7 +28,7 @@ dependencies = [ "fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.9.14 (registry+https://github.com/rust-lang/crates.io-index)", "isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 5.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-core 5.1.0 (git+https://github.com/ethcore/jsonrpc.git)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -106,6 +106,11 @@ dependencies = [ "syntex_syntax 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "base32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "bigint" version = "1.0.0" @@ -414,6 +419,7 @@ dependencies = [ name = "ethcore-dapps" version = "1.6.0" dependencies = [ + "base32 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore-devtools 1.6.0", @@ -422,7 +428,7 @@ dependencies = [ "fetch 0.1.0", "futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)", - "jsonrpc-core 5.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-core 5.1.0 (git+https://github.com/ethcore/jsonrpc.git)", "jsonrpc-http-server 7.0.0 (git+https://github.com/ethcore/jsonrpc.git)", "linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -595,11 +601,12 @@ dependencies = [ "ethsync 1.6.0", "fetch 0.1.0", "futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 5.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-core 5.1.0 (git+https://github.com/ethcore/jsonrpc.git)", "jsonrpc-http-server 7.0.0 (git+https://github.com/ethcore/jsonrpc.git)", "jsonrpc-ipc-server 1.0.0 (git+https://github.com/ethcore/jsonrpc.git)", "jsonrpc-macros 0.2.0 (git+https://github.com/ethcore/jsonrpc.git)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "order-stat 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "parity-reactor 0.1.0", "parity-updater 1.6.0", "rlp 0.1.0", @@ -622,7 +629,7 @@ dependencies = [ "ethcore-io 1.6.0", "ethcore-rpc 1.6.0", "ethcore-util 1.6.0", - "jsonrpc-core 5.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-core 5.1.0 (git+https://github.com/ethcore/jsonrpc.git)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-ui 1.6.0", @@ -642,7 +649,7 @@ dependencies = [ "ethcore-ipc-nano 1.6.0", "ethcore-util 1.6.0", "futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 5.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-core 5.1.0 (git+https://github.com/ethcore/jsonrpc.git)", "jsonrpc-macros 0.2.0 (git+https://github.com/ethcore/jsonrpc.git)", "jsonrpc-tcp-server 1.0.0 (git+https://github.com/ethcore/jsonrpc.git)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1005,8 +1012,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "jsonrpc-core" -version = "5.0.0" -source = "git+https://github.com/ethcore/jsonrpc.git#5eeee0980e4d2682a831c633fa03a8af99e0d68c" +version = "5.1.0" +source = "git+https://github.com/ethcore/jsonrpc.git#d179ce34d8da8ea1cd67e93a5b4cb1e30f48c156" dependencies = [ "futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1019,11 +1026,10 @@ dependencies = [ [[package]] name = "jsonrpc-http-server" version = "7.0.0" -source = "git+https://github.com/ethcore/jsonrpc.git#5eeee0980e4d2682a831c633fa03a8af99e0d68c" +source = "git+https://github.com/ethcore/jsonrpc.git#d179ce34d8da8ea1cd67e93a5b4cb1e30f48c156" dependencies = [ - "futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)", - "jsonrpc-core 5.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-core 5.1.0 (git+https://github.com/ethcore/jsonrpc.git)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-core 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1032,11 +1038,11 @@ dependencies = [ [[package]] name = "jsonrpc-ipc-server" version = "1.0.0" -source = "git+https://github.com/ethcore/jsonrpc.git#5eeee0980e4d2682a831c633fa03a8af99e0d68c" +source = "git+https://github.com/ethcore/jsonrpc.git#d179ce34d8da8ea1cd67e93a5b4cb1e30f48c156" dependencies = [ "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 5.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-core 5.1.0 (git+https://github.com/ethcore/jsonrpc.git)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1048,21 +1054,19 @@ dependencies = [ [[package]] name = "jsonrpc-macros" version = "0.2.0" -source = "git+https://github.com/ethcore/jsonrpc.git#5eeee0980e4d2682a831c633fa03a8af99e0d68c" +source = "git+https://github.com/ethcore/jsonrpc.git#d179ce34d8da8ea1cd67e93a5b4cb1e30f48c156" dependencies = [ - "futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 5.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-core 5.1.0 (git+https://github.com/ethcore/jsonrpc.git)", "serde 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "jsonrpc-tcp-server" version = "1.0.0" -source = "git+https://github.com/ethcore/jsonrpc.git#5eeee0980e4d2682a831c633fa03a8af99e0d68c" +source = "git+https://github.com/ethcore/jsonrpc.git#d179ce34d8da8ea1cd67e93a5b4cb1e30f48c156" dependencies = [ "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 5.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-core 5.1.0 (git+https://github.com/ethcore/jsonrpc.git)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1481,6 +1485,11 @@ dependencies = [ "user32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "order-stat" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "owning_ref" version = "0.2.2" @@ -1532,7 +1541,7 @@ dependencies = [ "ethcore-signer 1.6.0", "ethcore-util 1.6.0", "futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 5.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-core 5.1.0 (git+https://github.com/ethcore/jsonrpc.git)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1563,7 +1572,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#416d00db677b8219f7548bb4dfa2f25c4b19f36e" +source = "git+https://github.com/ethcore/js-precompiled.git#4110b5bc85a15ae3f0b5c02b1c3caf8423f51b50" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2473,6 +2482,7 @@ dependencies = [ "checksum app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b7d1c0d48a81bbb13043847f957971f4d87c81542d80ece5e84ba3cba4058fd4" "checksum arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "16e3bdb2f54b3ace0285975d59a97cf8ed3855294b2b6bc651fcf22a9c352975" "checksum aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07d344974f0a155f091948aa389fb1b912d3a58414fbdb9c8d446d193ee3496a" +"checksum base32 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1b9605ba46d61df0410d8ac686b0007add8172eba90e8e909c347856fe794d8c" "checksum bigint 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2311bcd71b281e142a095311c22509f0d6bcd87b3000d7dbaa810929b9d6f6ae" "checksum bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c" "checksum bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5b97c2c8e8bbb4251754f559df8af22fb264853c7d009084a576cdf12565089d" @@ -2525,7 +2535,7 @@ dependencies = [ "checksum itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)" = "d95557e7ba6b71377b0f2c3b3ae96c53f1b75a926a6901a500f557a370af730a" "checksum itoa 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae3088ea4baeceb0284ee9eea42f591226e6beaecf65373e41b38d95a1b8e7a1" "checksum itoa 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "91fd9dc2c587067de817fec4ad355e3818c3d893a78cab32a0a474c7a15bb8d5" -"checksum jsonrpc-core 5.0.0 (git+https://github.com/ethcore/jsonrpc.git)" = "" +"checksum jsonrpc-core 5.1.0 (git+https://github.com/ethcore/jsonrpc.git)" = "" "checksum jsonrpc-http-server 7.0.0 (git+https://github.com/ethcore/jsonrpc.git)" = "" "checksum jsonrpc-ipc-server 1.0.0 (git+https://github.com/ethcore/jsonrpc.git)" = "" "checksum jsonrpc-macros 0.2.0 (git+https://github.com/ethcore/jsonrpc.git)" = "" @@ -2574,6 +2584,7 @@ dependencies = [ "checksum ole32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2c49021782e5233cd243168edfa8037574afed4eba4bbaf538b3d8d1789d8c" "checksum openssl 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "12be61c7eaa23228316ff02c39807e4c1b1af84ba81420f19fd58dade304b25c" "checksum openssl-sys 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d2845e841700e7b04282ceaa115407ea84e0db918ae689ad9ceb6f06fa6046bd" +"checksum order-stat 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "efa535d5117d3661134dbf1719b6f0ffe06f2375843b13935db186cd094105eb" "checksum owning_ref 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8d91377085359426407a287ab16884a0111ba473aa6844ff01d4ec20ce3d75e7" "checksum parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "98378dec0a185da2b7180308752f0bad73aaa949c3e0a3b0528d0e067945f7ab" "checksum parity-ui-precompiled 1.4.0 (git+https://github.com/ethcore/js-precompiled.git)" = "" diff --git a/dapps/Cargo.toml b/dapps/Cargo.toml index 09c72cb47..459a5ea70 100644 --- a/dapps/Cargo.toml +++ b/dapps/Cargo.toml @@ -23,6 +23,7 @@ serde = "0.8" serde_json = "0.8" linked-hash-map = "0.3" parity-dapps-glue = "1.4" +base32 = "0.3" mime = "0.2" mime_guess = "1.6.1" time = "0.1.35" diff --git a/dapps/src/api/api.rs b/dapps/src/api/api.rs index 305d9bc83..b521c0ba1 100644 --- a/dapps/src/api/api.rs +++ b/dapps/src/api/api.rs @@ -123,6 +123,7 @@ impl server::Handler for RestApiRouter { return Next::write(); } + // TODO [ToDr] Consider using `path.app_params` instead let url = extract_url(&request); if url.is_none() { // Just return 404 if we can't parse URL diff --git a/dapps/src/apps/mod.rs b/dapps/src/apps/mod.rs index 1959940e7..b85f0dde9 100644 --- a/dapps/src/apps/mod.rs +++ b/dapps/src/apps/mod.rs @@ -32,8 +32,8 @@ pub mod manifest; extern crate parity_ui; -pub const HOME_PAGE: &'static str = "home"; -pub const DAPPS_DOMAIN: &'static str = ".parity"; +pub const HOME_PAGE: &'static str = "parity"; +pub const DAPPS_DOMAIN: &'static str = ".web3.site"; pub const RPC_PATH: &'static str = "rpc"; pub const API_PATH: &'static str = "api"; pub const UTILS_PATH: &'static str = "parity-utils"; diff --git a/dapps/src/endpoint.rs b/dapps/src/endpoint.rs index 5bfa1c3d5..648d82ff8 100644 --- a/dapps/src/endpoint.rs +++ b/dapps/src/endpoint.rs @@ -22,6 +22,7 @@ use std::collections::BTreeMap; #[derive(Debug, PartialEq, Default, Clone)] pub struct EndpointPath { pub app_id: String, + pub app_params: Vec, pub host: String, pub port: u16, pub using_dapps_domains: bool, diff --git a/dapps/src/lib.rs b/dapps/src/lib.rs index 4cf9c333f..50dcb39b1 100644 --- a/dapps/src/lib.rs +++ b/dapps/src/lib.rs @@ -19,6 +19,7 @@ #![warn(missing_docs)] #![cfg_attr(feature="nightly", plugin(clippy))] +extern crate base32; extern crate hyper; extern crate time; extern crate url as url_lib; @@ -69,9 +70,10 @@ use std::sync::{Arc, Mutex}; use std::net::SocketAddr; use std::collections::HashMap; -use ethcore_rpc::Metadata; +use ethcore_rpc::{Metadata}; use fetch::{Fetch, Client as FetchClient}; use hash_fetch::urlhint::ContractClient; +use jsonrpc_core::Middleware; use jsonrpc_core::reactor::RpcHandler; use router::auth::{Authorization, NoAuth, HttpBasicAuth}; use parity_reactor::Remote; @@ -91,11 +93,11 @@ impl SyncStatus for F where F: Fn() -> bool + Send + Sync { /// Validates Web Proxy tokens pub trait WebProxyTokens: Send + Sync { /// Should return true if token is a valid web proxy access token. - fn is_web_proxy_token_valid(&self, token: &String) -> bool; + fn is_web_proxy_token_valid(&self, token: &str) -> bool; } impl WebProxyTokens for F where F: Fn(String) -> bool + Send + Sync { - fn is_web_proxy_token_valid(&self, token: &String) -> bool { self(token.to_owned()) } + fn is_web_proxy_token_valid(&self, token: &str) -> bool { self(token.to_owned()) } } /// Webapps HTTP+RPC server build. @@ -178,7 +180,7 @@ impl ServerBuilder { /// Asynchronously start server with no authentication, /// returns result with `Server` handle on success or an error. - pub fn start_unsecured_http(self, addr: &SocketAddr, handler: RpcHandler) -> Result { + pub fn start_unsecured_http>(self, addr: &SocketAddr, handler: RpcHandler) -> Result { let fetch = self.fetch_client()?; Server::start_http( addr, @@ -198,7 +200,7 @@ impl ServerBuilder { /// Asynchronously start server with `HTTP Basic Authentication`, /// return result with `Server` handle on success or an error. - pub fn start_basic_auth_http(self, addr: &SocketAddr, username: &str, password: &str, handler: RpcHandler) -> Result { + pub fn start_basic_auth_http>(self, addr: &SocketAddr, username: &str, password: &str, handler: RpcHandler) -> Result { let fetch = self.fetch_client()?; Server::start_http( addr, @@ -257,11 +259,11 @@ impl Server { } } - fn start_http( + fn start_http>( addr: &SocketAddr, hosts: Option>, authorization: A, - handler: RpcHandler, + handler: RpcHandler, dapps_path: PathBuf, extra_dapps: Vec, signer_address: Option<(String, u16)>, @@ -409,6 +411,6 @@ mod util_tests { // then assert_eq!(none, Vec::::new()); - assert_eq!(some, vec!["http://home.parity".to_owned(), "http://127.0.0.1:18180".into()]); + assert_eq!(some, vec!["http://parity.web3.site".to_owned(), "http://127.0.0.1:18180".into()]); } } diff --git a/dapps/src/page/handler.rs b/dapps/src/page/handler.rs index 31e5da072..c2b68f750 100644 --- a/dapps/src/page/handler.rs +++ b/dapps/src/page/handler.rs @@ -252,6 +252,7 @@ fn should_extract_path_with_appid() { prefix: None, path: EndpointPath { app_id: "app".to_owned(), + app_params: vec![], host: "".to_owned(), port: 8080, using_dapps_domains: true, diff --git a/dapps/src/router/mod.rs b/dapps/src/router/mod.rs index 116ac7d8e..dbaf4dbb0 100644 --- a/dapps/src/router/mod.rs +++ b/dapps/src/router/mod.rs @@ -97,9 +97,7 @@ impl server::Handler for Router { => { trace!(target: "dapps", "Redirecting to correct web request: {:?}", referer_url); - // TODO [ToDr] Some nice util for this! - let using_domain = if referer.using_dapps_domains { 0 } else { 1 }; - let len = cmp::min(referer_url.path.len(), using_domain + 3); // token + protocol + hostname + let len = cmp::min(referer_url.path.len(), 2); // /web// let base = referer_url.path[..len].join("/"); let requested = url.map(|u| u.path.join("/")).unwrap_or_default(); Redirection::boxed(&format!("/{}/{}", base, requested)) @@ -262,20 +260,27 @@ fn extract_endpoint(url: &Option) -> (Option, SpecialEndpoint match *url { Some(ref url) => match url.host { Host::Domain(ref domain) if domain.ends_with(DAPPS_DOMAIN) => { - let len = domain.len() - DAPPS_DOMAIN.len(); - let id = domain[0..len].to_owned(); + let id = &domain[0..(domain.len() - DAPPS_DOMAIN.len())]; + let (id, params) = if let Some(split) = id.rfind('.') { + let (params, id) = id.split_at(split); + (id[1..].to_owned(), [params.to_owned()].into_iter().chain(&url.path).cloned().collect()) + } else { + (id.to_owned(), url.path.clone()) + }; (Some(EndpointPath { app_id: id, + app_params: params, host: domain.clone(), port: url.port, using_dapps_domains: true, }), special_endpoint(url)) }, _ if url.path.len() > 1 => { - let id = url.path[0].clone(); + let id = url.path[0].to_owned(); (Some(EndpointPath { - app_id: id.clone(), + app_id: id, + app_params: url.path[1..].to_vec(), host: format!("{}", url.host), port: url.port, using_dapps_domains: false, @@ -296,6 +301,7 @@ fn should_extract_endpoint() { extract_endpoint(&Url::parse("http://localhost:8080/status/index.html").ok()), (Some(EndpointPath { app_id: "status".to_owned(), + app_params: vec!["index.html".to_owned()], host: "localhost".to_owned(), port: 8080, using_dapps_domains: false, @@ -307,6 +313,7 @@ fn should_extract_endpoint() { extract_endpoint(&Url::parse("http://localhost:8080/rpc/").ok()), (Some(EndpointPath { app_id: "rpc".to_owned(), + app_params: vec!["".to_owned()], host: "localhost".to_owned(), port: 8080, using_dapps_domains: false, @@ -314,10 +321,11 @@ fn should_extract_endpoint() { ); assert_eq!( - extract_endpoint(&Url::parse("http://my.status.parity/parity-utils/inject.js").ok()), + extract_endpoint(&Url::parse("http://my.status.web3.site/parity-utils/inject.js").ok()), (Some(EndpointPath { - app_id: "my.status".to_owned(), - host: "my.status.parity".to_owned(), + app_id: "status".to_owned(), + app_params: vec!["my".to_owned(), "parity-utils".into(), "inject.js".into()], + host: "my.status.web3.site".to_owned(), port: 80, using_dapps_domains: true, }), SpecialEndpoint::Utils) @@ -325,10 +333,11 @@ fn should_extract_endpoint() { // By Subdomain assert_eq!( - extract_endpoint(&Url::parse("http://my.status.parity/test.html").ok()), + extract_endpoint(&Url::parse("http://status.web3.site/test.html").ok()), (Some(EndpointPath { - app_id: "my.status".to_owned(), - host: "my.status.parity".to_owned(), + app_id: "status".to_owned(), + app_params: vec!["test.html".to_owned()], + host: "status.web3.site".to_owned(), port: 80, using_dapps_domains: true, }), SpecialEndpoint::None) @@ -336,10 +345,11 @@ fn should_extract_endpoint() { // RPC by subdomain assert_eq!( - extract_endpoint(&Url::parse("http://my.status.parity/rpc/").ok()), + extract_endpoint(&Url::parse("http://my.status.web3.site/rpc/").ok()), (Some(EndpointPath { - app_id: "my.status".to_owned(), - host: "my.status.parity".to_owned(), + app_id: "status".to_owned(), + app_params: vec!["my".to_owned(), "rpc".into(), "".into()], + host: "my.status.web3.site".to_owned(), port: 80, using_dapps_domains: true, }), SpecialEndpoint::Rpc) @@ -347,10 +357,11 @@ fn should_extract_endpoint() { // API by subdomain assert_eq!( - extract_endpoint(&Url::parse("http://my.status.parity/api/").ok()), + extract_endpoint(&Url::parse("http://my.status.web3.site/api/").ok()), (Some(EndpointPath { - app_id: "my.status".to_owned(), - host: "my.status.parity".to_owned(), + app_id: "status".to_owned(), + app_params: vec!["my".to_owned(), "api".into(), "".into()], + host: "my.status.web3.site".to_owned(), port: 80, using_dapps_domains: true, }), SpecialEndpoint::Api) diff --git a/dapps/src/rpc.rs b/dapps/src/rpc.rs index 1fffec6ee..bf1b1dc93 100644 --- a/dapps/src/rpc.rs +++ b/dapps/src/rpc.rs @@ -18,11 +18,15 @@ use std::sync::{Arc, Mutex}; use hyper; use ethcore_rpc::{Metadata, Origin}; +use jsonrpc_core::Middleware; use jsonrpc_core::reactor::RpcHandler; use jsonrpc_http_server::{Rpc, ServerHandler, PanicHandler, AccessControlAllowOrigin, HttpMetaExtractor}; use endpoint::{Endpoint, EndpointPath, Handler}; -pub fn rpc(handler: RpcHandler, panic_handler: Arc () + Send>>>>) -> Box { +pub fn rpc>( + handler: RpcHandler, + panic_handler: Arc () + Send>>>>, +) -> Box { Box::new(RpcEndpoint { handler: handler, meta_extractor: Arc::new(MetadataExtractor), @@ -33,15 +37,15 @@ pub fn rpc(handler: RpcHandler, panic_handler: Arc, +struct RpcEndpoint> { + handler: RpcHandler, meta_extractor: Arc>, panic_handler: Arc () + Send>>>>, cors_domain: Option>, allowed_hosts: Option>, } -impl Endpoint for RpcEndpoint { +impl> Endpoint for RpcEndpoint { fn to_async_handler(&self, _path: EndpointPath, control: hyper::Control) -> Box { let panic_handler = PanicHandler { handler: self.panic_handler.clone() }; Box::new(ServerHandler::new( diff --git a/dapps/src/tests/api.rs b/dapps/src/tests/api.rs index 3c0ae528a..05e285264 100644 --- a/dapps/src/tests/api.rs +++ b/dapps/src/tests/api.rs @@ -143,7 +143,7 @@ fn should_return_signer_port_cors_headers_for_home_parity() { "\ POST /api/ping HTTP/1.1\r\n\ Host: localhost:8080\r\n\ - Origin: http://home.parity\r\n\ + Origin: http://parity.web3.site\r\n\ Connection: close\r\n\ \r\n\ {} @@ -153,8 +153,8 @@ fn should_return_signer_port_cors_headers_for_home_parity() { // then assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); assert!( - response.headers_raw.contains("Access-Control-Allow-Origin: http://home.parity"), - "CORS header for home.parity missing: {:?}", + response.headers_raw.contains("Access-Control-Allow-Origin: http://parity.web3.site"), + "CORS header for parity.web3.site missing: {:?}", response.headers ); } diff --git a/dapps/src/tests/fetch.rs b/dapps/src/tests/fetch.rs index 8f2e22e5c..4f343b3a3 100644 --- a/dapps/src/tests/fetch.rs +++ b/dapps/src/tests/fetch.rs @@ -31,7 +31,7 @@ fn should_resolve_dapp() { let response = request(server, "\ GET / HTTP/1.1\r\n\ - Host: 1472a9e190620cdf6b31f383373e45efcfe869a820c91f9ccd7eb9fb45e4985d.parity\r\n\ + Host: 1472a9e190620cdf6b31f383373e45efcfe869a820c91f9ccd7eb9fb45e4985d.web3.site\r\n\ Connection: close\r\n\ \r\n\ " @@ -52,7 +52,7 @@ fn should_return_503_when_syncing_but_should_make_the_calls() { let response = request(server, "\ GET / HTTP/1.1\r\n\ - Host: 1472a9e190620cdf6b31f383373e45efcfe869a820c91f9ccd7eb9fb45e4985d.parity\r\n\ + Host: 1472a9e190620cdf6b31f383373e45efcfe869a820c91f9ccd7eb9fb45e4985d.web3.site\r\n\ Connection: close\r\n\ \r\n\ " @@ -81,7 +81,7 @@ fn should_return_502_on_hash_mismatch() { let response = request(server, "\ GET / HTTP/1.1\r\n\ - Host: 94f093625c06887d94d9fee0d5f9cc4aaa46f33d24d1c7e4b5237e7c37d547dd.parity\r\n\ + Host: 94f093625c06887d94d9fee0d5f9cc4aaa46f33d24d1c7e4b5237e7c37d547dd.web3.site\r\n\ Connection: close\r\n\ \r\n\ " @@ -112,7 +112,7 @@ fn should_return_error_for_invalid_dapp_zip() { let response = request(server, "\ GET / HTTP/1.1\r\n\ - Host: 2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e.parity\r\n\ + Host: 2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e.web3.site\r\n\ Connection: close\r\n\ \r\n\ " @@ -144,7 +144,7 @@ fn should_return_fetched_dapp_content() { let response1 = http_client::request(server.addr(), "\ GET /index.html HTTP/1.1\r\n\ - Host: 9c94e154dab8acf859b30ee80fc828fb1d38359d938751b65db71d460588d82a.parity\r\n\ + Host: 9c94e154dab8acf859b30ee80fc828fb1d38359d938751b65db71d460588d82a.web3.site\r\n\ Connection: close\r\n\ \r\n\ " @@ -152,7 +152,7 @@ fn should_return_fetched_dapp_content() { let response2 = http_client::request(server.addr(), "\ GET /manifest.json HTTP/1.1\r\n\ - Host: 9c94e154dab8acf859b30ee80fc828fb1d38359d938751b65db71d460588d82a.parity\r\n\ + Host: 9c94e154dab8acf859b30ee80fc828fb1d38359d938751b65db71d460588d82a.web3.site\r\n\ Connection: close\r\n\ \r\n\ " @@ -207,7 +207,7 @@ fn should_return_fetched_content() { let response = request(server, "\ GET / HTTP/1.1\r\n\ - Host: 2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e.parity\r\n\ + Host: 2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e.web3.site\r\n\ Connection: close\r\n\ \r\n\ " @@ -234,7 +234,7 @@ fn should_cache_content() { ); let request_str = "\ GET / HTTP/1.1\r\n\ - Host: 2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e.parity\r\n\ + Host: 2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e.web3.site\r\n\ Connection: close\r\n\ \r\n\ "; @@ -265,7 +265,7 @@ fn should_not_request_content_twice() { ); let request_str = "\ GET / HTTP/1.1\r\n\ - Host: 2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e.parity\r\n\ + Host: 2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e.web3.site\r\n\ Connection: close\r\n\ \r\n\ "; @@ -298,6 +298,17 @@ fn should_not_request_content_twice() { response2.assert_status("HTTP/1.1 200 OK"); } +#[test] +fn should_encode_and_decode_base32() { + use base32; + + let encoded = base32::encode(base32::Alphabet::Crockford, "token+https://parity.io".as_bytes()); + assert_eq!("EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY", &encoded); + + let data = base32::decode(base32::Alphabet::Crockford, "EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY").unwrap(); + assert_eq!("token+https://parity.io", &String::from_utf8(data).unwrap()); +} + #[test] fn should_stream_web_content() { // given @@ -306,8 +317,8 @@ fn should_stream_web_content() { // when let response = request(server, "\ - GET /web/token/https/parity.io/ HTTP/1.1\r\n\ - Host: localhost:8080\r\n\ + GET / HTTP/1.1\r\n\ + Host: EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY.web.web3.site\r\n\ Connection: close\r\n\ \r\n\ " @@ -322,20 +333,90 @@ fn should_stream_web_content() { } #[test] -fn should_return_error_on_invalid_token() { +fn should_support_base32_encoded_web_urls() { // given let (server, fetch) = serve_with_fetch("token"); // when let response = request(server, "\ - GET /web/invalidtoken/https/parity.io/ HTTP/1.1\r\n\ + GET /styles.css?test=123 HTTP/1.1\r\n\ + Host: EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY.web.web3.site\r\n\ + Connection: close\r\n\ + \r\n\ + " + ); + + // then + response.assert_status("HTTP/1.1 200 OK"); + assert_security_headers_for_embed(&response.headers); + + fetch.assert_requested("https://parity.io/styles.css?test=123"); + fetch.assert_no_more_requests(); +} + +#[test] +fn should_correctly_handle_long_label_when_splitted() { + // given + let (server, fetch) = serve_with_fetch("xolrg9fePeQyKLnL"); + + // when + let response = request(server, + "\ + GET /styles.css?test=123 HTTP/1.1\r\n\ + Host: f1qprwk775k6am35a5wmpk3e9gnpgx3me1sk.mbsfcdqpwx3jd5h7ax39dxq2wvb5dhqpww3fe9t2wrvfdm.web.web3.site\r\n\ + Connection: close\r\n\ + \r\n\ + " + ); + + // then + response.assert_status("HTTP/1.1 200 OK"); + assert_security_headers_for_embed(&response.headers); + + fetch.assert_requested("https://contribution.melonport.com/styles.css?test=123"); + fetch.assert_no_more_requests(); +} + + +#[test] +fn should_support_base32_encoded_web_urls_as_path() { + // given + let (server, fetch) = serve_with_fetch("token"); + + // when + let response = request(server, + "\ + GET /web/EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY/styles.css?test=123 HTTP/1.1\r\n\ Host: localhost:8080\r\n\ Connection: close\r\n\ \r\n\ " ); + // then + response.assert_status("HTTP/1.1 200 OK"); + assert_security_headers_for_embed(&response.headers); + + fetch.assert_requested("https://parity.io/styles.css?test=123"); + fetch.assert_no_more_requests(); +} + +#[test] +fn should_return_error_on_invalid_token() { + // given + let (server, fetch) = serve_with_fetch("test"); + + // when + let response = request(server, + "\ + GET / HTTP/1.1\r\n\ + Host: EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY.web.web3.site\r\n\ + Connection: close\r\n\ + \r\n\ + " + ); + // then response.assert_status("HTTP/1.1 400 Bad Request"); assert_security_headers_for_embed(&response.headers); @@ -365,28 +446,6 @@ fn should_return_error_on_invalid_protocol() { fetch.assert_no_more_requests(); } -#[test] -fn should_redirect_if_trailing_slash_is_missing() { - // given - let (server, fetch) = serve_with_fetch("token"); - - // when - let response = request(server, - "\ - GET /web/token/https/parity.io HTTP/1.1\r\n\ - Host: localhost:8080\r\n\ - Connection: close\r\n\ - \r\n\ - " - ); - - // then - response.assert_status("HTTP/1.1 302 Found"); - response.assert_header("Location", "/web/token/https/parity.io/"); - - fetch.assert_no_more_requests(); -} - #[test] fn should_disallow_non_get_requests() { // given @@ -395,8 +454,8 @@ fn should_disallow_non_get_requests() { // when let response = request(server, "\ - POST /token/https/parity.io/ HTTP/1.1\r\n\ - Host: web.parity\r\n\ + POST / HTTP/1.1\r\n\ + Host: EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY.web.web3.site\r\n\ Content-Type: application/json\r\n\ Connection: close\r\n\ \r\n\ @@ -423,14 +482,37 @@ fn should_fix_absolute_requests_based_on_referer() { GET /styles.css HTTP/1.1\r\n\ Host: localhost:8080\r\n\ Connection: close\r\n\ - Referer: http://localhost:8080/web/token/https/parity.io/\r\n\ + Referer: http://localhost:8080/web/EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY/\r\n\ \r\n\ " ); // then response.assert_status("HTTP/1.1 302 Found"); - response.assert_header("Location", "/web/token/https/parity.io/styles.css"); + response.assert_header("Location", "/web/EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY/styles.css"); + + fetch.assert_no_more_requests(); +} + +#[test] +fn should_fix_absolute_requests_based_on_referer_in_url() { + // given + let (server, fetch) = serve_with_fetch("token"); + + // when + let response = request(server, + "\ + GET /styles.css HTTP/1.1\r\n\ + Host: localhost:8080\r\n\ + Connection: close\r\n\ + Referer: http://localhost:8080/?__referer=web/EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY/\r\n\ + \r\n\ + " + ); + + // then + response.assert_status("HTTP/1.1 302 Found"); + response.assert_header("Location", "/web/EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY/styles.css"); fetch.assert_no_more_requests(); } diff --git a/dapps/src/tests/redirection.rs b/dapps/src/tests/redirection.rs index a19888620..8b529a851 100644 --- a/dapps/src/tests/redirection.rs +++ b/dapps/src/tests/redirection.rs @@ -105,7 +105,7 @@ fn should_display_404_on_invalid_dapp_with_domain() { let response = request(server, "\ GET / HTTP/1.1\r\n\ - Host: invaliddapp.parity\r\n\ + Host: invaliddapp.web3.site\r\n\ Connection: close\r\n\ \r\n\ " @@ -179,7 +179,7 @@ fn should_serve_proxy_pac() { // then assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); - assert_eq!(response.body, "D5\n\nfunction FindProxyForURL(url, host) {\n\tif (shExpMatch(host, \"home.parity\"))\n\t{\n\t\treturn \"PROXY 127.0.0.1:18180\";\n\t}\n\n\tif (shExpMatch(host, \"*.parity\"))\n\t{\n\t\treturn \"PROXY 127.0.0.1:8080\";\n\t}\n\n\treturn \"DIRECT\";\n}\n\n0\n\n".to_owned()); + assert_eq!(response.body, "DD\n\nfunction FindProxyForURL(url, host) {\n\tif (shExpMatch(host, \"parity.web3.site\"))\n\t{\n\t\treturn \"PROXY 127.0.0.1:18180\";\n\t}\n\n\tif (shExpMatch(host, \"*.web3.site\"))\n\t{\n\t\treturn \"PROXY 127.0.0.1:8080\";\n\t}\n\n\treturn \"DIRECT\";\n}\n\n0\n\n".to_owned()); assert_security_headers(&response.headers); } diff --git a/dapps/src/tests/rpc.rs b/dapps/src/tests/rpc.rs index 54834e264..7c2486099 100644 --- a/dapps/src/tests/rpc.rs +++ b/dapps/src/tests/rpc.rs @@ -23,7 +23,7 @@ use tests::helpers::{serve_with_rpc, request}; #[test] fn should_serve_rpc() { // given - let mut io = MetaIoHandler::new(); + let mut io = MetaIoHandler::default(); io.add_method("rpc_test", |_| { Ok(Value::String("Hello World!".into())) }); @@ -53,7 +53,7 @@ fn should_serve_rpc() { #[test] fn should_extract_metadata() { // given - let mut io = MetaIoHandler::new(); + let mut io = MetaIoHandler::default(); io.add_method_with_meta("rpc_test", |_params, meta: Metadata| { assert_eq!(meta.dapp_id, Some("https://parity.io/".to_owned())); assert_eq!(meta.origin, Origin::Dapps); @@ -87,7 +87,7 @@ fn should_extract_metadata() { #[test] fn should_extract_metadata_from_custom_header() { // given - let mut io = MetaIoHandler::new(); + let mut io = MetaIoHandler::default(); io.add_method_with_meta("rpc_test", |_params, meta: Metadata| { assert_eq!(meta.dapp_id, Some("https://parity.io/".to_owned())); assert_eq!(meta.origin, Origin::Dapps); diff --git a/dapps/src/tests/validation.rs b/dapps/src/tests/validation.rs index e43621313..afeb7b5ef 100644 --- a/dapps/src/tests/validation.rs +++ b/dapps/src/tests/validation.rs @@ -66,7 +66,7 @@ fn should_serve_dapps_domains() { let response = request(server, "\ GET / HTTP/1.1\r\n\ - Host: ui.parity\r\n\ + Host: ui.web3.site\r\n\ Connection: close\r\n\ \r\n\ {} diff --git a/dapps/src/web.rs b/dapps/src/web.rs index 8287a8585..0e872daf2 100644 --- a/dapps/src/web.rs +++ b/dapps/src/web.rs @@ -20,6 +20,7 @@ use std::sync::Arc; use fetch::{self, Fetch}; use parity_reactor::Remote; +use base32; use hyper::{self, server, net, Next, Encoder, Decoder}; use hyper::status::StatusCode; @@ -27,7 +28,7 @@ use apps; use endpoint::{Endpoint, Handler, EndpointPath}; use handlers::{ ContentFetcherHandler, ContentHandler, ContentValidator, ValidatorResponse, - StreamingHandler, Redirection, extract_url, + StreamingHandler, extract_url, }; use url::Url; use WebProxyTokens; @@ -86,9 +87,10 @@ impl ContentValidator for WebInstaller { ); if is_html { handler.set_initial_content(&format!( - r#""#, + r#""#, apps::UTILS_PATH, apps::URL_REFERER, + apps::WEB_PATH, &self.referer, )); } @@ -99,7 +101,6 @@ impl ContentValidator for WebInstaller { enum State { Initial, Error(ContentHandler), - Redirecting(Redirection), Fetching(ContentFetcherHandler), } @@ -114,25 +115,26 @@ struct WebHandler { } impl WebHandler { - fn extract_target_url(&self, url: Option) -> Result<(String, String), State> { - let (path, query) = match url { - Some(url) => (url.path, url.query), - None => { - return Err(State::Error(ContentHandler::error( - StatusCode::BadRequest, "Invalid URL", "Couldn't parse URL", None, self.embeddable_on.clone() - ))); - } - }; + fn extract_target_url(&self, url: Option) -> Result> { + let token_and_url = self.path.app_params.get(0) + .map(|encoded| encoded.replace('.', "")) + .and_then(|encoded| base32::decode(base32::Alphabet::Crockford, &encoded.to_uppercase())) + .and_then(|data| String::from_utf8(data).ok()) + .ok_or_else(|| State::Error(ContentHandler::error( + StatusCode::BadRequest, + "Invalid parameter", + "Couldn't parse given parameter:", + self.path.app_params.get(0).map(String::as_str), + self.embeddable_on.clone() + )))?; - // Support domain based routing. - let idx = match path.get(0).map(|m| m.as_ref()) { - Some(apps::WEB_PATH) => 1, - _ => 0, - }; + let mut token_it = token_and_url.split('+'); + let token = token_it.next(); + let target_url = token_it.next(); // Check if token supplied in URL is correct. - match path.get(idx) { - Some(ref token) if self.web_proxy_tokens.is_web_proxy_token_valid(token) => {}, + match token { + Some(token) if self.web_proxy_tokens.is_web_proxy_token_valid(token) => {}, _ => { return Err(State::Error(ContentHandler::error( StatusCode::BadRequest, "Invalid Access Token", "Invalid or old web proxy access token supplied.", Some("Try refreshing the page."), self.embeddable_on.clone() @@ -141,9 +143,8 @@ impl WebHandler { } // Validate protocol - let protocol = match path.get(idx + 1).map(|a| a.as_str()) { - Some("http") => "http", - Some("https") => "https", + let mut target_url = match target_url { + Some(url) if url.starts_with("http://") || url.starts_with("https://") => url.to_owned(), _ => { return Err(State::Error(ContentHandler::error( StatusCode::BadRequest, "Invalid Protocol", "Invalid protocol used.", None, self.embeddable_on.clone() @@ -151,28 +152,35 @@ impl WebHandler { } }; - // Redirect if address to main page does not end with / - if let None = path.get(idx + 3) { - return Err(State::Redirecting( - Redirection::new(&format!("/{}/", path.join("/"))) - )); + if !target_url.ends_with("/") { + target_url = format!("{}/", target_url); } - let query = match query { - Some(query) => format!("?{}", query), + // TODO [ToDr] Should just use `path.app_params` + let (path, query) = match (&url, self.path.using_dapps_domains) { + (&Some(ref url), true) => (&url.path[..], &url.query), + (&Some(ref url), false) => (&url.path[2..], &url.query), + _ => { + return Err(State::Error(ContentHandler::error( + StatusCode::BadRequest, "Invalid URL", "Couldn't parse URL", None, self.embeddable_on.clone() + ))); + } + }; + + let query = match *query { + Some(ref query) => format!("?{}", query), None => "".into(), }; - Ok((format!("{}://{}{}", protocol, path[idx + 2..].join("/"), query), path[0..].join("/"))) + Ok(format!("{}{}{}", target_url, path.join("/"), query)) } } impl server::Handler for WebHandler { fn on_request(&mut self, request: server::Request) -> Next { let url = extract_url(&request); - // First extract the URL (reject invalid URLs) - let (target_url, referer) = match self.extract_target_url(url) { + let target_url = match self.extract_target_url(url) { Ok(url) => url, Err(error) => { self.state = error; @@ -186,7 +194,9 @@ impl server::Handler for WebHandler { self.control.clone(), WebInstaller { embeddable_on: self.embeddable_on.clone(), - referer: referer, + referer: self.path.app_params.get(0) + .expect("`target_url` is valid; app_params is not empty;qed") + .to_owned(), }, self.embeddable_on.clone(), self.remote.clone(), @@ -202,7 +212,6 @@ impl server::Handler for WebHandler { match self.state { State::Initial => Next::end(), State::Error(ref mut handler) => handler.on_request_readable(decoder), - State::Redirecting(ref mut handler) => handler.on_request_readable(decoder), State::Fetching(ref mut handler) => handler.on_request_readable(decoder), } } @@ -211,7 +220,6 @@ impl server::Handler for WebHandler { match self.state { State::Initial => Next::end(), State::Error(ref mut handler) => handler.on_response(res), - State::Redirecting(ref mut handler) => handler.on_response(res), State::Fetching(ref mut handler) => handler.on_response(res), } } @@ -220,7 +228,6 @@ impl server::Handler for WebHandler { match self.state { State::Initial => Next::end(), State::Error(ref mut handler) => handler.on_response_writable(encoder), - State::Redirecting(ref mut handler) => handler.on_response_writable(encoder), State::Fetching(ref mut handler) => handler.on_response_writable(encoder), } } diff --git a/devtools/src/http_client.rs b/devtools/src/http_client.rs index f2b8c7931..de59a7a71 100644 --- a/devtools/src/http_client.rs +++ b/devtools/src/http_client.rs @@ -87,7 +87,7 @@ pub fn request(address: &SocketAddr, request: &str) -> Response { let _ = req.read_to_string(&mut response); let mut lines = response.lines(); - let status = lines.next().unwrap().to_owned(); + let status = lines.next().expect("Expected a response").to_owned(); let headers_raw = read_block(&mut lines, false); let headers = headers_raw.split('\n').map(|v| v.to_owned()).collect(); let body = read_block(&mut lines, true); diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 77189c3fd..d85685f3f 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -262,6 +262,18 @@ impl Client { Ok(client) } + /// Wakes up client if it's a sleep. + pub fn keep_alive(&self) { + let should_wake = match *self.mode.lock() { + Mode::Dark(..) | Mode::Passive(..) => true, + _ => false, + }; + if should_wake { + self.wake_up(); + (*self.sleep_state.lock()).last_activity = Some(Instant::now()); + } + } + /// Adds an actor to be notified on certain events pub fn add_notify(&self, target: Arc) { self.notify.write().push(Arc::downgrade(&target)); @@ -1011,17 +1023,6 @@ impl BlockChainClient for Client { Ok(ret) } - fn keep_alive(&self) { - let should_wake = match *self.mode.lock() { - Mode::Dark(..) | Mode::Passive(..) => true, - _ => false, - }; - if should_wake { - self.wake_up(); - (*self.sleep_state.lock()).last_activity = Some(Instant::now()); - } - } - fn mode(&self) -> IpcMode { let r = self.mode.lock().clone().into(); trace!(target: "mode", "Asked for mode = {:?}. returning {:?}", &*self.mode.lock(), r); diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index 519c9185a..dfb251296 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -46,10 +46,6 @@ use encoded; /// Blockchain database client. Owns and manages a blockchain and a block queue. pub trait BlockChainClient : Sync + Send { - /// Should be called by any external-facing interface when actively using the client. - /// To minimise chatter, there's no need to call more than once every 30s. - fn keep_alive(&self) {} - /// Get raw block header data by block id. fn block_header(&self, id: BlockId) -> Option; diff --git a/js/package.json b/js/package.json index 9eb0cd804..705dd51df 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.3.62", + "version": "0.3.66", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", @@ -140,6 +140,7 @@ "yargs": "6.6.0" }, "dependencies": { + "base32.js": "0.1.0", "bignumber.js": "3.0.1", "blockies": "0.0.2", "brace": "0.9.0", @@ -193,6 +194,7 @@ "scryptsy": "2.0.0", "solc": "ngotchac/solc-js", "store": "1.3.20", + "useragent.js": "0.5.6", "utf8": "2.1.2", "valid-url": "1.0.9", "validator": "6.2.0", diff --git a/js/src/api/format/input.js b/js/src/api/format/input.js index 05593fc5e..74ddd4e56 100644 --- a/js/src/api/format/input.js +++ b/js/src/api/format/input.js @@ -127,6 +127,18 @@ export function inNumber16 (number) { return inHex(bn.toString(16)); } +export function inOptionsCondition (condition) { + if (condition) { + if (condition.block) { + condition.block = condition.block ? inNumber10(condition.block) : null; + } else if (condition.time) { + condition.time = inNumber10(Math.floor(condition.time.getTime() / 1000)); + } + } + + return condition; +} + export function inOptions (options) { if (options) { Object.keys(options).forEach((key) => { @@ -136,6 +148,10 @@ export function inOptions (options) { options[key] = inAddress(options[key]); break; + case 'condition': + options[key] = inOptionsCondition(options[key]); + break; + case 'gas': case 'gasPrice': options[key] = inNumber16((new BigNumber(options[key])).round()); diff --git a/js/src/api/format/output.js b/js/src/api/format/output.js index 35ec67ebf..dd61c82b1 100644 --- a/js/src/api/format/output.js +++ b/js/src/api/format/output.js @@ -221,6 +221,18 @@ export function outSyncing (syncing) { return syncing; } +export function outTransactionCondition (condition) { + if (condition) { + if (condition.block) { + condition.block = outNumber(condition.block); + } else if (condition.time) { + condition.time = outDate(condition.time); + } + } + + return condition; +} + export function outTransaction (tx) { if (tx) { Object.keys(tx).forEach((key) => { @@ -234,8 +246,14 @@ export function outTransaction (tx) { tx[key] = outNumber(tx[key]); break; + case 'condition': + tx[key] = outTransactionCondition(tx[key]); + break; + case 'minBlock': - tx[key] = tx[key] ? outNumber(tx[key]) : null; + tx[key] = tx[key] + ? outNumber(tx[key]) + : null; break; case 'creates': diff --git a/js/src/modals/AddDapps/addDapps.css b/js/src/modals/AddDapps/addDapps.css index 8de8f7f80..b26ffa623 100644 --- a/js/src/modals/AddDapps/addDapps.css +++ b/js/src/modals/AddDapps/addDapps.css @@ -20,7 +20,6 @@ } .container { - margin-top: 1.5em; overflow-y: auto; } diff --git a/js/src/modals/AddDapps/addDapps.js b/js/src/modals/AddDapps/addDapps.js index 0a4d19af9..9b1fcc760 100644 --- a/js/src/modals/AddDapps/addDapps.js +++ b/js/src/modals/AddDapps/addDapps.js @@ -18,7 +18,7 @@ import { observer } from 'mobx-react'; import React, { Component, PropTypes } from 'react'; import { FormattedMessage } from 'react-intl'; -import { ContainerTitle, DappCard, Portal, SectionList } from '~/ui'; +import { DappCard, Portal, SectionList } from '~/ui'; import { CheckIcon } from '~/ui/Icons'; import styles from './addDapps.css'; @@ -41,15 +41,13 @@ export default class AddDapps extends Component { className={ styles.modal } onClose={ store.closeModal } open + title={ + + } > - - } - />
{ diff --git a/js/src/modals/DappPermissions/dappPermissions.css b/js/src/modals/DappPermissions/dappPermissions.css index 8b7c03a6a..22df5d24b 100644 --- a/js/src/modals/DappPermissions/dappPermissions.css +++ b/js/src/modals/DappPermissions/dappPermissions.css @@ -15,12 +15,7 @@ /* along with Parity. If not, see . */ -.modal { - flex-direction: column; -} - .container { - margin-top: 1.5em; overflow-y: auto; } @@ -65,7 +60,6 @@ .legend { opacity: 0.75; - margin-top: 1em; span { line-height: 24px; diff --git a/js/src/modals/DappPermissions/dappPermissions.js b/js/src/modals/DappPermissions/dappPermissions.js index dd6318368..4cd7cc837 100644 --- a/js/src/modals/DappPermissions/dappPermissions.js +++ b/js/src/modals/DappPermissions/dappPermissions.js @@ -18,7 +18,7 @@ import { observer } from 'mobx-react'; import React, { Component, PropTypes } from 'react'; import { FormattedMessage } from 'react-intl'; -import { AccountCard, ContainerTitle, Portal, SectionList } from '~/ui'; +import { AccountCard, Portal, SectionList } from '~/ui'; import { CheckIcon, StarIcon, StarOutlineIcon } from '~/ui/Icons'; import styles from './dappPermissions.css'; @@ -38,18 +38,27 @@ export default class DappPermissions extends Component { return ( + , + defaultIcon: + } } + /> +
+ } onClose={ store.closeModal } open + title={ + + } > - - } - />
-
- , - defaultIcon: - } } - /> -
); } diff --git a/js/src/modals/ExecuteContract/AdvancedStep/advancedStep.js b/js/src/modals/ExecuteContract/AdvancedStep/advancedStep.js index cf592c5cb..268ca1f70 100644 --- a/js/src/modals/ExecuteContract/AdvancedStep/advancedStep.js +++ b/js/src/modals/ExecuteContract/AdvancedStep/advancedStep.js @@ -15,45 +15,22 @@ // along with Parity. If not, see . import React, { Component, PropTypes } from 'react'; -import { FormattedMessage } from 'react-intl'; -import { Input, GasPriceEditor } from '~/ui'; +import { GasPriceEditor } from '~/ui'; import styles from '../executeContract.css'; export default class AdvancedStep extends Component { static propTypes = { - gasStore: PropTypes.object.isRequired, - minBlock: PropTypes.string, - minBlockError: PropTypes.string, - onMinBlockChange: PropTypes.func + gasStore: PropTypes.object.isRequired }; render () { - const { gasStore, minBlock, minBlockError, onMinBlockChange } = this.props; + const { gasStore } = this.props; return ( -
- - } - label={ - - } - value={ minBlock } - onSubmit={ onMinBlockChange } - /> -
- -
+
+
); } diff --git a/js/src/modals/ExecuteContract/executeContract.js b/js/src/modals/ExecuteContract/executeContract.js index 6f750a8dc..0828a5f3e 100644 --- a/js/src/modals/ExecuteContract/executeContract.js +++ b/js/src/modals/ExecuteContract/executeContract.js @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import BigNumber from 'bignumber.js'; import { pick } from 'lodash'; import { observer } from 'mobx-react'; import React, { Component, PropTypes } from 'react'; @@ -100,8 +99,6 @@ class ExecuteContract extends Component { fromAddressError: null, func: null, funcError: null, - minBlock: '0', - minBlockError: null, rejected: false, sending: false, step: STEP_DETAILS, @@ -167,8 +164,8 @@ class ExecuteContract extends Component { renderDialogActions () { const { onClose, fromAddress } = this.props; - const { advancedOptions, sending, step, fromAddressError, minBlockError, valuesError } = this.state; - const hasError = fromAddressError || minBlockError || valuesError.find((error) => error); + const { advancedOptions, sending, step, fromAddressError, valuesError } = this.state; + const hasError = fromAddressError || valuesError.find((error) => error); const cancelBtn = (