Upgrade to rust-url 1.0 and hyper 0.9

This commit is contained in:
Simon Sapin 2016-04-21 00:18:37 +02:00
parent 305c283602
commit 7932ab6ac2
76 changed files with 524 additions and 888 deletions

View file

@ -35,9 +35,10 @@ git = "https://github.com/servo/webrender_traits"
[dependencies]
cookie = "0.2"
flate2 = "0.2.0"
hyper = { version = "0.8", features = [ "serde-serialization" ] }
hyper = { version = "0.9", features = [ "serde-serialization" ] }
immeta = "0.3.1"
log = "0.3.5"
matches = "0.1"
mime = "0.2.0"
mime_guess = "1.6.0"
openssl = "0.7.6"
@ -45,9 +46,9 @@ rustc-serialize = "0.3"
threadpool = "1.0"
time = "0.1.17"
unicase = "1.4.0"
url = {version = "0.5.7", features = ["heap_size"]}
url = {version = "1.0.0", features = ["heap_size", "rustc-serialize"]}
uuid = { version = "0.2", features = ["v4"] }
websocket = "0.16.1"
websocket = "0.17"
[target.'cfg(any(target_os = "macos", target_os = "linux"))'.dependencies]
tinyfiledialogs = {git = "https://github.com/jdm/tinyfiledialogs"}

View file

