mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
implements data-url fetching
This commit is contained in:
parent
9a8d62286c
commit
3e74164e5f
3 changed files with 85 additions and 23 deletions
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
},
|
||||
|
|
|
@ -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};
|
||||
|
@ -108,6 +109,33 @@ fn test_fetch_aboutblank() {
|
|||
assert!(*fetch_response.body.lock().unwrap() == ResponseBody::Done(vec![]));
|
||||
}
|
||||
|
||||
#[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() {
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue