diff --git a/dapps/src/apps/fetcher.rs b/dapps/src/apps/fetcher.rs index e1fa4c066..006858e73 100644 --- a/dapps/src/apps/fetcher.rs +++ b/dapps/src/apps/fetcher.rs @@ -151,6 +151,7 @@ impl ContentFetcher { Some(URLHintResult::Dapp(dapp)) => { let (handler, fetch_control) = ContentFetcherHandler::new( dapp.url(), + path, control, DappInstaller { id: content_id.clone(), @@ -166,6 +167,7 @@ impl ContentFetcher { Some(URLHintResult::Content(content)) => { let (handler, fetch_control) = ContentFetcherHandler::new( content.url, + path, control, ContentInstaller { id: content_id.clone(), @@ -255,7 +257,7 @@ struct ContentInstaller { impl ContentValidator for ContentInstaller { type Error = ValidationError; - fn validate_and_install(&self, path: PathBuf) -> Result<(String, LocalPageEndpoint), ValidationError> { + fn validate_and_install(&self, path: PathBuf) -> Result { let validate = || { // Create dir try!(fs::create_dir_all(&self.content_path)); @@ -287,7 +289,7 @@ impl ContentValidator for ContentInstaller { // Make sure to always call on_done (even in case of errors)! let result = validate(); (self.on_done)(result.as_ref().ok().cloned()); - result.map(|endpoint| (self.id.clone(), endpoint)) + result } } @@ -334,10 +336,10 @@ impl DappInstaller { impl ContentValidator for DappInstaller { type Error = ValidationError; - fn validate_and_install(&self, app_path: PathBuf) -> Result<(String, LocalPageEndpoint), ValidationError> { - trace!(target: "dapps", "Opening dapp bundle at {:?}", app_path); + fn validate_and_install(&self, path: PathBuf) -> Result { + trace!(target: "dapps", "Opening dapp bundle at {:?}", path); let validate = || { - let mut file_reader = io::BufReader::new(try!(fs::File::open(app_path))); + let mut file_reader = io::BufReader::new(try!(fs::File::open(path))); let hash = try!(sha3(&mut file_reader)); let id = try!(self.id.as_str().parse().map_err(|_| ValidationError::InvalidContentId)); if id != hash { @@ -390,13 +392,12 @@ impl ContentValidator for DappInstaller { try!(manifest_file.write_all(manifest_str.as_bytes())); // Create endpoint let endpoint = LocalPageEndpoint::new(target, manifest.clone().into(), PageCache::Enabled, self.embeddable_on.clone()); - // Return modified app manifest Ok(endpoint) }; let result = validate(); (self.on_done)(result.as_ref().ok().cloned()); - result.map(|endpoint| (self.id.clone(), endpoint)) + result } } diff --git a/dapps/src/handlers/fetch.rs b/dapps/src/handlers/fetch.rs index 8e5f250ce..d62b425d9 100644 --- a/dapps/src/handlers/fetch.rs +++ b/dapps/src/handlers/fetch.rs @@ -22,35 +22,41 @@ use std::sync::{mpsc, Arc}; use std::sync::atomic::{AtomicBool, Ordering}; use std::time::{Instant, Duration}; use util::Mutex; -use url::Url; use fetch::{Client, Fetch, FetchResult}; use hyper::{server, Decoder, Encoder, Next, Method, Control}; use hyper::net::HttpStream; +use hyper::uri::RequestUri; use hyper::status::StatusCode; use endpoint::EndpointPath; -use handlers::{ContentHandler, Redirection, extract_url}; -use page::LocalPageEndpoint; +use handlers::ContentHandler; +use page::{LocalPageEndpoint, PageHandlerWaiting}; const FETCH_TIMEOUT: u64 = 30; enum FetchState { + Waiting, NotStarted(String), Error(ContentHandler), InProgress(mpsc::Receiver), - Done(String, LocalPageEndpoint, Redirection), + Done(LocalPageEndpoint, Box), +} + +enum WaitResult { + Error(ContentHandler), + Done(LocalPageEndpoint), } pub trait ContentValidator { type Error: fmt::Debug + fmt::Display; - fn validate_and_install(&self, app: PathBuf) -> Result<(String, LocalPageEndpoint), Self::Error>; + fn validate_and_install(&self, path: PathBuf) -> Result; } pub struct FetchControl { abort: Arc, - listeners: Mutex)>>, + listeners: Mutex)>>, deadline: Instant, } @@ -65,9 +71,10 @@ impl Default for FetchControl { } impl FetchControl { - fn notify FetchState>(&self, status: F) { + fn notify WaitResult>(&self, status: F) { let mut listeners = self.listeners.lock(); for (control, sender) in listeners.drain(..) { + trace!(target: "dapps", "Resuming request waiting for content..."); if let Err(e) = sender.send(status()) { trace!(target: "dapps", "Waiting listener notification failed: {:?}", e); } else { @@ -78,9 +85,9 @@ impl FetchControl { fn set_status(&self, status: &FetchState) { match *status { - FetchState::Error(ref handler) => self.notify(|| FetchState::Error(handler.clone())), - FetchState::Done(ref id, ref endpoint, ref handler) => self.notify(|| FetchState::Done(id.clone(), endpoint.clone(), handler.clone())), - FetchState::NotStarted(_) | FetchState::InProgress(_) => {}, + FetchState::Error(ref handler) => self.notify(|| WaitResult::Error(handler.clone())), + FetchState::Done(ref endpoint, _) => self.notify(|| WaitResult::Done(endpoint.clone())), + FetchState::NotStarted(_) | FetchState::InProgress(_) | FetchState::Waiting => {}, } } @@ -89,45 +96,65 @@ impl FetchControl { } pub fn to_async_handler(&self, path: EndpointPath, control: Control) -> Box + Send> { - // TODO [ToDr] We should be able to pass EndpointPath to handler as well - // (request may be coming from different domain, etc) let (tx, rx) = mpsc::channel(); self.listeners.lock().push((control, tx)); Box::new(WaitingHandler { receiver: rx, - state: None, + state: FetchState::Waiting, + uri: RequestUri::default(), + path: path, }) } } pub struct WaitingHandler { - receiver: mpsc::Receiver, - state: Option, + receiver: mpsc::Receiver, + state: FetchState, + uri: RequestUri, + path: EndpointPath, } impl server::Handler for WaitingHandler { - fn on_request(&mut self, _request: server::Request) -> Next { + fn on_request(&mut self, request: server::Request) -> Next { + self.uri = request.uri().clone(); Next::wait() } - fn on_request_readable(&mut self, _decoder: &mut Decoder) -> Next { - self.state = self.receiver.try_recv().ok(); - Next::write() + fn on_request_readable(&mut self, decoder: &mut Decoder) -> Next { + let result = self.receiver.try_recv().ok(); + self.state = match result { + Some(WaitResult::Error(handler)) => FetchState::Error(handler), + Some(WaitResult::Done(endpoint)) => { + let mut page_handler = endpoint.to_page_handler(self.path.clone()); + page_handler.set_uri(&self.uri); + FetchState::Done(endpoint, page_handler) + }, + None => { + warn!("A result for waiting request was not received."); + FetchState::Waiting + }, + }; + + match self.state { + FetchState::Done(_, ref mut handler) => handler.on_request_readable(decoder), + FetchState::Error(ref mut handler) => handler.on_request_readable(decoder), + _ => Next::write(), + } } fn on_response(&mut self, res: &mut server::Response) -> Next { match self.state { - Some(FetchState::Done(_, _, ref mut handler)) => handler.on_response(res), - Some(FetchState::Error(ref mut handler)) => handler.on_response(res), + FetchState::Done(_, ref mut handler) => handler.on_response(res), + FetchState::Error(ref mut handler) => handler.on_response(res), _ => Next::end(), } } fn on_response_writable(&mut self, encoder: &mut Encoder) -> Next { match self.state { - Some(FetchState::Done(_, _, ref mut handler)) => handler.on_response_writable(encoder), - Some(FetchState::Error(ref mut handler)) => handler.on_response_writable(encoder), + FetchState::Done(_, ref mut handler) => handler.on_response_writable(encoder), + FetchState::Error(ref mut handler) => handler.on_response_writable(encoder), _ => Next::end(), } } @@ -139,13 +166,15 @@ pub struct ContentFetcherHandler { status: FetchState, client: Option, installer: H, - request_url: Option, + path: EndpointPath, + uri: RequestUri, embeddable_on: Option<(String, u16)>, } impl ContentFetcherHandler { pub fn new( url: String, + path: EndpointPath, control: Control, handler: H, embeddable_on: Option<(String, u16)>, @@ -158,7 +187,8 @@ impl ContentFetcherHandler { client: Some(client), status: FetchState::NotStarted(url), installer: handler, - request_url: None, + path: path, + uri: RequestUri::default(), embeddable_on: embeddable_on, }; @@ -182,7 +212,6 @@ impl ContentFetcherHandler { impl server::Handler for ContentFetcherHandler { fn on_request(&mut self, request: server::Request) -> Next { - self.request_url = extract_url(&request); let status = if let FetchState::NotStarted(ref url) = self.status { Some(match *request.method() { // Start fetching content @@ -195,8 +224,8 @@ impl server::Handler for ContentFetcherHandler< Ok(receiver) => FetchState::InProgress(receiver), Err(e) => FetchState::Error(ContentHandler::error( StatusCode::BadGateway, - "Unable To Start Dapp Download", - "Could not initialize download of the dapp. It might be a problem with the remote server.", + "Unable To Start Content Download", + "Could not initialize download of the content. It might be a problem with the remote server.", Some(&format!("{}", e)), self.embeddable_on.clone(), )), @@ -217,6 +246,7 @@ impl server::Handler for ContentFetcherHandler< self.fetch_control.set_status(&status); self.status = status; } + self.uri = request.uri().clone(); Next::read() } @@ -256,11 +286,10 @@ impl server::Handler for ContentFetcherHandler< self.embeddable_on.clone(), )) }, - Ok((id, result)) => { - let url: String = self.request_url.take() - .map(|url| url.raw.into_string()) - .expect("Request URL always read in on_request; qed"); - FetchState::Done(id, result, Redirection::new(&url)) + Ok(endpoint) => { + let mut handler = endpoint.to_page_handler(self.path.clone()); + handler.set_uri(&self.uri); + FetchState::Done(endpoint, handler) }, }; // Remove temporary zip file @@ -296,7 +325,7 @@ impl server::Handler for ContentFetcherHandler< fn on_response(&mut self, res: &mut server::Response) -> Next { match self.status { - FetchState::Done(_, _, ref mut handler) => handler.on_response(res), + FetchState::Done(_, ref mut handler) => handler.on_response(res), FetchState::Error(ref mut handler) => handler.on_response(res), _ => Next::end(), } @@ -304,7 +333,7 @@ impl server::Handler for ContentFetcherHandler< fn on_response_writable(&mut self, encoder: &mut Encoder) -> Next { match self.status { - FetchState::Done(_, _, ref mut handler) => handler.on_response_writable(encoder), + FetchState::Done(_, ref mut handler) => handler.on_response_writable(encoder), FetchState::Error(ref mut handler) => handler.on_response_writable(encoder), _ => Next::end(), } diff --git a/dapps/src/page/handler.rs b/dapps/src/page/handler.rs index 382dfa5d1..ba7a7ee04 100644 --- a/dapps/src/page/handler.rs +++ b/dapps/src/page/handler.rs @@ -83,13 +83,19 @@ impl Default for PageCache { } } +/// A generic type for `PageHandler` allowing to set the URL. +/// Used by dapps fetching to set the URL after the content was downloaded. +pub trait PageHandlerWaiting: server::Handler + Send { + fn set_uri(&mut self, uri: &RequestUri); +} + /// A handler for a single webapp. /// Resolves correct paths and serves as a plumbing code between /// hyper server and dapp. pub struct PageHandler { /// A Dapp. pub app: T, - /// File currently being served (or `None` if file does not exist). + /// File currently being served pub file: ServedFile, /// Optional prefix to strip from path. pub prefix: Option, @@ -101,6 +107,21 @@ pub struct PageHandler { pub cache: PageCache, } +impl PageHandlerWaiting for PageHandler { + fn set_uri(&mut self, uri: &RequestUri) { + trace!(target: "dapps", "Setting URI: {:?}", uri); + self.file = match *uri { + RequestUri::AbsolutePath { ref path, .. } => { + self.app.file(&self.extract_path(path)) + }, + RequestUri::AbsoluteUri(ref url) => { + self.app.file(&self.extract_path(url.path())) + }, + _ => None, + }.map_or_else(|| ServedFile::new(self.safe_to_embed_on.clone()), |f| ServedFile::File(f)); + } +} + impl PageHandler { fn extract_path(&self, path: &str) -> String { let app_id = &self.path.app_id; @@ -124,15 +145,7 @@ impl PageHandler { impl server::Handler for PageHandler { fn on_request(&mut self, req: server::Request) -> Next { - self.file = match *req.uri() { - RequestUri::AbsolutePath { ref path, .. } => { - self.app.file(&self.extract_path(path)) - }, - RequestUri::AbsoluteUri(ref url) => { - self.app.file(&self.extract_path(url.path())) - }, - _ => None, - }.map_or_else(|| ServedFile::new(self.safe_to_embed_on.clone()), |f| ServedFile::File(f)); + self.set_uri(req.uri()); Next::write() } diff --git a/dapps/src/page/local.rs b/dapps/src/page/local.rs index 77c91019d..e8ab9ce14 100644 --- a/dapps/src/page/local.rs +++ b/dapps/src/page/local.rs @@ -18,7 +18,7 @@ use mime_guess; use std::io::{Seek, Read, SeekFrom}; use std::fs; use std::path::{Path, PathBuf}; -use page::handler::{self, PageCache}; +use page::handler::{self, PageCache, PageHandlerWaiting}; use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler}; #[derive(Debug, Clone)] @@ -54,6 +54,36 @@ impl LocalPageEndpoint { pub fn path(&self) -> PathBuf { self.path.clone() } + + fn page_handler_with_mime(&self, path: EndpointPath, mime: &str) -> handler::PageHandler { + handler::PageHandler { + app: LocalSingleFile { path: self.path.clone(), mime: mime.into() }, + prefix: None, + path: path, + file: handler::ServedFile::new(None), + safe_to_embed_on: self.embeddable_on.clone(), + cache: self.cache, + } + } + + fn page_handler(&self, path: EndpointPath) -> handler::PageHandler { + handler::PageHandler { + app: LocalDapp { path: self.path.clone() }, + prefix: None, + path: path, + file: handler::ServedFile::new(None), + safe_to_embed_on: self.embeddable_on.clone(), + cache: self.cache, + } + } + + pub fn to_page_handler(&self, path: EndpointPath) -> Box { + if let Some(ref mime) = self.mime { + Box::new(self.page_handler_with_mime(path, mime)) + } else { + Box::new(self.page_handler(path)) + } + } } impl Endpoint for LocalPageEndpoint { @@ -63,23 +93,9 @@ impl Endpoint for LocalPageEndpoint { fn to_handler(&self, path: EndpointPath) -> Box { if let Some(ref mime) = self.mime { - Box::new(handler::PageHandler { - app: LocalSingleFile { path: self.path.clone(), mime: mime.clone() }, - prefix: None, - path: path, - file: handler::ServedFile::new(None), - safe_to_embed_on: self.embeddable_on.clone(), - cache: self.cache, - }) + Box::new(self.page_handler_with_mime(path, mime)) } else { - Box::new(handler::PageHandler { - app: LocalDapp { path: self.path.clone() }, - prefix: None, - path: path, - file: handler::ServedFile::new(None), - safe_to_embed_on: self.embeddable_on.clone(), - cache: self.cache, - }) + Box::new(self.page_handler(path)) } } } diff --git a/dapps/src/page/mod.rs b/dapps/src/page/mod.rs index 9619f1b10..5c2b008f8 100644 --- a/dapps/src/page/mod.rs +++ b/dapps/src/page/mod.rs @@ -21,5 +21,5 @@ mod handler; pub use self::local::LocalPageEndpoint; pub use self::builtin::PageEndpoint; -pub use self::handler::PageCache; +pub use self::handler::{PageCache, PageHandlerWaiting};