Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c6e3f314a | ||
|
|
96cbfba916 | ||
|
|
18b8d6aca6 | ||
|
|
6a44a0cf95 | ||
|
|
b7860e4a3f | ||
|
|
4a910a762d | ||
|
|
1626c78ae2 | ||
|
|
7f4e700013 | ||
|
|
987390fb7d | ||
|
|
797f23f30b | ||
|
|
c1da49bbc4 | ||
|
|
1164193019 | ||
|
|
1c217cbf2b | ||
|
|
d24f71f150 | ||
|
|
4c66a021ec | ||
|
|
ccc57328e2 | ||
|
|
77240b6e5a | ||
|
|
2e428ddd58 | ||
|
|
cd4081c149 | ||
|
|
428342d69d | ||
|
|
8ee9b262f9 | ||
|
|
e2bc251ff4 | ||
|
|
d15372c8a6 | ||
|
|
bcf2245110 | ||
|
|
36f74fdf0d | ||
|
|
45402544d3 | ||
|
|
ab236690df | ||
|
|
34e81101d7 | ||
|
|
07d4b9bbc9 | ||
|
|
d25a20b274 | ||
|
|
70d17ce1d8 | ||
|
|
3b14bcf29a | ||
|
|
4fc69e11d6 | ||
|
|
8e4df824b0 | ||
|
|
e1f2e840cb | ||
|
|
2a36a89c36 | ||
|
|
b643f4011d | ||
|
|
814304bdd7 | ||
|
|
f90607302d | ||
|
|
af826f877e | ||
|
|
f9a0aa0022 | ||
|
|
c4196a5de3 | ||
|
|
3b56e8eded | ||
|
|
b3ccbbe913 | ||
|
|
fe0f037f23 |
@@ -1,3 +0,0 @@
|
||||
[target.x86_64-pc-windows-msvc]
|
||||
# Link the C runtime statically ; https://github.com/paritytech/parity/issues/6643
|
||||
rustflags = ["-Ctarget-feature=+crt-static"]
|
||||
@@ -1,22 +0,0 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
target
|
||||
|
||||
*.swp
|
||||
*.swo
|
||||
*.swn
|
||||
*.DS_Store
|
||||
|
||||
# Visual Studio Code stuff
|
||||
.vscode
|
||||
|
||||
# GitEye stuff
|
||||
.project
|
||||
|
||||
# idea ide
|
||||
.idea
|
||||
|
||||
# git stuff
|
||||
.git
|
||||
|
||||
ethcore/res/ethereum/tests
|
||||
@@ -9,7 +9,7 @@ trim_trailing_whitespace=true
|
||||
max_line_length=120
|
||||
insert_final_newline=true
|
||||
|
||||
[*.{yml,sh}]
|
||||
[.travis.yml]
|
||||
indent_style=space
|
||||
indent_size=2
|
||||
tab_width=8
|
||||
|
||||
84
.github/CODE_OF_CONDUCT.md
vendored
84
.github/CODE_OF_CONDUCT.md
vendored
@@ -1,84 +0,0 @@
|
||||
# Code of Conduct
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
A primary goal of Parity is to be inclusive to the largest number of contributors, with the most varied and diverse backgrounds possible. As such, we are committed to providing a friendly, safe and welcoming environment for all, regardless of gender, sexual orientation, ability, ethnicity, socioeconomic status, and religion (or lack thereof).
|
||||
|
||||
This code of conduct outlines our expectations for all those who participate in our community, as well as the consequences for unacceptable behavior.
|
||||
|
||||
We invite all those who participate in Parity to help us create safe and positive experiences for everyone.
|
||||
|
||||
## 2. Open Source Citizenship
|
||||
|
||||
A supplemental goal of this Code of Conduct is to increase open source citizenship by encouraging participants to recognize and strengthen the relationships between our actions and their effects on our community.
|
||||
|
||||
Communities mirror the societies in which they exist and positive action is essential to counteract the many forms of inequality and abuses of power that exist in society.
|
||||
|
||||
If you see someone who is making an extra effort to ensure our community is welcoming, friendly, and encourages all participants to contribute to the fullest extent, we want to know.
|
||||
|
||||
## 3. Expected Behavior
|
||||
|
||||
The following behaviors are expected and requested of all community members:
|
||||
|
||||
* Participate in an authentic and active way. In doing so, you contribute to the health and longevity of this community.
|
||||
* Exercise consideration and respect in your speech and actions.
|
||||
* Attempt collaboration before conflict.
|
||||
* Refrain from demeaning, discriminatory, or harassing behavior and speech.
|
||||
* Be mindful of your surroundings and of your fellow participants. Alert community leaders if you notice a dangerous situation, someone in distress, or violations of this Code of Conduct, even if they seem inconsequential.
|
||||
* Remember that community event venues may be shared with members of the public; please be respectful to all patrons of these locations.
|
||||
|
||||
## 4. Unacceptable Behavior
|
||||
|
||||
The following behaviors are considered harassment and are unacceptable within our community:
|
||||
|
||||
* Violence, threats of violence or violent language directed against another person.
|
||||
* Sexist, racist, homophobic, transphobic, ableist or otherwise discriminatory jokes and language.
|
||||
* Posting or displaying sexually explicit or violent material.
|
||||
* Posting or threatening to post other people’s personally identifying information ("doxing").
|
||||
* Personal insults, particularly those related to gender, sexual orientation, race, religion, or disability.
|
||||
* Inappropriate photography or recording.
|
||||
* Inappropriate physical contact. You should have someone’s consent before touching them.
|
||||
* Unwelcome sexual attention. This includes, sexualized comments or jokes; inappropriate touching, groping, and unwelcomed sexual advances.
|
||||
* Deliberate intimidation, stalking or following (online or in person).
|
||||
* Advocating for, or encouraging, any of the above behavior.
|
||||
* Sustained disruption of community events, including talks and presentations.
|
||||
|
||||
## 5. Consequences of Unacceptable Behavior
|
||||
|
||||
Unacceptable behavior from any community member, including sponsors and those with decision-making authority, will not be tolerated.
|
||||
|
||||
Anyone asked to stop unacceptable behavior is expected to comply immediately.
|
||||
|
||||
If a community member engages in unacceptable behavior, the community organizers may take any action they deem appropriate, up to and including a temporary ban or permanent expulsion from the community without warning (and without refund in the case of a paid event).
|
||||
|
||||
## 6. Reporting Guidelines
|
||||
|
||||
If you are subject to or witness unacceptable behavior, or have any other concerns, please notify a community organizer as soon as possible. community@parity.io.
|
||||
|
||||
Link to reporting guidelines: [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||
|
||||
Link to security policy: [SECURITY.md](../SECURITY.md)
|
||||
|
||||
Additionally, community organizers are available to help community members engage with local law enforcement or to otherwise help those experiencing unacceptable behavior feel safe. In the context of in-person events, organizers will also provide escorts as desired by the person experiencing distress.
|
||||
|
||||
## 7. Addressing Grievances
|
||||
|
||||
If you feel you have been falsely or unfairly accused of violating this Code of Conduct, you should notify Parity Technologies with a concise description of your grievance. Your grievance will be handled in accordance with our existing governing policies.
|
||||
|
||||
## 8. Scope
|
||||
|
||||
We expect all community participants (contributors, paid or otherwise; sponsors; and other guests) to abide by this Code of Conduct in all community venues–online and in-person–as well as in all one-on-one communications pertaining to community business.
|
||||
|
||||
This code of conduct and its related procedures also applies to unacceptable behavior occurring outside the scope of community activities when such behavior has the potential to adversely affect the safety and well-being of community members.
|
||||
|
||||
## 9. Contact info
|
||||
|
||||
You can contact Parity via Email: community@parity.io
|
||||
|
||||
## 10. License and attribution
|
||||
|
||||
This Code of Conduct is distributed under a [Creative Commons Attribution-ShareAlike license](http://creativecommons.org/licenses/by-sa/3.0/).
|
||||
|
||||
Portions of text derived from the [Django Code of Conduct](https://www.djangoproject.com/conduct/) and the [Geek Feminism Anti-Harassment Policy](http://geekfeminism.wikia.com/wiki/Conference_anti-harassment/Policy).
|
||||
|
||||
Retrieved on November 22, 2016 from [http://citizencodeofconduct.org/](http://citizencodeofconduct.org/)
|
||||
33
.github/CONTRIBUTING.md
vendored
33
.github/CONTRIBUTING.md
vendored
@@ -1,33 +0,0 @@
|
||||
# Contributing Guidelines
|
||||
|
||||
## Do you have a question?
|
||||
|
||||
Check out our [Basic Usage](https://github.com/paritytech/parity/wiki/Basic-Usage), [Configuration](https://github.com/paritytech/parity/wiki/Configuring-Parity), and [FAQ](https://github.com/paritytech/parity/wiki/FAQ) articles on our [wiki](https://github.com/paritytech/parity/wiki)!
|
||||
|
||||
See also frequently asked questions [tagged with `parity`](https://ethereum.stackexchange.com/questions/tagged/parity?sort=votes&pageSize=50) on Stack Exchange.
|
||||
|
||||
## Report bugs!
|
||||
|
||||
Do **not** open an issue on Github if you think your discovered bug could be a **security-relevant vulnerability**. Please, read our [security policy](../SECURITY.md) instead.
|
||||
|
||||
Otherwise, just create a [new issue](https://github.com/paritytech/parity/issues/new) in our repository and state:
|
||||
|
||||
- What's your Parity version?
|
||||
- What's your operating system and version?
|
||||
- How did you install parity?
|
||||
- Is your node fully synchronized?
|
||||
- Did you try turning it off and on again?
|
||||
|
||||
Also, try to include **steps to reproduce** the issue and expand on the **actual versus expected behavior**.
|
||||
|
||||
## Contribute!
|
||||
|
||||
If you would like to contribute to Parity, please **fork it**, fix bugs or implement features, and [propose a pull request](https://github.com/paritytech/parity/compare).
|
||||
|
||||
Please, refer to the [Coding Guide](https://github.com/paritytech/parity/wiki/Coding-guide) in our wiki for more details about hacking on Parity.
|
||||
|
||||
## License.
|
||||
|
||||
By contributing to Parity, you agree that your contributions will be licensed under the [GPLv3 License](../LICENSE).
|
||||
|
||||
Each contributor has to sign our Contributor License Agreement. The purpose of the CLA is to ensure that the guardian of a project's outputs has the necessary ownership or grants of rights over all contributions to allow them to distribute under the chosen license. You can read and sign our full Contributor License Agreement at [cla.parity.io](https://cla.parity.io) before submitting a pull request.
|
||||
13
.github/ISSUE_TEMPLATE.md
vendored
13
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,13 +0,0 @@
|
||||
_Before filing a new issue, please **provide the following information**._
|
||||
|
||||
> I'm running:
|
||||
>
|
||||
> - **Which Parity version?**: 0.0.0
|
||||
> - **Which operating system?**: Windows / MacOS / Linux
|
||||
> - **How installed?**: via installer / homebrew / binaries / from source
|
||||
> - **Are you fully synchronized?**: no / yes
|
||||
> - **Which network are you connected to?**: ethereum / ropsten / kovan / ...
|
||||
> - **Did you try to restart the node?**: no / yes
|
||||
|
||||
_Your issue description goes here below. Try to include **actual** vs. **expected behavior** and **steps to reproduce** the issue._
|
||||
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -15,18 +15,10 @@
|
||||
|
||||
# vim stuff
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# mac stuff
|
||||
.DS_Store
|
||||
|
||||
# npm stuff
|
||||
npm-debug.log
|
||||
node_modules
|
||||
|
||||
# js build artifacts
|
||||
.git-release.log
|
||||
|
||||
# gdb files
|
||||
.gdb_history
|
||||
|
||||
|
||||
512
.gitlab-ci.yml
512
.gitlab-ci.yml
@@ -1,152 +1,393 @@
|
||||
stages:
|
||||
- test
|
||||
- js-build
|
||||
- push-release
|
||||
- build
|
||||
variables:
|
||||
GIT_DEPTH: "3"
|
||||
SIMPLECOV: "true"
|
||||
RUST_BACKTRACE: "1"
|
||||
RUSTFLAGS: ""
|
||||
CARGOFLAGS: ""
|
||||
CI_SERVER_NAME: "GitLab CI"
|
||||
LIBSSL: "libssl1.0.0 (>=1.0.0)"
|
||||
cache:
|
||||
key: "$CI_BUILD_STAGE-$CI_BUILD_REF_NAME"
|
||||
paths:
|
||||
- target/
|
||||
key: "$CI_BUILD_STAGE/$CI_BUILD_REF_NAME"
|
||||
untracked: true
|
||||
linux-ubuntu:
|
||||
linux-stable:
|
||||
stage: build
|
||||
image: parity/rust:gitlab-ci
|
||||
image: ethcore/rust:stable
|
||||
only:
|
||||
- beta
|
||||
- tags
|
||||
- stable
|
||||
- triggers
|
||||
script:
|
||||
- rustup default stable
|
||||
# ARGUMENTS: 1. BUILD_PLATFORM (target for binaries) 2. PLATFORM (target for cargo) 3. ARC (architecture) 4. & 5. CC & CXX flags 6. binary identifier
|
||||
- scripts/gitlab-build.sh x86_64-unknown-linux-gnu x86_64-unknown-linux-gnu amd64 gcc g++ ubuntu
|
||||
- cargo build -j $(nproc) --release --features final $CARGOFLAGS
|
||||
- cargo build -j $(nproc) --release -p evmbin
|
||||
- cargo build -j $(nproc) --release -p ethstore
|
||||
- cargo build -j $(nproc) --release -p ethkey
|
||||
- strip target/release/parity
|
||||
- strip target/release/parity-evm
|
||||
- strip target/release/ethstore
|
||||
- strip target/release/ethkey
|
||||
- export SHA3=$(target/release/parity tools hash target/release/parity)
|
||||
- md5sum target/release/parity > parity.md5
|
||||
- sh scripts/deb-build.sh amd64
|
||||
- cp target/release/parity deb/usr/bin/parity
|
||||
- cp target/release/parity-evm deb/usr/bin/parity-evm
|
||||
- cp target/release/ethstore deb/usr/bin/ethstore
|
||||
- cp target/release/ethkey deb/usr/bin/ethkey
|
||||
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||
- dpkg-deb -b deb "parity_"$VER"_amd64.deb"
|
||||
- md5sum "parity_"$VER"_amd64.deb" > "parity_"$VER"_amd64.deb.md5"
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
||||
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/parity --body target/release/parity
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/parity.md5 --body parity.md5
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/"parity_"$VER"_amd64.deb" --body "parity_"$VER"_amd64.deb"
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/"parity_"$VER"_amd64.deb.md5" --body "parity_"$VER"_amd64.deb.md5"
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu
|
||||
tags:
|
||||
- rust
|
||||
- rust-stable
|
||||
artifacts:
|
||||
paths:
|
||||
- parity.zip
|
||||
- target/release/parity
|
||||
- target/release/parity-evm
|
||||
- target/release/ethstore
|
||||
- target/release/ethkey
|
||||
name: "stable-x86_64-unknown-linux-gnu_parity"
|
||||
linux-debian:
|
||||
linux-stable-debian:
|
||||
stage: build
|
||||
image: parity/rust-debian:gitlab-ci
|
||||
image: ethcore/rust-debian:latest
|
||||
only:
|
||||
- beta
|
||||
- tags
|
||||
- stable
|
||||
- triggers
|
||||
script:
|
||||
- export LIBSSL="libssl1.1 (>=1.1.0)"
|
||||
- scripts/gitlab-build.sh x86_64-unknown-debian-gnu x86_64-unknown-linux-gnu amd64 gcc g++ debian
|
||||
- cargo build -j $(nproc) --release --features final $CARGOFLAGS
|
||||
- cargo build -j $(nproc) --release -p evmbin
|
||||
- cargo build -j $(nproc) --release -p ethstore
|
||||
- cargo build -j $(nproc) --release -p ethkey
|
||||
- strip target/release/parity
|
||||
- strip target/release/parity-evm
|
||||
- strip target/release/ethstore
|
||||
- strip target/release/ethkey
|
||||
- export SHA3=$(target/release/parity tools hash target/release/parity)
|
||||
- md5sum target/release/parity > parity.md5
|
||||
- sh scripts/deb-build.sh amd64
|
||||
- cp target/release/parity deb/usr/bin/parity
|
||||
- cp target/release/parity-evm deb/usr/bin/parity-evm
|
||||
- cp target/release/ethstore deb/usr/bin/ethstore
|
||||
- cp target/release/ethkey deb/usr/bin/ethkey
|
||||
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||
- dpkg-deb -b deb "parity_"$VER"_amd64.deb"
|
||||
- md5sum "parity_"$VER"_amd64.deb" > "parity_"$VER"_amd64.deb.md5"
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
||||
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/x86_64-unknown-debian-gnu
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-debian-gnu/parity --body target/release/parity
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-debian-gnu/parity.md5 --body parity.md5
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-debian-gnu/"parity_"$VER"_amd64.deb" --body "parity_"$VER"_amd64.deb"
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-debian-gnu/"parity_"$VER"_amd64.deb.md5" --body "parity_"$VER"_amd64.deb.md5"
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/x86_64-unknown-debian-gnu
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/x86_64-unknown-debian-gnu
|
||||
tags:
|
||||
- rust
|
||||
- rust-debian
|
||||
artifacts:
|
||||
paths:
|
||||
- parity.zip
|
||||
- target/release/parity
|
||||
name: "stable-x86_64-unknown-debian-gnu_parity"
|
||||
linux-centos:
|
||||
linux-beta:
|
||||
stage: build
|
||||
image: parity/rust-centos:gitlab-ci
|
||||
image: ethcore/rust:beta
|
||||
only:
|
||||
- beta
|
||||
- tags
|
||||
- stable
|
||||
- triggers
|
||||
script:
|
||||
- scripts/gitlab-build.sh x86_64-unknown-centos-gnu x86_64-unknown-linux-gnu x86_64 gcc g++ centos
|
||||
- cargo build -j $(nproc) --release $CARGOFLAGS
|
||||
- strip target/release/parity
|
||||
tags:
|
||||
- rust
|
||||
- rust-beta
|
||||
artifacts:
|
||||
paths:
|
||||
- target/release/parity
|
||||
name: "beta-x86_64-unknown-linux-gnu_parity"
|
||||
allow_failure: true
|
||||
linux-nightly:
|
||||
stage: build
|
||||
image: ethcore/rust:nightly
|
||||
only:
|
||||
- beta
|
||||
- tags
|
||||
- stable
|
||||
- triggers
|
||||
script:
|
||||
- cargo build -j $(nproc) --release $CARGOFLAGS
|
||||
- strip target/release/parity
|
||||
tags:
|
||||
- rust
|
||||
- rust-nightly
|
||||
artifacts:
|
||||
paths:
|
||||
- target/release/parity
|
||||
name: "nigthly-x86_64-unknown-linux-gnu_parity"
|
||||
allow_failure: true
|
||||
linux-centos:
|
||||
stage: build
|
||||
image: ethcore/rust-centos:latest
|
||||
only:
|
||||
- beta
|
||||
- tags
|
||||
- stable
|
||||
- triggers
|
||||
script:
|
||||
- export CXX="g++"
|
||||
- export CC="gcc"
|
||||
- export PLATFORM=x86_64-unknown-centos-gnu
|
||||
- cargo build -j $(nproc) --release --features final $CARGOFLAGS
|
||||
- strip target/release/parity
|
||||
- md5sum target/release/parity > parity.md5
|
||||
- export SHA3=$(target/release/parity tools hash target/release/parity)
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
||||
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/parity --body target/release/parity
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/parity.md5 --body parity.md5
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
tags:
|
||||
- rust
|
||||
- rust-centos
|
||||
artifacts:
|
||||
paths:
|
||||
- parity.zip
|
||||
- target/release/parity
|
||||
name: "x86_64-unknown-centos-gnu_parity"
|
||||
linux-i686:
|
||||
stage: build
|
||||
image: parity/rust-i686:gitlab-ci
|
||||
image: ethcore/rust-i686:latest
|
||||
only:
|
||||
- beta
|
||||
- tags
|
||||
- stable
|
||||
- triggers
|
||||
script:
|
||||
- scripts/gitlab-build.sh i686-unknown-linux-gnu i686-unknown-linux-gnu i386 gcc g++ ubuntu
|
||||
- export HOST_CC=gcc
|
||||
- export HOST_CXX=g++
|
||||
- export COMMIT=$(git rev-parse HEAD)
|
||||
- export PLATFORM=i686-unknown-linux-gnu
|
||||
- cargo build -j $(nproc) --target i686-unknown-linux-gnu --features final --release $CARGOFLAGS
|
||||
- strip target/$PLATFORM/release/parity
|
||||
- md5sum target/$PLATFORM/release/parity > parity.md5
|
||||
- export SHA3=$(target/$PLATFORM/release/parity tools hash target/$PLATFORM/release/parity)
|
||||
- sh scripts/deb-build.sh i386
|
||||
- cp target/$PLATFORM/release/parity deb/usr/bin/parity
|
||||
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||
- dpkg-deb -b deb "parity_"$VER"_i386.deb"
|
||||
- md5sum "parity_"$VER"_i386.deb" > "parity_"$VER"_i386.deb.md5"
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
||||
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity --body target/$PLATFORM/release/parity
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_i386.deb" --body "parity_"$VER"_i386.deb"
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_i386.deb.md5" --body "parity_"$VER"_i386.deb.md5"
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
tags:
|
||||
- rust
|
||||
- rust-i686
|
||||
artifacts:
|
||||
paths:
|
||||
- parity.zip
|
||||
- target/i686-unknown-linux-gnu/release/parity
|
||||
name: "i686-unknown-linux-gnu"
|
||||
allow_failure: true
|
||||
linux-armv7:
|
||||
stage: build
|
||||
image: parity/rust-armv7:gitlab-ci
|
||||
image: ethcore/rust-armv7:latest
|
||||
only:
|
||||
- beta
|
||||
- tags
|
||||
- stable
|
||||
- triggers
|
||||
script:
|
||||
- scripts/gitlab-build.sh armv7-unknown-linux-gnueabihf armv7-unknown-linux-gnueabihf armhf arm-linux-gnueabihf-gcc arm-linux-gnueabihf-g++ ubuntu
|
||||
- export CC=arm-linux-gnueabihf-gcc
|
||||
- export CXX=arm-linux-gnueabihf-g++
|
||||
- export HOST_CC=gcc
|
||||
- export HOST_CXX=g++
|
||||
- export PLATFORM=armv7-unknown-linux-gnueabihf
|
||||
- rm -rf .cargo
|
||||
- mkdir -p .cargo
|
||||
- echo "[target.$PLATFORM]" >> .cargo/config
|
||||
- echo "linker= \"arm-linux-gnueabihf-gcc\"" >> .cargo/config
|
||||
- cat .cargo/config
|
||||
- cargo build -j $(nproc) --target $PLATFORM --features final --release $CARGOFLAGS
|
||||
- arm-linux-gnueabihf-strip target/$PLATFORM/release/parity
|
||||
- export SHA3=$(rhash --sha3-256 ~/Core/parity/target/release/parity -p %h)
|
||||
- md5sum target/$PLATFORM/release/parity > parity.md5
|
||||
- sh scripts/deb-build.sh armhf
|
||||
- cp target/$PLATFORM/release/parity deb/usr/bin/parity
|
||||
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||
- dpkg-deb -b deb "parity_"$VER"_armhf.deb"
|
||||
- md5sum "parity_"$VER"_armhf.deb" > "parity_"$VER"_armhf.deb.md5"
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
||||
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity --body target/$PLATFORM/release/parity
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_armhf.deb" --body "parity_"$VER"_armhf.deb"
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_armhf.deb.md5" --body "parity_"$VER"_armhf.deb.md5"
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
tags:
|
||||
- rust
|
||||
- rust-arm
|
||||
artifacts:
|
||||
paths:
|
||||
- parity.zip
|
||||
- target/armv7-unknown-linux-gnueabihf/release/parity
|
||||
name: "armv7_unknown_linux_gnueabihf_parity"
|
||||
allow_failure: true
|
||||
linux-arm:
|
||||
stage: build
|
||||
image: parity/rust-arm:gitlab-ci
|
||||
image: ethcore/rust-arm:latest
|
||||
only:
|
||||
- beta
|
||||
- tags
|
||||
- stable
|
||||
- triggers
|
||||
script:
|
||||
- scripts/gitlab-build.sh arm-unknown-linux-gnueabihf arm-unknown-linux-gnueabihf armhf arm-linux-gnueabihf-gcc arm-linux-gnueabihf-g++ ubuntu
|
||||
- export CC=arm-linux-gnueabihf-gcc
|
||||
- export CXX=arm-linux-gnueabihf-g++
|
||||
- export HOST_CC=gcc
|
||||
- export HOST_CXX=g++
|
||||
- export PLATFORM=arm-unknown-linux-gnueabihf
|
||||
- rm -rf .cargo
|
||||
- mkdir -p .cargo
|
||||
- echo "[target.$PLATFORM]" >> .cargo/config
|
||||
- echo "linker= \"arm-linux-gnueabihf-gcc\"" >> .cargo/config
|
||||
- cat .cargo/config
|
||||
- cargo build -j $(nproc) --target $PLATFORM --features final --release $CARGOFLAGS
|
||||
- arm-linux-gnueabihf-strip target/$PLATFORM/release/parity
|
||||
- export SHA3=$(rhash --sha3-256 ~/Core/parity/target/release/parity -p %h)
|
||||
- md5sum target/$PLATFORM/release/parity > parity.md5
|
||||
- sh scripts/deb-build.sh armhf
|
||||
- cp target/$PLATFORM/release/parity deb/usr/bin/parity
|
||||
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||
- dpkg-deb -b deb "parity_"$VER"_armhf.deb"
|
||||
- md5sum "parity_"$VER"_armhf.deb" > "parity_"$VER"_armhf.deb.md5"
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
||||
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity --body target/$PLATFORM/release/parity
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_armhf.deb" --body "parity_"$VER"_armhf.deb"
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_armhf.deb.md5" --body "parity_"$VER"_armhf.deb.md5"
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
tags:
|
||||
- rust
|
||||
- rust-arm
|
||||
artifacts:
|
||||
paths:
|
||||
- parity.zip
|
||||
- target/arm-unknown-linux-gnueabihf/release/parity
|
||||
name: "arm-unknown-linux-gnueabihf_parity"
|
||||
allow_failure: true
|
||||
linux-armv6:
|
||||
stage: build
|
||||
image: ethcore/rust-armv6:latest
|
||||
only:
|
||||
# - beta
|
||||
# - tags
|
||||
# - stable
|
||||
- triggers
|
||||
script:
|
||||
- export CC=arm-linux-gnueabi-gcc
|
||||
- export CXX=arm-linux-gnueabi-g++
|
||||
- export HOST_CC=gcc
|
||||
- export HOST_CXX=g++
|
||||
- export PLATFORM=arm-unknown-linux-gnueabi
|
||||
- rm -rf .cargo
|
||||
- mkdir -p .cargo
|
||||
- echo "[target.$PLATFORM]" >> .cargo/config
|
||||
- echo "linker= \"arm-linux-gnueabi-gcc\"" >> .cargo/config
|
||||
- cat .cargo/config
|
||||
- cargo build -j $(nproc) --target $PLATFORM --features final --release $CARGOFLAGS
|
||||
- arm-linux-gnueabi-strip target/$PLATFORM/release/parity
|
||||
- export SHA3=$(rhash --sha3-256 ~/Core/parity/target/release/parity -p %h)
|
||||
- md5sum target/$PLATFORM/release/parity > parity.md5
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
||||
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity --body target/$PLATFORM/release/parity
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
tags:
|
||||
- rust
|
||||
- rust-arm
|
||||
artifacts:
|
||||
paths:
|
||||
- target/arm-unknown-linux-gnueabi/release/parity
|
||||
name: "arm-unknown-linux-gnueabi_parity"
|
||||
allow_failure: true
|
||||
linux-aarch64:
|
||||
stage: build
|
||||
image: parity/rust-arm64:gitlab-ci
|
||||
image: ethcore/rust-aarch64:latest
|
||||
only:
|
||||
- beta
|
||||
- tags
|
||||
- stable
|
||||
- triggers
|
||||
script:
|
||||
- scripts/gitlab-build.sh aarch64-unknown-linux-gnu aarch64-unknown-linux-gnu arm64 aarch64-linux-gnu-gcc aarch64-linux-gnu-g++ ubuntu
|
||||
- export CC=aarch64-linux-gnu-gcc
|
||||
- export CXX=aarch64-linux-gnu-g++
|
||||
- export HOST_CC=gcc
|
||||
- export HOST_CXX=g++
|
||||
- export PLATFORM=aarch64-unknown-linux-gnu
|
||||
- rm -rf .cargo
|
||||
- mkdir -p .cargo
|
||||
- echo "[target.$PLATFORM]" >> .cargo/config
|
||||
- echo "linker= \"aarch64-linux-gnu-gcc\"" >> .cargo/config
|
||||
- cat .cargo/config
|
||||
- cargo build -j $(nproc) --target $PLATFORM --features final --release $CARGOFLAGS
|
||||
- aarch64-linux-gnu-strip target/$PLATFORM/release/parity
|
||||
- export SHA3=$(rhash --sha3-256 ~/Core/parity/target/release/parity -p %h)
|
||||
- md5sum target/$PLATFORM/release/parity > parity.md5
|
||||
- sh scripts/deb-build.sh arm64
|
||||
- cp target/$PLATFORM/release/parity deb/usr/bin/parity
|
||||
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||
- dpkg-deb -b deb "parity_"$VER"_arm64.deb"
|
||||
- md5sum "parity_"$VER"_arm64.deb" > "parity_"$VER"_arm64.deb.md5"
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
||||
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_arm64.deb" --body "parity_"$VER"_arm64.deb"
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_arm64.deb.md5" --body "parity_"$VER"_arm64.deb.md5"
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
tags:
|
||||
- rust
|
||||
- rust-arm
|
||||
artifacts:
|
||||
paths:
|
||||
- parity.zip
|
||||
- target/aarch64-unknown-linux-gnu/release/parity
|
||||
name: "aarch64-unknown-linux-gnu_parity"
|
||||
linux-snap:
|
||||
stage: build
|
||||
image: parity/snapcraft:gitlab-ci
|
||||
only:
|
||||
- stable
|
||||
- beta
|
||||
- tags
|
||||
- triggers
|
||||
script:
|
||||
- scripts/gitlab-build.sh x86_64-unknown-snap-gnu x86_64-unknown-linux-gnu amd64 gcc g++ snap
|
||||
tags:
|
||||
- rust-stable
|
||||
artifacts:
|
||||
paths:
|
||||
- parity.zip
|
||||
name: "stable-x86_64-unknown-snap-gnu_parity"
|
||||
allow_failure: true
|
||||
darwin:
|
||||
stage: build
|
||||
only:
|
||||
@@ -154,17 +395,41 @@ darwin:
|
||||
- tags
|
||||
- stable
|
||||
- triggers
|
||||
script:
|
||||
- scripts/gitlab-build.sh x86_64-apple-darwin x86_64-apple-darwin macos gcc g++ macos
|
||||
script: |
|
||||
export COMMIT=$(git rev-parse HEAD)
|
||||
export PLATFORM=x86_64-apple-darwin
|
||||
cargo build -j 8 --features final --release #$CARGOFLAGS
|
||||
cargo build -j 8 --features final --release -p ethstore #$CARGOFLAGS
|
||||
rm -rf parity.md5
|
||||
md5sum target/release/parity > parity.md5
|
||||
export SHA3=$(target/release/parity tools hash target/release/parity)
|
||||
cd mac
|
||||
xcodebuild -configuration Release
|
||||
cd ..
|
||||
packagesbuild -v mac/Parity.pkgproj
|
||||
productsign --sign 'Developer ID Installer: PARITY TECHNOLOGIES LIMITED (P2PX3JU8FT)' target/release/Parity\ Ethereum.pkg target/release/Parity\ Ethereum-signed.pkg
|
||||
export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||
mv target/release/Parity\ Ethereum-signed.pkg "parity-"$VER"-osx-installer.pkg"
|
||||
md5sum "parity-"$VER"-osx-installer.pkg" >> "parity-"$VER"-osx-installer.pkg.md5"
|
||||
aws configure set aws_access_key_id $s3_key
|
||||
aws configure set aws_secret_access_key $s3_secret
|
||||
if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
||||
aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity --body target/release/parity
|
||||
aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5
|
||||
aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity-"$VER"-osx-installer.pkg" --body "parity-"$VER"-osx-installer.pkg"
|
||||
aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity-"$VER"-osx-installer.pkg.md5" --body "parity-"$VER"-osx-installer.pkg.md5"
|
||||
curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
tags:
|
||||
- osx
|
||||
artifacts:
|
||||
paths:
|
||||
- parity.zip
|
||||
- target/release/parity
|
||||
name: "x86_64-apple-darwin_parity"
|
||||
windows:
|
||||
cache:
|
||||
key: "%CI_BUILD_STAGE%-%CI_BUILD_REF_NAME%"
|
||||
key: "%CI_BUILD_STAGE%/%CI_BUILD_REF_NAME%"
|
||||
untracked: true
|
||||
stage: build
|
||||
only:
|
||||
@@ -173,12 +438,56 @@ windows:
|
||||
- stable
|
||||
- triggers
|
||||
script:
|
||||
- sh scripts/gitlab-build.sh x86_64-pc-windows-msvc x86_64-pc-windows-msvc installer "" "" windows
|
||||
- set PLATFORM=x86_64-pc-windows-msvc
|
||||
- set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include;C:\vs2015\VC\include;C:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\ucrt
|
||||
- set LIB=C:\vs2015\VC\lib;C:\Program Files (x86)\Windows Kits\10\Lib\10.0.10240.0\ucrt\x64
|
||||
- set RUST_BACKTRACE=1
|
||||
- set RUSTFLAGS=%RUSTFLAGS%
|
||||
- rustup default stable-x86_64-pc-windows-msvc
|
||||
- cargo build --features final --release #%CARGOFLAGS%
|
||||
- signtool sign /f %keyfile% /p %certpass% target\release\parity.exe
|
||||
- target\release\parity.exe tools hash target\release\parity.exe > parity.sha3
|
||||
- set /P SHA3=<parity.sha3
|
||||
- curl -sL --url "https://github.com/ethcore/win-build/raw/master/SimpleFC.dll" -o nsis\SimpleFC.dll
|
||||
- curl -sL --url "https://github.com/ethcore/win-build/raw/master/vc_redist.x64.exe" -o nsis\vc_redist.x64.exe
|
||||
- msbuild windows\ptray\ptray.vcxproj /p:Platform=x64 /p:Configuration=Release
|
||||
- signtool sign /f %keyfile% /p %certpass% windows\ptray\x64\release\ptray.exe
|
||||
- cd nsis
|
||||
- makensis.exe installer.nsi
|
||||
- copy installer.exe InstallParity.exe
|
||||
- signtool sign /f %keyfile% /p %certpass% InstallParity.exe
|
||||
- md5sums InstallParity.exe > InstallParity.exe.md5
|
||||
- zip win-installer.zip InstallParity.exe InstallParity.exe.md5
|
||||
- md5sums win-installer.zip > win-installer.zip.md5
|
||||
- cd ..\target\release\
|
||||
- md5sums parity.exe > parity.exe.md5
|
||||
- zip parity.zip parity.exe parity.md5
|
||||
- md5sums parity.zip > parity.zip.md5
|
||||
- cd ..\..
|
||||
- aws configure set aws_access_key_id %s3_key%
|
||||
- aws configure set aws_secret_access_key %s3_secret%
|
||||
- echo %CI_BUILD_REF_NAME%
|
||||
- echo %CI_BUILD_REF_NAME% | findstr /R "master" >nul 2>&1 && set S3_BUCKET=builds-parity-published || set S3_BUCKET=builds-parity
|
||||
- echo %CI_BUILD_REF_NAME% | findstr /R "beta" >nul 2>&1 && set S3_BUCKET=builds-parity-published || set S3_BUCKET=builds-parity
|
||||
- echo %CI_BUILD_REF_NAME% | findstr /R "stable" >nul 2>&1 && set S3_BUCKET=builds-parity-published || set S3_BUCKET=builds-parity
|
||||
- echo %S3_BUCKET%
|
||||
- aws s3 rm --recursive s3://%S3_BUCKET%/%CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc
|
||||
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity.exe --body target\release\parity.exe
|
||||
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity.exe.md5 --body target\release\parity.exe.md5
|
||||
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity.zip --body target\release\parity.zip
|
||||
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity.zip.md5 --body target\release\parity.zip.md5
|
||||
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/InstallParity.exe --body nsis\InstallParity.exe
|
||||
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/InstallParity.exe.md5 --body nsis\InstallParity.exe.md5
|
||||
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/win-installer.zip --body nsis\win-installer.zip
|
||||
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/win-installer.zip.md5 --body nsis\win-installer.zip.md5
|
||||
- curl --data "commit=%CI_BUILD_REF%&sha3=%SHA3%&filename=parity.exe&secret=%RELEASES_SECRET%" http://update.parity.io:1337/push-build/%CI_BUILD_REF_NAME%/%PLATFORM%
|
||||
- curl --data "commit=%CI_BUILD_REF%&sha3=%SHA3%&filename=parity.exe&secret=%RELEASES_SECRET%" http://update.parity.io:1338/push-build/%CI_BUILD_REF_NAME%/%PLATFORM%
|
||||
tags:
|
||||
- rust-windows
|
||||
artifacts:
|
||||
paths:
|
||||
- parity.zip
|
||||
- target/release/parity.exe
|
||||
- nsis/InstallParity.exe
|
||||
name: "x86_64-pc-windows-msvc_parity"
|
||||
docker-build:
|
||||
stage: build
|
||||
@@ -188,59 +497,114 @@ docker-build:
|
||||
before_script:
|
||||
- docker info
|
||||
script:
|
||||
- if [ "$CI_BUILD_REF_NAME" == "master" ]; then DOCKER_TAG="latest"; else DOCKER_TAG=$CI_BUILD_REF_NAME; fi
|
||||
- echo "Tag:" $DOCKER_TAG
|
||||
- docker login -u $Docker_Hub_User_Parity -p $Docker_Hub_Pass_Parity
|
||||
- scripts/docker-build.sh $DOCKER_TAG
|
||||
- docker logout
|
||||
- if [ "$CI_BUILD_REF_NAME" == "beta-release" ]; then DOCKER_TAG="latest"; else DOCKER_TAG=$CI_BUILD_REF_NAME; fi
|
||||
- docker login -u $Docker_Hub_User -p $Docker_Hub_Pass
|
||||
- sh scripts/docker-build.sh $DOCKER_TAG
|
||||
tags:
|
||||
- docker
|
||||
test-coverage:
|
||||
test-darwin:
|
||||
stage: test
|
||||
only:
|
||||
- master
|
||||
- triggers
|
||||
before_script:
|
||||
- git submodule update --init --recursive
|
||||
- export RUST_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep -v -e ^js -e ^\\. -e ^LICENSE -e ^README.md -e ^appveyor.yml -e ^test.sh -e ^windows/ -e ^scripts/ -e^mac/ -e ^nsis/ | wc -l)
|
||||
script:
|
||||
- scripts/gitlab-test.sh test-coverage
|
||||
- export RUST_BACKTRACE=1
|
||||
- if [ $RUST_FILES_MODIFIED -eq 0 ]; then echo "Skipping Rust tests since no Rust files modified."; else ./test.sh $CARGOFLAGS; fi
|
||||
tags:
|
||||
- kcov
|
||||
- osx
|
||||
allow_failure: true
|
||||
test-windows:
|
||||
stage: test
|
||||
only:
|
||||
- triggers
|
||||
before_script:
|
||||
- git submodule update --init --recursive
|
||||
script:
|
||||
- set RUST_BACKTRACE=1
|
||||
- echo cargo test --features json-tests -p rlp -p ethash -p ethcore -p ethcore-bigint -p ethcore-dapps -p ethcore-rpc -p ethcore-signer -p ethcore-util -p ethcore-network -p ethcore-io -p ethkey -p ethstore -p ethsync -p ethcore-ipc -p ethcore-ipc-tests -p ethcore-ipc-nano -p parity %CARGOFLAGS% --verbose --release
|
||||
tags:
|
||||
- rust-windows
|
||||
allow_failure: true
|
||||
test-rust-stable:
|
||||
stage: test
|
||||
image: parity/rust:gitlab-ci
|
||||
image: ethcore/rust:stable
|
||||
before_script:
|
||||
- git submodule update --init --recursive
|
||||
- export RUST_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep -v -e ^js -e ^\\. -e ^LICENSE -e ^README.md -e ^appveyor.yml -e ^test.sh -e ^windows/ -e ^scripts/ -e^mac/ -e ^nsis/ | wc -l)
|
||||
script:
|
||||
- scripts/gitlab-test.sh stable
|
||||
- export RUST_BACKTRACE=1
|
||||
- if [ $RUST_FILES_MODIFIED -eq 0 ]; then echo "Skipping Rust tests since no Rust files modified."; else ./test.sh $CARGOFLAGS&&./scripts/cov.sh "$KCOV_CMD"; fi
|
||||
tags:
|
||||
- rust
|
||||
- rust-stable
|
||||
js-test:
|
||||
stage: test
|
||||
image: ethcore/rust:stable
|
||||
before_script:
|
||||
- git submodule update --init --recursive
|
||||
- export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep ^js/ | wc -l)
|
||||
- if [ $JS_FILES_MODIFIED -eq 0 ]; then echo "Skipping JS deps install since no JS files modified."; else ./js/scripts/install-deps.sh;fi
|
||||
script:
|
||||
- if [ $JS_FILES_MODIFIED -eq 0 ]; then echo "Skipping JS lint since no JS files modified."; else ./js/scripts/lint.sh && ./js/scripts/test.sh && ./js/scripts/build.sh; fi
|
||||
tags:
|
||||
- rust
|
||||
- rust-stable
|
||||
test-rust-beta:
|
||||
stage: test
|
||||
only:
|
||||
- triggers
|
||||
- master
|
||||
image: parity/rust:gitlab-ci
|
||||
image: ethcore/rust:beta
|
||||
before_script:
|
||||
- git submodule update --init --recursive
|
||||
- export RUST_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep -v -e ^js -e ^\\. -e ^LICENSE -e ^README.md -e ^appveyor.yml -e ^test.sh -e ^windows/ -e ^scripts/ -e^mac/ -e ^nsis/ | wc -l)
|
||||
script:
|
||||
- scripts/gitlab-test.sh beta
|
||||
- export RUST_BACKTRACE=1
|
||||
- if [ $RUST_FILES_MODIFIED -eq 0 ]; then echo "Skipping Rust tests since no Rust files modified."; else ./test.sh $CARGOFLAGS; fi
|
||||
tags:
|
||||
- rust
|
||||
- rust-beta
|
||||
allow_failure: true
|
||||
test-rust-nightly:
|
||||
stage: test
|
||||
only:
|
||||
- triggers
|
||||
- master
|
||||
image: parity/rust:gitlab-ci
|
||||
image: ethcore/rust:nightly
|
||||
before_script:
|
||||
- git submodule update --init --recursive
|
||||
- export RUST_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep -v -e ^js -e ^\\. -e ^LICENSE -e ^README.md -e ^appveyor.yml -e ^test.sh -e ^windows/ -e ^scripts/ -e^mac/ -e ^nsis/ | wc -l)
|
||||
script:
|
||||
- scripts/gitlab-test.sh nightly
|
||||
- export RUST_BACKTRACE=1
|
||||
- if [ $RUST_FILES_MODIFIED -eq 0 ]; then echo "Skipping Rust tests since no Rust files modified."; else ./test.sh $CARGOFLAGS; fi
|
||||
tags:
|
||||
- rust
|
||||
- rust-nightly
|
||||
allow_failure: true
|
||||
js-release:
|
||||
stage: js-build
|
||||
only:
|
||||
- master
|
||||
- beta
|
||||
- stable
|
||||
- tags
|
||||
image: ethcore/rust:stable
|
||||
before_script:
|
||||
- export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep ^js/ | wc -l)
|
||||
- echo $JS_FILES_MODIFIED
|
||||
- if [ $JS_FILES_MODIFIED -eq 0 ]; then echo "Skipping JS deps install since no JS files modified."; else ./js/scripts/install-deps.sh;fi
|
||||
script:
|
||||
- echo $JS_FILES_MODIFIED
|
||||
- if [ $JS_FILES_MODIFIED -eq 0 ]; then echo "Skipping JS rebuild since no JS files modified."; else ./js/scripts/build.sh && ./js/scripts/release.sh; fi
|
||||
tags:
|
||||
- javascript
|
||||
push-release:
|
||||
stage: push-release
|
||||
only:
|
||||
- tags
|
||||
- triggers
|
||||
image: parity/rust:gitlab-ci
|
||||
image: ethcore/rust:stable
|
||||
script:
|
||||
- scripts/gitlab-push-release.sh
|
||||
- curl --data "secret=$RELEASES_SECRET" http://update.parity.io:1337/push-release/$CI_BUILD_REF_NAME/$CI_BUILD_REF
|
||||
- curl --data "secret=$RELEASES_SECRET" http://update.parity.io:1338/push-release/$CI_BUILD_REF_NAME/$CI_BUILD_REF
|
||||
tags:
|
||||
- curl
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -2,6 +2,3 @@
|
||||
path = ethcore/res/ethereum/tests
|
||||
url = https://github.com/ethereum/tests.git
|
||||
branch = develop
|
||||
[submodule "ethcore/res/wasm-tests"]
|
||||
path = ethcore/res/wasm-tests
|
||||
url = https://github.com/paritytech/wasm-tests
|
||||
|
||||
279
CHANGELOG.md
279
CHANGELOG.md
@@ -1,279 +0,0 @@
|
||||
## Parity [v1.10.2](https://github.com/paritytech/parity/releases/tag/v1.10.2) (2018-04-24)
|
||||
|
||||
Parity 1.10.2 is a bug-fix release to improve performance and stability.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Update Parity beta to 1.10.2 + Backports ([#8455](https://github.com/paritytech/parity/pull/8455))
|
||||
- Update Parity beta to 1.10.2
|
||||
- Allow 32-bit pipelines to fail ([#8454](https://github.com/paritytech/parity/pull/8454))
|
||||
- Disable 32-bit targets for Gitlab
|
||||
- Rename Linux pipelines
|
||||
- Update wasmi ([#8452](https://github.com/paritytech/parity/pull/8452))
|
||||
- Fix Cargo.lock
|
||||
- Backports ([#8450](https://github.com/paritytech/parity/pull/8450))
|
||||
- Use forked app_dirs crate for reverted Windows dir behavior ([#8438](https://github.com/paritytech/parity/pull/8438))
|
||||
- Remove unused app_dirs dependency in CLI
|
||||
- Use forked app_dirs crate for reverted Windows dir behavior
|
||||
- Remove Tendermint extra_info due to seal inconsistencies ([#8367](https://github.com/paritytech/parity/pull/8367))
|
||||
- Handle queue import errors a bit more gracefully ([#8385](https://github.com/paritytech/parity/pull/8385))
|
||||
- Improve VM executor stack size estimation rules ([#8439](https://github.com/paritytech/parity/pull/8439))
|
||||
- Improve VM executor stack size estimation rules
|
||||
- Typo: docs add "(Debug build)" comment
|
||||
- Fix an off by one typo and set minimal stack size
|
||||
- Use saturating_sub to avoid potential overflow
|
||||
|
||||
## Parity [v1.10.1](https://github.com/paritytech/parity/releases/tag/v1.10.1) (2018-04-17)
|
||||
|
||||
Parity 1.10.1 is a bug-fix release to improve performance and stability. Among other changes, you can now use `--warp-barrier [BLOCK]` to specify a minimum block number to `--warp` to. This is useful in cases where clients restore to outdated snapshots far behind the latest chain head.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Bump beta to 1.10.1 ([#8350](https://github.com/paritytech/parity/pull/8350))
|
||||
- Bump beta to 1.10.1
|
||||
- Unflag critical release
|
||||
- Backports ([#8346](https://github.com/paritytech/parity/pull/8346))
|
||||
- Warp-only sync with warp-barrier [blocknumber] flag. ([#8228](https://github.com/paritytech/parity/pull/8228))
|
||||
- Warp-only sync with warp-after [blocknumber] flag.
|
||||
- Fix tests.
|
||||
- Fix configuration tests.
|
||||
- Rename to warp barrier.
|
||||
- Allow unsafe js eval on Parity Wallet. ([#8204](https://github.com/paritytech/parity/pull/8204))
|
||||
- Update musicoin spec in line with gmc v2.6.2 ([#8242](https://github.com/paritytech/parity/pull/8242))
|
||||
- Supress TemporaryInvalid verification failures. ([#8256](https://github.com/paritytech/parity/pull/8256))
|
||||
- Include suicided accounts in state diff ([#8297](https://github.com/paritytech/parity/pull/8297))
|
||||
- Include suicided accounts in state diff
|
||||
- Shorten form match -> if let
|
||||
- Test suicide trace diff in State
|
||||
- Replace_home for password_files, reserved_peers and log_file ([#8324](https://github.com/paritytech/parity/pull/8324))
|
||||
- Replace_home for password_files, reserved_peers and log_file
|
||||
- Typo: arg_log_file is Option
|
||||
- Enable UI by default, but only display info page.
|
||||
- Fix test.
|
||||
- Fix naming and remove old todo.
|
||||
- Change "wallet" with "browser UI"
|
||||
- Change name Wallet -> UI ([#8164](https://github.com/paritytech/parity/pull/8164)) ([#8205](https://github.com/paritytech/parity/pull/8205))
|
||||
- Change name Wallet -> UI
|
||||
- Make warning bold
|
||||
- Backport [#8099](https://github.com/paritytech/parity/pull/8099) ([#8132](https://github.com/paritytech/parity/pull/8132))
|
||||
- WASM libs ([#8220](https://github.com/paritytech/parity/pull/8220))
|
||||
- Bump wasm libs ([#8171](https://github.com/paritytech/parity/pull/8171))
|
||||
- Bump wasmi version ([#8209](https://github.com/paritytech/parity/pull/8209))
|
||||
- Update hyper to 0.11.24 ([#8203](https://github.com/paritytech/parity/pull/8203))
|
||||
- Updated jsonrpc to include latest backports (beta) ([#8181](https://github.com/paritytech/parity/pull/8181))
|
||||
- Updated jsonrpc to include latest backports
|
||||
- Update dependencies.
|
||||
|
||||
## Parity [v1.10.0](https://github.com/paritytech/parity/releases/tag/v1.10.0) (2018-03-22)
|
||||
|
||||
This is the Parity 1.10.0-beta release! Cool!
|
||||
|
||||
### Disabling the Parity Wallet
|
||||
|
||||
The **Parity Wallet (a.k.a. "UI") is now disabled by default**. We are preparing to split the wallet from the core client.
|
||||
|
||||
To reactivate the parity wallet, you have to run Parity either with `parity --force-ui` (not recommended) or `parity ui` (deprecated) from the command line. Or, if you feel super fancy and want to test our pre-releases of the stand-alone electron wallet, head over to the [Parity-JS repositories and check the releases](https://github.com/Parity-JS/shell/releases).
|
||||
|
||||
Further reading:
|
||||
|
||||
- [Docs: Parity Wallet](https://wiki.parity.io/Parity-Wallet)
|
||||
- [Docs: How to customize Parity UI?](https://wiki.parity.io/FAQ-Customize-Parity-UI.html)
|
||||
- [Github: Parity-JS](https://github.com/parity-js)
|
||||
|
||||
### Introducing the Wasm VM
|
||||
|
||||
We are excited to announce support for **Wasm Smart Contracts on Kovan network**. The hard-fork to activate the Wasm-VM will take place on block `6_600_000`.
|
||||
|
||||
To enable Wasm contracts on your custom network, just schedule a `wasmActivationTransition` at your favorite block number (e.g., `42`, `666`, or `0xbada55`). To hack your first Wasm smart contracts in Rust, have a look at the [Parity Wasm Tutorials](https://github.com/paritytech/pwasm-tutorial).
|
||||
|
||||
Further reading:
|
||||
|
||||
- [Docs: WebAssembly (wasm)](https://wiki.parity.io/WebAssembly-Home)
|
||||
- [Docs: Wasm VM Design](https://wiki.parity.io/WebAssembly-Design)
|
||||
- [Docs: Wasm tutorials and examples](https://wiki.parity.io/WebAssembly-Links)
|
||||
|
||||
### Empty step messages in PoA
|
||||
|
||||
To **reduce blockchain bloat, proof-of-authority networks can now enable _empty step messages_ which replace empty blocks**. Each step message will be signed and broadcasted by the issuing authorities, and included and rewarded in the next non-empty block.
|
||||
|
||||
To enable empty step messages, set the `emptyStepsTransition` to your favorite block number. You can also specify a maximum number of empty steps with `maximumEmptySteps` in your chain spec.
|
||||
|
||||
### Other noteworthy changes
|
||||
|
||||
We removed the old database migrations from 2016. In case you upgrade Parity from a really, really old version, you will have to reset your database manually first with `parity <options> db kill`.
|
||||
|
||||
We fixed DELEGATECALL `from` and `to` fields, see [#7166](https://github.com/paritytech/parity/issues/7166).
|
||||
|
||||
We reduced the default USD per transaction value to 0.0001. Thanks, @MysticRyuujin!
|
||||
|
||||
The Musicoin chain is now enabled with Byzantium features starting at block `2_222_222`.
|
||||
|
||||
### Overview of all changes included
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Re-enable signer, even with no UI. ([#8167](https://github.com/paritytech/parity/pull/8167)) ([#8168](https://github.com/paritytech/parity/pull/8168))
|
||||
- Re-enable signer, even with no UI.
|
||||
- Fix message.
|
||||
- Beta Backports ([#8136](https://github.com/paritytech/parity/pull/8136))
|
||||
- Support parity protocol. ([#8035](https://github.com/paritytech/parity/pull/8035))
|
||||
- updater: apply exponential backoff after download failure ([#8059](https://github.com/paritytech/parity/pull/8059))
|
||||
- updater: apply exponential backoff after download failure
|
||||
- updater: reset backoff on new release
|
||||
- Max code size on Kovan ([#8067](https://github.com/paritytech/parity/pull/8067))
|
||||
- Enable code size limit on kovan
|
||||
- Fix formatting.
|
||||
- Limit incoming connections. ([#8060](https://github.com/paritytech/parity/pull/8060))
|
||||
- Limit ingress connections
|
||||
- Optimized handshakes logging
|
||||
- WASM libraries bump ([#7970](https://github.com/paritytech/parity/pull/7970))
|
||||
- update wasmi, parity-wasm, wasm-utils to latest version
|
||||
- Update to new wasmi & error handling
|
||||
- also utilize new stack limiter
|
||||
- fix typo
|
||||
- replace dependency url
|
||||
- Cargo.lock update
|
||||
- add some dos protection ([#8084](https://github.com/paritytech/parity/pull/8084))
|
||||
- revert removing blooms ([#8066](https://github.com/paritytech/parity/pull/8066))
|
||||
- Revert "fix traces, removed bloomchain crate, closes [#7228](https://github.com/paritytech/parity/issues/7228), closes [#7167](https://github.com/paritytech/parity/issues/7167)"
|
||||
- Revert "fixed broken logs ([#7934](https://github.com/paritytech/parity/pull/7934))"
|
||||
- fixed broken logs
|
||||
- bring back old lock order
|
||||
- remove migration v13
|
||||
- revert CURRENT_VERSION to 12 in migration.rs
|
||||
- more dos protection ([#8104](https://github.com/paritytech/parity/pull/8104))
|
||||
- Const time comparison ([#8113](https://github.com/paritytech/parity/pull/8113))
|
||||
- Use `subtle::slices_equal` for constant time comparison.
|
||||
- Also update the existing version of subtle in `ethcrypto` from 0.1 to 0.5
|
||||
- Test specifically for InvalidPassword error.
|
||||
- fix trace filter returning returning unrelated reward calls, closes #8070 ([#8098](https://github.com/paritytech/parity/pull/8098))
|
||||
- network: init discovery using healthy nodes ([#8061](https://github.com/paritytech/parity/pull/8061))
|
||||
- network: init discovery using healthy nodes
|
||||
- network: fix style grumble
|
||||
- network: fix typo
|
||||
- Postpone Kovan hard fork ([#8137](https://github.com/paritytech/parity/pull/8137))
|
||||
- ethcore: postpone Kovan hard fork
|
||||
- util: update version fork metadata
|
||||
- Disable UI by default. ([#8105](https://github.com/paritytech/parity/pull/8105))
|
||||
- dapps: update parity-ui dependencies ([#8160](https://github.com/paritytech/parity/pull/8160))
|
||||
- Probe changes one step deeper ([#8134](https://github.com/paritytech/parity/pull/8134)) ([#8135](https://github.com/paritytech/parity/pull/8135))
|
||||
- Beta backports ([#8053](https://github.com/paritytech/parity/pull/8053))
|
||||
- CI: Fix cargo cache ([#7968](https://github.com/paritytech/parity/pull/7968))
|
||||
- Fix cache
|
||||
- Only clean locked cargo cache on windows
|
||||
- fixed ethstore sign ([#8026](https://github.com/paritytech/parity/pull/8026))
|
||||
- fixed parsing ethash seals and verify_block_undordered ([#8031](https://github.com/paritytech/parity/pull/8031))
|
||||
- fix for verify_block_basic crashing on invalid transaction rlp ([#8032](https://github.com/paritytech/parity/pull/8032))
|
||||
- fix cache & snapcraft CI build ([#8052](https://github.com/paritytech/parity/pull/8052))
|
||||
- Add MCIP-6 Byzyantium transition to Musicoin spec ([#7841](https://github.com/paritytech/parity/pull/7841))
|
||||
- Add test chain spec for musicoin byzantium testnet
|
||||
- Add MCIP-6 Byzyantium transition to Musicoin spec
|
||||
- Update mcip6_byz.json
|
||||
- ethcore: update musicoin byzantium block number
|
||||
- ethcore: update musicoin bootnodes
|
||||
- Update musicoin.json
|
||||
- More bootnodes.
|
||||
- Make 1.10 beta ([#8022](https://github.com/paritytech/parity/pull/8022))
|
||||
- Make 1.10 beta
|
||||
- Fix gitlab builds
|
||||
- SecretStore: secretstore_generateDocumentKey RPC ([#7864](https://github.com/paritytech/parity/pull/7864))
|
||||
- SecretStore: ECDSA session for cases when 2*t < N ([#7739](https://github.com/paritytech/parity/pull/7739))
|
||||
- bump tiny-keccak ([#8019](https://github.com/paritytech/parity/pull/8019))
|
||||
- Remove un-necessary comment ([#8014](https://github.com/paritytech/parity/pull/8014))
|
||||
- clean up account fmt::Debug ([#7983](https://github.com/paritytech/parity/pull/7983))
|
||||
- improve quality of vote_collector module ([#7984](https://github.com/paritytech/parity/pull/7984))
|
||||
- ExecutedBlock cleanup ([#7991](https://github.com/paritytech/parity/pull/7991))
|
||||
- Hardware-wallet/usb-subscribe-refactor ([#7860](https://github.com/paritytech/parity/pull/7860))
|
||||
- remove wildcard imports from views, make tests more idiomatic ([#7986](https://github.com/paritytech/parity/pull/7986))
|
||||
- moved PerfTimer to a separate crate - "trace-time" ([#7985](https://github.com/paritytech/parity/pull/7985))
|
||||
- clean up ethcore::spec module imports ([#7990](https://github.com/paritytech/parity/pull/7990))
|
||||
- rpc: don't include current block in new_block_filter ([#7982](https://github.com/paritytech/parity/pull/7982))
|
||||
- fix traces, removed bloomchain crate ([#7979](https://github.com/paritytech/parity/pull/7979))
|
||||
- simplify compression and move it out of rlp crate ([#7957](https://github.com/paritytech/parity/pull/7957))
|
||||
- removed old migrations ([#7974](https://github.com/paritytech/parity/pull/7974))
|
||||
- Reject too large packets in snapshot sync. ([#7977](https://github.com/paritytech/parity/pull/7977))
|
||||
- fixed broken logs ([#7934](https://github.com/paritytech/parity/pull/7934))
|
||||
- Increase max download limit to 128MB ([#7965](https://github.com/paritytech/parity/pull/7965))
|
||||
- Calculate proper keccak256/sha3 using parity. ([#7953](https://github.com/paritytech/parity/pull/7953))
|
||||
- Add changelog for 1.8.10 stable and 1.9.3 beta ([#7947](https://github.com/paritytech/parity/pull/7947))
|
||||
- kvdb-rocksdb: remove buffered operations when committing transaction ([#7950](https://github.com/paritytech/parity/pull/7950))
|
||||
- Bump WebSockets ([#7952](https://github.com/paritytech/parity/pull/7952))
|
||||
- removed redundant Bloom conversions ([#7932](https://github.com/paritytech/parity/pull/7932))
|
||||
- simplify RefInfo fmt ([#7929](https://github.com/paritytech/parity/pull/7929))
|
||||
- Kovan WASM fork code ([#7849](https://github.com/paritytech/parity/pull/7849))
|
||||
- bring back trie and triehash benches ([#7926](https://github.com/paritytech/parity/pull/7926))
|
||||
- removed redundant PodAccount::new method ([#7928](https://github.com/paritytech/parity/pull/7928))
|
||||
- removed dummy wrapper structure - LogGroupPosition ([#7922](https://github.com/paritytech/parity/pull/7922))
|
||||
- spec: Validate required divisor fields are not 0 ([#7933](https://github.com/paritytech/parity/pull/7933))
|
||||
- simplify Client::filter_traces method ([#7936](https://github.com/paritytech/parity/pull/7936))
|
||||
- gitlab cache ([#7921](https://github.com/paritytech/parity/pull/7921))
|
||||
- Fix a division by zero in light client RPC handler ([#7917](https://github.com/paritytech/parity/pull/7917))
|
||||
- triehash optimisations ([#7920](https://github.com/paritytech/parity/pull/7920))
|
||||
- removed redundant Blockchain::db method ([#7919](https://github.com/paritytech/parity/pull/7919))
|
||||
- removed redundant Blockchain::rewind method ([#7918](https://github.com/paritytech/parity/pull/7918))
|
||||
- Pending transactions subscription ([#7906](https://github.com/paritytech/parity/pull/7906))
|
||||
- removed redundant otry! macro from ethcore ([#7916](https://github.com/paritytech/parity/pull/7916))
|
||||
- Make block generator easier to use ([#7888](https://github.com/paritytech/parity/pull/7888))
|
||||
- ECIP 1041 - Remove Difficulty Bomb ([#7905](https://github.com/paritytech/parity/pull/7905))
|
||||
- Fix CSP for dapps that require eval. ([#7867](https://github.com/paritytech/parity/pull/7867))
|
||||
- Fix gitlab ([#7901](https://github.com/paritytech/parity/pull/7901))
|
||||
- Gitlb snap master patch ([#7900](https://github.com/paritytech/parity/pull/7900))
|
||||
- fix snap build master ([#7896](https://github.com/paritytech/parity/pull/7896))
|
||||
- Fix wallet import ([#7873](https://github.com/paritytech/parity/pull/7873))
|
||||
- Fix snapcraft nightly ([#7884](https://github.com/paritytech/parity/pull/7884))
|
||||
- Add a timeout for light client sync requests ([#7848](https://github.com/paritytech/parity/pull/7848))
|
||||
- SecretStore: fixed test ([#7878](https://github.com/paritytech/parity/pull/7878))
|
||||
- Fix checksums and auto-update push ([#7846](https://github.com/paritytech/parity/pull/7846))
|
||||
- Forward-port snap fixes ([#7831](https://github.com/paritytech/parity/pull/7831))
|
||||
- Update gitlab-test.sh ([#7883](https://github.com/paritytech/parity/pull/7883))
|
||||
- Fix installer binary names for macos and windows ([#7881](https://github.com/paritytech/parity/pull/7881))
|
||||
- Fix string typo: "develoopment" -> "development" ([#7874](https://github.com/paritytech/parity/pull/7874))
|
||||
- Update the instructions to install the stable snap ([#7876](https://github.com/paritytech/parity/pull/7876))
|
||||
- SecretStore: 'broadcast' decryption session ([#7843](https://github.com/paritytech/parity/pull/7843))
|
||||
- Flush keyfiles. Resolves #7632 ([#7868](https://github.com/paritytech/parity/pull/7868))
|
||||
- Read registry_address from given block ([#7866](https://github.com/paritytech/parity/pull/7866))
|
||||
- Clean up docs formatting for Wasm runtime ([#7869](https://github.com/paritytech/parity/pull/7869))
|
||||
- WASM: Disable internal memory ([#7842](https://github.com/paritytech/parity/pull/7842))
|
||||
- Update gitlab-build.sh ([#7855](https://github.com/paritytech/parity/pull/7855))
|
||||
- ethabi version 5 ([#7723](https://github.com/paritytech/parity/pull/7723))
|
||||
- Light client: randomize the peer we dispatch requests to ([#7844](https://github.com/paritytech/parity/pull/7844))
|
||||
- Store updater metadata in a single place ([#7832](https://github.com/paritytech/parity/pull/7832))
|
||||
- Add new EF ropstens nodes. ([#7824](https://github.com/paritytech/parity/pull/7824))
|
||||
- refactor stratum to remove retain cycle ([#7827](https://github.com/paritytech/parity/pull/7827))
|
||||
- Bump jsonrpc. ([#7828](https://github.com/paritytech/parity/pull/7828))
|
||||
- Add binary identifiers and sha256sum to builds ([#7830](https://github.com/paritytech/parity/pull/7830))
|
||||
- Update references to UI shell & wallet ([#7808](https://github.com/paritytech/parity/pull/7808))
|
||||
- Adjust storage update evm-style ([#7812](https://github.com/paritytech/parity/pull/7812))
|
||||
- Updated WASM Runtime & new interpreter (wasmi) ([#7796](https://github.com/paritytech/parity/pull/7796))
|
||||
- SecretStore: ignore removed authorities when running auto-migration ([#7674](https://github.com/paritytech/parity/pull/7674))
|
||||
- Fix build ([#7807](https://github.com/paritytech/parity/pull/7807))
|
||||
- Move js & js-old code to github.com/parity-js ([#7685](https://github.com/paritytech/parity/pull/7685))
|
||||
- More changelogs :) ([#7782](https://github.com/paritytech/parity/pull/7782))
|
||||
- Actualized API set in help ([#7790](https://github.com/paritytech/parity/pull/7790))
|
||||
- Removed obsolete file ([#7788](https://github.com/paritytech/parity/pull/7788))
|
||||
- Update ropsten bootnodes ([#7776](https://github.com/paritytech/parity/pull/7776))
|
||||
- CHANGELOG for 1.9.1 and 1.8.8 ([#7775](https://github.com/paritytech/parity/pull/7775))
|
||||
- Enable byzantium features on non-ethash chains ([#7753](https://github.com/paritytech/parity/pull/7753))
|
||||
- Fix client not being dropped on shutdown ([#7695](https://github.com/paritytech/parity/pull/7695))
|
||||
- Filter-out nodes.json ([#7716](https://github.com/paritytech/parity/pull/7716))
|
||||
- Removes redundant parentheses ([#7721](https://github.com/paritytech/parity/pull/7721))
|
||||
- Transaction-pool fixes ([#7741](https://github.com/paritytech/parity/pull/7741))
|
||||
- More visible download link in README.md ([#7707](https://github.com/paritytech/parity/pull/7707))
|
||||
- Changelog for 1.9.0 ([#7664](https://github.com/paritytech/parity/pull/7664))
|
||||
- Add scroll when too many accounts ([#7677](https://github.com/paritytech/parity/pull/7677))
|
||||
- SecretStore: return HTTP 403 (access denied) if consensus is unreachable ([#7656](https://github.com/paritytech/parity/pull/7656))
|
||||
- Moved StopGaurd to it's own crate ([#7635](https://github.com/paritytech/parity/pull/7635))
|
||||
|
||||
## Previous releases
|
||||
|
||||
- [CHANGELOG-1.9](docs/CHANGELOG-1.9.md) (_stable_)
|
||||
- [CHANGELOG-1.8](docs/CHANGELOG-1.8.md) (EOL: 2018-03-22)
|
||||
- [CHANGELOG-1.7](docs/CHANGELOG-1.7.md) (EOL: 2018-01-25)
|
||||
- [CHANGELOG-1.6](docs/CHANGELOG-1.6.md) (EOL: 2017-10-15)
|
||||
- [CHANGELOG-1.5](docs/CHANGELOG-1.5.md) (EOL: 2017-07-28)
|
||||
- [CHANGELOG-1.4](docs/CHANGELOG-1.4.md) (EOL: 2017-03-13)
|
||||
- [CHANGELOG-1.3](docs/CHANGELOG-1.3.md) (EOL: 2017-01-19)
|
||||
- [CHANGELOG-1.2](docs/CHANGELOG-1.2.md) (EOL: 2016-11-07)
|
||||
- [CHANGELOG-1.1](docs/CHANGELOG-1.1.md) (EOL: 2016-08-12)
|
||||
- [CHANGELOG-1.0](docs/CHANGELOG-1.0.md) (EOL: 2016-06-24)
|
||||
- [CHANGELOG-0.9](docs/CHANGELOG-0.9.md) (EOL: 2016-05-02)
|
||||
4399
Cargo.lock
generated
4399
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
140
Cargo.toml
140
Cargo.toml
@@ -1,139 +1,97 @@
|
||||
[package]
|
||||
description = "Parity Ethereum client"
|
||||
name = "parity"
|
||||
# NOTE Make sure to update util/version/Cargo.toml as well
|
||||
version = "1.11.0"
|
||||
version = "1.6.6"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
log = "0.3"
|
||||
env_logger = "0.4"
|
||||
rustc-hex = "1.0"
|
||||
docopt = "0.8"
|
||||
clap = "2"
|
||||
term_size = "0.3"
|
||||
textwrap = "0.9"
|
||||
num_cpus = "1.2"
|
||||
env_logger = "0.3"
|
||||
rustc-serialize = "0.3"
|
||||
docopt = "0.6"
|
||||
time = "0.1"
|
||||
num_cpus = "0.2"
|
||||
number_prefix = "0.2"
|
||||
rpassword = "1.0"
|
||||
semver = "0.9"
|
||||
ansi_term = "0.10"
|
||||
parking_lot = "0.5"
|
||||
regex = "0.2"
|
||||
atty = "0.2.8"
|
||||
toml = "0.4"
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
serde_derive = "1.0"
|
||||
futures = "0.1"
|
||||
futures-cpupool = "0.1"
|
||||
rpassword = "0.2.1"
|
||||
semver = "0.5"
|
||||
ansi_term = "0.7"
|
||||
regex = "0.1"
|
||||
isatty = "0.1"
|
||||
toml = "0.2"
|
||||
serde = "0.9"
|
||||
serde_json = "0.9"
|
||||
app_dirs = "1.1.1"
|
||||
fdlimit = "0.1"
|
||||
ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" }
|
||||
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.11" }
|
||||
hyper = { default-features = false, git = "https://github.com/ethcore/hyper" }
|
||||
ctrlc = { git = "https://github.com/ethcore/rust-ctrlc.git" }
|
||||
jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git" }
|
||||
ethsync = { path = "sync" }
|
||||
ethcore = { path = "ethcore" }
|
||||
ethcore-bytes = { path = "util/bytes" }
|
||||
ethcore-util = { path = "util" }
|
||||
ethcore-io = { path = "util/io" }
|
||||
ethcore-devtools = { path = "devtools" }
|
||||
ethcore-rpc = { path = "rpc" }
|
||||
ethcore-signer = { path = "signer" }
|
||||
ethcore-ipc = { path = "ipc/rpc" }
|
||||
ethcore-ipc-nano = { path = "ipc/nano" }
|
||||
ethcore-ipc-hypervisor = { path = "ipc/hypervisor" }
|
||||
ethcore-light = { path = "ethcore/light" }
|
||||
ethcore-logger = { path = "logger" }
|
||||
ethcore-miner = { path = "miner" }
|
||||
ethcore-network = { path = "util/network" }
|
||||
ethcore-private-tx = { path = "ethcore/private-tx" }
|
||||
ethcore-service = { path = "ethcore/service" }
|
||||
ethcore-stratum = { path = "ethcore/stratum" }
|
||||
ethcore-sync = { path = "ethcore/sync" }
|
||||
ethcore-transaction = { path = "ethcore/transaction" }
|
||||
ethereum-types = "0.3"
|
||||
node-filter = { path = "ethcore/node_filter" }
|
||||
ethkey = { path = "ethkey" }
|
||||
node-health = { path = "dapps/node-health" }
|
||||
ethcore-stratum = { path = "stratum" }
|
||||
evmbin = { path = "evmbin" }
|
||||
rlp = { path = "util/rlp" }
|
||||
rpc-cli = { path = "rpc_cli" }
|
||||
parity-rpc-client = { path = "rpc_client" }
|
||||
parity-hash-fetch = { path = "hash-fetch" }
|
||||
parity-ipfs-api = { path = "ipfs" }
|
||||
parity-local-store = { path = "local-store" }
|
||||
parity-reactor = { path = "util/reactor" }
|
||||
parity-rpc = { path = "rpc" }
|
||||
parity-rpc-client = { path = "rpc_client" }
|
||||
parity-updater = { path = "updater" }
|
||||
parity-version = { path = "util/version" }
|
||||
parity-whisper = { path = "whisper" }
|
||||
path = { path = "util/path" }
|
||||
dir = { path = "util/dir" }
|
||||
panic_hook = { path = "util/panic_hook" }
|
||||
keccak-hash = { path = "util/hash" }
|
||||
migration-rocksdb = { path = "util/migration-rocksdb" }
|
||||
kvdb = { path = "util/kvdb" }
|
||||
kvdb-rocksdb = { path = "util/kvdb-rocksdb" }
|
||||
journaldb = { path = "util/journaldb" }
|
||||
mem = { path = "util/mem" }
|
||||
|
||||
parity-dapps = { path = "dapps", optional = true }
|
||||
parity-reactor = { path = "util/reactor" }
|
||||
parity-local-store = { path = "local-store" }
|
||||
ethcore-dapps = { path = "dapps", optional = true }
|
||||
clippy = { version = "0.0.103", optional = true}
|
||||
ethcore-secretstore = { path = "secret_store", optional = true }
|
||||
|
||||
registrar = { path = "registrar" }
|
||||
|
||||
[build-dependencies]
|
||||
rustc_version = "0.2"
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "0.1"
|
||||
ipnetwork = "0.12.6"
|
||||
tempdir = "0.3"
|
||||
fake-fetch = { path = "util/fake-fetch" }
|
||||
ethcore-ipc-tests = { path = "ipc/tests" }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3.4", features = ["winsock2", "winuser", "shellapi"] }
|
||||
winapi = "0.2"
|
||||
|
||||
[target.'cfg(not(windows))'.dependencies]
|
||||
daemonize = { git = "https://github.com/paritytech/daemonize" }
|
||||
daemonize = "0.2"
|
||||
|
||||
[features]
|
||||
default = ["ui-precompiled"]
|
||||
ui = [
|
||||
"ui-enabled",
|
||||
"parity-dapps/ui",
|
||||
"dapps",
|
||||
"ethcore-dapps/ui",
|
||||
"ethcore-signer/ui",
|
||||
]
|
||||
ui-precompiled = [
|
||||
"ui-enabled",
|
||||
"parity-dapps/ui-precompiled",
|
||||
"dapps",
|
||||
"ethcore-signer/ui-precompiled",
|
||||
"ethcore-dapps/ui-precompiled",
|
||||
]
|
||||
ui-enabled = ["dapps"]
|
||||
dapps = ["parity-dapps"]
|
||||
dapps = ["ethcore-dapps"]
|
||||
ipc = ["ethcore/ipc", "ethsync/ipc"]
|
||||
jit = ["ethcore/jit"]
|
||||
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev", "ethcore-dapps/dev", "ethcore-signer/dev"]
|
||||
json-tests = ["ethcore/json-tests"]
|
||||
test-heavy = ["ethcore/test-heavy"]
|
||||
ethkey-cli = ["ethcore/ethkey-cli"]
|
||||
ethstore-cli = ["ethcore/ethstore-cli"]
|
||||
evm-debug = ["ethcore/evm-debug"]
|
||||
evm-debug-tests = ["ethcore/evm-debug-tests"]
|
||||
slow-blocks = ["ethcore/slow-blocks"]
|
||||
final = ["ethcore-util/final"]
|
||||
secretstore = ["ethcore-secretstore"]
|
||||
final = ["parity-version/final"]
|
||||
|
||||
[[bin]]
|
||||
path = "parity/main.rs"
|
||||
name = "parity"
|
||||
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
|
||||
[profile.release]
|
||||
debug = false
|
||||
lto = false
|
||||
panic = "abort"
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"chainspec",
|
||||
"dapps/js-glue",
|
||||
"ethcore/wasm/run",
|
||||
"ethcore/types",
|
||||
"ethkey/cli",
|
||||
"ethstore/cli",
|
||||
"evmbin",
|
||||
"miner",
|
||||
"transaction-pool",
|
||||
"whisper",
|
||||
"whisper/cli",
|
||||
]
|
||||
|
||||
[patch.crates-io]
|
||||
ring = { git = "https://github.com/paritytech/ring" }
|
||||
|
||||
142
README.md
142
README.md
@@ -1,56 +1,58 @@
|
||||
# Parity - fast, light, and robust Ethereum client
|
||||
|
||||
## [» Download the latest release «](https://github.com/paritytech/parity/releases/latest)
|
||||
|
||||
[](https://gitlab.parity.io/parity/parity/commits/master)
|
||||
[](https://codecov.io/gh/paritytech/parity)
|
||||
[](https://build.snapcraft.io/user/paritytech/parity)
|
||||
[](https://www.gnu.org/licenses/gpl-3.0.en.html)
|
||||
# [Parity](https://ethcore.io/parity.html)
|
||||
### Fast, light, and robust Ethereum implementation
|
||||
|
||||
[](https://gitlab.ethcore.io/parity/parity/commits/master) [![Coverage Status][coveralls-image]][coveralls-url] [![GPLv3][license-image]][license-url]
|
||||
|
||||
### Join the chat!
|
||||
|
||||
Get in touch with us on Gitter:
|
||||
[](https://gitter.im/paritytech/parity)
|
||||
[](https://gitter.im/paritytech/parity.js)
|
||||
[](https://gitter.im/paritytech/parity/miners)
|
||||
[](https://gitter.im/paritytech/parity-poa)
|
||||
Parity [![Join the chat at https://gitter.im/ethcore/parity][gitter-image]][gitter-url] and
|
||||
parity.js [](https://gitter.im/ethcore/parity.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
Or join our community on Matrix:
|
||||
[](https://riot.im/app/#/group/+parity:matrix.parity.io)
|
||||
[Internal Documentation][doc-url]
|
||||
|
||||
Official website: https://parity.io
|
||||
|
||||
Be sure to check out [our wiki](https://wiki.parity.io) for more information.
|
||||
Be sure to check out [our wiki][wiki-url] for more information.
|
||||
|
||||
[coveralls-image]: https://coveralls.io/repos/github/ethcore/parity/badge.svg?branch=master
|
||||
[coveralls-url]: https://coveralls.io/github/ethcore/parity?branch=master
|
||||
[gitter-image]: https://badges.gitter.im/Join%20Chat.svg
|
||||
[gitter-url]: https://gitter.im/ethcore/parity?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
||||
[license-image]: https://img.shields.io/badge/license-GPL%20v3-green.svg
|
||||
[license-url]: https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
[doc-url]: https://ethcore.github.io/parity/ethcore/index.html
|
||||
[wiki-url]: https://github.com/ethcore/parity/wiki
|
||||
|
||||
**Parity requires Rust version 1.15.0 to build**
|
||||
|
||||
----
|
||||
|
||||
|
||||
## About Parity
|
||||
|
||||
Parity's goal is to be the fastest, lightest, and most secure Ethereum client. We are developing Parity using the sophisticated and cutting-edge Rust programming language. Parity is licensed under the GPLv3, and can be used for all your Ethereum needs.
|
||||
|
||||
Parity comes with a built-in wallet, to install it please follow [these instructions](https://wiki.parity.io/Parity-Wallet). It includes various functionality allowing you to:
|
||||
Parity's goal is to be the fastest, lightest, and most secure Ethereum client. We are developing Parity using the sophisticated and
|
||||
cutting-edge Rust programming language. Parity is licensed under the GPLv3, and can be used for all your Ethereum needs.
|
||||
|
||||
Parity comes with a built-in wallet. To access [Parity Wallet](http://127.0.0.1:8080/) simply go to http://127.0.0.1:8080/. It
|
||||
includes various functionality allowing you to:
|
||||
- create and manage your Ethereum accounts;
|
||||
- manage your Ether and any Ethereum tokens;
|
||||
- create and register your own tokens;
|
||||
- and much more.
|
||||
- and much more.
|
||||
|
||||
From Parity Ethereum client version >=1.10, the User Interface (UI) is accessible in a separate application called Parity UI. To keep using the UI in the browser (deprecated), [follow these steps](https://wiki.parity.io/FAQ-Basic-Operations,-Configuration,-and-Synchronization.md#the-parity-ui-application-isnt-working-the-way-i-want).
|
||||
By default, Parity will also run a JSONRPC server on `127.0.0.1:8545`. This is fully configurable and supports a number
|
||||
of RPC APIs.
|
||||
|
||||
By default, Parity will also run a JSONRPC server on `127.0.0.1:8545` and a websockets server on `127.0.0.1:8546`. This is fully configurable and supports a number of APIs.
|
||||
If you run into an issue while using parity, feel free to file one in this repository
|
||||
or hop on our [gitter chat room][gitter-url] to ask a question. We are glad to help!
|
||||
|
||||
If you run into an issue while using Parity, feel free to file one in this repository or hop on our [Gitter](https://gitter.im/paritytech/parity) or [Riot](https://riot.im/app/#/group/+parity:matrix.parity.io) chat room to ask a question. We are glad to help!
|
||||
|
||||
**For security-critical issues**, please refer to the security policy outlined in [SECURITY.MD](SECURITY.md).
|
||||
|
||||
Parity's current release is 1.9. You can download it at https://github.com/paritytech/parity/releases or follow the instructions below to build from source.
|
||||
Parity's current release is 1.5. You can download it at https://ethcore.io/parity.html or follow the instructions
|
||||
below to build from source.
|
||||
|
||||
----
|
||||
|
||||
## Build dependencies
|
||||
|
||||
**Parity requires Rust version 1.23.0 to build**
|
||||
Parity is fully compatible with Stable Rust.
|
||||
|
||||
We recommend installing Rust through [rustup](https://www.rustup.rs/). If you don't already have rustup, you can install it like this:
|
||||
|
||||
@@ -58,49 +60,30 @@ We recommend installing Rust through [rustup](https://www.rustup.rs/). If you do
|
||||
```bash
|
||||
$ curl https://sh.rustup.rs -sSf | sh
|
||||
```
|
||||
|
||||
|
||||
Parity also requires `gcc`, `g++`, `libssl-dev`/`openssl`, `libudev-dev` and `pkg-config` packages to be installed.
|
||||
|
||||
- OSX:
|
||||
```bash
|
||||
$ curl https://sh.rustup.rs -sSf | sh
|
||||
```
|
||||
|
||||
`clang` is required. It comes with Xcode command line tools or can be installed with homebrew.
|
||||
|
||||
- Windows
|
||||
Make sure you have Visual Studio 2015 with C++ support installed. Next, download and run the rustup installer from
|
||||
|
||||
Make sure you have Visual Studio 2015 with C++ support installed. Next, download and run the rustup installer from
|
||||
https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe, start "VS2015 x64 Native Tools Command Prompt", and use the following command to install and set up the msvc toolchain:
|
||||
```bash
|
||||
```
|
||||
$ rustup default stable-x86_64-pc-windows-msvc
|
||||
```
|
||||
```
|
||||
|
||||
Once you have rustup installed, then you need to install:
|
||||
* [Perl](https://www.perl.org)
|
||||
* [Yasm](http://yasm.tortall.net)
|
||||
|
||||
Make sure that these binaries are in your `PATH`. After that you should be able to build parity from source.
|
||||
Once you have rustup, install parity or download and build from source
|
||||
|
||||
----
|
||||
|
||||
## Install from the snap store
|
||||
|
||||
In any of the [supported Linux distros](https://snapcraft.io/docs/core/install):
|
||||
## Quick install
|
||||
|
||||
```bash
|
||||
sudo snap install parity
|
||||
```
|
||||
|
||||
Or, if you want to contribute testing the upcoming release:
|
||||
|
||||
```bash
|
||||
sudo snap install parity --beta
|
||||
```
|
||||
|
||||
And to test the latest code landed into the master branch:
|
||||
|
||||
```bash
|
||||
sudo snap install parity --edge
|
||||
cargo install --git https://github.com/ethcore/parity.git parity
|
||||
```
|
||||
|
||||
----
|
||||
@@ -109,7 +92,7 @@ sudo snap install parity --edge
|
||||
|
||||
```bash
|
||||
# download Parity code
|
||||
$ git clone https://github.com/paritytech/parity
|
||||
$ git clone https://github.com/ethcore/parity
|
||||
$ cd parity
|
||||
|
||||
# build in release mode
|
||||
@@ -117,39 +100,11 @@ $ cargo build --release
|
||||
```
|
||||
|
||||
This will produce an executable in the `./target/release` subdirectory.
|
||||
|
||||
Note: if cargo fails to parse manifest try:
|
||||
|
||||
```bash
|
||||
$ ~/.cargo/bin/cargo build --release
|
||||
```
|
||||
|
||||
Note: When compiling a crate and you receive the following error:
|
||||
|
||||
```
|
||||
error: the crate is compiled with the panic strategy `abort` which is incompatible with this crate's strategy of `unwind`
|
||||
```
|
||||
|
||||
Cleaning the repository will most likely solve the issue, try:
|
||||
|
||||
```bash
|
||||
$ cargo clean
|
||||
```
|
||||
|
||||
This will always compile the latest nightly builds. If you want to build stable or beta, do a
|
||||
|
||||
```bash
|
||||
$ git checkout stable
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```bash
|
||||
$ git checkout beta
|
||||
```
|
||||
|
||||
first.
|
||||
|
||||
----
|
||||
|
||||
## Simple one-line installer for Mac and Ubuntu
|
||||
@@ -158,18 +113,9 @@ first.
|
||||
bash <(curl https://get.parity.io -Lk)
|
||||
```
|
||||
|
||||
The one-line installer always defaults to the latest beta release. To install a stable release, run:
|
||||
|
||||
```bash
|
||||
bash <(curl https://get.parity.io -Lk) -r stable
|
||||
```
|
||||
|
||||
## Start Parity
|
||||
|
||||
### Manually
|
||||
|
||||
To start Parity manually, just run
|
||||
|
||||
```bash
|
||||
$ ./target/release/parity
|
||||
```
|
||||
@@ -177,9 +123,11 @@ $ ./target/release/parity
|
||||
and Parity will begin syncing the Ethereum blockchain.
|
||||
|
||||
### Using systemd service file
|
||||
|
||||
To start Parity as a regular user using systemd init:
|
||||
|
||||
1. Copy `./scripts/parity.service` to your
|
||||
1. Copy `parity/scripts/parity.service` to your
|
||||
systemd user directory (usually `~/.config/systemd/user`).
|
||||
2. To configure Parity, write a `/etc/parity/config.toml` config file, see [Configuring Parity](https://paritytech.github.io/wiki/Configuring-Parity) for details.
|
||||
2. To pass any argument to Parity, write a `~/.parity/parity.conf` file this way:
|
||||
`ARGS="ARG1 ARG2 ARG3"`.
|
||||
|
||||
Example: `ARGS="ui --identity MyMachine"`.
|
||||
|
||||
80
SECURITY.md
80
SECURITY.md
@@ -1,80 +0,0 @@
|
||||
# Security Policy
|
||||
|
||||
Parity Technologies is committed to resolving security vulnerabilities in our software quickly and carefully. We take the necessary steps to minimize risk, provide timely information, and deliver vulnerability fixes and mitigations required to address security issues.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Security vulnerabilities in Parity software should be reported by email to security@parity.io. If you think your report might be eligible for the Parity Bug Bounty Program, your email should be send to bugbounty@parity.io.
|
||||
|
||||
Your report should include the following:
|
||||
|
||||
- your name
|
||||
- description of the vulnerability
|
||||
- attack scenario (if any)
|
||||
- components
|
||||
- reproduction
|
||||
- other details
|
||||
|
||||
Try to include as much information in your report as you can, including a description of the vulnerability, its potential impact, and steps for reproducing it. Be sure to use a descriptive subject line.
|
||||
|
||||
You'll receive a response to your email within two business days indicating the next steps in handling your report. We encourage finders to use encrypted communication channels to protect the confidentiality of vulnerability reports. You can encrypt your report using our public key. This key is [on MIT's key server](https://pgp.mit.edu/pks/lookup?op=get&search=0x5D0F03018D07DE73) server and reproduced below.
|
||||
|
||||
After the initial reply to your report, our team will endeavor to keep you informed of the progress being made towards a fix. These updates will be sent at least every five business days.
|
||||
|
||||
Thank you for taking the time to responsibly disclose any vulnerabilities you find.
|
||||
|
||||
## Responsible Investigation and Reporting
|
||||
|
||||
Responsible investigation and reporting includes, but isn't limited to, the following:
|
||||
|
||||
- Don't violate the privacy of other users, destroy data, etc.
|
||||
- Don’t defraud or harm Parity Technologies Ltd or its users during your research; you should make a good faith effort to not interrupt or degrade our services.
|
||||
- Don't target our physical security measures, or attempt to use social engineering, spam, distributed denial of service (DDOS) attacks, etc.
|
||||
- Initially report the bug only to us and not to anyone else.
|
||||
- Give us a reasonable amount of time to fix the bug before disclosing it to anyone else, and give us adequate written warning before disclosing it to anyone else.
|
||||
- In general, please investigate and report bugs in a way that makes a reasonable, good faith effort not to be disruptive or harmful to us or our users. Otherwise your actions might be interpreted as an attack rather than an effort to be helpful.
|
||||
|
||||
## Bug Bounty Program
|
||||
|
||||
Our Bug Bounty Program allows us to recognise and reward members of the Parity community for helping us find and address significant bugs, in accordance with the terms of the Parity Bug Bounty Program. A detailed description on eligibility, rewards, legal information and terms & conditions for contributors can be found on [our website](https://paritytech.io/bug-bounty.html).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Plaintext PGP Key
|
||||
|
||||
```
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mQENBFlyIAwBCACe0keNPjgYzZ1Oy/8t3zj/Qw9bHHqrzx7FWy8NbXnYBM19NqOZ
|
||||
DIP7Oe0DvCaf/uruBskCS0iVstHlEFQ2AYe0Ei0REt9lQdy61GylU/DEB3879IG+
|
||||
6FO0SnFeYeerv1/hFI2K6uv8v7PyyVDiiJSW0I1KIs2OBwJicTKmWxLAeQsRgx9G
|
||||
yRGalrVk4KP+6pWTA7k3DxmDZKZyfYV/Ej10NtuzmsemwDbv98HKeomp/kgFOfSy
|
||||
3AZjeCpctlsNqpjUuXa0/HudmH2WLxZ0fz8XeoRh8XM9UudNIecjrDqmAFrt/btQ
|
||||
/3guvlzhFCdhYPVGsUusKMECk/JG+Xx1/1ZjABEBAAG0LFBhcml0eSBTZWN1cml0
|
||||
eSBDb250YWN0IDxzZWN1cml0eUBwYXJpdHkuaW8+iQFUBBMBCAA+FiEE2uUVYCjP
|
||||
N6B8aTiDXQ8DAY0H3nMFAllyIAwCGwMFCQPCZwAFCwkIBwIGFQgJCgsCBBYCAwEC
|
||||
HgECF4AACgkQXQ8DAY0H3nM60wgAkS3A36Zc+upiaxU7tumcGv+an17j7gin0sif
|
||||
+0ELSjVfrXInM6ovai+NhUdcLkJ7tCrKS90fvlaELK5Sg9CXBWCTFccKN4A/B7ey
|
||||
rOg2NPXUecnyBB/XqQgKYH7ujYlOlqBDXMfz6z8Hj6WToxg9PPMGGomyMGh8AWxM
|
||||
3yRPFs5RKt0VKgN++5N00oly5Y8ri5pgCidDvCLYMGTVDHFKwkuc9w6BlWlu1R1e
|
||||
/hXFWUFAP1ffTAul3QwyKhjPn2iotCdxXjvt48KaU8DN4iL7aMBN/ZBKqGS7yRdF
|
||||
D/JbJyaaJ0ZRvFSTSXy/sWY3z1B5mtCPBxco8hqqNfRkCwuZ6LkBDQRZciAMAQgA
|
||||
8BP8xrwe12TOUTqL/Vrbxv/FLdhKh53J6TrPKvC2TEEKOrTNo5ahRq+XOS5E7G2N
|
||||
x3b+fq8gR9BzFcldAx0XWUtGs/Wv++ulaSNqTBxj13J3G3WGsUfMKxRgj//piCUD
|
||||
bCFLQfGZdKk0M1o9QkPVARwwmvCNiNB/l++xGqPtfc44H5jWj3GoGvL2MkShPzrN
|
||||
yN/bJ+m+R5gtFGdInqa5KXBuxxuW25eDKJ+LzjbgUgeC76wNcfOiQHTdMkcupjdO
|
||||
bbGFwo10hcbRAOcZEv6//Zrlmk/6nPxEd2hN20St2bSN0+FqfZ267mWEu3ejsgF8
|
||||
ArdCpv5h4fBvJyNwiTZwIQARAQABiQE8BBgBCAAmFiEE2uUVYCjPN6B8aTiDXQ8D
|
||||
AY0H3nMFAllyIAwCGwwFCQPCZwAACgkQXQ8DAY0H3nNisggAl4fqhRlA34wIb190
|
||||
sqXHVxiCuzPaqS6krE9xAa1+gncX485OtcJNqnjugHm2rFE48lv7oasviuPXuInE
|
||||
/OgVFnXYv9d/Xx2JUeDs+bFTLouCDRY2Unh7KJZasfqnMcCHWcxHx5FvRNZRssaB
|
||||
WTZVo6sizPurGUtbpYe4/OLFhadBqAE0EUmVRFEUMc1YTnu4eLaRBzoWN4d2UWwi
|
||||
LN25RSrVSke7LTSFbgn9ntQrQ2smXSR+cdNkkfRCjFcpUaecvFl9HwIqoyVbT4Ym
|
||||
0hbpbbX/cJdc91tKa+psa29uMeGL/cgL9fAu19yNFRyOTMxjZnvql1X/WE1pLmoP
|
||||
ETBD1Q==
|
||||
=K9Qw
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
```
|
||||
@@ -1,9 +0,0 @@
|
||||
[package]
|
||||
name = "chainspec"
|
||||
version = "0.1.0"
|
||||
authors = ["debris <marek.kotewicz@gmail.com>"]
|
||||
|
||||
[dependencies]
|
||||
ethjson = { path = "../json" }
|
||||
serde_json = "1.0"
|
||||
serde_ignored = "0.0.4"
|
||||
@@ -1,48 +0,0 @@
|
||||
extern crate serde_json;
|
||||
extern crate serde_ignored;
|
||||
extern crate ethjson;
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
use std::{fs, env, process};
|
||||
use ethjson::spec::Spec;
|
||||
|
||||
fn quit(s: &str) -> ! {
|
||||
println!("{}", s);
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut args = env::args();
|
||||
if args.len() != 2 {
|
||||
quit("You need to specify chainspec.json\n\
|
||||
\n\
|
||||
./chainspec <chainspec.json>");
|
||||
}
|
||||
|
||||
let path = args.nth(1).expect("args.len() == 2; qed");
|
||||
let file = match fs::File::open(&path) {
|
||||
Ok(file) => file,
|
||||
Err(_) => quit(&format!("{} could not be opened", path)),
|
||||
};
|
||||
|
||||
let mut unused = BTreeSet::new();
|
||||
let mut deserializer = serde_json::Deserializer::from_reader(file);
|
||||
|
||||
let spec: Result<Spec, _> = serde_ignored::deserialize(&mut deserializer, |field| {
|
||||
unused.insert(field.to_string());
|
||||
});
|
||||
|
||||
if let Err(err) = spec {
|
||||
quit(&format!("{} {}", path, err.to_string()));
|
||||
}
|
||||
|
||||
if !unused.is_empty() {
|
||||
let err = unused.into_iter()
|
||||
.map(|field| format!("{} unexpected field `{}`", path, field))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
quit(&err);
|
||||
}
|
||||
|
||||
println!("{} is valid", path);
|
||||
}
|
||||
@@ -1,49 +1,45 @@
|
||||
[package]
|
||||
description = "Parity Dapps crate"
|
||||
name = "parity-dapps"
|
||||
version = "1.11.0"
|
||||
name = "ethcore-dapps"
|
||||
version = "1.6.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"
|
||||
rand = "0.3"
|
||||
log = "0.3"
|
||||
parity-dapps-glue = "1.9"
|
||||
parking_lot = "0.5"
|
||||
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" }
|
||||
|
||||
ethcore-bytes = { path = "../util/bytes" }
|
||||
ethereum-types = "0.3"
|
||||
env_logger = "0.3"
|
||||
futures = "0.1"
|
||||
jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git" }
|
||||
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc.git" }
|
||||
hyper = { default-features = false, git = "https://github.com/ethcore/hyper" }
|
||||
unicase = "1.3"
|
||||
url = "1.0"
|
||||
rustc-serialize = "0.3"
|
||||
serde = "0.9"
|
||||
serde_json = "0.9"
|
||||
serde_derive = "0.9"
|
||||
linked-hash-map = "0.3"
|
||||
parity-dapps-glue = "1.7"
|
||||
base32 = "0.3"
|
||||
mime = "0.2"
|
||||
mime_guess = "1.6.1"
|
||||
time = "0.1.35"
|
||||
zip = { version = "0.1", default-features = false }
|
||||
ethcore-devtools = { path = "../devtools" }
|
||||
ethcore-rpc = { path = "../rpc" }
|
||||
ethcore-util = { path = "../util" }
|
||||
fetch = { path = "../util/fetch" }
|
||||
node-health = { path = "./node-health" }
|
||||
parity-ui = { path = "./ui" }
|
||||
parity-hash-fetch = { path = "../hash-fetch" }
|
||||
parity-reactor = { path = "../util/reactor" }
|
||||
parity-ui = { path = "./ui" }
|
||||
parity-ui-deprecation = { path = "./ui-deprecation" }
|
||||
keccak-hash = { path = "../util/hash" }
|
||||
parity-version = { path = "../util/version" }
|
||||
registrar = { path = "../registrar" }
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.4"
|
||||
ethcore-devtools = { path = "../devtools" }
|
||||
clippy = { version = "0.0.103", optional = true}
|
||||
|
||||
[features]
|
||||
dev = ["clippy", "ethcore-rpc/dev", "ethcore-util/dev"]
|
||||
|
||||
ui = ["parity-ui/no-precompiled-js"]
|
||||
ui-precompiled = ["parity-ui/use-precompiled-js"]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
description = "Base Package for all Parity built-in dapps"
|
||||
name = "parity-dapps-glue"
|
||||
version = "1.9.1"
|
||||
version = "1.6.0"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
build = "build.rs"
|
||||
@@ -12,16 +12,19 @@ syntex = { version = "0.58", optional = true }
|
||||
|
||||
[dependencies]
|
||||
glob = { version = "0.2.11" }
|
||||
mime_guess = { version = "2.0.0-alpha.2" }
|
||||
mime_guess = { version = "1.6.1" }
|
||||
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 }
|
||||
clippy = { version = "0.0.90", optional = true }
|
||||
|
||||
[features]
|
||||
dev = ["clippy"]
|
||||
default = ["with-syntex"]
|
||||
nightly = ["quasi_macros"]
|
||||
nightly-testing = ["clippy"]
|
||||
with-syntex = ["quasi/with-syntex", "quasi_codegen", "quasi_codegen/with-syntex", "syntex", "syntex_syntax"]
|
||||
use-precompiled-js = []
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ Code generator to simplify creating a built-in Parity Dapp
|
||||
1. Clone this repository.
|
||||
|
||||
```bash
|
||||
$ git clone https://github.com/paritytech/parity.git
|
||||
$ git clone https://github.com/ethcore/parity.git
|
||||
```
|
||||
|
||||
1. Create a new directory for your Dapp. (`./myapp`)
|
||||
@@ -29,10 +29,10 @@ Code generator to simplify creating a built-in Parity Dapp
|
||||
|
||||
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).
|
||||
1. Create `./parity/dapps/myapp/Cargo.toml` with you apps details. See example here: [parity-status Cargo.toml](https://github.com/ethcore/parity-ui/blob/master/status/Cargo.toml).
|
||||
|
||||
```bash
|
||||
$ git clone https://github.com/paritytech/parity-ui.git
|
||||
$ git clone https://github.com/ethcore/parity-ui.git
|
||||
$ cd ./parity-ui/
|
||||
$ cp ./home/Cargo.toml ../parity/dapps/myapp/Cargo.toml
|
||||
$ cp ./home/build.rs ../parity/dapps/myapp/build.rs
|
||||
|
||||
@@ -101,7 +101,6 @@ fn implement_webapp(cx: &ExtCtxt, builder: &aster::AstBuilder, item: &Item, push
|
||||
|
||||
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
|
||||
|
||||
@@ -25,7 +25,7 @@ mod platform {
|
||||
use std::process::Command;
|
||||
|
||||
pub static NPM_CMD: &'static str = "npm";
|
||||
pub fn handle_cmd(cmd: &mut Command) -> &mut Command {
|
||||
pub fn handle_fd(cmd: &mut Command) -> &mut Command {
|
||||
cmd
|
||||
}
|
||||
}
|
||||
@@ -34,14 +34,14 @@ mod platform {
|
||||
mod platform {
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
pub static NPM_CMD: &'static str = "cmd.exe";
|
||||
pub static NPM_CMD: &'static str = "npm.cmd";
|
||||
// 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 {
|
||||
// We cannot have any file descriptors open when running a child process
|
||||
// during build phase.
|
||||
pub fn handle_fd(cmd: &mut Command) -> &mut Command {
|
||||
cmd.stdin(Stdio::null())
|
||||
.arg("/c")
|
||||
.arg("npm.cmd")
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ 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))
|
||||
let child = platform::handle_fd(&mut Command::new(platform::NPM_CMD))
|
||||
.arg("install")
|
||||
.arg("--no-progress")
|
||||
.current_dir(path)
|
||||
@@ -66,7 +66,7 @@ pub fn build(path: &str, dest: &str) {
|
||||
.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))
|
||||
let child = platform::handle_fd(&mut Command::new(platform::NPM_CMD))
|
||||
.arg("run")
|
||||
.arg("build")
|
||||
.env("NODE_ENV", "production")
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
[package]
|
||||
name = "node-health"
|
||||
description = "Node's health status"
|
||||
version = "0.1.0"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
futures = "0.1"
|
||||
futures-cpupool = "0.1"
|
||||
log = "0.3"
|
||||
ntp = "0.3.0"
|
||||
parking_lot = "0.5"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
time = "0.1.35"
|
||||
|
||||
parity-reactor = { path = "../../util/reactor" }
|
||||
@@ -1,122 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Reporting node's health.
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use futures::Future;
|
||||
use futures::sync::oneshot;
|
||||
use types::{HealthInfo, HealthStatus, Health};
|
||||
use time::{TimeChecker, MAX_DRIFT};
|
||||
use parity_reactor::Remote;
|
||||
use parking_lot::Mutex;
|
||||
use {SyncStatus};
|
||||
|
||||
const TIMEOUT: Duration = Duration::from_secs(5);
|
||||
const PROOF: &str = "Only one closure is invoked.";
|
||||
|
||||
/// A struct enabling you to query for node's health.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NodeHealth {
|
||||
sync_status: Arc<SyncStatus>,
|
||||
time: TimeChecker,
|
||||
remote: Remote,
|
||||
}
|
||||
|
||||
impl NodeHealth {
|
||||
/// Creates new `NodeHealth`.
|
||||
pub fn new(sync_status: Arc<SyncStatus>, time: TimeChecker, remote: Remote) -> Self {
|
||||
NodeHealth { sync_status, time, remote, }
|
||||
}
|
||||
|
||||
/// Query latest health report.
|
||||
pub fn health(&self) -> Box<Future<Item = Health, Error = ()> + Send> {
|
||||
trace!(target: "dapps", "Checking node health.");
|
||||
// Check timediff
|
||||
let sync_status = self.sync_status.clone();
|
||||
let time = self.time.time_drift();
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let tx = Arc::new(Mutex::new(Some(tx)));
|
||||
let tx2 = tx.clone();
|
||||
self.remote.spawn_with_timeout(
|
||||
move |_| time.then(move |result| {
|
||||
let _ = tx.lock().take().expect(PROOF).send(Ok(result));
|
||||
Ok(())
|
||||
}),
|
||||
TIMEOUT,
|
||||
move || {
|
||||
let _ = tx2.lock().take().expect(PROOF).send(Err(()));
|
||||
},
|
||||
);
|
||||
|
||||
Box::new(rx.map_err(|err| {
|
||||
warn!(target: "dapps", "Health request cancelled: {:?}", err);
|
||||
}).and_then(move |time| {
|
||||
// Check peers
|
||||
let peers = {
|
||||
let (connected, max) = sync_status.peers();
|
||||
let (status, message) = match connected {
|
||||
0 => {
|
||||
(HealthStatus::Bad, "You are not connected to any peers. There is most likely some network issue. Fix connectivity.".into())
|
||||
},
|
||||
1 => (HealthStatus::NeedsAttention, "You are connected to only one peer. Your node might not be reliable. Check your network connection.".into()),
|
||||
_ => (HealthStatus::Ok, "".into()),
|
||||
};
|
||||
HealthInfo { status, message, details: (connected, max) }
|
||||
};
|
||||
|
||||
// Check sync
|
||||
let sync = {
|
||||
let is_syncing = sync_status.is_major_importing();
|
||||
let (status, message) = if is_syncing {
|
||||
(HealthStatus::NeedsAttention, "Your node is still syncing, the values you see might be outdated. Wait until it's fully synced.".into())
|
||||
} else {
|
||||
(HealthStatus::Ok, "".into())
|
||||
};
|
||||
HealthInfo { status, message, details: is_syncing }
|
||||
};
|
||||
|
||||
// Check time
|
||||
let time = {
|
||||
let (status, message, details) = match time {
|
||||
Ok(Ok(diff)) if diff < MAX_DRIFT && diff > -MAX_DRIFT => {
|
||||
(HealthStatus::Ok, "".into(), diff)
|
||||
},
|
||||
Ok(Ok(diff)) => {
|
||||
(HealthStatus::Bad, format!(
|
||||
"Your clock is not in sync. Detected difference is too big for the protocol to work: {}ms. Synchronize your clock.",
|
||||
diff,
|
||||
), diff)
|
||||
},
|
||||
Ok(Err(err)) => {
|
||||
(HealthStatus::NeedsAttention, format!(
|
||||
"Unable to reach time API: {}. Make sure that your clock is synchronized.",
|
||||
err,
|
||||
), 0)
|
||||
},
|
||||
Err(_) => {
|
||||
(HealthStatus::NeedsAttention, "Time API request timed out. Make sure that the clock is synchronized.".into(), 0)
|
||||
},
|
||||
};
|
||||
|
||||
HealthInfo { status, message, details, }
|
||||
};
|
||||
|
||||
Ok(Health { peers, sync, time})
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Node Health status reporting.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
extern crate futures;
|
||||
extern crate futures_cpupool;
|
||||
extern crate ntp;
|
||||
extern crate time as time_crate;
|
||||
extern crate parity_reactor;
|
||||
extern crate parking_lot;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
mod health;
|
||||
mod time;
|
||||
mod types;
|
||||
|
||||
pub use futures_cpupool::CpuPool;
|
||||
pub use health::NodeHealth;
|
||||
pub use types::{Health, HealthInfo, HealthStatus};
|
||||
pub use time::{TimeChecker, Error};
|
||||
|
||||
/// Indicates sync status
|
||||
pub trait SyncStatus: ::std::fmt::Debug + Send + Sync {
|
||||
/// Returns true if there is a major sync happening.
|
||||
fn is_major_importing(&self) -> bool;
|
||||
|
||||
/// Returns number of connected and ideal peers.
|
||||
fn peers(&self) -> (usize, usize);
|
||||
}
|
||||
@@ -1,357 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Periodically checks node's time drift using [SNTP](https://tools.ietf.org/html/rfc1769).
|
||||
//!
|
||||
//! An NTP packet is sent to the server with a local timestamp, the server then completes the packet, yielding the
|
||||
//! following timestamps:
|
||||
//!
|
||||
//! Timestamp Name ID When Generated
|
||||
//! ------------------------------------------------------------
|
||||
//! Originate Timestamp T1 time request sent by client
|
||||
//! Receive Timestamp T2 time request received at server
|
||||
//! Transmit Timestamp T3 time reply sent by server
|
||||
//! Destination Timestamp T4 time reply received at client
|
||||
//!
|
||||
//! The drift is defined as:
|
||||
//!
|
||||
//! drift = ((T2 - T1) + (T3 - T4)) / 2.
|
||||
//!
|
||||
|
||||
use std::io;
|
||||
use std::{fmt, mem, time};
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::atomic::{self, AtomicUsize};
|
||||
use std::sync::Arc;
|
||||
|
||||
use futures::{self, Future};
|
||||
use futures::future::{self, IntoFuture};
|
||||
use futures_cpupool::{CpuPool, CpuFuture};
|
||||
use ntp;
|
||||
use parking_lot::RwLock;
|
||||
use time_crate::{Duration, Timespec};
|
||||
|
||||
/// Time checker error.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Error {
|
||||
/// No servers are currently available for a query.
|
||||
NoServersAvailable,
|
||||
/// There was an error when trying to reach the NTP server.
|
||||
Ntp(String),
|
||||
/// IO error when reading NTP response.
|
||||
Io(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
use self::Error::*;
|
||||
|
||||
match *self {
|
||||
NoServersAvailable => write!(fmt, "No NTP servers available"),
|
||||
Ntp(ref err) => write!(fmt, "NTP error: {}", err),
|
||||
Io(ref err) => write!(fmt, "Connection Error: {}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(err: io::Error) -> Self { Error::Io(format!("{}", err)) }
|
||||
}
|
||||
|
||||
impl From<ntp::errors::Error> for Error {
|
||||
fn from(err: ntp::errors::Error) -> Self { Error::Ntp(format!("{}", err)) }
|
||||
}
|
||||
|
||||
/// NTP time drift checker.
|
||||
pub trait Ntp {
|
||||
/// Returned Future.
|
||||
type Future: IntoFuture<Item=Duration, Error=Error>;
|
||||
|
||||
/// Returns the current time drift.
|
||||
fn drift(&self) -> Self::Future;
|
||||
}
|
||||
|
||||
const SERVER_MAX_POLL_INTERVAL_SECS: u64 = 60;
|
||||
#[derive(Debug)]
|
||||
struct Server {
|
||||
pub address: String,
|
||||
next_call: RwLock<time::Instant>,
|
||||
failures: AtomicUsize,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
pub fn is_available(&self) -> bool {
|
||||
*self.next_call.read() < time::Instant::now()
|
||||
}
|
||||
|
||||
pub fn report_success(&self) {
|
||||
self.failures.store(0, atomic::Ordering::SeqCst);
|
||||
self.update_next_call(1)
|
||||
}
|
||||
|
||||
pub fn report_failure(&self) {
|
||||
let errors = self.failures.fetch_add(1, atomic::Ordering::SeqCst);
|
||||
self.update_next_call(1 << errors)
|
||||
}
|
||||
|
||||
fn update_next_call(&self, delay: usize) {
|
||||
*self.next_call.write() = time::Instant::now() + time::Duration::from_secs(delay as u64 * SERVER_MAX_POLL_INTERVAL_SECS);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> From<T> for Server {
|
||||
fn from(t: T) -> Self {
|
||||
Server {
|
||||
address: t.as_ref().to_owned(),
|
||||
next_call: RwLock::new(time::Instant::now()),
|
||||
failures: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// NTP client using the SNTP algorithm for calculating drift.
|
||||
#[derive(Clone)]
|
||||
pub struct SimpleNtp {
|
||||
addresses: Vec<Arc<Server>>,
|
||||
pool: CpuPool,
|
||||
}
|
||||
|
||||
impl fmt::Debug for SimpleNtp {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f
|
||||
.debug_struct("SimpleNtp")
|
||||
.field("addresses", &self.addresses)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl SimpleNtp {
|
||||
fn new<T: AsRef<str>>(addresses: &[T], pool: CpuPool) -> SimpleNtp {
|
||||
SimpleNtp {
|
||||
addresses: addresses.iter().map(Server::from).map(Arc::new).collect(),
|
||||
pool: pool,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Ntp for SimpleNtp {
|
||||
type Future = future::Either<
|
||||
CpuFuture<Duration, Error>,
|
||||
future::FutureResult<Duration, Error>,
|
||||
>;
|
||||
|
||||
fn drift(&self) -> Self::Future {
|
||||
use self::future::Either::{A, B};
|
||||
|
||||
let server = self.addresses.iter().find(|server| server.is_available());
|
||||
server.map(|server| {
|
||||
let server = server.clone();
|
||||
A(self.pool.spawn_fn(move || {
|
||||
debug!(target: "dapps", "Fetching time from {}.", server.address);
|
||||
|
||||
match ntp::request(&server.address) {
|
||||
Ok(packet) => {
|
||||
let dest_time = ::time_crate::now_utc().to_timespec();
|
||||
let orig_time = Timespec::from(packet.orig_time);
|
||||
let recv_time = Timespec::from(packet.recv_time);
|
||||
let transmit_time = Timespec::from(packet.transmit_time);
|
||||
|
||||
let drift = ((recv_time - orig_time) + (transmit_time - dest_time)) / 2;
|
||||
|
||||
server.report_success();
|
||||
Ok(drift)
|
||||
},
|
||||
Err(err) => {
|
||||
server.report_failure();
|
||||
Err(err.into())
|
||||
},
|
||||
}
|
||||
}))
|
||||
}).unwrap_or_else(|| B(future::err(Error::NoServersAvailable)))
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE In a positive scenario first results will be seen after:
|
||||
// MAX_RESULTS * UPDATE_TIMEOUT_INCOMPLETE_SECS seconds.
|
||||
const MAX_RESULTS: usize = 4;
|
||||
const UPDATE_TIMEOUT_OK_SECS: u64 = 6 * 60 * 60;
|
||||
const UPDATE_TIMEOUT_WARN_SECS: u64 = 15 * 60;
|
||||
const UPDATE_TIMEOUT_ERR_SECS: u64 = 60;
|
||||
const UPDATE_TIMEOUT_INCOMPLETE_SECS: u64 = 10;
|
||||
|
||||
/// Maximal valid time drift.
|
||||
pub const MAX_DRIFT: i64 = 10_000;
|
||||
|
||||
type BoxFuture<A, B> = Box<Future<Item = A, Error = B> + Send>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// A time checker.
|
||||
pub struct TimeChecker<N: Ntp = SimpleNtp> {
|
||||
ntp: N,
|
||||
last_result: Arc<RwLock<(time::Instant, VecDeque<Result<i64, Error>>)>>,
|
||||
}
|
||||
|
||||
impl TimeChecker<SimpleNtp> {
|
||||
/// Creates new time checker given the NTP server address.
|
||||
pub fn new<T: AsRef<str>>(ntp_addresses: &[T], pool: CpuPool) -> Self {
|
||||
let last_result = Arc::new(RwLock::new(
|
||||
// Assume everything is ok at the very beginning.
|
||||
(time::Instant::now(), vec![Ok(0)].into())
|
||||
));
|
||||
|
||||
let ntp = SimpleNtp::new(ntp_addresses, pool);
|
||||
|
||||
TimeChecker {
|
||||
ntp,
|
||||
last_result,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Ntp> TimeChecker<N> where <N::Future as IntoFuture>::Future: Send + 'static {
|
||||
/// Updates the time
|
||||
pub fn update(&self) -> BoxFuture<i64, Error> {
|
||||
trace!(target: "dapps", "Updating time from NTP.");
|
||||
let last_result = self.last_result.clone();
|
||||
Box::new(self.ntp.drift().into_future().then(move |res| {
|
||||
let res = res.map(|d| d.num_milliseconds());
|
||||
|
||||
if let Err(Error::NoServersAvailable) = res {
|
||||
debug!(target: "dapps", "No NTP servers available. Selecting an older result.");
|
||||
return select_result(last_result.read().1.iter());
|
||||
}
|
||||
|
||||
// Update the results.
|
||||
let mut results = mem::replace(&mut last_result.write().1, VecDeque::new());
|
||||
let has_all_results = results.len() >= MAX_RESULTS;
|
||||
let valid_till = time::Instant::now() + time::Duration::from_secs(
|
||||
match res {
|
||||
Ok(time) if has_all_results && time < MAX_DRIFT => UPDATE_TIMEOUT_OK_SECS,
|
||||
Ok(_) if has_all_results => UPDATE_TIMEOUT_WARN_SECS,
|
||||
Err(_) if has_all_results => UPDATE_TIMEOUT_ERR_SECS,
|
||||
_ => UPDATE_TIMEOUT_INCOMPLETE_SECS,
|
||||
}
|
||||
);
|
||||
|
||||
trace!(target: "dapps", "New time drift received: {:?}", res);
|
||||
// Push the result.
|
||||
results.push_back(res);
|
||||
while results.len() > MAX_RESULTS {
|
||||
results.pop_front();
|
||||
}
|
||||
|
||||
// Select a response and update last result.
|
||||
let res = select_result(results.iter());
|
||||
*last_result.write() = (valid_till, results);
|
||||
res
|
||||
}))
|
||||
}
|
||||
|
||||
/// Returns a current time drift or error if last request to NTP server failed.
|
||||
pub fn time_drift(&self) -> BoxFuture<i64, Error> {
|
||||
// return cached result
|
||||
{
|
||||
let res = self.last_result.read();
|
||||
if res.0 > time::Instant::now() {
|
||||
return Box::new(futures::done(select_result(res.1.iter())));
|
||||
}
|
||||
}
|
||||
// or update and return result
|
||||
self.update()
|
||||
}
|
||||
}
|
||||
|
||||
fn select_result<'a, T: Iterator<Item=&'a Result<i64, Error>>>(results: T) -> Result<i64, Error> {
|
||||
let mut min = None;
|
||||
for res in results {
|
||||
min = Some(match (min.take(), res) {
|
||||
(Some(Ok(min)), &Ok(ref new)) => Ok(::std::cmp::min(min, *new)),
|
||||
(Some(Ok(old)), &Err(_)) => Ok(old),
|
||||
(_, ref new) => (*new).clone(),
|
||||
})
|
||||
}
|
||||
|
||||
min.unwrap_or_else(|| Err(Error::Ntp("NTP server unavailable.".into())))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::time::Instant;
|
||||
use time::Duration;
|
||||
use futures::{future, Future};
|
||||
use super::{Ntp, TimeChecker, Error};
|
||||
use parking_lot::RwLock;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct FakeNtp(RefCell<Vec<Duration>>, Cell<u64>);
|
||||
impl FakeNtp {
|
||||
fn new() -> FakeNtp {
|
||||
FakeNtp(
|
||||
RefCell::new(vec![Duration::milliseconds(150)]),
|
||||
Cell::new(0))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ntp for FakeNtp {
|
||||
type Future = future::FutureResult<Duration, Error>;
|
||||
|
||||
fn drift(&self) -> Self::Future {
|
||||
self.1.set(self.1.get() + 1);
|
||||
future::ok(self.0.borrow_mut().pop().expect("Unexpected call to drift()."))
|
||||
}
|
||||
}
|
||||
|
||||
fn time_checker() -> TimeChecker<FakeNtp> {
|
||||
let last_result = Arc::new(RwLock::new(
|
||||
(Instant::now(), vec![Err(Error::Ntp("NTP server unavailable".into()))].into())
|
||||
));
|
||||
|
||||
TimeChecker {
|
||||
ntp: FakeNtp::new(),
|
||||
last_result: last_result,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_fetch_time_on_start() {
|
||||
// given
|
||||
let time = time_checker();
|
||||
|
||||
// when
|
||||
let diff = time.time_drift().wait().unwrap();
|
||||
|
||||
// then
|
||||
assert_eq!(diff, 150);
|
||||
assert_eq!(time.ntp.1.get(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_fetch_twice_if_timeout_has_not_passed() {
|
||||
// given
|
||||
let time = time_checker();
|
||||
|
||||
// when
|
||||
let diff1 = time.time_drift().wait().unwrap();
|
||||
let diff2 = time.time_drift().wait().unwrap();
|
||||
|
||||
// then
|
||||
assert_eq!(diff1, 150);
|
||||
assert_eq!(diff2, 150);
|
||||
assert_eq!(time.ntp.1.get(), 1);
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Base health types.
|
||||
|
||||
/// Health API endpoint status.
|
||||
#[derive(Debug, PartialEq, Serialize)]
|
||||
pub enum HealthStatus {
|
||||
/// Everything's OK.
|
||||
#[serde(rename = "ok")]
|
||||
Ok,
|
||||
/// Node health need attention
|
||||
/// (the issue is not critical, but may need investigation)
|
||||
#[serde(rename = "needsAttention")]
|
||||
NeedsAttention,
|
||||
/// There is something bad detected with the node.
|
||||
#[serde(rename = "bad")]
|
||||
Bad,
|
||||
}
|
||||
|
||||
/// Represents a single check in node health.
|
||||
/// Cointains the status of that check and apropriate message and details.
|
||||
#[derive(Debug, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct HealthInfo<T> {
|
||||
/// Check status.
|
||||
pub status: HealthStatus,
|
||||
/// Human-readable message.
|
||||
pub message: String,
|
||||
/// Technical details of the check.
|
||||
pub details: T,
|
||||
}
|
||||
|
||||
/// Node Health status.
|
||||
#[derive(Debug, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Health {
|
||||
/// Status of peers.
|
||||
pub peers: HealthInfo<(usize, usize)>,
|
||||
/// Sync status.
|
||||
pub sync: HealthInfo<bool>,
|
||||
/// Time diff info.
|
||||
pub time: HealthInfo<i64>,
|
||||
}
|
||||
@@ -15,83 +15,161 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::sync::Arc;
|
||||
use unicase::UniCase;
|
||||
use hyper::{server, net, Decoder, Encoder, Next, Control};
|
||||
use hyper::header;
|
||||
use hyper::method::Method;
|
||||
use hyper::header::AccessControlAllowOrigin;
|
||||
|
||||
use hyper::{Method, StatusCode};
|
||||
|
||||
use api::types::{App, ApiError};
|
||||
use api::response;
|
||||
use apps::fetcher::Fetcher;
|
||||
use endpoint::{Endpoint, Request, Response, EndpointPath};
|
||||
use futures::{future, Future};
|
||||
use node_health::{NodeHealth, HealthStatus};
|
||||
|
||||
use handlers::extract_url;
|
||||
use endpoint::{Endpoint, Endpoints, Handler, EndpointPath};
|
||||
use jsonrpc_http_server::cors;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RestApi {
|
||||
cors_domains: Option<Vec<AccessControlAllowOrigin>>,
|
||||
endpoints: Arc<Endpoints>,
|
||||
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> {
|
||||
pub fn new(cors_domains: Vec<String>, endpoints: Arc<Endpoints>, fetcher: Arc<Fetcher>) -> Box<Endpoint> {
|
||||
Box::new(RestApi {
|
||||
fetcher,
|
||||
health,
|
||||
cors_domains: Some(cors_domains.into_iter().map(|domain| match domain.as_ref() {
|
||||
"all" | "*" | "any" => AccessControlAllowOrigin::Any,
|
||||
"null" => AccessControlAllowOrigin::Null,
|
||||
other => AccessControlAllowOrigin::Value(other.into()),
|
||||
}).collect()),
|
||||
endpoints: endpoints,
|
||||
fetcher: fetcher,
|
||||
})
|
||||
}
|
||||
|
||||
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 list_apps(&self) -> Vec<App> {
|
||||
self.endpoints.iter().filter_map(|(ref k, ref e)| {
|
||||
e.info().map(|ref info| App::from_info(k, info))
|
||||
}).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl Endpoint for RestApi {
|
||||
fn to_async_handler(&self, path: EndpointPath, control: Control) -> Box<Handler> {
|
||||
Box::new(RestApiRouter::new(self.clone(), path, control))
|
||||
}
|
||||
}
|
||||
|
||||
struct RestApiRouter {
|
||||
api: RestApi,
|
||||
origin: Option<String>,
|
||||
path: Option<EndpointPath>,
|
||||
control: Option<Control>,
|
||||
handler: Box<Handler>,
|
||||
}
|
||||
|
||||
impl RestApiRouter {
|
||||
fn new(api: RestApi, path: EndpointPath, control: Control) -> Self {
|
||||
RestApiRouter {
|
||||
path: Some(path),
|
||||
origin: None,
|
||||
control: Some(control),
|
||||
api: api,
|
||||
handler: response::as_json_error(&ApiError {
|
||||
code: "404".into(),
|
||||
title: "Not Found".into(),
|
||||
detail: "Resource you requested has not been found.".into(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
fn resolve_content(&self, hash: Option<&str>, path: EndpointPath, control: Control) -> Option<Box<Handler>> {
|
||||
match hash {
|
||||
Some(hash) if self.api.fetcher.contains(hash) => {
|
||||
Some(self.api.fetcher.to_async_handler(path, control))
|
||||
},
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
Ok(response::as_json(status, &health).into())
|
||||
})
|
||||
)
|
||||
/// Returns basic headers for a response (it may be overwritten by the handler)
|
||||
fn response_headers(&self) -> header::Headers {
|
||||
let mut headers = header::Headers::new();
|
||||
headers.set(header::AccessControlAllowCredentials);
|
||||
headers.set(header::AccessControlAllowMethods(vec![
|
||||
Method::Options,
|
||||
Method::Post,
|
||||
Method::Get,
|
||||
]));
|
||||
headers.set(header::AccessControlAllowHeaders(vec![
|
||||
UniCase("origin".to_owned()),
|
||||
UniCase("content-type".to_owned()),
|
||||
UniCase("accept".to_owned()),
|
||||
]));
|
||||
|
||||
if let Some(cors_header) = cors::get_cors_header(&self.api.cors_domains, &self.origin) {
|
||||
headers.set(cors_header);
|
||||
}
|
||||
|
||||
headers
|
||||
}
|
||||
}
|
||||
|
||||
impl server::Handler<net::HttpStream> for RestApiRouter {
|
||||
|
||||
fn on_request(&mut self, request: server::Request<net::HttpStream>) -> Next {
|
||||
self.origin = cors::read_origin(&request);
|
||||
|
||||
if let Method::Options = *request.method() {
|
||||
self.handler = response::empty();
|
||||
return Next::write();
|
||||
}
|
||||
|
||||
// TODO [ToDr] Consider using `path.app_params` instead
|
||||
let url = extract_url(&request);
|
||||
if url.is_none() {
|
||||
// Just return 404 if we can't parse URL
|
||||
return Next::write();
|
||||
}
|
||||
|
||||
let url = url.expect("Check for None early-exists above; qed");
|
||||
let mut path = self.path.take().expect("on_request called only once, and path is always defined in new; qed");
|
||||
let control = self.control.take().expect("on_request called only once, and control is always defined in new; qed");
|
||||
|
||||
let endpoint = url.path.get(1).map(|v| v.as_str());
|
||||
let hash = url.path.get(2).map(|v| v.as_str());
|
||||
// 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.clone().to_owned() }
|
||||
|
||||
let handler = endpoint.and_then(|v| match v {
|
||||
"apps" => Some(response::as_json(&self.api.list_apps())),
|
||||
"ping" => Some(response::ping()),
|
||||
"content" => self.resolve_content(hash, path, control),
|
||||
_ => None
|
||||
});
|
||||
|
||||
// Overwrite default
|
||||
if let Some(h) = handler {
|
||||
self.handler = h;
|
||||
}
|
||||
|
||||
self.handler.on_request(request)
|
||||
}
|
||||
|
||||
fn on_request_readable(&mut self, decoder: &mut Decoder<net::HttpStream>) -> Next {
|
||||
self.handler.on_request_readable(decoder)
|
||||
}
|
||||
|
||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||
*res.headers_mut() = self.response_headers();
|
||||
self.handler.on_response(res)
|
||||
}
|
||||
|
||||
fn on_response_writable(&mut self, encoder: &mut Encoder<net::HttpStream>) -> Next {
|
||||
self.handler.on_response_writable(encoder)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,3 +21,4 @@ mod response;
|
||||
mod types;
|
||||
|
||||
pub use self::api::RestApi;
|
||||
pub use self::types::App;
|
||||
|
||||
@@ -16,28 +16,25 @@
|
||||
|
||||
use serde::Serialize;
|
||||
use serde_json;
|
||||
use hyper::{self, mime, StatusCode};
|
||||
|
||||
use endpoint::Handler;
|
||||
use handlers::{ContentHandler, EchoHandler};
|
||||
|
||||
pub fn empty() -> hyper::Response {
|
||||
ContentHandler::ok("".into(), mime::TEXT_PLAIN).into()
|
||||
pub fn empty() -> Box<Handler> {
|
||||
Box::new(ContentHandler::ok("".into(), mime!(Text/Plain)))
|
||||
}
|
||||
|
||||
pub fn as_json<T: Serialize>(status: StatusCode, val: &T) -> hyper::Response {
|
||||
pub fn as_json<T: Serialize>(val: &T) -> Box<Handler> {
|
||||
let json = serde_json::to_string(val)
|
||||
.expect("serialization to string is infallible; qed");
|
||||
ContentHandler::new(status, json, mime::APPLICATION_JSON).into()
|
||||
Box::new(ContentHandler::ok(json, mime!(Application/Json)))
|
||||
}
|
||||
|
||||
pub fn ping(req: hyper::Request) -> hyper::Response {
|
||||
EchoHandler::new(req).into()
|
||||
pub fn as_json_error<T: Serialize>(val: &T) -> Box<Handler> {
|
||||
let json = serde_json::to_string(val)
|
||||
.expect("serialization to string is infallible; qed");
|
||||
Box::new(ContentHandler::not_found(json, mime!(Application/Json)))
|
||||
}
|
||||
|
||||
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(),
|
||||
})
|
||||
pub fn ping() -> Box<Handler> {
|
||||
Box::new(EchoHandler::default())
|
||||
}
|
||||
|
||||
@@ -14,14 +14,51 @@
|
||||
// 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.
|
||||
use endpoint::EndpointInfo;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct App {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub version: String,
|
||||
pub author: String,
|
||||
#[serde(rename="iconUrl")]
|
||||
pub icon_url: String,
|
||||
}
|
||||
|
||||
impl App {
|
||||
/// Creates `App` instance from `EndpointInfo` and `id`.
|
||||
pub fn from_info(id: &str, info: &EndpointInfo) -> Self {
|
||||
App {
|
||||
id: id.to_owned(),
|
||||
name: info.name.to_owned(),
|
||||
description: info.description.to_owned(),
|
||||
version: info.version.to_owned(),
|
||||
author: info.author.to_owned(),
|
||||
icon_url: info.icon_url.to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<EndpointInfo> for App {
|
||||
fn into(self) -> EndpointInfo {
|
||||
EndpointInfo {
|
||||
name: self.name,
|
||||
description: self.description,
|
||||
version: self.version,
|
||||
author: self.author,
|
||||
icon_url: self.icon_url,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ApiError {
|
||||
/// Error code.
|
||||
pub code: String,
|
||||
/// Human-readable error summary.
|
||||
pub title: String,
|
||||
/// More technical error details.
|
||||
pub detail: String,
|
||||
}
|
||||
|
||||
|
||||
@@ -19,12 +19,12 @@
|
||||
use std::fs;
|
||||
|
||||
use linked_hash_map::LinkedHashMap;
|
||||
use page::local;
|
||||
use page::LocalPageEndpoint;
|
||||
use handlers::FetchControl;
|
||||
|
||||
pub enum ContentStatus {
|
||||
Fetching(FetchControl),
|
||||
Ready(local::Dapp),
|
||||
Ready(LocalPageEndpoint),
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
||||
@@ -18,18 +18,15 @@ 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 fetch::{self, Mime};
|
||||
use util::H256;
|
||||
|
||||
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest, serialize_manifest, Manifest};
|
||||
use util::sha3::sha3;
|
||||
use page::{LocalPageEndpoint, PageCache};
|
||||
use handlers::{ContentValidator, ValidatorResponse};
|
||||
use page::{local, PageCache};
|
||||
use Embeddable;
|
||||
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest, serialize_manifest, Manifest};
|
||||
|
||||
type OnDone = Box<Fn(Option<local::Dapp>) + Send>;
|
||||
type OnDone = Box<Fn(Option<LocalPageEndpoint>) + Send>;
|
||||
|
||||
fn write_response_and_check_hash(
|
||||
id: &str,
|
||||
@@ -54,16 +51,16 @@ fn write_response_and_check_hash(
|
||||
|
||||
// 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()?;
|
||||
let mut reader = io::BufReader::new(response);
|
||||
io::copy(&mut reader, &mut file)?;
|
||||
file.flush()?;
|
||||
|
||||
// Validate hash
|
||||
// TODO [ToDr] calculate sha3 in-flight while reading the response
|
||||
let mut file = io::BufReader::new(fs::File::open(&content_path)?);
|
||||
let hash = sha3(&mut file)?;
|
||||
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))
|
||||
Ok((file.into_inner(), content_path))
|
||||
} else {
|
||||
Err(ValidationError::HashMismatch {
|
||||
expected: id,
|
||||
@@ -77,17 +74,15 @@ pub struct Content {
|
||||
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 {
|
||||
pub fn new(id: String, mime: Mime, content_path: PathBuf, on_done: OnDone) -> Self {
|
||||
Content {
|
||||
id,
|
||||
mime,
|
||||
content_path,
|
||||
on_done,
|
||||
pool,
|
||||
id: id,
|
||||
mime: mime,
|
||||
content_path: content_path,
|
||||
on_done: on_done,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,15 +90,12 @@ impl Content {
|
||||
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| {
|
||||
fn validate_and_install(&self, response: fetch::Response) -> Result<ValidatorResponse, ValidationError> {
|
||||
let validate = |content_path: PathBuf| {
|
||||
// Create dir
|
||||
let (_, content_path) = write_response_and_check_hash(&id, content_path, &id, response)?;
|
||||
let (_, content_path) = write_response_and_check_hash(self.id.as_str(), content_path.clone(), self.id.as_str(), response)?;
|
||||
|
||||
Ok(local::Dapp::single_file(pool, content_path, mime, PageCache::Enabled))
|
||||
Ok(LocalPageEndpoint::single_file(content_path, self.mime.clone(), PageCache::Enabled))
|
||||
};
|
||||
|
||||
// Prepare path for a file
|
||||
@@ -124,18 +116,16 @@ pub struct Dapp {
|
||||
id: String,
|
||||
dapps_path: PathBuf,
|
||||
on_done: OnDone,
|
||||
embeddable_on: Embeddable,
|
||||
pool: CpuPool,
|
||||
embeddable_on: Option<(String, u16)>,
|
||||
}
|
||||
|
||||
impl Dapp {
|
||||
pub fn new(id: String, dapps_path: PathBuf, on_done: OnDone, embeddable_on: Embeddable, pool: CpuPool) -> Self {
|
||||
pub fn new(id: String, dapps_path: PathBuf, on_done: OnDone, embeddable_on: Option<(String, u16)>) -> Self {
|
||||
Dapp {
|
||||
id,
|
||||
dapps_path,
|
||||
on_done,
|
||||
embeddable_on,
|
||||
pool,
|
||||
id: id,
|
||||
dapps_path: dapps_path,
|
||||
on_done: on_done,
|
||||
embeddable_on: embeddable_on,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,19 +157,16 @@ impl Dapp {
|
||||
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 embeddable_on = self.embeddable_on;
|
||||
let validate = move |dapp_path: PathBuf| {
|
||||
let (file, zip_path) = write_response_and_check_hash(&id, dapp_path.clone(), &format!("{}.zip", id), response)?;
|
||||
fn validate_and_install(&self, response: fetch::Response) -> Result<ValidatorResponse, ValidationError> {
|
||||
let validate = |dapp_path: PathBuf| {
|
||||
let (file, zip_path) = write_response_and_check_hash(self.id.as_str(), dapp_path.clone(), &format!("{}.zip", self.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);
|
||||
manifest.id = self.id.clone();
|
||||
|
||||
// Unpack zip
|
||||
for i in 0..zip.len() {
|
||||
@@ -210,7 +197,7 @@ impl ContentValidator for Dapp {
|
||||
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, embeddable_on);
|
||||
let endpoint = LocalPageEndpoint::new(dapp_path, manifest.clone().into(), PageCache::Enabled, self.embeddable_on.clone());
|
||||
Ok(endpoint)
|
||||
};
|
||||
|
||||
@@ -266,9 +253,3 @@ impl From<zip::result::ZipError> for ValidationError {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,123 +23,81 @@ mod installers;
|
||||
use std::{fs, env};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use futures::{future, Future};
|
||||
use futures_cpupool::CpuPool;
|
||||
use rustc_serialize::hex::FromHex;
|
||||
use fetch::{Client as FetchClient, Fetch};
|
||||
use hash_fetch::urlhint::{URLHintContract, URLHint, URLHintResult};
|
||||
use parity_reactor::Remote;
|
||||
|
||||
use hyper::StatusCode;
|
||||
use hyper;
|
||||
use hyper::status::StatusCode;
|
||||
|
||||
use ethereum_types::H256;
|
||||
use {Embeddable, SyncStatus, random_filename};
|
||||
use parking_lot::Mutex;
|
||||
use page::local;
|
||||
use {SyncStatus, random_filename};
|
||||
use util::Mutex;
|
||||
use page::LocalPageEndpoint;
|
||||
use handlers::{ContentHandler, ContentFetcherHandler};
|
||||
use endpoint::{self, Endpoint, EndpointPath};
|
||||
use endpoint::{Endpoint, EndpointPath, Handler};
|
||||
use apps::cache::{ContentCache, ContentStatus};
|
||||
|
||||
/// Limit of cached dapps/content
|
||||
const MAX_CACHED_DAPPS: usize = 20;
|
||||
|
||||
pub trait Fetcher: Endpoint + 'static {
|
||||
pub trait Fetcher: Send + Sync + 'static {
|
||||
fn contains(&self, content_id: &str) -> bool;
|
||||
|
||||
fn to_async_handler(&self, path: EndpointPath, control: hyper::Control) -> Box<Handler>;
|
||||
}
|
||||
|
||||
pub struct ContentFetcher<F: Fetch = FetchClient, R: URLHint + 'static = URLHintContract> {
|
||||
cache_path: PathBuf,
|
||||
pub struct ContentFetcher<F: Fetch = FetchClient, R: URLHint + Send + Sync + 'static = URLHintContract> {
|
||||
dapps_path: PathBuf,
|
||||
resolver: R,
|
||||
cache: Arc<Mutex<ContentCache>>,
|
||||
sync: Arc<SyncStatus>,
|
||||
embeddable_on: Embeddable,
|
||||
embeddable_on: Option<(String, u16)>,
|
||||
remote: Remote,
|
||||
fetch: F,
|
||||
pool: CpuPool,
|
||||
only_content: bool,
|
||||
}
|
||||
|
||||
impl<R: URLHint + 'static, F: Fetch> Drop for ContentFetcher<F, R> {
|
||||
impl<R: URLHint + Send + Sync + 'static, F: Fetch> Drop for ContentFetcher<F, R> {
|
||||
fn drop(&mut self) {
|
||||
// Clear cache path
|
||||
let _ = fs::remove_dir_all(&self.cache_path);
|
||||
let _ = fs::remove_dir_all(&self.dapps_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());
|
||||
impl<R: URLHint + Send + Sync + 'static, F: Fetch> ContentFetcher<F, R> {
|
||||
|
||||
pub fn new(resolver: R, sync_status: Arc<SyncStatus>, embeddable_on: Option<(String, u16)>, remote: Remote, fetch: F) -> Self {
|
||||
let mut dapps_path = env::temp_dir();
|
||||
dapps_path.push(random_filename());
|
||||
|
||||
ContentFetcher {
|
||||
cache_path,
|
||||
resolver,
|
||||
sync,
|
||||
dapps_path: dapps_path,
|
||||
resolver: resolver,
|
||||
sync: sync_status,
|
||||
cache: Arc::new(Mutex::new(ContentCache::default())),
|
||||
embeddable_on: None,
|
||||
fetch,
|
||||
pool,
|
||||
only_content: true,
|
||||
embeddable_on: embeddable_on,
|
||||
remote: remote,
|
||||
fetch: fetch,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn allow_dapps(mut self, dapps: bool) -> Self {
|
||||
self.only_content = !dapps;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn embeddable_on(mut self, embeddable_on: Embeddable) -> Self {
|
||||
self.embeddable_on = embeddable_on;
|
||||
self
|
||||
}
|
||||
|
||||
fn not_found(embeddable: Embeddable) -> endpoint::Response {
|
||||
Box::new(future::ok(ContentHandler::error(
|
||||
StatusCode::NotFound,
|
||||
"Resource Not Found",
|
||||
"Requested resource was not found.",
|
||||
None,
|
||||
embeddable,
|
||||
).into()))
|
||||
}
|
||||
|
||||
fn still_syncing(embeddable: Embeddable) -> endpoint::Response {
|
||||
Box::new(future::ok(ContentHandler::error(
|
||||
fn still_syncing(address: Option<(String, u16)>) -> Box<Handler> {
|
||||
Box::new(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>"),
|
||||
embeddable,
|
||||
).into()))
|
||||
}
|
||||
|
||||
fn dapps_disabled(address: Embeddable) -> 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,
|
||||
address,
|
||||
).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> {
|
||||
impl<R: URLHint + Send + Sync + 'static, F: Fetch> Fetcher for ContentFetcher<F, R> {
|
||||
fn contains(&self, content_id: &str) -> bool {
|
||||
{
|
||||
let mut cache = self.cache.lock();
|
||||
@@ -149,17 +107,17 @@ impl<R: URLHint + 'static, F: Fetch> Fetcher for ContentFetcher<F, R> {
|
||||
}
|
||||
}
|
||||
// fallback to resolver
|
||||
if let Ok(content_id) = content_id.parse() {
|
||||
if let Ok(content_id) = content_id.from_hex() {
|
||||
// else try to resolve the app_id
|
||||
let has_content = self.resolver.resolve(content_id).is_some();
|
||||
// if there is content or we are syncing return true
|
||||
self.sync.is_major_importing() || self.resolve(content_id).is_some()
|
||||
has_content || self.sync.is_major_importing()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: URLHint + 'static, F: Fetch> Endpoint for ContentFetcher<F, R> {
|
||||
fn respond(&self, path: EndpointPath, req: endpoint::Request) -> endpoint::Response {
|
||||
fn to_async_handler(&self, path: EndpointPath, control: hyper::Control) -> Box<Handler> {
|
||||
let mut cache = self.cache.lock();
|
||||
let content_id = path.app_id.clone();
|
||||
|
||||
@@ -168,22 +126,22 @@ impl<R: URLHint + 'static, F: Fetch> Endpoint for ContentFetcher<F, R> {
|
||||
match status {
|
||||
// Just serve the content
|
||||
Some(&mut ContentStatus::Ready(ref endpoint)) => {
|
||||
(None, endpoint.to_response(&path))
|
||||
(None, endpoint.to_async_handler(path, control))
|
||||
},
|
||||
// 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))
|
||||
(None, fetch_control.to_async_handler(path, control))
|
||||
},
|
||||
// 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 content_hex = content_id.from_hex().expect("to_handler is called only when `contains` returns true.");
|
||||
let content = self.resolver.resolve(content_hex);
|
||||
|
||||
let cache = self.cache.clone();
|
||||
let id = content_id.clone();
|
||||
let on_done = move |result: Option<local::Dapp>| {
|
||||
let on_done = move |result: Option<LocalPageEndpoint>| {
|
||||
let mut cache = cache.lock();
|
||||
match result {
|
||||
Some(endpoint) => cache.insert(id.clone(), ContentStatus::Ready(endpoint)),
|
||||
@@ -197,65 +155,41 @@ impl<R: URLHint + 'static, F: Fetch> Endpoint for ContentFetcher<F, R> {
|
||||
Some(URLHintResult::Dapp(_)) if self.sync.is_major_importing() => {
|
||||
(None, Self::still_syncing(self.embeddable_on.clone()))
|
||||
},
|
||||
Some(URLHintResult::Dapp(_)) if self.only_content => {
|
||||
(None, Self::dapps_disabled(self.embeddable_on.clone()))
|
||||
},
|
||||
Some(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.embeddable_on.clone(),
|
||||
self.pool.clone(),
|
||||
),
|
||||
self.embeddable_on.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.embeddable_on.clone(),
|
||||
self.pool.clone(),
|
||||
),
|
||||
self.embeddable_on.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.embeddable_on.clone(),
|
||||
self.fetch.clone(),
|
||||
self.pool.clone(),
|
||||
)
|
||||
},
|
||||
};
|
||||
Some(URLHintResult::Dapp(dapp)) => {
|
||||
let handler = ContentFetcherHandler::new(
|
||||
dapp.url(),
|
||||
path,
|
||||
control,
|
||||
installers::Dapp::new(
|
||||
content_id.clone(),
|
||||
self.dapps_path.clone(),
|
||||
Box::new(on_done),
|
||||
self.embeddable_on.clone(),
|
||||
),
|
||||
self.embeddable_on.clone(),
|
||||
self.remote.clone(),
|
||||
self.fetch.clone(),
|
||||
);
|
||||
|
||||
(Some(ContentStatus::Fetching(handler.fetch_control())), Box::new(handler) as endpoint::Response)
|
||||
(Some(ContentStatus::Fetching(handler.fetch_control())), Box::new(handler) as Box<Handler>)
|
||||
},
|
||||
Some(URLHintResult::Content(content)) => {
|
||||
let handler = ContentFetcherHandler::new(
|
||||
content.url,
|
||||
path,
|
||||
control,
|
||||
installers::Content::new(
|
||||
content_id.clone(),
|
||||
content.mime,
|
||||
self.dapps_path.clone(),
|
||||
Box::new(on_done),
|
||||
),
|
||||
self.embeddable_on.clone(),
|
||||
self.remote.clone(),
|
||||
self.fetch.clone(),
|
||||
);
|
||||
|
||||
(Some(ContentStatus::Fetching(handler.fetch_control())), Box::new(handler) as Box<Handler>)
|
||||
},
|
||||
None if self.sync.is_major_importing() => {
|
||||
(None, Self::still_syncing(self.embeddable_on.clone()))
|
||||
@@ -263,7 +197,13 @@ impl<R: URLHint + 'static, F: Fetch> Endpoint for ContentFetcher<F, R> {
|
||||
None => {
|
||||
// This may happen when sync status changes in between
|
||||
// `contains` and `to_handler`
|
||||
(None, Self::not_found(self.embeddable_on.clone()))
|
||||
(None, Box::new(ContentHandler::error(
|
||||
StatusCode::NotFound,
|
||||
"Resource Not Found",
|
||||
"Requested resource was not found.",
|
||||
None,
|
||||
self.embeddable_on.clone(),
|
||||
)) as Box<Handler>)
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -283,53 +223,34 @@ impl<R: URLHint + 'static, F: Fetch> Endpoint for ContentFetcher<F, R> {
|
||||
mod tests {
|
||||
use std::env;
|
||||
use std::sync::Arc;
|
||||
use fetch::Client;
|
||||
use futures::{future, Future};
|
||||
use util::Bytes;
|
||||
use fetch::{Fetch, Client};
|
||||
use hash_fetch::urlhint::{URLHint, URLHintResult};
|
||||
use ethereum_types::H256;
|
||||
use parity_reactor::Remote;
|
||||
|
||||
use apps::cache::ContentStatus;
|
||||
use endpoint::EndpointInfo;
|
||||
use page::local;
|
||||
use page::LocalPageEndpoint;
|
||||
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))
|
||||
fn resolve(&self, _id: Bytes) -> Option<URLHintResult> {
|
||||
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,
|
||||
let fetcher = ContentFetcher::new(FakeResolver, Arc::new(|| false), None, Remote::new_sync(), Client::new().unwrap());
|
||||
let handler = LocalPageEndpoint::new(path, EndpointInfo {
|
||||
name: "fake".into(),
|
||||
description: "".into(),
|
||||
version: "".into(),
|
||||
author: "".into(),
|
||||
icon_url: "".into(),
|
||||
local_url: Some("".into()),
|
||||
allow_js_eval: None,
|
||||
}, Default::default(), None);
|
||||
|
||||
// when
|
||||
|
||||
@@ -14,17 +14,13 @@
|
||||
// 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 page::{LocalPageEndpoint, PageCache};
|
||||
use endpoint::{Endpoints, EndpointInfo};
|
||||
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest};
|
||||
use endpoint::{Endpoint, EndpointInfo};
|
||||
use page::{local, PageCache};
|
||||
use Embeddable;
|
||||
|
||||
struct LocalDapp {
|
||||
id: String,
|
||||
@@ -46,18 +42,16 @@ fn read_manifest(name: &str, mut path: PathBuf) -> EndpointInfo {
|
||||
// Try to deserialize manifest
|
||||
deserialize_manifest(s)
|
||||
})
|
||||
.map(Into::into)
|
||||
.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),
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -65,14 +59,14 @@ fn read_manifest(name: &str, mut path: PathBuf) -> EndpointInfo {
|
||||
/// 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, embeddable: Embeddable, pool: CpuPool) -> Option<(String, Box<local::Dapp>)> {
|
||||
pub fn local_endpoint<P: AsRef<Path>>(path: P, signer_address: Option<(String, u16)>) -> Option<(String, Box<LocalPageEndpoint>)> {
|
||||
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, embeddable.clone())
|
||||
(dapp.id, Box::new(LocalPageEndpoint::new(
|
||||
dapp.path, dapp.info, PageCache::Disabled, signer_address.clone())
|
||||
))
|
||||
})
|
||||
})
|
||||
@@ -90,13 +84,13 @@ fn local_dapp(name: String, path: PathBuf) -> LocalDapp {
|
||||
}
|
||||
|
||||
/// 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, embeddable: Embeddable, pool: CpuPool) -> BTreeMap<String, Box<Endpoint>> {
|
||||
let mut pages = BTreeMap::<String, Box<Endpoint>>::new();
|
||||
/// Scans the directory and collects `LocalPageEndpoints`.
|
||||
pub fn local_endpoints<P: AsRef<Path>>(dapps_path: P, signer_address: Option<(String, u16)>) -> Endpoints {
|
||||
let mut pages = Endpoints::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, embeddable.clone()))
|
||||
Box::new(LocalPageEndpoint::new(dapp.path, dapp.info, PageCache::Disabled, signer_address.clone()))
|
||||
);
|
||||
}
|
||||
pages
|
||||
|
||||
@@ -15,18 +15,13 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use serde_json;
|
||||
pub use apps::App as Manifest;
|
||||
pub use api::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)
|
||||
serde_json::from_str::<Manifest>(&manifest).map_err(|e| format!("{:?}", e))
|
||||
// TODO [todr] Manifest validation (especialy: id (used as path))
|
||||
}
|
||||
|
||||
pub fn serialize_manifest(manifest: &Manifest) -> Result<String, String> {
|
||||
|
||||
@@ -16,61 +16,46 @@
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use endpoint::{Endpoints, Endpoint};
|
||||
use futures_cpupool::CpuPool;
|
||||
use page;
|
||||
use page::PageEndpoint;
|
||||
use proxypac::ProxyPac;
|
||||
use web::Web;
|
||||
use fetch::Fetch;
|
||||
use {WebProxyTokens, ParentFrameSettings};
|
||||
use parity_dapps::WebApp;
|
||||
use parity_reactor::Remote;
|
||||
use {WebProxyTokens};
|
||||
|
||||
mod app;
|
||||
mod cache;
|
||||
mod ui;
|
||||
pub mod fs;
|
||||
mod fs;
|
||||
pub mod fetcher;
|
||||
pub mod manifest;
|
||||
|
||||
pub use self::app::App;
|
||||
extern crate parity_ui;
|
||||
|
||||
pub const HOME_PAGE: &'static str = "home";
|
||||
pub const HOME_PAGE: &'static str = "parity";
|
||||
pub const DAPPS_DOMAIN: &'static str = ".web3.site";
|
||||
pub const RPC_PATH: &'static str = "rpc";
|
||||
pub const API_PATH: &'static str = "api";
|
||||
pub const UTILS_PATH: &'static str = "parity-utils";
|
||||
pub const WEB_PATH: &'static str = "web";
|
||||
pub const URL_REFERER: &'static str = "__referer=";
|
||||
|
||||
pub fn utils(pool: CpuPool) -> Box<Endpoint> {
|
||||
Box::new(page::builtin::Dapp::new(pool, ::parity_ui::App::default()))
|
||||
}
|
||||
|
||||
pub fn ui(pool: CpuPool) -> Box<Endpoint> {
|
||||
Box::new(page::builtin::Dapp::with_fallback_to_index(pool, ::parity_ui::App::default()))
|
||||
}
|
||||
|
||||
pub fn ui_deprecation(pool: CpuPool) -> Box<Endpoint> {
|
||||
Box::new(page::builtin::Dapp::with_fallback_to_index(pool, ::parity_ui_deprecation::App::default()))
|
||||
}
|
||||
|
||||
pub fn ui_redirection(embeddable: Option<ParentFrameSettings>) -> Box<Endpoint> {
|
||||
Box::new(ui::Redirection::new(embeddable))
|
||||
pub fn utils() -> Box<Endpoint> {
|
||||
Box::new(PageEndpoint::with_prefix(parity_ui::App::default(), UTILS_PATH.to_owned()))
|
||||
}
|
||||
|
||||
pub fn all_endpoints<F: Fetch>(
|
||||
dapps_path: PathBuf,
|
||||
extra_dapps: Vec<PathBuf>,
|
||||
dapps_domain: &str,
|
||||
embeddable: Option<ParentFrameSettings>,
|
||||
signer_address: Option<(String, u16)>,
|
||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||
remote: Remote,
|
||||
fetch: F,
|
||||
pool: CpuPool,
|
||||
) -> (Vec<String>, Endpoints) {
|
||||
) -> Endpoints {
|
||||
// fetch fs dapps at first to avoid overwriting builtins
|
||||
let mut pages = fs::local_endpoints(dapps_path.clone(), embeddable.clone(), pool.clone());
|
||||
let local_endpoints: Vec<String> = pages.keys().cloned().collect();
|
||||
let mut pages = fs::local_endpoints(dapps_path, signer_address.clone());
|
||||
for path in extra_dapps {
|
||||
if let Some((id, endpoint)) = fs::local_endpoint(path.clone(), embeddable.clone(), pool.clone()) {
|
||||
if let Some((id, endpoint)) = fs::local_endpoint(path.clone(), signer_address.clone()) {
|
||||
pages.insert(id, endpoint);
|
||||
} else {
|
||||
warn!(target: "dapps", "Ignoring invalid dapp at {}", path.display());
|
||||
@@ -78,28 +63,22 @@ pub fn all_endpoints<F: Fetch>(
|
||||
}
|
||||
|
||||
// NOTE [ToDr] Dapps will be currently embeded on 8180
|
||||
pages.insert(
|
||||
"ui".into(),
|
||||
Box::new(page::builtin::Dapp::new_safe_to_embed(pool.clone(), ::parity_ui::App::default(), embeddable.clone()))
|
||||
);
|
||||
// old version
|
||||
pages.insert(
|
||||
"v1".into(),
|
||||
Box::new({
|
||||
let mut page = page::builtin::Dapp::new_safe_to_embed(pool.clone(), ::parity_ui::old::App::default(), embeddable.clone());
|
||||
// allow JS eval on old Wallet
|
||||
page.allow_js_eval();
|
||||
page
|
||||
})
|
||||
);
|
||||
pages.insert(
|
||||
"proxy".into(),
|
||||
ProxyPac::boxed(embeddable.clone(), dapps_domain.to_owned())
|
||||
);
|
||||
pages.insert(
|
||||
WEB_PATH.into(),
|
||||
Web::boxed(embeddable.clone(), web_proxy_tokens.clone(), fetch.clone(), pool.clone())
|
||||
);
|
||||
insert::<parity_ui::App>(&mut pages, "ui", Embeddable::Yes(signer_address.clone()));
|
||||
pages.insert("proxy".into(), ProxyPac::boxed(signer_address.clone()));
|
||||
pages.insert(WEB_PATH.into(), Web::boxed(signer_address.clone(), web_proxy_tokens.clone(), remote.clone(), fetch.clone()));
|
||||
|
||||
(local_endpoints, pages)
|
||||
pages
|
||||
}
|
||||
|
||||
fn insert<T : WebApp + Default + 'static>(pages: &mut Endpoints, id: &str, embed_at: Embeddable) {
|
||||
pages.insert(id.to_owned(), Box::new(match embed_at {
|
||||
Embeddable::Yes(address) => PageEndpoint::new_safe_to_embed(T::default(), address),
|
||||
Embeddable::No => PageEndpoint::new(T::default()),
|
||||
}));
|
||||
}
|
||||
|
||||
enum Embeddable {
|
||||
Yes(Option<(String, u16)>),
|
||||
#[allow(dead_code)]
|
||||
No,
|
||||
}
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! UI redirections
|
||||
|
||||
use hyper::StatusCode;
|
||||
use futures::future;
|
||||
|
||||
use endpoint::{Endpoint, Request, Response, EndpointPath};
|
||||
use {handlers, Embeddable};
|
||||
|
||||
/// Redirection to UI server.
|
||||
pub struct Redirection {
|
||||
embeddable_on: Embeddable,
|
||||
}
|
||||
|
||||
impl Redirection {
|
||||
pub fn new(
|
||||
embeddable_on: Embeddable,
|
||||
) -> Self {
|
||||
Redirection {
|
||||
embeddable_on,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Endpoint for Redirection {
|
||||
fn respond(&self, _path: EndpointPath, req: Request) -> Response {
|
||||
Box::new(future::ok(if let Some(ref frame) = self.embeddable_on {
|
||||
trace!(target: "dapps", "Redirecting to signer interface.");
|
||||
let protocol = req.uri().scheme().unwrap_or("http");
|
||||
handlers::Redirection::new(format!("{}://{}:{}", protocol, &frame.host, frame.port)).into()
|
||||
} else {
|
||||
trace!(target: "dapps", "Signer disabled, returning 404.");
|
||||
handlers::ContentHandler::error(
|
||||
StatusCode::NotFound,
|
||||
"404 Not Found",
|
||||
"Your homepage is not available when Trusted Signer is disabled.",
|
||||
Some("You can still access dapps by writing a correct address, though. Re-enable Signer to get your homepage back."),
|
||||
None,
|
||||
).into()
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -16,34 +16,38 @@
|
||||
|
||||
//! URL Endpoint traits
|
||||
|
||||
use hyper::{self, server, net};
|
||||
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())
|
||||
}
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct EndpointInfo {
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub version: String,
|
||||
pub author: String,
|
||||
pub icon_url: String,
|
||||
}
|
||||
|
||||
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 type Handler = server::Handler<net::HttpStream> + Send;
|
||||
|
||||
pub trait Endpoint : Send + Sync {
|
||||
fn info(&self) -> Option<&EndpointInfo> { None }
|
||||
|
||||
fn respond(&self, path: EndpointPath, req: Request) -> Response;
|
||||
fn to_handler(&self, _path: EndpointPath) -> Box<Handler> {
|
||||
panic!("This Endpoint is asynchronous and requires Control object.");
|
||||
}
|
||||
|
||||
fn to_async_handler(&self, path: EndpointPath, _control: hyper::Control) -> Box<Handler> {
|
||||
self.to_handler(path)
|
||||
}
|
||||
}
|
||||
|
||||
44
dapps/src/handlers/auth.rs
Normal file
44
dapps/src/handlers/auth.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Authorization Handlers
|
||||
|
||||
use hyper::{server, Decoder, Encoder, Next};
|
||||
use hyper::net::HttpStream;
|
||||
use hyper::status::StatusCode;
|
||||
|
||||
pub struct AuthRequiredHandler;
|
||||
|
||||
impl server::Handler<HttpStream> for AuthRequiredHandler {
|
||||
fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||
res.set_status(StatusCode::Unauthorized);
|
||||
res.headers_mut().set_raw("WWW-Authenticate", vec![b"Basic realm=\"Parity\"".to_vec()]);
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_response_writable(&mut self, _encoder: &mut Encoder<HttpStream>) -> Next {
|
||||
Next::end()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,38 +16,38 @@
|
||||
|
||||
//! Simple Content Handler
|
||||
|
||||
use hyper::{self, mime, header};
|
||||
use hyper::StatusCode;
|
||||
use hyper::{header, server, Decoder, Encoder, Next};
|
||||
use hyper::net::HttpStream;
|
||||
use hyper::mime::Mime;
|
||||
use hyper::status::StatusCode;
|
||||
|
||||
use parity_version::version;
|
||||
use util::version;
|
||||
|
||||
use handlers::add_security_headers;
|
||||
use Embeddable;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Clone)]
|
||||
pub struct ContentHandler {
|
||||
code: StatusCode,
|
||||
content: String,
|
||||
mimetype: mime::Mime,
|
||||
safe_to_embed_on: Embeddable,
|
||||
mimetype: Mime,
|
||||
write_pos: usize,
|
||||
safe_to_embed_on: Option<(String, u16)>,
|
||||
}
|
||||
|
||||
impl ContentHandler {
|
||||
pub fn ok(content: String, mimetype: mime::Mime) -> Self {
|
||||
pub fn ok(content: String, mimetype: Mime) -> Self {
|
||||
Self::new(StatusCode::Ok, content, mimetype)
|
||||
}
|
||||
|
||||
pub fn html(code: StatusCode, content: String, embeddable_on: Embeddable) -> Self {
|
||||
Self::new_embeddable(code, content, mime::TEXT_HTML, embeddable_on)
|
||||
pub fn not_found(content: String, mimetype: Mime) -> Self {
|
||||
Self::new(StatusCode::NotFound, content, mimetype)
|
||||
}
|
||||
|
||||
pub fn error(
|
||||
code: StatusCode,
|
||||
title: &str,
|
||||
message: &str,
|
||||
details: Option<&str>,
|
||||
embeddable_on: Embeddable,
|
||||
) -> Self {
|
||||
pub fn html(code: StatusCode, content: String, embeddable_on: Option<(String, u16)>) -> Self {
|
||||
Self::new_embeddable(code, content, mime!(Text/Html), embeddable_on)
|
||||
}
|
||||
|
||||
pub fn error(code: StatusCode, title: &str, message: &str, details: Option<&str>, embeddable_on: Option<(String, u16)>) -> Self {
|
||||
Self::html(code, format!(
|
||||
include_str!("../error_tpl.html"),
|
||||
title=title,
|
||||
@@ -57,32 +57,52 @@ impl ContentHandler {
|
||||
), embeddable_on)
|
||||
}
|
||||
|
||||
pub fn new(code: StatusCode, content: String, mimetype: mime::Mime) -> Self {
|
||||
pub fn new(code: StatusCode, content: String, mimetype: Mime) -> Self {
|
||||
Self::new_embeddable(code, content, mimetype, None)
|
||||
}
|
||||
|
||||
pub fn new_embeddable(
|
||||
code: StatusCode,
|
||||
content: String,
|
||||
mimetype: mime::Mime,
|
||||
safe_to_embed_on: Embeddable,
|
||||
) -> Self {
|
||||
pub fn new_embeddable(code: StatusCode, content: String, mimetype: Mime, embeddable_on: Option<(String, u16)>) -> Self {
|
||||
ContentHandler {
|
||||
code,
|
||||
content,
|
||||
mimetype,
|
||||
safe_to_embed_on,
|
||||
code: code,
|
||||
content: content,
|
||||
mimetype: mimetype,
|
||||
write_pos: 0,
|
||||
safe_to_embed_on: embeddable_on,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(), self.safe_to_embed_on, false);
|
||||
res
|
||||
impl server::Handler<HttpStream> for ContentHandler {
|
||||
fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||
res.set_status(self.code);
|
||||
res.headers_mut().set(header::ContentType(self.mimetype.clone()));
|
||||
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.clone());
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||
let bytes = self.content.as_bytes();
|
||||
if self.write_pos == bytes.len() {
|
||||
return Next::end();
|
||||
}
|
||||
|
||||
match encoder.write(&bytes[self.write_pos..]) {
|
||||
Ok(bytes) => {
|
||||
self.write_pos += bytes;
|
||||
Next::write()
|
||||
},
|
||||
Err(e) => match e.kind() {
|
||||
::std::io::ErrorKind::WouldBlock => Next::write(),
|
||||
_ => Next::end()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,31 +16,45 @@
|
||||
|
||||
//! Echo Handler
|
||||
|
||||
use hyper::{self, header};
|
||||
use std::io::Read;
|
||||
use hyper::{server, Decoder, Encoder, Next};
|
||||
use hyper::net::HttpStream;
|
||||
use super::ContentHandler;
|
||||
|
||||
use handlers::add_security_headers;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Default)]
|
||||
pub struct EchoHandler {
|
||||
request: hyper::Request,
|
||||
content: String,
|
||||
handler: Option<ContentHandler>,
|
||||
}
|
||||
|
||||
impl EchoHandler {
|
||||
pub fn new(request: hyper::Request) -> Self {
|
||||
EchoHandler {
|
||||
request,
|
||||
impl server::Handler<HttpStream> for EchoHandler {
|
||||
fn on_request(&mut self, _: server::Request<HttpStream>) -> Next {
|
||||
Next::read()
|
||||
}
|
||||
|
||||
fn on_request_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
|
||||
match decoder.read_to_string(&mut self.content) {
|
||||
Ok(0) => {
|
||||
self.handler = Some(ContentHandler::ok(self.content.clone(), mime!(Application/Json)));
|
||||
Next::write()
|
||||
},
|
||||
Ok(_) => Next::read(),
|
||||
Err(e) => match e.kind() {
|
||||
::std::io::ErrorKind::WouldBlock => Next::read(),
|
||||
_ => Next::end(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||
self.handler.as_mut()
|
||||
.expect("handler always set in on_request, which is before now; qed")
|
||||
.on_response(res)
|
||||
}
|
||||
|
||||
add_security_headers(res.headers_mut(), None, false);
|
||||
res
|
||||
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||
self.handler.as_mut()
|
||||
.expect("handler always set in on_request, which is before now; qed")
|
||||
.on_response_writable(encoder)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,39 +16,56 @@
|
||||
|
||||
//! Hyper Server Handler that fetches a file during a request (proxy).
|
||||
|
||||
use std::{fmt, mem};
|
||||
use std::sync::Arc;
|
||||
use std::fmt;
|
||||
use std::sync::{mpsc, Arc};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::time::{Instant, Duration};
|
||||
use fetch::{self, Fetch};
|
||||
use futures::sync::oneshot;
|
||||
use futures::{self, Future};
|
||||
use futures_cpupool::CpuPool;
|
||||
use hyper::{self, StatusCode};
|
||||
use parking_lot::Mutex;
|
||||
use futures::Future;
|
||||
use parity_reactor::Remote;
|
||||
use util::Mutex;
|
||||
|
||||
use endpoint::{self, EndpointPath};
|
||||
use hyper::{server, Decoder, Encoder, Next, Method, Control};
|
||||
use hyper::net::HttpStream;
|
||||
use hyper::uri::RequestUri;
|
||||
use hyper::status::StatusCode;
|
||||
|
||||
use endpoint::EndpointPath;
|
||||
use handlers::{ContentHandler, StreamingHandler};
|
||||
use page::local;
|
||||
use {Embeddable};
|
||||
use page::{LocalPageEndpoint, PageHandlerWaiting};
|
||||
|
||||
const FETCH_TIMEOUT: Duration = Duration::from_secs(300);
|
||||
const FETCH_TIMEOUT: u64 = 300;
|
||||
|
||||
pub enum ValidatorResponse {
|
||||
Local(local::Dapp),
|
||||
Streaming(StreamingHandler<fetch::BodyReader>),
|
||||
Local(LocalPageEndpoint),
|
||||
Streaming(StreamingHandler<fetch::Response>),
|
||||
}
|
||||
|
||||
pub trait ContentValidator: Sized + Send + 'static {
|
||||
pub trait ContentValidator: Send + 'static {
|
||||
type Error: fmt::Debug + fmt::Display;
|
||||
|
||||
fn validate_and_install(self, fetch::Response) -> Result<ValidatorResponse, Self::Error>;
|
||||
fn validate_and_install(&self, fetch::Response) -> Result<ValidatorResponse, Self::Error>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum FetchState {
|
||||
Waiting,
|
||||
NotStarted(String),
|
||||
Error(ContentHandler),
|
||||
InProgress(mpsc::Receiver<FetchState>),
|
||||
Streaming(StreamingHandler<fetch::Response>),
|
||||
Done(LocalPageEndpoint, Box<PageHandlerWaiting>),
|
||||
}
|
||||
|
||||
enum WaitResult {
|
||||
Error(ContentHandler),
|
||||
Done(LocalPageEndpoint),
|
||||
NonAwaitable,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FetchControl {
|
||||
abort: Arc<AtomicBool>,
|
||||
listeners: Arc<Mutex<Vec<oneshot::Sender<WaitResult>>>>,
|
||||
listeners: Arc<Mutex<Vec<(Control, mpsc::Sender<WaitResult>)>>>,
|
||||
deadline: Instant,
|
||||
}
|
||||
|
||||
@@ -57,36 +74,20 @@ impl Default for FetchControl {
|
||||
FetchControl {
|
||||
abort: Arc::new(AtomicBool::new(false)),
|
||||
listeners: Arc::new(Mutex::new(Vec::new())),
|
||||
deadline: Instant::now() + FETCH_TIMEOUT,
|
||||
deadline: Instant::now() + Duration::from_secs(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(..) {
|
||||
for (control, 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.");
|
||||
if let Err(e) = sender.send(status()) {
|
||||
trace!(target: "dapps", "Waiting listener notification failed: {:?}", e);
|
||||
} else {
|
||||
let _ = control.ready(Next::read());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,79 +97,92 @@ impl FetchControl {
|
||||
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 => {},
|
||||
FetchState::NotStarted(_) | FetchState::InProgress(_) | FetchState::Waiting => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_deadline_reached(&self) -> bool {
|
||||
self.deadline < Instant::now()
|
||||
}
|
||||
|
||||
enum WaitState {
|
||||
Waiting(oneshot::Receiver<WaitResult>),
|
||||
Done(endpoint::Response),
|
||||
}
|
||||
pub fn abort(&self) {
|
||||
self.abort.store(true, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum WaitResult {
|
||||
Error(ContentHandler),
|
||||
Done(local::Dapp),
|
||||
NonAwaitable,
|
||||
pub fn to_async_handler(&self, path: EndpointPath, control: Control) -> Box<server::Handler<HttpStream> + Send> {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
self.listeners.lock().push((control, tx));
|
||||
|
||||
Box::new(WaitingHandler {
|
||||
receiver: rx,
|
||||
state: FetchState::Waiting,
|
||||
uri: RequestUri::default(),
|
||||
path: path,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WaitingHandler {
|
||||
receiver: mpsc::Receiver<WaitResult>,
|
||||
state: FetchState,
|
||||
uri: RequestUri,
|
||||
path: EndpointPath,
|
||||
state: WaitState,
|
||||
}
|
||||
|
||||
impl Future for WaitingHandler {
|
||||
type Item = hyper::Response;
|
||||
type Error = hyper::Error;
|
||||
impl server::Handler<HttpStream> for WaitingHandler {
|
||||
fn on_request(&mut self, request: server::Request<HttpStream>) -> Next {
|
||||
self.uri = request.uri().clone();
|
||||
Next::wait()
|
||||
}
|
||||
|
||||
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));
|
||||
fn on_request_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
|
||||
let result = self.receiver.try_recv().ok();
|
||||
self.state = match result {
|
||||
Some(WaitResult::Error(handler)) => FetchState::Error(handler),
|
||||
Some(WaitResult::Done(endpoint)) => {
|
||||
let mut page_handler = endpoint.to_page_handler(self.path.clone());
|
||||
page_handler.set_uri(&self.uri);
|
||||
FetchState::Done(endpoint, page_handler)
|
||||
},
|
||||
_ => {
|
||||
warn!("A result for waiting request was not received.");
|
||||
FetchState::Waiting
|
||||
},
|
||||
};
|
||||
|
||||
match result {
|
||||
WaitResult::Error(handler) => {
|
||||
return Ok(futures::Async::Ready(handler.into()));
|
||||
},
|
||||
WaitResult::NonAwaitable => {
|
||||
let errors = Errors { embeddable_on: None };
|
||||
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()
|
||||
},
|
||||
};
|
||||
match self.state {
|
||||
FetchState::Done(_, ref mut handler) => handler.on_request_readable(decoder),
|
||||
FetchState::Streaming(ref mut handler) => handler.on_request_readable(decoder),
|
||||
FetchState::Error(ref mut handler) => handler.on_request_readable(decoder),
|
||||
_ => Next::write(),
|
||||
}
|
||||
}
|
||||
|
||||
self.state = new_state;
|
||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||
match self.state {
|
||||
FetchState::Done(_, ref mut handler) => handler.on_response(res),
|
||||
FetchState::Streaming(ref mut handler) => handler.on_response(res),
|
||||
FetchState::Error(ref mut handler) => handler.on_response(res),
|
||||
_ => Next::end(),
|
||||
}
|
||||
}
|
||||
|
||||
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||
match self.state {
|
||||
FetchState::Done(_, ref mut handler) => handler.on_response_writable(encoder),
|
||||
FetchState::Streaming(ref mut handler) => handler.on_response_writable(encoder),
|
||||
FetchState::Error(ref mut handler) => handler.on_response_writable(encoder),
|
||||
_ => Next::end(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Clone)]
|
||||
struct Errors {
|
||||
embeddable_on: Embeddable,
|
||||
embeddable_on: Option<(String, u16)>,
|
||||
}
|
||||
|
||||
impl Errors {
|
||||
fn streaming(&self) -> ContentHandler {
|
||||
ContentHandler::error(
|
||||
StatusCode::BadGateway,
|
||||
"Streaming Error",
|
||||
"This content is being streamed in other place.",
|
||||
None,
|
||||
self.embeddable_on.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
fn download_error<E: fmt::Debug>(&self, e: E) -> ContentHandler {
|
||||
ContentHandler::error(
|
||||
StatusCode::BadGateway,
|
||||
@@ -193,7 +207,7 @@ impl Errors {
|
||||
ContentHandler::error(
|
||||
StatusCode::GatewayTimeout,
|
||||
"Download Timeout",
|
||||
&format!("Could not fetch content within {} seconds.", FETCH_TIMEOUT.as_secs()),
|
||||
&format!("Could not fetch content within {} seconds.", FETCH_TIMEOUT),
|
||||
None,
|
||||
self.embeddable_on.clone(),
|
||||
)
|
||||
@@ -210,105 +224,67 @@ impl Errors {
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
pub struct ContentFetcherHandler<H: ContentValidator, F: Fetch> {
|
||||
fetch_control: FetchControl,
|
||||
control: Control,
|
||||
remote: Remote,
|
||||
status: FetchState,
|
||||
fetch: F,
|
||||
installer: Option<H>,
|
||||
path: EndpointPath,
|
||||
errors: Errors,
|
||||
}
|
||||
|
||||
impl ContentFetcherHandler {
|
||||
impl<H: ContentValidator, F: Fetch> ContentFetcherHandler<H, F> {
|
||||
pub fn new(
|
||||
url: String,
|
||||
path: EndpointPath,
|
||||
control: Control,
|
||||
installer: H,
|
||||
embeddable_on: Option<(String, u16)>,
|
||||
remote: Remote,
|
||||
fetch: F,
|
||||
) -> Self {
|
||||
ContentFetcherHandler {
|
||||
fetch_control: FetchControl::default(),
|
||||
control: control,
|
||||
remote: remote,
|
||||
fetch: fetch,
|
||||
status: FetchState::NotStarted(url),
|
||||
installer: Some(installer),
|
||||
path: path,
|
||||
errors: Errors {
|
||||
embeddable_on: embeddable_on,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
embeddable_on: Embeddable,
|
||||
fetch: F,
|
||||
pool: CpuPool,
|
||||
) -> Self {
|
||||
let fetch_control = FetchControl::default();
|
||||
let errors = Errors { embeddable_on };
|
||||
fn fetch_content(&self, uri: RequestUri, url: &str, installer: H) -> mpsc::Receiver<FetchState> {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
let abort = self.fetch_control.abort.clone();
|
||||
|
||||
// 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,
|
||||
errors.clone(),
|
||||
installer,
|
||||
))
|
||||
},
|
||||
// or return error
|
||||
_ => FetchState::Error(errors.method_not_allowed()),
|
||||
};
|
||||
let path = self.path.clone();
|
||||
let tx2 = tx.clone();
|
||||
let control = self.control.clone();
|
||||
let errors = self.errors.clone();
|
||||
|
||||
ContentFetcherHandler {
|
||||
fetch_control,
|
||||
status,
|
||||
errors,
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch_content<H: ContentValidator, F: Fetch>(
|
||||
pool: CpuPool,
|
||||
fetch: F,
|
||||
url: &str,
|
||||
abort: Arc<AtomicBool>,
|
||||
path: EndpointPath,
|
||||
errors: Errors,
|
||||
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| {
|
||||
let future = self.fetch.fetch_with_abort(url, abort.into()).then(move |result| {
|
||||
trace!(target: "dapps", "Fetching content finished. Starting validation: {:?}", result);
|
||||
Ok(match result {
|
||||
let new_state = 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)
|
||||
let mut handler = endpoint.to_page_handler(path);
|
||||
handler.set_uri(&uri);
|
||||
FetchState::Done(endpoint, handler)
|
||||
},
|
||||
Ok(ValidatorResponse::Streaming(stream)) => {
|
||||
Ok(ValidatorResponse::Streaming(handler)) => {
|
||||
trace!(target: "dapps", "Validation OK. Streaming response.");
|
||||
let (reading, response) = stream.into_response();
|
||||
pool.spawn(reading).forget();
|
||||
FetchState::Streaming(response)
|
||||
FetchState::Streaming(handler)
|
||||
},
|
||||
Err(e) => {
|
||||
trace!(target: "dapps", "Error while validating content: {:?}", e);
|
||||
@@ -319,55 +295,100 @@ impl ContentFetcherHandler {
|
||||
warn!(target: "dapps", "Unable to fetch content: {:?}", e);
|
||||
FetchState::Error(errors.download_error(e))
|
||||
},
|
||||
})
|
||||
};
|
||||
// Content may be resolved when the connection is already dropped.
|
||||
let _ = tx2.send(new_state);
|
||||
// Ignoring control errors
|
||||
let _ = control.ready(Next::read());
|
||||
Ok(()) as Result<(), ()>
|
||||
});
|
||||
|
||||
// make sure to run within fetch thread pool.
|
||||
Box::new(pool2.spawn(future))
|
||||
let future = self.fetch.process(future);
|
||||
// spawn to event loop
|
||||
let control = self.control.clone();
|
||||
let errors = self.errors.clone();
|
||||
self.remote.spawn_with_timeout(|| future, Duration::from_secs(FETCH_TIMEOUT), move || {
|
||||
// Notify about the timeout
|
||||
let _ = tx.send(FetchState::Error(errors.timeout_error()));
|
||||
// Ignoring control errors
|
||||
let _ = control.ready(Next::read());
|
||||
});
|
||||
|
||||
rx
|
||||
}
|
||||
}
|
||||
|
||||
impl Future for ContentFetcherHandler {
|
||||
type Item = hyper::Response;
|
||||
type Error = hyper::Error;
|
||||
impl<H: ContentValidator, F: Fetch> server::Handler<HttpStream> for ContentFetcherHandler<H, F> {
|
||||
fn on_request(&mut self, request: server::Request<HttpStream>) -> Next {
|
||||
let status = if let FetchState::NotStarted(ref url) = self.status {
|
||||
let uri = request.uri().clone();
|
||||
let installer = self.installer.take().expect("Installer always set initialy; installer used only in on_request; on_request invoked only once; qed");
|
||||
|
||||
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()));
|
||||
Some(match *request.method() {
|
||||
// Start fetching content
|
||||
Method::Get => {
|
||||
trace!(target: "dapps", "Fetching content from: {:?}", url);
|
||||
let receiver = self.fetch_content(uri, url, installer);
|
||||
FetchState::InProgress(receiver)
|
||||
},
|
||||
FetchState::Streaming(response) => {
|
||||
return Ok(futures::Async::Ready(response));
|
||||
},
|
||||
any => any,
|
||||
};
|
||||
// or return error
|
||||
_ => FetchState::Error(self.errors.method_not_allowed()),
|
||||
})
|
||||
} else { None };
|
||||
|
||||
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(self.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);
|
||||
if let Some(status) = status {
|
||||
self.fetch_control.set_status(&status);
|
||||
self.status = status;
|
||||
}
|
||||
|
||||
Next::read()
|
||||
}
|
||||
|
||||
fn on_request_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
|
||||
let (status, next) = 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.");
|
||||
(Some(FetchState::Error(self.errors.timeout_error())), Next::write())
|
||||
},
|
||||
FetchState::InProgress(ref receiver) => {
|
||||
// Check if there is an answer
|
||||
let rec = receiver.try_recv();
|
||||
match rec {
|
||||
// just return the new state
|
||||
Ok(state) => (Some(state), Next::write()),
|
||||
// wait some more
|
||||
_ => (None, Next::wait())
|
||||
}
|
||||
},
|
||||
FetchState::Error(ref mut handler) => (None, handler.on_request_readable(decoder)),
|
||||
_ => (None, Next::write()),
|
||||
};
|
||||
|
||||
if let Some(status) = status {
|
||||
self.fetch_control.set_status(&status);
|
||||
self.status = status;
|
||||
}
|
||||
|
||||
next
|
||||
}
|
||||
|
||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||
match self.status {
|
||||
FetchState::Done(_, ref mut handler) => handler.on_response(res),
|
||||
FetchState::Streaming(ref mut handler) => handler.on_response(res),
|
||||
FetchState::Error(ref mut handler) => handler.on_response(res),
|
||||
_ => Next::end(),
|
||||
}
|
||||
}
|
||||
|
||||
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||
match self.status {
|
||||
FetchState::Done(_, ref mut handler) => handler.on_response_writable(encoder),
|
||||
FetchState::Streaming(ref mut handler) => handler.on_response_writable(encoder),
|
||||
FetchState::Error(ref mut handler) => handler.on_response_writable(encoder),
|
||||
_ => Next::end(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,106 +16,75 @@
|
||||
|
||||
//! Hyper handlers implementations.
|
||||
|
||||
mod auth;
|
||||
mod content;
|
||||
mod echo;
|
||||
mod fetch;
|
||||
mod reader;
|
||||
mod redirect;
|
||||
mod streaming;
|
||||
|
||||
pub use self::auth::AuthRequiredHandler;
|
||||
pub use self::content::ContentHandler;
|
||||
pub use self::echo::EchoHandler;
|
||||
pub use self::fetch::{ContentFetcherHandler, ContentValidator, FetchControl, ValidatorResponse};
|
||||
pub use self::reader::Reader;
|
||||
pub use self::redirect::Redirection;
|
||||
pub use self::streaming::StreamingHandler;
|
||||
|
||||
use std::iter;
|
||||
use itertools::Itertools;
|
||||
use hyper::header;
|
||||
use {apps, address, Embeddable};
|
||||
use url::Url;
|
||||
use hyper::{server, header, net, uri};
|
||||
use address;
|
||||
|
||||
/// Adds security-related headers to the Response.
|
||||
pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Embeddable, allow_js_eval: bool) {
|
||||
headers.set_raw("X-XSS-Protection", "1; mode=block");
|
||||
headers.set_raw("X-Content-Type-Options", "nosniff");
|
||||
pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Option<(String, u16)>) {
|
||||
headers.set_raw("X-XSS-Protection", vec![b"1; mode=block".to_vec()]);
|
||||
headers.set_raw("X-Content-Type-Options", vec![b"nosniff".to_vec()]);
|
||||
|
||||
// Embedding header:
|
||||
if let None = embeddable_on {
|
||||
headers.set_raw("X-Frame-Options", "SAMEORIGIN");
|
||||
if let Some(embeddable_on) = embeddable_on {
|
||||
headers.set_raw(
|
||||
"X-Frame-Options",
|
||||
vec![format!("ALLOW-FROM http://{}", address(&embeddable_on)).into_bytes()]
|
||||
);
|
||||
} else {
|
||||
// TODO [ToDr] Should we be more strict here (DENY?)?
|
||||
headers.set_raw("X-Frame-Options", vec![b"SAMEORIGIN".to_vec()]);
|
||||
}
|
||||
|
||||
// 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 = embeddable_on.as_ref()
|
||||
.map(|e| e.extra_script_src.iter()
|
||||
.map(|&(ref host, port)| address(host, port))
|
||||
.join(" ")
|
||||
).unwrap_or_default();
|
||||
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.
|
||||
+ &match embeddable_on {
|
||||
Some(ref embed) => {
|
||||
let std = address(&embed.host, embed.port);
|
||||
let proxy = format!("{}.{}", apps::HOME_PAGE, embed.dapps_domain);
|
||||
let domain = format!("*.{}:{}", embed.dapps_domain, embed.port);
|
||||
|
||||
let mut ancestors = vec![std, domain, proxy]
|
||||
.into_iter()
|
||||
.chain(embed.extra_embed_on
|
||||
.iter()
|
||||
.map(|&(ref host, port)| address(host, port))
|
||||
);
|
||||
|
||||
let ancestors = if embed.host == "127.0.0.1" {
|
||||
let localhost = address("localhost", embed.port);
|
||||
ancestors.chain(iter::once(localhost)).join(" ")
|
||||
} else {
|
||||
ancestors.join(" ")
|
||||
};
|
||||
|
||||
format!("frame-ancestors {};", ancestors)
|
||||
},
|
||||
None => format!("frame-ancestors 'self';"),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/// Extracts URL part from the Request.
|
||||
pub fn extract_url(req: &server::Request<net::HttpStream>) -> Option<Url> {
|
||||
convert_uri_to_url(req.uri(), req.headers().get::<header::Host>())
|
||||
}
|
||||
|
||||
/// Extracts URL given URI and Host header.
|
||||
pub fn convert_uri_to_url(uri: &uri::RequestUri, host: Option<&header::Host>) -> Option<Url> {
|
||||
match *uri {
|
||||
uri::RequestUri::AbsoluteUri(ref url) => {
|
||||
match Url::from_generic_url(url.clone()) {
|
||||
Ok(url) => Some(url),
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
uri::RequestUri::AbsolutePath { ref path, ref query } => {
|
||||
let query = match *query {
|
||||
Some(ref query) => format!("?{}", query),
|
||||
None => "".into(),
|
||||
};
|
||||
// Attempt to prepend the Host header (mandatory in HTTP/1.1)
|
||||
let url_string = match host {
|
||||
Some(ref host) => {
|
||||
format!("http://{}:{}{}{}", host.hostname, host.port.unwrap_or(80), path, query)
|
||||
},
|
||||
None => return None,
|
||||
};
|
||||
|
||||
match Url::parse(&url_string) {
|
||||
Ok(url) => Some(url),
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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))),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,9 @@
|
||||
|
||||
//! HTTP Redirection hyper handler
|
||||
|
||||
use hyper::{self, header, StatusCode};
|
||||
use hyper::{header, server, Decoder, Encoder, Next};
|
||||
use hyper::net::HttpStream;
|
||||
use hyper::status::StatusCode;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Redirection {
|
||||
@@ -24,18 +26,36 @@ pub struct Redirection {
|
||||
}
|
||||
|
||||
impl Redirection {
|
||||
pub fn new<T: Into<String>>(url: T) -> Self {
|
||||
pub fn new(url: &str) -> Self {
|
||||
Redirection {
|
||||
to_url: url.into()
|
||||
to_url: url.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn boxed(url: &str) -> Box<Self> {
|
||||
Box::new(Self::new(url))
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<hyper::Response> for Redirection {
|
||||
fn into(self) -> hyper::Response {
|
||||
impl server::Handler<HttpStream> for Redirection {
|
||||
fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||
// 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))
|
||||
res.set_status(StatusCode::Found);
|
||||
res.headers_mut().set(header::Location(self.to_url.to_owned()));
|
||||
Next::write()
|
||||
}
|
||||
fn on_response_writable(&mut self, _encoder: &mut Encoder<HttpStream>) -> Next {
|
||||
Next::end()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -16,43 +16,86 @@
|
||||
|
||||
//! Content Stream Response
|
||||
|
||||
use std::io;
|
||||
use hyper::{self, header, mime, StatusCode};
|
||||
use std::io::{self, Read};
|
||||
|
||||
use handlers::{add_security_headers, Reader};
|
||||
use Embeddable;
|
||||
use hyper::{header, server, Decoder, Encoder, Next};
|
||||
use hyper::net::HttpStream;
|
||||
use hyper::mime::Mime;
|
||||
use hyper::status::StatusCode;
|
||||
|
||||
pub struct StreamingHandler<R> {
|
||||
initial: Vec<u8>,
|
||||
content: R,
|
||||
use handlers::add_security_headers;
|
||||
|
||||
const BUFFER_SIZE: usize = 1024;
|
||||
|
||||
pub struct StreamingHandler<R: io::Read> {
|
||||
buffer: [u8; BUFFER_SIZE],
|
||||
buffer_leftover: usize,
|
||||
status: StatusCode,
|
||||
mimetype: mime::Mime,
|
||||
safe_to_embed_on: Embeddable,
|
||||
content: io::BufReader<R>,
|
||||
mimetype: Mime,
|
||||
safe_to_embed_on: Option<(String, u16)>,
|
||||
}
|
||||
|
||||
impl<R: io::Read> StreamingHandler<R> {
|
||||
pub fn new(content: R, status: StatusCode, mimetype: mime::Mime, safe_to_embed_on: Embeddable) -> Self {
|
||||
pub fn new(content: R, status: StatusCode, mimetype: Mime, embeddable_on: Option<(String, u16)>) -> Self {
|
||||
StreamingHandler {
|
||||
initial: Vec::new(),
|
||||
content,
|
||||
status,
|
||||
mimetype,
|
||||
safe_to_embed_on,
|
||||
buffer: [0; BUFFER_SIZE],
|
||||
buffer_leftover: 0,
|
||||
status: status,
|
||||
content: io::BufReader::new(content),
|
||||
mimetype: mimetype,
|
||||
safe_to_embed_on: embeddable_on,
|
||||
}
|
||||
}
|
||||
|
||||
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(), self.safe_to_embed_on, false);
|
||||
|
||||
(reader, res)
|
||||
assert_eq!(self.buffer_leftover, 0);
|
||||
let bytes = content.as_bytes();
|
||||
self.buffer_leftover = bytes.len();
|
||||
self.buffer[0..self.buffer_leftover].copy_from_slice(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: io::Read> server::Handler<HttpStream> for StreamingHandler<R> {
|
||||
fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||
res.set_status(self.status);
|
||||
res.headers_mut().set(header::ContentType(self.mimetype.clone()));
|
||||
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.clone());
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||
fn handle_error(e: io::Error) -> Next {
|
||||
match e.kind() {
|
||||
::std::io::ErrorKind::WouldBlock => Next::write(),
|
||||
_ => Next::end(),
|
||||
}
|
||||
}
|
||||
|
||||
let write_pos = self.buffer_leftover;
|
||||
match self.content.read(&mut self.buffer[write_pos..]) {
|
||||
Err(e) => handle_error(e),
|
||||
Ok(read) => match encoder.write(&self.buffer[..write_pos + read]) {
|
||||
Err(e) => handle_error(e),
|
||||
Ok(0) => Next::end(),
|
||||
Ok(wrote) => {
|
||||
self.buffer_leftover = write_pos + read - wrote;
|
||||
if self.buffer_leftover > 0 {
|
||||
for i in self.buffer_leftover..write_pos + read {
|
||||
self.buffer.swap(i, i - self.buffer_leftover);
|
||||
}
|
||||
}
|
||||
Next::write()
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
601
dapps/src/lib.rs
601
dapps/src/lib.rs
@@ -16,305 +16,384 @@
|
||||
|
||||
//! Ethcore Webapplications for Parity
|
||||
#![warn(missing_docs)]
|
||||
#![cfg_attr(feature="nightly", feature(plugin))]
|
||||
#![cfg_attr(feature="nightly", plugin(clippy))]
|
||||
|
||||
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 hyper;
|
||||
extern crate time;
|
||||
extern crate url as url_lib;
|
||||
extern crate unicase;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
extern crate unicase;
|
||||
extern crate zip;
|
||||
|
||||
extern crate rand;
|
||||
extern crate jsonrpc_core;
|
||||
extern crate jsonrpc_http_server;
|
||||
|
||||
extern crate ethcore_bytes as bytes;
|
||||
extern crate ethereum_types;
|
||||
extern crate fetch;
|
||||
extern crate node_health;
|
||||
extern crate parity_dapps_glue as parity_dapps;
|
||||
extern crate mime_guess;
|
||||
extern crate rustc_serialize;
|
||||
extern crate ethcore_rpc;
|
||||
extern crate ethcore_util as util;
|
||||
extern crate parity_hash_fetch as hash_fetch;
|
||||
extern crate parity_ui;
|
||||
extern crate parity_ui_deprecation;
|
||||
extern crate keccak_hash as hash;
|
||||
extern crate parity_version;
|
||||
extern crate registrar;
|
||||
|
||||
#[macro_use]
|
||||
extern crate linked_hash_map;
|
||||
extern crate fetch;
|
||||
extern crate parity_dapps_glue as parity_dapps;
|
||||
extern crate futures;
|
||||
extern crate parity_reactor;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
#[macro_use]
|
||||
extern crate mime;
|
||||
#[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;
|
||||
extern crate env_logger;
|
||||
|
||||
|
||||
mod endpoint;
|
||||
mod apps;
|
||||
mod page;
|
||||
mod router;
|
||||
mod handlers;
|
||||
mod rpc;
|
||||
mod api;
|
||||
mod proxypac;
|
||||
mod url;
|
||||
mod web;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::net::SocketAddr;
|
||||
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;
|
||||
use ethcore_rpc::{Metadata};
|
||||
use fetch::{Fetch, Client as FetchClient};
|
||||
use hash_fetch::urlhint::ContractClient;
|
||||
use jsonrpc_core::Middleware;
|
||||
use jsonrpc_core::reactor::RpcHandler;
|
||||
use router::auth::{Authorization, NoAuth, HttpBasicAuth};
|
||||
use parity_reactor::Remote;
|
||||
|
||||
pub use registrar::{RegistrarClient, Asynchronous};
|
||||
pub use node_health::SyncStatus;
|
||||
use self::apps::{HOME_PAGE, DAPPS_DOMAIN};
|
||||
|
||||
/// Indicates sync status
|
||||
pub trait SyncStatus: Send + Sync {
|
||||
/// Returns true if there is a major sync happening.
|
||||
fn is_major_importing(&self) -> bool;
|
||||
}
|
||||
|
||||
impl<F> SyncStatus for F where F: Fn() -> bool + Send + Sync {
|
||||
fn is_major_importing(&self) -> bool { self() }
|
||||
}
|
||||
|
||||
/// 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>;
|
||||
/// Should return true if token is a valid web proxy access token.
|
||||
fn is_web_proxy_token_valid(&self, token: &str) -> bool;
|
||||
}
|
||||
|
||||
impl<F> WebProxyTokens for F where F: Fn(String) -> Option<Origin> + Send + Sync {
|
||||
fn domain(&self, token: &str) -> Option<Origin> { self(token.to_owned()) }
|
||||
impl<F> WebProxyTokens for F where F: Fn(String) -> bool + Send + Sync {
|
||||
fn is_web_proxy_token_valid(&self, token: &str) -> bool { self(token.to_owned()) }
|
||||
}
|
||||
|
||||
/// Current supported endpoints.
|
||||
#[derive(Default, Clone)]
|
||||
pub struct Endpoints {
|
||||
local_endpoints: Arc<RwLock<Vec<String>>>,
|
||||
endpoints: Arc<RwLock<endpoint::Endpoints>>,
|
||||
/// Webapps HTTP+RPC server build.
|
||||
pub struct ServerBuilder<T: Fetch = FetchClient> {
|
||||
dapps_path: PathBuf,
|
||||
embeddable: Option<ParentFrameSettings>,
|
||||
pool: Option<CpuPool>,
|
||||
extra_dapps: Vec<PathBuf>,
|
||||
registrar: Arc<ContractClient>,
|
||||
sync_status: Arc<SyncStatus>,
|
||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||
signer_address: Option<(String, u16)>,
|
||||
allowed_hosts: Option<Vec<String>>,
|
||||
extra_cors: Option<Vec<String>>,
|
||||
remote: Remote,
|
||||
fetch: Option<T>,
|
||||
}
|
||||
|
||||
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()
|
||||
impl ServerBuilder {
|
||||
/// Construct new dapps server
|
||||
pub fn new<P: AsRef<Path>>(dapps_path: P, registrar: Arc<ContractClient>, remote: Remote) -> Self {
|
||||
ServerBuilder {
|
||||
dapps_path: dapps_path.as_ref().to_owned(),
|
||||
extra_dapps: vec![],
|
||||
registrar: registrar,
|
||||
sync_status: Arc::new(|| false),
|
||||
web_proxy_tokens: Arc::new(|_| false),
|
||||
signer_address: None,
|
||||
allowed_hosts: Some(vec![]),
|
||||
extra_cors: None,
|
||||
remote: remote,
|
||||
fetch: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Fetch> ServerBuilder<T> {
|
||||
/// Set a fetch client to use.
|
||||
pub fn fetch<X: Fetch>(self, fetch: X) -> ServerBuilder<X> {
|
||||
ServerBuilder {
|
||||
dapps_path: self.dapps_path,
|
||||
extra_dapps: vec![],
|
||||
registrar: self.registrar,
|
||||
sync_status: self.sync_status,
|
||||
web_proxy_tokens: self.web_proxy_tokens,
|
||||
signer_address: self.signer_address,
|
||||
allowed_hosts: self.allowed_hosts,
|
||||
extra_cors: self.extra_cors,
|
||||
remote: self.remote,
|
||||
fetch: Some(fetch),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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,
|
||||
/// Change default sync status.
|
||||
pub fn sync_status(mut self, status: Arc<SyncStatus>) -> Self {
|
||||
self.sync_status = status;
|
||||
self
|
||||
}
|
||||
|
||||
/// Change default web proxy tokens validator.
|
||||
pub fn web_proxy_tokens(mut self, tokens: Arc<WebProxyTokens>) -> Self {
|
||||
self.web_proxy_tokens = tokens;
|
||||
self
|
||||
}
|
||||
|
||||
/// Change default signer port.
|
||||
pub fn signer_address(mut self, signer_address: Option<(String, u16)>) -> Self {
|
||||
self.signer_address = signer_address;
|
||||
self
|
||||
}
|
||||
|
||||
/// Change allowed hosts.
|
||||
/// `None` - All hosts are allowed
|
||||
/// `Some(whitelist)` - Allow only whitelisted hosts (+ listen address)
|
||||
pub fn allowed_hosts(mut self, allowed_hosts: Option<Vec<String>>) -> Self {
|
||||
self.allowed_hosts = allowed_hosts;
|
||||
self
|
||||
}
|
||||
|
||||
/// Extra cors headers.
|
||||
/// `None` - no additional CORS URLs
|
||||
pub fn extra_cors_headers(mut self, cors: Option<Vec<String>>) -> Self {
|
||||
self.extra_cors = cors;
|
||||
self
|
||||
}
|
||||
|
||||
/// Change extra dapps paths (apart from `dapps_path`)
|
||||
pub fn extra_dapps<P: AsRef<Path>>(mut self, extra_dapps: &[P]) -> Self {
|
||||
self.extra_dapps = extra_dapps.iter().map(|p| p.as_ref().to_owned()).collect();
|
||||
self
|
||||
}
|
||||
|
||||
/// Asynchronously start server with no authentication,
|
||||
/// returns result with `Server` handle on success or an error.
|
||||
pub fn start_unsecured_http<S: Middleware<Metadata>>(self, addr: &SocketAddr, handler: RpcHandler<Metadata, S>) -> Result<Server, ServerError> {
|
||||
let fetch = self.fetch_client()?;
|
||||
Server::start_http(
|
||||
addr,
|
||||
self.allowed_hosts,
|
||||
self.extra_cors,
|
||||
NoAuth,
|
||||
handler,
|
||||
self.dapps_path,
|
||||
self.extra_dapps,
|
||||
self.signer_address,
|
||||
self.registrar,
|
||||
self.sync_status,
|
||||
self.web_proxy_tokens,
|
||||
self.remote,
|
||||
fetch,
|
||||
)
|
||||
}
|
||||
|
||||
/// Asynchronously start server with `HTTP Basic Authentication`,
|
||||
/// return result with `Server` handle on success or an error.
|
||||
pub fn start_basic_auth_http<S: Middleware<Metadata>>(self, addr: &SocketAddr, username: &str, password: &str, handler: RpcHandler<Metadata, S>) -> Result<Server, ServerError> {
|
||||
let fetch = self.fetch_client()?;
|
||||
Server::start_http(
|
||||
addr,
|
||||
self.allowed_hosts,
|
||||
self.extra_cors,
|
||||
HttpBasicAuth::single_user(username, password),
|
||||
handler,
|
||||
self.dapps_path,
|
||||
self.extra_dapps,
|
||||
self.signer_address,
|
||||
self.registrar,
|
||||
self.sync_status,
|
||||
self.web_proxy_tokens,
|
||||
self.remote,
|
||||
fetch,
|
||||
)
|
||||
}
|
||||
|
||||
fn fetch_client(&self) -> Result<T, ServerError> {
|
||||
match self.fetch.clone() {
|
||||
Some(fetch) => Ok(fetch),
|
||||
None => T::new().map_err(|_| ServerError::FetchInitialization),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Webapps HTTP server.
|
||||
pub struct Server {
|
||||
server: Option<hyper::server::Listening>,
|
||||
panic_handler: Arc<Mutex<Option<Box<Fn() -> () + Send>>>>,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
/// Returns a list of allowed hosts or `None` if all hosts are allowed.
|
||||
fn allowed_hosts(hosts: Option<Vec<String>>, bind_address: String) -> Option<Vec<String>> {
|
||||
let mut allowed = Vec::new();
|
||||
|
||||
match hosts {
|
||||
Some(hosts) => allowed.extend_from_slice(&hosts),
|
||||
None => return None,
|
||||
}
|
||||
|
||||
// Add localhost domain as valid too if listening on loopback interface.
|
||||
allowed.push(bind_address.replace("127.0.0.1", "localhost").into());
|
||||
allowed.push(bind_address.into());
|
||||
Some(allowed)
|
||||
}
|
||||
|
||||
/// Returns a list of CORS domains for API endpoint.
|
||||
fn cors_domains(signer_address: Option<(String, u16)>, extra_cors: Option<Vec<String>>) -> Vec<String> {
|
||||
let basic_cors = match signer_address {
|
||||
Some(signer_address) => vec![
|
||||
format!("http://{}{}", HOME_PAGE, DAPPS_DOMAIN),
|
||||
format!("http://{}{}:{}", HOME_PAGE, DAPPS_DOMAIN, signer_address.1),
|
||||
format!("http://{}", address(&signer_address)),
|
||||
format!("https://{}{}", HOME_PAGE, DAPPS_DOMAIN),
|
||||
format!("https://{}{}:{}", HOME_PAGE, DAPPS_DOMAIN, signer_address.1),
|
||||
format!("https://{}", address(&signer_address)),
|
||||
],
|
||||
None => vec![],
|
||||
};
|
||||
let new_local = apps::fs::local_endpoints(&self.dapps_path, self.embeddable.clone(), 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 middleware for UI server.
|
||||
pub fn ui<F: Fetch>(
|
||||
pool: CpuPool,
|
||||
health: NodeHealth,
|
||||
dapps_domain: &str,
|
||||
registrar: Arc<RegistrarClient<Call=Asynchronous>>,
|
||||
sync_status: Arc<SyncStatus>,
|
||||
fetch: F,
|
||||
info_page_only: bool,
|
||||
) -> Self {
|
||||
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(
|
||||
hash_fetch::urlhint::URLHintContract::new(registrar),
|
||||
sync_status.clone(),
|
||||
fetch.clone(),
|
||||
pool.clone(),
|
||||
).embeddable_on(None).allow_dapps(false));
|
||||
|
||||
if info_page_only {
|
||||
let mut special = HashMap::default();
|
||||
special.insert(router::SpecialEndpoint::Home, Some(apps::ui_deprecation(pool.clone())));
|
||||
|
||||
return Middleware {
|
||||
endpoints: Default::default(),
|
||||
router: router::Router::new(
|
||||
content_fetcher,
|
||||
None,
|
||||
special,
|
||||
None,
|
||||
dapps_domain.to_owned(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
let special = {
|
||||
let mut special = special_endpoints(
|
||||
pool.clone(),
|
||||
health,
|
||||
content_fetcher.clone(),
|
||||
);
|
||||
special.insert(router::SpecialEndpoint::Home, Some(apps::ui(pool.clone())));
|
||||
special
|
||||
};
|
||||
let router = router::Router::new(
|
||||
content_fetcher,
|
||||
None,
|
||||
special,
|
||||
None,
|
||||
dapps_domain.to_owned(),
|
||||
);
|
||||
|
||||
Middleware {
|
||||
endpoints: Default::default(),
|
||||
router: router,
|
||||
match extra_cors {
|
||||
None => basic_cors,
|
||||
Some(extra_cors) => basic_cors.into_iter().chain(extra_cors).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates new Dapps server middleware.
|
||||
pub fn dapps<F: Fetch>(
|
||||
pool: CpuPool,
|
||||
health: NodeHealth,
|
||||
ui_address: Option<(String, u16)>,
|
||||
extra_embed_on: Vec<(String, u16)>,
|
||||
extra_script_src: Vec<(String, u16)>,
|
||||
fn start_http<A: Authorization + 'static, F: Fetch, T: Middleware<Metadata>>(
|
||||
addr: &SocketAddr,
|
||||
hosts: Option<Vec<String>>,
|
||||
extra_cors: Option<Vec<String>>,
|
||||
authorization: A,
|
||||
handler: RpcHandler<Metadata, T>,
|
||||
dapps_path: PathBuf,
|
||||
extra_dapps: Vec<PathBuf>,
|
||||
dapps_domain: &str,
|
||||
registrar: Arc<RegistrarClient<Call=Asynchronous>>,
|
||||
signer_address: Option<(String, u16)>,
|
||||
registrar: Arc<ContractClient>,
|
||||
sync_status: Arc<SyncStatus>,
|
||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||
remote: Remote,
|
||||
fetch: F,
|
||||
) -> Self {
|
||||
let embeddable = as_embeddable(ui_address, extra_embed_on, extra_script_src, dapps_domain);
|
||||
) -> Result<Server, ServerError> {
|
||||
let panic_handler = Arc::new(Mutex::new(None));
|
||||
let authorization = Arc::new(authorization);
|
||||
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(
|
||||
hash_fetch::urlhint::URLHintContract::new(registrar),
|
||||
sync_status.clone(),
|
||||
sync_status,
|
||||
signer_address.clone(),
|
||||
remote.clone(),
|
||||
fetch.clone(),
|
||||
pool.clone(),
|
||||
).embeddable_on(embeddable.clone()).allow_dapps(true));
|
||||
let (local_endpoints, endpoints) = apps::all_endpoints(
|
||||
dapps_path.clone(),
|
||||
extra_dapps,
|
||||
dapps_domain,
|
||||
embeddable.clone(),
|
||||
web_proxy_tokens,
|
||||
fetch.clone(),
|
||||
pool.clone(),
|
||||
);
|
||||
let endpoints = Endpoints {
|
||||
endpoints: Arc::new(RwLock::new(endpoints)),
|
||||
));
|
||||
let endpoints = Arc::new(apps::all_endpoints(
|
||||
dapps_path,
|
||||
local_endpoints: Arc::new(RwLock::new(local_endpoints)),
|
||||
embeddable: embeddable.clone(),
|
||||
pool: Some(pool.clone()),
|
||||
};
|
||||
extra_dapps,
|
||||
signer_address.clone(),
|
||||
web_proxy_tokens,
|
||||
remote.clone(),
|
||||
fetch.clone(),
|
||||
));
|
||||
let cors_domains = Self::cors_domains(signer_address.clone(), extra_cors);
|
||||
|
||||
let special = {
|
||||
let mut special = special_endpoints(
|
||||
pool.clone(),
|
||||
health,
|
||||
content_fetcher.clone(),
|
||||
);
|
||||
let special = Arc::new({
|
||||
let mut special = HashMap::new();
|
||||
special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, cors_domains.clone(), panic_handler.clone()));
|
||||
special.insert(router::SpecialEndpoint::Utils, apps::utils());
|
||||
special.insert(
|
||||
router::SpecialEndpoint::Home,
|
||||
Some(apps::ui_redirection(embeddable.clone())),
|
||||
router::SpecialEndpoint::Api,
|
||||
api::RestApi::new(cors_domains, endpoints.clone(), content_fetcher.clone())
|
||||
);
|
||||
special
|
||||
};
|
||||
});
|
||||
let hosts = Self::allowed_hosts(hosts, format!("{}", addr));
|
||||
|
||||
let router = router::Router::new(
|
||||
content_fetcher,
|
||||
Some(endpoints.clone()),
|
||||
special,
|
||||
embeddable,
|
||||
dapps_domain.to_owned(),
|
||||
);
|
||||
hyper::Server::http(addr)?
|
||||
.handle(move |ctrl| router::Router::new(
|
||||
ctrl,
|
||||
signer_address.clone(),
|
||||
content_fetcher.clone(),
|
||||
endpoints.clone(),
|
||||
special.clone(),
|
||||
authorization.clone(),
|
||||
hosts.clone(),
|
||||
))
|
||||
.map(|(l, srv)| {
|
||||
|
||||
Middleware {
|
||||
endpoints,
|
||||
router,
|
||||
::std::thread::spawn(move || {
|
||||
srv.run();
|
||||
});
|
||||
|
||||
Server {
|
||||
server: Some(l),
|
||||
panic_handler: panic_handler,
|
||||
}
|
||||
})
|
||||
.map_err(ServerError::from)
|
||||
}
|
||||
|
||||
/// Set callback for panics.
|
||||
pub fn set_panic_handler<F>(&self, handler: F) where F : Fn() -> () + Send + 'static {
|
||||
*self.panic_handler.lock().unwrap() = Some(Box::new(handler));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
/// Returns address that this server is bound to.
|
||||
pub fn addr(&self) -> &SocketAddr {
|
||||
self.server.as_ref()
|
||||
.expect("server is always Some at the start; it's consumed only when object is dropped; qed")
|
||||
.addrs()
|
||||
.first()
|
||||
.expect("You cannot start the server without binding to at least one address; qed")
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Server {
|
||||
fn drop(&mut self) {
|
||||
self.server.take().unwrap().close()
|
||||
}
|
||||
}
|
||||
|
||||
/// Webapp Server startup error
|
||||
#[derive(Debug)]
|
||||
pub enum ServerError {
|
||||
/// Wrapped `std::io::Error`
|
||||
IoError(std::io::Error),
|
||||
/// Other `hyper` error
|
||||
Other(hyper::error::Error),
|
||||
/// Fetch service initialization error
|
||||
FetchInitialization,
|
||||
}
|
||||
|
||||
impl From<hyper::error::Error> for ServerError {
|
||||
fn from(err: hyper::error::Error) -> Self {
|
||||
match err {
|
||||
hyper::error::Error::Io(e) => ServerError::IoError(e),
|
||||
e => ServerError::Other(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl http::RequestMiddleware for Middleware {
|
||||
fn on_request(&self, req: hyper::Request) -> http::RequestMiddlewareAction {
|
||||
self.router.on_request(req)
|
||||
}
|
||||
}
|
||||
|
||||
fn special_endpoints(
|
||||
pool: CpuPool,
|
||||
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::Utils, Some(apps::utils(pool)));
|
||||
special.insert(router::SpecialEndpoint::Api, Some(api::RestApi::new(
|
||||
content_fetcher,
|
||||
health,
|
||||
)));
|
||||
special
|
||||
}
|
||||
|
||||
fn address(host: &str, port: u16) -> String {
|
||||
format!("{}:{}", host, port)
|
||||
}
|
||||
|
||||
fn as_embeddable(
|
||||
ui_address: Option<(String, u16)>,
|
||||
extra_embed_on: Vec<(String, u16)>,
|
||||
extra_script_src: Vec<(String, u16)>,
|
||||
dapps_domain: &str,
|
||||
) -> Option<ParentFrameSettings> {
|
||||
ui_address.map(|(host, port)| ParentFrameSettings {
|
||||
host,
|
||||
port,
|
||||
extra_embed_on,
|
||||
extra_script_src,
|
||||
dapps_domain: dapps_domain.to_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Random filename
|
||||
fn random_filename() -> String {
|
||||
use ::rand::Rng;
|
||||
@@ -322,19 +401,49 @@ fn random_filename() -> String {
|
||||
rng.gen_ascii_chars().take(12).collect()
|
||||
}
|
||||
|
||||
type Embeddable = Option<ParentFrameSettings>;
|
||||
|
||||
/// Parent frame host and port allowed to embed the content.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ParentFrameSettings {
|
||||
/// Hostname
|
||||
pub host: String,
|
||||
/// Port
|
||||
pub port: u16,
|
||||
/// Additional URLs the dapps can be embedded on.
|
||||
pub extra_embed_on: Vec<(String, u16)>,
|
||||
/// Additional URLs the dapp scripts can be loaded from.
|
||||
pub extra_script_src: Vec<(String, u16)>,
|
||||
/// Dapps Domain (web3.site)
|
||||
pub dapps_domain: String,
|
||||
fn address(address: &(String, u16)) -> String {
|
||||
format!("{}:{}", address.0, address.1)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod util_tests {
|
||||
use super::Server;
|
||||
|
||||
#[test]
|
||||
fn should_return_allowed_hosts() {
|
||||
// given
|
||||
let bind_address = "127.0.0.1".to_owned();
|
||||
|
||||
// when
|
||||
let all = Server::allowed_hosts(None, bind_address.clone());
|
||||
let address = Server::allowed_hosts(Some(Vec::new()), bind_address.clone());
|
||||
let some = Server::allowed_hosts(Some(vec!["ethcore.io".into()]), bind_address.clone());
|
||||
|
||||
// then
|
||||
assert_eq!(all, None);
|
||||
assert_eq!(address, Some(vec!["localhost".into(), "127.0.0.1".into()]));
|
||||
assert_eq!(some, Some(vec!["ethcore.io".into(), "localhost".into(), "127.0.0.1".into()]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_cors_domains() {
|
||||
// given
|
||||
|
||||
// when
|
||||
let none = Server::cors_domains(None, None);
|
||||
let some = Server::cors_domains(Some(("127.0.0.1".into(), 18180)), None);
|
||||
let extra = Server::cors_domains(None, Some(vec!["all".to_owned()]));
|
||||
|
||||
// then
|
||||
assert_eq!(none, Vec::<String>::new());
|
||||
assert_eq!(some, vec![
|
||||
"http://parity.web3.site".to_owned(),
|
||||
"http://parity.web3.site:18180".into(),
|
||||
"http://127.0.0.1:18180".into(),
|
||||
"https://parity.web3.site".into(),
|
||||
"https://parity.web3.site:18180".into(),
|
||||
"https://127.0.0.1:18180".into()
|
||||
]);
|
||||
assert_eq!(extra, vec!["all".to_owned()]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,152 +14,142 @@
|
||||
// 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};
|
||||
use Embeddable;
|
||||
use std::sync::Arc;
|
||||
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
|
||||
use parity_dapps::{WebApp, File, Info};
|
||||
|
||||
pub struct Dapp<T: WebApp + 'static> {
|
||||
/// futures cpu pool
|
||||
pool: CpuPool,
|
||||
pub struct PageEndpoint<T : WebApp + 'static> {
|
||||
/// Content of the files
|
||||
app: T,
|
||||
pub app: Arc<T>,
|
||||
/// Prefix to strip from the path (when `None` deducted from `app_id`)
|
||||
pub prefix: Option<String>,
|
||||
/// Safe to be loaded in frame by other origin. (use wisely!)
|
||||
safe_to_embed_on: Embeddable,
|
||||
safe_to_embed_on: Option<(String, u16)>,
|
||||
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 {
|
||||
impl<T: WebApp + 'static> PageEndpoint<T> {
|
||||
/// Creates new `PageEndpoint` for builtin (compile time) Dapp.
|
||||
pub fn new(app: T) -> Self {
|
||||
let info = app.info();
|
||||
Dapp {
|
||||
pool,
|
||||
app,
|
||||
PageEndpoint {
|
||||
app: Arc::new(app),
|
||||
prefix: None,
|
||||
safe_to_embed_on: None,
|
||||
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 {
|
||||
/// Create new `PageEndpoint` and specify prefix that should be removed before looking for a file.
|
||||
/// It's used only for special endpoints (i.e. `/parity-utils/`)
|
||||
/// So `/parity-utils/inject.js` will be resolved to `/inject.js` is prefix is set.
|
||||
pub fn with_prefix(app: T, prefix: String) -> Self {
|
||||
let info = app.info();
|
||||
Dapp {
|
||||
pool,
|
||||
app,
|
||||
PageEndpoint {
|
||||
app: Arc::new(app),
|
||||
prefix: Some(prefix),
|
||||
safe_to_embed_on: None,
|
||||
info: EndpointInfo::from(info),
|
||||
fallback_to_index_html: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates new `Dapp` which can be safely used in iframe
|
||||
/// Creates new `PageEndpoint` which can be safely used in iframe
|
||||
/// even from different origin. It might be dangerous (clickjacking).
|
||||
/// Use wisely!
|
||||
pub fn new_safe_to_embed(pool: CpuPool, app: T, address: Embeddable) -> Self {
|
||||
pub fn new_safe_to_embed(app: T, address: Option<(String, u16)>) -> Self {
|
||||
let info = app.info();
|
||||
Dapp {
|
||||
pool,
|
||||
app,
|
||||
PageEndpoint {
|
||||
app: Arc::new(app),
|
||||
prefix: None,
|
||||
safe_to_embed_on: address,
|
||||
info: EndpointInfo::from(info),
|
||||
fallback_to_index_html: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
impl<T: WebApp> Endpoint for PageEndpoint<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,
|
||||
fn to_handler(&self, path: EndpointPath) -> Box<Handler> {
|
||||
Box::new(handler::PageHandler {
|
||||
app: BuiltinDapp::new(self.app.clone()),
|
||||
prefix: self.prefix.clone(),
|
||||
path: path,
|
||||
file: handler::ServedFile::new(self.safe_to_embed_on.clone()),
|
||||
cache: PageCache::Disabled,
|
||||
safe_to_embed_on: self.safe_to_embed_on.clone(),
|
||||
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]>,
|
||||
struct BuiltinDapp<T: WebApp + 'static> {
|
||||
app: Arc<T>,
|
||||
}
|
||||
|
||||
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
|
||||
impl<T: WebApp + 'static> BuiltinDapp<T> {
|
||||
fn new(app: Arc<T>) -> Self {
|
||||
BuiltinDapp {
|
||||
app: app,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: WebApp + 'static> handler::Dapp for BuiltinDapp<T> {
|
||||
type DappFile = BuiltinDappFile<T>;
|
||||
|
||||
fn file(&self, path: &str) -> Option<Self::DappFile> {
|
||||
self.app.file(path).map(|_| {
|
||||
BuiltinDappFile {
|
||||
app: self.app.clone(),
|
||||
path: path.into(),
|
||||
write_pos: 0,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct BuiltinDappFile<T: WebApp + 'static> {
|
||||
app: Arc<T>,
|
||||
path: String,
|
||||
write_pos: usize,
|
||||
}
|
||||
|
||||
impl<T: WebApp + 'static> BuiltinDappFile<T> {
|
||||
fn file(&self) -> &File {
|
||||
self.app.file(&self.path).expect("Check is done when structure is created.")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: WebApp + 'static> handler::DappFile for BuiltinDappFile<T> {
|
||||
fn content_type(&self) -> &str {
|
||||
self.file().content_type
|
||||
}
|
||||
|
||||
fn is_drained(&self) -> bool {
|
||||
self.write_pos == self.file().content.len()
|
||||
}
|
||||
|
||||
fn next_chunk(&mut self) -> &[u8] {
|
||||
&self.file().content[self.write_pos..]
|
||||
}
|
||||
|
||||
fn bytes_written(&mut self, bytes: usize) {
|
||||
self.write_pos += bytes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,26 +14,60 @@
|
||||
// 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::{self, Mime};
|
||||
use time::{self, Duration};
|
||||
|
||||
use apps;
|
||||
use handlers::{Reader, ContentHandler, add_security_headers};
|
||||
use {Embeddable};
|
||||
use hyper::header;
|
||||
use hyper::server;
|
||||
use hyper::uri::RequestUri;
|
||||
use hyper::net::HttpStream;
|
||||
use hyper::status::StatusCode;
|
||||
use hyper::{Decoder, Encoder, Next};
|
||||
use endpoint::EndpointPath;
|
||||
use handlers::{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;
|
||||
|
||||
pub trait DappFile: Send {
|
||||
/// Returns a content-type of this file.
|
||||
fn content_type(&self) -> &Mime;
|
||||
fn content_type(&self) -> &str;
|
||||
|
||||
/// Convert this file into io::Read instance.
|
||||
fn into_reader(self) -> Self::Reader where Self: Sized;
|
||||
/// Checks if all bytes from that file were written.
|
||||
fn is_drained(&self) -> bool;
|
||||
|
||||
/// Fetch next chunk to write to the client.
|
||||
fn next_chunk(&mut self) -> &[u8];
|
||||
|
||||
/// How many files have been written to the client.
|
||||
fn bytes_written(&mut self, bytes: usize);
|
||||
}
|
||||
|
||||
/// Dapp as a (dynamic) set of files.
|
||||
pub trait Dapp: Send + 'static {
|
||||
/// File type
|
||||
type DappFile: DappFile;
|
||||
|
||||
/// Returns file under given path.
|
||||
fn file(&self, path: &str) -> Option<Self::DappFile>;
|
||||
}
|
||||
|
||||
/// Currently served by `PageHandler` file
|
||||
pub enum ServedFile<T: Dapp> {
|
||||
/// File from dapp
|
||||
File(T::DappFile),
|
||||
/// Error (404)
|
||||
Error(ContentHandler),
|
||||
}
|
||||
|
||||
impl<T: Dapp> ServedFile<T> {
|
||||
pub fn new(embeddable_on: Option<(String, u16)>) -> Self {
|
||||
ServedFile::Error(ContentHandler::error(
|
||||
StatusCode::NotFound,
|
||||
"404 Not Found",
|
||||
"Requested dapp resource was not found.",
|
||||
None,
|
||||
embeddable_on,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Defines what cache headers should be appended to returned resources.
|
||||
@@ -49,68 +83,194 @@ impl Default for PageCache {
|
||||
}
|
||||
}
|
||||
|
||||
/// A generic type for `PageHandler` allowing to set the URL.
|
||||
/// Used by dapps fetching to set the URL after the content was downloaded.
|
||||
pub trait PageHandlerWaiting: server::Handler<HttpStream> + Send {
|
||||
fn set_uri(&mut self, uri: &RequestUri);
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
pub struct PageHandler<T: Dapp> {
|
||||
/// A Dapp.
|
||||
pub app: T,
|
||||
/// File currently being served
|
||||
pub file: Option<T>,
|
||||
pub file: ServedFile<T>,
|
||||
/// Optional prefix to strip from path.
|
||||
pub prefix: Option<String>,
|
||||
/// Requested path.
|
||||
pub path: EndpointPath,
|
||||
/// Flag indicating if the file can be safely embeded (put in iframe).
|
||||
pub safe_to_embed_on: Embeddable,
|
||||
pub safe_to_embed_on: Option<(String, u16)>,
|
||||
/// 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,
|
||||
self.safe_to_embed_on,
|
||||
).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.safe_to_embed_on, self.allow_js_eval);
|
||||
}
|
||||
|
||||
let initial_content = if file.content_type().to_owned() == mime::TEXT_HTML {
|
||||
let content = &format!(
|
||||
r#"<script src="/{}/inject.js"></script>"#,
|
||||
apps::UTILS_PATH,
|
||||
);
|
||||
|
||||
content.as_bytes().to_vec()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
let (reader, body) = Reader::pair(file.into_reader(), initial_content);
|
||||
res.set_body(body);
|
||||
(Some(reader), res)
|
||||
impl<T: Dapp> PageHandlerWaiting for PageHandler<T> {
|
||||
fn set_uri(&mut self, uri: &RequestUri) {
|
||||
trace!(target: "dapps", "Setting URI: {:?}", uri);
|
||||
self.file = match *uri {
|
||||
RequestUri::AbsolutePath { ref path, .. } => {
|
||||
self.app.file(&self.extract_path(path))
|
||||
},
|
||||
RequestUri::AbsoluteUri(ref url) => {
|
||||
self.app.file(&self.extract_path(url.path()))
|
||||
},
|
||||
_ => None,
|
||||
}.map_or_else(|| ServedFile::new(self.safe_to_embed_on.clone()), |f| ServedFile::File(f));
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Dapp> PageHandler<T> {
|
||||
fn extract_path(&self, path: &str) -> String {
|
||||
let app_id = &self.path.app_id;
|
||||
let prefix = "/".to_owned() + self.prefix.as_ref().unwrap_or(app_id);
|
||||
let prefix_with_slash = prefix.clone() + "/";
|
||||
let query_pos = path.find('?').unwrap_or_else(|| path.len());
|
||||
|
||||
// Index file support
|
||||
match path == "/" || path == &prefix || path == &prefix_with_slash {
|
||||
true => "index.html".to_owned(),
|
||||
false => if path.starts_with(&prefix_with_slash) {
|
||||
path[prefix_with_slash.len()..query_pos].to_owned()
|
||||
} else if path.starts_with("/") {
|
||||
path[1..query_pos].to_owned()
|
||||
} else {
|
||||
path[0..query_pos].to_owned()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Dapp> server::Handler<HttpStream> for PageHandler<T> {
|
||||
fn on_request(&mut self, req: server::Request<HttpStream>) -> Next {
|
||||
self.set_uri(req.uri());
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||
match self.file {
|
||||
ServedFile::File(ref f) => {
|
||||
res.set_status(StatusCode::Ok);
|
||||
|
||||
if let PageCache::Enabled = self.cache {
|
||||
let mut headers = res.headers_mut();
|
||||
let validity = Duration::days(365);
|
||||
headers.set(header::CacheControl(vec![
|
||||
header::CacheDirective::Public,
|
||||
header::CacheDirective::MaxAge(validity.num_seconds() as u32),
|
||||
]));
|
||||
headers.set(header::Expires(header::HttpDate(time::now() + validity)));
|
||||
}
|
||||
|
||||
match f.content_type().parse() {
|
||||
Ok(mime) => res.headers_mut().set(header::ContentType(mime)),
|
||||
Err(()) => debug!(target: "dapps", "invalid MIME type: {}", f.content_type()),
|
||||
}
|
||||
|
||||
// Security headers:
|
||||
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.clone());
|
||||
Next::write()
|
||||
},
|
||||
ServedFile::Error(ref mut handler) => {
|
||||
handler.on_response(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||
match self.file {
|
||||
ServedFile::Error(ref mut handler) => handler.on_response_writable(encoder),
|
||||
ServedFile::File(ref f) if f.is_drained() => Next::end(),
|
||||
ServedFile::File(ref mut f) => match encoder.write(f.next_chunk()) {
|
||||
Ok(bytes) => {
|
||||
f.bytes_written(bytes);
|
||||
Next::write()
|
||||
},
|
||||
Err(e) => match e.kind() {
|
||||
::std::io::ErrorKind::WouldBlock => Next::write(),
|
||||
_ => Next::end(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
pub struct TestWebAppFile;
|
||||
|
||||
impl DappFile for TestWebAppFile {
|
||||
fn content_type(&self) -> &str {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn is_drained(&self) -> bool {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn next_chunk(&mut self) -> &[u8] {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn bytes_written(&mut self, _bytes: usize) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TestWebapp;
|
||||
|
||||
impl Dapp for TestWebapp {
|
||||
type DappFile = TestWebAppFile;
|
||||
|
||||
fn file(&self, _path: &str) -> Option<Self::DappFile> {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_extract_path_with_appid() {
|
||||
|
||||
// given
|
||||
let path1 = "/";
|
||||
let path2= "/test.css";
|
||||
let path3 = "/app/myfile.txt";
|
||||
let path4 = "/app/myfile.txt?query=123";
|
||||
let page_handler = PageHandler {
|
||||
app: test::TestWebapp,
|
||||
prefix: None,
|
||||
path: EndpointPath {
|
||||
app_id: "app".to_owned(),
|
||||
app_params: vec![],
|
||||
host: "".to_owned(),
|
||||
port: 8080,
|
||||
using_dapps_domains: true,
|
||||
},
|
||||
file: ServedFile::new(None),
|
||||
cache: Default::default(),
|
||||
safe_to_embed_on: None,
|
||||
};
|
||||
|
||||
// when
|
||||
let res1 = page_handler.extract_path(path1);
|
||||
let res2 = page_handler.extract_path(path2);
|
||||
let res3 = page_handler.extract_path(path3);
|
||||
let res4 = page_handler.extract_path(path4);
|
||||
|
||||
// then
|
||||
assert_eq!(&res1, "index.html");
|
||||
assert_eq!(&res2, "test.css");
|
||||
assert_eq!(&res3, "myfile.txt");
|
||||
assert_eq!(&res4, "myfile.txt");
|
||||
}
|
||||
|
||||
@@ -15,56 +15,39 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use mime_guess;
|
||||
use std::{fs, fmt};
|
||||
use std::io::{Seek, Read, SeekFrom};
|
||||
use std::fs;
|
||||
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;
|
||||
use Embeddable;
|
||||
use page::handler::{self, PageCache, PageHandlerWaiting};
|
||||
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
|
||||
use mime::Mime;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Dapp {
|
||||
pool: CpuPool,
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LocalPageEndpoint {
|
||||
path: PathBuf,
|
||||
mime: Option<Mime>,
|
||||
info: Option<EndpointInfo>,
|
||||
cache: PageCache,
|
||||
embeddable_on: Embeddable,
|
||||
embeddable_on: Option<(String, u16)>,
|
||||
}
|
||||
|
||||
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)
|
||||
.field("embeddable_on", &self.embeddable_on)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Dapp {
|
||||
pub fn new(pool: CpuPool, path: PathBuf, info: EndpointInfo, cache: PageCache, embeddable_on: Embeddable) -> Self {
|
||||
Dapp {
|
||||
pool,
|
||||
path,
|
||||
impl LocalPageEndpoint {
|
||||
pub fn new(path: PathBuf, info: EndpointInfo, cache: PageCache, embeddable_on: Option<(String, u16)>) -> Self {
|
||||
LocalPageEndpoint {
|
||||
path: path,
|
||||
mime: None,
|
||||
info: Some(info),
|
||||
cache,
|
||||
embeddable_on,
|
||||
cache: cache,
|
||||
embeddable_on: embeddable_on,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn single_file(pool: CpuPool, path: PathBuf, mime: Mime, cache: PageCache) -> Self {
|
||||
Dapp {
|
||||
pool,
|
||||
path,
|
||||
pub fn single_file(path: PathBuf, mime: Mime, cache: PageCache) -> Self {
|
||||
LocalPageEndpoint {
|
||||
path: path,
|
||||
mime: Some(mime),
|
||||
info: None,
|
||||
cache,
|
||||
cache: cache,
|
||||
embeddable_on: None,
|
||||
}
|
||||
}
|
||||
@@ -73,76 +56,125 @@ impl Dapp {
|
||||
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());
|
||||
fn page_handler_with_mime(&self, path: EndpointPath, mime: &Mime) -> handler::PageHandler<LocalSingleFile> {
|
||||
handler::PageHandler {
|
||||
app: LocalSingleFile { path: self.path.clone(), mime: format!("{}", mime) },
|
||||
prefix: None,
|
||||
path: path,
|
||||
file: handler::ServedFile::new(None),
|
||||
safe_to_embed_on: self.embeddable_on.clone(),
|
||||
cache: self.cache,
|
||||
}
|
||||
|
||||
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,
|
||||
fn page_handler(&self, path: EndpointPath) -> handler::PageHandler<LocalDapp> {
|
||||
handler::PageHandler {
|
||||
app: LocalDapp { path: self.path.clone() },
|
||||
prefix: None,
|
||||
path: path,
|
||||
file: handler::ServedFile::new(None),
|
||||
safe_to_embed_on: self.embeddable_on.clone(),
|
||||
allow_js_eval: self.info.as_ref().and_then(|x| x.allow_js_eval).unwrap_or(false),
|
||||
}.into_response();
|
||||
cache: self.cache,
|
||||
}
|
||||
}
|
||||
|
||||
self.pool.spawn(reader).forget();
|
||||
|
||||
Box::new(future::ok(response))
|
||||
pub fn to_page_handler(&self, path: EndpointPath) -> Box<PageHandlerWaiting> {
|
||||
if let Some(ref mime) = self.mime {
|
||||
Box::new(self.page_handler_with_mime(path, mime))
|
||||
} else {
|
||||
Box::new(self.page_handler(path))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Endpoint for Dapp {
|
||||
impl Endpoint for LocalPageEndpoint {
|
||||
fn info(&self) -> Option<&EndpointInfo> {
|
||||
self.info.as_ref()
|
||||
}
|
||||
|
||||
fn respond(&self, path: EndpointPath, _req: Request) -> Response {
|
||||
self.to_response(&path)
|
||||
fn to_handler(&self, path: EndpointPath) -> Box<Handler> {
|
||||
if let Some(ref mime) = self.mime {
|
||||
Box::new(self.page_handler_with_mime(path, mime))
|
||||
} else {
|
||||
Box::new(self.page_handler(path))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct LocalSingleFile {
|
||||
path: PathBuf,
|
||||
mime: String,
|
||||
}
|
||||
|
||||
impl handler::Dapp for LocalSingleFile {
|
||||
type DappFile = LocalFile;
|
||||
|
||||
fn file(&self, _path: &str) -> Option<Self::DappFile> {
|
||||
LocalFile::from_path(&self.path, Some(&self.mime))
|
||||
}
|
||||
}
|
||||
|
||||
struct LocalDapp {
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
impl handler::Dapp for LocalDapp {
|
||||
type DappFile = LocalFile;
|
||||
|
||||
fn file(&self, file_path: &str) -> Option<Self::DappFile> {
|
||||
let mut path = self.path.clone();
|
||||
for part in file_path.split('/') {
|
||||
path.push(part);
|
||||
}
|
||||
LocalFile::from_path(&path, None)
|
||||
}
|
||||
}
|
||||
|
||||
struct LocalFile {
|
||||
content_type: Mime,
|
||||
content_type: String,
|
||||
buffer: [u8; 4096],
|
||||
file: fs::File,
|
||||
len: u64,
|
||||
pos: u64,
|
||||
}
|
||||
|
||||
impl LocalFile {
|
||||
fn from_path<P: AsRef<Path>>(path: P, content_type: Mime) -> Option<Self> {
|
||||
trace!(target: "dapps", "Local file: {:?}", path.as_ref());
|
||||
fn from_path<P: AsRef<Path>>(path: P, mime: Option<&str>) -> Option<Self> {
|
||||
// Check if file exists
|
||||
fs::File::open(&path).ok().map(|file| {
|
||||
let content_type = mime.map(|mime| mime.to_owned())
|
||||
.unwrap_or_else(|| mime_guess::guess_mime_type(path).to_string());
|
||||
let len = file.metadata().ok().map_or(0, |meta| meta.len());
|
||||
LocalFile {
|
||||
content_type,
|
||||
file,
|
||||
content_type: content_type,
|
||||
buffer: [0; 4096],
|
||||
file: file,
|
||||
pos: 0,
|
||||
len: len,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl handler::DappFile for LocalFile {
|
||||
type Reader = fs::File;
|
||||
|
||||
fn content_type(&self) -> &Mime {
|
||||
fn content_type(&self) -> &str {
|
||||
&self.content_type
|
||||
}
|
||||
|
||||
fn into_reader(self) -> Self::Reader {
|
||||
self.file
|
||||
fn is_drained(&self) -> bool {
|
||||
self.pos == self.len
|
||||
}
|
||||
|
||||
fn next_chunk(&mut self) -> &[u8] {
|
||||
let _ = self.file.seek(SeekFrom::Start(self.pos));
|
||||
if let Ok(n) = self.file.read(&mut self.buffer) {
|
||||
&self.buffer[0..n]
|
||||
} else {
|
||||
&self.buffer[0..0]
|
||||
}
|
||||
}
|
||||
|
||||
fn bytes_written(&mut self, bytes: usize) {
|
||||
self.pos += bytes as u64;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,11 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
pub mod builtin;
|
||||
pub mod local;
|
||||
mod builtin;
|
||||
mod local;
|
||||
mod handler;
|
||||
|
||||
pub use self::handler::PageCache;
|
||||
pub use self::local::LocalPageEndpoint;
|
||||
pub use self::builtin::PageEndpoint;
|
||||
pub use self::handler::{PageCache, PageHandlerWaiting};
|
||||
|
||||
|
||||
@@ -16,40 +16,39 @@
|
||||
|
||||
//! Serving ProxyPac file
|
||||
|
||||
use apps::HOME_PAGE;
|
||||
use endpoint::{Endpoint, Request, Response, EndpointPath};
|
||||
use futures::future;
|
||||
use endpoint::{Endpoint, Handler, EndpointPath};
|
||||
use handlers::ContentHandler;
|
||||
use hyper::mime;
|
||||
use {address, Embeddable};
|
||||
use apps::{HOME_PAGE, DAPPS_DOMAIN};
|
||||
use address;
|
||||
|
||||
pub struct ProxyPac {
|
||||
embeddable: Embeddable,
|
||||
dapps_domain: String,
|
||||
signer_address: Option<(String, u16)>,
|
||||
}
|
||||
|
||||
impl ProxyPac {
|
||||
pub fn boxed(embeddable: Embeddable, dapps_domain: String) -> Box<Endpoint> {
|
||||
Box::new(ProxyPac { embeddable, dapps_domain })
|
||||
pub fn boxed(signer_address: Option<(String, u16)>) -> Box<Endpoint> {
|
||||
Box::new(ProxyPac {
|
||||
signer_address: signer_address
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Endpoint for ProxyPac {
|
||||
fn respond(&self, path: EndpointPath, _req: Request) -> Response {
|
||||
let ui = self.embeddable
|
||||
fn to_handler(&self, path: EndpointPath) -> Box<Handler> {
|
||||
let signer = self.signer_address
|
||||
.as_ref()
|
||||
.map(|ref parent| address(&parent.host, parent.port))
|
||||
.map(address)
|
||||
.unwrap_or_else(|| format!("{}:{}", path.host, path.port));
|
||||
|
||||
let content = format!(
|
||||
r#"
|
||||
function FindProxyForURL(url, host) {{
|
||||
if (shExpMatch(host, "{0}.{1}"))
|
||||
if (shExpMatch(host, "{0}{1}"))
|
||||
{{
|
||||
return "PROXY {4}";
|
||||
}}
|
||||
|
||||
if (shExpMatch(host, "*.{1}"))
|
||||
if (shExpMatch(host, "*{1}"))
|
||||
{{
|
||||
return "PROXY {2}:{3}";
|
||||
}}
|
||||
@@ -57,11 +56,9 @@ function FindProxyForURL(url, host) {{
|
||||
return "DIRECT";
|
||||
}}
|
||||
"#,
|
||||
HOME_PAGE, self.dapps_domain, path.host, path.port, ui);
|
||||
HOME_PAGE, DAPPS_DOMAIN, path.host, path.port, signer);
|
||||
|
||||
Box::new(future::ok(
|
||||
ContentHandler::ok(content, mime::TEXT_JAVASCRIPT).into()
|
||||
))
|
||||
Box::new(ContentHandler::ok(content, mime!(Application/Javascript)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,417 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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;
|
||||
use Embeddable;
|
||||
|
||||
/// Special endpoints are accessible on every domain (every dapp)
|
||||
#[derive(Debug, PartialEq, Hash, Eq)]
|
||||
pub enum SpecialEndpoint {
|
||||
Rpc,
|
||||
Api,
|
||||
Utils,
|
||||
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>>>,
|
||||
embeddable_on: Embeddable,
|
||||
dapps_domain: String,
|
||||
}
|
||||
|
||||
impl Router {
|
||||
fn resolve_request(&self, req: hyper::Request, refresh_dapps: bool) -> (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_utils = endpoint.1 == SpecialEndpoint::Utils;
|
||||
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);
|
||||
|
||||
(is_utils, 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,
|
||||
self.embeddable_on.clone(),
|
||||
).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,
|
||||
self.embeddable_on.clone(),
|
||||
).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 (is_utils, response) = self.resolve_request(req, self.endpoints.is_some());
|
||||
match response {
|
||||
Response::Some(response) => http::RequestMiddlewareAction::Respond {
|
||||
should_validate_hosts: !is_utils,
|
||||
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>>>,
|
||||
embeddable_on: Embeddable,
|
||||
dapps_domain: String,
|
||||
) -> Self {
|
||||
Router {
|
||||
endpoints: endpoints,
|
||||
fetch: content_fetcher,
|
||||
special: special,
|
||||
embeddable_on: embeddable_on,
|
||||
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::UTILS_PATH => SpecialEndpoint::Utils,
|
||||
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)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
extract_endpoint(&"http://my.status.web3.site/parity-utils/inject.js".parse().unwrap(), None, dapps_domain),
|
||||
(Some(EndpointPath {
|
||||
app_id: "status".to_owned(),
|
||||
app_params: vec!["my".into(), "inject.js".into()],
|
||||
query: None,
|
||||
host: "my.status.web3.site".to_owned(),
|
||||
port: 80,
|
||||
using_dapps_domains: true,
|
||||
}), SpecialEndpoint::Utils)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
extract_endpoint(&"http://my.status.web3.site/inject.js".parse().unwrap(), None, dapps_domain),
|
||||
(Some(EndpointPath {
|
||||
app_id: "status".to_owned(),
|
||||
app_params: vec!["my".into(), "inject.js".into()],
|
||||
query: None,
|
||||
host: "my.status.web3.site".to_owned(),
|
||||
port: 80,
|
||||
using_dapps_domains: true,
|
||||
}), SpecialEndpoint::None)
|
||||
);
|
||||
|
||||
// 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)
|
||||
);
|
||||
}
|
||||
}
|
||||
106
dapps/src/router/auth.rs
Normal file
106
dapps/src/router/auth.rs
Normal file
@@ -0,0 +1,106 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! HTTP Authorization implementations
|
||||
|
||||
use std::collections::HashMap;
|
||||
use hyper::{server, net, header, status};
|
||||
use endpoint::Handler;
|
||||
use handlers::{AuthRequiredHandler, ContentHandler};
|
||||
|
||||
/// Authorization result
|
||||
pub enum Authorized {
|
||||
/// Authorization was successful.
|
||||
Yes,
|
||||
/// Unsuccessful authorization. Handler for further work is returned.
|
||||
No(Box<Handler>),
|
||||
}
|
||||
|
||||
/// Authorization interface
|
||||
pub trait Authorization : Send + Sync {
|
||||
/// Checks if authorization is valid.
|
||||
fn is_authorized(&self, req: &server::Request<net::HttpStream>)-> Authorized;
|
||||
}
|
||||
|
||||
/// HTTP Basic Authorization handler
|
||||
pub struct HttpBasicAuth {
|
||||
users: HashMap<String, String>,
|
||||
}
|
||||
|
||||
/// No-authorization implementation (authorization disabled)
|
||||
pub struct NoAuth;
|
||||
|
||||
impl Authorization for NoAuth {
|
||||
fn is_authorized(&self, _req: &server::Request<net::HttpStream>)-> Authorized {
|
||||
Authorized::Yes
|
||||
}
|
||||
}
|
||||
|
||||
impl Authorization for HttpBasicAuth {
|
||||
fn is_authorized(&self, req: &server::Request<net::HttpStream>) -> Authorized {
|
||||
let auth = self.check_auth(&req);
|
||||
|
||||
match auth {
|
||||
Access::Denied => {
|
||||
Authorized::No(Box::new(ContentHandler::error(
|
||||
status::StatusCode::Unauthorized,
|
||||
"Unauthorized",
|
||||
"You need to provide valid credentials to access this page.",
|
||||
None,
|
||||
None,
|
||||
)))
|
||||
},
|
||||
Access::AuthRequired => {
|
||||
Authorized::No(Box::new(AuthRequiredHandler))
|
||||
},
|
||||
Access::Granted => {
|
||||
Authorized::Yes
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Access {
|
||||
Granted,
|
||||
Denied,
|
||||
AuthRequired,
|
||||
}
|
||||
|
||||
impl HttpBasicAuth {
|
||||
/// Creates `HttpBasicAuth` instance with only one user.
|
||||
pub fn single_user(username: &str, password: &str) -> Self {
|
||||
let mut users = HashMap::new();
|
||||
users.insert(username.to_owned(), password.to_owned());
|
||||
HttpBasicAuth {
|
||||
users: users
|
||||
}
|
||||
}
|
||||
|
||||
fn is_authorized(&self, username: &str, password: &str) -> bool {
|
||||
self.users.get(&username.to_owned()).map_or(false, |pass| pass == password)
|
||||
}
|
||||
|
||||
fn check_auth(&self, req: &server::Request<net::HttpStream>) -> Access {
|
||||
match req.headers().get::<header::Authorization<header::Basic>>() {
|
||||
Some(&header::Authorization(
|
||||
header::Basic { ref username, password: Some(ref password) }
|
||||
)) if self.is_authorized(username, password) => Access::Granted,
|
||||
Some(_) => Access::Denied,
|
||||
None => Access::AuthRequired,
|
||||
}
|
||||
}
|
||||
}
|
||||
47
dapps/src/router/host_validation.rs
Normal file
47
dapps/src/router/host_validation.rs
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
use apps::DAPPS_DOMAIN;
|
||||
use hyper::{server, header, StatusCode};
|
||||
use hyper::net::HttpStream;
|
||||
|
||||
use jsonrpc_http_server::{is_host_header_valid};
|
||||
use handlers::ContentHandler;
|
||||
|
||||
pub fn is_valid(request: &server::Request<HttpStream>, allowed_hosts: &[String], endpoints: Vec<String>) -> bool {
|
||||
let mut endpoints = endpoints.iter()
|
||||
.map(|endpoint| format!("{}{}", endpoint, DAPPS_DOMAIN))
|
||||
.collect::<Vec<String>>();
|
||||
endpoints.extend_from_slice(allowed_hosts);
|
||||
|
||||
let header_valid = is_host_header_valid(request, &endpoints);
|
||||
|
||||
match (header_valid, request.headers().get::<header::Host>()) {
|
||||
(true, _) => true,
|
||||
(_, Some(host)) => host.hostname.ends_with(DAPPS_DOMAIN),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn host_invalid_response() -> Box<server::Handler<HttpStream> + Send> {
|
||||
Box::new(ContentHandler::error(StatusCode::Forbidden,
|
||||
"Current Host Is Disallowed",
|
||||
"You are trying to access your node using incorrect address.",
|
||||
Some("Use allowed URL or specify different <code>hosts</code> CLI options."),
|
||||
None,
|
||||
))
|
||||
}
|
||||
369
dapps/src/router/mod.rs
Normal file
369
dapps/src/router/mod.rs
Normal file
@@ -0,0 +1,369 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Router implementation
|
||||
//! Processes request handling authorization and dispatching it to proper application.
|
||||
|
||||
pub mod auth;
|
||||
mod host_validation;
|
||||
|
||||
use address;
|
||||
use std::cmp;
|
||||
use std::sync::Arc;
|
||||
use std::collections::HashMap;
|
||||
use url::{Url, Host};
|
||||
use hyper::{self, server, header, Next, Encoder, Decoder, Control, StatusCode};
|
||||
use hyper::net::HttpStream;
|
||||
use apps::{self, DAPPS_DOMAIN};
|
||||
use apps::fetcher::Fetcher;
|
||||
use endpoint::{Endpoint, Endpoints, EndpointPath};
|
||||
use handlers::{self, Redirection, ContentHandler};
|
||||
use self::auth::{Authorization, Authorized};
|
||||
|
||||
/// Special endpoints are accessible on every domain (every dapp)
|
||||
#[derive(Debug, PartialEq, Hash, Eq)]
|
||||
pub enum SpecialEndpoint {
|
||||
Rpc,
|
||||
Api,
|
||||
Utils,
|
||||
None,
|
||||
}
|
||||
|
||||
pub struct Router<A: Authorization + 'static> {
|
||||
control: Option<Control>,
|
||||
signer_address: Option<(String, u16)>,
|
||||
endpoints: Arc<Endpoints>,
|
||||
fetch: Arc<Fetcher>,
|
||||
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
|
||||
authorization: Arc<A>,
|
||||
allowed_hosts: Option<Vec<String>>,
|
||||
handler: Box<server::Handler<HttpStream> + Send>,
|
||||
}
|
||||
|
||||
impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
|
||||
|
||||
fn on_request(&mut self, req: server::Request<HttpStream>) -> Next {
|
||||
// Choose proper handler depending on path / domain
|
||||
let url = handlers::extract_url(&req);
|
||||
let endpoint = extract_endpoint(&url);
|
||||
let referer = extract_referer_endpoint(&req);
|
||||
let is_utils = endpoint.1 == SpecialEndpoint::Utils;
|
||||
let is_get_request = *req.method() == hyper::Method::Get;
|
||||
|
||||
trace!(target: "dapps", "Routing request to {:?}. Details: {:?}", url, req);
|
||||
|
||||
// Validate Host header
|
||||
if let Some(ref hosts) = self.allowed_hosts {
|
||||
trace!(target: "dapps", "Validating host headers against: {:?}", hosts);
|
||||
let is_valid = is_utils || host_validation::is_valid(&req, hosts, self.endpoints.keys().cloned().collect());
|
||||
if !is_valid {
|
||||
debug!(target: "dapps", "Rejecting invalid host header.");
|
||||
self.handler = host_validation::host_invalid_response();
|
||||
return self.handler.on_request(req);
|
||||
}
|
||||
}
|
||||
|
||||
trace!(target: "dapps", "Checking authorization.");
|
||||
// Check authorization
|
||||
let auth = self.authorization.is_authorized(&req);
|
||||
if let Authorized::No(handler) = auth {
|
||||
debug!(target: "dapps", "Authorization denied.");
|
||||
self.handler = handler;
|
||||
return self.handler.on_request(req);
|
||||
}
|
||||
|
||||
|
||||
let control = self.control.take().expect("on_request is called only once; control is always defined at start; qed");
|
||||
debug!(target: "dapps", "Handling endpoint request: {:?}", endpoint);
|
||||
self.handler = match (endpoint.0, endpoint.1, referer) {
|
||||
// Handle invalid web requests that we can recover from
|
||||
(ref path, SpecialEndpoint::None, Some((ref referer, ref referer_url)))
|
||||
if referer.app_id == apps::WEB_PATH
|
||||
&& self.endpoints.contains_key(apps::WEB_PATH)
|
||||
&& !is_web_endpoint(path)
|
||||
=>
|
||||
{
|
||||
trace!(target: "dapps", "Redirecting to correct web request: {:?}", referer_url);
|
||||
let len = cmp::min(referer_url.path.len(), 2); // /web/<encoded>/
|
||||
let base = referer_url.path[..len].join("/");
|
||||
let requested = url.map(|u| u.path.join("/")).unwrap_or_default();
|
||||
Redirection::boxed(&format!("/{}/{}", base, requested))
|
||||
},
|
||||
// First check special endpoints
|
||||
(ref path, ref endpoint, _) if self.special.contains_key(endpoint) => {
|
||||
trace!(target: "dapps", "Resolving to special endpoint.");
|
||||
self.special.get(endpoint)
|
||||
.expect("special known to contain key; qed")
|
||||
.to_async_handler(path.clone().unwrap_or_default(), control)
|
||||
},
|
||||
// Then delegate to dapp
|
||||
(Some(ref path), _, _) if self.endpoints.contains_key(&path.app_id) => {
|
||||
trace!(target: "dapps", "Resolving to local/builtin dapp.");
|
||||
self.endpoints.get(&path.app_id)
|
||||
.expect("endpoints known to contain key; qed")
|
||||
.to_async_handler(path.clone(), control)
|
||||
},
|
||||
// Try to resolve and fetch the dapp
|
||||
(Some(ref path), _, _) if self.fetch.contains(&path.app_id) => {
|
||||
trace!(target: "dapps", "Resolving to fetchable content.");
|
||||
self.fetch.to_async_handler(path.clone(), control)
|
||||
},
|
||||
// NOTE [todr] /home is redirected to home page since some users may have the redirection cached
|
||||
// (in the past we used 301 instead of 302)
|
||||
// It should be safe to remove it in (near) future.
|
||||
//
|
||||
// 404 for non-existent content
|
||||
(Some(ref path), _, _) if is_get_request && path.app_id != "home" => {
|
||||
trace!(target: "dapps", "Resolving to 404.");
|
||||
Box::new(ContentHandler::error(
|
||||
StatusCode::NotFound,
|
||||
"404 Not Found",
|
||||
"Requested content was not found.",
|
||||
None,
|
||||
self.signer_address.clone(),
|
||||
))
|
||||
},
|
||||
// Redirect any other GET request to signer.
|
||||
_ if is_get_request => {
|
||||
if let Some(ref signer_address) = self.signer_address {
|
||||
trace!(target: "dapps", "Redirecting to signer interface.");
|
||||
Redirection::boxed(&format!("http://{}", address(signer_address)))
|
||||
} else {
|
||||
trace!(target: "dapps", "Signer disabled, returning 404.");
|
||||
Box::new(ContentHandler::error(
|
||||
StatusCode::NotFound,
|
||||
"404 Not Found",
|
||||
"Your homepage is not available when Trusted Signer is disabled.",
|
||||
Some("You can still access dapps by writing a correct address, though. Re-enable Signer to get your homepage back."),
|
||||
self.signer_address.clone(),
|
||||
))
|
||||
}
|
||||
},
|
||||
// RPC by default
|
||||
_ => {
|
||||
trace!(target: "dapps", "Resolving to RPC call.");
|
||||
self.special.get(&SpecialEndpoint::Rpc)
|
||||
.expect("RPC endpoint always stored; qed")
|
||||
.to_async_handler(EndpointPath::default(), control)
|
||||
}
|
||||
};
|
||||
|
||||
// Delegate on_request to proper handler
|
||||
self.handler.on_request(req)
|
||||
}
|
||||
|
||||
/// This event occurs each time the `Request` is ready to be read from.
|
||||
fn on_request_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
|
||||
self.handler.on_request_readable(decoder)
|
||||
}
|
||||
|
||||
/// This event occurs after the first time this handled signals `Next::write()`.
|
||||
fn on_response(&mut self, response: &mut server::Response) -> Next {
|
||||
self.handler.on_response(response)
|
||||
}
|
||||
|
||||
/// This event occurs each time the `Response` is ready to be written to.
|
||||
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||
self.handler.on_response_writable(encoder)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Authorization> Router<A> {
|
||||
pub fn new(
|
||||
control: Control,
|
||||
signer_address: Option<(String, u16)>,
|
||||
content_fetcher: Arc<Fetcher>,
|
||||
endpoints: Arc<Endpoints>,
|
||||
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
|
||||
authorization: Arc<A>,
|
||||
allowed_hosts: Option<Vec<String>>,
|
||||
) -> Self {
|
||||
|
||||
let handler = special.get(&SpecialEndpoint::Utils)
|
||||
.expect("Utils endpoint always stored; qed")
|
||||
.to_handler(EndpointPath::default());
|
||||
Router {
|
||||
control: Some(control),
|
||||
signer_address: signer_address,
|
||||
endpoints: endpoints,
|
||||
fetch: content_fetcher,
|
||||
special: special,
|
||||
authorization: authorization,
|
||||
allowed_hosts: allowed_hosts,
|
||||
handler: handler,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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: &server::Request<HttpStream>) -> Option<(EndpointPath, Url)> {
|
||||
let referer = req.headers().get::<header::Referer>();
|
||||
|
||||
let url = referer.and_then(|referer| Url::parse(&referer.0).ok());
|
||||
url.and_then(|url| {
|
||||
let option = Some(url);
|
||||
extract_url_referer_endpoint(&option).or_else(|| {
|
||||
extract_endpoint(&option).0.map(|endpoint| (endpoint, option.expect("Just wrapped; qed")))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn extract_url_referer_endpoint(url: &Option<Url>) -> Option<(EndpointPath, Url)> {
|
||||
let query = url.as_ref().and_then(|url| url.query.as_ref());
|
||||
match (url, query) {
|
||||
(&Some(ref url), Some(ref query)) if query.starts_with(apps::URL_REFERER) => {
|
||||
let referer_url = format!("http://{}:{}/{}", url.host, url.port, &query[apps::URL_REFERER.len()..]);
|
||||
debug!(target: "dapps", "Recovering referer from query parameter: {}", referer_url);
|
||||
|
||||
let referer_url = Url::parse(&referer_url).ok();
|
||||
extract_endpoint(&referer_url).0.map(|endpoint| {
|
||||
(endpoint, referer_url.expect("Endpoint returned only when url `is_some`").clone())
|
||||
})
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_endpoint(url: &Option<Url>) -> (Option<EndpointPath>, SpecialEndpoint) {
|
||||
fn special_endpoint(url: &Url) -> SpecialEndpoint {
|
||||
if url.path.len() <= 1 {
|
||||
return SpecialEndpoint::None;
|
||||
}
|
||||
|
||||
match url.path[0].as_ref() {
|
||||
apps::RPC_PATH => SpecialEndpoint::Rpc,
|
||||
apps::API_PATH => SpecialEndpoint::Api,
|
||||
apps::UTILS_PATH => SpecialEndpoint::Utils,
|
||||
_ => SpecialEndpoint::None,
|
||||
}
|
||||
}
|
||||
|
||||
match *url {
|
||||
Some(ref url) => match url.host {
|
||||
Host::Domain(ref domain) if domain.ends_with(DAPPS_DOMAIN) => {
|
||||
let id = &domain[0..(domain.len() - DAPPS_DOMAIN.len())];
|
||||
let (id, params) = if let Some(split) = id.rfind('.') {
|
||||
let (params, id) = id.split_at(split);
|
||||
(id[1..].to_owned(), [params.to_owned()].into_iter().chain(&url.path).cloned().collect())
|
||||
} else {
|
||||
(id.to_owned(), url.path.clone())
|
||||
};
|
||||
|
||||
(Some(EndpointPath {
|
||||
app_id: id,
|
||||
app_params: params,
|
||||
host: domain.clone(),
|
||||
port: url.port,
|
||||
using_dapps_domains: true,
|
||||
}), special_endpoint(url))
|
||||
},
|
||||
_ if url.path.len() > 1 => {
|
||||
let id = url.path[0].to_owned();
|
||||
(Some(EndpointPath {
|
||||
app_id: id,
|
||||
app_params: url.path[1..].to_vec(),
|
||||
host: format!("{}", url.host),
|
||||
port: url.port,
|
||||
using_dapps_domains: false,
|
||||
}), special_endpoint(url))
|
||||
},
|
||||
_ => (None, special_endpoint(url)),
|
||||
},
|
||||
_ => (None, SpecialEndpoint::None)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_extract_endpoint() {
|
||||
assert_eq!(extract_endpoint(&None), (None, SpecialEndpoint::None));
|
||||
|
||||
// With path prefix
|
||||
assert_eq!(
|
||||
extract_endpoint(&Url::parse("http://localhost:8080/status/index.html").ok()),
|
||||
(Some(EndpointPath {
|
||||
app_id: "status".to_owned(),
|
||||
app_params: vec!["index.html".to_owned()],
|
||||
host: "localhost".to_owned(),
|
||||
port: 8080,
|
||||
using_dapps_domains: false,
|
||||
}), SpecialEndpoint::None)
|
||||
);
|
||||
|
||||
// With path prefix
|
||||
assert_eq!(
|
||||
extract_endpoint(&Url::parse("http://localhost:8080/rpc/").ok()),
|
||||
(Some(EndpointPath {
|
||||
app_id: "rpc".to_owned(),
|
||||
app_params: vec!["".to_owned()],
|
||||
host: "localhost".to_owned(),
|
||||
port: 8080,
|
||||
using_dapps_domains: false,
|
||||
}), SpecialEndpoint::Rpc)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
extract_endpoint(&Url::parse("http://my.status.web3.site/parity-utils/inject.js").ok()),
|
||||
(Some(EndpointPath {
|
||||
app_id: "status".to_owned(),
|
||||
app_params: vec!["my".to_owned(), "parity-utils".into(), "inject.js".into()],
|
||||
host: "my.status.web3.site".to_owned(),
|
||||
port: 80,
|
||||
using_dapps_domains: true,
|
||||
}), SpecialEndpoint::Utils)
|
||||
);
|
||||
|
||||
// By Subdomain
|
||||
assert_eq!(
|
||||
extract_endpoint(&Url::parse("http://status.web3.site/test.html").ok()),
|
||||
(Some(EndpointPath {
|
||||
app_id: "status".to_owned(),
|
||||
app_params: vec!["test.html".to_owned()],
|
||||
host: "status.web3.site".to_owned(),
|
||||
port: 80,
|
||||
using_dapps_domains: true,
|
||||
}), SpecialEndpoint::None)
|
||||
);
|
||||
|
||||
// RPC by subdomain
|
||||
assert_eq!(
|
||||
extract_endpoint(&Url::parse("http://my.status.web3.site/rpc/").ok()),
|
||||
(Some(EndpointPath {
|
||||
app_id: "status".to_owned(),
|
||||
app_params: vec!["my".to_owned(), "rpc".into(), "".into()],
|
||||
host: "my.status.web3.site".to_owned(),
|
||||
port: 80,
|
||||
using_dapps_domains: true,
|
||||
}), SpecialEndpoint::Rpc)
|
||||
);
|
||||
|
||||
// API by subdomain
|
||||
assert_eq!(
|
||||
extract_endpoint(&Url::parse("http://my.status.web3.site/api/").ok()),
|
||||
(Some(EndpointPath {
|
||||
app_id: "status".to_owned(),
|
||||
app_params: vec!["my".to_owned(), "api".into(), "".into()],
|
||||
host: "my.status.web3.site".to_owned(),
|
||||
port: 80,
|
||||
using_dapps_domains: true,
|
||||
}), SpecialEndpoint::Api)
|
||||
);
|
||||
}
|
||||
83
dapps/src/rpc.rs
Normal file
83
dapps/src/rpc.rs
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
use hyper;
|
||||
|
||||
use ethcore_rpc::{Metadata, Origin};
|
||||
use jsonrpc_core::Middleware;
|
||||
use jsonrpc_core::reactor::RpcHandler;
|
||||
use jsonrpc_http_server::{Rpc, ServerHandler, PanicHandler, AccessControlAllowOrigin, HttpMetaExtractor};
|
||||
use endpoint::{Endpoint, EndpointPath, Handler};
|
||||
|
||||
pub fn rpc<T: Middleware<Metadata>>(
|
||||
handler: RpcHandler<Metadata, T>,
|
||||
cors_domains: Vec<String>,
|
||||
panic_handler: Arc<Mutex<Option<Box<Fn() -> () + Send>>>>,
|
||||
) -> Box<Endpoint> {
|
||||
Box::new(RpcEndpoint {
|
||||
handler: handler,
|
||||
meta_extractor: Arc::new(MetadataExtractor),
|
||||
panic_handler: panic_handler,
|
||||
cors_domain: Some(cors_domains.into_iter().map(AccessControlAllowOrigin::Value).collect()),
|
||||
// NOTE [ToDr] We don't need to do any hosts validation here. It's already done in router.
|
||||
allowed_hosts: None,
|
||||
})
|
||||
}
|
||||
|
||||
struct RpcEndpoint<T: Middleware<Metadata>> {
|
||||
handler: RpcHandler<Metadata, T>,
|
||||
meta_extractor: Arc<HttpMetaExtractor<Metadata>>,
|
||||
panic_handler: Arc<Mutex<Option<Box<Fn() -> () + Send>>>>,
|
||||
cors_domain: Option<Vec<AccessControlAllowOrigin>>,
|
||||
allowed_hosts: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
impl<T: Middleware<Metadata>> Endpoint for RpcEndpoint<T> {
|
||||
fn to_async_handler(&self, _path: EndpointPath, control: hyper::Control) -> Box<Handler> {
|
||||
let panic_handler = PanicHandler { handler: self.panic_handler.clone() };
|
||||
Box::new(ServerHandler::new(
|
||||
Rpc::new(self.handler.clone(), self.meta_extractor.clone()),
|
||||
self.cors_domain.clone(),
|
||||
self.allowed_hosts.clone(),
|
||||
panic_handler,
|
||||
control,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
struct MetadataExtractor;
|
||||
impl HttpMetaExtractor<Metadata> for MetadataExtractor {
|
||||
fn read_metadata(&self, request: &hyper::server::Request<hyper::net::HttpStream>) -> Metadata {
|
||||
let dapp_id = request.headers().get::<hyper::header::Origin>()
|
||||
.map(|origin| format!("{}://{}", origin.scheme, origin.host))
|
||||
.or_else(|| {
|
||||
// fallback to custom header, but only if origin is null
|
||||
request.headers().get_raw("origin")
|
||||
.and_then(|raw| raw.one())
|
||||
.and_then(|raw| if raw == "null".as_bytes() {
|
||||
request.headers().get_raw("x-parity-origin")
|
||||
.and_then(|raw| raw.one())
|
||||
.map(|raw| String::from_utf8_lossy(raw).into_owned())
|
||||
} else {
|
||||
None
|
||||
})
|
||||
});
|
||||
Metadata {
|
||||
origin: Origin::Dapps(dapp_id.map(Into::into).unwrap_or_default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use tests::helpers::{serve, serve_with_registrar, request, assert_security_headers};
|
||||
use tests::helpers::{serve, serve_with_registrar, serve_extra_cors, request, assert_security_headers};
|
||||
|
||||
#[test]
|
||||
fn should_return_error() {
|
||||
@@ -33,12 +33,35 @@ fn should_return_error() {
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 404 Not Found");
|
||||
response.assert_header("Content-Type", "application/json");
|
||||
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
|
||||
assert_eq!(response.headers.get(3).unwrap(), "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_serve_apps() {
|
||||
// given
|
||||
let server = serve();
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET /api/apps HTTP/1.1\r\n\
|
||||
Host: 127.0.0.1:8080\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
{}
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||
assert_eq!(response.headers.get(3).unwrap(), "Content-Type: application/json");
|
||||
assert!(response.body.contains("Parity UI"), response.body);
|
||||
assert_security_headers(&response.headers);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_handle_ping() {
|
||||
// given
|
||||
@@ -49,7 +72,6 @@ fn should_handle_ping() {
|
||||
"\
|
||||
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\
|
||||
{}
|
||||
@@ -57,8 +79,8 @@ fn should_handle_ping() {
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
response.assert_header("Content-Type", "application/json");
|
||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||
assert_eq!(response.headers.get(3).unwrap(), "Content-Type: application/json");
|
||||
assert_eq!(response.body, "0\n\n".to_owned());
|
||||
assert_security_headers(&response.headers);
|
||||
}
|
||||
@@ -80,7 +102,135 @@ fn should_try_to_resolve_dapp() {
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 404 Not Found");
|
||||
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
|
||||
assert_eq!(registrar.calls.lock().len(), 2);
|
||||
assert_security_headers(&response.headers);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_signer_port_cors_headers() {
|
||||
// given
|
||||
let server = serve();
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
POST /api/ping HTTP/1.1\r\n\
|
||||
Host: localhost:8080\r\n\
|
||||
Origin: http://127.0.0.1:18180\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
{}
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||
assert!(
|
||||
response.headers_raw.contains("Access-Control-Allow-Origin: http://127.0.0.1:18180"),
|
||||
"CORS header for signer missing: {:?}",
|
||||
response.headers
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_signer_port_cors_headers_for_home_parity() {
|
||||
// given
|
||||
let server = serve();
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
POST /api/ping HTTP/1.1\r\n\
|
||||
Host: localhost:8080\r\n\
|
||||
Origin: http://parity.web3.site\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
{}
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||
assert!(
|
||||
response.headers_raw.contains("Access-Control-Allow-Origin: http://parity.web3.site"),
|
||||
"CORS header for parity.web3.site missing: {:?}",
|
||||
response.headers
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn should_return_signer_port_cors_headers_for_home_parity_with_https() {
|
||||
// given
|
||||
let server = serve();
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
POST /api/ping HTTP/1.1\r\n\
|
||||
Host: localhost:8080\r\n\
|
||||
Origin: https://parity.web3.site\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
{}
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||
assert!(
|
||||
response.headers_raw.contains("Access-Control-Allow-Origin: https://parity.web3.site"),
|
||||
"CORS header for parity.web3.site missing: {:?}",
|
||||
response.headers
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_signer_port_cors_headers_for_home_parity_with_port() {
|
||||
// given
|
||||
let server = serve();
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
POST /api/ping HTTP/1.1\r\n\
|
||||
Host: localhost:8080\r\n\
|
||||
Origin: http://parity.web3.site:18180\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
{}
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||
assert!(
|
||||
response.headers_raw.contains("Access-Control-Allow-Origin: http://parity.web3.site:18180"),
|
||||
"CORS header for parity.web3.site missing: {:?}",
|
||||
response.headers
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_extra_cors_headers() {
|
||||
// given
|
||||
let server = serve_extra_cors(Some(vec!["all".to_owned()]));
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
POST /api/ping HTTP/1.1\r\n\
|
||||
Host: localhost:8080\r\n\
|
||||
Origin: http://somedomain.io\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
{}
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
response.assert_header("Access-Control-Allow-Origin", "http://somedomain.io");
|
||||
}
|
||||
|
||||
|
||||
@@ -14,35 +14,12 @@
|
||||
// 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_ui, request, assert_security_headers};
|
||||
use tests::helpers::{serve_with_auth, request, assert_security_headers_for_embed};
|
||||
|
||||
#[test]
|
||||
fn should_serve_home_js() {
|
||||
fn should_require_authorization() {
|
||||
// given
|
||||
let server = serve_ui();
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET /inject.js HTTP/1.1\r\n\
|
||||
Host: 127.0.0.1:8080\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
{}
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
response.assert_header("Content-Type", "application/javascript");
|
||||
assert_eq!(response.body.contains("function(){"), true, "Expected function in: {}", response.body);
|
||||
assert_security_headers(&response.headers);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_serve_home() {
|
||||
// given
|
||||
let server = serve_ui();
|
||||
let server = serve_with_auth("test", "test");
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
@@ -51,21 +28,18 @@ fn should_serve_home() {
|
||||
Host: 127.0.0.1:8080\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
{}
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
response.assert_header("Content-Type", "text/html");
|
||||
assert_security_headers(&response.headers);
|
||||
assert_eq!(response.status, "HTTP/1.1 401 Unauthorized".to_owned());
|
||||
assert_eq!(response.headers.get(0).unwrap(), "WWW-Authenticate: Basic realm=\"Parity\"");
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn should_inject_js() {
|
||||
fn should_reject_on_invalid_auth() {
|
||||
// given
|
||||
let server = serve_ui();
|
||||
let server = serve_with_auth("test", "test");
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
@@ -73,19 +47,34 @@ fn should_inject_js() {
|
||||
GET / HTTP/1.1\r\n\
|
||||
Host: 127.0.0.1:8080\r\n\
|
||||
Connection: close\r\n\
|
||||
Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l\r\n
|
||||
\r\n\
|
||||
{}
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
response.assert_header("Content-Type", "text/html");
|
||||
assert_eq!(
|
||||
response.body.contains(r#"/inject.js"></script>"#),
|
||||
true,
|
||||
"Expected inject script tag in: {}",
|
||||
response.body
|
||||
);
|
||||
assert_security_headers(&response.headers);
|
||||
assert_eq!(response.status, "HTTP/1.1 401 Unauthorized".to_owned());
|
||||
assert!(response.body.contains("Unauthorized"), response.body);
|
||||
assert_eq!(response.headers_raw.contains("WWW-Authenticate"), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_allow_on_valid_auth() {
|
||||
// given
|
||||
let server = serve_with_auth("Aladdin", "OpenSesame");
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET /ui/ HTTP/1.1\r\n\
|
||||
Host: 127.0.0.1:8080\r\n\
|
||||
Connection: close\r\n\
|
||||
Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l\r\n
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||
assert_security_headers_for_embed(&response.headers);
|
||||
}
|
||||
@@ -15,10 +15,10 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use devtools::http_client;
|
||||
use rustc_hex::FromHex;
|
||||
use rustc_serialize::hex::FromHex;
|
||||
use tests::helpers::{
|
||||
serve_with_registrar, serve_with_registrar_and_sync, serve_with_fetch,
|
||||
serve_with_registrar_and_fetch,
|
||||
serve_with_registrar_and_fetch, serve_with_registrar_and_fetch_and_threads,
|
||||
request, assert_security_headers_for_embed,
|
||||
};
|
||||
|
||||
@@ -39,7 +39,7 @@ fn should_resolve_dapp() {
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 404 Not Found");
|
||||
assert_eq!(registrar.calls.lock().len(), 4);
|
||||
assert_eq!(registrar.calls.lock().len(), 2);
|
||||
assert_security_headers_for_embed(&response.headers);
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ fn should_return_503_when_syncing_but_should_make_the_calls() {
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 503 Service Unavailable");
|
||||
assert_eq!(registrar.calls.lock().len(), 2);
|
||||
assert_eq!(registrar.calls.lock().len(), 4);
|
||||
assert_security_headers_for_embed(&response.headers);
|
||||
}
|
||||
|
||||
@@ -166,31 +166,26 @@ fn should_return_fetched_dapp_content() {
|
||||
|
||||
response1.assert_status("HTTP/1.1 200 OK");
|
||||
assert_security_headers_for_embed(&response1.headers);
|
||||
assert!(
|
||||
response1.body.contains(r#"18
|
||||
assert_eq!(
|
||||
response1.body,
|
||||
r#"18
|
||||
<h1>Hello Gavcoin!</h1>
|
||||
|
||||
0
|
||||
|
||||
"#),
|
||||
"Expected Gavcoin body: {}",
|
||||
response1.body
|
||||
"#
|
||||
);
|
||||
|
||||
response2.assert_status("HTTP/1.1 200 OK");
|
||||
assert_security_headers_for_embed(&response2.headers);
|
||||
assert_eq!(
|
||||
response2.body,
|
||||
r#"EA
|
||||
r#"BE
|
||||
{
|
||||
"id": "9c94e154dab8acf859b30ee80fc828fb1d38359d938751b65db71d460588d82a",
|
||||
"name": "Gavcoin",
|
||||
"description": "Gavcoin",
|
||||
"version": "1.0.0",
|
||||
"author": "",
|
||||
"iconUrl": "icon.png",
|
||||
"localUrl": null,
|
||||
"allowJsEval": false
|
||||
"iconUrl": "icon.png"
|
||||
}
|
||||
0
|
||||
|
||||
@@ -262,7 +257,7 @@ fn should_not_request_content_twice() {
|
||||
use std::thread;
|
||||
|
||||
// given
|
||||
let (server, fetch, registrar) = serve_with_registrar_and_fetch();
|
||||
let (server, fetch, registrar) = serve_with_registrar_and_fetch_and_threads(true);
|
||||
let gavcoin = GAVCOIN_ICON.from_hex().unwrap();
|
||||
registrar.set_result(
|
||||
"2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e".parse().unwrap(),
|
||||
@@ -317,7 +312,7 @@ fn should_encode_and_decode_base32() {
|
||||
#[test]
|
||||
fn should_stream_web_content() {
|
||||
// given
|
||||
let (server, fetch) = serve_with_fetch("token", "https://parity.io");
|
||||
let (server, fetch) = serve_with_fetch("token");
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
@@ -340,7 +335,7 @@ fn should_stream_web_content() {
|
||||
#[test]
|
||||
fn should_support_base32_encoded_web_urls() {
|
||||
// given
|
||||
let (server, fetch) = serve_with_fetch("token", "https://parity.io");
|
||||
let (server, fetch) = serve_with_fetch("token");
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
@@ -363,7 +358,7 @@ fn should_support_base32_encoded_web_urls() {
|
||||
#[test]
|
||||
fn should_correctly_handle_long_label_when_splitted() {
|
||||
// given
|
||||
let (server, fetch) = serve_with_fetch("xolrg9fePeQyKLnL", "https://contribution.melonport.com");
|
||||
let (server, fetch) = serve_with_fetch("xolrg9fePeQyKLnL");
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
@@ -387,7 +382,7 @@ fn should_correctly_handle_long_label_when_splitted() {
|
||||
#[test]
|
||||
fn should_support_base32_encoded_web_urls_as_path() {
|
||||
// given
|
||||
let (server, fetch) = serve_with_fetch("token", "https://parity.io");
|
||||
let (server, fetch) = serve_with_fetch("token");
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
@@ -407,32 +402,10 @@ fn should_support_base32_encoded_web_urls_as_path() {
|
||||
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_for_embed(&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");
|
||||
let (server, fetch) = serve_with_fetch("test");
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
@@ -454,7 +427,7 @@ fn should_return_error_on_invalid_token() {
|
||||
#[test]
|
||||
fn should_return_error_on_invalid_protocol() {
|
||||
// given
|
||||
let (server, fetch) = serve_with_fetch("token", "ftp://parity.io");
|
||||
let (server, fetch) = serve_with_fetch("token");
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
@@ -476,7 +449,7 @@ fn should_return_error_on_invalid_protocol() {
|
||||
#[test]
|
||||
fn should_disallow_non_get_requests() {
|
||||
// given
|
||||
let (server, fetch) = serve_with_fetch("token", "https://parity.io");
|
||||
let (server, fetch) = serve_with_fetch("token");
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
@@ -501,7 +474,7 @@ fn should_disallow_non_get_requests() {
|
||||
#[test]
|
||||
fn should_fix_absolute_requests_based_on_referer() {
|
||||
// given
|
||||
let (server, fetch) = serve_with_fetch("token", "https://parity.io");
|
||||
let (server, fetch) = serve_with_fetch("token");
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
@@ -524,7 +497,7 @@ fn should_fix_absolute_requests_based_on_referer() {
|
||||
#[test]
|
||||
fn should_fix_absolute_requests_based_on_referer_in_url() {
|
||||
// given
|
||||
let (server, fetch) = serve_with_fetch("token", "https://parity.io");
|
||||
let (server, fetch) = serve_with_fetch("token");
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
|
||||
@@ -14,13 +14,12 @@
|
||||
// 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::{io, thread, time};
|
||||
use std::sync::{atomic, mpsc, Arc};
|
||||
use parking_lot::Mutex;
|
||||
use hyper;
|
||||
use util::Mutex;
|
||||
|
||||
use futures::{self, future, Future};
|
||||
use fetch::{self, Fetch, Url, Request, Abort};
|
||||
use futures::{self, Future};
|
||||
use fetch::{self, Fetch};
|
||||
|
||||
pub struct FetchControl {
|
||||
sender: mpsc::Sender<()>,
|
||||
@@ -34,8 +33,8 @@ impl FetchControl {
|
||||
}
|
||||
|
||||
pub fn wait_for_requests(&self, len: usize) {
|
||||
const MAX_TIMEOUT: time::Duration = time::Duration::from_millis(5000);
|
||||
const ATTEMPTS: u32 = 10;
|
||||
const MAX_TIMEOUT_MS: u64 = 5000;
|
||||
const ATTEMPTS: u64 = 10;
|
||||
let mut attempts_left = ATTEMPTS;
|
||||
loop {
|
||||
let current = self.fetch.requested.lock().len();
|
||||
@@ -50,7 +49,7 @@ impl FetchControl {
|
||||
} else {
|
||||
attempts_left -= 1;
|
||||
// Should we handle spurious timeouts better?
|
||||
thread::park_timeout(MAX_TIMEOUT / ATTEMPTS);
|
||||
thread::park_timeout(time::Duration::from_millis(MAX_TIMEOUT_MS / ATTEMPTS));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,11 +94,14 @@ impl FakeFetch {
|
||||
}
|
||||
|
||||
impl Fetch for FakeFetch {
|
||||
type Result = Box<Future<Item = fetch::Response, Error = fetch::Error> + Send>;
|
||||
type Result = futures::BoxFuture<fetch::Response, fetch::Error>;
|
||||
|
||||
fn fetch(&self, request: Request, abort: fetch::Abort) -> Self::Result {
|
||||
let u = request.url().clone();
|
||||
self.requested.lock().push(u.as_str().into());
|
||||
fn new() -> Result<Self, fetch::Error> where Self: Sized {
|
||||
Ok(FakeFetch::default())
|
||||
}
|
||||
|
||||
fn fetch_with_abort(&self, url: &str, _abort: fetch::Abort) -> Self::Result {
|
||||
self.requested.lock().push(url.into());
|
||||
let manual = self.manual.clone();
|
||||
let response = self.response.clone();
|
||||
|
||||
@@ -109,26 +111,12 @@ impl Fetch for FakeFetch {
|
||||
// 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();
|
||||
let cursor = io::Cursor::new(data);
|
||||
tx.complete(fetch::Response::from_reader(cursor));
|
||||
});
|
||||
|
||||
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)
|
||||
rx.map_err(|_| fetch::Error::Aborted).boxed()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,21 +14,20 @@
|
||||
// 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::env;
|
||||
use std::str;
|
||||
use std::ops::Deref;
|
||||
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 ethcore_rpc::Metadata;
|
||||
use jsonrpc_core::MetaIoHandler;
|
||||
use jsonrpc_core::reactor::RpcEventLoop;
|
||||
|
||||
use ServerBuilder;
|
||||
use Server;
|
||||
use fetch::Fetch;
|
||||
use devtools::http_client;
|
||||
use registrar::{RegistrarClient, Asynchronous};
|
||||
use fetch::{Fetch, Client as FetchClient};
|
||||
use node_health::{NodeHealth, TimeChecker, CpuPool};
|
||||
|
||||
use {Middleware, SyncStatus, WebProxyTokens};
|
||||
use parity_reactor::Remote;
|
||||
|
||||
mod registrar;
|
||||
mod fetch;
|
||||
@@ -38,13 +37,6 @@ use self::fetch::FakeFetch;
|
||||
|
||||
const SIGNER_PORT: u16 = 18180;
|
||||
|
||||
#[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") {
|
||||
@@ -54,7 +46,20 @@ fn init_logger() {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_server<F, B>(process: F, io: IoHandler) -> (Server, Arc<FakeRegistrar>) where
|
||||
pub struct ServerLoop {
|
||||
pub server: Server,
|
||||
pub event_loop: RpcEventLoop,
|
||||
}
|
||||
|
||||
impl Deref for ServerLoop {
|
||||
type Target = Server;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.server
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_server<F, B>(process: F, io: MetaIoHandler<Metadata>, remote: Remote) -> (ServerLoop, Arc<FakeRegistrar>) where
|
||||
F: FnOnce(ServerBuilder) -> ServerBuilder<B>,
|
||||
B: Fetch,
|
||||
{
|
||||
@@ -63,73 +68,95 @@ pub fn init_server<F, B>(process: F, io: IoHandler) -> (Server, Arc<FakeRegistra
|
||||
let mut dapps_path = env::temp_dir();
|
||||
dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading");
|
||||
|
||||
let mut builder = ServerBuilder::new(FetchClient::new().unwrap(), &dapps_path, registrar.clone());
|
||||
builder.signer_address = Some(("127.0.0.1".into(), SIGNER_PORT));
|
||||
let server = process(builder).start_unsecured_http(&"127.0.0.1:0".parse().unwrap(), io).unwrap();
|
||||
// TODO [ToDr] When https://github.com/ethcore/jsonrpc/issues/26 is resolved
|
||||
// this additional EventLoop wouldn't be needed, we should be able to re-use remote.
|
||||
let event_loop = RpcEventLoop::spawn();
|
||||
let handler = event_loop.handler(Arc::new(io));
|
||||
let server = process(ServerBuilder::new(
|
||||
&dapps_path, registrar.clone(), remote,
|
||||
))
|
||||
.signer_address(Some(("127.0.0.1".into(), SIGNER_PORT)))
|
||||
.start_unsecured_http(&"127.0.0.1:0".parse().unwrap(), handler).unwrap();
|
||||
(
|
||||
server,
|
||||
ServerLoop { server: server, event_loop: event_loop },
|
||||
registrar,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn serve_with_rpc(io: IoHandler) -> Server {
|
||||
init_server(|builder| builder, io).0
|
||||
pub fn serve_with_auth(user: &str, pass: &str) -> ServerLoop {
|
||||
init_logger();
|
||||
let registrar = Arc::new(FakeRegistrar::new());
|
||||
let mut dapps_path = env::temp_dir();
|
||||
dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading");
|
||||
|
||||
let event_loop = RpcEventLoop::spawn();
|
||||
let handler = event_loop.handler(Arc::new(MetaIoHandler::default()));
|
||||
let server = ServerBuilder::new(&dapps_path, registrar, Remote::new(event_loop.remote()))
|
||||
.signer_address(Some(("127.0.0.1".into(), SIGNER_PORT)))
|
||||
.allowed_hosts(None)
|
||||
.start_basic_auth_http(&"127.0.0.1:0".parse().unwrap(), user, pass, handler).unwrap();
|
||||
ServerLoop {
|
||||
server: server,
|
||||
event_loop: event_loop,
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
pub fn serve_with_rpc(io: MetaIoHandler<Metadata>) -> ServerLoop {
|
||||
init_server(|builder| builder.allowed_hosts(None), io, Remote::new_sync()).0
|
||||
}
|
||||
|
||||
pub fn serve_hosts(hosts: Option<Vec<String>>) -> ServerLoop {
|
||||
init_server(|builder| builder.allowed_hosts(hosts), Default::default(), Remote::new_sync()).0
|
||||
}
|
||||
|
||||
pub fn serve_extra_cors(extra_cors: Option<Vec<String>>) -> ServerLoop {
|
||||
init_server(|builder| builder.allowed_hosts(None).extra_cors_headers(extra_cors), Default::default(), Remote::new_sync()).0
|
||||
}
|
||||
|
||||
pub fn serve_with_registrar() -> (ServerLoop, Arc<FakeRegistrar>) {
|
||||
init_server(|builder| builder.allowed_hosts(None), Default::default(), Remote::new_sync())
|
||||
}
|
||||
|
||||
pub fn serve_with_registrar_and_sync() -> (ServerLoop, Arc<FakeRegistrar>) {
|
||||
init_server(|builder| {
|
||||
builder
|
||||
}, Default::default()).0
|
||||
.sync_status(Arc::new(|| true))
|
||||
.allowed_hosts(None)
|
||||
}, Default::default(), Remote::new_sync())
|
||||
}
|
||||
|
||||
pub fn serve_with_registrar() -> (Server, Arc<FakeRegistrar>) {
|
||||
init_server(|builder| builder, Default::default())
|
||||
pub fn serve_with_registrar_and_fetch() -> (ServerLoop, FakeFetch, Arc<FakeRegistrar>) {
|
||||
serve_with_registrar_and_fetch_and_threads(false)
|
||||
}
|
||||
|
||||
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>) {
|
||||
pub fn serve_with_registrar_and_fetch_and_threads(multi_threaded: bool) -> (ServerLoop, FakeFetch, Arc<FakeRegistrar>) {
|
||||
let fetch = FakeFetch::default();
|
||||
let f = fetch.clone();
|
||||
let (server, reg) = init_server(move |builder| {
|
||||
builder.fetch(f.clone())
|
||||
}, Default::default());
|
||||
builder.allowed_hosts(None).fetch(f.clone())
|
||||
}, Default::default(), if multi_threaded { Remote::new_thread_per_future() } else { Remote::new_sync() });
|
||||
|
||||
(server, fetch, reg)
|
||||
}
|
||||
|
||||
pub fn serve_with_fetch(web_token: &'static str, domain: &'static str) -> (Server, FakeFetch) {
|
||||
pub fn serve_with_fetch(web_token: &'static str) -> (ServerLoop, 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());
|
||||
let (server, _) = init_server(move |builder| {
|
||||
builder
|
||||
.allowed_hosts(None)
|
||||
.fetch(f.clone())
|
||||
.web_proxy_tokens(Arc::new(move |token| &token == web_token))
|
||||
}, Default::default(), Remote::new_sync());
|
||||
|
||||
(server, fetch)
|
||||
}
|
||||
|
||||
pub fn serve() -> Server {
|
||||
init_server(|builder| builder, Default::default()).0
|
||||
pub fn serve() -> ServerLoop {
|
||||
init_server(|builder| builder.allowed_hosts(None), Default::default(), Remote::new_sync()).0
|
||||
}
|
||||
|
||||
pub fn serve_ui() -> Server {
|
||||
init_server(|mut builder| {
|
||||
builder.serve_ui = true;
|
||||
builder
|
||||
}, Default::default()).0
|
||||
}
|
||||
|
||||
pub fn request(server: Server, request: &str) -> http_client::Response {
|
||||
pub fn request(server: ServerLoop, request: &str) -> http_client::Response {
|
||||
http_client::request(server.addr(), request)
|
||||
}
|
||||
|
||||
@@ -139,152 +166,3 @@ pub fn assert_security_headers(headers: &[String]) {
|
||||
pub fn assert_security_headers_for_embed(headers: &[String]) {
|
||||
http_client::assert_security_headers_present(headers, Some(SIGNER_PORT))
|
||||
}
|
||||
|
||||
|
||||
/// Webapps HTTP+RPC server build.
|
||||
pub struct ServerBuilder<T: Fetch = FetchClient> {
|
||||
dapps_path: PathBuf,
|
||||
registrar: Arc<RegistrarClient<Call=Asynchronous>>,
|
||||
sync_status: Arc<SyncStatus>,
|
||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||
signer_address: Option<(String, u16)>,
|
||||
allowed_hosts: DomainsValidation<Host>,
|
||||
fetch: T,
|
||||
serve_ui: bool,
|
||||
}
|
||||
|
||||
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),
|
||||
signer_address: None,
|
||||
allowed_hosts: DomainsValidation::Disabled,
|
||||
fetch: fetch,
|
||||
serve_ui: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Fetch> ServerBuilder<T> {
|
||||
/// Set a fetch client to use.
|
||||
pub fn fetch<X: Fetch>(self, fetch: X) -> ServerBuilder<X> {
|
||||
ServerBuilder {
|
||||
dapps_path: self.dapps_path,
|
||||
registrar: self.registrar,
|
||||
sync_status: self.sync_status,
|
||||
web_proxy_tokens: self.web_proxy_tokens,
|
||||
signer_address: self.signer_address,
|
||||
allowed_hosts: self.allowed_hosts,
|
||||
fetch: fetch,
|
||||
serve_ui: self.serve_ui,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.signer_address,
|
||||
self.dapps_path,
|
||||
vec![],
|
||||
self.registrar,
|
||||
self.sync_status,
|
||||
self.web_proxy_tokens,
|
||||
Remote::new_sync(),
|
||||
self.fetch,
|
||||
self.serve_ui,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
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>,
|
||||
signer_address: Option<(String, u16)>,
|
||||
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,
|
||||
serve_ui: bool,
|
||||
) -> 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 = if serve_ui {
|
||||
Middleware::ui(
|
||||
pool,
|
||||
health,
|
||||
DAPPS_DOMAIN.into(),
|
||||
registrar,
|
||||
sync_status,
|
||||
fetch,
|
||||
false,
|
||||
)
|
||||
} else {
|
||||
Middleware::dapps(
|
||||
pool,
|
||||
health,
|
||||
signer_address,
|
||||
vec![],
|
||||
vec![],
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,12 +17,10 @@
|
||||
use std::str;
|
||||
use std::sync::Arc;
|
||||
use std::collections::HashMap;
|
||||
use rustc_serialize::hex::FromHex;
|
||||
|
||||
use ethereum_types::{H256, Address};
|
||||
use bytes::{Bytes, ToPretty};
|
||||
use registrar::{RegistrarClient, Asynchronous};
|
||||
use parking_lot::Mutex;
|
||||
use rustc_hex::FromHex;
|
||||
use hash_fetch::urlhint::ContractClient;
|
||||
use util::{Bytes, Address, Mutex, H256, ToPretty};
|
||||
|
||||
const REGISTRAR: &'static str = "8e4e9b13d4b45cb0befc93c3061b1408f67316b2";
|
||||
const URLHINT: &'static str = "deadbeefcafe0000000000000000000000000000";
|
||||
@@ -55,23 +53,20 @@ impl FakeRegistrar {
|
||||
|
||||
pub fn set_result(&self, hash: H256, result: Result<Bytes, String>) {
|
||||
self.responses.lock().insert(
|
||||
(URLHINT.into(), format!("{}{:x}", URLHINT_RESOLVE, hash)),
|
||||
(URLHINT.into(), format!("{}{:?}", URLHINT_RESOLVE, hash)),
|
||||
result
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl RegistrarClient for FakeRegistrar {
|
||||
type Call = Asynchronous;
|
||||
|
||||
fn registrar_address(&self) -> Result<Address, String> {
|
||||
impl ContractClient for FakeRegistrar {
|
||||
fn registrar(&self) -> Result<Address, String> {
|
||||
Ok(REGISTRAR.parse().unwrap())
|
||||
}
|
||||
|
||||
fn call_contract(&self, address: Address, data: Bytes) -> Self::Call {
|
||||
fn call(&self, address: Address, data: Bytes) -> Result<Bytes, String> {
|
||||
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))
|
||||
self.responses.lock().get(&call).cloned().expect(&format!("No response for call: {:?}", call))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
mod helpers;
|
||||
|
||||
mod api;
|
||||
mod authorization;
|
||||
mod fetch;
|
||||
mod home;
|
||||
mod redirection;
|
||||
mod rpc;
|
||||
mod validation;
|
||||
|
||||
@@ -32,27 +32,7 @@ fn should_redirect_to_home() {
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 302 Found");
|
||||
assert_eq!(response.headers.get(0).unwrap(), "Location: http://127.0.0.1:18180");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_redirect_to_home_with_domain() {
|
||||
// given
|
||||
let server = serve();
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET / HTTP/1.1\r\n\
|
||||
Host: home.web3.site\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 302 Found");
|
||||
assert_eq!(response.status, "HTTP/1.1 302 Found".to_owned());
|
||||
assert_eq!(response.headers.get(0).unwrap(), "Location: http://127.0.0.1:18180");
|
||||
}
|
||||
|
||||
@@ -72,7 +52,27 @@ fn should_redirect_to_home_when_trailing_slash_is_missing() {
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 302 Found");
|
||||
assert_eq!(response.status, "HTTP/1.1 302 Found".to_owned());
|
||||
assert_eq!(response.headers.get(0).unwrap(), "Location: http://127.0.0.1:18180");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_redirect_to_home_for_users_with_cached_redirection() {
|
||||
// given
|
||||
let server = serve();
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET /home/ HTTP/1.1\r\n\
|
||||
Host: 127.0.0.1:8080\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
assert_eq!(response.status, "HTTP/1.1 302 Found".to_owned());
|
||||
assert_eq!(response.headers.get(0).unwrap(), "Location: http://127.0.0.1:18180");
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ fn should_display_404_on_invalid_dapp() {
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 404 Not Found");
|
||||
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
|
||||
assert_security_headers_for_embed(&response.headers);
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ fn should_display_404_on_invalid_dapp_with_domain() {
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 404 Not Found");
|
||||
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
|
||||
assert_security_headers_for_embed(&response.headers);
|
||||
}
|
||||
|
||||
@@ -134,8 +134,8 @@ fn should_serve_rpc() {
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
assert_eq!(response.body, format!("4C\n{}\n\n0\n\n", r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"},"id":null}"#));
|
||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||
assert_eq!(response.body, format!("58\n{}\n\n0\n\n", r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error","data":null},"id":null}"#));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -156,8 +156,8 @@ fn should_serve_rpc_at_slash_rpc() {
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
assert_eq!(response.body, format!("4C\n{}\n\n0\n\n", r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"},"id":null}"#));
|
||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||
assert_eq!(response.body, format!("58\n{}\n\n0\n\n", r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error","data":null},"id":null}"#));
|
||||
}
|
||||
|
||||
|
||||
@@ -178,8 +178,8 @@ fn should_serve_proxy_pac() {
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
assert_eq!(response.body, "DB\n\nfunction FindProxyForURL(url, host) {\n\tif (shExpMatch(host, \"home.web3.site\"))\n\t{\n\t\treturn \"PROXY 127.0.0.1:18180\";\n\t}\n\n\tif (shExpMatch(host, \"*.web3.site\"))\n\t{\n\t\treturn \"PROXY 127.0.0.1:8080\";\n\t}\n\n\treturn \"DIRECT\";\n}\n\n0\n\n".to_owned());
|
||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||
assert_eq!(response.body, "DD\n\nfunction FindProxyForURL(url, host) {\n\tif (shExpMatch(host, \"parity.web3.site\"))\n\t{\n\t\treturn \"PROXY 127.0.0.1:18180\";\n\t}\n\n\tif (shExpMatch(host, \"*.web3.site\"))\n\t{\n\t\treturn \"PROXY 127.0.0.1:8080\";\n\t}\n\n\treturn \"DIRECT\";\n}\n\n0\n\n".to_owned());
|
||||
assert_security_headers(&response.headers);
|
||||
}
|
||||
|
||||
@@ -200,8 +200,8 @@ fn should_serve_utils() {
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
response.assert_header("Content-Type", "application/javascript");
|
||||
assert_eq!(response.body.contains("function(){"), true, "Expected function in: {}", response.body);
|
||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||
assert_eq!(response.body.contains("function(){"), true);
|
||||
assert_security_headers(&response.headers);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,14 +14,16 @@
|
||||
// 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 futures::{future, Future};
|
||||
use ethcore_rpc::{Metadata, Origin};
|
||||
use jsonrpc_core::{MetaIoHandler, Value};
|
||||
|
||||
use tests::helpers::{serve_with_rpc, request};
|
||||
|
||||
#[test]
|
||||
fn should_serve_rpc() {
|
||||
// given
|
||||
let mut io = IoHandler::default();
|
||||
let mut io = MetaIoHandler::default();
|
||||
io.add_method("rpc_test", |_| {
|
||||
Ok(Value::String("Hello World!".into()))
|
||||
});
|
||||
@@ -47,3 +49,71 @@ fn should_serve_rpc() {
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
assert_eq!(response.body, "31\n{\"jsonrpc\":\"2.0\",\"result\":\"Hello World!\",\"id\":1}\n\n0\n\n".to_owned());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_extract_metadata() {
|
||||
// given
|
||||
let mut io = MetaIoHandler::default();
|
||||
io.add_method_with_meta("rpc_test", |_params, meta: Metadata| {
|
||||
assert_eq!(meta.origin, Origin::Dapps("https://parity.io/".into()));
|
||||
assert_eq!(meta.dapp_id(), "https://parity.io/".into());
|
||||
future::ok(Value::String("Hello World!".into())).boxed()
|
||||
});
|
||||
let server = serve_with_rpc(io);
|
||||
|
||||
// when
|
||||
let req = r#"{"jsonrpc":"2.0","id":1,"method":"rpc_test","params":[]}"#;
|
||||
let response = request(server, &format!(
|
||||
"\
|
||||
POST /rpc/ HTTP/1.1\r\n\
|
||||
Host: 127.0.0.1:8080\r\n\
|
||||
Connection: close\r\n\
|
||||
Origin: https://parity.io/\r\n\
|
||||
X-Parity-Origin: https://this.should.be.ignored\r\n\
|
||||
Content-Type: application/json\r\n\
|
||||
Content-Length: {}\r\n\
|
||||
\r\n\
|
||||
{}\r\n\
|
||||
",
|
||||
req.as_bytes().len(),
|
||||
req,
|
||||
));
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
assert_eq!(response.body, "31\n{\"jsonrpc\":\"2.0\",\"result\":\"Hello World!\",\"id\":1}\n\n0\n\n".to_owned());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_extract_metadata_from_custom_header() {
|
||||
// given
|
||||
let mut io = MetaIoHandler::default();
|
||||
io.add_method_with_meta("rpc_test", |_params, meta: Metadata| {
|
||||
assert_eq!(meta.origin, Origin::Dapps("https://parity.io/".into()));
|
||||
assert_eq!(meta.dapp_id(), "https://parity.io/".into());
|
||||
future::ok(Value::String("Hello World!".into())).boxed()
|
||||
});
|
||||
let server = serve_with_rpc(io);
|
||||
|
||||
// when
|
||||
let req = r#"{"jsonrpc":"2.0","id":1,"method":"rpc_test","params":[]}"#;
|
||||
let response = request(server, &format!(
|
||||
"\
|
||||
POST /rpc/ HTTP/1.1\r\n\
|
||||
Host: 127.0.0.1:8080\r\n\
|
||||
Connection: close\r\n\
|
||||
Origin: null\r\n\
|
||||
X-Parity-Origin: https://parity.io/\r\n\
|
||||
Content-Type: application/json\r\n\
|
||||
Content-Length: {}\r\n\
|
||||
\r\n\
|
||||
{}\r\n\
|
||||
",
|
||||
req.as_bytes().len(),
|
||||
req,
|
||||
));
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
assert_eq!(response.body, "31\n{\"jsonrpc\":\"2.0\",\"result\":\"Hello World!\",\"id\":1}\n\n0\n\n".to_owned());
|
||||
}
|
||||
|
||||
@@ -33,8 +33,8 @@ fn should_reject_invalid_host() {
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 403 Forbidden");
|
||||
assert!(response.body.contains("Provided Host header is not whitelisted."), response.body);
|
||||
assert_eq!(response.status, "HTTP/1.1 403 Forbidden".to_owned());
|
||||
assert!(response.body.contains("Current Host Is Disallowed"), response.body);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -54,7 +54,7 @@ fn should_allow_valid_host() {
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -74,7 +74,7 @@ fn should_serve_dapps_domains() {
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -95,5 +95,33 @@ fn should_allow_parity_utils_even_on_invalid_domain() {
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_return_cors_headers_for_rpc() {
|
||||
// given
|
||||
let server = serve_hosts(Some(vec!["localhost:8080".into()]));
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
POST /rpc HTTP/1.1\r\n\
|
||||
Host: localhost:8080\r\n\
|
||||
Origin: null\r\n\
|
||||
Content-Type: application/json\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
{}
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||
assert!(
|
||||
!response.headers_raw.contains("Access-Control-Allow-Origin"),
|
||||
"CORS headers were not expected: {:?}",
|
||||
response.headers
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
150
dapps/src/url.rs
Normal file
150
dapps/src/url.rs
Normal file
@@ -0,0 +1,150 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! HTTP/HTTPS URL type. Based on URL type from Iron library.
|
||||
|
||||
use url_lib::{self};
|
||||
pub use url_lib::Host;
|
||||
|
||||
/// HTTP/HTTPS URL type for Iron.
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub struct Url {
|
||||
/// Raw url of url
|
||||
pub raw: url_lib::Url,
|
||||
|
||||
/// The host field of the URL, probably a domain.
|
||||
pub host: Host,
|
||||
|
||||
/// The connection port.
|
||||
pub port: u16,
|
||||
|
||||
/// The URL path, the resource to be accessed.
|
||||
///
|
||||
/// A *non-empty* vector encoding the parts of the URL path.
|
||||
/// Empty entries of `""` correspond to trailing slashes.
|
||||
pub path: Vec<String>,
|
||||
|
||||
/// The URL query.
|
||||
pub query: Option<String>,
|
||||
|
||||
/// The URL username field, from the userinfo section of the URL.
|
||||
///
|
||||
/// `None` if the `@` character was not part of the input OR
|
||||
/// if a blank username was provided.
|
||||
/// Otherwise, a non-empty string.
|
||||
pub username: Option<String>,
|
||||
|
||||
/// The URL password field, from the userinfo section of the URL.
|
||||
///
|
||||
/// `None` if the `@` character was not part of the input OR
|
||||
/// if a blank password was provided.
|
||||
/// Otherwise, a non-empty string.
|
||||
pub password: Option<String>,
|
||||
}
|
||||
|
||||
impl Url {
|
||||
/// Create a URL from a string.
|
||||
///
|
||||
/// The input must be a valid URL with a special scheme for this to succeed.
|
||||
///
|
||||
/// HTTP and HTTPS are special schemes.
|
||||
///
|
||||
/// See: http://url.spec.whatwg.org/#special-scheme
|
||||
pub fn parse(input: &str) -> Result<Url, String> {
|
||||
// Parse the string using rust-url, then convert.
|
||||
match url_lib::Url::parse(input) {
|
||||
Ok(raw_url) => Url::from_generic_url(raw_url),
|
||||
Err(e) => Err(format!("{}", e))
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a `Url` from a `rust-url` `Url`.
|
||||
pub fn from_generic_url(raw_url: url_lib::Url) -> Result<Url, String> {
|
||||
// Map empty usernames to None.
|
||||
let username = match raw_url.username() {
|
||||
"" => None,
|
||||
username => Some(username.to_owned())
|
||||
};
|
||||
|
||||
// Map empty passwords to None.
|
||||
let password = match raw_url.password() {
|
||||
Some(password) if !password.is_empty() => Some(password.to_owned()),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let port = raw_url.port_or_known_default().ok_or_else(|| format!("Unknown port for scheme: `{}`", raw_url.scheme()))?;
|
||||
let host = raw_url.host().ok_or_else(|| "Valid host, because only data:, mailto: protocols does not have host.".to_owned())?.to_owned();
|
||||
let path = raw_url.path_segments().ok_or_else(|| "Valid path segments. In HTTP we won't get cannot-be-a-base URLs".to_owned())?
|
||||
.map(|part| part.to_owned()).collect();
|
||||
let query = raw_url.query().map(|x| x.to_owned());
|
||||
|
||||
Ok(Url {
|
||||
port: port,
|
||||
host: host,
|
||||
path: path,
|
||||
query: query,
|
||||
raw: raw_url,
|
||||
username: username,
|
||||
password: password,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::Url;
|
||||
|
||||
#[test]
|
||||
fn test_default_port() {
|
||||
assert_eq!(Url::parse("http://example.com/wow").unwrap().port, 80u16);
|
||||
assert_eq!(Url::parse("https://example.com/wow").unwrap().port, 443u16);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_explicit_port() {
|
||||
assert_eq!(Url::parse("http://localhost:3097").unwrap().port, 3097u16);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_username() {
|
||||
assert!(Url::parse("http://@example.com").unwrap().username.is_none());
|
||||
assert!(Url::parse("http://:password@example.com").unwrap().username.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_not_empty_username() {
|
||||
let user = Url::parse("http://john:pass@example.com").unwrap().username;
|
||||
assert_eq!(user.unwrap(), "john");
|
||||
|
||||
let user = Url::parse("http://john:@example.com").unwrap().username;
|
||||
assert_eq!(user.unwrap(), "john");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_password() {
|
||||
assert!(Url::parse("http://michael@example.com").unwrap().password.is_none());
|
||||
assert!(Url::parse("http://:@example.com").unwrap().password.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_not_empty_password() {
|
||||
let pass = Url::parse("http://michael:pass@example.com").unwrap().password;
|
||||
assert_eq!(pass.unwrap(), "pass");
|
||||
|
||||
let pass = Url::parse("http://:pass@example.com").unwrap().password;
|
||||
assert_eq!(pass.unwrap(), "pass");
|
||||
}
|
||||
}
|
||||
273
dapps/src/web.rs
273
dapps/src/web.rs
@@ -17,144 +17,73 @@
|
||||
//! Serving web-based content (proxying)
|
||||
|
||||
use std::sync::Arc;
|
||||
use fetch::{self, Fetch};
|
||||
use parity_reactor::Remote;
|
||||
|
||||
use base32;
|
||||
use fetch::{self, Fetch};
|
||||
use hyper::{mime, StatusCode};
|
||||
use hyper::{self, server, net, Next, Encoder, Decoder};
|
||||
use hyper::status::StatusCode;
|
||||
|
||||
use apps;
|
||||
use endpoint::{Endpoint, EndpointPath, Request, Response};
|
||||
use futures::future;
|
||||
use futures_cpupool::CpuPool;
|
||||
use endpoint::{Endpoint, Handler, EndpointPath};
|
||||
use handlers::{
|
||||
ContentFetcherHandler, ContentHandler, ContentValidator, ValidatorResponse,
|
||||
StreamingHandler,
|
||||
StreamingHandler, extract_url,
|
||||
};
|
||||
use {Embeddable, WebProxyTokens};
|
||||
use url::Url;
|
||||
use WebProxyTokens;
|
||||
|
||||
pub type Embeddable = Option<(String, u16)>;
|
||||
|
||||
pub struct Web<F> {
|
||||
embeddable_on: Embeddable,
|
||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||
remote: Remote,
|
||||
fetch: F,
|
||||
pool: CpuPool,
|
||||
}
|
||||
|
||||
impl<F: Fetch> Web<F> {
|
||||
pub fn boxed(
|
||||
embeddable_on: Embeddable,
|
||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||
fetch: F,
|
||||
pool: CpuPool,
|
||||
) -> Box<Endpoint> {
|
||||
pub fn boxed(embeddable_on: Embeddable, web_proxy_tokens: Arc<WebProxyTokens>, remote: Remote, fetch: F) -> Box<Endpoint> {
|
||||
Box::new(Web {
|
||||
embeddable_on,
|
||||
web_proxy_tokens,
|
||||
fetch,
|
||||
pool,
|
||||
embeddable_on: embeddable_on,
|
||||
web_proxy_tokens: web_proxy_tokens,
|
||||
remote: remote,
|
||||
fetch: fetch,
|
||||
})
|
||||
}
|
||||
|
||||
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),
|
||||
self.embeddable_on.clone()
|
||||
))?;
|
||||
|
||||
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."), self.embeddable_on.clone()
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
// 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, self.embeddable_on.clone()
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
if !target_url.starts_with(&*domain) {
|
||||
return Err(ContentHandler::error(
|
||||
StatusCode::BadRequest, "Invalid Domain", "Dapp attempted to access invalid domain.", Some(&target_url), self.embeddable_on.clone(),
|
||||
));
|
||||
}
|
||||
|
||||
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 {
|
||||
embeddable_on: self.embeddable_on.clone(),
|
||||
token,
|
||||
},
|
||||
self.embeddable_on.clone(),
|
||||
self.fetch.clone(),
|
||||
self.pool.clone(),
|
||||
))
|
||||
fn to_async_handler(&self, path: EndpointPath, control: hyper::Control) -> Box<Handler> {
|
||||
Box::new(WebHandler {
|
||||
control: control,
|
||||
state: State::Initial,
|
||||
path: path,
|
||||
remote: self.remote.clone(),
|
||||
fetch: self.fetch.clone(),
|
||||
web_proxy_tokens: self.web_proxy_tokens.clone(),
|
||||
embeddable_on: self.embeddable_on.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct WebInstaller {
|
||||
embeddable_on: Embeddable,
|
||||
token: String,
|
||||
referer: String,
|
||||
}
|
||||
|
||||
impl ContentValidator for WebInstaller {
|
||||
type Error = String;
|
||||
|
||||
fn validate_and_install(self, response: fetch::Response) -> Result<ValidatorResponse, String> {
|
||||
let status = response.status();
|
||||
fn validate_and_install(&self, response: fetch::Response) -> Result<ValidatorResponse, String> {
|
||||
let status = StatusCode::from_u16(response.status().to_u16());
|
||||
let is_html = response.is_html();
|
||||
let mime = response.content_type().unwrap_or(mime::TEXT_HTML);
|
||||
let mime = response.content_type().unwrap_or(mime!(Text/Html));
|
||||
let mut handler = StreamingHandler::new(
|
||||
fetch::BodyReader::new(response),
|
||||
response,
|
||||
status,
|
||||
mime,
|
||||
self.embeddable_on,
|
||||
self.embeddable_on.clone(),
|
||||
);
|
||||
if is_html {
|
||||
handler.set_initial_content(&format!(
|
||||
@@ -162,10 +91,146 @@ impl ContentValidator for WebInstaller {
|
||||
apps::UTILS_PATH,
|
||||
apps::URL_REFERER,
|
||||
apps::WEB_PATH,
|
||||
&self.token,
|
||||
&self.referer,
|
||||
));
|
||||
}
|
||||
Ok(ValidatorResponse::Streaming(handler))
|
||||
}
|
||||
}
|
||||
|
||||
enum State<F: Fetch> {
|
||||
Initial,
|
||||
Error(ContentHandler),
|
||||
Fetching(ContentFetcherHandler<WebInstaller, F>),
|
||||
}
|
||||
|
||||
struct WebHandler<F: Fetch> {
|
||||
control: hyper::Control,
|
||||
state: State<F>,
|
||||
path: EndpointPath,
|
||||
remote: Remote,
|
||||
fetch: F,
|
||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||
embeddable_on: Embeddable,
|
||||
}
|
||||
|
||||
impl<F: Fetch> WebHandler<F> {
|
||||
fn extract_target_url(&self, url: Option<Url>) -> Result<String, State<F>> {
|
||||
let token_and_url = self.path.app_params.get(0)
|
||||
.map(|encoded| encoded.replace('.', ""))
|
||||
.and_then(|encoded| base32::decode(base32::Alphabet::Crockford, &encoded.to_uppercase()))
|
||||
.and_then(|data| String::from_utf8(data).ok())
|
||||
.ok_or_else(|| State::Error(ContentHandler::error(
|
||||
StatusCode::BadRequest,
|
||||
"Invalid parameter",
|
||||
"Couldn't parse given parameter:",
|
||||
self.path.app_params.get(0).map(String::as_str),
|
||||
self.embeddable_on.clone()
|
||||
)))?;
|
||||
|
||||
let mut token_it = token_and_url.split('+');
|
||||
let token = token_it.next();
|
||||
let target_url = token_it.next();
|
||||
|
||||
// Check if token supplied in URL is correct.
|
||||
match token {
|
||||
Some(token) if self.web_proxy_tokens.is_web_proxy_token_valid(token) => {},
|
||||
_ => {
|
||||
return Err(State::Error(ContentHandler::error(
|
||||
StatusCode::BadRequest, "Invalid Access Token", "Invalid or old web proxy access token supplied.", Some("Try refreshing the page."), self.embeddable_on.clone()
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
// 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(State::Error(ContentHandler::error(
|
||||
StatusCode::BadRequest, "Invalid Protocol", "Invalid protocol used.", None, self.embeddable_on.clone()
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
if !target_url.ends_with("/") {
|
||||
target_url = format!("{}/", target_url);
|
||||
}
|
||||
|
||||
// TODO [ToDr] Should just use `path.app_params`
|
||||
let (path, query) = match (&url, self.path.using_dapps_domains) {
|
||||
(&Some(ref url), true) => (&url.path[..], &url.query),
|
||||
(&Some(ref url), false) => (&url.path[2..], &url.query),
|
||||
_ => {
|
||||
return Err(State::Error(ContentHandler::error(
|
||||
StatusCode::BadRequest, "Invalid URL", "Couldn't parse URL", None, self.embeddable_on.clone()
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
let query = match *query {
|
||||
Some(ref query) => format!("?{}", query),
|
||||
None => "".into(),
|
||||
};
|
||||
|
||||
Ok(format!("{}{}{}", target_url, path.join("/"), query))
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Fetch> server::Handler<net::HttpStream> for WebHandler<F> {
|
||||
fn on_request(&mut self, request: server::Request<net::HttpStream>) -> Next {
|
||||
let url = extract_url(&request);
|
||||
// First extract the URL (reject invalid URLs)
|
||||
let target_url = match self.extract_target_url(url) {
|
||||
Ok(url) => url,
|
||||
Err(error) => {
|
||||
self.state = error;
|
||||
return Next::write();
|
||||
}
|
||||
};
|
||||
|
||||
let mut handler = ContentFetcherHandler::new(
|
||||
target_url,
|
||||
self.path.clone(),
|
||||
self.control.clone(),
|
||||
WebInstaller {
|
||||
embeddable_on: self.embeddable_on.clone(),
|
||||
referer: self.path.app_params.get(0)
|
||||
.expect("`target_url` is valid; app_params is not empty;qed")
|
||||
.to_owned(),
|
||||
},
|
||||
self.embeddable_on.clone(),
|
||||
self.remote.clone(),
|
||||
self.fetch.clone(),
|
||||
);
|
||||
let res = handler.on_request(request);
|
||||
self.state = State::Fetching(handler);
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
fn on_request_readable(&mut self, decoder: &mut Decoder<net::HttpStream>) -> Next {
|
||||
match self.state {
|
||||
State::Initial => Next::end(),
|
||||
State::Error(ref mut handler) => handler.on_request_readable(decoder),
|
||||
State::Fetching(ref mut handler) => handler.on_request_readable(decoder),
|
||||
}
|
||||
}
|
||||
|
||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||
match self.state {
|
||||
State::Initial => Next::end(),
|
||||
State::Error(ref mut handler) => handler.on_response(res),
|
||||
State::Fetching(ref mut handler) => handler.on_response(res),
|
||||
}
|
||||
}
|
||||
|
||||
fn on_response_writable(&mut self, encoder: &mut Encoder<net::HttpStream>) -> Next {
|
||||
match self.state {
|
||||
State::Initial => Next::end(),
|
||||
State::Error(ref mut handler) => handler.on_response_writable(encoder),
|
||||
State::Fetching(ref mut handler) => handler.on_response_writable(encoder),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>Parity</title>
|
||||
<style>
|
||||
/* 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/>.
|
||||
*/
|
||||
: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: 'Roboto', 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: 'Roboto Mono', 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>The Parity UI has been split off into a standalone project.</h1>
|
||||
<h3>Get the standalone Parity UI from <a href="https://github.com/Parity-JS/shell/releases">here</a></h3>
|
||||
<p>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
<div class="parity-status">
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,18 +3,16 @@ description = "Ethcore Parity UI"
|
||||
homepage = "http://parity.io"
|
||||
license = "GPL-3.0"
|
||||
name = "parity-ui"
|
||||
version = "1.11.0"
|
||||
version = "1.6.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[build-dependencies]
|
||||
rustc_version = "0.2"
|
||||
rustc_version = "0.1"
|
||||
|
||||
[dependencies]
|
||||
parity-ui-dev = { git = "https://github.com/parity-js/shell.git", rev = "eecaadcb9e421bce31e91680d14a20bbd38f92a2", optional = true }
|
||||
parity-ui-old-dev = { git = "https://github.com/parity-js/dapp-wallet.git", rev = "65deb02e7c007a0fd8aab0c089c93e3fd1de6f87", optional = true }
|
||||
parity-ui-precompiled = { git = "https://github.com/js-dist-paritytech/parity-master-1-10-shell.git", rev="bd25b41cd642c6b822d820dded3aa601a29aa079", optional = true }
|
||||
parity-ui-old-precompiled = { git = "https://github.com/js-dist-paritytech/parity-master-1-10-wallet.git", rev="4b6f112412716cd05123d32eeb7fda448288a6c6", optional = true }
|
||||
parity-ui-dev = { path = "../../js", optional = true }
|
||||
parity-ui-precompiled = { git = "https://github.com/ethcore/js-precompiled.git", branch = "beta", optional = true }
|
||||
|
||||
[features]
|
||||
no-precompiled-js = ["parity-ui-dev", "parity-ui-old-dev"]
|
||||
use-precompiled-js = ["parity-ui-precompiled", "parity-ui-old-precompiled"]
|
||||
no-precompiled-js = ["parity-ui-dev"]
|
||||
use-precompiled-js = ["parity-ui-precompiled"]
|
||||
|
||||
@@ -29,18 +29,5 @@ mod inner {
|
||||
pub use self::parity_ui_precompiled::*;
|
||||
}
|
||||
|
||||
#[cfg(feature = "parity-ui-old-dev")]
|
||||
pub mod old {
|
||||
extern crate parity_ui_old_dev;
|
||||
|
||||
pub use self::parity_ui_old_dev::*;
|
||||
}
|
||||
|
||||
#[cfg(feature = "parity-ui-old-precompiled")]
|
||||
pub mod old {
|
||||
extern crate parity_ui_old_precompiled;
|
||||
|
||||
pub use self::parity_ui_old_precompiled::*;
|
||||
}
|
||||
|
||||
pub use self::inner::*;
|
||||
|
||||
25
db/Cargo.toml
Normal file
25
db/Cargo.toml
Normal file
@@ -0,0 +1,25 @@
|
||||
[package]
|
||||
description = "Ethcore Database"
|
||||
homepage = "http://parity.io"
|
||||
license = "GPL-3.0"
|
||||
name = "ethcore-db"
|
||||
version = "1.6.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
build = "build.rs"
|
||||
|
||||
[build-dependencies]
|
||||
ethcore-ipc-codegen = { path = "../ipc/codegen" }
|
||||
|
||||
[dependencies]
|
||||
clippy = { version = "0.0.103", optional = true}
|
||||
ethcore-devtools = { path = "../devtools" }
|
||||
ethcore-ipc = { path = "../ipc/rpc" }
|
||||
rocksdb = { git = "https://github.com/ethcore/rust-rocksdb" }
|
||||
semver = "0.5"
|
||||
ethcore-ipc-nano = { path = "../ipc/nano" }
|
||||
nanomsg = { git = "https://github.com/ethcore/nanomsg.rs.git" }
|
||||
crossbeam = "0.2"
|
||||
ethcore-util = { path = "../util" }
|
||||
|
||||
[features]
|
||||
dev = ["clippy"]
|
||||
@@ -14,32 +14,25 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::fmt;
|
||||
extern crate ethcore_ipc_codegen as codegen;
|
||||
|
||||
/// Type of EVM to use.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum VMType {
|
||||
/// RUST EVM
|
||||
Interpreter
|
||||
}
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
|
||||
impl fmt::Display for VMType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", match *self {
|
||||
VMType::Interpreter => "INT"
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for VMType {
|
||||
fn default() -> Self {
|
||||
VMType::Interpreter
|
||||
}
|
||||
}
|
||||
|
||||
impl VMType {
|
||||
/// Return all possible VMs (Interpreter)
|
||||
pub fn all() -> Vec<VMType> {
|
||||
vec![VMType::Interpreter]
|
||||
pub fn main() {
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
|
||||
// ipc pass
|
||||
{
|
||||
let src = Path::new("src/lib.rs.in");
|
||||
let dst = Path::new(&out_dir).join("lib.intermediate.rs.in");
|
||||
codegen::expand(&src, &dst);
|
||||
}
|
||||
|
||||
// binary serialization pass
|
||||
{
|
||||
let src = Path::new(&out_dir).join("lib.intermediate.rs.in");
|
||||
let dst = Path::new(&out_dir).join("lib.rs");
|
||||
codegen::expand(&src, &dst);
|
||||
}
|
||||
}
|
||||
565
db/src/database.rs
Normal file
565
db/src/database.rs
Normal file
@@ -0,0 +1,565 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Ethcore rocksdb ipc service
|
||||
|
||||
use traits::*;
|
||||
use rocksdb::{DB, Writable, WriteBatch, IteratorMode, DBIterator, IndexType, Options, DBCompactionStyle, BlockBasedOptions, Direction};
|
||||
use std::sync::{RwLock, Arc};
|
||||
use std::convert::From;
|
||||
use ipc::IpcConfig;
|
||||
use std::mem;
|
||||
use ipc::binary::BinaryConvertError;
|
||||
use std::collections::{VecDeque, HashMap, BTreeMap};
|
||||
|
||||
enum WriteCacheEntry {
|
||||
Remove,
|
||||
Write(Vec<u8>),
|
||||
}
|
||||
|
||||
pub struct WriteCache {
|
||||
entries: HashMap<Vec<u8>, WriteCacheEntry>,
|
||||
preferred_len: usize,
|
||||
}
|
||||
|
||||
const FLUSH_BATCH_SIZE: usize = 4096;
|
||||
|
||||
impl WriteCache {
|
||||
fn new(cache_len: usize) -> WriteCache {
|
||||
WriteCache {
|
||||
entries: HashMap::new(),
|
||||
preferred_len: cache_len,
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, key: Vec<u8>, val: Vec<u8>) {
|
||||
self.entries.insert(key, WriteCacheEntry::Write(val));
|
||||
}
|
||||
|
||||
fn remove(&mut self, key: Vec<u8>) {
|
||||
self.entries.insert(key, WriteCacheEntry::Remove);
|
||||
}
|
||||
|
||||
fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
|
||||
self.entries.get(key).and_then(
|
||||
|vec_ref| match vec_ref {
|
||||
&WriteCacheEntry::Write(ref val) => Some(val.clone()),
|
||||
&WriteCacheEntry::Remove => None
|
||||
})
|
||||
}
|
||||
|
||||
/// WriteCache should be locked for this
|
||||
fn flush(&mut self, db: &DB, amount: usize) -> Result<(), Error> {
|
||||
let batch = WriteBatch::new();
|
||||
let mut removed_so_far = 0;
|
||||
while removed_so_far < amount {
|
||||
if self.entries.len() == 0 { break; }
|
||||
let removed_key = {
|
||||
let (key, cache_entry) = self.entries.iter().nth(0)
|
||||
.expect("if entries.len == 0, we should have break in the loop, still we got here somehow");
|
||||
|
||||
match *cache_entry {
|
||||
WriteCacheEntry::Write(ref val) => {
|
||||
batch.put(&key, val)?;
|
||||
},
|
||||
WriteCacheEntry::Remove => {
|
||||
batch.delete(&key)?;
|
||||
},
|
||||
}
|
||||
key.clone()
|
||||
};
|
||||
|
||||
self.entries.remove(&removed_key);
|
||||
|
||||
removed_so_far = removed_so_far + 1;
|
||||
}
|
||||
if removed_so_far > 0 {
|
||||
db.write(batch)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// flushes until cache is empty
|
||||
fn flush_all(&mut self, db: &DB) -> Result<(), Error> {
|
||||
while !self.is_empty() { self.flush(db, FLUSH_BATCH_SIZE)?; }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.entries.is_empty()
|
||||
}
|
||||
|
||||
fn try_shrink(&mut self, db: &DB) -> Result<(), Error> {
|
||||
if self.entries.len() > self.preferred_len {
|
||||
self.flush(db, FLUSH_BATCH_SIZE)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Database {
|
||||
db: RwLock<Option<DB>>,
|
||||
/// Iterators - dont't use between threads!
|
||||
iterators: RwLock<BTreeMap<IteratorHandle, DBIterator>>,
|
||||
write_cache: RwLock<WriteCache>,
|
||||
}
|
||||
|
||||
unsafe impl Send for Database {}
|
||||
unsafe impl Sync for Database {}
|
||||
|
||||
impl Database {
|
||||
pub fn new() -> Database {
|
||||
Database {
|
||||
db: RwLock::new(None),
|
||||
iterators: RwLock::new(BTreeMap::new()),
|
||||
write_cache: RwLock::new(WriteCache::new(DEFAULT_CACHE_LEN)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flush(&self) -> Result<(), Error> {
|
||||
let mut cache_lock = self.write_cache.write();
|
||||
let db_lock = self.db.read();
|
||||
if db_lock.is_none() { return Ok(()); }
|
||||
let db = db_lock.as_ref().unwrap();
|
||||
|
||||
cache_lock.try_shrink(&db)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn flush_all(&self) -> Result<(), Error> {
|
||||
let mut cache_lock = self.write_cache.write();
|
||||
let db_lock = self.db.read();
|
||||
if db_lock.is_none() { return Ok(()); }
|
||||
let db = db_lock.as_ref().expect("we should have exited with Ok(()) on the previous step");
|
||||
|
||||
cache_lock.flush_all(&db)?;
|
||||
Ok(())
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Database {
|
||||
fn drop(&mut self) {
|
||||
self.flush().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[ipc]
|
||||
impl DatabaseService for Database {
|
||||
fn open(&self, config: DatabaseConfig, path: String) -> Result<(), Error> {
|
||||
let mut db = self.db.write();
|
||||
if db.is_some() { return Err(Error::AlreadyOpen); }
|
||||
|
||||
let mut opts = Options::new();
|
||||
opts.set_max_open_files(256);
|
||||
opts.create_if_missing(true);
|
||||
opts.set_use_fsync(false);
|
||||
opts.set_compaction_style(DBCompactionStyle::DBUniversalCompaction);
|
||||
if let Some(size) = config.prefix_size {
|
||||
let mut block_opts = BlockBasedOptions::new();
|
||||
block_opts.set_index_type(IndexType::HashSearch);
|
||||
opts.set_block_based_table_factory(&block_opts);
|
||||
opts.set_prefix_extractor_fixed_size(size);
|
||||
}
|
||||
*db = Some(DB::open(&opts, &path)?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Opens database in the specified path with the default config
|
||||
fn open_default(&self, path: String) -> Result<(), Error> {
|
||||
self.open(DatabaseConfig::default(), path)
|
||||
}
|
||||
|
||||
fn close(&self) -> Result<(), Error> {
|
||||
self.flush_all()?;
|
||||
|
||||
let mut db = self.db.write();
|
||||
if db.is_none() { return Err(Error::IsClosed); }
|
||||
|
||||
*db = None;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn put(&self, key: &[u8], value: &[u8]) -> Result<(), Error> {
|
||||
let mut cache_lock = self.write_cache.write();
|
||||
cache_lock.write(key.to_vec(), value.to_vec());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn delete(&self, key: &[u8]) -> Result<(), Error> {
|
||||
let mut cache_lock = self.write_cache.write();
|
||||
cache_lock.remove(key.to_vec());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write(&self, transaction: DBTransaction) -> Result<(), Error> {
|
||||
let mut cache_lock = self.write_cache.write();
|
||||
|
||||
let mut writes = transaction.writes.borrow_mut();
|
||||
for kv in writes.drain(..) {
|
||||
cache_lock.write(kv.key, kv.value);
|
||||
}
|
||||
|
||||
let mut removes = transaction.removes.borrow_mut();
|
||||
for k in removes.drain(..) {
|
||||
cache_lock.remove(k);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Error> {
|
||||
{
|
||||
let key_vec = key.to_vec();
|
||||
let cache_hit = self.write_cache.read().get(&key_vec);
|
||||
|
||||
if cache_hit.is_some() {
|
||||
return Ok(Some(cache_hit.expect("cache_hit.is_some() = true, still there is none somehow here")))
|
||||
}
|
||||
}
|
||||
let db_lock = self.db.read();
|
||||
let db = db_lock.as_ref().ok_or(Error::IsClosed)?;
|
||||
|
||||
match db.get(key)? {
|
||||
Some(db_vec) => {
|
||||
Ok(Some(db_vec.to_vec()))
|
||||
},
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_by_prefix(&self, prefix: &[u8]) -> Result<Option<Vec<u8>>, Error> {
|
||||
let db_lock = self.db.read();
|
||||
let db = db_lock.as_ref().ok_or(Error::IsClosed)?;
|
||||
|
||||
let mut iter = db.iterator(IteratorMode::From(prefix, Direction::Forward));
|
||||
match iter.next() {
|
||||
// TODO: use prefix_same_as_start read option (not availabele in C API currently)
|
||||
Some((k, v)) => if k[0 .. prefix.len()] == prefix[..] { Ok(Some(v.to_vec())) } else { Ok(None) },
|
||||
_ => Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> Result<bool, Error> {
|
||||
let db_lock = self.db.read();
|
||||
let db = db_lock.as_ref().ok_or(Error::IsClosed)?;
|
||||
|
||||
Ok(db.iterator(IteratorMode::Start).next().is_none())
|
||||
}
|
||||
|
||||
fn iter(&self) -> Result<IteratorHandle, Error> {
|
||||
let db_lock = self.db.read();
|
||||
let db = db_lock.as_ref().ok_or(Error::IsClosed)?;
|
||||
|
||||
let mut iterators = self.iterators.write();
|
||||
let next_iterator = iterators.keys().last().unwrap_or(&0) + 1;
|
||||
iterators.insert(next_iterator, db.iterator(IteratorMode::Start));
|
||||
Ok(next_iterator)
|
||||
}
|
||||
|
||||
fn iter_next(&self, handle: IteratorHandle) -> Option<KeyValue> {
|
||||
let mut iterators = self.iterators.write();
|
||||
let mut iterator = match iterators.get_mut(&handle) {
|
||||
Some(some_iterator) => some_iterator,
|
||||
None => { return None; },
|
||||
};
|
||||
|
||||
iterator.next().and_then(|(some_key, some_val)| {
|
||||
Some(KeyValue {
|
||||
key: some_key.to_vec(),
|
||||
value: some_val.to_vec(),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn dispose_iter(&self, handle: IteratorHandle) -> Result<(), Error> {
|
||||
let mut iterators = self.iterators.write();
|
||||
iterators.remove(&handle);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// TODO : put proper at compile-time
|
||||
impl IpcConfig for Database {}
|
||||
|
||||
/// Database iterator
|
||||
pub struct DatabaseIterator {
|
||||
client: Arc<DatabaseClient<::nanomsg::Socket>>,
|
||||
handle: IteratorHandle,
|
||||
}
|
||||
|
||||
impl Iterator for DatabaseIterator {
|
||||
type Item = (Vec<u8>, Vec<u8>);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.client.iter_next(self.handle).and_then(|kv| Some((kv.key, kv.value)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DatabaseIterator {
|
||||
fn drop(&mut self) {
|
||||
self.client.dispose_iter(self.handle).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use super::Database;
|
||||
use traits::*;
|
||||
use devtools::*;
|
||||
|
||||
#[test]
|
||||
fn can_be_created() {
|
||||
let db = Database::new();
|
||||
assert!(db.is_empty().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_be_open_empty() {
|
||||
let db = Database::new();
|
||||
let path = RandomTempPath::create_dir();
|
||||
db.open_default(path.as_str().to_owned()).unwrap();
|
||||
|
||||
assert!(db.is_empty().is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_store_key() {
|
||||
let db = Database::new();
|
||||
let path = RandomTempPath::create_dir();
|
||||
db.open_default(path.as_str().to_owned()).unwrap();
|
||||
|
||||
db.put("xxx".as_bytes(), "1".as_bytes()).unwrap();
|
||||
db.flush_all().unwrap();
|
||||
assert!(!db.is_empty().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_retrieve() {
|
||||
let db = Database::new();
|
||||
let path = RandomTempPath::create_dir();
|
||||
db.open_default(path.as_str().to_owned()).unwrap();
|
||||
db.put("xxx".as_bytes(), "1".as_bytes()).unwrap();
|
||||
db.close().unwrap();
|
||||
|
||||
db.open_default(path.as_str().to_owned()).unwrap();
|
||||
assert_eq!(db.get("xxx".as_bytes()).unwrap().unwrap(), "1".as_bytes().to_vec());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod write_cache_tests {
|
||||
use super::Database;
|
||||
use traits::*;
|
||||
use devtools::*;
|
||||
|
||||
#[test]
|
||||
fn cache_write_flush() {
|
||||
let db = Database::new();
|
||||
let path = RandomTempPath::create_dir();
|
||||
|
||||
db.open_default(path.as_str().to_owned()).unwrap();
|
||||
db.put("100500".as_bytes(), "1".as_bytes()).unwrap();
|
||||
db.delete("100500".as_bytes()).unwrap();
|
||||
db.flush_all().unwrap();
|
||||
|
||||
let val = db.get("100500".as_bytes()).unwrap();
|
||||
assert!(val.is_none());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod client_tests {
|
||||
use super::{DatabaseClient, Database};
|
||||
use traits::*;
|
||||
use devtools::*;
|
||||
use nanoipc;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{Ordering, AtomicBool};
|
||||
use crossbeam;
|
||||
use run_worker;
|
||||
|
||||
fn init_worker(addr: &str) -> nanoipc::Worker<Database> {
|
||||
let mut worker = nanoipc::Worker::<Database>::new(&Arc::new(Database::new()));
|
||||
worker.add_duplex(addr).unwrap();
|
||||
worker
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_call_handshake() {
|
||||
let url = "ipc:///tmp/parity-db-ipc-test-10.ipc";
|
||||
let worker_should_exit = Arc::new(AtomicBool::new(false));
|
||||
let worker_is_ready = Arc::new(AtomicBool::new(false));
|
||||
let c_worker_should_exit = worker_should_exit.clone();
|
||||
let c_worker_is_ready = worker_is_ready.clone();
|
||||
|
||||
::std::thread::spawn(move || {
|
||||
let mut worker = init_worker(url);
|
||||
while !c_worker_should_exit.load(Ordering::Relaxed) {
|
||||
worker.poll();
|
||||
c_worker_is_ready.store(true, Ordering::Relaxed);
|
||||
}
|
||||
});
|
||||
|
||||
while !worker_is_ready.load(Ordering::Relaxed) { }
|
||||
let client = nanoipc::init_duplex_client::<DatabaseClient<_>>(url).unwrap();
|
||||
|
||||
let hs = client.handshake();
|
||||
|
||||
worker_should_exit.store(true, Ordering::Relaxed);
|
||||
assert!(hs.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_open_db() {
|
||||
let url = "ipc:///tmp/parity-db-ipc-test-20.ipc";
|
||||
let path = RandomTempPath::create_dir();
|
||||
|
||||
let worker_should_exit = Arc::new(AtomicBool::new(false));
|
||||
let worker_is_ready = Arc::new(AtomicBool::new(false));
|
||||
let c_worker_should_exit = worker_should_exit.clone();
|
||||
let c_worker_is_ready = worker_is_ready.clone();
|
||||
|
||||
::std::thread::spawn(move || {
|
||||
let mut worker = init_worker(url);
|
||||
while !c_worker_should_exit.load(Ordering::Relaxed) {
|
||||
worker.poll();
|
||||
c_worker_is_ready.store(true, Ordering::Relaxed);
|
||||
}
|
||||
});
|
||||
|
||||
while !worker_is_ready.load(Ordering::Relaxed) { }
|
||||
let client = nanoipc::init_duplex_client::<DatabaseClient<_>>(url).unwrap();
|
||||
|
||||
client.open_default(path.as_str().to_owned()).unwrap();
|
||||
assert!(client.is_empty().unwrap());
|
||||
worker_should_exit.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_put() {
|
||||
let url = "ipc:///tmp/parity-db-ipc-test-30.ipc";
|
||||
let path = RandomTempPath::create_dir();
|
||||
|
||||
crossbeam::scope(move |scope| {
|
||||
let stop = Arc::new(AtomicBool::new(false));
|
||||
run_worker(scope, stop.clone(), url);
|
||||
let client = nanoipc::generic_client::<DatabaseClient<_>>(url).unwrap();
|
||||
client.open_default(path.as_str().to_owned()).unwrap();
|
||||
client.put("xxx".as_bytes(), "1".as_bytes()).unwrap();
|
||||
client.close().unwrap();
|
||||
|
||||
stop.store(true, Ordering::Relaxed);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_put_and_read() {
|
||||
let url = "ipc:///tmp/parity-db-ipc-test-40.ipc";
|
||||
let path = RandomTempPath::create_dir();
|
||||
|
||||
crossbeam::scope(move |scope| {
|
||||
let stop = Arc::new(AtomicBool::new(false));
|
||||
run_worker(scope, stop.clone(), url);
|
||||
let client = nanoipc::generic_client::<DatabaseClient<_>>(url).unwrap();
|
||||
|
||||
client.open_default(path.as_str().to_owned()).unwrap();
|
||||
client.put("xxx".as_bytes(), "1".as_bytes()).unwrap();
|
||||
client.close().unwrap();
|
||||
|
||||
client.open_default(path.as_str().to_owned()).unwrap();
|
||||
assert_eq!(client.get("xxx".as_bytes()).unwrap().unwrap(), "1".as_bytes().to_vec());
|
||||
|
||||
stop.store(true, Ordering::Relaxed);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_read_empty() {
|
||||
let url = "ipc:///tmp/parity-db-ipc-test-45.ipc";
|
||||
let path = RandomTempPath::create_dir();
|
||||
|
||||
crossbeam::scope(move |scope| {
|
||||
let stop = Arc::new(AtomicBool::new(false));
|
||||
run_worker(scope, stop.clone(), url);
|
||||
let client = nanoipc::generic_client::<DatabaseClient<_>>(url).unwrap();
|
||||
|
||||
client.open_default(path.as_str().to_owned()).unwrap();
|
||||
assert!(client.get("xxx".as_bytes()).unwrap().is_none());
|
||||
|
||||
stop.store(true, Ordering::Relaxed);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn can_commit_client_transaction() {
|
||||
let url = "ipc:///tmp/parity-db-ipc-test-60.ipc";
|
||||
let path = RandomTempPath::create_dir();
|
||||
|
||||
crossbeam::scope(move |scope| {
|
||||
let stop = Arc::new(AtomicBool::new(false));
|
||||
run_worker(scope, stop.clone(), url);
|
||||
let client = nanoipc::generic_client::<DatabaseClient<_>>(url).unwrap();
|
||||
client.open_default(path.as_str().to_owned()).unwrap();
|
||||
|
||||
let transaction = DBTransaction::new();
|
||||
transaction.put("xxx".as_bytes(), "1".as_bytes());
|
||||
client.write(transaction).unwrap();
|
||||
|
||||
client.close().unwrap();
|
||||
|
||||
client.open_default(path.as_str().to_owned()).unwrap();
|
||||
assert_eq!(client.get("xxx".as_bytes()).unwrap().unwrap(), "1".as_bytes().to_vec());
|
||||
|
||||
stop.store(true, Ordering::Relaxed);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_write_read_ipc() {
|
||||
let url = "ipc:///tmp/parity-db-ipc-test-70.ipc";
|
||||
let path = RandomTempPath::create_dir();
|
||||
|
||||
crossbeam::scope(|scope| {
|
||||
let stop = StopGuard::new();
|
||||
run_worker(&scope, stop.share(), url);
|
||||
|
||||
let client = nanoipc::generic_client::<DatabaseClient<_>>(url).unwrap();
|
||||
|
||||
client.open_default(path.as_str().to_owned()).unwrap();
|
||||
let mut batch = Vec::new();
|
||||
for _ in 0..100 {
|
||||
batch.push((random_str(256).as_bytes().to_vec(), random_str(256).as_bytes().to_vec()));
|
||||
batch.push((random_str(256).as_bytes().to_vec(), random_str(2048).as_bytes().to_vec()));
|
||||
batch.push((random_str(2048).as_bytes().to_vec(), random_str(2048).as_bytes().to_vec()));
|
||||
batch.push((random_str(2048).as_bytes().to_vec(), random_str(256).as_bytes().to_vec()));
|
||||
}
|
||||
|
||||
for &(ref k, ref v) in batch.iter() {
|
||||
client.put(k, v).unwrap();
|
||||
}
|
||||
client.close().unwrap();
|
||||
|
||||
client.open_default(path.as_str().to_owned()).unwrap();
|
||||
for &(ref k, ref v) in batch.iter() {
|
||||
assert_eq!(v, &client.get(k).unwrap().unwrap());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
20
db/src/lib.rs
Normal file
20
db/src/lib.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Database ipc service
|
||||
|
||||
#![allow(dead_code, unused_assignments, unused_variables)] // codegen issues
|
||||
include!(concat!(env!("OUT_DIR"), "/lib.rs"));
|
||||
89
db/src/lib.rs.in
Normal file
89
db/src/lib.rs.in
Normal file
@@ -0,0 +1,89 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate ethcore_ipc as ipc;
|
||||
extern crate rocksdb;
|
||||
extern crate ethcore_devtools as devtools;
|
||||
extern crate semver;
|
||||
extern crate ethcore_ipc_nano as nanoipc;
|
||||
extern crate nanomsg;
|
||||
extern crate crossbeam;
|
||||
extern crate ethcore_util as util;
|
||||
|
||||
pub mod database;
|
||||
pub mod traits;
|
||||
|
||||
pub use traits::{DatabaseService, DBTransaction, Error};
|
||||
pub use database::{Database, DatabaseClient, DatabaseIterator};
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::*;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub type DatabaseNanoClient = DatabaseClient<::nanomsg::Socket>;
|
||||
pub type DatabaseConnection = nanoipc::GuardedSocket<DatabaseNanoClient>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ServiceError {
|
||||
Io(std::io::Error),
|
||||
Socket(nanoipc::SocketError),
|
||||
}
|
||||
|
||||
impl std::convert::From<std::io::Error> for ServiceError {
|
||||
fn from(io_error: std::io::Error) -> ServiceError { ServiceError::Io(io_error) }
|
||||
}
|
||||
|
||||
impl std::convert::From<nanoipc::SocketError> for ServiceError {
|
||||
fn from(socket_error: nanoipc::SocketError) -> ServiceError { ServiceError::Socket(socket_error) }
|
||||
}
|
||||
|
||||
pub fn blocks_service_url(db_path: &str) -> Result<String, std::io::Error> {
|
||||
let mut path = PathBuf::from(db_path);
|
||||
::std::fs::create_dir_all(db_path)?;
|
||||
path.push("blocks.ipc");
|
||||
Ok(format!("ipc://{}", path.to_str().unwrap()))
|
||||
}
|
||||
|
||||
pub fn extras_service_url(db_path: &str) -> Result<String, ::std::io::Error> {
|
||||
let mut path = PathBuf::from(db_path);
|
||||
::std::fs::create_dir_all(db_path)?;
|
||||
path.push("extras.ipc");
|
||||
Ok(format!("ipc://{}", path.to_str().unwrap()))
|
||||
}
|
||||
|
||||
pub fn blocks_client(db_path: &str) -> Result<DatabaseConnection, ServiceError> {
|
||||
let url = blocks_service_url(db_path)?;
|
||||
let client = nanoipc::generic_client::<DatabaseClient<_>>(&url)?;
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
pub fn extras_client(db_path: &str) -> Result<DatabaseConnection, ServiceError> {
|
||||
let url = extras_service_url(db_path)?;
|
||||
let client = nanoipc::generic_client::<DatabaseClient<_>>(&url)?;
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
// for tests
|
||||
pub fn run_worker(scope: &crossbeam::Scope, stop: Arc<AtomicBool>, socket_path: &str) {
|
||||
let socket_path = socket_path.to_owned();
|
||||
scope.spawn(move || {
|
||||
let mut worker = nanoipc::Worker::new(&Arc::new(Database::new()));
|
||||
worker.add_reqrep(&socket_path).unwrap();
|
||||
while !stop.load(Ordering::Relaxed) {
|
||||
worker.poll();
|
||||
}
|
||||
});
|
||||
}
|
||||
135
db/src/traits.rs
Normal file
135
db/src/traits.rs
Normal file
@@ -0,0 +1,135 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Ethcore database trait
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
||||
pub type IteratorHandle = u32;
|
||||
|
||||
pub const DEFAULT_CACHE_LEN: usize = 12288;
|
||||
|
||||
#[derive(Binary)]
|
||||
pub struct KeyValue {
|
||||
pub key: Vec<u8>,
|
||||
pub value: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Binary)]
|
||||
pub enum Error {
|
||||
AlreadyOpen,
|
||||
IsClosed,
|
||||
RocksDb(String),
|
||||
TransactionUnknown,
|
||||
IteratorUnknown,
|
||||
UncommitedTransactions,
|
||||
}
|
||||
|
||||
impl From<String> for Error {
|
||||
fn from(s: String) -> Error {
|
||||
Error::RocksDb(s)
|
||||
}
|
||||
}
|
||||
|
||||
/// Database configuration
|
||||
#[derive(Binary)]
|
||||
pub struct DatabaseConfig {
|
||||
/// Optional prefix size in bytes. Allows lookup by partial key.
|
||||
pub prefix_size: Option<usize>,
|
||||
/// write cache length
|
||||
pub cache: usize,
|
||||
}
|
||||
|
||||
impl Default for DatabaseConfig {
|
||||
fn default() -> DatabaseConfig {
|
||||
DatabaseConfig {
|
||||
prefix_size: None,
|
||||
cache: DEFAULT_CACHE_LEN,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DatabaseConfig {
|
||||
fn with_prefix(prefix: usize) -> DatabaseConfig {
|
||||
DatabaseConfig {
|
||||
prefix_size: Some(prefix),
|
||||
cache: DEFAULT_CACHE_LEN,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait DatabaseService : Sized {
|
||||
/// Opens database in the specified path
|
||||
fn open(&self, config: DatabaseConfig, path: String) -> Result<(), Error>;
|
||||
|
||||
/// Opens database in the specified path with the default config
|
||||
fn open_default(&self, path: String) -> Result<(), Error>;
|
||||
|
||||
/// Closes database
|
||||
fn close(&self) -> Result<(), Error>;
|
||||
|
||||
/// Insert a key-value pair in the transaction. Any existing value value will be overwritten.
|
||||
fn put(&self, key: &[u8], value: &[u8]) -> Result<(), Error>;
|
||||
|
||||
/// Delete value by key.
|
||||
fn delete(&self, key: &[u8]) -> Result<(), Error>;
|
||||
|
||||
/// Get value by key.
|
||||
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Error>;
|
||||
|
||||
/// Get value by partial key. Prefix size should match configured prefix size.
|
||||
fn get_by_prefix(&self, prefix: &[u8]) -> Result<Option<Vec<u8>>, Error>;
|
||||
|
||||
/// Check if there is anything in the database.
|
||||
fn is_empty(&self) -> Result<bool, Error>;
|
||||
|
||||
/// Get handle to iterate through keys
|
||||
fn iter(&self) -> Result<IteratorHandle, Error>;
|
||||
|
||||
/// Next key-value for the the given iterator
|
||||
fn iter_next(&self, iterator: IteratorHandle) -> Option<KeyValue>;
|
||||
|
||||
/// Dispose iteration that is no longer needed
|
||||
fn dispose_iter(&self, handle: IteratorHandle) -> Result<(), Error>;
|
||||
|
||||
/// Write client transaction
|
||||
fn write(&self, transaction: DBTransaction) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
#[derive(Binary)]
|
||||
pub struct DBTransaction {
|
||||
pub writes: RefCell<Vec<KeyValue>>,
|
||||
pub removes: RefCell<Vec<Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl DBTransaction {
|
||||
pub fn new() -> DBTransaction {
|
||||
DBTransaction {
|
||||
writes: RefCell::new(Vec::new()),
|
||||
removes: RefCell::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn put(&self, key: &[u8], value: &[u8]) {
|
||||
let mut brw = self.writes.borrow_mut();
|
||||
brw.push(KeyValue { key: key.to_vec(), value: value.to_vec() });
|
||||
}
|
||||
|
||||
pub fn delete(&self, key: &[u8]) {
|
||||
let mut brw = self.removes.borrow_mut();
|
||||
brw.push(key.to_vec());
|
||||
}
|
||||
}
|
||||
@@ -3,5 +3,14 @@ description = "Ethcore development/test/build tools"
|
||||
homepage = "http://parity.io"
|
||||
license = "GPL-3.0"
|
||||
name = "ethcore-devtools"
|
||||
version = "1.11.0"
|
||||
version = "1.6.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
rand = "0.3"
|
||||
|
||||
[features]
|
||||
|
||||
[lib]
|
||||
path = "src/lib.rs"
|
||||
test = true
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::io::{Read, Write};
|
||||
use std::str::{self, Lines};
|
||||
use std::net::{TcpStream, SocketAddr};
|
||||
|
||||
@@ -83,18 +83,9 @@ pub fn request(address: &SocketAddr, request: &str) -> Response {
|
||||
req.set_read_timeout(Some(Duration::from_secs(2))).unwrap();
|
||||
req.write_all(request.as_bytes()).unwrap();
|
||||
|
||||
let mut response = Vec::new();
|
||||
loop {
|
||||
let mut chunk = [0; 32 *1024];
|
||||
match req.read(&mut chunk) {
|
||||
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => break,
|
||||
Err(err) => panic!("Unable to read response: {:?}", err),
|
||||
Ok(0) => break,
|
||||
Ok(read) => response.extend_from_slice(&chunk[..read]),
|
||||
}
|
||||
}
|
||||
let mut response = String::new();
|
||||
let _ = req.read_to_string(&mut response);
|
||||
|
||||
let response = String::from_utf8_lossy(&response).into_owned();
|
||||
let mut lines = response.lines();
|
||||
let status = lines.next().expect("Expected a response").to_owned();
|
||||
let headers_raw = read_block(&mut lines, false);
|
||||
@@ -111,7 +102,12 @@ pub fn request(address: &SocketAddr, request: &str) -> Response {
|
||||
|
||||
/// Check if all required security headers are present
|
||||
pub fn assert_security_headers_present(headers: &[String], port: Option<u16>) {
|
||||
if let None = port {
|
||||
if let Some(port) = port {
|
||||
assert!(
|
||||
headers.iter().find(|header| header.as_str() == &format!("X-Frame-Options: ALLOW-FROM http://127.0.0.1:{}", port)).is_some(),
|
||||
"X-Frame-Options: ALLOW-FROM missing: {:?}", headers
|
||||
);
|
||||
} else {
|
||||
assert!(
|
||||
headers.iter().find(|header| header.as_str() == "X-Frame-Options: SAMEORIGIN").is_some(),
|
||||
"X-Frame-Options: SAMEORIGIN missing: {:?}", headers
|
||||
@@ -125,8 +121,4 @@ pub fn assert_security_headers_present(headers: &[String], port: Option<u16>) {
|
||||
headers.iter().find(|header| header.as_str() == "X-Content-Type-Options: nosniff").is_some(),
|
||||
"X-Content-Type-Options missing: {:?}", headers
|
||||
);
|
||||
assert!(
|
||||
headers.iter().find(|header| header.starts_with("Content-Security-Policy: ")).is_some(),
|
||||
"Content-Security-Policy missing: {:?}", headers
|
||||
)
|
||||
}
|
||||
|
||||
@@ -16,4 +16,14 @@
|
||||
|
||||
//! dev-tools
|
||||
|
||||
|
||||
extern crate rand;
|
||||
|
||||
mod random_path;
|
||||
mod test_socket;
|
||||
mod stop_guard;
|
||||
pub mod http_client;
|
||||
|
||||
pub use random_path::*;
|
||||
pub use test_socket::*;
|
||||
pub use stop_guard::*;
|
||||
|
||||
148
devtools/src/random_path.rs
Normal file
148
devtools/src/random_path.rs
Normal file
@@ -0,0 +1,148 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Random path
|
||||
|
||||
use std::path::*;
|
||||
use std::fs;
|
||||
use std::env;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use rand::random;
|
||||
|
||||
pub struct RandomTempPath {
|
||||
path: PathBuf,
|
||||
pub panic_on_drop_failure: bool,
|
||||
}
|
||||
|
||||
pub fn random_filename() -> String {
|
||||
random_str(8)
|
||||
}
|
||||
|
||||
pub fn random_str(len: usize) -> String {
|
||||
(0..len).map(|_| ((random::<f32>() * 26.0) as u8 + 97) as char).collect()
|
||||
}
|
||||
|
||||
impl RandomTempPath {
|
||||
pub fn new() -> RandomTempPath {
|
||||
let mut dir = env::temp_dir();
|
||||
dir.push(random_filename());
|
||||
RandomTempPath {
|
||||
path: dir.clone(),
|
||||
panic_on_drop_failure: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_dir() -> RandomTempPath {
|
||||
let mut dir = env::temp_dir();
|
||||
dir.push(random_filename());
|
||||
fs::create_dir_all(dir.as_path()).unwrap();
|
||||
RandomTempPath {
|
||||
path: dir.clone(),
|
||||
panic_on_drop_failure: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_path(&self) -> &PathBuf {
|
||||
&self.path
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.path.to_str().unwrap()
|
||||
}
|
||||
|
||||
pub fn new_in(&self, name: &str) -> String {
|
||||
let mut path = self.path.clone();
|
||||
path.push(name);
|
||||
path.to_str().unwrap().to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Path> for RandomTempPath {
|
||||
fn as_ref(&self) -> &Path {
|
||||
self.as_path()
|
||||
}
|
||||
}
|
||||
impl Deref for RandomTempPath {
|
||||
type Target = Path;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.as_path()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for RandomTempPath {
|
||||
fn drop(&mut self) {
|
||||
if let Err(_) = fs::remove_dir_all(&self) {
|
||||
if let Err(e) = fs::remove_file(&self) {
|
||||
if self.panic_on_drop_failure {
|
||||
panic!("Failed to remove temp directory. Here's what prevented this from happening: ({})", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GuardedTempResult<T> {
|
||||
pub result: Option<T>,
|
||||
pub _temp: RandomTempPath
|
||||
}
|
||||
|
||||
impl<T> GuardedTempResult<T> {
|
||||
pub fn reference(&self) -> &T {
|
||||
self.result.as_ref().unwrap()
|
||||
}
|
||||
|
||||
pub fn reference_mut(&mut self) -> &mut T {
|
||||
self.result.as_mut().unwrap()
|
||||
}
|
||||
|
||||
pub fn take(&mut self) -> T {
|
||||
self.result.take().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for GuardedTempResult<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T { self.result.as_ref().unwrap() }
|
||||
}
|
||||
|
||||
impl<T> DerefMut for GuardedTempResult<T> {
|
||||
fn deref_mut(&mut self) -> &mut T { self.result.as_mut().unwrap() }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn creates_dir() {
|
||||
let temp = RandomTempPath::create_dir();
|
||||
assert!(fs::metadata(temp.as_path()).unwrap().is_dir());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn destroys_dir() {
|
||||
let path_buf = {
|
||||
let temp = RandomTempPath::create_dir();
|
||||
assert!(fs::metadata(temp.as_path()).unwrap().is_dir());
|
||||
let path_buf = temp.as_path().to_path_buf();
|
||||
path_buf
|
||||
};
|
||||
|
||||
assert!(fs::metadata(&path_buf).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn provides_random() {
|
||||
let temp = RandomTempPath::create_dir();
|
||||
assert!(temp.as_path().to_str().is_some());
|
||||
}
|
||||
95
devtools/src/test_socket.rs
Normal file
95
devtools/src/test_socket.rs
Normal file
@@ -0,0 +1,95 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::io::*;
|
||||
use std::cmp;
|
||||
|
||||
pub struct TestSocket {
|
||||
pub read_buffer: Vec<u8>,
|
||||
pub write_buffer: Vec<u8>,
|
||||
pub cursor: usize,
|
||||
pub buf_size: usize,
|
||||
}
|
||||
|
||||
impl Default for TestSocket {
|
||||
fn default() -> Self {
|
||||
TestSocket::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl TestSocket {
|
||||
pub fn new() -> Self {
|
||||
TestSocket {
|
||||
read_buffer: vec![],
|
||||
write_buffer: vec![],
|
||||
cursor: 0,
|
||||
buf_size: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_buf(buf_size: usize) -> TestSocket {
|
||||
TestSocket {
|
||||
read_buffer: vec![],
|
||||
write_buffer: vec![],
|
||||
cursor: 0,
|
||||
buf_size: buf_size,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_ready(data: Vec<u8>) -> TestSocket {
|
||||
TestSocket {
|
||||
read_buffer: data,
|
||||
write_buffer: vec![],
|
||||
cursor: 0,
|
||||
buf_size: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for TestSocket {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||
let end_position = cmp::min(self.read_buffer.len(), self.cursor+buf.len());
|
||||
if self.cursor > end_position { return Ok(0) }
|
||||
let len = cmp::max(end_position - self.cursor, 0);
|
||||
match len {
|
||||
0 => Ok(0),
|
||||
_ => {
|
||||
for i in self.cursor..end_position {
|
||||
buf[i-self.cursor] = self.read_buffer[i];
|
||||
}
|
||||
self.cursor = end_position;
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for TestSocket {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
||||
if self.buf_size == 0 || buf.len() < self.buf_size {
|
||||
self.write_buffer.extend(buf.iter().cloned());
|
||||
Ok(buf.len())
|
||||
}
|
||||
else {
|
||||
self.write_buffer.extend(buf.iter().take(self.buf_size).cloned());
|
||||
Ok(self.buf_size)
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<()> {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
Usage
|
||||
|
||||
```docker build -f docker/ubuntu/Dockerfile --tag ethcore/parity:branch_or_tag_name .```
|
||||
@@ -1,77 +0,0 @@
|
||||
FROM ubuntu:xenial
|
||||
LABEL maintainer="Parity Technologies <devops@parity.io>"
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -yq sudo curl file build-essential wget git g++ cmake pkg-config bison flex \
|
||||
unzip lib32stdc++6 lib32z1 python autotools-dev automake autoconf libtool \
|
||||
gperf xsltproc docbook-xsl
|
||||
|
||||
# Rust & Cargo
|
||||
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
|
||||
ENV PATH /root/.cargo/bin:$PATH
|
||||
RUN rustup toolchain install stable
|
||||
RUN rustup target add --toolchain stable arm-linux-androideabi
|
||||
RUN rustup target add --toolchain stable armv7-linux-androideabi
|
||||
|
||||
# Android NDK and toolchain
|
||||
RUN cd /usr/local && \
|
||||
wget -q https://dl.google.com/android/repository/android-ndk-r16b-linux-x86_64.zip && \
|
||||
unzip -q android-ndk-r16b-linux-x86_64.zip && \
|
||||
rm android-ndk-r16b-linux-x86_64.zip
|
||||
ENV NDK_HOME /usr/local/android-ndk-r16b
|
||||
RUN /usr/local/android-ndk-r16b/build/tools/make-standalone-toolchain.sh \
|
||||
--arch=arm --install-dir=/opt/ndk-standalone --stl=libc++ --platform=android-26
|
||||
ENV PATH $PATH:/opt/ndk-standalone/bin
|
||||
|
||||
# Compiling OpenSSL for Android
|
||||
RUN cd /root && \
|
||||
git clone git://git.openssl.org/openssl.git && \
|
||||
cd openssl && \
|
||||
git checkout OpenSSL_1_1_0-stable
|
||||
ENV CROSS_SYSROOT /opt/ndk-standalone/sysroot
|
||||
RUN cd /root/openssl && \
|
||||
./Configure android-armeabi --cross-compile-prefix=arm-linux-androideabi- \
|
||||
-static no-stdio no-ui \
|
||||
-I/usr/local/android-ndk-r16b/sysroot/usr/include \
|
||||
-I/usr/local/android-ndk-r16b/sysroot/usr/include/arm-linux-androideabi \
|
||||
-L/usr/local/android-ndk-r16b/sysroot/usr/lib \
|
||||
--prefix=/opt/ndk-standalone/sysroot/usr
|
||||
RUN cd /root/openssl && \
|
||||
make build_libs && \
|
||||
make install_dev
|
||||
RUN rm -rf /root/openssl
|
||||
|
||||
# Compiling libudev for Android
|
||||
# This is the most hacky part of the process, as we need to apply a patch and pass specific
|
||||
# options that the compiler environment doesn't define.
|
||||
RUN cd /root && \
|
||||
git clone https://github.com/gentoo/eudev.git
|
||||
ADD libudev.patch /root
|
||||
RUN cd /root/eudev && \
|
||||
git checkout 83d918449f22720d84a341a05e24b6d109e6d3ae && \
|
||||
./autogen.sh && \
|
||||
./configure --disable-introspection --disable-programs --disable-hwdb \
|
||||
--host=arm-linux-androideabi --prefix=/opt/ndk-standalone/sysroot/usr/ \
|
||||
--enable-shared=false CC=arm-linux-androideabi-clang \
|
||||
CFLAGS="-D LINE_MAX=2048 -D RLIMIT_NLIMITS=15 -D IPTOS_LOWCOST=2 -std=gnu99" \
|
||||
CXX=arm-linux-androideabi-clang++ && \
|
||||
git apply - < /root/libudev.patch && \
|
||||
make && \
|
||||
make install
|
||||
RUN rm -rf /root/eudev
|
||||
RUN rm /root/libudev.patch
|
||||
|
||||
# Rust-related configuration
|
||||
ADD cargo-config.toml /root/.cargo/config
|
||||
ENV ARM_LINUX_ANDROIDEABI_OPENSSL_DIR /opt/ndk-standalone/sysroot/usr
|
||||
ENV ARMV7_LINUX_ANDROIDEABI_OPENSSL_DIR /opt/ndk-standalone/sysroot/usr
|
||||
ENV CC_arm_linux_androideabi arm-linux-androideabi-clang
|
||||
ENV CC_armv7_linux_androideabi arm-linux-androideabi-clang
|
||||
ENV CXX_arm_linux_androideabi arm-linux-androideabi-clang++
|
||||
ENV CXX_armv7_linux_androideabi arm-linux-androideabi-clang++
|
||||
ENV AR_arm_linux_androideabi arm-linux-androideabi-ar
|
||||
ENV AR_armv7_linux_androideabi arm-linux-androideabi-ar
|
||||
ENV CFLAGS_arm_linux_androideabi -std=gnu11 -fPIC -D OS_ANDROID
|
||||
ENV CFLAGS_armv7_linux_androideabi -std=gnu11 -fPIC -D OS_ANDROID
|
||||
ENV CXXFLAGS_arm_linux_androideabi -std=gnu++11 -fPIC -fexceptions -frtti -static-libstdc++ -D OS_ANDROID
|
||||
ENV CXXFLAGS_armv7_linux_androideabi -std=gnu++11 -fPIC -fexceptions -frtti -static-libstdc++ -D OS_ANDROID
|
||||
@@ -1,9 +0,0 @@
|
||||
[target.armv7-linux-androideabi]
|
||||
linker = "arm-linux-androideabi-clang"
|
||||
ar = "arm-linux-androideabi-ar"
|
||||
rustflags = ["-C", "link-arg=-lc++_static", "-C", "link-arg=-lc++abi", "-C", "link-arg=-landroid_support"]
|
||||
|
||||
[target.arm-linux-androideabi]
|
||||
linker = "arm-linux-androideabi-clang"
|
||||
ar = "arm-linux-androideabi-ar"
|
||||
rustflags = ["-C", "link-arg=-lc++_static", "-C", "link-arg=-lc++abi", "-C", "link-arg=-landroid_support"]
|
||||
@@ -1,216 +0,0 @@
|
||||
diff --git a/src/collect/collect.c b/src/collect/collect.c
|
||||
index 2cf1f00..b24f26b 100644
|
||||
--- a/src/collect/collect.c
|
||||
+++ b/src/collect/collect.c
|
||||
@@ -84,7 +84,7 @@ static void usage(void)
|
||||
" invoked for each ID in <idlist>) collect returns 0, the\n"
|
||||
" number of missing IDs otherwise.\n"
|
||||
" On error a negative number is returned.\n\n"
|
||||
- , program_invocation_short_name);
|
||||
+ , "parity");
|
||||
}
|
||||
|
||||
/*
|
||||
diff --git a/src/scsi_id/scsi_id.c b/src/scsi_id/scsi_id.c
|
||||
index 8b76d87..7bf3948 100644
|
||||
--- a/src/scsi_id/scsi_id.c
|
||||
+++ b/src/scsi_id/scsi_id.c
|
||||
@@ -321,7 +321,7 @@ static void help(void) {
|
||||
" -u --replace-whitespace Replace all whitespace by underscores\n"
|
||||
" -v --verbose Verbose logging\n"
|
||||
" -x --export Print values as environment keys\n"
|
||||
- , program_invocation_short_name);
|
||||
+ , "parity");
|
||||
|
||||
}
|
||||
|
||||
diff --git a/src/shared/hashmap.h b/src/shared/hashmap.h
|
||||
index a03ee58..a7c2005 100644
|
||||
--- a/src/shared/hashmap.h
|
||||
+++ b/src/shared/hashmap.h
|
||||
@@ -98,10 +98,7 @@ extern const struct hash_ops uint64_hash_ops;
|
||||
#if SIZEOF_DEV_T != 8
|
||||
unsigned long devt_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) _pure_;
|
||||
int devt_compare_func(const void *a, const void *b) _pure_;
|
||||
-extern const struct hash_ops devt_hash_ops = {
|
||||
- .hash = devt_hash_func,
|
||||
- .compare = devt_compare_func
|
||||
-};
|
||||
+extern const struct hash_ops devt_hash_ops;
|
||||
#else
|
||||
#define devt_hash_func uint64_hash_func
|
||||
#define devt_compare_func uint64_compare_func
|
||||
diff --git a/src/shared/log.c b/src/shared/log.c
|
||||
index 4a40996..1496984 100644
|
||||
--- a/src/shared/log.c
|
||||
+++ b/src/shared/log.c
|
||||
@@ -335,7 +335,7 @@ static int write_to_syslog(
|
||||
|
||||
IOVEC_SET_STRING(iovec[0], header_priority);
|
||||
IOVEC_SET_STRING(iovec[1], header_time);
|
||||
- IOVEC_SET_STRING(iovec[2], program_invocation_short_name);
|
||||
+ IOVEC_SET_STRING(iovec[2], "parity");
|
||||
IOVEC_SET_STRING(iovec[3], header_pid);
|
||||
IOVEC_SET_STRING(iovec[4], buffer);
|
||||
|
||||
@@ -383,7 +383,7 @@ static int write_to_kmsg(
|
||||
char_array_0(header_pid);
|
||||
|
||||
IOVEC_SET_STRING(iovec[0], header_priority);
|
||||
- IOVEC_SET_STRING(iovec[1], program_invocation_short_name);
|
||||
+ IOVEC_SET_STRING(iovec[1], "parity");
|
||||
IOVEC_SET_STRING(iovec[2], header_pid);
|
||||
IOVEC_SET_STRING(iovec[3], buffer);
|
||||
IOVEC_SET_STRING(iovec[4], "\n");
|
||||
diff --git a/src/udev/udevadm-control.c b/src/udev/udevadm-control.c
|
||||
index 6af7163..3271e56 100644
|
||||
--- a/src/udev/udevadm-control.c
|
||||
+++ b/src/udev/udevadm-control.c
|
||||
@@ -41,7 +41,7 @@ static void print_help(void) {
|
||||
" -p --property=KEY=VALUE Set a global property for all events\n"
|
||||
" -m --children-max=N Maximum number of children\n"
|
||||
" --timeout=SECONDS Maximum time to block for a reply\n"
|
||||
- , program_invocation_short_name);
|
||||
+ , "parity");
|
||||
}
|
||||
|
||||
static int adm_control(struct udev *udev, int argc, char *argv[]) {
|
||||
diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c
|
||||
index 0aec976..a31ac02 100644
|
||||
--- a/src/udev/udevadm-info.c
|
||||
+++ b/src/udev/udevadm-info.c
|
||||
@@ -279,7 +279,7 @@ static void help(void) {
|
||||
" -P --export-prefix Export the key name with a prefix\n"
|
||||
" -e --export-db Export the content of the udev database\n"
|
||||
" -c --cleanup-db Clean up the udev database\n"
|
||||
- , program_invocation_short_name);
|
||||
+ , "parity");
|
||||
}
|
||||
|
||||
static int uinfo(struct udev *udev, int argc, char *argv[]) {
|
||||
diff --git a/src/udev/udevadm-monitor.c b/src/udev/udevadm-monitor.c
|
||||
index 15ded09..b58dd08 100644
|
||||
--- a/src/udev/udevadm-monitor.c
|
||||
+++ b/src/udev/udevadm-monitor.c
|
||||
@@ -73,7 +73,7 @@ static void help(void) {
|
||||
" -u --udev Print udev events\n"
|
||||
" -s --subsystem-match=SUBSYSTEM[/DEVTYPE] Filter events by subsystem\n"
|
||||
" -t --tag-match=TAG Filter events by tag\n"
|
||||
- , program_invocation_short_name);
|
||||
+ , "parity");
|
||||
}
|
||||
|
||||
static int adm_monitor(struct udev *udev, int argc, char *argv[]) {
|
||||
diff --git a/src/udev/udevadm-settle.c b/src/udev/udevadm-settle.c
|
||||
index 33597bc..b36a504 100644
|
||||
--- a/src/udev/udevadm-settle.c
|
||||
+++ b/src/udev/udevadm-settle.c
|
||||
@@ -43,7 +43,7 @@ static void help(void) {
|
||||
" --version Show package version\n"
|
||||
" -t --timeout=SECONDS Maximum time to wait for events\n"
|
||||
" -E --exit-if-exists=FILE Stop waiting if file exists\n"
|
||||
- , program_invocation_short_name);
|
||||
+ , "parity");
|
||||
}
|
||||
|
||||
static int adm_settle(struct udev *udev, int argc, char *argv[]) {
|
||||
diff --git a/src/udev/udevadm-test-builtin.c b/src/udev/udevadm-test-builtin.c
|
||||
index baaeca9..50ed812 100644
|
||||
--- a/src/udev/udevadm-test-builtin.c
|
||||
+++ b/src/udev/udevadm-test-builtin.c
|
||||
@@ -39,7 +39,7 @@ static void help(struct udev *udev) {
|
||||
" -h --help Print this message\n"
|
||||
" --version Print version of the program\n\n"
|
||||
"Commands:\n"
|
||||
- , program_invocation_short_name);
|
||||
+ , "parity");
|
||||
|
||||
udev_builtin_list(udev);
|
||||
}
|
||||
diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c
|
||||
index 47fd924..a855412 100644
|
||||
--- a/src/udev/udevadm-test.c
|
||||
+++ b/src/udev/udevadm-test.c
|
||||
@@ -39,7 +39,7 @@ static void help(void) {
|
||||
" --version Show package version\n"
|
||||
" -a --action=ACTION Set action string\n"
|
||||
" -N --resolve-names=early|late|never When to resolve names\n"
|
||||
- , program_invocation_short_name);
|
||||
+ , "parity");
|
||||
}
|
||||
|
||||
static int adm_test(struct udev *udev, int argc, char *argv[]) {
|
||||
diff --git a/src/udev/udevadm-trigger.c b/src/udev/udevadm-trigger.c
|
||||
index 4dc756a..67787d3 100644
|
||||
--- a/src/udev/udevadm-trigger.c
|
||||
+++ b/src/udev/udevadm-trigger.c
|
||||
@@ -92,7 +92,7 @@ static void help(void) {
|
||||
" -y --sysname-match=NAME Trigger devices with this /sys path\n"
|
||||
" --name-match=NAME Trigger devices with this /dev name\n"
|
||||
" -b --parent-match=NAME Trigger devices with that parent device\n"
|
||||
- , program_invocation_short_name);
|
||||
+ , "parity");
|
||||
}
|
||||
|
||||
static int adm_trigger(struct udev *udev, int argc, char *argv[]) {
|
||||
diff --git a/src/udev/udevadm.c b/src/udev/udevadm.c
|
||||
index 3e57cf6..b03dfaa 100644
|
||||
--- a/src/udev/udevadm.c
|
||||
+++ b/src/udev/udevadm.c
|
||||
@@ -62,7 +62,7 @@ static int adm_help(struct udev *udev, int argc, char *argv[]) {
|
||||
printf("%s [--help] [--version] [--debug] COMMAND [COMMAND OPTIONS]\n\n"
|
||||
"Send control commands or test the device manager.\n\n"
|
||||
"Commands:\n"
|
||||
- , program_invocation_short_name);
|
||||
+ , "parity");
|
||||
|
||||
for (i = 0; i < ELEMENTSOF(udevadm_cmds); i++)
|
||||
if (udevadm_cmds[i]->help != NULL)
|
||||
@@ -128,7 +128,7 @@ int main(int argc, char *argv[]) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
- fprintf(stderr, "%s: missing or unknown command\n", program_invocation_short_name);
|
||||
+ fprintf(stderr, "%s: missing or unknown command\n", "parity");
|
||||
rc = 2;
|
||||
out:
|
||||
mac_selinux_finish();
|
||||
diff --git a/src/udev/udevd.c b/src/udev/udevd.c
|
||||
index cf826c6..4eec0af 100644
|
||||
--- a/src/udev/udevd.c
|
||||
+++ b/src/udev/udevd.c
|
||||
@@ -1041,7 +1041,7 @@ static void help(void) {
|
||||
" -t --event-timeout=SECONDS Seconds to wait before terminating an event\n"
|
||||
" -N --resolve-names=early|late|never\n"
|
||||
" When to resolve users and groups\n"
|
||||
- , program_invocation_short_name);
|
||||
+ , "parity");
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
diff --git a/src/v4l_id/v4l_id.c b/src/v4l_id/v4l_id.c
|
||||
index 1dce0d5..f65badf 100644
|
||||
--- a/src/v4l_id/v4l_id.c
|
||||
+++ b/src/v4l_id/v4l_id.c
|
||||
@@ -49,7 +49,7 @@ int main(int argc, char *argv[]) {
|
||||
printf("%s [-h,--help] <device file>\n\n"
|
||||
"Video4Linux device identification.\n\n"
|
||||
" -h Print this message\n"
|
||||
- , program_invocation_short_name);
|
||||
+ , "parity");
|
||||
return 0;
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
diff --git a/src/shared/path-util.c b/src/shared/path-util.c
|
||||
index 0744563..7151356 100644
|
||||
--- a/src/shared/path-util.c
|
||||
+++ b/src/shared/path-util.c
|
||||
@@ -109,7 +109,7 @@ char *path_make_absolute_cwd(const char *p) {
|
||||
if (path_is_absolute(p))
|
||||
return strdup(p);
|
||||
|
||||
- cwd = get_current_dir_name();
|
||||
+ cwd = getcwd(malloc(128), 128);
|
||||
if (!cwd)
|
||||
return NULL;
|
||||
|
||||
@@ -1,32 +1,29 @@
|
||||
FROM centos:latest
|
||||
WORKDIR /build
|
||||
|
||||
# install tools and dependencies
|
||||
RUN yum -y update&& \
|
||||
yum install -y git make gcc-c++ gcc file binutils
|
||||
|
||||
# install rustup
|
||||
RUN curl -sSf https://static.rust-lang.org/rustup.sh -o rustup.sh &&\
|
||||
ls&&\
|
||||
sh rustup.sh --disable-sudo
|
||||
|
||||
sh rustup.sh -s -- --disable-sudo
|
||||
# show backtraces
|
||||
ENV RUST_BACKTRACE 1
|
||||
|
||||
# set compiler
|
||||
ENV CXX g++
|
||||
ENV CC gcc
|
||||
|
||||
# show tools
|
||||
RUN rustc -vV && \
|
||||
cargo -V && \
|
||||
gcc -v &&\
|
||||
g++ -v
|
||||
|
||||
# build parity
|
||||
ADD . /build/parity
|
||||
RUN cd parity&&\
|
||||
cargo build --release --verbose && \
|
||||
RUN git clone https://github.com/ethcore/parity && \
|
||||
cd parity&&\
|
||||
git checkout beta && \
|
||||
git pull && \
|
||||
ls -a&&\
|
||||
cargo build --release --verbose && \
|
||||
ls /build/parity/target/release/parity && \
|
||||
strip /build/parity/target/release/parity
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ WORKDIR /build
|
||||
#ENV for build TAG
|
||||
ARG BUILD_TAG
|
||||
ENV BUILD_TAG ${BUILD_TAG:-master}
|
||||
RUN echo "Build tag:" $BUILD_TAG
|
||||
RUN echo $BUILD_TAG
|
||||
# install tools and dependencies
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --force-yes --no-install-recommends \
|
||||
@@ -27,7 +27,20 @@ RUN apt-get update && \
|
||||
libudev-dev \
|
||||
pkg-config \
|
||||
dpkg-dev \
|
||||
libudev-dev &&\
|
||||
# evmjit dependencies
|
||||
zlib1g-dev \
|
||||
libedit-dev \
|
||||
libudev-dev &&\
|
||||
# cmake and llvm ppa's. then update ppa's
|
||||
add-apt-repository -y "ppa:george-edison55/cmake-3.x" && \
|
||||
add-apt-repository "deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.7 main" && \
|
||||
apt-get update && \
|
||||
apt-get install -y --force-yes cmake llvm-3.7-dev && \
|
||||
# install evmjit
|
||||
git clone https://github.com/debris/evmjit && \
|
||||
cd evmjit && \
|
||||
mkdir build && cd build && \
|
||||
cmake .. && make && make install && cd && \
|
||||
# install rustup
|
||||
curl https://sh.rustup.rs -sSf | sh -s -- -y && \
|
||||
# rustup directory
|
||||
@@ -35,7 +48,7 @@ RUN apt-get update && \
|
||||
# show backtraces
|
||||
RUST_BACKTRACE=1 && \
|
||||
# build parity
|
||||
cd /build&&git clone https://github.com/paritytech/parity && \
|
||||
cd /build&&git clone https://github.com/ethcore/parity && \
|
||||
cd parity && \
|
||||
git pull&& \
|
||||
git checkout $BUILD_TAG && \
|
||||
@@ -59,7 +72,11 @@ cd /build&&git clone https://github.com/paritytech/parity && \
|
||||
binutils \
|
||||
file \
|
||||
pkg-config \
|
||||
dpkg-dev &&\
|
||||
dpkg-dev \
|
||||
# evmjit dependencies
|
||||
zlib1g-dev \
|
||||
libedit-dev \
|
||||
cmake llvm-3.7-dev&&\
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
# setup ENTRYPOINT
|
||||
EXPOSE 8080 8545 8180
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
FROM ubuntu:14.04
|
||||
WORKDIR /build
|
||||
|
||||
# install tools and dependencies
|
||||
RUN apt-get -y update && \
|
||||
apt-get install -y --force-yes --no-install-recommends \
|
||||
@@ -25,11 +24,14 @@ RUN rustup target add aarch64-unknown-linux-gnu
|
||||
ENV RUST_BACKTRACE 1
|
||||
|
||||
# show tools
|
||||
RUN rustc -vV && cargo -V
|
||||
RUN rustc -vV && \
|
||||
cargo -V
|
||||
|
||||
# build parity
|
||||
ADD . /build/parity
|
||||
RUN cd parity && \
|
||||
RUN git clone https://github.com/ethcore/parity && \
|
||||
cd parity && \
|
||||
git checkout beta && \
|
||||
git pull && \
|
||||
mkdir -p .cargo && \
|
||||
echo '[target.aarch64-unknown-linux-gnu]\n\
|
||||
linker = "aarch64-linux-gnu-gcc"\n'\
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
FROM ubuntu:14.04
|
||||
WORKDIR /build
|
||||
|
||||
# install tools and dependencies
|
||||
RUN apt-get -y update && \
|
||||
apt-get install -y --force-yes --no-install-recommends \
|
||||
@@ -24,12 +23,16 @@ RUN rustup target add armv7-unknown-linux-gnueabihf
|
||||
# show backtraces
|
||||
ENV RUST_BACKTRACE 1
|
||||
|
||||
|
||||
# show tools
|
||||
RUN rustc -vV && cargo -V
|
||||
RUN rustc -vV && \
|
||||
cargo -V
|
||||
|
||||
# build parity
|
||||
ADD . /build/parity
|
||||
RUN cd parity && \
|
||||
RUN git clone https://github.com/ethcore/parity && \
|
||||
cd parity && \
|
||||
git checkout beta && \
|
||||
git pull && \
|
||||
mkdir -p .cargo && \
|
||||
echo '[target.armv7-unknown-linux-gnueabihf]\n\
|
||||
linker = "arm-linux-gnueabihf-gcc"\n'\
|
||||
|
||||
37
docker/ubuntu-dev/Dockerfile
Normal file
37
docker/ubuntu-dev/Dockerfile
Normal file
@@ -0,0 +1,37 @@
|
||||
FROM ubuntu:14.04
|
||||
|
||||
# install tools and dependencies
|
||||
RUN apt-get update && \
|
||||
apt-get install -y \
|
||||
# make
|
||||
build-essential \
|
||||
# add-apt-repository
|
||||
software-properties-common \
|
||||
curl \
|
||||
g++ \
|
||||
wget \
|
||||
git \
|
||||
# evmjit dependencies
|
||||
zlib1g-dev \
|
||||
libedit-dev
|
||||
|
||||
# cmake, llvm and rocksdb ppas. then update ppas
|
||||
RUN add-apt-repository -y "ppa:george-edison55/cmake-3.x" && \
|
||||
add-apt-repository "deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.7 main" && \
|
||||
apt-get update && \
|
||||
apt-get install -y --force-yes cmake llvm-3.7-dev
|
||||
|
||||
# install evmjit
|
||||
RUN git clone https://github.com/debris/evmjit && \
|
||||
cd evmjit && \
|
||||
mkdir build && cd build && \
|
||||
cmake .. && make && make install && cd
|
||||
|
||||
# install rustup
|
||||
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
|
||||
|
||||
# rustup directory
|
||||
ENV PATH /root/.cargo/bin:$PATH
|
||||
|
||||
# show backtraces
|
||||
ENV RUST_BACKTRACE 1
|
||||
59
docker/ubuntu-jit/Dockerfile
Normal file
59
docker/ubuntu-jit/Dockerfile
Normal file
@@ -0,0 +1,59 @@
|
||||
FROM ubuntu:14.04
|
||||
WORKDIR /build
|
||||
# install tools and dependencies
|
||||
RUN apt-get update && \
|
||||
apt-get install -y \
|
||||
# make
|
||||
build-essential \
|
||||
# add-apt-repository
|
||||
software-properties-common \
|
||||
curl \
|
||||
wget \
|
||||
git \
|
||||
g++ \
|
||||
binutils \
|
||||
file \
|
||||
# evmjit dependencies
|
||||
zlib1g-dev \
|
||||
libedit-dev
|
||||
|
||||
# cmake and llvm ppas. then update ppas
|
||||
RUN add-apt-repository -y "ppa:george-edison55/cmake-3.x" && \
|
||||
add-apt-repository "deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.7 main" && \
|
||||
apt-get update && \
|
||||
apt-get install -y --force-yes cmake llvm-3.7-dev
|
||||
|
||||
# install evmjit
|
||||
RUN git clone https://github.com/debris/evmjit && \
|
||||
cd evmjit && \
|
||||
mkdir build && cd build && \
|
||||
cmake .. && make && make install && cd
|
||||
|
||||
# install rustup
|
||||
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
|
||||
|
||||
# rustup directory
|
||||
ENV PATH /root/.cargo/bin:$PATH
|
||||
|
||||
# show backtraces
|
||||
ENV RUST_BACKTRACE 1
|
||||
|
||||
# show tools
|
||||
RUN rustc -vV && \
|
||||
cargo -V && \
|
||||
gcc -v &&\
|
||||
g++ -v
|
||||
|
||||
# build parity
|
||||
RUN git clone https://github.com/ethcore/parity && \
|
||||
cd parity && \
|
||||
git checkout beta && \
|
||||
git pull && \
|
||||
cargo build --release --features ethcore/jit --verbose && \
|
||||
ls /build/parity/target/release/parity && \
|
||||
strip /build/parity/target/release/parity
|
||||
|
||||
RUN file /build/parity/target/release/parity
|
||||
|
||||
EXPOSE 8080 8545 8180
|
||||
ENTRYPOINT ["/build/parity/target/release/parity"]
|
||||
40
docker/ubuntu-stable/Dockerfile
Normal file
40
docker/ubuntu-stable/Dockerfile
Normal file
@@ -0,0 +1,40 @@
|
||||
FROM ubuntu:14.04
|
||||
WORKDIR /build
|
||||
# install tools and dependencies
|
||||
RUN apt-get update && \
|
||||
apt-get install -y \
|
||||
build-essential \
|
||||
g++ \
|
||||
curl \
|
||||
git \
|
||||
file \
|
||||
binutils
|
||||
|
||||
# install rustup
|
||||
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
|
||||
|
||||
# rustup directory
|
||||
ENV PATH /root/.cargo/bin:$PATH
|
||||
|
||||
# show backtraces
|
||||
ENV RUST_BACKTRACE 1
|
||||
|
||||
# show tools
|
||||
RUN rustc -vV && \
|
||||
cargo -V && \
|
||||
gcc -v &&\
|
||||
g++ -v
|
||||
|
||||
# build parity
|
||||
RUN git clone https://github.com/ethcore/parity && \
|
||||
cd parity && \
|
||||
git checkout stable && \
|
||||
git pull && \
|
||||
cargo build --release --verbose && \
|
||||
ls /build/parity/target/release/parity && \
|
||||
strip /build/parity/target/release/parity
|
||||
|
||||
RUN file /build/parity/target/release/parity
|
||||
|
||||
EXPOSE 8080 8545 8180
|
||||
ENTRYPOINT ["/build/parity/target/release/parity"]
|
||||
@@ -1,6 +1,5 @@
|
||||
FROM ubuntu:14.04
|
||||
WORKDIR /build
|
||||
|
||||
# install tools and dependencies
|
||||
RUN apt-get update && \
|
||||
apt-get install -y \
|
||||
@@ -9,10 +8,7 @@ RUN apt-get update && \
|
||||
curl \
|
||||
git \
|
||||
file \
|
||||
binutils \
|
||||
libssl-dev \
|
||||
pkg-config \
|
||||
libudev-dev
|
||||
binutils
|
||||
|
||||
# install rustup
|
||||
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
|
||||
@@ -30,8 +26,10 @@ gcc -v &&\
|
||||
g++ -v
|
||||
|
||||
# build parity
|
||||
ADD . /build/parity
|
||||
RUN cd parity && \
|
||||
RUN git clone https://github.com/ethcore/parity && \
|
||||
cd parity && \
|
||||
git checkout beta && \
|
||||
git pull && \
|
||||
cargo build --release --verbose && \
|
||||
ls /build/parity/target/release/parity && \
|
||||
strip /build/parity/target/release/parity
|
||||
|
||||
@@ -1,228 +0,0 @@
|
||||
Note: Parity 0.9 reached End-of-Life on 2016-05-02 (EOL).
|
||||
|
||||
## Parity [beta-0.9.1](https://github.com/paritytech/parity/releases/tag/beta-0.9.1) (2016-02-16)
|
||||
|
||||
Homestead transition block changed to 1100000.
|
||||
|
||||
- Beta patch to 0.9.1 [#445](https://github.com/paritytech/parity/pull/445)
|
||||
- Delay homestead transition [#430](https://github.com/paritytech/parity/pull/430)
|
||||
- (BETA) https link in the installer (?) [#392](https://github.com/paritytech/parity/pull/392)
|
||||
- beta: Check for handshake expiration before attempting replace [#377](https://github.com/paritytech/parity/pull/377)
|
||||
|
||||
## Parity [beta-0.9](https://github.com/paritytech/parity/releases/tag/beta-0.9) (2016-02-08)
|
||||
|
||||
First Parity Beta 0.9 released.
|
||||
|
||||
- Panic on missing counters; Client cleanup [#368](https://github.com/paritytech/parity/pull/368)
|
||||
- Update README for new PPAs. [#369](https://github.com/paritytech/parity/pull/369)
|
||||
- block_queue::clear should be more thorough [#365](https://github.com/paritytech/parity/pull/365)
|
||||
- Fixed an issue with forked counters [#363](https://github.com/paritytech/parity/pull/363)
|
||||
- Install parity [#362](https://github.com/paritytech/parity/pull/362)
|
||||
- DB directory versioning [#358](https://github.com/paritytech/parity/pull/358)
|
||||
- Raise FD limit for MacOS [#357](https://github.com/paritytech/parity/pull/357)
|
||||
- Travis slack integration. [#356](https://github.com/paritytech/parity/pull/356)
|
||||
- SignedTransaction structure [#350](https://github.com/paritytech/parity/pull/350)
|
||||
- License [#354](https://github.com/paritytech/parity/pull/354)
|
||||
- Performance optimizations [#353](https://github.com/paritytech/parity/pull/353)
|
||||
- Gitter in README. [#355](https://github.com/paritytech/parity/pull/355)
|
||||
- test efforts, receipt requests [#352](https://github.com/paritytech/parity/pull/352)
|
||||
- sync tests setup & local module coverage [#348](https://github.com/paritytech/parity/pull/348)
|
||||
- install parity script [#347](https://github.com/paritytech/parity/pull/347)
|
||||
- evmjit homestead merge [#342](https://github.com/paritytech/parity/pull/342)
|
||||
- Fixed sync stalling on fork [#343](https://github.com/paritytech/parity/pull/343)
|
||||
- Remerge 264 [#334](https://github.com/paritytech/parity/pull/334)
|
||||
- Ethsync tests bfix [#339](https://github.com/paritytech/parity/pull/339)
|
||||
- Fix default options. [#335](https://github.com/paritytech/parity/pull/335)
|
||||
- sync queue limit hotfix [#338](https://github.com/paritytech/parity/pull/338)
|
||||
- Network tests, separate local coverage for utils [#333](https://github.com/paritytech/parity/pull/333)
|
||||
- fix parity version so netstats can parse it [#332](https://github.com/paritytech/parity/pull/332)
|
||||
- reveal surprise [#331](https://github.com/paritytech/parity/pull/331)
|
||||
- Revert removal of `new_code`. [#330](https://github.com/paritytech/parity/pull/330)
|
||||
- Network mod tests first part [#329](https://github.com/paritytech/parity/pull/329)
|
||||
- Look ma no `dead_code` [#323](https://github.com/paritytech/parity/pull/323)
|
||||
- Fixing JIT, Updating hook to run `ethcore` tests. [#326](https://github.com/paritytech/parity/pull/326)
|
||||
- Final docs [#327](https://github.com/paritytech/parity/pull/327)
|
||||
- update install-deps.sh [#316](https://github.com/paritytech/parity/pull/316)
|
||||
- Finish all my docs. Fix previous test compilation. [#320](https://github.com/paritytech/parity/pull/320)
|
||||
- Additional evm tests (extops, call, jumps) and some docs [#317](https://github.com/paritytech/parity/pull/317)
|
||||
- More documentation. [#318](https://github.com/paritytech/parity/pull/318)
|
||||
- Additional documentation. [#315](https://github.com/paritytech/parity/pull/315)
|
||||
- unused functions cleanup [#310](https://github.com/paritytech/parity/pull/310)
|
||||
- update ethcore.github.io documentation automatically [#311](https://github.com/paritytech/parity/pull/311)
|
||||
- Another try with travis ci credentials [#314](https://github.com/paritytech/parity/pull/314)
|
||||
- Document some stuff. [#309](https://github.com/paritytech/parity/pull/309)
|
||||
- Check block parent on import; Peer timeouts [#303](https://github.com/paritytech/parity/pull/303)
|
||||
- Increasing coverage for evm. [#306](https://github.com/paritytech/parity/pull/306)
|
||||
- ethcore docs [#301](https://github.com/paritytech/parity/pull/301)
|
||||
- Replacing secure token for deployment [#305](https://github.com/paritytech/parity/pull/305)
|
||||
- doc.sh [#299](https://github.com/paritytech/parity/pull/299)
|
||||
- Building beta-* and stable-* tags [#302](https://github.com/paritytech/parity/pull/302)
|
||||
- Deploying artifacts for tags (release/beta) [#300](https://github.com/paritytech/parity/pull/300)
|
||||
- cov.sh to show coverage locally [#298](https://github.com/paritytech/parity/pull/298)
|
||||
- benchmark fixes [#297](https://github.com/paritytech/parity/pull/297)
|
||||
- Include JSONRPC CLI options. [#296](https://github.com/paritytech/parity/pull/296)
|
||||
- travis.yml fixes [#293](https://github.com/paritytech/parity/pull/293)
|
||||
- Improve version string. [#295](https://github.com/paritytech/parity/pull/295)
|
||||
- Fixed block queue test [#294](https://github.com/paritytech/parity/pull/294)
|
||||
- Util docs [#292](https://github.com/paritytech/parity/pull/292)
|
||||
- fixed building docs [#289](https://github.com/paritytech/parity/pull/289)
|
||||
- update travis to build PRs only against master [#290](https://github.com/paritytech/parity/pull/290)
|
||||
- Coverage effort [#272](https://github.com/paritytech/parity/pull/272)
|
||||
- updated docker containers [#288](https://github.com/paritytech/parity/pull/288)
|
||||
- rpc module fixes [#287](https://github.com/paritytech/parity/pull/287)
|
||||
- Test for Receipt RLP. [#282](https://github.com/paritytech/parity/pull/282)
|
||||
- Building from source guide [#284](https://github.com/paritytech/parity/pull/284)
|
||||
- Fixed neted empty list RLP encoding [#283](https://github.com/paritytech/parity/pull/283)
|
||||
- Fix CALLDATACOPY (and bonus CODECOPY, too!). [#279](https://github.com/paritytech/parity/pull/279)
|
||||
- added travis && coveralls badge to README.md [#280](https://github.com/paritytech/parity/pull/280)
|
||||
- coveralls coverage [#277](https://github.com/paritytech/parity/pull/277)
|
||||
- Travis [in progress] [#257](https://github.com/paritytech/parity/pull/257)
|
||||
- Travis on reorganized repo [#276](https://github.com/paritytech/parity/pull/276)
|
||||
- umbrella project [#275](https://github.com/paritytech/parity/pull/275)
|
||||
- Ethash disk cache [#273](https://github.com/paritytech/parity/pull/273)
|
||||
- Parity executable name and version [#274](https://github.com/paritytech/parity/pull/274)
|
||||
- Dockerfile [#195](https://github.com/paritytech/parity/pull/195)
|
||||
- Garbage collection test fix [#267](https://github.com/paritytech/parity/pull/267)
|
||||
- Fix stCallCreateCallCodeTest, add more tests [#271](https://github.com/paritytech/parity/pull/271)
|
||||
- Moved sync out of ethcore crate; Added block validation [#265](https://github.com/paritytech/parity/pull/265)
|
||||
- RLP encoder refactoring [#252](https://github.com/paritytech/parity/pull/252)
|
||||
- Chain sync tests and minor refactoring [#264](https://github.com/paritytech/parity/pull/264)
|
||||
- Common log init function [#263](https://github.com/paritytech/parity/pull/263)
|
||||
- changed max vm depth from 128 to 64, change homestead block to 1_000_000 [#262](https://github.com/paritytech/parity/pull/262)
|
||||
- fixed blockchain tests crash on log init [#261](https://github.com/paritytech/parity/pull/261)
|
||||
- Blockchain tests and some helpers for guarding temp directory [#256](https://github.com/paritytech/parity/pull/256)
|
||||
- Fix logging and random tests. [#260](https://github.com/paritytech/parity/pull/260)
|
||||
- Fix difficulty calculation algo. [#259](https://github.com/paritytech/parity/pull/259)
|
||||
- fix submodule version [#258](https://github.com/paritytech/parity/pull/258)
|
||||
- temp dir spawn refactoring [#246](https://github.com/paritytech/parity/pull/246)
|
||||
- fixed tests submodule branch [#254](https://github.com/paritytech/parity/pull/254)
|
||||
- rpc net methods returns real peer count && protocol version [#253](https://github.com/paritytech/parity/pull/253)
|
||||
- Add homestead & random tests. [#245](https://github.com/paritytech/parity/pull/245)
|
||||
- Fixing suicide with self-refund to be consistent with CPP. [#247](https://github.com/paritytech/parity/pull/247)
|
||||
- stubs for rpc methods [#251](https://github.com/paritytech/parity/pull/251)
|
||||
- clippy, missing docs, renaming etc. [#244](https://github.com/paritytech/parity/pull/244)
|
||||
- impl missing methods in tests [#243](https://github.com/paritytech/parity/pull/243)
|
||||
- General tests and some helpers [#239](https://github.com/paritytech/parity/pull/239)
|
||||
- Note additional tests are fixed, fix doc test. [#242](https://github.com/paritytech/parity/pull/242)
|
||||
- jsonrpc http server [#193](https://github.com/paritytech/parity/pull/193)
|
||||
- Ethash nonce is H64 not a u64 [#240](https://github.com/paritytech/parity/pull/240)
|
||||
- Fix import for bcMultiChainTest [#236](https://github.com/paritytech/parity/pull/236)
|
||||
- Client basic tests [#232](https://github.com/paritytech/parity/pull/232)
|
||||
- Fix ensure_db_good() and flush_queue(), block refactoring, check block format, be strict. [#231](https://github.com/paritytech/parity/pull/231)
|
||||
- Rlp [#207](https://github.com/paritytech/parity/pull/207)
|
||||
- Schedule documentation [#219](https://github.com/paritytech/parity/pull/219)
|
||||
- U256<->H256 Conversion [#206](https://github.com/paritytech/parity/pull/206)
|
||||
- Spawning new thread when we are reaching stack limit [#217](https://github.com/paritytech/parity/pull/217)
|
||||
- Blockchain tests [#211](https://github.com/paritytech/parity/pull/211)
|
||||
- fixed failing sync test [#218](https://github.com/paritytech/parity/pull/218)
|
||||
- Removing println [#216](https://github.com/paritytech/parity/pull/216)
|
||||
- Cleaning readme [#212](https://github.com/paritytech/parity/pull/212)
|
||||
- Fixing delegatecall [#196](https://github.com/paritytech/parity/pull/196)
|
||||
- Autogenerate the Args from the docopt macro. [#205](https://github.com/paritytech/parity/pull/205)
|
||||
- Networking fixes [#202](https://github.com/paritytech/parity/pull/202)
|
||||
- Argument parsing from CLI [#204](https://github.com/paritytech/parity/pull/204)
|
||||
- Removed wildcard from clippy version [#203](https://github.com/paritytech/parity/pull/203)
|
||||
- Fixed tests and tweaked sync progress report [#201](https://github.com/paritytech/parity/pull/201)
|
||||
- Heavy tests [#199](https://github.com/paritytech/parity/pull/199)
|
||||
- Mutithreaded IO [#198](https://github.com/paritytech/parity/pull/198)
|
||||
- Populating last_hashes [#197](https://github.com/paritytech/parity/pull/197)
|
||||
- Fixing clippy stuff [#170](https://github.com/paritytech/parity/pull/170)
|
||||
- basic .travis.yml [#194](https://github.com/paritytech/parity/pull/194)
|
||||
- Generating coverage reports. [#190](https://github.com/paritytech/parity/pull/190)
|
||||
- Adding doc requests comments [#192](https://github.com/paritytech/parity/pull/192)
|
||||
- moved src/bin/client.rs -> src/bin/client/main.rs [#185](https://github.com/paritytech/parity/pull/185)
|
||||
- removed overflowing_shr [#188](https://github.com/paritytech/parity/pull/188)
|
||||
- fixed wrapping ops on latest nightly [#187](https://github.com/paritytech/parity/pull/187)
|
||||
- Pruned state DB [#176](https://github.com/paritytech/parity/pull/176)
|
||||
- Memory management for cache [#180](https://github.com/paritytech/parity/pull/180)
|
||||
- Implement signs having low-s. [#183](https://github.com/paritytech/parity/pull/183)
|
||||
- Introduce sha3 crate and use it in ethash [#178](https://github.com/paritytech/parity/pull/178)
|
||||
- Multithreaded block queue [#173](https://github.com/paritytech/parity/pull/173)
|
||||
- Iterator for NibbleSlice and TrieDB. [#171](https://github.com/paritytech/parity/pull/171)
|
||||
- Handling all possible overflows [#145](https://github.com/paritytech/parity/pull/145)
|
||||
- Global secp256k1 context [#164](https://github.com/paritytech/parity/pull/164)
|
||||
- Ethash [#152](https://github.com/paritytech/parity/pull/152)
|
||||
- Move util into here [#153](https://github.com/paritytech/parity/pull/153)
|
||||
- EVM Interpreter [#103](https://github.com/paritytech/parity/pull/103)
|
||||
- Homestead transition support, maybe. [#141](https://github.com/paritytech/parity/pull/141)
|
||||
- externalities refactor [#131](https://github.com/paritytech/parity/pull/131)
|
||||
- More open files. [#140](https://github.com/paritytech/parity/pull/140)
|
||||
- Single array for logs output. [#133](https://github.com/paritytech/parity/pull/133)
|
||||
- Client app event handler [#132](https://github.com/paritytech/parity/pull/132)
|
||||
- Various consensus fixes. [#130](https://github.com/paritytech/parity/pull/130)
|
||||
- callcode builtins tests pass [#127](https://github.com/paritytech/parity/pull/127)
|
||||
- Client state syncing [#119](https://github.com/paritytech/parity/pull/119)
|
||||
- Split externalities from executive. [#126](https://github.com/paritytech/parity/pull/126)
|
||||
- executive error on not enoguh base gas [#124](https://github.com/paritytech/parity/pull/124)
|
||||
- Gav [#125](https://github.com/paritytech/parity/pull/125)
|
||||
- builtin sets excepted to true [#123](https://github.com/paritytech/parity/pull/123)
|
||||
- More state tests. [#122](https://github.com/paritytech/parity/pull/122)
|
||||
- updated to rocksdb wrapper version 0.3 [#121](https://github.com/paritytech/parity/pull/121)
|
||||
- out_of_gas -> excepted [#120](https://github.com/paritytech/parity/pull/120)
|
||||
- Parametrizing evm::Factory [#111](https://github.com/paritytech/parity/pull/111)
|
||||
- stLogs tests passing [#118](https://github.com/paritytech/parity/pull/118)
|
||||
- Fix executive. [#117](https://github.com/paritytech/parity/pull/117)
|
||||
- Fixes for marek's shooting from the hip. [#116](https://github.com/paritytech/parity/pull/116)
|
||||
- Executive revert fix [#115](https://github.com/paritytech/parity/pull/115)
|
||||
- Fix storage/account and add butress test. [#114](https://github.com/paritytech/parity/pull/114)
|
||||
- Refactored Pod & Diff types into separate files, JSON infrastructure revamp. [#113](https://github.com/paritytech/parity/pull/113)
|
||||
- Fix storage stuff and introduce per-item dirty-tracking. [#112](https://github.com/paritytech/parity/pull/112)
|
||||
- Check logs in state tests. [#109](https://github.com/paritytech/parity/pull/109)
|
||||
- executive gas calculation fixes [#108](https://github.com/paritytech/parity/pull/108)
|
||||
- proper gas calculation in executive [#107](https://github.com/paritytech/parity/pull/107)
|
||||
- Fixing MaxDepth param for executive [#105](https://github.com/paritytech/parity/pull/105)
|
||||
- Fix determination of state roots. [#106](https://github.com/paritytech/parity/pull/106)
|
||||
- transact substracts tx_gas [#104](https://github.com/paritytech/parity/pull/104)
|
||||
- Pretty-print and fix for state. [#102](https://github.com/paritytech/parity/pull/102)
|
||||
- Tier step price. [#101](https://github.com/paritytech/parity/pull/101)
|
||||
- Refactor Diff datastructures. [#100](https://github.com/paritytech/parity/pull/100)
|
||||
- externalities use u256 instead of u64 for gas calculation [#99](https://github.com/paritytech/parity/pull/99)
|
||||
- Executive tests [#97](https://github.com/paritytech/parity/pull/97)
|
||||
- State conensus tests now print mismatching diff on fail. [#98](https://github.com/paritytech/parity/pull/98)
|
||||
- State testing framework. First test is failing. [#96](https://github.com/paritytech/parity/pull/96)
|
||||
- executive tests [#95](https://github.com/paritytech/parity/pull/95)
|
||||
- Use U512s for ether cost calculation, complete transaction API [#94](https://github.com/paritytech/parity/pull/94)
|
||||
- Utils for consensus test decoding and better layout. [#93](https://github.com/paritytech/parity/pull/93)
|
||||
- executive fixes + tests [#89](https://github.com/paritytech/parity/pull/89)
|
||||
- All transaction tests pass. Nicer testing framework. [#92](https://github.com/paritytech/parity/pull/92)
|
||||
- Block verification tests; BlockProvider blockchain trait for testing [#88](https://github.com/paritytech/parity/pull/88)
|
||||
- State::exists, docs and tests. [#87](https://github.com/paritytech/parity/pull/87)
|
||||
- Add tests module, add two more transaction tests. [#86](https://github.com/paritytech/parity/pull/86)
|
||||
- bring back removed tests, removed build warnings [#82](https://github.com/paritytech/parity/pull/82)
|
||||
- Nicer transaction validation API. Nicer OutOfBounds API in general. [#85](https://github.com/paritytech/parity/pull/85)
|
||||
- Transaction fixes and consensus tests (all passing) [#84](https://github.com/paritytech/parity/pull/84)
|
||||
- fixed getting block info in evmjit + tests [#81](https://github.com/paritytech/parity/pull/81)
|
||||
- evm tests cleanup [#80](https://github.com/paritytech/parity/pull/80)
|
||||
- renamed VmFactory -> Factory [#77](https://github.com/paritytech/parity/pull/77)
|
||||
- fixed rust-evmjit description of improper_ctypes usage [#76](https://github.com/paritytech/parity/pull/76)
|
||||
- jit feature enabled by default [#75](https://github.com/paritytech/parity/pull/75)
|
||||
- evm [#52](https://github.com/paritytech/parity/pull/52)
|
||||
- state clone [#74](https://github.com/paritytech/parity/pull/74)
|
||||
- Block Verification (no tests yet) [#72](https://github.com/paritytech/parity/pull/72)
|
||||
- Improvements to LogEntry and Transaction [#73](https://github.com/paritytech/parity/pull/73)
|
||||
- Use getter in header in preparation for a Header trait; additional testing in enact_block(). [#64](https://github.com/paritytech/parity/pull/64)
|
||||
- BlockChain sync and Client app [#55](https://github.com/paritytech/parity/pull/55)
|
||||
- Block enactment (including test) [#63](https://github.com/paritytech/parity/pull/63)
|
||||
- Block complete. Needs tests. [#62](https://github.com/paritytech/parity/pull/62)
|
||||
- More on OpenBlock::close; State::kill_account added [#61](https://github.com/paritytech/parity/pull/61)
|
||||
- Remove genesis module, add more chain specs and separate out ethereum-specific stuff [#60](https://github.com/paritytech/parity/pull/60)
|
||||
- State::new_contract, camelCase engine params, missing param [#59](https://github.com/paritytech/parity/pull/59)
|
||||
- Use reorganisation [#58](https://github.com/paritytech/parity/pull/58)
|
||||
- Initial Ethash/Block skeleton implementations. [#57](https://github.com/paritytech/parity/pull/57)
|
||||
- Spec with tested Morden genesis decoder and builtins. [#54](https://github.com/paritytech/parity/pull/54)
|
||||
- Move all chain parameters into `engine_params` [#50](https://github.com/paritytech/parity/pull/50)
|
||||
- jit ffi improvements [please review] [#51](https://github.com/paritytech/parity/pull/51)
|
||||
- blockchain [please review] [#34](https://github.com/paritytech/parity/pull/34)
|
||||
- Move information from networkparams.rs into spec.rs [#48](https://github.com/paritytech/parity/pull/48)
|
||||
- Move bulking out in Engine/Params. [#47](https://github.com/paritytech/parity/pull/47)
|
||||
- Removed need for mutation in State. [#46](https://github.com/paritytech/parity/pull/46)
|
||||
- State::code and State::storage_at + tests. [#45](https://github.com/paritytech/parity/pull/45)
|
||||
- State functions for balance and nonce operations [#44](https://github.com/paritytech/parity/pull/44)
|
||||
- Account::storage_at, Account::ensure_cached and tests. [#43](https://github.com/paritytech/parity/pull/43)
|
||||
- Additional tests. [#42](https://github.com/paritytech/parity/pull/42)
|
||||
- seal todo done [#41](https://github.com/paritytech/parity/pull/41)
|
||||
- missing rustc_serialize crate && rlp `as_list` function [#40](https://github.com/paritytech/parity/pull/40)
|
||||
- More methods in Account, documentation and tests. [#39](https://github.com/paritytech/parity/pull/39)
|
||||
- Minor reworking of Account. [#38](https://github.com/paritytech/parity/pull/38)
|
||||
- Add Account and State classes. [#37](https://github.com/paritytech/parity/pull/37)
|
||||
- Revert regressions [#36](https://github.com/paritytech/parity/pull/36)
|
||||
@@ -1,317 +0,0 @@
|
||||
Note: Parity 1.0 reached End-of-Life on 2016-06-24 (EOL).
|
||||
|
||||
## Parity [v1.0.2](https://github.com/paritytech/parity/releases/tag/v1.0.2) (2016-04-11)
|
||||
|
||||
Parity 1.0.2 release improves Json RPC compatibility and fixes a number of stability issues.
|
||||
|
||||
- Flush password prompt [#1031](https://github.com/paritytech/parity/pull/1031)
|
||||
- [beta] dependencies update [#949](https://github.com/paritytech/parity/pull/949)
|
||||
- Master to beta v1.0.2 [#922](https://github.com/paritytech/parity/pull/922)
|
||||
- Master to beta 1.0.2 [#908](https://github.com/paritytech/parity/pull/908)
|
||||
|
||||
## Parity [v1.0.1](https://github.com/paritytech/parity/releases/tag/v1.0.1) (2016-03-28)
|
||||
|
||||
Parity 1.0.1 update fixes a number of issues with Json RPC, transaction propagation and syncing.
|
||||
|
||||
- Imporved sync error handling [#905](https://github.com/paritytech/parity/pull/905)
|
||||
- Publish locally-made transactions to peers. [#851](https://github.com/paritytech/parity/pull/851)
|
||||
- Merge fixes from master to beta [#845](https://github.com/paritytech/parity/pull/845)
|
||||
- Full sync restart on bad block [#844](https://github.com/paritytech/parity/pull/844)
|
||||
- Make BlockNumber optional, fix eth_call [#828](https://github.com/paritytech/parity/pull/828)
|
||||
- Web3sha3 beta [#826](https://github.com/paritytech/parity/pull/826)
|
||||
- Use network id for the web3_net_version return. [#821](https://github.com/paritytech/parity/pull/821)
|
||||
- Fix mining from spinning [#806](https://github.com/paritytech/parity/pull/806)
|
||||
- Merge master to beta [#796](https://github.com/paritytech/parity/pull/796)
|
||||
|
||||
## Parity [v1.0.0](https://github.com/paritytech/parity/releases/tag/v1.0.0) (2016-03-24)
|
||||
|
||||
Parity 1.0.0 release adds the following features:
|
||||
|
||||
- Standard JsonRPC interface.
|
||||
- Full Homestead compatibility.
|
||||
- Transaction management.
|
||||
- Mining with external miner.
|
||||
- Account management.
|
||||
- Geth key chain compatibility.
|
||||
- Additional command line options.
|
||||
- State trie pruning.
|
||||
- Cache and queue footprint.
|
||||
- Network discovery & NAT traversal.
|
||||
- Custom chain specification files.
|
||||
|
||||
Note that in this release the state database is in archive (full) mode by default. Run with one of the `--pruning` options to enable pruning.
|
||||
|
||||
- First part of multi-mining support [#804](https://github.com/paritytech/parity/pull/804)
|
||||
- Fixing future-current transactions clash [#802](https://github.com/paritytech/parity/pull/802)
|
||||
- Increase threads to num_cpus & fix author reporting [#800](https://github.com/paritytech/parity/pull/800)
|
||||
- another batch of rpc improvements [#798](https://github.com/paritytech/parity/pull/798)
|
||||
- Avoid tracing DELEGATECALL and CALLCODE. Plus tests for it. [#794](https://github.com/paritytech/parity/pull/794)
|
||||
- complete getting started steps for OS X [#793](https://github.com/paritytech/parity/pull/793)
|
||||
- Auto detect available port (with fixed test) [#788](https://github.com/paritytech/parity/pull/788)
|
||||
- eth_getTransactionReceipt [#792](https://github.com/paritytech/parity/pull/792)
|
||||
- Comprehensive tests for tracing transactions [#791](https://github.com/paritytech/parity/pull/791)
|
||||
- Disable preparing work package if miners don't ask for it. [#771](https://github.com/paritytech/parity/pull/771)
|
||||
- Listen on all interfaces for JSONRPC by default. [#786](https://github.com/paritytech/parity/pull/786)
|
||||
- eth_call [#783](https://github.com/paritytech/parity/pull/783)
|
||||
- Revert "Auto detect available port" [#789](https://github.com/paritytech/parity/pull/789)
|
||||
- added output to execution result [#777](https://github.com/paritytech/parity/pull/777)
|
||||
- Auto detect available port [#782](https://github.com/paritytech/parity/pull/782)
|
||||
- Allow 0x prefix for --author. [#785](https://github.com/paritytech/parity/pull/785)
|
||||
- updated dependencies, moved rpctest to its own submodule [#784](https://github.com/paritytech/parity/pull/784)
|
||||
- use ethjson module to load chain json tests [#778](https://github.com/paritytech/parity/pull/778)
|
||||
- Tracing implemented. [#772](https://github.com/paritytech/parity/pull/772)
|
||||
- test ethjson module on travis [#780](https://github.com/paritytech/parity/pull/780)
|
||||
- batch of rpc fixes [#775](https://github.com/paritytech/parity/pull/775)
|
||||
- rpctest executable [#757](https://github.com/paritytech/parity/pull/757)
|
||||
- Refactoring error transaction_queue error handling and `update_sealing` method. [#753](https://github.com/paritytech/parity/pull/753)
|
||||
- Avoid importing transactions with gas above 1.1*block_gas_limit to transaction queue [#760](https://github.com/paritytech/parity/pull/760)
|
||||
- Removing transactions that failed to be pushed to block. [#752](https://github.com/paritytech/parity/pull/752)
|
||||
- Updating clippy [#766](https://github.com/paritytech/parity/pull/766)
|
||||
- Attempting to add all transactions to mined block [#754](https://github.com/paritytech/parity/pull/754)
|
||||
- Prettier version w/o git dir; Use rustc compile time version [#761](https://github.com/paritytech/parity/pull/761)
|
||||
- Stop adding transactions to queue while not fully synced [#751](https://github.com/paritytech/parity/pull/751)
|
||||
- Verify sender's balance before importing transaction to queue [#746](https://github.com/paritytech/parity/pull/746)
|
||||
- Returning number of transactions pending in block not queue [#750](https://github.com/paritytech/parity/pull/750)
|
||||
- Speeding up build [#733](https://github.com/paritytech/parity/pull/733)
|
||||
- adding check for a sync when giving work to miner [#742](https://github.com/paritytech/parity/pull/742)
|
||||
- json deserialization module [#745](https://github.com/paritytech/parity/pull/745)
|
||||
- Update install-parity.sh [#749](https://github.com/paritytech/parity/pull/749)
|
||||
- Restart sync on getting old unknown header [#747](https://github.com/paritytech/parity/pull/747)
|
||||
- Missing return for #737 [#744](https://github.com/paritytech/parity/pull/744)
|
||||
- Enact block with uncles test [#741](https://github.com/paritytech/parity/pull/741)
|
||||
- Fix outdated libc version on dependency [#740](https://github.com/paritytech/parity/pull/740)
|
||||
- Fixing possible race in transaction queue [#735](https://github.com/paritytech/parity/pull/735)
|
||||
- Sync fixed again [#737](https://github.com/paritytech/parity/pull/737)
|
||||
- Don't change best block until extras is committed. [#734](https://github.com/paritytech/parity/pull/734)
|
||||
- stable only until travis speedup [#736](https://github.com/paritytech/parity/pull/736)
|
||||
- Optimizing uint operations (architecture independent) [#629](https://github.com/paritytech/parity/pull/629)
|
||||
- Add RLP, not a data item. [#725](https://github.com/paritytech/parity/pull/725)
|
||||
- PV63 receipts response [#687](https://github.com/paritytech/parity/pull/687)
|
||||
- another batch of rpc tests [#723](https://github.com/paritytech/parity/pull/723)
|
||||
- dockerfiles update [#726](https://github.com/paritytech/parity/pull/726)
|
||||
- Lock reports to avoid out of order badness. [#721](https://github.com/paritytech/parity/pull/721)
|
||||
- Fixed handshake leak [#722](https://github.com/paritytech/parity/pull/722)
|
||||
- Allow configuration of target gas limit. [#719](https://github.com/paritytech/parity/pull/719)
|
||||
- Version 1.1 in master [#714](https://github.com/paritytech/parity/pull/714)
|
||||
- Silence UDP warnings [#720](https://github.com/paritytech/parity/pull/720)
|
||||
- Rpc personal tests [#715](https://github.com/paritytech/parity/pull/715)
|
||||
- Fixing warnings [#704](https://github.com/paritytech/parity/pull/704)
|
||||
- docopts cleanups [#713](https://github.com/paritytech/parity/pull/713)
|
||||
- Removed rocksdb build dependency [#717](https://github.com/paritytech/parity/pull/717)
|
||||
- Fixed splitting Neighbours packet [#710](https://github.com/paritytech/parity/pull/710)
|
||||
- management of account expiration & memory [#701](https://github.com/paritytech/parity/pull/701)
|
||||
- Remove EarlyMerge from user docs. [#708](https://github.com/paritytech/parity/pull/708)
|
||||
- Fixes and traces for refcountdb. [#705](https://github.com/paritytech/parity/pull/705)
|
||||
- Check for NULL_RLP in AccountDB [#706](https://github.com/paritytech/parity/pull/706)
|
||||
- ethminer as crate [#700](https://github.com/paritytech/parity/pull/700)
|
||||
- Old ref-counted DB code [#692](https://github.com/paritytech/parity/pull/692)
|
||||
- next batch of rpc tests and fixes [#699](https://github.com/paritytech/parity/pull/699)
|
||||
- implemented eth_geStorageAt rpc method, added more tests for rpc [#695](https://github.com/paritytech/parity/pull/695)
|
||||
- Fix JournalDB era marker [#690](https://github.com/paritytech/parity/pull/690)
|
||||
- More sync fixes [#685](https://github.com/paritytech/parity/pull/685)
|
||||
- mark some key tests as heavy [#694](https://github.com/paritytech/parity/pull/694)
|
||||
- Limit incoming connections [#693](https://github.com/paritytech/parity/pull/693)
|
||||
- Updating clippy [#688](https://github.com/paritytech/parity/pull/688)
|
||||
- eth_accounts, eth_getBalance rpc functions && tests [#691](https://github.com/paritytech/parity/pull/691)
|
||||
- state query for archive jdb [#683](https://github.com/paritytech/parity/pull/683)
|
||||
- Fix for option 1 of JournalDB [#658](https://github.com/paritytech/parity/pull/658)
|
||||
- Rename into something that is a little more descriptive. [#689](https://github.com/paritytech/parity/pull/689)
|
||||
- JournalDB with in-memory overlay (option2) [#634](https://github.com/paritytech/parity/pull/634)
|
||||
- additional (failing) SecretStore test [#682](https://github.com/paritytech/parity/pull/682)
|
||||
- Updating clippy & fixing warnings. [#670](https://github.com/paritytech/parity/pull/670)
|
||||
- rpc web3 tests [#681](https://github.com/paritytech/parity/pull/681)
|
||||
- Making personal json-rpc configurable via cli [#677](https://github.com/paritytech/parity/pull/677)
|
||||
- RPC Pending Transactions Filter [#661](https://github.com/paritytech/parity/pull/661)
|
||||
- Rearrange journaldb infrastructure to make more extensible [#678](https://github.com/paritytech/parity/pull/678)
|
||||
- JournalDB -> Box<JournalDB>, and it's a trait. [#673](https://github.com/paritytech/parity/pull/673)
|
||||
- fix warning for transaction_queue.add usage [#676](https://github.com/paritytech/parity/pull/676)
|
||||
- Adding std::mem back (only for asm) [#680](https://github.com/paritytech/parity/pull/680)
|
||||
- update readme to exclude beta step (stable is ok) [#679](https://github.com/paritytech/parity/pull/679)
|
||||
- fixed U256 and transaction request deserialization [#675](https://github.com/paritytech/parity/pull/675)
|
||||
- More geth compatibility. [#666](https://github.com/paritytech/parity/pull/666)
|
||||
- Removing running clippy by default on nightly. [#671](https://github.com/paritytech/parity/pull/671)
|
||||
- rpc net submodule tests [#667](https://github.com/paritytech/parity/pull/667)
|
||||
- Client module overhaul [#665](https://github.com/paritytech/parity/pull/665)
|
||||
- Rpc transaction signing [#587](https://github.com/paritytech/parity/pull/587)
|
||||
- Transaction queue exposed via JSON rpc. [#652](https://github.com/paritytech/parity/pull/652)
|
||||
- Remove unneeded locking [#499](https://github.com/paritytech/parity/pull/499)
|
||||
- extend sync status interface to sync provider [#664](https://github.com/paritytech/parity/pull/664)
|
||||
- --archive is default. --pruning is option. [#663](https://github.com/paritytech/parity/pull/663)
|
||||
- jsonrpc uses client and sync interfaces [#641](https://github.com/paritytech/parity/pull/641)
|
||||
- Expose transaction insertion in sync lib [#609](https://github.com/paritytech/parity/pull/609)
|
||||
- Removing get prefix from poll_info [#660](https://github.com/paritytech/parity/pull/660)
|
||||
- Tx queue update height bug [#657](https://github.com/paritytech/parity/pull/657)
|
||||
- Tx_queue_docs -> To master [#651](https://github.com/paritytech/parity/pull/651)
|
||||
- blockchain import_route [#645](https://github.com/paritytech/parity/pull/645)
|
||||
- Stop workers before stopping event loop [#655](https://github.com/paritytech/parity/pull/655)
|
||||
- Validate sender before importing to queue [#650](https://github.com/paritytech/parity/pull/650)
|
||||
- Gas price threshold for transactions [#640](https://github.com/paritytech/parity/pull/640)
|
||||
- `dev` feature enabled when compiling without `--release` [#627](https://github.com/paritytech/parity/pull/627)
|
||||
- Don't call mark_as_bad needlessly [#648](https://github.com/paritytech/parity/pull/648)
|
||||
- Fixed sync handling large forks [#647](https://github.com/paritytech/parity/pull/647)
|
||||
- Additional documentation for transaction queue [#631](https://github.com/paritytech/parity/pull/631)
|
||||
- Transaction Queue Integration [#607](https://github.com/paritytech/parity/pull/607)
|
||||
- Keys cli [#639](https://github.com/paritytech/parity/pull/639)
|
||||
- fix build warning [#643](https://github.com/paritytech/parity/pull/643)
|
||||
- updated jsonrpc-core and http-server libs [#642](https://github.com/paritytech/parity/pull/642)
|
||||
- jsonrpc panics gracefully shutdown client [#638](https://github.com/paritytech/parity/pull/638)
|
||||
- Fixing CLI parameters [#633](https://github.com/paritytech/parity/pull/633)
|
||||
- Normal CLI options with geth. [#628](https://github.com/paritytech/parity/pull/628)
|
||||
- Do not remove the peer immediatelly on send error [#626](https://github.com/paritytech/parity/pull/626)
|
||||
- Jsonrpc block behind [#622](https://github.com/paritytech/parity/pull/622)
|
||||
- Remove println!s. [#624](https://github.com/paritytech/parity/pull/624)
|
||||
- JournalDB option 1 fix [#613](https://github.com/paritytech/parity/pull/613)
|
||||
- Network tracing cleanup [#611](https://github.com/paritytech/parity/pull/611)
|
||||
- Revert "Transaction Queue integration" [#602](https://github.com/paritytech/parity/pull/602)
|
||||
- fix benches compilation [#601](https://github.com/paritytech/parity/pull/601)
|
||||
- Transaction Queue integration [#595](https://github.com/paritytech/parity/pull/595)
|
||||
- verifier trait improvements [#597](https://github.com/paritytech/parity/pull/597)
|
||||
- build on rust stable [#600](https://github.com/paritytech/parity/pull/600)
|
||||
- Geth import silent if no geth [#599](https://github.com/paritytech/parity/pull/599)
|
||||
- Additional journaldb logging and assert [#593](https://github.com/paritytech/parity/pull/593)
|
||||
- Uncle inclusion in block authoring. [#578](https://github.com/paritytech/parity/pull/578)
|
||||
- Fixed potential deadlock on startup [#592](https://github.com/paritytech/parity/pull/592)
|
||||
- Fixing an overflow panic [#591](https://github.com/paritytech/parity/pull/591)
|
||||
- Fixed one more case of sync stalling [#590](https://github.com/paritytech/parity/pull/590)
|
||||
- JournalDB can now operate in "archive" mode [#589](https://github.com/paritytech/parity/pull/589)
|
||||
- Secret store integration with client [#586](https://github.com/paritytech/parity/pull/586)
|
||||
- fix build on nightly rust [#588](https://github.com/paritytech/parity/pull/588)
|
||||
- deserialization for uint generic [#585](https://github.com/paritytech/parity/pull/585)
|
||||
- TransactionsQueue implementation [#559](https://github.com/paritytech/parity/pull/559)
|
||||
- JSON-RPC personal service (follows #582) [#583](https://github.com/paritytech/parity/pull/583)
|
||||
- making key directory thread-safe [#582](https://github.com/paritytech/parity/pull/582)
|
||||
- verifier trait [#581](https://github.com/paritytech/parity/pull/581)
|
||||
- shrink_to_fit after removing hashes. [#580](https://github.com/paritytech/parity/pull/580)
|
||||
- support for rpc polling [#504](https://github.com/paritytech/parity/pull/504)
|
||||
- limit serde codegen only to rpc types submodule [#569](https://github.com/paritytech/parity/pull/569)
|
||||
- fork test for Issue test/568 [#573](https://github.com/paritytech/parity/pull/573)
|
||||
- Fixing clippy warnings = small refactoring of `request_blocks` [#560](https://github.com/paritytech/parity/pull/560)
|
||||
- Improved journaldb logging [#571](https://github.com/paritytech/parity/pull/571)
|
||||
- Additional check to ancient enactments. [#570](https://github.com/paritytech/parity/pull/570)
|
||||
- chainfilter shouldnt exclude to_block from results [#564](https://github.com/paritytech/parity/pull/564)
|
||||
- Fix coverage test run [#567](https://github.com/paritytech/parity/pull/567)
|
||||
- Mining [#547](https://github.com/paritytech/parity/pull/547)
|
||||
- fix uint warnings [#565](https://github.com/paritytech/parity/pull/565)
|
||||
- Finished blockchain generator. [#562](https://github.com/paritytech/parity/pull/562)
|
||||
- fixed broken master [#563](https://github.com/paritytech/parity/pull/563)
|
||||
- uint to separate crate [#544](https://github.com/paritytech/parity/pull/544)
|
||||
- improved test chain generator [#554](https://github.com/paritytech/parity/pull/554)
|
||||
- Fixing spelling in propagade->propagate [#558](https://github.com/paritytech/parity/pull/558)
|
||||
- Changing RefCell to Cell in transaction. [#557](https://github.com/paritytech/parity/pull/557)
|
||||
- Fix for morden consensus. [#556](https://github.com/paritytech/parity/pull/556)
|
||||
- blockchain generator [#550](https://github.com/paritytech/parity/pull/550)
|
||||
- Sparse Table Implementation (Row, Col) -> Val [#545](https://github.com/paritytech/parity/pull/545)
|
||||
- fixup install script [#548](https://github.com/paritytech/parity/pull/548)
|
||||
- Fixing clippy warnings [#546](https://github.com/paritytech/parity/pull/546)
|
||||
- ignore out directory [#543](https://github.com/paritytech/parity/pull/543)
|
||||
- u256 full multiplication [#539](https://github.com/paritytech/parity/pull/539)
|
||||
- Fix panic when downloading stales, update homestead transition [#537](https://github.com/paritytech/parity/pull/537)
|
||||
- changing x64 asm config [#534](https://github.com/paritytech/parity/pull/534)
|
||||
- uncomment state transition tests [#533](https://github.com/paritytech/parity/pull/533)
|
||||
- jsonrpc uses weak pointers to client [#532](https://github.com/paritytech/parity/pull/532)
|
||||
- Morden switch to Homestead rules at #494,000. [#531](https://github.com/paritytech/parity/pull/531)
|
||||
- Blockchain module cleanup [#524](https://github.com/paritytech/parity/pull/524)
|
||||
- Multiplication issue + very exhaustive tests for it [#528](https://github.com/paritytech/parity/pull/528)
|
||||
- EIP-8 [#498](https://github.com/paritytech/parity/pull/498)
|
||||
- Make "random" trie tests fully deterministic. [#527](https://github.com/paritytech/parity/pull/527)
|
||||
- udpated serde to version 0.7.0 [#526](https://github.com/paritytech/parity/pull/526)
|
||||
- Better memory management [#516](https://github.com/paritytech/parity/pull/516)
|
||||
- Typo [#523](https://github.com/paritytech/parity/pull/523)
|
||||
- U512 add/sub optimize [#521](https://github.com/paritytech/parity/pull/521)
|
||||
- Account management + geth keystore import (no utility crate added) [#509](https://github.com/paritytech/parity/pull/509)
|
||||
- Delayed UPnP initialization [#505](https://github.com/paritytech/parity/pull/505)
|
||||
- Fixing marking blocks as bad & SyncMessage bugs + small client refactoring. [#503](https://github.com/paritytech/parity/pull/503)
|
||||
- optimization of U256 [#515](https://github.com/paritytech/parity/pull/515)
|
||||
- Removed rocksdb from build scripts and instructions [#520](https://github.com/paritytech/parity/pull/520)
|
||||
- RocksDB abstraction layer + Hash index for state DB [#464](https://github.com/paritytech/parity/pull/464)
|
||||
- bloomfilter [#418](https://github.com/paritytech/parity/pull/418)
|
||||
- Fixed a race condition when connecting peer disconnects immediately [#519](https://github.com/paritytech/parity/pull/519)
|
||||
- ignore intellij idea project files as well [#518](https://github.com/paritytech/parity/pull/518)
|
||||
- updated version of unicase [#517](https://github.com/paritytech/parity/pull/517)
|
||||
- jsonrpc security, cors headers, fixed #359 [#493](https://github.com/paritytech/parity/pull/493)
|
||||
- Rust implementations to replace data tables (#161) [#482](https://github.com/paritytech/parity/pull/482)
|
||||
- fix issue with starting requested block number was not included itself [#512](https://github.com/paritytech/parity/pull/512)
|
||||
- fixed travis --org GH_TOKEN [#510](https://github.com/paritytech/parity/pull/510)
|
||||
- Improved log format [#506](https://github.com/paritytech/parity/pull/506)
|
||||
- Log address on failed connection attempt [#502](https://github.com/paritytech/parity/pull/502)
|
||||
- Bumping clippy and fixing warnings. [#501](https://github.com/paritytech/parity/pull/501)
|
||||
- Bumping versions. Fixes #496 [#500](https://github.com/paritytech/parity/pull/500)
|
||||
- Manage final user-input errors. [#494](https://github.com/paritytech/parity/pull/494)
|
||||
- Remove unneeded code, fix minor potential issue with length. [#495](https://github.com/paritytech/parity/pull/495)
|
||||
- Remove "unknown" from version string. [#488](https://github.com/paritytech/parity/pull/488)
|
||||
- Include git commit date & hash. [#486](https://github.com/paritytech/parity/pull/486)
|
||||
- Use proper version string. [#485](https://github.com/paritytech/parity/pull/485)
|
||||
- Networking fixes [#480](https://github.com/paritytech/parity/pull/480)
|
||||
- Fix potential deadlock on node table update [#484](https://github.com/paritytech/parity/pull/484)
|
||||
- Squash more warnings [#481](https://github.com/paritytech/parity/pull/481)
|
||||
- dev/test/build tools to separate crate [#477](https://github.com/paritytech/parity/pull/477)
|
||||
- Back to original slab crate [#479](https://github.com/paritytech/parity/pull/479)
|
||||
- Better user errors. [#476](https://github.com/paritytech/parity/pull/476)
|
||||
- UDP Discovery [#440](https://github.com/paritytech/parity/pull/440)
|
||||
- update readme with rust override [#475](https://github.com/paritytech/parity/pull/475)
|
||||
- fixed warnings on rust beta [#474](https://github.com/paritytech/parity/pull/474)
|
||||
- Secret store (part2 - encrypted key/value svc) [#449](https://github.com/paritytech/parity/pull/449)
|
||||
- Kill bad test. [#473](https://github.com/paritytech/parity/pull/473)
|
||||
- Make clippy an optional dependency [#422](https://github.com/paritytech/parity/pull/422)
|
||||
- parity compiling fine [#469](https://github.com/paritytech/parity/pull/469)
|
||||
- compiling ethcore on beta [#468](https://github.com/paritytech/parity/pull/468)
|
||||
- Utils compiling in beta [#467](https://github.com/paritytech/parity/pull/467)
|
||||
- Get rid of lru_cache dependency [#466](https://github.com/paritytech/parity/pull/466)
|
||||
- Add daemonization. [#459](https://github.com/paritytech/parity/pull/459)
|
||||
- Master upgrade [#448](https://github.com/paritytech/parity/pull/448)
|
||||
- Remove contributing stuff now that we have CLA bot. [#447](https://github.com/paritytech/parity/pull/447)
|
||||
- Add Morden bootnode. [#446](https://github.com/paritytech/parity/pull/446)
|
||||
- beta fixes to master [#441](https://github.com/paritytech/parity/pull/441)
|
||||
- Secret store (part1 - key management) [#423](https://github.com/paritytech/parity/pull/423)
|
||||
- Use 1100000 as the homestead transition, fix build instructions. [#438](https://github.com/paritytech/parity/pull/438)
|
||||
- More sync and propagation fixes [#420](https://github.com/paritytech/parity/pull/420)
|
||||
- back to cargo crates [#436](https://github.com/paritytech/parity/pull/436)
|
||||
- Fixing clippy warnings [#435](https://github.com/paritytech/parity/pull/435)
|
||||
- preserving root cargo lock [#434](https://github.com/paritytech/parity/pull/434)
|
||||
- Nightly fix [#432](https://github.com/paritytech/parity/pull/432)
|
||||
- nightly fixes [#431](https://github.com/paritytech/parity/pull/431)
|
||||
- Delay Homestead transition from 1,000,000. [#429](https://github.com/paritytech/parity/pull/429)
|
||||
- Nightly fix effort (still should fail) [#428](https://github.com/paritytech/parity/pull/428)
|
||||
- clippy version update, docopt-macro moving to fork [#425](https://github.com/paritytech/parity/pull/425)
|
||||
- Network/Sync fixes and optimizations [#416](https://github.com/paritytech/parity/pull/416)
|
||||
- Use latest era instead of end era as journal marker [#414](https://github.com/paritytech/parity/pull/414)
|
||||
- api changes [#402](https://github.com/paritytech/parity/pull/402)
|
||||
- Option for no init nodes. [#408](https://github.com/paritytech/parity/pull/408)
|
||||
- Fixed block_bodies not returning a list [#406](https://github.com/paritytech/parity/pull/406)
|
||||
- Fix test. [#405](https://github.com/paritytech/parity/pull/405)
|
||||
- Allow path to be configured. [#404](https://github.com/paritytech/parity/pull/404)
|
||||
- Upnp [#400](https://github.com/paritytech/parity/pull/400)
|
||||
- eth_syncing, fixed #397 [#398](https://github.com/paritytech/parity/pull/398)
|
||||
- Using modified version of ctrlc that catches SIGTERM [#399](https://github.com/paritytech/parity/pull/399)
|
||||
- Catching panics. [#396](https://github.com/paritytech/parity/pull/396)
|
||||
- jsonrpc [#391](https://github.com/paritytech/parity/pull/391)
|
||||
- Externalities tests (still clumsy) [#394](https://github.com/paritytech/parity/pull/394)
|
||||
- excluding test code itself from coverage [#395](https://github.com/paritytech/parity/pull/395)
|
||||
- Additional tweaks to options. [#390](https://github.com/paritytech/parity/pull/390)
|
||||
- --chain option for setting which network to go on. [#388](https://github.com/paritytech/parity/pull/388)
|
||||
- Ethash unit tests final [#387](https://github.com/paritytech/parity/pull/387)
|
||||
- jsonrpc [#374](https://github.com/paritytech/parity/pull/374)
|
||||
- Editorconfig file. [#384](https://github.com/paritytech/parity/pull/384)
|
||||
- Coverage effort [in progress] [#382](https://github.com/paritytech/parity/pull/382)
|
||||
- making root kcov runner simular to the one running on CI [#380](https://github.com/paritytech/parity/pull/380)
|
||||
- add gcc as a dependency to dockerfiles [#381](https://github.com/paritytech/parity/pull/381)
|
||||
- Check for handshake expiration before attempting connection replace [#375](https://github.com/paritytech/parity/pull/375)
|
||||
- Blocks propagation [#364](https://github.com/paritytech/parity/pull/364)
|
||||
- Network params. [#376](https://github.com/paritytech/parity/pull/376)
|
||||
- Add parity-node-zero to bootnodes. [#373](https://github.com/paritytech/parity/pull/373)
|
||||
- kcov uses travis_job_id instead of coveralls token [#370](https://github.com/paritytech/parity/pull/370)
|
||||
- Add parity-node-zero.ethcore.io to boot nodes. [#371](https://github.com/paritytech/parity/pull/371)
|
||||
|
||||
## Parity [v1.0.0-rc1](https://github.com/paritytech/parity/releases/tag/v1.0.0-rc1) (2016-03-15)
|
||||
|
||||
First Parity 1.0.0 release candidate.
|
||||
|
||||
- Version 1.0 in beta [#712](https://github.com/paritytech/parity/pull/712)
|
||||
- Fix test for beta [#617](https://github.com/paritytech/parity/pull/617)
|
||||
- JournalDB fix option 1 for beta [#614](https://github.com/paritytech/parity/pull/614)
|
||||
- Failing test. [#606](https://github.com/paritytech/parity/pull/606)
|
||||
- Fix transition points [#604](https://github.com/paritytech/parity/pull/604)
|
||||
- (BETA) Update README.md [#549](https://github.com/paritytech/parity/pull/549)
|
||||
- (BETA) instructions for beta release channel [#456](https://github.com/paritytech/parity/pull/456)
|
||||
- (BETA) fix nightly - remerge [#454](https://github.com/paritytech/parity/pull/454)
|
||||
- (BETA) fixing nightly version for beta [#452](https://github.com/paritytech/parity/pull/452)
|
||||
@@ -1,154 +0,0 @@
|
||||
Note: Parity 1.1 reached End-of-Life on 2016-08-12 (EOL).
|
||||
|
||||
## Parity [v1.1.0](https://github.com/paritytech/parity/releases/tag/v1.1.0) (2016-05-02)
|
||||
|
||||
Parity 1.1.0 introduces:
|
||||
|
||||
- Transaction tracing. Parity now optionally indexes & stores message-call/"internal transaction" information and provides additional RPC for querying.
|
||||
- Web interface for logs, status & JSON RPC.
|
||||
- Improved JSON RPC compatibility.
|
||||
- Reduced memory footprint.
|
||||
- Optimized EVM interpreter performance.
|
||||
|
||||
Full Changes:
|
||||
|
||||
- Exposing default extra data via ethcore RPC [#1032](https://github.com/paritytech/parity/pull/1032)
|
||||
- Net etiquette [#1028](https://github.com/paritytech/parity/pull/1028)
|
||||
- Bumping clippy & fixing warnings [#1024](https://github.com/paritytech/parity/pull/1024)
|
||||
- Tracedb interface && cli [#997](https://github.com/paritytech/parity/pull/997)
|
||||
- Switching to geth-attach supporting version of rpc core and server [#1022](https://github.com/paritytech/parity/pull/1022)
|
||||
- Fixing status page displaying homestead [#1020](https://github.com/paritytech/parity/pull/1020)
|
||||
- Core tracedb functionality. [#996](https://github.com/paritytech/parity/pull/996)
|
||||
- RPC method for supported modules [#1019](https://github.com/paritytech/parity/pull/1019)
|
||||
- Updating status page [#1015](https://github.com/paritytech/parity/pull/1015)
|
||||
- Disabling wallet [#1017](https://github.com/paritytech/parity/pull/1017)
|
||||
- More detailed fatal error reporting [#1016](https://github.com/paritytech/parity/pull/1016)
|
||||
- Support 'pending' block in RPC [#1007](https://github.com/paritytech/parity/pull/1007)
|
||||
- Enable pending block when there is local transaction pending. [#1005](https://github.com/paritytech/parity/pull/1005)
|
||||
- updating key files permissions on save [#1010](https://github.com/paritytech/parity/pull/1010)
|
||||
- IPC JSON RPC (for external interface) [#1009](https://github.com/paritytech/parity/pull/1009)
|
||||
- Fixing Firefox authorization issues [#1013](https://github.com/paritytech/parity/pull/1013)
|
||||
- cargo update [#1012](https://github.com/paritytech/parity/pull/1012)
|
||||
- Switching to rust-url@1.0.0 [#1011](https://github.com/paritytech/parity/pull/1011)
|
||||
- Exception handling in RPC & WebApps [#988](https://github.com/paritytech/parity/pull/988)
|
||||
- Fixed uint deserialization from hex [#1008](https://github.com/paritytech/parity/pull/1008)
|
||||
- Tweak timeout and packet size to handle slow networks better [#1004](https://github.com/paritytech/parity/pull/1004)
|
||||
- db key is generic and can be made smaller [#1006](https://github.com/paritytech/parity/pull/1006)
|
||||
- IPC with new serialization [#998](https://github.com/paritytech/parity/pull/998)
|
||||
- make jsonrpc api engine agnostic [#1001](https://github.com/paritytech/parity/pull/1001)
|
||||
- updated cargo.lock [#1002](https://github.com/paritytech/parity/pull/1002)
|
||||
- updated parity dependencies [#993](https://github.com/paritytech/parity/pull/993)
|
||||
- Auto (with codegen) binary serializer [#980](https://github.com/paritytech/parity/pull/980)
|
||||
- Fixing transaction queue last_nonces update [#995](https://github.com/paritytech/parity/pull/995)
|
||||
- import route contains ommited blocks [#994](https://github.com/paritytech/parity/pull/994)
|
||||
- fixed encoding 0u8 [#992](https://github.com/paritytech/parity/pull/992)
|
||||
- Use latest netstats [#989](https://github.com/paritytech/parity/pull/989)
|
||||
- RPC shared external miner [#984](https://github.com/paritytech/parity/pull/984)
|
||||
- Additional RPC methods for settings [#983](https://github.com/paritytech/parity/pull/983)
|
||||
- Fixing transaction_queue deadlock [#985](https://github.com/paritytech/parity/pull/985)
|
||||
- Refactoring of `parity/main.rs` [#981](https://github.com/paritytech/parity/pull/981)
|
||||
- Fixing clippy warnings. [#982](https://github.com/paritytech/parity/pull/982)
|
||||
- Bumping status page [#977](https://github.com/paritytech/parity/pull/977)
|
||||
- querying extras separated to its own module [#972](https://github.com/paritytech/parity/pull/972)
|
||||
- Exposing application logs via RPC. [#976](https://github.com/paritytech/parity/pull/976)
|
||||
- Addressing binary serialization for db types [#966](https://github.com/paritytech/parity/pull/966)
|
||||
- removed redundant unwraps [#935](https://github.com/paritytech/parity/pull/935)
|
||||
- fixed transaction queue merge conflict [#975](https://github.com/paritytech/parity/pull/975)
|
||||
- Configurable limit for transaction queue (CLI & Ethcore-RPC) [#974](https://github.com/paritytech/parity/pull/974)
|
||||
- Enforce limit caused `last_nonce` to return incorrect values. [#973](https://github.com/paritytech/parity/pull/973)
|
||||
- Even more detailed errors for transaction queue [#969](https://github.com/paritytech/parity/pull/969)
|
||||
- temporary fix of panic in blockchain garbage collection [#970](https://github.com/paritytech/parity/pull/970)
|
||||
- IPC codegen - some minor fixes & enhancements [#967](https://github.com/paritytech/parity/pull/967)
|
||||
- Additional logging for transactions [#968](https://github.com/paritytech/parity/pull/968)
|
||||
- refactored blockchain extras keys building [#963](https://github.com/paritytech/parity/pull/963)
|
||||
- Using hyper-mio branch in webapps. [#957](https://github.com/paritytech/parity/pull/957)
|
||||
- Remove nanomsg from build-dependencies [#965](https://github.com/paritytech/parity/pull/965)
|
||||
- Fix build for --target=armv7-unknown-linux-gnueabihf [#964](https://github.com/paritytech/parity/pull/964)
|
||||
- IPC RPC codegen extra feature [#962](https://github.com/paritytech/parity/pull/962)
|
||||
- IPC RPC codegen for generic implementation [#961](https://github.com/paritytech/parity/pull/961)
|
||||
- using db_path directory when upgrading [#960](https://github.com/paritytech/parity/pull/960)
|
||||
- IPC hypervisor [#958](https://github.com/paritytech/parity/pull/958)
|
||||
- Removing a transaction from queue now removes all from this sender with lower nonces. [#950](https://github.com/paritytech/parity/pull/950)
|
||||
- bump status page version 0.1.7 [#955](https://github.com/paritytech/parity/pull/955)
|
||||
- Changing cors header to be optional [#956](https://github.com/paritytech/parity/pull/956)
|
||||
- Update ARM Dockerfile [#959](https://github.com/paritytech/parity/pull/959)
|
||||
- Sensible gas limits for eth_sendTransaction [#953](https://github.com/paritytech/parity/pull/953)
|
||||
- Fix upgrade script and make parity run when no .parity dir. [#954](https://github.com/paritytech/parity/pull/954)
|
||||
- Tracing and docs for --pruning=auto. [#952](https://github.com/paritytech/parity/pull/952)
|
||||
- IPC serialization for custom parameters [#946](https://github.com/paritytech/parity/pull/946)
|
||||
- default filter from block should be Latest, not Earliest [#948](https://github.com/paritytech/parity/pull/948)
|
||||
- README.md: removes sudo from multirust installation [#943](https://github.com/paritytech/parity/pull/943)
|
||||
- Disable long lines formatting + ethash example. [#939](https://github.com/paritytech/parity/pull/939)
|
||||
- Ethcore-specific RPC methods for altering miner parameters. [#934](https://github.com/paritytech/parity/pull/934)
|
||||
- Use ethcore nanomsg bindings [#941](https://github.com/paritytech/parity/pull/941)
|
||||
- Update IPC codegen to latest syntax libs [#938](https://github.com/paritytech/parity/pull/938)
|
||||
- IPC documentation [#937](https://github.com/paritytech/parity/pull/937)
|
||||
- Bumping clippy and fixing warnings. [#936](https://github.com/paritytech/parity/pull/936)
|
||||
- Pruning auto [#927](https://github.com/paritytech/parity/pull/927)
|
||||
- IPC persistent client link [#933](https://github.com/paritytech/parity/pull/933)
|
||||
- IPC persistent client link [#930](https://github.com/paritytech/parity/pull/930)
|
||||
- IPC handshake (negotiating protocol/api version) [#928](https://github.com/paritytech/parity/pull/928)
|
||||
- Upgrade logic between versions [#914](https://github.com/paritytech/parity/pull/914)
|
||||
- executive tracing cleanup [#903](https://github.com/paritytech/parity/pull/903)
|
||||
- Ethcore-specific RPC methods [#923](https://github.com/paritytech/parity/pull/923)
|
||||
- Parameter to allow user to force the sealing mechanism [#918](https://github.com/paritytech/parity/pull/918)
|
||||
- updated dependencies [#921](https://github.com/paritytech/parity/pull/921)
|
||||
- Fixed send transaction deadlock [#920](https://github.com/paritytech/parity/pull/920)
|
||||
- --unlock is comma-delimited. [#916](https://github.com/paritytech/parity/pull/916)
|
||||
- fixed eth_getLogs [#915](https://github.com/paritytech/parity/pull/915)
|
||||
- create provided custom dir for keys if none [#912](https://github.com/paritytech/parity/pull/912)
|
||||
- spec loading cleanup [#858](https://github.com/paritytech/parity/pull/858)
|
||||
- WebApps HTTP Basic Auth Support [#906](https://github.com/paritytech/parity/pull/906)
|
||||
- Removing match on constant [#888](https://github.com/paritytech/parity/pull/888)
|
||||
- Update auth.rs [#907](https://github.com/paritytech/parity/pull/907)
|
||||
- Enabling webapps compilation by default [#904](https://github.com/paritytech/parity/pull/904)
|
||||
- fixed #895 [#898](https://github.com/paritytech/parity/pull/898)
|
||||
- Support for compile-time included WebApplications. [#899](https://github.com/paritytech/parity/pull/899)
|
||||
- Propagate transaction queue [#894](https://github.com/paritytech/parity/pull/894)
|
||||
- Use new json RPC server [#901](https://github.com/paritytech/parity/pull/901)
|
||||
- Gracefully dying when trying to enable RPC and app is compiled without it. [#900](https://github.com/paritytech/parity/pull/900)
|
||||
- Additional logging and friendlier error messages [#893](https://github.com/paritytech/parity/pull/893)
|
||||
- Avoid signalling readiness when app is about to be closed. [#897](https://github.com/paritytech/parity/pull/897)
|
||||
- fixed #875 and added tests for eth_sendTransaction [#890](https://github.com/paritytech/parity/pull/890)
|
||||
- passing key path to all invocations [#891](https://github.com/paritytech/parity/pull/891)
|
||||
- Fixed eth_call nonce and gas handling [#892](https://github.com/paritytech/parity/pull/892)
|
||||
- ipc rpc with nano transport (simple duplex) [#886](https://github.com/paritytech/parity/pull/886)
|
||||
- Bumping clippy and fixing warnings [#889](https://github.com/paritytech/parity/pull/889)
|
||||
- More descriptive expectations to transaction queue consistency. [#878](https://github.com/paritytech/parity/pull/878)
|
||||
- uint bug - replace add with or [#879](https://github.com/paritytech/parity/pull/879)
|
||||
- Fixing typo in bigint [#877](https://github.com/paritytech/parity/pull/877)
|
||||
- update misleading cli help msg for author [#874](https://github.com/paritytech/parity/pull/874)
|
||||
- Find geth data store cross-platform. [#871](https://github.com/paritytech/parity/pull/871)
|
||||
- Import geth 1.4.0 keys [#872](https://github.com/paritytech/parity/pull/872)
|
||||
- Syntax helpers for IPC RPC (part 2) [#854](https://github.com/paritytech/parity/pull/854)
|
||||
- Fixed bootnode URL and error message [#870](https://github.com/paritytech/parity/pull/870)
|
||||
- replace popcnt with mov (861) [#867](https://github.com/paritytech/parity/pull/867)
|
||||
- weekly dependencies update [#865](https://github.com/paritytech/parity/pull/865)
|
||||
- Remove unused mut [#866](https://github.com/paritytech/parity/pull/866)
|
||||
- fixed #855 [#864](https://github.com/paritytech/parity/pull/864)
|
||||
- simplified trace from functions, removed clippy warnings [#862](https://github.com/paritytech/parity/pull/862)
|
||||
- Update deprecated HashDB methods in docs. [#857](https://github.com/paritytech/parity/pull/857)
|
||||
- refactored loading transaction json tests [#853](https://github.com/paritytech/parity/pull/853)
|
||||
- reorganised price info lookup [#852](https://github.com/paritytech/parity/pull/852)
|
||||
- Publish locally-made transactions to peers. [#850](https://github.com/paritytech/parity/pull/850)
|
||||
- Add generalbeck's token [#847](https://github.com/paritytech/parity/pull/847)
|
||||
- Fix response for mining. [#846](https://github.com/paritytech/parity/pull/846)
|
||||
- USD-based pricing of gas. [#843](https://github.com/paritytech/parity/pull/843)
|
||||
- Parity can accept older work packages [#811](https://github.com/paritytech/parity/pull/811)
|
||||
- Caching for computing seed hashes (#541) [#841](https://github.com/paritytech/parity/pull/841)
|
||||
- checking transaction queue for pending transaction [#838](https://github.com/paritytech/parity/pull/838)
|
||||
- refactored loading of state tests [#817](https://github.com/paritytech/parity/pull/817)
|
||||
- tests for deserialization of transaction from issue #835 [#837](https://github.com/paritytech/parity/pull/837)
|
||||
- unlocks with no expiration [on top of 833] [#834](https://github.com/paritytech/parity/pull/834)
|
||||
- Unlock accounts on CLI. [#833](https://github.com/paritytech/parity/pull/833)
|
||||
- Make BlockNumber optional, fix eth_call [#829](https://github.com/paritytech/parity/pull/829)
|
||||
- Test socket to common test code (ethcore-devtools) [#831](https://github.com/paritytech/parity/pull/831)
|
||||
- Use network id for the web3_net_version return. [#822](https://github.com/paritytech/parity/pull/822)
|
||||
- json-rpc web3_sha3 [#824](https://github.com/paritytech/parity/pull/824)
|
||||
- remove some unused files [#819](https://github.com/paritytech/parity/pull/819)
|
||||
- debug symbols for master/beta [#818](https://github.com/paritytech/parity/pull/818)
|
||||
- Syntax helpers for IPC RPC [#809](https://github.com/paritytech/parity/pull/809)
|
||||
- refactored loading of execution tests [#803](https://github.com/paritytech/parity/pull/803)
|
||||
- Rustfmt.toml [#805](https://github.com/paritytech/parity/pull/805)
|
||||
- install-partiy runs brew reinstall parity on osx [#810](https://github.com/paritytech/parity/pull/810)
|
||||
- Fix mining from spinning [#807](https://github.com/paritytech/parity/pull/807)
|
||||
@@ -1,376 +0,0 @@
|
||||
Note: Parity 1.2 reached End-of-Life on 2016-11-07 (EOL).
|
||||
|
||||
## Parity [v1.2.4](https://github.com/paritytech/parity/releases/tag/v1.2.4) (2016-08-09)
|
||||
|
||||
Parity 1.2.4 Is a maintenance release that fixes a [few](https://github.com/paritytech/parity/pull/1888/commits) issues related to mining and peer synchronization.
|
||||
This release is marked as stable.
|
||||
|
||||
- Backports for beta [#1888](https://github.com/paritytech/parity/pull/1888)
|
||||
- BETA: fixed trace_transaction crash when block contained suicide [#1782](https://github.com/paritytech/parity/pull/1782)
|
||||
|
||||
## Parity [v1.2.3](https://github.com/paritytech/parity/releases/tag/v1.2.3) (2016-07-31)
|
||||
|
||||
Parity 1.2.3 is a patch release that addresses network stability issues for both Ethereum HF and Ethereum classic chains and brings a few changes to the transaction tracing API.
|
||||
|
||||
#### Tracing API changes
|
||||
- Added tracing for `CALLCODE`, `DELEGATECALL` and `SUICIDE`
|
||||
- `trace_call` returns traces in flat format
|
||||
- Added 2 new methods: `trace_rawTransaction` and `trace_replayTransaction`
|
||||
|
||||
Note that to continue using tracing features in this version you need to re-sync the blockchain. This can be done by using `parity export $HOME/ethereum-chain-backup.rlp` , deleting the database usually located at `~/.parity/906a34e69aec8c0d` followed by `parity import $HOME/ethereum-chain-backup.rlp`.
|
||||
|
||||
- [beta] Updating UI [#1778](https://github.com/paritytech/parity/pull/1778)
|
||||
- tracing backport [#1770](https://github.com/paritytech/parity/pull/1770)
|
||||
- Backport commits to beta [#1763](https://github.com/paritytech/parity/pull/1763)
|
||||
- Deadlock on incoming connection (#1672) [#1675](https://github.com/paritytech/parity/pull/1675)
|
||||
- [BETA] Removed DAO soft fork traces [#1640](https://github.com/paritytech/parity/pull/1640)
|
||||
|
||||
|
||||
## Parity [v1.2.2](https://github.com/paritytech/parity/releases/tag/v1.2.2) (2016-07-16)
|
||||
|
||||
#### New
|
||||
- DAO hard-fork.
|
||||
|
||||
DAO hard-fork implementation conforms to the [specification](https://blog.slock.it/hard-fork-specification-24b889e70703) and is enabled by default.
|
||||
|
||||
#### Changed
|
||||
- `--reseal-on-txs` defaults to `own`.
|
||||
- DAO soft-fork support has been removed along with related command line options.
|
||||
|
||||
#### Resolved issues
|
||||
- `--db-cache-size` consuming too much memory.
|
||||
`eth_getWork` RPC response additionally includes the block number.
|
||||
- Skipping transactions with invalid nonces when pushing to block.
|
||||
- Update sealing just once when externally importing many blocks (#1541).
|
||||
- Transaction tracing skipping simple transactions (#1606).
|
||||
- Other small fixes and improvements.
|
||||
|
||||
Full changelog
|
||||
|
||||
- DAO hard-fork (#1483) [#1636](https://github.com/paritytech/parity/pull/1636)
|
||||
- Backports for beta [#1628](https://github.com/paritytech/parity/pull/1628)
|
||||
- don't batch best block for branches (#1623) [#1626](https://github.com/paritytech/parity/pull/1626)
|
||||
- Merge bugfixes from master to beta [#1605](https://github.com/paritytech/parity/pull/1605)
|
||||
- (BETA) using block options cache instead of general cache for rocksdb [#1613](https://github.com/paritytech/parity/pull/1613)
|
||||
- Backport sealing fixes to beta [#1583](https://github.com/paritytech/parity/pull/1583)
|
||||
- v1.2.2 in beta [#1581](https://github.com/paritytech/parity/pull/1581)
|
||||
- Skipping transactions with invalid nonces when pushing to block. (#1545) [#1547](https://github.com/paritytech/parity/pull/1547)
|
||||
|
||||
|
||||
## Parity [v1.2.1](https://github.com/paritytech/parity/releases/tag/v1.2.1) (2016-07-01)
|
||||
|
||||
#### New
|
||||
- Options for more precise mining tuning (see below).
|
||||
- Informative notification when block mined.
|
||||
- HTTP signal on new work-package.
|
||||
- Optimised database insertion for self-mined blocks.
|
||||
- Short-circuit for local transaction gas-price approval.
|
||||
- A number of issues related to mining have been fixed.
|
||||
|
||||
##### Mining options
|
||||
- `--author` is now required for mining.
|
||||
- `--reseal-on-txs` Specify which transactions should force the node to reseal a block. By default parity updates the seal on incoming transactions to reduce transaction latency. Set this option to `none` to force updates on new blocks only.
|
||||
- `--reseal-min-period` Can be used to control how often a new pending block is generated if `none` is not selected on prior option.
|
||||
- `--work-queue-size` Controls how many pending blocks to keep in memory.
|
||||
- `--relay-set` Can be used to enable more strict transaction verification.
|
||||
- `--remove-solved` Move solved blocks from the work package queue instead of cloning them. This gives a slightly faster import speed, but means that extra solutions submitted for the same work package will go unused.
|
||||
- `--notify-work` Accepts a list of URLs that will receive a POST request when new work package is available. The body of the POST message is JSON encoded and has the same format as `eth_getWork` RPC response.
|
||||
|
||||
##### RPC
|
||||
|
||||
`eth_getWork` RPC response additionally includes the block number.
|
||||
|
||||
##### DAO soft-fork
|
||||
|
||||
DAO soft-fork control options have been replaced by the single `--fork` option which disables the soft-fork by default.
|
||||
|
||||
#### Changes
|
||||
|
||||
- v1.2.1 in beta [#1492](https://github.com/paritytech/parity/pull/1492)
|
||||
- (BETA) add artifacts [#1420](https://github.com/paritytech/parity/pull/1420)
|
||||
|
||||
## Parity [v1.2.0: "Security"](https://github.com/paritytech/parity/releases/tag/v1.2.0) (2016-06-24)
|
||||
|
||||
[Blog post](https://blog.parity.io/announcing-parity-1-2/)
|
||||
|
||||
#### New
|
||||
|
||||
- Transaction signing UI.
|
||||
- IPC/RPC module.
|
||||
- Optimised mining support.
|
||||
- Windows build.
|
||||
- DAO soft-fork support.
|
||||
|
||||
##### Transaction signing UI
|
||||
|
||||
This is a new framework for signing transactions. It fulfills three requirements:
|
||||
- You should never have to type your passwords into a Dapp.
|
||||
- No Javascript code should ever hold a secret.
|
||||
- No transaction should ever be signed without the consent of the user.
|
||||
|
||||
The feature is enabled through the `--signer` flag. When enabled, the user must ensure at least one "Signer UI" is set-up for managing transaction confirmation. There are two such UIs available; one through a Google Chrome Extension, separately installable and the second through a special web page hosted locally. Set-up must be done once for each such UI, through copying and pasting a token from the output console of Parity into the UI. Specific instructions are given in the UI.
|
||||
|
||||
From this point on, no transaction may ever be signed by Parity except through one of these allowed Signer UIs, and no password should ever be entered anywhere else.
|
||||
|
||||
##### IPC/RPC module and Mist/Geth compatibility
|
||||
|
||||
Should be started with `--geth` to ensure Mist compatibility.
|
||||
|
||||
##### Optimised mining support
|
||||
|
||||
Numerous improvements and optimisations have been added to our mining implementation. A large "active queue" ensures that late-included transactions are included in the mined block without sacrificing older results from latent-reported `ethminer` results.
|
||||
|
||||
##### Windows build
|
||||
|
||||
We're happy to announce full Windows support with 1.2!
|
||||
|
||||
##### Soft-fork
|
||||
|
||||
This release includes support for the proposed [DAO soft-fork](https://docs.google.com/document/d/10RktunzjKNfp6Y8Cu4EhR5V9IqxEZq42LU126EYhWY4/pub). Upon upgrade, all mining nodes can vote for or against the soft fork (this is done through altering the block gas limit; a gas limit of at most 4M results in the soft-fork being triggered).
|
||||
|
||||
By default, nodes vote "for" the DAO soft-fork (and try to reduce the gas limit to 3.1M). To vote against the soft-fork (keeping it at 4.7M), run with `--dont-help-rescue-dao`. Not upgrading is not recommended; if the majority votes with a soft-fork, an upgrade will be necessary to mine on the correct chain.
|
||||
|
||||
#### Changed
|
||||
- Fast pruning method is now default for a fresh sync.
|
||||
- Web UI renamed to Dapps UI.
|
||||
- JSONRPC and Dapps UI enabled by default.
|
||||
- CLI options ending `-off` renamed to GNU-consistent prefix `--no-`.
|
||||
- Dynamic gas-pricing (data feed and statistical techniques used to determine optimum gas prices).
|
||||
|
||||
Full changes:
|
||||
|
||||
- Signer enabled by default for UI [#1417](https://github.com/paritytech/parity/pull/1417)
|
||||
- Remove experimental pruning options. [#1415](https://github.com/paritytech/parity/pull/1415)
|
||||
- Fixing interface and port for parity ui [#1414](https://github.com/paritytech/parity/pull/1414)
|
||||
- Configurable gas limit cap. [#1405](https://github.com/paritytech/parity/pull/1405)
|
||||
- Bumping TopBar, Minimal SignerUI and wallet [#1413](https://github.com/paritytech/parity/pull/1413)
|
||||
- Sync: Update highest block for progress reporting [#1411](https://github.com/paritytech/parity/pull/1411)
|
||||
- Tweaked CLI options for the release [#1407](https://github.com/paritytech/parity/pull/1407)
|
||||
- Further rocksdb tuning [#1409](https://github.com/paritytech/parity/pull/1409)
|
||||
- Fixing jit compilation [#1406](https://github.com/paritytech/parity/pull/1406)
|
||||
- Bump clippy [#1403](https://github.com/paritytech/parity/pull/1403)
|
||||
- Shortcut SF condition when canon known [#1401](https://github.com/paritytech/parity/pull/1401)
|
||||
- Additional assertions for internal state of queue [#1402](https://github.com/paritytech/parity/pull/1402)
|
||||
- Replace deprecated hashdb trait names [#1394](https://github.com/paritytech/parity/pull/1394)
|
||||
- rpc api by default for ipc [#1400](https://github.com/paritytech/parity/pull/1400)
|
||||
- Ensure judging the SF trigger by relative branch [#1399](https://github.com/paritytech/parity/pull/1399)
|
||||
- Signer with unlocked account working as expected. [#1398](https://github.com/paritytech/parity/pull/1398)
|
||||
- Make --signer default. [#1392](https://github.com/paritytech/parity/pull/1392)
|
||||
- Presale wallet [#1376](https://github.com/paritytech/parity/pull/1376)
|
||||
- Removing signer connection limit [#1396](https://github.com/paritytech/parity/pull/1396)
|
||||
- Optional gas price in transactions come from statistics [#1388](https://github.com/paritytech/parity/pull/1388)
|
||||
- Update README.md with cargo install [ci-skip] [#1389](https://github.com/paritytech/parity/pull/1389)
|
||||
- Fixing possible overflow during multiplication [#1381](https://github.com/paritytech/parity/pull/1381)
|
||||
- Update SF to latest spec [#1386](https://github.com/paritytech/parity/pull/1386)
|
||||
- Sync optimization [#1385](https://github.com/paritytech/parity/pull/1385)
|
||||
- Fixing order of if statements to avoid overflows. [#1384](https://github.com/paritytech/parity/pull/1384)
|
||||
- New topbar & signer UI [#1383](https://github.com/paritytech/parity/pull/1383)
|
||||
- Install trigger for DAO-rescue soft-fork. [#1329](https://github.com/paritytech/parity/pull/1329)
|
||||
- Rocksdb flush/compact limit [#1375](https://github.com/paritytech/parity/pull/1375)
|
||||
- CentOS Dockerfile [#1377](https://github.com/paritytech/parity/pull/1377)
|
||||
- RPC method to return number of unconfirmed transactions... [#1371](https://github.com/paritytech/parity/pull/1371)
|
||||
- bump jsonrpc-http-server [#1369](https://github.com/paritytech/parity/pull/1369)
|
||||
- Fix lock order when updating sealing [#1364](https://github.com/paritytech/parity/pull/1364)
|
||||
- Update sealing on new transactions [#1365](https://github.com/paritytech/parity/pull/1365)
|
||||
- Fixed panic on aborted connection [#1370](https://github.com/paritytech/parity/pull/1370)
|
||||
- importing presale wallet [#1368](https://github.com/paritytech/parity/pull/1368)
|
||||
- Set default database file size large enough [#1363](https://github.com/paritytech/parity/pull/1363)
|
||||
- Reserved peers rpc API [#1360](https://github.com/paritytech/parity/pull/1360)
|
||||
- Fixing replacing transaction with lower gas_price result. [#1343](https://github.com/paritytech/parity/pull/1343)
|
||||
- fixed migration of empty pruning dir [#1362](https://github.com/paritytech/parity/pull/1362)
|
||||
- Transaction processing queue [#1335](https://github.com/paritytech/parity/pull/1335)
|
||||
- Fixing last nonce values in case transaction is replaced [#1359](https://github.com/paritytech/parity/pull/1359)
|
||||
- docopt is an optional dependency of ethkey and ethstore [#1358](https://github.com/paritytech/parity/pull/1358)
|
||||
- Fixing clippy warnings [#1354](https://github.com/paritytech/parity/pull/1354)
|
||||
- Reduce locking when syncing [#1357](https://github.com/paritytech/parity/pull/1357)
|
||||
- removed unnecessary logs [#1356](https://github.com/paritytech/parity/pull/1356)
|
||||
- Updating parity-dapps [#1353](https://github.com/paritytech/parity/pull/1353)
|
||||
- moved keystore tests files from util to ethstore [#1352](https://github.com/paritytech/parity/pull/1352)
|
||||
- removed redundant bigint deps [#1351](https://github.com/paritytech/parity/pull/1351)
|
||||
- Reopen "reserved peers and reserved-only flag" [#1350](https://github.com/paritytech/parity/pull/1350)
|
||||
- Configurable rocksdb cache size [#1348](https://github.com/paritytech/parity/pull/1348)
|
||||
- Fixing future order and errors when reaching limit. [#1346](https://github.com/paritytech/parity/pull/1346)
|
||||
- Removing priority on local transactions [#1342](https://github.com/paritytech/parity/pull/1342)
|
||||
- Revert "Reserved peers, reserved-only flag" [#1349](https://github.com/paritytech/parity/pull/1349)
|
||||
- Sync attack defense: Deactivate peers on invalid block bodies [#1345](https://github.com/paritytech/parity/pull/1345)
|
||||
- Reserved peers, reserved-only flag [#1347](https://github.com/paritytech/parity/pull/1347)
|
||||
- CI for ethkey and ethstore [#1341](https://github.com/paritytech/parity/pull/1341)
|
||||
- Fixed empty block body composition [#1340](https://github.com/paritytech/parity/pull/1340)
|
||||
- Provide a signer UI token by default. [#1334](https://github.com/paritytech/parity/pull/1334)
|
||||
- docker uses rustup, fixes #1337 [#1344](https://github.com/paritytech/parity/pull/1344)
|
||||
- Fixed network service dispose [#1339](https://github.com/paritytech/parity/pull/1339)
|
||||
- Sync: Cache last sync round block parents [#1331](https://github.com/paritytech/parity/pull/1331)
|
||||
- secret store separated from util [#1304](https://github.com/paritytech/parity/pull/1304)
|
||||
- --geth prevent getTransactionReceipt from using pending. [#1325](https://github.com/paritytech/parity/pull/1325)
|
||||
- Fixing locks order in miner. [#1328](https://github.com/paritytech/parity/pull/1328)
|
||||
- Update default gas limit, rename field [#1324](https://github.com/paritytech/parity/pull/1324)
|
||||
- Use constants for DatabaseConfig [#1318](https://github.com/paritytech/parity/pull/1318)
|
||||
- Fixing clippy warnings [#1321](https://github.com/paritytech/parity/pull/1321)
|
||||
- Bumping topbar. Fixing ws server closing when suspending [#1312](https://github.com/paritytech/parity/pull/1312)
|
||||
- Syncing fix [#1320](https://github.com/paritytech/parity/pull/1320)
|
||||
- Filling-in optional fields of TransactionRequest... [#1305](https://github.com/paritytech/parity/pull/1305)
|
||||
- Removing MakerOTC and DAO dapps [#1319](https://github.com/paritytech/parity/pull/1319)
|
||||
- Disabling ethcore_set* APIs by default (+ Status page update) [#1315](https://github.com/paritytech/parity/pull/1315)
|
||||
- fixed #1180 [#1282](https://github.com/paritytech/parity/pull/1282)
|
||||
- Network start/stop [#1313](https://github.com/paritytech/parity/pull/1313)
|
||||
- Additional logging for own transactions in queue [#1311](https://github.com/paritytech/parity/pull/1311)
|
||||
- DAO Rescue soft fork [#1309](https://github.com/paritytech/parity/pull/1309)
|
||||
- Appveyor config for windows build+installer [#1302](https://github.com/paritytech/parity/pull/1302)
|
||||
- Key load avoid warning [#1303](https://github.com/paritytech/parity/pull/1303)
|
||||
- More meaningful errors when sending transaction [#1290](https://github.com/paritytech/parity/pull/1290)
|
||||
- Gas price statistics. [#1291](https://github.com/paritytech/parity/pull/1291)
|
||||
- Fix read-ahead bug. [#1298](https://github.com/paritytech/parity/pull/1298)
|
||||
- firewall rules for windows installer [#1297](https://github.com/paritytech/parity/pull/1297)
|
||||
- x64 program files path for installer [#1296](https://github.com/paritytech/parity/pull/1296)
|
||||
- Fixed loosing peers on incoming connections. [#1293](https://github.com/paritytech/parity/pull/1293)
|
||||
- fixed #1261, overflow when calculating work [#1283](https://github.com/paritytech/parity/pull/1283)
|
||||
- snappy and minor block compression [#1286](https://github.com/paritytech/parity/pull/1286)
|
||||
- clarify build instructions [#1287](https://github.com/paritytech/parity/pull/1287)
|
||||
- fixed #1255 [#1280](https://github.com/paritytech/parity/pull/1280)
|
||||
- bump rust-crypto [#1289](https://github.com/paritytech/parity/pull/1289)
|
||||
- Security audit issues fixed [#1279](https://github.com/paritytech/parity/pull/1279)
|
||||
- Fixing origin/host validation [#1273](https://github.com/paritytech/parity/pull/1273)
|
||||
- windows installer + parity start ui cli option [#1284](https://github.com/paritytech/parity/pull/1284)
|
||||
- ipc lib version bump [#1285](https://github.com/paritytech/parity/pull/1285)
|
||||
- Syncing improvements [#1274](https://github.com/paritytech/parity/pull/1274)
|
||||
- removed redundant if condition [#1270](https://github.com/paritytech/parity/pull/1270)
|
||||
- Naive chunk creation, snapshotting [#1263](https://github.com/paritytech/parity/pull/1263)
|
||||
- Fixing generating new token while another parity instance is running. [#1272](https://github.com/paritytech/parity/pull/1272)
|
||||
- README: rustup and windows instructions [#1266](https://github.com/paritytech/parity/pull/1266)
|
||||
- Windows build [#1253](https://github.com/paritytech/parity/pull/1253)
|
||||
- removed try_seal from MiningBlockChainClient [#1262](https://github.com/paritytech/parity/pull/1262)
|
||||
- simplified block opening [#1232](https://github.com/paritytech/parity/pull/1232)
|
||||
- Clippy bump [#1259](https://github.com/paritytech/parity/pull/1259)
|
||||
- Fixing uint ASM macros compilation [#1258](https://github.com/paritytech/parity/pull/1258)
|
||||
- Signer port returned from RPC + Topbar showing count of unconfirmed transactions. [#1252](https://github.com/paritytech/parity/pull/1252)
|
||||
- codegen - avoid unwraps leading to compilation crash [#1250](https://github.com/paritytech/parity/pull/1250)
|
||||
- Dapps bump [#1257](https://github.com/paritytech/parity/pull/1257)
|
||||
- Windows named pipes [#1254](https://github.com/paritytech/parity/pull/1254)
|
||||
- remove unsafety from util/hash.rs and util/bigint/uint.rs [#1236](https://github.com/paritytech/parity/pull/1236)
|
||||
- Fixing CORS settings for special values: * & null. [#1247](https://github.com/paritytech/parity/pull/1247)
|
||||
- JSONRPC test strings avoid using \ char [#1246](https://github.com/paritytech/parity/pull/1246)
|
||||
- Tests for JSON serialisation of statediff/vmtrace [#1241](https://github.com/paritytech/parity/pull/1241)
|
||||
- Bumping Dapps & TopBar to newest version. [#1245](https://github.com/paritytech/parity/pull/1245)
|
||||
- keys import [#1240](https://github.com/paritytech/parity/pull/1240)
|
||||
- Splitting RPC Apis into more fine-grained sets [#1234](https://github.com/paritytech/parity/pull/1234)
|
||||
- Refactor triedb constructors to error on invalid state root [#1230](https://github.com/paritytech/parity/pull/1230)
|
||||
- Signer RPC method to check if signer is enabled [#1238](https://github.com/paritytech/parity/pull/1238)
|
||||
- Fixing signer behaviour when confirming transaction with wrong password. [#1237](https://github.com/paritytech/parity/pull/1237)
|
||||
- SystemUIs authorization [#1233](https://github.com/paritytech/parity/pull/1233)
|
||||
- IPC path for tesetnet with --geth compatibility [#1231](https://github.com/paritytech/parity/pull/1231)
|
||||
- Transaction tracing for eth_call [#1210](https://github.com/paritytech/parity/pull/1210)
|
||||
- Removing compilation warnings [#1227](https://github.com/paritytech/parity/pull/1227)
|
||||
- Allowing connections only from chrome-extension and self-hosted client [#1226](https://github.com/paritytech/parity/pull/1226)
|
||||
- Clippy bump & fixing warnings [#1219](https://github.com/paritytech/parity/pull/1219)
|
||||
- Bumping serde & syntex [#1216](https://github.com/paritytech/parity/pull/1216)
|
||||
- Minimal Signer UI (System UI) exposed over websockets. [#1211](https://github.com/paritytech/parity/pull/1211)
|
||||
- Switch RPC namespace form ethcore_ to trace_ [#1208](https://github.com/paritytech/parity/pull/1208)
|
||||
- Verify the state root exists before creating a State [#1217](https://github.com/paritytech/parity/pull/1217)
|
||||
- Integrate state diffing into the ethcore JSONRPC [#1206](https://github.com/paritytech/parity/pull/1206)
|
||||
- Updating topbar to latest version [#1220](https://github.com/paritytech/parity/pull/1220)
|
||||
- Loading local Dapps from FS. [#1214](https://github.com/paritytech/parity/pull/1214)
|
||||
- Ipc serialization & protocol fixes [#1188](https://github.com/paritytech/parity/pull/1188)
|
||||
- Have Ext::ret take self by value [#1187](https://github.com/paritytech/parity/pull/1187)
|
||||
- Simple WebSockets notification about new request [#1202](https://github.com/paritytech/parity/pull/1202)
|
||||
- Removing leftovers of ethminer [#1207](https://github.com/paritytech/parity/pull/1207)
|
||||
- fixed #1204 [#1205](https://github.com/paritytech/parity/pull/1205)
|
||||
- VM tracing and JSON RPC endpoint for it. [#1169](https://github.com/paritytech/parity/pull/1169)
|
||||
- devtools helpers extended [#1186](https://github.com/paritytech/parity/pull/1186)
|
||||
- Networking refactoring [#1172](https://github.com/paritytech/parity/pull/1172)
|
||||
- Client & Miner refactoring [#1195](https://github.com/paritytech/parity/pull/1195)
|
||||
- update readme [#1201](https://github.com/paritytech/parity/pull/1201)
|
||||
- Simple signing queue, confirmation APIs exposed in signer WebSockets. [#1182](https://github.com/paritytech/parity/pull/1182)
|
||||
- Using ordered hashmap to keep the order of dapps on home screen [#1199](https://github.com/paritytech/parity/pull/1199)
|
||||
- Disabling `ethcore` by default, adding x-frame-options header to dapps. [#1197](https://github.com/paritytech/parity/pull/1197)
|
||||
- transaction count verifier tests [#1196](https://github.com/paritytech/parity/pull/1196)
|
||||
- expunge x! and xx! from the codebase [#1192](https://github.com/paritytech/parity/pull/1192)
|
||||
- Database service upgrade (from the ipc branch) [#1185](https://github.com/paritytech/parity/pull/1185)
|
||||
- stop eth_syncing from returning true forever [#1181](https://github.com/paritytech/parity/pull/1181)
|
||||
- Sync fixes and tweaks [#1164](https://github.com/paritytech/parity/pull/1164)
|
||||
- Exposing RPC over Signer WebSockets [#1167](https://github.com/paritytech/parity/pull/1167)
|
||||
- implement missing rpc methods and tests [#1171](https://github.com/paritytech/parity/pull/1171)
|
||||
- json ipc server version bump [#1170](https://github.com/paritytech/parity/pull/1170)
|
||||
- Updated dependencies for windows build [#1173](https://github.com/paritytech/parity/pull/1173)
|
||||
- Framework for improved RPC unit tests [#1141](https://github.com/paritytech/parity/pull/1141)
|
||||
- remove all possible unsafe code in crypto [#1168](https://github.com/paritytech/parity/pull/1168)
|
||||
- Base for Signer Websockets server [#1158](https://github.com/paritytech/parity/pull/1158)
|
||||
- Write queue to speed-up db ipc [#1160](https://github.com/paritytech/parity/pull/1160)
|
||||
- Fixing few clippy warnings [#1163](https://github.com/paritytech/parity/pull/1163)
|
||||
- Change eth_signAndSendTransaction to personal_SignAndSendTransaction [#1154](https://github.com/paritytech/parity/pull/1154)
|
||||
- Support "earliest" and specific block parameters in RPC where possible [#1149](https://github.com/paritytech/parity/pull/1149)
|
||||
- migration fixes [#1155](https://github.com/paritytech/parity/pull/1155)
|
||||
- Empty trusted signer crate with it's general purpose described. [#1150](https://github.com/paritytech/parity/pull/1150)
|
||||
- More bootnodes for morden. [#1153](https://github.com/paritytech/parity/pull/1153)
|
||||
- move existing rpc tests into mocked module [#1151](https://github.com/paritytech/parity/pull/1151)
|
||||
- Bloomchain [#1014](https://github.com/paritytech/parity/pull/1014)
|
||||
- Renaming dapps repos. Updating dapps [#1142](https://github.com/paritytech/parity/pull/1142)
|
||||
- fixed pending transactions [#1147](https://github.com/paritytech/parity/pull/1147)
|
||||
- Basic benches to provide metrics for ipc optimizations [#1145](https://github.com/paritytech/parity/pull/1145)
|
||||
- Fixing clippy warnings [#1148](https://github.com/paritytech/parity/pull/1148)
|
||||
- correct signature of SecTrieDB::raw_mut [#1143](https://github.com/paritytech/parity/pull/1143)
|
||||
- Merge to master and start hypervisor for import/export [#1138](https://github.com/paritytech/parity/pull/1138)
|
||||
- Bumping clippy. Fixing warnings [#1139](https://github.com/paritytech/parity/pull/1139)
|
||||
- Display progress when importing [#1136](https://github.com/paritytech/parity/pull/1136)
|
||||
- foundation of simple db migration [#1128](https://github.com/paritytech/parity/pull/1128)
|
||||
- Fixpending [#1074](https://github.com/paritytech/parity/pull/1074)
|
||||
- Sync: Propagate uncles and fix status reporting [#1134](https://github.com/paritytech/parity/pull/1134)
|
||||
- Coloured, padding logging. [#1133](https://github.com/paritytech/parity/pull/1133)
|
||||
- Importing [#1132](https://github.com/paritytech/parity/pull/1132)
|
||||
- Have `die_with_error` use `fmt::Display` rather than Debug [#1116](https://github.com/paritytech/parity/pull/1116)
|
||||
- Exporting [#1129](https://github.com/paritytech/parity/pull/1129)
|
||||
- Sign and send transaction [#1124](https://github.com/paritytech/parity/pull/1124)
|
||||
- Fixing unused imports warnings [#1125](https://github.com/paritytech/parity/pull/1125)
|
||||
- Adding info messages on mined blocks [#1127](https://github.com/paritytech/parity/pull/1127)
|
||||
- Fix styling - don't mix spaces with tabs!!! [#1123](https://github.com/paritytech/parity/pull/1123)
|
||||
- Fix is_syncing so it's false as long as the update is trivial. [#1122](https://github.com/paritytech/parity/pull/1122)
|
||||
- Relock unlocked accounts after first use [#1120](https://github.com/paritytech/parity/pull/1120)
|
||||
- Avoid importing keys into wrong place. [#1119](https://github.com/paritytech/parity/pull/1119)
|
||||
- Implement receipt's gasUsed field [#1118](https://github.com/paritytech/parity/pull/1118)
|
||||
- New dapps & query parameter handling [#1113](https://github.com/paritytech/parity/pull/1113)
|
||||
- pretty print trace error [#1098](https://github.com/paritytech/parity/pull/1098)
|
||||
- New syncing strategy [#1095](https://github.com/paritytech/parity/pull/1095)
|
||||
- ethcore-db crate [#1097](https://github.com/paritytech/parity/pull/1097)
|
||||
- Fix the default for pruning. [#1107](https://github.com/paritytech/parity/pull/1107)
|
||||
- Make Id/ID and db/Db/DB usage consistent [#1105](https://github.com/paritytech/parity/pull/1105)
|
||||
- Miner holds it's own copy of spec/engine [#1091](https://github.com/paritytech/parity/pull/1091)
|
||||
- Apps listing API & Home webapp. [#1101](https://github.com/paritytech/parity/pull/1101)
|
||||
- CLI option for using JITEVM [#1103](https://github.com/paritytech/parity/pull/1103)
|
||||
- Fix up the seal fields in RPC output [#1096](https://github.com/paritytech/parity/pull/1096)
|
||||
- Fixing some warnings [#1102](https://github.com/paritytech/parity/pull/1102)
|
||||
- fixed incorrect decoding of header seal_fields. added tests. #1090 [#1094](https://github.com/paritytech/parity/pull/1094)
|
||||
- Bumping Clippy [#1093](https://github.com/paritytech/parity/pull/1093)
|
||||
- Injectable topbar support. [#1092](https://github.com/paritytech/parity/pull/1092)
|
||||
- New syncing part 1: Block collection [#1088](https://github.com/paritytech/parity/pull/1088)
|
||||
- Moving all Client public API types to separate mod & binary serialization codegen for that mod [#1051](https://github.com/paritytech/parity/pull/1051)
|
||||
- Subdomains support in content server (webapps server). [#1082](https://github.com/paritytech/parity/pull/1082)
|
||||
- Fix uncle getter [#1087](https://github.com/paritytech/parity/pull/1087)
|
||||
- Provide fallback for usd-per-eth option when offline. [#1085](https://github.com/paritytech/parity/pull/1085)
|
||||
- path centralized [#1083](https://github.com/paritytech/parity/pull/1083)
|
||||
- Limiting result of the execution to execution-specific errors [#1071](https://github.com/paritytech/parity/pull/1071)
|
||||
- Configurable keys security [#1080](https://github.com/paritytech/parity/pull/1080)
|
||||
- comma delimeting multiple cors headers [#1078](https://github.com/paritytech/parity/pull/1078)
|
||||
- Update error message [#1081](https://github.com/paritytech/parity/pull/1081)
|
||||
- Updating dapp-wallet [#1076](https://github.com/paritytech/parity/pull/1076)
|
||||
- Fixed connecting to local nodes on startup [#1070](https://github.com/paritytech/parity/pull/1070)
|
||||
- Validate signature in Tx queue [#1068](https://github.com/paritytech/parity/pull/1068)
|
||||
- moving deps to ethcore/hyper and bumping jsonrpc-http-server version [#1067](https://github.com/paritytech/parity/pull/1067)
|
||||
- Updating status page. Bringing back wallet [#1064](https://github.com/paritytech/parity/pull/1064)
|
||||
- Fix --geth IPC for MacOS. [#1062](https://github.com/paritytech/parity/pull/1062)
|
||||
- Fixing formatter for defaultExtraData [#1060](https://github.com/paritytech/parity/pull/1060)
|
||||
- --geth IPC compatibility [#1059](https://github.com/paritytech/parity/pull/1059)
|
||||
- Moving dependencies to ethcore & uniforming syntax libs through all crates [#1050](https://github.com/paritytech/parity/pull/1050)
|
||||
- update hyper branch mio [#1054](https://github.com/paritytech/parity/pull/1054)
|
||||
- IPC lib update [#1047](https://github.com/paritytech/parity/pull/1047)
|
||||
- Updating hyper-mio revision [#1048](https://github.com/paritytech/parity/pull/1048)
|
||||
- Bump ipc-lib version [#1046](https://github.com/paritytech/parity/pull/1046)
|
||||
- Tidy up CLI options and make JSONRPC & webapps on by default. [#1045](https://github.com/paritytech/parity/pull/1045)
|
||||
- Fixing clippy warnings [#1044](https://github.com/paritytech/parity/pull/1044)
|
||||
- Fixing RPC modules compatibility [#1041](https://github.com/paritytech/parity/pull/1041)
|
||||
- Fixing hyper-mio revision [#1043](https://github.com/paritytech/parity/pull/1043)
|
||||
- Updating locations of webapp stuff [#1040](https://github.com/paritytech/parity/pull/1040)
|
||||
- JSON-RPC over IPC [#1039](https://github.com/paritytech/parity/pull/1039)
|
||||
- Update nix/mio for ARM [#1036](https://github.com/paritytech/parity/pull/1036)
|
||||
- Basic Authority [#991](https://github.com/paritytech/parity/pull/991)
|
||||
- Prioritizing of local transaction [#1023](https://github.com/paritytech/parity/pull/1023)
|
||||
- Version 1.2 [#1030](https://github.com/paritytech/parity/pull/1030)
|
||||
- Bumping status page [#1033](https://github.com/paritytech/parity/pull/1033)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user