Dapps interface RPC (#3311)

* Dapps Interface RPC

* Adding JS apis

* Support for signer interface in proxypac and embeds

* Fixing tests

* fixing tests again
This commit is contained in:
Tomasz Drwięga 2016-11-09 19:41:47 +01:00 committed by Arkadiy Paronyan
parent eba0dd5023
commit 88c9cea04d
23 changed files with 173 additions and 117 deletions

View File

@ -47,7 +47,7 @@ pub struct ContentFetcher<R: URLHint = URLHintContract> {
resolver: R,
cache: Arc<Mutex<ContentCache>>,
sync: Arc<SyncStatus>,
embeddable_at: Option<u16>,
embeddable_on: Option<(String, u16)>,
}
impl<R: URLHint> Drop for ContentFetcher<R> {
@ -59,7 +59,7 @@ impl<R: URLHint> Drop for ContentFetcher<R> {
impl<R: URLHint> ContentFetcher<R> {
pub fn new(resolver: R, sync_status: Arc<SyncStatus>, embeddable_at: Option<u16>) -> Self {
pub fn new(resolver: R, sync_status: Arc<SyncStatus>, embeddable_on: Option<(String, u16)>) -> Self {
let mut dapps_path = env::temp_dir();
dapps_path.push(random_filename());
@ -68,17 +68,17 @@ impl<R: URLHint> ContentFetcher<R> {
resolver: resolver,
sync: sync_status,
cache: Arc::new(Mutex::new(ContentCache::default())),
embeddable_at: embeddable_at,
embeddable_on: embeddable_on,
}
}
fn still_syncing(port: Option<u16>) -> Box<Handler> {
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>"),
port,
address,
))
}
@ -145,7 +145,7 @@ impl<R: URLHint> ContentFetcher<R> {
match content {
// Don't serve dapps if we are still syncing (but serve content)
Some(URLHintResult::Dapp(_)) if self.sync.is_major_importing() => {
(None, Self::still_syncing(self.embeddable_at))
(None, Self::still_syncing(self.embeddable_on.clone()))
},
Some(URLHintResult::Dapp(dapp)) => {
let (handler, fetch_control) = ContentFetcherHandler::new(
@ -155,9 +155,9 @@ impl<R: URLHint> ContentFetcher<R> {
id: content_id.clone(),
dapps_path: self.dapps_path.clone(),
on_done: Box::new(on_done),
embeddable_at: self.embeddable_at,
embeddable_on: self.embeddable_on.clone(),
},
self.embeddable_at,
self.embeddable_on.clone(),
);
(Some(ContentStatus::Fetching(fetch_control)), Box::new(handler) as Box<Handler>)
@ -172,13 +172,13 @@ impl<R: URLHint> ContentFetcher<R> {
content_path: self.dapps_path.clone(),
on_done: Box::new(on_done),
},
self.embeddable_at,
self.embeddable_on.clone(),
);
(Some(ContentStatus::Fetching(fetch_control)), Box::new(handler) as Box<Handler>)
},
None if self.sync.is_major_importing() => {
(None, Self::still_syncing(self.embeddable_at))
(None, Self::still_syncing(self.embeddable_on.clone()))
},
None => {
// This may happen when sync status changes in between
@ -188,7 +188,7 @@ impl<R: URLHint> ContentFetcher<R> {
"Resource Not Found",
"Requested resource was not found.",
None,
self.embeddable_at,
self.embeddable_on.clone(),
)) as Box<Handler>)
},
}
@ -293,7 +293,7 @@ struct DappInstaller {
id: String,
dapps_path: PathBuf,
on_done: Box<Fn(String, Option<LocalPageEndpoint>) + Send>,
embeddable_at: Option<u16>,
embeddable_on: Option<(String, u16)>,
}
impl DappInstaller {
@ -386,7 +386,7 @@ impl ContentValidator for DappInstaller {
try!(manifest_file.write_all(manifest_str.as_bytes()));
// Create endpoint
let app = LocalPageEndpoint::new(target, manifest.clone().into(), PageCache::Enabled, self.embeddable_at);
let app = LocalPageEndpoint::new(target, manifest.clone().into(), PageCache::Enabled, self.embeddable_on.clone());
// Return modified app manifest
Ok((manifest.id.clone(), app))

View File

@ -97,12 +97,12 @@ fn read_manifest(name: &str, mut path: PathBuf) -> EndpointInfo {
})
}
pub fn local_endpoints(dapps_path: String, signer_port: Option<u16>) -> Endpoints {
pub fn local_endpoints(dapps_path: String, signer_address: Option<(String, u16)>) -> Endpoints {
let mut pages = Endpoints::new();
for dapp in local_dapps(dapps_path) {
pages.insert(
dapp.id,
Box::new(LocalPageEndpoint::new(dapp.path, dapp.info, PageCache::Disabled, signer_port))
Box::new(LocalPageEndpoint::new(dapp.path, dapp.info, PageCache::Disabled, signer_address.clone()))
);
}
pages

View File

@ -37,26 +37,26 @@ pub fn utils() -> Box<Endpoint> {
Box::new(PageEndpoint::with_prefix(parity_ui::App::default(), UTILS_PATH.to_owned()))
}
pub fn all_endpoints(dapps_path: String, signer_port: Option<u16>) -> Endpoints {
pub fn all_endpoints(dapps_path: String, signer_address: Option<(String, u16)>) -> Endpoints {
// fetch fs dapps at first to avoid overwriting builtins
let mut pages = fs::local_endpoints(dapps_path, signer_port);
let mut pages = fs::local_endpoints(dapps_path, signer_address.clone());
// NOTE [ToDr] Dapps will be currently embeded on 8180
insert::<parity_ui::App>(&mut pages, "ui", Embeddable::Yes(signer_port));
pages.insert("proxy".into(), ProxyPac::boxed(signer_port));
insert::<parity_ui::App>(&mut pages, "ui", Embeddable::Yes(signer_address.clone()));
pages.insert("proxy".into(), ProxyPac::boxed(signer_address));
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(port) => PageEndpoint::new_safe_to_embed(T::default(), port),
Embeddable::Yes(address) => PageEndpoint::new_safe_to_embed(T::default(), address),
Embeddable::No => PageEndpoint::new(T::default()),
}));
}
enum Embeddable {
Yes(Option<u16>),
Yes(Option<(String, u16)>),
#[allow(dead_code)]
No,
}

View File

@ -32,7 +32,7 @@ pub struct ContentHandler {
content: String,
mimetype: Mime,
write_pos: usize,
safe_to_embed_at_port: Option<u16>,
safe_to_embed_on: Option<(String, u16)>,
}
impl ContentHandler {
@ -44,31 +44,31 @@ impl ContentHandler {
Self::new(StatusCode::NotFound, content, mimetype)
}
pub fn html(code: StatusCode, content: String, embeddable_at: Option<u16>) -> Self {
Self::new_embeddable(code, content, mime!(Text/Html), embeddable_at)
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_at: Option<u16>) -> Self {
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,
message=message,
details=details.unwrap_or_else(|| ""),
version=version(),
), embeddable_at)
), embeddable_on)
}
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, embeddable_at: Option<u16>) -> Self {
pub fn new_embeddable(code: StatusCode, content: String, mimetype: Mime, embeddable_on: Option<(String, u16)>) -> Self {
ContentHandler {
code: code,
content: content,
mimetype: mimetype,
write_pos: 0,
safe_to_embed_at_port: embeddable_at,
safe_to_embed_on: embeddable_on,
}
}
}
@ -85,7 +85,7 @@ impl server::Handler<HttpStream> for ContentHandler {
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_at_port.clone());
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.clone());
Next::write()
}

