From 679cf0a5e2af069b58825309fdd10be676c32433 Mon Sep 17 00:00:00 2001 From: Zhen Zhang Date: Wed, 1 Jun 2016 10:09:07 +0800 Subject: [PATCH] Add blob loader --- components/net/blob_loader.rs | 87 +++++++++++++++++++++ components/net/filemanager_thread.rs | 48 ++++++++++++ components/net/lib.rs | 1 + components/net_traits/blob_url_store.rs | 50 ++++++++++++ components/net_traits/filemanager_thread.rs | 4 + components/net_traits/lib.rs | 1 + 6 files changed, 191 insertions(+) create mode 100644 components/net/blob_loader.rs create mode 100644 components/net_traits/blob_url_store.rs diff --git a/components/net/blob_loader.rs b/components/net/blob_loader.rs new file mode 100644 index 00000000000..00b7f4ae083 --- /dev/null +++ b/components/net/blob_loader.rs @@ -0,0 +1,87 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use filemanager_thread::BlobURLStore; +use hyper::header::{DispositionType, ContentDisposition, DispositionParam}; +use hyper::header::{Headers, ContentType, ContentLength, Charset}; +use hyper::http::RawStatus; +use mime::{Mime, Attr}; +use mime_classifier::MIMEClassifier; +use net_traits::ProgressMsg::Done; +use net_traits::blob_url_store::{parse_blob_url, BlobURLStoreEntry, BlobURLStoreError}; +use net_traits::response::HttpsState; +use net_traits::{LoadConsumer, LoadData, Metadata, NetworkError}; +use resource_thread::{send_error, start_sending_sniffed_opt}; +use std::str; +use std::sync::{Arc, RwLock}; + + +// TODO: Check on GET +// https://w3c.github.io/FileAPI/#requestResponseModel + +pub fn load(load_data: LoadData, consumer: LoadConsumer, + blob_url_store: Arc>, + classifier: Arc) { // XXX: Move it into net process later + + match parse_blob_url(&load_data.url) { + None => { + let format_err = NetworkError::Internal(format!("Invalid blob URL format {:?}", load_data.url)); + send_error(load_data.url.clone(), format_err, consumer); + } + Some((uuid, _fragment)) => { + match blob_url_store.read().unwrap().request(uuid, &load_data.url.origin()) { + Ok(entry) => load_blob(&load_data, consumer, classifier, entry), + Err(e) => { + let err = match e { + BlobURLStoreError::InvalidKey => + format!("Invalid blob URL key {:?}", uuid.simple().to_string()), + BlobURLStoreError::InvalidOrigin => + format!("Invalid blob URL origin {:?}", load_data.url.origin()), + }; + send_error(load_data.url.clone(), NetworkError::Internal(err), consumer); + } + } + } + } +} + +fn load_blob(load_data: &LoadData, + start_chan: LoadConsumer, + classifier: Arc, + entry: &BlobURLStoreEntry) { + let content_type: Mime = entry.type_string.parse().unwrap_or(mime!(Text / Plain)); + let charset = content_type.get_param(Attr::Charset); + + let mut headers = Headers::new(); + + if let Some(ref name) = entry.filename { + let charset = charset.and_then(|c| c.as_str().parse().ok()); + headers.set(ContentDisposition { + disposition: DispositionType::Inline, + parameters: vec![ + DispositionParam::Filename(charset.unwrap_or(Charset::Us_Ascii), + None, name.as_bytes().to_vec()) + ] + }); + } + + headers.set(ContentType(content_type.clone())); + headers.set(ContentLength(entry.size)); + + let metadata = Metadata { + final_url: load_data.url.clone(), + content_type: Some(ContentType(content_type.clone())), + charset: charset.map(|c| c.as_str().to_string()), + headers: Some(headers), + // https://w3c.github.io/FileAPI/#TwoHundredOK + status: Some(RawStatus(200, "OK".into())), + https_state: HttpsState::None, + }; + + if let Ok(chan) = + start_sending_sniffed_opt(start_chan, metadata, classifier, + &entry.bytes, load_data.context.clone()) { + let _ = chan.send(Done(Ok(()))); + } +} diff --git a/components/net/filemanager_thread.rs b/components/net/filemanager_thread.rs index fc65446a587..9c741d00d49 100644 --- a/components/net/filemanager_thread.rs +++ b/components/net/filemanager_thread.rs @@ -2,20 +2,27 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use blob_loader; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; +use mime_classifier::MIMEClassifier; use mime_guess::guess_mime_type_opt; +use net_traits::blob_url_store::{BlobURLStoreEntry, BlobURLStoreError}; use net_traits::filemanager_thread::{FileManagerThreadMsg, FileManagerResult}; use net_traits::filemanager_thread::{SelectedFile, FileManagerThreadError}; use std::collections::HashMap; use std::fs::File; use std::io::Read; use std::path::{Path, PathBuf}; +use std::sync::{Arc, RwLock}; +use url::Origin; use util::thread::spawn_named; use uuid::Uuid; pub struct FileManager { receiver: IpcReceiver, idmap: HashMap, + classifier: Arc, + blob_url_store: Arc>, } pub trait FileManagerThreadFactory { @@ -41,6 +48,8 @@ impl FileManager { FileManager { receiver: recv, idmap: HashMap::new(), + classifier: Arc::new(MIMEClassifier::new()), + blob_url_store: Arc::new(RwLock::new(BlobURLStore::new())), } } @@ -52,6 +61,11 @@ impl FileManager { FileManagerThreadMsg::SelectFiles(sender) => self.select_files(sender), FileManagerThreadMsg::ReadFile(sender, id) => self.read_file(sender, id), FileManagerThreadMsg::DeleteFileID(id) => self.delete_fileid(id), + FileManagerThreadMsg::LoadBlob(load_data, consumer) => { + blob_loader::load(load_data, consumer, + self.blob_url_store.clone(), + self.classifier.clone()); + }, FileManagerThreadMsg::Exit => break, } } @@ -156,3 +170,37 @@ impl FileManager { self.idmap.remove(&id); } } + +pub struct BlobURLStore { + entries: HashMap, +} + +impl BlobURLStore { + pub fn new() -> BlobURLStore { + BlobURLStore { + entries: HashMap::new(), + } + } + + pub fn request(&self, id: Uuid, origin: &Origin) -> Result<&BlobURLStoreEntry, BlobURLStoreError> { + match self.entries.get(&id) { + Some(ref pair) => { + if pair.0 == *origin { + Ok(&pair.1) + } else { + Err(BlobURLStoreError::InvalidOrigin) + } + } + None => Err(BlobURLStoreError::InvalidKey) + } + } + + pub fn add_entry(&mut self, id: Uuid, origin: Origin, blob: BlobURLStoreEntry) { + self.entries.insert(id, (origin, blob)); + } + + pub fn delete_entry(&mut self, id: Uuid) { + self.entries.remove(&id); + } +} + diff --git a/components/net/lib.rs b/components/net/lib.rs index 24912cfa2e5..52c3560b1fb 100644 --- a/components/net/lib.rs +++ b/components/net/lib.rs @@ -49,6 +49,7 @@ extern crate webrender_traits; extern crate websocket; pub mod about_loader; +pub mod blob_loader; pub mod bluetooth_thread; pub mod chrome_loader; pub mod connector; diff --git a/components/net_traits/blob_url_store.rs b/components/net_traits/blob_url_store.rs new file mode 100644 index 00000000000..85fa81aa341 --- /dev/null +++ b/components/net_traits/blob_url_store.rs @@ -0,0 +1,50 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use ipc_channel::ipc::IpcSender; +use std::str::FromStr; +use url::Url; +use uuid::Uuid; + +/// Errors returns to BlobURLStoreMsg::Request +#[derive(Clone, Serialize, Deserialize)] +pub enum BlobURLStoreError { + /// Invalid UUID key + InvalidKey, + /// Invalid URL origin + InvalidOrigin, +} + +/// Blob URL store entry, a packaged form of Blob DOM object +#[derive(Clone, Serialize, Deserialize)] +pub struct BlobURLStoreEntry { + /// MIME type string + pub type_string: String, + /// Some filename if the backend of Blob is a file + pub filename: Option, + /// Size of content in bytes + pub size: u64, + /// Content of blob + pub bytes: Vec, +} + +/// Message-passing style interface between store and loader +#[derive(Serialize, Deserialize)] +pub enum BlobURLStoreMsg { + /// Request for an blob entry identified by uuid + Request(Uuid, IpcSender>), +} + +/// Parse URL as Blob URL scheme's definition +/// https://w3c.github.io/FileAPI/#DefinitionOfScheme +pub fn parse_blob_url(url: &Url) -> Option<(Uuid, Option<&str>)> { + url.path_segments().and_then(|mut segments| { + let id_str = match (segments.next(), segments.next()) { + (Some(s), None) => s, + _ => return None, + }; + + Uuid::from_str(id_str).map(|id| (id, url.fragment())).ok() + }) +} diff --git a/components/net_traits/filemanager_thread.rs b/components/net_traits/filemanager_thread.rs index 2ced0701eff..07a6180834f 100644 --- a/components/net_traits/filemanager_thread.rs +++ b/components/net_traits/filemanager_thread.rs @@ -4,6 +4,7 @@ use ipc_channel::ipc::IpcSender; use std::path::PathBuf; +use super::{LoadConsumer, LoadData}; use uuid::Uuid; #[derive(Debug, Deserialize, Serialize)] @@ -29,6 +30,9 @@ pub enum FileManagerThreadMsg { /// Delete the FileID entry DeleteFileID(Uuid), + /// Load resource by Blob URL + LoadBlob(LoadData, LoadConsumer), + /// Shut down this thread Exit, } diff --git a/components/net_traits/lib.rs b/components/net_traits/lib.rs index 4eab3e725e9..77ba143e20b 100644 --- a/components/net_traits/lib.rs +++ b/components/net_traits/lib.rs @@ -43,6 +43,7 @@ use storage_thread::StorageThreadMsg; use url::Url; use websocket::header; +pub mod blob_url_store; pub mod bluetooth_scanfilter; pub mod bluetooth_thread; pub mod filemanager_thread;