@ -27,8 +27,7 @@ pub fn factory(mut load_data: LoadData,
classifier: Arc<MIMEClassifier>,
cancel_listener: CancellationListener) {
let url = load_data.url.clone();
let non_relative_scheme_data = url.non_relative_scheme_data().unwrap();
match non_relative_scheme_data {
match url.path() {
"blank" => {
let metadata = Metadata {
final_url: load_data.url,
@ -49,7 +48,7 @@ pub fn factory(mut load_data: LoadData,
}
"crash" => panic!("Loading the about:crash URL."),
"failure" | "not-found" =>
url_from_non_relative_scheme(&mut load_data, &(non_relative_scheme_data.to_owned() + ".html")),
url_from_non_relative_scheme(&mut load_data, &(url.path().to_owned() + ".html")),
"sslfail" => url_from_non_relative_scheme(&mut load_data, "badcert.html"),
_ => {
send_error(load_data.url, NetworkError::Internal("Unknown about: URL.".to_owned()), start_chan);

View file

@ -6,26 +6,21 @@ use file_loader;
use mime_classifier::MIMEClassifier;
use net_traits::{LoadConsumer, LoadData, NetworkError};
use resource_thread::{CancellationListener, send_error};
use std::path::Path;
use std::sync::Arc;
use url::Url;
use util::resource_files::resources_dir_path;
pub fn resolve_chrome_url(url: &Url) -> Result<Url, ()> {
assert_eq!(url.scheme, "chrome");
// Skip the initial //
let non_relative_scheme_data = &url.non_relative_scheme_data().unwrap()[2..];
let relative_path = Path::new(non_relative_scheme_data);
assert_eq!(url.scheme(), "chrome");
let resources = resources_dir_path();
let mut path = resources.clone();
for segment in url.path_segments().unwrap() {
path.push(segment)
}
// Don't allow chrome URLs access to files outside of the resources directory.
if non_relative_scheme_data.find("..").is_some() ||
relative_path.is_absolute() ||
relative_path.has_root() {
if !(path.starts_with(resources) && path.exists()) {
return Err(());
}
let mut path = resources_dir_path();
path.push(non_relative_scheme_data);
assert!(path.exists());
return Ok(Url::from_file_path(&*path).unwrap());
}

View file

@ -40,7 +40,7 @@ impl Cookie {
_ => (false, None)
};
let url_host = request.host().map_or("".to_owned(), |host| host.serialize());
let url_host = request.host_str().unwrap_or("").to_owned();
// Step 4
let mut domain = cookie.domain.clone().unwrap_or("".to_owned());
@ -68,9 +68,7 @@ impl Cookie {
// Step 7
let mut path = cookie.path.unwrap_or("".to_owned());
if path.chars().next() != Some('/') {
let url_path = request.serialize_path();
let url_path = url_path.as_ref().map(|path| &**path);
path = Cookie::default_path(url_path.unwrap_or("")).to_owned();
path = Cookie::default_path(request.path()).to_owned();
}
cookie.path = Some(path);
@ -147,26 +145,26 @@ impl Cookie {
// 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());
let domain = url.host_str();
if self.host_only {
if self.cookie.domain != domain {
if self.cookie.domain.as_ref().map(String::as_str) != domain {
return false;
}
} else {
if let (Some(ref domain), &Some(ref cookie_domain)) = (domain, &self.cookie.domain) {
if let (Some(domain), &Some(ref cookie_domain)) = (domain, &self.cookie.domain) {
if !Cookie::domain_match(domain, cookie_domain) {
return false;
}
}
}
if let (Some(ref path), &Some(ref cookie_path)) = (url.serialize_path(), &self.cookie.path) {
if !Cookie::path_match(path, cookie_path) {
if let Some(ref cookie_path) = self.cookie.path {
if !Cookie::path_match(url.path(), cookie_path) {
return false;
}
}
if self.cookie.secure && url.scheme != "https" {
if self.cookie.secure && url.scheme() != "https" {
return false;
}
if self.cookie.httponly && source == CookieSource::NonHTTP {

View file

@ -10,9 +10,8 @@ use net_traits::{LoadData, Metadata, NetworkError};
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;
use url::{Position, Url};
pub fn factory(load_data: LoadData,
senders: LoadConsumer,
@ -33,58 +32,42 @@ pub enum DecodeError {
pub type DecodeData = (Mime, Vec<u8>);
pub fn decode(url: &Url) -> Result<DecodeData, DecodeError> {
assert!(&*url.scheme == "data");
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."),
};
match url.query {
Some(ref query) => {
scheme_data.push_str("?");
scheme_data.push_str(query);
},
None => ()
}
let parts: Vec<&str> = scheme_data.splitn(2, ',').collect();
let parts: Vec<&str> = url[Position::BeforePath..Position::AfterQuery].splitn(2, ',').collect();
if parts.len() != 2 {
return Err(DecodeError::InvalidDataUri);
}
// ";base64" must come at the end of the content type, per RFC 2397.
// rust-http will fail to parse it because there's no =value part.
let mut is_base64 = false;
let mut ct_str = parts[0].to_owned();
if ct_str.ends_with(";base64") {
is_base64 = true;
let end_index = ct_str.len() - 7;
ct_str.truncate(end_index);
}
if ct_str.starts_with(";charset=") {
ct_str = format!("text/plain{}", ct_str);
let mut ct_str = parts[0];
let is_base64 = ct_str.ends_with(";base64");
if is_base64 {
ct_str = &ct_str[..ct_str.len() - ";base64".len()];
}
let ct_str = if ct_str.starts_with(";charset=") {
format!("text/plain{}", ct_str)
} else {
ct_str.to_owned()
};
// Parse the content type using rust-http.
// FIXME: this can go into an infinite loop! (rust-http #25)
let mut content_type: Option<Mime> = ct_str.parse().ok();
if content_type == None {
content_type = Some(Mime(TopLevel::Text, SubLevel::Plain,
vec!((Attr::Charset, Value::Ext("US-ASCII".to_owned())))));
}
let content_type = ct_str.parse().unwrap_or_else(|_| {
Mime(TopLevel::Text, SubLevel::Plain,
vec![(Attr::Charset, Value::Ext("US-ASCII".to_owned()))])
});
let bytes = percent_decode(parts[1].as_bytes());
let bytes = if is_base64 {
let mut bytes = percent_decode(parts[1].as_bytes()).collect::<Vec<_>>();
if is_base64 {
// FIXME(#2909): Its 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>>();
bytes = bytes.into_iter().filter(|&b| b != ' ' as u8).collect::<Vec<u8>>();
match bytes.from_base64() {
Err(..) => return Err(DecodeError::NonBase64DataUri),
Ok(data) => data,
Ok(data) => bytes = data,
}
} else {
bytes
};
Ok((content_type.unwrap(), bytes))
}
Ok((content_type, bytes))
}
pub fn load(load_data: LoadData,

View file

@ -26,8 +26,7 @@ use std::iter::FromIterator;
use std::rc::Rc;
use std::thread;
use unicase::UniCase;
use url::idna::domain_to_ascii;
use url::{Origin as UrlOrigin, OpaqueOrigin, Url, UrlParser, whatwg_scheme_type_mapper};
use url::{Origin as UrlOrigin, Url};
use util::thread::spawn_named;
pub fn fetch_async(request: Request, listener: Box<AsyncFetchListener + Send>) {
@ -115,7 +114,7 @@ fn main_fetch(request: Rc<Request>, cors_flag: bool, recursive_flag: bool) -> Re
// Step 2
if request.local_urls_only {
match &*request.current_url().scheme {
match request.current_url().scheme() {
"about" | "blob" | "data" | "filesystem" => (), // Ok, the URL is local.
_ => response = Some(Response::network_error())
}
@ -153,8 +152,8 @@ fn main_fetch(request: Rc<Request>, cors_flag: bool, recursive_flag: bool) -> Re
};
if (same_origin && !cors_flag ) ||
(current_url.scheme == "data" && request.same_origin_data.get()) ||
current_url.scheme == "about" ||
(current_url.scheme() == "data" && request.same_origin_data.get()) ||
current_url.scheme() == "about" ||
request.mode == RequestMode::Navigate {
basic_fetch(request.clone())
@ -166,7 +165,7 @@ fn main_fetch(request: Rc<Request>, cors_flag: bool, recursive_flag: bool) -> Re
request.response_tainting.set(ResponseTainting::Opaque);
basic_fetch(request.clone())
} else if current_url.scheme != "http" && current_url.scheme != "https" {
} else if !matches!(current_url.scheme(), "http" | "https") {
Response::network_error()
} else if request.use_cors_preflight ||
@ -253,10 +252,7 @@ fn main_fetch(request: Rc<Request>, cors_flag: bool, recursive_flag: bool) -> Re
}
// Step 17
if request.body.borrow().is_some() && match &*request.current_url().scheme {
"http" | "https" => true,
_ => false }
{
if request.body.borrow().is_some() && matches!(request.current_url().scheme(), "http" | "https") {
// TODO queue a fetch task on request to process end-of-file
}
@ -287,20 +283,14 @@ fn main_fetch(request: Rc<Request>, cors_flag: bool, recursive_flag: bool) -> Re
fn basic_fetch(request: Rc<Request>) -> Response {
let url = request.current_url();
let scheme = url.scheme.clone();
match &*scheme {
match url.scheme() {
"about" => {
match url.non_relative_scheme_data() {
Some(s) if &*s == "blank" => {
let mut response = Response::new();
response.headers.set(ContentType(mime!(Text / Html; Charset = Utf8)));
*response.body.lock().unwrap() = ResponseBody::Done(vec![]);
response
},
_ => Response::network_error()
}
"about" if url.path() == "blank" => {
let mut response = Response::new();
response.headers.set(ContentType(mime!(Text / Html; Charset = Utf8)));
*response.body.lock().unwrap() = ResponseBody::Done(vec![]);
response
},
"http" | "https" => {
@ -535,7 +525,7 @@ fn http_redirect_fetch(request: Rc<Request>,
// Step 5
let response_url = response.actual_response().url.as_ref().unwrap();
let location_url = UrlParser::new().base_url(response_url).parse(&*location);
let location_url = response_url.join(&*location);
// Step 6
let location_url = match location_url {
@ -573,7 +563,7 @@ fn http_redirect_fetch(request: Rc<Request>,
// Step 12
if cors_flag && !same_origin {
*request.origin.borrow_mut() = Origin::Origin(UrlOrigin::UID(OpaqueOrigin::new()));
*request.origin.borrow_mut() = Origin::Origin(UrlOrigin::new_opaque());
}
// Step 13
@ -632,7 +622,7 @@ fn http_network_or_cache_fetch(request: Rc<Request>,
Referer::NoReferer =>
http_request.headers.borrow_mut().set(RefererHeader("".to_owned())),
Referer::RefererUrl(ref http_request_referer) =>
http_request.headers.borrow_mut().set(RefererHeader(http_request_referer.serialize())),
http_request.headers.borrow_mut().set(RefererHeader(http_request_referer.to_string())),
Referer::Client =>
// it should be impossible for referer to be anything else during fetching
// https://fetch.spec.whatwg.org/#concept-request-referrer
@ -702,9 +692,9 @@ fn http_network_or_cache_fetch(request: Rc<Request>,
let current_url = http_request.current_url();
authorization_value = if includes_credentials(&current_url) {
authorization_value = if has_credentials(&current_url) {
Some(Basic {
username: current_url.username().unwrap_or("").to_owned(),
username: current_url.username().to_owned(),
password: current_url.password().map(str::to_owned)
})
} else {
@ -1059,13 +1049,8 @@ fn cors_check(request: Rc<Request>, response: &Response) -> Result<(), ()> {
_ => return Err(())
};
// strings are already utf-8 encoded, so there's no need to re-encode origin for this step
match ascii_serialise_origin(&request.origin.borrow()) {
Ok(request_origin) => {
if request_origin != origin {
return Err(());
}
},
match *request.origin.borrow() {
Origin::Origin(ref o) if o.ascii_serialization() == origin => {},
_ => return Err(())
}
@ -1086,39 +1071,6 @@ fn cors_check(request: Rc<Request>, response: &Response) -> Result<(), ()> {
Err(())
}
/// [ASCII serialisation of an origin](https://html.spec.whatwg.org/multipage/#ascii-serialisation-of-an-origin)
fn ascii_serialise_origin(origin: &Origin) -> Result<String, ()> {
// Step 6
match *origin {
// Step 1
Origin::Origin(UrlOrigin::UID(_)) => Ok("null".to_owned()),
// Step 2
Origin::Origin(UrlOrigin::Tuple(ref scheme, ref host, ref port)) => {
// Step 3
// this step is handled by the format!()s later in the function
// Step 4
// TODO throw a SecurityError in a meaningful way
// let host = host.as_str();
let host = try!(domain_to_ascii(host.serialize().as_str()).or(Err(())));
// Step 5
let default_port = whatwg_scheme_type_mapper(scheme).default_port();
if Some(*port) == default_port {
Ok(format!("{}://{}", scheme, host))
} else {
Ok(format!("{}://{}{}", scheme, host, port))
}
}
_ => Err(())
}
}
fn global_user_agent() -> String {
// TODO have a better useragent string
const USER_AGENT_STRING: &'static str = "Servo";
@ -1126,7 +1078,7 @@ fn global_user_agent() -> String {
}
fn has_credentials(url: &Url) -> bool {
!url.username().unwrap_or("").is_empty() || url.password().is_some()
!url.username().is_empty() || url.password().is_some()
}
fn is_no_store_cache(headers: &Headers) -> bool {
@ -1156,19 +1108,6 @@ fn is_simple_method(m: &Method) -> bool {
}
}
fn includes_credentials(url: &Url) -> bool {
if url.password().is_some() {
return true
}
if let Some(name) = url.username() {
return name.len() > 0
}
false
}
fn response_needs_revalidation(_response: &Response) -> bool {
// TODO this function
false

View file

@ -67,7 +67,7 @@ pub fn factory(load_data: LoadData,
senders: LoadConsumer,
classifier: Arc<MIMEClassifier>,
cancel_listener: CancellationListener) {
assert!(&*load_data.url.scheme == "file");
assert!(load_data.url.scheme() == "file");
spawn_named("file_loader".to_owned(), move || {
let file_path = match load_data.url.to_file_path() {
Ok(file_path) => file_path,

View file

@ -122,13 +122,11 @@ impl HstsList {
}
pub fn secure_url(url: &Url) -> Url {
if &*url.scheme == "http" {
if url.scheme() == "http" {
let mut secure_url = url.clone();
secure_url.scheme = "https".to_owned();
secure_url.relative_scheme_data_mut()
.map(|scheme_data| {
scheme_data.default_port = Some(443);
});
secure_url.set_scheme("https").unwrap();
// .set_port(Some(443)) would set the port to None,
// and should only be done when it was already None.
secure_url
} else {
url.clone()

View file

@ -43,7 +43,7 @@ use time;
use time::Tm;
#[cfg(any(target_os = "macos", target_os = "linux"))]
use tinyfiledialogs;
use url::Url;
use url::{Url, Position};
use util::prefs;
use util::resource_files::resources_dir_path;
use util::thread::spawn_named;
@ -89,7 +89,7 @@ pub fn factory(user_agent: String,
Arc<MIMEClassifier>,
CancellationListener) + Send> {
box move |load_data: LoadData, senders, classifier, cancel_listener| {
spawn_named(format!("http_loader for {}", load_data.url.serialize()), move || {
spawn_named(format!("http_loader for {}", load_data.url), move || {
load_for_consumer(load_data,
senders,
classifier,
@ -120,11 +120,6 @@ pub fn read_block<R: Read>(reader: &mut R) -> Result<ReadResult, ()> {
}
}
fn inner_url(url: &Url) -> Url {
let inner_url = url.non_relative_scheme_data().unwrap();
Url::parse(inner_url).unwrap()
}
pub struct HttpState {
pub hsts_list: Arc<RwLock<HstsList>>,
pub cookie_jar: Arc<RwLock<CookieStorage>>,
@ -399,7 +394,7 @@ fn set_cookies_from_response(url: Url, response: &HttpResponse, cookie_jar: &Arc
}
fn update_sts_list_from_response(url: &Url, response: &HttpResponse, hsts_list: &Arc<RwLock<HstsList>>) {
if url.scheme != "https" {
if url.scheme() != "https" {
return;
}
@ -526,8 +521,8 @@ pub fn modify_request_headers(headers: &mut Headers,
load_data: &LoadData) {
// Ensure that the host header is set from the original url
let host = Host {
hostname: url.serialize_host().unwrap(),
port: url.port_or_default()
hostname: url.host_str().unwrap().to_owned(),
port: url.port_or_known_default()
};
headers.set(host);
@ -576,14 +571,14 @@ fn auth_from_entry(auth_entry: &AuthCacheEntry, headers: &mut Headers) {
}
fn auth_from_url(doc_url: &Url) -> Option<Authorization<Basic>> {
match doc_url.username() {
Some(username) if username != "" => {
Some(Authorization(Basic {
username: username.to_owned(),
password: Some(doc_url.password().unwrap_or("").to_owned())
}))
},
_ => None
let username = doc_url.username();
if username != "" {
Some(Authorization(Basic {
username: username.to_owned(),
password: Some(doc_url.password().unwrap_or("").to_owned())
}))
} else {
None
}
}
@ -739,16 +734,16 @@ pub fn load<A, B>(load_data: &LoadData,
// real URL that should be used for which the source is to be viewed.
// Change our existing URL to that and keep note that we are viewing
// the source rather than rendering the contents of the URL.
let viewing_source = doc_url.scheme == "view-source";
let viewing_source = doc_url.scheme() == "view-source";
if viewing_source {
doc_url = inner_url(&load_data.url);
doc_url = Url::parse(&load_data.url[Position::BeforeUsername..]).unwrap();
}
// Loop to handle redirects.
loop {
iters = iters + 1;
if &*doc_url.scheme == "http" && request_must_be_secured(&doc_url, &http_state.hsts_list) {
if doc_url.scheme() == "http" && request_must_be_secured(&doc_url, &http_state.hsts_list) {
info!("{} is in the strict transport security list, requesting secure host", doc_url);
doc_url = secure_url(&doc_url);
}
@ -758,8 +753,8 @@ pub fn load<A, B>(load_data: &LoadData,
"too many redirects".to_owned()));
}
if &*doc_url.scheme != "http" && &*doc_url.scheme != "https" {
let s = format!("{} request, but we don't support that scheme", &*doc_url.scheme);
if !matches!(doc_url.scheme(), "http" | "https") {
let s = format!("{} request, but we don't support that scheme", doc_url.scheme());
return Err(LoadError::new(doc_url, LoadErrorType::UnsupportedScheme, s));
}
@ -767,7 +762,7 @@ pub fn load<A, B>(load_data: &LoadData,
return Err(LoadError::new(doc_url, LoadErrorType::Cancelled, "load cancelled".to_owned()));
}
info!("requesting {}", doc_url.serialize());
info!("requesting {}", doc_url);
// Avoid automatically preserving request headers when redirects occur.
// See https://bugzilla.mozilla.org/show_bug.cgi?id=401564 and
@ -877,7 +872,7 @@ pub fn load<A, B>(load_data: &LoadData,
});
metadata.headers = Some(adjusted_headers);
metadata.status = Some(response.status_raw().clone());
metadata.https_state = if doc_url.scheme == "https" {
metadata.https_state = if doc_url.scheme() == "https" {
HttpsState::Modern
} else {
HttpsState::None

View file

@ -18,8 +18,8 @@ extern crate flate2;
extern crate hyper;
extern crate immeta;
extern crate ipc_channel;
#[macro_use]
extern crate log;
#[macro_use] extern crate log;
#[macro_use] extern crate matches;
#[macro_use]
extern crate mime;
extern crate mime_guess;

View file

@ -397,7 +397,7 @@ impl ResourceManager {
});
let cancel_listener = CancellationListener::new(cancel_resource);
let loader = match &*load_data.url.scheme {
let loader = match load_data.url.scheme() {
"chrome" => from_factory(chrome_loader::factory),
"file" => from_factory(file_loader::factory),
"http" | "https" | "view-source" => {
@ -414,12 +414,12 @@ impl ResourceManager {
"data" => from_factory(data_loader::factory),
"about" => from_factory(about_loader::factory),
_ => {
debug!("resource_thread: no loader for scheme {}", load_data.url.scheme);
debug!("resource_thread: no loader for scheme {}", load_data.url.scheme());
send_error(load_data.url, NetworkError::Internal("no loader for scheme".to_owned()), consumer);
return
}
};
debug!("resource_thread: loading url: {}", load_data.url.serialize());
debug!("resource_thread: loading url: {}", load_data.url);
loader.call_box((load_data,
consumer,

View file

@ -221,15 +221,6 @@ impl StorageManager {
}
fn origin_as_string(&self, url: Url) -> String {
let mut origin = "".to_owned();
origin.push_str(&url.scheme);
origin.push_str("://");
url.domain().map(|domain| origin.push_str(&domain));
url.port().map(|port| {
origin.push_str(":");
origin.push_str(&port.to_string());
});
origin.push_str("/");
origin
url.origin().ascii_serialization()
}
}

View file

@ -32,8 +32,8 @@ fn establish_a_websocket_connection(resource_url: &Url, net_url: (Host, String,
-> WebSocketResult<(Headers, Sender<WebSocketStream>, Receiver<WebSocketStream>)> {
let host = Host {
hostname: resource_url.serialize_host().unwrap(),
port: resource_url.port_or_default()
hostname: resource_url.host_str().unwrap().to_owned(),
port: resource_url.port_or_known_default(),
};
let mut request = try!(Client::connect(net_url));