View File

@ -138,7 +138,7 @@ pub struct ContentFetcherHandler<H: ContentValidator> {
client: Option<Client>,
installer: H,
request_url: Option<Url>,
embeddable_at: Option<u16>,
embeddable_on: Option<(String, u16)>,
}
impl<H: ContentValidator> Drop for ContentFetcherHandler<H> {
@ -157,7 +157,7 @@ impl<H: ContentValidator> ContentFetcherHandler<H> {
url: String,
control: Control,
handler: H,
embeddable_at: Option<u16>,
embeddable_on: Option<(String, u16)>,
) -> (Self, Arc<FetchControl>) {
let fetch_control = Arc::new(FetchControl::default());
@ -169,7 +169,7 @@ impl<H: ContentValidator> ContentFetcherHandler<H> {
status: FetchState::NotStarted(url),
installer: handler,
request_url: None,
embeddable_at: embeddable_at,
embeddable_on: embeddable_on,
};
(handler, fetch_control)
@ -208,7 +208,7 @@ impl<H: ContentValidator> server::Handler<HttpStream> for ContentFetcherHandler<
"Unable To Start Dapp Download",
"Could not initialize download of the dapp. It might be a problem with the remote server.",
Some(&format!("{}", e)),
self.embeddable_at,
self.embeddable_on.clone(),
)),
}
},
@ -218,7 +218,7 @@ impl<H: ContentValidator> server::Handler<HttpStream> for ContentFetcherHandler<
"Method Not Allowed",
"Only <code>GET</code> requests are allowed.",
None,
self.embeddable_at,
self.embeddable_on.clone(),
)),
})
} else { None };
@ -241,7 +241,7 @@ impl<H: ContentValidator> server::Handler<HttpStream> for ContentFetcherHandler<
"Download Timeout",
&format!("Could not fetch content within {} seconds.", FETCH_TIMEOUT),
None,
self.embeddable_at,
self.embeddable_on.clone(),
);
Self::close_client(&mut self.client);
(Some(FetchState::Error(timeout)), Next::write())
@ -263,7 +263,7 @@ impl<H: ContentValidator> server::Handler<HttpStream> for ContentFetcherHandler<
"Invalid Dapp",
"Downloaded bundle does not contain a valid content.",
Some(&format!("{:?}", e)),
self.embeddable_at,
self.embeddable_on.clone(),
))
},
Ok((id, result)) => {
@ -284,7 +284,7 @@ impl<H: ContentValidator> server::Handler<HttpStream> for ContentFetcherHandler<
"Download Error",
"There was an error when fetching the content.",
Some(&format!("{:?}", e)),
self.embeddable_at,
self.embeddable_on.clone(),
);
(Some(FetchState::Error(error)), Next::write())
},

