Dapps and RPC server merge (#5365)

* Dapps server as a middleware.

* Dapps RPC - Work in Progress

* Merging Dapps and RPC server.

* Fast HTTP server configuration.

* Bump jsonrpc

* Fixing test target

* Re-implementing commented-out tests.
This commit is contained in:
Tomasz Drwięga 2017-04-03 10:27:37 +02:00 committed by Gav Wood
parent 6a05967bef
commit 2df4532d50
35 changed files with 869 additions and 1248 deletions

148
Cargo.lock generated
View File

@ -10,7 +10,6 @@ dependencies = [
"docopt 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore 1.7.0",
"ethcore-dapps 1.7.0",
"ethcore-devtools 1.7.0",
"ethcore-io 1.7.0",
"ethcore-ipc 1.7.0",
@ -27,12 +26,12 @@ dependencies = [
"ethsync 1.7.0",
"evmbin 0.1.0",
"fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.10.0-a.0 (git+https://github.com/paritytech/hyper)",
"isatty 0.1.1 (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)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-dapps 1.7.0",
"parity-hash-fetch 1.7.0",
"parity-ipfs-api 1.7.0",
"parity-local-store 0.1.0",
@ -40,6 +39,7 @@ dependencies = [
"parity-rpc-client 1.4.0",
"parity-updater 1.7.0",
"path 0.1.0",
"pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rlp 0.1.0",
"rpassword 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -309,6 +309,11 @@ dependencies = [
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "difference"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "docopt"
version = "0.7.0"
@ -446,40 +451,6 @@ dependencies = [
"siphasher 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ethcore-dapps"
version = "1.7.0"
dependencies = [
"base32 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-devtools 1.7.0",
"ethcore-rpc 1.7.0",
"ethcore-util 1.7.0",
"fetch 0.1.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-http-server 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)",
"linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"mime_guess 1.6.1 (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-hash-fetch 1.7.0",
"parity-reactor 0.1.0",
"parity-ui 1.7.0",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 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)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"zip 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ethcore-devtools"
version = "1.7.0"
@ -642,6 +613,7 @@ dependencies = [
"jsonrpc-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"jsonrpc-ipc-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"jsonrpc-macros 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"jsonrpc-minihttp-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"order-stat 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-reactor 0.1.0",
@ -1088,7 +1060,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "jsonrpc-core"
version = "7.0.0"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#f4521e8a543145bec7936de0f875d6550e92c7f7"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#32c1c083139db50db6a5d532ccfc2004236dbfc3"
dependencies = [
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1100,7 +1072,7 @@ dependencies = [
[[package]]
name = "jsonrpc-http-server"
version = "7.0.0"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#f4521e8a543145bec7936de0f875d6550e92c7f7"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#32c1c083139db50db6a5d532ccfc2004236dbfc3"
dependencies = [
"hyper 0.10.0-a.0 (git+https://github.com/paritytech/hyper)",
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
@ -1113,7 +1085,7 @@ dependencies = [
[[package]]
name = "jsonrpc-ipc-server"
version = "7.0.0"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#f4521e8a543145bec7936de0f875d6550e92c7f7"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#32c1c083139db50db6a5d532ccfc2004236dbfc3"
dependencies = [
"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)",
@ -1125,17 +1097,31 @@ dependencies = [
[[package]]
name = "jsonrpc-macros"
version = "7.0.0"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#f4521e8a543145bec7936de0f875d6550e92c7f7"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#32c1c083139db50db6a5d532ccfc2004236dbfc3"
dependencies = [
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"jsonrpc-pubsub 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "jsonrpc-minihttp-server"
version = "7.0.0"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#32c1c083139db50db6a5d532ccfc2004236dbfc3"
dependencies = [
"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.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-minihttp 0.1.0 (git+https://github.com/tomusdrw/tokio-minihttp)",
"tokio-proto 0.1.0 (git+https://github.com/tomusdrw/tokio-proto)",
"tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "jsonrpc-pubsub"
version = "7.0.0"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#f4521e8a543145bec7936de0f875d6550e92c7f7"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#32c1c083139db50db6a5d532ccfc2004236dbfc3"
dependencies = [
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1145,7 +1131,7 @@ dependencies = [
[[package]]
name = "jsonrpc-server-utils"
version = "7.0.0"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#f4521e8a543145bec7936de0f875d6550e92c7f7"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#32c1c083139db50db6a5d532ccfc2004236dbfc3"
dependencies = [
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1156,7 +1142,7 @@ dependencies = [
[[package]]
name = "jsonrpc-tcp-server"
version = "7.0.0"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#f4521e8a543145bec7936de0f875d6550e92c7f7"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#32c1c083139db50db6a5d532ccfc2004236dbfc3"
dependencies = [
"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)",
@ -1604,6 +1590,38 @@ dependencies = [
"stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parity-dapps"
version = "1.7.0"
dependencies = [
"base32 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-devtools 1.7.0",
"ethcore-util 1.7.0",
"fetch 0.1.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-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"mime_guess 1.6.1 (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-hash-fetch 1.7.0",
"parity-reactor 0.1.0",
"parity-ui 1.7.0",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 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)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"zip 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parity-dapps-glue"
version = "1.7.0"
@ -1826,6 +1844,14 @@ name = "podio"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "pretty_assertions"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "primal"
version = "0.2.3"
@ -2431,6 +2457,21 @@ dependencies = [
"tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-minihttp"
version = "0.1.0"
source = "git+https://github.com/tomusdrw/tokio-minihttp#8acbafae3e77e7f7eb516b441ec84695580221dd"
dependencies = [
"futures 0.1.11 (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.6 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-proto 0.1.0 (git+https://github.com/tomusdrw/tokio-proto)",
"tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-named-pipes"
version = "0.1.0"
@ -2441,6 +2482,22 @@ dependencies = [
"tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-proto"
version = "0.1.0"
source = "git+https://github.com/tomusdrw/tokio-proto#f6ee08cb594fa2fc1b4178eaaca0855d66e68fd3"
dependencies = [
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-proto"
version = "0.1.0"
@ -2706,6 +2763,7 @@ dependencies = [
"checksum ctrlc 1.1.1 (git+https://github.com/paritytech/rust-ctrlc.git)" = "<none>"
"checksum daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "271ec51b7e0bee92f0d04601422c73eb76ececf197026711c97ad25038a010cf"
"checksum deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1614659040e711785ed8ea24219140654da1729f3ec8a47a9719d041112fe7bf"
"checksum difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3304d19798a8e067e48d8e69b2c37f0b5e9b4e462504ad9e27e9f3fce02bba8"
"checksum docopt 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab32ea6e284d87987066f21a9e809a73c14720571ef34516f0890b3d355ccfd8"
"checksum dtoa 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5edd69c67b2f8e0911629b7e6b8a34cb3956613cd7c6e6414966dee349c2db4f"
"checksum either 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3d2b503c86dad62aaf414ecf2b8c527439abedb3f8d812537f0b12bfd6f32a91"
@ -2739,6 +2797,7 @@ dependencies = [
"checksum jsonrpc-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "<none>"
"checksum jsonrpc-ipc-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "<none>"
"checksum jsonrpc-macros 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "<none>"
"checksum jsonrpc-minihttp-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "<none>"
"checksum jsonrpc-pubsub 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "<none>"
"checksum jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "<none>"
"checksum jsonrpc-tcp-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "<none>"
@ -2802,6 +2861,7 @@ dependencies = [
"checksum phf_shared 0.7.14 (registry+https://github.com/rust-lang/crates.io-index)" = "fee4d039930e4f45123c9b15976cf93a499847b6483dc09c42ea0ec4940f2aa6"
"checksum pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8cee804ecc7eaf201a4a207241472cc870e825206f6c031e3ee2a72fa425f2fa"
"checksum podio 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e5422a1ee1bc57cc47ae717b0137314258138f38fd5f3cea083f43a9725383a0"
"checksum pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2412f3332a07c7a2a50168988dcc184f32180a9758ad470390e5f55e089f6b6e"
"checksum primal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0e31b86efadeaeb1235452171a66689682783149a6249ff334a2c5d8218d00a4"
"checksum primal-bit 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "464a91febc06166783d4f5ba3577b5ed8dda8e421012df80bfe48a971ed7be8f"
"checksum primal-check 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "647c81b67bb9551a7b88d0bcd785ac35b7d0bf4b2f358683d7c2375d04daec51"
@ -2871,7 +2931,9 @@ dependencies = [
"checksum tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3d1be481b55126f02ef88ff86748086473cb537a949fc4a8f4be403a530ae54b"
"checksum tokio-io 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6a278fde45f1be68e44995227d426aaa4841e0980bb0a21b981092f28c3c8473"
"checksum tokio-line 0.1.0 (git+https://github.com/tokio-rs/tokio-line)" = "<none>"
"checksum tokio-minihttp 0.1.0 (git+https://github.com/tomusdrw/tokio-minihttp)" = "<none>"
"checksum tokio-named-pipes 0.1.0 (git+https://github.com/alexcrichton/tokio-named-pipes)" = "<none>"
"checksum tokio-proto 0.1.0 (git+https://github.com/tomusdrw/tokio-proto)" = "<none>"
"checksum tokio-proto 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c0d6031f94d78d7b4d509d4a7c5e1cdf524a17e7b08d1c188a83cf720e69808"
"checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162"
"checksum tokio-uds 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ffc7b5fc8e19e220b29566d1750949224a518478eab9cebc8df60583242ca30a"

View File

@ -25,7 +25,6 @@ serde_json = "0.9"
app_dirs = "1.1.1"
fdlimit = "0.1"
ws2_32-sys = "0.2"
hyper = { default-features = false, git = "https://github.com/paritytech/hyper" }
ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" }
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
ethsync = { path = "sync" }
@ -50,8 +49,9 @@ parity-ipfs-api = { path = "ipfs" }
parity-updater = { path = "updater" }
parity-reactor = { path = "util/reactor" }
parity-local-store = { path = "local-store" }
ethcore-dapps = { path = "dapps", optional = true }
path = { path = "util/path" }
parity-dapps = { path = "dapps", optional = true }
clippy = { version = "0.0.103", optional = true}
ethcore-secretstore = { path = "secret_store", optional = true }
@ -60,6 +60,7 @@ rustc_version = "0.2"
[dev-dependencies]
ethcore-ipc-tests = { path = "ipc/tests" }
pretty_assertions = "0.1"
[target.'cfg(windows)'.dependencies]
winapi = "0.2"
@ -71,18 +72,18 @@ daemonize = "0.2"
default = ["ui-precompiled"]
ui = [
"dapps",
"ethcore-dapps/ui",
"parity-dapps/ui",
"ethcore-signer/ui",
]
ui-precompiled = [
"dapps",
"ethcore-signer/ui-precompiled",
"ethcore-dapps/ui-precompiled",
"parity-dapps/ui-precompiled",
]
dapps = ["ethcore-dapps"]
dapps = ["parity-dapps"]
ipc = ["ethcore/ipc", "ethsync/ipc"]
jit = ["ethcore/jit"]
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev", "ethcore-dapps/dev", "ethcore-signer/dev"]
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev", "parity-dapps/dev", "ethcore-signer/dev"]
json-tests = ["ethcore/json-tests"]
test-heavy = ["ethcore/test-heavy"]
ethkey-cli = ["ethcore/ethkey-cli"]

View File

@ -1,6 +1,6 @@
[package]
description = "Parity Dapps crate"
name = "ethcore-dapps"
name = "parity-dapps"
version = "1.7.0"
license = "GPL-3.0"
authors = ["Parity Technologies <admin@parity.io>"]
@ -28,11 +28,8 @@ zip = { version = "0.1", default-features = false }
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
# TODO [ToDr] Temporary solution, server should be merged with RPC.
jsonrpc-server-utils = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
ethcore-devtools = { path = "../devtools" }
ethcore-rpc = { path = "../rpc" }
ethcore-util = { path = "../util" }
fetch = { path = "../util/fetch" }
parity-hash-fetch = { path = "../hash-fetch" }
@ -42,7 +39,7 @@ parity-ui = { path = "./ui" }
clippy = { version = "0.0.103", optional = true}
[features]
dev = ["clippy", "ethcore-rpc/dev", "ethcore-util/dev"]
dev = ["clippy", "ethcore-util/dev"]
ui = ["parity-ui/no-precompiled-js"]
ui-precompiled = ["parity-ui/use-precompiled-js"]

View File

@ -14,7 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc;
use unicase::UniCase;
use hyper::{server, net, Decoder, Encoder, Next, Control};
use hyper::header;
@ -26,48 +25,49 @@ use apps::fetcher::Fetcher;
use handlers::extract_url;
use endpoint::{Endpoint, Endpoints, Handler, EndpointPath};
use jsonrpc_http_server;
use jsonrpc_server_utils::cors;
use jsonrpc_http_server::{self, AccessControlAllowOrigin};
#[derive(Clone)]
pub struct RestApi {
cors_domains: Option<Vec<cors::AccessControlAllowOrigin>>,
endpoints: Arc<Endpoints>,
fetcher: Arc<Fetcher>,
pub struct RestApi<F> {
// 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<Vec<AccessControlAllowOrigin>>,
apps: Vec<App>,
fetcher: F,
}
impl RestApi {
pub fn new(cors_domains: Vec<cors::AccessControlAllowOrigin>, endpoints: Arc<Endpoints>, fetcher: Arc<Fetcher>) -> Box<Endpoint> {
impl<F: Fetcher + Clone> RestApi<F> {
pub fn new(cors_domains: Vec<AccessControlAllowOrigin>, endpoints: &Endpoints, fetcher: F) -> Box<Endpoint> {
Box::new(RestApi {
cors_domains: Some(cors_domains),
endpoints: endpoints,
apps: Self::list_apps(endpoints),
fetcher: fetcher,
})
}
fn list_apps(&self) -> Vec<App> {
self.endpoints.iter().filter_map(|(ref k, ref e)| {
fn list_apps(endpoints: &Endpoints) -> Vec<App> {
endpoints.iter().filter_map(|(ref k, ref e)| {
e.info().map(|ref info| App::from_info(k, info))
}).collect()
}
}
impl Endpoint for RestApi {
impl<F: Fetcher + Clone> Endpoint for RestApi<F> {
fn to_async_handler(&self, path: EndpointPath, control: Control) -> Box<Handler> {
Box::new(RestApiRouter::new(self.clone(), path, control))
Box::new(RestApiRouter::new((*self).clone(), path, control))
}
}
struct RestApiRouter {
api: RestApi,
struct RestApiRouter<F> {
api: RestApi<F>,
cors_header: Option<header::AccessControlAllowOrigin>,
path: Option<EndpointPath>,
control: Option<Control>,
handler: Box<Handler>,
}
impl RestApiRouter {
fn new(api: RestApi, path: EndpointPath, control: Control) -> Self {
impl<F: Fetcher> RestApiRouter<F> {
fn new(api: RestApi<F>, path: EndpointPath, control: Control) -> Self {
RestApiRouter {
path: Some(path),
cors_header: None,
@ -114,7 +114,7 @@ impl RestApiRouter {
}
}
impl server::Handler<net::HttpStream> for RestApiRouter {
impl<F: Fetcher> server::Handler<net::HttpStream> for RestApiRouter<F> {
fn on_request(&mut self, request: server::Request<net::HttpStream>) -> Next {
self.cors_header = jsonrpc_http_server::cors_header(&request, &self.api.cors_domains).into();
@ -142,7 +142,7 @@ impl server::Handler<net::HttpStream> 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.list_apps())),
"apps" => Some(response::as_json(&self.api.apps)),
"ping" => Some(response::ping()),
"content" => self.resolve_content(hash, path, control),
_ => None

View File

@ -47,7 +47,8 @@ pub trait Fetcher: Send + Sync + 'static {
fn to_async_handler(&self, path: EndpointPath, control: hyper::Control) -> Box<Handler>;
}
pub struct ContentFetcher<F: Fetch = FetchClient, R: URLHint + Send + Sync + 'static = URLHintContract> {
#[derive(Clone)]
pub struct ContentFetcher<F: Fetch + Clone = FetchClient, R: URLHint + Clone + 'static = URLHintContract> {
dapps_path: PathBuf,
resolver: R,
cache: Arc<Mutex<ContentCache>>,
@ -57,14 +58,14 @@ pub struct ContentFetcher<F: Fetch = FetchClient, R: URLHint + Send + Sync + 'st
fetch: F,
}
impl<R: URLHint + Send + Sync + 'static, F: Fetch> Drop for ContentFetcher<F, R> {
impl<R: URLHint + Clone + 'static, F: Fetch + Clone> Drop for ContentFetcher<F, R> {
fn drop(&mut self) {
// Clear cache path
let _ = fs::remove_dir_all(&self.dapps_path);
}
}
impl<R: URLHint + Send + Sync + 'static, F: Fetch> ContentFetcher<F, R> {
impl<R: URLHint + Clone + 'static, F: Fetch + Clone> ContentFetcher<F, R> {
pub fn new(resolver: R, sync_status: Arc<SyncStatus>, embeddable_on: Option<(String, u16)>, remote: Remote, fetch: F) -> Self {
let mut dapps_path = env::temp_dir();
@ -97,7 +98,7 @@ impl<R: URLHint + Send + Sync + 'static, F: Fetch> ContentFetcher<F, R> {
}
}
impl<R: URLHint + Send + Sync + 'static, F: Fetch> Fetcher for ContentFetcher<F, R> {
impl<R: URLHint + Clone + 'static, F: Fetch + Clone> Fetcher for ContentFetcher<F, R> {
fn contains(&self, content_id: &str) -> bool {
{
let mut cache = self.cache.lock();
@ -233,6 +234,7 @@ mod tests {
use page::LocalPageEndpoint;
use super::{ContentFetcher, Fetcher};
#[derive(Clone)]
struct FakeResolver;
impl URLHint for FakeResolver {
fn resolve(&self, _id: Bytes) -> Option<URLHintResult> {

View File

@ -16,9 +16,10 @@
//! URL Endpoint traits
use hyper::{self, server, net};
use std::collections::BTreeMap;
use hyper::{self, server, net};
#[derive(Debug, PartialEq, Default, Clone)]
pub struct EndpointPath {
pub app_id: String,

View File

@ -1,44 +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 <http://www.gnu.org/licenses/>.
//! Authorization Handlers
use hyper::{server, Decoder, Encoder, Next};
use hyper::net::HttpStream;
use hyper::status::StatusCode;
pub struct AuthRequiredHandler;
impl server::Handler<HttpStream> for AuthRequiredHandler {
fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
Next::write()
}
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
Next::write()
}
fn on_response(&mut self, res: &mut server::Response) -> Next {
res.set_status(StatusCode::Unauthorized);
res.headers_mut().set_raw("WWW-Authenticate", vec![b"Basic realm=\"Parity\"".to_vec()]);
Next::write()
}
fn on_response_writable(&mut self, _encoder: &mut Encoder<HttpStream>) -> Next {
Next::end()
}
}

View File

@ -16,14 +16,12 @@
//! Hyper handlers implementations.
mod auth;
mod content;
mod echo;
mod fetch;
mod redirect;
mod streaming;
pub use self::auth::AuthRequiredHandler;
pub use self::content::ContentHandler;
pub use self::echo::EchoHandler;
pub use self::fetch::{ContentFetcherHandler, ContentValidator, FetchControl, ValidatorResponse};

View File

@ -34,9 +34,7 @@ extern crate zip;
extern crate jsonrpc_core;
extern crate jsonrpc_http_server;
extern crate jsonrpc_server_utils;
extern crate ethcore_rpc;
extern crate ethcore_util as util;
extern crate fetch;
extern crate parity_dapps_glue as parity_dapps;
@ -61,7 +59,6 @@ mod apps;
mod page;
mod router;
mod handlers;
mod rpc;
mod api;
mod proxypac;
mod url;
@ -69,23 +66,16 @@ mod web;
#[cfg(test)]
mod tests;
use std::path::{Path, PathBuf};
use std::path::PathBuf;
use std::sync::Arc;
use std::net::SocketAddr;
use std::collections::HashMap;
use jsonrpc_core::{Middleware, MetaIoHandler};
use jsonrpc_http_server::tokio_core::reactor::Remote as TokioRemote;
pub use jsonrpc_http_server::{DomainsValidation, Host, AccessControlAllowOrigin};
pub use jsonrpc_http_server::hyper;
use jsonrpc_http_server::{self as http, hyper, AccessControlAllowOrigin};
use ethcore_rpc::Metadata;
use fetch::{Fetch, Client as FetchClient};
use hash_fetch::urlhint::ContractClient;
use fetch::Fetch;
use parity_reactor::Remote;
use router::auth::{Authorization, NoAuth, HttpBasicAuth};
use self::apps::{HOME_PAGE, DAPPS_DOMAIN};
pub use hash_fetch::urlhint::ContractClient;
/// Indicates sync status
pub trait SyncStatus: Send + Sync {
@ -107,173 +97,76 @@ impl<F> WebProxyTokens for F where F: Fn(String) -> bool + Send + Sync {
fn is_web_proxy_token_valid(&self, token: &str) -> bool { self(token.to_owned()) }
}
/// Webapps HTTP+RPC server build.
pub struct ServerBuilder<T: Fetch = FetchClient> {
/// Dapps server as `jsonrpc-http-server` request middleware.
pub struct Middleware<F: Fetch + Clone> {
router: router::Router<apps::fetcher::ContentFetcher<F>>,
}
impl<F: Fetch + Clone> Middleware<F> {
/// Creates new Dapps server middleware.
pub fn new(
remote: Remote,
signer_address: Option<(String, u16)>,
dapps_path: PathBuf,
extra_dapps: Vec<PathBuf>,
registrar: Arc<ContractClient>,
sync_status: Arc<SyncStatus>,
web_proxy_tokens: Arc<WebProxyTokens>,
signer_address: Option<(String, u16)>,
allowed_hosts: Option<Vec<Host>>,
extra_cors: Option<Vec<AccessControlAllowOrigin>>,
remote: Remote,
fetch: Option<T>,
}
fetch: F,
) -> Self {
let content_fetcher = apps::fetcher::ContentFetcher::new(
hash_fetch::urlhint::URLHintContract::new(registrar),
sync_status,
signer_address.clone(),
remote.clone(),
fetch.clone(),
);
let endpoints = apps::all_endpoints(
dapps_path,
extra_dapps,
signer_address.clone(),
web_proxy_tokens,
remote.clone(),
fetch.clone(),
);
impl ServerBuilder {
/// Construct new dapps server
pub fn new<P: AsRef<Path>>(dapps_path: P, registrar: Arc<ContractClient>, remote: Remote) -> Self {
ServerBuilder {
dapps_path: dapps_path.as_ref().to_owned(),
extra_dapps: vec![],
registrar: registrar,
sync_status: Arc::new(|| false),
web_proxy_tokens: Arc::new(|_| false),
signer_address: None,
allowed_hosts: Some(vec![]),
extra_cors: None,
remote: remote,
fetch: None,
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())),
);
special
};
let router = router::Router::new(
signer_address,
content_fetcher,
endpoints,
special,
);
Middleware {
router: router,
}
}
}
impl<T: Fetch> ServerBuilder<T> {
/// Set a fetch client to use.
pub fn fetch<X: Fetch>(self, fetch: X) -> ServerBuilder<X> {
ServerBuilder {
dapps_path: self.dapps_path,
extra_dapps: vec![],
registrar: self.registrar,
sync_status: self.sync_status,
web_proxy_tokens: self.web_proxy_tokens,
signer_address: self.signer_address,
allowed_hosts: self.allowed_hosts,
extra_cors: self.extra_cors,
remote: self.remote,
fetch: Some(fetch),
impl<F: Fetch + Clone> http::RequestMiddleware for Middleware<F> {
fn on_request(&self, req: &hyper::server::Request<hyper::net::HttpStream>, control: &hyper::Control) -> http::RequestMiddlewareAction {
self.router.on_request(req, control)
}
}
/// Change default sync status.
pub fn sync_status(mut self, status: Arc<SyncStatus>) -> Self {
self.sync_status = status;
self
}
/// Change default web proxy tokens validator.
pub fn web_proxy_tokens(mut self, tokens: Arc<WebProxyTokens>) -> Self {
self.web_proxy_tokens = tokens;
self
}
/// Change default signer port.
pub fn signer_address(mut self, signer_address: Option<(String, u16)>) -> Self {
self.signer_address = signer_address;
self
}
/// Change allowed hosts.
/// `None` - All hosts are allowed
/// `Some(whitelist)` - Allow only whitelisted hosts (+ listen address)
pub fn allowed_hosts(mut self, allowed_hosts: DomainsValidation<Host>) -> Self {
self.allowed_hosts = allowed_hosts.into();
self
}
/// Extra cors headers.
/// `None` - no additional CORS URLs
pub fn extra_cors_headers(mut self, cors: DomainsValidation<AccessControlAllowOrigin>) -> Self {
self.extra_cors = cors.into();
self
}
/// Change extra dapps paths (apart from `dapps_path`)
pub fn extra_dapps<P: AsRef<Path>>(mut self, extra_dapps: &[P]) -> Self {
self.extra_dapps = extra_dapps.iter().map(|p| p.as_ref().to_owned()).collect();
self
}
/// Asynchronously start server with no authentication,
/// returns result with `Server` handle on success or an error.
pub fn start_unsecured_http<S: Middleware<Metadata>>(self, addr: &SocketAddr, handler: MetaIoHandler<Metadata, S>, tokio_remote: TokioRemote) -> Result<Server, ServerError> {
let fetch = self.fetch_client()?;
Server::start_http(
addr,
self.allowed_hosts,
self.extra_cors,
NoAuth,
handler,
self.dapps_path,
self.extra_dapps,
self.signer_address,
self.registrar,
self.sync_status,
self.web_proxy_tokens,
self.remote,
tokio_remote,
fetch,
)
}
/// Asynchronously start server with `HTTP Basic Authentication`,
/// return result with `Server` handle on success or an error.
pub fn start_basic_auth_http<S: Middleware<Metadata>>(self, addr: &SocketAddr, username: &str, password: &str, handler: MetaIoHandler<Metadata, S>, tokio_remote: TokioRemote) -> Result<Server, ServerError> {
let fetch = self.fetch_client()?;
Server::start_http(
addr,
self.allowed_hosts,
self.extra_cors,
HttpBasicAuth::single_user(username, password),
handler,
self.dapps_path,
self.extra_dapps,
self.signer_address,
self.registrar,
self.sync_status,
self.web_proxy_tokens,
self.remote,
tokio_remote,
fetch,
)
}
fn fetch_client(&self) -> Result<T, ServerError> {
match self.fetch.clone() {
Some(fetch) => Ok(fetch),
None => T::new().map_err(|_| ServerError::FetchInitialization),
}
}
}
/// Webapps HTTP server.
pub struct Server {
server: Option<hyper::server::Listening>,
}
impl Server {
/// Returns a list of allowed hosts or `None` if all hosts are allowed.
fn allowed_hosts(hosts: Option<Vec<Host>>, bind_address: String) -> Option<Vec<Host>> {
let mut allowed = Vec::new();
match hosts {
Some(hosts) => allowed.extend_from_slice(&hosts),
None => return None,
}
// Add localhost domain as valid too if listening on loopback interface.
allowed.push(bind_address.replace("127.0.0.1", "localhost").into());
allowed.push(bind_address.into());
Some(allowed)
}
/// Returns a list of CORS domains for API endpoint.
fn cors_domains(
signer_address: Option<(String, u16)>,
extra_cors: Option<Vec<AccessControlAllowOrigin>>,
) -> Vec<AccessControlAllowOrigin> {
let basic_cors = match signer_address {
fn cors_domains(signer_address: Option<(String, u16)>) -> Vec<AccessControlAllowOrigin> {
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),
@ -283,118 +176,11 @@ impl Server {
format!("https://{}", address(&signer_address)),
].into_iter().map(|val| AccessControlAllowOrigin::Value(val.into())).collect(),
None => vec![],
};
match extra_cors {
None => basic_cors,
Some(extra_cors) => basic_cors.into_iter().chain(extra_cors).collect(),
}
}
fn start_http<A: Authorization + 'static, F: Fetch, T: Middleware<Metadata>>(
addr: &SocketAddr,
hosts: Option<Vec<Host>>,
extra_cors: Option<Vec<AccessControlAllowOrigin>>,
authorization: A,
handler: MetaIoHandler<Metadata, T>,
dapps_path: PathBuf,
extra_dapps: Vec<PathBuf>,
signer_address: Option<(String, u16)>,
registrar: Arc<ContractClient>,
sync_status: Arc<SyncStatus>,
web_proxy_tokens: Arc<WebProxyTokens>,
remote: Remote,
tokio_remote: TokioRemote,
fetch: F,
) -> Result<Server, ServerError> {
let authorization = Arc::new(authorization);
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(
hash_fetch::urlhint::URLHintContract::new(registrar),
sync_status,
signer_address.clone(),
remote.clone(),
fetch.clone(),
));
let endpoints = Arc::new(apps::all_endpoints(
dapps_path,
extra_dapps,
signer_address.clone(),
web_proxy_tokens,
remote.clone(),
fetch.clone(),
));
let cors_domains = Self::cors_domains(signer_address.clone(), extra_cors);
let special = Arc::new({
let mut special = HashMap::new();
special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, tokio_remote, cors_domains.clone()));
special.insert(router::SpecialEndpoint::Utils, apps::utils());
special.insert(
router::SpecialEndpoint::Api,
api::RestApi::new(cors_domains, endpoints.clone(), content_fetcher.clone())
);
special
});
let hosts = Self::allowed_hosts(hosts, format!("{}", addr));
hyper::Server::http(addr)?
.handle(move |ctrl| router::Router::new(
ctrl,
signer_address.clone(),
content_fetcher.clone(),
endpoints.clone(),
special.clone(),
authorization.clone(),
hosts.clone(),
))
.map(|(l, srv)| {
::std::thread::spawn(move || {
srv.run();
});
Server {
server: Some(l),
}
})
.map_err(ServerError::from)
}
#[cfg(test)]
/// Returns address that this server is bound to.
pub fn addr(&self) -> &SocketAddr {
self.server.as_ref()
.expect("server is always Some at the start; it's consumed only when object is dropped; qed")
.addrs()
.first()
.expect("You cannot start the server without binding to at least one address; qed")
}
}
impl Drop for Server {
fn drop(&mut self) {
self.server.take().unwrap().close()
}
}
/// Webapp Server startup error
#[derive(Debug)]
pub enum ServerError {
/// Wrapped `std::io::Error`
IoError(std::io::Error),
/// Other `hyper` error
Other(hyper::error::Error),
/// Fetch service initialization error
FetchInitialization,
}
impl From<hyper::error::Error> for ServerError {
fn from(err: hyper::error::Error) -> Self {
match err {
hyper::error::Error::Io(e) => ServerError::IoError(e),
e => ServerError::Other(e),
}
}
fn address(address: &(String, u16)) -> String {
format!("{}:{}", address.0, address.1)
}
/// Random filename
@ -404,39 +190,18 @@ fn random_filename() -> String {
rng.gen_ascii_chars().take(12).collect()
}
fn address(address: &(String, u16)) -> String {
format!("{}:{}", address.0, address.1)
}
#[cfg(test)]
mod util_tests {
use super::Server;
use super::cors_domains;
use jsonrpc_http_server::AccessControlAllowOrigin;
#[test]
fn should_return_allowed_hosts() {
// given
let bind_address = "127.0.0.1".to_owned();
// when
let all = Server::allowed_hosts(None, bind_address.clone());
let address = Server::allowed_hosts(Some(Vec::new()), bind_address.clone());
let some = Server::allowed_hosts(Some(vec!["ethcore.io".into()]), bind_address.clone());
// then
assert_eq!(all, None);
assert_eq!(address, Some(vec!["localhost".into(), "127.0.0.1".into()]));
assert_eq!(some, Some(vec!["ethcore.io".into(), "localhost".into(), "127.0.0.1".into()]));
}
#[test]
fn should_return_cors_domains() {
// given
// when
let none = Server::cors_domains(None, None);
let some = Server::cors_domains(Some(("127.0.0.1".into(), 18180)), None);
let extra = Server::cors_domains(None, Some(vec!["all".into()]));
let none = cors_domains(None);
let some = cors_domains(Some(("127.0.0.1".into(), 18180)));
// then
assert_eq!(none, Vec::<AccessControlAllowOrigin>::new());
@ -448,6 +213,5 @@ mod util_tests {
"https://parity.web3.site:18180".into(),
"https://127.0.0.1:18180".into(),
]);
assert_eq!(extra, vec![AccessControlAllowOrigin::Any]);
}
}

View File

@ -15,24 +15,20 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Router implementation
//! Processes request handling authorization and dispatching it to proper application.
pub mod auth;
mod host_validation;
//! 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, Next, Encoder, Decoder, Control, StatusCode};
use hyper::{self, server, header, Control, StatusCode};
use hyper::net::HttpStream;
use jsonrpc_server_utils::hosts;
use jsonrpc_http_server as http;
use apps::{self, DAPPS_DOMAIN};
use apps::fetcher::Fetcher;
use endpoint::{Endpoint, Endpoints, EndpointPath};
use endpoint::{Endpoint, Endpoints, EndpointPath, Handler};
use handlers::{self, Redirection, ContentHandler};
/// Special endpoints are accessible on every domain (every dapp)
@ -44,51 +40,29 @@ pub enum SpecialEndpoint {
None,
}
pub struct Router<A: auth::Authorization + 'static> {
control: Option<Control>,
pub struct Router<F> {
signer_address: Option<(String, u16)>,
endpoints: Arc<Endpoints>,
fetch: Arc<Fetcher>,
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
authorization: Arc<A>,
allowed_hosts: Option<Vec<hosts::Host>>,
handler: Box<server::Handler<HttpStream> + Send>,
endpoints: Endpoints,
fetch: F,
special: HashMap<SpecialEndpoint, Option<Box<Endpoint>>>,
}
impl<A: auth::Authorization + 'static> server::Handler<HttpStream> for Router<A> {
fn on_request(&mut self, req: server::Request<HttpStream>) -> Next {
impl<F: Fetcher + 'static> http::RequestMiddleware for Router<F> {
fn on_request(&self, req: &server::Request<HttpStream>, control: &Control) -> http::RequestMiddlewareAction {
// Choose proper handler depending on path / domain
let url = handlers::extract_url(&req);
let url = handlers::extract_url(req);
let endpoint = extract_endpoint(&url);
let referer = extract_referer_endpoint(&req);
let referer = extract_referer_endpoint(req);
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::<http::hyper::header::Origin>().is_some();
let is_get_request = *req.method() == hyper::Method::Get;
trace!(target: "dapps", "Routing request to {:?}. Details: {:?}", url, req);
// Validate Host header
trace!(target: "dapps", "Validating host headers against: {:?}", self.allowed_hosts);
let is_valid = is_utils || host_validation::is_valid(&req, &self.allowed_hosts);
if !is_valid {
debug!(target: "dapps", "Rejecting invalid host header.");
self.handler = host_validation::host_invalid_response();
return self.handler.on_request(req);
}
trace!(target: "dapps", "Checking authorization.");
// Check authorization
let auth = self.authorization.is_authorized(&req);
if let auth::Authorized::No(handler) = auth {
debug!(target: "dapps", "Authorization denied.");
self.handler = handler;
return self.handler.on_request(req);
}
let control = self.control.take().expect("on_request is called only once; control is always defined at start; qed");
let control = control.clone();
debug!(target: "dapps", "Handling endpoint request: {:?}", endpoint);
self.handler = match (endpoint.0, endpoint.1, referer) {
let handler: Option<Box<Handler>> = match (endpoint.0, endpoint.1, referer) {
// 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
@ -100,26 +74,27 @@ impl<A: auth::Authorization + 'static> server::Handler<HttpStream> for Router<A>
let len = cmp::min(referer_url.path.len(), 2); // /web/<encoded>/
let base = referer_url.path[..len].join("/");
let requested = url.map(|u| u.path.join("/")).unwrap_or_default();
Redirection::boxed(&format!("/{}/{}", base, requested))
Some(Redirection::boxed(&format!("/{}/{}", base, requested)))
},
// First check special endpoints
(ref path, ref endpoint, _) if self.special.contains_key(endpoint) => {
trace!(target: "dapps", "Resolving to special endpoint.");
self.special.get(endpoint)
.expect("special known to contain key; qed")
.to_async_handler(path.clone().unwrap_or_default(), control)
.as_ref()
.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) => {
trace!(target: "dapps", "Resolving to local/builtin dapp.");
self.endpoints.get(&path.app_id)
Some(self.endpoints.get(&path.app_id)
.expect("endpoints known to contain key; qed")
.to_async_handler(path.clone(), control)
.to_async_handler(path.clone(), control))
},
// Try to resolve and fetch the dapp
(Some(ref path), _, _) if self.fetch.contains(&path.app_id) => {
trace!(target: "dapps", "Resolving to fetchable content.");
self.fetch.to_async_handler(path.clone(), control)
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)
@ -128,82 +103,61 @@ impl<A: auth::Authorization + 'static> server::Handler<HttpStream> for Router<A>
// 404 for non-existent content
(Some(ref path), _, _) if is_get_request && path.app_id != "home" => {
trace!(target: "dapps", "Resolving to 404.");
Box::new(ContentHandler::error(
Some(Box::new(ContentHandler::error(
StatusCode::NotFound,
"404 Not Found",
"Requested content was not found.",
None,
self.signer_address.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.");
Redirection::boxed(&format!("http://{}", address(signer_address)))
Some(Redirection::boxed(&format!("http://{}", address(signer_address))))
} else {
trace!(target: "dapps", "Signer disabled, returning 404.");
Box::new(ContentHandler::error(
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(),
))
)))
}
},
// RPC by default
_ => {
trace!(target: "dapps", "Resolving to RPC call.");
self.special.get(&SpecialEndpoint::Rpc)
.expect("RPC endpoint always stored; qed")
.to_async_handler(EndpointPath::default(), control)
None
}
};
// Delegate on_request to proper handler
self.handler.on_request(req)
match handler {
Some(handler) => http::RequestMiddlewareAction::Respond {
should_validate_hosts: !(is_utils || is_dapps_domain),
handler: handler,
},
None => http::RequestMiddlewareAction::Proceed {
should_continue_on_invalid_cors: !is_origin_set,
},
}
/// This event occurs each time the `Request` is ready to be read from.
fn on_request_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
self.handler.on_request_readable(decoder)
}
/// This event occurs after the first time this handled signals `Next::write()`.
fn on_response(&mut self, response: &mut server::Response) -> Next {
self.handler.on_response(response)
}
/// This event occurs each time the `Response` is ready to be written to.
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
self.handler.on_response_writable(encoder)
}
}
impl<A: auth::Authorization> Router<A> {
impl<F> Router<F> {
pub fn new(
control: Control,
signer_address: Option<(String, u16)>,
content_fetcher: Arc<Fetcher>,
endpoints: Arc<Endpoints>,
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
authorization: Arc<A>,
allowed_hosts: Option<Vec<hosts::Host>>,
content_fetcher: F,
endpoints: Endpoints,
special: HashMap<SpecialEndpoint, Option<Box<Endpoint>>>,
) -> Self {
let handler = special.get(&SpecialEndpoint::Utils)
.expect("Utils endpoint always stored; qed")
.to_handler(EndpointPath::default());
Router {
control: Some(control),
signer_address: signer_address,
endpoints: endpoints,
fetch: content_fetcher,
special: special,
authorization: authorization,
allowed_hosts: allowed_hosts,
handler: handler,
}
}
}

View File

@ -1,106 +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 <http://www.gnu.org/licenses/>.
//! HTTP Authorization implementations
use std::collections::HashMap;
use hyper::{server, net, header, status};
use endpoint::Handler;
use handlers::{AuthRequiredHandler, ContentHandler};
/// Authorization result
pub enum Authorized {
/// Authorization was successful.
Yes,
/// Unsuccessful authorization. Handler for further work is returned.
No(Box<Handler>),
}
/// Authorization interface
pub trait Authorization : Send + Sync {
/// Checks if authorization is valid.
fn is_authorized(&self, req: &server::Request<net::HttpStream>)-> Authorized;
}
/// HTTP Basic Authorization handler
pub struct HttpBasicAuth {
users: HashMap<String, String>,
}
/// No-authorization implementation (authorization disabled)
pub struct NoAuth;
impl Authorization for NoAuth {
fn is_authorized(&self, _req: &server::Request<net::HttpStream>)-> Authorized {
Authorized::Yes
}
}
impl Authorization for HttpBasicAuth {
fn is_authorized(&self, req: &server::Request<net::HttpStream>) -> Authorized {
let auth = self.check_auth(&req);
match auth {
Access::Denied => {
Authorized::No(Box::new(ContentHandler::error(
status::StatusCode::Unauthorized,
"Unauthorized",
"You need to provide valid credentials to access this page.",
None,
None,
)))
},
Access::AuthRequired => {
Authorized::No(Box::new(AuthRequiredHandler))
},
Access::Granted => {
Authorized::Yes
},
}
}
}
#[derive(Debug)]
enum Access {
Granted,
Denied,
AuthRequired,
}
impl HttpBasicAuth {
/// Creates `HttpBasicAuth` instance with only one user.
pub fn single_user(username: &str, password: &str) -> Self {
let mut users = HashMap::new();
users.insert(username.to_owned(), password.to_owned());
HttpBasicAuth {
users: users
}
}
fn is_authorized(&self, username: &str, password: &str) -> bool {
self.users.get(&username.to_owned()).map_or(false, |pass| pass == password)
}
fn check_auth(&self, req: &server::Request<net::HttpStream>) -> Access {
match req.headers().get::<header::Authorization<header::Basic>>() {
Some(&header::Authorization(
header::Basic { ref username, password: Some(ref password) }
)) if self.is_authorized(username, password) => Access::Granted,
Some(_) => Access::Denied,
None => Access::AuthRequired,
}
}
}

View File

@ -1,42 +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 <http://www.gnu.org/licenses/>.
use apps::DAPPS_DOMAIN;
use hyper::{server, header, StatusCode};
use hyper::net::HttpStream;
use handlers::ContentHandler;
use jsonrpc_http_server;
use jsonrpc_server_utils::hosts;
pub fn is_valid(req: &server::Request<HttpStream>, allowed_hosts: &Option<Vec<hosts::Host>>) -> bool {
let header_valid = jsonrpc_http_server::is_host_allowed(req, allowed_hosts);
match (header_valid, req.headers().get::<header::Host>()) {
(true, _) => true,
(_, Some(host)) => host.hostname.ends_with(DAPPS_DOMAIN),
_ => false,
}
}
pub fn host_invalid_response() -> Box<server::Handler<HttpStream> + Send> {
Box::new(ContentHandler::error(StatusCode::Forbidden,
"Current Host Is Disallowed",
"You are trying to access your node using incorrect address.",
Some("Use allowed URL or specify different <code>hosts</code> CLI options."),
None,
))
}

View File

@ -66,14 +66,14 @@ impl<T: Middleware<Metadata>> Endpoint for RpcEndpoint<T> {
#[derive(Default)]
struct NoopMiddleware;
impl http::RequestMiddleware for NoopMiddleware {
fn on_request(&self, request: &http::hyper::server::Request<http::hyper::net::HttpStream>) -> http::RequestMiddlewareAction {
fn on_request(&self, request: &http::hyper::server::Request<http::hyper::net::HttpStream>, _control: &http::hyper::Control) -> http::RequestMiddlewareAction {
http::RequestMiddlewareAction::Proceed {
should_continue_on_invalid_cors: request.headers().get::<http::hyper::header::Origin>().is_none(),
}
}
}
struct MetadataExtractor;
pub struct MetadataExtractor;
impl HttpMetaExtractor<Metadata> for MetadataExtractor {
fn read_metadata(&self, request: &http::hyper::server::Request<http::hyper::net::HttpStream>) -> Metadata {
let dapp_id = request.headers().get::<http::hyper::header::Origin>()

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use tests::helpers::{serve, serve_with_registrar, serve_extra_cors, request, assert_security_headers};
use tests::helpers::{serve, serve_with_registrar, request, assert_security_headers};
#[test]
fn should_return_error() {
@ -195,26 +195,3 @@ fn should_return_signer_port_cors_headers_for_home_parity_with_port() {
response.assert_status("HTTP/1.1 200 OK");
response.assert_header("Access-Control-Allow-Origin", "http://parity.web3.site:18180");
}
#[test]
fn should_return_extra_cors_headers() {
// given
let server = serve_extra_cors(Some(vec!["all".to_owned()]));
// when
let response = request(server,
"\
POST /api/ping HTTP/1.1\r\n\
Host: localhost:8080\r\n\
Origin: http://somedomain.io\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://somedomain.io");
}

View File

@ -1,80 +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 <http://www.gnu.org/licenses/>.
use tests::helpers::{serve_with_auth, request, assert_security_headers_for_embed};
#[test]
fn should_require_authorization() {
// given
let server = serve_with_auth("test", "test");
// when
let response = request(server,
"\
GET / HTTP/1.1\r\n\
Host: 127.0.0.1:8080\r\n\
Connection: close\r\n\
\r\n\
"
);
// then
assert_eq!(response.status, "HTTP/1.1 401 Unauthorized".to_owned());
assert_eq!(response.headers.get(0).unwrap(), "WWW-Authenticate: Basic realm=\"Parity\"");
}
#[test]
fn should_reject_on_invalid_auth() {
// given
let server = serve_with_auth("test", "test");
// when
let response = request(server,
"\
GET / HTTP/1.1\r\n\
Host: 127.0.0.1:8080\r\n\
Connection: close\r\n\
Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l\r\n
\r\n\
"
);
// then
assert_eq!(response.status, "HTTP/1.1 401 Unauthorized".to_owned());
assert!(response.body.contains("Unauthorized"), response.body);
assert_eq!(response.headers_raw.contains("WWW-Authenticate"), false);
}
#[test]
fn should_allow_on_valid_auth() {
// given
let server = serve_with_auth("Aladdin", "OpenSesame");
// when
let response = request(server,
"\
GET /ui/ HTTP/1.1\r\n\
Host: 127.0.0.1:8080\r\n\
Connection: close\r\n\
Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l\r\n
\r\n\
"
);
// then
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
assert_security_headers_for_embed(&response.headers);
}

View File

@ -16,18 +16,20 @@
use std::env;
use std::str;
use std::ops::Deref;
use std::net::SocketAddr;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use env_logger::LogBuilder;
use ethcore_rpc::Metadata;
use jsonrpc_core::MetaIoHandler;
use jsonrpc_core::IoHandler;
use jsonrpc_http_server::{self as http, Host, DomainsValidation};
use ServerBuilder;
use Server;
use fetch::Fetch;
use devtools::http_client;
use hash_fetch::urlhint::ContractClient;
use fetch::{Fetch, Client as FetchClient};
use parity_reactor::{EventLoop, Remote};
use {Middleware, SyncStatus, WebProxyTokens};
mod registrar;
mod fetch;
@ -50,7 +52,7 @@ pub struct ServerLoop {
pub event_loop: EventLoop,
}
impl Deref for ServerLoop {
impl ::std::ops::Deref for ServerLoop {
type Target = Server;
fn deref(&self) -> &Self::Target {
@ -58,7 +60,7 @@ impl Deref for ServerLoop {
}
}
pub fn init_server<F, B>(process: F, io: MetaIoHandler<Metadata>, remote: Remote) -> (ServerLoop, Arc<FakeRegistrar>) where
pub fn init_server<F, B>(process: F, io: IoHandler, remote: Remote) -> (ServerLoop, Arc<FakeRegistrar>) where
F: FnOnce(ServerBuilder) -> ServerBuilder<B>,
B: Fetch,
{
@ -74,33 +76,15 @@ pub fn init_server<F, B>(process: F, io: MetaIoHandler<Metadata>, remote: Remote
&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, event_loop.raw_remote()).unwrap();
.start_unsecured_http(&"127.0.0.1:0".parse().unwrap(), io).unwrap();
(
ServerLoop { server: server, event_loop: event_loop },
registrar,
)
}
pub fn serve_with_auth(user: &str, pass: &str) -> ServerLoop {
init_logger();
let registrar = Arc::new(FakeRegistrar::new());
let mut dapps_path = env::temp_dir();
dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading");
let event_loop = EventLoop::spawn();
let io = MetaIoHandler::default();
let server = ServerBuilder::new(&dapps_path, registrar, event_loop.remote())
.signer_address(Some(("127.0.0.1".into(), SIGNER_PORT)))
.allowed_hosts(None.into())
.start_basic_auth_http(&"127.0.0.1:0".parse().unwrap(), user, pass, io, event_loop.raw_remote()).unwrap();
ServerLoop {
server: server,
event_loop: event_loop,
}
}
pub fn serve_with_rpc(io: MetaIoHandler<Metadata>) -> ServerLoop {
init_server(|builder| builder.allowed_hosts(None.into()), io, Remote::new_sync()).0
pub fn serve_with_rpc(io: IoHandler) -> ServerLoop {
init_server(|builder| builder, io, Remote::new_sync()).0
}
pub fn serve_hosts(hosts: Option<Vec<String>>) -> ServerLoop {
@ -108,20 +92,13 @@ pub fn serve_hosts(hosts: Option<Vec<String>>) -> ServerLoop {
init_server(|builder| builder.allowed_hosts(hosts.into()), Default::default(), Remote::new_sync()).0
}
pub fn serve_extra_cors(extra_cors: Option<Vec<String>>) -> ServerLoop {
let extra_cors = extra_cors.map(|cors| cors.into_iter().map(Into::into).collect());
init_server(|builder| builder.allowed_hosts(None.into()).extra_cors_headers(extra_cors.into()), Default::default(), Remote::new_sync()).0
}
pub fn serve_with_registrar() -> (ServerLoop, Arc<FakeRegistrar>) {
init_server(|builder| builder.allowed_hosts(None.into()), Default::default(), Remote::new_sync())
init_server(|builder| builder, Default::default(), Remote::new_sync())
}
pub fn serve_with_registrar_and_sync() -> (ServerLoop, Arc<FakeRegistrar>) {
init_server(|builder| {
builder
.sync_status(Arc::new(|| true))
.allowed_hosts(None.into())
builder.sync_status(Arc::new(|| true))
}, Default::default(), Remote::new_sync())
}
@ -133,7 +110,7 @@ pub fn serve_with_registrar_and_fetch_and_threads(multi_threaded: bool) -> (Serv
let fetch = FakeFetch::default();
let f = fetch.clone();
let (server, reg) = init_server(move |builder| {
builder.allowed_hosts(None.into()).fetch(f.clone())
builder.fetch(f.clone())
}, Default::default(), if multi_threaded { Remote::new_thread_per_future() } else { Remote::new_sync() });
(server, fetch, reg)
@ -144,7 +121,6 @@ pub fn serve_with_fetch(web_token: &'static str) -> (ServerLoop, FakeFetch) {
let f = fetch.clone();
let (server, _) = init_server(move |builder| {
builder
.allowed_hosts(None.into())
.fetch(f.clone())
.web_proxy_tokens(Arc::new(move |token| &token == web_token))
}, Default::default(), Remote::new_sync());
@ -153,7 +129,7 @@ pub fn serve_with_fetch(web_token: &'static str) -> (ServerLoop, FakeFetch) {
}
pub fn serve() -> ServerLoop {
init_server(|builder| builder.allowed_hosts(None.into()), Default::default(), Remote::new_sync()).0
init_server(|builder| builder, Default::default(), Remote::new_sync()).0
}
pub fn request(server: ServerLoop, request: &str) -> http_client::Response {
@ -166,3 +142,157 @@ pub fn assert_security_headers(headers: &[String]) {
pub fn assert_security_headers_for_embed(headers: &[String]) {
http_client::assert_security_headers_present(headers, Some(SIGNER_PORT))
}
/// Webapps HTTP+RPC server build.
pub struct ServerBuilder<T: Fetch = FetchClient> {
dapps_path: PathBuf,
registrar: Arc<ContractClient>,
sync_status: Arc<SyncStatus>,
web_proxy_tokens: Arc<WebProxyTokens>,
signer_address: Option<(String, u16)>,
allowed_hosts: DomainsValidation<Host>,
remote: Remote,
fetch: Option<T>,
}
impl ServerBuilder {
/// Construct new dapps server
pub fn new<P: AsRef<Path>>(dapps_path: P, registrar: Arc<ContractClient>, remote: Remote) -> Self {
ServerBuilder {
dapps_path: dapps_path.as_ref().to_owned(),
registrar: registrar,
sync_status: Arc::new(|| false),
web_proxy_tokens: Arc::new(|_| false),
signer_address: None,
allowed_hosts: DomainsValidation::Disabled,
remote: remote,
fetch: None,
}
}
}
impl<T: Fetch> ServerBuilder<T> {
/// Set a fetch client to use.
pub fn fetch<X: Fetch>(self, fetch: X) -> ServerBuilder<X> {
ServerBuilder {
dapps_path: self.dapps_path,
registrar: self.registrar,
sync_status: self.sync_status,
web_proxy_tokens: self.web_proxy_tokens,
signer_address: self.signer_address,
allowed_hosts: self.allowed_hosts,
remote: self.remote,
fetch: Some(fetch),
}
}
/// Change default sync status.
pub fn sync_status(mut self, status: Arc<SyncStatus>) -> Self {
self.sync_status = status;
self
}
/// Change default web proxy tokens validator.
pub fn web_proxy_tokens(mut self, tokens: Arc<WebProxyTokens>) -> Self {
self.web_proxy_tokens = tokens;
self
}
/// Change default signer port.
pub fn signer_address(mut self, signer_address: Option<(String, u16)>) -> Self {
self.signer_address = signer_address;
self
}
/// Change allowed hosts.
/// `None` - All hosts are allowed
/// `Some(whitelist)` - Allow only whitelisted hosts (+ listen address)
pub fn allowed_hosts(mut self, allowed_hosts: DomainsValidation<Host>) -> Self {
self.allowed_hosts = allowed_hosts;
self
}
/// Asynchronously start server with no authentication,
/// returns result with `Server` handle on success or an error.
pub fn start_unsecured_http(self, addr: &SocketAddr, io: IoHandler) -> Result<Server, http::Error> {
let fetch = self.fetch_client();
Server::start_http(
addr,
io,
self.allowed_hosts,
self.signer_address,
self.dapps_path,
vec![],
self.registrar,
self.sync_status,
self.web_proxy_tokens,
self.remote,
fetch,
)
}
fn fetch_client(&self) -> T {
match self.fetch.clone() {
Some(fetch) => fetch,
None => T::new().unwrap(),
}
}
}
/// Webapps HTTP server.
pub struct Server {
server: Option<http::Server>,
}
impl Server {
fn start_http<F: Fetch>(
addr: &SocketAddr,
io: IoHandler,
allowed_hosts: DomainsValidation<Host>,
signer_address: Option<(String, u16)>,
dapps_path: PathBuf,
extra_dapps: Vec<PathBuf>,
registrar: Arc<ContractClient>,
sync_status: Arc<SyncStatus>,
web_proxy_tokens: Arc<WebProxyTokens>,
remote: Remote,
fetch: F,
) -> Result<Server, http::Error> {
let middleware = Middleware::new(
remote,
signer_address,
dapps_path,
extra_dapps,
registrar,
sync_status,
web_proxy_tokens,
fetch,
);
http::ServerBuilder::new(io)
.request_middleware(middleware)
.allowed_hosts(allowed_hosts)
.cors(http::DomainsValidation::Disabled)
.start_http(addr)
.map(|server| Server {
server: Some(server),
})
}
/// Returns address that this server is bound to.
pub fn addr(&self) -> &SocketAddr {
self.server.as_ref()
.expect("server is always Some at the start; it's consumed only when object is dropped; qed")
.addrs()
.first()
.expect("You cannot start the server without binding to at least one address; qed")
}
}
impl Drop for Server {
fn drop(&mut self) {
self.server.take().unwrap().close()
}
}

View File

@ -19,7 +19,6 @@
mod helpers;
mod api;
mod authorization;
mod fetch;
mod redirection;
mod rpc;

View File

@ -14,16 +14,14 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use futures::{future, Future};
use ethcore_rpc::{Metadata, Origin};
use jsonrpc_core::{MetaIoHandler, Value};
use jsonrpc_core::{IoHandler, Value};
use tests::helpers::{serve_with_rpc, request};
#[test]
fn should_serve_rpc() {
// given
let mut io = MetaIoHandler::default();
let mut io = IoHandler::default();
io.add_method("rpc_test", |_| {
Ok(Value::String("Hello World!".into()))
});
@ -49,70 +47,3 @@ fn should_serve_rpc() {
response.assert_status("HTTP/1.1 200 OK");
assert_eq!(response.body, "31\n{\"jsonrpc\":\"2.0\",\"result\":\"Hello World!\",\"id\":1}\n\n0\n\n".to_owned());
}
#[test]
fn should_extract_metadata() {
// given
let mut io = MetaIoHandler::default();
io.add_method_with_meta("rpc_test", |_params, meta: Metadata| {
assert_eq!(meta.origin, Origin::Dapps("".into()));
assert_eq!(meta.dapp_id(), "".into());
future::ok(Value::String("Hello World!".into())).boxed()
});
let server = serve_with_rpc(io);
// when
let req = r#"{"jsonrpc":"2.0","id":1,"method":"rpc_test","params":[]}"#;
let response = request(server, &format!(
"\
POST /rpc/ HTTP/1.1\r\n\
Host: 127.0.0.1:8080\r\n\
Connection: close\r\n\
X-Parity-Origin: https://this.should.be.ignored\r\n\
Content-Type: application/json\r\n\
Content-Length: {}\r\n\
\r\n\
{}\r\n\
",
req.as_bytes().len(),
req,
));
// then
response.assert_status("HTTP/1.1 200 OK");
assert_eq!(response.body, "31\n{\"jsonrpc\":\"2.0\",\"result\":\"Hello World!\",\"id\":1}\n\n0\n\n".to_owned());
}
#[test]
fn should_extract_metadata_from_custom_header() {
// given
let mut io = MetaIoHandler::default();
io.add_method_with_meta("rpc_test", |_params, meta: Metadata| {
assert_eq!(meta.origin, Origin::Dapps("https://parity.io/".into()));
assert_eq!(meta.dapp_id(), "https://parity.io/".into());
future::ok(Value::String("Hello World!".into())).boxed()
});
let server = serve_with_rpc(io);
// when
let req = r#"{"jsonrpc":"2.0","id":1,"method":"rpc_test","params":[]}"#;
let response = request(server, &format!(
"\
POST /rpc/ HTTP/1.1\r\n\
Host: 127.0.0.1:8080\r\n\
Connection: close\r\n\
Origin: null\r\n\
X-Parity-Origin: https://parity.io/\r\n\
Content-Type: application/json\r\n\
Content-Length: {}\r\n\
\r\n\
{}\r\n\
",
req.as_bytes().len(),
req,
));
// then
response.assert_status("HTTP/1.1 200 OK");
assert_eq!(response.body, "31\n{\"jsonrpc\":\"2.0\",\"result\":\"Hello World!\",\"id\":1}\n\n0\n\n".to_owned());
}

View File

@ -34,7 +34,7 @@ fn should_reject_invalid_host() {
// then
assert_eq!(response.status, "HTTP/1.1 403 Forbidden".to_owned());
assert!(response.body.contains("Current Host Is Disallowed"), response.body);
assert!(response.body.contains("Provided Host header is not whitelisted."), response.body);
}
#[test]
@ -97,31 +97,3 @@ fn should_allow_parity_utils_even_on_invalid_domain() {
// then
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
}
#[test]
fn should_not_return_cors_headers_for_rpc() {
// given
let server = serve_hosts(Some(vec!["localhost:8080".into()]));
// when
let response = request(server,
"\
POST /rpc HTTP/1.1\r\n\
Host: localhost:8080\r\n\
Origin: null\r\n\
Content-Type: application/json\r\n\
Connection: close\r\n\
\r\n\
{}
"
);
// then
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
assert!(
!response.headers_raw.contains("Access-Control-Allow-Origin"),
"CORS headers were not expected: {:?}",
response.headers
);
}

View File

@ -92,12 +92,13 @@ pub enum URLHintResult {
}
/// URLHint Contract interface
pub trait URLHint {
pub trait URLHint: Send + Sync {
/// Resolves given id to registrar entry.
fn resolve(&self, id: Bytes) -> Option<URLHintResult>;
}
/// `URLHintContract` API
#[derive(Clone)]
pub struct URLHintContract {
urlhint: Contract,
registrar: Contract,

View File

@ -32,12 +32,13 @@ use std::sync::Arc;
use std::net::{SocketAddr, IpAddr};
use error::ServerError;
use route::Out;
use http::hyper::server::{Listening, Handler, Request, Response};
use http::hyper::server::{Handler, Request, Response};
use http::hyper::net::HttpStream;
use http::hyper::header::{self, Vary, ContentLength, ContentType};
use http::hyper::{Next, Encoder, Decoder, Method, RequestUri, StatusCode};
use ethcore::client::BlockChainClient;
pub use http::hyper::server::Listening;
pub use http::{AccessControlAllowOrigin, Host, DomainsValidation};
/// Request/response handler

View File

@ -164,6 +164,8 @@ usage! {
or |c: &Config| otry!(c.rpc).apis.as_ref().map(|vec| vec.join(",")),
flag_jsonrpc_hosts: String = "none",
or |c: &Config| otry!(c.rpc).hosts.as_ref().map(|vec| vec.join(",")),
flag_jsonrpc_threads: Option<usize> = None,
or |c: &Config| otry!(c.rpc).threads.map(Some),
// IPC
flag_no_ipc: bool = false,
@ -176,21 +178,8 @@ usage! {
// DAPPS
flag_no_dapps: bool = false,
or |c: &Config| otry!(c.dapps).disable.clone(),
flag_dapps_port: u16 = 8080u16,
or |c: &Config| otry!(c.dapps).port.clone(),
flag_dapps_interface: String = "local",
or |c: &Config| otry!(c.dapps).interface.clone(),
flag_dapps_hosts: String = "none",
or |c: &Config| otry!(c.dapps).hosts.as_ref().map(|vec| vec.join(",")),
flag_dapps_cors: Option<String> = None,
or |c: &Config| otry!(c.dapps).cors.clone().map(Some),
flag_dapps_path: String = "$BASE/dapps",
or |c: &Config| otry!(c.dapps).path.clone(),
flag_dapps_user: Option<String> = None,
or |c: &Config| otry!(c.dapps).user.clone().map(Some),
flag_dapps_pass: Option<String> = None,
or |c: &Config| otry!(c.dapps).pass.clone().map(Some),
flag_dapps_apis_all: bool = false, or |_| None,
// Secret Store
flag_no_secretstore: bool = false,
@ -330,6 +319,22 @@ usage! {
or |c: &Config| otry!(c.misc).log_file.clone().map(Some),
flag_no_color: bool = false,
or |c: &Config| otry!(c.misc).color.map(|c| !c).clone(),
// -- Legacy Options supported in configs
flag_dapps_port: Option<u16> = None,
or |c: &Config| otry!(c.dapps).port.clone().map(Some),
flag_dapps_interface: Option<String> = None,
or |c: &Config| otry!(c.dapps).interface.clone().map(Some),
flag_dapps_hosts: Option<String> = None,
or |c: &Config| otry!(c.dapps).hosts.as_ref().map(|vec| Some(vec.join(","))),
flag_dapps_cors: Option<String> = None,
or |c: &Config| otry!(c.dapps).cors.clone().map(Some),
flag_dapps_user: Option<String> = None,
or |c: &Config| otry!(c.dapps).user.clone().map(Some),
flag_dapps_pass: Option<String> = None,
or |c: &Config| otry!(c.dapps).pass.clone().map(Some),
flag_dapps_apis_all: Option<bool> = None, or |_| None,
}
{
// Values with optional default value.
@ -419,6 +424,7 @@ struct Rpc {
cors: Option<String>,
apis: Option<Vec<String>>,
hosts: Option<Vec<String>>,
threads: Option<usize>,
}
#[derive(Default, Debug, PartialEq, RustcDecodable)]
@ -672,6 +678,7 @@ mod tests {
flag_jsonrpc_cors: Some("null".into()),
flag_jsonrpc_apis: "web3,eth,net,parity,traces,rpc".into(),
flag_jsonrpc_hosts: "none".into(),
flag_jsonrpc_threads: None,
// IPC
flag_no_ipc: false,
@ -679,15 +686,8 @@ mod tests {
flag_ipc_apis: "web3,eth,net,parity,parity_accounts,personal,traces,rpc".into(),
// DAPPS
flag_no_dapps: false,
flag_dapps_port: 8080u16,
flag_dapps_interface: "local".into(),
flag_dapps_hosts: "none".into(),
flag_dapps_cors: None,
flag_dapps_path: "$HOME/.parity/dapps".into(),
flag_dapps_user: Some("test_user".into()),
flag_dapps_pass: Some("test_pass".into()),
flag_dapps_apis_all: false,
flag_no_dapps: false,
flag_no_secretstore: false,
flag_secretstore_port: 8082u16,
@ -792,6 +792,14 @@ mod tests {
flag_extradata: None,
flag_cache: None,
flag_warp: Some(true),
// Legacy-Dapps
flag_dapps_port: Some(8080),
flag_dapps_interface: Some("local".into()),
flag_dapps_hosts: Some("none".into()),
flag_dapps_cors: None,
flag_dapps_user: Some("test_user".into()),
flag_dapps_pass: Some("test_pass".into()),
flag_dapps_apis_all: None,
// -- Miscellaneous Options
flag_version: false,
@ -873,6 +881,7 @@ mod tests {
cors: None,
apis: None,
hosts: None,
threads: None,
}),
ipc: Some(Ipc {
disable: None,

View File

@ -149,6 +149,8 @@ API and Console Options:
is additional security against some attack
vectors. Special options: "all", "none",
(default: {flag_jsonrpc_hosts}).
--jsonrpc-threads THREADS Enables experimental faster implementation of JSON-RPC server.
Requires Dapps server to be disabled using --no-dapps. (default: {flag_jsonrpc_threads:?})
--no-ipc Disable JSON-RPC over IPC service. (default: {flag_no_ipc})
--ipc-path PATH Specify custom path for JSON-RPC over IPC service
@ -157,29 +159,8 @@ API and Console Options:
IPC (default: {flag_ipc_apis}).
--no-dapps Disable the Dapps server (e.g. status page). (default: {flag_no_dapps})
--dapps-port PORT Specify the port portion of the Dapps server
(default: {flag_dapps_port}).
--dapps-interface IP Specify the hostname portion of the Dapps
server, IP should be an interface's IP address,
or local (default: {flag_dapps_interface}).
--dapps-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_dapps_hosts}).
--dapps-cors URL Specify CORS headers for Dapps server APIs.
(default: {flag_dapps_cors:?})
--dapps-user USERNAME Specify username for Dapps server. It will be
used in HTTP Basic Authentication Scheme.
If --dapps-pass is not specified you will be
asked for password on startup. (default: {flag_dapps_user:?})
--dapps-pass PASSWORD Specify password for Dapps server. Use only in
conjunction with --dapps-user. (default: {flag_dapps_pass:?})
--dapps-path PATH Specify directory where dapps should be installed.
(default: {flag_dapps_path})
--dapps-apis-all Expose all possible RPC APIs on Dapps port.
WARNING: INSECURE. Used only for development.
(default: {flag_dapps_apis_all})
--ipfs-api Enable IPFS-compatible HTTP API. (default: {flag_ipfs_api})
--ipfs-api-port PORT Configure on which port the IPFS HTTP API should listen.
(default: {flag_ipfs_api_port})
@ -392,6 +373,13 @@ Legacy Options:
--jsonrpc-off Equivalent to --no-jsonrpc.
-w --webapp Does nothing; dapps server is on by default now.
--dapps-off Equivalent to --no-dapps.
--dapps-user USERNAME Dapps server authentication has been removed. (default: {flag_dapps_user:?})
--dapps-pass PASSWORD Dapps server authentication has been removed. (default: {flag_dapps_pass:?})
--dapps-apis-all Dapps server is merged with RPC server. Use --jsonrpc-apis. (default: {flag_dapps_apis_all:?})
--dapps-cors URL Dapps server is merged with RPC server. Use --jsonrpc-cors. (default: {flag_dapps_cors:?})
--dapps-hosts HOSTS Dapps server is merged with RPC server. Use --jsonrpc-hosts. (default: {flag_dapps_hosts:?})
--dapps-interface IP Dapps server is merged with RPC server. Use --jsonrpc-interface. (default: {flag_dapps_interface:?})
--dapps-port PORT Dapps server is merged with RPC server. Use --jsonrpc-port. (default: {flag_dapps_port:?})
--rpc Does nothing; JSON-RPC is on by default now.
--warp Does nothing; Warp sync is on by default. (default: {flag_warp})
--rpcaddr IP Equivalent to --jsonrpc-interface IP.

View File

@ -132,12 +132,17 @@ impl Configuration {
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 dapps_conf = self.dapps_config();
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()?;
if self.args.flag_jsonrpc_threads.is_some() && dapps_conf.enabled {
dapps_conf.enabled = false;
writeln!(&mut stderr(), "Warning: Disabling Dapps server because fast RPC server was enabled.").expect("Error writing to stderr.")
}
let cmd = if self.args.flag_version {
Cmd::Version
} else if self.args.cmd_signer {
@ -554,19 +559,12 @@ impl Configuration {
fn dapps_config(&self) -> DappsConfiguration {
DappsConfiguration {
enabled: self.dapps_enabled(),
interface: self.dapps_interface(),
port: self.args.flag_dapps_port,
hosts: self.dapps_hosts(),
cors: self.dapps_cors(),
user: self.args.flag_dapps_user.clone(),
pass: self.args.flag_dapps_pass.clone(),
dapps_path: PathBuf::from(self.directories().dapps),
extra_dapps: if self.args.cmd_dapp {
self.args.arg_path.iter().map(|path| PathBuf::from(path)).collect()
} else {
vec![]
},
all_apis: self.args.flag_dapps_apis_all,
}
}
@ -746,14 +744,10 @@ impl Configuration {
Self::cors(self.args.flag_ipfs_api_cors.as_ref())
}
fn dapps_cors(&self) -> Option<Vec<String>> {
Self::cors(self.args.flag_dapps_cors.as_ref())
}
fn hosts(hosts: &str) -> Option<Vec<String>> {
match hosts {
"none" => return Some(Vec::new()),
"all" => return None,
"*" | "all" | "any" => return None,
_ => {}
}
let hosts = hosts.split(',').map(Into::into).collect();
@ -764,10 +758,6 @@ impl Configuration {
Self::hosts(&self.args.flag_jsonrpc_hosts)
}
fn dapps_hosts(&self) -> Option<Vec<String>> {
Self::hosts(&self.args.flag_dapps_hosts)
}
fn ipfs_hosts(&self) -> Option<Vec<String>> {
Self::hosts(&self.args.flag_ipfs_api_hosts)
}
@ -793,12 +783,17 @@ impl Configuration {
fn http_config(&self) -> Result<HttpConfiguration, String> {
let conf = HttpConfiguration {
enabled: !self.args.flag_jsonrpc_off && !self.args.flag_no_jsonrpc,
enabled: self.rpc_enabled(),
interface: self.rpc_interface(),
port: self.args.flag_rpcport.unwrap_or(self.args.flag_jsonrpc_port),
apis: self.rpc_apis().parse()?,
hosts: self.rpc_hosts(),
cors: self.rpc_cors(),
threads: match self.args.flag_jsonrpc_threads {
Some(threads) if threads > 0 => Some(threads),
None => None,
_ => return Err("--jsonrpc-threads number needs to be positive.".into()),
}
};
Ok(conf)
@ -809,7 +804,7 @@ impl Configuration {
name: self.args.flag_identity.clone(),
chain: self.chain(),
network_port: self.args.flag_port,
rpc_enabled: !self.args.flag_jsonrpc_off && !self.args.flag_no_jsonrpc,
rpc_enabled: self.rpc_enabled(),
rpc_interface: self.args.flag_rpcaddr.clone().unwrap_or(self.args.flag_jsonrpc_interface.clone()),
rpc_port: self.args.flag_rpcport.unwrap_or(self.args.flag_jsonrpc_port),
}
@ -916,13 +911,6 @@ impl Configuration {
Self::interface(&self.network_settings().rpc_interface)
}
fn dapps_interface(&self) -> String {
match self.args.flag_dapps_interface.as_str() {
"local" => "127.0.0.1",
x => x,
}.into()
}
fn ipfs_interface(&self) -> String {
Self::interface(&self.args.flag_ipfs_api_interface)
}
@ -938,8 +926,12 @@ impl Configuration {
Self::interface(&self.args.flag_stratum_interface)
}
fn rpc_enabled(&self) -> bool {
!self.args.flag_jsonrpc_off && !self.args.flag_no_jsonrpc
}
fn dapps_enabled(&self) -> bool {
!self.args.flag_dapps_off && !self.args.flag_no_dapps && cfg!(feature = "dapps")
!self.args.flag_dapps_off && !self.args.flag_no_dapps && self.rpc_enabled() && cfg!(feature = "dapps")
}
fn secretstore_enabled(&self) -> bool {
@ -1317,23 +1309,6 @@ mod tests {
assert_eq!(conf3.rpc_hosts(), Some(vec!["ethcore.io".into(), "something.io".into()]));
}
#[test]
fn should_parse_dapps_hosts() {
// given
// when
let conf0 = parse(&["parity"]);
let conf1 = parse(&["parity", "--dapps-hosts", "none"]);
let conf2 = parse(&["parity", "--dapps-hosts", "all"]);
let conf3 = parse(&["parity", "--dapps-hosts", "ethcore.io,something.io"]);
// then
assert_eq!(conf0.dapps_hosts(), Some(Vec::new()));
assert_eq!(conf1.dapps_hosts(), Some(Vec::new()));
assert_eq!(conf2.dapps_hosts(), None);
assert_eq!(conf3.dapps_hosts(), Some(vec!["ethcore.io".into(), "something.io".into()]));
}
#[test]
fn should_parse_ipfs_hosts() {
// given

View File

@ -19,25 +19,17 @@ use std::sync::Arc;
use dir::default_data_path;
use ethcore::client::Client;
use ethcore_rpc::informant::RpcStats;
use ethsync::SyncProvider;
use hash_fetch::fetch::Client as FetchClient;
use helpers::replace_home;
use rpc_apis::{self, SignerService};
use rpc_apis::SignerService;
use parity_reactor;
#[derive(Debug, PartialEq, Clone)]
pub struct Configuration {
pub enabled: bool,
pub interface: String,
pub port: u16,
pub hosts: Option<Vec<String>>,
pub cors: Option<Vec<String>>,
pub user: Option<String>,
pub pass: Option<String>,
pub dapps_path: PathBuf,
pub extra_dapps: Vec<PathBuf>,
pub all_apis: bool,
}
impl Default for Configuration {
@ -45,80 +37,56 @@ impl Default for Configuration {
let data_dir = default_data_path();
Configuration {
enabled: true,
interface: "127.0.0.1".into(),
port: 8080,
hosts: Some(Vec::new()),
cors: None,
user: None,
pass: None,
dapps_path: replace_home(&data_dir, "$BASE/dapps").into(),
extra_dapps: vec![],
all_apis: false,
}
}
}
pub struct Dependencies {
pub apis: Arc<rpc_apis::Dependencies>,
pub client: Arc<Client>,
pub sync: Arc<SyncProvider>,
pub remote: parity_reactor::TokioRemote,
pub fetch: FetchClient,
pub signer: Arc<SignerService>,
pub stats: Arc<RpcStats>,
}
pub fn new(configuration: Configuration, deps: Dependencies) -> Result<Option<WebappServer>, String> {
pub fn new(configuration: Configuration, deps: Dependencies) -> Result<Option<Middleware>, String> {
if !configuration.enabled {
return Ok(None);
}
let url = format!("{}:{}", configuration.interface, configuration.port);
let addr = url.parse().map_err(|_| format!("Invalid Webapps listen host/port given: {}", url))?;
let auth = configuration.user.as_ref().map(|username| {
let password = configuration.pass.as_ref().map_or_else(|| {
use rpassword::read_password;
println!("Type password for WebApps server (user: {}): ", username);
let pass = read_password().unwrap();
println!("OK, got it. Starting server...");
pass
}, |pass| pass.to_owned());
(username.to_owned(), password)
});
Ok(Some(setup_dapps_server(
dapps_middleware(
deps,
configuration.dapps_path,
configuration.extra_dapps,
&addr,
configuration.hosts,
configuration.cors,
auth,
configuration.all_apis,
)?))
).map(Some)
}
pub use self::server::WebappServer;
pub use self::server::setup_dapps_server;
pub use self::server::Middleware;
pub use self::server::dapps_middleware;
#[cfg(not(feature = "dapps"))]
mod server {
use super::Dependencies;
use std::net::SocketAddr;
use std::path::PathBuf;
use ethcore_rpc::{hyper, RequestMiddleware, RequestMiddlewareAction};
pub struct WebappServer;
pub fn setup_dapps_server(
pub struct Middleware;
impl RequestMiddleware for Middleware {
fn on_request(
&self, req: &hyper::server::Request<hyper::net::HttpStream>, control: &hyper::Control
) -> RequestMiddlewareAction {
unreachable!()
}
}
pub fn dapps_middleware(
_deps: Dependencies,
_dapps_path: PathBuf,
_extra_dapps: Vec<PathBuf>,
_url: &SocketAddr,
_allowed_hosts: Option<Vec<String>>,
_cors: Option<Vec<String>>,
_auth: Option<(String, String)>,
_all_apis: bool,
) -> Result<WebappServer, String> {
) -> Result<Middleware, String> {
Err("Your Parity version has been compiled without WebApps support.".into())
}
}
@ -128,78 +96,41 @@ mod server {
use super::Dependencies;
use std::path::PathBuf;
use std::sync::Arc;
use std::net::SocketAddr;
use std::io;
use util::{Bytes, Address, U256};
use ansi_term::Colour;
use ethcore::transaction::{Transaction, Action};
use ethcore::client::{Client, BlockChainClient, BlockId};
use ethcore_dapps::{AccessControlAllowOrigin, Host};
use ethcore_rpc::is_major_importing;
use hash_fetch::fetch::Client as FetchClient;
use hash_fetch::urlhint::ContractClient;
use parity_dapps;
use parity_reactor;
use rpc_apis;
pub use ethcore_dapps::Server as WebappServer;
pub type Middleware = parity_dapps::Middleware<FetchClient>;
pub fn setup_dapps_server(
pub fn dapps_middleware(
deps: Dependencies,
dapps_path: PathBuf,
extra_dapps: Vec<PathBuf>,
url: &SocketAddr,
allowed_hosts: Option<Vec<String>>,
cors: Option<Vec<String>>,
auth: Option<(String, String)>,
all_apis: bool,
) -> Result<WebappServer, String> {
use ethcore_dapps as dapps;
let server = dapps::ServerBuilder::new(
&dapps_path,
Arc::new(Registrar { client: deps.client.clone() }),
parity_reactor::Remote::new(deps.remote.clone()),
);
let allowed_hosts: Option<Vec<_>> = allowed_hosts.map(|hosts| hosts.into_iter().map(Host::from).collect());
let cors: Option<Vec<_>> = cors.map(|cors| cors.into_iter().map(AccessControlAllowOrigin::from).collect());
let sync = deps.sync.clone();
let client = deps.client.clone();
) -> Result<Middleware, String> {
let sync = deps.sync;
let signer = deps.signer.clone();
let server = server
.fetch(deps.fetch.clone())
.sync_status(Arc::new(move || is_major_importing(Some(sync.status().state), client.queue_info())))
.web_proxy_tokens(Arc::new(move |token| signer.is_valid_web_proxy_access_token(&token)))
.extra_dapps(&extra_dapps)
.signer_address(deps.signer.address())
.allowed_hosts(allowed_hosts.into())
.extra_cors_headers(cors.into());
let client = deps.client;
let parity_remote = parity_reactor::Remote::new(deps.remote.clone());
let registrar = Arc::new(Registrar { client: client.clone() });
let sync_status = Arc::new(move || is_major_importing(Some(sync.status().state), client.queue_info()));
let web_proxy_tokens = Arc::new(move |token| signer.is_valid_web_proxy_access_token(&token));
let api_set = if all_apis {
warn!("{}", Colour::Red.bold().paint("*** INSECURE *** Running Dapps with all APIs exposed."));
info!("If you do not intend this, exit now.");
rpc_apis::ApiSet::SafeContext
} else {
rpc_apis::ApiSet::UnsafeContext
};
let apis = rpc_apis::setup_rpc(deps.stats, deps.apis.clone(), api_set);
let start_result = match auth {
None => {
server.start_unsecured_http(url, apis, deps.remote)
},
Some((username, password)) => {
server.start_basic_auth_http(url, &username, &password, apis, deps.remote)
},
};
match start_result {
Err(dapps::ServerError::IoError(err)) => match err.kind() {
io::ErrorKind::AddrInUse => Err(format!("WebApps address {} is already in use, make sure that another instance of an Ethereum client is not running or change the address using the --dapps-port and --dapps-interface options.", url)),
_ => Err(format!("WebApps io error: {}", err)),
},
Err(e) => Err(format!("WebApps error: {:?}", e)),
Ok(server) => Ok(server),
}
Ok(parity_dapps::Middleware::new(
parity_remote,
deps.signer.address(),
dapps_path,
extra_dapps,
registrar,
sync_status,
web_proxy_tokens,
deps.fetch.clone(),
))
}
struct Registrar {

View File

@ -21,94 +21,89 @@ use cli::Args;
pub enum Deprecated {
DoesNothing(&'static str),
Replaced(&'static str, &'static str),
Removed(&'static str),
}
impl fmt::Display for Deprecated {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
Deprecated::DoesNothing(s) => write!(f, "Option '{}' does nothing. It's on by default", s),
Deprecated::Replaced(old, new) => write!(f, "Option '{}' is deprecated. Please use '{}' instead", old, new),
Deprecated::DoesNothing(s) => write!(f, "Option '{}' does nothing. It's on by default.", s),
Deprecated::Replaced(old, new) => write!(f, "Option '{}' is deprecated. Please use '{}' instead.", old, new),
Deprecated::Removed(s) => write!(f, "Option '{}' has been removed and is no longer supported.", s)
}
}
}
impl Deprecated {
fn jsonrpc() -> Self {
Deprecated::DoesNothing("--jsonrpc")
}
fn rpc() -> Self {
Deprecated::DoesNothing("--rpc")
}
fn jsonrpc_off() -> Self {
Deprecated::Replaced("--jsonrpc-off", "--no-jsonrpc")
}
fn webapp() -> Self {
Deprecated::DoesNothing("--webapp")
}
fn dapps_off() -> Self {
Deprecated::Replaced("--dapps-off", "--no-dapps")
}
fn ipcdisable() -> Self {
Deprecated::Replaced("--ipcdisable", "--no-ipc")
}
fn ipc_off() -> Self {
Deprecated::Replaced("--ipc-off", "--no-ipc")
}
fn etherbase() -> Self {
Deprecated::Replaced("--etherbase", "--author")
}
fn extradata() -> Self {
Deprecated::Replaced("--extradata", "--extra-data")
}
}
pub fn find_deprecated(args: &Args) -> Vec<Deprecated> {
let mut result = vec![];
if args.flag_jsonrpc {
result.push(Deprecated::jsonrpc());
result.push(Deprecated::DoesNothing("--jsonrpc"));
}
if args.flag_rpc {
result.push(Deprecated::rpc());
result.push(Deprecated::DoesNothing("--rpc"));
}
if args.flag_jsonrpc_off {
result.push(Deprecated::jsonrpc_off());
result.push(Deprecated::Replaced("--jsonrpc-off", "--no-jsonrpc"));
}
if args.flag_webapp {
result.push(Deprecated::webapp())
result.push(Deprecated::DoesNothing("--webapp"));
}
if args.flag_dapps_off {
result.push(Deprecated::dapps_off());
result.push(Deprecated::Replaced("--dapps-off", "--no-dapps"));
}
if args.flag_ipcdisable {
result.push(Deprecated::ipcdisable());
result.push(Deprecated::Replaced("--ipcdisable", "--no-ipc"));
}
if args.flag_ipc_off {
result.push(Deprecated::ipc_off());
result.push(Deprecated::Replaced("--ipc-off", "--no-ipc"));
}
if args.flag_etherbase.is_some() {
result.push(Deprecated::etherbase());
result.push(Deprecated::Replaced("--etherbase", "--author"));
}
if args.flag_extradata.is_some() {
result.push(Deprecated::extradata());
result.push(Deprecated::Replaced("--extradata", "--extra-data"));
}
// Removed in 1.7
if args.flag_dapps_port.is_some() {
result.push(Deprecated::Replaced("--dapps-port", "--jsonrpc-port"));
}
if args.flag_dapps_interface.is_some() {
result.push(Deprecated::Replaced("--dapps-interface", "--jsonrpc-interface"));
}
if args.flag_dapps_hosts.is_some() {
result.push(Deprecated::Replaced("--dapps-hosts", "--jsonrpc-hosts"));
}
if args.flag_dapps_cors.is_some() {
result.push(Deprecated::Replaced("--dapps-cors", "--jsonrpc-cors"));
}
if args.flag_dapps_user.is_some() {
result.push(Deprecated::Removed("--dapps-user"));
}
if args.flag_dapps_pass.is_some() {
result.push(Deprecated::Removed("--dapps-pass"));
}
if args.flag_dapps_apis_all.is_some() {
result.push(Deprecated::Replaced("--dapps-apis-all", "--jsonrpc-apis"));
}
// Removed in 1.8
result
}
@ -131,17 +126,31 @@ mod tests {
args.flag_ipc_off = true;
args.flag_etherbase = Some(Default::default());
args.flag_extradata = Some(Default::default());
args.flag_dapps_port = Some(Default::default());
args.flag_dapps_interface = Some(Default::default());
args.flag_dapps_hosts = Some(Default::default());
args.flag_dapps_cors = Some(Default::default());
args.flag_dapps_user = Some(Default::default());
args.flag_dapps_pass = Some(Default::default());
args.flag_dapps_apis_all = Some(Default::default());
args
}), vec![
Deprecated::jsonrpc(),
Deprecated::rpc(),
Deprecated::jsonrpc_off(),
Deprecated::webapp(),
Deprecated::dapps_off(),
Deprecated::ipcdisable(),
Deprecated::ipc_off(),
Deprecated::etherbase(),
Deprecated::extradata(),
Deprecated::DoesNothing("--jsonrpc"),
Deprecated::DoesNothing("--rpc"),
Deprecated::Replaced("--jsonrpc-off", "--no-jsonrpc"),
Deprecated::DoesNothing("--webapp"),
Deprecated::Replaced("--dapps-off", "--no-dapps"),
Deprecated::Replaced("--ipcdisable", "--no-ipc"),
Deprecated::Replaced("--ipc-off", "--no-ipc"),
Deprecated::Replaced("--etherbase", "--author"),
Deprecated::Replaced("--extradata", "--extra-data"),
Deprecated::Replaced("--dapps-port", "--jsonrpc-port"),
Deprecated::Replaced("--dapps-interface", "--jsonrpc-interface"),
Deprecated::Replaced("--dapps-hosts", "--jsonrpc-hosts"),
Deprecated::Replaced("--dapps-cors", "--jsonrpc-cors"),
Deprecated::Removed("--dapps-user"),
Deprecated::Removed("--dapps-pass"),
Deprecated::Replaced("--dapps-apis-all", "--jsonrpc-apis"),
]);
}
}

View File

@ -15,10 +15,9 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc;
use parity_ipfs_api::{self, AccessControlAllowOrigin, Host};
use parity_ipfs_api::{self, AccessControlAllowOrigin, Host, Listening};
use parity_ipfs_api::error::ServerError;
use ethcore::client::BlockChainClient;
use hyper::server::Listening;
#[derive(Debug, PartialEq, Clone)]
pub struct Configuration {

View File

@ -28,7 +28,6 @@ extern crate ctrlc;
extern crate docopt;
extern crate env_logger;
extern crate fdlimit;
extern crate hyper;
extern crate isatty;
extern crate jsonrpc_core;
extern crate num_cpus;
@ -73,7 +72,11 @@ extern crate ethcore_stratum;
extern crate ethcore_secretstore;
#[cfg(feature = "dapps")]
extern crate ethcore_dapps;
extern crate parity_dapps;
#[cfg(test)]
#[macro_use]
extern crate pretty_assertions;
#[cfg(windows)] extern crate ws2_32;
#[cfg(windows)] extern crate winapi;

View File

@ -14,24 +14,21 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::fmt;
use std::{io, fmt};
use std::sync::Arc;
use std::net::SocketAddr;
use std::io;
use dapps;
use dir::default_data_path;
use ethcore_rpc::{self as rpc, HttpServerError, Metadata, Origin, AccessControlAllowOrigin, Host};
use ethcore_rpc::informant::{RpcStats, Middleware};
use ethcore_rpc::{self as rpc, HttpServerError, Metadata, Origin, AccessControlAllowOrigin, Host};
use helpers::parity_ipc_path;
use hyper;
use jsonrpc_core::MetaIoHandler;
use rpc_apis;
use rpc_apis::ApiSet;
use parity_reactor::TokioRemote;
use rpc_apis::{self, ApiSet};
pub use ethcore_rpc::{IpcServer, HttpServer};
pub use ethcore_rpc::{IpcServer, HttpServer, RequestMiddleware};
#[derive(Debug, PartialEq)]
#[derive(Debug, Clone, PartialEq)]
pub struct HttpConfiguration {
pub enabled: bool,
pub interface: String,
@ -39,6 +36,7 @@ pub struct HttpConfiguration {
pub apis: ApiSet,
pub cors: Option<Vec<String>>,
pub hosts: Option<Vec<String>>,
pub threads: Option<usize>,
}
impl Default for HttpConfiguration {
@ -50,6 +48,7 @@ impl Default for HttpConfiguration {
apis: ApiSet::UnsafeContext,
cors: None,
hosts: Some(Vec::new()),
threads: None,
}
}
}
@ -89,13 +88,17 @@ pub struct Dependencies {
}
pub struct RpcExtractor;
impl rpc::HttpMetaExtractor<Metadata> for RpcExtractor {
fn read_metadata(&self, req: &hyper::server::Request<hyper::net::HttpStream>) -> Metadata {
let origin = req.headers().get::<hyper::header::Origin>()
.map(|origin| format!("{}://{}", origin.scheme, origin.host))
.unwrap_or_else(|| "unknown".into());
impl rpc::HttpMetaExtractor for RpcExtractor {
type Metadata = Metadata;
fn read_metadata(&self, origin: String, dapps_origin: Option<String>) -> Metadata {
let mut metadata = Metadata::default();
metadata.origin = Origin::Rpc(origin);
metadata.origin = match (origin.as_str(), dapps_origin) {
("null", Some(dapp)) => Origin::Dapps(dapp.into()),
_ => Origin::Rpc(origin),
};
metadata
}
}
@ -109,52 +112,92 @@ impl rpc::IpcMetaExtractor<Metadata> for RpcExtractor {
}
}
pub fn new_http(conf: HttpConfiguration, deps: &Dependencies) -> Result<Option<HttpServer>, String> {
fn setup_apis(apis: ApiSet, deps: &Dependencies) -> MetaIoHandler<Metadata, Middleware> {
rpc_apis::setup_rpc(deps.stats.clone(), deps.apis.clone(), apis)
}
pub fn new_http(conf: HttpConfiguration, deps: &Dependencies, middleware: Option<dapps::Middleware>) -> Result<Option<HttpServer>, String> {
if !conf.enabled {
return Ok(None);
}
let url = format!("{}:{}", conf.interface, conf.port);
let addr = url.parse().map_err(|_| format!("Invalid JSONRPC listen host/port given: {}", url))?;
Ok(Some(setup_http_rpc_server(deps, &addr, conf.cors, conf.hosts, conf.apis)?))
}
let handler = setup_apis(conf.apis, deps);
let remote = deps.remote.clone();
fn setup_apis(apis: ApiSet, deps: &Dependencies) -> MetaIoHandler<Metadata, Middleware> {
rpc_apis::setup_rpc(deps.stats.clone(), deps.apis.clone(), apis)
}
let cors_domains: Option<Vec<_>> = conf.cors.map(|domains| domains.into_iter().map(AccessControlAllowOrigin::from).collect());
let allowed_hosts: Option<Vec<_>> = conf.hosts.map(|hosts| hosts.into_iter().map(Host::from).collect());
let start_result = rpc::start_http(
&addr,
cors_domains.into(),
allowed_hosts.into(),
handler,
remote,
RpcExtractor,
match (conf.threads, middleware) {
(Some(threads), None) => rpc::HttpSettings::Threads(threads),
(None, middleware) => rpc::HttpSettings::Dapps(middleware),
(Some(_), Some(_)) => {
return Err("Dapps and fast multi-threaded RPC server cannot be enabled at the same time.".into())
},
}
);
pub fn setup_http_rpc_server(
dependencies: &Dependencies,
url: &SocketAddr,
cors_domains: Option<Vec<String>>,
allowed_hosts: Option<Vec<String>>,
apis: ApiSet
) -> Result<HttpServer, String> {
let handler = setup_apis(apis, dependencies);
let remote = dependencies.remote.clone();
let cors_domains: Option<Vec<_>> = cors_domains.map(|domains| domains.into_iter().map(AccessControlAllowOrigin::from).collect());
let allowed_hosts: Option<Vec<_>> = allowed_hosts.map(|hosts| hosts.into_iter().map(Host::from).collect());
let start_result = rpc::start_http(url, cors_domains.into(), allowed_hosts.into(), handler, remote, RpcExtractor);
match start_result {
Err(HttpServerError::IoError(err)) => match err.kind() {
io::ErrorKind::AddrInUse => Err(format!("RPC address {} is already in use, make sure that another instance of an Ethereum client is not running or change the address using the --jsonrpc-port and --jsonrpc-interface options.", url)),
Ok(server) => Ok(Some(server)),
Err(HttpServerError::Io(err)) => match err.kind() {
io::ErrorKind::AddrInUse => Err(
format!("RPC address {} is already in use, make sure that another instance of an Ethereum client is not running or change the address using the --jsonrpc-port and --jsonrpc-interface options.", url)
),
_ => Err(format!("RPC io error: {}", err)),
},
Err(e) => Err(format!("RPC error: {:?}", e)),
Ok(server) => Ok(server),
}
}
pub fn new_ipc(conf: IpcConfiguration, deps: &Dependencies) -> Result<Option<IpcServer>, String> {
if !conf.enabled { return Ok(None); }
Ok(Some(setup_ipc_rpc_server(deps, &conf.socket_addr, conf.apis)?))
pub fn new_ipc(conf: IpcConfiguration, dependencies: &Dependencies) -> Result<Option<IpcServer>, String> {
if !conf.enabled {
return Ok(None);
}
pub fn setup_ipc_rpc_server(dependencies: &Dependencies, addr: &str, apis: ApiSet) -> Result<IpcServer, String> {
let handler = setup_apis(apis, dependencies);
let handler = setup_apis(conf.apis, dependencies);
let remote = dependencies.remote.clone();
match rpc::start_ipc(addr, handler, remote, RpcExtractor) {
match rpc::start_ipc(&conf.socket_addr, handler, remote, RpcExtractor) {
Ok(server) => Ok(Some(server)),
Err(io_error) => Err(format!("RPC io error: {}", io_error)),
Ok(server) => Ok(server)
}
}
#[cfg(test)]
mod tests {
use super::RpcExtractor;
use ethcore_rpc::{HttpMetaExtractor, Origin};
#[test]
fn should_extract_rpc_origin() {
// given
let extractor = RpcExtractor;
// when
let meta = extractor.read_metadata("http://parity.io".into(), None);
let meta1 = extractor.read_metadata("http://parity.io".into(), Some("ignored".into()));
// then
assert_eq!(meta.origin, Origin::Rpc("http://parity.io".into()));
assert_eq!(meta1.origin, Origin::Rpc("http://parity.io".into()));
}
#[test]
fn should_dapps_origin() {
// given
let extractor = RpcExtractor;
let dapp = "https://wallet.ethereum.org".to_owned();
// when
let meta = extractor.read_metadata("null".into(), Some(dapp.clone()));
// then
assert_eq!(meta.origin, Origin::Dapps(dapp.into()));
}
}

View File

@ -81,7 +81,7 @@ impl FromStr for Api {
}
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum ApiSet {
SafeContext,
UnsafeContext,

View File

@ -36,7 +36,6 @@ use updater::{UpdatePolicy, Updater};
use parity_reactor::EventLoop;
use hash_fetch::fetch::{Fetch, Client as FetchClient};
use rpc::{HttpConfiguration, IpcConfiguration};
use params::{
SpecType, Pruning, AccountsConfig, GasPricerConfig, MinerExtras, Switch,
tracing_switch_to_bool, fatdb_switch_to_bool, mode_switch_to_bool
@ -76,8 +75,8 @@ pub struct RunCmd {
pub daemon: Option<String>,
pub logger_config: LogConfig,
pub miner_options: MinerOptions,
pub http_conf: HttpConfiguration,
pub ipc_conf: IpcConfiguration,
pub http_conf: rpc::HttpConfiguration,
pub ipc_conf: rpc::IpcConfiguration,
pub net_conf: NetworkConfiguration,
pub network_id: Option<u64>,
pub warp_sync: bool,
@ -110,11 +109,7 @@ pub struct RunCmd {
pub verifier_settings: VerifierSettings,
}
pub fn open_ui(dapps_conf: &dapps::Configuration, signer_conf: &signer::Configuration) -> Result<(), String> {
if !dapps_conf.enabled {
return Err("Cannot use UI command with Dapps turned off.".into())
}
pub fn open_ui(signer_conf: &signer::Configuration) -> Result<(), String> {
if !signer_conf.enabled {
return Err("Cannot use UI command with UI turned off.".into())
}
@ -127,12 +122,12 @@ pub fn open_ui(dapps_conf: &dapps::Configuration, signer_conf: &signer::Configur
Ok(())
}
pub fn open_dapp(dapps_conf: &dapps::Configuration, dapp: &str) -> Result<(), String> {
pub fn open_dapp(dapps_conf: &dapps::Configuration, rpc_conf: &rpc::HttpConfiguration, dapp: &str) -> Result<(), String> {
if !dapps_conf.enabled {
return Err("Cannot use DAPP command with Dapps turned off.".into())
}
let url = format!("http://{}:{}/{}/", dapps_conf.interface, dapps_conf.port, dapp);
let url = format!("http://{}:{}/{}/", rpc_conf.interface, rpc_conf.port, dapp);
url::open(&url);
Ok(())
}
@ -156,9 +151,9 @@ impl ::local_store::NodeInfo for FullNodeInfo {
pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> Result<(bool, Option<String>), String> {
if cmd.ui && cmd.dapps_conf.enabled {
// Check if Parity is already running
let addr = format!("{}:{}", cmd.dapps_conf.interface, cmd.dapps_conf.port);
let addr = format!("{}:{}", cmd.signer_conf.interface, cmd.signer_conf.port);
if !TcpListener::bind(&addr as &str).is_ok() {
return open_ui(&cmd.dapps_conf, &cmd.signer_conf).map(|_| (false, None));
return open_ui(&cmd.signer_conf).map(|_| (false, None));
}
}
@ -429,11 +424,11 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
updater: updater.clone(),
geth_compatibility: cmd.geth_compatibility,
dapps_interface: match cmd.dapps_conf.enabled {
true => Some(cmd.dapps_conf.interface.clone()),
true => Some(cmd.http_conf.interface.clone()),
false => None,
},
dapps_port: match cmd.dapps_conf.enabled {
true => Some(cmd.dapps_conf.port),
true => Some(cmd.http_conf.port),
false => None,
},
fetch: fetch.clone(),
@ -445,21 +440,19 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
stats: rpc_stats.clone(),
};
// start rpc servers
let http_server = rpc::new_http(cmd.http_conf, &dependencies)?;
let ipc_server = rpc::new_ipc(cmd.ipc_conf, &dependencies)?;
// the dapps server
// the dapps middleware
let dapps_deps = dapps::Dependencies {
apis: deps_for_rpc_apis.clone(),
client: client.clone(),
sync: sync_provider.clone(),
remote: event_loop.raw_remote(),
fetch: fetch.clone(),
signer: deps_for_rpc_apis.signer_service.clone(),
stats: rpc_stats.clone(),
};
let dapps_server = dapps::new(cmd.dapps_conf.clone(), dapps_deps)?;
let dapps_middleware = dapps::new(cmd.dapps_conf.clone(), dapps_deps)?;
// start rpc servers
let http_server = rpc::new_http(cmd.http_conf.clone(), &dependencies, dapps_middleware)?;
let ipc_server = rpc::new_ipc(cmd.ipc_conf, &dependencies)?;
// the signer server
let signer_deps = signer::Dependencies {
@ -524,18 +517,18 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
// start ui
if cmd.ui {
open_ui(&cmd.dapps_conf, &cmd.signer_conf)?;
open_ui(&cmd.signer_conf)?;
}
if let Some(dapp) = cmd.dapp {
open_dapp(&cmd.dapps_conf, &dapp)?;
open_dapp(&cmd.dapps_conf, &cmd.http_conf, &dapp)?;
}
// Handle exit
let restart = wait_for_exit(panic_handler, Some(updater), Some(client), can_restart);
// drop this stuff as soon as exit detected.
drop((http_server, ipc_server, dapps_server, signer_server, secretstore_key_server, ipfs_server, event_loop));
drop((http_server, ipc_server, signer_server, secretstore_key_server, ipfs_server, event_loop));
info!("Finishing work, please wait...");

View File

@ -21,6 +21,7 @@ transient-hashmap = "0.4"
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
jsonrpc-minihttp-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
jsonrpc-ipc-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
jsonrpc-macros = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }

View File

@ -29,8 +29,9 @@ extern crate time;
extern crate transient_hashmap;
extern crate jsonrpc_core;
pub extern crate jsonrpc_http_server as http;
pub extern crate jsonrpc_ipc_server as ipc;
extern crate jsonrpc_http_server as http;
extern crate jsonrpc_minihttp_server as minihttp;
extern crate jsonrpc_ipc_server as ipc;
extern crate ethash;
extern crate ethcore;
@ -62,10 +63,15 @@ extern crate ethjson;
#[cfg(test)]
extern crate ethcore_devtools as devtools;
mod metadata;
pub mod v1;
pub use ipc::{Server as IpcServer, MetaExtractor as IpcMetaExtractor, RequestContext as IpcRequestContext};
pub use http::{HttpMetaExtractor, Server as HttpServer, Error as HttpServerError, AccessControlAllowOrigin, Host};
pub use http::{
hyper,
RequestMiddleware, RequestMiddlewareAction,
AccessControlAllowOrigin, Host,
};
pub use v1::{SigningQueue, SignerService, ConfirmationsQueue, NetworkSettings, Metadata, Origin, informant, dispatch};
pub use v1::block_import::is_major_importing;
@ -73,26 +79,98 @@ pub use v1::block_import::is_major_importing;
use std::net::SocketAddr;
use http::tokio_core;
/// RPC HTTP Server instance
pub enum HttpServer {
/// Fast MiniHTTP variant
Mini(minihttp::Server),
/// Hyper variant
Hyper(http::Server),
}
/// RPC HTTP Server error
#[derive(Debug)]
pub enum HttpServerError {
/// IO error
Io(::std::io::Error),
/// Other hyper error
Hyper(hyper::Error),
}
impl From<http::Error> for HttpServerError {
fn from(e: http::Error) -> Self {
use self::HttpServerError::*;
match e {
http::Error::Io(io) => Io(io),
http::Error::Other(hyper) => Hyper(hyper),
}
}
}
impl From<minihttp::Error> for HttpServerError {
fn from(e: minihttp::Error) -> Self {
use self::HttpServerError::*;
match e {
minihttp::Error::Io(io) => Io(io),
}
}
}
/// HTTP RPC server impl-independent metadata extractor
pub trait HttpMetaExtractor: Send + Sync + 'static {
/// Type of Metadata
type Metadata: jsonrpc_core::Metadata;
/// Extracts metadata from given params.
fn read_metadata(&self, origin: String, dapps_origin: Option<String>) -> Self::Metadata;
}
/// HTTP server implementation-specific settings.
pub enum HttpSettings<R: RequestMiddleware> {
/// Enable fast minihttp server with given number of threads.
Threads(usize),
/// Enable standard server with optional dapps middleware.
Dapps(Option<R>),
}
/// Start http server asynchronously and returns result with `Server` handle on success or an error.
pub fn start_http<M, S, H, T>(
pub fn start_http<M, S, H, T, R>(
addr: &SocketAddr,
cors_domains: http::DomainsValidation<http::AccessControlAllowOrigin>,
allowed_hosts: http::DomainsValidation<http::Host>,
handler: H,
remote: tokio_core::reactor::Remote,
extractor: T,
settings: HttpSettings<R>,
) -> Result<HttpServer, HttpServerError> where
M: jsonrpc_core::Metadata,
S: jsonrpc_core::Middleware<M>,
H: Into<jsonrpc_core::MetaIoHandler<M, S>>,
T: HttpMetaExtractor<M>,
T: HttpMetaExtractor<Metadata=M>,
R: RequestMiddleware,
{
http::ServerBuilder::new(handler)
Ok(match settings {
HttpSettings::Dapps(middleware) => {
let mut builder = http::ServerBuilder::new(handler)
.event_loop_remote(remote)
.meta_extractor(extractor)
.meta_extractor(metadata::HyperMetaExtractor::new(extractor))
.cors(cors_domains.into())
.allowed_hosts(allowed_hosts.into());
if let Some(dapps) = middleware {
builder = builder.request_middleware(dapps)
}
builder.start_http(addr)
.map(HttpServer::Hyper)?
},
HttpSettings::Threads(threads) => {
minihttp::ServerBuilder::new(handler)
.threads(threads)
.meta_extractor(metadata::MiniMetaExtractor::new(extractor))
.cors(cors_domains.into())
.allowed_hosts(allowed_hosts.into())
.start_http(addr)
.map(HttpServer::Mini)?
},
})
}
/// Start ipc server asynchronously and returns result with `Server` handle on success or an error.

74
rpc/src/metadata.rs Normal file
View File

@ -0,0 +1,74 @@
// 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 <http://www.gnu.org/licenses/>.
use jsonrpc_core;
use http;
use hyper;
use minihttp;
use HttpMetaExtractor;
pub struct HyperMetaExtractor<T> {
extractor: T,
}
impl<T> HyperMetaExtractor<T> {
pub fn new(extractor: T) -> Self {
HyperMetaExtractor {
extractor: extractor,
}
}
}
impl<M, T> http::MetaExtractor<M> for HyperMetaExtractor<T> where
T: HttpMetaExtractor<Metadata = M>,
M: jsonrpc_core::Metadata,
{
fn read_metadata(&self, req: &hyper::server::Request<hyper::net::HttpStream>) -> M {
let origin = req.headers().get::<hyper::header::Origin>()
.map(|origin| format!("{}://{}", origin.scheme, origin.host))
.unwrap_or_else(|| "unknown".into());
let dapps_origin = req.headers().get_raw("x-parity-origin")
.and_then(|raw| raw.one())
.map(|raw| String::from_utf8_lossy(raw).into_owned());
self.extractor.read_metadata(origin, dapps_origin)
}
}
pub struct MiniMetaExtractor<T> {
extractor: T,
}
impl<T> MiniMetaExtractor<T> {
pub fn new(extractor: T) -> Self {
MiniMetaExtractor {
extractor: extractor,
}
}
}
impl<M, T> minihttp::MetaExtractor<M> for MiniMetaExtractor<T> where
T: HttpMetaExtractor<Metadata = M>,
M: jsonrpc_core::Metadata,
{
fn read_metadata(&self, req: &minihttp::Req) -> M {
let origin = req.header("origin")
.unwrap_or_else(|| "unknown")
.to_owned();
let dapps_origin = req.header("x-parity-origin").map(|h| h.to_owned());
self.extractor.read_metadata(origin, dapps_origin)
}
}

View File

@ -5,7 +5,7 @@ export TARGETS="
-p ethash \
-p ethcore \
-p ethcore-bigint\
-p ethcore-dapps \
-p parity-dapps \
-p ethcore-rpc \
-p ethcore-signer \
-p ethcore-util \