Inject parity script to all dapps // Expand dapps to any ZIP file (#7260)

* Inject Parity script to all HTML dapps pages

* Small glitch

* Add injection test

* Add Special GHH commit value for serving ZIP files as DAPPS

* Refactor GithubDapp fetcher

* PR Grumbles
This commit is contained in:
Nicolas Gotchac 2017-12-13 07:56:35 +01:00 committed by Svyatoslav Nikolsky
parent f3297dd44a
commit 82d7fc54b3
6 changed files with 125 additions and 45 deletions

View File

@ -200,39 +200,57 @@ impl<R: URLHint + 'static, F: Fetch> Endpoint for ContentFetcher<F, R> {
Some(URLHintResult::Dapp(_)) if self.only_content => { Some(URLHintResult::Dapp(_)) if self.only_content => {
(None, Self::dapps_disabled(self.embeddable_on.clone())) (None, Self::dapps_disabled(self.embeddable_on.clone()))
}, },
Some(URLHintResult::Dapp(dapp)) => { Some(content) => {
let handler = ContentFetcherHandler::new( let handler = match content {
req.method(), URLHintResult::Dapp(dapp) => {
&dapp.url(), ContentFetcherHandler::new(
path, req.method(),
installers::Dapp::new( &dapp.url(),
content_id.clone(), path,
self.cache_path.clone(), installers::Dapp::new(
Box::new(on_done), content_id.clone(),
self.embeddable_on.clone(), self.cache_path.clone(),
self.pool.clone(), Box::new(on_done),
), self.embeddable_on.clone(),
self.embeddable_on.clone(), self.pool.clone(),
self.fetch.clone(), ),
); self.embeddable_on.clone(),
self.fetch.clone(),
(Some(ContentStatus::Fetching(handler.fetch_control())), Box::new(handler) as endpoint::Response) )
}, },
Some(URLHintResult::Content(content)) => { URLHintResult::GithubDapp(content) => {
let handler = ContentFetcherHandler::new( ContentFetcherHandler::new(
req.method(), req.method(),
&content.url, &content.url,
path, path,
installers::Content::new( installers::Dapp::new(
content_id.clone(), content_id.clone(),
content.mime, self.cache_path.clone(),
self.cache_path.clone(), Box::new(on_done),
Box::new(on_done), self.embeddable_on.clone(),
self.pool.clone(), self.pool.clone(),
), ),
self.embeddable_on.clone(), self.embeddable_on.clone(),
self.fetch.clone(), self.fetch.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(),
)
},
};
(Some(ContentStatus::Fetching(handler.fetch_control())), Box::new(handler) as endpoint::Response) (Some(ContentStatus::Fetching(handler.fetch_control())), Box::new(handler) as endpoint::Response)
}, },

View File

@ -17,8 +17,9 @@
use std::io; use std::io;
use std::time::{Duration, SystemTime}; use std::time::{Duration, SystemTime};
use hyper::{self, header, StatusCode}; use hyper::{self, header, StatusCode};
use hyper::mime::Mime; use hyper::mime::{self, Mime};
use apps;
use handlers::{Reader, ContentHandler, add_security_headers}; use handlers::{Reader, ContentHandler, add_security_headers};
use {Embeddable}; use {Embeddable};
@ -95,7 +96,18 @@ impl<T: DappFile> PageHandler<T> {
add_security_headers(&mut headers, self.safe_to_embed_on); add_security_headers(&mut headers, self.safe_to_embed_on);
} }
let (reader, body) = Reader::pair(file.into_reader(), Vec::new()); 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); res.set_body(body);
(Some(reader), res) (Some(reader), res)
} }

View File