View File

@ -30,18 +30,18 @@ pub use self::fetch::{ContentFetcherHandler, ContentValidator, FetchControl};
use url::Url;
use hyper::{server, header, net, uri};
use signer_address;
use address;
/// Adds security-related headers to the Response.
pub fn add_security_headers(headers: &mut header::Headers, embeddable_at: Option<u16>) {
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 Some(port) = embeddable_at {
if let Some(embeddable_on) = embeddable_on {
headers.set_raw(
"X-Frame-Options",
vec![format!("ALLOW-FROM http://{}", signer_address(port)).into_bytes()]
vec![format!("ALLOW-FROM http://{}", address(embeddable_on)).into_bytes()]
);
} else {
// TODO [ToDr] Should we be more strict here (DENY?)?

View File

@ -112,7 +112,7 @@ pub struct ServerBuilder {
handler: Arc<IoHandler>,
registrar: Arc<ContractClient>,
sync_status: Arc<SyncStatus>,
signer_port: Option<u16>,
signer_address: Option<(String, u16)>,
}
impl Extendable for ServerBuilder {
@ -129,7 +129,7 @@ impl ServerBuilder {
handler: Arc::new(IoHandler::new()),
registrar: registrar,
sync_status: Arc::new(|| false),
signer_port: None,
signer_address: None,
}
}
@ -139,8 +139,8 @@ impl ServerBuilder {
}
/// Change default signer port.
pub fn with_signer_port(&mut self, signer_port: Option<u16>) {
self.signer_port = signer_port;
pub fn with_signer_address(&mut self, signer_address: Option<(String, u16)>) {
self.signer_address = signer_address;
}
/// Asynchronously start server with no authentication,
@ -152,7 +152,7 @@ impl ServerBuilder {
NoAuth,
self.handler.clone(),
self.dapps_path.clone(),
self.signer_port.clone(),
self.signer_address.clone(),
self.registrar.clone(),
self.sync_status.clone(),
)
@ -167,7 +167,7 @@ impl ServerBuilder {
HttpBasicAuth::single_user(username, password),
self.handler.clone(),
self.dapps_path.clone(),
self.signer_port.clone(),
self.signer_address.clone(),
self.registrar.clone(),
self.sync_status.clone(),
)
@ -197,11 +197,11 @@ impl Server {
}
/// Returns a list of CORS domains for API endpoint.
fn cors_domains(signer_port: Option<u16>) -> Vec<String> {
match signer_port {
Some(port) => vec![
fn cors_domains(signer_address: Option<(String, u16)>) -> Vec<String> {
match signer_address {
Some(signer_address) => vec![
format!("http://{}{}", HOME_PAGE, DAPPS_DOMAIN),
format!("http://{}", signer_address(port)),
format!("http://{}", address(signer_address)),
],
None => vec![],
}
@ -213,15 +213,15 @@ impl Server {
authorization: A,
handler: Arc<IoHandler>,
dapps_path: String,
signer_port: Option<u16>,
signer_address: Option<(String, u16)>,
registrar: Arc<ContractClient>,
sync_status: Arc<SyncStatus>,
) -> 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(apps::urlhint::URLHintContract::new(registrar), sync_status, signer_port));
let endpoints = Arc::new(apps::all_endpoints(dapps_path, signer_port.clone()));
let cors_domains = Self::cors_domains(signer_port);
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(apps::urlhint::URLHintContract::new(registrar), sync_status, signer_address.clone()));
let endpoints = Arc::new(apps::all_endpoints(dapps_path, signer_address.clone()));
let cors_domains = Self::cors_domains(signer_address.clone());
let special = Arc::new({
let mut special = HashMap::new();
@ -238,7 +238,7 @@ impl Server {
try!(hyper::Server::http(addr))
.handle(move |ctrl| router::Router::new(
ctrl,
signer_port.clone(),
signer_address.clone(),
content_fetcher.clone(),
endpoints.clone(),
special.clone(),
@ -302,8 +302,8 @@ pub fn random_filename() -> String {
rng.gen_ascii_chars().take(12).collect()
}
fn signer_address(port: u16) -> String {
format!("127.0.0.1:{}", port)
fn address(address: (String, u16)) -> String {
format!("{}:{}", address.0, address.1)
}
#[cfg(test)]
@ -332,7 +332,7 @@ mod util_tests {
// when
let none = Server::cors_domains(None);
let some = Server::cors_domains(Some(18180));
let some = Server::cors_domains(Some(("127.0.0.1".into(), 18180)));
// then
assert_eq!(none, Vec::<String>::new());

View File

@ -25,7 +25,7 @@ pub struct PageEndpoint<T : WebApp + 'static> {
/// 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_at_port: Option<u16>,
safe_to_embed_on: Option<(String, u16)>,
info: EndpointInfo,
}
@ -36,7 +36,7 @@ impl<T: WebApp + 'static> PageEndpoint<T> {
PageEndpoint {
app: Arc::new(app),
prefix: None,
safe_to_embed_at_port: None,
safe_to_embed_on: None,
info: EndpointInfo::from(info),
}
}
@ -49,7 +49,7 @@ impl<T: WebApp + 'static> PageEndpoint<T> {
PageEndpoint {
app: Arc::new(app),
prefix: Some(prefix),
safe_to_embed_at_port: None,
safe_to_embed_on: None,
info: EndpointInfo::from(info),
}
}
@ -57,12 +57,12 @@ impl<T: WebApp + 'static> PageEndpoint<T> {
/// 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(app: T, port: Option<u16>) -> Self {
pub fn new_safe_to_embed(app: T, address: Option<(String, u16)>) -> Self {
let info = app.info();
PageEndpoint {
app: Arc::new(app),
prefix: None,
safe_to_embed_at_port: port,
safe_to_embed_on: address,
info: EndpointInfo::from(info),
}
}
@ -79,9 +79,9 @@ impl<T: WebApp> Endpoint for PageEndpoint<T> {
app: BuiltinDapp::new(self.app.clone()),
prefix: self.prefix.clone(),
path: path,
file: handler::ServedFile::new(self.safe_to_embed_at_port.clone()),
file: handler::ServedFile::new(self.safe_to_embed_on.clone()),
cache: PageCache::Disabled,
safe_to_embed_at_port: self.safe_to_embed_at_port.clone(),
safe_to_embed_on: self.safe_to_embed_on.clone(),
})
}
}

View File

@ -60,13 +60,13 @@ pub enum ServedFile<T: Dapp> {
}
impl<T: Dapp> ServedFile<T> {
pub fn new(embeddable_at: Option<u16>) -> Self {
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_at,
embeddable_on,
))
}
}
@ -97,7 +97,7 @@ pub struct PageHandler<T: Dapp> {
/// Requested path.
pub path: EndpointPath,
/// Flag indicating if the file can be safely embeded (put in iframe).
pub safe_to_embed_at_port: Option<u16>,
pub safe_to_embed_on: Option<(String, u16)>,
/// Cache settings for this page.
pub cache: PageCache,
}
@ -133,7 +133,7 @@ impl<T: Dapp> server::Handler<HttpStream> for PageHandler<T> {
self.app.file(&self.extract_path(url.path()))
},
_ => None,
}.map_or_else(|| ServedFile::new(self.safe_to_embed_at_port.clone()), |f| ServedFile::File(f));
}.map_or_else(|| ServedFile::new(self.safe_to_embed_on.clone()), |f| ServedFile::File(f));
Next::write()
}
@ -162,7 +162,7 @@ impl<T: Dapp> server::Handler<HttpStream> for PageHandler<T> {
}
// Security headers:
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_at_port);
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.clone());
Next::write()
},
ServedFile::Error(ref mut handler) => {
@ -246,7 +246,7 @@ fn should_extract_path_with_appid() {
},
file: ServedFile::new(None),
cache: Default::default(),
safe_to_embed_at_port: None,
safe_to_embed_on: None,
};
// when

