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, resolver: R,
cache: Arc<Mutex<ContentCache>>, cache: Arc<Mutex<ContentCache>>,
sync: Arc<SyncStatus>, sync: Arc<SyncStatus>,
embeddable_at: Option<u16>, embeddable_on: Option<(String, u16)>,
} }
impl<R: URLHint> Drop for ContentFetcher<R> { impl<R: URLHint> Drop for ContentFetcher<R> {
@ -59,7 +59,7 @@ impl<R: URLHint> Drop for ContentFetcher<R> {
impl<R: URLHint> 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(); let mut dapps_path = env::temp_dir();
dapps_path.push(random_filename()); dapps_path.push(random_filename());
@ -68,17 +68,17 @@ impl<R: URLHint> ContentFetcher<R> {
resolver: resolver, resolver: resolver,
sync: sync_status, sync: sync_status,
cache: Arc::new(Mutex::new(ContentCache::default())), 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( Box::new(ContentHandler::error(
StatusCode::ServiceUnavailable, StatusCode::ServiceUnavailable,
"Sync In Progress", "Sync In Progress",
"Your node is still syncing. We cannot resolve any content before it's fully synced.", "Your node is still syncing. We cannot resolve any content before it's fully synced.",
Some("<a href=\"javascript:window.location.reload()\">Refresh</a>"), Some("<a href=\"javascript:window.location.reload()\">Refresh</a>"),
port, address,
)) ))
} }
@ -145,7 +145,7 @@ impl<R: URLHint> ContentFetcher<R> {
match content { match content {
// Don't serve dapps if we are still syncing (but serve content) // Don't serve dapps if we are still syncing (but serve content)
Some(URLHintResult::Dapp(_)) if self.sync.is_major_importing() => { 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)) => { Some(URLHintResult::Dapp(dapp)) => {
let (handler, fetch_control) = ContentFetcherHandler::new( let (handler, fetch_control) = ContentFetcherHandler::new(
@ -155,9 +155,9 @@ impl<R: URLHint> ContentFetcher<R> {
id: content_id.clone(), id: content_id.clone(),
dapps_path: self.dapps_path.clone(), dapps_path: self.dapps_path.clone(),
on_done: Box::new(on_done), 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>) (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(), content_path: self.dapps_path.clone(),
on_done: Box::new(on_done), on_done: Box::new(on_done),
}, },
self.embeddable_at, self.embeddable_on.clone(),
); );
(Some(ContentStatus::Fetching(fetch_control)), Box::new(handler) as Box<Handler>) (Some(ContentStatus::Fetching(fetch_control)), Box::new(handler) as Box<Handler>)
}, },
None if self.sync.is_major_importing() => { None if self.sync.is_major_importing() => {
(None, Self::still_syncing(self.embeddable_at)) (None, Self::still_syncing(self.embeddable_on.clone()))
}, },
None => { None => {
// This may happen when sync status changes in between // This may happen when sync status changes in between
@ -188,7 +188,7 @@ impl<R: URLHint> ContentFetcher<R> {
"Resource Not Found", "Resource Not Found",
"Requested resource was not found.", "Requested resource was not found.",
None, None,
self.embeddable_at, self.embeddable_on.clone(),
)) as Box<Handler>) )) as Box<Handler>)
}, },
} }
@ -293,7 +293,7 @@ struct DappInstaller {
id: String, id: String,
dapps_path: PathBuf, dapps_path: PathBuf,
on_done: Box<Fn(String, Option<LocalPageEndpoint>) + Send>, on_done: Box<Fn(String, Option<LocalPageEndpoint>) + Send>,
embeddable_at: Option<u16>, embeddable_on: Option<(String, u16)>,
} }
impl DappInstaller { impl DappInstaller {
@ -386,7 +386,7 @@ impl ContentValidator for DappInstaller {
try!(manifest_file.write_all(manifest_str.as_bytes())); try!(manifest_file.write_all(manifest_str.as_bytes()));
// Create endpoint // 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 // Return modified app manifest
Ok((manifest.id.clone(), app)) 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(); let mut pages = Endpoints::new();
for dapp in local_dapps(dapps_path) { for dapp in local_dapps(dapps_path) {
pages.insert( pages.insert(
dapp.id, 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 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())) 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 // 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 // NOTE [ToDr] Dapps will be currently embeded on 8180
insert::<parity_ui::App>(&mut pages, "ui", Embeddable::Yes(signer_port)); insert::<parity_ui::App>(&mut pages, "ui", Embeddable::Yes(signer_address.clone()));
pages.insert("proxy".into(), ProxyPac::boxed(signer_port)); pages.insert("proxy".into(), ProxyPac::boxed(signer_address));
pages pages
} }
fn insert<T : WebApp + Default + 'static>(pages: &mut Endpoints, id: &str, embed_at: Embeddable) { fn insert<T : WebApp + Default + 'static>(pages: &mut Endpoints, id: &str, embed_at: Embeddable) {
pages.insert(id.to_owned(), Box::new(match embed_at { 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()), Embeddable::No => PageEndpoint::new(T::default()),
})); }));
} }
enum Embeddable { enum Embeddable {
Yes(Option<u16>), Yes(Option<(String, u16)>),
#[allow(dead_code)] #[allow(dead_code)]
No, No,
} }

