Remove the dapps system (#9017)
* Remove the dapps system from Parity * Move node-health outside of dapps * Fix set dapps list test * Update Cargo.lock * Deprecate options * Add _legacy_ prefixes in Dapps * Fix tests * Fix deprecatedness of dapps-path
This commit is contained in:
parent
fe678dcd2f
commit
494eb4ab6b
251
Cargo.lock
generated
251
Cargo.lock
generated
@ -1,8 +1,3 @@
|
|||||||
[[package]]
|
|
||||||
name = "adler32"
|
|
||||||
version = "1.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "0.6.4"
|
version = "0.6.4"
|
||||||
@ -40,14 +35,6 @@ name = "assert_matches"
|
|||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aster"
|
|
||||||
version = "0.41.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atty"
|
name = "atty"
|
||||||
version = "0.2.8"
|
version = "0.2.8"
|
||||||
@ -84,11 +71,6 @@ name = "base-x"
|
|||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "base32"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
@ -135,11 +117,6 @@ name = "bitflags"
|
|||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitflags"
|
|
||||||
version = "0.8.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "0.9.1"
|
version = "0.9.1"
|
||||||
@ -170,11 +147,6 @@ dependencies = [
|
|||||||
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "build_const"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
@ -267,14 +239,6 @@ dependencies = [
|
|||||||
"custom_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"custom_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crc"
|
|
||||||
version = "1.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam"
|
name = "crossbeam"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
@ -1119,15 +1083,6 @@ name = "fixedbitset"
|
|||||||
version = "0.1.9"
|
version = "0.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "flate2"
|
|
||||||
version = "1.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"miniz_oxide_c_api 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fnv"
|
name = "fnv"
|
||||||
version = "1.0.5"
|
version = "1.0.5"
|
||||||
@ -1182,11 +1137,6 @@ name = "getopts"
|
|||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "glob"
|
|
||||||
version = "0.2.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "globset"
|
name = "globset"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@ -1743,26 +1693,6 @@ dependencies = [
|
|||||||
"unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "miniz_oxide"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"adler32 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "miniz_oxide_c_api"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"crc 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"miniz_oxide 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "0.6.14"
|
version = "0.6.14"
|
||||||
@ -1821,15 +1751,6 @@ dependencies = [
|
|||||||
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "msdos_time"
|
|
||||||
version = "0.1.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "multibase"
|
name = "multibase"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
@ -2072,7 +1993,6 @@ dependencies = [
|
|||||||
"number_prefix 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"number_prefix 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"panic_hook 0.1.0",
|
"panic_hook 0.1.0",
|
||||||
"parity-bytes 0.1.0 (git+https://github.com/paritytech/parity-common)",
|
"parity-bytes 0.1.0 (git+https://github.com/paritytech/parity-common)",
|
||||||
"parity-dapps 1.12.0",
|
|
||||||
"parity-hash-fetch 1.12.0",
|
"parity-hash-fetch 1.12.0",
|
||||||
"parity-ipfs-api 1.12.0",
|
"parity-ipfs-api 1.12.0",
|
||||||
"parity-local-store 0.1.0",
|
"parity-local-store 0.1.0",
|
||||||
@ -2127,55 +2047,6 @@ dependencies = [
|
|||||||
"tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "parity-dapps"
|
|
||||||
version = "1.12.0"
|
|
||||||
dependencies = [
|
|
||||||
"base32 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"ethcore-devtools 1.12.0",
|
|
||||||
"ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"fetch 0.1.0",
|
|
||||||
"futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)",
|
|
||||||
"jsonrpc-http-server 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)",
|
|
||||||
"keccak-hash 0.1.2 (git+https://github.com/paritytech/parity-common)",
|
|
||||||
"linked-hash-map 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"mime_guess 2.0.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"node-health 0.1.0",
|
|
||||||
"parity-bytes 0.1.0 (git+https://github.com/paritytech/parity-common)",
|
|
||||||
"parity-dapps-glue 1.9.1",
|
|
||||||
"parity-hash-fetch 1.12.0",
|
|
||||||
"parity-reactor 0.1.0",
|
|
||||||
"parity-version 1.12.0",
|
|
||||||
"parking_lot 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"registrar 0.0.1",
|
|
||||||
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"serde_derive 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"zip 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "parity-dapps-glue"
|
|
||||||
version = "1.9.1"
|
|
||||||
dependencies = [
|
|
||||||
"aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"mime_guess 2.0.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"quasi_macros 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"syntex 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-hash-fetch"
|
name = "parity-hash-fetch"
|
||||||
version = "1.12.0"
|
version = "1.12.0"
|
||||||
@ -2547,11 +2418,6 @@ dependencies = [
|
|||||||
"hashdb 0.2.0 (git+https://github.com/paritytech/parity-common)",
|
"hashdb 0.2.0 (git+https://github.com/paritytech/parity-common)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "podio"
|
|
||||||
version = "0.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pretty_assertions"
|
name = "pretty_assertions"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
@ -2663,34 +2529,6 @@ dependencies = [
|
|||||||
"parity-wasm 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parity-wasm 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "quasi"
|
|
||||||
version = "0.32.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "quasi_codegen"
|
|
||||||
version = "0.32.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"syntex 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "quasi_macros"
|
|
||||||
version = "0.32.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quick-error"
|
name = "quick-error"
|
||||||
version = "1.2.2"
|
version = "1.2.2"
|
||||||
@ -3132,48 +2970,6 @@ dependencies = [
|
|||||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syntex"
|
|
||||||
version = "0.58.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syntex_errors"
|
|
||||||
version = "0.58.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syntex_pos"
|
|
||||||
version = "0.58.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syntex_syntax"
|
|
||||||
version = "0.58.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "take"
|
name = "take"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -3204,15 +3000,6 @@ dependencies = [
|
|||||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "term"
|
|
||||||
version = "0.4.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "term_size"
|
name = "term_size"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@ -3619,11 +3406,6 @@ name = "unicode-width"
|
|||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-xid"
|
|
||||||
version = "0.0.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -3846,41 +3628,25 @@ dependencies = [
|
|||||||
"xml-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"xml-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zip"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"msdos_time 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"podio 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
"checksum adler32 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6cbd0b9af8587c72beadc9f72d35b9fbb070982c9e6203e46e93f10df25f8f45"
|
|
||||||
"checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4"
|
"checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4"
|
||||||
"checksum ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b3568b48b7cefa6b8ce125f9bb4989e52fbcc29ebea88df04cc7c5f12f70455"
|
"checksum ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b3568b48b7cefa6b8ce125f9bb4989e52fbcc29ebea88df04cc7c5f12f70455"
|
||||||
"checksum app_dirs 1.2.1 (git+https://github.com/paritytech/app-dirs-rs)" = "<none>"
|
"checksum app_dirs 1.2.1 (git+https://github.com/paritytech/app-dirs-rs)" = "<none>"
|
||||||
"checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef"
|
"checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef"
|
||||||
"checksum assert_matches 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "664470abf00fae0f31c0eb6e1ca12d82961b2a2541ef898bc9dd51a9254d218b"
|
"checksum assert_matches 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "664470abf00fae0f31c0eb6e1ca12d82961b2a2541ef898bc9dd51a9254d218b"
|
||||||
"checksum aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccfdf7355d9db158df68f976ed030ab0f6578af811f5a7bb6dcf221ec24e0e0"
|
|
||||||
"checksum atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "af80143d6f7608d746df1520709e5d141c96f240b0e62b0aa41bdfb53374d9d4"
|
"checksum atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "af80143d6f7608d746df1520709e5d141c96f240b0e62b0aa41bdfb53374d9d4"
|
||||||
"checksum backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbbf59b1c43eefa8c3ede390fcc36820b4999f7914104015be25025e0d62af2"
|
"checksum backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbbf59b1c43eefa8c3ede390fcc36820b4999f7914104015be25025e0d62af2"
|
||||||
"checksum backtrace-sys 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "c63ea141ef8fdb10409d0f5daf30ac51f84ef43bff66f16627773d2a292cd189"
|
"checksum backtrace-sys 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "c63ea141ef8fdb10409d0f5daf30ac51f84ef43bff66f16627773d2a292cd189"
|
||||||
"checksum base-x 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f59103b47307f76e03bef1633aec7fa9e29bfb5aa6daf5a334f94233c71f6c1"
|
"checksum base-x 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f59103b47307f76e03bef1633aec7fa9e29bfb5aa6daf5a334f94233c71f6c1"
|
||||||
"checksum base32 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1b9605ba46d61df0410d8ac686b0007add8172eba90e8e909c347856fe794d8c"
|
|
||||||
"checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9"
|
"checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9"
|
||||||
"checksum base64 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "229d032f1a99302697f10b27167ae6d03d49d032e6a8e2550e8d3fc13356d2b4"
|
"checksum base64 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "229d032f1a99302697f10b27167ae6d03d49d032e6a8e2550e8d3fc13356d2b4"
|
||||||
"checksum bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e103c8b299b28a9c6990458b7013dc4a8356a9b854c51b9883241f5866fac36e"
|
"checksum bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e103c8b299b28a9c6990458b7013dc4a8356a9b854c51b9883241f5866fac36e"
|
||||||
"checksum bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c"
|
"checksum bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c"
|
||||||
"checksum bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f"
|
"checksum bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f"
|
||||||
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
|
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
|
||||||
"checksum bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1370e9fc2a6ae53aea8b7a5110edbd08836ed87c88736dfabccade1c2b44bff4"
|
|
||||||
"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
|
"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
|
||||||
"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf"
|
"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf"
|
||||||
"checksum bn 0.4.4 (git+https://github.com/paritytech/bn)" = "<none>"
|
"checksum bn 0.4.4 (git+https://github.com/paritytech/bn)" = "<none>"
|
||||||
"checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39"
|
|
||||||
"checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23"
|
"checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23"
|
||||||
"checksum bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1b7db437d718977f6dc9b2e3fd6fc343c02ac6b899b73fdd2179163447bd9ce9"
|
"checksum bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1b7db437d718977f6dc9b2e3fd6fc343c02ac6b899b73fdd2179163447bd9ce9"
|
||||||
"checksum cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "49ec142f5768efb5b7622aebc3fdbdbb8950a4b9ba996393cb76ef7466e8747d"
|
"checksum cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "49ec142f5768efb5b7622aebc3fdbdbb8950a4b9ba996393cb76ef7466e8747d"
|
||||||
@ -3889,7 +3655,6 @@ dependencies = [
|
|||||||
"checksum cid 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d85ee025368e69063c420cbb2ed9f852cb03a5e69b73be021e65726ce03585b6"
|
"checksum cid 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d85ee025368e69063c420cbb2ed9f852cb03a5e69b73be021e65726ce03585b6"
|
||||||
"checksum clap 2.29.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8f4a2b3bb7ef3c672d7c13d15613211d5a6976b6892c598b0fcb5d40765f19c2"
|
"checksum clap 2.29.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8f4a2b3bb7ef3c672d7c13d15613211d5a6976b6892c598b0fcb5d40765f19c2"
|
||||||
"checksum conv 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "78ff10625fd0ac447827aa30ea8b861fead473bb60aeb73af6c1c58caf0d1299"
|
"checksum conv 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "78ff10625fd0ac447827aa30ea8b861fead473bb60aeb73af6c1c58caf0d1299"
|
||||||
"checksum crc 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd5d02c0aac6bd68393ed69e00bbc2457f3e89075c6349db7189618dc4ddc1d7"
|
|
||||||
"checksum crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24ce9782d4d5c53674646a6a4c1863a21a8fc0cb649b3c94dfc16e45071dea19"
|
"checksum crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24ce9782d4d5c53674646a6a4c1863a21a8fc0cb649b3c94dfc16e45071dea19"
|
||||||
"checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3"
|
"checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3"
|
||||||
"checksum crossbeam-deque 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c1bdc73742c36f7f35ebcda81dbb33a7e0d33757d03a06d9ddca762712ec5ea2"
|
"checksum crossbeam-deque 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c1bdc73742c36f7f35ebcda81dbb33a7e0d33757d03a06d9ddca762712ec5ea2"
|
||||||
@ -3922,7 +3687,6 @@ dependencies = [
|
|||||||
"checksum fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa"
|
"checksum fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa"
|
||||||
"checksum fixed-hash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18d6fd718fb4396e7a9c93ac59ba7143501467ca7a143c145b5555a571d5576"
|
"checksum fixed-hash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18d6fd718fb4396e7a9c93ac59ba7143501467ca7a143c145b5555a571d5576"
|
||||||
"checksum fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33"
|
"checksum fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33"
|
||||||
"checksum flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9fac2277e84e5e858483756647a9d0aa8d9a2b7cba517fd84325a0aaa69a0909"
|
|
||||||
"checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344"
|
"checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344"
|
||||||
"checksum fs-swap 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67f816b2a5f8a6628764a4323d1a8d9ad5303266c4e4e4486ba680f477ba7e62"
|
"checksum fs-swap 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67f816b2a5f8a6628764a4323d1a8d9ad5303266c4e4e4486ba680f477ba7e62"
|
||||||
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
|
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
|
||||||
@ -3931,7 +3695,6 @@ dependencies = [
|
|||||||
"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4"
|
"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4"
|
||||||
"checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb"
|
"checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb"
|
||||||
"checksum getopts 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "65922871abd2f101a2eb0eaebadc66668e54a87ad9c3dd82520b5f86ede5eff9"
|
"checksum getopts 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "65922871abd2f101a2eb0eaebadc66668e54a87ad9c3dd82520b5f86ede5eff9"
|
||||||
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
|
|
||||||
"checksum globset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "464627f948c3190ae3d04b1bc6d7dca2f785bda0ac01278e6db129ad383dbeb6"
|
"checksum globset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "464627f948c3190ae3d04b1bc6d7dca2f785bda0ac01278e6db129ad383dbeb6"
|
||||||
"checksum hamming 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65043da274378d68241eb9a8f8f8aa54e349136f7b8e12f63e3ef44043cc30e1"
|
"checksum hamming 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65043da274378d68241eb9a8f8f8aa54e349136f7b8e12f63e3ef44043cc30e1"
|
||||||
"checksum hashdb 0.2.0 (git+https://github.com/paritytech/parity-common)" = "<none>"
|
"checksum hashdb 0.2.0 (git+https://github.com/paritytech/parity-common)" = "<none>"
|
||||||
@ -3986,14 +3749,11 @@ dependencies = [
|
|||||||
"checksum memorydb 0.2.0 (git+https://github.com/paritytech/parity-common)" = "<none>"
|
"checksum memorydb 0.2.0 (git+https://github.com/paritytech/parity-common)" = "<none>"
|
||||||
"checksum mime 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e3d709ffbb330e1566dc2f2a3c9b58a5ad4a381f740b810cd305dc3f089bc160"
|
"checksum mime 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e3d709ffbb330e1566dc2f2a3c9b58a5ad4a381f740b810cd305dc3f089bc160"
|
||||||
"checksum mime_guess 2.0.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)" = "27a5e6679a0614e25adc14c6434ba84e41632b765a6d9cb2031a0cca682699ae"
|
"checksum mime_guess 2.0.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)" = "27a5e6679a0614e25adc14c6434ba84e41632b765a6d9cb2031a0cca682699ae"
|
||||||
"checksum miniz_oxide 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aaa2d3ad070f428fffbd7d3ca2ea20bb0d8cffe9024405c44e1840bc1418b398"
|
|
||||||
"checksum miniz_oxide_c_api 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "92d98fdbd6145645828069b37ea92ca3de225e000d80702da25c20d3584b38a5"
|
|
||||||
"checksum mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "6d771e3ef92d58a8da8df7d6976bfca9371ed1de6619d9d5a5ce5b1f29b85bfe"
|
"checksum mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "6d771e3ef92d58a8da8df7d6976bfca9371ed1de6619d9d5a5ce5b1f29b85bfe"
|
||||||
"checksum mio-named-pipes 0.1.5 (git+https://github.com/alexcrichton/mio-named-pipes)" = "<none>"
|
"checksum mio-named-pipes 0.1.5 (git+https://github.com/alexcrichton/mio-named-pipes)" = "<none>"
|
||||||
"checksum mio-uds 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1731a873077147b626d89cc6c2a0db6288d607496c5d10c0cfcf3adc697ec673"
|
"checksum mio-uds 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1731a873077147b626d89cc6c2a0db6288d607496c5d10c0cfcf3adc697ec673"
|
||||||
"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
|
"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
|
||||||
"checksum miow 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9224c91f82b3c47cf53dcf78dfaa20d6888fbcc5d272d5f2fcdf8a697f3c987d"
|
"checksum miow 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9224c91f82b3c47cf53dcf78dfaa20d6888fbcc5d272d5f2fcdf8a697f3c987d"
|
||||||
"checksum msdos_time 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aad9dfe950c057b1bfe9c1f2aa51583a8468ef2a5baba2ebbe06d775efeb7729"
|
|
||||||
"checksum multibase 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b9c35dac080fd6e16a99924c8dfdef0af89d797dd851adab25feaffacf7850d6"
|
"checksum multibase 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b9c35dac080fd6e16a99924c8dfdef0af89d797dd851adab25feaffacf7850d6"
|
||||||
"checksum multihash 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d49add5f49eb08bfc4d01ff286b84a48f53d45314f165c2d6efe477222d24f3"
|
"checksum multihash 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d49add5f49eb08bfc4d01ff286b84a48f53d45314f165c2d6efe477222d24f3"
|
||||||
"checksum nan-preserving-float 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34d4f00fcc2f4c9efa8cc971db0da9e28290e28e97af47585e48691ef10ff31f"
|
"checksum nan-preserving-float 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34d4f00fcc2f4c9efa8cc971db0da9e28290e28e97af47585e48691ef10ff31f"
|
||||||
@ -4029,7 +3789,6 @@ dependencies = [
|
|||||||
"checksum phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "6b07ffcc532ccc85e3afc45865469bf5d9e4ef5bfcf9622e3cfe80c2d275ec03"
|
"checksum phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "6b07ffcc532ccc85e3afc45865469bf5d9e4ef5bfcf9622e3cfe80c2d275ec03"
|
||||||
"checksum phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "07e24b0ca9643bdecd0632f2b3da6b1b89bbb0030e0b992afc1113b23a7bc2f2"
|
"checksum phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "07e24b0ca9643bdecd0632f2b3da6b1b89bbb0030e0b992afc1113b23a7bc2f2"
|
||||||
"checksum plain_hasher 0.1.0 (git+https://github.com/paritytech/parity-common)" = "<none>"
|
"checksum plain_hasher 0.1.0 (git+https://github.com/paritytech/parity-common)" = "<none>"
|
||||||
"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 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 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0e31b86efadeaeb1235452171a66689682783149a6249ff334a2c5d8218d00a4"
|
||||||
"checksum primal-bit 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "686a64e2f50194c64942992af5799e6b6e8775b8f88c607d72ed0a2fd58b9b21"
|
"checksum primal-bit 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "686a64e2f50194c64942992af5799e6b6e8775b8f88c607d72ed0a2fd58b9b21"
|
||||||
@ -4040,9 +3799,6 @@ dependencies = [
|
|||||||
"checksum protobuf 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40e2484e639dcae0985fc483ad76ce7ad78ee5aa092751d7d538f0b20d76486b"
|
"checksum protobuf 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40e2484e639dcae0985fc483ad76ce7ad78ee5aa092751d7d538f0b20d76486b"
|
||||||
"checksum pulldown-cmark 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8361e81576d2e02643b04950e487ec172b687180da65c731c03cf336784e6c07"
|
"checksum pulldown-cmark 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8361e81576d2e02643b04950e487ec172b687180da65c731c03cf336784e6c07"
|
||||||
"checksum pwasm-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "90d2b3c5bf24275fc77db6b14ec00a7a085d8ff9d1c4215fb6f6263e8d7b01bc"
|
"checksum pwasm-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "90d2b3c5bf24275fc77db6b14ec00a7a085d8ff9d1c4215fb6f6263e8d7b01bc"
|
||||||
"checksum quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18c45c4854d6d1cf5d531db97c75880feb91c958b0720f4ec1057135fec358b3"
|
|
||||||
"checksum quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9e25fa23c044c1803f43ca59c98dac608976dd04ce799411edd58ece776d4"
|
|
||||||
"checksum quasi_macros 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29cec87bc2816766d7e4168302d505dd06b0a825aed41b00633d296e922e02dd"
|
|
||||||
"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
|
"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
|
||||||
"checksum quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7b0ff51282f28dc1b53fd154298feaa2e77c5ea0dba68e1fd8b03b72fbe13d2a"
|
"checksum quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7b0ff51282f28dc1b53fd154298feaa2e77c5ea0dba68e1fd8b03b72fbe13d2a"
|
||||||
"checksum rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)" = "512870020642bb8c221bf68baa1b2573da814f6ccfe5c9699b1c303047abe9b1"
|
"checksum rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)" = "512870020642bb8c221bf68baa1b2573da814f6ccfe5c9699b1c303047abe9b1"
|
||||||
@ -4093,15 +3849,10 @@ dependencies = [
|
|||||||
"checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b"
|
"checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b"
|
||||||
"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694"
|
"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694"
|
||||||
"checksum syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "91b52877572087400e83d24b9178488541e3d535259e04ff17a63df1e5ceff59"
|
"checksum syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "91b52877572087400e83d24b9178488541e3d535259e04ff17a63df1e5ceff59"
|
||||||
"checksum syntex 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a8f5e3aaa79319573d19938ea38d068056b826db9883a5d47f86c1cecc688f0e"
|
|
||||||
"checksum syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "867cc5c2d7140ae7eaad2ae9e8bf39cb18a67ca651b7834f88d46ca98faadb9c"
|
|
||||||
"checksum syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "13ad4762fe52abc9f4008e85c4fb1b1fe3aa91ccb99ff4826a439c7c598e1047"
|
|
||||||
"checksum syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6e0e4dbae163dd98989464c23dd503161b338790640e11537686f2ef0f25c791"
|
|
||||||
"checksum take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5"
|
"checksum take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5"
|
||||||
"checksum target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c63f48baada5c52e65a29eef93ab4f8982681b67f9e8d29c7b05abcfec2b9ffe"
|
"checksum target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c63f48baada5c52e65a29eef93ab4f8982681b67f9e8d29c7b05abcfec2b9ffe"
|
||||||
"checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6"
|
"checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6"
|
||||||
"checksum tempfile 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "11ce2fe9db64b842314052e2421ac61a73ce41b898dc8e3750398b219c5fc1e0"
|
"checksum tempfile 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "11ce2fe9db64b842314052e2421ac61a73ce41b898dc8e3750398b219c5fc1e0"
|
||||||
"checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1"
|
|
||||||
"checksum term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e5b9a66db815dcfd2da92db471106457082577c3c278d4138ab3e3b4e189327"
|
"checksum term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e5b9a66db815dcfd2da92db471106457082577c3c278d4138ab3e3b4e189327"
|
||||||
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
|
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
|
||||||
"checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693"
|
"checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693"
|
||||||
@ -4139,7 +3890,6 @@ dependencies = [
|
|||||||
"checksum unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "51ccda9ef9efa3f7ef5d91e8f9b83bbe6955f9bf86aec89d5cce2c874625920f"
|
"checksum unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "51ccda9ef9efa3f7ef5d91e8f9b83bbe6955f9bf86aec89d5cce2c874625920f"
|
||||||
"checksum unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8083c594e02b8ae1654ae26f0ade5158b119bd88ad0e8227a5d8fcd72407946"
|
"checksum unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8083c594e02b8ae1654ae26f0ade5158b119bd88ad0e8227a5d8fcd72407946"
|
||||||
"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f"
|
"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f"
|
||||||
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
|
|
||||||
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
||||||
"checksum unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f2ae5ddb18e1c92664717616dd9549dde73f539f01bd7b77c2edb2446bdff91"
|
"checksum unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f2ae5ddb18e1c92664717616dd9549dde73f539f01bd7b77c2edb2446bdff91"
|
||||||
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
|
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
|
||||||
@ -4163,4 +3913,3 @@ dependencies = [
|
|||||||
"checksum xdg 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a66b7c2281ebde13cf4391d70d4c7e5946c3c25e72a7b859ca8f677dcd0b0c61"
|
"checksum xdg 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a66b7c2281ebde13cf4391d70d4c7e5946c3c25e72a7b859ca8f677dcd0b0c61"
|
||||||
"checksum xml-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c1cb601d29fe2c2ac60a2b2e5e293994d87a1f6fa9687a31a15270f909be9c2"
|
"checksum xml-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c1cb601d29fe2c2ac60a2b2e5e293994d87a1f6fa9687a31a15270f909be9c2"
|
||||||
"checksum xmltree 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9cfb54ca6b8f17d2377219ce485b134d53561b77e1393c7ea416f543a527431"
|
"checksum xmltree 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9cfb54ca6b8f17d2377219ce485b134d53561b77e1393c7ea416f543a527431"
|
||||||
"checksum zip 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "10931e278527cea65682696481e6d840371d581079df529ebfee186e0eaad719"
|
|
||||||
|
@ -46,7 +46,7 @@ ethcore-transaction = { path = "ethcore/transaction" }
|
|||||||
ethereum-types = "0.3"
|
ethereum-types = "0.3"
|
||||||
node-filter = { path = "ethcore/node_filter" }
|
node-filter = { path = "ethcore/node_filter" }
|
||||||
ethkey = { path = "ethkey" }
|
ethkey = { path = "ethkey" }
|
||||||
node-health = { path = "dapps/node-health" }
|
node-health = { path = "node-health" }
|
||||||
rlp = { git = "https://github.com/paritytech/parity-common" }
|
rlp = { git = "https://github.com/paritytech/parity-common" }
|
||||||
rpc-cli = { path = "rpc_cli" }
|
rpc-cli = { path = "rpc_cli" }
|
||||||
parity-hash-fetch = { path = "hash-fetch" }
|
parity-hash-fetch = { path = "hash-fetch" }
|
||||||
@ -68,7 +68,6 @@ kvdb-rocksdb = { git = "https://github.com/paritytech/parity-common" }
|
|||||||
journaldb = { path = "util/journaldb" }
|
journaldb = { path = "util/journaldb" }
|
||||||
mem = { path = "util/mem" }
|
mem = { path = "util/mem" }
|
||||||
|
|
||||||
parity-dapps = { path = "dapps", optional = true }
|
|
||||||
ethcore-secretstore = { path = "secret_store", optional = true }
|
ethcore-secretstore = { path = "secret_store", optional = true }
|
||||||
|
|
||||||
registrar = { path = "registrar" }
|
registrar = { path = "registrar" }
|
||||||
@ -89,8 +88,6 @@ winapi = { version = "0.3.4", features = ["winsock2", "winuser", "shellapi"] }
|
|||||||
daemonize = { git = "https://github.com/paritytech/daemonize" }
|
daemonize = { git = "https://github.com/paritytech/daemonize" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["dapps"]
|
|
||||||
dapps = ["parity-dapps"]
|
|
||||||
json-tests = ["ethcore/json-tests"]
|
json-tests = ["ethcore/json-tests"]
|
||||||
test-heavy = ["ethcore/test-heavy"]
|
test-heavy = ["ethcore/test-heavy"]
|
||||||
evm-debug = ["ethcore/evm-debug"]
|
evm-debug = ["ethcore/evm-debug"]
|
||||||
@ -126,7 +123,6 @@ debug = false
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"chainspec",
|
"chainspec",
|
||||||
"dapps/js-glue",
|
|
||||||
"ethcore/wasm/run",
|
"ethcore/wasm/run",
|
||||||
"ethcore/types",
|
"ethcore/types",
|
||||||
"ethkey/cli",
|
"ethkey/cli",
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
[package]
|
|
||||||
description = "Parity Dapps crate"
|
|
||||||
name = "parity-dapps"
|
|
||||||
version = "1.12.0"
|
|
||||||
license = "GPL-3.0"
|
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
base32 = "0.3"
|
|
||||||
futures = "0.1"
|
|
||||||
futures-cpupool = "0.1"
|
|
||||||
linked-hash-map = "0.5"
|
|
||||||
log = "0.3"
|
|
||||||
parking_lot = "0.6"
|
|
||||||
mime_guess = "2.0.0-alpha.2"
|
|
||||||
rand = "0.4"
|
|
||||||
rustc-hex = "1.0"
|
|
||||||
serde = "1.0"
|
|
||||||
serde_derive = "1.0"
|
|
||||||
serde_json = "1.0"
|
|
||||||
unicase = "1.4"
|
|
||||||
zip = { version = "0.3", default-features = false, features = ["deflate"] }
|
|
||||||
itertools = "0.5"
|
|
||||||
|
|
||||||
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" }
|
|
||||||
jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" }
|
|
||||||
|
|
||||||
parity-bytes = { git = "https://github.com/paritytech/parity-common" }
|
|
||||||
ethereum-types = "0.3"
|
|
||||||
fetch = { path = "../util/fetch" }
|
|
||||||
node-health = { path = "./node-health" }
|
|
||||||
parity-dapps-glue = { path = "./js-glue" }
|
|
||||||
parity-hash-fetch = { path = "../hash-fetch" }
|
|
||||||
parity-reactor = { path = "../util/reactor" }
|
|
||||||
keccak-hash = { git = "https://github.com/paritytech/parity-common" }
|
|
||||||
parity-version = { path = "../util/version" }
|
|
||||||
registrar = { path = "../registrar" }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
env_logger = "0.4"
|
|
||||||
ethcore-devtools = { path = "../devtools" }
|
|
@ -1,28 +0,0 @@
|
|||||||
[package]
|
|
||||||
description = "Base Package for all Parity built-in dapps"
|
|
||||||
name = "parity-dapps-glue"
|
|
||||||
version = "1.9.1"
|
|
||||||
license = "GPL-3.0"
|
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
|
||||||
build = "build.rs"
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
quasi_codegen = { version = "0.32", optional = true }
|
|
||||||
syntex = { version = "0.58", optional = true }
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
glob = { version = "0.2.11" }
|
|
||||||
mime_guess = { version = "2.0.0-alpha.2" }
|
|
||||||
aster = { version = "0.41", default-features = false }
|
|
||||||
quasi = { version = "0.32", default-features = false }
|
|
||||||
quasi_macros = { version = "0.32", optional = true }
|
|
||||||
syntex = { version = "0.58", optional = true }
|
|
||||||
syntex_syntax = { version = "0.58", optional = true }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["with-syntex"]
|
|
||||||
nightly = ["quasi_macros"]
|
|
||||||
with-syntex = ["quasi/with-syntex", "quasi_codegen", "quasi_codegen/with-syntex", "syntex", "syntex_syntax"]
|
|
||||||
use-precompiled-js = []
|
|
||||||
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
|||||||
# Parity Dapps (JS-glue)
|
|
||||||
|
|
||||||
Code generator to simplify creating a built-in Parity Dapp
|
|
||||||
|
|
||||||
# How to create new builtin Dapp.
|
|
||||||
1. Clone this repository.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ git clone https://github.com/paritytech/parity.git
|
|
||||||
```
|
|
||||||
|
|
||||||
1. Create a new directory for your Dapp. (`./myapp`)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ mkdir -p ./parity/dapps/myapp/src/web
|
|
||||||
```
|
|
||||||
|
|
||||||
1. Copy your frontend files to `./dapps/myapp/src/web` (bundled ones)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ cp -r ./myapp-src/* ./parity/dapps/myapp/src/web
|
|
||||||
```
|
|
||||||
|
|
||||||
1. Instead of creating `web3` in your app. Load (as the first script tag in `head`):
|
|
||||||
|
|
||||||
```html
|
|
||||||
<script src="/parity-utils/inject.js"></script>
|
|
||||||
```
|
|
||||||
|
|
||||||
The `inject.js` script will create global `web3` instance with proper provider that should be used by your dapp.
|
|
||||||
|
|
||||||
1. Create `./parity/dapps/myapp/Cargo.toml` with you apps details. See example here: [parity-status Cargo.toml](https://github.com/paritytech/parity-ui/blob/master/status/Cargo.toml).
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ git clone https://github.com/paritytech/parity-ui.git
|
|
||||||
$ cd ./parity-ui/
|
|
||||||
$ cp ./home/Cargo.toml ../parity/dapps/myapp/Cargo.toml
|
|
||||||
$ cp ./home/build.rs ../parity/dapps/myapp/build.rs
|
|
||||||
$ cp ./home/src/lib.rs ../parity/dapps/myapp/src/lib.rs
|
|
||||||
$ cp ./home/src/lib.rs.in ../parity/dapps/myapp/src/lib.rs.in
|
|
||||||
# And edit the details of your app
|
|
||||||
$ vim ../parity/dapps/myapp/Cargo.toml # Edit the details
|
|
||||||
$ vim ./parity/dapps/myapp/src/lib.rs.in # Edit the details
|
|
||||||
```
|
|
||||||
# How to include your Dapp into `Parity`?
|
|
||||||
1. Edit `dapps/Cargo.toml` and add dependency to your application (it can be optional)
|
|
||||||
|
|
||||||
```toml
|
|
||||||
# Use git repo and version
|
|
||||||
parity-dapps-myapp = { path="./myapp" }
|
|
||||||
```
|
|
||||||
|
|
||||||
1. Edit `dapps/src/apps.rs` and add your application to `all_pages` (if it's optional you need to specify two functions - see `parity-dapps-wallet` example)
|
|
||||||
|
|
||||||
1. Compile parity.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ cargo build --release # While inside `parity`
|
|
||||||
```
|
|
||||||
|
|
||||||
1. Commit the results.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ git add myapp && git commit -am "My first Parity Dapp".
|
|
||||||
```
|
|
@ -1,42 +0,0 @@
|
|||||||
// Copyright 2015-2018 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/>.
|
|
||||||
|
|
||||||
#[cfg(feature = "with-syntex")]
|
|
||||||
mod inner {
|
|
||||||
extern crate syntex;
|
|
||||||
extern crate quasi_codegen;
|
|
||||||
|
|
||||||
use std::env;
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
pub fn main() {
|
|
||||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
|
||||||
|
|
||||||
let src = Path::new("src/lib.rs.in");
|
|
||||||
let dst = Path::new(&out_dir).join("lib.rs");
|
|
||||||
|
|
||||||
quasi_codegen::expand(&src, &dst).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "with-syntex"))]
|
|
||||||
mod inner {
|
|
||||||
pub fn main() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
inner::main();
|
|
||||||
}
|
|
@ -1,80 +0,0 @@
|
|||||||
// Copyright 2015-2018 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/>.
|
|
||||||
|
|
||||||
#[cfg(feature = "with-syntex")]
|
|
||||||
pub mod inner {
|
|
||||||
use syntex;
|
|
||||||
use codegen;
|
|
||||||
use syntax::{ast, fold};
|
|
||||||
use std::env;
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
fn strip_attributes(krate: ast::Crate) -> ast::Crate {
|
|
||||||
/// Helper folder that strips the serde attributes after the extensions have been expanded.
|
|
||||||
struct StripAttributeFolder;
|
|
||||||
|
|
||||||
impl fold::Folder for StripAttributeFolder {
|
|
||||||
fn fold_attribute(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
|
|
||||||
if &*attr.value.name.as_str() == "webapp" {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(attr)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
|
|
||||||
fold::noop_fold_mac(mac, self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fold::Folder::fold_crate(&mut StripAttributeFolder, krate)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn register(reg: &mut syntex::Registry) {
|
|
||||||
reg.add_attr("feature(custom_derive)");
|
|
||||||
reg.add_attr("feature(custom_attribute)");
|
|
||||||
|
|
||||||
reg.add_decorator("derive_WebAppFiles", codegen::expand_webapp_implementation);
|
|
||||||
reg.add_post_expansion_pass(strip_attributes);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn generate() {
|
|
||||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
|
||||||
let mut registry = syntex::Registry::new();
|
|
||||||
register(&mut registry);
|
|
||||||
|
|
||||||
let src = Path::new("src/lib.rs.in");
|
|
||||||
let dst = Path::new(&out_dir).join("lib.rs");
|
|
||||||
|
|
||||||
registry.expand("", &src, &dst).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "with-syntex"))]
|
|
||||||
pub mod inner {
|
|
||||||
use codegen;
|
|
||||||
|
|
||||||
pub fn register(reg: &mut rustc_plugin::Registry) {
|
|
||||||
reg.register_syntax_extension(
|
|
||||||
syntax::parse::token::intern("derive_WebAppFiles"),
|
|
||||||
syntax::ext::base::MultiDecorator(
|
|
||||||
Box::new(codegen::expand_webapp_implementation)));
|
|
||||||
|
|
||||||
reg.register_attribute("webapp".to_owned(), AttributeType::Normal);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn generate() {}
|
|
||||||
}
|
|
@ -1,194 +0,0 @@
|
|||||||
// Copyright 2015-2018 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/>.
|
|
||||||
|
|
||||||
extern crate aster;
|
|
||||||
extern crate glob;
|
|
||||||
extern crate mime_guess;
|
|
||||||
|
|
||||||
use self::mime_guess::guess_mime_type;
|
|
||||||
use std::path::{self, Path, PathBuf};
|
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
use syntax::attr;
|
|
||||||
use syntax::ast::{self, MetaItem, Item};
|
|
||||||
use syntax::codemap::Span;
|
|
||||||
use syntax::ext::base::{Annotatable, ExtCtxt};
|
|
||||||
use syntax::print::pprust::lit_to_string;
|
|
||||||
use syntax::symbol::InternedString;
|
|
||||||
|
|
||||||
pub fn expand_webapp_implementation(
|
|
||||||
cx: &mut ExtCtxt,
|
|
||||||
span: Span,
|
|
||||||
meta_item: &MetaItem,
|
|
||||||
annotatable: &Annotatable,
|
|
||||||
push: &mut FnMut(Annotatable)
|
|
||||||
) {
|
|
||||||
let item = match *annotatable {
|
|
||||||
Annotatable::Item(ref item) => item,
|
|
||||||
_ => {
|
|
||||||
cx.span_err(meta_item.span, "`#[derive(WebAppFiles)]` may only be applied to struct implementations");
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
let builder = aster::AstBuilder::new().span(span);
|
|
||||||
implement_webapp(cx, &builder, item, push);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn implement_webapp(cx: &ExtCtxt, builder: &aster::AstBuilder, item: &Item, push: &mut FnMut(Annotatable)) {
|
|
||||||
let static_files_dir = extract_path(cx, item);
|
|
||||||
|
|
||||||
let src = Path::new("src");
|
|
||||||
let static_files = {
|
|
||||||
let mut buf = src.to_path_buf();
|
|
||||||
buf.push(static_files_dir.deref());
|
|
||||||
buf
|
|
||||||
};
|
|
||||||
|
|
||||||
let search_location = {
|
|
||||||
let mut buf = static_files.to_path_buf();
|
|
||||||
buf.push("**");
|
|
||||||
buf.push("*");
|
|
||||||
buf
|
|
||||||
};
|
|
||||||
|
|
||||||
let files = glob::glob(search_location.to_str().expect("Valid UTF8 path"))
|
|
||||||
.expect("The sources directory is missing.")
|
|
||||||
.collect::<Result<Vec<PathBuf>, glob::GlobError>>()
|
|
||||||
.expect("There should be no error when reading a list of files.");
|
|
||||||
|
|
||||||
let statements = files
|
|
||||||
.iter()
|
|
||||||
.filter(|path_buf| path_buf.is_file())
|
|
||||||
.map(|path_buf| {
|
|
||||||
let path = path_buf.as_path();
|
|
||||||
let filename = path.file_name().and_then(|s| s.to_str()).expect("Only UTF8 paths.");
|
|
||||||
let mime_type = guess_mime_type(filename).to_string();
|
|
||||||
let file_path = as_uri(path.strip_prefix(&static_files).ok().expect("Prefix is always there, cause it's absolute path;qed"));
|
|
||||||
let file_path_in_source = path.to_str().expect("Only UTF8 paths.");
|
|
||||||
|
|
||||||
let path_lit = builder.expr().str(file_path.as_str());
|
|
||||||
let mime_lit = builder.expr().str(mime_type.as_str());
|
|
||||||
let web_path_lit = builder.expr().str(file_path_in_source);
|
|
||||||
let separator_lit = builder.expr().str(path::MAIN_SEPARATOR.to_string().as_str());
|
|
||||||
let concat_id = builder.id("concat!");
|
|
||||||
let env_id = builder.id("env!");
|
|
||||||
let macro_id = builder.id("include_bytes!");
|
|
||||||
|
|
||||||
let content = quote_expr!(
|
|
||||||
cx,
|
|
||||||
$macro_id($concat_id($env_id("CARGO_MANIFEST_DIR"), $separator_lit, $web_path_lit))
|
|
||||||
);
|
|
||||||
quote_stmt!(
|
|
||||||
cx,
|
|
||||||
files.insert($path_lit, File { path: $path_lit, content_type: $mime_lit, content: $content });
|
|
||||||
).expect("The statement is always ok, because it just uses literals.")
|
|
||||||
}).collect::<Vec<ast::Stmt>>();
|
|
||||||
|
|
||||||
let type_name = item.ident;
|
|
||||||
|
|
||||||
let files_impl = quote_item!(cx,
|
|
||||||
impl $type_name {
|
|
||||||
#[allow(unused_mut)]
|
|
||||||
fn files() -> ::std::collections::HashMap<&'static str, File> {
|
|
||||||
let mut files = ::std::collections::HashMap::new();
|
|
||||||
$statements
|
|
||||||
files
|
|
||||||
}
|
|
||||||
}
|
|
||||||
).unwrap();
|
|
||||||
|
|
||||||
push(Annotatable::Item(files_impl));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extract_path(cx: &ExtCtxt, item: &Item) -> String {
|
|
||||||
for meta_items in item.attrs.iter().filter_map(webapp_meta_items) {
|
|
||||||
for meta_item in meta_items {
|
|
||||||
let is_path = &*meta_item.name.as_str() == "path";
|
|
||||||
match meta_item.node {
|
|
||||||
ast::MetaItemKind::NameValue(ref lit) if is_path => {
|
|
||||||
if let Some(s) = get_str_from_lit(cx, lit) {
|
|
||||||
return s.deref().to_owned();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// default
|
|
||||||
"web".to_owned()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn webapp_meta_items(attr: &ast::Attribute) -> Option<Vec<ast::MetaItem>> {
|
|
||||||
let is_webapp = &*attr.value.name.as_str() == "webapp";
|
|
||||||
match attr.value.node {
|
|
||||||
ast::MetaItemKind::List(ref items) if is_webapp => {
|
|
||||||
attr::mark_used(&attr);
|
|
||||||
Some(
|
|
||||||
items.iter()
|
|
||||||
.map(|item| item.node.clone())
|
|
||||||
.filter_map(|item| match item {
|
|
||||||
ast::NestedMetaItemKind::MetaItem(item) => Some(item),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_str_from_lit(cx: &ExtCtxt, lit: &ast::Lit) -> Option<InternedString> {
|
|
||||||
match lit.node {
|
|
||||||
ast::LitKind::Str(ref s, _) => Some(s.clone().as_str()),
|
|
||||||
_ => {
|
|
||||||
cx.span_err(
|
|
||||||
lit.span,
|
|
||||||
&format!("webapp annotation path must be a string, not `{}`",
|
|
||||||
lit_to_string(lit)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_uri(path: &Path) -> String {
|
|
||||||
let mut s = String::new();
|
|
||||||
for component in path.iter() {
|
|
||||||
s.push_str(component.to_str().expect("Only UTF-8 filenames are supported."));
|
|
||||||
s.push('/');
|
|
||||||
}
|
|
||||||
s[0..s.len()-1].into()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_convert_path_separators_on_all_platforms() {
|
|
||||||
// given
|
|
||||||
let p = {
|
|
||||||
let mut p = PathBuf::new();
|
|
||||||
p.push("web");
|
|
||||||
p.push("src");
|
|
||||||
p.push("index.html");
|
|
||||||
p
|
|
||||||
};
|
|
||||||
|
|
||||||
// when
|
|
||||||
let path = as_uri(&p);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(path, "web/src/index.html".to_owned());
|
|
||||||
}
|
|
@ -1,89 +0,0 @@
|
|||||||
// Copyright 2015-2018 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/>.
|
|
||||||
|
|
||||||
#![cfg_attr(feature = "use-precompiled-js", allow(dead_code))]
|
|
||||||
#![cfg_attr(feature = "use-precompiled-js", allow(unused_imports))]
|
|
||||||
|
|
||||||
use std::fmt;
|
|
||||||
use std::process::Command;
|
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
mod platform {
|
|
||||||
use std::process::Command;
|
|
||||||
|
|
||||||
pub static NPM_CMD: &'static str = "npm";
|
|
||||||
pub fn handle_cmd(cmd: &mut Command) -> &mut Command {
|
|
||||||
cmd
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
mod platform {
|
|
||||||
use std::process::{Command, Stdio};
|
|
||||||
|
|
||||||
pub static NPM_CMD: &'static str = "cmd.exe";
|
|
||||||
// NOTE [ToDr] For some reason on windows
|
|
||||||
// The command doesn't have %~dp0 set properly
|
|
||||||
// and it cannot load globally installed node.exe
|
|
||||||
pub fn handle_cmd(cmd: &mut Command) -> &mut Command {
|
|
||||||
cmd.stdin(Stdio::null())
|
|
||||||
.arg("/c")
|
|
||||||
.arg("npm.cmd")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn die<T : fmt::Debug>(s: &'static str, e: T) -> ! {
|
|
||||||
panic!("Error: {}: {:?}", s, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "use-precompiled-js")]
|
|
||||||
pub fn test(_path: &str) {
|
|
||||||
}
|
|
||||||
#[cfg(feature = "use-precompiled-js")]
|
|
||||||
pub fn build(_path: &str, _dest: &str) {
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "use-precompiled-js"))]
|
|
||||||
pub fn build(path: &str, dest: &str) {
|
|
||||||
let child = platform::handle_cmd(&mut Command::new(platform::NPM_CMD))
|
|
||||||
.arg("install")
|
|
||||||
.arg("--no-progress")
|
|
||||||
.current_dir(path)
|
|
||||||
.status()
|
|
||||||
.unwrap_or_else(|e| die("Installing node.js dependencies with npm", e));
|
|
||||||
assert!(child.success(), "There was an error installing dependencies.");
|
|
||||||
|
|
||||||
let child = platform::handle_cmd(&mut Command::new(platform::NPM_CMD))
|
|
||||||
.arg("run")
|
|
||||||
.arg("build")
|
|
||||||
.env("NODE_ENV", "production")
|
|
||||||
.env("BUILD_DEST", dest)
|
|
||||||
.current_dir(path)
|
|
||||||
.status()
|
|
||||||
.unwrap_or_else(|e| die("Building JS code", e));
|
|
||||||
assert!(child.success(), "There was an error build JS code.");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "use-precompiled-js"))]
|
|
||||||
pub fn test(path: &str) {
|
|
||||||
let child = Command::new(platform::NPM_CMD)
|
|
||||||
.arg("run")
|
|
||||||
.arg("test")
|
|
||||||
.current_dir(path)
|
|
||||||
.status()
|
|
||||||
.unwrap_or_else(|e| die("Running test command", e));
|
|
||||||
assert!(child.success(), "There was an error while running JS tests.");
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
// Copyright 2015-2018 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/>.
|
|
||||||
|
|
||||||
#![cfg_attr(not(feature = "with-syntex"), feature(rustc_private, plugin))]
|
|
||||||
#![cfg_attr(not(feature = "with-syntex"), plugin(quasi_macros))]
|
|
||||||
|
|
||||||
#[cfg(feature = "with-syntex")]
|
|
||||||
extern crate syntex;
|
|
||||||
|
|
||||||
#[cfg(feature = "with-syntex")]
|
|
||||||
extern crate syntex_syntax as syntax;
|
|
||||||
|
|
||||||
#[cfg(feature = "with-syntex")]
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/lib.rs"));
|
|
||||||
|
|
||||||
#[cfg(not(feature = "with-syntex"))]
|
|
||||||
#[macro_use]
|
|
||||||
extern crate syntax;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "with-syntex"))]
|
|
||||||
extern crate rustc_plugin;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "with-syntex"))]
|
|
||||||
include!("lib.rs.in");
|
|
@ -1,47 +0,0 @@
|
|||||||
// Copyright 2015-2018 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/>.
|
|
||||||
|
|
||||||
|
|
||||||
extern crate quasi;
|
|
||||||
|
|
||||||
mod codegen;
|
|
||||||
mod build;
|
|
||||||
pub mod js;
|
|
||||||
pub use build::inner::generate;
|
|
||||||
|
|
||||||
use std::default::Default;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct File {
|
|
||||||
pub path: &'static str,
|
|
||||||
pub content: &'static [u8],
|
|
||||||
// TODO: use strongly-typed MIME.
|
|
||||||
pub content_type: &'static str,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct Info {
|
|
||||||
pub name: &'static str,
|
|
||||||
pub version: &'static str,
|
|
||||||
pub author: &'static str,
|
|
||||||
pub description: &'static str,
|
|
||||||
pub icon_url: &'static str,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait WebApp : Default + Send + Sync {
|
|
||||||
fn file(&self, path: &str) -> Option<&File>;
|
|
||||||
fn info(&self) -> Info;
|
|
||||||
}
|
|
Binary file not shown.
@ -1,97 +0,0 @@
|
|||||||
// Copyright 2015-2018 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 std::sync::Arc;
|
|
||||||
|
|
||||||
use hyper::{Method, StatusCode};
|
|
||||||
|
|
||||||
use api::response;
|
|
||||||
use apps::fetcher::Fetcher;
|
|
||||||
use endpoint::{Endpoint, Request, Response, EndpointPath};
|
|
||||||
use futures::{future, Future};
|
|
||||||
use node_health::{NodeHealth, HealthStatus};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct RestApi {
|
|
||||||
fetcher: Arc<Fetcher>,
|
|
||||||
health: NodeHealth,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Endpoint for RestApi {
|
|
||||||
fn respond(&self, mut path: EndpointPath, req: Request) -> Response {
|
|
||||||
if let Method::Options = *req.method() {
|
|
||||||
return Box::new(future::ok(response::empty()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let endpoint = path.app_params.get(0).map(String::to_owned);
|
|
||||||
let hash = path.app_params.get(1).map(String::to_owned);
|
|
||||||
|
|
||||||
// at this point path.app_id contains 'api', adjust it to the hash properly, otherwise
|
|
||||||
// we will try and retrieve 'api' as the hash when doing the /api/content route
|
|
||||||
if let Some(ref hash) = hash {
|
|
||||||
path.app_id = hash.to_owned();
|
|
||||||
}
|
|
||||||
|
|
||||||
trace!(target: "dapps", "Handling /api request: {:?}/{:?}", endpoint, hash);
|
|
||||||
match endpoint.as_ref().map(String::as_str) {
|
|
||||||
Some("ping") => Box::new(future::ok(response::ping(req))),
|
|
||||||
Some("health") => self.health(),
|
|
||||||
Some("content") => self.resolve_content(hash.as_ref().map(String::as_str), path, req),
|
|
||||||
_ => Box::new(future::ok(response::not_found())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RestApi {
|
|
||||||
pub fn new(
|
|
||||||
fetcher: Arc<Fetcher>,
|
|
||||||
health: NodeHealth,
|
|
||||||
) -> Box<Endpoint> {
|
|
||||||
Box::new(RestApi {
|
|
||||||
fetcher,
|
|
||||||
health,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve_content(&self, hash: Option<&str>, path: EndpointPath, req: Request) -> Response {
|
|
||||||
trace!(target: "dapps", "Resolving content: {:?} from path: {:?}", hash, path);
|
|
||||||
match hash {
|
|
||||||
Some(hash) if self.fetcher.contains(hash) => {
|
|
||||||
self.fetcher.respond(path, req)
|
|
||||||
},
|
|
||||||
_ => Box::new(future::ok(response::not_found())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn health(&self) -> Response {
|
|
||||||
Box::new(self.health.health()
|
|
||||||
.then(|health| {
|
|
||||||
let status = match health {
|
|
||||||
Ok(ref health) => {
|
|
||||||
if [&health.peers.status, &health.sync.status].iter().any(|x| *x != &HealthStatus::Ok) {
|
|
||||||
StatusCode::PreconditionFailed // HTTP 412
|
|
||||||
} else {
|
|
||||||
StatusCode::Ok // HTTP 200
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => StatusCode::ServiceUnavailable, // HTTP 503
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(response::as_json(status, &health).into())
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
// Copyright 2015-2018 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/>.
|
|
||||||
|
|
||||||
//! REST API
|
|
||||||
|
|
||||||
mod api;
|
|
||||||
mod response;
|
|
||||||
mod types;
|
|
||||||
|
|
||||||
pub use self::api::RestApi;
|
|
@ -1,43 +0,0 @@
|
|||||||
// Copyright 2015-2018 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 serde::Serialize;
|
|
||||||
use serde_json;
|
|
||||||
use hyper::{self, mime, StatusCode};
|
|
||||||
|
|
||||||
use handlers::{ContentHandler, EchoHandler};
|
|
||||||
|
|
||||||
pub fn empty() -> hyper::Response {
|
|
||||||
ContentHandler::ok("".into(), mime::TEXT_PLAIN).into()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_json<T: Serialize>(status: StatusCode, val: &T) -> hyper::Response {
|
|
||||||
let json = serde_json::to_string(val)
|
|
||||||
.expect("serialization to string is infallible; qed");
|
|
||||||
ContentHandler::new(status, json, mime::APPLICATION_JSON).into()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ping(req: hyper::Request) -> hyper::Response {
|
|
||||||
EchoHandler::new(req).into()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn not_found() -> hyper::Response {
|
|
||||||
as_json(StatusCode::NotFound, &::api::types::ApiError {
|
|
||||||
code: "404".into(),
|
|
||||||
title: "Not Found".into(),
|
|
||||||
detail: "Resource you requested has not been found.".into(),
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
// Copyright 2015-2018 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/>.
|
|
||||||
|
|
||||||
/// A structure representing any error in REST API.
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
|
||||||
#[serde(deny_unknown_fields)]
|
|
||||||
pub struct ApiError {
|
|
||||||
/// Error code.
|
|
||||||
pub code: String,
|
|
||||||
/// Human-readable error summary.
|
|
||||||
pub title: String,
|
|
||||||
/// More technical error details.
|
|
||||||
pub detail: String,
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
// Copyright 2015-2018 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/>.
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
|
||||||
#[serde(deny_unknown_fields)]
|
|
||||||
pub struct App {
|
|
||||||
pub id: Option<String>,
|
|
||||||
pub name: String,
|
|
||||||
pub description: String,
|
|
||||||
pub version: String,
|
|
||||||
pub author: String,
|
|
||||||
#[serde(rename="iconUrl")]
|
|
||||||
pub icon_url: String,
|
|
||||||
#[serde(rename="localUrl")]
|
|
||||||
pub local_url: Option<String>,
|
|
||||||
#[serde(rename="allowJsEval")]
|
|
||||||
pub allow_js_eval: Option<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl App {
|
|
||||||
pub fn with_id(&self, id: &str) -> Self {
|
|
||||||
let mut app = self.clone();
|
|
||||||
app.id = Some(id.into());
|
|
||||||
app
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,128 +0,0 @@
|
|||||||
// Copyright 2015-2018 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/>.
|
|
||||||
|
|
||||||
//! Fetchable Dapps support.
|
|
||||||
|
|
||||||
use std::fs;
|
|
||||||
|
|
||||||
use linked_hash_map::LinkedHashMap;
|
|
||||||
use page::local;
|
|
||||||
use handlers::FetchControl;
|
|
||||||
|
|
||||||
pub enum ContentStatus {
|
|
||||||
Fetching(FetchControl),
|
|
||||||
Ready(local::Dapp),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct ContentCache {
|
|
||||||
cache: LinkedHashMap<String, ContentStatus>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ContentCache {
|
|
||||||
pub fn insert(&mut self, content_id: String, status: ContentStatus) -> Option<ContentStatus> {
|
|
||||||
self.cache.insert(content_id, status)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove(&mut self, content_id: &str) -> Option<ContentStatus> {
|
|
||||||
self.cache.remove(content_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(&mut self, content_id: &str) -> Option<&mut ContentStatus> {
|
|
||||||
self.cache.get_refresh(content_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear_garbage(&mut self, expected_size: usize) -> Vec<(String, ContentStatus)> {
|
|
||||||
let len = self.cache.len();
|
|
||||||
|
|
||||||
if len <= expected_size {
|
|
||||||
return Vec::new();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut removed = Vec::with_capacity(len - expected_size);
|
|
||||||
|
|
||||||
while self.cache.len() > expected_size {
|
|
||||||
let entry = self.cache.pop_front().expect("expected_size bounded at 0, len is greater; qed");
|
|
||||||
|
|
||||||
match entry.1 {
|
|
||||||
ContentStatus::Fetching(ref fetch) => {
|
|
||||||
trace!(target: "dapps", "Aborting {} because of limit.", entry.0);
|
|
||||||
// Mark as aborted
|
|
||||||
fetch.abort()
|
|
||||||
},
|
|
||||||
ContentStatus::Ready(ref endpoint) => {
|
|
||||||
trace!(target: "dapps", "Removing {} because of limit.", entry.0);
|
|
||||||
// Remove path (dir or file)
|
|
||||||
let res = fs::remove_dir_all(&endpoint.path()).or_else(|_| fs::remove_file(&endpoint.path()));
|
|
||||||
if let Err(e) = res {
|
|
||||||
warn!(target: "dapps", "Unable to remove dapp/content from cache: {:?}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
removed.push(entry);
|
|
||||||
}
|
|
||||||
removed
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.cache.len()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
fn only_keys(data: Vec<(String, ContentStatus)>) -> Vec<String> {
|
|
||||||
data.into_iter().map(|x| x.0).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_remove_least_recently_used() {
|
|
||||||
// given
|
|
||||||
let mut cache = ContentCache::default();
|
|
||||||
cache.insert("a".into(), ContentStatus::Fetching(Default::default()));
|
|
||||||
cache.insert("b".into(), ContentStatus::Fetching(Default::default()));
|
|
||||||
cache.insert("c".into(), ContentStatus::Fetching(Default::default()));
|
|
||||||
|
|
||||||
// when
|
|
||||||
let res = cache.clear_garbage(2);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(cache.len(), 2);
|
|
||||||
assert_eq!(only_keys(res), vec!["a"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_update_lru_if_accessed() {
|
|
||||||
// given
|
|
||||||
let mut cache = ContentCache::default();
|
|
||||||
cache.insert("a".into(), ContentStatus::Fetching(Default::default()));
|
|
||||||
cache.insert("b".into(), ContentStatus::Fetching(Default::default()));
|
|
||||||
cache.insert("c".into(), ContentStatus::Fetching(Default::default()));
|
|
||||||
|
|
||||||
// when
|
|
||||||
cache.get("a");
|
|
||||||
let res = cache.clear_garbage(2);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(cache.len(), 2);
|
|
||||||
assert_eq!(only_keys(res), vec!["b"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,270 +0,0 @@
|
|||||||
// Copyright 2015-2018 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 zip;
|
|
||||||
use std::{fs, fmt};
|
|
||||||
use std::io::{self, Read, Write};
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use ethereum_types::H256;
|
|
||||||
use fetch;
|
|
||||||
use futures_cpupool::CpuPool;
|
|
||||||
use hash::keccak_pipe;
|
|
||||||
use mime_guess::Mime;
|
|
||||||
|
|
||||||
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest, serialize_manifest, Manifest};
|
|
||||||
use handlers::{ContentValidator, ValidatorResponse};
|
|
||||||
use page::{local, PageCache};
|
|
||||||
|
|
||||||
type OnDone = Box<Fn(Option<local::Dapp>) + Send>;
|
|
||||||
|
|
||||||
fn write_response_and_check_hash(
|
|
||||||
id: &str,
|
|
||||||
mut content_path: PathBuf,
|
|
||||||
filename: &str,
|
|
||||||
response: fetch::Response
|
|
||||||
) -> Result<(fs::File, PathBuf), ValidationError> {
|
|
||||||
// try to parse id
|
|
||||||
let id = id.parse().map_err(|_| ValidationError::InvalidContentId)?;
|
|
||||||
|
|
||||||
// check if content exists
|
|
||||||
if content_path.exists() {
|
|
||||||
warn!(target: "dapps", "Overwriting existing content at 0x{:?}", id);
|
|
||||||
fs::remove_dir_all(&content_path)?
|
|
||||||
}
|
|
||||||
|
|
||||||
// create directory
|
|
||||||
fs::create_dir_all(&content_path)?;
|
|
||||||
|
|
||||||
// append filename
|
|
||||||
content_path.push(filename);
|
|
||||||
|
|
||||||
// Now write the response
|
|
||||||
let mut file = io::BufWriter::new(fs::File::create(&content_path)?);
|
|
||||||
let mut reader = io::BufReader::new(fetch::BodyReader::new(response));
|
|
||||||
let hash = keccak_pipe(&mut reader, &mut file)?;
|
|
||||||
let mut file = file.into_inner()?;
|
|
||||||
file.flush()?;
|
|
||||||
|
|
||||||
// Validate hash
|
|
||||||
if id == hash {
|
|
||||||
// The writing above changed the file Read position, which we need later. So we just create a new file handle
|
|
||||||
// here.
|
|
||||||
Ok((fs::File::open(&content_path)?, content_path))
|
|
||||||
} else {
|
|
||||||
Err(ValidationError::HashMismatch {
|
|
||||||
expected: id,
|
|
||||||
got: hash,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Content {
|
|
||||||
id: String,
|
|
||||||
mime: Mime,
|
|
||||||
content_path: PathBuf,
|
|
||||||
on_done: OnDone,
|
|
||||||
pool: CpuPool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Content {
|
|
||||||
pub fn new(id: String, mime: Mime, content_path: PathBuf, on_done: OnDone, pool: CpuPool) -> Self {
|
|
||||||
Content {
|
|
||||||
id,
|
|
||||||
mime,
|
|
||||||
content_path,
|
|
||||||
on_done,
|
|
||||||
pool,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ContentValidator for Content {
|
|
||||||
type Error = ValidationError;
|
|
||||||
|
|
||||||
fn validate_and_install(self, response: fetch::Response) -> Result<ValidatorResponse, ValidationError> {
|
|
||||||
let pool = self.pool;
|
|
||||||
let id = self.id.clone();
|
|
||||||
let mime = self.mime;
|
|
||||||
let validate = move |content_path: PathBuf| {
|
|
||||||
// Create dir
|
|
||||||
let (_, content_path) = write_response_and_check_hash(&id, content_path, &id, response)?;
|
|
||||||
|
|
||||||
Ok(local::Dapp::single_file(pool, content_path, mime, PageCache::Enabled))
|
|
||||||
};
|
|
||||||
|
|
||||||
// Prepare path for a file
|
|
||||||
let content_path = self.content_path.join(&self.id);
|
|
||||||
// Make sure to always call on_done (even in case of errors)!
|
|
||||||
let result = validate(content_path.clone());
|
|
||||||
// remove the file if there was an error
|
|
||||||
if result.is_err() {
|
|
||||||
// Ignore errors since the file might not exist
|
|
||||||
let _ = fs::remove_dir_all(&content_path);
|
|
||||||
}
|
|
||||||
(self.on_done)(result.as_ref().ok().cloned());
|
|
||||||
result.map(ValidatorResponse::Local)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Dapp {
|
|
||||||
id: String,
|
|
||||||
dapps_path: PathBuf,
|
|
||||||
on_done: OnDone,
|
|
||||||
pool: CpuPool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Dapp {
|
|
||||||
pub fn new(id: String, dapps_path: PathBuf, on_done: OnDone, pool: CpuPool) -> Self {
|
|
||||||
Dapp {
|
|
||||||
id,
|
|
||||||
dapps_path,
|
|
||||||
on_done,
|
|
||||||
pool,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_manifest(zip: &mut zip::ZipArchive<fs::File>) -> Result<(Manifest, PathBuf), ValidationError> {
|
|
||||||
for i in 0..zip.len() {
|
|
||||||
let mut file = zip.by_index(i)?;
|
|
||||||
|
|
||||||
if !file.name().ends_with(MANIFEST_FILENAME) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to read manifest
|
|
||||||
let mut manifest = String::new();
|
|
||||||
let manifest = file
|
|
||||||
.read_to_string(&mut manifest).ok()
|
|
||||||
.and_then(|_| deserialize_manifest(manifest).ok());
|
|
||||||
|
|
||||||
if let Some(manifest) = manifest {
|
|
||||||
let mut manifest_location = PathBuf::from(file.name());
|
|
||||||
manifest_location.pop(); // get rid of filename
|
|
||||||
return Ok((manifest, manifest_location));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(ValidationError::ManifestNotFound)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ContentValidator for Dapp {
|
|
||||||
type Error = ValidationError;
|
|
||||||
|
|
||||||
fn validate_and_install(self, response: fetch::Response) -> Result<ValidatorResponse, ValidationError> {
|
|
||||||
let id = self.id.clone();
|
|
||||||
let pool = self.pool;
|
|
||||||
let validate = move |dapp_path: PathBuf| {
|
|
||||||
let (file, zip_path) = write_response_and_check_hash(&id, dapp_path.clone(), &format!("{}.zip", id), response)?;
|
|
||||||
trace!(target: "dapps", "Opening dapp bundle at {:?}", zip_path);
|
|
||||||
// Unpack archive
|
|
||||||
let mut zip = zip::ZipArchive::new(file)?;
|
|
||||||
// First find manifest file
|
|
||||||
let (mut manifest, manifest_dir) = Self::find_manifest(&mut zip)?;
|
|
||||||
// Overwrite id to match hash
|
|
||||||
manifest.id = Some(id);
|
|
||||||
|
|
||||||
// Unpack zip
|
|
||||||
for i in 0..zip.len() {
|
|
||||||
let mut file = zip.by_index(i)?;
|
|
||||||
let is_dir = file.name().chars().rev().next() == Some('/');
|
|
||||||
|
|
||||||
let file_path = PathBuf::from(file.name());
|
|
||||||
let location_in_manifest_base = file_path.strip_prefix(&manifest_dir);
|
|
||||||
// Create files that are inside manifest directory
|
|
||||||
if let Ok(location_in_manifest_base) = location_in_manifest_base {
|
|
||||||
let p = dapp_path.join(location_in_manifest_base);
|
|
||||||
// Check if it's a directory
|
|
||||||
if is_dir {
|
|
||||||
fs::create_dir_all(p)?;
|
|
||||||
} else {
|
|
||||||
let mut target = fs::File::create(p)?;
|
|
||||||
io::copy(&mut file, &mut target)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove zip
|
|
||||||
fs::remove_file(&zip_path)?;
|
|
||||||
|
|
||||||
// Write manifest
|
|
||||||
let manifest_str = serialize_manifest(&manifest).map_err(ValidationError::ManifestSerialization)?;
|
|
||||||
let manifest_path = dapp_path.join(MANIFEST_FILENAME);
|
|
||||||
let mut manifest_file = fs::File::create(manifest_path)?;
|
|
||||||
manifest_file.write_all(manifest_str.as_bytes())?;
|
|
||||||
// Create endpoint
|
|
||||||
let endpoint = local::Dapp::new(pool, dapp_path, manifest.into(), PageCache::Enabled);
|
|
||||||
Ok(endpoint)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Prepare directory for dapp
|
|
||||||
let target = self.dapps_path.join(&self.id);
|
|
||||||
// Validate the dapp
|
|
||||||
let result = validate(target.clone());
|
|
||||||
// remove the file if there was an error
|
|
||||||
if result.is_err() {
|
|
||||||
// Ignore errors since the file might not exist
|
|
||||||
let _ = fs::remove_dir_all(&target);
|
|
||||||
}
|
|
||||||
(self.on_done)(result.as_ref().ok().cloned());
|
|
||||||
result.map(ValidatorResponse::Local)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum ValidationError {
|
|
||||||
Io(io::Error),
|
|
||||||
Zip(zip::result::ZipError),
|
|
||||||
InvalidContentId,
|
|
||||||
ManifestNotFound,
|
|
||||||
ManifestSerialization(String),
|
|
||||||
HashMismatch { expected: H256, got: H256, },
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for ValidationError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
|
||||||
match *self {
|
|
||||||
ValidationError::Io(ref io) => write!(f, "Unexpected IO error occured: {:?}", io),
|
|
||||||
ValidationError::Zip(ref zip) => write!(f, "Unable to read ZIP archive: {:?}", zip),
|
|
||||||
ValidationError::InvalidContentId => write!(f, "ID is invalid. It should be 256 bits keccak hash of content."),
|
|
||||||
ValidationError::ManifestNotFound => write!(f, "Downloaded Dapp bundle did not contain valid manifest.json file."),
|
|
||||||
ValidationError::ManifestSerialization(ref err) => {
|
|
||||||
write!(f, "There was an error during Dapp Manifest serialization: {:?}", err)
|
|
||||||
},
|
|
||||||
ValidationError::HashMismatch { ref expected, ref got } => {
|
|
||||||
write!(f, "Hash of downloaded content did not match. Expected:{:?}, Got:{:?}.", expected, got)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<io::Error> for ValidationError {
|
|
||||||
fn from(err: io::Error) -> Self {
|
|
||||||
ValidationError::Io(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<zip::result::ZipError> for ValidationError {
|
|
||||||
fn from(err: zip::result::ZipError) -> Self {
|
|
||||||
ValidationError::Zip(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<io::IntoInnerError<io::BufWriter<fs::File>>> for ValidationError {
|
|
||||||
fn from(err: io::IntoInnerError<io::BufWriter<fs::File>>) -> Self {
|
|
||||||
ValidationError::Io(err.into())
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,329 +0,0 @@
|
|||||||
// Copyright 2015-2018 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/>.
|
|
||||||
|
|
||||||
//! Fetchable Dapps support.
|
|
||||||
//! Manages downloaded (cached) Dapps and downloads them when necessary.
|
|
||||||
//! Uses `URLHint` to resolve addresses into Dapps bundle file location.
|
|
||||||
|
|
||||||
mod installers;
|
|
||||||
|
|
||||||
use std::{fs, env};
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use futures::{future, Future};
|
|
||||||
use futures_cpupool::CpuPool;
|
|
||||||
use fetch::{Client as FetchClient, Fetch};
|
|
||||||
use hash_fetch::urlhint::{URLHintContract, URLHint, URLHintResult};
|
|
||||||
|
|
||||||
use hyper::StatusCode;
|
|
||||||
|
|
||||||
use ethereum_types::H256;
|
|
||||||
use {SyncStatus, random_filename};
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
use page::local;
|
|
||||||
use handlers::{ContentHandler, ContentFetcherHandler};
|
|
||||||
use endpoint::{self, Endpoint, EndpointPath};
|
|
||||||
use apps::cache::{ContentCache, ContentStatus};
|
|
||||||
|
|
||||||
/// Limit of cached dapps/content
|
|
||||||
const MAX_CACHED_DAPPS: usize = 20;
|
|
||||||
|
|
||||||
pub trait Fetcher: Endpoint + 'static {
|
|
||||||
fn contains(&self, content_id: &str) -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ContentFetcher<F: Fetch = FetchClient, R: URLHint + 'static = URLHintContract> {
|
|
||||||
cache_path: PathBuf,
|
|
||||||
resolver: R,
|
|
||||||
cache: Arc<Mutex<ContentCache>>,
|
|
||||||
sync: Arc<SyncStatus>,
|
|
||||||
fetch: F,
|
|
||||||
pool: CpuPool,
|
|
||||||
only_content: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: URLHint + 'static, F: Fetch> Drop for ContentFetcher<F, R> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
// Clear cache path
|
|
||||||
let _ = fs::remove_dir_all(&self.cache_path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: URLHint + 'static, F: Fetch> ContentFetcher<F, R> {
|
|
||||||
pub fn new(
|
|
||||||
resolver: R,
|
|
||||||
sync: Arc<SyncStatus>,
|
|
||||||
fetch: F,
|
|
||||||
pool: CpuPool,
|
|
||||||
) -> Self {
|
|
||||||
let mut cache_path = env::temp_dir();
|
|
||||||
cache_path.push(random_filename());
|
|
||||||
|
|
||||||
ContentFetcher {
|
|
||||||
cache_path,
|
|
||||||
resolver,
|
|
||||||
sync,
|
|
||||||
cache: Arc::new(Mutex::new(ContentCache::default())),
|
|
||||||
fetch,
|
|
||||||
pool,
|
|
||||||
only_content: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn allow_dapps(mut self, dapps: bool) -> Self {
|
|
||||||
self.only_content = !dapps;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn not_found() -> endpoint::Response {
|
|
||||||
Box::new(future::ok(ContentHandler::error(
|
|
||||||
StatusCode::NotFound,
|
|
||||||
"Resource Not Found",
|
|
||||||
"Requested resource was not found.",
|
|
||||||
None,
|
|
||||||
).into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn still_syncing() -> endpoint::Response {
|
|
||||||
Box::new(future::ok(ContentHandler::error(
|
|
||||||
StatusCode::ServiceUnavailable,
|
|
||||||
"Sync In Progress",
|
|
||||||
"Your node is still syncing. We cannot resolve any content before it's fully synced.",
|
|
||||||
Some("<a href=\"javascript:window.location.reload()\">Refresh</a>"),
|
|
||||||
).into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dapps_disabled() -> endpoint::Response {
|
|
||||||
Box::new(future::ok(ContentHandler::error(
|
|
||||||
StatusCode::ServiceUnavailable,
|
|
||||||
"Network Dapps Not Available",
|
|
||||||
"This interface doesn't support network dapps for security reasons.",
|
|
||||||
None,
|
|
||||||
).into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
fn set_status(&self, content_id: &str, status: ContentStatus) {
|
|
||||||
self.cache.lock().insert(content_id.to_owned(), status);
|
|
||||||
}
|
|
||||||
|
|
||||||
// resolve contract call synchronously.
|
|
||||||
// TODO: port to futures-based hyper and make it all async.
|
|
||||||
fn resolve(&self, content_id: H256) -> Option<URLHintResult> {
|
|
||||||
self.resolver.resolve(content_id)
|
|
||||||
.wait()
|
|
||||||
.unwrap_or_else(|e| { warn!("Error resolving content-id: {}", e); None })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: URLHint + 'static, F: Fetch> Fetcher for ContentFetcher<F, R> {
|
|
||||||
fn contains(&self, content_id: &str) -> bool {
|
|
||||||
{
|
|
||||||
let mut cache = self.cache.lock();
|
|
||||||
// Check if we already have the app
|
|
||||||
if cache.get(content_id).is_some() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// fallback to resolver
|
|
||||||
if let Ok(content_id) = content_id.parse() {
|
|
||||||
// if there is content or we are syncing return true
|
|
||||||
self.sync.is_major_importing() || self.resolve(content_id).is_some()
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: URLHint + 'static, F: Fetch> Endpoint for ContentFetcher<F, R> {
|
|
||||||
fn respond(&self, path: EndpointPath, req: endpoint::Request) -> endpoint::Response {
|
|
||||||
let mut cache = self.cache.lock();
|
|
||||||
let content_id = path.app_id.clone();
|
|
||||||
|
|
||||||
let (new_status, handler) = {
|
|
||||||
let status = cache.get(&content_id);
|
|
||||||
match status {
|
|
||||||
// Just serve the content
|
|
||||||
Some(&mut ContentStatus::Ready(ref endpoint)) => {
|
|
||||||
(None, endpoint.to_response(&path))
|
|
||||||
},
|
|
||||||
// Content is already being fetched
|
|
||||||
Some(&mut ContentStatus::Fetching(ref fetch_control)) if !fetch_control.is_deadline_reached() => {
|
|
||||||
trace!(target: "dapps", "Content fetching in progress. Waiting...");
|
|
||||||
(None, fetch_control.to_response(path))
|
|
||||||
},
|
|
||||||
// We need to start fetching the content
|
|
||||||
_ => {
|
|
||||||
trace!(target: "dapps", "Content unavailable. Fetching... {:?}", content_id);
|
|
||||||
let content_hex = content_id.parse().expect("to_handler is called only when `contains` returns true.");
|
|
||||||
let content = self.resolve(content_hex);
|
|
||||||
|
|
||||||
let cache = self.cache.clone();
|
|
||||||
let id = content_id.clone();
|
|
||||||
let on_done = move |result: Option<local::Dapp>| {
|
|
||||||
let mut cache = cache.lock();
|
|
||||||
match result {
|
|
||||||
Some(endpoint) => cache.insert(id.clone(), ContentStatus::Ready(endpoint)),
|
|
||||||
// In case of error
|
|
||||||
None => cache.remove(&id),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
match content {
|
|
||||||
// Don't serve dapps if we are still syncing (but serve content)
|
|
||||||
Some(URLHintResult::Dapp(_)) if self.sync.is_major_importing() => {
|
|
||||||
(None, Self::still_syncing())
|
|
||||||
},
|
|
||||||
Some(URLHintResult::Dapp(_)) if self.only_content => {
|
|
||||||
(None, Self::dapps_disabled())
|
|
||||||
},
|
|
||||||
Some(content) => {
|
|
||||||
let handler = match content {
|
|
||||||
URLHintResult::Dapp(dapp) => {
|
|
||||||
ContentFetcherHandler::new(
|
|
||||||
req.method(),
|
|
||||||
&dapp.url(),
|
|
||||||
path,
|
|
||||||
installers::Dapp::new(
|
|
||||||
content_id.clone(),
|
|
||||||
self.cache_path.clone(),
|
|
||||||
Box::new(on_done),
|
|
||||||
self.pool.clone(),
|
|
||||||
),
|
|
||||||
self.fetch.clone(),
|
|
||||||
self.pool.clone(),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
URLHintResult::GithubDapp(content) => {
|
|
||||||
ContentFetcherHandler::new(
|
|
||||||
req.method(),
|
|
||||||
&content.url,
|
|
||||||
path,
|
|
||||||
installers::Dapp::new(
|
|
||||||
content_id.clone(),
|
|
||||||
self.cache_path.clone(),
|
|
||||||
Box::new(on_done),
|
|
||||||
self.pool.clone(),
|
|
||||||
),
|
|
||||||
self.fetch.clone(),
|
|
||||||
self.pool.clone(),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
URLHintResult::Content(content) => {
|
|
||||||
ContentFetcherHandler::new(
|
|
||||||
req.method(),
|
|
||||||
&content.url,
|
|
||||||
path,
|
|
||||||
installers::Content::new(
|
|
||||||
content_id.clone(),
|
|
||||||
content.mime,
|
|
||||||
self.cache_path.clone(),
|
|
||||||
Box::new(on_done),
|
|
||||||
self.pool.clone(),
|
|
||||||
),
|
|
||||||
self.fetch.clone(),
|
|
||||||
self.pool.clone(),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
(Some(ContentStatus::Fetching(handler.fetch_control())), Box::new(handler) as endpoint::Response)
|
|
||||||
},
|
|
||||||
None if self.sync.is_major_importing() => {
|
|
||||||
(None, Self::still_syncing())
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
// This may happen when sync status changes in between
|
|
||||||
// `contains` and `to_handler`
|
|
||||||
(None, Self::not_found())
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(status) = new_status {
|
|
||||||
cache.clear_garbage(MAX_CACHED_DAPPS);
|
|
||||||
cache.insert(content_id, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
handler
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use std::env;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use fetch::Client;
|
|
||||||
use futures::{future, Future};
|
|
||||||
use hash_fetch::urlhint::{URLHint, URLHintResult};
|
|
||||||
use ethereum_types::H256;
|
|
||||||
|
|
||||||
use apps::cache::ContentStatus;
|
|
||||||
use endpoint::EndpointInfo;
|
|
||||||
use page::local;
|
|
||||||
use super::{ContentFetcher, Fetcher};
|
|
||||||
use {SyncStatus};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct FakeResolver;
|
|
||||||
impl URLHint for FakeResolver {
|
|
||||||
fn resolve(&self, _id: H256) -> Box<Future<Item = Option<URLHintResult>, Error = String> + Send> {
|
|
||||||
Box::new(future::ok(None))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct FakeSync(bool);
|
|
||||||
impl SyncStatus for FakeSync {
|
|
||||||
fn is_major_importing(&self) -> bool { self.0 }
|
|
||||||
fn peers(&self) -> (usize, usize) { (0, 5) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_true_if_contains_the_app() {
|
|
||||||
// given
|
|
||||||
let pool = ::futures_cpupool::CpuPool::new(1);
|
|
||||||
let path = env::temp_dir();
|
|
||||||
let fetcher = ContentFetcher::new(
|
|
||||||
FakeResolver,
|
|
||||||
Arc::new(FakeSync(false)),
|
|
||||||
Client::new().unwrap(),
|
|
||||||
pool.clone(),
|
|
||||||
).allow_dapps(true);
|
|
||||||
|
|
||||||
let handler = local::Dapp::new(pool, path, EndpointInfo {
|
|
||||||
id: None,
|
|
||||||
name: "fake".into(),
|
|
||||||
description: "".into(),
|
|
||||||
version: "".into(),
|
|
||||||
author: "".into(),
|
|
||||||
icon_url: "".into(),
|
|
||||||
local_url: Some("".into()),
|
|
||||||
allow_js_eval: None,
|
|
||||||
}, Default::default());
|
|
||||||
|
|
||||||
// when
|
|
||||||
fetcher.set_status("test", ContentStatus::Ready(handler));
|
|
||||||
fetcher.set_status("test2", ContentStatus::Fetching(Default::default()));
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(fetcher.contains("test"), true);
|
|
||||||
assert_eq!(fetcher.contains("test2"), true);
|
|
||||||
assert_eq!(fetcher.contains("test3"), false);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,136 +0,0 @@
|
|||||||
// Copyright 2015-2018 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 std::collections::BTreeMap;
|
|
||||||
use std::io;
|
|
||||||
use std::io::Read;
|
|
||||||
use std::fs;
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
use futures_cpupool::CpuPool;
|
|
||||||
|
|
||||||
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest};
|
|
||||||
use endpoint::{Endpoint, EndpointInfo};
|
|
||||||
use page::{local, PageCache};
|
|
||||||
|
|
||||||
struct LocalDapp {
|
|
||||||
id: String,
|
|
||||||
path: PathBuf,
|
|
||||||
info: EndpointInfo,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tries to find and read manifest file in given `path` to extract `EndpointInfo`
|
|
||||||
/// If manifest is not found sensible default `EndpointInfo` is returned based on given `name`.
|
|
||||||
fn read_manifest(name: &str, mut path: PathBuf) -> EndpointInfo {
|
|
||||||
path.push(MANIFEST_FILENAME);
|
|
||||||
|
|
||||||
fs::File::open(path.clone())
|
|
||||||
.map_err(|e| format!("{:?}", e))
|
|
||||||
.and_then(|mut f| {
|
|
||||||
// Reat file
|
|
||||||
let mut s = String::new();
|
|
||||||
f.read_to_string(&mut s).map_err(|e| format!("{:?}", e))?;
|
|
||||||
// Try to deserialize manifest
|
|
||||||
deserialize_manifest(s)
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|e| {
|
|
||||||
warn!(target: "dapps", "Cannot read manifest file at: {:?}. Error: {:?}", path, e);
|
|
||||||
|
|
||||||
EndpointInfo {
|
|
||||||
id: None,
|
|
||||||
name: name.into(),
|
|
||||||
description: name.into(),
|
|
||||||
version: "0.0.0".into(),
|
|
||||||
author: "?".into(),
|
|
||||||
icon_url: "icon.png".into(),
|
|
||||||
local_url: None,
|
|
||||||
allow_js_eval: Some(false),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns Dapp Id and Local Dapp Endpoint for given filesystem path.
|
|
||||||
/// Parses the path to extract last component (for name).
|
|
||||||
/// `None` is returned when path is invalid or non-existent.
|
|
||||||
pub fn local_endpoint<P: AsRef<Path>>(path: P, pool: CpuPool) -> Option<(String, Box<local::Dapp>)> {
|
|
||||||
let path = path.as_ref().to_owned();
|
|
||||||
path.canonicalize().ok().and_then(|path| {
|
|
||||||
let name = path.file_name().and_then(|name| name.to_str());
|
|
||||||
name.map(|name| {
|
|
||||||
let dapp = local_dapp(name.into(), path.clone());
|
|
||||||
(dapp.id, Box::new(local::Dapp::new(
|
|
||||||
pool.clone(), dapp.path, dapp.info, PageCache::Disabled)
|
|
||||||
))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn local_dapp(name: String, path: PathBuf) -> LocalDapp {
|
|
||||||
// try to get manifest file
|
|
||||||
let info = read_manifest(&name, path.clone());
|
|
||||||
LocalDapp {
|
|
||||||
id: name,
|
|
||||||
path: path,
|
|
||||||
info: info,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns endpoints for Local Dapps found for given filesystem path.
|
|
||||||
/// Scans the directory and collects `local::Dapp`.
|
|
||||||
pub fn local_endpoints<P: AsRef<Path>>(dapps_path: P, pool: CpuPool) -> BTreeMap<String, Box<Endpoint>> {
|
|
||||||
let mut pages = BTreeMap::<String, Box<Endpoint>>::new();
|
|
||||||
for dapp in local_dapps(dapps_path.as_ref()) {
|
|
||||||
pages.insert(
|
|
||||||
dapp.id,
|
|
||||||
Box::new(local::Dapp::new(pool.clone(), dapp.path, dapp.info, PageCache::Disabled))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
pages
|
|
||||||
}
|
|
||||||
|
|
||||||
fn local_dapps(dapps_path: &Path) -> Vec<LocalDapp> {
|
|
||||||
let files = fs::read_dir(dapps_path);
|
|
||||||
if let Err(e) = files {
|
|
||||||
warn!(target: "dapps", "Unable to load local dapps from: {}. Reason: {:?}", dapps_path.display(), e);
|
|
||||||
return vec![];
|
|
||||||
}
|
|
||||||
|
|
||||||
let files = files.expect("Check is done earlier");
|
|
||||||
files.map(|dir| {
|
|
||||||
let entry = dir?;
|
|
||||||
let file_type = entry.file_type()?;
|
|
||||||
|
|
||||||
// skip files
|
|
||||||
if file_type.is_file() {
|
|
||||||
return Err(io::Error::new(io::ErrorKind::NotFound, "Not a file"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// take directory name and path
|
|
||||||
entry.file_name().into_string()
|
|
||||||
.map(|name| (name, entry.path()))
|
|
||||||
.map_err(|e| {
|
|
||||||
info!(target: "dapps", "Unable to load dapp: {:?}. Reason: {:?}", entry.path(), e);
|
|
||||||
io::Error::new(io::ErrorKind::NotFound, "Invalid name")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.filter_map(|m| {
|
|
||||||
if let Err(ref e) = m {
|
|
||||||
debug!(target: "dapps", "Ignoring local dapp: {:?}", e);
|
|
||||||
}
|
|
||||||
m.ok()
|
|
||||||
})
|
|
||||||
.map(|(name, path)| local_dapp(name, path))
|
|
||||||
.collect()
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
// Copyright 2015-2018 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 serde_json;
|
|
||||||
pub use apps::App as Manifest;
|
|
||||||
|
|
||||||
pub const MANIFEST_FILENAME: &'static str = "manifest.json";
|
|
||||||
|
|
||||||
pub fn deserialize_manifest(manifest: String) -> Result<Manifest, String> {
|
|
||||||
let mut manifest = serde_json::from_str::<Manifest>(&manifest).map_err(|e| format!("{:?}", e))?;
|
|
||||||
if manifest.id.is_none() {
|
|
||||||
return Err("App 'id' is missing.".into());
|
|
||||||
}
|
|
||||||
manifest.allow_js_eval = Some(manifest.allow_js_eval.unwrap_or(false));
|
|
||||||
|
|
||||||
Ok(manifest)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn serialize_manifest(manifest: &Manifest) -> Result<String, String> {
|
|
||||||
serde_json::to_string_pretty(manifest).map_err(|e| format!("{:?}", e))
|
|
||||||
}
|
|
@ -1,70 +0,0 @@
|
|||||||
// Copyright 2015-2018 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 std::path::PathBuf;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use endpoint::Endpoints;
|
|
||||||
use futures_cpupool::CpuPool;
|
|
||||||
use proxypac::ProxyPac;
|
|
||||||
use web::Web;
|
|
||||||
use fetch::Fetch;
|
|
||||||
use WebProxyTokens;
|
|
||||||
|
|
||||||
mod app;
|
|
||||||
mod cache;
|
|
||||||
pub mod fs;
|
|
||||||
pub mod fetcher;
|
|
||||||
pub mod manifest;
|
|
||||||
|
|
||||||
pub use self::app::App;
|
|
||||||
|
|
||||||
pub const HOME_PAGE: &'static str = "home";
|
|
||||||
pub const RPC_PATH: &'static str = "rpc";
|
|
||||||
pub const API_PATH: &'static str = "api";
|
|
||||||
pub const WEB_PATH: &'static str = "web";
|
|
||||||
pub const URL_REFERER: &'static str = "__referer=";
|
|
||||||
|
|
||||||
pub fn all_endpoints<F: Fetch>(
|
|
||||||
dapps_path: PathBuf,
|
|
||||||
extra_dapps: Vec<PathBuf>,
|
|
||||||
dapps_domain: &str,
|
|
||||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
|
||||||
fetch: F,
|
|
||||||
pool: CpuPool,
|
|
||||||
) -> (Vec<String>, Endpoints) {
|
|
||||||
// fetch fs dapps at first to avoid overwriting builtins
|
|
||||||
let mut pages = fs::local_endpoints(dapps_path.clone(), pool.clone());
|
|
||||||
let local_endpoints: Vec<String> = pages.keys().cloned().collect();
|
|
||||||
for path in extra_dapps {
|
|
||||||
if let Some((id, endpoint)) = fs::local_endpoint(path.clone(), pool.clone()) {
|
|
||||||
pages.insert(id, endpoint);
|
|
||||||
} else {
|
|
||||||
warn!(target: "dapps", "Ignoring invalid dapp at {}", path.display());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pages.insert(
|
|
||||||
"proxy".into(),
|
|
||||||
ProxyPac::boxed(dapps_domain.to_owned())
|
|
||||||
);
|
|
||||||
pages.insert(
|
|
||||||
WEB_PATH.into(),
|
|
||||||
Web::boxed(web_proxy_tokens.clone(), fetch.clone(), pool.clone())
|
|
||||||
);
|
|
||||||
|
|
||||||
(local_endpoints, pages)
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
// Copyright 2015-2018 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/>.
|
|
||||||
|
|
||||||
//! URL Endpoint traits
|
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
|
|
||||||
use futures::Future;
|
|
||||||
use hyper;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Default, Clone)]
|
|
||||||
pub struct EndpointPath {
|
|
||||||
pub app_id: String,
|
|
||||||
pub app_params: Vec<String>,
|
|
||||||
pub query: Option<String>,
|
|
||||||
pub host: String,
|
|
||||||
pub port: u16,
|
|
||||||
pub using_dapps_domains: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EndpointPath {
|
|
||||||
pub fn has_no_params(&self) -> bool {
|
|
||||||
self.app_params.is_empty() || self.app_params.iter().all(|x| x.is_empty())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type EndpointInfo = ::apps::App;
|
|
||||||
pub type Endpoints = BTreeMap<String, Box<Endpoint>>;
|
|
||||||
pub type Response = Box<Future<Item=hyper::Response, Error=hyper::Error> + Send>;
|
|
||||||
pub type Request = hyper::Request;
|
|
||||||
|
|
||||||
pub trait Endpoint : Send + Sync {
|
|
||||||
fn info(&self) -> Option<&EndpointInfo> { None }
|
|
||||||
|
|
||||||
fn respond(&self, path: EndpointPath, req: Request) -> Response;
|
|
||||||
}
|
|
@ -1,102 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width">
|
|
||||||
<title>{title}</title>
|
|
||||||
<style>
|
|
||||||
:root, :root body {{
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
border: 0;
|
|
||||||
font-size: 100%;
|
|
||||||
font: inherit;
|
|
||||||
vertical-align: baseline;
|
|
||||||
background: rgb(95, 95, 95);
|
|
||||||
color: rgba(255, 255, 255, 0.75);
|
|
||||||
font-size: 16px;
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-weight: 300;
|
|
||||||
}}
|
|
||||||
|
|
||||||
:root a, :root a:visited {{
|
|
||||||
text-decoration: none;
|
|
||||||
cursor: pointer;
|
|
||||||
color: rgb(0, 151, 167); /* #f80 */
|
|
||||||
}}
|
|
||||||
|
|
||||||
:root a:hover {{
|
|
||||||
color: rgb(0, 174, 193);
|
|
||||||
}}
|
|
||||||
|
|
||||||
h1,h2,h3,h4,h5,h6 {{
|
|
||||||
font-weight: 300;
|
|
||||||
text-transform: uppercase;
|
|
||||||
text-decoration: none;
|
|
||||||
}}
|
|
||||||
|
|
||||||
h1 {{
|
|
||||||
font-size: 24px;
|
|
||||||
line-height: 36px;
|
|
||||||
color: rgb(0, 151, 167);
|
|
||||||
}}
|
|
||||||
|
|
||||||
h2 {{
|
|
||||||
font-size: 20px;
|
|
||||||
line-height: 34px;
|
|
||||||
}}
|
|
||||||
|
|
||||||
code,kbd,pre,samp {{
|
|
||||||
font-family: monospace;
|
|
||||||
}}
|
|
||||||
|
|
||||||
.parity-navbar {{
|
|
||||||
background: rgb(65, 65, 65);
|
|
||||||
height: 72px;
|
|
||||||
padding: 0 1rem;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
}}
|
|
||||||
|
|
||||||
.parity-status {{
|
|
||||||
clear: both;
|
|
||||||
padding: 1rem;
|
|
||||||
margin: 1rem 0;
|
|
||||||
text-align: right;
|
|
||||||
opacity: 0.75;
|
|
||||||
}}
|
|
||||||
|
|
||||||
.parity-box {{
|
|
||||||
margin: 1rem;
|
|
||||||
padding: 1rem;
|
|
||||||
background-color: rgb(48, 48, 48);
|
|
||||||
box-sizing: border-box;
|
|
||||||
box-shadow: rgba(0, 0, 0, 0.117647) 0px 1px 6px, rgba(0, 0, 0, 0.117647) 0px 1px 4px;
|
|
||||||
border-radius: 2px;
|
|
||||||
z-index: 1;
|
|
||||||
color: #aaa;
|
|
||||||
}}
|
|
||||||
|
|
||||||
.parity-box h1,
|
|
||||||
.parity-box h2,
|
|
||||||
.parity-box h3,
|
|
||||||
.parity-box h4,
|
|
||||||
.parity-box h5,
|
|
||||||
.parity-box h6 {{
|
|
||||||
margin: 0;
|
|
||||||
}}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="parity-navbar">
|
|
||||||
</div>
|
|
||||||
<div class="parity-box">
|
|
||||||
<h1>{title}</h1>
|
|
||||||
<h3>{message}</h3>
|
|
||||||
<p><code>{details}</code></p>
|
|
||||||
</div>
|
|
||||||
<div class="parity-status">
|
|
||||||
<small>{version}</small>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,79 +0,0 @@
|
|||||||
// Copyright 2015-2018 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/>.
|
|
||||||
|
|
||||||
//! Simple Content Handler
|
|
||||||
|
|
||||||
use hyper::{self, mime, header};
|
|
||||||
use hyper::StatusCode;
|
|
||||||
|
|
||||||
use parity_version::version;
|
|
||||||
|
|
||||||
use handlers::add_security_headers;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct ContentHandler {
|
|
||||||
code: StatusCode,
|
|
||||||
content: String,
|
|
||||||
mimetype: mime::Mime,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ContentHandler {
|
|
||||||
pub fn ok(content: String, mimetype: mime::Mime) -> Self {
|
|
||||||
Self::new(StatusCode::Ok, content, mimetype)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn html(code: StatusCode, content: String) -> Self {
|
|
||||||
Self::new(code, content, mime::TEXT_HTML)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn error(
|
|
||||||
code: StatusCode,
|
|
||||||
title: &str,
|
|
||||||
message: &str,
|
|
||||||
details: Option<&str>,
|
|
||||||
) -> Self {
|
|
||||||
Self::html(code, format!(
|
|
||||||
include_str!("../error_tpl.html"),
|
|
||||||
title=title,
|
|
||||||
message=message,
|
|
||||||
details=details.unwrap_or_else(|| ""),
|
|
||||||
version=version(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new(
|
|
||||||
code: StatusCode,
|
|
||||||
content: String,
|
|
||||||
mimetype: mime::Mime,
|
|
||||||
) -> Self {
|
|
||||||
ContentHandler {
|
|
||||||
code,
|
|
||||||
content,
|
|
||||||
mimetype,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<hyper::Response> for ContentHandler {
|
|
||||||
fn into(self) -> hyper::Response {
|
|
||||||
let mut res = hyper::Response::new()
|
|
||||||
.with_status(self.code)
|
|
||||||
.with_header(header::ContentType(self.mimetype))
|
|
||||||
.with_body(self.content);
|
|
||||||
add_security_headers(&mut res.headers_mut(), false);
|
|
||||||
res
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
// Copyright 2015-2018 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/>.
|
|
||||||
|
|
||||||
//! Echo Handler
|
|
||||||
|
|
||||||
use hyper::{self, header};
|
|
||||||
|
|
||||||
use handlers::add_security_headers;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct EchoHandler {
|
|
||||||
request: hyper::Request,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EchoHandler {
|
|
||||||
pub fn new(request: hyper::Request) -> Self {
|
|
||||||
EchoHandler {
|
|
||||||
request,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<hyper::Response> for EchoHandler {
|
|
||||||
fn into(self) -> hyper::Response {
|
|
||||||
let content_type = self.request.headers().get().cloned();
|
|
||||||
let mut res = hyper::Response::new()
|
|
||||||
.with_header(content_type.unwrap_or(header::ContentType::json()))
|
|
||||||
.with_body(self.request.body());
|
|
||||||
|
|
||||||
add_security_headers(res.headers_mut(), false);
|
|
||||||
res
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
// Copyright 2015-2018 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/>.
|
|
||||||
|
|
||||||
//! Handler errors.
|
|
||||||
|
|
||||||
use handlers::{ContentHandler, FETCH_TIMEOUT};
|
|
||||||
use hyper::StatusCode;
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
pub fn streaming() -> ContentHandler {
|
|
||||||
ContentHandler::error(
|
|
||||||
StatusCode::BadGateway,
|
|
||||||
"Streaming Error",
|
|
||||||
"This content is being streamed in other place.",
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn download_error<E: fmt::Debug>(e: E) -> ContentHandler {
|
|
||||||
ContentHandler::error(
|
|
||||||
StatusCode::BadGateway,
|
|
||||||
"Download Error",
|
|
||||||
"There was an error when fetching the content.",
|
|
||||||
Some(&format!("{:?}", e)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn invalid_content<E: fmt::Debug>(e: E) -> ContentHandler {
|
|
||||||
ContentHandler::error(
|
|
||||||
StatusCode::BadGateway,
|
|
||||||
"Invalid Dapp",
|
|
||||||
"Downloaded bundle does not contain a valid content.",
|
|
||||||
Some(&format!("{:?}", e)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn timeout_error() -> ContentHandler {
|
|
||||||
ContentHandler::error(
|
|
||||||
StatusCode::GatewayTimeout,
|
|
||||||
"Download Timeout",
|
|
||||||
&format!("Could not fetch content within {} seconds.", FETCH_TIMEOUT.as_secs()),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn method_not_allowed() -> ContentHandler {
|
|
||||||
ContentHandler::error(
|
|
||||||
StatusCode::MethodNotAllowed,
|
|
||||||
"Method Not Allowed",
|
|
||||||
"Only <code>GET</code> requests are allowed.",
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,305 +0,0 @@
|
|||||||
// Copyright 2015-2018 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/>.
|
|
||||||
|
|
||||||
//! Hyper Server Handler that fetches a file during a request (proxy).
|
|
||||||
|
|
||||||
use std::{fmt, mem};
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
|
||||||
use std::time::Instant;
|
|
||||||
use fetch::{self, Fetch};
|
|
||||||
use futures::sync::oneshot;
|
|
||||||
use futures::{self, Future};
|
|
||||||
use futures_cpupool::CpuPool;
|
|
||||||
use hyper;
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
|
|
||||||
use endpoint::{self, EndpointPath};
|
|
||||||
use handlers::{ContentHandler, StreamingHandler, FETCH_TIMEOUT, errors};
|
|
||||||
use page::local;
|
|
||||||
|
|
||||||
pub enum ValidatorResponse {
|
|
||||||
Local(local::Dapp),
|
|
||||||
Streaming(StreamingHandler<fetch::BodyReader>),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ContentValidator: Sized + Send + 'static {
|
|
||||||
type Error: fmt::Debug + fmt::Display;
|
|
||||||
|
|
||||||
fn validate_and_install(self, fetch::Response) -> Result<ValidatorResponse, Self::Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct FetchControl {
|
|
||||||
abort: Arc<AtomicBool>,
|
|
||||||
listeners: Arc<Mutex<Vec<oneshot::Sender<WaitResult>>>>,
|
|
||||||
deadline: Instant,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for FetchControl {
|
|
||||||
fn default() -> Self {
|
|
||||||
FetchControl {
|
|
||||||
abort: Arc::new(AtomicBool::new(false)),
|
|
||||||
listeners: Arc::new(Mutex::new(Vec::new())),
|
|
||||||
deadline: Instant::now() + FETCH_TIMEOUT,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FetchControl {
|
|
||||||
pub fn is_deadline_reached(&self) -> bool {
|
|
||||||
self.deadline < Instant::now()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn abort(&self) {
|
|
||||||
self.abort.store(true, Ordering::SeqCst);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_response(&self, path: EndpointPath) -> endpoint::Response {
|
|
||||||
let (tx, receiver) = oneshot::channel();
|
|
||||||
self.listeners.lock().push(tx);
|
|
||||||
|
|
||||||
Box::new(WaitingHandler {
|
|
||||||
path,
|
|
||||||
state: WaitState::Waiting(receiver),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn notify<F: Fn() -> WaitResult>(&self, status: F) {
|
|
||||||
let mut listeners = self.listeners.lock();
|
|
||||||
for sender in listeners.drain(..) {
|
|
||||||
trace!(target: "dapps", "Resuming request waiting for content...");
|
|
||||||
if let Err(_) = sender.send(status()) {
|
|
||||||
trace!(target: "dapps", "Waiting listener notification failed.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_status(&self, status: &FetchState) {
|
|
||||||
match *status {
|
|
||||||
FetchState::Error(ref handler) => self.notify(|| WaitResult::Error(handler.clone())),
|
|
||||||
FetchState::Done(ref endpoint, _) => self.notify(|| WaitResult::Done(endpoint.clone())),
|
|
||||||
FetchState::Streaming(_) => self.notify(|| WaitResult::NonAwaitable),
|
|
||||||
FetchState::InProgress(_) => {},
|
|
||||||
FetchState::Empty => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum WaitState {
|
|
||||||
Waiting(oneshot::Receiver<WaitResult>),
|
|
||||||
Done(endpoint::Response),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum WaitResult {
|
|
||||||
Error(ContentHandler),
|
|
||||||
Done(local::Dapp),
|
|
||||||
NonAwaitable,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct WaitingHandler {
|
|
||||||
path: EndpointPath,
|
|
||||||
state: WaitState,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Future for WaitingHandler {
|
|
||||||
type Item = hyper::Response;
|
|
||||||
type Error = hyper::Error;
|
|
||||||
|
|
||||||
fn poll(&mut self) -> futures::Poll<Self::Item, Self::Error> {
|
|
||||||
loop {
|
|
||||||
let new_state = match self.state {
|
|
||||||
WaitState::Waiting(ref mut receiver) => {
|
|
||||||
let result = try_ready!(receiver.poll().map_err(|_| hyper::Error::Timeout));
|
|
||||||
|
|
||||||
match result {
|
|
||||||
WaitResult::Error(handler) => {
|
|
||||||
return Ok(futures::Async::Ready(handler.into()));
|
|
||||||
},
|
|
||||||
WaitResult::NonAwaitable => {
|
|
||||||
return Ok(futures::Async::Ready(errors::streaming().into()));
|
|
||||||
},
|
|
||||||
WaitResult::Done(endpoint) => {
|
|
||||||
WaitState::Done(endpoint.to_response(&self.path).into())
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
WaitState::Done(ref mut response) => {
|
|
||||||
return response.poll()
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
self.state = new_state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum FetchState {
|
|
||||||
Error(ContentHandler),
|
|
||||||
InProgress(Box<Future<Item=FetchState, Error=()> + Send>),
|
|
||||||
Streaming(hyper::Response),
|
|
||||||
Done(local::Dapp, endpoint::Response),
|
|
||||||
Empty,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for FetchState {
|
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
use self::FetchState::*;
|
|
||||||
|
|
||||||
write!(fmt, "FetchState(")?;
|
|
||||||
match *self {
|
|
||||||
Error(ref error) => write!(fmt, "error: {:?}", error),
|
|
||||||
InProgress(_) => write!(fmt, "in progress"),
|
|
||||||
Streaming(ref res) => write!(fmt, "streaming: {:?}", res),
|
|
||||||
Done(ref endpoint, _) => write!(fmt, "done: {:?}", endpoint),
|
|
||||||
Empty => write!(fmt, "?"),
|
|
||||||
}?;
|
|
||||||
write!(fmt, ")")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ContentFetcherHandler {
|
|
||||||
fetch_control: FetchControl,
|
|
||||||
status: FetchState,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ContentFetcherHandler {
|
|
||||||
pub fn fetch_control(&self) -> FetchControl {
|
|
||||||
self.fetch_control.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new<H: ContentValidator, F: Fetch>(
|
|
||||||
method: &hyper::Method,
|
|
||||||
url: &str,
|
|
||||||
path: EndpointPath,
|
|
||||||
installer: H,
|
|
||||||
fetch: F,
|
|
||||||
pool: CpuPool,
|
|
||||||
) -> Self {
|
|
||||||
let fetch_control = FetchControl::default();
|
|
||||||
|
|
||||||
// Validation of method
|
|
||||||
let status = match *method {
|
|
||||||
// Start fetching content
|
|
||||||
hyper::Method::Get => {
|
|
||||||
trace!(target: "dapps", "Fetching content from: {:?}", url);
|
|
||||||
FetchState::InProgress(Self::fetch_content(
|
|
||||||
pool,
|
|
||||||
fetch,
|
|
||||||
url,
|
|
||||||
fetch_control.abort.clone(),
|
|
||||||
path,
|
|
||||||
installer,
|
|
||||||
))
|
|
||||||
},
|
|
||||||
// or return error
|
|
||||||
_ => FetchState::Error(errors::method_not_allowed()),
|
|
||||||
};
|
|
||||||
|
|
||||||
ContentFetcherHandler {
|
|
||||||
fetch_control,
|
|
||||||
status,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fetch_content<H: ContentValidator, F: Fetch>(
|
|
||||||
pool: CpuPool,
|
|
||||||
fetch: F,
|
|
||||||
url: &str,
|
|
||||||
abort: Arc<AtomicBool>,
|
|
||||||
path: EndpointPath,
|
|
||||||
installer: H,
|
|
||||||
) -> Box<Future<Item=FetchState, Error=()> + Send> {
|
|
||||||
// Start fetching the content
|
|
||||||
let pool2 = pool.clone();
|
|
||||||
let future = fetch.get(url, abort.into()).then(move |result| {
|
|
||||||
trace!(target: "dapps", "Fetching content finished. Starting validation: {:?}", result);
|
|
||||||
Ok(match result {
|
|
||||||
Ok(response) => match installer.validate_and_install(response) {
|
|
||||||
Ok(ValidatorResponse::Local(endpoint)) => {
|
|
||||||
trace!(target: "dapps", "Validation OK. Returning response.");
|
|
||||||
let response = endpoint.to_response(&path);
|
|
||||||
FetchState::Done(endpoint, response)
|
|
||||||
},
|
|
||||||
Ok(ValidatorResponse::Streaming(stream)) => {
|
|
||||||
trace!(target: "dapps", "Validation OK. Streaming response.");
|
|
||||||
let (reading, response) = stream.into_response();
|
|
||||||
pool.spawn(reading).forget();
|
|
||||||
FetchState::Streaming(response)
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
trace!(target: "dapps", "Error while validating content: {:?}", e);
|
|
||||||
FetchState::Error(errors::invalid_content(e))
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
warn!(target: "dapps", "Unable to fetch content: {:?}", e);
|
|
||||||
FetchState::Error(errors::download_error(e))
|
|
||||||
},
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
// make sure to run within fetch thread pool.
|
|
||||||
Box::new(pool2.spawn(future))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Future for ContentFetcherHandler {
|
|
||||||
type Item = hyper::Response;
|
|
||||||
type Error = hyper::Error;
|
|
||||||
|
|
||||||
fn poll(&mut self) -> futures::Poll<Self::Item, Self::Error> {
|
|
||||||
loop {
|
|
||||||
trace!(target: "dapps", "Polling status: {:?}", self.status);
|
|
||||||
self.status = match mem::replace(&mut self.status, FetchState::Empty) {
|
|
||||||
FetchState::Error(error) => {
|
|
||||||
return Ok(futures::Async::Ready(error.into()));
|
|
||||||
},
|
|
||||||
FetchState::Streaming(response) => {
|
|
||||||
return Ok(futures::Async::Ready(response));
|
|
||||||
},
|
|
||||||
any => any,
|
|
||||||
};
|
|
||||||
|
|
||||||
let status = match self.status {
|
|
||||||
// Request may time out
|
|
||||||
FetchState::InProgress(_) if self.fetch_control.is_deadline_reached() => {
|
|
||||||
trace!(target: "dapps", "Fetching dapp failed because of timeout.");
|
|
||||||
FetchState::Error(errors::timeout_error())
|
|
||||||
},
|
|
||||||
FetchState::InProgress(ref mut receiver) => {
|
|
||||||
// Check if there is a response
|
|
||||||
trace!(target: "dapps", "Polling streaming response.");
|
|
||||||
try_ready!(receiver.poll().map_err(|err| {
|
|
||||||
warn!(target: "dapps", "Error while fetching response: {:?}", err);
|
|
||||||
hyper::Error::Timeout
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
FetchState::Done(_, ref mut response) => {
|
|
||||||
return response.poll()
|
|
||||||
},
|
|
||||||
FetchState::Empty => panic!("Future polled twice."),
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
trace!(target: "dapps", "New status: {:?}", status);
|
|
||||||
self.fetch_control.set_status(&status);
|
|
||||||
self.status = status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,91 +0,0 @@
|
|||||||
// Copyright 2015-2018 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/>.
|
|
||||||
|
|
||||||
//! Hyper handlers implementations.
|
|
||||||
|
|
||||||
mod content;
|
|
||||||
mod echo;
|
|
||||||
mod fetch;
|
|
||||||
mod reader;
|
|
||||||
mod redirect;
|
|
||||||
mod streaming;
|
|
||||||
mod errors;
|
|
||||||
|
|
||||||
pub use self::content::ContentHandler;
|
|
||||||
pub use self::echo::EchoHandler;
|
|
||||||
pub use self::fetch::{ContentFetcherHandler, ContentValidator, FetchControl, ValidatorResponse};
|
|
||||||
pub use self::reader::Reader;
|
|
||||||
pub use self::redirect::Redirection;
|
|
||||||
pub use self::streaming::StreamingHandler;
|
|
||||||
|
|
||||||
use hyper::header;
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
const FETCH_TIMEOUT: Duration = Duration::from_secs(300);
|
|
||||||
|
|
||||||
/// Adds security-related headers to the Response.
|
|
||||||
pub fn add_security_headers(headers: &mut header::Headers, allow_js_eval: bool) {
|
|
||||||
headers.set_raw("X-XSS-Protection", "1; mode=block");
|
|
||||||
headers.set_raw("X-Content-Type-Options", "nosniff");
|
|
||||||
headers.set_raw("X-Frame-Options", "SAMEORIGIN");
|
|
||||||
|
|
||||||
// Content Security Policy headers
|
|
||||||
headers.set_raw("Content-Security-Policy", String::new()
|
|
||||||
// Restrict everything to the same origin by default.
|
|
||||||
+ "default-src 'self';"
|
|
||||||
// Allow connecting to WS servers and HTTP(S) servers.
|
|
||||||
// We could be more restrictive and allow only RPC server URL.
|
|
||||||
+ "connect-src http: https: ws: wss:;"
|
|
||||||
// Allow framing any content from HTTP(S).
|
|
||||||
// Again we could only allow embedding from RPC server URL.
|
|
||||||
// (deprecated)
|
|
||||||
+ "frame-src 'self' http: https:;"
|
|
||||||
// Allow framing and web workers from HTTP(S).
|
|
||||||
+ "child-src 'self' http: https:;"
|
|
||||||
// We allow data: blob: and HTTP(s) images.
|
|
||||||
// We could get rid of wildcarding HTTP and only allow RPC server URL.
|
|
||||||
// (http required for local dapps icons)
|
|
||||||
+ "img-src 'self' 'unsafe-inline' data: blob: http: https:;"
|
|
||||||
// Allow style from data: blob: and HTTPS.
|
|
||||||
+ "style-src 'self' 'unsafe-inline' data: blob: https:;"
|
|
||||||
// Allow fonts from data: and HTTPS.
|
|
||||||
+ "font-src 'self' data: https:;"
|
|
||||||
// Disallow objects
|
|
||||||
+ "object-src 'none';"
|
|
||||||
// Allow scripts
|
|
||||||
+ {
|
|
||||||
let script_src = "";
|
|
||||||
let eval = if allow_js_eval { " 'unsafe-eval'" } else { "" };
|
|
||||||
|
|
||||||
&format!(
|
|
||||||
"script-src 'self' {}{};",
|
|
||||||
script_src,
|
|
||||||
eval
|
|
||||||
)
|
|
||||||
}
|
|
||||||
// Same restrictions as script-src with additional
|
|
||||||
// blob: that is required for camera access (worker)
|
|
||||||
+ "worker-src 'self' https: blob:;"
|
|
||||||
// Run in sandbox mode (although it's not fully safe since we allow same-origin and script)
|
|
||||||
+ "sandbox allow-same-origin allow-forms allow-modals allow-popups allow-presentation allow-scripts;"
|
|
||||||
// Disallow submitting forms from any dapps
|
|
||||||
+ "form-action 'none';"
|
|
||||||
// Never allow mixed content
|
|
||||||
+ "block-all-mixed-content;"
|
|
||||||
// Specify if the site can be embedded.
|
|
||||||
+ "frame-ancestors 'self';"
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
// Copyright 2015-2018 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/>.
|
|
||||||
|
|
||||||
//! A chunk-producing io::Read wrapper.
|
|
||||||
|
|
||||||
use std::io::{self, Read};
|
|
||||||
|
|
||||||
use futures::{self, sink, Sink, Future};
|
|
||||||
use futures::sync::mpsc;
|
|
||||||
use hyper;
|
|
||||||
|
|
||||||
type Sender = mpsc::Sender<Result<hyper::Chunk, hyper::Error>>;
|
|
||||||
|
|
||||||
const MAX_CHUNK_SIZE: usize = 32 * 1024;
|
|
||||||
|
|
||||||
/// A Reader is essentially a stream of `hyper::Chunks`.
|
|
||||||
/// The chunks are read from given `io::Read` instance.
|
|
||||||
///
|
|
||||||
/// Unfortunately `hyper` doesn't allow you to pass `Stream`
|
|
||||||
/// directly to the response, so you need to create
|
|
||||||
/// a `Body::pair()` and send over chunks using `sink::Send`.
|
|
||||||
/// Also `Chunks` need to take `Vec` by value, so we need
|
|
||||||
/// to allocate it for each chunk being sent.
|
|
||||||
pub struct Reader<R: io::Read> {
|
|
||||||
buffer: [u8; MAX_CHUNK_SIZE],
|
|
||||||
content: io::BufReader<R>,
|
|
||||||
sending: sink::Send<Sender>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: io::Read> Reader<R> {
|
|
||||||
pub fn pair(content: R, initial: Vec<u8>) -> (Self, hyper::Body) {
|
|
||||||
let (tx, rx) = hyper::Body::pair();
|
|
||||||
let reader = Reader {
|
|
||||||
buffer: [0; MAX_CHUNK_SIZE],
|
|
||||||
content: io::BufReader::new(content),
|
|
||||||
sending: tx.send(Ok(initial.into())),
|
|
||||||
};
|
|
||||||
|
|
||||||
(reader, rx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: io::Read> Future for Reader<R> {
|
|
||||||
type Item = ();
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn poll(&mut self) -> futures::Poll<Self::Item, Self::Error> {
|
|
||||||
loop {
|
|
||||||
let next = try_ready!(self.sending.poll().map_err(|err| {
|
|
||||||
warn!(target: "dapps", "Unable to send next chunk: {:?}", err);
|
|
||||||
}));
|
|
||||||
|
|
||||||
self.sending = match self.content.read(&mut self.buffer) {
|
|
||||||
Ok(0) => return Ok(futures::Async::Ready(())),
|
|
||||||
Ok(read) => next.send(Ok(self.buffer[..read].to_vec().into())),
|
|
||||||
Err(err) => next.send(Err(hyper::Error::Io(err))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
// Copyright 2015-2018 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 Redirection hyper handler
|
|
||||||
|
|
||||||
use hyper::{self, header, StatusCode};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Redirection {
|
|
||||||
to_url: String
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Redirection {
|
|
||||||
pub fn new<T: Into<String>>(url: T) -> Self {
|
|
||||||
Redirection {
|
|
||||||
to_url: url.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<hyper::Response> for Redirection {
|
|
||||||
fn into(self) -> hyper::Response {
|
|
||||||
// Don't use `MovedPermanently` here to prevent browser from caching the redirections.
|
|
||||||
hyper::Response::new()
|
|
||||||
.with_status(StatusCode::Found)
|
|
||||||
.with_header(header::Location::new(self.to_url))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
// Copyright 2015-2018 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/>.
|
|
||||||
|
|
||||||
//! Content Stream Response
|
|
||||||
|
|
||||||
use std::io;
|
|
||||||
use hyper::{self, header, mime, StatusCode};
|
|
||||||
|
|
||||||
use handlers::{add_security_headers, Reader};
|
|
||||||
|
|
||||||
pub struct StreamingHandler<R> {
|
|
||||||
initial: Vec<u8>,
|
|
||||||
content: R,
|
|
||||||
status: StatusCode,
|
|
||||||
mimetype: mime::Mime,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: io::Read> StreamingHandler<R> {
|
|
||||||
pub fn new(content: R, status: StatusCode, mimetype: mime::Mime) -> Self {
|
|
||||||
StreamingHandler {
|
|
||||||
initial: Vec::new(),
|
|
||||||
content,
|
|
||||||
status,
|
|
||||||
mimetype,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_initial_content(&mut self, content: &str) {
|
|
||||||
self.initial = content.as_bytes().to_vec();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_response(self) -> (Reader<R>, hyper::Response) {
|
|
||||||
let (reader, body) = Reader::pair(self.content, self.initial);
|
|
||||||
let mut res = hyper::Response::new()
|
|
||||||
.with_status(self.status)
|
|
||||||
.with_header(header::ContentType(self.mimetype))
|
|
||||||
.with_body(body);
|
|
||||||
add_security_headers(&mut res.headers_mut(), false);
|
|
||||||
|
|
||||||
(reader, res)
|
|
||||||
}
|
|
||||||
}
|
|
228
dapps/src/lib.rs
228
dapps/src/lib.rs
@ -1,228 +0,0 @@
|
|||||||
// Copyright 2015-2018 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/>.
|
|
||||||
|
|
||||||
//! Ethcore Webapplications for Parity
|
|
||||||
#![warn(missing_docs)]
|
|
||||||
|
|
||||||
extern crate base32;
|
|
||||||
extern crate futures_cpupool;
|
|
||||||
extern crate itertools;
|
|
||||||
extern crate linked_hash_map;
|
|
||||||
extern crate mime_guess;
|
|
||||||
extern crate parking_lot;
|
|
||||||
extern crate rand;
|
|
||||||
extern crate rustc_hex;
|
|
||||||
extern crate serde;
|
|
||||||
extern crate serde_json;
|
|
||||||
extern crate unicase;
|
|
||||||
extern crate zip;
|
|
||||||
|
|
||||||
extern crate jsonrpc_http_server;
|
|
||||||
|
|
||||||
extern crate parity_bytes as bytes;
|
|
||||||
extern crate ethereum_types;
|
|
||||||
extern crate fetch;
|
|
||||||
extern crate node_health;
|
|
||||||
extern crate parity_dapps_glue as parity_dapps;
|
|
||||||
extern crate parity_hash_fetch as hash_fetch;
|
|
||||||
extern crate keccak_hash as hash;
|
|
||||||
extern crate parity_version;
|
|
||||||
extern crate registrar;
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate futures;
|
|
||||||
#[macro_use]
|
|
||||||
extern crate log;
|
|
||||||
#[macro_use]
|
|
||||||
extern crate serde_derive;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
extern crate env_logger;
|
|
||||||
#[cfg(test)]
|
|
||||||
extern crate ethcore_devtools as devtools;
|
|
||||||
#[cfg(test)]
|
|
||||||
extern crate jsonrpc_core;
|
|
||||||
#[cfg(test)]
|
|
||||||
extern crate parity_reactor;
|
|
||||||
|
|
||||||
mod endpoint;
|
|
||||||
mod apps;
|
|
||||||
mod page;
|
|
||||||
mod router;
|
|
||||||
mod handlers;
|
|
||||||
mod api;
|
|
||||||
mod proxypac;
|
|
||||||
mod web;
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests;
|
|
||||||
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::mem;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use futures_cpupool::CpuPool;
|
|
||||||
use jsonrpc_http_server::{self as http, hyper, Origin};
|
|
||||||
use parking_lot::RwLock;
|
|
||||||
|
|
||||||
use fetch::Fetch;
|
|
||||||
use node_health::NodeHealth;
|
|
||||||
|
|
||||||
pub use registrar::{RegistrarClient, Asynchronous};
|
|
||||||
pub use node_health::SyncStatus;
|
|
||||||
pub use page::builtin::Dapp;
|
|
||||||
|
|
||||||
/// Validates Web Proxy tokens
|
|
||||||
pub trait WebProxyTokens: Send + Sync {
|
|
||||||
/// Should return a domain allowed to be accessed by this token or `None` if the token is not valid
|
|
||||||
fn domain(&self, token: &str) -> Option<Origin>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F> WebProxyTokens for F where F: Fn(String) -> Option<Origin> + Send + Sync {
|
|
||||||
fn domain(&self, token: &str) -> Option<Origin> { self(token.to_owned()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Current supported endpoints.
|
|
||||||
#[derive(Default, Clone)]
|
|
||||||
pub struct Endpoints {
|
|
||||||
local_endpoints: Arc<RwLock<Vec<String>>>,
|
|
||||||
endpoints: Arc<RwLock<endpoint::Endpoints>>,
|
|
||||||
dapps_path: PathBuf,
|
|
||||||
pool: Option<CpuPool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Endpoints {
|
|
||||||
/// Returns a current list of app endpoints.
|
|
||||||
pub fn list(&self) -> Vec<apps::App> {
|
|
||||||
self.endpoints.read().iter().filter_map(|(ref k, ref e)| {
|
|
||||||
e.info().map(|ref info| info.with_id(k))
|
|
||||||
}).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check for any changes in the local dapps folder and update.
|
|
||||||
pub fn refresh_local_dapps(&self) {
|
|
||||||
let pool = match self.pool.as_ref() {
|
|
||||||
None => return,
|
|
||||||
Some(pool) => pool,
|
|
||||||
};
|
|
||||||
let new_local = apps::fs::local_endpoints(&self.dapps_path, pool.clone());
|
|
||||||
let old_local = mem::replace(&mut *self.local_endpoints.write(), new_local.keys().cloned().collect());
|
|
||||||
let (_, to_remove): (_, Vec<_>) = old_local
|
|
||||||
.into_iter()
|
|
||||||
.partition(|k| new_local.contains_key(&k.clone()));
|
|
||||||
|
|
||||||
let mut endpoints = self.endpoints.write();
|
|
||||||
// remove the dead dapps
|
|
||||||
for k in to_remove {
|
|
||||||
endpoints.remove(&k);
|
|
||||||
}
|
|
||||||
// new dapps to be added
|
|
||||||
for (k, v) in new_local {
|
|
||||||
if !endpoints.contains_key(&k) {
|
|
||||||
endpoints.insert(k, v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Dapps server as `jsonrpc-http-server` request middleware.
|
|
||||||
pub struct Middleware {
|
|
||||||
endpoints: Endpoints,
|
|
||||||
router: router::Router,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Middleware {
|
|
||||||
/// Get local endpoints handle.
|
|
||||||
pub fn endpoints(&self) -> &Endpoints {
|
|
||||||
&self.endpoints
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates new Dapps server middleware.
|
|
||||||
pub fn dapps<F: Fetch>(
|
|
||||||
pool: CpuPool,
|
|
||||||
health: NodeHealth,
|
|
||||||
dapps_path: PathBuf,
|
|
||||||
extra_dapps: Vec<PathBuf>,
|
|
||||||
dapps_domain: &str,
|
|
||||||
registrar: Arc<RegistrarClient<Call=Asynchronous>>,
|
|
||||||
sync_status: Arc<SyncStatus>,
|
|
||||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
|
||||||
fetch: F,
|
|
||||||
) -> Self {
|
|
||||||
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(
|
|
||||||
hash_fetch::urlhint::URLHintContract::new(registrar),
|
|
||||||
sync_status.clone(),
|
|
||||||
fetch.clone(),
|
|
||||||
pool.clone(),
|
|
||||||
).allow_dapps(true));
|
|
||||||
let (local_endpoints, endpoints) = apps::all_endpoints(
|
|
||||||
dapps_path.clone(),
|
|
||||||
extra_dapps,
|
|
||||||
dapps_domain,
|
|
||||||
web_proxy_tokens,
|
|
||||||
fetch.clone(),
|
|
||||||
pool.clone(),
|
|
||||||
);
|
|
||||||
let endpoints = Endpoints {
|
|
||||||
endpoints: Arc::new(RwLock::new(endpoints)),
|
|
||||||
dapps_path,
|
|
||||||
local_endpoints: Arc::new(RwLock::new(local_endpoints)),
|
|
||||||
pool: Some(pool.clone()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let special = special_endpoints(
|
|
||||||
health,
|
|
||||||
content_fetcher.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let router = router::Router::new(
|
|
||||||
content_fetcher,
|
|
||||||
Some(endpoints.clone()),
|
|
||||||
special,
|
|
||||||
dapps_domain.to_owned(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Middleware {
|
|
||||||
endpoints,
|
|
||||||
router,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl http::RequestMiddleware for Middleware {
|
|
||||||
fn on_request(&self, req: hyper::Request) -> http::RequestMiddlewareAction {
|
|
||||||
self.router.on_request(req)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn special_endpoints(
|
|
||||||
health: NodeHealth,
|
|
||||||
content_fetcher: Arc<apps::fetcher::Fetcher>,
|
|
||||||
) -> HashMap<router::SpecialEndpoint, Option<Box<endpoint::Endpoint>>> {
|
|
||||||
let mut special = HashMap::new();
|
|
||||||
special.insert(router::SpecialEndpoint::Rpc, None);
|
|
||||||
special.insert(router::SpecialEndpoint::Api, Some(api::RestApi::new(
|
|
||||||
content_fetcher,
|
|
||||||
health,
|
|
||||||
)));
|
|
||||||
special
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Random filename
|
|
||||||
fn random_filename() -> String {
|
|
||||||
use ::rand::Rng;
|
|
||||||
let mut rng = ::rand::OsRng::new().unwrap();
|
|
||||||
rng.gen_ascii_chars().take(12).collect()
|
|
||||||
}
|
|
@ -1,145 +0,0 @@
|
|||||||
// Copyright 2015-2018 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 std::io;
|
|
||||||
use futures::future;
|
|
||||||
use futures_cpupool::CpuPool;
|
|
||||||
use hyper::mime::{self, Mime};
|
|
||||||
use itertools::Itertools;
|
|
||||||
use parity_dapps::{WebApp, Info};
|
|
||||||
|
|
||||||
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Request, Response};
|
|
||||||
use page::{handler, PageCache};
|
|
||||||
|
|
||||||
/// Represents a builtin Dapp.
|
|
||||||
pub struct Dapp<T: WebApp + 'static> {
|
|
||||||
/// futures cpu pool
|
|
||||||
pool: CpuPool,
|
|
||||||
/// Content of the files
|
|
||||||
app: T,
|
|
||||||
info: EndpointInfo,
|
|
||||||
fallback_to_index_html: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: WebApp + 'static> Dapp<T> {
|
|
||||||
/// Creates new `Dapp` for builtin (compile time) Dapp.
|
|
||||||
pub fn new(pool: CpuPool, app: T) -> Self {
|
|
||||||
let info = app.info();
|
|
||||||
Dapp {
|
|
||||||
pool,
|
|
||||||
app,
|
|
||||||
info: EndpointInfo::from(info),
|
|
||||||
fallback_to_index_html: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new `Dapp` for builtin (compile time) Dapp.
|
|
||||||
/// Instead of returning 404 this endpoint will always server index.html.
|
|
||||||
pub fn with_fallback_to_index(pool: CpuPool, app: T) -> Self {
|
|
||||||
let info = app.info();
|
|
||||||
Dapp {
|
|
||||||
pool,
|
|
||||||
app,
|
|
||||||
info: EndpointInfo::from(info),
|
|
||||||
fallback_to_index_html: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Allow the dapp to use `unsafe-eval` to run JS.
|
|
||||||
pub fn allow_js_eval(&mut self) {
|
|
||||||
self.info.allow_js_eval = Some(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: WebApp> Endpoint for Dapp<T> {
|
|
||||||
fn info(&self) -> Option<&EndpointInfo> {
|
|
||||||
Some(&self.info)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn respond(&self, path: EndpointPath, _req: Request) -> Response {
|
|
||||||
trace!(target: "dapps", "Builtin file path: {:?}", path);
|
|
||||||
let file_path = if path.has_no_params() {
|
|
||||||
"index.html".to_owned()
|
|
||||||
} else {
|
|
||||||
path.app_params.into_iter().filter(|x| !x.is_empty()).join("/")
|
|
||||||
};
|
|
||||||
trace!(target: "dapps", "Builtin file: {:?}", file_path);
|
|
||||||
|
|
||||||
let file = {
|
|
||||||
let file = |path| self.app.file(path).map(|file| {
|
|
||||||
let content_type = match file.content_type.parse() {
|
|
||||||
Ok(mime) => mime,
|
|
||||||
Err(_) => {
|
|
||||||
warn!(target: "dapps", "invalid MIME type: {}", file.content_type);
|
|
||||||
mime::TEXT_HTML
|
|
||||||
},
|
|
||||||
};
|
|
||||||
BuiltinFile {
|
|
||||||
content_type,
|
|
||||||
content: io::Cursor::new(file.content),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let res = file(&file_path);
|
|
||||||
if self.fallback_to_index_html {
|
|
||||||
res.or_else(|| file("index.html"))
|
|
||||||
} else {
|
|
||||||
res
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let (reader, response) = handler::PageHandler {
|
|
||||||
file,
|
|
||||||
cache: PageCache::Disabled,
|
|
||||||
allow_js_eval: self.info.allow_js_eval.clone().unwrap_or(false),
|
|
||||||
}.into_response();
|
|
||||||
|
|
||||||
self.pool.spawn(reader).forget();
|
|
||||||
|
|
||||||
Box::new(future::ok(response))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Info> for EndpointInfo {
|
|
||||||
fn from(info: Info) -> Self {
|
|
||||||
EndpointInfo {
|
|
||||||
id: None,
|
|
||||||
name: info.name.into(),
|
|
||||||
description: info.description.into(),
|
|
||||||
author: info.author.into(),
|
|
||||||
icon_url: info.icon_url.into(),
|
|
||||||
local_url: None,
|
|
||||||
version: info.version.into(),
|
|
||||||
allow_js_eval: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct BuiltinFile {
|
|
||||||
content_type: Mime,
|
|
||||||
content: io::Cursor<&'static [u8]>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl handler::DappFile for BuiltinFile {
|
|
||||||
type Reader = io::Cursor<&'static [u8]>;
|
|
||||||
|
|
||||||
fn content_type(&self) -> &Mime {
|
|
||||||
&self.content_type
|
|
||||||
}
|
|
||||||
|
|
||||||
fn into_reader(self) -> Self::Reader {
|
|
||||||
self.content
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,100 +0,0 @@
|
|||||||
// Copyright 2015-2018 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 std::io;
|
|
||||||
use std::time::{Duration, SystemTime};
|
|
||||||
use hyper::{self, header, StatusCode};
|
|
||||||
use hyper::mime::{Mime};
|
|
||||||
|
|
||||||
use handlers::{Reader, ContentHandler, add_security_headers};
|
|
||||||
|
|
||||||
/// Represents a file that can be sent to client.
|
|
||||||
/// Implementation should keep track of bytes already sent internally.
|
|
||||||
pub trait DappFile {
|
|
||||||
/// A reader type returned by this file.
|
|
||||||
type Reader: io::Read;
|
|
||||||
|
|
||||||
/// Returns a content-type of this file.
|
|
||||||
fn content_type(&self) -> &Mime;
|
|
||||||
|
|
||||||
/// Convert this file into io::Read instance.
|
|
||||||
fn into_reader(self) -> Self::Reader where Self: Sized;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Defines what cache headers should be appended to returned resources.
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
|
||||||
pub enum PageCache {
|
|
||||||
Enabled,
|
|
||||||
Disabled,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for PageCache {
|
|
||||||
fn default() -> Self {
|
|
||||||
PageCache::Disabled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A handler for a single webapp.
|
|
||||||
/// Resolves correct paths and serves as a plumbing code between
|
|
||||||
/// hyper server and dapp.
|
|
||||||
pub struct PageHandler<T: DappFile> {
|
|
||||||
/// File currently being served
|
|
||||||
pub file: Option<T>,
|
|
||||||
/// Cache settings for this page.
|
|
||||||
pub cache: PageCache,
|
|
||||||
/// Allow JS unsafe-eval.
|
|
||||||
pub allow_js_eval: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: DappFile> PageHandler<T> {
|
|
||||||
pub fn into_response(self) -> (Option<Reader<T::Reader>>, hyper::Response) {
|
|
||||||
let file = match self.file {
|
|
||||||
None => return (None, ContentHandler::error(
|
|
||||||
StatusCode::NotFound,
|
|
||||||
"File not found",
|
|
||||||
"Requested file has not been found.",
|
|
||||||
None,
|
|
||||||
).into()),
|
|
||||||
Some(file) => file,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut res = hyper::Response::new()
|
|
||||||
.with_status(StatusCode::Ok);
|
|
||||||
|
|
||||||
// headers
|
|
||||||
{
|
|
||||||
let mut headers = res.headers_mut();
|
|
||||||
|
|
||||||
if let PageCache::Enabled = self.cache {
|
|
||||||
let validity_secs = 365u32 * 24 * 3600;
|
|
||||||
let validity = Duration::from_secs(validity_secs as u64);
|
|
||||||
headers.set(header::CacheControl(vec![
|
|
||||||
header::CacheDirective::Public,
|
|
||||||
header::CacheDirective::MaxAge(validity_secs),
|
|
||||||
]));
|
|
||||||
headers.set(header::Expires(header::HttpDate::from(SystemTime::now() + validity)));
|
|
||||||
}
|
|
||||||
|
|
||||||
headers.set(header::ContentType(file.content_type().to_owned()));
|
|
||||||
|
|
||||||
add_security_headers(&mut headers, self.allow_js_eval);
|
|
||||||
}
|
|
||||||
|
|
||||||
let (reader, body) = Reader::pair(file.into_reader(), Vec::new());
|
|
||||||
res.set_body(body);
|
|
||||||
(Some(reader), res)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,141 +0,0 @@
|
|||||||
// Copyright 2015-2018 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 mime_guess;
|
|
||||||
use std::{fs, fmt};
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
use futures::{future};
|
|
||||||
use futures_cpupool::CpuPool;
|
|
||||||
use page::handler::{self, PageCache};
|
|
||||||
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Request, Response};
|
|
||||||
use hyper::mime::Mime;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Dapp {
|
|
||||||
pool: CpuPool,
|
|
||||||
path: PathBuf,
|
|
||||||
mime: Option<Mime>,
|
|
||||||
info: Option<EndpointInfo>,
|
|
||||||
cache: PageCache,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for Dapp {
|
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
fmt.debug_struct("Dapp")
|
|
||||||
.field("path", &self.path)
|
|
||||||
.field("mime", &self.mime)
|
|
||||||
.field("info", &self.info)
|
|
||||||
.field("cache", &self.cache)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Dapp {
|
|
||||||
pub fn new(pool: CpuPool, path: PathBuf, info: EndpointInfo, cache: PageCache) -> Self {
|
|
||||||
Dapp {
|
|
||||||
pool,
|
|
||||||
path,
|
|
||||||
mime: None,
|
|
||||||
info: Some(info),
|
|
||||||
cache,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn single_file(pool: CpuPool, path: PathBuf, mime: Mime, cache: PageCache) -> Self {
|
|
||||||
Dapp {
|
|
||||||
pool,
|
|
||||||
path,
|
|
||||||
mime: Some(mime),
|
|
||||||
info: None,
|
|
||||||
cache,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn path(&self) -> PathBuf {
|
|
||||||
self.path.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_file(&self, path: &EndpointPath) -> Option<LocalFile> {
|
|
||||||
if let Some(ref mime) = self.mime {
|
|
||||||
return LocalFile::from_path(&self.path, mime.to_owned());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut file_path = self.path.to_owned();
|
|
||||||
|
|
||||||
if path.has_no_params() {
|
|
||||||
file_path.push("index.html");
|
|
||||||
} else {
|
|
||||||
for part in &path.app_params {
|
|
||||||
file_path.push(part);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mime = mime_guess::guess_mime_type(&file_path);
|
|
||||||
LocalFile::from_path(&file_path, mime)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_response(&self, path: &EndpointPath) -> Response {
|
|
||||||
let (reader, response) = handler::PageHandler {
|
|
||||||
file: self.get_file(path),
|
|
||||||
cache: self.cache,
|
|
||||||
allow_js_eval: self.info.as_ref().and_then(|x| x.allow_js_eval).unwrap_or(false),
|
|
||||||
}.into_response();
|
|
||||||
|
|
||||||
self.pool.spawn(reader).forget();
|
|
||||||
|
|
||||||
Box::new(future::ok(response))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Endpoint for Dapp {
|
|
||||||
fn info(&self) -> Option<&EndpointInfo> {
|
|
||||||
self.info.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn respond(&self, path: EndpointPath, _req: Request) -> Response {
|
|
||||||
self.to_response(&path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LocalFile {
|
|
||||||
content_type: Mime,
|
|
||||||
file: fs::File,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LocalFile {
|
|
||||||
fn from_path<P: AsRef<Path>>(path: P, content_type: Mime) -> Option<Self> {
|
|
||||||
trace!(target: "dapps", "Local file: {:?}", path.as_ref());
|
|
||||||
// Check if file exists
|
|
||||||
fs::File::open(&path).ok().map(|file| {
|
|
||||||
LocalFile {
|
|
||||||
content_type,
|
|
||||||
file,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl handler::DappFile for LocalFile {
|
|
||||||
type Reader = fs::File;
|
|
||||||
|
|
||||||
fn content_type(&self) -> &Mime {
|
|
||||||
&self.content_type
|
|
||||||
}
|
|
||||||
|
|
||||||
fn into_reader(self) -> Self::Reader {
|
|
||||||
self.file
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
// Copyright 2015-2018 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/>.
|
|
||||||
|
|
||||||
pub mod builtin;
|
|
||||||
pub mod local;
|
|
||||||
mod handler;
|
|
||||||
|
|
||||||
pub use self::handler::PageCache;
|
|
@ -1,61 +0,0 @@
|
|||||||
// Copyright 2015-2018 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/>.
|
|
||||||
|
|
||||||
//! Serving ProxyPac file
|
|
||||||
|
|
||||||
use apps::HOME_PAGE;
|
|
||||||
use endpoint::{Endpoint, Request, Response, EndpointPath};
|
|
||||||
use futures::future;
|
|
||||||
use handlers::ContentHandler;
|
|
||||||
use hyper::mime;
|
|
||||||
|
|
||||||
pub struct ProxyPac {
|
|
||||||
dapps_domain: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ProxyPac {
|
|
||||||
pub fn boxed(dapps_domain: String) -> Box<Endpoint> {
|
|
||||||
Box::new(ProxyPac { dapps_domain })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Endpoint for ProxyPac {
|
|
||||||
fn respond(&self, path: EndpointPath, _req: Request) -> Response {
|
|
||||||
let ui = format!("{}:{}", path.host, path.port);
|
|
||||||
|
|
||||||
let content = format!(
|
|
||||||
r#"
|
|
||||||
function FindProxyForURL(url, host) {{
|
|
||||||
if (shExpMatch(host, "{0}.{1}"))
|
|
||||||
{{
|
|
||||||
return "PROXY {4}";
|
|
||||||
}}
|
|
||||||
|
|
||||||
if (shExpMatch(host, "*.{1}"))
|
|
||||||
{{
|
|
||||||
return "PROXY {2}:{3}";
|
|
||||||
}}
|
|
||||||
|
|
||||||
return "DIRECT";
|
|
||||||
}}
|
|
||||||
"#,
|
|
||||||
HOME_PAGE, self.dapps_domain, path.host, path.port, ui);
|
|
||||||
|
|
||||||
Box::new(future::ok(
|
|
||||||
ContentHandler::ok(content, mime::TEXT_JAVASCRIPT).into()
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,384 +0,0 @@
|
|||||||
// Copyright 2015-2018 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/>.
|
|
||||||
|
|
||||||
//! Router implementation
|
|
||||||
//! Dispatch requests to proper application.
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use futures::future;
|
|
||||||
use hyper::{self, header, Uri};
|
|
||||||
use jsonrpc_http_server as http;
|
|
||||||
|
|
||||||
use apps;
|
|
||||||
use apps::fetcher::Fetcher;
|
|
||||||
use endpoint::{self, Endpoint, EndpointPath};
|
|
||||||
use Endpoints;
|
|
||||||
use handlers;
|
|
||||||
|
|
||||||
/// Special endpoints are accessible on every domain (every dapp)
|
|
||||||
#[derive(Debug, PartialEq, Hash, Eq)]
|
|
||||||
pub enum SpecialEndpoint {
|
|
||||||
Rpc,
|
|
||||||
Api,
|
|
||||||
Home,
|
|
||||||
None,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Response {
|
|
||||||
Some(endpoint::Response),
|
|
||||||
None(hyper::Request),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An endpoint router.
|
|
||||||
/// Dispatches the request to particular Endpoint by requested uri/path.
|
|
||||||
pub struct Router {
|
|
||||||
endpoints: Option<Endpoints>,
|
|
||||||
fetch: Arc<Fetcher>,
|
|
||||||
special: HashMap<SpecialEndpoint, Option<Box<Endpoint>>>,
|
|
||||||
dapps_domain: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Router {
|
|
||||||
fn resolve_request(&self, req: hyper::Request, refresh_dapps: bool) -> Response {
|
|
||||||
// Choose proper handler depending on path / domain
|
|
||||||
let endpoint = extract_endpoint(req.uri(), req.headers().get(), &self.dapps_domain);
|
|
||||||
let referer = extract_referer_endpoint(&req, &self.dapps_domain);
|
|
||||||
let is_get_request = *req.method() == hyper::Method::Get;
|
|
||||||
let is_head_request = *req.method() == hyper::Method::Head;
|
|
||||||
let has_dapp = |dapp: &str| self.endpoints
|
|
||||||
.as_ref()
|
|
||||||
.map_or(false, |endpoints| endpoints.endpoints.read().contains_key(dapp));
|
|
||||||
|
|
||||||
trace!(target: "dapps", "Routing request to {:?}. Details: {:?}", req.uri(), req);
|
|
||||||
debug!(target: "dapps", "Handling endpoint request: {:?}, referer: {:?}", endpoint, referer);
|
|
||||||
|
|
||||||
match (endpoint.0, endpoint.1, referer) {
|
|
||||||
// Handle invalid web requests that we can recover from
|
|
||||||
(ref path, SpecialEndpoint::None, Some(ref referer))
|
|
||||||
if referer.app_id == apps::WEB_PATH
|
|
||||||
&& has_dapp(apps::WEB_PATH)
|
|
||||||
&& !is_web_endpoint(path)
|
|
||||||
=>
|
|
||||||
{
|
|
||||||
let token = referer.app_params.get(0).map(String::as_str).unwrap_or("");
|
|
||||||
let requested = req.uri().path();
|
|
||||||
let query = req.uri().query().map_or_else(String::new, |query| format!("?{}", query));
|
|
||||||
let redirect_url = format!("/{}/{}{}{}", apps::WEB_PATH, token, requested, query);
|
|
||||||
trace!(target: "dapps", "Redirecting to correct web request: {:?}", redirect_url);
|
|
||||||
Response::Some(Box::new(future::ok(
|
|
||||||
handlers::Redirection::new(redirect_url).into()
|
|
||||||
)))
|
|
||||||
},
|
|
||||||
// First check special endpoints
|
|
||||||
(ref path, ref endpoint, _) if self.special.contains_key(endpoint) => {
|
|
||||||
trace!(target: "dapps", "Resolving to special endpoint.");
|
|
||||||
let special = self.special.get(endpoint).expect("special known to contain key; qed");
|
|
||||||
match *special {
|
|
||||||
Some(ref special) => Response::Some(special.respond(path.clone().unwrap_or_default(), req)),
|
|
||||||
None => Response::None(req),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// Then delegate to dapp
|
|
||||||
(Some(ref path), _, _) if has_dapp(&path.app_id) => {
|
|
||||||
trace!(target: "dapps", "Resolving to local/builtin dapp.");
|
|
||||||
Response::Some(self.endpoints
|
|
||||||
.as_ref()
|
|
||||||
.expect("endpoints known to be set; qed")
|
|
||||||
.endpoints
|
|
||||||
.read()
|
|
||||||
.get(&path.app_id)
|
|
||||||
.expect("endpoints known to contain key; qed")
|
|
||||||
.respond(path.clone(), req))
|
|
||||||
},
|
|
||||||
// Try to resolve and fetch the dapp
|
|
||||||
(Some(ref path), _, _) if self.fetch.contains(&path.app_id) => {
|
|
||||||
trace!(target: "dapps", "Resolving to fetchable content.");
|
|
||||||
Response::Some(self.fetch.respond(path.clone(), req))
|
|
||||||
},
|
|
||||||
// 404 for non-existent content (only if serving endpoints and not homepage)
|
|
||||||
(Some(ref path), _, _)
|
|
||||||
if (is_get_request || is_head_request)
|
|
||||||
&& self.endpoints.is_some()
|
|
||||||
&& path.app_id != apps::HOME_PAGE
|
|
||||||
=>
|
|
||||||
{
|
|
||||||
trace!(target: "dapps", "Resolving to 404.");
|
|
||||||
if refresh_dapps {
|
|
||||||
debug!(target: "dapps", "Refreshing dapps and re-trying.");
|
|
||||||
self.endpoints.as_ref().map(|endpoints| endpoints.refresh_local_dapps());
|
|
||||||
return self.resolve_request(req, false);
|
|
||||||
} else {
|
|
||||||
Response::Some(Box::new(future::ok(handlers::ContentHandler::error(
|
|
||||||
hyper::StatusCode::NotFound,
|
|
||||||
"404 Not Found",
|
|
||||||
"Requested content was not found.",
|
|
||||||
None,
|
|
||||||
).into())))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// Any other GET|HEAD requests to home page.
|
|
||||||
_ if (is_get_request || is_head_request) && self.special.contains_key(&SpecialEndpoint::Home) => {
|
|
||||||
trace!(target: "dapps", "Resolving to home page.");
|
|
||||||
let special = self.special.get(&SpecialEndpoint::Home).expect("special known to contain key; qed");
|
|
||||||
match *special {
|
|
||||||
Some(ref special) => {
|
|
||||||
let mut endpoint = EndpointPath::default();
|
|
||||||
endpoint.app_params = req.uri().path().split('/').map(str::to_owned).collect();
|
|
||||||
Response::Some(special.respond(endpoint, req))
|
|
||||||
},
|
|
||||||
None => Response::None(req),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// RPC by default
|
|
||||||
_ if self.special.contains_key(&SpecialEndpoint::Rpc) => {
|
|
||||||
trace!(target: "dapps", "Resolving to RPC call.");
|
|
||||||
Response::None(req)
|
|
||||||
},
|
|
||||||
// 404 otherwise
|
|
||||||
_ => {
|
|
||||||
Response::Some(Box::new(future::ok(handlers::ContentHandler::error(
|
|
||||||
hyper::StatusCode::NotFound,
|
|
||||||
"404 Not Found",
|
|
||||||
"Requested content was not found.",
|
|
||||||
None,
|
|
||||||
).into())))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl http::RequestMiddleware for Router {
|
|
||||||
fn on_request(&self, req: hyper::Request) -> http::RequestMiddlewareAction {
|
|
||||||
let is_origin_set = req.headers().get::<header::Origin>().is_some();
|
|
||||||
let response = self.resolve_request(req, self.endpoints.is_some());
|
|
||||||
match response {
|
|
||||||
Response::Some(response) => http::RequestMiddlewareAction::Respond {
|
|
||||||
should_validate_hosts: true,
|
|
||||||
response,
|
|
||||||
},
|
|
||||||
Response::None(request) => http::RequestMiddlewareAction::Proceed {
|
|
||||||
should_continue_on_invalid_cors: !is_origin_set,
|
|
||||||
request,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Router {
|
|
||||||
pub fn new(
|
|
||||||
content_fetcher: Arc<Fetcher>,
|
|
||||||
endpoints: Option<Endpoints>,
|
|
||||||
special: HashMap<SpecialEndpoint, Option<Box<Endpoint>>>,
|
|
||||||
dapps_domain: String,
|
|
||||||
) -> Self {
|
|
||||||
Router {
|
|
||||||
endpoints: endpoints,
|
|
||||||
fetch: content_fetcher,
|
|
||||||
special: special,
|
|
||||||
dapps_domain: format!(".{}", dapps_domain),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_web_endpoint(path: &Option<EndpointPath>) -> bool {
|
|
||||||
match *path {
|
|
||||||
Some(ref path) if path.app_id == apps::WEB_PATH => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extract_referer_endpoint(req: &hyper::Request, dapps_domain: &str) -> Option<EndpointPath> {
|
|
||||||
let referer = req.headers().get::<header::Referer>();
|
|
||||||
|
|
||||||
let url = referer.and_then(|referer| referer.parse().ok());
|
|
||||||
url.and_then(|url| {
|
|
||||||
extract_url_referer_endpoint(&url, dapps_domain).or_else(|| {
|
|
||||||
extract_endpoint(&url, None, dapps_domain).0
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extract_url_referer_endpoint(url: &Uri, dapps_domain: &str) -> Option<EndpointPath> {
|
|
||||||
let query = url.query();
|
|
||||||
match query {
|
|
||||||
Some(query) if query.starts_with(apps::URL_REFERER) => {
|
|
||||||
let scheme = url.scheme().unwrap_or("http");
|
|
||||||
let host = url.host().unwrap_or("unknown");
|
|
||||||
let port = default_port(url, None);
|
|
||||||
let referer_url = format!("{}://{}:{}/{}", scheme, host, port, &query[apps::URL_REFERER.len()..]);
|
|
||||||
debug!(target: "dapps", "Recovering referer from query parameter: {}", referer_url);
|
|
||||||
|
|
||||||
if let Some(referer_url) = referer_url.parse().ok() {
|
|
||||||
extract_endpoint(&referer_url, None, dapps_domain).0
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extract_endpoint(url: &Uri, extra_host: Option<&header::Host>, dapps_domain: &str) -> (Option<EndpointPath>, SpecialEndpoint) {
|
|
||||||
fn special_endpoint(path: &[&str]) -> SpecialEndpoint {
|
|
||||||
if path.len() <= 1 {
|
|
||||||
return SpecialEndpoint::None;
|
|
||||||
}
|
|
||||||
|
|
||||||
match path[0].as_ref() {
|
|
||||||
apps::RPC_PATH => SpecialEndpoint::Rpc,
|
|
||||||
apps::API_PATH => SpecialEndpoint::Api,
|
|
||||||
apps::HOME_PAGE => SpecialEndpoint::Home,
|
|
||||||
_ => SpecialEndpoint::None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let port = default_port(url, extra_host.as_ref().and_then(|h| h.port()));
|
|
||||||
let host = url.host().or_else(|| extra_host.as_ref().map(|h| h.hostname()));
|
|
||||||
let query = url.query().map(str::to_owned);
|
|
||||||
let mut path_segments = url.path().split('/').skip(1).collect::<Vec<_>>();
|
|
||||||
trace!(
|
|
||||||
target: "dapps",
|
|
||||||
"Extracting endpoint from: {:?} (dapps: {}). Got host {:?}:{} with path {:?}",
|
|
||||||
url, dapps_domain, host, port, path_segments
|
|
||||||
);
|
|
||||||
match host {
|
|
||||||
Some(host) if host.ends_with(dapps_domain) => {
|
|
||||||
let id = &host[0..(host.len() - dapps_domain.len())];
|
|
||||||
let special = special_endpoint(&path_segments);
|
|
||||||
|
|
||||||
// remove special endpoint id from params
|
|
||||||
if special != SpecialEndpoint::None {
|
|
||||||
path_segments.remove(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
let (app_id, app_params) = if let Some(split) = id.rfind('.') {
|
|
||||||
let (params, id) = id.split_at(split);
|
|
||||||
path_segments.insert(0, params);
|
|
||||||
(id[1..].to_owned(), path_segments)
|
|
||||||
} else {
|
|
||||||
(id.to_owned(), path_segments)
|
|
||||||
};
|
|
||||||
|
|
||||||
(Some(EndpointPath {
|
|
||||||
app_id,
|
|
||||||
app_params: app_params.into_iter().map(Into::into).collect(),
|
|
||||||
query,
|
|
||||||
host: host.to_owned(),
|
|
||||||
port,
|
|
||||||
using_dapps_domains: true,
|
|
||||||
}), special)
|
|
||||||
},
|
|
||||||
Some(host) if path_segments.len() > 1 => {
|
|
||||||
let special = special_endpoint(&path_segments);
|
|
||||||
let id = path_segments.remove(0);
|
|
||||||
(Some(EndpointPath {
|
|
||||||
app_id: id.to_owned(),
|
|
||||||
app_params: path_segments.into_iter().map(Into::into).collect(),
|
|
||||||
query,
|
|
||||||
host: host.to_owned(),
|
|
||||||
port,
|
|
||||||
using_dapps_domains: false,
|
|
||||||
}), special)
|
|
||||||
},
|
|
||||||
_ => (None, special_endpoint(&path_segments)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_port(url: &Uri, extra_port: Option<u16>) -> u16 {
|
|
||||||
let scheme = url.scheme().unwrap_or("http");
|
|
||||||
url.port().or(extra_port).unwrap_or_else(|| match scheme {
|
|
||||||
"http" => 80,
|
|
||||||
"https" => 443,
|
|
||||||
_ => 80,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::{SpecialEndpoint, EndpointPath, extract_endpoint};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_extract_endpoint() {
|
|
||||||
let dapps_domain = ".web3.site";
|
|
||||||
|
|
||||||
// With path prefix
|
|
||||||
assert_eq!(
|
|
||||||
extract_endpoint(&"http://localhost:8080/status/index.html?q=1".parse().unwrap(), None, dapps_domain),
|
|
||||||
(Some(EndpointPath {
|
|
||||||
app_id: "status".to_owned(),
|
|
||||||
app_params: vec!["index.html".to_owned()],
|
|
||||||
query: Some("q=1".into()),
|
|
||||||
host: "localhost".to_owned(),
|
|
||||||
port: 8080,
|
|
||||||
using_dapps_domains: false,
|
|
||||||
}), SpecialEndpoint::None)
|
|
||||||
);
|
|
||||||
|
|
||||||
// With path prefix
|
|
||||||
assert_eq!(
|
|
||||||
extract_endpoint(&"http://localhost:8080/rpc/".parse().unwrap(), None, dapps_domain),
|
|
||||||
(Some(EndpointPath {
|
|
||||||
app_id: "rpc".to_owned(),
|
|
||||||
app_params: vec!["".to_owned()],
|
|
||||||
query: None,
|
|
||||||
host: "localhost".to_owned(),
|
|
||||||
port: 8080,
|
|
||||||
using_dapps_domains: false,
|
|
||||||
}), SpecialEndpoint::Rpc)
|
|
||||||
);
|
|
||||||
|
|
||||||
// By Subdomain
|
|
||||||
assert_eq!(
|
|
||||||
extract_endpoint(&"http://status.web3.site/test.html".parse().unwrap(), None, dapps_domain),
|
|
||||||
(Some(EndpointPath {
|
|
||||||
app_id: "status".to_owned(),
|
|
||||||
app_params: vec!["test.html".to_owned()],
|
|
||||||
query: None,
|
|
||||||
host: "status.web3.site".to_owned(),
|
|
||||||
port: 80,
|
|
||||||
using_dapps_domains: true,
|
|
||||||
}), SpecialEndpoint::None)
|
|
||||||
);
|
|
||||||
|
|
||||||
// RPC by subdomain
|
|
||||||
assert_eq!(
|
|
||||||
extract_endpoint(&"http://my.status.web3.site/rpc/".parse().unwrap(), None, dapps_domain),
|
|
||||||
(Some(EndpointPath {
|
|
||||||
app_id: "status".to_owned(),
|
|
||||||
app_params: vec!["my".into(), "".into()],
|
|
||||||
query: None,
|
|
||||||
host: "my.status.web3.site".to_owned(),
|
|
||||||
port: 80,
|
|
||||||
using_dapps_domains: true,
|
|
||||||
}), SpecialEndpoint::Rpc)
|
|
||||||
);
|
|
||||||
|
|
||||||
// API by subdomain
|
|
||||||
assert_eq!(
|
|
||||||
extract_endpoint(&"http://my.status.web3.site/api/".parse().unwrap(), None, dapps_domain),
|
|
||||||
(Some(EndpointPath {
|
|
||||||
app_id: "status".to_owned(),
|
|
||||||
app_params: vec!["my".into(), "".into()],
|
|
||||||
query: None,
|
|
||||||
host: "my.status.web3.site".to_owned(),
|
|
||||||
port: 80,
|
|
||||||
using_dapps_domains: true,
|
|
||||||
}), SpecialEndpoint::Api)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,85 +0,0 @@
|
|||||||
// Copyright 2015-2018 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, serve_with_registrar, request, assert_security_headers};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_return_error() {
|
|
||||||
// given
|
|
||||||
let server = serve();
|
|
||||||
|
|
||||||
// when
|
|
||||||
let response = request(server,
|
|
||||||
"\
|
|
||||||
GET /api/empty HTTP/1.1\r\n\
|
|
||||||
Host: 127.0.0.1:8080\r\n\
|
|
||||||
Connection: close\r\n\
|
|
||||||
\r\n\
|
|
||||||
{}
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
// then
|
|
||||||
response.assert_status("HTTP/1.1 404 Not Found");
|
|
||||||
response.assert_header("Content-Type", "application/json");
|
|
||||||
assert_eq!(response.body, format!("58\n{}\n0\n\n", r#"{"code":"404","title":"Not Found","detail":"Resource you requested has not been found."}"#));
|
|
||||||
assert_security_headers(&response.headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_handle_ping() {
|
|
||||||
// given
|
|
||||||
let server = serve();
|
|
||||||
|
|
||||||
// when
|
|
||||||
let response = request(server,
|
|
||||||
"\
|
|
||||||
POST /api/ping HTTP/1.1\r\n\
|
|
||||||
Host: home.parity\r\n\
|
|
||||||
Content-Type: application/json\r\n\
|
|
||||||
Connection: close\r\n\
|
|
||||||
\r\n\
|
|
||||||
{}
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
// then
|
|
||||||
response.assert_status("HTTP/1.1 200 OK");
|
|
||||||
response.assert_header("Content-Type", "application/json");
|
|
||||||
assert_eq!(response.body, "0\n\n".to_owned());
|
|
||||||
assert_security_headers(&response.headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_try_to_resolve_dapp() {
|
|
||||||
// given
|
|
||||||
let (server, registrar) = serve_with_registrar();
|
|
||||||
|
|
||||||
// when
|
|
||||||
let response = request(server,
|
|
||||||
"\
|
|
||||||
GET /api/content/1472a9e190620cdf6b31f383373e45efcfe869a820c91f9ccd7eb9fb45e4985d HTTP/1.1\r\n\
|
|
||||||
Host: home.parity\r\n\
|
|
||||||
Connection: close\r\n\
|
|
||||||
\r\n\
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
// then
|
|
||||||
response.assert_status("HTTP/1.1 404 Not Found");
|
|
||||||
assert_eq!(registrar.calls.lock().len(), 2);
|
|
||||||
assert_security_headers(&response.headers);
|
|
||||||
}
|
|
@ -1,544 +0,0 @@
|
|||||||
// Copyright 2015-2018 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 devtools::http_client;
|
|
||||||
use rustc_hex::FromHex;
|
|
||||||
use tests::helpers::{
|
|
||||||
serve_with_registrar, serve_with_registrar_and_sync, serve_with_fetch,
|
|
||||||
serve_with_registrar_and_fetch,
|
|
||||||
request, assert_security_headers
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_resolve_dapp() {
|
|
||||||
// given
|
|
||||||
let (server, registrar) = serve_with_registrar();
|
|
||||||
|
|
||||||
// when
|
|
||||||
let response = request(server,
|
|
||||||
"\
|
|
||||||
GET / HTTP/1.1\r\n\
|
|
||||||
Host: 1472a9e190620cdf6b31f383373e45efcfe869a820c91f9ccd7eb9fb45e4985d.web3.site\r\n\
|
|
||||||
Connection: close\r\n\
|
|
||||||
\r\n\
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
// then
|
|
||||||
response.assert_status("HTTP/1.1 404 Not Found");
|
|
||||||
assert_eq!(registrar.calls.lock().len(), 4);
|
|
||||||
assert_security_headers(&response.headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_return_503_when_syncing_but_should_make_the_calls() {
|
|
||||||
// given
|
|
||||||
let (server, registrar) = serve_with_registrar_and_sync();
|
|
||||||
|
|
||||||
// when
|
|
||||||
let response = request(server,
|
|
||||||
"\
|
|
||||||
GET / HTTP/1.1\r\n\
|
|
||||||
Host: 1472a9e190620cdf6b31f383373e45efcfe869a820c91f9ccd7eb9fb45e4985d.web3.site\r\n\
|
|
||||||
Connection: close\r\n\
|
|
||||||
\r\n\
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
// then
|
|
||||||
response.assert_status("HTTP/1.1 503 Service Unavailable");
|
|
||||||
assert_eq!(registrar.calls.lock().len(), 2);
|
|
||||||
assert_security_headers(&response.headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
const GAVCOIN_DAPP: &'static str = "00000000000000000000000000000000000000000000000000000000000000609faf32e1e3845e237cc6efd27187cee13b3b99db000000000000000000000000000000000000000000000000d8bd350823e28ff75e74a34215faefdc8a52fd8e00000000000000000000000000000000000000000000000000000000000000116761766f66796f726b2f676176636f696e000000000000000000000000000000";
|
|
||||||
const GAVCOIN_ICON: &'static str = "00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d8bd350823e28ff75e74a34215faefdc8a52fd8e000000000000000000000000000000000000000000000000000000000000007768747470733a2f2f7261772e67697468756275736572636f6e74656e742e636f6d2f657468636f72652f646170702d6173736574732f623838653938336162616131613661363334356238643934343863313562313137646462353430652f746f6b656e732f676176636f696e2d36347836342e706e67000000000000000000";
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_return_502_on_hash_mismatch() {
|
|
||||||
// given
|
|
||||||
let (server, fetch, registrar) = serve_with_registrar_and_fetch();
|
|
||||||
let gavcoin = GAVCOIN_DAPP.from_hex().unwrap();
|
|
||||||
registrar.set_result(
|
|
||||||
"94f093625c06887d94d9fee0d5f9cc4aaa46f33d24d1c7e4b5237e7c37d547dd".parse().unwrap(),
|
|
||||||
Ok(gavcoin.clone())
|
|
||||||
);
|
|
||||||
|
|
||||||
// when
|
|
||||||
let response = request(server,
|
|
||||||
"\
|
|
||||||
GET / HTTP/1.1\r\n\
|
|
||||||
Host: 94f093625c06887d94d9fee0d5f9cc4aaa46f33d24d1c7e4b5237e7c37d547dd.web3.site\r\n\
|
|
||||||
Connection: close\r\n\
|
|
||||||
\r\n\
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(registrar.calls.lock().len(), 4);
|
|
||||||
|
|
||||||
fetch.assert_requested("https://codeload.github.com/gavofyork/gavcoin/zip/9faf32e1e3845e237cc6efd27187cee13b3b99db");
|
|
||||||
fetch.assert_no_more_requests();
|
|
||||||
|
|
||||||
response.assert_status("HTTP/1.1 502 Bad Gateway");
|
|
||||||
assert!(response.body.contains("HashMismatch"), "Expected hash mismatch response, got: {:?}", response.body);
|
|
||||||
assert_security_headers(&response.headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_return_error_for_invalid_dapp_zip() {
|
|
||||||
// given
|
|
||||||
let (server, fetch, registrar) = serve_with_registrar_and_fetch();
|
|
||||||
let gavcoin = GAVCOIN_DAPP.from_hex().unwrap();
|
|
||||||
registrar.set_result(
|
|
||||||
"2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e".parse().unwrap(),
|
|
||||||
Ok(gavcoin.clone())
|
|
||||||
);
|
|
||||||
|
|
||||||
// when
|
|
||||||
let response = request(server,
|
|
||||||
"\
|
|
||||||
GET / HTTP/1.1\r\n\
|
|
||||||
Host: 2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e.web3.site\r\n\
|
|
||||||
Connection: close\r\n\
|
|
||||||
\r\n\
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(registrar.calls.lock().len(), 4);
|
|
||||||
|
|
||||||
fetch.assert_requested("https://codeload.github.com/gavofyork/gavcoin/zip/9faf32e1e3845e237cc6efd27187cee13b3b99db");
|
|
||||||
fetch.assert_no_more_requests();
|
|
||||||
|
|
||||||
response.assert_status("HTTP/1.1 502 Bad Gateway");
|
|
||||||
assert!(response.body.contains("InvalidArchive"), "Expected invalid zip response, got: {:?}", response.body);
|
|
||||||
assert_security_headers(&response.headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_return_fetched_dapp_content() {
|
|
||||||
// given
|
|
||||||
let (server, fetch, registrar) = serve_with_registrar_and_fetch();
|
|
||||||
let gavcoin = GAVCOIN_DAPP.from_hex().unwrap();
|
|
||||||
registrar.set_result(
|
|
||||||
"9c94e154dab8acf859b30ee80fc828fb1d38359d938751b65db71d460588d82a".parse().unwrap(),
|
|
||||||
Ok(gavcoin.clone())
|
|
||||||
);
|
|
||||||
fetch.set_response(include_bytes!("../../res/gavcoin.zip"));
|
|
||||||
|
|
||||||
// when
|
|
||||||
let response1 = http_client::request(server.addr(),
|
|
||||||
"\
|
|
||||||
GET /index.html HTTP/1.1\r\n\
|
|
||||||
Host: 9c94e154dab8acf859b30ee80fc828fb1d38359d938751b65db71d460588d82a.web3.site\r\n\
|
|
||||||
Connection: close\r\n\
|
|
||||||
\r\n\
|
|
||||||
"
|
|
||||||
);
|
|
||||||
let response2 = http_client::request(server.addr(),
|
|
||||||
"\
|
|
||||||
GET /manifest.json HTTP/1.1\r\n\
|
|
||||||
Host: 9c94e154dab8acf859b30ee80fc828fb1d38359d938751b65db71d460588d82a.web3.site\r\n\
|
|
||||||
Connection: close\r\n\
|
|
||||||
\r\n\
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(registrar.calls.lock().len(), 4);
|
|
||||||
|
|
||||||
fetch.assert_requested("https://codeload.github.com/gavofyork/gavcoin/zip/9faf32e1e3845e237cc6efd27187cee13b3b99db");
|
|
||||||
fetch.assert_no_more_requests();
|
|
||||||
|
|
||||||
response1.assert_status("HTTP/1.1 200 OK");
|
|
||||||
assert_security_headers(&response1.headers);
|
|
||||||
assert!(
|
|
||||||
response1.body.contains(r#"18
|
|
||||||
<h1>Hello Gavcoin!</h1>
|
|
||||||
|
|
||||||
0
|
|
||||||
|
|
||||||
"#),
|
|
||||||
"Expected Gavcoin body: {}",
|
|
||||||
response1.body
|
|
||||||
);
|
|
||||||
|
|
||||||
response2.assert_status("HTTP/1.1 200 OK");
|
|
||||||
assert_security_headers(&response2.headers);
|
|
||||||
assert_eq!(
|
|
||||||
response2.body,
|
|
||||||
r#"EA
|
|
||||||
{
|
|
||||||
"id": "9c94e154dab8acf859b30ee80fc828fb1d38359d938751b65db71d460588d82a",
|
|
||||||
"name": "Gavcoin",
|
|
||||||
"description": "Gavcoin",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"author": "",
|
|
||||||
"iconUrl": "icon.png",
|
|
||||||
"localUrl": null,
|
|
||||||
"allowJsEval": false
|
|
||||||
}
|
|
||||||
0
|
|
||||||
|
|
||||||
"#
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_return_fetched_content() {
|
|
||||||
// given
|
|
||||||
let (server, fetch, registrar) = serve_with_registrar_and_fetch();
|
|
||||||
let gavcoin = GAVCOIN_ICON.from_hex().unwrap();
|
|
||||||
registrar.set_result(
|
|
||||||
"2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e".parse().unwrap(),
|
|
||||||
Ok(gavcoin.clone())
|
|
||||||
);
|
|
||||||
|
|
||||||
// when
|
|
||||||
let response = request(server,
|
|
||||||
"\
|
|
||||||
GET / HTTP/1.1\r\n\
|
|
||||||
Host: 2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e.web3.site\r\n\
|
|
||||||
Connection: close\r\n\
|
|
||||||
\r\n\
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(registrar.calls.lock().len(), 4);
|
|
||||||
|
|
||||||
fetch.assert_requested("https://raw.githubusercontent.com/ethcore/dapp-assets/b88e983abaa1a6a6345b8d9448c15b117ddb540e/tokens/gavcoin-64x64.png");
|
|
||||||
fetch.assert_no_more_requests();
|
|
||||||
|
|
||||||
response.assert_status("HTTP/1.1 200 OK");
|
|
||||||
response.assert_security_headers_present(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_cache_content() {
|
|
||||||
// given
|
|
||||||
let (server, fetch, registrar) = serve_with_registrar_and_fetch();
|
|
||||||
let gavcoin = GAVCOIN_ICON.from_hex().unwrap();
|
|
||||||
registrar.set_result(
|
|
||||||
"2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e".parse().unwrap(),
|
|
||||||
Ok(gavcoin.clone())
|
|
||||||
);
|
|
||||||
let request_str = "\
|
|
||||||
GET / HTTP/1.1\r\n\
|
|
||||||
Host: 2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e.web3.site\r\n\
|
|
||||||
Connection: close\r\n\
|
|
||||||
\r\n\
|
|
||||||
";
|
|
||||||
|
|
||||||
let response = http_client::request(server.addr(), request_str);
|
|
||||||
fetch.assert_requested("https://raw.githubusercontent.com/ethcore/dapp-assets/b88e983abaa1a6a6345b8d9448c15b117ddb540e/tokens/gavcoin-64x64.png");
|
|
||||||
fetch.assert_no_more_requests();
|
|
||||||
response.assert_status("HTTP/1.1 200 OK");
|
|
||||||
|
|
||||||
// when
|
|
||||||
let response = http_client::request(server.addr(), request_str);
|
|
||||||
|
|
||||||
// then
|
|
||||||
fetch.assert_no_more_requests();
|
|
||||||
response.assert_status("HTTP/1.1 200 OK");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_not_request_content_twice() {
|
|
||||||
use std::thread;
|
|
||||||
|
|
||||||
// given
|
|
||||||
let (server, fetch, registrar) = serve_with_registrar_and_fetch();
|
|
||||||
let gavcoin = GAVCOIN_ICON.from_hex().unwrap();
|
|
||||||
registrar.set_result(
|
|
||||||
"2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e".parse().unwrap(),
|
|
||||||
Ok(gavcoin.clone())
|
|
||||||
);
|
|
||||||
let request_str = "\
|
|
||||||
GET / HTTP/1.1\r\n\
|
|
||||||
Host: 2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e.web3.site\r\n\
|
|
||||||
Connection: close\r\n\
|
|
||||||
\r\n\
|
|
||||||
";
|
|
||||||
let fire_request = || {
|
|
||||||
let addr = server.addr().to_owned();
|
|
||||||
let req = request_str.to_owned();
|
|
||||||
thread::spawn(move || {
|
|
||||||
http_client::request(&addr, &req)
|
|
||||||
})
|
|
||||||
};
|
|
||||||
let control = fetch.manual();
|
|
||||||
|
|
||||||
// when
|
|
||||||
|
|
||||||
// Fire two requests at the same time
|
|
||||||
let r1 = fire_request();
|
|
||||||
let r2 = fire_request();
|
|
||||||
|
|
||||||
// wait for single request in fetch, the second one should go into waiting state.
|
|
||||||
control.wait_for_requests(1);
|
|
||||||
control.respond();
|
|
||||||
|
|
||||||
let response1 = r1.join().unwrap();
|
|
||||||
let response2 = r2.join().unwrap();
|
|
||||||
|
|
||||||
// then
|
|
||||||
fetch.assert_requested("https://raw.githubusercontent.com/ethcore/dapp-assets/b88e983abaa1a6a6345b8d9448c15b117ddb540e/tokens/gavcoin-64x64.png");
|
|
||||||
fetch.assert_no_more_requests();
|
|
||||||
response1.assert_status("HTTP/1.1 200 OK");
|
|
||||||
response2.assert_status("HTTP/1.1 200 OK");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_encode_and_decode_base32() {
|
|
||||||
use base32;
|
|
||||||
|
|
||||||
let encoded = base32::encode(base32::Alphabet::Crockford, "token+https://parity.io".as_bytes());
|
|
||||||
assert_eq!("EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY", &encoded);
|
|
||||||
|
|
||||||
let data = base32::decode(base32::Alphabet::Crockford, "EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY").unwrap();
|
|
||||||
assert_eq!("token+https://parity.io", &String::from_utf8(data).unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_stream_web_content() {
|
|
||||||
// given
|
|
||||||
let (server, fetch) = serve_with_fetch("token", "https://parity.io");
|
|
||||||
|
|
||||||
// when
|
|
||||||
let response = request(server,
|
|
||||||
"\
|
|
||||||
GET / HTTP/1.1\r\n\
|
|
||||||
Host: EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY.web.web3.site\r\n\
|
|
||||||
Connection: close\r\n\
|
|
||||||
\r\n\
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
// then
|
|
||||||
response.assert_status("HTTP/1.1 200 OK");
|
|
||||||
assert_security_headers(&response.headers);
|
|
||||||
|
|
||||||
fetch.assert_requested("https://parity.io/");
|
|
||||||
fetch.assert_no_more_requests();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_support_base32_encoded_web_urls() {
|
|
||||||
// given
|
|
||||||
let (server, fetch) = serve_with_fetch("token", "https://parity.io");
|
|
||||||
|
|
||||||
// when
|
|
||||||
let response = request(server,
|
|
||||||
"\
|
|
||||||
GET /styles.css?test=123 HTTP/1.1\r\n\
|
|
||||||
Host: EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY.web.web3.site\r\n\
|
|
||||||
Connection: close\r\n\
|
|
||||||
\r\n\
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
// then
|
|
||||||
response.assert_status("HTTP/1.1 200 OK");
|
|
||||||
assert_security_headers(&response.headers);
|
|
||||||
|
|
||||||
fetch.assert_requested("https://parity.io/styles.css?test=123");
|
|
||||||
fetch.assert_no_more_requests();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_correctly_handle_long_label_when_splitted() {
|
|
||||||
// given
|
|
||||||
let (server, fetch) = serve_with_fetch("xolrg9fePeQyKLnL", "https://contribution.melonport.com");
|
|
||||||
|
|
||||||
// when
|
|
||||||
let response = request(server,
|
|
||||||
"\
|
|
||||||
GET /styles.css?test=123 HTTP/1.1\r\n\
|
|
||||||
Host: f1qprwk775k6am35a5wmpk3e9gnpgx3me1sk.mbsfcdqpwx3jd5h7ax39dxq2wvb5dhqpww3fe9t2wrvfdm.web.web3.site\r\n\
|
|
||||||
Connection: close\r\n\
|
|
||||||
\r\n\
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
// then
|
|
||||||
response.assert_status("HTTP/1.1 200 OK");
|
|
||||||
assert_security_headers(&response.headers);
|
|
||||||
|
|
||||||
fetch.assert_requested("https://contribution.melonport.com/styles.css?test=123");
|
|
||||||
fetch.assert_no_more_requests();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_support_base32_encoded_web_urls_as_path() {
|
|
||||||
// given
|
|
||||||
let (server, fetch) = serve_with_fetch("token", "https://parity.io");
|
|
||||||
|
|
||||||
// when
|
|
||||||
let response = request(server,
|
|
||||||
"\
|
|
||||||
GET /web/EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY/styles.css?test=123 HTTP/1.1\r\n\
|
|
||||||
Host: localhost:8080\r\n\
|
|
||||||
Connection: close\r\n\
|
|
||||||
\r\n\
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
// then
|
|
||||||
response.assert_status("HTTP/1.1 200 OK");
|
|
||||||
assert_security_headers(&response.headers);
|
|
||||||
|
|
||||||
fetch.assert_requested("https://parity.io/styles.css?test=123");
|
|
||||||
fetch.assert_no_more_requests();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_return_error_on_non_whitelisted_domain() {
|
|
||||||
// given
|
|
||||||
let (server, fetch) = serve_with_fetch("token", "https://ethcore.io");
|
|
||||||
|
|
||||||
// when
|
|
||||||
let response = request(server,
|
|
||||||
"\
|
|
||||||
GET / HTTP/1.1\r\n\
|
|
||||||
Host: EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY.web.web3.site\r\n\
|
|
||||||
Connection: close\r\n\
|
|
||||||
\r\n\
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
// then
|
|
||||||
response.assert_status("HTTP/1.1 400 Bad Request");
|
|
||||||
assert_security_headers(&response.headers);
|
|
||||||
|
|
||||||
fetch.assert_no_more_requests();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_return_error_on_invalid_token() {
|
|
||||||
// given
|
|
||||||
let (server, fetch) = serve_with_fetch("test", "https://parity.io");
|
|
||||||
|
|
||||||
// when
|
|
||||||
let response = request(server,
|
|
||||||
"\
|
|
||||||
GET / HTTP/1.1\r\n\
|
|
||||||
Host: EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY.web.web3.site\r\n\
|
|
||||||
Connection: close\r\n\
|
|
||||||
\r\n\
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
// then
|
|
||||||
response.assert_status("HTTP/1.1 400 Bad Request");
|
|
||||||
assert_security_headers(&response.headers);
|
|
||||||
|
|
||||||
fetch.assert_no_more_requests();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_return_error_on_invalid_protocol() {
|
|
||||||
// given
|
|
||||||
let (server, fetch) = serve_with_fetch("token", "ftp://parity.io");
|
|
||||||
|
|
||||||
// when
|
|
||||||
let response = request(server,
|
|
||||||
"\
|
|
||||||
GET /web/token/ftp/parity.io/ HTTP/1.1\r\n\
|
|
||||||
Host: localhost:8080\r\n\
|
|
||||||
Connection: close\r\n\
|
|
||||||
\r\n\
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
// then
|
|
||||||
response.assert_status("HTTP/1.1 400 Bad Request");
|
|
||||||
assert_security_headers(&response.headers);
|
|
||||||
|
|
||||||
fetch.assert_no_more_requests();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_disallow_non_get_requests() {
|
|
||||||
// given
|
|
||||||
let (server, fetch) = serve_with_fetch("token", "https://parity.io");
|
|
||||||
|
|
||||||
// when
|
|
||||||
let response = request(server,
|
|
||||||
"\
|
|
||||||
POST / HTTP/1.1\r\n\
|
|
||||||
Host: EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY.web.web3.site\r\n\
|
|
||||||
Content-Type: application/json\r\n\
|
|
||||||
Connection: close\r\n\
|
|
||||||
\r\n\
|
|
||||||
123\r\n\
|
|
||||||
\r\n\
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
// then
|
|
||||||
response.assert_status("HTTP/1.1 405 Method Not Allowed");
|
|
||||||
assert_security_headers(&response.headers);
|
|
||||||
|
|
||||||
fetch.assert_no_more_requests();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_fix_absolute_requests_based_on_referer() {
|
|
||||||
// given
|
|
||||||
let (server, fetch) = serve_with_fetch("token", "https://parity.io");
|
|
||||||
|
|
||||||
// when
|
|
||||||
let response = request(server,
|
|
||||||
"\
|
|
||||||
GET /styles.css HTTP/1.1\r\n\
|
|
||||||
Host: localhost:8080\r\n\
|
|
||||||
Connection: close\r\n\
|
|
||||||
Referer: http://localhost:8080/web/EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY/\r\n\
|
|
||||||
\r\n\
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
// then
|
|
||||||
response.assert_status("HTTP/1.1 302 Found");
|
|
||||||
response.assert_header("Location", "/web/EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY/styles.css");
|
|
||||||
|
|
||||||
fetch.assert_no_more_requests();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_fix_absolute_requests_based_on_referer_in_url() {
|
|
||||||
// given
|
|
||||||
let (server, fetch) = serve_with_fetch("token", "https://parity.io");
|
|
||||||
|
|
||||||
// when
|
|
||||||
let response = request(server,
|
|
||||||
"\
|
|
||||||
GET /styles.css HTTP/1.1\r\n\
|
|
||||||
Host: localhost:8080\r\n\
|
|
||||||
Connection: close\r\n\
|
|
||||||
Referer: http://localhost:8080/?__referer=web/EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY/\r\n\
|
|
||||||
\r\n\
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
// then
|
|
||||||
response.assert_status("HTTP/1.1 302 Found");
|
|
||||||
response.assert_header("Location", "/web/EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY/styles.css");
|
|
||||||
|
|
||||||
fetch.assert_no_more_requests();
|
|
||||||
}
|
|
@ -1,134 +0,0 @@
|
|||||||
// Copyright 2015-2018 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 std::{thread, time};
|
|
||||||
use std::sync::{atomic, mpsc, Arc};
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
use hyper;
|
|
||||||
|
|
||||||
use futures::{self, future, Future};
|
|
||||||
use fetch::{self, Fetch, Url, Request, Abort};
|
|
||||||
|
|
||||||
pub struct FetchControl {
|
|
||||||
sender: mpsc::Sender<()>,
|
|
||||||
fetch: FakeFetch,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FetchControl {
|
|
||||||
pub fn respond(self) {
|
|
||||||
self.sender.send(())
|
|
||||||
.expect("Fetch cannot be finished without sending a response at least once.");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn wait_for_requests(&self, len: usize) {
|
|
||||||
const MAX_TIMEOUT: time::Duration = time::Duration::from_millis(5000);
|
|
||||||
const ATTEMPTS: u32 = 10;
|
|
||||||
let mut attempts_left = ATTEMPTS;
|
|
||||||
loop {
|
|
||||||
let current = self.fetch.requested.lock().len();
|
|
||||||
|
|
||||||
if current == len {
|
|
||||||
break;
|
|
||||||
} else if attempts_left == 0 {
|
|
||||||
panic!(
|
|
||||||
"Timeout reached when waiting for pending requests. Expected: {}, current: {}",
|
|
||||||
len, current
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
attempts_left -= 1;
|
|
||||||
// Should we handle spurious timeouts better?
|
|
||||||
thread::park_timeout(MAX_TIMEOUT / ATTEMPTS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
|
||||||
pub struct FakeFetch {
|
|
||||||
manual: Arc<Mutex<Option<mpsc::Receiver<()>>>>,
|
|
||||||
response: Arc<Mutex<Option<&'static [u8]>>>,
|
|
||||||
asserted: Arc<atomic::AtomicUsize>,
|
|
||||||
requested: Arc<Mutex<Vec<String>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FakeFetch {
|
|
||||||
pub fn set_response(&self, data: &'static [u8]) {
|
|
||||||
*self.response.lock() = Some(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn manual(&self) -> FetchControl {
|
|
||||||
assert!(self.manual.lock().is_none(), "Only one manual control may be active.");
|
|
||||||
let (tx, rx) = mpsc::channel();
|
|
||||||
*self.manual.lock() = Some(rx);
|
|
||||||
|
|
||||||
FetchControl {
|
|
||||||
sender: tx,
|
|
||||||
fetch: self.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn assert_requested(&self, url: &str) {
|
|
||||||
let requests = self.requested.lock();
|
|
||||||
let idx = self.asserted.fetch_add(1, atomic::Ordering::SeqCst);
|
|
||||||
|
|
||||||
assert_eq!(requests.get(idx), Some(&url.to_owned()), "Expected fetch from specific URL.");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn assert_no_more_requests(&self) {
|
|
||||||
let requests = self.requested.lock();
|
|
||||||
let len = self.asserted.load(atomic::Ordering::SeqCst);
|
|
||||||
assert_eq!(requests.len(), len, "Didn't expect any more requests, got: {:?}", &requests[len..]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Fetch for FakeFetch {
|
|
||||||
type Result = Box<Future<Item = fetch::Response, Error = fetch::Error> + Send>;
|
|
||||||
|
|
||||||
fn fetch(&self, request: Request, abort: fetch::Abort) -> Self::Result {
|
|
||||||
let u = request.url().clone();
|
|
||||||
self.requested.lock().push(u.as_str().into());
|
|
||||||
let manual = self.manual.clone();
|
|
||||||
let response = self.response.clone();
|
|
||||||
|
|
||||||
let (tx, rx) = futures::oneshot();
|
|
||||||
thread::spawn(move || {
|
|
||||||
if let Some(rx) = manual.lock().take() {
|
|
||||||
// wait for manual resume
|
|
||||||
let _ = rx.recv();
|
|
||||||
}
|
|
||||||
let data = response.lock().take().unwrap_or(b"Some content");
|
|
||||||
tx.send(fetch::Response::new(u, hyper::Response::new().with_body(data), abort)).unwrap();
|
|
||||||
});
|
|
||||||
|
|
||||||
Box::new(rx.map_err(|_| fetch::Error::Aborted))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get(&self, url: &str, abort: Abort) -> Self::Result {
|
|
||||||
let url: Url = match url.parse() {
|
|
||||||
Ok(u) => u,
|
|
||||||
Err(e) => return Box::new(future::err(e.into()))
|
|
||||||
};
|
|
||||||
self.fetch(Request::get(url), abort)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn post(&self, url: &str, abort: Abort) -> Self::Result {
|
|
||||||
let url: Url = match url.parse() {
|
|
||||||
Ok(u) => u,
|
|
||||||
Err(e) => return Box::new(future::err(e.into()))
|
|
||||||
};
|
|
||||||
self.fetch(Request::post(url), abort)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,252 +0,0 @@
|
|||||||
// Copyright 2015-2018 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 std::{env, io, str};
|
|
||||||
use std::net::SocketAddr;
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
use std::sync::Arc;
|
|
||||||
use env_logger::LogBuilder;
|
|
||||||
use jsonrpc_core::IoHandler;
|
|
||||||
use jsonrpc_http_server::{self as http, Host, DomainsValidation};
|
|
||||||
use parity_reactor::Remote;
|
|
||||||
|
|
||||||
use devtools::http_client;
|
|
||||||
use registrar::{RegistrarClient, Asynchronous};
|
|
||||||
use fetch::{Fetch, Client as FetchClient};
|
|
||||||
use node_health::{NodeHealth, TimeChecker, CpuPool};
|
|
||||||
|
|
||||||
use {Middleware, SyncStatus, WebProxyTokens};
|
|
||||||
|
|
||||||
mod registrar;
|
|
||||||
mod fetch;
|
|
||||||
|
|
||||||
use self::registrar::FakeRegistrar;
|
|
||||||
use self::fetch::FakeFetch;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct FakeSync(bool);
|
|
||||||
impl SyncStatus for FakeSync {
|
|
||||||
fn is_major_importing(&self) -> bool { self.0 }
|
|
||||||
fn peers(&self) -> (usize, usize) { (0, 5) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init_logger() {
|
|
||||||
// Initialize logger
|
|
||||||
if let Ok(log) = env::var("RUST_LOG") {
|
|
||||||
let mut builder = LogBuilder::new();
|
|
||||||
builder.parse(&log);
|
|
||||||
let _ = builder.init(); // ignore errors since ./test.sh will call this multiple times.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init_server<F, B>(process: F, io: IoHandler) -> (Server, Arc<FakeRegistrar>) where
|
|
||||||
F: FnOnce(ServerBuilder) -> ServerBuilder<B>,
|
|
||||||
B: Fetch,
|
|
||||||
{
|
|
||||||
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 builder = ServerBuilder::new(FetchClient::new().unwrap(), &dapps_path, registrar.clone());
|
|
||||||
let server = process(builder).start_unsecured_http(&"127.0.0.1:0".parse().unwrap(), io).unwrap();
|
|
||||||
(
|
|
||||||
server,
|
|
||||||
registrar,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn serve_with_rpc(io: IoHandler) -> Server {
|
|
||||||
init_server(|builder| builder, io).0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn serve_hosts(hosts: Option<Vec<String>>) -> Server {
|
|
||||||
let hosts = hosts.map(|hosts| hosts.into_iter().map(Into::into).collect());
|
|
||||||
init_server(|mut builder| {
|
|
||||||
builder.allowed_hosts = hosts.into();
|
|
||||||
builder
|
|
||||||
}, Default::default()).0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn serve_with_registrar() -> (Server, Arc<FakeRegistrar>) {
|
|
||||||
init_server(|builder| builder, Default::default())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn serve_with_registrar_and_sync() -> (Server, Arc<FakeRegistrar>) {
|
|
||||||
init_server(|mut builder| {
|
|
||||||
builder.sync_status = Arc::new(FakeSync(true));
|
|
||||||
builder
|
|
||||||
}, Default::default())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn serve_with_registrar_and_fetch() -> (Server, FakeFetch, Arc<FakeRegistrar>) {
|
|
||||||
let fetch = FakeFetch::default();
|
|
||||||
let f = fetch.clone();
|
|
||||||
let (server, reg) = init_server(move |builder| {
|
|
||||||
builder.fetch(f.clone())
|
|
||||||
}, Default::default());
|
|
||||||
|
|
||||||
(server, fetch, reg)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn serve_with_fetch(web_token: &'static str, domain: &'static str) -> (Server, FakeFetch) {
|
|
||||||
let fetch = FakeFetch::default();
|
|
||||||
let f = fetch.clone();
|
|
||||||
let (server, _) = init_server(move |mut builder| {
|
|
||||||
builder.web_proxy_tokens = Arc::new(move |token| {
|
|
||||||
if &token == web_token { Some(domain.into()) } else { None }
|
|
||||||
});
|
|
||||||
builder.fetch(f.clone())
|
|
||||||
}, Default::default());
|
|
||||||
|
|
||||||
(server, fetch)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn serve() -> Server {
|
|
||||||
init_server(|builder| builder, Default::default()).0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn request(server: Server, request: &str) -> http_client::Response {
|
|
||||||
http_client::request(server.addr(), request)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn assert_security_headers(headers: &[String]) {
|
|
||||||
http_client::assert_security_headers_present(headers, None)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Webapps HTTP+RPC server build.
|
|
||||||
pub struct ServerBuilder<T: Fetch = FetchClient> {
|
|
||||||
dapps_path: PathBuf,
|
|
||||||
registrar: Arc<RegistrarClient<Call=Asynchronous>>,
|
|
||||||
sync_status: Arc<SyncStatus>,
|
|
||||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
|
||||||
allowed_hosts: DomainsValidation<Host>,
|
|
||||||
fetch: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ServerBuilder {
|
|
||||||
/// Construct new dapps server
|
|
||||||
pub fn new<P: AsRef<Path>>(fetch: FetchClient, dapps_path: P, registrar: Arc<RegistrarClient<Call=Asynchronous>>) -> Self {
|
|
||||||
ServerBuilder {
|
|
||||||
dapps_path: dapps_path.as_ref().to_owned(),
|
|
||||||
registrar: registrar,
|
|
||||||
sync_status: Arc::new(FakeSync(false)),
|
|
||||||
web_proxy_tokens: Arc::new(|_| None),
|
|
||||||
allowed_hosts: DomainsValidation::Disabled,
|
|
||||||
fetch: fetch,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
allowed_hosts: self.allowed_hosts,
|
|
||||||
fetch: fetch,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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) -> io::Result<Server> {
|
|
||||||
Server::start_http(
|
|
||||||
addr,
|
|
||||||
io,
|
|
||||||
self.allowed_hosts,
|
|
||||||
self.dapps_path,
|
|
||||||
vec![],
|
|
||||||
self.registrar,
|
|
||||||
self.sync_status,
|
|
||||||
self.web_proxy_tokens,
|
|
||||||
Remote::new_sync(),
|
|
||||||
self.fetch,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const DAPPS_DOMAIN: &'static str = "web3.site";
|
|
||||||
|
|
||||||
/// 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>,
|
|
||||||
dapps_path: PathBuf,
|
|
||||||
extra_dapps: Vec<PathBuf>,
|
|
||||||
registrar: Arc<RegistrarClient<Call=Asynchronous>>,
|
|
||||||
sync_status: Arc<SyncStatus>,
|
|
||||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
|
||||||
remote: Remote,
|
|
||||||
fetch: F,
|
|
||||||
) -> io::Result<Server> {
|
|
||||||
let health = NodeHealth::new(
|
|
||||||
sync_status.clone(),
|
|
||||||
TimeChecker::new::<String>(&[], CpuPool::new(1)),
|
|
||||||
remote.clone(),
|
|
||||||
);
|
|
||||||
let pool = ::futures_cpupool::CpuPool::new(1);
|
|
||||||
let middleware =
|
|
||||||
Middleware::dapps(
|
|
||||||
pool,
|
|
||||||
health,
|
|
||||||
dapps_path,
|
|
||||||
extra_dapps,
|
|
||||||
DAPPS_DOMAIN.into(),
|
|
||||||
registrar,
|
|
||||||
sync_status,
|
|
||||||
web_proxy_tokens,
|
|
||||||
fetch,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut allowed_hosts: Option<Vec<Host>> = allowed_hosts.into();
|
|
||||||
allowed_hosts.as_mut().map(|hosts| {
|
|
||||||
hosts.push(format!("http://*.{}:*", DAPPS_DOMAIN).into());
|
|
||||||
hosts.push(format!("http://*.{}", DAPPS_DOMAIN).into());
|
|
||||||
});
|
|
||||||
|
|
||||||
http::ServerBuilder::new(io)
|
|
||||||
.request_middleware(middleware)
|
|
||||||
.allowed_hosts(allowed_hosts.into())
|
|
||||||
.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")
|
|
||||||
.address()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Server {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.server.take().unwrap().close()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,77 +0,0 @@
|
|||||||
// Copyright 2015-2018 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 std::str;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use ethereum_types::{H256, Address};
|
|
||||||
use bytes::{Bytes, ToPretty};
|
|
||||||
use registrar::{RegistrarClient, Asynchronous};
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
use rustc_hex::FromHex;
|
|
||||||
|
|
||||||
const REGISTRAR: &'static str = "8e4e9b13d4b45cb0befc93c3061b1408f67316b2";
|
|
||||||
const URLHINT: &'static str = "deadbeefcafe0000000000000000000000000000";
|
|
||||||
const URLHINT_RESOLVE: &'static str = "267b6922";
|
|
||||||
const DEFAULT_HASH: &'static str = "1472a9e190620cdf6b31f383373e45efcfe869a820c91f9ccd7eb9fb45e4985d";
|
|
||||||
|
|
||||||
pub struct FakeRegistrar {
|
|
||||||
pub calls: Arc<Mutex<Vec<(String, String)>>>,
|
|
||||||
pub responses: Mutex<HashMap<(String, String), Result<Bytes, String>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FakeRegistrar {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
FakeRegistrar {
|
|
||||||
calls: Arc::new(Mutex::new(Vec::new())),
|
|
||||||
responses: Mutex::new({
|
|
||||||
let mut map = HashMap::new();
|
|
||||||
map.insert(
|
|
||||||
(REGISTRAR.into(), "6795dbcd058740ee9a5a3fb9f1cfa10752baec87e09cc45cd7027fd54708271aca300c75000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000014100000000000000000000000000000000000000000000000000000000000000".into()),
|
|
||||||
Ok(format!("000000000000000000000000{}", URLHINT).from_hex().unwrap()),
|
|
||||||
);
|
|
||||||
map.insert(
|
|
||||||
(URLHINT.into(), format!("{}{}", URLHINT_RESOLVE, DEFAULT_HASH)),
|
|
||||||
Ok(vec![])
|
|
||||||
);
|
|
||||||
map
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_result(&self, hash: H256, result: Result<Bytes, String>) {
|
|
||||||
self.responses.lock().insert(
|
|
||||||
(URLHINT.into(), format!("{}{:x}", URLHINT_RESOLVE, hash)),
|
|
||||||
result
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RegistrarClient for FakeRegistrar {
|
|
||||||
type Call = Asynchronous;
|
|
||||||
|
|
||||||
fn registrar_address(&self) -> Result<Address, String> {
|
|
||||||
Ok(REGISTRAR.parse().unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call_contract(&self, address: Address, data: Bytes) -> Self::Call {
|
|
||||||
let call = (address.to_hex(), data.to_hex());
|
|
||||||
self.calls.lock().push(call.clone());
|
|
||||||
let res = self.responses.lock().get(&call).cloned().expect(&format!("No response for call: {:?}", call));
|
|
||||||
Box::new(::futures::future::done(res))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
// Copyright 2015-2018 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/>.
|
|
||||||
|
|
||||||
//! Dapps server test suite
|
|
||||||
|
|
||||||
mod helpers;
|
|
||||||
|
|
||||||
mod api;
|
|
||||||
mod fetch;
|
|
||||||
mod rpc;
|
|
||||||
mod validation;
|
|
@ -1,49 +0,0 @@
|
|||||||
// Copyright 2015-2018 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::{IoHandler, Value};
|
|
||||||
|
|
||||||
use tests::helpers::{serve_with_rpc, request};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_serve_rpc() {
|
|
||||||
// given
|
|
||||||
let mut io = IoHandler::default();
|
|
||||||
io.add_method("rpc_test", |_| {
|
|
||||||
Ok(Value::String("Hello World!".into()))
|
|
||||||
});
|
|
||||||
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\
|
|
||||||
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());
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
// Copyright 2015-2018 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_hosts, request};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_reject_invalid_host() {
|
|
||||||
// given
|
|
||||||
let server = serve_hosts(Some(vec!["localhost:8080".into()]));
|
|
||||||
|
|
||||||
// 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
|
|
||||||
response.assert_status("HTTP/1.1 403 Forbidden");
|
|
||||||
assert!(response.body.contains("Provided Host header is not whitelisted."), response.body);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_serve_dapps_domains() {
|
|
||||||
// given
|
|
||||||
let server = serve_hosts(Some(vec!["localhost:8080".into()]));
|
|
||||||
|
|
||||||
// when
|
|
||||||
let response = request(server,
|
|
||||||
"\
|
|
||||||
GET / HTTP/1.1\r\n\
|
|
||||||
Host: proxy.web3.site\r\n\
|
|
||||||
Connection: close\r\n\
|
|
||||||
\r\n\
|
|
||||||
{}
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
// then
|
|
||||||
response.assert_status("HTTP/1.1 200 OK");
|
|
||||||
}
|
|
161
dapps/src/web.rs
161
dapps/src/web.rs
@ -1,161 +0,0 @@
|
|||||||
// Copyright 2015-2018 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/>.
|
|
||||||
|
|
||||||
//! Serving web-based content (proxying)
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use base32;
|
|
||||||
use fetch::{self, Fetch};
|
|
||||||
use hyper::{mime, StatusCode};
|
|
||||||
|
|
||||||
use apps;
|
|
||||||
use endpoint::{Endpoint, EndpointPath, Request, Response};
|
|
||||||
use futures::future;
|
|
||||||
use futures_cpupool::CpuPool;
|
|
||||||
use handlers::{
|
|
||||||
ContentFetcherHandler, ContentHandler, ContentValidator, ValidatorResponse,
|
|
||||||
StreamingHandler,
|
|
||||||
};
|
|
||||||
use WebProxyTokens;
|
|
||||||
|
|
||||||
pub struct Web<F> {
|
|
||||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
|
||||||
fetch: F,
|
|
||||||
pool: CpuPool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F: Fetch> Web<F> {
|
|
||||||
pub fn boxed(
|
|
||||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
|
||||||
fetch: F,
|
|
||||||
pool: CpuPool,
|
|
||||||
) -> Box<Endpoint> {
|
|
||||||
Box::new(Web {
|
|
||||||
web_proxy_tokens,
|
|
||||||
fetch,
|
|
||||||
pool,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extract_target_url(&self, path: &EndpointPath) -> Result<String, ContentHandler> {
|
|
||||||
let token_and_url = path.app_params.get(0)
|
|
||||||
.map(|encoded| encoded.replace('.', ""))
|
|
||||||
.and_then(|encoded| base32::decode(base32::Alphabet::Crockford, &encoded.to_uppercase()))
|
|
||||||
.and_then(|data| String::from_utf8(data).ok())
|
|
||||||
.ok_or_else(|| ContentHandler::error(
|
|
||||||
StatusCode::BadRequest,
|
|
||||||
"Invalid parameter",
|
|
||||||
"Couldn't parse given parameter:",
|
|
||||||
path.app_params.get(0).map(String::as_str),
|
|
||||||
))?;
|
|
||||||
|
|
||||||
let mut token_it = token_and_url.split('+');
|
|
||||||
let token = token_it.next();
|
|
||||||
let target_url = token_it.next();
|
|
||||||
|
|
||||||
// Check if token supplied in URL is correct.
|
|
||||||
let domain = match token.and_then(|token| self.web_proxy_tokens.domain(token)) {
|
|
||||||
Some(domain) => domain,
|
|
||||||
_ => {
|
|
||||||
return Err(ContentHandler::error(
|
|
||||||
StatusCode::BadRequest, "Invalid Access Token", "Invalid or old web proxy access token supplied.", Some("Try refreshing the page."),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Validate protocol
|
|
||||||
let mut target_url = match target_url {
|
|
||||||
Some(url) if url.starts_with("http://") || url.starts_with("https://") => url.to_owned(),
|
|
||||||
_ => {
|
|
||||||
return Err(ContentHandler::error(
|
|
||||||
StatusCode::BadRequest, "Invalid Protocol", "Invalid protocol used.", None,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if !target_url.starts_with(&*domain) {
|
|
||||||
return Err(ContentHandler::error(
|
|
||||||
StatusCode::BadRequest, "Invalid Domain", "Dapp attempted to access invalid domain.", Some(&target_url),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if !target_url.ends_with("/") {
|
|
||||||
target_url = format!("{}/", target_url);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip the token
|
|
||||||
let query = path.query.as_ref().map_or_else(String::new, |query| format!("?{}", query));
|
|
||||||
let path = path.app_params[1..].join("/");
|
|
||||||
|
|
||||||
Ok(format!("{}{}{}", target_url, path, query))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F: Fetch> Endpoint for Web<F> {
|
|
||||||
fn respond(&self, path: EndpointPath, req: Request) -> Response {
|
|
||||||
// First extract the URL (reject invalid URLs)
|
|
||||||
let target_url = match self.extract_target_url(&path) {
|
|
||||||
Ok(url) => url,
|
|
||||||
Err(response) => {
|
|
||||||
return Box::new(future::ok(response.into()));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let token = path.app_params.get(0)
|
|
||||||
.expect("`target_url` is valid; app_params is not empty;qed")
|
|
||||||
.to_owned();
|
|
||||||
|
|
||||||
Box::new(ContentFetcherHandler::new(
|
|
||||||
req.method(),
|
|
||||||
&target_url,
|
|
||||||
path,
|
|
||||||
WebInstaller {
|
|
||||||
token,
|
|
||||||
},
|
|
||||||
self.fetch.clone(),
|
|
||||||
self.pool.clone(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct WebInstaller {
|
|
||||||
token: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ContentValidator for WebInstaller {
|
|
||||||
type Error = String;
|
|
||||||
|
|
||||||
fn validate_and_install(self, response: fetch::Response) -> Result<ValidatorResponse, String> {
|
|
||||||
let status = response.status();
|
|
||||||
let is_html = response.is_html();
|
|
||||||
let mime = response.content_type().unwrap_or(mime::TEXT_HTML);
|
|
||||||
let mut handler = StreamingHandler::new(
|
|
||||||
fetch::BodyReader::new(response),
|
|
||||||
status,
|
|
||||||
mime,
|
|
||||||
);
|
|
||||||
if is_html {
|
|
||||||
handler.set_initial_content(&format!(
|
|
||||||
r#"<script>history.replaceState({{}}, "", "/?{}{}/{}")</script>"#,
|
|
||||||
apps::URL_REFERER,
|
|
||||||
apps::WEB_PATH,
|
|
||||||
&self.token,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Ok(ValidatorResponse::Streaming(handler))
|
|
||||||
}
|
|
||||||
}
|
|
@ -15,4 +15,4 @@ serde = "1.0"
|
|||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
time = "0.1.35"
|
time = "0.1.35"
|
||||||
|
|
||||||
parity-reactor = { path = "../../util/reactor" }
|
parity-reactor = { path = "../util/reactor" }
|
@ -184,7 +184,7 @@ fn execute_import_light(cmd: ImportBlockchain) -> Result<(), String> {
|
|||||||
execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, &cmd.compaction)?;
|
execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, &cmd.compaction)?;
|
||||||
|
|
||||||
// create dirs used by parity
|
// create dirs used by parity
|
||||||
cmd.dirs.create_dirs(false, false, false)?;
|
cmd.dirs.create_dirs(false, false)?;
|
||||||
|
|
||||||
let cache = Arc::new(Mutex::new(
|
let cache = Arc::new(Mutex::new(
|
||||||
LightDataCache::new(Default::default(), Duration::new(0, 0))
|
LightDataCache::new(Default::default(), Duration::new(0, 0))
|
||||||
@ -337,7 +337,7 @@ fn execute_import(cmd: ImportBlockchain) -> Result<(), String> {
|
|||||||
execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, &cmd.compaction)?;
|
execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, &cmd.compaction)?;
|
||||||
|
|
||||||
// create dirs used by parity
|
// create dirs used by parity
|
||||||
cmd.dirs.create_dirs(false, false, false)?;
|
cmd.dirs.create_dirs(false, false)?;
|
||||||
|
|
||||||
// prepare client config
|
// prepare client config
|
||||||
let mut client_config = to_client_config(
|
let mut client_config = to_client_config(
|
||||||
@ -528,7 +528,7 @@ fn start_client(
|
|||||||
execute_upgrades(&dirs.base, &db_dirs, algorithm, &compaction)?;
|
execute_upgrades(&dirs.base, &db_dirs, algorithm, &compaction)?;
|
||||||
|
|
||||||
// create dirs used by parity
|
// create dirs used by parity
|
||||||
dirs.create_dirs(false, false, false)?;
|
dirs.create_dirs(false, false)?;
|
||||||
|
|
||||||
// prepare client config
|
// prepare client config
|
||||||
let client_config = to_client_config(
|
let client_config = to_client_config(
|
||||||
|
@ -26,15 +26,6 @@ usage! {
|
|||||||
// Arguments must start with arg_
|
// Arguments must start with arg_
|
||||||
// Flags must start with flag_
|
// Flags must start with flag_
|
||||||
|
|
||||||
CMD cmd_dapp
|
|
||||||
{
|
|
||||||
"Manage dapps",
|
|
||||||
|
|
||||||
ARG arg_dapp_path: (Option<String>) = None,
|
|
||||||
"<PATH>",
|
|
||||||
"Path to the dapps",
|
|
||||||
}
|
|
||||||
|
|
||||||
CMD cmd_daemon
|
CMD cmd_daemon
|
||||||
{
|
{
|
||||||
"Use Parity as a daemon",
|
"Use Parity as a daemon",
|
||||||
@ -232,6 +223,17 @@ usage! {
|
|||||||
{
|
{
|
||||||
"Print the hashed light clients headers of the given --chain (default: mainnet) in a JSON format. To be used as hardcoded headers in a genesis file.",
|
"Print the hashed light clients headers of the given --chain (default: mainnet) in a JSON format. To be used as hardcoded headers in a genesis file.",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CMD removed in 2.0
|
||||||
|
|
||||||
|
CMD cmd_dapp
|
||||||
|
{
|
||||||
|
"Manage dapps",
|
||||||
|
|
||||||
|
ARG arg_dapp_path: (Option<String>) = None,
|
||||||
|
"<PATH>",
|
||||||
|
"Path to the dapps",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
// Global flags and arguments
|
// Global flags and arguments
|
||||||
@ -539,15 +541,6 @@ usage! {
|
|||||||
"--ipc-apis=[APIS]",
|
"--ipc-apis=[APIS]",
|
||||||
"Specify custom API set available via JSON-RPC over IPC using a comma-delimited list of API names. Possible names are: all, safe, web3, net, eth, pubsub, personal, signer, parity, parity_pubsub, parity_accounts, parity_set, traces, rpc, secretstore, shh, shh_pubsub. You can also disable a specific API by putting '-' in the front, example: all,-personal. 'safe' enables the following APIs: web3, net, eth, pubsub, parity, parity_pubsub, traces, rpc, shh, shh_pubsub",
|
"Specify custom API set available via JSON-RPC over IPC using a comma-delimited list of API names. Possible names are: all, safe, web3, net, eth, pubsub, personal, signer, parity, parity_pubsub, parity_accounts, parity_set, traces, rpc, secretstore, shh, shh_pubsub. You can also disable a specific API by putting '-' in the front, example: all,-personal. 'safe' enables the following APIs: web3, net, eth, pubsub, parity, parity_pubsub, traces, rpc, shh, shh_pubsub",
|
||||||
|
|
||||||
["API and Console Options – Dapps"]
|
|
||||||
FLAG flag_no_dapps: (bool) = false, or |c: &Config| c.dapps.as_ref()?.disable.clone(),
|
|
||||||
"--no-dapps",
|
|
||||||
"Disable the Dapps server (e.g. status page).",
|
|
||||||
|
|
||||||
ARG arg_dapps_path: (String) = "$BASE/dapps", or |c: &Config| c.dapps.as_ref()?.path.clone(),
|
|
||||||
"--dapps-path=[PATH]",
|
|
||||||
"Specify directory where dapps should be installed.",
|
|
||||||
|
|
||||||
["API and Console Options – IPFS"]
|
["API and Console Options – IPFS"]
|
||||||
FLAG flag_ipfs_api: (bool) = false, or |c: &Config| c.ipfs.as_ref()?.enable.clone(),
|
FLAG flag_ipfs_api: (bool) = false, or |c: &Config| c.ipfs.as_ref()?.enable.clone(),
|
||||||
"--ipfs-api",
|
"--ipfs-api",
|
||||||
@ -969,6 +962,10 @@ usage! {
|
|||||||
"--fast-and-loose",
|
"--fast-and-loose",
|
||||||
"Does nothing; DB WAL is always activated.",
|
"Does nothing; DB WAL is always activated.",
|
||||||
|
|
||||||
|
FLAG flag_no_dapps: (bool) = false, or |c: &Config| c.dapps.as_ref()?._legacy_disable.clone(),
|
||||||
|
"--no-dapps",
|
||||||
|
"Disable the Dapps server (e.g. status page).",
|
||||||
|
|
||||||
// ARG Removed in 1.6 or before.
|
// ARG Removed in 1.6 or before.
|
||||||
|
|
||||||
ARG arg_etherbase: (Option<String>) = None, or |_| None,
|
ARG arg_etherbase: (Option<String>) = None, or |_| None,
|
||||||
@ -1029,27 +1026,27 @@ usage! {
|
|||||||
|
|
||||||
// ARG Removed in 1.7.
|
// ARG Removed in 1.7.
|
||||||
|
|
||||||
ARG arg_dapps_port: (Option<u16>) = None, or |c: &Config| c.dapps.as_ref()?.port.clone(),
|
ARG arg_dapps_port: (Option<u16>) = None, or |c: &Config| c.dapps.as_ref()?._legacy_port.clone(),
|
||||||
"--dapps-port=[PORT]",
|
"--dapps-port=[PORT]",
|
||||||
"Does nothing; dapps server has been removed.",
|
"Does nothing; dapps server has been removed.",
|
||||||
|
|
||||||
ARG arg_dapps_interface: (Option<String>) = None, or |c: &Config| c.dapps.as_ref()?.interface.clone(),
|
ARG arg_dapps_interface: (Option<String>) = None, or |c: &Config| c.dapps.as_ref()?._legacy_interface.clone(),
|
||||||
"--dapps-interface=[IP]",
|
"--dapps-interface=[IP]",
|
||||||
"Does nothing; dapps server has been removed.",
|
"Does nothing; dapps server has been removed.",
|
||||||
|
|
||||||
ARG arg_dapps_hosts: (Option<String>) = None, or |c: &Config| c.dapps.as_ref()?.hosts.as_ref().map(|vec| vec.join(",")),
|
ARG arg_dapps_hosts: (Option<String>) = None, or |c: &Config| c.dapps.as_ref()?._legacy_hosts.as_ref().map(|vec| vec.join(",")),
|
||||||
"--dapps-hosts=[HOSTS]",
|
"--dapps-hosts=[HOSTS]",
|
||||||
"Does nothing; dapps server has been removed.",
|
"Does nothing; dapps server has been removed.",
|
||||||
|
|
||||||
ARG arg_dapps_cors: (Option<String>) = None, or |c: &Config| c.dapps.as_ref()?.cors.clone(),
|
ARG arg_dapps_cors: (Option<String>) = None, or |c: &Config| c.dapps.as_ref()?._legacy_cors.clone(),
|
||||||
"--dapps-cors=[URL]",
|
"--dapps-cors=[URL]",
|
||||||
"Does nothing; dapps server has been removed.",
|
"Does nothing; dapps server has been removed.",
|
||||||
|
|
||||||
ARG arg_dapps_user: (Option<String>) = None, or |c: &Config| c.dapps.as_ref()?.user.clone(),
|
ARG arg_dapps_user: (Option<String>) = None, or |c: &Config| c.dapps.as_ref()?._legacy_user.clone(),
|
||||||
"--dapps-user=[USERNAME]",
|
"--dapps-user=[USERNAME]",
|
||||||
"Dapps server authentication has been removed.",
|
"Dapps server authentication has been removed.",
|
||||||
|
|
||||||
ARG arg_dapps_pass: (Option<String>) = None, or |c: &Config| c.dapps.as_ref()?.pass.clone(),
|
ARG arg_dapps_pass: (Option<String>) = None, or |c: &Config| c.dapps.as_ref()?._legacy_pass.clone(),
|
||||||
"--dapps-pass=[PASSWORD]",
|
"--dapps-pass=[PASSWORD]",
|
||||||
"Dapps server authentication has been removed.",
|
"Dapps server authentication has been removed.",
|
||||||
|
|
||||||
@ -1074,6 +1071,12 @@ usage! {
|
|||||||
ARG arg_tx_queue_ban_time: (Option<u16>) = None, or |c: &Config| c.mining.as_ref()?.tx_queue_ban_time.clone(),
|
ARG arg_tx_queue_ban_time: (Option<u16>) = None, or |c: &Config| c.mining.as_ref()?.tx_queue_ban_time.clone(),
|
||||||
"--tx-queue-ban-time=[SEC]",
|
"--tx-queue-ban-time=[SEC]",
|
||||||
"Not supported.",
|
"Not supported.",
|
||||||
|
|
||||||
|
// ARG removed in 2.0.
|
||||||
|
|
||||||
|
ARG arg_dapps_path: (Option<String>) = None, or |c: &Config| c.dapps.as_ref()?._legacy_path.clone(),
|
||||||
|
"--dapps-path=[PATH]",
|
||||||
|
"Specify directory where dapps should be installed.",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1223,14 +1226,22 @@ struct Ipc {
|
|||||||
#[derive(Default, Debug, PartialEq, Deserialize)]
|
#[derive(Default, Debug, PartialEq, Deserialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
struct Dapps {
|
struct Dapps {
|
||||||
disable: Option<bool>,
|
#[serde(rename="disable")]
|
||||||
port: Option<u16>,
|
_legacy_disable: Option<bool>,
|
||||||
interface: Option<String>,
|
#[serde(rename="port")]
|
||||||
hosts: Option<Vec<String>>,
|
_legacy_port: Option<u16>,
|
||||||
cors: Option<String>,
|
#[serde(rename="interface")]
|
||||||
path: Option<String>,
|
_legacy_interface: Option<String>,
|
||||||
user: Option<String>,
|
#[serde(rename="hosts")]
|
||||||
pass: Option<String>,
|
_legacy_hosts: Option<Vec<String>>,
|
||||||
|
#[serde(rename="cors")]
|
||||||
|
_legacy_cors: Option<String>,
|
||||||
|
#[serde(rename="path")]
|
||||||
|
_legacy_path: Option<String>,
|
||||||
|
#[serde(rename="user")]
|
||||||
|
_legacy_user: Option<String>,
|
||||||
|
#[serde(rename="pass")]
|
||||||
|
_legacy_pass: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, PartialEq, Deserialize)]
|
#[derive(Default, Debug, PartialEq, Deserialize)]
|
||||||
@ -1663,7 +1674,7 @@ mod tests {
|
|||||||
arg_ipc_apis: "web3,eth,net,parity,parity_accounts,personal,traces,rpc,secretstore".into(),
|
arg_ipc_apis: "web3,eth,net,parity,parity_accounts,personal,traces,rpc,secretstore".into(),
|
||||||
|
|
||||||
// DAPPS
|
// DAPPS
|
||||||
arg_dapps_path: "$HOME/.parity/dapps".into(),
|
arg_dapps_path: Some("$HOME/.parity/dapps".into()),
|
||||||
flag_no_dapps: false,
|
flag_no_dapps: false,
|
||||||
|
|
||||||
// SECRETSTORE
|
// SECRETSTORE
|
||||||
@ -1922,14 +1933,14 @@ mod tests {
|
|||||||
apis: Some(vec!["rpc".into(), "eth".into()]),
|
apis: Some(vec!["rpc".into(), "eth".into()]),
|
||||||
}),
|
}),
|
||||||
dapps: Some(Dapps {
|
dapps: Some(Dapps {
|
||||||
disable: None,
|
_legacy_disable: None,
|
||||||
port: Some(8080),
|
_legacy_port: Some(8080),
|
||||||
path: None,
|
_legacy_path: None,
|
||||||
interface: None,
|
_legacy_interface: None,
|
||||||
hosts: None,
|
_legacy_hosts: None,
|
||||||
cors: None,
|
_legacy_cors: None,
|
||||||
user: Some("username".into()),
|
_legacy_user: Some("username".into()),
|
||||||
pass: Some("password".into())
|
_legacy_pass: Some("password".into())
|
||||||
}),
|
}),
|
||||||
secretstore: Some(SecretStore {
|
secretstore: Some(SecretStore {
|
||||||
disable: None,
|
disable: None,
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::PathBuf;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use cli::{Args, ArgsError};
|
use cli::{Args, ArgsError};
|
||||||
@ -41,7 +41,6 @@ use dir::helpers::{replace_home, replace_home_and_local};
|
|||||||
use params::{ResealPolicy, AccountsConfig, GasPricerConfig, MinerExtras, SpecType};
|
use params::{ResealPolicy, AccountsConfig, GasPricerConfig, MinerExtras, SpecType};
|
||||||
use ethcore_logger::Config as LogConfig;
|
use ethcore_logger::Config as LogConfig;
|
||||||
use dir::{self, Directories, default_hypervisor_path, default_local_path, default_data_path};
|
use dir::{self, Directories, default_hypervisor_path, default_local_path, default_data_path};
|
||||||
use dapps::Configuration as DappsConfiguration;
|
|
||||||
use ipfs::Configuration as IpfsConfiguration;
|
use ipfs::Configuration as IpfsConfiguration;
|
||||||
use ethcore_private_tx::{ProviderConfig, EncryptorConfig};
|
use ethcore_private_tx::{ProviderConfig, EncryptorConfig};
|
||||||
use secretstore::{NodeSecretKey, Configuration as SecretStoreConfiguration, ContractAddress as SecretStoreContractAddress};
|
use secretstore::{NodeSecretKey, Configuration as SecretStoreConfiguration, ContractAddress as SecretStoreContractAddress};
|
||||||
@ -136,7 +135,6 @@ impl Configuration {
|
|||||||
let compaction = self.args.arg_db_compaction.parse()?;
|
let compaction = self.args.arg_db_compaction.parse()?;
|
||||||
let warp_sync = !self.args.flag_no_warp;
|
let warp_sync = !self.args.flag_no_warp;
|
||||||
let geth_compatibility = self.args.flag_geth;
|
let geth_compatibility = self.args.flag_geth;
|
||||||
let dapps_conf = self.dapps_config();
|
|
||||||
let ipfs_conf = self.ipfs_config();
|
let ipfs_conf = self.ipfs_config();
|
||||||
let secretstore_conf = self.secretstore_config()?;
|
let secretstore_conf = self.secretstore_config()?;
|
||||||
let format = self.format()?;
|
let format = self.format()?;
|
||||||
@ -370,13 +368,11 @@ impl Configuration {
|
|||||||
warp_barrier: self.args.arg_warp_barrier,
|
warp_barrier: self.args.arg_warp_barrier,
|
||||||
geth_compatibility: geth_compatibility,
|
geth_compatibility: geth_compatibility,
|
||||||
net_settings: self.network_settings()?,
|
net_settings: self.network_settings()?,
|
||||||
dapps_conf: dapps_conf,
|
|
||||||
ipfs_conf: ipfs_conf,
|
ipfs_conf: ipfs_conf,
|
||||||
secretstore_conf: secretstore_conf,
|
secretstore_conf: secretstore_conf,
|
||||||
private_provider_conf: private_provider_conf,
|
private_provider_conf: private_provider_conf,
|
||||||
private_encryptor_conf: private_enc_conf,
|
private_encryptor_conf: private_enc_conf,
|
||||||
private_tx_enabled,
|
private_tx_enabled,
|
||||||
dapp: self.dapp_to_open()?,
|
|
||||||
name: self.args.arg_identity,
|
name: self.args.arg_identity,
|
||||||
custom_bootnodes: self.args.arg_bootnodes.is_some(),
|
custom_bootnodes: self.args.arg_bootnodes.is_some(),
|
||||||
no_periodic_snapshot: self.args.flag_no_periodic_snapshot,
|
no_periodic_snapshot: self.args.flag_no_periodic_snapshot,
|
||||||
@ -582,18 +578,6 @@ impl Configuration {
|
|||||||
self.args.arg_ntp_servers.split(",").map(str::to_owned).collect()
|
self.args.arg_ntp_servers.split(",").map(str::to_owned).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dapps_config(&self) -> DappsConfiguration {
|
|
||||||
DappsConfiguration {
|
|
||||||
enabled: self.dapps_enabled(),
|
|
||||||
dapps_path: PathBuf::from(self.directories().dapps),
|
|
||||||
extra_dapps: if self.args.cmd_dapp {
|
|
||||||
self.args.arg_dapp_path.iter().map(|path| PathBuf::from(path)).collect()
|
|
||||||
} else {
|
|
||||||
vec![]
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn secretstore_config(&self) -> Result<SecretStoreConfiguration, String> {
|
fn secretstore_config(&self) -> Result<SecretStoreConfiguration, String> {
|
||||||
Ok(SecretStoreConfiguration {
|
Ok(SecretStoreConfiguration {
|
||||||
enabled: self.secretstore_enabled(),
|
enabled: self.secretstore_enabled(),
|
||||||
@ -627,19 +611,6 @@ impl Configuration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dapp_to_open(&self) -> Result<Option<String>, String> {
|
|
||||||
if !self.args.cmd_dapp {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
let path = self.args.arg_dapp_path.as_ref().map(String::as_str).unwrap_or(".");
|
|
||||||
let path = Path::new(path).canonicalize()
|
|
||||||
.map_err(|e| format!("Invalid path: {}. Error: {:?}", path, e))?;
|
|
||||||
let name = path.file_name()
|
|
||||||
.and_then(|name| name.to_str())
|
|
||||||
.ok_or_else(|| "Root path is not supported.".to_owned())?;
|
|
||||||
Ok(Some(name.into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gas_pricer_config(&self) -> Result<GasPricerConfig, String> {
|
fn gas_pricer_config(&self) -> Result<GasPricerConfig, String> {
|
||||||
fn wei_per_gas(usd_per_tx: f32, usd_per_eth: f32) -> U256 {
|
fn wei_per_gas(usd_per_tx: f32, usd_per_eth: f32) -> U256 {
|
||||||
let wei_per_usd: f32 = 1.0e18 / usd_per_eth;
|
let wei_per_usd: f32 = 1.0e18 / usd_per_eth;
|
||||||
@ -881,8 +852,6 @@ impl Configuration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn ws_config(&self) -> Result<WsConfiguration, String> {
|
fn ws_config(&self) -> Result<WsConfiguration, String> {
|
||||||
let http = self.http_config()?;
|
|
||||||
|
|
||||||
let support_token_api =
|
let support_token_api =
|
||||||
// enabled when not unlocking
|
// enabled when not unlocking
|
||||||
self.args.arg_unlock.is_none();
|
self.args.arg_unlock.is_none();
|
||||||
@ -896,7 +865,6 @@ impl Configuration {
|
|||||||
origins: self.ws_origins(),
|
origins: self.ws_origins(),
|
||||||
signer_path: self.directories().signer.into(),
|
signer_path: self.directories().signer.into(),
|
||||||
support_token_api,
|
support_token_api,
|
||||||
dapps_address: http.address(),
|
|
||||||
max_connections: self.args.arg_ws_max_connections,
|
max_connections: self.args.arg_ws_max_connections,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -980,7 +948,6 @@ impl Configuration {
|
|||||||
let db_path = replace_home_and_local(&data_path, &local_path, &base_db_path);
|
let db_path = replace_home_and_local(&data_path, &local_path, &base_db_path);
|
||||||
let cache_path = replace_home_and_local(&data_path, &local_path, cache_path);
|
let cache_path = replace_home_and_local(&data_path, &local_path, cache_path);
|
||||||
let keys_path = replace_home(&data_path, &self.args.arg_keys_path);
|
let keys_path = replace_home(&data_path, &self.args.arg_keys_path);
|
||||||
let dapps_path = replace_home(&data_path, &self.args.arg_dapps_path);
|
|
||||||
let secretstore_path = replace_home(&data_path, &self.args.arg_secretstore_path);
|
let secretstore_path = replace_home(&data_path, &self.args.arg_secretstore_path);
|
||||||
let ui_path = replace_home(&data_path, &self.args.arg_ui_path);
|
let ui_path = replace_home(&data_path, &self.args.arg_ui_path);
|
||||||
|
|
||||||
@ -989,7 +956,6 @@ impl Configuration {
|
|||||||
base: data_path,
|
base: data_path,
|
||||||
cache: cache_path,
|
cache: cache_path,
|
||||||
db: db_path,
|
db: db_path,
|
||||||
dapps: dapps_path,
|
|
||||||
signer: ui_path,
|
signer: ui_path,
|
||||||
secretstore: secretstore_path,
|
secretstore: secretstore_path,
|
||||||
}
|
}
|
||||||
@ -1094,10 +1060,6 @@ impl Configuration {
|
|||||||
!self.args.flag_no_ws
|
!self.args.flag_no_ws
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dapps_enabled(&self) -> bool {
|
|
||||||
!self.args.flag_dapps_off && !self.args.flag_no_dapps && self.rpc_enabled() && cfg!(feature = "dapps")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn secretstore_enabled(&self) -> bool {
|
fn secretstore_enabled(&self) -> bool {
|
||||||
!self.args.flag_no_secretstore && cfg!(feature = "secretstore")
|
!self.args.flag_no_secretstore && cfg!(feature = "secretstore")
|
||||||
}
|
}
|
||||||
@ -1364,7 +1326,6 @@ mod tests {
|
|||||||
origins: Some(vec!["parity://*".into(),"chrome-extension://*".into(), "moz-extension://*".into()]),
|
origins: Some(vec!["parity://*".into(),"chrome-extension://*".into(), "moz-extension://*".into()]),
|
||||||
hosts: Some(vec![]),
|
hosts: Some(vec![]),
|
||||||
signer_path: expected.into(),
|
signer_path: expected.into(),
|
||||||
dapps_address: Some("127.0.0.1:8545".into()),
|
|
||||||
support_token_api: true,
|
support_token_api: true,
|
||||||
max_connections: 100,
|
max_connections: 100,
|
||||||
}, LogConfig {
|
}, LogConfig {
|
||||||
@ -1433,13 +1394,11 @@ mod tests {
|
|||||||
vm_type: Default::default(),
|
vm_type: Default::default(),
|
||||||
geth_compatibility: false,
|
geth_compatibility: false,
|
||||||
net_settings: Default::default(),
|
net_settings: Default::default(),
|
||||||
dapps_conf: Default::default(),
|
|
||||||
ipfs_conf: Default::default(),
|
ipfs_conf: Default::default(),
|
||||||
secretstore_conf: Default::default(),
|
secretstore_conf: Default::default(),
|
||||||
private_provider_conf: Default::default(),
|
private_provider_conf: Default::default(),
|
||||||
private_encryptor_conf: Default::default(),
|
private_encryptor_conf: Default::default(),
|
||||||
private_tx_enabled: false,
|
private_tx_enabled: false,
|
||||||
dapp: None,
|
|
||||||
name: "".into(),
|
name: "".into(),
|
||||||
custom_bootnodes: false,
|
custom_bootnodes: false,
|
||||||
fat_db: Default::default(),
|
fat_db: Default::default(),
|
||||||
@ -1649,20 +1608,6 @@ mod tests {
|
|||||||
assert_eq!(conf4.directories().signer, "signer".to_owned());
|
assert_eq!(conf4.directories().signer, "signer".to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_parse_dapp_opening() {
|
|
||||||
// given
|
|
||||||
let tempdir = TempDir::new("").unwrap();
|
|
||||||
|
|
||||||
// when
|
|
||||||
let conf0 = parse(&["parity", "dapp", tempdir.path().to_str().unwrap()]);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(conf0.dapp_to_open(), Ok(Some(tempdir.path().file_name().unwrap().to_str().unwrap().into())));
|
|
||||||
let extra_dapps = conf0.dapps_config().extra_dapps;
|
|
||||||
assert_eq!(extra_dapps, vec![tempdir.path().to_owned()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_not_bail_on_empty_line_in_reserved_peers() {
|
fn should_not_bail_on_empty_line_in_reserved_peers() {
|
||||||
let tempdir = TempDir::new("").unwrap();
|
let tempdir = TempDir::new("").unwrap();
|
||||||
@ -1708,7 +1653,6 @@ mod tests {
|
|||||||
assert_eq!(c.net_conf.min_peers, 50);
|
assert_eq!(c.net_conf.min_peers, 50);
|
||||||
assert_eq!(c.net_conf.max_peers, 100);
|
assert_eq!(c.net_conf.max_peers, 100);
|
||||||
assert_eq!(c.ipc_conf.enabled, false);
|
assert_eq!(c.ipc_conf.enabled, false);
|
||||||
assert_eq!(c.dapps_conf.enabled, false);
|
|
||||||
assert_eq!(c.miner_options.force_sealing, true);
|
assert_eq!(c.miner_options.force_sealing, true);
|
||||||
assert_eq!(c.miner_options.reseal_on_external_tx, true);
|
assert_eq!(c.miner_options.reseal_on_external_tx, true);
|
||||||
assert_eq!(c.miner_options.reseal_on_own_tx, true);
|
assert_eq!(c.miner_options.reseal_on_own_tx, true);
|
||||||
|
272
parity/dapps.rs
272
parity/dapps.rs
@ -1,272 +0,0 @@
|
|||||||
// Copyright 2015-2018 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 std::path::PathBuf;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use bytes::Bytes;
|
|
||||||
use dir::default_data_path;
|
|
||||||
use dir::helpers::replace_home;
|
|
||||||
use ethcore::client::{Client, BlockChainClient, BlockId, CallContract};
|
|
||||||
use sync::LightSync;
|
|
||||||
use futures::{Future, future, IntoFuture};
|
|
||||||
use futures_cpupool::CpuPool;
|
|
||||||
use hash_fetch::fetch::Client as FetchClient;
|
|
||||||
use registrar::{RegistrarClient, Asynchronous};
|
|
||||||
use light::client::LightChainClient;
|
|
||||||
use light::on_demand::{self, OnDemand};
|
|
||||||
use node_health::{SyncStatus, NodeHealth};
|
|
||||||
use rpc;
|
|
||||||
use rpc_apis::SignerService;
|
|
||||||
use transaction::{Transaction, Action};
|
|
||||||
use ethereum_types::Address;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
|
||||||
pub struct Configuration {
|
|
||||||
pub enabled: bool,
|
|
||||||
pub dapps_path: PathBuf,
|
|
||||||
pub extra_dapps: Vec<PathBuf>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Configuration {
|
|
||||||
fn default() -> Self {
|
|
||||||
let data_dir = default_data_path();
|
|
||||||
Configuration {
|
|
||||||
enabled: true,
|
|
||||||
dapps_path: replace_home(&data_dir, "$BASE/dapps").into(),
|
|
||||||
extra_dapps: vec![],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Configuration {
|
|
||||||
pub fn address(&self, address: Option<::parity_rpc::Host>) -> Option<::parity_rpc::Host> {
|
|
||||||
match self.enabled {
|
|
||||||
true => address,
|
|
||||||
false => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Registrar implementation of the full client.
|
|
||||||
pub struct FullRegistrar {
|
|
||||||
/// Handle to the full client.
|
|
||||||
pub client: Arc<Client>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FullRegistrar {
|
|
||||||
pub fn new(client: Arc<Client>) -> Self {
|
|
||||||
FullRegistrar {
|
|
||||||
client,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RegistrarClient for FullRegistrar {
|
|
||||||
type Call = Asynchronous;
|
|
||||||
|
|
||||||
fn registrar_address(&self) -> Result<Address, String> {
|
|
||||||
self.client.registrar_address()
|
|
||||||
.ok_or_else(|| "Registrar not defined.".into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call_contract(&self, address: Address, data: Bytes) -> Self::Call {
|
|
||||||
Box::new(self.client.call_contract(BlockId::Latest, address, data).into_future())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Registrar implementation for the light client.
|
|
||||||
pub struct LightRegistrar<T> {
|
|
||||||
/// The light client.
|
|
||||||
pub client: Arc<T>,
|
|
||||||
/// Handle to the on-demand service.
|
|
||||||
pub on_demand: Arc<OnDemand>,
|
|
||||||
/// Handle to the light network service.
|
|
||||||
pub sync: Arc<LightSync>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: LightChainClient + 'static> RegistrarClient for LightRegistrar<T> {
|
|
||||||
type Call = Box<Future<Item = Bytes, Error = String> + Send>;
|
|
||||||
|
|
||||||
fn registrar_address(&self) -> Result<Address, String> {
|
|
||||||
self.client.engine().additional_params().get("registrar")
|
|
||||||
.ok_or_else(|| "Registrar not defined.".into())
|
|
||||||
.and_then(|registrar| {
|
|
||||||
registrar.parse().map_err(|e| format!("Invalid registrar address: {:?}", e))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call_contract(&self, address: Address, data: Bytes) -> Self::Call {
|
|
||||||
let header = self.client.best_block_header();
|
|
||||||
let env_info = self.client.env_info(BlockId::Hash(header.hash()))
|
|
||||||
.ok_or_else(|| format!("Cannot fetch env info for header {}", header.hash()));
|
|
||||||
|
|
||||||
let env_info = match env_info {
|
|
||||||
Ok(e) => e,
|
|
||||||
Err(e) => return Box::new(future::err(e)),
|
|
||||||
};
|
|
||||||
|
|
||||||
let maybe_future = self.sync.with_context(move |ctx| {
|
|
||||||
self.on_demand
|
|
||||||
.request(ctx, on_demand::request::TransactionProof {
|
|
||||||
tx: Transaction {
|
|
||||||
nonce: self.client.engine().account_start_nonce(header.number()),
|
|
||||||
action: Action::Call(address),
|
|
||||||
gas: 50_000.into(), // should be enough for all registry lookups. TODO: exponential backoff
|
|
||||||
gas_price: 0.into(),
|
|
||||||
value: 0.into(),
|
|
||||||
data: data,
|
|
||||||
}.fake_sign(Address::default()),
|
|
||||||
header: header.into(),
|
|
||||||
env_info: env_info,
|
|
||||||
engine: self.client.engine().clone(),
|
|
||||||
})
|
|
||||||
.expect("No back-references; therefore all back-refs valid; qed")
|
|
||||||
.then(|res| match res {
|
|
||||||
Ok(Ok(executed)) => Ok(executed.output),
|
|
||||||
Ok(Err(e)) => Err(format!("Failed to execute transaction: {}", e)),
|
|
||||||
Err(_) => Err(format!("On-demand service dropped request unexpectedly.")),
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
match maybe_future {
|
|
||||||
Some(fut) => Box::new(fut),
|
|
||||||
None => Box::new(future::err("cannot query registry: network disabled".into())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: light client implementation forwarding to OnDemand and waiting for future
|
|
||||||
// to resolve.
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Dependencies {
|
|
||||||
pub node_health: NodeHealth,
|
|
||||||
pub sync_status: Arc<SyncStatus>,
|
|
||||||
pub contract_client: Arc<RegistrarClient<Call=Asynchronous>>,
|
|
||||||
pub fetch: FetchClient,
|
|
||||||
pub pool: CpuPool,
|
|
||||||
pub signer: Arc<SignerService>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new(configuration: Configuration, deps: Dependencies) -> Result<Option<Middleware>, String> {
|
|
||||||
if !configuration.enabled {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
server::dapps_middleware(
|
|
||||||
deps,
|
|
||||||
configuration.dapps_path,
|
|
||||||
configuration.extra_dapps,
|
|
||||||
rpc::DAPPS_DOMAIN,
|
|
||||||
).map(Some)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub use self::server::{Middleware, service};
|
|
||||||
|
|
||||||
#[cfg(not(feature = "dapps"))]
|
|
||||||
mod server {
|
|
||||||
use super::Dependencies;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use parity_rpc::{hyper, RequestMiddleware, RequestMiddlewareAction};
|
|
||||||
use rpc_apis;
|
|
||||||
|
|
||||||
pub struct Middleware;
|
|
||||||
impl RequestMiddleware for Middleware {
|
|
||||||
fn on_request(&self, _req: hyper::Request) -> RequestMiddlewareAction {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dapps_middleware(
|
|
||||||
_deps: Dependencies,
|
|
||||||
_dapps_path: PathBuf,
|
|
||||||
_extra_dapps: Vec<PathBuf>,
|
|
||||||
_dapps_domain: &str,
|
|
||||||
) -> Result<Middleware, String> {
|
|
||||||
Err("Your Parity version has been compiled without WebApps support.".into())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn service(_: &Option<Middleware>) -> Option<Arc<rpc_apis::DappsService>> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "dapps")]
|
|
||||||
mod server {
|
|
||||||
use super::Dependencies;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use rpc_apis;
|
|
||||||
|
|
||||||
use parity_dapps;
|
|
||||||
|
|
||||||
pub use parity_dapps::Middleware;
|
|
||||||
|
|
||||||
pub fn dapps_middleware(
|
|
||||||
deps: Dependencies,
|
|
||||||
dapps_path: PathBuf,
|
|
||||||
extra_dapps: Vec<PathBuf>,
|
|
||||||
dapps_domain: &str,
|
|
||||||
) -> Result<Middleware, String> {
|
|
||||||
let signer = deps.signer;
|
|
||||||
let web_proxy_tokens = Arc::new(move |token| signer.web_proxy_access_token_domain(&token));
|
|
||||||
|
|
||||||
Ok(parity_dapps::Middleware::dapps(
|
|
||||||
deps.pool,
|
|
||||||
deps.node_health,
|
|
||||||
dapps_path,
|
|
||||||
extra_dapps,
|
|
||||||
dapps_domain,
|
|
||||||
deps.contract_client,
|
|
||||||
deps.sync_status,
|
|
||||||
web_proxy_tokens,
|
|
||||||
deps.fetch,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn service(middleware: &Option<Middleware>) -> Option<Arc<rpc_apis::DappsService>> {
|
|
||||||
middleware.as_ref().map(|m| Arc::new(DappsServiceWrapper {
|
|
||||||
endpoints: m.endpoints().clone(),
|
|
||||||
}) as Arc<rpc_apis::DappsService>)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DappsServiceWrapper {
|
|
||||||
endpoints: parity_dapps::Endpoints,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl rpc_apis::DappsService for DappsServiceWrapper {
|
|
||||||
fn list_dapps(&self) -> Vec<rpc_apis::LocalDapp> {
|
|
||||||
self.endpoints.list()
|
|
||||||
.into_iter()
|
|
||||||
.map(|app| rpc_apis::LocalDapp {
|
|
||||||
id: app.id.unwrap_or_else(|| "unknown".into()),
|
|
||||||
name: app.name,
|
|
||||||
description: app.description,
|
|
||||||
version: app.version,
|
|
||||||
author: app.author,
|
|
||||||
icon_url: app.icon_url,
|
|
||||||
local_url: app.local_url,
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn refresh_local_dapps(&self) -> bool {
|
|
||||||
self.endpoints.refresh_local_dapps();
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -209,6 +209,22 @@ pub fn find_deprecated(args: &Args) -> Vec<Deprecated> {
|
|||||||
result.push(Deprecated::Removed("--fast-and-loose"));
|
result.push(Deprecated::Removed("--fast-and-loose"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if args.cmd_dapp {
|
||||||
|
result.push(Deprecated::Removed("parity dapp"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.arg_dapp_path.is_some() {
|
||||||
|
result.push(Deprecated::Removed("--dapp-path"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.flag_no_dapps {
|
||||||
|
result.push(Deprecated::Removed("--no-dapps"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.arg_dapps_path.is_some() {
|
||||||
|
result.push(Deprecated::Removed("--dapps-path"));
|
||||||
|
}
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ pub fn execute(cmd: ExportHsyncCmd) -> Result<String, String> {
|
|||||||
execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, &cmd.compaction)?;
|
execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, &cmd.compaction)?;
|
||||||
|
|
||||||
// create dirs used by parity
|
// create dirs used by parity
|
||||||
cmd.dirs.create_dirs(false, false, false)?;
|
cmd.dirs.create_dirs(false, false)?;
|
||||||
|
|
||||||
// TODO: configurable cache size.
|
// TODO: configurable cache size.
|
||||||
let cache = LightDataCache::new(Default::default(), Duration::from_secs(60 * GAS_CORPUS_EXPIRATION_MINUTES));
|
let cache = LightDataCache::new(Default::default(), Duration::from_secs(60 * GAS_CORPUS_EXPIRATION_MINUTES));
|
||||||
|
@ -87,8 +87,6 @@ extern crate parity_dapps;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate pretty_assertions;
|
extern crate pretty_assertions;
|
||||||
|
|
||||||
#[cfg(windows)] extern crate winapi;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
extern crate tempdir;
|
extern crate tempdir;
|
||||||
|
|
||||||
@ -97,7 +95,6 @@ mod blockchain;
|
|||||||
mod cache;
|
mod cache;
|
||||||
mod cli;
|
mod cli;
|
||||||
mod configuration;
|
mod configuration;
|
||||||
mod dapps;
|
|
||||||
mod export_hardcoded_sync;
|
mod export_hardcoded_sync;
|
||||||
mod ipfs;
|
mod ipfs;
|
||||||
mod deprecated;
|
mod deprecated;
|
||||||
@ -114,7 +111,6 @@ mod secretstore;
|
|||||||
mod signer;
|
mod signer;
|
||||||
mod snapshot;
|
mod snapshot;
|
||||||
mod upgrade;
|
mod upgrade;
|
||||||
mod url;
|
|
||||||
mod user_defaults;
|
mod user_defaults;
|
||||||
mod whisper;
|
mod whisper;
|
||||||
mod db;
|
mod db;
|
||||||
@ -201,10 +197,6 @@ fn execute<Cr, Rr>(command: Execute, on_client_rq: Cr, on_updater_rq: Rr) -> Res
|
|||||||
|
|
||||||
match command.cmd {
|
match command.cmd {
|
||||||
Cmd::Run(run_cmd) => {
|
Cmd::Run(run_cmd) => {
|
||||||
if let Some(ref dapp) = run_cmd.dapp {
|
|
||||||
open_dapp(&run_cmd.dapps_conf, &run_cmd.http_conf, dapp)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let outcome = run::execute(run_cmd, logger, on_client_rq, on_updater_rq)?;
|
let outcome = run::execute(run_cmd, logger, on_client_rq, on_updater_rq)?;
|
||||||
Ok(ExecutionAction::Running(outcome))
|
Ok(ExecutionAction::Running(outcome))
|
||||||
},
|
},
|
||||||
@ -244,13 +236,3 @@ pub fn start<Cr, Rr>(conf: Configuration, on_client_rq: Cr, on_updater_rq: Rr) -
|
|||||||
|
|
||||||
execute(conf.into_command()?, on_client_rq, on_updater_rq)
|
execute(conf.into_command()?, on_client_rq, on_updater_rq)
|
||||||
}
|
}
|
||||||
|
|
||||||
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://{}:{}/{}/", rpc_conf.interface, rpc_conf.port, dapp);
|
|
||||||
url::open(&url).map_err(|e| format!("{}", e))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
@ -19,7 +19,6 @@ use std::sync::Arc;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use dapps;
|
|
||||||
use dir::default_data_path;
|
use dir::default_data_path;
|
||||||
use dir::helpers::replace_home;
|
use dir::helpers::replace_home;
|
||||||
use helpers::parity_ipc_path;
|
use helpers::parity_ipc_path;
|
||||||
@ -48,12 +47,6 @@ pub struct HttpConfiguration {
|
|||||||
pub max_payload: usize,
|
pub max_payload: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HttpConfiguration {
|
|
||||||
pub fn address(&self) -> Option<rpc::Host> {
|
|
||||||
address(self.enabled, &self.interface, self.port, &self.hosts)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for HttpConfiguration {
|
impl Default for HttpConfiguration {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
HttpConfiguration {
|
HttpConfiguration {
|
||||||
@ -103,7 +96,6 @@ pub struct WsConfiguration {
|
|||||||
pub hosts: Option<Vec<String>>,
|
pub hosts: Option<Vec<String>>,
|
||||||
pub signer_path: PathBuf,
|
pub signer_path: PathBuf,
|
||||||
pub support_token_api: bool,
|
pub support_token_api: bool,
|
||||||
pub dapps_address: Option<rpc::Host>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for WsConfiguration {
|
impl Default for WsConfiguration {
|
||||||
@ -119,7 +111,6 @@ impl Default for WsConfiguration {
|
|||||||
hosts: Some(Vec::new()),
|
hosts: Some(Vec::new()),
|
||||||
signer_path: replace_home(&data_dir, "$BASE/signer").into(),
|
signer_path: replace_home(&data_dir, "$BASE/signer").into(),
|
||||||
support_token_api: true,
|
support_token_api: true,
|
||||||
dapps_address: Some("127.0.0.1:8545".into()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -173,7 +164,7 @@ pub fn new_ws<D: rpc_apis::Dependencies>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let remote = deps.remote.clone();
|
let remote = deps.remote.clone();
|
||||||
let allowed_origins = into_domains(with_domain(conf.origins, domain, &conf.dapps_address));
|
let allowed_origins = into_domains(with_domain(conf.origins, domain, &None));
|
||||||
let allowed_hosts = into_domains(with_domain(conf.hosts, domain, &Some(url.clone().into())));
|
let allowed_hosts = into_domains(with_domain(conf.hosts, domain, &Some(url.clone().into())));
|
||||||
|
|
||||||
let signer_path;
|
let signer_path;
|
||||||
@ -210,7 +201,6 @@ pub fn new_http<D: rpc_apis::Dependencies>(
|
|||||||
options: &str,
|
options: &str,
|
||||||
conf: HttpConfiguration,
|
conf: HttpConfiguration,
|
||||||
deps: &Dependencies<D>,
|
deps: &Dependencies<D>,
|
||||||
middleware: Option<dapps::Middleware>,
|
|
||||||
) -> Result<Option<HttpServer>, String> {
|
) -> Result<Option<HttpServer>, String> {
|
||||||
if !conf.enabled {
|
if !conf.enabled {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
@ -232,7 +222,6 @@ pub fn new_http<D: rpc_apis::Dependencies>(
|
|||||||
handler,
|
handler,
|
||||||
remote,
|
remote,
|
||||||
rpc::RpcExtractor,
|
rpc::RpcExtractor,
|
||||||
middleware,
|
|
||||||
conf.server_threads,
|
conf.server_threads,
|
||||||
conf.max_payload,
|
conf.max_payload,
|
||||||
);
|
);
|
||||||
|
@ -20,7 +20,7 @@ use std::str::FromStr;
|
|||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
pub use parity_rpc::signer::SignerService;
|
pub use parity_rpc::signer::SignerService;
|
||||||
pub use parity_rpc::dapps::{DappsService, LocalDapp};
|
pub use parity_rpc::dapps::LocalDapp;
|
||||||
|
|
||||||
use ethcore_service::PrivateTxService;
|
use ethcore_service::PrivateTxService;
|
||||||
use ethcore::account_provider::AccountProvider;
|
use ethcore::account_provider::AccountProvider;
|
||||||
@ -227,8 +227,6 @@ pub struct FullDependencies {
|
|||||||
pub updater: Arc<Updater>,
|
pub updater: Arc<Updater>,
|
||||||
pub health: NodeHealth,
|
pub health: NodeHealth,
|
||||||
pub geth_compatibility: bool,
|
pub geth_compatibility: bool,
|
||||||
pub dapps_service: Option<Arc<DappsService>>,
|
|
||||||
pub dapps_address: Option<Host>,
|
|
||||||
pub ws_address: Option<Host>,
|
pub ws_address: Option<Host>,
|
||||||
pub fetch: FetchClient,
|
pub fetch: FetchClient,
|
||||||
pub pool: CpuPool,
|
pub pool: CpuPool,
|
||||||
@ -337,7 +335,6 @@ impl FullDependencies {
|
|||||||
self.logger.clone(),
|
self.logger.clone(),
|
||||||
self.settings.clone(),
|
self.settings.clone(),
|
||||||
signer,
|
signer,
|
||||||
self.dapps_address.clone(),
|
|
||||||
self.ws_address.clone(),
|
self.ws_address.clone(),
|
||||||
).to_delegate());
|
).to_delegate());
|
||||||
|
|
||||||
@ -362,7 +359,6 @@ impl FullDependencies {
|
|||||||
&self.miner,
|
&self.miner,
|
||||||
&self.updater,
|
&self.updater,
|
||||||
&self.net_service,
|
&self.net_service,
|
||||||
self.dapps_service.clone(),
|
|
||||||
self.fetch.clone(),
|
self.fetch.clone(),
|
||||||
self.pool.clone(),
|
self.pool.clone(),
|
||||||
).to_delegate())
|
).to_delegate())
|
||||||
@ -439,8 +435,6 @@ pub struct LightDependencies<T> {
|
|||||||
pub on_demand: Arc<::light::on_demand::OnDemand>,
|
pub on_demand: Arc<::light::on_demand::OnDemand>,
|
||||||
pub cache: Arc<Mutex<LightDataCache>>,
|
pub cache: Arc<Mutex<LightDataCache>>,
|
||||||
pub transaction_queue: Arc<RwLock<LightTransactionQueue>>,
|
pub transaction_queue: Arc<RwLock<LightTransactionQueue>>,
|
||||||
pub dapps_service: Option<Arc<DappsService>>,
|
|
||||||
pub dapps_address: Option<Host>,
|
|
||||||
pub ws_address: Option<Host>,
|
pub ws_address: Option<Host>,
|
||||||
pub fetch: FetchClient,
|
pub fetch: FetchClient,
|
||||||
pub pool: CpuPool,
|
pub pool: CpuPool,
|
||||||
@ -553,7 +547,6 @@ impl<C: LightChainClient + 'static> LightDependencies<C> {
|
|||||||
self.settings.clone(),
|
self.settings.clone(),
|
||||||
self.health.clone(),
|
self.health.clone(),
|
||||||
signer,
|
signer,
|
||||||
self.dapps_address.clone(),
|
|
||||||
self.ws_address.clone(),
|
self.ws_address.clone(),
|
||||||
self.gas_price_percentile,
|
self.gas_price_percentile,
|
||||||
).to_delegate());
|
).to_delegate());
|
||||||
@ -576,7 +569,6 @@ impl<C: LightChainClient + 'static> LightDependencies<C> {
|
|||||||
Api::ParitySet => {
|
Api::ParitySet => {
|
||||||
handler.extend_with(light::ParitySetClient::new(
|
handler.extend_with(light::ParitySetClient::new(
|
||||||
self.sync.clone(),
|
self.sync.clone(),
|
||||||
self.dapps_service.clone(),
|
|
||||||
self.fetch.clone(),
|
self.fetch.clone(),
|
||||||
self.pool.clone(),
|
self.pool.clone(),
|
||||||
).to_delegate())
|
).to_delegate())
|
||||||
|
@ -21,8 +21,9 @@ use std::time::{Duration, Instant};
|
|||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
use ansi_term::Colour;
|
use ansi_term::Colour;
|
||||||
|
use bytes::Bytes;
|
||||||
use ethcore::account_provider::{AccountProvider, AccountProviderSettings};
|
use ethcore::account_provider::{AccountProvider, AccountProviderSettings};
|
||||||
use ethcore::client::{Client, Mode, DatabaseCompactionProfile, VMType, BlockChainClient, BlockInfo};
|
use ethcore::client::{BlockId, CallContract, Client, Mode, DatabaseCompactionProfile, VMType, BlockChainClient, BlockInfo};
|
||||||
use ethcore::ethstore::ethkey;
|
use ethcore::ethstore::ethkey;
|
||||||
use ethcore::miner::{stratum, Miner, MinerService, MinerOptions};
|
use ethcore::miner::{stratum, Miner, MinerService, MinerOptions};
|
||||||
use ethcore::snapshot;
|
use ethcore::snapshot;
|
||||||
@ -30,9 +31,11 @@ use ethcore::spec::{SpecParams, OptimizeFor};
|
|||||||
use ethcore::verification::queue::VerifierSettings;
|
use ethcore::verification::queue::VerifierSettings;
|
||||||
use ethcore_logger::{Config as LogConfig, RotatingLogger};
|
use ethcore_logger::{Config as LogConfig, RotatingLogger};
|
||||||
use ethcore_service::ClientService;
|
use ethcore_service::ClientService;
|
||||||
|
use ethereum_types::Address;
|
||||||
use sync::{self, SyncConfig};
|
use sync::{self, SyncConfig};
|
||||||
#[cfg(feature = "work-notify")]
|
#[cfg(feature = "work-notify")]
|
||||||
use miner::work_notify::WorkPoster;
|
use miner::work_notify::WorkPoster;
|
||||||
|
use futures::IntoFuture;
|
||||||
use futures_cpupool::CpuPool;
|
use futures_cpupool::CpuPool;
|
||||||
use hash_fetch::{self, fetch};
|
use hash_fetch::{self, fetch};
|
||||||
use informant::{Informant, LightNodeInformantData, FullNodeInformantData};
|
use informant::{Informant, LightNodeInformantData, FullNodeInformantData};
|
||||||
@ -55,10 +58,10 @@ use upgrade::upgrade_key_location;
|
|||||||
use dir::{Directories, DatabaseDirectories};
|
use dir::{Directories, DatabaseDirectories};
|
||||||
use cache::CacheConfig;
|
use cache::CacheConfig;
|
||||||
use user_defaults::UserDefaults;
|
use user_defaults::UserDefaults;
|
||||||
use dapps;
|
|
||||||
use ipfs;
|
use ipfs;
|
||||||
use jsonrpc_core;
|
use jsonrpc_core;
|
||||||
use modules;
|
use modules;
|
||||||
|
use registrar::{RegistrarClient, Asynchronous};
|
||||||
use rpc;
|
use rpc;
|
||||||
use rpc_apis;
|
use rpc_apis;
|
||||||
use secretstore;
|
use secretstore;
|
||||||
@ -112,13 +115,11 @@ pub struct RunCmd {
|
|||||||
pub vm_type: VMType,
|
pub vm_type: VMType,
|
||||||
pub geth_compatibility: bool,
|
pub geth_compatibility: bool,
|
||||||
pub net_settings: NetworkSettings,
|
pub net_settings: NetworkSettings,
|
||||||
pub dapps_conf: dapps::Configuration,
|
|
||||||
pub ipfs_conf: ipfs::Configuration,
|
pub ipfs_conf: ipfs::Configuration,
|
||||||
pub secretstore_conf: secretstore::Configuration,
|
pub secretstore_conf: secretstore::Configuration,
|
||||||
pub private_provider_conf: ProviderConfig,
|
pub private_provider_conf: ProviderConfig,
|
||||||
pub private_encryptor_conf: EncryptorConfig,
|
pub private_encryptor_conf: EncryptorConfig,
|
||||||
pub private_tx_enabled: bool,
|
pub private_tx_enabled: bool,
|
||||||
pub dapp: Option<String>,
|
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub custom_bootnodes: bool,
|
pub custom_bootnodes: bool,
|
||||||
pub stratum: Option<stratum::Options>,
|
pub stratum: Option<stratum::Options>,
|
||||||
@ -185,10 +186,10 @@ fn execute_light_impl(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<Runnin
|
|||||||
execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, &cmd.compaction)?;
|
execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, &cmd.compaction)?;
|
||||||
|
|
||||||
// create dirs used by parity
|
// create dirs used by parity
|
||||||
cmd.dirs.create_dirs(cmd.dapps_conf.enabled, cmd.acc_conf.unlocked_accounts.len() == 0, cmd.secretstore_conf.enabled)?;
|
cmd.dirs.create_dirs(cmd.acc_conf.unlocked_accounts.len() == 0, cmd.secretstore_conf.enabled)?;
|
||||||
|
|
||||||
//print out running parity environment
|
//print out running parity environment
|
||||||
print_running_environment(&spec.name, &cmd.dirs, &db_dirs, &cmd.dapps_conf);
|
print_running_environment(&spec.name, &cmd.dirs, &db_dirs);
|
||||||
|
|
||||||
info!("Running in experimental {} mode.", Colour::Blue.bold().paint("Light Client"));
|
info!("Running in experimental {} mode.", Colour::Blue.bold().paint("Light Client"));
|
||||||
|
|
||||||
@ -287,13 +288,7 @@ fn execute_light_impl(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<Runnin
|
|||||||
|
|
||||||
// the dapps server
|
// the dapps server
|
||||||
let signer_service = Arc::new(signer::new_service(&cmd.ws_conf, &cmd.logger_config));
|
let signer_service = Arc::new(signer::new_service(&cmd.ws_conf, &cmd.logger_config));
|
||||||
let (node_health, dapps_deps) = {
|
let node_health = {
|
||||||
let contract_client = ::dapps::LightRegistrar {
|
|
||||||
client: client.clone(),
|
|
||||||
sync: light_sync.clone(),
|
|
||||||
on_demand: on_demand.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
struct LightSyncStatus(Arc<LightSync>);
|
struct LightSyncStatus(Arc<LightSync>);
|
||||||
impl fmt::Debug for LightSyncStatus {
|
impl fmt::Debug for LightSyncStatus {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
@ -309,26 +304,14 @@ fn execute_light_impl(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<Runnin
|
|||||||
}
|
}
|
||||||
|
|
||||||
let sync_status = Arc::new(LightSyncStatus(light_sync.clone()));
|
let sync_status = Arc::new(LightSyncStatus(light_sync.clone()));
|
||||||
let node_health = node_health::NodeHealth::new(
|
node_health::NodeHealth::new(
|
||||||
sync_status.clone(),
|
sync_status.clone(),
|
||||||
node_health::TimeChecker::new(&cmd.ntp_servers, cpu_pool.clone()),
|
node_health::TimeChecker::new(&cmd.ntp_servers, cpu_pool.clone()),
|
||||||
event_loop.remote(),
|
event_loop.remote(),
|
||||||
);
|
)
|
||||||
|
|
||||||
(node_health.clone(), dapps::Dependencies {
|
|
||||||
sync_status,
|
|
||||||
node_health,
|
|
||||||
contract_client: Arc::new(contract_client),
|
|
||||||
fetch: fetch.clone(),
|
|
||||||
pool: cpu_pool.clone(),
|
|
||||||
signer: signer_service.clone(),
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let dapps_middleware = dapps::new(cmd.dapps_conf.clone(), dapps_deps.clone())?;
|
|
||||||
|
|
||||||
// start RPCs
|
// start RPCs
|
||||||
let dapps_service = dapps::service(&dapps_middleware);
|
|
||||||
let deps_for_rpc_apis = Arc::new(rpc_apis::LightDependencies {
|
let deps_for_rpc_apis = Arc::new(rpc_apis::LightDependencies {
|
||||||
signer_service: signer_service,
|
signer_service: signer_service,
|
||||||
client: client.clone(),
|
client: client.clone(),
|
||||||
@ -341,8 +324,6 @@ fn execute_light_impl(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<Runnin
|
|||||||
on_demand: on_demand,
|
on_demand: on_demand,
|
||||||
cache: cache.clone(),
|
cache: cache.clone(),
|
||||||
transaction_queue: txq,
|
transaction_queue: txq,
|
||||||
dapps_service: dapps_service,
|
|
||||||
dapps_address: cmd.dapps_conf.address(cmd.http_conf.address()),
|
|
||||||
ws_address: cmd.ws_conf.address(),
|
ws_address: cmd.ws_conf.address(),
|
||||||
fetch: fetch,
|
fetch: fetch,
|
||||||
pool: cpu_pool.clone(),
|
pool: cpu_pool.clone(),
|
||||||
@ -368,7 +349,7 @@ fn execute_light_impl(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<Runnin
|
|||||||
// start rpc servers
|
// start rpc servers
|
||||||
let rpc_direct = rpc::setup_apis(rpc_apis::ApiSet::All, &dependencies);
|
let rpc_direct = rpc::setup_apis(rpc_apis::ApiSet::All, &dependencies);
|
||||||
let ws_server = rpc::new_ws(cmd.ws_conf, &dependencies)?;
|
let ws_server = rpc::new_ws(cmd.ws_conf, &dependencies)?;
|
||||||
let http_server = rpc::new_http("HTTP JSON-RPC", "jsonrpc", cmd.http_conf.clone(), &dependencies, dapps_middleware)?;
|
let http_server = rpc::new_http("HTTP JSON-RPC", "jsonrpc", cmd.http_conf.clone(), &dependencies)?;
|
||||||
let ipc_server = rpc::new_ipc(cmd.ipc_conf, &dependencies)?;
|
let ipc_server = rpc::new_ipc(cmd.ipc_conf, &dependencies)?;
|
||||||
|
|
||||||
// the informant
|
// the informant
|
||||||
@ -440,7 +421,7 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
|
|||||||
execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, &cmd.compaction)?;
|
execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, &cmd.compaction)?;
|
||||||
|
|
||||||
// create dirs used by parity
|
// create dirs used by parity
|
||||||
cmd.dirs.create_dirs(cmd.dapps_conf.enabled, cmd.acc_conf.unlocked_accounts.len() == 0, cmd.secretstore_conf.enabled)?;
|
cmd.dirs.create_dirs(cmd.acc_conf.unlocked_accounts.len() == 0, cmd.secretstore_conf.enabled)?;
|
||||||
|
|
||||||
// run in daemon mode
|
// run in daemon mode
|
||||||
if let Some(pid_file) = cmd.daemon {
|
if let Some(pid_file) = cmd.daemon {
|
||||||
@ -448,7 +429,7 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
|
|||||||
}
|
}
|
||||||
|
|
||||||
//print out running parity environment
|
//print out running parity environment
|
||||||
print_running_environment(&spec.name, &cmd.dirs, &db_dirs, &cmd.dapps_conf);
|
print_running_environment(&spec.name, &cmd.dirs, &db_dirs);
|
||||||
|
|
||||||
// display info about used pruning algorithm
|
// display info about used pruning algorithm
|
||||||
info!("State DB configuration: {}{}{}",
|
info!("State DB configuration: {}{}{}",
|
||||||
@ -709,7 +690,21 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
|
|||||||
chain_notify.start();
|
chain_notify.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
let contract_client = Arc::new(::dapps::FullRegistrar::new(client.clone()));
|
let contract_client = {
|
||||||
|
struct FullRegistrar { client: Arc<Client> }
|
||||||
|
impl RegistrarClient for FullRegistrar {
|
||||||
|
type Call = Asynchronous;
|
||||||
|
fn registrar_address(&self) -> Result<Address, String> {
|
||||||
|
self.client.registrar_address()
|
||||||
|
.ok_or_else(|| "Registrar not defined.".into())
|
||||||
|
}
|
||||||
|
fn call_contract(&self, address: Address, data: Bytes) -> Self::Call {
|
||||||
|
Box::new(self.client.call_contract(BlockId::Latest, address, data).into_future())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Arc::new(FullRegistrar { client: client.clone() })
|
||||||
|
};
|
||||||
|
|
||||||
// the updater service
|
// the updater service
|
||||||
let updater_fetch = fetch.clone();
|
let updater_fetch = fetch.clone();
|
||||||
@ -727,7 +722,7 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
|
|||||||
let signer_service = Arc::new(signer::new_service(&cmd.ws_conf, &cmd.logger_config));
|
let signer_service = Arc::new(signer::new_service(&cmd.ws_conf, &cmd.logger_config));
|
||||||
|
|
||||||
// the dapps server
|
// the dapps server
|
||||||
let (node_health, dapps_deps) = {
|
let node_health = {
|
||||||
let (sync, client) = (sync_provider.clone(), client.clone());
|
let (sync, client) = (sync_provider.clone(), client.clone());
|
||||||
|
|
||||||
struct SyncStatus(Arc<sync::SyncProvider>, Arc<Client>, sync::NetworkConfiguration);
|
struct SyncStatus(Arc<sync::SyncProvider>, Arc<Client>, sync::NetworkConfiguration);
|
||||||
@ -747,23 +742,13 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
|
|||||||
}
|
}
|
||||||
|
|
||||||
let sync_status = Arc::new(SyncStatus(sync, client, net_conf));
|
let sync_status = Arc::new(SyncStatus(sync, client, net_conf));
|
||||||
let node_health = node_health::NodeHealth::new(
|
node_health::NodeHealth::new(
|
||||||
sync_status.clone(),
|
sync_status.clone(),
|
||||||
node_health::TimeChecker::new(&cmd.ntp_servers, cpu_pool.clone()),
|
node_health::TimeChecker::new(&cmd.ntp_servers, cpu_pool.clone()),
|
||||||
event_loop.remote(),
|
event_loop.remote(),
|
||||||
);
|
)
|
||||||
(node_health.clone(), dapps::Dependencies {
|
|
||||||
sync_status,
|
|
||||||
node_health,
|
|
||||||
contract_client,
|
|
||||||
fetch: fetch.clone(),
|
|
||||||
pool: cpu_pool.clone(),
|
|
||||||
signer: signer_service.clone(),
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
let dapps_middleware = dapps::new(cmd.dapps_conf.clone(), dapps_deps.clone())?;
|
|
||||||
|
|
||||||
let dapps_service = dapps::service(&dapps_middleware);
|
|
||||||
let deps_for_rpc_apis = Arc::new(rpc_apis::FullDependencies {
|
let deps_for_rpc_apis = Arc::new(rpc_apis::FullDependencies {
|
||||||
signer_service: signer_service,
|
signer_service: signer_service,
|
||||||
snapshot: snapshot_service.clone(),
|
snapshot: snapshot_service.clone(),
|
||||||
@ -779,8 +764,6 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
|
|||||||
net_service: manage_network.clone(),
|
net_service: manage_network.clone(),
|
||||||
updater: updater.clone(),
|
updater: updater.clone(),
|
||||||
geth_compatibility: cmd.geth_compatibility,
|
geth_compatibility: cmd.geth_compatibility,
|
||||||
dapps_service: dapps_service,
|
|
||||||
dapps_address: cmd.dapps_conf.address(cmd.http_conf.address()),
|
|
||||||
ws_address: cmd.ws_conf.address(),
|
ws_address: cmd.ws_conf.address(),
|
||||||
fetch: fetch.clone(),
|
fetch: fetch.clone(),
|
||||||
pool: cpu_pool.clone(),
|
pool: cpu_pool.clone(),
|
||||||
@ -807,7 +790,7 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
|
|||||||
let rpc_direct = rpc::setup_apis(rpc_apis::ApiSet::All, &dependencies);
|
let rpc_direct = rpc::setup_apis(rpc_apis::ApiSet::All, &dependencies);
|
||||||
let ws_server = rpc::new_ws(cmd.ws_conf.clone(), &dependencies)?;
|
let ws_server = rpc::new_ws(cmd.ws_conf.clone(), &dependencies)?;
|
||||||
let ipc_server = rpc::new_ipc(cmd.ipc_conf, &dependencies)?;
|
let ipc_server = rpc::new_ipc(cmd.ipc_conf, &dependencies)?;
|
||||||
let http_server = rpc::new_http("HTTP JSON-RPC", "jsonrpc", cmd.http_conf.clone(), &dependencies, dapps_middleware)?;
|
let http_server = rpc::new_http("HTTP JSON-RPC", "jsonrpc", cmd.http_conf.clone(), &dependencies)?;
|
||||||
|
|
||||||
// secret store key server
|
// secret store key server
|
||||||
let secretstore_deps = secretstore::Dependencies {
|
let secretstore_deps = secretstore::Dependencies {
|
||||||
@ -1001,11 +984,10 @@ fn daemonize(_pid_file: String) -> Result<(), String> {
|
|||||||
Err("daemon is no supported on windows".into())
|
Err("daemon is no supported on windows".into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_running_environment(spec_name: &String, dirs: &Directories, db_dirs: &DatabaseDirectories, dapps_conf: &dapps::Configuration) {
|
fn print_running_environment(spec_name: &String, dirs: &Directories, db_dirs: &DatabaseDirectories) {
|
||||||
info!("Starting {}", Colour::White.bold().paint(version()));
|
info!("Starting {}", Colour::White.bold().paint(version()));
|
||||||
info!("Keys path {}", Colour::White.bold().paint(dirs.keys_path(spec_name).to_string_lossy().into_owned()));
|
info!("Keys path {}", Colour::White.bold().paint(dirs.keys_path(spec_name).to_string_lossy().into_owned()));
|
||||||
info!("DB path {}", Colour::White.bold().paint(db_dirs.db_root_path().to_string_lossy().into_owned()));
|
info!("DB path {}", Colour::White.bold().paint(db_dirs.db_root_path().to_string_lossy().into_owned()));
|
||||||
info!("Path to dapps {}", Colour::White.bold().paint(dapps_conf.dapps_path.to_string_lossy().into_owned()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare_account_provider(spec: &SpecType, dirs: &Directories, data_dir: &str, cfg: AccountsConfig, passwords: &[Password]) -> Result<AccountProvider, String> {
|
fn prepare_account_provider(spec: &SpecType, dirs: &Directories, data_dir: &str, cfg: AccountsConfig, passwords: &[Password]) -> Result<AccountProvider, String> {
|
||||||
|
@ -1,88 +0,0 @@
|
|||||||
// Copyright 2015-2018 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/>.
|
|
||||||
|
|
||||||
//! Cross-platform open url in default browser
|
|
||||||
|
|
||||||
use std;
|
|
||||||
use std::os::raw::c_int;
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
pub enum Error {
|
|
||||||
ProcessError(std::io::Error),
|
|
||||||
WindowsShellExecute(c_int),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<std::io::Error> for Error {
|
|
||||||
fn from(err: std::io::Error) -> Self {
|
|
||||||
Error::ProcessError(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for Error {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
|
||||||
match *self {
|
|
||||||
Error::ProcessError(ref e) => write!(f, "{}", e),
|
|
||||||
Error::WindowsShellExecute(e) => write!(f, "WindowsShellExecute error: {}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
pub fn open(url: &str) -> Result<(), Error> {
|
|
||||||
use std::ffi::CString;
|
|
||||||
use std::ptr;
|
|
||||||
use winapi::um::shellapi::ShellExecuteA;
|
|
||||||
use winapi::um::winuser::SW_SHOWNORMAL as Normal;
|
|
||||||
|
|
||||||
const WINDOWS_SHELL_EXECUTE_SUCCESS: c_int = 32;
|
|
||||||
|
|
||||||
let h_instance = unsafe {
|
|
||||||
ShellExecuteA(ptr::null_mut(),
|
|
||||||
CString::new("open").unwrap().as_ptr(),
|
|
||||||
CString::new(url.to_owned().replace("\n", "%0A")).unwrap().as_ptr(),
|
|
||||||
ptr::null(),
|
|
||||||
ptr::null(),
|
|
||||||
Normal) as c_int
|
|
||||||
};
|
|
||||||
|
|
||||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/bb762153(v=vs.85).aspx
|
|
||||||
// `ShellExecute` returns a value greater than 32 on success
|
|
||||||
if h_instance > WINDOWS_SHELL_EXECUTE_SUCCESS {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(Error::WindowsShellExecute(h_instance))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(target_os="macos", target_os="freebsd"))]
|
|
||||||
pub fn open(url: &str) -> Result<(), Error> {
|
|
||||||
let _ = std::process::Command::new("open").arg(url).spawn()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os="linux")]
|
|
||||||
pub fn open(url: &str) -> Result<(), Error> {
|
|
||||||
let _ = std::process::Command::new("xdg-open").arg(url).spawn()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(target_os="android", target_os="ios"))]
|
|
||||||
pub fn open(_url: &str) -> Result<(), Error> {
|
|
||||||
// TODO: While it is generally always bad to leave a function implemented, there is not much
|
|
||||||
// more we can do here. This function will eventually be removed when we compile Parity
|
|
||||||
// as a library and not as a full binary.
|
|
||||||
Ok(())
|
|
||||||
}
|
|
@ -54,7 +54,7 @@ ethkey = { path = "../ethkey" }
|
|||||||
ethstore = { path = "../ethstore" }
|
ethstore = { path = "../ethstore" }
|
||||||
fetch = { path = "../util/fetch" }
|
fetch = { path = "../util/fetch" }
|
||||||
keccak-hash = { git = "https://github.com/paritytech/parity-common" }
|
keccak-hash = { git = "https://github.com/paritytech/parity-common" }
|
||||||
node-health = { path = "../dapps/node-health" }
|
node-health = { path = "../node-health" }
|
||||||
parity-reactor = { path = "../util/reactor" }
|
parity-reactor = { path = "../util/reactor" }
|
||||||
parity-updater = { path = "../updater" }
|
parity-updater = { path = "../updater" }
|
||||||
parity-version = { path = "../util/version" }
|
parity-version = { path = "../util/version" }
|
||||||
|
@ -131,14 +131,41 @@ use http::tokio_core;
|
|||||||
pub type HttpServer = http::Server;
|
pub type HttpServer = http::Server;
|
||||||
|
|
||||||
/// Start http server asynchronously and returns result with `Server` handle on success or an error.
|
/// Start http server asynchronously and returns result with `Server` handle on success or an error.
|
||||||
pub fn start_http<M, S, H, T, R>(
|
pub fn start_http<M, S, H, T>(
|
||||||
addr: &SocketAddr,
|
addr: &SocketAddr,
|
||||||
cors_domains: http::DomainsValidation<http::AccessControlAllowOrigin>,
|
cors_domains: http::DomainsValidation<http::AccessControlAllowOrigin>,
|
||||||
allowed_hosts: http::DomainsValidation<http::Host>,
|
allowed_hosts: http::DomainsValidation<http::Host>,
|
||||||
handler: H,
|
handler: H,
|
||||||
remote: tokio_core::reactor::Remote,
|
remote: tokio_core::reactor::Remote,
|
||||||
extractor: T,
|
extractor: T,
|
||||||
middleware: Option<R>,
|
threads: usize,
|
||||||
|
max_payload: usize,
|
||||||
|
) -> ::std::io::Result<HttpServer> where
|
||||||
|
M: jsonrpc_core::Metadata,
|
||||||
|
S: jsonrpc_core::Middleware<M>,
|
||||||
|
H: Into<jsonrpc_core::MetaIoHandler<M, S>>,
|
||||||
|
T: HttpMetaExtractor<Metadata=M>,
|
||||||
|
{
|
||||||
|
let extractor = http_common::MetaExtractor::new(extractor);
|
||||||
|
Ok(http::ServerBuilder::with_meta_extractor(handler, extractor)
|
||||||
|
.threads(threads)
|
||||||
|
.event_loop_remote(remote)
|
||||||
|
.cors(cors_domains.into())
|
||||||
|
.allowed_hosts(allowed_hosts.into())
|
||||||
|
.max_request_body_size(max_payload * 1024 * 1024)
|
||||||
|
.start_http(addr)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Same as `start_http`, but takes an additional `middleware` parameter that is introduced as a
|
||||||
|
/// hyper middleware.
|
||||||
|
pub fn start_http_with_middleware<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,
|
||||||
|
middleware: R,
|
||||||
threads: usize,
|
threads: usize,
|
||||||
max_payload: usize,
|
max_payload: usize,
|
||||||
) -> ::std::io::Result<HttpServer> where
|
) -> ::std::io::Result<HttpServer> where
|
||||||
@ -149,18 +176,14 @@ pub fn start_http<M, S, H, T, R>(
|
|||||||
R: RequestMiddleware,
|
R: RequestMiddleware,
|
||||||
{
|
{
|
||||||
let extractor = http_common::MetaExtractor::new(extractor);
|
let extractor = http_common::MetaExtractor::new(extractor);
|
||||||
let mut builder = http::ServerBuilder::with_meta_extractor(handler, extractor)
|
Ok(http::ServerBuilder::with_meta_extractor(handler, extractor)
|
||||||
.threads(threads)
|
.threads(threads)
|
||||||
.event_loop_remote(remote)
|
.event_loop_remote(remote)
|
||||||
.cors(cors_domains.into())
|
.cors(cors_domains.into())
|
||||||
.allowed_hosts(allowed_hosts.into())
|
.allowed_hosts(allowed_hosts.into())
|
||||||
.max_request_body_size(max_payload * 1024 * 1024);
|
.max_request_body_size(max_payload * 1024 * 1024)
|
||||||
|
.request_middleware(middleware)
|
||||||
if let Some(dapps) = middleware {
|
.start_http(addr)?)
|
||||||
builder = builder.request_middleware(dapps)
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(builder.start_http(addr)?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start ipc server asynchronously and returns result with `Server` handle on success or an error.
|
/// Start ipc server asynchronously and returns result with `Server` handle on success or an error.
|
||||||
|
@ -26,19 +26,19 @@ fn serve(handler: Option<MetaIoHandler<Metadata>>) -> Server<HttpServer> {
|
|||||||
let address = "127.0.0.1:0".parse().unwrap();
|
let address = "127.0.0.1:0".parse().unwrap();
|
||||||
let handler = handler.unwrap_or_default();
|
let handler = handler.unwrap_or_default();
|
||||||
|
|
||||||
Server::new(|remote| ::start_http(
|
Server::new(|remote| ::start_http_with_middleware(
|
||||||
&address,
|
&address,
|
||||||
http::DomainsValidation::Disabled,
|
http::DomainsValidation::Disabled,
|
||||||
http::DomainsValidation::Disabled,
|
http::DomainsValidation::Disabled,
|
||||||
handler,
|
handler,
|
||||||
remote,
|
remote,
|
||||||
extractors::RpcExtractor,
|
extractors::RpcExtractor,
|
||||||
Some(|request: hyper::Request| {
|
|request: hyper::Request| {
|
||||||
http::RequestMiddlewareAction::Proceed {
|
http::RequestMiddlewareAction::Proceed {
|
||||||
should_continue_on_invalid_cors: false,
|
should_continue_on_invalid_cors: false,
|
||||||
request,
|
request,
|
||||||
}
|
}
|
||||||
}),
|
},
|
||||||
1,
|
1,
|
||||||
5,
|
5,
|
||||||
).unwrap())
|
).unwrap())
|
||||||
|
@ -58,7 +58,6 @@ pub struct ParityClient {
|
|||||||
settings: Arc<NetworkSettings>,
|
settings: Arc<NetworkSettings>,
|
||||||
health: NodeHealth,
|
health: NodeHealth,
|
||||||
signer: Option<Arc<SignerService>>,
|
signer: Option<Arc<SignerService>>,
|
||||||
dapps_address: Option<Host>,
|
|
||||||
ws_address: Option<Host>,
|
ws_address: Option<Host>,
|
||||||
eip86_transition: u64,
|
eip86_transition: u64,
|
||||||
gas_price_percentile: usize,
|
gas_price_percentile: usize,
|
||||||
@ -74,7 +73,6 @@ impl ParityClient {
|
|||||||
settings: Arc<NetworkSettings>,
|
settings: Arc<NetworkSettings>,
|
||||||
health: NodeHealth,
|
health: NodeHealth,
|
||||||
signer: Option<Arc<SignerService>>,
|
signer: Option<Arc<SignerService>>,
|
||||||
dapps_address: Option<Host>,
|
|
||||||
ws_address: Option<Host>,
|
ws_address: Option<Host>,
|
||||||
gas_price_percentile: usize,
|
gas_price_percentile: usize,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@ -85,7 +83,6 @@ impl ParityClient {
|
|||||||
settings,
|
settings,
|
||||||
health,
|
health,
|
||||||
signer,
|
signer,
|
||||||
dapps_address,
|
|
||||||
ws_address,
|
ws_address,
|
||||||
eip86_transition: client.eip86_transition(),
|
eip86_transition: client.eip86_transition(),
|
||||||
client,
|
client,
|
||||||
@ -330,8 +327,7 @@ impl Parity for ParityClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn dapps_url(&self) -> Result<String> {
|
fn dapps_url(&self) -> Result<String> {
|
||||||
helpers::to_url(&self.dapps_address)
|
Err(errors::dapps_disabled())
|
||||||
.ok_or_else(|| errors::dapps_disabled())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ws_url(&self) -> Result<String> {
|
fn ws_url(&self) -> Result<String> {
|
||||||
|
@ -27,7 +27,6 @@ use hash::keccak_buffer;
|
|||||||
|
|
||||||
use jsonrpc_core::{Result, BoxFuture};
|
use jsonrpc_core::{Result, BoxFuture};
|
||||||
use jsonrpc_core::futures::Future;
|
use jsonrpc_core::futures::Future;
|
||||||
use v1::helpers::dapps::DappsService;
|
|
||||||
use v1::helpers::errors;
|
use v1::helpers::errors;
|
||||||
use v1::traits::ParitySet;
|
use v1::traits::ParitySet;
|
||||||
use v1::types::{Bytes, H160, H256, U256, ReleaseInfo, Transaction, LocalDapp};
|
use v1::types::{Bytes, H160, H256, U256, ReleaseInfo, Transaction, LocalDapp};
|
||||||
@ -35,17 +34,15 @@ use v1::types::{Bytes, H160, H256, U256, ReleaseInfo, Transaction, LocalDapp};
|
|||||||
/// Parity-specific rpc interface for operations altering the settings.
|
/// Parity-specific rpc interface for operations altering the settings.
|
||||||
pub struct ParitySetClient<F> {
|
pub struct ParitySetClient<F> {
|
||||||
net: Arc<ManageNetwork>,
|
net: Arc<ManageNetwork>,
|
||||||
dapps: Option<Arc<DappsService>>,
|
|
||||||
fetch: F,
|
fetch: F,
|
||||||
pool: CpuPool,
|
pool: CpuPool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: Fetch> ParitySetClient<F> {
|
impl<F: Fetch> ParitySetClient<F> {
|
||||||
/// Creates new `ParitySetClient` with given `Fetch`.
|
/// Creates new `ParitySetClient` with given `Fetch`.
|
||||||
pub fn new(net: Arc<ManageNetwork>, dapps: Option<Arc<DappsService>>, fetch: F, p: CpuPool) -> Self {
|
pub fn new(net: Arc<ManageNetwork>, fetch: F, p: CpuPool) -> Self {
|
||||||
ParitySetClient {
|
ParitySetClient {
|
||||||
net: net,
|
net: net,
|
||||||
dapps: dapps,
|
|
||||||
fetch: fetch,
|
fetch: fetch,
|
||||||
pool: p,
|
pool: p,
|
||||||
}
|
}
|
||||||
@ -141,11 +138,11 @@ impl<F: Fetch> ParitySet for ParitySetClient<F> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn dapps_refresh(&self) -> Result<bool> {
|
fn dapps_refresh(&self) -> Result<bool> {
|
||||||
self.dapps.as_ref().map(|dapps| dapps.refresh_local_dapps()).ok_or_else(errors::dapps_disabled)
|
Err(errors::dapps_disabled())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dapps_list(&self) -> Result<Vec<LocalDapp>> {
|
fn dapps_list(&self) -> Result<Vec<LocalDapp>> {
|
||||||
self.dapps.as_ref().map(|dapps| dapps.list_dapps()).ok_or_else(errors::dapps_disabled)
|
Err(errors::dapps_disabled())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn upgrade_ready(&self) -> Result<Option<ReleaseInfo>> {
|
fn upgrade_ready(&self) -> Result<Option<ReleaseInfo>> {
|
||||||
|
@ -63,7 +63,6 @@ pub struct ParityClient<C, M, U> {
|
|||||||
logger: Arc<RotatingLogger>,
|
logger: Arc<RotatingLogger>,
|
||||||
settings: Arc<NetworkSettings>,
|
settings: Arc<NetworkSettings>,
|
||||||
signer: Option<Arc<SignerService>>,
|
signer: Option<Arc<SignerService>>,
|
||||||
dapps_address: Option<Host>,
|
|
||||||
ws_address: Option<Host>,
|
ws_address: Option<Host>,
|
||||||
eip86_transition: u64,
|
eip86_transition: u64,
|
||||||
}
|
}
|
||||||
@ -83,7 +82,6 @@ impl<C, M, U> ParityClient<C, M, U> where
|
|||||||
logger: Arc<RotatingLogger>,
|
logger: Arc<RotatingLogger>,
|
||||||
settings: Arc<NetworkSettings>,
|
settings: Arc<NetworkSettings>,
|
||||||
signer: Option<Arc<SignerService>>,
|
signer: Option<Arc<SignerService>>,
|
||||||
dapps_address: Option<Host>,
|
|
||||||
ws_address: Option<Host>,
|
ws_address: Option<Host>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let eip86_transition = client.eip86_transition();
|
let eip86_transition = client.eip86_transition();
|
||||||
@ -98,7 +96,6 @@ impl<C, M, U> ParityClient<C, M, U> where
|
|||||||
logger,
|
logger,
|
||||||
settings,
|
settings,
|
||||||
signer,
|
signer,
|
||||||
dapps_address,
|
|
||||||
ws_address,
|
ws_address,
|
||||||
eip86_transition,
|
eip86_transition,
|
||||||
}
|
}
|
||||||
@ -351,8 +348,7 @@ impl<C, M, U, S> Parity for ParityClient<C, M, U> where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn dapps_url(&self) -> Result<String> {
|
fn dapps_url(&self) -> Result<String> {
|
||||||
helpers::to_url(&self.dapps_address)
|
Err(errors::dapps_disabled())
|
||||||
.ok_or_else(|| errors::dapps_disabled())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ws_url(&self) -> Result<String> {
|
fn ws_url(&self) -> Result<String> {
|
||||||
|
@ -29,7 +29,6 @@ use updater::{Service as UpdateService};
|
|||||||
|
|
||||||
use jsonrpc_core::{BoxFuture, Result};
|
use jsonrpc_core::{BoxFuture, Result};
|
||||||
use jsonrpc_core::futures::Future;
|
use jsonrpc_core::futures::Future;
|
||||||
use v1::helpers::dapps::DappsService;
|
|
||||||
use v1::helpers::errors;
|
use v1::helpers::errors;
|
||||||
use v1::traits::ParitySet;
|
use v1::traits::ParitySet;
|
||||||
use v1::types::{Bytes, H160, H256, U256, ReleaseInfo, Transaction, LocalDapp};
|
use v1::types::{Bytes, H160, H256, U256, ReleaseInfo, Transaction, LocalDapp};
|
||||||
@ -40,7 +39,6 @@ pub struct ParitySetClient<C, M, U, F = fetch::Client> {
|
|||||||
miner: Arc<M>,
|
miner: Arc<M>,
|
||||||
updater: Arc<U>,
|
updater: Arc<U>,
|
||||||
net: Arc<ManageNetwork>,
|
net: Arc<ManageNetwork>,
|
||||||
dapps: Option<Arc<DappsService>>,
|
|
||||||
fetch: F,
|
fetch: F,
|
||||||
pool: CpuPool,
|
pool: CpuPool,
|
||||||
eip86_transition: u64,
|
eip86_transition: u64,
|
||||||
@ -55,7 +53,6 @@ impl<C, M, U, F> ParitySetClient<C, M, U, F>
|
|||||||
miner: &Arc<M>,
|
miner: &Arc<M>,
|
||||||
updater: &Arc<U>,
|
updater: &Arc<U>,
|
||||||
net: &Arc<ManageNetwork>,
|
net: &Arc<ManageNetwork>,
|
||||||
dapps: Option<Arc<DappsService>>,
|
|
||||||
fetch: F,
|
fetch: F,
|
||||||
pool: CpuPool,
|
pool: CpuPool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@ -64,7 +61,6 @@ impl<C, M, U, F> ParitySetClient<C, M, U, F>
|
|||||||
miner: miner.clone(),
|
miner: miner.clone(),
|
||||||
updater: updater.clone(),
|
updater: updater.clone(),
|
||||||
net: net.clone(),
|
net: net.clone(),
|
||||||
dapps: dapps,
|
|
||||||
fetch: fetch,
|
fetch: fetch,
|
||||||
pool: pool,
|
pool: pool,
|
||||||
eip86_transition: client.eip86_transition(),
|
eip86_transition: client.eip86_transition(),
|
||||||
@ -187,11 +183,11 @@ impl<C, M, U, F> ParitySet for ParitySetClient<C, M, U, F> where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn dapps_refresh(&self) -> Result<bool> {
|
fn dapps_refresh(&self) -> Result<bool> {
|
||||||
self.dapps.as_ref().map(|dapps| dapps.refresh_local_dapps()).ok_or_else(errors::dapps_disabled)
|
Err(errors::dapps_disabled())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dapps_list(&self) -> Result<Vec<LocalDapp>> {
|
fn dapps_list(&self) -> Result<Vec<LocalDapp>> {
|
||||||
self.dapps.as_ref().map(|dapps| dapps.list_dapps()).ok_or_else(errors::dapps_disabled)
|
Err(errors::dapps_disabled())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn upgrade_ready(&self) -> Result<Option<ReleaseInfo>> {
|
fn upgrade_ready(&self) -> Result<Option<ReleaseInfo>> {
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
// Copyright 2015-2018 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/>.
|
|
||||||
|
|
||||||
//! Test implementation of dapps service.
|
|
||||||
|
|
||||||
use v1::types::LocalDapp;
|
|
||||||
use v1::helpers::dapps::DappsService;
|
|
||||||
|
|
||||||
/// Test implementation of dapps service. Will always return the same list of dapps.
|
|
||||||
#[derive(Default, Clone)]
|
|
||||||
pub struct TestDappsService;
|
|
||||||
|
|
||||||
impl DappsService for TestDappsService {
|
|
||||||
fn list_dapps(&self) -> Vec<LocalDapp> {
|
|
||||||
vec![LocalDapp {
|
|
||||||
id: "skeleton".into(),
|
|
||||||
name: "Skeleton".into(),
|
|
||||||
description: "A skeleton dapp".into(),
|
|
||||||
version: "0.1".into(),
|
|
||||||
author: "Parity Technologies Ltd".into(),
|
|
||||||
icon_url: "title.png".into(),
|
|
||||||
local_url: None,
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn refresh_local_dapps(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
@ -16,13 +16,11 @@
|
|||||||
|
|
||||||
//! Test rpc services.
|
//! Test rpc services.
|
||||||
|
|
||||||
mod dapps;
|
|
||||||
mod miner_service;
|
mod miner_service;
|
||||||
mod snapshot_service;
|
mod snapshot_service;
|
||||||
mod sync_provider;
|
mod sync_provider;
|
||||||
mod update_service;
|
mod update_service;
|
||||||
|
|
||||||
pub use self::dapps::TestDappsService;
|
|
||||||
pub use self::miner_service::TestMinerService;
|
pub use self::miner_service::TestMinerService;
|
||||||
pub use self::snapshot_service::TestSnapshotService;
|
pub use self::snapshot_service::TestSnapshotService;
|
||||||
pub use self::sync_provider::{Config, TestSyncProvider};
|
pub use self::sync_provider::{Config, TestSyncProvider};
|
||||||
|
@ -45,7 +45,6 @@ pub struct Dependencies {
|
|||||||
pub settings: Arc<NetworkSettings>,
|
pub settings: Arc<NetworkSettings>,
|
||||||
pub network: Arc<ManageNetwork>,
|
pub network: Arc<ManageNetwork>,
|
||||||
pub accounts: Arc<AccountProvider>,
|
pub accounts: Arc<AccountProvider>,
|
||||||
pub dapps_address: Option<Host>,
|
|
||||||
pub ws_address: Option<Host>,
|
pub ws_address: Option<Host>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +74,6 @@ impl Dependencies {
|
|||||||
}),
|
}),
|
||||||
network: Arc::new(TestManageNetwork),
|
network: Arc::new(TestManageNetwork),
|
||||||
accounts: Arc::new(AccountProvider::transient_provider()),
|
accounts: Arc::new(AccountProvider::transient_provider()),
|
||||||
dapps_address: Some("127.0.0.1:18080".into()),
|
|
||||||
ws_address: Some("127.0.0.1:18546".into()),
|
ws_address: Some("127.0.0.1:18546".into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -92,7 +90,6 @@ impl Dependencies {
|
|||||||
self.logger.clone(),
|
self.logger.clone(),
|
||||||
self.settings.clone(),
|
self.settings.clone(),
|
||||||
signer,
|
signer,
|
||||||
self.dapps_address.clone(),
|
|
||||||
self.ws_address.clone(),
|
self.ws_address.clone(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -431,19 +428,15 @@ fn rpc_parity_ws_address() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn rpc_parity_dapps_address() {
|
fn rpc_parity_dapps_address() {
|
||||||
// given
|
// given
|
||||||
let mut deps = Dependencies::new();
|
let deps = Dependencies::new();
|
||||||
let io1 = deps.default_client();
|
let io1 = deps.default_client();
|
||||||
deps.dapps_address = None;
|
|
||||||
let io2 = deps.default_client();
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let request = r#"{"jsonrpc": "2.0", "method": "parity_dappsUrl", "params": [], "id": 1}"#;
|
let request = r#"{"jsonrpc": "2.0", "method": "parity_dappsUrl", "params": [], "id": 1}"#;
|
||||||
let response1 = r#"{"jsonrpc":"2.0","result":"127.0.0.1:18080","id":1}"#;
|
let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"Dapps Server is disabled. This API is not available."},"id":1}"#;
|
||||||
let response2 = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"Dapps Server is disabled. This API is not available."},"id":1}"#;
|
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(io1.handle_request_sync(request), Some(response1.to_owned()));
|
assert_eq!(io1.handle_request_sync(request), Some(response.to_owned()));
|
||||||
assert_eq!(io2.handle_request_sync(request), Some(response2.to_owned()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -26,7 +26,7 @@ use futures_cpupool::CpuPool;
|
|||||||
|
|
||||||
use jsonrpc_core::IoHandler;
|
use jsonrpc_core::IoHandler;
|
||||||
use v1::{ParitySet, ParitySetClient};
|
use v1::{ParitySet, ParitySetClient};
|
||||||
use v1::tests::helpers::{TestMinerService, TestUpdater, TestDappsService};
|
use v1::tests::helpers::{TestMinerService, TestUpdater};
|
||||||
use super::manage_network::TestManageNetwork;
|
use super::manage_network::TestManageNetwork;
|
||||||
|
|
||||||
use fake_fetch::FakeFetch;
|
use fake_fetch::FakeFetch;
|
||||||
@ -55,9 +55,8 @@ fn parity_set_client(
|
|||||||
updater: &Arc<TestUpdater>,
|
updater: &Arc<TestUpdater>,
|
||||||
net: &Arc<TestManageNetwork>,
|
net: &Arc<TestManageNetwork>,
|
||||||
) -> TestParitySetClient {
|
) -> TestParitySetClient {
|
||||||
let dapps_service = Arc::new(TestDappsService);
|
|
||||||
let pool = CpuPool::new(1);
|
let pool = CpuPool::new(1);
|
||||||
ParitySetClient::new(client, miner, updater, &(net.clone() as Arc<ManageNetwork>), Some(dapps_service), FakeFetch::new(Some(1)), pool)
|
ParitySetClient::new(client, miner, updater, &(net.clone() as Arc<ManageNetwork>), FakeFetch::new(Some(1)), pool)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -250,7 +249,7 @@ fn rpc_parity_set_dapps_list() {
|
|||||||
io.extend_with(parity_set_client(&client, &miner, &updater, &network).to_delegate());
|
io.extend_with(parity_set_client(&client, &miner, &updater, &network).to_delegate());
|
||||||
|
|
||||||
let request = r#"{"jsonrpc": "2.0", "method": "parity_dappsList", "params":[], "id": 1}"#;
|
let request = r#"{"jsonrpc": "2.0", "method": "parity_dappsList", "params":[], "id": 1}"#;
|
||||||
let response = r#"{"jsonrpc":"2.0","result":[{"author":"Parity Technologies Ltd","description":"A skeleton dapp","iconUrl":"title.png","id":"skeleton","localUrl":null,"name":"Skeleton","version":"0.1"}],"id":1}"#;
|
let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"Dapps Server is disabled. This API is not available."},"id":1}"#;
|
||||||
|
|
||||||
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
|
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
|
||||||
}
|
}
|
||||||
|
@ -162,6 +162,7 @@ build_rpc_trait! {
|
|||||||
fn local_transactions(&self) -> Result<BTreeMap<H256, LocalTransactionStatus>>;
|
fn local_transactions(&self) -> Result<BTreeMap<H256, LocalTransactionStatus>>;
|
||||||
|
|
||||||
/// Returns current Dapps Server interface and port or an error if dapps server is disabled.
|
/// Returns current Dapps Server interface and port or an error if dapps server is disabled.
|
||||||
|
/// (deprecated, should always return an error now).
|
||||||
#[rpc(name = "parity_dappsUrl")]
|
#[rpc(name = "parity_dappsUrl")]
|
||||||
fn dapps_url(&self) -> Result<String>;
|
fn dapps_url(&self) -> Result<String>;
|
||||||
|
|
||||||
|
@ -95,11 +95,12 @@ build_rpc_trait! {
|
|||||||
#[rpc(name = "parity_hashContent")]
|
#[rpc(name = "parity_hashContent")]
|
||||||
fn hash_content(&self, String) -> BoxFuture<H256>;
|
fn hash_content(&self, String) -> BoxFuture<H256>;
|
||||||
|
|
||||||
/// Returns true if refresh successful, error if unsuccessful or server is disabled.
|
/// Returns true if refresh successful, error if unsuccessful or server is disabled
|
||||||
|
/// (deprecated, should always return an error now).
|
||||||
#[rpc(name = "parity_dappsRefresh")]
|
#[rpc(name = "parity_dappsRefresh")]
|
||||||
fn dapps_refresh(&self) -> Result<bool>;
|
fn dapps_refresh(&self) -> Result<bool>;
|
||||||
|
|
||||||
/// Returns a list of local dapps
|
/// Returns a list of local dapps (deprecated, should always return an error now).
|
||||||
#[rpc(name = "parity_dappsList")]
|
#[rpc(name = "parity_dappsList")]
|
||||||
fn dapps_list(&self) -> Result<Vec<LocalDapp>>;
|
fn dapps_list(&self) -> Result<Vec<LocalDapp>>;
|
||||||
|
|
||||||
|
@ -61,8 +61,6 @@ pub struct Directories {
|
|||||||
pub keys: String,
|
pub keys: String,
|
||||||
/// Signer dir
|
/// Signer dir
|
||||||
pub signer: String,
|
pub signer: String,
|
||||||
/// Dir to store dapps
|
|
||||||
pub dapps: String,
|
|
||||||
/// Secrets dir
|
/// Secrets dir
|
||||||
pub secretstore: String,
|
pub secretstore: String,
|
||||||
}
|
}
|
||||||
@ -77,7 +75,6 @@ impl Default for Directories {
|
|||||||
cache: replace_home_and_local(&data_dir, &local_dir, CACHE_PATH),
|
cache: replace_home_and_local(&data_dir, &local_dir, CACHE_PATH),
|
||||||
keys: replace_home(&data_dir, "$BASE/keys"),
|
keys: replace_home(&data_dir, "$BASE/keys"),
|
||||||
signer: replace_home(&data_dir, "$BASE/signer"),
|
signer: replace_home(&data_dir, "$BASE/signer"),
|
||||||
dapps: replace_home(&data_dir, "$BASE/dapps"),
|
|
||||||
secretstore: replace_home(&data_dir, "$BASE/secretstore"),
|
secretstore: replace_home(&data_dir, "$BASE/secretstore"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,7 +82,7 @@ impl Default for Directories {
|
|||||||
|
|
||||||
impl Directories {
|
impl Directories {
|
||||||
/// Create local directories
|
/// Create local directories
|
||||||
pub fn create_dirs(&self, dapps_enabled: bool, signer_enabled: bool, secretstore_enabled: bool) -> Result<(), String> {
|
pub fn create_dirs(&self, signer_enabled: bool, secretstore_enabled: bool) -> Result<(), String> {
|
||||||
fs::create_dir_all(&self.base).map_err(|e| e.to_string())?;
|
fs::create_dir_all(&self.base).map_err(|e| e.to_string())?;
|
||||||
fs::create_dir_all(&self.db).map_err(|e| e.to_string())?;
|
fs::create_dir_all(&self.db).map_err(|e| e.to_string())?;
|
||||||
fs::create_dir_all(&self.cache).map_err(|e| e.to_string())?;
|
fs::create_dir_all(&self.cache).map_err(|e| e.to_string())?;
|
||||||
@ -93,9 +90,6 @@ impl Directories {
|
|||||||
if signer_enabled {
|
if signer_enabled {
|
||||||
fs::create_dir_all(&self.signer).map_err(|e| e.to_string())?;
|
fs::create_dir_all(&self.signer).map_err(|e| e.to_string())?;
|
||||||
}
|
}
|
||||||
if dapps_enabled {
|
|
||||||
fs::create_dir_all(&self.dapps).map_err(|e| e.to_string())?;
|
|
||||||
}
|
|
||||||
if secretstore_enabled {
|
if secretstore_enabled {
|
||||||
fs::create_dir_all(&self.secretstore).map_err(|e| e.to_string())?;
|
fs::create_dir_all(&self.secretstore).map_err(|e| e.to_string())?;
|
||||||
}
|
}
|
||||||
@ -356,7 +350,6 @@ mod tests {
|
|||||||
),
|
),
|
||||||
keys: replace_home(&data_dir, "$BASE/keys"),
|
keys: replace_home(&data_dir, "$BASE/keys"),
|
||||||
signer: replace_home(&data_dir, "$BASE/signer"),
|
signer: replace_home(&data_dir, "$BASE/signer"),
|
||||||
dapps: replace_home(&data_dir, "$BASE/dapps"),
|
|
||||||
secretstore: replace_home(&data_dir, "$BASE/secretstore"),
|
secretstore: replace_home(&data_dir, "$BASE/secretstore"),
|
||||||
};
|
};
|
||||||
assert_eq!(expected, Directories::default());
|
assert_eq!(expected, Directories::default());
|
||||||
|
Loading…
Reference in New Issue
Block a user