View File

@ -27,17 +27,17 @@ pub struct LocalPageEndpoint {
mime: Option<String>,
info: Option<EndpointInfo>,
cache: PageCache,
embeddable_at: Option<u16>,
embeddable_on: Option<(String, u16)>,
}
impl LocalPageEndpoint {
pub fn new(path: PathBuf, info: EndpointInfo, cache: PageCache, embeddable_at: Option<u16>) -> Self {
pub fn new(path: PathBuf, info: EndpointInfo, cache: PageCache, embeddable_on: Option<(String, u16)>) -> Self {
LocalPageEndpoint {
path: path,
mime: None,
info: Some(info),
cache: cache,
embeddable_at: embeddable_at,
embeddable_on: embeddable_on,
}
}
@ -47,7 +47,7 @@ impl LocalPageEndpoint {
mime: Some(mime),
info: None,
cache: cache,
embeddable_at: None,
embeddable_on: None,
}
}
@ -68,7 +68,7 @@ impl Endpoint for LocalPageEndpoint {
prefix: None,
path: path,
file: handler::ServedFile::new(None),
safe_to_embed_at_port: self.embeddable_at,
safe_to_embed_on: self.embeddable_on.clone(),
cache: self.cache,
})
} else {
@ -77,7 +77,7 @@ impl Endpoint for LocalPageEndpoint {
prefix: None,
path: path,
file: handler::ServedFile::new(None),
safe_to_embed_at_port: self.embeddable_at,
safe_to_embed_on: self.embeddable_on.clone(),
cache: self.cache,
})
}