View File

@ -32,7 +32,7 @@ pub struct ContentHandler {
content: String, content: String,
mimetype: Mime, mimetype: Mime,
write_pos: usize, write_pos: usize,
safe_to_embed_at_port: Option<u16>, safe_to_embed_on: Option<(String, u16)>,
} }
impl ContentHandler { impl ContentHandler {
@ -44,31 +44,31 @@ impl ContentHandler {
Self::new(StatusCode::NotFound, content, mimetype) Self::new(StatusCode::NotFound, content, mimetype)
} }
pub fn html(code: StatusCode, content: String, embeddable_at: Option<u16>) -> Self { pub fn html(code: StatusCode, content: String, embeddable_on: Option<(String, u16)>) -> Self {
Self::new_embeddable(code, content, mime!(Text/Html), embeddable_at) 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!( Self::html(code, format!(
include_str!("../error_tpl.html"), include_str!("../error_tpl.html"),
title=title, title=title,
message=message, message=message,
details=details.unwrap_or_else(|| ""), details=details.unwrap_or_else(|| ""),
version=version(), version=version(),
), embeddable_at) ), embeddable_on)
} }
pub fn new(code: StatusCode, content: String, mimetype: Mime) -> Self { pub fn new(code: StatusCode, content: String, mimetype: Mime) -> Self {
Self::new_embeddable(code, content, mimetype, None) 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 { ContentHandler {
code: code, code: code,
content: content, content: content,
mimetype: mimetype, mimetype: mimetype,
write_pos: 0, 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 { fn on_response(&mut self, res: &mut server::Response) -> Next {
res.set_status(self.code); res.set_status(self.code);
res.headers_mut().set(header::ContentType(self.mimetype.clone())); 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() Next::write()
} }

View File

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

View File

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

View File

@ -112,7 +112,7 @@ pub struct ServerBuilder {
handler: Arc<IoHandler>, handler: Arc<IoHandler>,
registrar: Arc<ContractClient>, registrar: Arc<ContractClient>,
sync_status: Arc<SyncStatus>, sync_status: Arc<SyncStatus>,
signer_port: Option<u16>, signer_address: Option<(String, u16)>,
} }
impl Extendable for ServerBuilder { impl Extendable for ServerBuilder {
@ -129,7 +129,7 @@ impl ServerBuilder {
handler: Arc::new(IoHandler::new()), handler: Arc::new(IoHandler::new()),
registrar: registrar, registrar: registrar,
sync_status: Arc::new(|| false), sync_status: Arc::new(|| false),
signer_port: None, signer_address: None,
} }
} }
@ -139,8 +139,8 @@ impl ServerBuilder {
} }
/// Change default signer port. /// Change default signer port.
pub fn with_signer_port(&mut self, signer_port: Option<u16>) { pub fn with_signer_address(&mut self, signer_address: Option<(String, u16)>) {
self.signer_port = signer_port; self.signer_address = signer_address;
} }
/// Asynchronously start server with no authentication, /// Asynchronously start server with no authentication,
@ -152,7 +152,7 @@ impl ServerBuilder {
NoAuth, NoAuth,
self.handler.clone(), self.handler.clone(),
self.dapps_path.clone(), self.dapps_path.clone(),
self.signer_port.clone(), self.signer_address.clone(),
self.registrar.clone(), self.registrar.clone(),
self.sync_status.clone(), self.sync_status.clone(),
) )
@ -167,7 +167,7 @@ impl ServerBuilder {
HttpBasicAuth::single_user(username, password), HttpBasicAuth::single_user(username, password),
self.handler.clone(), self.handler.clone(),
self.dapps_path.clone(), self.dapps_path.clone(),
self.signer_port.clone(), self.signer_address.clone(),
self.registrar.clone(), self.registrar.clone(),
self.sync_status.clone(), self.sync_status.clone(),
) )
@ -197,11 +197,11 @@ impl Server {
} }
/// Returns a list of CORS domains for API endpoint. /// Returns a list of CORS domains for API endpoint.
fn cors_domains(signer_port: Option<u16>) -> Vec<String> { fn cors_domains(signer_address: Option<(String, u16)>) -> Vec<String> {
match signer_port { match signer_address {
Some(port) => vec![ Some(signer_address) => vec![
format!("http://{}{}", HOME_PAGE, DAPPS_DOMAIN), format!("http://{}{}", HOME_PAGE, DAPPS_DOMAIN),
format!("http://{}", signer_address(port)), format!("http://{}", address(signer_address)),
], ],
None => vec![], None => vec![],
} }
@ -213,15 +213,15 @@ impl Server {
authorization: A, authorization: A,
handler: Arc<IoHandler>, handler: Arc<IoHandler>,
dapps_path: String, dapps_path: String,
signer_port: Option<u16>, signer_address: Option<(String, u16)>,
registrar: Arc<ContractClient>, registrar: Arc<ContractClient>,
sync_status: Arc<SyncStatus>, sync_status: Arc<SyncStatus>,
) -> Result<Server, ServerError> { ) -> Result<Server, ServerError> {
let panic_handler = Arc::new(Mutex::new(None)); let panic_handler = Arc::new(Mutex::new(None));
let authorization = Arc::new(authorization); let authorization = Arc::new(authorization);
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(apps::urlhint::URLHintContract::new(registrar), sync_status, 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_port.clone())); let endpoints = Arc::new(apps::all_endpoints(dapps_path, signer_address.clone()));
let cors_domains = Self::cors_domains(signer_port); let cors_domains = Self::cors_domains(signer_address.clone());
let special = Arc::new({ let special = Arc::new({
let mut special = HashMap::new(); let mut special = HashMap::new();
@ -238,7 +238,7 @@ impl Server {
try!(hyper::Server::http(addr)) try!(hyper::Server::http(addr))
.handle(move |ctrl| router::Router::new( .handle(move |ctrl| router::Router::new(
ctrl, ctrl,
signer_port.clone(), signer_address.clone(),
content_fetcher.clone(), content_fetcher.clone(),
endpoints.clone(), endpoints.clone(),
special.clone(), special.clone(),
@ -302,8 +302,8 @@ pub fn random_filename() -> String {
rng.gen_ascii_chars().take(12).collect() rng.gen_ascii_chars().take(12).collect()
} }
fn signer_address(port: u16) -> String { fn address(address: (String, u16)) -> String {
format!("127.0.0.1:{}", port) format!("{}:{}", address.0, address.1)
} }
#[cfg(test)] #[cfg(test)]
@ -332,7 +332,7 @@ mod util_tests {
// when // when
let none = Server::cors_domains(None); 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 // then
assert_eq!(none, Vec::<String>::new()); 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`) /// Prefix to strip from the path (when `None` deducted from `app_id`)
pub prefix: Option<String>, pub prefix: Option<String>,
/// Safe to be loaded in frame by other origin. (use wisely!) /// 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, info: EndpointInfo,
} }
@ -36,7 +36,7 @@ impl<T: WebApp + 'static> PageEndpoint<T> {
PageEndpoint { PageEndpoint {
app: Arc::new(app), app: Arc::new(app),
prefix: None, prefix: None,
safe_to_embed_at_port: None, safe_to_embed_on: None,
info: EndpointInfo::from(info), info: EndpointInfo::from(info),
} }
} }
@ -49,7 +49,7 @@ impl<T: WebApp + 'static> PageEndpoint<T> {
PageEndpoint { PageEndpoint {
app: Arc::new(app), app: Arc::new(app),
prefix: Some(prefix), prefix: Some(prefix),
safe_to_embed_at_port: None, safe_to_embed_on: None,
info: EndpointInfo::from(info), info: EndpointInfo::from(info),
} }
} }
@ -57,12 +57,12 @@ impl<T: WebApp + 'static> PageEndpoint<T> {
/// Creates new `PageEndpoint` 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). /// even from different origin. It might be dangerous (clickjacking).
/// Use wisely! /// 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(); let info = app.info();
PageEndpoint { PageEndpoint {
app: Arc::new(app), app: Arc::new(app),
prefix: None, prefix: None,
safe_to_embed_at_port: port, safe_to_embed_on: address,
info: EndpointInfo::from(info), info: EndpointInfo::from(info),
} }
} }
@ -79,9 +79,9 @@ impl<T: WebApp> Endpoint for PageEndpoint<T> {
app: BuiltinDapp::new(self.app.clone()), app: BuiltinDapp::new(self.app.clone()),
prefix: self.prefix.clone(), prefix: self.prefix.clone(),
path: path, 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, 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> { 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( ServedFile::Error(ContentHandler::error(
StatusCode::NotFound, StatusCode::NotFound,
"404 Not Found", "404 Not Found",
"Requested dapp resource was not found.", "Requested dapp resource was not found.",
None, None,
embeddable_at, embeddable_on,
)) ))
} }
} }
@ -97,7 +97,7 @@ pub struct PageHandler<T: Dapp> {
/// Requested path. /// Requested path.
pub path: EndpointPath, pub path: EndpointPath,
/// Flag indicating if the file can be safely embeded (put in iframe). /// 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. /// Cache settings for this page.
pub cache: PageCache, pub cache: PageCache,
} }
@ -133,7 +133,7 @@ impl<T: Dapp> server::Handler<HttpStream> for PageHandler<T> {
self.app.file(&self.extract_path(url.path())) self.app.file(&self.extract_path(url.path()))
}, },
_ => None, _ => 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() Next::write()
} }
@ -162,7 +162,7 @@ impl<T: Dapp> server::Handler<HttpStream> for PageHandler<T> {
} }
// Security headers: // 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() Next::write()
}, },
ServedFile::Error(ref mut handler) => { ServedFile::Error(ref mut handler) => {
@ -246,7 +246,7 @@ fn should_extract_path_with_appid() {
}, },
file: ServedFile::new(None), file: ServedFile::new(None),
cache: Default::default(), cache: Default::default(),
safe_to_embed_at_port: None, safe_to_embed_on: None,
}; };
// when // when

View File

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

View File

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

View File

@ -20,7 +20,7 @@
pub mod auth; pub mod auth;
mod host_validation; mod host_validation;
use signer_address; use address;
use std::sync::Arc; use std::sync::Arc;
use std::collections::HashMap; use std::collections::HashMap;
use url::{Url, Host}; use url::{Url, Host};
@ -43,7 +43,7 @@ pub enum SpecialEndpoint {
pub struct Router<A: Authorization + 'static> { pub struct Router<A: Authorization + 'static> {
control: Option<Control>, control: Option<Control>,
signer_port: Option<u16>, signer_address: Option<(String, u16)>,
endpoints: Arc<Endpoints>, endpoints: Arc<Endpoints>,
fetch: Arc<ContentFetcher>, fetch: Arc<ContentFetcher>,
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>, special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
@ -117,14 +117,14 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
"404 Not Found", "404 Not Found",
"Requested content was not found.", "Requested content was not found.",
None, None,
self.signer_port, self.signer_address.clone(),
)) ))
}, },
// Redirect any other GET request to signer. // Redirect any other GET request to signer.
_ if *req.method() == hyper::Method::Get => { _ 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."); trace!(target: "dapps", "Redirecting to signer interface.");
Redirection::boxed(&format!("http://{}", signer_address(port))) Redirection::boxed(&format!("http://{}", address(signer_address)))
} else { } else {
trace!(target: "dapps", "Signer disabled, returning 404."); trace!(target: "dapps", "Signer disabled, returning 404.");
Box::new(ContentHandler::error( Box::new(ContentHandler::error(
@ -132,7 +132,7 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
"404 Not Found", "404 Not Found",
"Your homepage is not available when Trusted Signer is disabled.", "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."), 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> { impl<A: Authorization> Router<A> {
pub fn new( pub fn new(
control: Control, control: Control,
signer_port: Option<u16>, signer_address: Option<(String, u16)>,
content_fetcher: Arc<ContentFetcher>, content_fetcher: Arc<ContentFetcher>,
endpoints: Arc<Endpoints>, endpoints: Arc<Endpoints>,
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>, special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
@ -181,7 +181,7 @@ impl<A: Authorization> Router<A> {
.to_handler(EndpointPath::default()); .to_handler(EndpointPath::default());
Router { Router {
control: Some(control), control: Some(control),
signer_port: signer_port, signer_address: signer_address,
endpoints: endpoints, endpoints: endpoints,
fetch: content_fetcher, fetch: content_fetcher,
special: special, 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"); 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()); let mut builder = ServerBuilder::new(dapps_path.to_str().unwrap().into(), registrar.clone());
builder.with_sync_status(Arc::new(move || is_syncing)); 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(), builder.start_unsecured_http(&"127.0.0.1:0".parse().unwrap(), hosts).unwrap(),
registrar, registrar,
@ -89,7 +89,7 @@ pub fn serve_with_auth(user: &str, pass: &str) -> Server {
let mut dapps_path = env::temp_dir(); let mut dapps_path = env::temp_dir();
dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading"); dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading");
let mut builder = ServerBuilder::new(dapps_path.to_str().unwrap().into(), registrar); 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() 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); .then(outNumber);
} }
dappsInterface () {
return this._transport
.execute('parity_dappsInterface');
}
defaultExtraData () { defaultExtraData () {
return this._transport return this._transport
.execute('parity_defaultExtraData'); .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: { defaultExtraData: {
desc: 'Returns the default extra data', desc: 'Returns the default extra data',
params: [], params: [],

View File

@ -95,7 +95,7 @@ impl Configuration {
let wal = !self.args.flag_fast_and_loose; let wal = !self.args.flag_fast_and_loose;
let warp_sync = self.args.flag_warp; let warp_sync = self.args.flag_warp;
let geth_compatibility = self.args.flag_geth; 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 dapps_conf = self.dapps_config();
let signer_conf = self.signer_config(); let signer_conf = self.signer_config();
let format = try!(self.format()); let format = try!(self.format());
@ -243,7 +243,7 @@ impl Configuration {
vm_type: vm_type, vm_type: vm_type,
warp_sync: warp_sync, warp_sync: warp_sync,
geth_compatibility: geth_compatibility, geth_compatibility: geth_compatibility,
ui_port: ui_port, ui_address: ui_address,
net_settings: self.network_settings(), net_settings: self.network_settings(),
dapps_conf: dapps_conf, dapps_conf: dapps_conf,
signer_conf: signer_conf, signer_conf: signer_conf,
@ -859,7 +859,7 @@ mod tests {
wal: true, wal: true,
vm_type: Default::default(), vm_type: Default::default(),
geth_compatibility: false, geth_compatibility: false,
ui_port: Some(8180), ui_address: Some(("127.0.0.1".into(), 8180)),
net_settings: Default::default(), net_settings: Default::default(),
dapps_conf: Default::default(), dapps_conf: Default::default(),
signer_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); 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 url = format!("{}:{}", configuration.interface, configuration.port);
let addr = try!(url.parse().map_err(|_| format!("Invalid Webapps listen host/port given: {}", url))); 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) (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; pub use self::server::WebappServer;
@ -91,7 +91,7 @@ mod server {
_url: &SocketAddr, _url: &SocketAddr,
_allowed_hosts: Option<Vec<String>>, _allowed_hosts: Option<Vec<String>>,
_auth: Option<(String, String)>, _auth: Option<(String, String)>,
_signer_port: Option<u16>, _signer_address: Option<(String, u16)>,
) -> Result<WebappServer, String> { ) -> Result<WebappServer, String> {
Err("Your Parity version has been compiled without WebApps support.".into()) Err("Your Parity version has been compiled without WebApps support.".into())
} }
@ -120,7 +120,7 @@ mod server {
url: &SocketAddr, url: &SocketAddr,
allowed_hosts: Option<Vec<String>>, allowed_hosts: Option<Vec<String>>,
auth: Option<(String, String)>, auth: Option<(String, String)>,
signer_port: Option<u16>, signer_address: Option<(String, u16)>,
) -> Result<WebappServer, String> { ) -> Result<WebappServer, String> {
use ethcore_dapps as dapps; use ethcore_dapps as dapps;
@ -131,7 +131,7 @@ mod server {
let sync = deps.sync.clone(); let sync = deps.sync.clone();
let client = deps.client.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_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 server = rpc_apis::setup_rpc(server, deps.apis.clone(), rpc_apis::ApiSet::UnsafeContext);
let start_result = match auth { let start_result = match auth {

View File

@ -37,7 +37,7 @@ pub enum Api {
Net, Net,
/// Eth (Safe) /// Eth (Safe)
Eth, Eth,
/// Geth-compatible "personal" API (DEPRECATED; only used in `--geth` mode.) /// Geth-compatible "personal" API (DEPRECATED; only used in `--geth` mode.)
Personal, Personal,
/// Signer - Confirm transactions in Signer (UNSAFE: Passwords, List of transactions) /// Signer - Confirm transactions in Signer (UNSAFE: Passwords, List of transactions)
Signer, Signer,
@ -119,6 +119,7 @@ pub struct Dependencies {
pub settings: Arc<NetworkSettings>, pub settings: Arc<NetworkSettings>,
pub net_service: Arc<ManageNetwork>, pub net_service: Arc<ManageNetwork>,
pub geth_compatibility: bool, pub geth_compatibility: bool,
pub dapps_interface: Option<String>,
pub dapps_port: Option<u16>, 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.logger.clone(),
deps.settings.clone(), deps.settings.clone(),
signer, signer,
deps.dapps_interface.clone(),
deps.dapps_port, deps.dapps_port,
).to_delegate()); ).to_delegate());

View File

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

View File

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

View File

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

View File

@ -38,6 +38,7 @@ pub struct Dependencies {
pub settings: Arc<NetworkSettings>, pub settings: Arc<NetworkSettings>,
pub network: Arc<ManageNetwork>, pub network: Arc<ManageNetwork>,
pub accounts: Arc<AccountProvider>, pub accounts: Arc<AccountProvider>,
pub dapps_interface: Option<String>,
pub dapps_port: Option<u16>, pub dapps_port: Option<u16>,
} }
@ -61,6 +62,7 @@ impl Dependencies {
}), }),
network: Arc::new(TestManageNetwork), network: Arc::new(TestManageNetwork),
accounts: Arc::new(AccountProvider::transient_provider()), accounts: Arc::new(AccountProvider::transient_provider()),
dapps_interface: Some("127.0.0.1".into()),
dapps_port: Some(18080), dapps_port: Some(18080),
} }
} }
@ -75,6 +77,7 @@ impl Dependencies {
self.logger.clone(), self.logger.clone(),
self.settings.clone(), self.settings.clone(),
signer, signer,
self.dapps_interface.clone(),
self.dapps_port, self.dapps_port,
) )
} }
@ -238,7 +241,7 @@ fn rpc_parity_node_name() {
#[test] #[test]
fn rpc_parity_unsigned_transactions_count() { fn rpc_parity_unsigned_transactions_count() {
let deps = Dependencies::new(); 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 request = r#"{"jsonrpc": "2.0", "method": "parity_unsignedTransactionsCount", "params":[], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":0,"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() { fn rpc_parity_signer_port() {
// given // given
let deps = Dependencies::new(); 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(); let io2 = deps.default_client();
// when // when
@ -313,6 +316,24 @@ fn rpc_parity_dapps_port() {
assert_eq!(io2.handle_request_sync(request), Some(response2.to_owned())); 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] #[test]
fn rpc_parity_next_nonce() { fn rpc_parity_next_nonce() {
let deps = Dependencies::new(); let deps = Dependencies::new();

View File

@ -123,6 +123,10 @@ build_rpc_trait! {
#[rpc(name = "parity_dappsPort")] #[rpc(name = "parity_dappsPort")]
fn dapps_port(&self) -> Result<u16, Error>; 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. /// Returns next nonce for particular sender. Should include all transactions in the queue.
#[rpc(name = "parity_nextNonce")] #[rpc(name = "parity_nextNonce")]
fn next_nonce(&self, H160) -> Result<U256, Error>; fn next_nonce(&self, H160) -> Result<U256, Error>;