diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index bc9d9257db5..c8f5f4d774a 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -564,7 +564,7 @@ test-windows:
- git submodule update --init --recursive
script:
- set RUST_BACKTRACE=1
- - echo cargo test --features json-tests -p rlp -p ethash -p ethcore -p ethcore-bigint -p ethcore-dapps -p parity-rpc -p ethcore-signer -p ethcore-util -p ethcore-network -p ethcore-io -p ethkey -p ethstore -p ethsync -p ethcore-ipc -p ethcore-ipc-tests -p ethcore-ipc-nano -p parity %CARGOFLAGS% --verbose --release
+ - echo cargo test --features json-tests -p rlp -p ethash -p ethcore -p ethcore-bigint -p parity-dapps -p parity-rpc -p ethcore-util -p ethcore-network -p ethcore-io -p ethkey -p ethstore -p ethsync -p ethcore-ipc -p ethcore-ipc-tests -p ethcore-ipc-nano -p parity-rpc-client -p parity %CARGOFLAGS% --verbose --release
tags:
- rust-windows
allow_failure: true
diff --git a/Cargo.lock b/Cargo.lock
index 05a44cd6b41..b63349f7735 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -609,26 +609,6 @@ dependencies = [
"url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
-[[package]]
-name = "ethcore-signer"
-version = "1.7.0"
-dependencies = [
- "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
- "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "ethcore-devtools 1.7.0",
- "ethcore-io 1.7.0",
- "ethcore-util 1.7.0",
- "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
- "jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
- "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "parity-rpc 1.7.0",
- "parity-ui 1.7.0",
- "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "ws 0.5.3 (git+https://github.com/paritytech/ws-rs.git?branch=parity-1.7)",
-]
-
[[package]]
name = "ethcore-stratum"
version = "1.7.0"
@@ -1608,7 +1588,6 @@ dependencies = [
"ethcore-light 1.7.0",
"ethcore-logger 1.7.0",
"ethcore-secretstore 1.0.0",
- "ethcore-signer 1.7.0",
"ethcore-stratum 1.7.0",
"ethcore-util 1.7.0",
"ethkey 0.2.0",
@@ -1797,18 +1776,18 @@ dependencies = [
name = "parity-rpc-client"
version = "1.4.0"
dependencies = [
- "ethcore-signer 1.7.0",
"ethcore-util 1.7.0",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
+ "jsonrpc-ws-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-rpc 1.7.0",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
"tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "ws 0.5.3 (git+https://github.com/paritytech/ws-rs.git?branch=parity-1.7)",
]
[[package]]
@@ -2385,11 +2364,6 @@ name = "siphasher"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-[[package]]
-name = "slab"
-version = "0.2.0"
-source = "git+https://github.com/carllerche/slab?rev=5476efcafb#5476efcafbc5ef4d7315b1bea3f756d8a1fe975e"
-
[[package]]
name = "slab"
version = "0.2.0"
@@ -2823,25 +2797,10 @@ name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-[[package]]
-name = "ws"
-version = "0.5.3"
-source = "git+https://github.com/paritytech/ws-rs.git?branch=parity-1.7#30415c17f1bec53b2dcabae5b8b887df75dcbe34"
-dependencies = [
- "bytes 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "mio 0.6.1 (git+https://github.com/paritytech/mio)",
- "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
- "sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "slab 0.2.0 (git+https://github.com/carllerche/slab?rev=5476efcafb)",
- "url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
[[package]]
name = "ws"
version = "0.6.0"
-source = "git+https://github.com/tomusdrw/ws-rs#3259e7ca906c848beae109eb32e492871f8f397d"
+source = "git+https://github.com/tomusdrw/ws-rs#7f8e416b7f048880228005457e117128be38bf0f"
dependencies = [
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3082,7 +3041,6 @@ dependencies = [
"checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c"
"checksum shell32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72f20b8f3c060374edb8046591ba28f62448c369ccbdc7b02075103fb3a9e38d"
"checksum siphasher 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c44e42fa187b5a8782489cf7740cc27c3125806be2bf33563cf5e02e9533fcd"
-"checksum slab 0.2.0 (git+https://github.com/carllerche/slab?rev=5476efcafb)" = ""
"checksum slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6dbdd334bd28d328dad1c41b0ea662517883d8880d8533895ef96c8003dec9c4"
"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
"checksum smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "fcc8d19212aacecf95e4a7a2179b26f7aeb9732a915cf01f05b0d3e044865410"
@@ -3135,7 +3093,6 @@ dependencies = [
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
-"checksum ws 0.5.3 (git+https://github.com/paritytech/ws-rs.git?branch=parity-1.7)" = ""
"checksum ws 0.6.0 (git+https://github.com/tomusdrw/ws-rs)" = ""
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
"checksum xdg 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "77b831a5ba77110f438f0ac5583aafeb087f70432998ba6b7dcb1d32185db453"
diff --git a/Cargo.toml b/Cargo.toml
index c0eccff2836..597766a4992 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -33,7 +33,6 @@ ethcore = { path = "ethcore" }
ethcore-util = { path = "util" }
ethcore-io = { path = "util/io" }
ethcore-devtools = { path = "devtools" }
-ethcore-signer = { path = "signer" }
ethcore-ipc = { path = "ipc/rpc" }
ethcore-ipc-nano = { path = "ipc/nano" }
ethcore-ipc-hypervisor = { path = "ipc/hypervisor" }
@@ -75,17 +74,15 @@ default = ["ui-precompiled"]
ui = [
"dapps",
"parity-dapps/ui",
- "ethcore-signer/ui",
]
ui-precompiled = [
"dapps",
- "ethcore-signer/ui-precompiled",
"parity-dapps/ui-precompiled",
]
dapps = ["parity-dapps"]
ipc = ["ethcore/ipc", "ethsync/ipc"]
jit = ["ethcore/jit"]
-dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "parity-rpc/dev", "parity-dapps/dev", "ethcore-signer/dev"]
+dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "parity-rpc/dev", "parity-dapps/dev"]
json-tests = ["ethcore/json-tests"]
test-heavy = ["ethcore/test-heavy"]
ethkey-cli = ["ethcore/ethkey-cli"]
diff --git a/dapps/src/api/api.rs b/dapps/src/api/api.rs
index 064ad6d429a..d377ebe57f1 100644
--- a/dapps/src/api/api.rs
+++ b/dapps/src/api/api.rs
@@ -16,42 +16,27 @@
use std::sync::Arc;
-use unicase::UniCase;
use hyper::{server, net, Decoder, Encoder, Next, Control};
-use hyper::header;
use hyper::method::Method;
-use api::types::{App, ApiError};
+use api::types::ApiError;
use api::response;
use apps::fetcher::Fetcher;
use handlers::extract_url;
-use endpoint::{Endpoint, Endpoints, Handler, EndpointPath};
-use jsonrpc_http_server::{self, AccessControlAllowOrigin};
+use endpoint::{Endpoint, Handler, EndpointPath};
#[derive(Clone)]
pub struct RestApi {
- // TODO [ToDr] cors_domains should be handled by the server to avoid duplicated logic.
- // RequestMiddleware should be able to tell that cors headers should be included.
- cors_domains: Option>,
- apps: Vec,
fetcher: Arc,
}
impl RestApi {
- pub fn new(cors_domains: Vec, endpoints: &Endpoints, fetcher: Arc) -> Box {
+ pub fn new(fetcher: Arc) -> Box {
Box::new(RestApi {
- cors_domains: Some(cors_domains),
- apps: Self::list_apps(endpoints),
fetcher: fetcher,
})
}
-
- fn list_apps(endpoints: &Endpoints) -> Vec {
- endpoints.iter().filter_map(|(ref k, ref e)| {
- e.info().map(|ref info| App::from_info(k, info))
- }).collect()
- }
}
impl Endpoint for RestApi {
@@ -62,7 +47,6 @@ impl Endpoint for RestApi {
struct RestApiRouter {
api: RestApi,
- cors_header: Option,
path: Option,
control: Option,
handler: Box,
@@ -72,7 +56,6 @@ impl RestApiRouter {
fn new(api: RestApi, path: EndpointPath, control: Control) -> Self {
RestApiRouter {
path: Some(path),
- cors_header: None,
control: Some(control),
api: api,
handler: response::as_json_error(&ApiError {
@@ -92,35 +75,10 @@ impl RestApiRouter {
_ => None
}
}
-
- /// Returns basic headers for a response (it may be overwritten by the handler)
- fn response_headers(cors_header: Option) -> header::Headers {
- let mut headers = header::Headers::new();
-
- if let Some(cors_header) = cors_header {
- headers.set(header::AccessControlAllowCredentials);
- headers.set(header::AccessControlAllowMethods(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(cors_header);
- }
-
- headers
- }
}
impl server::Handler for RestApiRouter {
fn on_request(&mut self, request: server::Request) -> Next {
- self.cors_header = jsonrpc_http_server::cors_header(&request, &self.api.cors_domains).into();
-
if let Method::Options = *request.method() {
self.handler = response::empty();
return Next::write();
@@ -144,7 +102,6 @@ impl server::Handler for RestApiRouter {
if let Some(ref hash) = hash { path.app_id = hash.clone().to_owned() }
let handler = endpoint.and_then(|v| match v {
- "apps" => Some(response::as_json(&self.api.apps)),
"ping" => Some(response::ping()),
"content" => self.resolve_content(hash, path, control),
_ => None
@@ -163,7 +120,6 @@ impl server::Handler for RestApiRouter {
}
fn on_response(&mut self, res: &mut server::Response) -> Next {
- *res.headers_mut() = Self::response_headers(self.cors_header.take());
self.handler.on_response(res)
}
diff --git a/dapps/src/api/mod.rs b/dapps/src/api/mod.rs
index f04b18878e6..4ffb9f791a7 100644
--- a/dapps/src/api/mod.rs
+++ b/dapps/src/api/mod.rs
@@ -21,4 +21,3 @@ mod response;
mod types;
pub use self::api::RestApi;
-pub use self::types::App;
diff --git a/dapps/src/api/response.rs b/dapps/src/api/response.rs
index 380b1f996c8..2da2d0c14bd 100644
--- a/dapps/src/api/response.rs
+++ b/dapps/src/api/response.rs
@@ -23,12 +23,6 @@ pub fn empty() -> Box {
Box::new(ContentHandler::ok("".into(), mime!(Text/Plain)))
}
-pub fn as_json(val: &T) -> Box {
- let json = serde_json::to_string(val)
- .expect("serialization to string is infallible; qed");
- Box::new(ContentHandler::ok(json, mime!(Application/Json)))
-}
-
pub fn as_json_error(val: &T) -> Box {
let json = serde_json::to_string(val)
.expect("serialization to string is infallible; qed");
diff --git a/dapps/src/api/types.rs b/dapps/src/api/types.rs
index a690a0b2bbe..549186955f9 100644
--- a/dapps/src/api/types.rs
+++ b/dapps/src/api/types.rs
@@ -14,46 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
-use endpoint::EndpointInfo;
-
-#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
-#[serde(deny_unknown_fields)]
-pub struct App {
- pub id: String,
- pub name: String,
- pub description: String,
- pub version: String,
- pub author: String,
- #[serde(rename="iconUrl")]
- pub icon_url: String,
-}
-
-impl App {
- /// Creates `App` instance from `EndpointInfo` and `id`.
- pub fn from_info(id: &str, info: &EndpointInfo) -> Self {
- App {
- id: id.to_owned(),
- name: info.name.to_owned(),
- description: info.description.to_owned(),
- version: info.version.to_owned(),
- author: info.author.to_owned(),
- icon_url: info.icon_url.to_owned(),
- }
- }
-}
-
-impl Into for App {
- fn into(self) -> EndpointInfo {
- EndpointInfo {
- name: self.name,
- description: self.description,
- version: self.version,
- author: self.author,
- icon_url: self.icon_url,
- }
- }
-}
-
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ApiError {
diff --git a/dapps/src/apps/app.rs b/dapps/src/apps/app.rs
new file mode 100644
index 00000000000..1d2c9dca6a9
--- /dev/null
+++ b/dapps/src/apps/app.rs
@@ -0,0 +1,55 @@
+// Copyright 2015-2017 Parity Technologies (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 .
+
+use endpoint::EndpointInfo;
+
+#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
+#[serde(deny_unknown_fields)]
+pub struct App {
+ pub id: String,
+ pub name: String,
+ pub description: String,
+ pub version: String,
+ pub author: String,
+ #[serde(rename="iconUrl")]
+ pub icon_url: String,
+}
+
+impl App {
+ /// Creates `App` instance from `EndpointInfo` and `id`.
+ pub fn from_info(id: &str, info: &EndpointInfo) -> Self {
+ App {
+ id: id.to_owned(),
+ name: info.name.to_owned(),
+ description: info.description.to_owned(),
+ version: info.version.to_owned(),
+ author: info.author.to_owned(),
+ icon_url: info.icon_url.to_owned(),
+ }
+ }
+}
+
+impl Into for App {
+ fn into(self) -> EndpointInfo {
+ EndpointInfo {
+ name: self.name,
+ description: self.description,
+ version: self.version,
+ author: self.author,
+ icon_url: self.icon_url,
+ }
+ }
+}
diff --git a/dapps/src/apps/fetcher/mod.rs b/dapps/src/apps/fetcher/mod.rs
index ec8004b300e..d621042c4a9 100644
--- a/dapps/src/apps/fetcher/mod.rs
+++ b/dapps/src/apps/fetcher/mod.rs
@@ -55,6 +55,7 @@ pub struct ContentFetcher,
remote: Remote,
fetch: F,
+ only_content: bool,
}
impl Drop for ContentFetcher {
@@ -66,7 +67,12 @@ impl Drop for ContentFetcher {
impl ContentFetcher {
- pub fn new(resolver: R, sync_status: Arc, embeddable_on: Option<(String, u16)>, remote: Remote, fetch: F) -> Self {
+ pub fn new(
+ resolver: R,
+ sync_status: Arc,
+ remote: Remote,
+ fetch: F,
+ ) -> Self {
let mut dapps_path = env::temp_dir();
dapps_path.push(random_filename());
@@ -75,12 +81,23 @@ impl ContentFetcher {
resolver: resolver,
sync: sync_status,
cache: Arc::new(Mutex::new(ContentCache::default())),
- embeddable_on: embeddable_on,
+ embeddable_on: None,
remote: remote,
fetch: fetch,
+ only_content: true,
}
}
+ pub fn allow_dapps(mut self, dapps: bool) -> Self {
+ self.only_content = !dapps;
+ self
+ }
+
+ pub fn embeddable_on(mut self, embeddable_on: Option<(String, u16)>) -> Self {
+ self.embeddable_on = embeddable_on;
+ self
+ }
+
fn still_syncing(address: Option<(String, u16)>) -> Box {
Box::new(ContentHandler::error(
StatusCode::ServiceUnavailable,
@@ -91,6 +108,16 @@ impl ContentFetcher {
))
}
+ fn dapps_disabled(address: Option<(String, u16)>) -> Box {
+ Box::new(ContentHandler::error(
+ StatusCode::ServiceUnavailable,
+ "Network Dapps Not Available",
+ "This interface doesn't support network dapps for security reasons.",
+ None,
+ address,
+ ))
+ }
+
#[cfg(test)]
fn set_status(&self, content_id: &str, status: ContentStatus) {
self.cache.lock().insert(content_id.to_owned(), status);
@@ -163,6 +190,9 @@ impl Fetcher for ContentFetcher {
Some(URLHintResult::Dapp(_)) if self.sync.is_major_importing() => {
(None, Self::still_syncing(self.embeddable_on.clone()))
},
+ Some(URLHintResult::Dapp(_)) if self.only_content => {
+ (None, Self::dapps_disabled(self.embeddable_on.clone()))
+ },
Some(URLHintResult::Dapp(dapp)) => {
let handler = ContentFetcherHandler::new(
dapp.url(),
@@ -254,7 +284,8 @@ mod tests {
fn should_true_if_contains_the_app() {
// given
let path = env::temp_dir();
- let fetcher = ContentFetcher::new(FakeResolver, Arc::new(|| false), None, Remote::new_sync(), Client::new().unwrap());
+ let fetcher = ContentFetcher::new(FakeResolver, Arc::new(|| false), Remote::new_sync(), Client::new().unwrap())
+ .allow_dapps(true);
let handler = LocalPageEndpoint::new(path, EndpointInfo {
name: "fake".into(),
description: "".into(),
diff --git a/dapps/src/apps/fs.rs b/dapps/src/apps/fs.rs
index d14f52c690e..8c5c65202ff 100644
--- a/dapps/src/apps/fs.rs
+++ b/dapps/src/apps/fs.rs
@@ -14,12 +14,13 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
+use std::collections::BTreeMap;
use std::io;
use std::io::Read;
use std::fs;
use std::path::{Path, PathBuf};
use page::{LocalPageEndpoint, PageCache};
-use endpoint::{Endpoints, EndpointInfo};
+use endpoint::{Endpoint, EndpointInfo};
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest};
struct LocalDapp {
@@ -85,8 +86,8 @@ fn local_dapp(name: String, path: PathBuf) -> LocalDapp {
/// Returns endpoints for Local Dapps found for given filesystem path.
/// Scans the directory and collects `LocalPageEndpoints`.
-pub fn local_endpoints>(dapps_path: P, signer_address: Option<(String, u16)>) -> Endpoints {
- let mut pages = Endpoints::new();
+pub fn local_endpoints>(dapps_path: P, signer_address: Option<(String, u16)>) -> BTreeMap> {
+ let mut pages = BTreeMap::>::new();
for dapp in local_dapps(dapps_path.as_ref()) {
pages.insert(
dapp.id,
diff --git a/dapps/src/apps/manifest.rs b/dapps/src/apps/manifest.rs
index a40cfb8b980..94611498043 100644
--- a/dapps/src/apps/manifest.rs
+++ b/dapps/src/apps/manifest.rs
@@ -15,7 +15,7 @@
// along with Parity. If not, see .
use serde_json;
-pub use api::App as Manifest;
+pub use apps::App as Manifest;
pub const MANIFEST_FILENAME: &'static str = "manifest.json";
diff --git a/dapps/src/apps/mod.rs b/dapps/src/apps/mod.rs
index b85f0dde9a3..b3c5a5cef3b 100644
--- a/dapps/src/apps/mod.rs
+++ b/dapps/src/apps/mod.rs
@@ -14,8 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
+use std::collections::BTreeMap;
use std::path::PathBuf;
use std::sync::Arc;
+
use endpoint::{Endpoints, Endpoint};
use page::PageEndpoint;
use proxypac::ProxyPac;
@@ -23,17 +25,19 @@ use web::Web;
use fetch::Fetch;
use parity_dapps::WebApp;
use parity_reactor::Remote;
+use parity_ui;
use {WebProxyTokens};
+mod app;
mod cache;
mod fs;
+mod ui;
pub mod fetcher;
pub mod manifest;
-extern crate parity_ui;
+pub use self::app::App;
-pub const HOME_PAGE: &'static str = "parity";
-pub const DAPPS_DOMAIN: &'static str = ".web3.site";
+pub const HOME_PAGE: &'static str = "home";
pub const RPC_PATH: &'static str = "rpc";
pub const API_PATH: &'static str = "api";
pub const UTILS_PATH: &'static str = "parity-utils";
@@ -44,18 +48,27 @@ pub fn utils() -> Box {
Box::new(PageEndpoint::with_prefix(parity_ui::App::default(), UTILS_PATH.to_owned()))
}
+pub fn ui() -> Box {
+ Box::new(PageEndpoint::with_fallback_to_index(parity_ui::App::default()))
+}
+
+pub fn ui_redirection(ui_address: Option<(String, u16)>) -> Box {
+ Box::new(ui::Redirection::new(ui_address))
+}
+
pub fn all_endpoints(
dapps_path: PathBuf,
extra_dapps: Vec,
- signer_address: Option<(String, u16)>,
+ dapps_domain: String,
+ ui_address: Option<(String, u16)>,
web_proxy_tokens: Arc,
remote: Remote,
fetch: F,
) -> Endpoints {
// fetch fs dapps at first to avoid overwriting builtins
- let mut pages = fs::local_endpoints(dapps_path, signer_address.clone());
+ let mut pages = fs::local_endpoints(dapps_path, ui_address.clone());
for path in extra_dapps {
- if let Some((id, endpoint)) = fs::local_endpoint(path.clone(), signer_address.clone()) {
+ if let Some((id, endpoint)) = fs::local_endpoint(path.clone(), ui_address.clone()) {
pages.insert(id, endpoint);
} else {
warn!(target: "dapps", "Ignoring invalid dapp at {}", path.display());
@@ -63,14 +76,14 @@ pub fn all_endpoints(
}
// NOTE [ToDr] Dapps will be currently embeded on 8180
- insert::(&mut pages, "ui", Embeddable::Yes(signer_address.clone()));
- pages.insert("proxy".into(), ProxyPac::boxed(signer_address.clone()));
- pages.insert(WEB_PATH.into(), Web::boxed(signer_address.clone(), web_proxy_tokens.clone(), remote.clone(), fetch.clone()));
+ insert::(&mut pages, "ui", Embeddable::Yes(ui_address.clone()));
+ pages.insert("proxy".into(), ProxyPac::boxed(ui_address.clone(), dapps_domain));
+ pages.insert(WEB_PATH.into(), Web::boxed(ui_address.clone(), web_proxy_tokens.clone(), remote.clone(), fetch.clone()));
- pages
+ Arc::new(pages)
}
-fn insert(pages: &mut Endpoints, id: &str, embed_at: Embeddable) {
+fn insert(pages: &mut BTreeMap>, id: &str, embed_at: Embeddable) {
pages.insert(id.to_owned(), Box::new(match embed_at {
Embeddable::Yes(address) => PageEndpoint::new_safe_to_embed(T::default(), address),
Embeddable::No => PageEndpoint::new(T::default()),
diff --git a/dapps/src/apps/ui.rs b/dapps/src/apps/ui.rs
new file mode 100644
index 00000000000..d5e7bd5e8f7
--- /dev/null
+++ b/dapps/src/apps/ui.rs
@@ -0,0 +1,55 @@
+// Copyright 2015-2017 Parity Technologies (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 .
+
+//! UI redirections
+
+use hyper::{Control, StatusCode};
+
+use endpoint::{Endpoint, Handler, EndpointPath};
+use {address, handlers};
+
+/// Redirection to UI server.
+pub struct Redirection {
+ signer_address: Option<(String, u16)>,
+}
+
+impl Redirection {
+ pub fn new(
+ signer_address: Option<(String, u16)>,
+ ) -> Self {
+ Redirection {
+ signer_address: signer_address,
+ }
+ }
+}
+
+impl Endpoint for Redirection {
+ fn to_async_handler(&self, _path: EndpointPath, _control: Control) -> Box {
+ if let Some(ref signer_address) = self.signer_address {
+ trace!(target: "dapps", "Redirecting to signer interface.");
+ handlers::Redirection::boxed(&format!("http://{}", address(signer_address)))
+ } else {
+ trace!(target: "dapps", "Signer disabled, returning 404.");
+ Box::new(handlers::ContentHandler::error(
+ StatusCode::NotFound,
+ "404 Not Found",
+ "Your homepage is not available when Trusted Signer is disabled.",
+ Some("You can still access dapps by writing a correct address, though. Re-enable Signer to get your homepage back."),
+ self.signer_address.clone(),
+ ))
+ }
+ }
+}
diff --git a/dapps/src/endpoint.rs b/dapps/src/endpoint.rs
index ea5825b7495..ea8fd0a3842 100644
--- a/dapps/src/endpoint.rs
+++ b/dapps/src/endpoint.rs
@@ -16,6 +16,7 @@
//! URL Endpoint traits
+use std::sync::Arc;
use std::collections::BTreeMap;
use hyper::{self, server, net};
@@ -38,7 +39,7 @@ pub struct EndpointInfo {
pub icon_url: String,
}
-pub type Endpoints = BTreeMap>;
+pub type Endpoints = Arc>>;
pub type Handler = server::Handler + Send;
pub trait Endpoint : Send + Sync {
diff --git a/dapps/src/lib.rs b/dapps/src/lib.rs
index 5f4b833252a..0860f0c1092 100644
--- a/dapps/src/lib.rs
+++ b/dapps/src/lib.rs
@@ -40,6 +40,7 @@ extern crate fetch;
extern crate parity_dapps_glue as parity_dapps;
extern crate parity_hash_fetch as hash_fetch;
extern crate parity_reactor;
+extern crate parity_ui;
#[macro_use]
extern crate log;
@@ -70,7 +71,7 @@ use std::path::PathBuf;
use std::sync::Arc;
use std::collections::HashMap;
-use jsonrpc_http_server::{self as http, hyper, AccessControlAllowOrigin};
+use jsonrpc_http_server::{self as http, hyper};
use fetch::Fetch;
use parity_reactor::Remote;
@@ -97,18 +98,74 @@ impl WebProxyTokens for F where F: Fn(String) -> bool + Send + Sync {
fn is_web_proxy_token_valid(&self, token: &str) -> bool { self(token.to_owned()) }
}
+/// Current supported endpoints.
+pub struct Endpoints {
+ endpoints: endpoint::Endpoints,
+}
+
+impl Endpoints {
+ /// Returns a current list of app endpoints.
+ pub fn list(&self) -> Vec {
+ self.endpoints.iter().filter_map(|(ref k, ref e)| {
+ e.info().map(|ref info| apps::App::from_info(k, info))
+ }).collect()
+ }
+}
+
/// Dapps server as `jsonrpc-http-server` request middleware.
pub struct Middleware {
router: router::Router,
+ endpoints: endpoint::Endpoints,
}
impl Middleware {
+ /// Get local endpoints handle.
+ pub fn endpoints(&self) -> Endpoints {
+ Endpoints {
+ endpoints: self.endpoints.clone(),
+ }
+ }
+
+ /// Creates new middleware for UI server.
+ pub fn ui(
+ remote: Remote,
+ registrar: Arc,
+ sync_status: Arc,
+ fetch: F,
+ dapps_domain: String,
+ ) -> Self {
+ let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(
+ hash_fetch::urlhint::URLHintContract::new(registrar),
+ sync_status,
+ remote.clone(),
+ fetch.clone(),
+ ).embeddable_on(None).allow_dapps(false));
+ let special = {
+ let mut special = special_endpoints(content_fetcher.clone());
+ special.insert(router::SpecialEndpoint::Home, Some(apps::ui()));
+ special
+ };
+ let router = router::Router::new(
+ content_fetcher,
+ None,
+ special,
+ None,
+ dapps_domain,
+ );
+
+ Middleware {
+ router: router,
+ endpoints: Default::default(),
+ }
+ }
+
/// Creates new Dapps server middleware.
- pub fn new(
+ pub fn dapps(
remote: Remote,
- signer_address: Option<(String, u16)>,
+ ui_address: Option<(String, u16)>,
dapps_path: PathBuf,
extra_dapps: Vec,
+ dapps_domain: String,
registrar: Arc,
sync_status: Arc,
web_proxy_tokens: Arc,
@@ -117,45 +174,36 @@ impl Middleware {
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(
hash_fetch::urlhint::URLHintContract::new(registrar),
sync_status,
- signer_address.clone(),
remote.clone(),
fetch.clone(),
- ));
+ ).embeddable_on(ui_address.clone()).allow_dapps(true));
let endpoints = apps::all_endpoints(
dapps_path,
extra_dapps,
- signer_address.clone(),
+ dapps_domain.clone(),
+ ui_address.clone(),
web_proxy_tokens,
remote.clone(),
fetch.clone(),
);
- let cors_domains = cors_domains(signer_address.clone());
-
let special = {
- let mut special = HashMap::new();
- special.insert(router::SpecialEndpoint::Rpc, None);
- special.insert(router::SpecialEndpoint::Utils, Some(apps::utils()));
- special.insert(
- router::SpecialEndpoint::Api,
- Some(api::RestApi::new(
- cors_domains.clone(),
- &endpoints,
- content_fetcher.clone()
- )),
- );
+ let mut special = special_endpoints(content_fetcher.clone());
+ special.insert(router::SpecialEndpoint::Home, Some(apps::ui_redirection(ui_address.clone())));
special
};
let router = router::Router::new(
- signer_address,
content_fetcher,
- endpoints,
+ Some(endpoints.clone()),
special,
+ ui_address,
+ dapps_domain,
);
Middleware {
router: router,
+ endpoints: endpoints,
}
}
}
@@ -166,21 +214,12 @@ impl http::RequestMiddleware for Middleware {
}
}
-/// Returns a list of CORS domains for API endpoint.
-fn cors_domains(signer_address: Option<(String, u16)>) -> Vec {
- use self::apps::{HOME_PAGE, DAPPS_DOMAIN};
-
- match signer_address {
- Some(signer_address) => [
- format!("http://{}{}", HOME_PAGE, DAPPS_DOMAIN),
- format!("http://{}{}:{}", HOME_PAGE, DAPPS_DOMAIN, signer_address.1),
- format!("http://{}", address(&signer_address)),
- format!("https://{}{}", HOME_PAGE, DAPPS_DOMAIN),
- format!("https://{}{}:{}", HOME_PAGE, DAPPS_DOMAIN, signer_address.1),
- format!("https://{}", address(&signer_address)),
- ].into_iter().map(|val| AccessControlAllowOrigin::Value(val.into())).collect(),
- None => vec![],
- }
+fn special_endpoints(content_fetcher: Arc) -> HashMap>> {
+ let mut special = HashMap::new();
+ special.insert(router::SpecialEndpoint::Rpc, None);
+ special.insert(router::SpecialEndpoint::Utils, Some(apps::utils()));
+ special.insert(router::SpecialEndpoint::Api, Some(api::RestApi::new(content_fetcher)));
+ special
}
fn address(address: &(String, u16)) -> String {
@@ -193,29 +232,3 @@ fn random_filename() -> String {
let mut rng = ::rand::OsRng::new().unwrap();
rng.gen_ascii_chars().take(12).collect()
}
-
-#[cfg(test)]
-mod util_tests {
- use super::cors_domains;
- use jsonrpc_http_server::AccessControlAllowOrigin;
-
- #[test]
- fn should_return_cors_domains() {
- // given
-
- // when
- let none = cors_domains(None);
- let some = cors_domains(Some(("127.0.0.1".into(), 18180)));
-
- // then
- assert_eq!(none, Vec::::new());
- assert_eq!(some, vec![
- "http://parity.web3.site".into(),
- "http://parity.web3.site:18180".into(),
- "http://127.0.0.1:18180".into(),
- "https://parity.web3.site".into(),
- "https://parity.web3.site:18180".into(),
- "https://127.0.0.1:18180".into(),
- ]);
- }
-}
diff --git a/dapps/src/page/builtin.rs b/dapps/src/page/builtin.rs
index c778b29776e..e93c2953832 100644
--- a/dapps/src/page/builtin.rs
+++ b/dapps/src/page/builtin.rs
@@ -27,6 +27,7 @@ pub struct PageEndpoint {
/// Safe to be loaded in frame by other origin. (use wisely!)
safe_to_embed_on: Option<(String, u16)>,
info: EndpointInfo,
+ fallback_to_index_html: bool,
}
impl PageEndpoint {
@@ -38,6 +39,20 @@ impl PageEndpoint {
prefix: None,
safe_to_embed_on: None,
info: EndpointInfo::from(info),
+ fallback_to_index_html: false,
+ }
+ }
+
+ /// Creates a new `PageEndpoint` for builtin (compile time) Dapp.
+ /// Instead of returning 404 this endpoint will always server index.html.
+ pub fn with_fallback_to_index(app: T) -> Self {
+ let info = app.info();
+ PageEndpoint {
+ app: Arc::new(app),
+ prefix: None,
+ safe_to_embed_on: None,
+ info: EndpointInfo::from(info),
+ fallback_to_index_html: true,
}
}
@@ -51,6 +66,7 @@ impl PageEndpoint {
prefix: Some(prefix),
safe_to_embed_on: None,
info: EndpointInfo::from(info),
+ fallback_to_index_html: false,
}
}
@@ -64,6 +80,7 @@ impl PageEndpoint {
prefix: None,
safe_to_embed_on: address,
info: EndpointInfo::from(info),
+ fallback_to_index_html: false,
}
}
}
@@ -76,7 +93,7 @@ impl Endpoint for PageEndpoint {
fn to_handler(&self, path: EndpointPath) -> Box {
Box::new(handler::PageHandler {
- app: BuiltinDapp::new(self.app.clone()),
+ app: BuiltinDapp::new(self.app.clone(), self.fallback_to_index_html),
prefix: self.prefix.clone(),
path: path,
file: handler::ServedFile::new(self.safe_to_embed_on.clone()),
@@ -100,12 +117,14 @@ impl From for EndpointInfo {
struct BuiltinDapp {
app: Arc,
+ fallback_to_index_html: bool,
}
impl BuiltinDapp {
- fn new(app: Arc) -> Self {
+ fn new(app: Arc, fallback_to_index_html: bool) -> Self {
BuiltinDapp {
app: app,
+ fallback_to_index_html: fallback_to_index_html,
}
}
}
@@ -114,13 +133,19 @@ impl handler::Dapp for BuiltinDapp {
type DappFile = BuiltinDappFile;
fn file(&self, path: &str) -> Option {
- self.app.file(path).map(|_| {
+ let file = |path| self.app.file(path).map(|_| {
BuiltinDappFile {
app: self.app.clone(),
path: path.into(),
write_pos: 0,
}
- })
+ });
+ let res = file(path);
+ if self.fallback_to_index_html {
+ res.or_else(|| file("index.html"))
+ } else {
+ res
+ }
}
}
diff --git a/dapps/src/proxypac.rs b/dapps/src/proxypac.rs
index 16459d88e76..13ea4c665e9 100644
--- a/dapps/src/proxypac.rs
+++ b/dapps/src/proxypac.rs
@@ -18,17 +18,19 @@
use endpoint::{Endpoint, Handler, EndpointPath};
use handlers::ContentHandler;
-use apps::{HOME_PAGE, DAPPS_DOMAIN};
+use apps::HOME_PAGE;
use address;
pub struct ProxyPac {
signer_address: Option<(String, u16)>,
+ dapps_domain: String,
}
impl ProxyPac {
- pub fn boxed(signer_address: Option<(String, u16)>) -> Box {
+ pub fn boxed(signer_address: Option<(String, u16)>, dapps_domain: String) -> Box {
Box::new(ProxyPac {
- signer_address: signer_address
+ signer_address: signer_address,
+ dapps_domain: dapps_domain,
})
}
}
@@ -43,12 +45,12 @@ impl Endpoint for ProxyPac {
let content = format!(
r#"
function FindProxyForURL(url, host) {{
- if (shExpMatch(host, "{0}{1}"))
+ if (shExpMatch(host, "{0}.{1}"))
{{
return "PROXY {4}";
}}
- if (shExpMatch(host, "*{1}"))
+ if (shExpMatch(host, "*.{1}"))
{{
return "PROXY {2}:{3}";
}}
@@ -56,7 +58,7 @@ function FindProxyForURL(url, host) {{
return "DIRECT";
}}
"#,
- HOME_PAGE, DAPPS_DOMAIN, path.host, path.port, signer);
+ HOME_PAGE, self.dapps_domain, path.host, path.port, signer);
Box::new(ContentHandler::ok(content, mime!(Application/Javascript)))
}
diff --git a/dapps/src/router.rs b/dapps/src/router.rs
index c7b7fb7ffc2..b3454c78274 100644
--- a/dapps/src/router.rs
+++ b/dapps/src/router.rs
@@ -17,20 +17,19 @@
//! Router implementation
//! Dispatch requests to proper application.
-use address;
use std::cmp;
use std::sync::Arc;
use std::collections::HashMap;
use url::{Url, Host};
-use hyper::{self, server, header, Control, StatusCode};
+use hyper::{self, server, header, Control};
use hyper::net::HttpStream;
use jsonrpc_http_server as http;
-use apps::{self, DAPPS_DOMAIN};
+use apps;
use apps::fetcher::Fetcher;
use endpoint::{Endpoint, Endpoints, EndpointPath, Handler};
-use handlers::{self, Redirection, ContentHandler};
+use handlers;
/// Special endpoints are accessible on every domain (every dapp)
#[derive(Debug, PartialEq, Hash, Eq)]
@@ -38,26 +37,28 @@ pub enum SpecialEndpoint {
Rpc,
Api,
Utils,
+ Home,
None,
}
pub struct Router {
- signer_address: Option<(String, u16)>,
- endpoints: Endpoints,
+ endpoints: Option,
fetch: Arc,
special: HashMap>>,
+ embeddable_on: Option<(String, u16)>,
+ dapps_domain: String,
}
impl http::RequestMiddleware for Router {
fn on_request(&self, req: &server::Request, control: &Control) -> http::RequestMiddlewareAction {
// Choose proper handler depending on path / domain
let url = handlers::extract_url(req);
- let endpoint = extract_endpoint(&url);
- let referer = extract_referer_endpoint(req);
+ let endpoint = extract_endpoint(&url, &self.dapps_domain);
+ let referer = extract_referer_endpoint(req, &self.dapps_domain);
let is_utils = endpoint.1 == SpecialEndpoint::Utils;
- let is_dapps_domain = endpoint.0.as_ref().map(|endpoint| endpoint.using_dapps_domains).unwrap_or(false);
- let is_origin_set = req.headers().get::().is_some();
+ let is_origin_set = req.headers().get::().is_some();
let is_get_request = *req.method() == hyper::Method::Get;
+ let is_head_request = *req.method() == hyper::Method::Head;
trace!(target: "dapps", "Routing request to {:?}. Details: {:?}", url, req);
@@ -67,7 +68,7 @@ impl http::RequestMiddleware for Router {
// Handle invalid web requests that we can recover from
(ref path, SpecialEndpoint::None, Some((ref referer, ref referer_url)))
if referer.app_id == apps::WEB_PATH
- && self.endpoints.contains_key(apps::WEB_PATH)
+ && self.endpoints.as_ref().map(|ep| ep.contains_key(apps::WEB_PATH)).unwrap_or(false)
&& !is_web_endpoint(path)
=>
{
@@ -75,7 +76,7 @@ impl http::RequestMiddleware for Router {
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();
- Some(Redirection::boxed(&format!("/{}/{}", base, requested)))
+ Some(handlers::Redirection::boxed(&format!("/{}/{}", base, requested)))
},
// First check special endpoints
(ref path, ref endpoint, _) if self.special.contains_key(endpoint) => {
@@ -86,9 +87,12 @@ impl http::RequestMiddleware for Router {
.map(|special| special.to_async_handler(path.clone().unwrap_or_default(), control))
},
// Then delegate to dapp
- (Some(ref path), _, _) if self.endpoints.contains_key(&path.app_id) => {
+ (Some(ref path), _, _) if self.endpoints.as_ref().map(|ep| ep.contains_key(&path.app_id)).unwrap_or(false) => {
trace!(target: "dapps", "Resolving to local/builtin dapp.");
- Some(self.endpoints.get(&path.app_id)
+ Some(self.endpoints
+ .as_ref()
+ .expect("endpoints known to be set; qed")
+ .get(&path.app_id)
.expect("endpoints known to contain key; qed")
.to_async_handler(path.clone(), control))
},
@@ -97,36 +101,28 @@ impl http::RequestMiddleware for Router {
trace!(target: "dapps", "Resolving to fetchable content.");
Some(self.fetch.to_async_handler(path.clone(), control))
},
- // NOTE [todr] /home is redirected to home page since some users may have the redirection cached
- // (in the past we used 301 instead of 302)
- // It should be safe to remove it in (near) future.
- //
- // 404 for non-existent content
- (Some(ref path), _, _) if is_get_request && path.app_id != "home" => {
+ // 404 for non-existent content (only if serving endpoints and not homepage)
+ (Some(ref path), _, _)
+ if (is_get_request || is_head_request)
+ && self.endpoints.is_some()
+ && path.app_id != apps::HOME_PAGE
+ =>
+ {
trace!(target: "dapps", "Resolving to 404.");
- Some(Box::new(ContentHandler::error(
- StatusCode::NotFound,
+ Some(Box::new(handlers::ContentHandler::error(
+ hyper::StatusCode::NotFound,
"404 Not Found",
"Requested content was not found.",
None,
- self.signer_address.clone(),
+ self.embeddable_on.clone(),
)))
},
- // Redirect any other GET request to signer.
- _ if is_get_request => {
- if let Some(ref signer_address) = self.signer_address {
- trace!(target: "dapps", "Redirecting to signer interface.");
- Some(Redirection::boxed(&format!("http://{}", address(signer_address))))
- } else {
- trace!(target: "dapps", "Signer disabled, returning 404.");
- Some(Box::new(ContentHandler::error(
- StatusCode::NotFound,
- "404 Not Found",
- "Your homepage is not available when Trusted Signer is disabled.",
- Some("You can still access dapps by writing a correct address, though. Re-enable Signer to get your homepage back."),
- self.signer_address.clone(),
- )))
- }
+ // Any other GET|HEAD requests to home page.
+ _ if (is_get_request || is_head_request) && self.special.contains_key(&SpecialEndpoint::Home) => {
+ self.special.get(&SpecialEndpoint::Home)
+ .expect("special known to contain key; qed")
+ .as_ref()
+ .map(|special| special.to_async_handler(Default::default(), control))
},
// RPC by default
_ => {
@@ -137,7 +133,7 @@ impl http::RequestMiddleware for Router {
match handler {
Some(handler) => http::RequestMiddlewareAction::Respond {
- should_validate_hosts: !(is_utils || is_dapps_domain),
+ should_validate_hosts: !is_utils,
handler: handler,
},
None => http::RequestMiddlewareAction::Proceed {
@@ -149,16 +145,18 @@ impl http::RequestMiddleware for Router {
impl Router {
pub fn new(
- signer_address: Option<(String, u16)>,
content_fetcher: Arc,
- endpoints: Endpoints,
+ endpoints: Option,
special: HashMap>>,
+ embeddable_on: Option<(String, u16)>,
+ dapps_domain: String,
) -> Self {
Router {
- signer_address: signer_address,
endpoints: endpoints,
fetch: content_fetcher,
special: special,
+ embeddable_on: embeddable_on,
+ dapps_domain: format!(".{}", dapps_domain),
}
}
}
@@ -170,19 +168,19 @@ fn is_web_endpoint(path: &Option) -> bool {
}
}
-fn extract_referer_endpoint(req: &server::Request) -> Option<(EndpointPath, Url)> {
+fn extract_referer_endpoint(req: &server::Request, dapps_domain: &str) -> Option<(EndpointPath, Url)> {
let referer = req.headers().get::();
let url = referer.and_then(|referer| Url::parse(&referer.0).ok());
url.and_then(|url| {
let option = Some(url);
- extract_url_referer_endpoint(&option).or_else(|| {
- extract_endpoint(&option).0.map(|endpoint| (endpoint, option.expect("Just wrapped; qed")))
+ extract_url_referer_endpoint(&option, dapps_domain).or_else(|| {
+ extract_endpoint(&option, dapps_domain).0.map(|endpoint| (endpoint, option.expect("Just wrapped; qed")))
})
})
}
-fn extract_url_referer_endpoint(url: &Option) -> Option<(EndpointPath, Url)> {
+fn extract_url_referer_endpoint(url: &Option, dapps_domain: &str) -> Option<(EndpointPath, Url)> {
let query = url.as_ref().and_then(|url| url.query.as_ref());
match (url, query) {
(&Some(ref url), Some(ref query)) if query.starts_with(apps::URL_REFERER) => {
@@ -190,7 +188,7 @@ fn extract_url_referer_endpoint(url: &Option) -> Option<(EndpointPath, Url)
debug!(target: "dapps", "Recovering referer from query parameter: {}", referer_url);
let referer_url = Url::parse(&referer_url).ok();
- extract_endpoint(&referer_url).0.map(|endpoint| {
+ extract_endpoint(&referer_url, dapps_domain).0.map(|endpoint| {
(endpoint, referer_url.expect("Endpoint returned only when url `is_some`").clone())
})
},
@@ -198,7 +196,7 @@ fn extract_url_referer_endpoint(url: &Option) -> Option<(EndpointPath, Url)
}
}
-fn extract_endpoint(url: &Option) -> (Option, SpecialEndpoint) {
+fn extract_endpoint(url: &Option, dapps_domain: &str) -> (Option, SpecialEndpoint) {
fn special_endpoint(url: &Url) -> SpecialEndpoint {
if url.path.len() <= 1 {
return SpecialEndpoint::None;
@@ -208,14 +206,15 @@ fn extract_endpoint(url: &Option) -> (Option, SpecialEndpoint
apps::RPC_PATH => SpecialEndpoint::Rpc,
apps::API_PATH => SpecialEndpoint::Api,
apps::UTILS_PATH => SpecialEndpoint::Utils,
+ apps::HOME_PAGE => SpecialEndpoint::Home,
_ => SpecialEndpoint::None,
}
}
match *url {
Some(ref url) => match url.host {
- Host::Domain(ref domain) if domain.ends_with(DAPPS_DOMAIN) => {
- let id = &domain[0..(domain.len() - DAPPS_DOMAIN.len())];
+ Host::Domain(ref domain) if domain.ends_with(dapps_domain) => {
+ 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())
@@ -249,11 +248,12 @@ fn extract_endpoint(url: &Option) -> (Option, SpecialEndpoint
#[test]
fn should_extract_endpoint() {
- assert_eq!(extract_endpoint(&None), (None, SpecialEndpoint::None));
+ let dapps_domain = ".web3.site";
+ assert_eq!(extract_endpoint(&None, dapps_domain), (None, SpecialEndpoint::None));
// With path prefix
assert_eq!(
- extract_endpoint(&Url::parse("http://localhost:8080/status/index.html").ok()),
+ extract_endpoint(&Url::parse("http://localhost:8080/status/index.html").ok(), dapps_domain),
(Some(EndpointPath {
app_id: "status".to_owned(),
app_params: vec!["index.html".to_owned()],
@@ -265,7 +265,7 @@ fn should_extract_endpoint() {
// With path prefix
assert_eq!(
- extract_endpoint(&Url::parse("http://localhost:8080/rpc/").ok()),
+ extract_endpoint(&Url::parse("http://localhost:8080/rpc/").ok(), dapps_domain),
(Some(EndpointPath {
app_id: "rpc".to_owned(),
app_params: vec!["".to_owned()],
@@ -276,7 +276,7 @@ fn should_extract_endpoint() {
);
assert_eq!(
- extract_endpoint(&Url::parse("http://my.status.web3.site/parity-utils/inject.js").ok()),
+ extract_endpoint(&Url::parse("http://my.status.web3.site/parity-utils/inject.js").ok(), dapps_domain),
(Some(EndpointPath {
app_id: "status".to_owned(),
app_params: vec!["my".to_owned(), "parity-utils".into(), "inject.js".into()],
@@ -288,7 +288,7 @@ fn should_extract_endpoint() {
// By Subdomain
assert_eq!(
- extract_endpoint(&Url::parse("http://status.web3.site/test.html").ok()),
+ extract_endpoint(&Url::parse("http://status.web3.site/test.html").ok(), dapps_domain),
(Some(EndpointPath {
app_id: "status".to_owned(),
app_params: vec!["test.html".to_owned()],
@@ -300,7 +300,7 @@ fn should_extract_endpoint() {
// RPC by subdomain
assert_eq!(
- extract_endpoint(&Url::parse("http://my.status.web3.site/rpc/").ok()),
+ extract_endpoint(&Url::parse("http://my.status.web3.site/rpc/").ok(), dapps_domain),
(Some(EndpointPath {
app_id: "status".to_owned(),
app_params: vec!["my".to_owned(), "rpc".into(), "".into()],
@@ -312,7 +312,7 @@ fn should_extract_endpoint() {
// API by subdomain
assert_eq!(
- extract_endpoint(&Url::parse("http://my.status.web3.site/api/").ok()),
+ extract_endpoint(&Url::parse("http://my.status.web3.site/api/").ok(), dapps_domain),
(Some(EndpointPath {
app_id: "status".to_owned(),
app_params: vec!["my".to_owned(), "api".into(), "".into()],
diff --git a/dapps/src/rpc.rs b/dapps/src/rpc.rs
deleted file mode 100644
index 74c6d8d8984..00000000000
--- a/dapps/src/rpc.rs
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2015-2017 Parity Technologies (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 .
-
-use std::sync::Arc;
-use hyper;
-
-use parity_rpc::{Metadata, Origin};
-use jsonrpc_core::{Middleware, MetaIoHandler};
-use jsonrpc_http_server::{self as http, AccessControlAllowOrigin, HttpMetaExtractor};
-use jsonrpc_http_server::tokio_core::reactor::Remote;
-use endpoint::{Endpoint, EndpointPath, Handler};
-
-pub fn rpc>(
- handler: MetaIoHandler,
- remote: Remote,
- cors_domains: Vec,
-) -> Box {
- Box::new(RpcEndpoint {
- handler: Arc::new(handler),
- remote: remote,
- meta_extractor: Arc::new(MetadataExtractor),
- cors_domain: Some(cors_domains),
- // NOTE [ToDr] We don't need to do any hosts validation here. It's already done in router.
- allowed_hosts: None,
- })
-}
-
-struct RpcEndpoint> {
- handler: Arc>,
- remote: Remote,
- meta_extractor: Arc>,
- cors_domain: Option>,
- allowed_hosts: Option>,
-}
-
-
-impl> Endpoint for RpcEndpoint {
- fn to_async_handler(&self, _path: EndpointPath, control: hyper::Control) -> Box {
- Box::new(http::ServerHandler::new(
- http::Rpc {
- handler: self.handler.clone(),
- remote: self.remote.clone(),
- extractor: self.meta_extractor.clone(),
- },
- self.cors_domain.clone(),
- self.allowed_hosts.clone(),
- Arc::new(NoopMiddleware),
- control,
- ))
- }
-}
-
-#[derive(Default)]
-struct NoopMiddleware;
-impl http::RequestMiddleware for NoopMiddleware {
- fn on_request(&self, request: &http::hyper::server::Request, _control: &http::hyper::Control) -> http::RequestMiddlewareAction {
- http::RequestMiddlewareAction::Proceed {
- should_continue_on_invalid_cors: request.headers().get::().is_none(),
- }
- }
-}
-
-pub struct MetadataExtractor;
-impl HttpMetaExtractor for MetadataExtractor {
- fn read_metadata(&self, request: &http::hyper::server::Request) -> Metadata {
- let dapp_id = request.headers().get::()
- .map(|origin| format!("{}://{}", origin.scheme, origin.host))
- .or_else(|| {
- // fallback to custom header, but only if origin is null
- request.headers().get_raw("origin")
- .and_then(|raw| raw.one())
- .and_then(|raw| if raw == "null".as_bytes() {
- request.headers().get_raw("x-parity-origin")
- .and_then(|raw| raw.one())
- .map(|raw| String::from_utf8_lossy(raw).into_owned())
- } else {
- None
- })
- });
- Metadata {
- origin: Origin::Dapps(dapp_id.map(Into::into).unwrap_or_default()),
- }
- }
-}
diff --git a/dapps/src/tests/api.rs b/dapps/src/tests/api.rs
index 04381437734..b75cd25f29e 100644
--- a/dapps/src/tests/api.rs
+++ b/dapps/src/tests/api.rs
@@ -39,29 +39,6 @@ fn should_return_error() {
assert_security_headers(&response.headers);
}
-#[test]
-fn should_serve_apps() {
- // given
- let server = serve();
-
- // when
- let response = request(server,
- "\
- GET /api/apps HTTP/1.1\r\n\
- Host: 127.0.0.1:8080\r\n\
- Connection: close\r\n\
- \r\n\
- {}
- "
- );
-
- // then
- response.assert_status("HTTP/1.1 200 OK");
- response.assert_header("Content-Type", "application/json");
- assert!(response.body.contains("Parity UI"), response.body);
- assert_security_headers(&response.headers);
-}
-
#[test]
fn should_handle_ping() {
// given
@@ -106,92 +83,3 @@ fn should_try_to_resolve_dapp() {
assert_eq!(registrar.calls.lock().len(), 2);
assert_security_headers(&response.headers);
}
-
-#[test]
-fn should_return_signer_port_cors_headers() {
- // given
- let server = serve();
-
- // when
- let response = request(server,
- "\
- POST /api/ping HTTP/1.1\r\n\
- Host: localhost:8080\r\n\
- Origin: http://127.0.0.1:18180\r\n\
- Connection: close\r\n\
- \r\n\
- {}
- "
- );
-
- // then
- response.assert_status("HTTP/1.1 200 OK");
- response.assert_header("Access-Control-Allow-Origin", "http://127.0.0.1:18180");
-}
-
-#[test]
-fn should_return_signer_port_cors_headers_for_home_parity() {
- // given
- let server = serve();
-
- // when
- let response = request(server,
- "\
- POST /api/ping HTTP/1.1\r\n\
- Host: localhost:8080\r\n\
- Origin: http://parity.web3.site\r\n\
- Connection: close\r\n\
- \r\n\
- {}
- "
- );
-
- // then
- response.assert_status("HTTP/1.1 200 OK");
- response.assert_header("Access-Control-Allow-Origin", "http://parity.web3.site");
-}
-
-
-#[test]
-fn should_return_signer_port_cors_headers_for_home_parity_with_https() {
- // given
- let server = serve();
-
- // when
- let response = request(server,
- "\
- POST /api/ping HTTP/1.1\r\n\
- Host: localhost:8080\r\n\
- Origin: https://parity.web3.site\r\n\
- Connection: close\r\n\
- \r\n\
- {}
- "
- );
-
- // then
- response.assert_status("HTTP/1.1 200 OK");
- response.assert_header("Access-Control-Allow-Origin", "https://parity.web3.site");
-}
-
-#[test]
-fn should_return_signer_port_cors_headers_for_home_parity_with_port() {
- // given
- let server = serve();
-
- // when
- let response = request(server,
- "\
- POST /api/ping HTTP/1.1\r\n\
- Host: localhost:8080\r\n\
- Origin: http://parity.web3.site:18180\r\n\
- Connection: close\r\n\
- \r\n\
- {}
- "
- );
-
- // then
- response.assert_status("HTTP/1.1 200 OK");
- response.assert_header("Access-Control-Allow-Origin", "http://parity.web3.site:18180");
-}
diff --git a/dapps/src/tests/helpers/mod.rs b/dapps/src/tests/helpers/mod.rs
index e6c0325491d..6bc0006ce83 100644
--- a/dapps/src/tests/helpers/mod.rs
+++ b/dapps/src/tests/helpers/mod.rs
@@ -26,7 +26,7 @@ use jsonrpc_http_server::{self as http, Host, DomainsValidation};
use devtools::http_client;
use hash_fetch::urlhint::ContractClient;
use fetch::{Fetch, Client as FetchClient};
-use parity_reactor::{EventLoop, Remote};
+use parity_reactor::Remote;
use {Middleware, SyncStatus, WebProxyTokens};
@@ -47,20 +47,7 @@ fn init_logger() {
}
}
-pub struct ServerLoop {
- pub server: Server,
- pub event_loop: EventLoop,
-}
-
-impl ::std::ops::Deref for ServerLoop {
- type Target = Server;
-
- fn deref(&self) -> &Self::Target {
- &self.server
- }
-}
-
-pub fn init_server(process: F, io: IoHandler, remote: Remote) -> (ServerLoop, Arc) where
+pub fn init_server(process: F, io: IoHandler, remote: Remote) -> (Server, Arc) where
F: FnOnce(ServerBuilder) -> ServerBuilder,
B: Fetch,
{
@@ -69,44 +56,41 @@ pub fn init_server(process: F, io: IoHandler, remote: Remote) -> (ServerLo
let mut dapps_path = env::temp_dir();
dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading");
- // TODO [ToDr] When https://github.com/paritytech/jsonrpc/issues/26 is resolved
- // this additional EventLoop wouldn't be needed, we should be able to re-use remote.
- let event_loop = EventLoop::spawn();
let server = process(ServerBuilder::new(
&dapps_path, registrar.clone(), remote,
))
.signer_address(Some(("127.0.0.1".into(), SIGNER_PORT)))
.start_unsecured_http(&"127.0.0.1:0".parse().unwrap(), io).unwrap();
(
- ServerLoop { server: server, event_loop: event_loop },
+ server,
registrar,
)
}
-pub fn serve_with_rpc(io: IoHandler) -> ServerLoop {
+pub fn serve_with_rpc(io: IoHandler) -> Server {
init_server(|builder| builder, io, Remote::new_sync()).0
}
-pub fn serve_hosts(hosts: Option>) -> ServerLoop {
+pub fn serve_hosts(hosts: Option>) -> Server {
let hosts = hosts.map(|hosts| hosts.into_iter().map(Into::into).collect());
init_server(|builder| builder.allowed_hosts(hosts.into()), Default::default(), Remote::new_sync()).0
}
-pub fn serve_with_registrar() -> (ServerLoop, Arc) {
+pub fn serve_with_registrar() -> (Server, Arc) {
init_server(|builder| builder, Default::default(), Remote::new_sync())
}
-pub fn serve_with_registrar_and_sync() -> (ServerLoop, Arc) {
+pub fn serve_with_registrar_and_sync() -> (Server, Arc) {
init_server(|builder| {
builder.sync_status(Arc::new(|| true))
}, Default::default(), Remote::new_sync())
}
-pub fn serve_with_registrar_and_fetch() -> (ServerLoop, FakeFetch, Arc) {
+pub fn serve_with_registrar_and_fetch() -> (Server, FakeFetch, Arc) {
serve_with_registrar_and_fetch_and_threads(false)
}
-pub fn serve_with_registrar_and_fetch_and_threads(multi_threaded: bool) -> (ServerLoop, FakeFetch, Arc) {
+pub fn serve_with_registrar_and_fetch_and_threads(multi_threaded: bool) -> (Server, FakeFetch, Arc) {
let fetch = FakeFetch::default();
let f = fetch.clone();
let (server, reg) = init_server(move |builder| {
@@ -116,7 +100,7 @@ pub fn serve_with_registrar_and_fetch_and_threads(multi_threaded: bool) -> (Serv
(server, fetch, reg)
}
-pub fn serve_with_fetch(web_token: &'static str) -> (ServerLoop, FakeFetch) {
+pub fn serve_with_fetch(web_token: &'static str) -> (Server, FakeFetch) {
let fetch = FakeFetch::default();
let f = fetch.clone();
let (server, _) = init_server(move |builder| {
@@ -128,11 +112,11 @@ pub fn serve_with_fetch(web_token: &'static str) -> (ServerLoop, FakeFetch) {
(server, fetch)
}
-pub fn serve() -> ServerLoop {
+pub fn serve() -> Server {
init_server(|builder| builder, Default::default(), Remote::new_sync()).0
}
-pub fn request(server: ServerLoop, request: &str) -> http_client::Response {
+pub fn request(server: Server, request: &str) -> http_client::Response {
http_client::request(server.addr(), request)
}
@@ -240,6 +224,7 @@ impl ServerBuilder {
}
}
+const DAPPS_DOMAIN: &'static str = "web3.site";
/// Webapps HTTP server.
pub struct Server {
@@ -260,19 +245,27 @@ impl Server {
remote: Remote,
fetch: F,
) -> Result {
- let middleware = Middleware::new(
+ let middleware = Middleware::dapps(
remote,
signer_address,
dapps_path,
extra_dapps,
+ DAPPS_DOMAIN.into(),
registrar,
sync_status,
web_proxy_tokens,
fetch,
);
+
+ let mut allowed_hosts: Option> = allowed_hosts.into();
+ allowed_hosts.as_mut().map(|mut hosts| {
+ hosts.push(format!("http://*.{}:*", DAPPS_DOMAIN).into());
+ hosts.push(format!("http://*.{}", DAPPS_DOMAIN).into());
+ });
+
http::ServerBuilder::new(io)
.request_middleware(middleware)
- .allowed_hosts(allowed_hosts)
+ .allowed_hosts(allowed_hosts.into())
.cors(http::DomainsValidation::Disabled)
.start_http(addr)
.map(|server| Server {
diff --git a/dapps/src/tests/redirection.rs b/dapps/src/tests/redirection.rs
index 4e3fff4dc97..1e9b039e2ea 100644
--- a/dapps/src/tests/redirection.rs
+++ b/dapps/src/tests/redirection.rs
@@ -37,15 +37,15 @@ fn should_redirect_to_home() {
}
#[test]
-fn should_redirect_to_home_when_trailing_slash_is_missing() {
+fn should_redirect_to_home_with_domain() {
// given
let server = serve();
// when
let response = request(server,
"\
- GET /app HTTP/1.1\r\n\
- Host: 127.0.0.1:8080\r\n\
+ GET / HTTP/1.1\r\n\
+ Host: home.web3.site\r\n\
Connection: close\r\n\
\r\n\
"
@@ -57,14 +57,14 @@ fn should_redirect_to_home_when_trailing_slash_is_missing() {
}
#[test]
-fn should_redirect_to_home_for_users_with_cached_redirection() {
+fn should_redirect_to_home_when_trailing_slash_is_missing() {
// given
let server = serve();
// when
let response = request(server,
"\
- GET /home/ HTTP/1.1\r\n\
+ GET /app HTTP/1.1\r\n\
Host: 127.0.0.1:8080\r\n\
Connection: close\r\n\
\r\n\
@@ -179,7 +179,7 @@ fn should_serve_proxy_pac() {
// then
response.assert_status("HTTP/1.1 200 OK");
- 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_eq!(response.body, "DB\n\nfunction FindProxyForURL(url, host) {\n\tif (shExpMatch(host, \"home.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/js/src/api/rpc/parity/parity.js b/js/src/api/rpc/parity/parity.js
index 22b3f97514d..4fdaf5b1beb 100644
--- a/js/src/api/rpc/parity/parity.js
+++ b/js/src/api/rpc/parity/parity.js
@@ -90,15 +90,14 @@ export default class Parity {
.execute('parity_consensusCapability');
}
- dappsPort () {
+ dappsList () {
return this._transport
- .execute('parity_dappsPort')
- .then(outNumber);
+ .execute('parity_dappsList');
}
- dappsInterface () {
+ dappsUrl () {
return this._transport
- .execute('parity_dappsInterface');
+ .execute('parity_dappsUrl');
}
decryptMessage (address, data) {
@@ -530,12 +529,6 @@ export default class Parity {
.execute('parity_setVaultMeta', vaultName, JSON.stringify(meta));
}
- signerPort () {
- return this._transport
- .execute('parity_signerPort')
- .then(outNumber);
- }
-
signMessage (address, password, messageHash) {
return this._transport
.execute('parity_signMessage', inAddress(address), password, inHex(messageHash));
@@ -567,4 +560,9 @@ export default class Parity {
return this._transport
.execute('parity_versionInfo');
}
+
+ wsUrl () {
+ return this._transport
+ .execute('parity_wsUrl');
+ }
}
diff --git a/js/src/dapps/console/parity.js b/js/src/dapps/console/parity.js
index 9fce483e19b..d05cc2350d0 100644
--- a/js/src/dapps/console/parity.js
+++ b/js/src/dapps/console/parity.js
@@ -19,312 +19,308 @@ import Web3 from 'web3';
const api = window.parent.secureApi;
let web3;
-Promise
- .all([
- api.parity.dappsInterface(),
- api.parity.dappsPort()
- ]).then((res) => {
- web3 = new Web3(new Web3.providers.HttpProvider(`http://${res.join(':')}/rpc/`));
- window.web3 = web3;
-
- // Usage example:
- // web3.eth.traceCall({
- // to: theChicken.address,
- // data: theChicken.withdraw.getData(100000000000000000),
- // gas: 100000
- // },
- // `["trace", "vmTrace", "stateDiff"]
- // )
- web3._extend({
- property: 'eth',
- methods: [
- new web3._extend.Method({
- name: 'traceCall',
- call: 'trace_call',
- params: 2,
- inputFormatter: [web3._extend.formatters.inputCallFormatter, null]
- })
- ]
- });
-
- web3._extend({
- property: 'eth',
- methods: [
- new web3._extend.Method({
- name: 'traceSendRawTransaction',
- call: 'trace_rawTransaction',
- params: 2,
- inputFormatter: [null, null]
- })
- ]
- });
-
- web3._extend({
- property: 'eth',
- methods: [
- new web3._extend.Method({
- name: 'traceReplayTransaction',
- call: 'trace_replayTransaction',
- params: 2,
- inputFormatter: [null, null]
- })
- ]
- });
-
- web3._extend({
- property: 'eth',
- methods: [
- new web3._extend.Method({
- name: 'setMode',
- call: 'parity_setMode',
- params: 1
- })
- ]
- });
-
- web3._extend({
- property: 'eth',
- methods: [
- new web3._extend.Method({
- name: 'mode',
- call: 'parity_mode',
- params: 0
- })
- ]
- });
-
- web3._extend({
- property: 'eth',
- methods: [
- new web3._extend.Method({
- name: 'traceTransaction',
- call: 'trace_Transaction',
- params: 1,
- inputFormatter: [null]
- })
- ]
- });
-
- web3._extend({
- property: 'eth',
- methods: [
- new web3._extend.Method({
- name: 'gasPriceStatistics',
- call: 'parity_gasPriceStatistics',
- params: 0,
- outputFormatter: function (a) { return a.map(web3.toBigNumber); }
- })
- ]
- });
-
- web3._extend({
- property: 'eth',
- methods: [
- new web3._extend.Method({
- name: 'registryAddress',
- call: 'parity_registryAddress',
- params: 0
- })
- ]
- });
-
- web3._extend({
- property: 'eth',
- methods: [
- new web3._extend.Method({
- name: 'accountsInfo',
- call: 'personal_accountsInfo',
- outputFormatter: function (m) {
- Object.keys(m).forEach(k => {
- m[k].meta = JSON.parse(m[k].meta);
- m[k].meta.name = m[k].name;
- m[k].meta.uuid = m[k].uuid;
- m[k] = m[k].meta;
- }); return m;
- },
- params: 0
- })
- ]
- });
-
- web3._extend({
- property: 'eth',
- methods: [
- new web3._extend.Method({
- name: 'setAccountName',
- call: 'personal_setAccountName',
- params: 2
- })
- ]
- });
-
- web3._extend({
- property: 'eth',
- methods: [
- new web3._extend.Method({
- name: 'setAccountMeta',
- call: 'personal_setAccountMeta',
- params: 2,
- inputFormatter: [a => a, JSON.stringify]
- })
- ]
- });
-
- web3._extend({
- property: 'eth',
- methods: [
- new web3._extend.Method({
- name: 'postTransaction',
- call: 'eth_postTransaction',
- params: 1,
- inputFormatter: [web3._extend.formatters.inputCallFormatter]
- })
- ]
- });
-
- web3._extend({
- property: 'eth',
- methods: [
- new web3._extend.Method({
- name: 'postSign',
- call: 'eth_postSign',
- params: 1
- })
- ]
- });
-
- web3._extend({
- property: 'eth',
- methods: [
- new web3._extend.Method({
- name: 'encryptMessage',
- call: 'parity_encryptMessage',
- params: 2
- })
- ]
- });
-
- web3._extend({
- property: 'eth',
- methods: [
- new web3._extend.Method({
- name: 'checkRequest',
- call: 'eth_checkRequest',
- params: 1
- })
- ]
- });
-
- web3._extend({
- property: 'eth',
- methods: [
- new web3._extend.Method({
- name: 'listAccounts',
- call: 'parity_listAccounts',
- params: 0
- })
- ]
- });
-
- {
- let postTransaction = web3.eth.postTransaction.bind(web3.eth);
- let sendTransaction = web3.eth.sendTransaction.bind(web3.eth);
-
- web3.eth.sendTransaction = function (options, f) {
- // No callback - do sync API.
- if (typeof f !== 'function') {
- return sendTransaction(options);
- }
- // Callback - use async API.
- let id = postTransaction(options);
-
- console.log('Posted trasaction id=' + id);
- let timerId = window.setInterval(check, 500);
-
- function check () {
- try {
- let r = web3.eth.checkRequest(id);
-
- if (typeof r === 'string') {
- clearInterval(timerId);
- if (r === '0x0000000000000000000000000000000000000000000000000000000000000000') {
- f('Rejected', r);
- } else {
- f(null, r);
- }
- } else if (r !== null) {
- console.log('checkRequest returned: ' + r);
- }
- } catch (e) {
- clearInterval(timerId);
- f('Rejected', null);
- }
- }
- };
- }
+api.parity.dappsUrl().then(url => {
+ web3 = new Web3(new Web3.providers.HttpProvider(`${window.location.protocol}//${url}/rpc/`));
+ window.web3 = web3;
+
+ // Usage example:
+ // web3.eth.traceCall({
+ // to: theChicken.address,
+ // data: theChicken.withdraw.getData(100000000000000000),
+ // gas: 100000
+ // },
+ // `["trace", "vmTrace", "stateDiff"]
+ // )
+ web3._extend({
+ property: 'eth',
+ methods: [
+ new web3._extend.Method({
+ name: 'traceCall',
+ call: 'trace_call',
+ params: 2,
+ inputFormatter: [web3._extend.formatters.inputCallFormatter, null]
+ })
+ ]
+ });
- web3.eth.installInterceptor = function (interceptor) {
- let oldSendTransaction = web3.eth.sendTransaction.bind(web3.eth);
+ web3._extend({
+ property: 'eth',
+ methods: [
+ new web3._extend.Method({
+ name: 'traceSendRawTransaction',
+ call: 'trace_rawTransaction',
+ params: 2,
+ inputFormatter: [null, null]
+ })
+ ]
+ });
- web3.eth.sendTransaction = function (options, f) {
- if (!interceptor(options)) {
- return '0x0000000000000000000000000000000000000000000000000000000000000000';
- }
+ web3._extend({
+ property: 'eth',
+ methods: [
+ new web3._extend.Method({
+ name: 'traceReplayTransaction',
+ call: 'trace_replayTransaction',
+ params: 2,
+ inputFormatter: [null, null]
+ })
+ ]
+ });
- return oldSendTransaction(options, f);
- };
- };
+ web3._extend({
+ property: 'eth',
+ methods: [
+ new web3._extend.Method({
+ name: 'setMode',
+ call: 'parity_setMode',
+ params: 1
+ })
+ ]
+ });
- web3.eth.reporter = function (e, r) {
- if (e) {
- console.log('Error confirming transaction: ' + e);
- } else {
- let addr = r;
- let confirmed = false;
- let timerId = window.setInterval(function check () {
- let receipt = web3.eth.getTransactionReceipt(addr);
-
- if (receipt != null) {
- if (!confirmed) {
- console.log('Transaction confirmed (' + r + '); used ' + receipt.gasUsed + ' gas; left ' + receipt.logs.length + ' logs; mining...');
- confirmed = true;
- }
- if (typeof receipt.blockHash === 'string') {
- clearInterval(timerId);
- console.log('Mined into block ' + receipt.blockNumber);
+ web3._extend({
+ property: 'eth',
+ methods: [
+ new web3._extend.Method({
+ name: 'mode',
+ call: 'parity_mode',
+ params: 0
+ })
+ ]
+ });
+
+ web3._extend({
+ property: 'eth',
+ methods: [
+ new web3._extend.Method({
+ name: 'traceTransaction',
+ call: 'trace_Transaction',
+ params: 1,
+ inputFormatter: [null]
+ })
+ ]
+ });
+
+ web3._extend({
+ property: 'eth',
+ methods: [
+ new web3._extend.Method({
+ name: 'gasPriceStatistics',
+ call: 'parity_gasPriceStatistics',
+ params: 0,
+ outputFormatter: function (a) { return a.map(web3.toBigNumber); }
+ })
+ ]
+ });
+
+ web3._extend({
+ property: 'eth',
+ methods: [
+ new web3._extend.Method({
+ name: 'registryAddress',
+ call: 'parity_registryAddress',
+ params: 0
+ })
+ ]
+ });
+
+ web3._extend({
+ property: 'eth',
+ methods: [
+ new web3._extend.Method({
+ name: 'accountsInfo',
+ call: 'personal_accountsInfo',
+ outputFormatter: function (m) {
+ Object.keys(m).forEach(k => {
+ m[k].meta = JSON.parse(m[k].meta);
+ m[k].meta.name = m[k].name;
+ m[k].meta.uuid = m[k].uuid;
+ m[k] = m[k].meta;
+ }); return m;
+ },
+ params: 0
+ })
+ ]
+ });
+
+ web3._extend({
+ property: 'eth',
+ methods: [
+ new web3._extend.Method({
+ name: 'setAccountName',
+ call: 'personal_setAccountName',
+ params: 2
+ })
+ ]
+ });
+
+ web3._extend({
+ property: 'eth',
+ methods: [
+ new web3._extend.Method({
+ name: 'setAccountMeta',
+ call: 'personal_setAccountMeta',
+ params: 2,
+ inputFormatter: [a => a, JSON.stringify]
+ })
+ ]
+ });
+
+ web3._extend({
+ property: 'eth',
+ methods: [
+ new web3._extend.Method({
+ name: 'postTransaction',
+ call: 'eth_postTransaction',
+ params: 1,
+ inputFormatter: [web3._extend.formatters.inputCallFormatter]
+ })
+ ]
+ });
+
+ web3._extend({
+ property: 'eth',
+ methods: [
+ new web3._extend.Method({
+ name: 'postSign',
+ call: 'eth_postSign',
+ params: 1
+ })
+ ]
+ });
+
+ web3._extend({
+ property: 'eth',
+ methods: [
+ new web3._extend.Method({
+ name: 'encryptMessage',
+ call: 'parity_encryptMessage',
+ params: 2
+ })
+ ]
+ });
+
+ web3._extend({
+ property: 'eth',
+ methods: [
+ new web3._extend.Method({
+ name: 'checkRequest',
+ call: 'eth_checkRequest',
+ params: 1
+ })
+ ]
+ });
+
+ web3._extend({
+ property: 'eth',
+ methods: [
+ new web3._extend.Method({
+ name: 'listAccounts',
+ call: 'parity_listAccounts',
+ params: 0
+ })
+ ]
+ });
+
+ {
+ let postTransaction = web3.eth.postTransaction.bind(web3.eth);
+ let sendTransaction = web3.eth.sendTransaction.bind(web3.eth);
+
+ web3.eth.sendTransaction = function (options, f) {
+ // No callback - do sync API.
+ if (typeof f !== 'function') {
+ return sendTransaction(options);
+ }
+ // Callback - use async API.
+ let id = postTransaction(options);
+
+ console.log('Posted trasaction id=' + id);
+ let timerId = window.setInterval(check, 500);
+
+ function check () {
+ try {
+ let r = web3.eth.checkRequest(id);
+
+ if (typeof r === 'string') {
+ clearInterval(timerId);
+ if (r === '0x0000000000000000000000000000000000000000000000000000000000000000') {
+ f('Rejected', r);
+ } else {
+ f(null, r);
}
+ } else if (r !== null) {
+ console.log('checkRequest returned: ' + r);
}
- }, 500);
+ } catch (e) {
+ clearInterval(timerId);
+ f('Rejected', null);
+ }
}
};
+ }
- {
- let oldSha3 = web3.sha3;
+ web3.eth.installInterceptor = function (interceptor) {
+ let oldSendTransaction = web3.eth.sendTransaction.bind(web3.eth);
+
+ web3.eth.sendTransaction = function (options, f) {
+ if (!interceptor(options)) {
+ return '0x0000000000000000000000000000000000000000000000000000000000000000';
+ }
- web3.sha3 = function (data, format) {
- if (typeof format !== 'string' || (format !== 'hex' && format !== 'bin')) {
- format = data.startsWith('0x') ? 'hex' : 'bin';
+ return oldSendTransaction(options, f);
+ };
+ };
+
+ web3.eth.reporter = function (e, r) {
+ if (e) {
+ console.log('Error confirming transaction: ' + e);
+ } else {
+ let addr = r;
+ let confirmed = false;
+ let timerId = window.setInterval(function check () {
+ let receipt = web3.eth.getTransactionReceipt(addr);
+
+ if (receipt != null) {
+ if (!confirmed) {
+ console.log('Transaction confirmed (' + r + '); used ' + receipt.gasUsed + ' gas; left ' + receipt.logs.length + ' logs; mining...');
+ confirmed = true;
+ }
+ if (typeof receipt.blockHash === 'string') {
+ clearInterval(timerId);
+ console.log('Mined into block ' + receipt.blockNumber);
+ }
}
- return oldSha3(data, { encoding: format });
- };
+ }, 500);
}
+ };
- {
- let Registry = web3.eth.contract([{ 'constant': false, 'inputs': [{ 'name': '_new', 'type': 'address' }], 'name': 'setOwner', 'outputs': [], 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': '_name', 'type': 'string' }], 'name': 'confirmReverse', 'outputs': [{ 'name': 'success', 'type': 'bool' }], 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': '_name', 'type': 'bytes32' }], 'name': 'reserve', 'outputs': [{ 'name': 'success', 'type': 'bool' }], 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': '_name', 'type': 'bytes32' }, { 'name': '_key', 'type': 'string' }, { 'name': '_value', 'type': 'bytes32' }], 'name': 'set', 'outputs': [{ 'name': 'success', 'type': 'bool' }], 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': '_name', 'type': 'bytes32' }], 'name': 'drop', 'outputs': [{ 'name': 'success', 'type': 'bool' }], 'type': 'function' }, { 'constant': true, 'inputs': [{ 'name': '_name', 'type': 'bytes32' }, { 'name': '_key', 'type': 'string' }], 'name': 'getAddress', 'outputs': [{ 'name': '', 'type': 'address' }], 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': '_amount', 'type': 'uint256' }], 'name': 'setFee', 'outputs': [], 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': '_name', 'type': 'bytes32' }, { 'name': '_to', 'type': 'address' }], 'name': 'transfer', 'outputs': [{ 'name': 'success', 'type': 'bool' }], 'type': 'function' }, { 'constant': true, 'inputs': [], 'name': 'owner', 'outputs': [{ 'name': '', 'type': 'address' }], 'type': 'function' }, { 'constant': true, 'inputs': [{ 'name': '_name', 'type': 'bytes32' }], 'name': 'reserved', 'outputs': [{ 'name': 'reserved', 'type': 'bool' }], 'type': 'function' }, { 'constant': false, 'inputs': [], 'name': 'drain', 'outputs': [], 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': '_name', 'type': 'string' }, { 'name': '_who', 'type': 'address' }], 'name': 'proposeReverse', 'outputs': [{ 'name': 'success', 'type': 'bool' }], 'type': 'function' }, { 'constant': true, 'inputs': [{ 'name': '_name', 'type': 'bytes32' }, { 'name': '_key', 'type': 'string' }], 'name': 'getUint', 'outputs': [{ 'name': '', 'type': 'uint256' }], 'type': 'function' }, { 'constant': true, 'inputs': [{ 'name': '_name', 'type': 'bytes32' }, { 'name': '_key', 'type': 'string' }], 'name': 'get', 'outputs': [{ 'name': '', 'type': 'bytes32' }], 'type': 'function' }, { 'constant': true, 'inputs': [], 'name': 'fee', 'outputs': [{ 'name': '', 'type': 'uint256' }], 'type': 'function' }, { 'constant': true, 'inputs': [{ 'name': '', 'type': 'address' }], 'name': 'reverse', 'outputs': [{ 'name': '', 'type': 'string' }], 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': '_name', 'type': 'bytes32' }, { 'name': '_key', 'type': 'string' }, { 'name': '_value', 'type': 'uint256' }], 'name': 'setUint', 'outputs': [{ 'name': 'success', 'type': 'bool' }], 'type': 'function' }, { 'constant': false, 'inputs': [], 'name': 'removeReverse', 'outputs': [], 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': '_name', 'type': 'bytes32' }, { 'name': '_key', 'type': 'string' }, { 'name': '_value', 'type': 'address' }], 'name': 'setAddress', 'outputs': [{ 'name': 'success', 'type': 'bool' }], 'type': 'function' }, { 'anonymous': false, 'inputs': [{ 'indexed': false, 'name': 'amount', 'type': 'uint256' }], 'name': 'Drained', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': false, 'name': 'amount', 'type': 'uint256' }], 'name': 'FeeChanged', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'name', 'type': 'bytes32' }, { 'indexed': true, 'name': 'owner', 'type': 'address' }], 'name': 'Reserved', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'name', 'type': 'bytes32' }, { 'indexed': true, 'name': 'oldOwner', 'type': 'address' }, { 'indexed': true, 'name': 'newOwner', 'type': 'address' }], 'name': 'Transferred', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'name', 'type': 'bytes32' }, { 'indexed': true, 'name': 'owner', 'type': 'address' }], 'name': 'Dropped', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'name', 'type': 'bytes32' }, { 'indexed': true, 'name': 'owner', 'type': 'address' }, { 'indexed': true, 'name': 'key', 'type': 'string' }, { 'indexed': false, 'name': 'plainKey', 'type': 'string' }], 'name': 'DataChanged', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'name', 'type': 'string' }, { 'indexed': true, 'name': 'reverse', 'type': 'address' }], 'name': 'ReverseProposed', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'name', 'type': 'string' }, { 'indexed': true, 'name': 'reverse', 'type': 'address' }], 'name': 'ReverseConfirmed', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'name', 'type': 'string' }, { 'indexed': true, 'name': 'reverse', 'type': 'address' }], 'name': 'ReverseRemoved', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'old', 'type': 'address' }, { 'indexed': true, 'name': 'current', 'type': 'address' }], 'name': 'NewOwner', 'type': 'event' }]);
+ {
+ let oldSha3 = web3.sha3;
- web3.eth.registry = Registry.at(web3.eth.registryAddress());
- web3.eth.registry.lookup = (name, field) => web3.eth.registry.get(web3.sha3(name), field);
- web3.eth.registry.lookupAddress = (name, field) => web3.eth.registry.getAddress(web3.sha3(name), field);
- web3.eth.registry.lookupUint = (name, field) => web3.eth.registry.getUint(web3.sha3(name), field);
+ web3.sha3 = function (data, format) {
+ if (typeof format !== 'string' || (format !== 'hex' && format !== 'bin')) {
+ format = data.startsWith('0x') ? 'hex' : 'bin';
+ }
+ return oldSha3(data, { encoding: format });
+ };
+ }
- let TokenReg = web3.eth.contract([{ 'constant': true, 'inputs': [{ 'name': '_id', 'type': 'uint256' }], 'name': 'token', 'outputs': [{ 'name': 'addr', 'type': 'address' }, { 'name': 'tla', 'type': 'string' }, { 'name': 'base', 'type': 'uint256' }, { 'name': 'name', 'type': 'string' }, { 'name': 'owner', 'type': 'address' }], 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': '_new', 'type': 'address' }], 'name': 'setOwner', 'outputs': [], 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': '_addr', 'type': 'address' }, { 'name': '_tla', 'type': 'string' }, { 'name': '_base', 'type': 'uint256' }, { 'name': '_name', 'type': 'string' }], 'name': 'register', 'outputs': [{ 'name': '', 'type': 'bool' }], 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': '_fee', 'type': 'uint256' }], 'name': 'setFee', 'outputs': [], 'type': 'function' }, { 'constant': true, 'inputs': [{ 'name': '_id', 'type': 'uint256' }, { 'name': '_key', 'type': 'bytes32' }], 'name': 'meta', 'outputs': [{ 'name': '', 'type': 'bytes32' }], 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': '_addr', 'type': 'address' }, { 'name': '_tla', 'type': 'string' }, { 'name': '_base', 'type': 'uint256' }, { 'name': '_name', 'type': 'string' }, { 'name': '_owner', 'type': 'address' }], 'name': 'registerAs', 'outputs': [{ 'name': '', 'type': 'bool' }], 'type': 'function' }, { 'constant': true, 'inputs': [{ 'name': '_tla', 'type': 'string' }], 'name': 'fromTLA', 'outputs': [{ 'name': 'id', 'type': 'uint256' }, { 'name': 'addr', 'type': 'address' }, { 'name': 'base', 'type': 'uint256' }, { 'name': 'name', 'type': 'string' }, { 'name': 'owner', 'type': 'address' }], 'type': 'function' }, { 'constant': true, 'inputs': [], 'name': 'owner', 'outputs': [{ 'name': '', 'type': 'address' }], 'type': 'function' }, { 'constant': false, 'inputs': [], 'name': 'drain', 'outputs': [], 'type': 'function' }, { 'constant': true, 'inputs': [], 'name': 'tokenCount', 'outputs': [{ 'name': '', 'type': 'uint256' }], 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': '_id', 'type': 'uint256' }], 'name': 'unregister', 'outputs': [], 'type': 'function' }, { 'constant': true, 'inputs': [{ 'name': '_addr', 'type': 'address' }], 'name': 'fromAddress', 'outputs': [{ 'name': 'id', 'type': 'uint256' }, { 'name': 'tla', 'type': 'string' }, { 'name': 'base', 'type': 'uint256' }, { 'name': 'name', 'type': 'string' }, { 'name': 'owner', 'type': 'address' }], 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': '_id', 'type': 'uint256' }, { 'name': '_key', 'type': 'bytes32' }, { 'name': '_value', 'type': 'bytes32' }], 'name': 'setMeta', 'outputs': [], 'type': 'function' }, { 'constant': true, 'inputs': [], 'name': 'fee', 'outputs': [{ 'name': '', 'type': 'uint256' }], 'type': 'function' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'tla', 'type': 'string' }, { 'indexed': true, 'name': 'id', 'type': 'uint256' }, { 'indexed': false, 'name': 'addr', 'type': 'address' }, { 'indexed': false, 'name': 'name', 'type': 'string' }], 'name': 'Registered', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'tla', 'type': 'string' }, { 'indexed': true, 'name': 'id', 'type': 'uint256' }], 'name': 'Unregistered', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'id', 'type': 'uint256' }, { 'indexed': true, 'name': 'key', 'type': 'bytes32' }, { 'indexed': false, 'name': 'value', 'type': 'bytes32' }], 'name': 'MetaChanged', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'old', 'type': 'address' }, { 'indexed': true, 'name': 'current', 'type': 'address' }], 'name': 'NewOwner', 'type': 'event' }]);
+ {
+ let Registry = web3.eth.contract([{ 'constant': false, 'inputs': [{ 'name': '_new', 'type': 'address' }], 'name': 'setOwner', 'outputs': [], 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': '_name', 'type': 'string' }], 'name': 'confirmReverse', 'outputs': [{ 'name': 'success', 'type': 'bool' }], 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': '_name', 'type': 'bytes32' }], 'name': 'reserve', 'outputs': [{ 'name': 'success', 'type': 'bool' }], 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': '_name', 'type': 'bytes32' }, { 'name': '_key', 'type': 'string' }, { 'name': '_value', 'type': 'bytes32' }], 'name': 'set', 'outputs': [{ 'name': 'success', 'type': 'bool' }], 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': '_name', 'type': 'bytes32' }], 'name': 'drop', 'outputs': [{ 'name': 'success', 'type': 'bool' }], 'type': 'function' }, { 'constant': true, 'inputs': [{ 'name': '_name', 'type': 'bytes32' }, { 'name': '_key', 'type': 'string' }], 'name': 'getAddress', 'outputs': [{ 'name': '', 'type': 'address' }], 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': '_amount', 'type': 'uint256' }], 'name': 'setFee', 'outputs': [], 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': '_name', 'type': 'bytes32' }, { 'name': '_to', 'type': 'address' }], 'name': 'transfer', 'outputs': [{ 'name': 'success', 'type': 'bool' }], 'type': 'function' }, { 'constant': true, 'inputs': [], 'name': 'owner', 'outputs': [{ 'name': '', 'type': 'address' }], 'type': 'function' }, { 'constant': true, 'inputs': [{ 'name': '_name', 'type': 'bytes32' }], 'name': 'reserved', 'outputs': [{ 'name': 'reserved', 'type': 'bool' }], 'type': 'function' }, { 'constant': false, 'inputs': [], 'name': 'drain', 'outputs': [], 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': '_name', 'type': 'string' }, { 'name': '_who', 'type': 'address' }], 'name': 'proposeReverse', 'outputs': [{ 'name': 'success', 'type': 'bool' }], 'type': 'function' }, { 'constant': true, 'inputs': [{ 'name': '_name', 'type': 'bytes32' }, { 'name': '_key', 'type': 'string' }], 'name': 'getUint', 'outputs': [{ 'name': '', 'type': 'uint256' }], 'type': 'function' }, { 'constant': true, 'inputs': [{ 'name': '_name', 'type': 'bytes32' }, { 'name': '_key', 'type': 'string' }], 'name': 'get', 'outputs': [{ 'name': '', 'type': 'bytes32' }], 'type': 'function' }, { 'constant': true, 'inputs': [], 'name': 'fee', 'outputs': [{ 'name': '', 'type': 'uint256' }], 'type': 'function' }, { 'constant': true, 'inputs': [{ 'name': '', 'type': 'address' }], 'name': 'reverse', 'outputs': [{ 'name': '', 'type': 'string' }], 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': '_name', 'type': 'bytes32' }, { 'name': '_key', 'type': 'string' }, { 'name': '_value', 'type': 'uint256' }], 'name': 'setUint', 'outputs': [{ 'name': 'success', 'type': 'bool' }], 'type': 'function' }, { 'constant': false, 'inputs': [], 'name': 'removeReverse', 'outputs': [], 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': '_name', 'type': 'bytes32' }, { 'name': '_key', 'type': 'string' }, { 'name': '_value', 'type': 'address' }], 'name': 'setAddress', 'outputs': [{ 'name': 'success', 'type': 'bool' }], 'type': 'function' }, { 'anonymous': false, 'inputs': [{ 'indexed': false, 'name': 'amount', 'type': 'uint256' }], 'name': 'Drained', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': false, 'name': 'amount', 'type': 'uint256' }], 'name': 'FeeChanged', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'name', 'type': 'bytes32' }, { 'indexed': true, 'name': 'owner', 'type': 'address' }], 'name': 'Reserved', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'name', 'type': 'bytes32' }, { 'indexed': true, 'name': 'oldOwner', 'type': 'address' }, { 'indexed': true, 'name': 'newOwner', 'type': 'address' }], 'name': 'Transferred', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'name', 'type': 'bytes32' }, { 'indexed': true, 'name': 'owner', 'type': 'address' }], 'name': 'Dropped', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'name', 'type': 'bytes32' }, { 'indexed': true, 'name': 'owner', 'type': 'address' }, { 'indexed': true, 'name': 'key', 'type': 'string' }, { 'indexed': false, 'name': 'plainKey', 'type': 'string' }], 'name': 'DataChanged', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'name', 'type': 'string' }, { 'indexed': true, 'name': 'reverse', 'type': 'address' }], 'name': 'ReverseProposed', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'name', 'type': 'string' }, { 'indexed': true, 'name': 'reverse', 'type': 'address' }], 'name': 'ReverseConfirmed', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'name', 'type': 'string' }, { 'indexed': true, 'name': 'reverse', 'type': 'address' }], 'name': 'ReverseRemoved', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'old', 'type': 'address' }, { 'indexed': true, 'name': 'current', 'type': 'address' }], 'name': 'NewOwner', 'type': 'event' }]);
- web3.eth.tokenReg = TokenReg.at(web3.eth.registry.lookupAddress('tokenreg', 'A'));
- }
- })
- .catch((error) => {
- console.error(error);
- });
+ web3.eth.registry = Registry.at(web3.eth.registryAddress());
+ web3.eth.registry.lookup = (name, field) => web3.eth.registry.get(web3.sha3(name), field);
+ web3.eth.registry.lookupAddress = (name, field) => web3.eth.registry.getAddress(web3.sha3(name), field);
+ web3.eth.registry.lookupUint = (name, field) => web3.eth.registry.getUint(web3.sha3(name), field);
+
+ let TokenReg = web3.eth.contract([{ 'constant': true, 'inputs': [{ 'name': '_id', 'type': 'uint256' }], 'name': 'token', 'outputs': [{ 'name': 'addr', 'type': 'address' }, { 'name': 'tla', 'type': 'string' }, { 'name': 'base', 'type': 'uint256' }, { 'name': 'name', 'type': 'string' }, { 'name': 'owner', 'type': 'address' }], 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': '_new', 'type': 'address' }], 'name': 'setOwner', 'outputs': [], 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': '_addr', 'type': 'address' }, { 'name': '_tla', 'type': 'string' }, { 'name': '_base', 'type': 'uint256' }, { 'name': '_name', 'type': 'string' }], 'name': 'register', 'outputs': [{ 'name': '', 'type': 'bool' }], 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': '_fee', 'type': 'uint256' }], 'name': 'setFee', 'outputs': [], 'type': 'function' }, { 'constant': true, 'inputs': [{ 'name': '_id', 'type': 'uint256' }, { 'name': '_key', 'type': 'bytes32' }], 'name': 'meta', 'outputs': [{ 'name': '', 'type': 'bytes32' }], 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': '_addr', 'type': 'address' }, { 'name': '_tla', 'type': 'string' }, { 'name': '_base', 'type': 'uint256' }, { 'name': '_name', 'type': 'string' }, { 'name': '_owner', 'type': 'address' }], 'name': 'registerAs', 'outputs': [{ 'name': '', 'type': 'bool' }], 'type': 'function' }, { 'constant': true, 'inputs': [{ 'name': '_tla', 'type': 'string' }], 'name': 'fromTLA', 'outputs': [{ 'name': 'id', 'type': 'uint256' }, { 'name': 'addr', 'type': 'address' }, { 'name': 'base', 'type': 'uint256' }, { 'name': 'name', 'type': 'string' }, { 'name': 'owner', 'type': 'address' }], 'type': 'function' }, { 'constant': true, 'inputs': [], 'name': 'owner', 'outputs': [{ 'name': '', 'type': 'address' }], 'type': 'function' }, { 'constant': false, 'inputs': [], 'name': 'drain', 'outputs': [], 'type': 'function' }, { 'constant': true, 'inputs': [], 'name': 'tokenCount', 'outputs': [{ 'name': '', 'type': 'uint256' }], 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': '_id', 'type': 'uint256' }], 'name': 'unregister', 'outputs': [], 'type': 'function' }, { 'constant': true, 'inputs': [{ 'name': '_addr', 'type': 'address' }], 'name': 'fromAddress', 'outputs': [{ 'name': 'id', 'type': 'uint256' }, { 'name': 'tla', 'type': 'string' }, { 'name': 'base', 'type': 'uint256' }, { 'name': 'name', 'type': 'string' }, { 'name': 'owner', 'type': 'address' }], 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': '_id', 'type': 'uint256' }, { 'name': '_key', 'type': 'bytes32' }, { 'name': '_value', 'type': 'bytes32' }], 'name': 'setMeta', 'outputs': [], 'type': 'function' }, { 'constant': true, 'inputs': [], 'name': 'fee', 'outputs': [{ 'name': '', 'type': 'uint256' }], 'type': 'function' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'tla', 'type': 'string' }, { 'indexed': true, 'name': 'id', 'type': 'uint256' }, { 'indexed': false, 'name': 'addr', 'type': 'address' }, { 'indexed': false, 'name': 'name', 'type': 'string' }], 'name': 'Registered', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'tla', 'type': 'string' }, { 'indexed': true, 'name': 'id', 'type': 'uint256' }], 'name': 'Unregistered', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'id', 'type': 'uint256' }, { 'indexed': true, 'name': 'key', 'type': 'bytes32' }, { 'indexed': false, 'name': 'value', 'type': 'bytes32' }], 'name': 'MetaChanged', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'old', 'type': 'address' }, { 'indexed': true, 'name': 'current', 'type': 'address' }], 'name': 'NewOwner', 'type': 'event' }]);
+
+ web3.eth.tokenReg = TokenReg.at(web3.eth.registry.lookupAddress('tokenreg', 'A'));
+ }
+})
+.catch((error) => {
+ console.error(error);
+});
window.api = api;
window.web3 = web3;
diff --git a/js/src/dapps/registry/ui/image.js b/js/src/dapps/registry/ui/image.js
index 3f0a90abe76..88cae4e308c 100644
--- a/js/src/dapps/registry/ui/image.js
+++ b/js/src/dapps/registry/ui/image.js
@@ -16,8 +16,6 @@
import React from 'react';
-import { parityNode } from '../../../environment';
-
const styles = {
padding: '.5em',
border: '1px solid #777'
@@ -34,7 +32,7 @@ export default (address) => {
return (
diff --git a/js/src/dapps/tokenreg/Tokens/Token/token.js b/js/src/dapps/tokenreg/Tokens/Token/token.js
index 5ea50535c16..9d60d45802c 100644
--- a/js/src/dapps/tokenreg/Tokens/Token/token.js
+++ b/js/src/dapps/tokenreg/Tokens/Token/token.js
@@ -30,7 +30,6 @@ import styles from './token.css';
import { metaDataKeys } from '../../constants';
import { api } from '../../parity';
-import { parityNode } from '../../../../environment';
export default class Token extends Component {
static propTypes = {
@@ -312,7 +311,7 @@ export default class Token extends Component {
meta-data:
-
+
);
diff --git a/js/src/embed.js b/js/src/embed.js
index 218bb2f4f2a..56532257ccc 100644
--- a/js/src/embed.js
+++ b/js/src/embed.js
@@ -55,6 +55,9 @@ class FakeTransport {
return Promise.reject('not connected');
}
+ addMiddleware () {
+ }
+
on () {
}
}
diff --git a/js/src/environment/index.js b/js/src/environment/index.js
index 1123ddd9b83..8eaa6519d35 100644
--- a/js/src/environment/index.js
+++ b/js/src/environment/index.js
@@ -19,14 +19,4 @@
import './tests';
-const parityNode = (
- process.env.PARITY_URL && `http://${process.env.PARITY_URL}`
- ) || (
- process.env.NODE_ENV === 'production'
- ? 'http://127.0.0.1:8545'
- : ''
- );
-
-export {
- parityNode
-};
+export {};
diff --git a/js/src/index.js b/js/src/index.js
index 1436f30c06b..9a5c34d3ee0 100644
--- a/js/src/index.js
+++ b/js/src/index.js
@@ -53,8 +53,7 @@ if (process.env.NODE_ENV === 'development') {
}
const AUTH_HASH = '#/auth?';
-const parityUrl = process.env.PARITY_URL || window.location.host;
-const urlScheme = window.location.href.match(/^https/) ? 'wss://' : 'ws://';
+const parityUrl = process.env.PARITY_URL || '127.0.0.1:8546';
let token = null;
@@ -62,7 +61,7 @@ if (window.location.hash && window.location.hash.indexOf(AUTH_HASH) === 0) {
token = qs.parse(window.location.hash.substr(AUTH_HASH.length)).token;
}
-const api = new SecureApi(`${urlScheme}${parityUrl}`, token);
+const api = new SecureApi(parityUrl, token);
patchApi(api);
loadSender(api);
diff --git a/js/src/jsonrpc/interfaces/parity.js b/js/src/jsonrpc/interfaces/parity.js
index 6ab5ea212b5..326ca98314a 100644
--- a/js/src/jsonrpc/interfaces/parity.js
+++ b/js/src/jsonrpc/interfaces/parity.js
@@ -143,25 +143,34 @@ export default {
}
},
- dappsPort: {
- section: SECTION_NODE,
- desc: 'Returns the port the dapps are running on, error if not enabled.',
+ dappsList: {
+ subdoc: SUBDOC_SET,
+ desc: 'Returns a list of available local dapps.',
params: [],
returns: {
- type: Quantity,
- desc: 'The port number',
- example: 8080
+ type: Array,
+ desc: 'The list of dapps',
+ example: [
+ {
+ author: 'Parity Technologies Ltd',
+ description: 'A skeleton dapp',
+ iconUrl: 'title.png',
+ id: 'skeleton',
+ name: 'Skeleton',
+ version: '0.1'
+ }
+ ]
}
},
- dappsInterface: {
+ dappsUrl: {
section: SECTION_NODE,
- desc: 'Returns the interface the dapps are running on, error if not enabled.',
+ desc: 'Returns the hostname and the port of dapps/rpc server, error if not enabled.',
params: [],
returns: {
type: String,
- desc: 'The interface',
- example: '127.0.0.1'
+ desc: 'The hostname and port number',
+ example: 'localhost:8545'
}
},
@@ -788,17 +797,6 @@ export default {
}
},
- signerPort: {
- section: SECTION_NODE,
- desc: 'Returns the port the signer is running on, error if not enabled',
- params: [],
- returns: {
- type: Quantity,
- desc: 'The port number',
- example: 8180
- }
- },
-
transactionsLimit: {
section: SECTION_MINING,
desc: 'Changes limit for transactions in queue.',
@@ -1916,6 +1914,17 @@ export default {
}
},
+ wsUrl: {
+ section: SECTION_NODE,
+ desc: 'Returns the hostname and the port of WebSockets/Signer server, error if not enabled.',
+ params: [],
+ returns: {
+ type: String,
+ desc: 'The hostname and port number',
+ example: 'localhost:8546'
+ }
+ },
+
composeTransaction: {
desc: 'Given partial transaction request produces transaction with all fields filled in. Such transaction can be then signed externally.',
params: [
@@ -1997,4 +2006,5 @@ export default {
example: 'QmSbFjqjd6nFwNHqsBCC7SK8GShGcayLUEtysJjNGhZAnC'
}
}
+
};
diff --git a/js/src/secureApi.js b/js/src/secureApi.js
index e19d7ae998c..2fd33fb9b7b 100644
--- a/js/src/secureApi.js
+++ b/js/src/secureApi.js
@@ -27,21 +27,28 @@ export default class SecureApi extends Api {
_needsToken = false;
_tokens = [];
- _dappsInterface = null;
- _dappsPort = 8545;
- _signerPort = 8180;
+ _dappsUrl = null;
+ _wsUrl = null;
- static getTransport (url, sysuiToken) {
- return new Api.Transport.Ws(url, sysuiToken, false);
+ static getTransport (url, sysuiToken, protocol) {
+ const proto = protocol() === 'https:' ? 'wss:' : 'ws:';
+
+ return new Api.Transport.Ws(`${proto}//${url}`, sysuiToken, false);
+ }
+
+ // Returns a protocol with `:` at the end.
+ static protocol () {
+ return window.location.protocol;
}
- constructor (url, nextToken, getTransport = SecureApi.getTransport) {
+ constructor (url, nextToken, getTransport = SecureApi.getTransport, protocol = SecureApi.protocol) {
const sysuiToken = store.get('sysuiToken');
- const transport = getTransport(url, sysuiToken);
+ const transport = getTransport(url, sysuiToken, protocol);
super(transport);
- this._url = url;
+ this._wsUrl = url;
+ this.protocol = protocol;
// Try tokens from localStorage, from hash and 'initial'
this._tokens = uniq([sysuiToken, nextToken, 'initial'])
.filter((token) => token)
@@ -53,12 +60,30 @@ export default class SecureApi extends Api {
this.connect();
}
+ get _dappsAddress () {
+ if (!this._dappsUrl) {
+ return {
+ host: null,
+ port: 8545
+ };
+ }
+
+ const [host, port] = this._dappsUrl.split(':');
+
+ return {
+ host,
+ port: parseInt(port, 10)
+ };
+ }
+
get dappsPort () {
- return this._dappsPort;
+ return this._dappsAddress.port;
}
get dappsUrl () {
- return `http://${this.hostname}:${this.dappsPort}`;
+ const { port } = this._dappsAddress;
+
+ return `${this.protocol()}//${this.hostname}:${port}`;
}
get hostname () {
@@ -66,15 +91,13 @@ export default class SecureApi extends Api {
return 'dapps.parity';
}
- if (!this._dappsInterface || this._dappsInterface === '0.0.0.0') {
+ const { host } = this._dappsAddress;
+
+ if (!host || host === '0.0.0.0') {
return window.location.hostname;
}
- return this._dappsInterface;
- }
-
- get signerPort () {
- return this._signerPort;
+ return host;
}
get isConnecting () {
@@ -98,18 +121,18 @@ export default class SecureApi extends Api {
* (`signerPort`, `dappsInterface`, `dappsPort`, ...)
*/
configure (configuration) {
- const { dappsInterface, dappsPort, signerPort } = configuration;
+ const { dappsInterface, dappsPort, signerPort, wsPort } = configuration;
if (dappsInterface) {
- this._dappsInterface = dappsInterface;
+ this._dappsUrl = `${dappsInterface}:${this._dappsAddress.port}`;
}
if (dappsPort) {
- this._dappsPort = dappsPort;
+ this._dappsUrl = `${this.hostname}:${dappsPort}`;
}
- if (signerPort) {
- this._signerPort = signerPort;
+ if (signerPort || wsPort) {
+ this._wsUrl = `${this.hostname}:${signerPort || wsPort}`;
}
}
@@ -166,9 +189,7 @@ export default class SecureApi extends Api {
* otherwise (HEAD request to the Node)
*/
isNodeUp () {
- const url = this._url.replace(/wss?/, 'http');
-
- return fetch(url, { method: 'HEAD' })
+ return fetch(`${this.protocol()}//${this._wsUrl}`, { method: 'HEAD', mode: 'no-cors' })
.then(
(r) => r.status === 200,
() => false
@@ -297,14 +318,12 @@ export default class SecureApi extends Api {
_fetchSettings () {
return Promise
.all([
- this.parity.dappsPort(),
- this.parity.dappsInterface(),
- this.parity.signerPort()
+ this.parity.dappsUrl(),
+ this.parity.wsUrl()
])
- .then(([dappsPort, dappsInterface, signerPort]) => {
- this._dappsPort = dappsPort.toNumber();
- this._dappsInterface = dappsInterface;
- this._signerPort = signerPort.toNumber();
+ .then(([dappsUrl, wsUrl]) => {
+ this._dappsUrl = dappsUrl;
+ this._wsUrl = dappsUrl;
});
}
diff --git a/js/src/util/dapps.js b/js/src/util/dapps.js
index 2ca416e1a28..58b33e49e7d 100644
--- a/js/src/util/dapps.js
+++ b/js/src/util/dapps.js
@@ -25,21 +25,6 @@ import builtinJson from '~/views/Dapps/builtin.json';
const builtinApps = builtinJson.filter((app) => app.id);
-function getHost (api) {
- const host = process.env.DAPPS_URL ||
- (
- process.env.NODE_ENV === 'production'
- ? api.dappsUrl
- : ''
- );
-
- if (host === '/') {
- return '';
- }
-
- return host;
-}
-
export function subscribeToChanges (api, dappReg, callback) {
return dappReg
.getContract()
@@ -105,12 +90,7 @@ export function fetchBuiltinApps () {
}
export function fetchLocalApps (api) {
- return fetch(`${getHost(api)}/api/apps`)
- .then((response) => {
- return response.ok
- ? response.json()
- : [];
- })
+ return api.parity.dappsList()
.then((apps) => {
return apps
.map((app) => {
@@ -195,7 +175,7 @@ export function fetchManifest (api, manifestHash) {
}
return fetch(
- `${getHost(api)}/api/content/${manifestHash}/`,
+ `/api/content/${manifestHash}/`,
{ redirect: 'follow', mode: 'cors' }
)
.then((response) => {
diff --git a/js/src/views/Dapps/dappStore.spec.js b/js/src/views/Dapps/dappStore.spec.js
index b08e1f1dcd3..3b4fb0ded62 100644
--- a/js/src/views/Dapps/dappStore.spec.js
+++ b/js/src/views/Dapps/dappStore.spec.js
@@ -26,17 +26,11 @@ const APPID_DAPPREG = '0x7bbc4f1a27628781b96213e781a1b8eec6982c1db8fac739af6e4c5
const APPID_GHH = '0x058740ee9a5a3fb9f1cfa10752baec87e09cc45cd7027fd54708271aca300c75';
const APPID_LOCALTX = '0xae74ad174b95cdbd01c88ac5b73a296d33e9088fc2a200e76bcedf3a94a7815d';
const APPID_TOKENDEPLOY = '0xf9f2d620c2e08f83e45555247146c62185e4ab7cf82a4b9002a265a0d020348f';
-const FETCH_OK = {
- ok: true,
- status: 200
-};
let globalContractsGet;
-let globalFetch;
function stubGlobals () {
globalContractsGet = Contracts.get;
- globalFetch = global.fetch;
Contracts.get = () => {
return {
@@ -50,31 +44,21 @@ function stubGlobals () {
}
};
};
-
- global.fetch = (url) => {
- switch (url) {
- case '/api/apps':
- return Promise.resolve(Object.assign({}, FETCH_OK, {
- json: sinon.stub().resolves([]) // TODO: Local stubs in here
- }));
-
- default:
- console.log('Unknown fetch stub endpoint', url);
- return Promise.reject();
- }
- };
}
function restoreGlobals () {
Contracts.get = globalContractsGet;
- global.fetch = globalFetch;
}
let api;
let store;
function create () {
- api = {};
+ api = {
+ parity: {
+ dappsList: () => Promise.resolve([])
+ }
+ };
store = new Store(api);
return store;
diff --git a/js/src/views/Web/store.spec.js b/js/src/views/Web/store.spec.js
index 58b2f1b3c68..9f7d2e77749 100644
--- a/js/src/views/Web/store.spec.js
+++ b/js/src/views/Web/store.spec.js
@@ -34,8 +34,8 @@ let store;
function createApi () {
api = {
- dappsPort: 8080,
- dappsUrl: 'http://home.web3.site:8080',
+ dappsPort: 8545,
+ dappsUrl: 'http://home.web3.site:8545',
parity: {
listRecentDapps: sinon.stub().resolves(TEST_HISTORY)
},
@@ -159,7 +159,7 @@ describe('views/Web/Store', () => {
it('encodes current', () => {
store.setCurrentUrl(TEST_URL1);
expect(store.encodedPath).to.match(
- /http:\/\/home\.web3\.site:8080\/web\/DSTPRV1BD1T78W1T5WQQ6VVDCMQ78SBKEGQ68VVDC5MPWBK3DXPG\?t=[0-9]*$/
+ /http:\/\/home\.web3\.site:8545\/web\/DSTPRV1BD1T78W1T5WQQ6VVDCMQ78SBKEGQ68VVDC5MPWBK3DXPG\?t=[0-9]*$/
);
});
});
@@ -167,7 +167,7 @@ describe('views/Web/Store', () => {
it('encodes current', () => {
store.setCurrentUrl(TEST_URL1);
expect(store.encodedUrl).to.match(
- /^http:\/\/DSTPRV1BD1T78W1T5WQQ6VVDCMQ78SBKEGQ68VVDC5MPWBK3DXPG\.web\.web3\.site:8080\?t=[0-9]*$/
+ /^http:\/\/DSTPRV1BD1T78W1T5WQQ6VVDCMQ78SBKEGQ68VVDC5MPWBK3DXPG\.web\.web3\.site:8545\?t=[0-9]*$/
);
});
});
diff --git a/js/webpack/build.server.js b/js/webpack/build.server.js
index efc8a2cdaa3..486209ccbd0 100644
--- a/js/webpack/build.server.js
+++ b/js/webpack/build.server.js
@@ -15,26 +15,21 @@
// along with Parity. If not, see .
// test only
/**
- * Run `DAPPS_URL="/" PARITY_URL="127.0.0.1:8180" NODE_ENV="production" npm run build`
+ * Run `DAPPS_URL="/" PARITY_URL="127.0.0.1:8546" NODE_ENV="production" npm run build`
* to build the project ; use this server to test that the minifed
* version is working (this is a simple proxy server)
*/
var express = require('express');
-var proxy = require('http-proxy-middleware');
var Shared = require('./shared');
var app = express();
-var wsProxy = proxy('ws://127.0.0.1:8180', { changeOrigin: true });
Shared.addProxies(app);
app.use(express.static('.build'));
-app.use(wsProxy);
var server = app.listen(process.env.PORT || 3000, function () {
console.log('Listening on port', server.address().port);
});
-
-server.on('upgrade', wsProxy.upgrade);
diff --git a/js/webpack/dev.server.js b/js/webpack/dev.server.js
index 75ea7703afe..03894501464 100644
--- a/js/webpack/dev.server.js
+++ b/js/webpack/dev.server.js
@@ -22,7 +22,6 @@ const webpackHotMiddleware = require('webpack-hot-middleware');
const http = require('http');
const express = require('express');
const ProgressBar = require('progress');
-const proxy = require('http-proxy-middleware');
const webpackConfig = require('./app');
const Shared = require('./shared');
@@ -84,18 +83,13 @@ app.use(webpackDevMiddleware(compiler, {
}
}));
-var wsProxy = proxy('ws://127.0.0.1:8180', { changeOrigin: true });
-
// Add the dev proxies in the express App
Shared.addProxies(app);
app.use(express.static(webpackConfig.output.path));
-app.use(wsProxy);
const server = http.createServer(app);
server.listen(process.env.PORT || 3000, function () {
console.log('Listening on port', server.address().port);
progressBar = new ProgressBar('[:bar] :percent :etas', { total: 50 });
});
-
-server.on('upgrade', wsProxy.upgrade);
diff --git a/js/webpack/shared.js b/js/webpack/shared.js
index 3e2eef8f12d..ded064642e1 100644
--- a/js/webpack/shared.js
+++ b/js/webpack/shared.js
@@ -162,16 +162,8 @@ function getDappsEntry () {
function addProxies (app) {
const proxy = require('http-proxy-middleware');
- app.use(proxy((pathname, req) => {
- return pathname === '/' && req.method === 'HEAD';
- }, {
- target: 'http://127.0.0.1:8180',
- changeOrigin: true,
- autoRewrite: true
- }));
-
app.use('/api', proxy({
- target: 'http://127.0.0.1:8545',
+ target: 'http://127.0.0.1:8180',
changeOrigin: true,
autoRewrite: true
}));
diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs
index b5afb8d0a49..cd8f14add63 100644
--- a/parity/cli/mod.rs
+++ b/parity/cli/mod.rs
@@ -124,6 +124,8 @@ usage! {
or |c: &Config| otry!(c.ui).port.clone(),
flag_ui_interface: String = "local",
or |c: &Config| otry!(c.ui).interface.clone(),
+ flag_ui_hosts: String = "none",
+ or |c: &Config| otry!(c.ui).hosts.as_ref().map(|vec| vec.join(",")),
flag_ui_path: String = "$BASE/signer",
or |c: &Config| otry!(c.ui).path.clone(),
// NOTE [todr] For security reasons don't put this to config files
@@ -188,7 +190,7 @@ usage! {
or |c: &Config| otry!(c.websockets).interface.clone(),
flag_ws_apis: String = "web3,eth,pubsub,net,parity,parity_pubsub,traces,rpc,secretstore",
or |c: &Config| otry!(c.websockets).apis.as_ref().map(|vec| vec.join(",")),
- flag_ws_origins: String = "none",
+ flag_ws_origins: String = "chrome-extension://*",
or |c: &Config| otry!(c.websockets).origins.as_ref().map(|vec| vec.join(",")),
flag_ws_hosts: String = "none",
or |c: &Config| otry!(c.websockets).hosts.as_ref().map(|vec| vec.join(",")),
@@ -430,6 +432,7 @@ struct Ui {
disable: Option,
port: Option,
interface: Option,
+ hosts: Option>,
path: Option,
}
@@ -709,6 +712,7 @@ mod tests {
flag_no_ui: false,
flag_ui_port: 8180u16,
flag_ui_interface: "127.0.0.1".into(),
+ flag_ui_hosts: "none".into(),
flag_ui_path: "$HOME/.parity/signer".into(),
flag_ui_no_validation: false,
@@ -929,6 +933,7 @@ mod tests {
disable: Some(true),
port: None,
interface: None,
+ hosts: None,
path: None,
}),
network: Some(Network {
diff --git a/parity/cli/usage.txt b/parity/cli/usage.txt
index 34352450aab..99f6c7304c5 100644
--- a/parity/cli/usage.txt
+++ b/parity/cli/usage.txt
@@ -110,6 +110,11 @@ UI Options:
--ui-interface IP Specify the hostname portion of the Trusted UI
server, IP should be an interface's IP address,
or local (default: {flag_ui_interface}).
+ --ui-hosts HOSTS List of allowed Host header values. This option will
+ validate the Host header sent by the browser, it
+ is additional security against some attack
+ vectors. Special options: "all", "none",
+ (default: {flag_ui_hosts}).
--ui-path PATH Specify directory where Trusted UIs tokens should
be stored. (default: {flag_ui_path})
--ui-no-validation Disable Origin and Host headers validation for
diff --git a/parity/configuration.rs b/parity/configuration.rs
index 7985c9cd3c3..ad1353d097f 100644
--- a/parity/configuration.rs
+++ b/parity/configuration.rs
@@ -30,7 +30,7 @@ use ethcore::client::{VMType};
use ethcore::miner::{MinerOptions, Banning, StratumOptions};
use ethcore::verification::queue::VerifierSettings;
-use rpc::{IpcConfiguration, HttpConfiguration, WsConfiguration};
+use rpc::{IpcConfiguration, HttpConfiguration, WsConfiguration, UiConfiguration};
use rpc_apis::ApiSet;
use parity_rpc::NetworkSettings;
use cache::CacheConfig;
@@ -41,7 +41,6 @@ use ethcore_logger::Config as LogConfig;
use dir::{self, Directories, default_hypervisor_path, default_local_path, default_data_path};
use dapps::Configuration as DappsConfiguration;
use ipfs::Configuration as IpfsConfiguration;
-use signer::{Configuration as SignerConfiguration};
use secretstore::Configuration as SecretStoreConfiguration;
use updater::{UpdatePolicy, UpdateFilter, ReleaseTrack};
use run::RunCmd;
@@ -50,8 +49,6 @@ use presale::ImportWallet;
use account::{AccountCmd, NewAccount, ListAccounts, ImportAccounts, ImportFromGethAccounts};
use snapshot::{self, SnapshotCommand};
-const AUTHCODE_FILENAME: &'static str = "authcodes";
-
#[derive(Debug, PartialEq)]
pub enum Cmd {
Run(RunCmd),
@@ -59,7 +56,7 @@ pub enum Cmd {
Account(AccountCmd),
ImportPresaleWallet(ImportWallet),
Blockchain(BlockchainCmd),
- SignerToken(SignerConfiguration),
+ SignerToken(WsConfiguration, UiConfiguration),
SignerSign {
id: Option,
pwfile: Option,
@@ -118,6 +115,7 @@ impl Configuration {
let http_conf = self.http_config()?;
let ipc_conf = self.ipc_config()?;
let net_conf = self.net_config()?;
+ let ui_conf = self.ui_config();
let network_id = self.network_id();
let cache_config = self.cache_config();
let tracing = self.args.flag_tracing.parse()?;
@@ -134,10 +132,8 @@ impl Configuration {
let public_node = self.args.flag_public_node;
let warp_sync = !self.args.flag_no_warp && fat_db != Switch::On && tracing != Switch::On && pruning != Pruning::Specific(Algorithm::Archive);
let geth_compatibility = self.args.flag_geth;
- let ui_address = self.ui_port().map(|port| (self.ui_interface(), port));
let mut dapps_conf = self.dapps_config();
let ipfs_conf = self.ipfs_config();
- let signer_conf = self.signer_config();
let secretstore_conf = self.secretstore_config()?;
let format = self.format()?;
@@ -149,11 +145,10 @@ impl Configuration {
let cmd = if self.args.flag_version {
Cmd::Version
} else if self.args.cmd_signer {
- let mut authfile = PathBuf::from(signer_conf.signer_path.clone());
- authfile.push(AUTHCODE_FILENAME);
+ let authfile = ::signer::codes_path(&ws_conf.signer_path);
if self.args.cmd_new_token {
- Cmd::SignerToken(signer_conf)
+ Cmd::SignerToken(ws_conf, ui_conf)
} else if self.args.cmd_sign {
let pwfile = self.args.flag_password.get(0).map(|pwfile| {
PathBuf::from(pwfile)
@@ -161,18 +156,18 @@ impl Configuration {
Cmd::SignerSign {
id: self.args.arg_id,
pwfile: pwfile,
- port: signer_conf.port,
+ port: ws_conf.port,
authfile: authfile,
}
} else if self.args.cmd_reject {
Cmd::SignerReject {
id: self.args.arg_id,
- port: signer_conf.port,
+ port: ws_conf.port,
authfile: authfile,
}
} else if self.args.cmd_list {
Cmd::SignerList {
- port: signer_conf.port,
+ port: ws_conf.port,
authfile: authfile,
}
} else {
@@ -372,11 +367,10 @@ impl Configuration {
warp_sync: warp_sync,
public_node: public_node,
geth_compatibility: geth_compatibility,
- ui_address: ui_address,
net_settings: self.network_settings()?,
dapps_conf: dapps_conf,
ipfs_conf: ipfs_conf,
- signer_conf: signer_conf,
+ ui_conf: ui_conf,
secretstore_conf: secretstore_conf,
dapp: self.dapp_to_open()?,
ui: self.args.cmd_ui,
@@ -553,13 +547,12 @@ impl Configuration {
Ok(options)
}
- fn signer_config(&self) -> SignerConfiguration {
- SignerConfiguration {
+ fn ui_config(&self) -> UiConfiguration {
+ UiConfiguration {
enabled: self.ui_enabled(),
- port: self.args.flag_ports_shift + self.args.flag_ui_port,
interface: self.ui_interface(),
- signer_path: self.directories().signer,
- skip_origin_validation: self.args.flag_unsafe_expose || self.args.flag_ui_no_validation,
+ port: self.args.flag_ports_shift + self.args.flag_ui_port,
+ hosts: self.ui_hosts(),
}
}
@@ -768,6 +761,14 @@ impl Configuration {
Some(hosts)
}
+ fn ui_hosts(&self) -> Option> {
+ if self.args.flag_ui_no_validation {
+ return None;
+ }
+
+ self.hosts(&self.args.flag_ui_hosts, &self.ui_interface())
+ }
+
fn rpc_hosts(&self) -> Option> {
self.hosts(&self.args.flag_jsonrpc_hosts, &self.rpc_interface())
}
@@ -825,13 +826,17 @@ impl Configuration {
}
fn ws_config(&self) -> Result {
+ let ui = self.ui_config();
+
let conf = WsConfiguration {
enabled: self.ws_enabled(),
interface: self.ws_interface(),
port: self.args.flag_ports_shift + self.args.flag_ws_port,
apis: self.args.flag_ws_apis.parse()?,
hosts: self.ws_hosts(),
- origins: self.ws_origins()
+ origins: self.ws_origins(),
+ signer_path: self.directories().signer.into(),
+ ui_address: ui.address(),
};
Ok(conf)
@@ -928,18 +933,6 @@ impl Configuration {
}
}
- fn ui_port(&self) -> Option {
- if !self.ui_enabled() {
- None
- } else {
- Some(self.args.flag_ui_port)
- }
- }
-
- fn ui_interface(&self) -> String {
- self.interface(&self.args.flag_ui_interface)
- }
-
fn interface(&self, interface: &str) -> String {
if self.args.flag_unsafe_expose {
return "0.0.0.0".into();
@@ -952,6 +945,11 @@ impl Configuration {
}.into()
}
+
+ fn ui_interface(&self) -> String {
+ self.interface(&self.args.flag_ui_interface)
+ }
+
fn rpc_interface(&self) -> String {
let rpc_interface = self.args.flag_rpcaddr.clone().unwrap_or(self.args.flag_jsonrpc_interface.clone());
self.interface(&rpc_interface)
@@ -1050,23 +1048,26 @@ impl Configuration {
#[cfg(test)]
mod tests {
- use super::*;
- use cli::Args;
- use parity_rpc::NetworkSettings;
+ use std::io::Write;
+ use std::fs::{File, create_dir};
+
+ use devtools::{RandomTempPath};
use ethcore::client::{VMType, BlockId};
use ethcore::miner::{MinerOptions, PrioritizationStrategy};
- use helpers::{default_network_config};
- use run::RunCmd;
- use dir::{Directories, default_hypervisor_path};
- use signer::{Configuration as SignerConfiguration};
+ use parity_rpc::NetworkSettings;
+ use updater::{UpdatePolicy, UpdateFilter, ReleaseTrack};
+
+ use account::{AccountCmd, NewAccount, ImportAccounts, ListAccounts};
use blockchain::{BlockchainCmd, ImportBlockchain, ExportBlockchain, DataFormat, ExportState};
- use presale::ImportWallet;
+ use cli::Args;
+ use dir::{Directories, default_hypervisor_path};
+ use helpers::{default_network_config};
use params::SpecType;
- use account::{AccountCmd, NewAccount, ImportAccounts, ListAccounts};
- use devtools::{RandomTempPath};
- use updater::{UpdatePolicy, UpdateFilter, ReleaseTrack};
- use std::io::Write;
- use std::fs::{File, create_dir};
+ use presale::ImportWallet;
+ use rpc::{WsConfiguration, UiConfiguration};
+ use run::RunCmd;
+
+ use super::*;
#[derive(Debug, PartialEq)]
struct TestPasswordReader(&'static str);
@@ -1233,12 +1234,20 @@ mod tests {
let args = vec!["parity", "signer", "new-token"];
let conf = parse(&args);
let expected = Directories::default().signer;
- assert_eq!(conf.into_command().unwrap().cmd, Cmd::SignerToken(SignerConfiguration {
+ assert_eq!(conf.into_command().unwrap().cmd, Cmd::SignerToken(WsConfiguration {
+ enabled: true,
+ interface: "127.0.0.1".into(),
+ port: 8546,
+ apis: ApiSet::UnsafeContext,
+ origins: Some(vec!["chrome-extension://*".into()]),
+ hosts: Some(vec![]),
+ signer_path: expected.into(),
+ ui_address: Some(("127.0.0.1".to_owned(), 8180)),
+ }, UiConfiguration {
enabled: true,
- signer_path: expected,
interface: "127.0.0.1".into(),
port: 8180,
- skip_origin_validation: false,
+ hosts: Some(vec![]),
}));
}
@@ -1273,11 +1282,10 @@ mod tests {
wal: true,
vm_type: Default::default(),
geth_compatibility: false,
- ui_address: Some(("127.0.0.1".into(), 8180)),
net_settings: Default::default(),
dapps_conf: Default::default(),
ipfs_conf: Default::default(),
- signer_conf: Default::default(),
+ ui_conf: Default::default(),
secretstore_conf: Default::default(),
ui: false,
dapp: None,
@@ -1457,7 +1465,7 @@ mod tests {
}
#[test]
- fn should_parse_signer_configration() {
+ fn should_parse_ui_configuration() {
// given
// when
@@ -1467,33 +1475,33 @@ mod tests {
let conf3 = parse(&["parity", "--ui-path", "signer", "--ui-interface", "test"]);
// then
- assert_eq!(conf0.signer_config(), SignerConfiguration {
+ assert_eq!(conf0.directories().signer, "signer".to_owned());
+ assert_eq!(conf0.ui_config(), UiConfiguration {
enabled: true,
- port: 8180,
interface: "127.0.0.1".into(),
- signer_path: "signer".into(),
- skip_origin_validation: false,
+ port: 8180,
+ hosts: Some(vec![]),
});
- assert_eq!(conf1.signer_config(), SignerConfiguration {
+ assert_eq!(conf1.directories().signer, "signer".to_owned());
+ assert_eq!(conf1.ui_config(), UiConfiguration {
enabled: true,
- port: 8180,
interface: "127.0.0.1".into(),
- signer_path: "signer".into(),
- skip_origin_validation: true,
+ port: 8180,
+ hosts: None,
});
- assert_eq!(conf2.signer_config(), SignerConfiguration {
+ assert_eq!(conf2.directories().signer, "signer".to_owned());
+ assert_eq!(conf2.ui_config(), UiConfiguration {
enabled: true,
- port: 3123,
interface: "127.0.0.1".into(),
- signer_path: "signer".into(),
- skip_origin_validation: false,
+ port: 3123,
+ hosts: Some(vec![]),
});
- assert_eq!(conf3.signer_config(), SignerConfiguration {
+ assert_eq!(conf3.directories().signer, "signer".to_owned());
+ assert_eq!(conf3.ui_config(), UiConfiguration {
enabled: true,
- port: 8180,
interface: "test".into(),
- signer_path: "signer".into(),
- skip_origin_validation: false,
+ port: 8180,
+ hosts: Some(vec![]),
});
}
@@ -1551,7 +1559,7 @@ mod tests {
assert_eq!(conf0.network_settings().unwrap().rpc_port, 8546);
assert_eq!(conf0.http_config().unwrap().port, 8546);
assert_eq!(conf0.ws_config().unwrap().port, 8547);
- assert_eq!(conf0.signer_config().port, 8181);
+ assert_eq!(conf0.ui_config().port, 8181);
assert_eq!(conf0.secretstore_config().unwrap().port, 8084);
assert_eq!(conf0.secretstore_config().unwrap().http_port, 8083);
assert_eq!(conf0.ipfs_config().port, 5002);
@@ -1563,7 +1571,7 @@ mod tests {
assert_eq!(conf1.network_settings().unwrap().rpc_port, 8545);
assert_eq!(conf1.http_config().unwrap().port, 8545);
assert_eq!(conf1.ws_config().unwrap().port, 8547);
- assert_eq!(conf1.signer_config().port, 8181);
+ assert_eq!(conf1.ui_config().port, 8181);
assert_eq!(conf1.secretstore_config().unwrap().port, 8084);
assert_eq!(conf1.secretstore_config().unwrap().http_port, 8083);
assert_eq!(conf1.ipfs_config().port, 5002);
@@ -1582,8 +1590,8 @@ mod tests {
assert_eq!(conf0.http_config().unwrap().hosts, None);
assert_eq!(&conf0.ws_config().unwrap().interface, "0.0.0.0");
assert_eq!(conf0.ws_config().unwrap().hosts, None);
- assert_eq!(&conf0.signer_config().interface, "0.0.0.0");
- assert_eq!(conf0.signer_config().skip_origin_validation, true);
+ assert_eq!(&conf0.ui_config().interface, "0.0.0.0");
+ assert_eq!(conf0.ui_config().hosts, None);
assert_eq!(&conf0.secretstore_config().unwrap().interface, "0.0.0.0");
assert_eq!(&conf0.secretstore_config().unwrap().http_interface, "0.0.0.0");
assert_eq!(&conf0.ipfs_config().interface, "0.0.0.0");
diff --git a/parity/dapps.rs b/parity/dapps.rs
index 324e4040305..f0ae06c6b30 100644
--- a/parity/dapps.rs
+++ b/parity/dapps.rs
@@ -27,6 +27,7 @@ use hash_fetch::urlhint::ContractClient;
use helpers::replace_home;
use light::client::Client as LightClient;
use light::on_demand::{self, OnDemand};
+use rpc;
use rpc_apis::SignerService;
use parity_reactor;
use util::{Bytes, Address};
@@ -49,6 +50,15 @@ impl Default for Configuration {
}
}
+impl Configuration {
+ pub fn address(&self, address: Option<(String, u16)>) -> Option<(String, u16)> {
+ match self.enabled {
+ true => address,
+ false => None,
+ }
+ }
+}
+
/// Registrar implementation of the full client.
pub struct FullRegistrar {
/// Handle to the full client.
@@ -125,35 +135,49 @@ impl ContractClient for LightRegistrar {
// TODO: light client implementation forwarding to OnDemand and waiting for future
// to resolve.
+#[derive(Clone)]
pub struct Dependencies {
pub sync_status: Arc,
pub contract_client: Arc,
pub remote: parity_reactor::TokioRemote,
pub fetch: FetchClient,
pub signer: Arc,
+ pub ui_address: Option<(String, u16)>,
}
-pub fn new(configuration: Configuration, deps: Dependencies)
- -> Result