View File

@ -19,24 +19,24 @@
use endpoint::{Endpoint, Handler, EndpointPath};
use handlers::ContentHandler;
use apps::{HOME_PAGE, DAPPS_DOMAIN};
use signer_address;
use address;
pub struct ProxyPac {
signer_port: Option<u16>,
signer_address: Option<(String, u16)>,
}
impl ProxyPac {
pub fn boxed(signer_port: Option<u16>) -> Box<Endpoint> {
pub fn boxed(signer_address: Option<(String, u16)>) -> Box<Endpoint> {
Box::new(ProxyPac {
signer_port: signer_port
signer_address: signer_address
})
}
}
impl Endpoint for ProxyPac {
fn to_handler(&self, path: EndpointPath) -> Box<Handler> {
let signer = self.signer_port
.map(signer_address)
let signer = self.signer_address.clone()
.map(address)
.unwrap_or_else(|| format!("{}:{}", path.host, path.port));
let content = format!(

View File

@ -20,7 +20,7 @@
pub mod auth;
mod host_validation;
use signer_address;
use address;
use std::sync::Arc;
use std::collections::HashMap;
use url::{Url, Host};
@ -43,7 +43,7 @@ pub enum SpecialEndpoint {
pub struct Router<A: Authorization + 'static> {
control: Option<Control>,
signer_port: Option<u16>,
signer_address: Option<(String, u16)>,
endpoints: Arc<Endpoints>,
fetch: Arc<ContentFetcher>,
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
@ -117,14 +117,14 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
"404 Not Found",
"Requested content was not found.",
None,
self.signer_port,
self.signer_address.clone(),
))
},
// Redirect any other GET request to signer.
_ if *req.method() == hyper::Method::Get => {
if let Some(port) = self.signer_port {
if let Some(signer_address) = self.signer_address.clone() {
trace!(target: "dapps", "Redirecting to signer interface.");
Redirection::boxed(&format!("http://{}", signer_address(port)))
Redirection::boxed(&format!("http://{}", address(signer_address)))
} else {
trace!(target: "dapps", "Signer disabled, returning 404.");
Box::new(ContentHandler::error(
@ -132,7 +132,7 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
"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-enabled Signer to get your homepage back."),
self.signer_port,
self.signer_address.clone(),
))
}
},
@ -168,7 +168,7 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
impl<A: Authorization> Router<A> {
pub fn new(
control: Control,
signer_port: Option<u16>,
signer_address: Option<(String, u16)>,
content_fetcher: Arc<ContentFetcher>,
endpoints: Arc<Endpoints>,
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
@ -181,7 +181,7 @@ impl<A: Authorization> Router<A> {
.to_handler(EndpointPath::default());
Router {
control: Some(control),
signer_port: signer_port,
signer_address: signer_address,
endpoints: endpoints,
fetch: content_fetcher,
special: special,

View File

@ -76,7 +76,7 @@ pub fn init_server(hosts: Option<Vec<String>>, is_syncing: bool) -> (Server, Arc
dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading");
let mut builder = ServerBuilder::new(dapps_path.to_str().unwrap().into(), registrar.clone());
builder.with_sync_status(Arc::new(move || is_syncing));
builder.with_signer_port(Some(SIGNER_PORT));
builder.with_signer_address(Some(("127.0.0.1".into(), SIGNER_PORT)));
(
builder.start_unsecured_http(&"127.0.0.1:0".parse().unwrap(), hosts).unwrap(),
registrar,
@ -89,7 +89,7 @@ pub fn serve_with_auth(user: &str, pass: &str) -> Server {
let mut dapps_path = env::temp_dir();
dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading");
let mut builder = ServerBuilder::new(dapps_path.to_str().unwrap().into(), registrar);
builder.with_signer_port(Some(SIGNER_PORT));
builder.with_signer_address(Some(("127.0.0.1".into(), SIGNER_PORT)));
builder.start_basic_auth_http(&"127.0.0.1:0".parse().unwrap(), None, user, pass).unwrap()
}

View File

@ -60,6 +60,11 @@ export default class Parity {
.then(outNumber);
}
dappsInterface () {
return this._transport
.execute('parity_dappsInterface');
}
defaultExtraData () {
return this._transport
.execute('parity_defaultExtraData');

View File

@ -109,6 +109,15 @@ export default {
}
},
dappsInterface: {
desc: 'Returns the interface the dapps are running on, error if not enabled',
params: [],
returns: {
type: String,
desc: 'The interface'
}
},
defaultExtraData: {
desc: 'Returns the default extra data',
params: [],

View File

@ -95,7 +95,7 @@ impl Configuration {
let wal = !self.args.flag_fast_and_loose;
let warp_sync = self.args.flag_warp;
let geth_compatibility = self.args.flag_geth;
let ui_port = self.ui_port();
let ui_address = self.ui_port().map(|port| (self.ui_interface(), port));
let dapps_conf = self.dapps_config();
let signer_conf = self.signer_config();
let format = try!(self.format());
@ -243,7 +243,7 @@ impl Configuration {
vm_type: vm_type,
warp_sync: warp_sync,
geth_compatibility: geth_compatibility,
ui_port: ui_port,
ui_address: ui_address,
net_settings: self.network_settings(),
dapps_conf: dapps_conf,
signer_conf: signer_conf,
@ -859,7 +859,7 @@ mod tests {
wal: true,
vm_type: Default::default(),
geth_compatibility: false,
ui_port: Some(8180),
ui_address: Some(("127.0.0.1".into(), 8180)),
net_settings: Default::default(),
dapps_conf: Default::default(),
signer_conf: Default::default(),

View File

@ -58,7 +58,7 @@ pub fn new(configuration: Configuration, deps: Dependencies) -> Result<Option<We
return Ok(None);
}
let signer_port = deps.apis.signer_service.port();
let signer_address = deps.apis.signer_service.address();
let url = format!("{}:{}", configuration.interface, configuration.port);
let addr = try!(url.parse().map_err(|_| format!("Invalid Webapps listen host/port given: {}", url)));
@ -73,7 +73,7 @@ pub fn new(configuration: Configuration, deps: Dependencies) -> Result<Option<We
(username.to_owned(), password)
});
Ok(Some(try!(setup_dapps_server(deps, configuration.dapps_path, &addr, configuration.hosts, auth, signer_port))))
Ok(Some(try!(setup_dapps_server(deps, configuration.dapps_path, &addr, configuration.hosts, auth, signer_address))))
}
pub use self::server::WebappServer;
@ -91,7 +91,7 @@ mod server {
_url: &SocketAddr,
_allowed_hosts: Option<Vec<String>>,
_auth: Option<(String, String)>,
_signer_port: Option<u16>,
_signer_address: Option<(String, u16)>,
) -> Result<WebappServer, String> {
Err("Your Parity version has been compiled without WebApps support.".into())
}
@ -120,7 +120,7 @@ mod server {
url: &SocketAddr,
allowed_hosts: Option<Vec<String>>,
auth: Option<(String, String)>,
signer_port: Option<u16>,
signer_address: Option<(String, u16)>,
) -> Result<WebappServer, String> {
use ethcore_dapps as dapps;
@ -131,7 +131,7 @@ mod server {
let sync = deps.sync.clone();
let client = deps.client.clone();
server.with_sync_status(Arc::new(move || is_major_importing(Some(sync.status().state), client.queue_info())));
server.with_signer_port(signer_port);
server.with_signer_address(signer_address);
let server = rpc_apis::setup_rpc(server, deps.apis.clone(), rpc_apis::ApiSet::UnsafeContext);
let start_result = match auth {

View File

@ -119,6 +119,7 @@ pub struct Dependencies {
pub settings: Arc<NetworkSettings>,
pub net_service: Arc<ManageNetwork>,
pub geth_compatibility: bool,
pub dapps_interface: Option<String>,
pub dapps_port: Option<u16>,
}
@ -228,6 +229,7 @@ pub fn setup_rpc<T: Extendable>(server: T, deps: Arc<Dependencies>, apis: ApiSet
deps.logger.clone(),
deps.settings.clone(),
signer,
deps.dapps_interface.clone(),
deps.dapps_port,
).to_delegate());

View File

@ -82,7 +82,7 @@ pub struct RunCmd {
pub wal: bool,
pub vm_type: VMType,
pub geth_compatibility: bool,
pub ui_port: Option<u16>,
pub ui_address: Option<(String, u16)>,
pub net_settings: NetworkSettings,
pub dapps_conf: dapps::Configuration,
pub signer_conf: signer::Configuration,
@ -262,7 +262,7 @@ pub fn execute(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<(), String> {
let deps_for_rpc_apis = Arc::new(rpc_apis::Dependencies {
signer_service: Arc::new(rpc_apis::SignerService::new(move || {
signer::generate_new_token(signer_path.clone()).map_err(|e| format!("{:?}", e))
}, cmd.ui_port)),
}, cmd.ui_address)),
snapshot: snapshot_service.clone(),
client: client.clone(),
sync: sync_provider.clone(),
@ -274,6 +274,10 @@ pub fn execute(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<(), String> {
settings: Arc::new(cmd.net_settings.clone()),
net_service: manage_network.clone(),
geth_compatibility: cmd.geth_compatibility,
dapps_interface: match cmd.dapps_conf.enabled {
true => Some(cmd.dapps_conf.interface.clone()),
false => None,
},
dapps_port: match cmd.dapps_conf.enabled {
true => Some(cmd.dapps_conf.port),
false => None,

View File

@ -22,18 +22,18 @@ use v1::helpers::signing_queue::{ConfirmationsQueue};
pub struct SignerService {
queue: Arc<ConfirmationsQueue>,
generate_new_token: Box<Fn() -> Result<String, String> + Send + Sync + 'static>,
port: Option<u16>,
address: Option<(String, u16)>,
}
impl SignerService {
/// Creates new Signer Service given function to generate new tokens.
pub fn new<F>(new_token: F, port: Option<u16>) -> Self
pub fn new<F>(new_token: F, address: Option<(String, u16)>) -> Self
where F: Fn() -> Result<String, String> + Send + Sync + 'static {
SignerService {
queue: Arc::new(ConfirmationsQueue::default()),
generate_new_token: Box::new(new_token),
port: port,
address: address,
}
}
@ -47,20 +47,20 @@ impl SignerService {
self.queue.clone()
}
/// Returns signer port (if signer enabled) or `None` otherwise
pub fn port(&self) -> Option<u16> {
self.port
/// Returns signer address (if signer enabled) or `None` otherwise
pub fn address(&self) -> Option<(String, u16)> {
self.address.clone()
}
/// Returns true if Signer is enabled.
pub fn is_enabled(&self) -> bool {
self.port.is_some()
self.address.is_some()
}
#[cfg(test)]
/// Creates new Signer Service for tests.
pub fn new_test(port: Option<u16>) -> Self {
SignerService::new(|| Ok("new_token".into()), port)
pub fn new_test(address: Option<(String, u16)>) -> Self {
SignerService::new(|| Ok("new_token".into()), address)
}
}

View File

@ -52,6 +52,7 @@ pub struct ParityClient<C, M, S: ?Sized> where
logger: Arc<RotatingLogger>,
settings: Arc<NetworkSettings>,
signer: Option<Arc<SignerService>>,
dapps_interface: Option<String>,
dapps_port: Option<u16>,
}
@ -70,6 +71,7 @@ impl<C, M, S: ?Sized> ParityClient<C, M, S> where
logger: Arc<RotatingLogger>,
settings: Arc<NetworkSettings>,
signer: Option<Arc<SignerService>>,
dapps_interface: Option<String>,
dapps_port: Option<u16>,
) -> Self {
ParityClient {
@ -81,6 +83,7 @@ impl<C, M, S: ?Sized> ParityClient<C, M, S> where
logger: logger,
settings: settings,
signer: signer,
dapps_interface: dapps_interface,
dapps_port: dapps_port,
}
}
@ -261,7 +264,8 @@ impl<C, M, S: ?Sized> Parity for ParityClient<C, M, S> where
self.signer
.clone()
.and_then(|signer| signer.port())
.and_then(|signer| signer.address())
.map(|address| address.1)
.ok_or_else(|| errors::signer_disabled())
}
@ -272,6 +276,13 @@ impl<C, M, S: ?Sized> Parity for ParityClient<C, M, S> where
.ok_or_else(|| errors::dapps_disabled())
}
fn dapps_interface(&self) -> Result<String, Error> {
try!(self.active());
self.dapps_interface.clone()
.ok_or_else(|| errors::dapps_disabled())
}
fn next_nonce(&self, address: H160) -> Result<U256, Error> {
try!(self.active());
let address: Address = address.into();

View File

@ -38,6 +38,7 @@ pub struct Dependencies {
pub settings: Arc<NetworkSettings>,
pub network: Arc<ManageNetwork>,
pub accounts: Arc<AccountProvider>,
pub dapps_interface: Option<String>,
pub dapps_port: Option<u16>,
}
@ -61,6 +62,7 @@ impl Dependencies {
}),
network: Arc::new(TestManageNetwork),
accounts: Arc::new(AccountProvider::transient_provider()),
dapps_interface: Some("127.0.0.1".into()),
dapps_port: Some(18080),
}
}
@ -75,6 +77,7 @@ impl Dependencies {
self.logger.clone(),
self.settings.clone(),
signer,
self.dapps_interface.clone(),
self.dapps_port,
)
}
@ -238,7 +241,7 @@ fn rpc_parity_node_name() {
#[test]
fn rpc_parity_unsigned_transactions_count() {
let deps = Dependencies::new();
let io = deps.with_signer(SignerService::new_test(Some(18180)));
let io = deps.with_signer(SignerService::new_test(Some(("127.0.0.1".into(), 18180))));
let request = r#"{"jsonrpc": "2.0", "method": "parity_unsignedTransactionsCount", "params":[], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":0,"id":1}"#;
@ -282,7 +285,7 @@ fn rpc_parity_encrypt() {
fn rpc_parity_signer_port() {
// given
let deps = Dependencies::new();
let io1 = deps.with_signer(SignerService::new_test(Some(18180)));
let io1 = deps.with_signer(SignerService::new_test(Some(("127.0.0.1".into(), 18180))));
let io2 = deps.default_client();
// when
@ -313,6 +316,24 @@ fn rpc_parity_dapps_port() {
assert_eq!(io2.handle_request_sync(request), Some(response2.to_owned()));
}
#[test]
fn rpc_parity_dapps_interface() {
// given
let mut deps = Dependencies::new();
let io1 = deps.default_client();
deps.dapps_interface = None;
let io2 = deps.default_client();
// when
let request = r#"{"jsonrpc": "2.0", "method": "parity_dappsInterface", "params": [], "id": 1}"#;
let response1 = r#"{"jsonrpc":"2.0","result":"127.0.0.1","id":1}"#;
let response2 = r#"{"jsonrpc":"2.0","error":{"code":-32031,"message":"Dapps Server is disabled. This API is not available.","data":null},"id":1}"#;
// then
assert_eq!(io1.handle_request_sync(request), Some(response1.to_owned()));
assert_eq!(io2.handle_request_sync(request), Some(response2.to_owned()));
}
#[test]
fn rpc_parity_next_nonce() {
let deps = Dependencies::new();

View File

@ -123,6 +123,10 @@ build_rpc_trait! {
#[rpc(name = "parity_dappsPort")]
fn dapps_port(&self) -> Result<u16, Error>;
/// Returns current Dapps Server interface address or an error if dapps server is disabled.
#[rpc(name = "parity_dappsInterface")]
fn dapps_interface(&self) -> Result<String, Error>;
/// Returns next nonce for particular sender. Should include all transactions in the queue.
#[rpc(name = "parity_nextNonce")]
fn next_nonce(&self, H160) -> Result<U256, Error>;