@ -166,14 +166,15 @@ fn should_return_fetched_dapp_content() {
response1.assert_status("HTTP/1.1 200 OK"); response1.assert_status("HTTP/1.1 200 OK");
assert_security_headers_for_embed(&response1.headers); assert_security_headers_for_embed(&response1.headers);
assert_eq!( assert!(
response1.body, response1.body.contains(r#"18
r#"18
<h1>Hello Gavcoin!</h1> <h1>Hello Gavcoin!</h1>
0 0
"# "#),
"Expected Gavcoin body: {}",
response1.body
); );
response2.assert_status("HTTP/1.1 200 OK"); response2.assert_status("HTTP/1.1 200 OK");

View File

@ -60,3 +60,32 @@ fn should_serve_home() {
response.assert_header("Content-Type", "text/html"); response.assert_header("Content-Type", "text/html");
assert_security_headers(&response.headers); assert_security_headers(&response.headers);
} }
#[test]
fn should_inject_js() {
// given
let server = serve_ui();
// when
let response = request(server,
"\
GET / HTTP/1.1\r\n\
Host: 127.0.0.1:8080\r\n\
Connection: close\r\n\
\r\n\
{}
"
);
// then
response.assert_status("HTTP/1.1 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);
}

View File

@ -150,6 +150,9 @@ impl<F: Fetch + 'static> HashFetch for Client<F> {
URLHintResult::Dapp(dapp) => { URLHintResult::Dapp(dapp) => {
dapp.url() dapp.url()
}, },
URLHintResult::GithubDapp(content) => {
content.url
},
URLHintResult::Content(content) => { URLHintResult::Content(content) => {
content.url content.url
}, },

View File

@ -32,6 +32,10 @@ use bytes::Bytes;
pub type BoxFuture<A, B> = Box<Future<Item = A, Error = B> + Send>; pub type BoxFuture<A, B> = Box<Future<Item = A, Error = B> + Send>;
const COMMIT_LEN: usize = 20; const COMMIT_LEN: usize = 20;
/// GithubHint entries with commit set as `0x0..01` should be treated
/// as Github Dapp, downloadable zip files, than can be extracted, containing
/// the manifest.json file along with the dapp
static GITHUB_DAPP_COMMIT: &[u8; COMMIT_LEN] = &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
/// RAW Contract interface. /// RAW Contract interface.
/// Should execute transaction using current blockchain state. /// Should execute transaction using current blockchain state.
@ -93,6 +97,8 @@ pub struct Content {
pub enum URLHintResult { pub enum URLHintResult {
/// Dapp /// Dapp
Dapp(GithubApp), Dapp(GithubApp),
/// GithubDapp
GithubDapp(Content),
/// Content /// Content
Content(Content), Content(Content),
} }
@ -122,6 +128,15 @@ impl URLHintContract {
} }
} }
fn get_urlhint_content(account_slash_repo: String, owner: Address) -> Content {
let mime = guess_mime_type(&account_slash_repo).unwrap_or(mime::APPLICATION_JSON);
Content {
url: account_slash_repo,
mime,
owner,
}
}
fn decode_urlhint_output(output: (String, ::bigint::hash::H160, Address)) -> Option<URLHintResult> { fn decode_urlhint_output(output: (String, ::bigint::hash::H160, Address)) -> Option<URLHintResult> {
let (account_slash_repo, commit, owner) = output; let (account_slash_repo, commit, owner) = output;
@ -130,13 +145,15 @@ fn decode_urlhint_output(output: (String, ::bigint::hash::H160, Address)) -> Opt
} }
let commit = GithubApp::commit(&commit); let commit = GithubApp::commit(&commit);
if commit == Some(Default::default()) { if commit == Some(Default::default()) {
let mime = guess_mime_type(&account_slash_repo).unwrap_or(mime::APPLICATION_JSON); let content = get_urlhint_content(account_slash_repo, owner);
return Some(URLHintResult::Content(Content { return Some(URLHintResult::Content(content));
url: account_slash_repo, }
mime: mime,
owner: owner, if commit == Some(*GITHUB_DAPP_COMMIT) {
})); let content = get_urlhint_content(account_slash_repo, owner);
return Some(URLHintResult::GithubDapp(content));
} }
let (account, repo) = { let (account, repo) = {