diff --git a/components/net/blob_loader.rs b/components/net/blob_loader.rs index bb9537e48dd..9bd4eec04c9 100644 --- a/components/net/blob_loader.rs +++ b/components/net/blob_loader.rs @@ -18,6 +18,7 @@ use resource_thread::{send_error, start_sending_sniffed_opt}; use resource_thread::CancellationListener; use std::boxed::FnBox; use std::sync::Arc; +use url::Url; use util::thread::spawn_named; // TODO: Check on GET @@ -119,3 +120,72 @@ fn load_blob send_error(load_data.url.clone(), format_err, start_chan); } } + +/// https://fetch.spec.whatwg.org/#concept-basic-fetch (partial) +// TODO: make async. +pub fn load_blob_sync + (url: Url, + filemanager: FileManager) + -> Result<(Headers, Vec), NetworkError> { + let (id, origin) = match parse_blob_url(&url) { + Ok((id, origin, _fragment)) => (id, origin), + Err(()) => { + let e = format!("Invalid blob URL format {:?}", url); + return Err(NetworkError::Internal(e)); + } + }; + + let (sender, receiver) = ipc::channel().unwrap(); + let check_url_validity = true; + let msg = FileManagerThreadMsg::ReadFile(sender, id, check_url_validity, origin); + let _ = filemanager.handle(msg, None); + + let blob_buf = match receiver.recv().unwrap() { + Ok(ReadFileProgress::Meta(blob_buf)) => blob_buf, + Ok(_) => { + return Err(NetworkError::Internal("Invalid filemanager reply".to_string())); + } + Err(e) => { + return Err(NetworkError::Internal(format!("{:?}", e))); + } + }; + + let content_type: Mime = blob_buf.type_string.parse().unwrap_or(mime!(Text / Plain)); + let charset = content_type.get_param(Attr::Charset); + + let mut headers = Headers::new(); + + if let Some(name) = blob_buf.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()) + ] + }); + } + + // Basic fetch, Step 4. + headers.set(ContentLength(blob_buf.size as u64)); + // Basic fetch, Step 5. + headers.set(ContentType(content_type.clone())); + + let mut bytes = blob_buf.bytes; + loop { + match receiver.recv().unwrap() { + Ok(ReadFileProgress::Partial(ref mut new_bytes)) => { + bytes.append(new_bytes); + } + Ok(ReadFileProgress::EOF) => { + return Ok((headers, bytes)); + } + Ok(_) => { + return Err(NetworkError::Internal("Invalid filemanager reply".to_string())); + } + Err(e) => { + return Err(NetworkError::Internal(format!("{:?}", e))); + } + } + } +} diff --git a/components/net/fetch/methods.rs b/components/net/fetch/methods.rs index 717ddb784e1..0d5b2d2835e 100644 --- a/components/net/fetch/methods.rs +++ b/components/net/fetch/methods.rs @@ -2,6 +2,7 @@ * 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::load_blob_sync; use connector::create_http_connector; use data_loader::decode; use devtools_traits::DevtoolsControlMsg; @@ -464,7 +465,29 @@ fn basic_fetch(request: Rc, } }, - "blob" | "ftp" => { + "blob" => { + println!("Loading blob {}", url.as_str()); + // Step 2. + if *request.method.borrow() != Method::Get { + return Response::network_error(); + } + + match load_blob_sync(url.clone(), context.filemanager.clone()) { + Ok((headers, bytes)) => { + let mut response = Response::new(); + response.url = Some(url.clone()); + response.headers = headers; + *response.body.lock().unwrap() = ResponseBody::Done(bytes); + response + }, + Err(e) => { + debug!("Failed to load {}: {:?}", url, e); + Response::network_error() + }, + } + }, + + "ftp" => { // XXXManishearth handle these panic!("Unimplemented scheme for Fetch") }, diff --git a/tests/unit/net/fetch.rs b/tests/unit/net/fetch.rs index 10156af5486..60f6456ed0a 100644 --- a/tests/unit/net/fetch.rs +++ b/tests/unit/net/fetch.rs @@ -167,6 +167,48 @@ fn test_fetch_data() { } } +#[test] +fn test_fetch_blob() { + use ipc_channel::ipc; + use net_traits::blob_url_store::BlobBuf; + use net_traits::filemanager_thread::FileManagerThreadMsg; + + let context = new_fetch_context(None); + + let bytes = b"content"; + let blob_buf = BlobBuf { + filename: Some("test.txt".into()), + type_string: "text/plain".into(), + size: bytes.len() as u64, + bytes: bytes.to_vec(), + }; + + let origin = Url::parse("http://www.example.org/").unwrap(); + + let (sender, receiver) = ipc::channel().unwrap(); + let message = FileManagerThreadMsg::PromoteMemory(blob_buf, true, sender, "http://www.example.org".into()); + context.filemanager.handle(message, None); + let id = receiver.recv().unwrap().unwrap(); + let url = Url::parse(&format!("blob:{}{}", origin.as_str(), id.simple())).unwrap(); + + + let request = Request::new(url, Some(Origin::Origin(origin.origin())), false, None); + let fetch_response = fetch(Rc::new(request), &mut None, context); + + assert!(!fetch_response.is_network_error()); + + assert_eq!(fetch_response.headers.len(), 2); + + let content_type: &ContentType = fetch_response.headers.get().unwrap(); + assert_eq!(**content_type, Mime(TopLevel::Text, SubLevel::Plain, vec![])); + + let content_length: &ContentLength = fetch_response.headers.get().unwrap(); + assert_eq!(**content_length, bytes.len() as u64); + + assert_eq!(*fetch_response.body.lock().unwrap(), + ResponseBody::Done(bytes.to_vec())); +} + #[test] fn test_fetch_file() { let mut path = resources_dir_path().expect("Cannot find resource dir"); diff --git a/tests/wpt/metadata/FileAPI/blob/Blob-XHR-revoke.html.ini b/tests/wpt/metadata/FileAPI/blob/Blob-XHR-revoke.html.ini index 6fb7d0ba5ca..c427a661f26 100644 --- a/tests/wpt/metadata/FileAPI/blob/Blob-XHR-revoke.html.ini +++ b/tests/wpt/metadata/FileAPI/blob/Blob-XHR-revoke.html.ini @@ -1,4 +1,6 @@ [Blob-XHR-revoke.html] type: testharness - expected: CRASH bug: https://github.com/servo/servo/issues/10539 + [Revoking blob URL used with XMLHttpRequest] + expected: FAIL + diff --git a/tests/wpt/metadata/fetch/api/basic/scheme-blob-worker.html.ini b/tests/wpt/metadata/fetch/api/basic/scheme-blob-worker.html.ini index 74240439891..7c2d1d343b3 100644 --- a/tests/wpt/metadata/fetch/api/basic/scheme-blob-worker.html.ini +++ b/tests/wpt/metadata/fetch/api/basic/scheme-blob-worker.html.ini @@ -1,3 +1,6 @@ [scheme-blob-worker.html] type: testharness - expected: CRASH + [Fetching [GET\] URL.createObjectURL(blob) is OK] + bug: https://github.com/servo/servo/issues/13766 + expected: FAIL + diff --git a/tests/wpt/metadata/fetch/api/basic/scheme-blob.html.ini b/tests/wpt/metadata/fetch/api/basic/scheme-blob.html.ini index ef3371f4e38..599e25f7f0b 100644 --- a/tests/wpt/metadata/fetch/api/basic/scheme-blob.html.ini +++ b/tests/wpt/metadata/fetch/api/basic/scheme-blob.html.ini @@ -1,3 +1,6 @@ [scheme-blob.html] type: testharness - expected: CRASH + [Fetching [GET\] URL.createObjectURL(blob) is OK] + bug: https://github.com/servo/servo/issues/13766 + expected: FAIL +