UI server refactoring (#5580)

* Full API in Authenticated WS server.

* Replacing UI server with Hyper.

* Solving CLI, RPCs and tests.

* Porting signer tests.

* Fixing origin recognition for dapps/rpc.

* Fixing tests. Adding parity-rpc-client to test.

* Dapps exposed as RPC method.

* JS code to support new connection scheme.

* Fixing dapps tests.

* Updating allowed origins/hosts to support web3.site.

* Fixing tests, fixing UI.

* Fixing tests.

* Removing invalid tests.

* Fixing merge.

* 404 fallback for UI

* Improve ContentFetcher constructor readability.

* Naming.

* Update .gitlab-ci.yml

fix CI lint error

* Fixing tests and linting issues.

* Fixing new tests.

* UI hosts.

* Submodules fix.
This commit is contained in:
Tomasz Drwięga
2017-05-24 12:24:07 +02:00
committed by Arkadiy Paronyan
parent 7499efecf6
commit cbcc369a2d
91 changed files with 2171 additions and 2591 deletions

55
dapps/src/apps/app.rs Normal file
View File

@@ -0,0 +1,55 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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,
}
}
}

View File

@@ -55,6 +55,7 @@ pub struct ContentFetcher<F: Fetch = FetchClient, R: URLHint + 'static = URLHint
embeddable_on: Option<(String, u16)>,
remote: Remote,
fetch: F,
only_content: bool,
}
impl<R: URLHint + 'static, F: Fetch> Drop for ContentFetcher<F, R> {
@@ -66,7 +67,12 @@ impl<R: URLHint + 'static, F: Fetch> Drop for ContentFetcher<F, R> {
impl<R: URLHint + '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 {
pub fn new(
resolver: R,
sync_status: Arc<SyncStatus>,
remote: Remote,
fetch: F,
) -> Self {
let mut dapps_path = env::temp_dir();
dapps_path.push(random_filename());
@@ -75,12 +81,23 @@ impl<R: URLHint + 'static, F: Fetch> ContentFetcher<F, R> {
resolver: resolver,
sync: sync_status,
cache: Arc::new(Mutex::new(ContentCache::default())),
embeddable_on: embeddable_on,
embeddable_on: None,
remote: remote,
fetch: fetch,
only_content: true,
}
}
pub fn allow_dapps(mut self, dapps: bool) -> Self {
self.only_content = !dapps;
self
}
pub fn embeddable_on(mut self, embeddable_on: Option<(String, u16)>) -> Self {
self.embeddable_on = embeddable_on;
self
}
fn still_syncing(address: Option<(String, u16)>) -> Box<Handler> {
Box::new(ContentHandler::error(
StatusCode::ServiceUnavailable,
@@ -91,6 +108,16 @@ impl<R: URLHint + 'static, F: Fetch> ContentFetcher<F, R> {
))
}
fn dapps_disabled(address: Option<(String, u16)>) -> Box<Handler> {
Box::new(ContentHandler::error(
StatusCode::ServiceUnavailable,
"Network Dapps Not Available",
"This interface doesn't support network dapps for security reasons.",
None,
address,
))
}
#[cfg(test)]
fn set_status(&self, content_id: &str, status: ContentStatus) {
self.cache.lock().insert(content_id.to_owned(), status);
@@ -163,6 +190,9 @@ impl<R: URLHint + 'static, F: Fetch> Fetcher 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(URLHintResult::Dapp(dapp)) => {
let handler = ContentFetcherHandler::new(
dapp.url(),
@@ -254,7 +284,8 @@ mod tests {
fn should_true_if_contains_the_app() {
// given
let path = env::temp_dir();
let fetcher = ContentFetcher::new(FakeResolver, Arc::new(|| false), None, Remote::new_sync(), Client::new().unwrap());
let fetcher = ContentFetcher::new(FakeResolver, Arc::new(|| false), Remote::new_sync(), Client::new().unwrap())
.allow_dapps(true);
let handler = LocalPageEndpoint::new(path, EndpointInfo {
name: "fake".into(),
description: "".into(),

View File

@@ -14,12 +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 page::{LocalPageEndpoint, PageCache};
use endpoint::{Endpoints, EndpointInfo};
use endpoint::{Endpoint, EndpointInfo};
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest};
struct LocalDapp {
@@ -85,8 +86,8 @@ fn local_dapp(name: String, path: PathBuf) -> LocalDapp {
/// Returns endpoints for Local Dapps found for given filesystem path.
/// Scans the directory and collects `LocalPageEndpoints`.
pub fn local_endpoints<P: AsRef<Path>>(dapps_path: P, signer_address: Option<(String, u16)>) -> Endpoints {
let mut pages = Endpoints::new();
pub fn local_endpoints<P: AsRef<Path>>(dapps_path: P, signer_address: Option<(String, u16)>) -> BTreeMap<String, Box<Endpoint>> {
let mut pages = BTreeMap::<String, Box<Endpoint>>::new();
for dapp in local_dapps(dapps_path.as_ref()) {
pages.insert(
dapp.id,

View File

@@ -15,7 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use serde_json;
pub use api::App as Manifest;
pub use apps::App as Manifest;
pub const MANIFEST_FILENAME: &'static str = "manifest.json";

View File

@@ -14,8 +14,10 @@
// 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::path::PathBuf;
use std::sync::Arc;
use endpoint::{Endpoints, Endpoint};
use page::PageEndpoint;
use proxypac::ProxyPac;
@@ -23,17 +25,19 @@ use web::Web;
use fetch::Fetch;
use parity_dapps::WebApp;
use parity_reactor::Remote;
use parity_ui;
use {WebProxyTokens};
mod app;
mod cache;
mod fs;
mod ui;
pub mod fetcher;
pub mod manifest;
extern crate parity_ui;
pub use self::app::App;
pub const HOME_PAGE: &'static str = "parity";
pub const DAPPS_DOMAIN: &'static str = ".web3.site";
pub const HOME_PAGE: &'static str = "home";
pub const RPC_PATH: &'static str = "rpc";
pub const API_PATH: &'static str = "api";
pub const UTILS_PATH: &'static str = "parity-utils";
@@ -44,18 +48,27 @@ pub fn utils() -> Box<Endpoint> {
Box::new(PageEndpoint::with_prefix(parity_ui::App::default(), UTILS_PATH.to_owned()))
}
pub fn ui() -> Box<Endpoint> {
Box::new(PageEndpoint::with_fallback_to_index(parity_ui::App::default()))
}
pub fn ui_redirection(ui_address: Option<(String, u16)>) -> Box<Endpoint> {
Box::new(ui::Redirection::new(ui_address))
}
pub fn all_endpoints<F: Fetch>(
dapps_path: PathBuf,
extra_dapps: Vec<PathBuf>,
signer_address: Option<(String, u16)>,
dapps_domain: String,
ui_address: Option<(String, u16)>,
web_proxy_tokens: Arc<WebProxyTokens>,
remote: Remote,
fetch: F,
) -> Endpoints {
// fetch fs dapps at first to avoid overwriting builtins
let mut pages = fs::local_endpoints(dapps_path, signer_address.clone());
let mut pages = fs::local_endpoints(dapps_path, ui_address.clone());
for path in extra_dapps {
if let Some((id, endpoint)) = fs::local_endpoint(path.clone(), signer_address.clone()) {
if let Some((id, endpoint)) = fs::local_endpoint(path.clone(), ui_address.clone()) {
pages.insert(id, endpoint);
} else {
warn!(target: "dapps", "Ignoring invalid dapp at {}", path.display());
@@ -63,14 +76,14 @@ pub fn all_endpoints<F: Fetch>(
}
// NOTE [ToDr] Dapps will be currently embeded on 8180
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()));
insert::<parity_ui::App>(&mut pages, "ui", Embeddable::Yes(ui_address.clone()));
pages.insert("proxy".into(), ProxyPac::boxed(ui_address.clone(), dapps_domain));
pages.insert(WEB_PATH.into(), Web::boxed(ui_address.clone(), web_proxy_tokens.clone(), remote.clone(), fetch.clone()));
pages
Arc::new(pages)
}
fn insert<T : WebApp + Default + 'static>(pages: &mut Endpoints, id: &str, embed_at: Embeddable) {
fn insert<T : WebApp + Default + 'static>(pages: &mut BTreeMap<String, Box<Endpoint>>, 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()),

55
dapps/src/apps/ui.rs Normal file
View File

@@ -0,0 +1,55 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! UI redirections
use hyper::{Control, StatusCode};
use endpoint::{Endpoint, Handler, EndpointPath};
use {address, handlers};
/// Redirection to UI server.
pub struct Redirection {
signer_address: Option<(String, u16)>,
}
impl Redirection {
pub fn new(
signer_address: Option<(String, u16)>,
) -> Self {
Redirection {
signer_address: signer_address,
}
}
}
impl Endpoint for Redirection {
fn to_async_handler(&self, _path: EndpointPath, _control: Control) -> Box<Handler> {
if let Some(ref signer_address) = self.signer_address {
trace!(target: "dapps", "Redirecting to signer interface.");
handlers::Redirection::boxed(&format!("http://{}", address(signer_address)))
} else {
trace!(target: "dapps", "Signer disabled, returning 404.");
Box::new(handlers::ContentHandler::error(
StatusCode::NotFound,
"404 Not Found",
"Your homepage is not available when Trusted Signer is disabled.",
Some("You can still access dapps by writing a correct address, though. Re-enable Signer to get your homepage back."),
self.signer_address.clone(),
))
}
}
}