diff options
author | Rahul Sharma <rsconceptx@gmail.com> | 2016-03-29 21:54:19 +0530 |
---|---|---|
committer | Rahul Sharma <rsconceptx@gmail.com> | 2016-04-01 09:49:08 +0530 |
commit | 3e74164e5f79a2eb177f778c7f8bedb72514673f (patch) | |
tree | 4dea6aa8e51233e02a565b142c1dcc12fa05e7b3 | |
parent | 9a8d62286c6556fca7471f2db37f896174bc4d9e (diff) | |
download | servo-3e74164e5f79a2eb177f778c7f8bedb72514673f.tar.gz servo-3e74164e5f79a2eb177f778c7f8bedb72514673f.zip |
implements data-url fetching
-rw-r--r-- | components/net/data_loader.rs | 59 | ||||
-rw-r--r-- | components/net/fetch/methods.rs | 19 | ||||
-rw-r--r-- | tests/unit/net/fetch.rs | 30 |
3 files changed, 85 insertions, 23 deletions
diff --git a/components/net/data_loader.rs b/components/net/data_loader.rs index f8386451deb..50452302627 100644 --- a/components/net/data_loader.rs +++ b/components/net/data_loader.rs @@ -4,12 +4,14 @@ use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value}; use mime_classifier::MIMEClassifier; -use net_traits::ProgressMsg::{Done, Payload}; -use net_traits::{LoadConsumer, LoadData, Metadata}; +use net_traits::LoadConsumer; +use net_traits::ProgressMsg::{Payload, Done}; +use net_traits::{LoadData, Metadata}; use resource_thread::{CancellationListener, send_error, start_sending_sniffed_opt}; use rustc_serialize::base64::FromBase64; use std::sync::Arc; use url::SchemeData; +use url::Url; use url::percent_encoding::percent_decode; pub fn factory(load_data: LoadData, @@ -23,17 +25,19 @@ pub fn factory(load_data: LoadData, load(load_data, senders, classifier, cancel_listener) } -pub fn load(load_data: LoadData, - start_chan: LoadConsumer, - classifier: Arc<MIMEClassifier>, - cancel_listener: CancellationListener) { - let url = load_data.url; - assert!(&*url.scheme == "data"); +pub enum DecodeError { + InvalidDataUri, + NonBase64DataUri, +} +pub type DecodeData = (Mime, Vec<u8>); + +pub fn decode(url: &Url) -> Result<DecodeData, DecodeError> { + assert!(&*url.scheme == "data"); // Split out content type and data. let mut scheme_data = match url.scheme_data { SchemeData::NonRelative(ref scheme_data) => scheme_data.clone(), - _ => panic!("Expected a non-relative scheme URL.") + _ => panic!("Expected a non-relative scheme URL."), }; match url.query { Some(ref query) => { @@ -44,8 +48,7 @@ pub fn load(load_data: LoadData, } let parts: Vec<&str> = scheme_data.splitn(2, ',').collect(); if parts.len() != 2 { - send_error(url, "invalid data uri".to_owned(), start_chan); - return; + return Err(DecodeError::InvalidDataUri); } // ";base64" must come at the end of the content type, per RFC 2397. @@ -69,31 +72,45 @@ pub fn load(load_data: LoadData, vec!((Attr::Charset, Value::Ext("US-ASCII".to_owned()))))); } - if cancel_listener.is_cancelled() { - return; - } - let bytes = percent_decode(parts[1].as_bytes()); let bytes = if is_base64 { // FIXME(#2909): It’s unclear what to do with non-alphabet characters, // but Acid 3 apparently depends on spaces being ignored. let bytes = bytes.into_iter().filter(|&b| b != ' ' as u8).collect::<Vec<u8>>(); match bytes.from_base64() { - Err(..) => return send_error(url, "non-base64 data uri".to_owned(), start_chan), + Err(..) => return Err(DecodeError::NonBase64DataUri), Ok(data) => data, } } else { bytes }; + Ok((content_type.unwrap(), bytes)) +} - let mut metadata = Metadata::default(url); - metadata.set_content_type(content_type.as_ref()); - if let Ok(chan) = start_sending_sniffed_opt(start_chan, +pub fn load(load_data: LoadData, + start_chan: LoadConsumer, + classifier: Arc<MIMEClassifier>, + cancel_listener: CancellationListener) { + let url = load_data.url; + + if cancel_listener.is_cancelled() { + return; + } + + match decode(&url) { + Ok((content_type, bytes)) => { + let mut metadata = Metadata::default(url); + metadata.set_content_type(Some(content_type).as_ref()); + if let Ok(chan) = start_sending_sniffed_opt(start_chan, metadata, classifier, &bytes, load_data.context) { - let _ = chan.send(Payload(bytes)); - let _ = chan.send(Done(Ok(()))); + let _ = chan.send(Payload(bytes)); + let _ = chan.send(Done(Ok(()))); + } + }, + Err(DecodeError::InvalidDataUri) => send_error(url, "invalid data uri".to_owned(), start_chan), + Err(DecodeError::NonBase64DataUri) => send_error(url, "non-base64 data uri".to_owned(), start_chan), } } diff --git a/components/net/fetch/methods.rs b/components/net/fetch/methods.rs index 976b1e0b5ee..7c1cb5e3416 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 data_loader::decode; use fetch::cors_cache::{BasicCORSCache, CORSCache, CacheRequestDetails}; use http_loader::{NetworkHttpRequestFactory, create_http_connector, obtain_response}; use hyper::header::{Accept, CacheControl, IfMatch, IfRange, IfUnmodifiedSince, Location}; @@ -305,7 +306,23 @@ fn basic_fetch(request: Rc<Request>) -> Response { http_fetch(request.clone(), BasicCORSCache::new(), false, false, false) }, - "blob" | "data" | "file" | "ftp" => { + "data" => { + if *request.method.borrow() == Method::Get { + match decode(&url) { + Ok((mime, bytes)) => { + let mut response = Response::new(); + *response.body.lock().unwrap() = ResponseBody::Done(bytes); + response.headers.set(ContentType(mime)); + response + }, + Err(_) => Response::network_error() + } + } else { + Response::network_error() + } + }, + + "blob" | "file" | "ftp" => { // XXXManishearth handle these panic!("Unimplemented scheme for Fetch") }, diff --git a/tests/unit/net/fetch.rs b/tests/unit/net/fetch.rs index 1a6168f77d6..3cf9cd84f9d 100644 --- a/tests/unit/net/fetch.rs +++ b/tests/unit/net/fetch.rs @@ -6,14 +6,15 @@ use hyper::header::{AccessControlAllowHeaders, AccessControlAllowOrigin}; use hyper::header::{CacheControl, ContentLanguage, ContentType, Expires, LastModified}; use hyper::header::{Headers, HttpDate, Location, SetCookie, Pragma}; use hyper::method::Method; +use hyper::mime::{Mime, TopLevel, SubLevel}; use hyper::server::{Handler, Listening, Server}; use hyper::server::{Request as HyperRequest, Response as HyperResponse}; use hyper::status::StatusCode; use hyper::uri::RequestUri; use net::fetch::methods::{fetch, fetch_async}; +use net_traits::AsyncFetchListener; use net_traits::request::{Origin, RedirectMode, Referer, Request, RequestMode}; use net_traits::response::{CacheState, Response, ResponseBody, ResponseType}; -use net_traits::{AsyncFetchListener}; use std::rc::Rc; use std::sync::mpsc::{Sender, channel}; use std::sync::{Arc, Mutex}; @@ -109,6 +110,33 @@ fn test_fetch_aboutblank() { } #[test] +fn test_fetch_data() { + + let url = Url::parse("data:text/html,<p>Servo</p>").unwrap(); + let origin = Origin::Origin(url.origin()); + let request = Request::new(url, Some(origin), false); + request.same_origin_data.set(true); + let expected_resp_body = "<p>Servo</p>".to_owned(); + let fetch_response = fetch(Rc::new(request)); + + assert!(!fetch_response.is_network_error()); + assert_eq!(fetch_response.headers.len(), 1); + let content_type: &ContentType = fetch_response.headers.get().unwrap(); + assert!(**content_type == Mime(TopLevel::Text, SubLevel::Html, vec![])); + let resp_body = fetch_response.body.lock().unwrap(); + + match *resp_body { + ResponseBody::Done(ref val) => { + assert_eq!(val, &expected_resp_body.into_bytes()); + } + ResponseBody::Receiving(_) => { + panic!(); + }, + ResponseBody::Empty => panic!(), + } +} + +#[test] fn test_fetch_response_is_basic_filtered() { static MESSAGE: &'static [u8] = b""; |