mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-21 17:25:36 +00:00
servo: Merge #4519 - Cookie support (from jdm:cookies); r=Ms2ger
As specified in http://tools.ietf.org/html/rfc6265. Requires https://github.com/servo/cookie-rs/pull/1. Tested against http://www.joshmatthews.net/cookie.php, http://www.html-kit.com/tools/cookietester/, https://github.com/login, and https://mobile.twitter.com/session/new . Source-Repo: https://github.com/servo/servo Source-Revision: 7e3f504d94ffb77ec6148166d2ab73978e1c71c8
This commit is contained in:
parent
18cd53c8d1
commit
21a974a25d
@ -9,3 +9,6 @@ path = "lib.rs"
|
||||
|
||||
[dependencies.msg]
|
||||
path = "../msg"
|
||||
|
||||
[dependencies.util]
|
||||
path = "../util"
|
||||
|
@ -22,7 +22,7 @@ git = "https://github.com/servo/rust-geom"
|
||||
|
||||
[dependencies.hyper]
|
||||
git = "https://github.com/servo/hyper"
|
||||
branch = "servo"
|
||||
branch = "old_servo_new_cookies"
|
||||
|
||||
[dependencies.layers]
|
||||
git = "https://github.com/servo/rust-layers"
|
||||
@ -34,4 +34,4 @@ git = "https://github.com/servo/rust-core-foundation"
|
||||
git = "https://github.com/servo/rust-io-surface"
|
||||
|
||||
[dependencies]
|
||||
url = "0.2.16"
|
||||
url = "0.2.16"
|
||||
|
@ -15,7 +15,11 @@ git = "https://github.com/servo/rust-geom"
|
||||
|
||||
[dependencies.hyper]
|
||||
git = "https://github.com/servo/hyper"
|
||||
branch = "servo"
|
||||
branch = "old_servo_new_cookies"
|
||||
|
||||
[dependencies.cookie]
|
||||
git = "https://github.com/servo/cookie-rs"
|
||||
branch = "lenientparse_backport"
|
||||
|
||||
[dependencies.png]
|
||||
git = "https://github.com/servo/rust-png"
|
||||
@ -26,4 +30,4 @@ git = "https://github.com/servo/rust-stb-image"
|
||||
[dependencies]
|
||||
url = "0.2.16"
|
||||
time = "0.1.12"
|
||||
openssl="0.2.15"
|
||||
openssl="0.2.15"
|
||||
|
@ -26,7 +26,7 @@ pub fn factory(mut load_data: LoadData, start_chan: Sender<TargetedLoadResponse>
|
||||
content_type: Some(("text".to_string(), "html".to_string())),
|
||||
charset: Some("utf-8".to_string()),
|
||||
headers: None,
|
||||
status: Some(RawStatus(200, "OK".to_owned()))
|
||||
status: Some(RawStatus(200, "OK".to_owned())),
|
||||
});
|
||||
chan.send(Done(Ok(()))).unwrap();
|
||||
return
|
||||
|
233
servo/components/net/cookie.rs
Normal file
233
servo/components/net/cookie.rs
Normal file
@ -0,0 +1,233 @@
|
||||
/* 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/. */
|
||||
|
||||
//! Implementation of cookie creation and matching as specified by
|
||||
//! http://tools.ietf.org/html/rfc6265
|
||||
|
||||
use cookie_storage::CookieSource;
|
||||
use pub_domains::PUB_DOMAINS;
|
||||
|
||||
use cookie_rs;
|
||||
use time::{Tm, now, at, Timespec};
|
||||
use url::Url;
|
||||
use std::borrow::ToOwned;
|
||||
use std::i64;
|
||||
use std::io::net::ip::IpAddr;
|
||||
use std::time::Duration;
|
||||
|
||||
/// A stored cookie that wraps the definition in cookie-rs. This is used to implement
|
||||
/// various behaviours defined in the spec that rely on an associated request URL,
|
||||
/// which cookie-rs and hyper's header parsing do not support.
|
||||
#[derive(Clone, Show)]
|
||||
pub struct Cookie {
|
||||
pub cookie: cookie_rs::Cookie,
|
||||
pub host_only: bool,
|
||||
pub persistent: bool,
|
||||
pub creation_time: Tm,
|
||||
pub last_access: Tm,
|
||||
pub expiry_time: Tm,
|
||||
}
|
||||
|
||||
impl Cookie {
|
||||
/// http://tools.ietf.org/html/rfc6265#section-5.3
|
||||
pub fn new_wrapped(mut cookie: cookie_rs::Cookie, request: &Url, source: CookieSource)
|
||||
-> Option<Cookie> {
|
||||
// Step 3
|
||||
let (persistent, expiry_time) = match (&cookie.max_age, &cookie.expires) {
|
||||
(&Some(max_age), _) => (true, at(now().to_timespec() + Duration::seconds(max_age as i64))),
|
||||
(_, &Some(expires)) => (true, expires),
|
||||
_ => (false, at(Timespec::new(i64::MAX, 0)))
|
||||
};
|
||||
|
||||
let url_host = request.host().map(|host| host.serialize()).unwrap_or("".to_owned());
|
||||
|
||||
// Step 4
|
||||
let mut domain = cookie.domain.clone().unwrap_or("".to_owned());
|
||||
|
||||
// Step 5
|
||||
match PUB_DOMAINS.iter().find(|&x| domain == *x) {
|
||||
Some(val) if *val == url_host => domain = "".to_string(),
|
||||
Some(_) => return None,
|
||||
None => {}
|
||||
}
|
||||
|
||||
// Step 6
|
||||
let host_only = if !domain.is_empty() {
|
||||
if !Cookie::domain_match(url_host.as_slice(), domain.as_slice()) {
|
||||
return None;
|
||||
} else {
|
||||
cookie.domain = Some(domain);
|
||||
false
|
||||
}
|
||||
} else {
|
||||
cookie.domain = Some(url_host);
|
||||
true
|
||||
};
|
||||
|
||||
// Step 7
|
||||
let mut path = cookie.path.unwrap_or("".to_owned());
|
||||
if path.is_empty() || path.char_at(0) != '/' {
|
||||
let url_path = request.serialize_path();
|
||||
let url_path = url_path.as_ref().map(|path| path.as_slice());
|
||||
path = Cookie::default_path(url_path.unwrap_or(""));
|
||||
}
|
||||
cookie.path = Some(path);
|
||||
|
||||
|
||||
// Step 10
|
||||
if cookie.httponly && source != CookieSource::HTTP {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(Cookie {
|
||||
cookie: cookie,
|
||||
host_only: host_only,
|
||||
persistent: persistent,
|
||||
creation_time: now(),
|
||||
last_access: now(),
|
||||
expiry_time: expiry_time,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn touch(&mut self) {
|
||||
self.last_access = now();
|
||||
}
|
||||
|
||||
// http://tools.ietf.org/html/rfc6265#section-5.1.4
|
||||
fn default_path(request_path: &str) -> String {
|
||||
if request_path == "" || request_path.char_at(0) != '/' ||
|
||||
request_path.chars().filter(|&c| c == '/').count() == 1 {
|
||||
"/".to_owned()
|
||||
} else if request_path.ends_with("/") {
|
||||
request_path.slice_to(request_path.len()-1).to_owned()
|
||||
} else {
|
||||
request_path.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
// http://tools.ietf.org/html/rfc6265#section-5.1.4
|
||||
pub fn path_match(request_path: &str, cookie_path: &str) -> bool {
|
||||
request_path == cookie_path ||
|
||||
( request_path.starts_with(cookie_path) &&
|
||||
( request_path.ends_with("/") || request_path.char_at(cookie_path.len() - 1) == '/' )
|
||||
)
|
||||
}
|
||||
|
||||
// http://tools.ietf.org/html/rfc6265#section-5.1.3
|
||||
pub fn domain_match(string: &str, domain_string: &str) -> bool {
|
||||
if string == domain_string {
|
||||
return true;
|
||||
}
|
||||
if string.ends_with(domain_string)
|
||||
&& string.char_at(string.len()-domain_string.len()-1) == '.'
|
||||
&& string.parse::<IpAddr>().is_none() {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
// http://tools.ietf.org/html/rfc6265#section-5.4 step 1
|
||||
pub fn appropriate_for_url(&self, url: &Url, source: CookieSource) -> bool {
|
||||
let domain = url.host().map(|host| host.serialize());
|
||||
if self.host_only {
|
||||
if self.cookie.domain != domain {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if let (Some(ref domain), &Some(ref cookie_domain)) = (domain, &self.cookie.domain) {
|
||||
if !Cookie::domain_match(domain.as_slice(), cookie_domain.as_slice()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let (Some(ref path), &Some(ref cookie_path)) = (url.serialize_path(), &self.cookie.path) {
|
||||
if !Cookie::path_match(path.as_slice(), cookie_path.as_slice()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if self.cookie.secure && url.scheme != "https".to_string() {
|
||||
return false;
|
||||
}
|
||||
if self.cookie.httponly && source == CookieSource::NonHTTP {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_domain_match() {
|
||||
assert!(Cookie::domain_match("foo.com", "foo.com"));
|
||||
assert!(Cookie::domain_match("bar.foo.com", "foo.com"));
|
||||
assert!(Cookie::domain_match("baz.bar.foo.com", "foo.com"));
|
||||
|
||||
assert!(!Cookie::domain_match("bar.foo.com", "bar.com"));
|
||||
assert!(!Cookie::domain_match("bar.com", "baz.bar.com"));
|
||||
assert!(!Cookie::domain_match("foo.com", "bar.com"));
|
||||
|
||||
assert!(!Cookie::domain_match("bar.com", "bbar.com"));
|
||||
assert!(Cookie::domain_match("235.132.2.3", "235.132.2.3"));
|
||||
assert!(!Cookie::domain_match("235.132.2.3", "1.1.1.1"));
|
||||
assert!(!Cookie::domain_match("235.132.2.3", ".2.3"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_path() {
|
||||
assert!(Cookie::default_path("/foo/bar/baz/").as_slice() == "/foo/bar/baz");
|
||||
assert!(Cookie::default_path("/foo/").as_slice() == "/foo");
|
||||
assert!(Cookie::default_path("/foo").as_slice() == "/");
|
||||
assert!(Cookie::default_path("/").as_slice() == "/");
|
||||
assert!(Cookie::default_path("").as_slice() == "/");
|
||||
assert!(Cookie::default_path("foo").as_slice() == "/");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fn_cookie_constructor() {
|
||||
use cookie_storage::CookieSource;
|
||||
|
||||
let url = &Url::parse("http://example.com/foo").unwrap();
|
||||
|
||||
let gov_url = &Url::parse("http://gov.ac/foo").unwrap();
|
||||
// cookie name/value test
|
||||
assert!(cookie_rs::Cookie::parse(" baz ").is_err());
|
||||
assert!(cookie_rs::Cookie::parse(" = bar ").is_err());
|
||||
assert!(cookie_rs::Cookie::parse(" baz = ").is_ok());
|
||||
|
||||
// cookie domains test
|
||||
let cookie = cookie_rs::Cookie::parse(" baz = bar; Domain = ").unwrap();
|
||||
assert!(Cookie::new_wrapped(cookie.clone(), url, CookieSource::HTTP).is_some());
|
||||
let cookie = Cookie::new_wrapped(cookie, url, CookieSource::HTTP).unwrap();
|
||||
assert!(cookie.cookie.domain.as_ref().unwrap().as_slice() == "example.com");
|
||||
|
||||
// cookie public domains test
|
||||
let cookie = cookie_rs::Cookie::parse(" baz = bar; Domain = gov.ac").unwrap();
|
||||
assert!(Cookie::new_wrapped(cookie.clone(), url, CookieSource::HTTP).is_none());
|
||||
assert!(Cookie::new_wrapped(cookie, gov_url, CookieSource::HTTP).is_some());
|
||||
|
||||
// cookie domain matching test
|
||||
let cookie = cookie_rs::Cookie::parse(" baz = bar ; Secure; Domain = bazample.com").unwrap();
|
||||
assert!(Cookie::new_wrapped(cookie, url, CookieSource::HTTP).is_none());
|
||||
|
||||
let cookie = cookie_rs::Cookie::parse(" baz = bar ; Secure; Path = /foo/bar/").unwrap();
|
||||
assert!(Cookie::new_wrapped(cookie, url, CookieSource::HTTP).is_some());
|
||||
|
||||
let cookie = cookie_rs::Cookie::parse(" baz = bar ; HttpOnly").unwrap();
|
||||
assert!(Cookie::new_wrapped(cookie, url, CookieSource::NonHTTP).is_none());
|
||||
|
||||
let cookie = cookie_rs::Cookie::parse(" baz = bar ; Secure; Path = /foo/bar/").unwrap();
|
||||
let cookie = Cookie::new_wrapped(cookie, url, CookieSource::HTTP).unwrap();
|
||||
assert!(cookie.cookie.value.as_slice() == "bar");
|
||||
assert!(cookie.cookie.name.as_slice() == "baz");
|
||||
assert!(cookie.cookie.secure);
|
||||
assert!(cookie.cookie.path.as_ref().unwrap().as_slice() == "/foo/bar/");
|
||||
assert!(cookie.cookie.domain.as_ref().unwrap().as_slice() == "example.com");
|
||||
assert!(cookie.host_only);
|
||||
|
||||
let u = &Url::parse("http://example.com/foobar").unwrap();
|
||||
let cookie = cookie_rs::Cookie::parse("foobar=value;path=/").unwrap();
|
||||
assert!(Cookie::new_wrapped(cookie, u, CookieSource::HTTP).is_some());
|
||||
}
|
143
servo/components/net/cookie_storage.rs
Normal file
143
servo/components/net/cookie_storage.rs
Normal file
@ -0,0 +1,143 @@
|
||||
/* 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/. */
|
||||
|
||||
//! Implementation of cookie storage as specified in
|
||||
//! http://tools.ietf.org/html/rfc6265
|
||||
|
||||
use url::Url;
|
||||
use cookie::Cookie;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
/// The creator of a given cookie
|
||||
#[derive(PartialEq, Copy)]
|
||||
pub enum CookieSource {
|
||||
/// An HTTP API
|
||||
HTTP,
|
||||
/// A non-HTTP API
|
||||
NonHTTP,
|
||||
}
|
||||
|
||||
pub struct CookieStorage {
|
||||
cookies: Vec<Cookie>
|
||||
}
|
||||
|
||||
impl CookieStorage {
|
||||
pub fn new() -> CookieStorage {
|
||||
CookieStorage {
|
||||
cookies: Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
// http://tools.ietf.org/html/rfc6265#section-5.3
|
||||
pub fn remove(&mut self, cookie: &Cookie, source: CookieSource) -> Result<Option<Cookie>, ()> {
|
||||
// Step 1
|
||||
let position = self.cookies.iter().position(|c| {
|
||||
c.cookie.domain == cookie.cookie.domain &&
|
||||
c.cookie.path == cookie.cookie.path &&
|
||||
c.cookie.name == cookie.cookie.name
|
||||
});
|
||||
|
||||
if let Some(ind) = position {
|
||||
let c = self.cookies.remove(ind);
|
||||
|
||||
// http://tools.ietf.org/html/rfc6265#section-5.3 step 11.2
|
||||
if !c.cookie.httponly || source == CookieSource::HTTP {
|
||||
Ok(Some(c))
|
||||
} else {
|
||||
// Undo the removal.
|
||||
self.cookies.push(c);
|
||||
Err(())
|
||||
}
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
// http://tools.ietf.org/html/rfc6265#section-5.3
|
||||
pub fn push(&mut self, mut cookie: Cookie, source: CookieSource) {
|
||||
let old_cookie = self.remove(&cookie, source);
|
||||
if old_cookie.is_err() {
|
||||
// This new cookie is not allowed to overwrite an existing one.
|
||||
return;
|
||||
}
|
||||
|
||||
if cookie.cookie.value.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 11
|
||||
if let Some(old_cookie) = old_cookie.unwrap() {
|
||||
// Step 11.3
|
||||
cookie.creation_time = old_cookie.creation_time;
|
||||
}
|
||||
|
||||
// Step 12
|
||||
self.cookies.push(cookie);
|
||||
}
|
||||
|
||||
fn cookie_comparator(a: &Cookie, b: &Cookie) -> Ordering {
|
||||
let a_path_len = a.cookie.path.as_ref().map(|p| p.len()).unwrap_or(0);
|
||||
let b_path_len = b.cookie.path.as_ref().map(|p| p.len()).unwrap_or(0);
|
||||
match a_path_len.cmp(&b_path_len) {
|
||||
Ordering::Equal => {
|
||||
let a_creation_time = a.creation_time.to_timespec();
|
||||
let b_creation_time = b.creation_time.to_timespec();
|
||||
a_creation_time.cmp(&b_creation_time)
|
||||
}
|
||||
// Ensure that longer paths are sorted earlier than shorter paths
|
||||
Ordering::Greater => Ordering::Less,
|
||||
Ordering::Less => Ordering::Greater,
|
||||
}
|
||||
}
|
||||
|
||||
// http://tools.ietf.org/html/rfc6265#section-5.4
|
||||
pub fn cookies_for_url(&mut self, url: &Url, source: CookieSource) -> Option<String> {
|
||||
let filterer = |&:c: &&mut Cookie| -> bool {
|
||||
info!(" === SENT COOKIE : {} {} {:?} {:?}", c.cookie.name, c.cookie.value, c.cookie.domain, c.cookie.path);
|
||||
info!(" === SENT COOKIE RESULT {}", c.appropriate_for_url(url, source));
|
||||
// Step 1
|
||||
c.appropriate_for_url(url, source)
|
||||
};
|
||||
|
||||
// Step 2
|
||||
let mut url_cookies: Vec<&mut Cookie> = self.cookies.iter_mut().filter(filterer).collect();
|
||||
url_cookies.sort_by(|a, b| CookieStorage::cookie_comparator(*a, *b));
|
||||
|
||||
let reducer = |&:acc: String, c: &mut &mut Cookie| -> String {
|
||||
// Step 3
|
||||
c.touch();
|
||||
|
||||
// Step 4
|
||||
(match acc.len() {
|
||||
0 => acc,
|
||||
_ => acc + ";"
|
||||
}) + c.cookie.name.as_slice() + "=" + c.cookie.value.as_slice()
|
||||
};
|
||||
let result = url_cookies.iter_mut().fold("".to_string(), reducer);
|
||||
|
||||
info!(" === COOKIES SENT: {}", result);
|
||||
match result.len() {
|
||||
0 => None,
|
||||
_ => Some(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sort_order() {
|
||||
use cookie_rs;
|
||||
let url = &Url::parse("http://example.com/foo").unwrap();
|
||||
let a_wrapped = cookie_rs::Cookie::parse("baz=bar; Path=/foo/bar/").unwrap();
|
||||
let a = Cookie::new_wrapped(a_wrapped.clone(), url, CookieSource::HTTP).unwrap();
|
||||
let a_prime = Cookie::new_wrapped(a_wrapped, url, CookieSource::HTTP).unwrap();
|
||||
let b = cookie_rs::Cookie::parse("baz=bar;Path=/foo/bar/baz/").unwrap();
|
||||
let b = Cookie::new_wrapped(b, url, CookieSource::HTTP).unwrap();
|
||||
|
||||
assert!(b.cookie.path.as_ref().unwrap().len() > a.cookie.path.as_ref().unwrap().len());
|
||||
assert!(CookieStorage::cookie_comparator(&a, &b) == Ordering::Greater);
|
||||
assert!(CookieStorage::cookie_comparator(&b, &a) == Ordering::Less);
|
||||
assert!(CookieStorage::cookie_comparator(&a, &a_prime) == Ordering::Less);
|
||||
assert!(CookieStorage::cookie_comparator(&a_prime, &a) == Ordering::Greater);
|
||||
assert!(CookieStorage::cookie_comparator(&a, &a) == Ordering::Equal);
|
||||
}
|
@ -2,7 +2,9 @@
|
||||
* 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 cookie_storage::CookieSource;
|
||||
use resource_task::{Metadata, TargetedLoadResponse, LoadData, start_sending_opt, ResponseSenders};
|
||||
use resource_task::ControlMsg;
|
||||
use resource_task::ProgressMsg::{Payload, Done};
|
||||
|
||||
use log;
|
||||
@ -13,19 +15,23 @@ use hyper::header::common::{ContentLength, ContentType, Host, Location};
|
||||
use hyper::HttpError;
|
||||
use hyper::method::Method;
|
||||
use hyper::net::HttpConnector;
|
||||
use hyper::status::StatusClass;
|
||||
use hyper::status::{StatusCode, StatusClass};
|
||||
use std::error::Error;
|
||||
use openssl::ssl::{SslContext, SslVerifyMode};
|
||||
use std::io::{IoError, IoErrorKind, Reader};
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::sync::mpsc::{Sender, channel};
|
||||
use std::thunk::Invoke;
|
||||
use util::task::spawn_named;
|
||||
use util::resource_files::resources_dir_path;
|
||||
use url::{Url, UrlParser};
|
||||
|
||||
use std::borrow::ToOwned;
|
||||
|
||||
pub fn factory(load_data: LoadData, start_chan: Sender<TargetedLoadResponse>) {
|
||||
spawn_named("http_loader".to_owned(), move || load(load_data, start_chan))
|
||||
pub fn factory(cookies_chan: Sender<ControlMsg>)
|
||||
-> Box<Invoke<(LoadData, Sender<TargetedLoadResponse>)> + Send> {
|
||||
box move |:(load_data, start_chan)| {
|
||||
spawn_named("http_loader".to_owned(), move || load(load_data, start_chan, cookies_chan))
|
||||
}
|
||||
}
|
||||
|
||||
fn send_error(url: Url, err: String, senders: ResponseSenders) {
|
||||
@ -38,7 +44,7 @@ fn send_error(url: Url, err: String, senders: ResponseSenders) {
|
||||
};
|
||||
}
|
||||
|
||||
fn load(load_data: LoadData, start_chan: Sender<TargetedLoadResponse>) {
|
||||
fn load(mut load_data: LoadData, start_chan: Sender<TargetedLoadResponse>, cookies_chan: Sender<ControlMsg>) {
|
||||
// FIXME: At the time of writing this FIXME, servo didn't have any central
|
||||
// location for configuration. If you're reading this and such a
|
||||
// repository DOES exist, please update this constant to use it.
|
||||
@ -61,13 +67,6 @@ fn load(load_data: LoadData, start_chan: Sender<TargetedLoadResponse>) {
|
||||
return;
|
||||
}
|
||||
|
||||
if redirected_to.contains(&url) {
|
||||
send_error(url, "redirect loop".to_string(), senders);
|
||||
return;
|
||||
}
|
||||
|
||||
redirected_to.insert(url.clone());
|
||||
|
||||
match url.scheme.as_slice() {
|
||||
"http" | "https" => {}
|
||||
_ => {
|
||||
@ -111,15 +110,45 @@ reason: \"certificate verify failed\" }]";
|
||||
|
||||
// Preserve the `host` header set automatically by Request.
|
||||
let host = req.headers().get::<Host>().unwrap().clone();
|
||||
*req.headers_mut() = load_data.headers.clone();
|
||||
|
||||
// Avoid automatically preserving request headers when redirects occur.
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=401564 and
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=216828 .
|
||||
// Only preserve ones which have been explicitly marked as such.
|
||||
if iters == 1 {
|
||||
let mut combined_headers = load_data.headers.clone();
|
||||
combined_headers.extend(load_data.preserved_headers.iter());
|
||||
*req.headers_mut() = combined_headers;
|
||||
} else {
|
||||
*req.headers_mut() = load_data.preserved_headers.clone();
|
||||
}
|
||||
|
||||
req.headers_mut().set(host);
|
||||
|
||||
let (tx, rx) = channel();
|
||||
cookies_chan.send(ControlMsg::GetCookiesForUrl(url.clone(), tx, CookieSource::HTTP));
|
||||
if let Some(cookie_list) = rx.recv().unwrap() {
|
||||
let mut v = Vec::new();
|
||||
v.push(cookie_list.into_bytes());
|
||||
req.headers_mut().set_raw("Cookie".to_owned(), v);
|
||||
}
|
||||
|
||||
// FIXME(seanmonstar): use AcceptEncoding from Hyper once available
|
||||
//if !req.headers.has::<AcceptEncoding>() {
|
||||
// We currently don't support HTTP Compression (FIXME #2587)
|
||||
req.headers_mut().set_raw("Accept-Encoding".to_owned(), vec![b"identity".to_vec()]);
|
||||
//}
|
||||
if log_enabled!(log::INFO) {
|
||||
info!("{}", load_data.method);
|
||||
for header in req.headers().iter() {
|
||||
info!(" - {}", header);
|
||||
}
|
||||
info!("{:?}", load_data.data);
|
||||
}
|
||||
|
||||
// Avoid automatically sending request body if a redirect has occurred.
|
||||
let writer = match load_data.data {
|
||||
Some(ref data) => {
|
||||
Some(ref data) if iters == 1 => {
|
||||
req.headers_mut().set(ContentLength(data.len() as u64));
|
||||
let mut writer = match req.start() {
|
||||
Ok(w) => w,
|
||||
@ -137,7 +166,7 @@ reason: \"certificate verify failed\" }]";
|
||||
};
|
||||
writer
|
||||
},
|
||||
None => {
|
||||
_ => {
|
||||
match load_data.method {
|
||||
Method::Get | Method::Head => (),
|
||||
_ => req.headers_mut().set(ContentLength(0))
|
||||
@ -167,6 +196,16 @@ reason: \"certificate verify failed\" }]";
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(cookies) = response.headers.get_raw("set-cookie") {
|
||||
for cookie in cookies.iter() {
|
||||
if let Ok(cookies) = String::from_utf8(cookie.clone()) {
|
||||
cookies_chan.send(ControlMsg::SetCookiesForUrl(url.clone(),
|
||||
cookies,
|
||||
CookieSource::HTTP));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if response.status.class() == StatusClass::Redirection {
|
||||
match response.headers.get::<Location>() {
|
||||
Some(&Location(ref new_url)) => {
|
||||
@ -193,6 +232,21 @@ reason: \"certificate verify failed\" }]";
|
||||
};
|
||||
info!("redirecting to {}", new_url);
|
||||
url = new_url;
|
||||
|
||||
// According to https://tools.ietf.org/html/rfc7231#section-6.4.2,
|
||||
// historically UAs have rewritten POST->GET on 301 and 302 responses.
|
||||
if load_data.method == Method::Post &&
|
||||
(response.status == StatusCode::MovedPermanently ||
|
||||
response.status == StatusCode::Found) {
|
||||
load_data.method = Method::Get;
|
||||
}
|
||||
|
||||
if redirected_to.contains(&url) {
|
||||
send_error(url, "redirect loop".to_string(), senders);
|
||||
return;
|
||||
}
|
||||
|
||||
redirected_to.insert(url.clone());
|
||||
continue;
|
||||
}
|
||||
None => ()
|
||||
|
@ -568,7 +568,8 @@ mod tests {
|
||||
Url::parse("file:///fake").unwrap()));
|
||||
on_load.invoke(chan);
|
||||
}
|
||||
resource_task::ControlMsg::Exit => break
|
||||
resource_task::ControlMsg::Exit => break,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -730,6 +731,7 @@ mod tests {
|
||||
resource_task_exited_chan.send(());
|
||||
break
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -762,7 +764,6 @@ mod tests {
|
||||
let (image_bin_sent_chan, image_bin_sent) = channel();
|
||||
|
||||
let (resource_task_exited_chan, resource_task_exited) = channel();
|
||||
|
||||
let mock_resource_task = spawn_listener(move |port: Receiver<resource_task::ControlMsg>| {
|
||||
loop {
|
||||
match port.recv().unwrap() {
|
||||
@ -782,6 +783,7 @@ mod tests {
|
||||
resource_task_exited_chan.send(());
|
||||
break
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -11,6 +11,7 @@
|
||||
#![allow(missing_copy_implementations)]
|
||||
#![allow(unstable)]
|
||||
|
||||
extern crate "cookie" as cookie_rs;
|
||||
extern crate collections;
|
||||
extern crate geom;
|
||||
extern crate hyper;
|
||||
@ -38,8 +39,11 @@ pub mod about_loader;
|
||||
pub mod file_loader;
|
||||
pub mod http_loader;
|
||||
pub mod data_loader;
|
||||
pub mod cookie;
|
||||
pub mod cookie_storage;
|
||||
pub mod image_cache_task;
|
||||
pub mod local_image_cache;
|
||||
pub mod pub_domains;
|
||||
pub mod resource_task;
|
||||
pub mod storage_task;
|
||||
mod sniffer_task;
|
||||
|
5959
servo/components/net/pub_domains.rs
Normal file
5959
servo/components/net/pub_domains.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -10,11 +10,13 @@ use file_loader;
|
||||
use http_loader;
|
||||
use sniffer_task;
|
||||
use sniffer_task::SnifferTask;
|
||||
use cookie_storage::{CookieStorage, CookieSource};
|
||||
use cookie;
|
||||
|
||||
use util::task::spawn_named;
|
||||
|
||||
use hyper::header::common::UserAgent;
|
||||
use hyper::header::Headers;
|
||||
use hyper::header::{Headers, Header, SetCookie};
|
||||
use hyper::http::RawStatus;
|
||||
use hyper::method::Method;
|
||||
use hyper::mime::{Mime, Attr};
|
||||
@ -22,10 +24,15 @@ use url::Url;
|
||||
|
||||
use std::borrow::ToOwned;
|
||||
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||
use std::thunk::Invoke;
|
||||
|
||||
pub enum ControlMsg {
|
||||
/// Request the data associated with a particular URL
|
||||
Load(LoadData),
|
||||
/// Store a set of cookies for a given originating URL
|
||||
SetCookiesForUrl(Url, String, CookieSource),
|
||||
/// Retrieve the stored cookies for a given URL
|
||||
GetCookiesForUrl(Url, Sender<Option<String>>, CookieSource),
|
||||
Exit
|
||||
}
|
||||
|
||||
@ -33,7 +40,10 @@ pub enum ControlMsg {
|
||||
pub struct LoadData {
|
||||
pub url: Url,
|
||||
pub method: Method,
|
||||
/// Headers that will apply to the initial request only
|
||||
pub headers: Headers,
|
||||
/// Headers that will apply to the initial request and any redirects
|
||||
pub preserved_headers: Headers,
|
||||
pub data: Option<Vec<u8>>,
|
||||
pub cors: Option<ResourceCORSData>,
|
||||
pub consumer: Sender<LoadResponse>,
|
||||
@ -45,6 +55,7 @@ impl LoadData {
|
||||
url: url,
|
||||
method: Method::Get,
|
||||
headers: Headers::new(),
|
||||
preserved_headers: Headers::new(),
|
||||
data: None,
|
||||
cors: None,
|
||||
consumer: consumer,
|
||||
@ -61,6 +72,7 @@ pub struct ResourceCORSData {
|
||||
}
|
||||
|
||||
/// Metadata about a loaded resource, such as is obtained from HTTP headers.
|
||||
#[deriving(Clone)]
|
||||
pub struct Metadata {
|
||||
/// Final URL after redirects.
|
||||
pub final_url: Url,
|
||||
@ -75,7 +87,7 @@ pub struct Metadata {
|
||||
pub headers: Option<Headers>,
|
||||
|
||||
/// HTTP Status
|
||||
pub status: Option<RawStatus>
|
||||
pub status: Option<RawStatus>,
|
||||
}
|
||||
|
||||
impl Metadata {
|
||||
@ -87,7 +99,7 @@ impl Metadata {
|
||||
charset: None,
|
||||
headers: None,
|
||||
// http://fetch.spec.whatwg.org/#concept-response-status-message
|
||||
status: Some(RawStatus(200, "OK".to_owned()))
|
||||
status: Some(RawStatus(200, "OK".to_owned())),
|
||||
}
|
||||
}
|
||||
|
||||
@ -184,8 +196,9 @@ pub type ResourceTask = Sender<ControlMsg>;
|
||||
pub fn new_resource_task(user_agent: Option<String>) -> ResourceTask {
|
||||
let (setup_chan, setup_port) = channel();
|
||||
let sniffer_task = sniffer_task::new_sniffer_task();
|
||||
let setup_chan_clone = setup_chan.clone();
|
||||
spawn_named("ResourceManager".to_owned(), move || {
|
||||
ResourceManager::new(setup_port, user_agent, sniffer_task).start();
|
||||
ResourceManager::new(setup_port, user_agent, sniffer_task, setup_chan_clone).start();
|
||||
});
|
||||
setup_chan
|
||||
}
|
||||
@ -194,26 +207,44 @@ struct ResourceManager {
|
||||
from_client: Receiver<ControlMsg>,
|
||||
user_agent: Option<String>,
|
||||
sniffer_task: SnifferTask,
|
||||
cookie_storage: CookieStorage,
|
||||
resource_task: Sender<ControlMsg>,
|
||||
}
|
||||
|
||||
impl ResourceManager {
|
||||
fn new(from_client: Receiver<ControlMsg>, user_agent: Option<String>, sniffer_task: SnifferTask) -> ResourceManager {
|
||||
fn new(from_client: Receiver<ControlMsg>, user_agent: Option<String>, sniffer_task: SnifferTask,
|
||||
resource_task: Sender<ControlMsg>) -> ResourceManager {
|
||||
ResourceManager {
|
||||
from_client: from_client,
|
||||
user_agent: user_agent,
|
||||
sniffer_task: sniffer_task,
|
||||
cookie_storage: CookieStorage::new(),
|
||||
resource_task: resource_task,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl ResourceManager {
|
||||
fn start(&self) {
|
||||
fn start(&mut self) {
|
||||
loop {
|
||||
match self.from_client.recv().unwrap() {
|
||||
ControlMsg::Load(load_data) => {
|
||||
self.load(load_data)
|
||||
}
|
||||
ControlMsg::SetCookiesForUrl(request, cookie_list, source) => {
|
||||
let header = Header::parse_header([cookie_list.into_bytes()].as_slice());
|
||||
if let Some(SetCookie(cookies)) = header {
|
||||
for bare_cookie in cookies.into_iter() {
|
||||
if let Some(cookie) = cookie::Cookie::new_wrapped(bare_cookie, &request, source) {
|
||||
self.cookie_storage.push(cookie, source);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ControlMsg::GetCookiesForUrl(url, consumer, source) => {
|
||||
consumer.send(self.cookie_storage.cookies_for_url(&url, source));
|
||||
}
|
||||
ControlMsg::Exit => {
|
||||
break
|
||||
}
|
||||
@ -221,7 +252,7 @@ impl ResourceManager {
|
||||
}
|
||||
}
|
||||
|
||||
fn load(&self, load_data: LoadData) {
|
||||
fn load(&mut self, load_data: LoadData) {
|
||||
let mut load_data = load_data;
|
||||
self.user_agent.as_ref().map(|ua| load_data.headers.set(UserAgent(ua.clone())));
|
||||
let senders = ResponseSenders {
|
||||
@ -229,19 +260,28 @@ impl ResourceManager {
|
||||
eventual_consumer: load_data.consumer.clone(),
|
||||
};
|
||||
|
||||
debug!("resource_task: loading url: {}", load_data.url.serialize());
|
||||
match load_data.url.scheme.as_slice() {
|
||||
"file" => file_loader::factory(load_data, self.sniffer_task.clone()),
|
||||
"http" | "https" => http_loader::factory(load_data, self.sniffer_task.clone()),
|
||||
"data" => data_loader::factory(load_data, self.sniffer_task.clone()),
|
||||
"about" => about_loader::factory(load_data, self.sniffer_task.clone()),
|
||||
fn from_factory(factory: fn(LoadData, Sender<TargetedLoadResponse>))
|
||||
-> Box<Invoke<(LoadData, Sender<TargetedLoadResponse>)> + Send> {
|
||||
box move |&:(load_data, start_chan)| {
|
||||
factory(load_data, start_chan)
|
||||
}
|
||||
}
|
||||
|
||||
let loader = match load_data.url.scheme.as_slice() {
|
||||
"file" => from_factory(file_loader::factory),
|
||||
"http" | "https" => http_loader::factory(self.resource_task.clone()),
|
||||
"data" => from_factory(data_loader::factory),
|
||||
"about" => from_factory(about_loader::factory),
|
||||
_ => {
|
||||
debug!("resource_task: no loader for scheme {}", load_data.url.scheme);
|
||||
start_sending(senders, Metadata::default(load_data.url))
|
||||
.send(ProgressMsg::Done(Err("no loader for scheme".to_string()))).unwrap();
|
||||
return
|
||||
}
|
||||
}
|
||||
};
|
||||
debug!("resource_task: loading url: {}", load_data.url.serialize());
|
||||
|
||||
loader.invoke((load_data, self.sniffer_task.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ git = "https://github.com/servo/html5ever"
|
||||
|
||||
[dependencies.hyper]
|
||||
git = "https://github.com/servo/hyper"
|
||||
branch = "servo"
|
||||
branch = "old_servo_new_cookies"
|
||||
|
||||
[dependencies.js]
|
||||
git = "https://github.com/servo/rust-mozjs"
|
||||
@ -67,4 +67,4 @@ git = "https://github.com/servo/string-cache"
|
||||
[dependencies]
|
||||
encoding = "0.2"
|
||||
url = "0.2.16"
|
||||
time = "0.1.12"
|
||||
time = "0.1.12"
|
||||
|
@ -19,7 +19,7 @@ use dom::bindings::codegen::InheritTypes::{HTMLAreaElementDerived, HTMLEmbedElem
|
||||
use dom::bindings::codegen::InheritTypes::{HTMLFormElementDerived, HTMLImageElementDerived};
|
||||
use dom::bindings::codegen::InheritTypes::{HTMLScriptElementDerived};
|
||||
use dom::bindings::error::{ErrorResult, Fallible};
|
||||
use dom::bindings::error::Error::{NotSupported, InvalidCharacter};
|
||||
use dom::bindings::error::Error::{NotSupported, InvalidCharacter, Security};
|
||||
use dom::bindings::error::Error::{HierarchyRequest, NamespaceError};
|
||||
use dom::bindings::global::GlobalRef;
|
||||
use dom::bindings::js::{MutNullableJS, JS, JSRef, LayoutJS, Temporary, TemporaryPushable};
|
||||
@ -54,6 +54,8 @@ use dom::range::Range;
|
||||
use dom::treewalker::TreeWalker;
|
||||
use dom::uievent::UIEvent;
|
||||
use dom::window::{Window, WindowHelpers};
|
||||
use net::resource_task::ControlMsg::{SetCookiesForUrl, GetCookiesForUrl};
|
||||
use net::cookie_storage::CookieSource::NonHTTP;
|
||||
use util::namespace;
|
||||
use util::str::{DOMString, split_html_space_chars};
|
||||
|
||||
@ -68,6 +70,7 @@ use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||
use std::ascii::AsciiExt;
|
||||
use std::cell::{Cell, Ref};
|
||||
use std::default::Default;
|
||||
use std::sync::mpsc::channel;
|
||||
use time;
|
||||
|
||||
#[derive(PartialEq)]
|
||||
@ -170,7 +173,7 @@ pub trait DocumentHelpers<'a> {
|
||||
fn window(self) -> Temporary<Window>;
|
||||
fn encoding_name(self) -> Ref<'a, DOMString>;
|
||||
fn is_html_document(self) -> bool;
|
||||
fn url(self) -> &'a Url;
|
||||
fn url(self) -> Url;
|
||||
fn quirks_mode(self) -> QuirksMode;
|
||||
fn set_quirks_mode(self, mode: QuirksMode);
|
||||
fn set_last_modified(self, value: DOMString);
|
||||
@ -206,8 +209,9 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> {
|
||||
self.is_html_document
|
||||
}
|
||||
|
||||
fn url(self) -> &'a Url {
|
||||
&self.extended_deref().url
|
||||
// http://dom.spec.whatwg.org/#dom-document-url
|
||||
fn url(self) -> Url {
|
||||
self.url.clone()
|
||||
}
|
||||
|
||||
fn quirks_mode(self) -> QuirksMode {
|
||||
@ -1003,7 +1007,38 @@ impl<'a> DocumentMethods for JSRef<'a, Document> {
|
||||
Temporary::new(self.window)
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/dom.html#dom-document-cookie
|
||||
fn GetCookie(self) -> Fallible<DOMString> {
|
||||
//TODO: return empty string for cookie-averse Document
|
||||
let url = self.url();
|
||||
if !is_scheme_host_port_tuple(&url) {
|
||||
return Err(Security);
|
||||
}
|
||||
let window = self.window.root();
|
||||
let page = window.page();
|
||||
let (tx, rx) = channel();
|
||||
let _ = page.resource_task.send(GetCookiesForUrl(url, tx, NonHTTP));
|
||||
let cookies = rx.recv().unwrap();
|
||||
Ok(cookies.unwrap_or("".to_owned()))
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/dom.html#dom-document-cookie
|
||||
fn SetCookie(self, cookie: DOMString) -> ErrorResult {
|
||||
//TODO: ignore for cookie-averse Document
|
||||
let url = self.url();
|
||||
if !is_scheme_host_port_tuple(&url) {
|
||||
return Err(Security);
|
||||
}
|
||||
let window = self.window.root();
|
||||
let page = window.page();
|
||||
let _ = page.resource_task.send(SetCookiesForUrl(url, cookie, NonHTTP));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
global_event_handlers!();
|
||||
event_handler!(readystatechange, GetOnreadystatechange, SetOnreadystatechange);
|
||||
}
|
||||
|
||||
fn is_scheme_host_port_tuple(url: &Url) -> bool {
|
||||
url.host().is_some() && url.port_or_default().is_some()
|
||||
}
|
||||
|
@ -777,7 +777,7 @@ impl<'a> AttributeHandlers for JSRef<'a, Element> {
|
||||
let base = doc.r().url();
|
||||
// https://html.spec.whatwg.org/multipage/infrastructure.html#reflect
|
||||
// XXXManishearth this doesn't handle `javascript:` urls properly
|
||||
match UrlParser::new().base_url(base).parse(url.as_slice()) {
|
||||
match UrlParser::new().base_url(&base).parse(url.as_slice()) {
|
||||
Ok(parsed) => parsed.serialize(),
|
||||
Err(_) => "".to_owned()
|
||||
}
|
||||
@ -1174,7 +1174,7 @@ impl<'a> VirtualMethods for JSRef<'a, Element> {
|
||||
// Modifying the `style` attribute might change style.
|
||||
let node: JSRef<Node> = NodeCast::from_ref(*self);
|
||||
let doc = document_from_node(*self).root();
|
||||
let base_url = doc.r().url().clone();
|
||||
let base_url = doc.r().url();
|
||||
let value = attr.value();
|
||||
let style = Some(parse_style_attribute(value.as_slice(), &base_url));
|
||||
*self.style_attribute.borrow_mut() = style;
|
||||
|
@ -23,6 +23,8 @@ use dom::htmlbuttonelement::{HTMLButtonElement};
|
||||
use dom::htmltextareaelement::{HTMLTextAreaElement, HTMLTextAreaElementHelpers};
|
||||
use dom::node::{Node, NodeHelpers, NodeTypeId, document_from_node, window_from_node};
|
||||
use hyper::method::Method;
|
||||
use hyper::header::common::ContentType;
|
||||
use hyper::mime;
|
||||
use servo_msg::constellation_msg::LoadData;
|
||||
use util::str::DOMString;
|
||||
use script_task::{ScriptChan, ScriptMsg};
|
||||
@ -178,7 +180,7 @@ impl<'a> HTMLFormElementHelpers for JSRef<'a, HTMLFormElement> {
|
||||
}
|
||||
// TODO: Resolve the url relative to the submitter element
|
||||
// Step 10-15
|
||||
let action_components = UrlParser::new().base_url(base).parse(action.as_slice()).unwrap_or(base.clone());
|
||||
let action_components = UrlParser::new().base_url(&base).parse(action.as_slice()).unwrap_or(base);
|
||||
let _action = action_components.serialize();
|
||||
let scheme = action_components.scheme.clone();
|
||||
let enctype = submitter.enctype();
|
||||
@ -186,12 +188,17 @@ impl<'a> HTMLFormElementHelpers for JSRef<'a, HTMLFormElement> {
|
||||
let _target = submitter.target();
|
||||
// TODO: Handle browsing contexts, partially loaded documents (step 16-17)
|
||||
|
||||
let mut load_data = LoadData::new(action_components);
|
||||
|
||||
let parsed_data = match enctype {
|
||||
FormEncType::UrlEncoded => serialize(form_data.iter().map(|d| (d.name.as_slice(), d.value.as_slice()))),
|
||||
FormEncType::UrlEncoded => {
|
||||
let mime: mime::Mime = "application/x-www-form-urlencoded".parse().unwrap();
|
||||
load_data.headers.set(ContentType(mime));
|
||||
serialize(form_data.iter().map(|d| (d.name.as_slice(), d.value.as_slice())))
|
||||
}
|
||||
_ => "".to_owned() // TODO: Add serializers for the other encoding types
|
||||
};
|
||||
|
||||
let mut load_data = LoadData::new(action_components);
|
||||
// Step 18
|
||||
match (scheme.as_slice(), method) {
|
||||
(_, FormMethod::FormDialog) => return, // Unimplemented
|
||||
|
@ -1574,7 +1574,7 @@ impl Node {
|
||||
false => IsHTMLDocument::NonHTMLDocument,
|
||||
};
|
||||
let window = document.window().root();
|
||||
let document = Document::new(window.r(), Some(document.url().clone()),
|
||||
let document = Document::new(window.r(), Some(document.url()),
|
||||
is_html_doc, None,
|
||||
DocumentSource::NotFromParser);
|
||||
NodeCast::from_temporary(document)
|
||||
|
@ -65,6 +65,8 @@ partial interface Document {
|
||||
readonly attribute DocumentReadyState readyState;
|
||||
readonly attribute DOMString lastModified;
|
||||
readonly attribute Location location;
|
||||
[Throws]
|
||||
attribute DOMString cookie;
|
||||
|
||||
// DOM tree accessors
|
||||
[SetterThrows]
|
||||
|
@ -561,41 +561,35 @@ impl<'a> XMLHttpRequestMethods for JSRef<'a, XMLHttpRequest> {
|
||||
let mut load_data = LoadData::new(self.request_url.borrow().clone().unwrap(), start_chan);
|
||||
load_data.data = extracted;
|
||||
|
||||
// Default headers
|
||||
{
|
||||
#[inline]
|
||||
fn join_raw(a: &str, b: &str) -> Vec<u8> {
|
||||
let len = a.len() + b.len();
|
||||
let mut vec = Vec::with_capacity(len);
|
||||
vec.push_all(a.as_bytes());
|
||||
vec.push_all(b.as_bytes());
|
||||
vec
|
||||
}
|
||||
let ref mut request_headers = self.request_headers.borrow_mut();
|
||||
if !request_headers.has::<ContentType>() {
|
||||
// XHR spec differs from http, and says UTF-8 should be in capitals,
|
||||
// instead of "utf-8", which is what Hyper defaults to.
|
||||
let params = ";charset=UTF-8";
|
||||
let n = "content-type";
|
||||
match data {
|
||||
Some(eString(_)) =>
|
||||
request_headers.set_raw(n.to_owned(), vec![join_raw("text/plain", params)]),
|
||||
Some(eURLSearchParams(_)) =>
|
||||
request_headers.set_raw(
|
||||
n.to_owned(), vec![join_raw("application/x-www-form-urlencoded", params)]),
|
||||
None => ()
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn join_raw(a: &str, b: &str) -> Vec<u8> {
|
||||
let len = a.len() + b.len();
|
||||
let mut vec = Vec::with_capacity(len);
|
||||
vec.push_all(a.as_bytes());
|
||||
vec.push_all(b.as_bytes());
|
||||
vec
|
||||
}
|
||||
|
||||
// XHR spec differs from http, and says UTF-8 should be in capitals,
|
||||
// instead of "utf-8", which is what Hyper defaults to.
|
||||
let params = ";charset=UTF-8";
|
||||
let n = "content-type";
|
||||
match data {
|
||||
Some(eString(_)) =>
|
||||
load_data.headers.set_raw(n.to_owned(), vec![join_raw("text/plain", params)]),
|
||||
Some(eURLSearchParams(_)) =>
|
||||
load_data.headers.set_raw(
|
||||
n.to_owned(), vec![join_raw("application/x-www-form-urlencoded", params)]),
|
||||
None => ()
|
||||
}
|
||||
|
||||
if !request_headers.has::<Accept>() {
|
||||
let mime = Mime(mime::TopLevel::Star, mime::SubLevel::Star, vec![]);
|
||||
request_headers.set(
|
||||
Accept(vec![QualityItem::new(mime, 1.0)]));
|
||||
}
|
||||
} // drops the borrow_mut
|
||||
load_data.preserved_headers = (*self.request_headers.borrow()).clone();
|
||||
|
||||
if !load_data.preserved_headers.has::<Accept>() {
|
||||
let mime = Mime(mime::TopLevel::Star, mime::SubLevel::Star, vec![]);
|
||||
load_data.preserved_headers.set(Accept(vec![QualityItem::new(mime, 1.0)]));
|
||||
}
|
||||
|
||||
load_data.headers = (*self.request_headers.borrow()).clone();
|
||||
load_data.method = (*self.request_method.borrow()).clone();
|
||||
let (terminate_sender, terminate_receiver) = channel();
|
||||
*self.terminate_sender.borrow_mut() = Some(terminate_sender);
|
||||
@ -607,8 +601,10 @@ impl<'a> XMLHttpRequestMethods for JSRef<'a, XMLHttpRequest> {
|
||||
} else {
|
||||
RequestMode::CORS
|
||||
};
|
||||
let mut combined_headers = load_data.headers.clone();
|
||||
combined_headers.extend(load_data.preserved_headers.iter());
|
||||
let cors_request = CORSRequest::maybe_new(referer_url.clone(), load_data.url.clone(), mode,
|
||||
load_data.method.clone(), load_data.headers.clone());
|
||||
load_data.method.clone(), combined_headers);
|
||||
match cors_request {
|
||||
Ok(None) => {
|
||||
let mut buf = String::new();
|
||||
|
@ -67,7 +67,7 @@ use util::task::spawn_named_with_send_on_failure;
|
||||
use util::task_state;
|
||||
|
||||
use geom::point::Point2D;
|
||||
use hyper::header::{Header, HeaderFormat};
|
||||
use hyper::header::{Header, Headers, HeaderFormat};
|
||||
use hyper::header::shared::util as header_util;
|
||||
use js::jsapi::{JS_SetWrapObjectCallbacks, JS_SetGCZeal, JS_DEFAULT_ZEAL_FREQ, JS_GC};
|
||||
use js::jsapi::{JSContext, JSRuntime, JSObject};
|
||||
@ -787,6 +787,44 @@ impl ScriptTask {
|
||||
|
||||
let is_javascript = url.scheme.as_slice() == "javascript";
|
||||
|
||||
self.compositor.borrow_mut().set_ready_state(pipeline_id, Loading);
|
||||
|
||||
let (mut parser_input, final_url, last_modified) = if !is_javascript {
|
||||
// Wait for the LoadResponse so that the parser knows the final URL.
|
||||
let (input_chan, input_port) = channel();
|
||||
self.resource_task.send(ControlMsg::Load(NetLoadData {
|
||||
url: url,
|
||||
method: load_data.method,
|
||||
headers: Headers::new(),
|
||||
preserved_headers: load_data.headers,
|
||||
data: load_data.data,
|
||||
cors: None,
|
||||
consumer: input_chan,
|
||||
})).unwrap();
|
||||
|
||||
let load_response = input_port.recv().unwrap();
|
||||
|
||||
let last_modified = load_response.metadata.headers.as_ref().and_then(|headers| {
|
||||
headers.get().map(|&LastModified(ref tm)| tm.clone())
|
||||
});
|
||||
|
||||
let final_url = load_response.metadata.final_url.clone();
|
||||
|
||||
(Some(HTMLInput::InputUrl(load_response)), final_url, last_modified)
|
||||
} else {
|
||||
let doc_url = last_url.unwrap_or_else(|| {
|
||||
Url::parse("about:blank").unwrap()
|
||||
});
|
||||
(None, doc_url, None)
|
||||
};
|
||||
|
||||
// Store the final URL before we start parsing, so that DOM routines
|
||||
// (e.g. HTMLImageElement::update_image) can resolve relative URLs
|
||||
// correctly.
|
||||
{
|
||||
*page.mut_url() = Some((final_url.clone(), true));
|
||||
}
|
||||
|
||||
let cx = self.js_context.borrow();
|
||||
let cx = cx.as_ref().unwrap();
|
||||
// Create the window and document objects.
|
||||
@ -796,22 +834,15 @@ impl ScriptTask {
|
||||
self.control_chan.clone(),
|
||||
self.compositor.borrow_mut().dup(),
|
||||
self.image_cache_task.clone()).root();
|
||||
let doc_url = if is_javascript {
|
||||
let doc_url = last_url.unwrap_or_else(|| {
|
||||
Url::parse("about:blank").unwrap()
|
||||
});
|
||||
*page.mut_url() = Some((doc_url.clone(), true));
|
||||
doc_url
|
||||
} else {
|
||||
url.clone()
|
||||
};
|
||||
let document = Document::new(window.r(), Some(doc_url.clone()),
|
||||
|
||||
let document = Document::new(window.r(), Some(final_url.clone()),
|
||||
IsHTMLDocument::HTMLDocument, None,
|
||||
DocumentSource::FromParser).root();
|
||||
|
||||
if let Some(tm) = last_modified {
|
||||
document.set_last_modified(dom_last_modified(&tm));
|
||||
}
|
||||
window.r().init_browser_context(document.r());
|
||||
|
||||
self.compositor.borrow_mut().set_ready_state(pipeline_id, Loading);
|
||||
|
||||
{
|
||||
// Create the root frame.
|
||||
@ -822,45 +853,15 @@ impl ScriptTask {
|
||||
});
|
||||
}
|
||||
|
||||
let (parser_input, final_url) = if !is_javascript {
|
||||
// Wait for the LoadResponse so that the parser knows the final URL.
|
||||
let (input_chan, input_port) = channel();
|
||||
self.resource_task.send(ControlMsg::Load(NetLoadData {
|
||||
url: url,
|
||||
method: load_data.method,
|
||||
headers: load_data.headers,
|
||||
data: load_data.data,
|
||||
cors: None,
|
||||
consumer: input_chan,
|
||||
})).unwrap();
|
||||
|
||||
let load_response = input_port.recv().unwrap();
|
||||
|
||||
load_response.metadata.headers.as_ref().map(|headers| {
|
||||
headers.get().map(|&LastModified(ref tm)| {
|
||||
document.r().set_last_modified(dom_last_modified(tm));
|
||||
});
|
||||
});
|
||||
|
||||
let final_url = load_response.metadata.final_url.clone();
|
||||
|
||||
{
|
||||
// Store the final URL before we start parsing, so that DOM routines
|
||||
// (e.g. HTMLImageElement::update_image) can resolve relative URLs
|
||||
// correctly.
|
||||
*page.mut_url() = Some((final_url.clone(), true));
|
||||
}
|
||||
|
||||
(HTMLInput::InputUrl(load_response), final_url)
|
||||
} else {
|
||||
if is_javascript {
|
||||
let evalstr = load_data.url.non_relative_scheme_data().unwrap();
|
||||
let jsval = window.r().evaluate_js_on_global_with_result(evalstr);
|
||||
let strval = FromJSValConvertible::from_jsval(self.get_cx(), jsval,
|
||||
StringificationBehavior::Empty);
|
||||
(HTMLInput::InputString(strval.unwrap_or("".to_owned())), doc_url)
|
||||
parser_input = Some(HTMLInput::InputString(strval.unwrap_or("".to_owned())));
|
||||
};
|
||||
|
||||
parse_html(document.r(), parser_input, &final_url);
|
||||
parse_html(document.r(), parser_input.unwrap(), &final_url);
|
||||
|
||||
document.r().set_ready_state(DocumentReadyState::Interactive);
|
||||
self.compositor.borrow_mut().set_ready_state(pipeline_id, PerformingLayout);
|
||||
|
14
servo/components/servo/Cargo.lock
generated
14
servo/components/servo/Cargo.lock
generated
@ -100,7 +100,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "cookie"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
source = "git+https://github.com/servo/cookie-rs?branch=lenientparse_backport#47ffa4d3c6f85d28f222d6e1d54635fff5622ea3"
|
||||
dependencies = [
|
||||
"openssl 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -154,6 +154,7 @@ name = "devtools_traits"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"msg 0.0.1",
|
||||
"util 0.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -398,9 +399,9 @@ source = "git+https://github.com/servo/html5ever#d35dfaaf0d85007057a299afc370d07
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.1.1"
|
||||
source = "git+https://github.com/servo/hyper?branch=servo#7f48a7e945180a4f762dc75236210d20a69b4a6a"
|
||||
source = "git+https://github.com/servo/hyper?branch=old_servo_new_cookies#7a346f481d683705709526594aa5f13b5c923bc1"
|
||||
dependencies = [
|
||||
"cookie 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cookie 0.1.8 (git+https://github.com/servo/cookie-rs?branch=lenientparse_backport)",
|
||||
"log 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mime 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mucell 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -541,7 +542,7 @@ dependencies = [
|
||||
"azure 0.1.0 (git+https://github.com/servo/rust-azure)",
|
||||
"core_foundation 0.1.0 (git+https://github.com/servo/rust-core-foundation)",
|
||||
"geom 0.1.0 (git+https://github.com/servo/rust-geom)",
|
||||
"hyper 0.1.1 (git+https://github.com/servo/hyper?branch=servo)",
|
||||
"hyper 0.1.1 (git+https://github.com/servo/hyper?branch=old_servo_new_cookies)",
|
||||
"io_surface 0.1.0 (git+https://github.com/servo/rust-io-surface)",
|
||||
"layers 0.1.0 (git+https://github.com/servo/rust-layers)",
|
||||
"style 0.0.1",
|
||||
@ -558,8 +559,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
name = "net"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"cookie 0.1.8 (git+https://github.com/servo/cookie-rs?branch=lenientparse_backport)",
|
||||
"geom 0.1.0 (git+https://github.com/servo/rust-geom)",
|
||||
"hyper 0.1.1 (git+https://github.com/servo/hyper?branch=servo)",
|
||||
"hyper 0.1.1 (git+https://github.com/servo/hyper?branch=old_servo_new_cookies)",
|
||||
"openssl 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"png 0.1.0 (git+https://github.com/servo/rust-png)",
|
||||
"stb_image 0.1.0 (git+https://github.com/servo/rust-stb-image)",
|
||||
@ -655,7 +657,7 @@ dependencies = [
|
||||
"geom 0.1.0 (git+https://github.com/servo/rust-geom)",
|
||||
"gfx 0.0.1",
|
||||
"html5ever 0.0.0 (git+https://github.com/servo/html5ever)",
|
||||
"hyper 0.1.1 (git+https://github.com/servo/hyper?branch=servo)",
|
||||
"hyper 0.1.1 (git+https://github.com/servo/hyper?branch=old_servo_new_cookies)",
|
||||
"js 0.1.0 (git+https://github.com/servo/rust-mozjs)",
|
||||
"msg 0.0.1",
|
||||
"net 0.0.1",
|
||||
|
14
servo/ports/cef/Cargo.lock
generated
14
servo/ports/cef/Cargo.lock
generated
@ -99,7 +99,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "cookie"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
source = "git+https://github.com/servo/cookie-rs?branch=lenientparse_backport#47ffa4d3c6f85d28f222d6e1d54635fff5622ea3"
|
||||
dependencies = [
|
||||
"openssl 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -153,6 +153,7 @@ name = "devtools_traits"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"msg 0.0.1",
|
||||
"util 0.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -397,9 +398,9 @@ source = "git+https://github.com/servo/html5ever#d35dfaaf0d85007057a299afc370d07
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.1.1"
|
||||
source = "git+https://github.com/servo/hyper?branch=servo#7f48a7e945180a4f762dc75236210d20a69b4a6a"
|
||||
source = "git+https://github.com/servo/hyper?branch=old_servo_new_cookies#7a346f481d683705709526594aa5f13b5c923bc1"
|
||||
dependencies = [
|
||||
"cookie 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cookie 0.1.8 (git+https://github.com/servo/cookie-rs?branch=lenientparse_backport)",
|
||||
"log 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mime 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mucell 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -540,7 +541,7 @@ dependencies = [
|
||||
"azure 0.1.0 (git+https://github.com/servo/rust-azure)",
|
||||
"core_foundation 0.1.0 (git+https://github.com/servo/rust-core-foundation)",
|
||||
"geom 0.1.0 (git+https://github.com/servo/rust-geom)",
|
||||
"hyper 0.1.1 (git+https://github.com/servo/hyper?branch=servo)",
|
||||
"hyper 0.1.1 (git+https://github.com/servo/hyper?branch=old_servo_new_cookies)",
|
||||
"io_surface 0.1.0 (git+https://github.com/servo/rust-io-surface)",
|
||||
"layers 0.1.0 (git+https://github.com/servo/rust-layers)",
|
||||
"style 0.0.1",
|
||||
@ -557,8 +558,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
name = "net"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"cookie 0.1.8 (git+https://github.com/servo/cookie-rs?branch=lenientparse_backport)",
|
||||
"geom 0.1.0 (git+https://github.com/servo/rust-geom)",
|
||||
"hyper 0.1.1 (git+https://github.com/servo/hyper?branch=servo)",
|
||||
"hyper 0.1.1 (git+https://github.com/servo/hyper?branch=old_servo_new_cookies)",
|
||||
"openssl 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"png 0.1.0 (git+https://github.com/servo/rust-png)",
|
||||
"stb_image 0.1.0 (git+https://github.com/servo/rust-stb-image)",
|
||||
@ -654,7 +656,7 @@ dependencies = [
|
||||
"geom 0.1.0 (git+https://github.com/servo/rust-geom)",
|
||||
"gfx 0.0.1",
|
||||
"html5ever 0.0.0 (git+https://github.com/servo/html5ever)",
|
||||
"hyper 0.1.1 (git+https://github.com/servo/hyper?branch=servo)",
|
||||
"hyper 0.1.1 (git+https://github.com/servo/hyper?branch=old_servo_new_cookies)",
|
||||
"js 0.1.0 (git+https://github.com/servo/rust-mozjs)",
|
||||
"msg 0.0.1",
|
||||
"net 0.0.1",
|
||||
|
1
servo/ports/gonk/Cargo.lock
generated
1
servo/ports/gonk/Cargo.lock
generated
@ -124,6 +124,7 @@ name = "devtools_traits"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"msg 0.0.1",
|
||||
"util 0.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -163,4 +163,3 @@ impl<Window> Browser<Window> where Window: WindowMethods + 'static {
|
||||
self.compositor.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user