Auto merge of #22099 - pyfisch:net-fmt, r=jdm

Format net and net_traits crates

Manually formatted the fetch_scheme file portion to limit right drift.

Part of #21373

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/22099)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2018-11-05 16:03:58 -05:00 committed by GitHub
commit b00ee41ac1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 5200 additions and 3031 deletions

View file

@ -19,16 +19,16 @@ use servo_url::ServoUrl;
/// https://fetch.spec.whatwg.org/#concept-basic-fetch (partial)
// TODO: make async.
pub fn load_blob_sync
(url: ServoUrl,
filemanager: FileManager)
-> Result<(HeaderMap, Vec<u8>), NetworkError> {
pub fn load_blob_sync(
url: ServoUrl,
filemanager: FileManager,
) -> Result<(HeaderMap, Vec<u8>), NetworkError> {
let (id, origin) = match parse_blob_url(&url) {
Ok((id, origin)) => (id, origin),
Err(()) => {
let e = format!("Invalid blob URL format {:?}", url);
return Err(NetworkError::Internal(e));
}
},
};
let (sender, receiver) = ipc::channel().unwrap();
@ -38,11 +38,13 @@ pub fn load_blob_sync
let blob_buf = match receiver.recv().unwrap() {
Ok(ReadFileProgress::Meta(blob_buf)) => blob_buf,
Ok(_) => {
return Err(NetworkError::Internal("Invalid filemanager reply".to_string()));
}
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);
@ -51,19 +53,31 @@ pub fn load_blob_sync
let mut headers = HeaderMap::new();
if let Some(name) = blob_buf.filename {
let charset = charset.map(|c| c.as_ref().into()).unwrap_or("us-ascii".to_owned());
let charset = charset
.map(|c| c.as_ref().into())
.unwrap_or("us-ascii".to_owned());
// TODO(eijebong): Replace this once the typed header is there
headers.insert(
header::CONTENT_DISPOSITION,
HeaderValue::from_bytes(
format!("inline; {}",
format!(
"inline; {}",
if charset.to_lowercase() == "utf-8" {
format!("filename=\"{}\"", String::from_utf8(name.as_bytes().into()).unwrap())
format!(
"filename=\"{}\"",
String::from_utf8(name.as_bytes().into()).unwrap()
)
} else {
format!("filename*=\"{}\"''{}", charset, http_percent_encode(name.as_bytes()))
format!(
"filename*=\"{}\"''{}",
charset,
http_percent_encode(name.as_bytes())
)
}
).as_bytes()
).unwrap()
)
.as_bytes(),
)
.unwrap(),
);
}
@ -77,16 +91,18 @@ pub fn load_blob_sync
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()));
}
return Err(NetworkError::Internal(
"Invalid filemanager reply".to_string(),
));
},
Err(e) => {
return Err(NetworkError::Internal(format!("{:?}", e)));
}
},
}
}
}

View file

@ -20,7 +20,7 @@ use tokio::prelude::future::Executor;
pub const BUF_SIZE: usize = 32768;
pub struct HttpConnector {
inner: HyperHttpConnector
inner: HyperHttpConnector,
}
impl HttpConnector {
@ -28,9 +28,7 @@ impl HttpConnector {
let mut inner = HyperHttpConnector::new(4);
inner.enforce_http(false);
inner.set_happy_eyeballs_timeout(None);
HttpConnector {
inner
}
HttpConnector { inner }
}
}
@ -60,10 +58,7 @@ impl WrappedBody {
}
pub fn new_with_decoder(body: Body, decoder: Decoder) -> Self {
WrappedBody {
body,
decoder,
}
WrappedBody { body, decoder }
}
}
@ -90,7 +85,7 @@ impl Stream for WrappedBody {
let len = decoder.read(&mut buf).ok()?;
buf.truncate(len);
Some(buf.into())
}
},
Decoder::Gzip(None) => {
let mut buf = vec![0; BUF_SIZE];
let mut decoder = GzDecoder::new(Cursor::new(chunk.into_bytes()));
@ -98,21 +93,21 @@ impl Stream for WrappedBody {
buf.truncate(len);
self.decoder = Decoder::Gzip(Some(decoder));
Some(buf.into())
}
},
Decoder::Deflate(ref mut decoder) => {
let mut buf = vec![0; BUF_SIZE];
*decoder.get_mut() = Cursor::new(chunk.into_bytes());
let len = decoder.read(&mut buf).ok()?;
buf.truncate(len);
Some(buf.into())
}
},
Decoder::Brotli(ref mut decoder) => {
let mut buf = vec![0; BUF_SIZE];
decoder.get_mut().get_mut().extend(&chunk.into_bytes());
let len = decoder.read(&mut buf).ok()?;
buf.truncate(len);
Some(buf.into())
}
},
}
} else {
None
@ -134,33 +129,46 @@ pub fn create_ssl_connector_builder(certs: &str) -> SslConnectorBuilder {
let (cert, rest) = certs.split_at(index + token.len());
certs = rest;
let cert = x509::X509::from_pem(cert.as_bytes()).unwrap();
ssl_connector_builder.cert_store_mut().add_cert(cert).or_else(|e| {
let v: Option<Option<&str>> = e.errors().iter().nth(0).map(|e| e.reason());
if v == Some(Some("cert already in hash table")) {
warn!("Cert already in hash table. Ignoring.");
// Ignore error X509_R_CERT_ALREADY_IN_HASH_TABLE which means the
// certificate is already in the store.
Ok(())
} else {
Err(e)
}
}).expect("could not set CA file");
ssl_connector_builder
.cert_store_mut()
.add_cert(cert)
.or_else(|e| {
let v: Option<Option<&str>> = e.errors().iter().nth(0).map(|e| e.reason());
if v == Some(Some("cert already in hash table")) {
warn!("Cert already in hash table. Ignoring.");
// Ignore error X509_R_CERT_ALREADY_IN_HASH_TABLE which means the
// certificate is already in the store.
Ok(())
} else {
Err(e)
}
})
.expect("could not set CA file");
} else {
break;
}
}
ssl_connector_builder.set_cipher_list(DEFAULT_CIPHERS).expect("could not set ciphers");
ssl_connector_builder.set_options(SslOptions::NO_SSLV2 | SslOptions::NO_SSLV3 | SslOptions::NO_COMPRESSION);
ssl_connector_builder
.set_cipher_list(DEFAULT_CIPHERS)
.expect("could not set ciphers");
ssl_connector_builder
.set_options(SslOptions::NO_SSLV2 | SslOptions::NO_SSLV3 | SslOptions::NO_COMPRESSION);
ssl_connector_builder
}
pub fn create_http_client<E>(ssl_connector_builder: SslConnectorBuilder, executor: E)
-> Client<Connector, WrappedBody>
where
E: Executor<Box<Future<Error=(), Item=()> + Send + 'static>> + Sync + Send + 'static
pub fn create_http_client<E>(
ssl_connector_builder: SslConnectorBuilder,
executor: E,
) -> Client<Connector, WrappedBody>
where
E: Executor<Box<Future<Error = (), Item = ()> + Send + 'static>> + Sync + Send + 'static,
{
let connector = HttpsConnector::with_connector(HttpConnector::new(), ssl_connector_builder).unwrap();
Client::builder().http1_title_case_headers(true).executor(executor).build(connector)
let connector =
HttpsConnector::with_connector(HttpConnector::new(), ssl_connector_builder).unwrap();
Client::builder()
.http1_title_case_headers(true)
.executor(executor)
.build(connector)
}
// The basic logic here is to prefer ciphers with ECDSA certificates, Forward

View file

@ -19,23 +19,32 @@ use time::{Tm, now, at, Duration};
/// which cookie-rs and hyper's header parsing do not support.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Cookie {
#[serde(deserialize_with = "hyper_serde::deserialize",
serialize_with = "hyper_serde::serialize")]
#[serde(
deserialize_with = "hyper_serde::deserialize",
serialize_with = "hyper_serde::serialize"
)]
pub cookie: cookie_rs::Cookie<'static>,
pub host_only: bool,
pub persistent: bool,
#[serde(deserialize_with = "hyper_serde::deserialize",
serialize_with = "hyper_serde::serialize")]
#[serde(
deserialize_with = "hyper_serde::deserialize",
serialize_with = "hyper_serde::serialize"
)]
pub creation_time: Tm,
#[serde(deserialize_with = "hyper_serde::deserialize",
serialize_with = "hyper_serde::serialize")]
#[serde(
deserialize_with = "hyper_serde::deserialize",
serialize_with = "hyper_serde::serialize"
)]
pub last_access: Tm,
pub expiry_time: Option<Serde<Tm>>,
}
impl Cookie {
pub fn from_cookie_string(cookie_str: String, request: &ServoUrl,
source: CookieSource) -> Option<Cookie> {
pub fn from_cookie_string(
cookie_str: String,
request: &ServoUrl,
source: CookieSource,
) -> Option<Cookie> {
cookie_rs::Cookie::parse(cookie_str)
.ok()
.map(|cookie| Cookie::new_wrapped(cookie, request, source))
@ -43,15 +52,21 @@ impl Cookie {
}
/// <http://tools.ietf.org/html/rfc6265#section-5.3>
pub fn new_wrapped(mut cookie: cookie_rs::Cookie<'static>, request: &ServoUrl, source: CookieSource)
-> Option<Cookie> {
pub fn new_wrapped(
mut cookie: cookie_rs::Cookie<'static>,
request: &ServoUrl,
source: CookieSource,
) -> Option<Cookie> {
// Step 3
let (persistent, expiry_time) = match (cookie.max_age(), cookie.expires()) {
(Some(max_age), _) => {
(true, Some(at(now().to_timespec() + Duration::seconds(max_age.num_seconds()))))
}
(Some(max_age), _) => (
true,
Some(at(
now().to_timespec() + Duration::seconds(max_age.num_seconds())
)),
),
(_, Some(expires)) => (true, Some(expires)),
_ => (false, None)
_ => (false, None),
};
let url_host = request.host_str().unwrap_or("").to_owned();
@ -64,7 +79,7 @@ impl Cookie {
if domain == url_host {
domain = "".to_string();
} else {
return None
return None;
}
}
@ -83,16 +98,18 @@ impl Cookie {
// Step 7
let mut has_path_specified = true;
let mut path = cookie.path().unwrap_or_else(|| {
has_path_specified = false;
""
}).to_owned();
let mut path = cookie
.path()
.unwrap_or_else(|| {
has_path_specified = false;
""
})
.to_owned();
if path.chars().next() != Some('/') {
path = Cookie::default_path(&request.path().to_owned()).to_string();
}
cookie.set_path(path);
// Step 10
if cookie.http_only().unwrap_or(false) && source == CookieSource::NonHTTP {
return None;
@ -101,14 +118,14 @@ impl Cookie {
// https://tools.ietf.org/html/draft-west-cookie-prefixes-04#section-4
// Step 1 of cookie prefixes
if (cookie.name().starts_with("__Secure-") || cookie.name().starts_with("__Host-")) &&
!(cookie.secure().unwrap_or(false) && request.is_secure_scheme())
!(cookie.secure().unwrap_or(false) && request.is_secure_scheme())
{
return None;
}
// Step 2 of cookie prefixes
if cookie.name().starts_with("__Host-") &&
!(host_only && has_path_specified && cookie.path().unwrap() == "/")
!(host_only && has_path_specified && cookie.path().unwrap() == "/")
{
return None;
}
@ -152,16 +169,16 @@ impl Cookie {
// The cookie-path and the request-path are identical.
request_path == cookie_path ||
(request_path.starts_with(cookie_path) && (
// The cookie-path is a prefix of the request-path, and the last
// character of the cookie-path is %x2F ("/").
cookie_path.ends_with("/") ||
(request_path.starts_with(cookie_path) &&
(
// The cookie-path is a prefix of the request-path, and the last
// character of the cookie-path is %x2F ("/").
cookie_path.ends_with("/") ||
// The cookie-path is a prefix of the request-path, and the first
// character of the request-path that is not included in the cookie-
// path is a %x2F ("/") character.
request_path[cookie_path.len()..].starts_with("/")
))
))
}
// http://tools.ietf.org/html/rfc6265#section-5.1.3
@ -170,10 +187,10 @@ impl Cookie {
let domain_string = &domain_string.to_lowercase();
string == domain_string ||
(string.ends_with(domain_string) &&
string.as_bytes()[string.len()-domain_string.len()-1] == b'.' &&
string.parse::<Ipv4Addr>().is_err() &&
string.parse::<Ipv6Addr>().is_err())
(string.ends_with(domain_string) &&
string.as_bytes()[string.len() - domain_string.len() - 1] == b'.' &&
string.parse::<Ipv4Addr>().is_err() &&
string.parse::<Ipv6Addr>().is_err())
}
// http://tools.ietf.org/html/rfc6265#section-5.4 step 1

View file

@ -33,7 +33,12 @@ impl CookieStorage {
}
// http://tools.ietf.org/html/rfc6265#section-5.3
pub fn remove(&mut self, cookie: &Cookie, url: &ServoUrl, source: CookieSource) -> Result<Option<Cookie>, ()> {
pub fn remove(
&mut self,
cookie: &Cookie,
url: &ServoUrl,
source: CookieSource,
) -> Result<Option<Cookie>, ()> {
let domain = reg_host(cookie.cookie.domain().as_ref().unwrap_or(&""));
let cookies = self.cookies_map.entry(domain).or_insert(vec![]);
@ -47,10 +52,10 @@ impl CookieStorage {
let existing_path = c.cookie.path().as_ref().unwrap().to_owned();
c.cookie.name() == cookie.cookie.name() &&
c.cookie.secure().unwrap_or(false) &&
(Cookie::domain_match(new_domain, existing_domain) ||
Cookie::domain_match(existing_domain, new_domain)) &&
Cookie::path_match(new_path, existing_path)
c.cookie.secure().unwrap_or(false) &&
(Cookie::domain_match(new_domain, existing_domain) ||
Cookie::domain_match(existing_domain, new_domain)) &&
Cookie::path_match(new_path, existing_path)
});
if any_overlapping {
@ -61,8 +66,8 @@ impl CookieStorage {
// Step 11.1
let position = cookies.iter().position(|c| {
c.cookie.domain() == cookie.cookie.domain() &&
c.cookie.path() == cookie.cookie.path() &&
c.cookie.name() == cookie.cookie.name()
c.cookie.path() == cookie.cookie.path() &&
c.cookie.name() == cookie.cookie.name()
});
if let Some(ind) = position {
@ -111,7 +116,9 @@ impl CookieStorage {
let new_len = cookies.len();
// https://www.ietf.org/id/draft-ietf-httpbis-cookie-alone-01.txt
if new_len == old_len && !evict_one_cookie(cookie.cookie.secure().unwrap_or(false), cookies) {
if new_len == old_len &&
!evict_one_cookie(cookie.cookie.secure().unwrap_or(false), cookies)
{
return;
}
}
@ -126,7 +133,7 @@ impl CookieStorage {
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,
@ -136,13 +143,17 @@ impl CookieStorage {
// http://tools.ietf.org/html/rfc6265#section-5.4
pub fn cookies_for_url(&mut self, url: &ServoUrl, 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));
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)
};
@ -161,7 +172,9 @@ impl CookieStorage {
(match acc.len() {
0 => acc,
_ => acc + "; ",
}) + &c.cookie.name() + "=" + &c.cookie.value()
}) + &c.cookie.name() +
"=" +
&c.cookie.value()
};
let result = url_cookies.iter_mut().fold("".to_owned(), reducer);
@ -172,17 +185,21 @@ impl CookieStorage {
}
}
pub fn cookies_data_for_url<'a>(&'a mut self,
url: &'a ServoUrl,
source: CookieSource)
-> impl Iterator<Item = cookie_rs::Cookie<'static>> + 'a {
pub fn cookies_data_for_url<'a>(
&'a mut self,
url: &'a ServoUrl,
source: CookieSource,
) -> impl Iterator<Item = cookie_rs::Cookie<'static>> + 'a {
let domain = reg_host(url.host_str().unwrap_or(""));
let cookies = self.cookies_map.entry(domain).or_insert(vec![]);
cookies.iter_mut().filter(move |c| c.appropriate_for_url(url, source)).map(|c| {
c.touch();
c.cookie.clone()
})
cookies
.iter_mut()
.filter(move |c| c.appropriate_for_url(url, source))
.map(|c| {
c.touch();
c.cookie.clone()
})
}
}
@ -220,7 +237,10 @@ fn get_oldest_accessed(is_secure_cookie: bool, cookies: &mut Vec<Cookie>) -> Opt
let mut oldest_accessed: Option<(usize, Tm)> = None;
for (i, c) in cookies.iter().enumerate() {
if (c.cookie.secure().unwrap_or(false) == is_secure_cookie) &&
oldest_accessed.as_ref().map_or(true, |a| c.last_access < a.1) {
oldest_accessed
.as_ref()
.map_or(true, |a| c.last_access < a.1)
{
oldest_accessed = Some((i, c.last_access));
}
}

View file

@ -18,7 +18,9 @@ pub type DecodeData = (Mime, Vec<u8>);
pub fn decode(url: &ServoUrl) -> Result<DecodeData, DecodeError> {
assert_eq!(url.scheme(), "data");
// Split out content type and data.
let parts: Vec<&str> = url[Position::BeforePath..Position::AfterQuery].splitn(2, ',').collect();
let parts: Vec<&str> = url[Position::BeforePath..Position::AfterQuery]
.splitn(2, ',')
.collect();
if parts.len() != 2 {
return Err(DecodeError::InvalidDataUri);
}
@ -36,15 +38,18 @@ pub fn decode(url: &ServoUrl) -> Result<DecodeData, DecodeError> {
ct_str.to_owned()
};
let content_type = ct_str.parse().unwrap_or_else(|_| {
"text/plain; charset=US-ASCII".parse().unwrap()
});
let content_type = ct_str
.parse()
.unwrap_or_else(|_| "text/plain; charset=US-ASCII".parse().unwrap());
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.
bytes = bytes.into_iter().filter(|&b| b != b' ').collect::<Vec<u8>>();
bytes = bytes
.into_iter()
.filter(|&b| b != b' ')
.collect::<Vec<u8>>();
match base64::decode(&bytes) {
Err(..) => return Err(DecodeError::NonBase64DataUri),
Ok(data) => bytes = data,

View file

@ -21,21 +21,21 @@ use time::{self, Timespec};
#[derive(Clone, Debug)]
pub enum HeaderOrMethod {
HeaderData(HeaderName),
MethodData(Method)
MethodData(Method),
}
impl HeaderOrMethod {
fn match_header(&self, header_name: &HeaderName) -> bool {
match *self {
HeaderOrMethod::HeaderData(ref n) => n == header_name,
_ => false
_ => false,
}
}
fn match_method(&self, method: &Method) -> bool {
match *self {
HeaderOrMethod::MethodData(ref m) => m == method,
_ => false
_ => false,
}
}
}
@ -48,19 +48,24 @@ pub struct CorsCacheEntry {
pub max_age: u32,
pub credentials: bool,
pub header_or_method: HeaderOrMethod,
created: Timespec
created: Timespec,
}
impl CorsCacheEntry {
fn new(origin: Origin, url: ServoUrl, max_age: u32, credentials: bool,
header_or_method: HeaderOrMethod) -> CorsCacheEntry {
fn new(
origin: Origin,
url: ServoUrl,
max_age: u32,
credentials: bool,
header_or_method: HeaderOrMethod,
) -> CorsCacheEntry {
CorsCacheEntry {
origin: origin,
url: url,
max_age: max_age,
credentials: credentials,
header_or_method: header_or_method,
created: time::now().to_timespec()
created: time::now().to_timespec(),
}
}
}
@ -80,25 +85,36 @@ impl CorsCache {
CorsCache(vec![])
}
fn find_entry_by_header<'a>(&'a mut self, request: &Request,
header_name: &HeaderName) -> Option<&'a mut CorsCacheEntry> {
fn find_entry_by_header<'a>(
&'a mut self,
request: &Request,
header_name: &HeaderName,
) -> Option<&'a mut CorsCacheEntry> {
self.cleanup();
self.0.iter_mut().find(|e| match_headers(e, request) && e.header_or_method.match_header(header_name))
self.0
.iter_mut()
.find(|e| match_headers(e, request) && e.header_or_method.match_header(header_name))
}
fn find_entry_by_method<'a>(&'a mut self, request: &Request,
method: Method) -> Option<&'a mut CorsCacheEntry> {
fn find_entry_by_method<'a>(
&'a mut self,
request: &Request,
method: Method,
) -> Option<&'a mut CorsCacheEntry> {
// we can take the method from CorSRequest itself
self.cleanup();
self.0.iter_mut().find(|e| match_headers(e, request) && e.header_or_method.match_method(&method))
self.0
.iter_mut()
.find(|e| match_headers(e, request) && e.header_or_method.match_method(&method))
}
/// [Clear the cache](https://fetch.spec.whatwg.org/#concept-cache-clear)
pub fn clear(&mut self, request: &Request) {
let CorsCache(buf) = self.clone();
let new_buf: Vec<CorsCacheEntry> =
buf.into_iter().filter(|e| e.origin == request.origin &&
request.current_url() == e.url).collect();
let new_buf: Vec<CorsCacheEntry> = buf
.into_iter()
.filter(|e| e.origin == request.origin && request.current_url() == e.url)
.collect();
*self = CorsCache(new_buf);
}
@ -106,9 +122,10 @@ impl CorsCache {
pub fn cleanup(&mut self) {
let CorsCache(buf) = self.clone();
let now = time::now().to_timespec();
let new_buf: Vec<CorsCacheEntry> = buf.into_iter()
.filter(|e| now.sec < e.created.sec + e.max_age as i64)
.collect();
let new_buf: Vec<CorsCacheEntry> = buf
.into_iter()
.filter(|e| now.sec < e.created.sec + e.max_age as i64)
.collect();
*self = CorsCache(new_buf);
}
@ -122,16 +139,27 @@ impl CorsCache {
/// [matching header](https://fetch.spec.whatwg.org/#concept-cache-match-header) is found.
///
/// If not, it will insert an equivalent entry
pub fn match_header_and_update(&mut self, request: &Request,
header_name: &HeaderName, new_max_age: u32) -> bool {
match self.find_entry_by_header(&request, header_name).map(|e| e.max_age = new_max_age) {
pub fn match_header_and_update(
&mut self,
request: &Request,
header_name: &HeaderName,
new_max_age: u32,
) -> bool {
match self
.find_entry_by_header(&request, header_name)
.map(|e| e.max_age = new_max_age)
{
Some(_) => true,
None => {
self.insert(CorsCacheEntry::new(request.origin.clone(), request.current_url(), new_max_age,
request.credentials_mode == CredentialsMode::Include,
HeaderOrMethod::HeaderData(header_name.clone())));
self.insert(CorsCacheEntry::new(
request.origin.clone(),
request.current_url(),
new_max_age,
request.credentials_mode == CredentialsMode::Include,
HeaderOrMethod::HeaderData(header_name.clone()),
));
false
}
},
}
}
@ -145,15 +173,27 @@ impl CorsCache {
/// [a matching method](https://fetch.spec.whatwg.org/#concept-cache-match-method) is found.
///
/// If not, it will insert an equivalent entry
pub fn match_method_and_update(&mut self, request: &Request, method: Method, new_max_age: u32) -> bool {
match self.find_entry_by_method(&request, method.clone()).map(|e| e.max_age = new_max_age) {
pub fn match_method_and_update(
&mut self,
request: &Request,
method: Method,
new_max_age: u32,
) -> bool {
match self
.find_entry_by_method(&request, method.clone())
.map(|e| e.max_age = new_max_age)
{
Some(_) => true,
None => {
self.insert(CorsCacheEntry::new(request.origin.clone(), request.current_url(), new_max_age,
request.credentials_mode == CredentialsMode::Include,
HeaderOrMethod::MethodData(method)));
self.insert(CorsCacheEntry::new(
request.origin.clone(),
request.current_url(),
new_max_age,
request.credentials_mode == CredentialsMode::Include,
HeaderOrMethod::MethodData(method),
));
false
}
},
}
}

View file

@ -35,7 +35,8 @@ use std::thread;
use subresource_integrity::is_response_integrity_valid;
lazy_static! {
static ref X_CONTENT_TYPE_OPTIONS: HeaderName = HeaderName::from_static("x-content-type-options");
static ref X_CONTENT_TYPE_OPTIONS: HeaderName =
HeaderName::from_static("x-content-type-options");
}
const FILE_CHUNK_SIZE: usize = 32768; //32 KB
@ -87,16 +88,16 @@ impl CancellationListener {
pub type DoneChannel = Option<(Sender<Data>, Receiver<Data>)>;
/// [Fetch](https://fetch.spec.whatwg.org#concept-fetch)
pub fn fetch(request: &mut Request,
target: Target,
context: &FetchContext) {
pub fn fetch(request: &mut Request, target: Target, context: &FetchContext) {
fetch_with_cors_cache(request, &mut CorsCache::new(), target, context);
}
pub fn fetch_with_cors_cache(request: &mut Request,
cache: &mut CorsCache,
target: Target,
context: &FetchContext) {
pub fn fetch_with_cors_cache(
request: &mut Request,
cache: &mut CorsCache,
target: Target,
context: &FetchContext,
) {
// Step 1.
if request.window == Window::Client {
// TODO: Set window to request's client object if client is a Window object
@ -132,21 +133,27 @@ pub fn fetch_with_cors_cache(request: &mut Request,
}
/// [Main fetch](https://fetch.spec.whatwg.org/#concept-main-fetch)
pub fn main_fetch(request: &mut Request,
cache: &mut CorsCache,
cors_flag: bool,
recursive_flag: bool,
target: Target,
done_chan: &mut DoneChannel,
context: &FetchContext)
-> Response {
pub fn main_fetch(
request: &mut Request,
cache: &mut CorsCache,
cors_flag: bool,
recursive_flag: bool,
target: Target,
done_chan: &mut DoneChannel,
context: &FetchContext,
) -> Response {
// Step 1.
let mut response = None;
// Step 2.
if request.local_urls_only {
if !matches!(request.current_url().scheme(), "about" | "blob" | "data" | "filesystem") {
response = Some(Response::network_error(NetworkError::Internal("Non-local scheme".into())));
if !matches!(
request.current_url().scheme(),
"about" | "blob" | "data" | "filesystem"
) {
response = Some(Response::network_error(NetworkError::Internal(
"Non-local scheme".into(),
)));
}
}
@ -158,7 +165,9 @@ pub fn main_fetch(request: &mut Request,
// Step 5.
if should_be_blocked_due_to_bad_port(&request.current_url()) {
response = Some(Response::network_error(NetworkError::Internal("Request attempted on bad port".into())));
response = Some(Response::network_error(NetworkError::Internal(
"Request attempted on bad port".into(),
)));
}
// TODO: handle blocking as mixed content.
// TODO: handle blocking by content security policy.
@ -167,7 +176,9 @@ pub fn main_fetch(request: &mut Request,
// TODO: handle request's client's referrer policy.
// Step 7.
request.referrer_policy = request.referrer_policy.or(Some(ReferrerPolicy::NoReferrerWhenDowngrade));
request.referrer_policy = request
.referrer_policy
.or(Some(ReferrerPolicy::NoReferrerWhenDowngrade));
// Step 8.
{
@ -182,11 +193,13 @@ pub fn main_fetch(request: &mut Request,
Referrer::ReferrerUrl(url) => {
request.headers.remove(header::REFERER);
let current_url = request.current_url().clone();
determine_request_referrer(&mut request.headers,
request.referrer_policy.unwrap(),
url,
current_url)
}
determine_request_referrer(
&mut request.headers,
request.referrer_policy.unwrap(),
url,
current_url,
)
},
};
if let Some(referrer_url) = referrer_url {
request.referrer = Referrer::ReferrerUrl(referrer_url);
@ -197,8 +210,12 @@ pub fn main_fetch(request: &mut Request,
// TODO: handle FTP URLs.
// Step 10.
context.state.hsts_list.read().unwrap().switch_known_hsts_host_domain_url_to_https(
request.current_url_mut());
context
.state
.hsts_list
.read()
.unwrap()
.switch_known_hsts_host_domain_url_to_https(request.current_url_mut());
// Step 11.
// Not applicable: see fetch_async.
@ -218,47 +235,48 @@ pub fn main_fetch(request: &mut Request,
// and about: schemes, but CSS tests will break on loading Ahem
// since we load them through a file: URL.
current_url.scheme() == "about" ||
request.mode == RequestMode::Navigate {
request.mode == RequestMode::Navigate
{
// Substep 1.
request.response_tainting = ResponseTainting::Basic;
// Substep 2.
scheme_fetch(request, cache, target, done_chan, context)
} else if request.mode == RequestMode::SameOrigin {
Response::network_error(NetworkError::Internal("Cross-origin response".into()))
} else if request.mode == RequestMode::NoCors {
// Substep 1.
request.response_tainting = ResponseTainting::Opaque;
// Substep 2.
scheme_fetch(request, cache, target, done_chan, context)
} else if !matches!(current_url.scheme(), "http" | "https") {
Response::network_error(NetworkError::Internal("Non-http scheme".into()))
} else if request.use_cors_preflight ||
(request.unsafe_request &&
(!is_cors_safelisted_method(&request.method) ||
request.headers.iter().any(|(name, value)| !is_cors_safelisted_request_header(&name, &value)))) {
request.headers.iter().any(|(name, value)| {
!is_cors_safelisted_request_header(&name, &value)
}))) {
// Substep 1.
request.response_tainting = ResponseTainting::CorsTainting;
// Substep 2.
let response = http_fetch(request, cache, true, true, false,
target, done_chan, context);
let response = http_fetch(
request, cache, true, true, false, target, done_chan, context,
);
// Substep 3.
if response.is_network_error() {
// TODO clear cache entries using request
}
// Substep 4.
response
} else {
// Substep 1.
request.response_tainting = ResponseTainting::CorsTainting;
// Substep 2.
http_fetch(request, cache, true, false, false, target, done_chan, context)
http_fetch(
request, cache, true, false, false, target, done_chan, context,
)
}
});
@ -272,19 +290,25 @@ pub fn main_fetch(request: &mut Request,
// Substep 1.
if request.response_tainting == ResponseTainting::CorsTainting {
// Subsubstep 1.
let header_names: Option<Vec<HeaderName>> = response.headers.typed_get::<AccessControlExposeHeaders>()
let header_names: Option<Vec<HeaderName>> = response
.headers
.typed_get::<AccessControlExposeHeaders>()
.map(|v| v.iter().collect());
match header_names {
// Subsubstep 2.
Some(ref list) if request.credentials_mode != CredentialsMode::Include => {
if list.len() == 1 && list[0] == "*" {
response.cors_exposed_header_name_list =
response.headers.iter().map(|(name, _)| name.as_str().to_owned()).collect();
response.cors_exposed_header_name_list = response
.headers
.iter()
.map(|(name, _)| name.as_str().to_owned())
.collect();
}
},
// Subsubstep 3.
Some(list) => {
response.cors_exposed_header_name_list = list.iter().map(|h| h.as_str().to_owned()).collect();
response.cors_exposed_header_name_list =
list.iter().map(|h| h.as_str().to_owned()).collect();
},
_ => (),
}
@ -304,13 +328,16 @@ pub fn main_fetch(request: &mut Request,
let internal_error = {
// Tests for steps 17 and 18, before step 15 for borrowing concerns.
let response_is_network_error = response.is_network_error();
let should_replace_with_nosniff_error =
!response_is_network_error && should_be_blocked_due_to_nosniff(request.destination, &response.headers);
let should_replace_with_mime_type_error =
!response_is_network_error && should_be_blocked_due_to_mime_type(request.destination, &response.headers);
let should_replace_with_nosniff_error = !response_is_network_error &&
should_be_blocked_due_to_nosniff(request.destination, &response.headers);
let should_replace_with_mime_type_error = !response_is_network_error &&
should_be_blocked_due_to_mime_type(request.destination, &response.headers);
// Step 15.
let mut network_error_response = response.get_network_error().cloned().map(Response::network_error);
let mut network_error_response = response
.get_network_error()
.cloned()
.map(Response::network_error);
let internal_response = if let Some(error_response) = network_error_response.as_mut() {
error_response
} else {
@ -326,27 +353,30 @@ pub fn main_fetch(request: &mut Request,
// TODO: handle blocking as mixed content.
// TODO: handle blocking by content security policy.
let blocked_error_response;
let internal_response =
if should_replace_with_nosniff_error {
// Defer rebinding result
blocked_error_response = Response::network_error(NetworkError::Internal("Blocked by nosniff".into()));
&blocked_error_response
} else if should_replace_with_mime_type_error {
// Defer rebinding result
blocked_error_response = Response::network_error(NetworkError::Internal("Blocked by mime type".into()));
&blocked_error_response
} else {
internal_response
};
let internal_response = if should_replace_with_nosniff_error {
// Defer rebinding result
blocked_error_response =
Response::network_error(NetworkError::Internal("Blocked by nosniff".into()));
&blocked_error_response
} else if should_replace_with_mime_type_error {
// Defer rebinding result
blocked_error_response =
Response::network_error(NetworkError::Internal("Blocked by mime type".into()));
&blocked_error_response
} else {
internal_response
};
// Step 18.
// We check `internal_response` since we did not mutate `response`
// in the previous step.
let not_network_error = !response_is_network_error && !internal_response.is_network_error();
if not_network_error && (is_null_body_status(&internal_response.status) ||
match request.method {
Method::HEAD | Method::CONNECT => true,
_ => false }) {
if not_network_error &&
(is_null_body_status(&internal_response.status) ||
match request.method {
Method::HEAD | Method::CONNECT => true,
_ => false,
}) {
// when Fetch is used only asynchronously, we will need to make sure
// that nothing tries to write to the body at this point
let mut body = internal_response.body.lock().unwrap();
@ -373,8 +403,11 @@ pub fn main_fetch(request: &mut Request,
// Step 19.2.
let ref integrity_metadata = &request.integrity_metadata;
if response.termination_reason.is_none() &&
!is_response_integrity_valid(integrity_metadata, &response) {
Response::network_error(NetworkError::Internal("Subresource integrity validation failed".into()))
!is_response_integrity_valid(integrity_metadata, &response)
{
Response::network_error(NetworkError::Internal(
"Subresource integrity validation failed".into(),
))
} else {
response
}
@ -410,7 +443,7 @@ pub fn main_fetch(request: &mut Request,
// Step 23.
if !response_loaded {
wait_for_response(&mut response, target, done_chan);
wait_for_response(&mut response, target, done_chan);
}
// Step 24.
@ -430,8 +463,11 @@ pub fn main_fetch(request: &mut Request,
fn wait_for_response(response: &mut Response, target: Target, done_chan: &mut DoneChannel) {
if let Some(ref ch) = *done_chan {
loop {
match ch.1.recv()
.expect("fetch worker should always send Done before terminating") {
match ch
.1
.recv()
.expect("fetch worker should always send Done before terminating")
{
Data::Payload(vec) => {
target.process_response_chunk(vec);
},
@ -439,7 +475,7 @@ fn wait_for_response(response: &mut Response, target: Target, done_chan: &mut Do
Data::Cancelled => {
response.aborted.store(true, Ordering::Relaxed);
break;
}
},
}
}
} else {
@ -456,139 +492,156 @@ fn wait_for_response(response: &mut Response, target: Target, done_chan: &mut Do
}
/// [Scheme fetch](https://fetch.spec.whatwg.org#scheme-fetch)
fn scheme_fetch(request: &mut Request,
cache: &mut CorsCache,
target: Target,
done_chan: &mut DoneChannel,
context: &FetchContext)
-> Response {
fn scheme_fetch(
request: &mut Request,
cache: &mut CorsCache,
target: Target,
done_chan: &mut DoneChannel,
context: &FetchContext,
) -> Response {
let url = request.current_url();
match url.scheme() {
"about" if url.path() == "blank" => {
let mut response = Response::new(url);
response.headers.typed_insert(ContentType::from(mime::TEXT_HTML_UTF_8));
response
.headers
.typed_insert(ContentType::from(mime::TEXT_HTML_UTF_8));
*response.body.lock().unwrap() = ResponseBody::Done(vec![]);
response
},
"http" | "https" => {
http_fetch(request, cache, false, false, false, target, done_chan, context)
},
"http" | "https" => http_fetch(
request, cache, false, false, false, target, done_chan, context,
),
"data" => {
match decode(&url) {
Ok((mime, bytes)) => {
let mut response = Response::new(url);
*response.body.lock().unwrap() = ResponseBody::Done(bytes);
response.headers.typed_insert(ContentType::from(mime));
response
},
Err(_) => Response::network_error(NetworkError::Internal("Decoding data URL failed".into()))
}
"data" => match decode(&url) {
Ok((mime, bytes)) => {
let mut response = Response::new(url);
*response.body.lock().unwrap() = ResponseBody::Done(bytes);
response.headers.typed_insert(ContentType::from(mime));
response
},
Err(_) => {
Response::network_error(NetworkError::Internal("Decoding data URL failed".into()))
},
},
"file" => {
if request.method == Method::GET {
match url.to_file_path() {
Ok(file_path) => {
match File::open(file_path.clone()) {
Ok(mut file) => {
let mime = guess_mime_type(file_path);
if request.method != Method::GET {
return Response::network_error(NetworkError::Internal(
"Unexpected method for file".into(),
));
}
if let Ok(file_path) = url.to_file_path() {
if let Ok(file) = File::open(file_path.clone()) {
let mime = guess_mime_type(file_path);
let mut response = Response::new(url);
response.headers.typed_insert(ContentType::from(mime));
let mut response = Response::new(url);
response.headers.typed_insert(ContentType::from(mime));
let (done_sender, done_receiver) = channel();
*done_chan = Some((done_sender.clone(), done_receiver));
*response.body.lock().unwrap() = ResponseBody::Receiving(vec![]);
let (done_sender, done_receiver) = channel();
*done_chan = Some((done_sender.clone(), done_receiver));
*response.body.lock().unwrap() = ResponseBody::Receiving(vec![]);
let mut res_body = response.body.clone();
let mut res_body = response.body.clone();
let cancellation_listener = context.cancellation_listener.clone();
let cancellation_listener = context.cancellation_listener.clone();
let (start, end) = if let Some(ref range) = request.headers.typed_get::<Range>() {
match range.iter().collect::<Vec<(Bound<u64>, Bound<u64>)>>().first() {
Some(&(Bound::Included(start), Bound::Unbounded)) => (start, None),
Some(&(Bound::Included(start), Bound::Included(end))) => {
// `end` should be less or equal to `start`.
(start, Some(u64::max(start, end)))
},
Some(&(Bound::Unbounded, Bound::Included(offset))) => {
if let Ok(metadata) = file.metadata() {
// `offset` cannot be bigger than the file size.
(metadata.len() - u64::min(metadata.len(), offset), None)
} else {
(0, None)
}
},
_ => (0, None)
}
let (start, end) = if let Some(ref range) = request.headers.typed_get::<Range>()
{
match range
.iter()
.collect::<Vec<(Bound<u64>, Bound<u64>)>>()
.first()
{
Some(&(Bound::Included(start), Bound::Unbounded)) => (start, None),
Some(&(Bound::Included(start), Bound::Included(end))) => {
// `end` should be less or equal to `start`.
(start, Some(u64::max(start, end)))
},
Some(&(Bound::Unbounded, Bound::Included(offset))) => {
if let Ok(metadata) = file.metadata() {
// `offset` cannot be bigger than the file size.
(metadata.len() - u64::min(metadata.len(), offset), None)
} else {
(0, None)
};
}
},
_ => (0, None),
}
} else {
(0, None)
};
thread::Builder::new().name("fetch file worker thread".to_string()).spawn(move || {
let mut reader = BufReader::with_capacity(FILE_CHUNK_SIZE, file);
if reader.seek(SeekFrom::Start(start)).is_err() {
warn!("Fetch - could not seek to {:?}", start);
}
thread::Builder::new()
.name("fetch file worker thread".to_string())
.spawn(move || {
let mut reader = BufReader::with_capacity(FILE_CHUNK_SIZE, file);
if reader.seek(SeekFrom::Start(start)).is_err() {
warn!("Fetch - could not seek to {:?}", start);
}
loop {
if cancellation_listener.lock().unwrap().cancelled() {
*res_body.lock().unwrap() = ResponseBody::Done(vec![]);
let _ = done_sender.send(Data::Cancelled);
return;
}
let length = {
let mut buffer = reader.fill_buf().unwrap().to_vec();
let mut buffer_len = buffer.len();
if let ResponseBody::Receiving(ref mut body) = *res_body.lock().unwrap() {
let offset = usize::min({
if let Some(end) = end {
let remaining_bytes =
end as usize - start as usize - body.len();
if remaining_bytes <= FILE_CHUNK_SIZE {
// This is the last chunk so we set buffer len to 0 to break
// the reading loop.
buffer_len = 0;
remaining_bytes
} else {
FILE_CHUNK_SIZE
}
loop {
if cancellation_listener.lock().unwrap().cancelled() {
*res_body.lock().unwrap() = ResponseBody::Done(vec![]);
let _ = done_sender.send(Data::Cancelled);
return;
}
let length = {
let mut buffer = reader.fill_buf().unwrap().to_vec();
let mut buffer_len = buffer.len();
if let ResponseBody::Receiving(ref mut body) =
*res_body.lock().unwrap()
{
let offset = usize::min(
{
if let Some(end) = end {
let remaining_bytes =
end as usize - start as usize - body.len();
if remaining_bytes <= FILE_CHUNK_SIZE {
// This is the last chunk so we set buffer
// len to 0 to break the reading loop.
buffer_len = 0;
remaining_bytes
} else {
FILE_CHUNK_SIZE
}
}, buffer.len());
body.extend_from_slice(&buffer[0..offset]);
let _ = done_sender.send(Data::Payload(buffer));
}
buffer_len
};
if length == 0 {
let mut body = res_body.lock().unwrap();
let completed_body = match *body {
ResponseBody::Receiving(ref mut body) => {
mem::replace(body, vec![])
},
_ => vec![],
};
*body = ResponseBody::Done(completed_body);
let _ = done_sender.send(Data::Done);
break;
}
reader.consume(length);
} else {
FILE_CHUNK_SIZE
}
},
buffer.len(),
);
body.extend_from_slice(&buffer[0..offset]);
let _ = done_sender.send(Data::Payload(buffer));
}
}).expect("Failed to create fetch file worker thread");
response
},
_ => Response::network_error(NetworkError::Internal("Opening file failed".into())),
}
},
_ => Response::network_error(NetworkError::Internal("Constructing file path failed".into()))
buffer_len
};
if length == 0 {
let mut body = res_body.lock().unwrap();
let completed_body = match *body {
ResponseBody::Receiving(ref mut body) => {
mem::replace(body, vec![])
},
_ => vec![],
};
*body = ResponseBody::Done(completed_body);
let _ = done_sender.send(Data::Done);
break;
}
reader.consume(length);
}
})
.expect("Failed to create fetch file worker thread");
response
} else {
Response::network_error(NetworkError::Internal("Opening file failed".into()))
}
} else {
Response::network_error(NetworkError::Internal("Unexpected method for file".into()))
Response::network_error(NetworkError::Internal(
"Constructing file path failed".into(),
))
}
},
@ -596,7 +649,9 @@ fn scheme_fetch(request: &mut Request,
println!("Loading blob {}", url.as_str());
// Step 2.
if request.method != Method::GET {
return Response::network_error(NetworkError::Internal("Unexpected method for blob".into()));
return Response::network_error(NetworkError::Internal(
"Unexpected method for blob".into(),
));
}
match load_blob_sync(url.clone(), context.filemanager.clone()) {
@ -618,7 +673,7 @@ fn scheme_fetch(request: &mut Request,
Response::network_error(NetworkError::Internal("Unexpected scheme".into()))
},
_ => Response::network_error(NetworkError::Internal("Unexpected scheme".into()))
_ => Response::network_error(NetworkError::Internal("Unexpected scheme".into())),
}
}
@ -627,13 +682,15 @@ pub fn is_cors_safelisted_request_header(name: &HeaderName, value: &HeaderValue)
if name == header::CONTENT_TYPE {
if let Some(m) = value.to_str().ok().and_then(|s| s.parse::<Mime>().ok()) {
m.type_() == mime::TEXT && m.subtype() == mime::PLAIN ||
m.type_() == mime::APPLICATION && m.subtype() == mime::WWW_FORM_URLENCODED ||
m.type_() == mime::MULTIPART && m.subtype() == mime::FORM_DATA
m.type_() == mime::APPLICATION && m.subtype() == mime::WWW_FORM_URLENCODED ||
m.type_() == mime::MULTIPART && m.subtype() == mime::FORM_DATA
} else {
false
}
} else {
name == header::ACCEPT || name == header::ACCEPT_LANGUAGE || name == header::CONTENT_LANGUAGE
name == header::ACCEPT ||
name == header::ACCEPT_LANGUAGE ||
name == header::CONTENT_LANGUAGE
}
}
@ -641,28 +698,35 @@ pub fn is_cors_safelisted_request_header(name: &HeaderName, value: &HeaderValue)
pub fn is_cors_safelisted_method(m: &Method) -> bool {
match *m {
Method::GET | Method::HEAD | Method::POST => true,
_ => false
_ => false,
}
}
fn is_null_body_status(status: &Option<(StatusCode, String)>) -> bool {
match *status {
Some((status, _)) => match status {
StatusCode::SWITCHING_PROTOCOLS | StatusCode::NO_CONTENT |
StatusCode::RESET_CONTENT | StatusCode::NOT_MODIFIED => true,
_ => false
StatusCode::SWITCHING_PROTOCOLS |
StatusCode::NO_CONTENT |
StatusCode::RESET_CONTENT |
StatusCode::NOT_MODIFIED => true,
_ => false,
},
_ => false
_ => false,
}
}
/// <https://fetch.spec.whatwg.org/#should-response-to-request-be-blocked-due-to-nosniff?>
pub fn should_be_blocked_due_to_nosniff(destination: Destination, response_headers: &HeaderMap) -> bool {
pub fn should_be_blocked_due_to_nosniff(
destination: Destination,
response_headers: &HeaderMap,
) -> bool {
// Steps 1-3.
// TODO(eijebong): Replace this once typed headers allow custom ones...
if response_headers.get("x-content-type-options")
.map_or(true, |val| val.to_str().unwrap_or("").to_lowercase() != "nosniff")
{
if response_headers
.get("x-content-type-options")
.map_or(true, |val| {
val.to_str().unwrap_or("").to_lowercase() != "nosniff"
}) {
return false;
}
@ -692,30 +756,34 @@ pub fn should_be_blocked_due_to_nosniff(destination: Destination, response_heade
"text/x-javascript".parse().unwrap(),
];
javascript_mime_types.iter()
javascript_mime_types
.iter()
.any(|mime| mime.type_() == mime_type.type_() && mime.subtype() == mime_type.subtype())
}
match content_type_header {
// Step 6
Some(ref ct) if destination.is_script_like()
=> !is_javascript_mime_type(&ct.clone().into()),
Some(ref ct) if destination.is_script_like() => {
!is_javascript_mime_type(&ct.clone().into())
},
// Step 7
Some(ref ct) if destination == Destination::Style
=> {
let m: mime::Mime = ct.clone().into();
m.type_() != mime::TEXT && m.subtype() != mime::CSS
},
Some(ref ct) if destination == Destination::Style => {
let m: mime::Mime = ct.clone().into();
m.type_() != mime::TEXT && m.subtype() != mime::CSS
},
None if destination == Destination::Style || destination.is_script_like() => true,
// Step 8
_ => false
_ => false,
}
}
/// <https://fetch.spec.whatwg.org/#should-response-to-request-be-blocked-due-to-mime-type?>
fn should_be_blocked_due_to_mime_type(destination: Destination, response_headers: &HeaderMap) -> bool {
fn should_be_blocked_due_to_mime_type(
destination: Destination,
response_headers: &HeaderMap,
) -> bool {
// Step 1
let mime_type: mime::Mime = match response_headers.typed_get::<ContentType>() {
Some(header) => header.into(),
@ -725,12 +793,10 @@ fn should_be_blocked_due_to_mime_type(destination: Destination, response_headers
// Step 2-3
destination.is_script_like() &&
match mime_type.type_() {
mime::AUDIO |
mime::VIDEO |
mime::IMAGE => true,
mime::AUDIO | mime::VIDEO | mime::IMAGE => true,
mime::TEXT if mime_type.subtype() == mime::CSV => true,
// Step 4
_ => false
_ => false,
}
}
@ -745,14 +811,17 @@ pub fn should_be_blocked_due_to_bad_port(url: &ServoUrl) -> bool {
// If there is no explicit port, this means the default one is used for
// the given scheme, and thus this means the request should not be blocked
// due to a bad port.
let port = if let Some(port) = url.port() { port } else { return false };
let port = if let Some(port) = url.port() {
port
} else {
return false;
};
// Step 4.
if scheme == "ftp" && (port == 20 || port == 21) {
return false;
}
// Step 5.
if is_network_scheme(scheme) && is_bad_port(port) {
return true;
@ -770,12 +839,10 @@ fn is_network_scheme(scheme: &str) -> bool {
/// <https://fetch.spec.whatwg.org/#bad-port>
fn is_bad_port(port: u16) -> bool {
static BAD_PORTS: [u16; 64] = [
1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42,
43, 53, 77, 79, 87, 95, 101, 102, 103, 104, 109, 110, 111,
113, 115, 117, 119, 123, 135, 139, 143, 179, 389, 465, 512,
513, 514, 515, 526, 530, 531, 532, 540, 556, 563, 587, 601,
636, 993, 995, 2049, 3659, 4045, 6000, 6665, 6666, 6667,
6668, 6669
1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, 43, 53, 77, 79, 87, 95, 101, 102,
103, 104, 109, 110, 111, 113, 115, 117, 119, 123, 135, 139, 143, 179, 389, 465, 512, 513,
514, 515, 526, 530, 531, 532, 540, 556, 563, 587, 601, 636, 993, 995, 2049, 3659, 4045,
6000, 6665, 6666, 6667, 6668, 6669,
];
BAD_PORTS.binary_search(&port).is_ok()

View file

@ -34,7 +34,7 @@ struct FileStoreEntry {
/// UUIDs only become valid blob URIs when explicitly requested
/// by the user with createObjectURL. Validity can be revoked as well.
/// (The UUID is the one that maps to this entry in `FileManagerStore`)
is_valid_url: AtomicBool
is_valid_url: AtomicBool,
}
#[derive(Clone)]
@ -71,29 +71,38 @@ impl FileManager {
}
}
pub fn read_file(&self,
sender: IpcSender<FileManagerResult<ReadFileProgress>>,
id: Uuid,
check_url_validity: bool,
origin: FileOrigin) {
pub fn read_file(
&self,
sender: IpcSender<FileManagerResult<ReadFileProgress>>,
id: Uuid,
check_url_validity: bool,
origin: FileOrigin,
) {
let store = self.store.clone();
thread::Builder::new().name("read file".to_owned()).spawn(move || {
if let Err(e) = store.try_read_file(&sender, id, check_url_validity,
origin) {
let _ = sender.send(Err(FileManagerThreadError::BlobURLStoreError(e)));
}
}).expect("Thread spawning failed");
thread::Builder::new()
.name("read file".to_owned())
.spawn(move || {
if let Err(e) = store.try_read_file(&sender, id, check_url_validity, origin) {
let _ = sender.send(Err(FileManagerThreadError::BlobURLStoreError(e)));
}
})
.expect("Thread spawning failed");
}
pub fn promote_memory(&self,
blob_buf: BlobBuf,
set_valid: bool,
sender: IpcSender<Result<Uuid, BlobURLStoreError>>,
origin: FileOrigin) {
pub fn promote_memory(
&self,
blob_buf: BlobBuf,
set_valid: bool,
sender: IpcSender<Result<Uuid, BlobURLStoreError>>,
origin: FileOrigin,
) {
let store = self.store.clone();
thread::Builder::new().name("transfer memory".to_owned()).spawn(move || {
store.promote_memory(blob_buf, set_valid, sender, origin);
}).expect("Thread spawning failed");
thread::Builder::new()
.name("transfer memory".to_owned())
.spawn(move || {
store.promote_memory(blob_buf, set_valid, sender, origin);
})
.expect("Thread spawning failed");
}
/// Message handler
@ -102,35 +111,41 @@ impl FileManager {
FileManagerThreadMsg::SelectFile(filter, sender, origin, opt_test_path) => {
let store = self.store.clone();
let embedder = self.embedder_proxy.clone();
thread::Builder::new().name("select file".to_owned()).spawn(move || {
store.select_file(filter, sender, origin, opt_test_path, embedder);
}).expect("Thread spawning failed");
}
thread::Builder::new()
.name("select file".to_owned())
.spawn(move || {
store.select_file(filter, sender, origin, opt_test_path, embedder);
})
.expect("Thread spawning failed");
},
FileManagerThreadMsg::SelectFiles(filter, sender, origin, opt_test_paths) => {
let store = self.store.clone();
let embedder = self.embedder_proxy.clone();
thread::Builder::new().name("select files".to_owned()).spawn(move || {
store.select_files(filter, sender, origin, opt_test_paths, embedder);
}).expect("Thread spawning failed");
}
thread::Builder::new()
.name("select files".to_owned())
.spawn(move || {
store.select_files(filter, sender, origin, opt_test_paths, embedder);
})
.expect("Thread spawning failed");
},
FileManagerThreadMsg::ReadFile(sender, id, check_url_validity, origin) => {
self.read_file(sender, id, check_url_validity, origin);
}
},
FileManagerThreadMsg::PromoteMemory(blob_buf, set_valid, sender, origin) => {
self.promote_memory(blob_buf, set_valid, sender, origin);
}
FileManagerThreadMsg::AddSlicedURLEntry(id, rel_pos, sender, origin) =>{
},
FileManagerThreadMsg::AddSlicedURLEntry(id, rel_pos, sender, origin) => {
self.store.add_sliced_url_entry(id, rel_pos, sender, origin);
}
},
FileManagerThreadMsg::DecRef(id, origin, sender) => {
let _ = sender.send(self.store.dec_ref(&id, &origin));
}
},
FileManagerThreadMsg::RevokeBlobURL(id, origin, sender) => {
let _ = sender.send(self.store.set_blob_url_validity(false, &id, &origin));
}
},
FileManagerThreadMsg::ActivateBlobURL(id, sender, origin) => {
let _ = sender.send(self.store.set_blob_url_validity(true, &id, &origin));
}
},
}
}
}
@ -150,8 +165,12 @@ impl FileManagerStore {
}
/// Copy out the file backend implementation content
fn get_impl(&self, id: &Uuid, origin_in: &FileOrigin,
check_url_validity: bool) -> Result<FileImpl, BlobURLStoreError> {
fn get_impl(
&self,
id: &Uuid,
origin_in: &FileOrigin,
check_url_validity: bool,
) -> Result<FileImpl, BlobURLStoreError> {
match self.entries.read().unwrap().get(id) {
Some(ref entry) => {
if *origin_in != *entry.origin {
@ -164,7 +183,7 @@ impl FileManagerStore {
Ok(entry.file_impl.clone())
}
}
}
},
None => Err(BlobURLStoreError::InvalidFileID),
}
}
@ -177,7 +196,7 @@ impl FileManagerStore {
self.entries.write().unwrap().remove(id);
}
fn inc_ref(&self, id: &Uuid, origin_in: &FileOrigin) -> Result<(), BlobURLStoreError>{
fn inc_ref(&self, id: &Uuid, origin_in: &FileOrigin) -> Result<(), BlobURLStoreError> {
match self.entries.read().unwrap().get(id) {
Some(entry) => {
if entry.origin == *origin_in {
@ -186,41 +205,53 @@ impl FileManagerStore {
} else {
Err(BlobURLStoreError::InvalidOrigin)
}
}
},
None => Err(BlobURLStoreError::InvalidFileID),
}
}
fn add_sliced_url_entry(&self, parent_id: Uuid, rel_pos: RelativePos,
sender: IpcSender<Result<Uuid, BlobURLStoreError>>,
origin_in: FileOrigin) {
fn add_sliced_url_entry(
&self,
parent_id: Uuid,
rel_pos: RelativePos,
sender: IpcSender<Result<Uuid, BlobURLStoreError>>,
origin_in: FileOrigin,
) {
match self.inc_ref(&parent_id, &origin_in) {
Ok(_) => {
let new_id = Uuid::new_v4();
self.insert(new_id, FileStoreEntry {
origin: origin_in,
file_impl: FileImpl::Sliced(parent_id, rel_pos),
refs: AtomicUsize::new(1),
// Valid here since AddSlicedURLEntry implies URL creation
// from a BlobImpl::Sliced
is_valid_url: AtomicBool::new(true),
});
self.insert(
new_id,
FileStoreEntry {
origin: origin_in,
file_impl: FileImpl::Sliced(parent_id, rel_pos),
refs: AtomicUsize::new(1),
// Valid here since AddSlicedURLEntry implies URL creation
// from a BlobImpl::Sliced
is_valid_url: AtomicBool::new(true),
},
);
// We assume that the returned id will be held by BlobImpl::File
let _ = sender.send(Ok(new_id));
}
},
Err(e) => {
let _ = sender.send(Err(e));
}
},
}
}
fn query_files_from_embedder(&self,
patterns: Vec<FilterPattern>,
multiple_files: bool,
embedder_proxy: EmbedderProxy) -> Option<Vec<String>> {
fn query_files_from_embedder(
&self,
patterns: Vec<FilterPattern>,
multiple_files: bool,
embedder_proxy: EmbedderProxy,
) -> Option<Vec<String>> {
let (ipc_sender, ipc_receiver) = ipc::channel().expect("Failed to create IPC channel!");
let msg = (None, EmbedderMsg::SelectFiles(patterns, multiple_files, ipc_sender));
let msg = (
None,
EmbedderMsg::SelectFiles(patterns, multiple_files, ipc_sender),
);
embedder_proxy.send(msg);
match ipc_receiver.recv() {
@ -228,23 +259,26 @@ impl FileManagerStore {
Err(e) => {
warn!("Failed to receive files from embedder ({}).", e);
None
}
},
}
}
fn select_file(&self,
patterns: Vec<FilterPattern>,
sender: IpcSender<FileManagerResult<SelectedFile>>,
origin: FileOrigin,
opt_test_path: Option<String>,
embedder_proxy: EmbedderProxy) {
fn select_file(
&self,
patterns: Vec<FilterPattern>,
sender: IpcSender<FileManagerResult<SelectedFile>>,
origin: FileOrigin,
opt_test_path: Option<String>,
embedder_proxy: EmbedderProxy,
) {
// Check if the select_files preference is enabled
// to ensure process-level security against compromised script;
// Then try applying opt_test_path directly for testing convenience
let opt_s = if select_files_pref_enabled() {
opt_test_path
} else {
self.query_files_from_embedder(patterns, false, embedder_proxy).and_then(|mut x| x.pop())
self.query_files_from_embedder(patterns, false, embedder_proxy)
.and_then(|mut x| x.pop())
};
match opt_s {
@ -252,20 +286,22 @@ impl FileManagerStore {
let selected_path = Path::new(&s);
let result = self.create_entry(selected_path, &origin);
let _ = sender.send(result);
}
},
None => {
let _ = sender.send(Err(FileManagerThreadError::UserCancelled));
return;
}
},
}
}
fn select_files(&self,
patterns: Vec<FilterPattern>,
sender: IpcSender<FileManagerResult<Vec<SelectedFile>>>,
origin: FileOrigin,
opt_test_paths: Option<Vec<String>>,
embedder_proxy: EmbedderProxy) {
fn select_files(
&self,
patterns: Vec<FilterPattern>,
sender: IpcSender<FileManagerResult<Vec<SelectedFile>>>,
origin: FileOrigin,
opt_test_paths: Option<Vec<String>>,
embedder_proxy: EmbedderProxy,
) {
// Check if the select_files preference is enabled
// to ensure process-level security against compromised script;
// Then try applying opt_test_paths directly for testing convenience
@ -291,30 +327,42 @@ impl FileManagerStore {
Err(e) => {
let _ = sender.send(Err(e));
return;
}
},
};
}
let _ = sender.send(Ok(replies));
}
},
None => {
let _ = sender.send(Err(FileManagerThreadError::UserCancelled));
return;
}
},
}
}
fn create_entry(&self, file_path: &Path, origin: &str) -> Result<SelectedFile, FileManagerThreadError> {
fn create_entry(
&self,
file_path: &Path,
origin: &str,
) -> Result<SelectedFile, FileManagerThreadError> {
use net_traits::filemanager_thread::FileManagerThreadError::FileSystemError;
let file = File::open(file_path).map_err(|e| FileSystemError(e.to_string()))?;
let metadata = file.metadata().map_err(|e| FileSystemError(e.to_string()))?;
let modified = metadata.modified().map_err(|e| FileSystemError(e.to_string()))?;
let elapsed = modified.elapsed().map_err(|e| FileSystemError(e.to_string()))?;
let metadata = file
.metadata()
.map_err(|e| FileSystemError(e.to_string()))?;
let modified = metadata
.modified()
.map_err(|e| FileSystemError(e.to_string()))?;
let elapsed = modified
.elapsed()
.map_err(|e| FileSystemError(e.to_string()))?;
// Unix Epoch: https://doc.servo.org/std/time/constant.UNIX_EPOCH.html
let modified_epoch = elapsed.as_secs() * 1000 + elapsed.subsec_nanos() as u64 / 1000000;
let file_size = metadata.len();
let file_name = file_path.file_name().ok_or(FileSystemError("Invalid filepath".to_string()))?;
let file_name = file_path
.file_name()
.ok_or(FileSystemError("Invalid filepath".to_string()))?;
let file_impl = FileImpl::MetaDataOnly(FileMetaData {
path: file_path.to_path_buf(),
@ -324,18 +372,21 @@ impl FileManagerStore {
let id = Uuid::new_v4();
self.insert(id, FileStoreEntry {
origin: origin.to_string(),
file_impl: file_impl,
refs: AtomicUsize::new(1),
// Invalid here since create_entry is called by file selection
is_valid_url: AtomicBool::new(false),
});
self.insert(
id,
FileStoreEntry {
origin: origin.to_string(),
file_impl: file_impl,
refs: AtomicUsize::new(1),
// Invalid here since create_entry is called by file selection
is_valid_url: AtomicBool::new(false),
},
);
let filename_path = Path::new(file_name);
let type_string = match guess_mime_type_opt(filename_path) {
Some(x) => format!("{}", x),
None => "".to_string(),
None => "".to_string(),
};
Ok(SelectedFile {
@ -347,9 +398,14 @@ impl FileManagerStore {
})
}
fn get_blob_buf(&self, sender: &IpcSender<FileManagerResult<ReadFileProgress>>,
id: &Uuid, origin_in: &FileOrigin, rel_pos: RelativePos,
check_url_validity: bool) -> Result<(), BlobURLStoreError> {
fn get_blob_buf(
&self,
sender: &IpcSender<FileManagerResult<ReadFileProgress>>,
id: &Uuid,
origin_in: &FileOrigin,
rel_pos: RelativePos,
check_url_validity: bool,
) -> Result<(), BlobURLStoreError> {
let file_impl = self.get_impl(id, origin_in, check_url_validity)?;
match file_impl {
FileImpl::Memory(buf) => {
@ -365,7 +421,7 @@ impl FileManagerStore {
let _ = sender.send(Ok(ReadFileProgress::EOF));
Ok(())
}
},
FileImpl::MetaDataOnly(metadata) => {
/* XXX: Snapshot state check (optional) https://w3c.github.io/FileAPI/#snapshot-state.
Concretely, here we create another file, and this file might not
@ -373,45 +429,62 @@ impl FileManagerStore {
create_entry is called.
*/
let opt_filename = metadata.path.file_name()
.and_then(|osstr| osstr.to_str())
.map(|s| s.to_string());
let opt_filename = metadata
.path
.file_name()
.and_then(|osstr| osstr.to_str())
.map(|s| s.to_string());
let mime = guess_mime_type_opt(metadata.path.clone());
let range = rel_pos.to_abs_range(metadata.size as usize);
let mut file = File::open(&metadata.path)
.map_err(|e| BlobURLStoreError::External(e.to_string()))?;
let seeked_start = file.seek(SeekFrom::Start(range.start as u64))
.map_err(|e| BlobURLStoreError::External(e.to_string()))?;
.map_err(|e| BlobURLStoreError::External(e.to_string()))?;
let seeked_start = file
.seek(SeekFrom::Start(range.start as u64))
.map_err(|e| BlobURLStoreError::External(e.to_string()))?;
if seeked_start == (range.start as u64) {
let type_string = match mime {
Some(x) => format!("{}", x),
None => "".to_string(),
None => "".to_string(),
};
chunked_read(sender, &mut file, range.len(), opt_filename,
type_string);
chunked_read(sender, &mut file, range.len(), opt_filename, type_string);
Ok(())
} else {
Err(BlobURLStoreError::InvalidEntry)
}
}
},
FileImpl::Sliced(parent_id, inner_rel_pos) => {
// Next time we don't need to check validity since
// we have already done that for requesting URL if necessary
self.get_blob_buf(sender, &parent_id, origin_in,
rel_pos.slice_inner(&inner_rel_pos), false)
}
self.get_blob_buf(
sender,
&parent_id,
origin_in,
rel_pos.slice_inner(&inner_rel_pos),
false,
)
},
}
}
// Convenient wrapper over get_blob_buf
fn try_read_file(&self, sender: &IpcSender<FileManagerResult<ReadFileProgress>>,
id: Uuid, check_url_validity: bool, origin_in: FileOrigin)
-> Result<(), BlobURLStoreError> {
self.get_blob_buf(sender, &id, &origin_in, RelativePos::full_range(), check_url_validity)
fn try_read_file(
&self,
sender: &IpcSender<FileManagerResult<ReadFileProgress>>,
id: Uuid,
check_url_validity: bool,
origin_in: FileOrigin,
) -> Result<(), BlobURLStoreError> {
self.get_blob_buf(
sender,
&id,
&origin_in,
RelativePos::full_range(),
check_url_validity,
)
}
fn dec_ref(&self, id: &Uuid, origin_in: &FileOrigin) -> Result<(), BlobURLStoreError> {
@ -436,7 +509,7 @@ impl FileManagerStore {
} else {
return Err(BlobURLStoreError::InvalidOrigin);
}
}
},
None => return Err(BlobURLStoreError::InvalidFileID),
};
@ -454,28 +527,41 @@ impl FileManagerStore {
Ok(())
}
fn promote_memory(&self, blob_buf: BlobBuf, set_valid: bool,
sender: IpcSender<Result<Uuid, BlobURLStoreError>>, origin: FileOrigin) {
match Url::parse(&origin) { // parse to check sanity
fn promote_memory(
&self,
blob_buf: BlobBuf,
set_valid: bool,
sender: IpcSender<Result<Uuid, BlobURLStoreError>>,
origin: FileOrigin,
) {
match Url::parse(&origin) {
// parse to check sanity
Ok(_) => {
let id = Uuid::new_v4();
self.insert(id, FileStoreEntry {
origin: origin.clone(),
file_impl: FileImpl::Memory(blob_buf),
refs: AtomicUsize::new(1),
is_valid_url: AtomicBool::new(set_valid),
});
self.insert(
id,
FileStoreEntry {
origin: origin.clone(),
file_impl: FileImpl::Memory(blob_buf),
refs: AtomicUsize::new(1),
is_valid_url: AtomicBool::new(set_valid),
},
);
let _ = sender.send(Ok(id));
}
},
Err(_) => {
let _ = sender.send(Err(BlobURLStoreError::InvalidOrigin));
}
},
}
}
fn set_blob_url_validity(&self, validity: bool, id: &Uuid,
origin_in: &FileOrigin) -> Result<(), BlobURLStoreError> {
fn set_blob_url_validity(
&self,
validity: bool,
id: &Uuid,
origin_in: &FileOrigin,
) -> Result<(), BlobURLStoreError> {
let (do_remove, opt_parent_id, res) = match self.entries.read().unwrap().get(id) {
Some(entry) => {
if *entry.origin == *origin_in {
@ -485,7 +571,7 @@ impl FileManagerStore {
// Check if it is the last possible reference
// since refs only accounts for blob id holders
// and store entry id holders
let zero_refs = entry.refs.load(Ordering::Acquire) == 0;
let zero_refs = entry.refs.load(Ordering::Acquire) == 0;
if let FileImpl::Sliced(ref parent_id, _) = entry.file_impl {
(zero_refs, Some(parent_id.clone()), Ok(()))
@ -498,8 +584,8 @@ impl FileManagerStore {
} else {
(false, None, Err(BlobURLStoreError::InvalidOrigin))
}
}
None => (false, None, Err(BlobURLStoreError::InvalidFileID))
},
None => (false, None, Err(BlobURLStoreError::InvalidFileID)),
};
if do_remove {
@ -515,15 +601,21 @@ impl FileManagerStore {
}
fn select_files_pref_enabled() -> bool {
PREFS.get("dom.testing.htmlinputelement.select_files.enabled")
.as_boolean().unwrap_or(false)
PREFS
.get("dom.testing.htmlinputelement.select_files.enabled")
.as_boolean()
.unwrap_or(false)
}
const CHUNK_SIZE: usize = 8192;
fn chunked_read(sender: &IpcSender<FileManagerResult<ReadFileProgress>>,
file: &mut File, size: usize, opt_filename: Option<String>,
type_string: String) {
fn chunked_read(
sender: &IpcSender<FileManagerResult<ReadFileProgress>>,
file: &mut File,
size: usize,
opt_filename: Option<String>,
type_string: String,
) {
// First chunk
let mut buf = vec![0; CHUNK_SIZE];
match file.read(&mut buf) {
@ -536,11 +628,11 @@ fn chunked_read(sender: &IpcSender<FileManagerResult<ReadFileProgress>>,
bytes: buf,
};
let _ = sender.send(Ok(ReadFileProgress::Meta(blob_buf)));
}
},
Err(e) => {
let _ = sender.send(Err(FileManagerThreadError::FileSystemError(e.to_string())));
return;
}
},
}
// Send the remaining chunks
@ -550,15 +642,15 @@ fn chunked_read(sender: &IpcSender<FileManagerResult<ReadFileProgress>>,
Ok(0) => {
let _ = sender.send(Ok(ReadFileProgress::EOF));
return;
}
},
Ok(n) => {
buf.truncate(n);
let _ = sender.send(Ok(ReadFileProgress::Partial(buf)));
}
},
Err(e) => {
let _ = sender.send(Err(FileManagerThreadError::FileSystemError(e.to_string())));
return;
}
},
}
}
}

View file

@ -31,21 +31,32 @@ pub fn replace_host_table(table: HashMap<String, IpAddr>) {
}
pub fn parse_hostsfile(hostsfile_content: &str) -> HashMap<String, IpAddr> {
hostsfile_content.lines().filter_map(|line| {
let mut iter = line.split('#').next().unwrap().split_whitespace();
Some((iter.next()?.parse().ok()?, iter))
}).flat_map(|(ip, hosts)| {
hosts.filter(|host| {
let invalid = ['\0', '\t', '\n', '\r', ' ', '#', '%', '/', ':', '?', '@', '[', '\\', ']'];
host.parse::<Ipv4Addr>().is_err() && !host.contains(&invalid[..])
}).map(move |host| {
(host.to_owned(), ip)
hostsfile_content
.lines()
.filter_map(|line| {
let mut iter = line.split('#').next().unwrap().split_whitespace();
Some((iter.next()?.parse().ok()?, iter))
})
}).collect()
.flat_map(|(ip, hosts)| {
hosts
.filter(|host| {
let invalid = [
'\0', '\t', '\n', '\r', ' ', '#', '%', '/', ':', '?', '@', '[', '\\', ']',
];
host.parse::<Ipv4Addr>().is_err() && !host.contains(&invalid[..])
})
.map(move |host| (host.to_owned(), ip))
})
.collect()
}
pub fn replace_host(host: &str) -> Cow<str> {
HOST_TABLE.lock().unwrap().as_ref()
HOST_TABLE
.lock()
.unwrap()
.as_ref()
.and_then(|table| table.get(host))
.map_or(host.into(), |replaced_host| replaced_host.to_string().into())
.map_or(host.into(), |replaced_host| {
replaced_host.to_string().into()
})
}

View file

@ -16,11 +16,15 @@ pub struct HstsEntry {
pub host: String,
pub include_subdomains: bool,
pub max_age: Option<u64>,
pub timestamp: Option<u64>
pub timestamp: Option<u64>,
}
impl HstsEntry {
pub fn new(host: String, subdomains: IncludeSubdomains, max_age: Option<u64>) -> Option<HstsEntry> {
pub fn new(
host: String,
subdomains: IncludeSubdomains,
max_age: Option<u64>,
) -> Option<HstsEntry> {
if host.parse::<Ipv4Addr>().is_ok() || host.parse::<Ipv6Addr>().is_ok() {
None
} else {
@ -28,7 +32,7 @@ impl HstsEntry {
host: host,
include_subdomains: (subdomains == IncludeSubdomains::Included),
max_age: max_age,
timestamp: Some(time::get_time().sec as u64)
timestamp: Some(time::get_time().sec as u64),
})
}
}
@ -37,9 +41,9 @@ impl HstsEntry {
match (self.max_age, self.timestamp) {
(Some(max_age), Some(timestamp)) => {
(time::get_time().sec as u64) - timestamp >= max_age
}
},
_ => false
_ => false,
}
}
@ -59,7 +63,9 @@ pub struct HstsList {
impl HstsList {
pub fn new() -> HstsList {
HstsList { entries_map: HashMap::new() }
HstsList {
entries_map: HashMap::new(),
}
}
/// Create an `HstsList` from the bytes of a JSON preload file.
@ -107,9 +113,9 @@ impl HstsList {
}
fn has_subdomain(&self, host: &str, base_domain: &str) -> bool {
self.entries_map.get(base_domain).map_or(false, |entries| {
entries.iter().any(|e| e.matches_subdomain(host))
})
self.entries_map.get(base_domain).map_or(false, |entries| {
entries.iter().any(|e| e.matches_subdomain(host))
})
}
pub fn push(&mut self, entry: HstsEntry) {
@ -118,7 +124,10 @@ impl HstsList {
let have_domain = self.has_domain(&entry.host, base_domain);
let have_subdomain = self.has_subdomain(&entry.host, base_domain);
let entries = self.entries_map.entry(base_domain.to_owned()).or_insert(vec![]);
let entries = self
.entries_map
.entry(base_domain.to_owned())
.or_insert(vec![]);
if !have_domain && !have_subdomain {
entries.push(entry);
} else if !have_subdomain {
@ -136,7 +145,10 @@ impl HstsList {
if url.scheme() != "http" {
return;
}
if url.domain().map_or(false, |domain| self.is_host_secure(domain)) {
if url
.domain()
.map_or(false, |domain| self.is_host_secure(domain))
{
url.as_mut_url().set_scheme("https").unwrap();
}
}

View file

@ -30,23 +30,22 @@ use std::time::SystemTime;
use time;
use time::{Duration, Timespec, Tm};
/// The key used to differentiate requests in the cache.
#[derive(Clone, Eq, Hash, MallocSizeOf, PartialEq )]
#[derive(Clone, Eq, Hash, MallocSizeOf, PartialEq)]
pub struct CacheKey {
url: ServoUrl
url: ServoUrl,
}
impl CacheKey {
fn new(request: Request) -> CacheKey {
CacheKey {
url: request.current_url().clone()
url: request.current_url().clone(),
}
}
fn from_servo_url(servo_url: &ServoUrl) -> CacheKey {
CacheKey {
url: servo_url.clone()
url: servo_url.clone(),
}
}
@ -63,7 +62,7 @@ struct CachedResource {
body: Arc<Mutex<ResponseBody>>,
aborted: Arc<AtomicBool>,
awaiting_body: Arc<Mutex<Vec<Sender<Data>>>>,
data: Measurable<MeasurableCachedResource>
data: Measurable<MeasurableCachedResource>,
}
#[derive(Clone, MallocSizeOf)]
@ -82,9 +81,9 @@ impl MallocSizeOf for CachedResource {
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
// TODO: self.request_headers.unconditional_size_of(ops) +
self.body.unconditional_size_of(ops) +
self.aborted.unconditional_size_of(ops) +
self.awaiting_body.unconditional_size_of(ops) +
self.data.size_of(ops)
self.aborted.unconditional_size_of(ops) +
self.awaiting_body.unconditional_size_of(ops) +
self.data.size_of(ops)
}
}
@ -94,7 +93,7 @@ struct CachedMetadata {
/// Headers
pub headers: Arc<Mutex<HeaderMap>>,
/// Fields that implement MallocSizeOf
pub data: Measurable<MeasurableCachedMetadata>
pub data: Measurable<MeasurableCachedMetadata>,
}
#[derive(Clone, MallocSizeOf)]
@ -106,7 +105,7 @@ struct MeasurableCachedMetadata {
/// Character set.
pub charset: Option<String>,
/// HTTP Status
pub status: Option<(u16, Vec<u8>)>
pub status: Option<(u16, Vec<u8>)>,
}
impl MallocSizeOf for CachedMetadata {
@ -122,7 +121,7 @@ pub struct CachedResponse {
/// The response constructed from the cached resource
pub response: Response,
/// The revalidation flag for the stored response
pub needs_validation: bool
pub needs_validation: bool,
}
/// A memory cache.
@ -132,7 +131,6 @@ pub struct HttpCache {
entries: HashMap<CacheKey, Vec<CachedResource>>,
}
/// Determine if a given response is cacheable based on the initial metadata received.
/// Based on <https://tools.ietf.org/html/rfc7234#section-3>
fn response_is_cacheable(metadata: &Metadata) -> bool {
@ -143,15 +141,18 @@ fn response_is_cacheable(metadata: &Metadata) -> bool {
let headers = metadata.headers.as_ref().unwrap();
if headers.contains_key(header::EXPIRES) ||
headers.contains_key(header::LAST_MODIFIED) ||
headers.contains_key(header::ETAG) {
headers.contains_key(header::ETAG)
{
is_cacheable = true;
}
if let Some(ref directive) = headers.typed_get::<CacheControl>() {
if directive.no_store() {
return false
return false;
}
if directive.public() || directive.s_max_age().is_some() ||
directive.max_age().is_some() || directive.no_cache()
if directive.public() ||
directive.s_max_age().is_some() ||
directive.max_age().is_some() ||
directive.no_cache()
{
is_cacheable = true;
}
@ -245,12 +246,11 @@ fn get_response_expiry(response: &Response) -> Duration {
match *code {
200 | 203 | 204 | 206 | 300 | 301 | 404 | 405 | 410 | 414 | 501 => {
// Status codes that are cacheable by default <https://tools.ietf.org/html/rfc7231#section-6.1>
return heuristic_freshness
return heuristic_freshness;
},
_ => {
// Other status codes can only use heuristic freshness if the public cache directive is present.
if let Some(ref directives) = response.headers.typed_get::<CacheControl>()
{
if let Some(ref directives) = response.headers.typed_get::<CacheControl>() {
if directives.public() {
return heuristic_freshness;
}
@ -288,25 +288,30 @@ fn get_expiry_adjustment_from_request_headers(request: &Request, expires: Durati
return expires - min_fresh;
}
if directive.no_cache() || directive.no_store() {
return Duration::min_value()
return Duration::min_value();
}
expires
}
/// Create a CachedResponse from a request and a CachedResource.
fn create_cached_response(request: &Request,
fn create_cached_response(
request: &Request,
cached_resource: &CachedResource,
cached_headers: &HeaderMap,
done_chan: &mut DoneChannel)
-> CachedResponse {
done_chan: &mut DoneChannel,
) -> CachedResponse {
let mut response = Response::new(cached_resource.data.metadata.data.final_url.clone());
response.headers = cached_headers.clone();
response.body = cached_resource.body.clone();
if let ResponseBody::Receiving(_) = *cached_resource.body.lock().unwrap() {
let (done_sender, done_receiver) = channel();
*done_chan = Some((done_sender.clone(), done_receiver));
cached_resource.awaiting_body.lock().unwrap().push(done_sender);
cached_resource
.awaiting_body
.lock()
.unwrap()
.push(done_sender);
}
response.location_url = cached_resource.data.location_url.clone();
response.status = cached_resource.data.status.clone();
@ -324,15 +329,20 @@ fn create_cached_response(request: &Request,
// TODO: take must-revalidate into account <https://tools.ietf.org/html/rfc7234#section-5.2.2.1>
// TODO: if this cache is to be considered shared, take proxy-revalidate into account
// <https://tools.ietf.org/html/rfc7234#section-5.2.2.7>
let has_expired = (adjusted_expires < time_since_validated) ||
(adjusted_expires == time_since_validated);
CachedResponse { response: response, needs_validation: has_expired }
let has_expired =
(adjusted_expires < time_since_validated) || (adjusted_expires == time_since_validated);
CachedResponse {
response: response,
needs_validation: has_expired,
}
}
/// Create a new resource, based on the bytes requested, and an existing resource,
/// with a status-code of 206.
fn create_resource_with_bytes_from_resource(bytes: &[u8], resource: &CachedResource)
-> CachedResource {
fn create_resource_with_bytes_from_resource(
bytes: &[u8],
resource: &CachedResource,
) -> CachedResource {
CachedResource {
request_headers: resource.request_headers.clone(),
body: Arc::new(Mutex::new(ResponseBody::Done(bytes.to_owned()))),
@ -347,29 +357,35 @@ fn create_resource_with_bytes_from_resource(bytes: &[u8], resource: &CachedResou
url_list: resource.data.url_list.clone(),
expires: resource.data.expires.clone(),
last_validated: resource.data.last_validated.clone(),
})
}),
}
}
/// Support for range requests <https://tools.ietf.org/html/rfc7233>.
fn handle_range_request(request: &Request,
fn handle_range_request(
request: &Request,
candidates: Vec<&CachedResource>,
range_spec: Vec<(Bound<u64>, Bound<u64>)>,
done_chan: &mut DoneChannel)
-> Option<CachedResponse> {
let mut complete_cached_resources = candidates.iter().filter(|resource| {
match resource.data.raw_status {
Some((ref code, _)) => *code == 200,
None => false
}
});
let partial_cached_resources = candidates.iter().filter(|resource| {
match resource.data.raw_status {
Some((ref code, _)) => *code == 206,
None => false
}
});
match (range_spec.first().unwrap(), complete_cached_resources.next()) {
done_chan: &mut DoneChannel,
) -> Option<CachedResponse> {
let mut complete_cached_resources =
candidates
.iter()
.filter(|resource| match resource.data.raw_status {
Some((ref code, _)) => *code == 200,
None => false,
});
let partial_cached_resources =
candidates
.iter()
.filter(|resource| match resource.data.raw_status {
Some((ref code, _)) => *code == 206,
None => false,
});
match (
range_spec.first().unwrap(),
complete_cached_resources.next(),
) {
// TODO: take the full range spec into account.
// If we have a complete resource, take the request range from the body.
// When there isn't a complete resource available, we loop over cached partials,
@ -384,9 +400,11 @@ fn handle_range_request(request: &Request,
let e = end as usize + 1;
let requested = body.get(b..e);
if let Some(bytes) = requested {
let new_resource = create_resource_with_bytes_from_resource(bytes, complete_resource);
let new_resource =
create_resource_with_bytes_from_resource(bytes, complete_resource);
let cached_headers = new_resource.data.metadata.headers.lock().unwrap();
let cached_response = create_cached_response(request, &new_resource, &*cached_headers, done_chan);
let cached_response =
create_cached_response(request, &new_resource, &*cached_headers, done_chan);
return Some(cached_response);
}
}
@ -400,9 +418,9 @@ fn handle_range_request(request: &Request,
if let Some(bytes_range) = range.bytes_range() {
bytes_range
} else {
continue
continue;
}
}
},
_ => continue,
};
if res_beginning - 1 < beginning && res_end + 1 > end {
@ -416,8 +434,10 @@ fn handle_range_request(request: &Request,
_ => continue,
};
if let Some(bytes) = requested {
let new_resource = create_resource_with_bytes_from_resource(&bytes, partial_resource);
let cached_response = create_cached_response(request, &new_resource, &*headers, done_chan);
let new_resource =
create_resource_with_bytes_from_resource(&bytes, partial_resource);
let cached_response =
create_cached_response(request, &new_resource, &*headers, done_chan);
return Some(cached_response);
}
}
@ -428,9 +448,11 @@ fn handle_range_request(request: &Request,
let b = beginning as usize;
let requested = body.get(b..);
if let Some(bytes) = requested {
let new_resource = create_resource_with_bytes_from_resource(bytes, complete_resource);
let new_resource =
create_resource_with_bytes_from_resource(bytes, complete_resource);
let cached_headers = new_resource.data.metadata.headers.lock().unwrap();
let cached_response = create_cached_response(request, &new_resource, &*cached_headers, done_chan);
let cached_response =
create_cached_response(request, &new_resource, &*cached_headers, done_chan);
return Some(cached_response);
}
}
@ -457,8 +479,10 @@ fn handle_range_request(request: &Request,
_ => continue,
};
if let Some(bytes) = requested {
let new_resource = create_resource_with_bytes_from_resource(&bytes, partial_resource);
let cached_response = create_cached_response(request, &new_resource, &*headers, done_chan);
let new_resource =
create_resource_with_bytes_from_resource(&bytes, partial_resource);
let cached_response =
create_cached_response(request, &new_resource, &*headers, done_chan);
return Some(cached_response);
}
}
@ -469,9 +493,11 @@ fn handle_range_request(request: &Request,
let from_byte = body.len() - offset as usize;
let requested = body.get(from_byte..);
if let Some(bytes) = requested {
let new_resource = create_resource_with_bytes_from_resource(bytes, complete_resource);
let new_resource =
create_resource_with_bytes_from_resource(bytes, complete_resource);
let cached_headers = new_resource.data.metadata.headers.lock().unwrap();
let cached_response = create_cached_response(request, &new_resource, &*cached_headers, done_chan);
let cached_response =
create_cached_response(request, &new_resource, &*cached_headers, done_chan);
return Some(cached_response);
}
}
@ -488,7 +514,7 @@ fn handle_range_request(request: &Request,
} else {
continue;
};
if (total - res_beginning) > (offset - 1 ) && (total - res_end) < offset + 1 {
if (total - res_beginning) > (offset - 1) && (total - res_end) < offset + 1 {
let resource_body = &*partial_resource.body.lock().unwrap();
let requested = match resource_body {
&ResponseBody::Done(ref body) => {
@ -498,38 +524,47 @@ fn handle_range_request(request: &Request,
_ => continue,
};
if let Some(bytes) = requested {
let new_resource = create_resource_with_bytes_from_resource(&bytes, partial_resource);
let cached_response = create_cached_response(request, &new_resource, &*headers, done_chan);
let new_resource =
create_resource_with_bytes_from_resource(&bytes, partial_resource);
let cached_response =
create_cached_response(request, &new_resource, &*headers, done_chan);
return Some(cached_response);
}
}
}
},
// All the cases with Bound::Excluded should be unreachable anyway
_ => return None
_ => return None,
}
None
}
impl HttpCache {
/// Create a new memory cache instance.
pub fn new() -> HttpCache {
HttpCache {
entries: HashMap::new()
entries: HashMap::new(),
}
}
/// Constructing Responses from Caches.
/// <https://tools.ietf.org/html/rfc7234#section-4>
pub fn construct_response(&self, request: &Request, done_chan: &mut DoneChannel) -> Option<CachedResponse> {
pub fn construct_response(
&self,
request: &Request,
done_chan: &mut DoneChannel,
) -> Option<CachedResponse> {
// TODO: generate warning headers as appropriate <https://tools.ietf.org/html/rfc7234#section-5.5>
if request.method != Method::GET {
// Only Get requests are cached, avoid a url based match for others.
return None;
}
let entry_key = CacheKey::new(request.clone());
let resources = self.entries.get(&entry_key)?.into_iter().filter(|r| { !r.aborted.load(Ordering::Relaxed) });
let resources = self
.entries
.get(&entry_key)?
.into_iter()
.filter(|r| !r.aborted.load(Ordering::Relaxed));
let mut candidates = vec![];
for cached_resource in resources {
let mut can_be_constructed = true;
@ -545,7 +580,9 @@ impl HttpCache {
match request.headers.get(vary_val) {
Some(header_data) => {
// If the header is present in the request.
if let Some(original_header_data) = original_request_headers.get(vary_val) {
if let Some(original_header_data) =
original_request_headers.get(vary_val)
{
// Check that the value of the nominated header field,
// in the original request, matches the value in the current request.
if original_header_data != header_data {
@ -558,7 +595,8 @@ impl HttpCache {
// If a header field is absent from a request,
// it can only match a stored response if those headers,
// were also absent in the original request.
can_be_constructed = original_request_headers.get(vary_val).is_none();
can_be_constructed =
original_request_headers.get(vary_val).is_none();
},
}
if !can_be_constructed {
@ -573,7 +611,12 @@ impl HttpCache {
}
// Support for range requests
if let Some(range_spec) = request.headers.typed_get::<Range>() {
return handle_range_request(request, candidates, range_spec.iter().collect(), done_chan);
return handle_range_request(
request,
candidates,
range_spec.iter().collect(),
done_chan,
);
} else {
// Not a Range request.
if let Some(ref cached_resource) = candidates.first() {
@ -581,7 +624,8 @@ impl HttpCache {
// TODO: select the most appropriate one, using a known mechanism from a selecting header field,
// or using the Date header to return the most recent one.
let cached_headers = cached_resource.data.metadata.headers.lock().unwrap();
let cached_response = create_cached_response(request, cached_resource, &*cached_headers, done_chan);
let cached_response =
create_cached_response(request, cached_resource, &*cached_headers, done_chan);
return Some(cached_response);
}
}
@ -602,7 +646,7 @@ impl HttpCache {
let _ = done_sender.send(Data::Payload(completed_body.clone()));
let _ = done_sender.send(Data::Done);
}
};
}
}
}
}
@ -610,7 +654,12 @@ impl HttpCache {
/// Freshening Stored Responses upon Validation.
/// <https://tools.ietf.org/html/rfc7234#section-4.3.4>
pub fn refresh(&mut self, request: &Request, response: Response, done_chan: &mut DoneChannel) -> Option<Response> {
pub fn refresh(
&mut self,
request: &Request,
response: Response,
done_chan: &mut DoneChannel,
) -> Option<Response> {
assert_eq!(response.status.map(|s| s.0), Some(StatusCode::NOT_MODIFIED));
let entry_key = CacheKey::new(request.clone());
if let Some(cached_resources) = self.entries.get_mut(&entry_key) {
@ -620,22 +669,25 @@ impl HttpCache {
// Otherwise, create a new dedicated channel to update the consumer.
// The response constructed here will replace the 304 one from the network.
let in_progress_channel = match *cached_resource.body.lock().unwrap() {
ResponseBody::Receiving(..) => {
Some(channel())
},
ResponseBody::Empty | ResponseBody::Done(..) => None
ResponseBody::Receiving(..) => Some(channel()),
ResponseBody::Empty | ResponseBody::Done(..) => None,
};
match in_progress_channel {
Some((done_sender, done_receiver)) => {
*done_chan = Some((done_sender.clone(), done_receiver));
cached_resource.awaiting_body.lock().unwrap().push(done_sender);
cached_resource
.awaiting_body
.lock()
.unwrap()
.push(done_sender);
},
None => *done_chan = None
None => *done_chan = None,
}
// Received a response with 304 status code, in response to a request that matches a cached resource.
// 1. update the headers of the cached resource.
// 2. return a response, constructed from the cached resource.
let mut constructed_response = Response::new(cached_resource.data.metadata.data.final_url.clone());
let mut constructed_response =
Response::new(cached_resource.data.metadata.data.final_url.clone());
constructed_response.body = cached_resource.body.clone();
constructed_response.status = cached_resource.data.status.clone();
constructed_response.https_state = cached_resource.data.https_state.clone();
@ -666,12 +718,19 @@ impl HttpCache {
/// <https://tools.ietf.org/html/rfc7234#section-4.4>
pub fn invalidate(&mut self, request: &Request, response: &Response) {
// TODO(eijebong): Once headers support typed_get, update this to use them
if let Some(Ok(location)) = response.headers.get(header::LOCATION).map(HeaderValue::to_str) {
if let Some(Ok(location)) = response
.headers
.get(header::LOCATION)
.map(HeaderValue::to_str)
{
if let Ok(url) = request.current_url().join(location) {
self.invalidate_for_url(&url);
}
}
if let Some(Ok(ref content_location)) = response.headers.get(header::CONTENT_LOCATION).map(HeaderValue::to_str)
if let Some(Ok(ref content_location)) = response
.headers
.get(header::CONTENT_LOCATION)
.map(HeaderValue::to_str)
{
if let Ok(url) = request.current_url().join(&content_location) {
self.invalidate_for_url(&url);
@ -683,18 +742,23 @@ impl HttpCache {
/// Storing Responses in Caches.
/// <https://tools.ietf.org/html/rfc7234#section-3>
pub fn store(&mut self, request: &Request, response: &Response) {
if PREFS.get("network.http-cache.disabled").as_boolean().unwrap_or(false) {
return
if PREFS
.get("network.http-cache.disabled")
.as_boolean()
.unwrap_or(false)
{
return;
}
if request.method != Method::GET {
// Only Get requests are cached.
return
return;
}
let entry_key = CacheKey::new(request.clone());
let metadata = match response.metadata() {
Ok(FetchMetadata::Filtered {
filtered: _,
unsafe_: metadata }) |
filtered: _,
unsafe_: metadata,
}) |
Ok(FetchMetadata::Unfiltered(metadata)) => metadata,
_ => return,
};
@ -708,8 +772,8 @@ impl HttpCache {
final_url: metadata.final_url,
content_type: metadata.content_type.map(|v| v.0.to_string()),
charset: metadata.charset,
status: metadata.status
})
status: metadata.status,
}),
};
let entry_resource = CachedResource {
request_headers: Arc::new(Mutex::new(request.headers.clone())),
@ -724,11 +788,10 @@ impl HttpCache {
raw_status: response.raw_status.clone(),
url_list: response.url_list.clone(),
expires: expiry,
last_validated: time::now()
})
last_validated: time::now(),
}),
};
let entry = self.entries.entry(entry_key).or_insert(vec![]);
entry.push(entry_resource);
}
}

File diff suppressed because it is too large Load diff

View file

@ -37,39 +37,39 @@ fn decode_bytes_sync(key: LoadKey, bytes: &[u8]) -> DecoderMsg {
let image = load_from_memory(bytes);
DecoderMsg {
key: key,
image: image
image: image,
}
}
fn get_placeholder_image(webrender_api: &webrender_api::RenderApi, data: &[u8]) -> io::Result<Arc<Image>> {
fn get_placeholder_image(
webrender_api: &webrender_api::RenderApi,
data: &[u8],
) -> io::Result<Arc<Image>> {
let mut image = load_from_memory(&data).unwrap();
set_webrender_image_key(webrender_api, &mut image);
Ok(Arc::new(image))
}
fn set_webrender_image_key(webrender_api: &webrender_api::RenderApi, image: &mut Image) {
if image.id.is_some() { return; }
if image.id.is_some() {
return;
}
let mut bytes = Vec::new();
let is_opaque = match image.format {
PixelFormat::BGRA8 => {
bytes.extend_from_slice(&*image.bytes);
pixels::premultiply_inplace(bytes.as_mut_slice())
}
},
PixelFormat::RGB8 => {
for bgr in image.bytes.chunks(3) {
bytes.extend_from_slice(&[
bgr[2],
bgr[1],
bgr[0],
0xff
]);
bytes.extend_from_slice(&[bgr[2], bgr[1], bgr[0], 0xff]);
}
true
}
},
PixelFormat::K8 | PixelFormat::KA8 => {
panic!("Not support by webrender yet");
}
},
};
let descriptor = webrender_api::ImageDescriptor {
size: webrender_api::DeviceUintSize::new(image.width, image.height),
@ -121,20 +121,22 @@ impl AllPendingLoads {
}
fn remove(&mut self, key: &LoadKey) -> Option<PendingLoad> {
self.loads.remove(key).
and_then(|pending_load| {
self.url_to_load_key.remove(&pending_load.url).unwrap();
Some(pending_load)
})
self.loads.remove(key).and_then(|pending_load| {
self.url_to_load_key.remove(&pending_load.url).unwrap();
Some(pending_load)
})
}
fn get_cached<'a>(&'a mut self, url: ServoUrl, can_request: CanRequestImages)
-> CacheResult<'a> {
fn get_cached<'a>(
&'a mut self,
url: ServoUrl,
can_request: CanRequestImages,
) -> CacheResult<'a> {
match self.url_to_load_key.entry(url.clone()) {
Occupied(url_entry) => {
let load_key = url_entry.get();
CacheResult::Hit(*load_key, self.loads.get_mut(load_key).unwrap())
}
},
Vacant(url_entry) => {
if can_request == CanRequestImages::No {
return CacheResult::Miss(None);
@ -149,9 +151,9 @@ impl AllPendingLoads {
Vacant(load_entry) => {
let mut_load = load_entry.insert(pending_load);
CacheResult::Miss(Some((load_key, mut_load)))
}
},
}
}
},
}
}
}
@ -226,14 +228,12 @@ impl ImageBytes {
type LoadKey = PendingImageId;
struct LoadKeyGenerator {
counter: u64
counter: u64,
}
impl LoadKeyGenerator {
fn new() -> LoadKeyGenerator {
LoadKeyGenerator {
counter: 0
}
LoadKeyGenerator { counter: 0 }
}
fn next(&mut self) -> PendingImageId {
self.counter += 1;
@ -244,7 +244,7 @@ impl LoadKeyGenerator {
enum LoadResult {
Loaded(Image),
PlaceholderLoaded(Arc<Image>),
None
None,
}
/// Represents an image that is either being loaded
@ -271,10 +271,10 @@ struct PendingLoad {
impl PendingLoad {
fn new(url: ServoUrl) -> PendingLoad {
PendingLoad {
bytes: ImageBytes::InProgress(vec!()),
bytes: ImageBytes::InProgress(vec![]),
metadata: None,
result: None,
listeners: vec!(),
listeners: vec![],
url: url,
final_url: None,
}
@ -314,20 +314,24 @@ impl ImageCacheStore {
};
match load_result {
LoadResult::Loaded(ref mut image) => set_webrender_image_key(&self.webrender_api, image),
LoadResult::PlaceholderLoaded(..) | LoadResult::None => {}
LoadResult::Loaded(ref mut image) => {
set_webrender_image_key(&self.webrender_api, image)
},
LoadResult::PlaceholderLoaded(..) | LoadResult::None => {},
}
let url = pending_load.final_url.clone();
let image_response = match load_result {
LoadResult::Loaded(image) => ImageResponse::Loaded(Arc::new(image), url.unwrap()),
LoadResult::PlaceholderLoaded(image) =>
ImageResponse::PlaceholderLoaded(image, self.placeholder_url.clone()),
LoadResult::PlaceholderLoaded(image) => {
ImageResponse::PlaceholderLoaded(image, self.placeholder_url.clone())
},
LoadResult::None => ImageResponse::None,
};
let completed_load = CompletedLoad::new(image_response.clone(), key);
self.completed_loads.insert(pending_load.url.into(), completed_load);
self.completed_loads
.insert(pending_load.url.into(), completed_load);
for listener in pending_load.listeners {
listener.respond(image_response.clone());
@ -336,21 +340,20 @@ impl ImageCacheStore {
/// Return a completed image if it exists, or None if there is no complete load
/// or the complete load is not fully decoded or is unavailable.
fn get_completed_image_if_available(&self,
url: &ServoUrl,
placeholder: UsePlaceholder)
-> Option<Result<ImageOrMetadataAvailable, ImageState>> {
fn get_completed_image_if_available(
&self,
url: &ServoUrl,
placeholder: UsePlaceholder,
) -> Option<Result<ImageOrMetadataAvailable, ImageState>> {
self.completed_loads.get(url).map(|completed_load| {
match (&completed_load.image_response, placeholder) {
(&ImageResponse::Loaded(ref image, ref url), _) |
(&ImageResponse::PlaceholderLoaded(ref image, ref url), UsePlaceholder::Yes) => {
Ok(ImageOrMetadataAvailable::ImageAvailable(image.clone(), url.clone()))
}
(&ImageResponse::PlaceholderLoaded(ref image, ref url), UsePlaceholder::Yes) => Ok(
ImageOrMetadataAvailable::ImageAvailable(image.clone(), url.clone()),
),
(&ImageResponse::PlaceholderLoaded(_, _), UsePlaceholder::No) |
(&ImageResponse::None, _) |
(&ImageResponse::MetadataLoaded(_), _) => {
Err(ImageState::LoadError)
}
(&ImageResponse::MetadataLoaded(_), _) => Err(ImageState::LoadError),
}
})
}
@ -383,18 +386,19 @@ impl ImageCache for ImageCacheImpl {
placeholder_image: get_placeholder_image(&webrender_api, &rippy_data).ok(),
placeholder_url: ServoUrl::parse("chrome://resources/rippy.png").unwrap(),
webrender_api: webrender_api,
}))
})),
}
}
/// Return any available metadata or image for the given URL,
/// or an indication that the image is not yet available if it is in progress,
/// or else reserve a slot in the cache for the URL if the consumer can request images.
fn find_image_or_metadata(&self,
url: ServoUrl,
use_placeholder: UsePlaceholder,
can_request: CanRequestImages)
-> Result<ImageOrMetadataAvailable, ImageState> {
fn find_image_or_metadata(
&self,
url: ServoUrl,
use_placeholder: UsePlaceholder,
can_request: CanRequestImages,
) -> Result<ImageOrMetadataAvailable, ImageState> {
debug!("Find image or metadata for {}", url);
let mut store = self.store.lock().unwrap();
if let Some(result) = store.get_completed_image_if_available(&url, use_placeholder) {
@ -409,24 +413,24 @@ impl ImageCache for ImageCacheImpl {
(&Some(Ok(_)), _) => {
debug!("Sync decoding {} ({:?})", url, key);
decode_bytes_sync(key, &pl.bytes.as_slice())
}
},
(&None, &Some(ref meta)) => {
debug!("Metadata available for {} ({:?})", url, key);
return Ok(ImageOrMetadataAvailable::MetadataAvailable(meta.clone()))
}
return Ok(ImageOrMetadataAvailable::MetadataAvailable(meta.clone()));
},
(&Some(Err(_)), _) | (&None, &None) => {
debug!("{} ({:?}) is still pending", url, key);
return Err(ImageState::Pending(key));
}
},
},
CacheResult::Miss(Some((key, _pl))) => {
debug!("Should be requesting {} ({:?})", url, key);
return Err(ImageState::NotRequested(key));
}
},
CacheResult::Miss(None) => {
debug!("Couldn't find an entry for {}", url);
return Err(ImageState::LoadError);
}
},
}
};
@ -468,17 +472,15 @@ impl ImageCache for ImageCacheImpl {
let mut store = self.store.lock().unwrap();
let pending_load = store.pending_loads.get_by_key_mut(&id).unwrap();
let metadata = match response {
Ok(meta) => {
Some(match meta {
FetchMetadata::Unfiltered(m) => m,
FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
})
},
Ok(meta) => Some(match meta {
FetchMetadata::Unfiltered(m) => m,
FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
}),
Err(_) => None,
};
let final_url = metadata.as_ref().map(|m| m.final_url.clone());
pending_load.final_url = final_url;
}
},
(FetchResponseMsg::ProcessResponseChunk(data), _) => {
debug!("Got some data for {:?}", id);
let mut store = self.store.lock().unwrap();
@ -488,16 +490,17 @@ impl ImageCache for ImageCacheImpl {
if let None = pending_load.metadata {
if let Ok(metadata) = load_from_buf(&pending_load.bytes.as_slice()) {
let dimensions = metadata.dimensions();
let img_metadata = ImageMetadata { width: dimensions.width,
height: dimensions.height };
let img_metadata = ImageMetadata {
width: dimensions.width,
height: dimensions.height,
};
for listener in &pending_load.listeners {
listener.respond(
ImageResponse::MetadataLoaded(img_metadata.clone()));
listener.respond(ImageResponse::MetadataLoaded(img_metadata.clone()));
}
pending_load.metadata = Some(img_metadata);
}
}
}
},
(FetchResponseMsg::ProcessResponseEOF(result), key) => {
debug!("Received EOF for {:?}", key);
match result {
@ -516,20 +519,20 @@ impl ImageCache for ImageCacheImpl {
debug!("Image decoded");
local_store.lock().unwrap().handle_decoder(msg);
});
}
},
Err(_) => {
debug!("Processing error for {:?}", key);
let mut store = self.store.lock().unwrap();
match store.placeholder_image.clone() {
Some(placeholder_image) => {
store.complete_load(
id, LoadResult::PlaceholderLoaded(placeholder_image))
}
Some(placeholder_image) => store.complete_load(
id,
LoadResult::PlaceholderLoaded(placeholder_image),
),
None => store.complete_load(id, LoadResult::None),
}
}
},
}
}
},
}
}

View file

@ -21,10 +21,14 @@ extern crate immeta;
extern crate ipc_channel;
#[macro_use]
extern crate lazy_static;
#[macro_use] extern crate log;
#[macro_use]
extern crate log;
extern crate malloc_size_of;
#[macro_use] extern crate malloc_size_of_derive;
#[macro_use] #[no_link] extern crate matches;
#[macro_use]
extern crate malloc_size_of_derive;
#[macro_use]
#[no_link]
extern crate matches;
extern crate mime;
extern crate mime_guess;
extern crate msg;
@ -33,7 +37,8 @@ extern crate openssl;
extern crate pixels;
#[macro_use]
extern crate profile_traits;
#[macro_use] extern crate serde;
#[macro_use]
extern crate serde;
extern crate serde_json;
extern crate servo_allocator;
extern crate servo_arc;

View file

@ -25,16 +25,17 @@ pub enum MediaType {
pub enum ApacheBugFlag {
On,
Off
Off,
}
impl ApacheBugFlag {
/// <https://mimesniff.spec.whatwg.org/#supplied-mime-type-detection-algorithm>
pub fn from_content_type(last_raw_content_type: &[u8]) -> ApacheBugFlag {
if last_raw_content_type == b"text/plain"
|| last_raw_content_type == b"text/plain; charset=ISO-8859-1"
|| last_raw_content_type == b"text/plain; charset=iso-8859-1"
|| last_raw_content_type == b"text/plain; charset=UTF-8" {
if last_raw_content_type == b"text/plain" ||
last_raw_content_type == b"text/plain; charset=ISO-8859-1" ||
last_raw_content_type == b"text/plain; charset=iso-8859-1" ||
last_raw_content_type == b"text/plain; charset=UTF-8"
{
ApacheBugFlag::On
} else {
ApacheBugFlag::Off
@ -45,19 +46,22 @@ impl ApacheBugFlag {
#[derive(PartialEq)]
pub enum NoSniffFlag {
On,
Off
Off,
}
impl MimeClassifier {
//Performs MIME Type Sniffing Algorithm (sections 7 and 8)
pub fn classify<'a>(&'a self,
context: LoadContext,
no_sniff_flag: NoSniffFlag,
apache_bug_flag: ApacheBugFlag,
supplied_type: &Option<Mime>,
data: &'a [u8]) -> Mime {
let supplied_type_or_octet_stream = supplied_type.clone().unwrap_or(mime::APPLICATION_OCTET_STREAM);
pub fn classify<'a>(
&'a self,
context: LoadContext,
no_sniff_flag: NoSniffFlag,
apache_bug_flag: ApacheBugFlag,
supplied_type: &Option<Mime>,
data: &'a [u8],
) -> Mime {
let supplied_type_or_octet_stream = supplied_type
.clone()
.unwrap_or(mime::APPLICATION_OCTET_STREAM);
match context {
LoadContext::Browsing => match *supplied_type {
None => self.sniff_unknown_type(no_sniff_flag, data),
@ -69,30 +73,41 @@ impl MimeClassifier {
NoSniffFlag::On => supplied_type.clone(),
NoSniffFlag::Off => match apache_bug_flag {
ApacheBugFlag::On => self.sniff_text_or_data(data),
ApacheBugFlag::Off => match MimeClassifier::get_media_type(supplied_type) {
Some(MediaType::Html) => self.feeds_classifier.classify(data),
Some(MediaType::Image) => self.image_classifier.classify(data),
Some(MediaType::AudioVideo) => self.audio_video_classifier.classify(data),
Some(MediaType::Xml) | None => None,
}.unwrap_or(supplied_type.clone())
}
ApacheBugFlag::Off => {
match MimeClassifier::get_media_type(supplied_type) {
Some(MediaType::Html) => {
self.feeds_classifier.classify(data)
},
Some(MediaType::Image) => {
self.image_classifier.classify(data)
},
Some(MediaType::AudioVideo) => {
self.audio_video_classifier.classify(data)
},
Some(MediaType::Xml) | None => None,
}
.unwrap_or(supplied_type.clone())
},
},
}
}
}
},
},
LoadContext::Image => {
// Section 8.2 Sniffing an image context
match MimeClassifier::maybe_get_media_type(supplied_type) {
Some(MediaType::Xml) => None,
_ => self.image_classifier.classify(data),
}.unwrap_or(supplied_type_or_octet_stream)
}
.unwrap_or(supplied_type_or_octet_stream)
},
LoadContext::AudioVideo => {
// Section 8.3 Sniffing an image context
match MimeClassifier::maybe_get_media_type(supplied_type) {
Some(MediaType::Xml) => None,
_ => self.audio_video_classifier.classify(data),
}.unwrap_or(supplied_type_or_octet_stream)
}
.unwrap_or(supplied_type_or_octet_stream)
},
LoadContext::Plugin => {
// 8.4 Sniffing in a plugin context
@ -129,7 +144,8 @@ impl MimeClassifier {
match MimeClassifier::maybe_get_media_type(supplied_type) {
Some(MediaType::Xml) => None,
_ => self.font_classifier.classify(data),
}.unwrap_or(supplied_type_or_octet_stream)
}
.unwrap_or(supplied_type_or_octet_stream)
},
LoadContext::TextTrack => {
// 8.8 Sniffing in a text track context
@ -149,16 +165,16 @@ impl MimeClassifier {
}
pub fn new() -> MimeClassifier {
MimeClassifier {
image_classifier: GroupedClassifier::image_classifer(),
audio_video_classifier: GroupedClassifier::audio_video_classifier(),
scriptable_classifier: GroupedClassifier::scriptable_classifier(),
plaintext_classifier: GroupedClassifier::plaintext_classifier(),
archive_classifier: GroupedClassifier::archive_classifier(),
binary_or_plaintext: BinaryOrPlaintextClassifier,
feeds_classifier: FeedsClassifier,
font_classifier: GroupedClassifier::font_classifier()
}
MimeClassifier {
image_classifier: GroupedClassifier::image_classifer(),
audio_video_classifier: GroupedClassifier::audio_video_classifier(),
scriptable_classifier: GroupedClassifier::scriptable_classifier(),
plaintext_classifier: GroupedClassifier::plaintext_classifier(),
archive_classifier: GroupedClassifier::archive_classifier(),
binary_or_plaintext: BinaryOrPlaintextClassifier,
feeds_classifier: FeedsClassifier,
font_classifier: GroupedClassifier::font_classifier(),
}
}
pub fn validate(&self) -> Result<(), String> {
@ -182,7 +198,8 @@ impl MimeClassifier {
None
};
sniffed.or_else(|| self.plaintext_classifier.classify(data))
sniffed
.or_else(|| self.plaintext_classifier.classify(data))
.or_else(|| self.image_classifier.classify(data))
.or_else(|| self.audio_video_classifier.classify(data))
.or_else(|| self.archive_classifier.classify(data))
@ -191,13 +208,15 @@ impl MimeClassifier {
}
fn sniff_text_or_data<'a>(&'a self, data: &'a [u8]) -> Mime {
self.binary_or_plaintext.classify(data).expect("BinaryOrPlaintextClassifier always succeeds")
self.binary_or_plaintext
.classify(data)
.expect("BinaryOrPlaintextClassifier always succeeds")
}
fn is_xml(mt: &Mime) -> bool {
mt.suffix() == Some(mime::XML) ||
(mt.type_() == mime::APPLICATION && mt.subtype() == mime::XML) ||
(mt.type_() == mime::TEXT && mt.subtype() == mime::XML)
(mt.type_() == mime::APPLICATION && mt.subtype() == mime::XML) ||
(mt.type_() == mime::TEXT && mt.subtype() == mime::XML)
}
fn is_html(mt: &Mime) -> bool {
@ -210,21 +229,21 @@ impl MimeClassifier {
fn is_audio_video(mt: &Mime) -> bool {
mt.type_() == mime::AUDIO ||
mt.type_() == mime::VIDEO ||
mt.type_() == mime::APPLICATION && mt.subtype() == mime::OGG
mt.type_() == mime::VIDEO ||
mt.type_() == mime::APPLICATION && mt.subtype() == mime::OGG
}
fn is_explicit_unknown(mt: &Mime) -> bool {
mt.type_().as_str() == "unknown" && mt.subtype().as_str() == "unknown" ||
mt.type_() == mime::APPLICATION && mt.subtype().as_str() == "unknown" ||
mt.type_() == mime::STAR && mt.subtype() == mime::STAR
mt.type_() == mime::APPLICATION && mt.subtype().as_str() == "unknown" ||
mt.type_() == mime::STAR && mt.subtype() == mime::STAR
}
fn get_media_type(mime: &Mime) -> Option<MediaType> {
if MimeClassifier::is_xml(&mime) {
Some(MediaType::Xml)
} else if MimeClassifier::is_html(&mime) {
Some(MediaType::Html)
Some(MediaType::Html)
} else if MimeClassifier::is_image(&mime) {
Some(MediaType::Image)
} else if MimeClassifier::is_audio_video(&mime) {
@ -235,9 +254,9 @@ impl MimeClassifier {
}
fn maybe_get_media_type(supplied_type: &Option<Mime>) -> Option<MediaType> {
supplied_type.as_ref().and_then(|ref mime| {
MimeClassifier::get_media_type(mime)
})
supplied_type
.as_ref()
.and_then(|ref mime| MimeClassifier::get_media_type(mime))
}
}
@ -252,7 +271,7 @@ trait Matches {
fn matches(&mut self, matches: &[u8]) -> bool;
}
impl <'a, T: Iterator<Item=&'a u8> + Clone> Matches for T {
impl<'a, T: Iterator<Item = &'a u8> + Clone> Matches for T {
// Matching function that works on an iterator.
// see if the next matches.len() bytes in data_iterator equal matches
// move iterator and return true or just return false
@ -270,7 +289,7 @@ impl <'a, T: Iterator<Item=&'a u8> + Clone> Matches for T {
fn matches(&mut self, matches: &[u8]) -> bool {
if self.clone().nth(matches.len()).is_none() {
// there are less than matches.len() elements in self
return false
return false;
}
let result = self.clone().zip(matches).all(|(s, m)| *s == *m);
if result {
@ -294,64 +313,68 @@ impl ByteMatcher {
} else if data == self.pattern {
Some(self.pattern.len())
} else {
data[..data.len() - self.pattern.len() + 1].iter()
data[..data.len() - self.pattern.len() + 1]
.iter()
.position(|x| !self.leading_ignore.contains(x))
.and_then(|start|
if data[start..].iter()
.zip(self.pattern.iter()).zip(self.mask.iter())
.all(|((&data, &pattern), &mask)| (data & mask) == pattern) {
.and_then(|start| {
if data[start..]
.iter()
.zip(self.pattern.iter())
.zip(self.mask.iter())
.all(|((&data, &pattern), &mask)| (data & mask) == pattern)
{
Some(start + self.pattern.len())
} else {
None
})
}
})
}
}
}
impl MIMEChecker for ByteMatcher {
fn classify(&self, data: &[u8]) -> Option<Mime> {
self.matches(data).map(|_| {
self.content_type.clone()
})
self.matches(data).map(|_| self.content_type.clone())
}
fn validate(&self) -> Result<(), String> {
if self.pattern.len() == 0 {
return Err(format!(
"Zero length pattern for {:?}",
self.content_type
))
return Err(format!("Zero length pattern for {:?}", self.content_type));
}
if self.pattern.len() != self.mask.len() {
return Err(format!(
"Unequal pattern and mask length for {:?}",
self.content_type
))
));
}
if self.pattern.iter().zip(self.mask.iter()).any(
|(&pattern, &mask)| pattern & mask != pattern
) {
if self
.pattern
.iter()
.zip(self.mask.iter())
.any(|(&pattern, &mask)| pattern & mask != pattern)
{
return Err(format!(
"Pattern not pre-masked for {:?}",
self.content_type
))
));
}
Ok(())
}
}
struct TagTerminatedByteMatcher {
matcher: ByteMatcher
matcher: ByteMatcher,
}
impl MIMEChecker for TagTerminatedByteMatcher {
fn classify(&self, data: &[u8]) -> Option<Mime> {
self.matcher.matches(data).and_then(|j|
self.matcher.matches(data).and_then(|j| {
if j < data.len() && (data[j] == b' ' || data[j] == b'>') {
Some(self.matcher.content_type.clone())
} else {
None
})
}
})
}
fn validate(&self) -> Result<(), String> {
@ -367,8 +390,10 @@ impl Mp4Matcher {
return false;
}
let box_size = ((data[0] as u32) << 24 | (data[1] as u32) << 16 |
(data[2] as u32) << 8 | (data[3] as u32)) as usize;
let box_size = ((data[0] as u32) << 24 |
(data[1] as u32) << 16 |
(data[2] as u32) << 8 |
(data[3] as u32)) as usize;
if (data.len() < box_size) || (box_size % 4 != 0) {
return false;
}
@ -380,9 +405,10 @@ impl Mp4Matcher {
let mp4 = [0x6D, 0x70, 0x34];
data[8..].starts_with(&mp4) ||
data[16..box_size].chunks(4).any(|chunk| chunk.starts_with(&mp4))
data[16..box_size]
.chunks(4)
.any(|chunk| chunk.starts_with(&mp4))
}
}
impl MIMEChecker for Mp4Matcher {
fn classify(&self, data: &[u8]) -> Option<Mime> {
@ -403,14 +429,16 @@ struct BinaryOrPlaintextClassifier;
impl BinaryOrPlaintextClassifier {
fn classify_impl(&self, data: &[u8]) -> Mime {
if data.starts_with(&[0xFFu8, 0xFEu8]) ||
data.starts_with(&[0xFEu8, 0xFFu8]) ||
data.starts_with(&[0xEFu8, 0xBBu8, 0xBFu8])
data.starts_with(&[0xFEu8, 0xFFu8]) ||
data.starts_with(&[0xEFu8, 0xBBu8, 0xBFu8])
{
mime::TEXT_PLAIN
} else if data.iter().any(|&x| x <= 0x08u8 ||
x == 0x0Bu8 ||
(x >= 0x0Eu8 && x <= 0x1Au8) ||
(x >= 0x1Cu8 && x <= 0x1Fu8)) {
} else if data.iter().any(|&x| {
x <= 0x08u8 ||
x == 0x0Bu8 ||
(x >= 0x0Eu8 && x <= 0x1Au8) ||
(x >= 0x1Cu8 && x <= 0x1Fu8)
}) {
mime::APPLICATION_OCTET_STREAM
} else {
mime::TEXT_PLAIN
@ -425,7 +453,6 @@ impl MIMEChecker for BinaryOrPlaintextClassifier {
fn validate(&self) -> Result<(), String> {
Ok(())
}
}
struct GroupedClassifier {
byte_matchers: Vec<Box<MIMEChecker + Send + Sync>>,
@ -442,7 +469,7 @@ impl GroupedClassifier {
Box::new(ByteMatcher::image_webp()),
Box::new(ByteMatcher::image_png()),
Box::new(ByteMatcher::image_jpeg()),
]
],
}
}
fn audio_video_classifier() -> GroupedClassifier {
@ -456,8 +483,8 @@ impl GroupedClassifier {
Box::new(ByteMatcher::audio_midi()),
Box::new(ByteMatcher::video_avi()),
Box::new(ByteMatcher::audio_wave()),
Box::new(Mp4Matcher)
]
Box::new(Mp4Matcher),
],
}
}
fn scriptable_classifier() -> GroupedClassifier {
@ -481,8 +508,8 @@ impl GroupedClassifier {
Box::new(ByteMatcher::text_html_p()),
Box::new(ByteMatcher::text_html_comment()),
Box::new(ByteMatcher::text_xml()),
Box::new(ByteMatcher::application_pdf())
]
Box::new(ByteMatcher::application_pdf()),
],
}
}
fn plaintext_classifier() -> GroupedClassifier {
@ -491,8 +518,8 @@ impl GroupedClassifier {
Box::new(ByteMatcher::text_plain_utf_8_bom()),
Box::new(ByteMatcher::text_plain_utf_16le_bom()),
Box::new(ByteMatcher::text_plain_utf_16be_bom()),
Box::new(ByteMatcher::application_postscript())
]
Box::new(ByteMatcher::application_postscript()),
],
}
}
fn archive_classifier() -> GroupedClassifier {
@ -500,8 +527,8 @@ impl GroupedClassifier {
byte_matchers: vec![
Box::new(ByteMatcher::application_x_gzip()),
Box::new(ByteMatcher::application_zip()),
Box::new(ByteMatcher::application_x_rar_compressed())
]
Box::new(ByteMatcher::application_x_rar_compressed()),
],
}
}
@ -513,7 +540,7 @@ impl GroupedClassifier {
Box::new(ByteMatcher::open_type()),
Box::new(ByteMatcher::true_type()),
Box::new(ByteMatcher::application_vnd_ms_font_object()),
]
],
}
}
}
@ -536,7 +563,7 @@ impl MIMEChecker for GroupedClassifier {
enum Match {
Start,
DidNotMatch,
StartAndEnd
StartAndEnd,
}
impl Match {
@ -549,7 +576,9 @@ impl Match {
}
fn eats_until<'a, T>(matcher: &mut T, start: &[u8], end: &[u8]) -> Match
where T: Iterator<Item=&'a u8> + Clone {
where
T: Iterator<Item = &'a u8> + Clone,
{
if !matcher.matches(start) {
Match::DidNotMatch
} else if end.len() == 1 {
@ -593,11 +622,12 @@ impl FeedsClassifier {
// Steps 5.2.1 to 5.2.4
match eats_until(&mut matcher, b"?", b"?>")
.chain(|| eats_until(&mut matcher, b"!--", b"-->"))
.chain(|| eats_until(&mut matcher, b"!", b">")) {
.chain(|| eats_until(&mut matcher, b"!--", b"-->"))
.chain(|| eats_until(&mut matcher, b"!", b">"))
{
Match::StartAndEnd => continue,
Match::DidNotMatch => {},
Match::Start => return None
Match::Start => return None,
}
// Step 5.2.5
@ -611,15 +641,21 @@ impl FeedsClassifier {
// Step 5.2.7
if matcher.matches(b"rdf:RDF") {
while matcher.next().is_some() {
match eats_until(&mut matcher,
b"http://purl.org/rss/1.0/",
b"http://www.w3.org/1999/02/22-rdf-syntax-ns#")
.chain(|| eats_until(&mut matcher,
b"http://www.w3.org/1999/02/22-rdf-syntax-ns#",
b"http://purl.org/rss/1.0/")) {
match eats_until(
&mut matcher,
b"http://purl.org/rss/1.0/",
b"http://www.w3.org/1999/02/22-rdf-syntax-ns#",
)
.chain(|| {
eats_until(
&mut matcher,
b"http://www.w3.org/1999/02/22-rdf-syntax-ns#",
b"http://purl.org/rss/1.0/",
)
}) {
Match::StartAndEnd => return Some("application/rss+xml".parse().unwrap()),
Match::DidNotMatch => {},
Match::Start => return None
Match::Start => return None,
}
}
return None;
@ -630,7 +666,7 @@ impl FeedsClassifier {
impl MIMEChecker for FeedsClassifier {
fn classify(&self, data: &[u8]) -> Option<Mime> {
self.classify_impl(data)
self.classify_impl(data)
}
fn validate(&self) -> Result<(), String> {
@ -647,7 +683,7 @@ impl ByteMatcher {
pattern: b"\x00\x00\x01\x00",
mask: b"\xFF\xFF\xFF\xFF",
content_type: "image/x-icon".parse().unwrap(),
leading_ignore: &[]
leading_ignore: &[],
}
}
//A Windows Cursor signature.
@ -656,7 +692,7 @@ impl ByteMatcher {
pattern: b"\x00\x00\x02\x00",
mask: b"\xFF\xFF\xFF\xFF",
content_type: "image/x-icon".parse().unwrap(),
leading_ignore: &[]
leading_ignore: &[],
}
}
//The string "BM", a BMP signature.
@ -665,7 +701,7 @@ impl ByteMatcher {
pattern: b"BM",
mask: b"\xFF\xFF",
content_type: mime::IMAGE_BMP,
leading_ignore: &[]
leading_ignore: &[],
}
}
//The string "GIF89a", a GIF signature.
@ -674,7 +710,7 @@ impl ByteMatcher {
pattern: b"GIF89a",
mask: b"\xFF\xFF\xFF\xFF\xFF\xFF",
content_type: mime::IMAGE_GIF,
leading_ignore: &[]
leading_ignore: &[],
}
}
//The string "GIF87a", a GIF signature.
@ -683,7 +719,7 @@ impl ByteMatcher {
pattern: b"GIF87a",
mask: b"\xFF\xFF\xFF\xFF\xFF\xFF",
content_type: mime::IMAGE_GIF,
leading_ignore: &[]
leading_ignore: &[],
}
}
//The string "RIFF" followed by four bytes followed by the string "WEBPVP".
@ -692,7 +728,7 @@ impl ByteMatcher {
pattern: b"RIFF\x00\x00\x00\x00WEBPVP",
mask: b"\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF",
content_type: "image/webp".parse().unwrap(),
leading_ignore: &[]
leading_ignore: &[],
}
}
//An error-checking byte followed by the string "PNG" followed by CR LF SUB LF, the PNG
@ -702,7 +738,7 @@ impl ByteMatcher {
pattern: b"\x89PNG\r\n\x1A\n",
mask: b"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
content_type: mime::IMAGE_PNG,
leading_ignore: &[]
leading_ignore: &[],
}
}
// The JPEG Start of Image marker followed by the indicator byte of another marker.
@ -711,7 +747,7 @@ impl ByteMatcher {
pattern: b"\xFF\xD8\xFF",
mask: b"\xFF\xFF\xFF",
content_type: mime::IMAGE_JPEG,
leading_ignore: &[]
leading_ignore: &[],
}
}
//The WebM signature. [TODO: Use more bytes?]
@ -720,7 +756,7 @@ impl ByteMatcher {
pattern: b"\x1A\x45\xDF\xA3",
mask: b"\xFF\xFF\xFF\xFF",
content_type: "video/webm".parse().unwrap(),
leading_ignore: &[]
leading_ignore: &[],
}
}
//The string ".snd", the basic audio signature.
@ -729,16 +765,16 @@ impl ByteMatcher {
pattern: b".snd",
mask: b"\xFF\xFF\xFF\xFF",
content_type: "audio/basic".parse().unwrap(),
leading_ignore: &[]
leading_ignore: &[],
}
}
//The string "FORM" followed by four bytes followed by the string "AIFF", the AIFF signature.
fn audio_aiff() -> ByteMatcher {
ByteMatcher {
pattern: b"FORM\x00\x00\x00\x00AIFF",
mask: b"\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF",
pattern: b"FORM\x00\x00\x00\x00AIFF",
mask: b"\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF",
content_type: "audio/aiff".parse().unwrap(),
leading_ignore: &[]
leading_ignore: &[],
}
}
//The string "ID3", the ID3v2-tagged MP3 signature.
@ -747,7 +783,7 @@ impl ByteMatcher {
pattern: b"ID3",
mask: b"\xFF\xFF\xFF",
content_type: "audio/mpeg".parse().unwrap(),
leading_ignore: &[]
leading_ignore: &[],
}
}
//The string "OggS" followed by NUL, the Ogg container signature.
@ -756,7 +792,7 @@ impl ByteMatcher {
pattern: b"OggS\x00",
mask: b"\xFF\xFF\xFF\xFF\xFF",
content_type: "application/ogg".parse().unwrap(),
leading_ignore: &[]
leading_ignore: &[],
}
}
//The string "MThd" followed by four bytes representing the number 6 in 32 bits (big-endian),
@ -766,7 +802,7 @@ impl ByteMatcher {
pattern: b"MThd\x00\x00\x00\x06",
mask: b"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
content_type: "audio/midi".parse().unwrap(),
leading_ignore: &[]
leading_ignore: &[],
}
}
//The string "RIFF" followed by four bytes followed by the string "AVI ", the AVI signature.
@ -775,7 +811,7 @@ impl ByteMatcher {
pattern: b"RIFF\x00\x00\x00\x00AVI ",
mask: b"\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF",
content_type: "video/avi".parse().unwrap(),
leading_ignore: &[]
leading_ignore: &[],
}
}
// The string "RIFF" followed by four bytes followed by the string "WAVE", the WAVE signature.
@ -784,7 +820,7 @@ impl ByteMatcher {
pattern: b"RIFF\x00\x00\x00\x00WAVE",
mask: b"\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF",
content_type: "audio/wave".parse().unwrap(),
leading_ignore: &[]
leading_ignore: &[],
}
}
// doctype terminated with Tag terminating (TT) Byte
@ -794,8 +830,8 @@ impl ByteMatcher {
pattern: b"<!DOCTYPE HTML",
mask: b"\xFF\xFF\xDF\xDF\xDF\xDF\xDF\xDF\xDF\xFF\xDF\xDF\xDF\xDF",
content_type: mime::TEXT_HTML,
leading_ignore: b"\t\n\x0C\r "
}
leading_ignore: b"\t\n\x0C\r ",
},
}
}
@ -806,20 +842,20 @@ impl ByteMatcher {
pattern: b"<HTML",
mask: b"\xFF\xDF\xDF\xDF\xDF",
content_type: mime::TEXT_HTML,
leading_ignore: b"\t\n\x0C\r "
}
leading_ignore: b"\t\n\x0C\r ",
},
}
}
// head terminated with Tag Terminating (TT) Byte
fn text_html_head() -> TagTerminatedByteMatcher {
TagTerminatedByteMatcher {
TagTerminatedByteMatcher {
matcher: ByteMatcher {
pattern: b"<HEAD",
mask: b"\xFF\xDF\xDF\xDF\xDF",
content_type: mime::TEXT_HTML,
leading_ignore: b"\t\n\x0C\r "
}
leading_ignore: b"\t\n\x0C\r ",
},
}
}
@ -830,8 +866,8 @@ impl ByteMatcher {
pattern: b"<SCRIPT",
mask: b"\xFF\xDF\xDF\xDF\xDF\xDF\xDF",
content_type: mime::TEXT_HTML,
leading_ignore: b"\t\n\x0C\r "
}
leading_ignore: b"\t\n\x0C\r ",
},
}
}
@ -842,8 +878,8 @@ impl ByteMatcher {
pattern: b"<IFRAME",
mask: b"\xFF\xDF\xDF\xDF\xDF\xDF\xDF",
content_type: mime::TEXT_HTML,
leading_ignore: b"\t\n\x0C\r "
}
leading_ignore: b"\t\n\x0C\r ",
},
}
}
@ -854,8 +890,8 @@ impl ByteMatcher {
pattern: b"<H1",
mask: b"\xFF\xDF\xFF",
content_type: mime::TEXT_HTML,
leading_ignore: b"\t\n\x0C\r "
}
leading_ignore: b"\t\n\x0C\r ",
},
}
}
@ -866,8 +902,8 @@ impl ByteMatcher {
pattern: b"<DIV",
mask: b"\xFF\xDF\xDF\xDF",
content_type: mime::TEXT_HTML,
leading_ignore: b"\t\n\x0C\r "
}
leading_ignore: b"\t\n\x0C\r ",
},
}
}
@ -878,8 +914,8 @@ impl ByteMatcher {
pattern: b"<FONT",
mask: b"\xFF\xDF\xDF\xDF\xDF",
content_type: mime::TEXT_HTML,
leading_ignore: b"\t\n\x0C\r "
}
leading_ignore: b"\t\n\x0C\r ",
},
}
}
@ -887,11 +923,11 @@ impl ByteMatcher {
fn text_html_table() -> TagTerminatedByteMatcher {
TagTerminatedByteMatcher {
matcher: ByteMatcher {
pattern: b"<TABLE",
mask: b"\xFF\xDF\xDF\xDF\xDF\xDF",
content_type: mime::TEXT_HTML,
leading_ignore: b"\t\n\x0C\r "
}
pattern: b"<TABLE",
mask: b"\xFF\xDF\xDF\xDF\xDF\xDF",
content_type: mime::TEXT_HTML,
leading_ignore: b"\t\n\x0C\r ",
},
}
}
@ -902,8 +938,8 @@ impl ByteMatcher {
pattern: b"<A",
mask: b"\xFF\xDF",
content_type: mime::TEXT_HTML,
leading_ignore: b"\t\n\x0C\r "
}
leading_ignore: b"\t\n\x0C\r ",
},
}
}
@ -914,8 +950,8 @@ impl ByteMatcher {
pattern: b"<STYLE",
mask: b"\xFF\xDF\xDF\xDF\xDF\xDF",
content_type: mime::TEXT_HTML,
leading_ignore: b"\t\n\x0C\r "
}
leading_ignore: b"\t\n\x0C\r ",
},
}
}
@ -926,8 +962,8 @@ impl ByteMatcher {
pattern: b"<TITLE",
mask: b"\xFF\xDF\xDF\xDF\xDF\xDF",
content_type: mime::TEXT_HTML,
leading_ignore: b"\t\n\x0C\r "
}
leading_ignore: b"\t\n\x0C\r ",
},
}
}
@ -938,8 +974,8 @@ impl ByteMatcher {
pattern: b"<B",
mask: b"\xFF\xDF",
content_type: mime::TEXT_HTML,
leading_ignore: b"\t\n\x0C\r "
}
leading_ignore: b"\t\n\x0C\r ",
},
}
}
@ -950,8 +986,8 @@ impl ByteMatcher {
pattern: b"<BODY",
mask: b"\xFF\xDF\xDF\xDF\xDF",
content_type: mime::TEXT_HTML,
leading_ignore: b"\t\n\x0C\r "
}
leading_ignore: b"\t\n\x0C\r ",
},
}
}
@ -962,8 +998,8 @@ impl ByteMatcher {
pattern: b"<BR",
mask: b"\xFF\xDF\xDF",
content_type: mime::TEXT_HTML,
leading_ignore: b"\t\n\x0C\r "
}
leading_ignore: b"\t\n\x0C\r ",
},
}
}
@ -974,8 +1010,8 @@ impl ByteMatcher {
pattern: b"<P",
mask: b"\xFF\xDF",
content_type: mime::TEXT_HTML,
leading_ignore: b"\t\n\x0C\r "
}
leading_ignore: b"\t\n\x0C\r ",
},
}
}
@ -986,8 +1022,8 @@ impl ByteMatcher {
pattern: b"<!--",
mask: b"\xFF\xFF\xFF\xFF",
content_type: mime::TEXT_HTML,
leading_ignore: b"\t\n\x0C\r "
}
leading_ignore: b"\t\n\x0C\r ",
},
}
}
@ -997,7 +1033,7 @@ impl ByteMatcher {
pattern: b"<?xml",
mask: b"\xFF\xFF\xFF\xFF\xFF",
content_type: mime::TEXT_XML,
leading_ignore: b"\t\n\x0C\r "
leading_ignore: b"\t\n\x0C\r ",
}
}
//The string "%PDF-", the PDF signature.
@ -1006,7 +1042,7 @@ impl ByteMatcher {
pattern: b"%PDF-",
mask: b"\xFF\xFF\xFF\xFF\xFF",
content_type: mime::APPLICATION_PDF,
leading_ignore: &[]
leading_ignore: &[],
}
}
//34 bytes followed by the string "LP", the Embedded OpenType signature.
@ -1019,7 +1055,7 @@ impl ByteMatcher {
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\xFF\xFF",
content_type: "application/vnd.ms-fontobject".parse().unwrap(),
leading_ignore: &[]
leading_ignore: &[],
}
}
//4 bytes representing the version number 1.0, a TrueType signature.
@ -1028,7 +1064,7 @@ impl ByteMatcher {
pattern: b"\x00\x01\x00\x00",
mask: b"\xFF\xFF\xFF\xFF",
content_type: "application/font-sfnt".parse().unwrap(),
leading_ignore: &[]
leading_ignore: &[],
}
}
//The string "OTTO", the OpenType signature.
@ -1037,7 +1073,7 @@ impl ByteMatcher {
pattern: b"OTTO",
mask: b"\xFF\xFF\xFF\xFF",
content_type: "application/font-sfnt".parse().unwrap(),
leading_ignore: &[]
leading_ignore: &[],
}
}
// The string "ttcf", the TrueType Collection signature.
@ -1046,7 +1082,7 @@ impl ByteMatcher {
pattern: b"ttcf",
mask: b"\xFF\xFF\xFF\xFF",
content_type: "application/font-sfnt".parse().unwrap(),
leading_ignore: &[]
leading_ignore: &[],
}
}
// The string "wOFF", the Web Open Font Format signature.
@ -1055,7 +1091,7 @@ impl ByteMatcher {
pattern: b"wOFF",
mask: b"\xFF\xFF\xFF\xFF",
content_type: "application/font-woff".parse().unwrap(),
leading_ignore: &[]
leading_ignore: &[],
}
}
//The GZIP archive signature.
@ -1064,7 +1100,7 @@ impl ByteMatcher {
pattern: b"\x1F\x8B\x08",
mask: b"\xFF\xFF\xFF",
content_type: "application/x-gzip".parse().unwrap(),
leading_ignore: &[]
leading_ignore: &[],
}
}
//The string "PK" followed by ETX EOT, the ZIP archive signature.
@ -1073,7 +1109,7 @@ impl ByteMatcher {
pattern: b"PK\x03\x04",
mask: b"\xFF\xFF\xFF\xFF",
content_type: "application/zip".parse().unwrap(),
leading_ignore: &[]
leading_ignore: &[],
}
}
//The string "Rar " followed by SUB BEL NUL, the RAR archive signature.
@ -1082,16 +1118,16 @@ impl ByteMatcher {
pattern: b"Rar \x1A\x07\x00",
mask: b"\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
content_type: "application/x-rar-compressed".parse().unwrap(),
leading_ignore: &[]
leading_ignore: &[],
}
}
// The string "%!PS-Adobe-", the PostScript signature.
fn application_postscript() -> ByteMatcher {
ByteMatcher {
pattern: b"%!PS-Adobe-",
mask: b"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
mask: b"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
content_type: "application/postscript".parse().unwrap(),
leading_ignore: &[]
leading_ignore: &[],
}
}
// UTF-16BE BOM
@ -1100,7 +1136,7 @@ impl ByteMatcher {
pattern: b"\xFE\xFF\x00\x00",
mask: b"\xFF\xFF\x00\x00",
content_type: mime::TEXT_PLAIN,
leading_ignore: &[]
leading_ignore: &[],
}
}
//UTF-16LE BOM
@ -1109,7 +1145,7 @@ impl ByteMatcher {
pattern: b"\xFF\xFE\x00\x00",
mask: b"\xFF\xFF\x00\x00",
content_type: mime::TEXT_PLAIN,
leading_ignore: &[]
leading_ignore: &[],
}
}
//UTF-8 BOM
@ -1118,7 +1154,7 @@ impl ByteMatcher {
pattern: b"\xEF\xBB\xBF\x00",
mask: b"\xFF\xFF\xFF\x00",
content_type: mime::TEXT_PLAIN,
leading_ignore: &[]
leading_ignore: &[],
}
}
}

View file

@ -48,59 +48,65 @@ use storage_thread::StorageThreadFactory;
use websocket_loader;
/// Returns a tuple of (public, private) senders to the new threads.
pub fn new_resource_threads(user_agent: Cow<'static, str>,
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
time_profiler_chan: ProfilerChan,
mem_profiler_chan: MemProfilerChan,
embedder_proxy: EmbedderProxy,
config_dir: Option<PathBuf>)
-> (ResourceThreads, ResourceThreads) {
pub fn new_resource_threads(
user_agent: Cow<'static, str>,
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
time_profiler_chan: ProfilerChan,
mem_profiler_chan: MemProfilerChan,
embedder_proxy: EmbedderProxy,
config_dir: Option<PathBuf>,
) -> (ResourceThreads, ResourceThreads) {
let (public_core, private_core) = new_core_resource_thread(
user_agent,
devtools_chan,
time_profiler_chan,
mem_profiler_chan,
embedder_proxy,
config_dir.clone());
config_dir.clone(),
);
let storage: IpcSender<StorageThreadMsg> = StorageThreadFactory::new(config_dir);
(ResourceThreads::new(public_core, storage.clone()),
ResourceThreads::new(private_core, storage))
(
ResourceThreads::new(public_core, storage.clone()),
ResourceThreads::new(private_core, storage),
)
}
/// Create a CoreResourceThread
pub fn new_core_resource_thread(user_agent: Cow<'static, str>,
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
time_profiler_chan: ProfilerChan,
mem_profiler_chan: MemProfilerChan,
embedder_proxy: EmbedderProxy,
config_dir: Option<PathBuf>)
-> (CoreResourceThread, CoreResourceThread) {
pub fn new_core_resource_thread(
user_agent: Cow<'static, str>,
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
time_profiler_chan: ProfilerChan,
mem_profiler_chan: MemProfilerChan,
embedder_proxy: EmbedderProxy,
config_dir: Option<PathBuf>,
) -> (CoreResourceThread, CoreResourceThread) {
let (public_setup_chan, public_setup_port) = ipc::channel().unwrap();
let (private_setup_chan, private_setup_port) = ipc::channel().unwrap();
let (report_chan, report_port) = ipc::channel().unwrap();
thread::Builder::new().name("ResourceManager".to_owned()).spawn(move || {
let resource_manager = CoreResourceManager::new(
user_agent, devtools_chan, time_profiler_chan, embedder_proxy
);
thread::Builder::new()
.name("ResourceManager".to_owned())
.spawn(move || {
let resource_manager = CoreResourceManager::new(
user_agent,
devtools_chan,
time_profiler_chan,
embedder_proxy,
);
let mut channel_manager = ResourceChannelManager {
resource_manager: resource_manager,
config_dir: config_dir,
};
let mut channel_manager = ResourceChannelManager {
resource_manager: resource_manager,
config_dir: config_dir,
};
mem_profiler_chan.run_with_memory_reporting(|| (
channel_manager.start(
public_setup_port,
private_setup_port,
report_port)
),
String::from("network-cache-reporter"),
report_chan,
|report_chan| report_chan);
}).expect("Thread spawning failed");
mem_profiler_chan.run_with_memory_reporting(
|| (channel_manager.start(public_setup_port, private_setup_port, report_port)),
String::from("network-cache-reporter"),
report_chan,
|report_chan| report_chan,
);
})
.expect("Thread spawning failed");
(public_setup_chan, private_setup_chan)
}
@ -121,12 +127,8 @@ fn create_http_states(config_dir: Option<&Path>) -> (Arc<HttpState>, Arc<HttpSta
}
let certs = match opts::get().certificate_path {
Some(ref path) => {
fs::read_to_string(path).expect("Couldn't not find certificate file")
}
None => {
resources::read_string(Resource::SSLCertificates)
},
Some(ref path) => fs::read_to_string(path).expect("Couldn't not find certificate file"),
None => resources::read_string(Resource::SSLCertificates),
};
let ssl_connector_builder = create_ssl_connector_builder(&certs);
@ -147,10 +149,12 @@ fn create_http_states(config_dir: Option<&Path>) -> (Arc<HttpState>, Arc<HttpSta
impl ResourceChannelManager {
#[allow(unsafe_code)]
fn start(&mut self,
public_receiver: IpcReceiver<CoreResourceMsg>,
private_receiver: IpcReceiver<CoreResourceMsg>,
memory_reporter: IpcReceiver<ReportsChan>) {
fn start(
&mut self,
public_receiver: IpcReceiver<CoreResourceMsg>,
private_receiver: IpcReceiver<CoreResourceMsg>,
memory_reporter: IpcReceiver<ReportsChan>,
) {
let (public_http_state, private_http_state) =
create_http_states(self.config_dir.as_ref().map(Deref::deref));
@ -164,7 +168,7 @@ impl ResourceChannelManager {
// Handles case where profiler thread shuts down before resource thread.
match receiver {
ipc::IpcSelectionResult::ChannelClosed(..) => continue,
_ => {}
_ => {},
}
let (id, data) = receiver.unwrap();
// If message is memory report, get the size_of of public and private http caches
@ -190,10 +194,12 @@ impl ResourceChannelManager {
}
}
fn process_report(&mut self,
msg: ReportsChan,
public_http_state: &Arc<HttpState>,
private_http_state: &Arc<HttpState>) {
fn process_report(
&mut self,
msg: ReportsChan,
public_http_state: &Arc<HttpState>,
private_http_state: &Arc<HttpState>,
) {
let mut ops = MallocSizeOfOps::new(servo_allocator::usable_size, None, None);
let public_cache = public_http_state.http_cache.read().unwrap();
let private_cache = private_http_state.http_cache.read().unwrap();
@ -201,74 +207,95 @@ impl ResourceChannelManager {
let public_report = Report {
path: path!["memory-cache", "public"],
kind: ReportKind::ExplicitJemallocHeapSize,
size: public_cache.size_of(&mut ops)
size: public_cache.size_of(&mut ops),
};
let private_report = Report {
path: path!["memory-cache", "private"],
kind: ReportKind::ExplicitJemallocHeapSize,
size: private_cache.size_of(&mut ops)
size: private_cache.size_of(&mut ops),
};
msg.send(vec!(public_report, private_report));
msg.send(vec![public_report, private_report]);
}
/// Returns false if the thread should exit.
fn process_msg(&mut self,
msg: CoreResourceMsg,
http_state: &Arc<HttpState>) -> bool {
fn process_msg(&mut self, msg: CoreResourceMsg, http_state: &Arc<HttpState>) -> bool {
match msg {
CoreResourceMsg::Fetch(req_init, channels) => {
match channels {
FetchChannels::ResponseMsg(sender, cancel_chan) =>
self.resource_manager.fetch(req_init, None, sender, http_state, cancel_chan),
FetchChannels::WebSocket { event_sender, action_receiver } =>
self.resource_manager.websocket_connect(req_init, event_sender, action_receiver, http_state),
}
}
CoreResourceMsg::FetchRedirect(req_init, res_init, sender, cancel_chan) =>
self.resource_manager.fetch(req_init, Some(res_init), sender, http_state, cancel_chan),
CoreResourceMsg::SetCookieForUrl(request, cookie, source) =>
self.resource_manager.set_cookie_for_url(&request, cookie.into_inner(), source, http_state),
CoreResourceMsg::Fetch(req_init, channels) => match channels {
FetchChannels::ResponseMsg(sender, cancel_chan) => {
self.resource_manager
.fetch(req_init, None, sender, http_state, cancel_chan)
},
FetchChannels::WebSocket {
event_sender,
action_receiver,
} => self.resource_manager.websocket_connect(
req_init,
event_sender,
action_receiver,
http_state,
),
},
CoreResourceMsg::FetchRedirect(req_init, res_init, sender, cancel_chan) => self
.resource_manager
.fetch(req_init, Some(res_init), sender, http_state, cancel_chan),
CoreResourceMsg::SetCookieForUrl(request, cookie, source) => self
.resource_manager
.set_cookie_for_url(&request, cookie.into_inner(), source, http_state),
CoreResourceMsg::SetCookiesForUrl(request, cookies, source) => {
for cookie in cookies {
self.resource_manager.set_cookie_for_url(&request, cookie.into_inner(), source, http_state);
self.resource_manager.set_cookie_for_url(
&request,
cookie.into_inner(),
source,
http_state,
);
}
}
},
CoreResourceMsg::GetCookiesForUrl(url, consumer, source) => {
let mut cookie_jar = http_state.cookie_jar.write().unwrap();
consumer.send(cookie_jar.cookies_for_url(&url, source)).unwrap();
}
consumer
.send(cookie_jar.cookies_for_url(&url, source))
.unwrap();
},
CoreResourceMsg::NetworkMediator(mediator_chan) => {
self.resource_manager.swmanager_chan = Some(mediator_chan)
}
},
CoreResourceMsg::GetCookiesDataForUrl(url, consumer, source) => {
let mut cookie_jar = http_state.cookie_jar.write().unwrap();
let cookies = cookie_jar.cookies_data_for_url(&url, source).map(Serde).collect();
let cookies = cookie_jar
.cookies_data_for_url(&url, source)
.map(Serde)
.collect();
consumer.send(cookies).unwrap();
}
},
CoreResourceMsg::GetHistoryState(history_state_id, consumer) => {
let history_states = http_state.history_states.read().unwrap();
consumer.send(history_states.get(&history_state_id).cloned()).unwrap();
}
consumer
.send(history_states.get(&history_state_id).cloned())
.unwrap();
},
CoreResourceMsg::SetHistoryState(history_state_id, history_state) => {
let mut history_states = http_state.history_states.write().unwrap();
history_states.insert(history_state_id, history_state);
}
},
CoreResourceMsg::RemoveHistoryStates(states_to_remove) => {
let mut history_states = http_state.history_states.write().unwrap();
for history_state in states_to_remove {
history_states.remove(&history_state);
}
}
},
CoreResourceMsg::Synchronize(sender) => {
let _ = sender.send(());
}
},
CoreResourceMsg::ToFileManager(msg) => self.resource_manager.filemanager.handle(msg),
CoreResourceMsg::Exit(sender) => {
if let Some(ref config_dir) = self.config_dir {
match http_state.auth_cache.read() {
Ok(auth_cache) => write_json_to_file(&*auth_cache, config_dir, "auth_cache.json"),
Ok(auth_cache) => {
write_json_to_file(&*auth_cache, config_dir, "auth_cache.json")
},
Err(_) => warn!("Error writing auth cache to disk"),
}
match http_state.cookie_jar.read() {
@ -282,14 +309,15 @@ impl ResourceChannelManager {
}
let _ = sender.send(());
return false;
}
},
}
true
}
}
pub fn read_json_from_file<T>(data: &mut T, config_dir: &Path, filename: &str)
where T: for<'de> Deserialize<'de>
where
T: for<'de> Deserialize<'de>,
{
let path = config_dir.join(filename);
let display = path.display();
@ -304,10 +332,11 @@ pub fn read_json_from_file<T>(data: &mut T, config_dir: &Path, filename: &str)
let mut string_buffer: String = String::new();
match file.read_to_string(&mut string_buffer) {
Err(why) => {
panic!("couldn't read from {}: {}", display,
Error::description(&why))
},
Err(why) => panic!(
"couldn't read from {}: {}",
display,
Error::description(&why)
),
Ok(_) => println!("successfully read from {}", display),
}
@ -318,7 +347,8 @@ pub fn read_json_from_file<T>(data: &mut T, config_dir: &Path, filename: &str)
}
pub fn write_json_to_file<T>(data: &T, config_dir: &Path, filename: &str)
where T: Serialize
where
T: Serialize,
{
let json_encoded: String;
match serde_json::to_string_pretty(&data) {
@ -329,17 +359,16 @@ pub fn write_json_to_file<T>(data: &T, config_dir: &Path, filename: &str)
let display = path.display();
let mut file = match File::create(&path) {
Err(why) => panic!("couldn't create {}: {}",
display,
Error::description(&why)),
Err(why) => panic!("couldn't create {}: {}", display, Error::description(&why)),
Ok(file) => file,
};
match file.write_all(json_encoded.as_bytes()) {
Err(why) => {
panic!("couldn't write to {}: {}", display,
Error::description(&why))
},
Err(why) => panic!(
"couldn't write to {}: {}",
display,
Error::description(&why)
),
Ok(_) => println!("successfully wrote to {}", display),
}
}
@ -354,7 +383,7 @@ impl AuthCache {
pub fn new() -> AuthCache {
AuthCache {
version: 1,
entries: HashMap::new()
entries: HashMap::new(),
}
}
}
@ -373,10 +402,12 @@ pub struct CoreResourceManager {
}
impl CoreResourceManager {
pub fn new(user_agent: Cow<'static, str>,
devtools_channel: Option<Sender<DevtoolsControlMsg>>,
_profiler_chan: ProfilerChan,
embedder_proxy: EmbedderProxy) -> CoreResourceManager {
pub fn new(
user_agent: Cow<'static, str>,
devtools_channel: Option<Sender<DevtoolsControlMsg>>,
_profiler_chan: ProfilerChan,
embedder_proxy: EmbedderProxy,
) -> CoreResourceManager {
CoreResourceManager {
user_agent: user_agent,
devtools_chan: devtools_channel,
@ -385,55 +416,67 @@ impl CoreResourceManager {
}
}
fn set_cookie_for_url(&mut self, request: &ServoUrl,
cookie: cookie_rs::Cookie<'static>,
source: CookieSource,
http_state: &Arc<HttpState>) {
fn set_cookie_for_url(
&mut self,
request: &ServoUrl,
cookie: cookie_rs::Cookie<'static>,
source: CookieSource,
http_state: &Arc<HttpState>,
) {
if let Some(cookie) = cookie::Cookie::new_wrapped(cookie, request, source) {
let mut cookie_jar = http_state.cookie_jar.write().unwrap();
cookie_jar.push(cookie, request, source)
}
}
fn fetch(&self,
req_init: RequestInit,
res_init_: Option<ResponseInit>,
mut sender: IpcSender<FetchResponseMsg>,
http_state: &Arc<HttpState>,
cancel_chan: Option<IpcReceiver<()>>) {
fn fetch(
&self,
req_init: RequestInit,
res_init_: Option<ResponseInit>,
mut sender: IpcSender<FetchResponseMsg>,
http_state: &Arc<HttpState>,
cancel_chan: Option<IpcReceiver<()>>,
) {
let http_state = http_state.clone();
let ua = self.user_agent.clone();
let dc = self.devtools_chan.clone();
let filemanager = self.filemanager.clone();
thread::Builder::new().name(format!("fetch thread for {}", req_init.url)).spawn(move || {
let mut request = Request::from_init(req_init);
// XXXManishearth: Check origin against pipeline id (also ensure that the mode is allowed)
// todo load context / mimesniff in fetch
// todo referrer policy?
// todo service worker stuff
let context = FetchContext {
state: http_state,
user_agent: ua,
devtools_chan: dc,
filemanager: filemanager,
cancellation_listener: Arc::new(Mutex::new(CancellationListener::new(cancel_chan))),
};
thread::Builder::new()
.name(format!("fetch thread for {}", req_init.url))
.spawn(move || {
let mut request = Request::from_init(req_init);
// XXXManishearth: Check origin against pipeline id (also ensure that the mode is allowed)
// todo load context / mimesniff in fetch
// todo referrer policy?
// todo service worker stuff
let context = FetchContext {
state: http_state,
user_agent: ua,
devtools_chan: dc,
filemanager: filemanager,
cancellation_listener: Arc::new(Mutex::new(CancellationListener::new(
cancel_chan,
))),
};
match res_init_ {
Some(res_init) => {
let response = Response::from_init(res_init);
http_redirect_fetch(&mut request,
&mut CorsCache::new(),
response,
true,
&mut sender,
&mut None,
&context);
},
None => fetch(&mut request, &mut sender, &context),
};
}).expect("Thread spawning failed");
match res_init_ {
Some(res_init) => {
let response = Response::from_init(res_init);
http_redirect_fetch(
&mut request,
&mut CorsCache::new(),
response,
true,
&mut sender,
&mut None,
&context,
);
},
None => fetch(&mut request, &mut sender, &context),
};
})
.expect("Thread spawning failed");
}
fn websocket_connect(
@ -441,7 +484,7 @@ impl CoreResourceManager {
request: RequestInit,
event_sender: IpcSender<WebSocketNetworkEvent>,
action_receiver: IpcReceiver<WebSocketDomAction>,
http_state: &Arc<HttpState>
http_state: &Arc<HttpState>,
) {
websocket_loader::init(request, event_sender, action_receiver, http_state.clone());
}

View file

@ -22,9 +22,12 @@ impl StorageThreadFactory for IpcSender<StorageThreadMsg> {
/// Create a storage thread
fn new(config_dir: Option<PathBuf>) -> IpcSender<StorageThreadMsg> {
let (chan, port) = ipc::channel().unwrap();
thread::Builder::new().name("StorageManager".to_owned()).spawn(move || {
StorageManager::new(port, config_dir).start();
}).expect("Thread spawning failed");
thread::Builder::new()
.name("StorageManager".to_owned())
.spawn(move || {
StorageManager::new(port, config_dir).start();
})
.expect("Thread spawning failed");
chan
}
}
@ -37,9 +40,7 @@ struct StorageManager {
}
impl StorageManager {
fn new(port: IpcReceiver<StorageThreadMsg>,
config_dir: Option<PathBuf>)
-> StorageManager {
fn new(port: IpcReceiver<StorageThreadMsg>, config_dir: Option<PathBuf>) -> StorageManager {
let mut local_data = HashMap::new();
if let Some(ref config_dir) = config_dir {
resource_thread::read_json_from_file(&mut local_data, config_dir, "local_data.json");
@ -59,33 +60,33 @@ impl StorageManager {
match self.port.recv().unwrap() {
StorageThreadMsg::Length(sender, url, storage_type) => {
self.length(sender, url, storage_type)
}
},
StorageThreadMsg::Key(sender, url, storage_type, index) => {
self.key(sender, url, storage_type, index)
}
},
StorageThreadMsg::Keys(sender, url, storage_type) => {
self.keys(sender, url, storage_type)
}
},
StorageThreadMsg::SetItem(sender, url, storage_type, name, value) => {
self.set_item(sender, url, storage_type, name, value);
self.save_state()
}
},
StorageThreadMsg::GetItem(sender, url, storage_type, name) => {
self.request_item(sender, url, storage_type, name)
}
},
StorageThreadMsg::RemoveItem(sender, url, storage_type, name) => {
self.remove_item(sender, url, storage_type, name);
self.save_state()
}
},
StorageThreadMsg::Clear(sender, url, storage_type) => {
self.clear(sender, url, storage_type);
self.save_state()
}
},
StorageThreadMsg::Exit(sender) => {
// Nothing to do since we save localstorage set eagerly.
let _ = sender.send(());
break
}
break;
},
}
}
}
@ -96,49 +97,56 @@ impl StorageManager {
}
}
fn select_data(&self, storage_type: StorageType)
-> &HashMap<String, (usize, BTreeMap<String, String>)> {
fn select_data(
&self,
storage_type: StorageType,
) -> &HashMap<String, (usize, BTreeMap<String, String>)> {
match storage_type {
StorageType::Session => &self.session_data,
StorageType::Local => &self.local_data
StorageType::Local => &self.local_data,
}
}
fn select_data_mut(&mut self, storage_type: StorageType)
-> &mut HashMap<String, (usize, BTreeMap<String, String>)> {
fn select_data_mut(
&mut self,
storage_type: StorageType,
) -> &mut HashMap<String, (usize, BTreeMap<String, String>)> {
match storage_type {
StorageType::Session => &mut self.session_data,
StorageType::Local => &mut self.local_data
StorageType::Local => &mut self.local_data,
}
}
fn length(&self, sender: IpcSender<usize>, url: ServoUrl, storage_type: StorageType) {
let origin = self.origin_as_string(url);
let data = self.select_data(storage_type);
sender.send(data.get(&origin).map_or(0, |&(_, ref entry)| entry.len())).unwrap();
sender
.send(data.get(&origin).map_or(0, |&(_, ref entry)| entry.len()))
.unwrap();
}
fn key(&self,
sender: IpcSender<Option<String>>,
url: ServoUrl,
storage_type: StorageType,
index: u32) {
fn key(
&self,
sender: IpcSender<Option<String>>,
url: ServoUrl,
storage_type: StorageType,
index: u32,
) {
let origin = self.origin_as_string(url);
let data = self.select_data(storage_type);
let key = data.get(&origin)
.and_then(|&(_, ref entry)| entry.keys().nth(index as usize))
.cloned();
let key = data
.get(&origin)
.and_then(|&(_, ref entry)| entry.keys().nth(index as usize))
.cloned();
sender.send(key).unwrap();
}
fn keys(&self,
sender: IpcSender<Vec<String>>,
url: ServoUrl,
storage_type: StorageType) {
fn keys(&self, sender: IpcSender<Vec<String>>, url: ServoUrl, storage_type: StorageType) {
let origin = self.origin_as_string(url);
let data = self.select_data(storage_type);
let keys = data.get(&origin)
.map_or(vec![], |&(_, ref entry)| entry.keys().cloned().collect());
let keys = data
.get(&origin)
.map_or(vec![], |&(_, ref entry)| entry.keys().cloned().collect());
sender.send(keys).unwrap();
}
@ -147,12 +155,14 @@ impl StorageManager {
/// value with the same key name but with different value name
/// otherwise sends Err(()) to indicate that the operation would result in
/// exceeding the quota limit
fn set_item(&mut self,
sender: IpcSender<Result<(bool, Option<String>), ()>>,
url: ServoUrl,
storage_type: StorageType,
name: String,
value: String) {
fn set_item(
&mut self,
sender: IpcSender<Result<(bool, Option<String>), ()>>,
url: ServoUrl,
storage_type: StorageType,
name: String,
value: String,
) {
let origin = self.origin_as_string(url);
let (this_storage_size, other_storage_size) = {
@ -171,64 +181,82 @@ impl StorageManager {
data.insert(origin.clone(), (0, BTreeMap::new()));
}
let message = data.get_mut(&origin).map(|&mut (ref mut total, ref mut entry)| {
let mut new_total_size = this_storage_size + value.as_bytes().len();
if let Some(old_value) = entry.get(&name) {
new_total_size -= old_value.as_bytes().len();
} else {
new_total_size += name.as_bytes().len();
}
if (new_total_size + other_storage_size) > QUOTA_SIZE_LIMIT {
return Err(());
}
let message = entry.insert(name.clone(), value.clone()).map_or(
Ok((true, None)),
|old| if old == value {
Ok((false, None))
let message = data
.get_mut(&origin)
.map(|&mut (ref mut total, ref mut entry)| {
let mut new_total_size = this_storage_size + value.as_bytes().len();
if let Some(old_value) = entry.get(&name) {
new_total_size -= old_value.as_bytes().len();
} else {
Ok((true, Some(old)))
});
*total = new_total_size;
message
}).unwrap();
new_total_size += name.as_bytes().len();
}
if (new_total_size + other_storage_size) > QUOTA_SIZE_LIMIT {
return Err(());
}
let message =
entry
.insert(name.clone(), value.clone())
.map_or(Ok((true, None)), |old| {
if old == value {
Ok((false, None))
} else {
Ok((true, Some(old)))
}
});
*total = new_total_size;
message
})
.unwrap();
sender.send(message).unwrap();
}
fn request_item(&self,
sender: IpcSender<Option<String>>,
url: ServoUrl,
storage_type: StorageType,
name: String) {
fn request_item(
&self,
sender: IpcSender<Option<String>>,
url: ServoUrl,
storage_type: StorageType,
name: String,
) {
let origin = self.origin_as_string(url);
let data = self.select_data(storage_type);
sender.send(data.get(&origin)
sender
.send(
data.get(&origin)
.and_then(|&(_, ref entry)| entry.get(&name))
.map(String::clone)).unwrap();
.map(String::clone),
)
.unwrap();
}
/// Sends Some(old_value) in case there was a previous value with the key name, otherwise sends None
fn remove_item(&mut self,
sender: IpcSender<Option<String>>,
url: ServoUrl,
storage_type: StorageType,
name: String) {
fn remove_item(
&mut self,
sender: IpcSender<Option<String>>,
url: ServoUrl,
storage_type: StorageType,
name: String,
) {
let origin = self.origin_as_string(url);
let data = self.select_data_mut(storage_type);
let old_value = data.get_mut(&origin).and_then(|&mut (ref mut total, ref mut entry)| {
entry.remove(&name).and_then(|old| {
*total -= name.as_bytes().len() + old.as_bytes().len();
Some(old)
})
});
let old_value = data
.get_mut(&origin)
.and_then(|&mut (ref mut total, ref mut entry)| {
entry.remove(&name).and_then(|old| {
*total -= name.as_bytes().len() + old.as_bytes().len();
Some(old)
})
});
sender.send(old_value).unwrap();
}
fn clear(&mut self, sender: IpcSender<bool>, url: ServoUrl, storage_type: StorageType) {
let origin = self.origin_as_string(url);
let data = self.select_data_mut(storage_type);
sender.send(data.get_mut(&origin)
sender
.send(
data.get_mut(&origin)
.map_or(false, |&mut (ref mut total, ref mut entry)| {
if !entry.is_empty() {
entry.clear();
@ -236,7 +264,10 @@ impl StorageManager {
true
} else {
false
}})).unwrap();
}
}),
)
.unwrap();
}
fn origin_as_string(&self, url: ServoUrl) -> String {

View file

@ -9,22 +9,13 @@ use std::iter::Filter;
use std::str::Split;
use std::sync::MutexGuard;
const SUPPORTED_ALGORITHM: &'static [&'static str] = &[
"sha256",
"sha384",
"sha512",
];
const SUPPORTED_ALGORITHM: &'static [&'static str] = &["sha256", "sha384", "sha512"];
pub type StaticCharVec = &'static [char];
/// A "space character" according to:
///
/// <https://html.spec.whatwg.org/multipage/#space-character>
pub static HTML_SPACE_CHARACTERS: StaticCharVec = &[
'\u{0020}',
'\u{0009}',
'\u{000a}',
'\u{000c}',
'\u{000d}',
];
pub static HTML_SPACE_CHARACTERS: StaticCharVec =
&['\u{0020}', '\u{0009}', '\u{000a}', '\u{000c}', '\u{000d}'];
#[derive(Clone)]
pub struct SriEntry {
pub alg: String,
@ -79,9 +70,18 @@ pub fn parsed_metadata(integrity_metadata: &str) -> Vec<SriEntry> {
}
/// <https://w3c.github.io/webappsec-subresource-integrity/#getprioritizedhashfunction>
pub fn get_prioritized_hash_function(hash_func_left: &str, hash_func_right: &str) -> Option<String> {
let left_priority = SUPPORTED_ALGORITHM.iter().position(|s| s.to_owned() == hash_func_left).unwrap();
let right_priority = SUPPORTED_ALGORITHM.iter().position(|s| s.to_owned() == hash_func_right).unwrap();
pub fn get_prioritized_hash_function(
hash_func_left: &str,
hash_func_right: &str,
) -> Option<String> {
let left_priority = SUPPORTED_ALGORITHM
.iter()
.position(|s| s.to_owned() == hash_func_left)
.unwrap();
let right_priority = SUPPORTED_ALGORITHM
.iter()
.position(|s| s.to_owned() == hash_func_right)
.unwrap();
if left_priority == right_priority {
return None;
@ -91,7 +91,6 @@ pub fn get_prioritized_hash_function(hash_func_left: &str, hash_func_right: &str
} else {
Some(hash_func_right.to_owned())
}
}
/// <https://w3c.github.io/webappsec-subresource-integrity/#get-the-strongest-metadata>
@ -100,8 +99,8 @@ pub fn get_strongest_metadata(integrity_metadata_list: Vec<SriEntry>) -> Vec<Sri
let mut current_algorithm = result[0].alg.clone();
for integrity_metadata in &integrity_metadata_list[1..] {
let prioritized_hash = get_prioritized_hash_function(&integrity_metadata.alg,
&*current_algorithm);
let prioritized_hash =
get_prioritized_hash_function(&integrity_metadata.alg, &*current_algorithm);
if prioritized_hash.is_none() {
result.push(integrity_metadata.clone());
} else if let Some(algorithm) = prioritized_hash {
@ -116,9 +115,10 @@ pub fn get_strongest_metadata(integrity_metadata_list: Vec<SriEntry>) -> Vec<Sri
}
/// <https://w3c.github.io/webappsec-subresource-integrity/#apply-algorithm-to-response>
fn apply_algorithm_to_response(body: MutexGuard<ResponseBody>,
message_digest: MessageDigest)
-> String {
fn apply_algorithm_to_response(
body: MutexGuard<ResponseBody>,
message_digest: MessageDigest,
) -> String {
if let ResponseBody::Done(ref vec) = *body {
let response_digest = hash(message_digest, vec).unwrap(); //Now hash
base64::encode(&response_digest)
@ -171,8 +171,12 @@ pub fn is_response_integrity_valid(integrity_metadata: &str, response: &Response
false
}
pub fn split_html_space_chars<'a>(s: &'a str) ->
Filter<Split<'a, StaticCharVec>, fn(&&str) -> bool> {
fn not_empty(&split: &&str) -> bool { !split.is_empty() }
s.split(HTML_SPACE_CHARACTERS).filter(not_empty as fn(&&str) -> bool)
pub fn split_html_space_chars<'a>(
s: &'a str,
) -> Filter<Split<'a, StaticCharVec>, fn(&&str) -> bool> {
fn not_empty(&split: &&str) -> bool {
!split.is_empty()
}
s.split(HTML_SPACE_CHARACTERS)
.filter(not_empty as fn(&&str) -> bool)
}

View file

@ -118,11 +118,13 @@ fn test_cookie_secure_prefix() {
assert!(Cookie::new_wrapped(cookie, url, CookieSource::HTTP).is_none());
let url = &ServoUrl::parse("http://example.com").unwrap();
let cookie = cookie_rs::Cookie::parse("__Secure-SID=12345; Secure; Domain=example.com").unwrap();
let cookie =
cookie_rs::Cookie::parse("__Secure-SID=12345; Secure; Domain=example.com").unwrap();
assert!(Cookie::new_wrapped(cookie, url, CookieSource::HTTP).is_none());
let url = &ServoUrl::parse("https://example.com").unwrap();
let cookie = cookie_rs::Cookie::parse("__Secure-SID=12345; Secure; Domain=example.com").unwrap();
let cookie =
cookie_rs::Cookie::parse("__Secure-SID=12345; Secure; Domain=example.com").unwrap();
assert!(Cookie::new_wrapped(cookie, url, CookieSource::HTTP).is_some());
}
@ -157,7 +159,8 @@ fn test_cookie_host_prefix() {
assert!(Cookie::new_wrapped(cookie, url, CookieSource::HTTP).is_none());
let url = &ServoUrl::parse("https://example.com").unwrap();
let cookie = cookie_rs::Cookie::parse("__Host-SID=12345; Secure; Domain=example.com; Path=/").unwrap();
let cookie =
cookie_rs::Cookie::parse("__Host-SID=12345; Secure; Domain=example.com; Path=/").unwrap();
assert!(Cookie::new_wrapped(cookie, url, CookieSource::HTTP).is_none());
let url = &ServoUrl::parse("https://example.com").unwrap();
@ -193,13 +196,18 @@ fn test_sort_order() {
assert!(b.cookie.path().as_ref().unwrap().len() > a.cookie.path().as_ref().unwrap().len());
assert_eq!(CookieStorage::cookie_comparator(&a, &b), Ordering::Greater);
assert_eq!(CookieStorage::cookie_comparator(&b, &a), Ordering::Less);
assert_eq!(CookieStorage::cookie_comparator(&a, &a_prime), Ordering::Less);
assert_eq!(CookieStorage::cookie_comparator(&a_prime, &a), Ordering::Greater);
assert_eq!(
CookieStorage::cookie_comparator(&a, &a_prime),
Ordering::Less
);
assert_eq!(
CookieStorage::cookie_comparator(&a_prime, &a),
Ordering::Greater
);
assert_eq!(CookieStorage::cookie_comparator(&a, &a), Ordering::Equal);
}
fn add_cookie_to_storage(storage: &mut CookieStorage, url: &ServoUrl, cookie_str: &str)
{
fn add_cookie_to_storage(storage: &mut CookieStorage, url: &ServoUrl, cookie_str: &str) {
let source = CookieSource::HTTP;
let cookie = cookie_rs::Cookie::parse(cookie_str.to_owned()).unwrap();
let cookie = Cookie::new_wrapped(cookie, url, source).unwrap();
@ -225,21 +233,40 @@ fn test_insecure_cookies_cannot_evict_secure_cookie() {
let insecure_url = ServoUrl::parse("http://home.example.org:8888/cookie-parser?0001").unwrap();
add_cookie_to_storage(&mut storage, &insecure_url, "foo=value; Domain=home.example.org");
add_cookie_to_storage(&mut storage, &insecure_url, "foo2=value; Domain=.example.org");
add_cookie_to_storage(
&mut storage,
&insecure_url,
"foo=value; Domain=home.example.org",
);
add_cookie_to_storage(
&mut storage,
&insecure_url,
"foo2=value; Domain=.example.org",
);
add_cookie_to_storage(&mut storage, &insecure_url, "foo3=value; Path=/foo/bar");
add_cookie_to_storage(&mut storage, &insecure_url, "foo4=value; Path=/foo");
let source = CookieSource::HTTP;
assert_eq!(storage.cookies_for_url(&secure_url, source).unwrap(), "foo=bar; foo2=bar");
assert_eq!(
storage.cookies_for_url(&secure_url, source).unwrap(),
"foo=bar; foo2=bar"
);
let url = ServoUrl::parse("https://home.example.org:8888/foo/cookie-parser-result?0001").unwrap();
let url =
ServoUrl::parse("https://home.example.org:8888/foo/cookie-parser-result?0001").unwrap();
let source = CookieSource::HTTP;
assert_eq!(storage.cookies_for_url(&url, source).unwrap(), "foo3=bar; foo4=value; foo=bar; foo2=bar");
assert_eq!(
storage.cookies_for_url(&url, source).unwrap(),
"foo3=bar; foo4=value; foo=bar; foo2=bar"
);
let url = ServoUrl::parse("https://home.example.org:8888/foo/bar/cookie-parser-result?0001").unwrap();
let url =
ServoUrl::parse("https://home.example.org:8888/foo/bar/cookie-parser-result?0001").unwrap();
let source = CookieSource::HTTP;
assert_eq!(storage.cookies_for_url(&url, source).unwrap(), "foo4=bar; foo3=bar; foo4=value; foo=bar; foo2=bar");
assert_eq!(
storage.cookies_for_url(&url, source).unwrap(),
"foo4=bar; foo3=bar; foo4=value; foo=bar; foo2=bar"
);
}
#[test]
@ -267,14 +294,21 @@ fn test_secure_cookies_eviction() {
let source = CookieSource::HTTP;
assert_eq!(storage.cookies_for_url(&url, source).unwrap(), "foo2=value");
let url = ServoUrl::parse("https://home.example.org:8888/foo/cookie-parser-result?0001").unwrap();
let url =
ServoUrl::parse("https://home.example.org:8888/foo/cookie-parser-result?0001").unwrap();
let source = CookieSource::HTTP;
assert_eq!(storage.cookies_for_url(&url, source).unwrap(), "foo3=bar; foo4=value; foo2=value");
assert_eq!(
storage.cookies_for_url(&url, source).unwrap(),
"foo3=bar; foo4=value; foo2=value"
);
let url = ServoUrl::parse("https://home.example.org:8888/foo/bar/cookie-parser-result?0001").unwrap();
let url =
ServoUrl::parse("https://home.example.org:8888/foo/bar/cookie-parser-result?0001").unwrap();
let source = CookieSource::HTTP;
assert_eq!(storage.cookies_for_url(&url, source).unwrap(),
"foo4=bar; foo3=value; foo3=bar; foo4=value; foo2=value");
assert_eq!(
storage.cookies_for_url(&url, source).unwrap(),
"foo4=bar; foo3=value; foo3=bar; foo4=value; foo2=value"
);
}
#[test]
@ -302,21 +336,28 @@ fn test_secure_cookies_eviction_non_http_source() {
let source = CookieSource::HTTP;
assert_eq!(storage.cookies_for_url(&url, source).unwrap(), "foo2=value");
let url = ServoUrl::parse("https://home.example.org:8888/foo/cookie-parser-result?0001").unwrap();
let url =
ServoUrl::parse("https://home.example.org:8888/foo/cookie-parser-result?0001").unwrap();
let source = CookieSource::HTTP;
assert_eq!(storage.cookies_for_url(&url, source).unwrap(), "foo3=bar; foo4=value; foo2=value");
assert_eq!(
storage.cookies_for_url(&url, source).unwrap(),
"foo3=bar; foo4=value; foo2=value"
);
let url = ServoUrl::parse("https://home.example.org:8888/foo/bar/cookie-parser-result?0001").unwrap();
let url =
ServoUrl::parse("https://home.example.org:8888/foo/bar/cookie-parser-result?0001").unwrap();
let source = CookieSource::HTTP;
assert_eq!(storage.cookies_for_url(&url, source).unwrap(),
"foo4=bar; foo3=value; foo3=bar; foo4=value; foo2=value");
assert_eq!(
storage.cookies_for_url(&url, source).unwrap(),
"foo4=bar; foo3=value; foo3=bar; foo4=value; foo2=value"
);
}
fn add_retrieve_cookies(set_location: &str,
set_cookies: &[String],
final_location: &str)
-> String {
fn add_retrieve_cookies(
set_location: &str,
set_cookies: &[String],
final_location: &str,
) -> String {
let mut storage = CookieStorage::new(5);
let url = ServoUrl::parse(set_location).unwrap();
let source = CookieSource::HTTP;
@ -329,56 +370,75 @@ fn add_retrieve_cookies(set_location: &str,
// Get cookies for the test location
let url = ServoUrl::parse(final_location).unwrap();
storage.cookies_for_url(&url, source).unwrap_or("".to_string())
storage
.cookies_for_url(&url, source)
.unwrap_or("".to_string())
}
#[test]
fn test_cookie_eviction_expired() {
let mut vec = Vec::new();
for i in 1..6 {
let st = format!("extra{}=bar; Secure; expires=Sun, 18-Apr-2000 21:06:29 GMT",
i);
let st = format!(
"extra{}=bar; Secure; expires=Sun, 18-Apr-2000 21:06:29 GMT",
i
);
vec.push(st);
}
vec.push("foo=bar; Secure; expires=Sun, 18-Apr-2027 21:06:29 GMT".to_owned());
let r = add_retrieve_cookies("https://home.example.org:8888/cookie-parser?0001",
&vec, "https://home.example.org:8888/cookie-parser-result?0001");
let r = add_retrieve_cookies(
"https://home.example.org:8888/cookie-parser?0001",
&vec,
"https://home.example.org:8888/cookie-parser-result?0001",
);
assert_eq!(&r, "foo=bar");
}
#[test]
fn test_cookie_eviction_all_secure_one_nonsecure() {
let mut vec = Vec::new();
for i in 1..5 {
let st = format!("extra{}=bar; Secure; expires=Sun, 18-Apr-2026 21:06:29 GMT",
i);
let st = format!(
"extra{}=bar; Secure; expires=Sun, 18-Apr-2026 21:06:29 GMT",
i
);
vec.push(st);
}
vec.push("foo=bar; expires=Sun, 18-Apr-2026 21:06:29 GMT".to_owned());
vec.push("foo2=bar; Secure; expires=Sun, 18-Apr-2028 21:06:29 GMT".to_owned());
let r = add_retrieve_cookies("https://home.example.org:8888/cookie-parser?0001",
&vec, "https://home.example.org:8888/cookie-parser-result?0001");
assert_eq!(&r, "extra1=bar; extra2=bar; extra3=bar; extra4=bar; foo2=bar");
let r = add_retrieve_cookies(
"https://home.example.org:8888/cookie-parser?0001",
&vec,
"https://home.example.org:8888/cookie-parser-result?0001",
);
assert_eq!(
&r,
"extra1=bar; extra2=bar; extra3=bar; extra4=bar; foo2=bar"
);
}
#[test]
fn test_cookie_eviction_all_secure_new_nonsecure() {
let mut vec = Vec::new();
for i in 1..6 {
let st = format!("extra{}=bar; Secure; expires=Sun, 18-Apr-2026 21:06:29 GMT",
i);
let st = format!(
"extra{}=bar; Secure; expires=Sun, 18-Apr-2026 21:06:29 GMT",
i
);
vec.push(st);
}
vec.push("foo=bar; expires=Sun, 18-Apr-2077 21:06:29 GMT".to_owned());
let r = add_retrieve_cookies("https://home.example.org:8888/cookie-parser?0001",
&vec, "https://home.example.org:8888/cookie-parser-result?0001");
assert_eq!(&r, "extra1=bar; extra2=bar; extra3=bar; extra4=bar; extra5=bar");
let r = add_retrieve_cookies(
"https://home.example.org:8888/cookie-parser?0001",
&vec,
"https://home.example.org:8888/cookie-parser-result?0001",
);
assert_eq!(
&r,
"extra1=bar; extra2=bar; extra3=bar; extra4=bar; extra5=bar"
);
}
#[test]
fn test_cookie_eviction_all_nonsecure_new_secure() {
let mut vec = Vec::new();
@ -387,12 +447,17 @@ fn test_cookie_eviction_all_nonsecure_new_secure() {
vec.push(st);
}
vec.push("foo=bar; Secure; expires=Sun, 18-Apr-2077 21:06:29 GMT".to_owned());
let r = add_retrieve_cookies("https://home.example.org:8888/cookie-parser?0001",
&vec, "https://home.example.org:8888/cookie-parser-result?0001");
assert_eq!(&r, "extra2=bar; extra3=bar; extra4=bar; extra5=bar; foo=bar");
let r = add_retrieve_cookies(
"https://home.example.org:8888/cookie-parser?0001",
&vec,
"https://home.example.org:8888/cookie-parser-result?0001",
);
assert_eq!(
&r,
"extra2=bar; extra3=bar; extra4=bar; extra5=bar; foo=bar"
);
}
#[test]
fn test_cookie_eviction_all_nonsecure_new_nonsecure() {
let mut vec = Vec::new();
@ -401,7 +466,13 @@ fn test_cookie_eviction_all_nonsecure_new_nonsecure() {
vec.push(st);
}
vec.push("foo=bar; expires=Sun, 18-Apr-2077 21:06:29 GMT".to_owned());
let r = add_retrieve_cookies("https://home.example.org:8888/cookie-parser?0001",
&vec, "https://home.example.org:8888/cookie-parser-result?0001");
assert_eq!(&r, "extra2=bar; extra3=bar; extra4=bar; extra5=bar; foo=bar");
let r = add_retrieve_cookies(
"https://home.example.org:8888/cookie-parser?0001",
&vec,
"https://home.example.org:8888/cookie-parser-result?0001",
);
assert_eq!(
&r,
"extra2=bar; extra3=bar; extra4=bar; extra5=bar; foo=bar"
);
}

File diff suppressed because it is too large Load diff

View file

@ -14,10 +14,12 @@ use servo_url::ServoUrl;
use std::ops::Deref;
#[cfg(test)]
fn assert_parse(url: &'static str,
content_type: Option<ContentType>,
charset: Option<&str>,
data: Option<&[u8]>) {
fn assert_parse(
url: &'static str,
content_type: Option<ContentType>,
charset: Option<&str>,
data: Option<&[u8]>,
) {
let url = ServoUrl::parse(url).unwrap();
let origin = Origin::Origin(url.origin());
let mut request = Request::new(url, Some(origin), None);
@ -33,7 +35,10 @@ fn assert_parse(url: &'static str,
assert_eq!(header_content_type, content_type);
let metadata = match response.metadata() {
Ok(FetchMetadata::Filtered { filtered: FilteredMetadata::Basic(m), .. }) => m,
Ok(FetchMetadata::Filtered {
filtered: FilteredMetadata::Basic(m),
..
}) => m,
result => panic!(result),
};
assert_eq!(metadata.content_type.map(Serde::into_inner), content_type);
@ -49,7 +54,12 @@ fn assert_parse(url: &'static str,
},
None => {
assert!(response.is_network_error());
assert_eq!(response.metadata().err(), Some(NetworkError::Internal("Decoding data URL failed".to_owned())));
assert_eq!(
response.metadata().err(),
Some(NetworkError::Internal(
"Decoding data URL failed".to_owned()
))
);
},
}
}
@ -63,9 +73,12 @@ fn empty_invalid() {
fn plain() {
assert_parse(
"data:,hello%20world",
Some(ContentType::from("text/plain; charset=US-ASCII".parse::<Mime>().unwrap())),
Some(ContentType::from(
"text/plain; charset=US-ASCII".parse::<Mime>().unwrap(),
)),
Some("us-ascii"),
Some(b"hello world"));
Some(b"hello world"),
);
}
#[test]
@ -74,7 +87,8 @@ fn plain_ct() {
"data:text/plain,hello",
Some(ContentType::from(mime::TEXT_PLAIN)),
None,
Some(b"hello"));
Some(b"hello"),
);
}
#[test]
@ -83,16 +97,20 @@ fn plain_html() {
"data:text/html,<p>Servo</p>",
Some(ContentType::from(mime::TEXT_HTML)),
None,
Some(b"<p>Servo</p>"));
Some(b"<p>Servo</p>"),
);
}
#[test]
fn plain_charset() {
assert_parse(
"data:text/plain;charset=latin1,hello",
Some(ContentType::from("text/plain; charset=latin1".parse::<Mime>().unwrap())),
Some(ContentType::from(
"text/plain; charset=latin1".parse::<Mime>().unwrap(),
)),
Some("latin1"),
Some(b"hello"));
Some(b"hello"),
);
}
#[test]
@ -101,16 +119,20 @@ fn plain_only_charset() {
"data:;charset=utf-8,hello",
Some(ContentType::from(mime::TEXT_PLAIN_UTF_8)),
Some("utf-8"),
Some(b"hello"));
Some(b"hello"),
);
}
#[test]
fn base64() {
assert_parse(
"data:;base64,C62+7w==",
Some(ContentType::from("text/plain; charset=US-ASCII".parse::<Mime>().unwrap())),
Some(ContentType::from(
"text/plain; charset=US-ASCII".parse::<Mime>().unwrap(),
)),
Some("us-ascii"),
Some(&[0x0B, 0xAD, 0xBE, 0xEF]));
Some(&[0x0B, 0xAD, 0xBE, 0xEF]),
);
}
#[test]
@ -119,14 +141,20 @@ fn base64_ct() {
"data:application/octet-stream;base64,C62+7w==",
Some(ContentType::from(mime::APPLICATION_OCTET_STREAM)),
None,
Some(&[0x0B, 0xAD, 0xBE, 0xEF]));
Some(&[0x0B, 0xAD, 0xBE, 0xEF]),
);
}
#[test]
fn base64_charset() {
assert_parse(
"data:text/plain;charset=koi8-r;base64,8PLl9+XkIO3l5Pfl5A==",
Some(ContentType::from("text/plain; charset=koi8-r".parse::<Mime>().unwrap())),
Some(ContentType::from(
"text/plain; charset=koi8-r".parse::<Mime>().unwrap(),
)),
Some("koi8-r"),
Some(&[0xF0, 0xF2, 0xE5, 0xF7, 0xE5, 0xE4, 0x20, 0xED, 0xE5, 0xE4, 0xF7, 0xE5, 0xE4]));
Some(&[
0xF0, 0xF2, 0xE5, 0xF7, 0xE5, 0xE4, 0x20, 0xED, 0xE5, 0xE4, 0xF7, 0xE5, 0xE4,
]),
);
}

View file

@ -70,7 +70,10 @@ fn test_fetch_on_bad_port_is_network_error() {
let fetch_response = fetch(&mut request, None);
assert!(fetch_response.is_network_error());
let fetch_error = fetch_response.get_network_error().unwrap();
assert_eq!(fetch_error, &NetworkError::Internal("Request attempted on bad port".into()))
assert_eq!(
fetch_error,
&NetworkError::Internal("Request attempted on bad port".into())
)
}
#[test]
@ -94,7 +97,7 @@ fn test_fetch_response_body_matches_const_message() {
ResponseBody::Done(ref body) => {
assert_eq!(&**body, MESSAGE);
},
_ => panic!()
_ => panic!(),
};
}
@ -106,7 +109,10 @@ fn test_fetch_aboutblank() {
request.referrer = Referrer::NoReferrer;
let fetch_response = fetch(&mut request, None);
assert!(!fetch_response.is_network_error());
assert_eq!(*fetch_response.body.lock().unwrap(), ResponseBody::Done(vec![]));
assert_eq!(
*fetch_response.body.lock().unwrap(),
ResponseBody::Done(vec![])
);
}
#[test]
@ -127,11 +133,12 @@ fn test_fetch_blob() {
let origin = ServoUrl::parse("http://www.example.org/").unwrap();
let (sender, receiver) = ipc::channel().unwrap();
context.filemanager.promote_memory(blob_buf, true, sender, "http://www.example.org".into());
context
.filemanager
.promote_memory(blob_buf, true, sender, "http://www.example.org".into());
let id = receiver.recv().unwrap().unwrap();
let url = ServoUrl::parse(&format!("blob:{}{}", origin.as_str(), id.simple())).unwrap();
let mut request = Request::new(url, Some(Origin::Origin(origin.origin())), None);
let fetch_response = fetch_with_context(&mut request, &context);
@ -139,19 +146,27 @@ fn test_fetch_blob() {
assert_eq!(fetch_response.headers.len(), 2);
let content_type: Mime = fetch_response.headers.typed_get::<ContentType>().unwrap().into();
let content_type: Mime = fetch_response
.headers
.typed_get::<ContentType>()
.unwrap()
.into();
assert_eq!(content_type, mime::TEXT_PLAIN);
let content_length: ContentLength = fetch_response.headers.typed_get().unwrap();
assert_eq!(content_length.0, bytes.len() as u64);
assert_eq!(*fetch_response.body.lock().unwrap(),
ResponseBody::Done(bytes.to_vec()));
assert_eq!(
*fetch_response.body.lock().unwrap(),
ResponseBody::Done(bytes.to_vec())
);
}
#[test]
fn test_fetch_file() {
let path = Path::new("../../resources/servo.css").canonicalize().unwrap();
let path = Path::new("../../resources/servo.css")
.canonicalize()
.unwrap();
let url = ServoUrl::from_file_path(path.clone()).unwrap();
let origin = Origin::Origin(url.origin());
let mut request = Request::new(url, Some(origin), None);
@ -159,7 +174,11 @@ fn test_fetch_file() {
let fetch_response = fetch(&mut request, None);
assert!(!fetch_response.is_network_error());
assert_eq!(fetch_response.headers.len(), 1);
let content_type: Mime = fetch_response.headers.typed_get::<ContentType>().unwrap().into();
let content_type: Mime = fetch_response
.headers
.typed_get::<ContentType>()
.unwrap()
.into();
assert_eq!(content_type, mime::TEXT_CSS);
let resp_body = fetch_response.body.lock().unwrap();
@ -171,7 +190,7 @@ fn test_fetch_file() {
ResponseBody::Done(ref val) => {
assert_eq!(val, &bytes);
},
_ => panic!()
_ => panic!(),
}
}
@ -200,15 +219,34 @@ fn test_cors_preflight_fetch() {
static ACK: &'static [u8] = b"ACK";
let state = Arc::new(AtomicUsize::new(0));
let handler = move |request: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
if request.method() == Method::OPTIONS && state.clone().fetch_add(1, Ordering::SeqCst) == 0 {
assert!(request.headers().contains_key(header::ACCESS_CONTROL_REQUEST_METHOD));
assert!(!request.headers().contains_key(header::ACCESS_CONTROL_REQUEST_HEADERS));
assert!(!request.headers().get(header::REFERER).unwrap().to_str().unwrap().contains("a.html"));
response.headers_mut().typed_insert(AccessControlAllowOrigin::ANY);
response.headers_mut().typed_insert(AccessControlAllowCredentials);
response.headers_mut().typed_insert(AccessControlAllowMethods::from_iter(vec![Method::GET]));
if request.method() == Method::OPTIONS && state.clone().fetch_add(1, Ordering::SeqCst) == 0
{
assert!(request
.headers()
.contains_key(header::ACCESS_CONTROL_REQUEST_METHOD));
assert!(!request
.headers()
.contains_key(header::ACCESS_CONTROL_REQUEST_HEADERS));
assert!(!request
.headers()
.get(header::REFERER)
.unwrap()
.to_str()
.unwrap()
.contains("a.html"));
response
.headers_mut()
.typed_insert(AccessControlAllowOrigin::ANY);
response
.headers_mut()
.typed_insert(AccessControlAllowCredentials);
response
.headers_mut()
.typed_insert(AccessControlAllowMethods::from_iter(vec![Method::GET]));
} else {
response.headers_mut().typed_insert(AccessControlAllowOrigin::ANY);
response
.headers_mut()
.typed_insert(AccessControlAllowOrigin::ANY);
*response.body_mut() = ACK.to_vec().into();
}
};
@ -228,7 +266,7 @@ fn test_cors_preflight_fetch() {
assert!(!fetch_response.is_network_error());
match *fetch_response.body.lock().unwrap() {
ResponseBody::Done(ref body) => assert_eq!(&**body, ACK),
_ => panic!()
_ => panic!(),
};
}
@ -239,15 +277,30 @@ fn test_cors_preflight_cache_fetch() {
let counter = state.clone();
let mut cache = CorsCache::new();
let handler = move |request: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
if request.method() == Method::OPTIONS && state.clone().fetch_add(1, Ordering::SeqCst) == 0 {
assert!(request.headers().contains_key(header::ACCESS_CONTROL_REQUEST_METHOD));
assert!(!request.headers().contains_key(header::ACCESS_CONTROL_REQUEST_HEADERS));
response.headers_mut().typed_insert(AccessControlAllowOrigin::ANY);
response.headers_mut().typed_insert(AccessControlAllowCredentials);
response.headers_mut().typed_insert(AccessControlAllowMethods::from_iter(vec![Method::GET]));
response.headers_mut().typed_insert(AccessControlMaxAge::from(Duration::new(6000, 0)));
if request.method() == Method::OPTIONS && state.clone().fetch_add(1, Ordering::SeqCst) == 0
{
assert!(request
.headers()
.contains_key(header::ACCESS_CONTROL_REQUEST_METHOD));
assert!(!request
.headers()
.contains_key(header::ACCESS_CONTROL_REQUEST_HEADERS));
response
.headers_mut()
.typed_insert(AccessControlAllowOrigin::ANY);
response
.headers_mut()
.typed_insert(AccessControlAllowCredentials);
response
.headers_mut()
.typed_insert(AccessControlAllowMethods::from_iter(vec![Method::GET]));
response
.headers_mut()
.typed_insert(AccessControlMaxAge::from(Duration::new(6000, 0)));
} else {
response.headers_mut().typed_insert(AccessControlAllowOrigin::ANY);
response
.headers_mut()
.typed_insert(AccessControlAllowOrigin::ANY);
*response.body_mut() = ACK.to_vec().into();
}
};
@ -276,11 +329,11 @@ fn test_cors_preflight_cache_fetch() {
match *fetch_response0.body.lock().unwrap() {
ResponseBody::Done(ref body) => assert_eq!(&**body, ACK),
_ => panic!()
_ => panic!(),
};
match *fetch_response1.body.lock().unwrap() {
ResponseBody::Done(ref body) => assert_eq!(&**body, ACK),
_ => panic!()
_ => panic!(),
};
}
@ -289,14 +342,27 @@ fn test_cors_preflight_fetch_network_error() {
static ACK: &'static [u8] = b"ACK";
let state = Arc::new(AtomicUsize::new(0));
let handler = move |request: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
if request.method() == Method::OPTIONS && state.clone().fetch_add(1, Ordering::SeqCst) == 0 {
assert!(request.headers().contains_key(header::ACCESS_CONTROL_REQUEST_METHOD));
assert!(!request.headers().contains_key(header::ACCESS_CONTROL_REQUEST_HEADERS));
response.headers_mut().typed_insert(AccessControlAllowOrigin::ANY);
response.headers_mut().typed_insert(AccessControlAllowCredentials);
response.headers_mut().typed_insert(AccessControlAllowMethods::from_iter(vec![Method::GET]));
if request.method() == Method::OPTIONS && state.clone().fetch_add(1, Ordering::SeqCst) == 0
{
assert!(request
.headers()
.contains_key(header::ACCESS_CONTROL_REQUEST_METHOD));
assert!(!request
.headers()
.contains_key(header::ACCESS_CONTROL_REQUEST_HEADERS));
response
.headers_mut()
.typed_insert(AccessControlAllowOrigin::ANY);
response
.headers_mut()
.typed_insert(AccessControlAllowCredentials);
response
.headers_mut()
.typed_insert(AccessControlAllowMethods::from_iter(vec![Method::GET]));
} else {
response.headers_mut().typed_insert(AccessControlAllowOrigin::ANY);
response
.headers_mut()
.typed_insert(AccessControlAllowOrigin::ANY);
*response.body_mut() = ACK.to_vec().into();
}
};
@ -318,11 +384,13 @@ fn test_cors_preflight_fetch_network_error() {
fn test_fetch_response_is_basic_filtered() {
static MESSAGE: &'static [u8] = b"";
let handler = move |_: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
response.headers_mut().insert(header::SET_COOKIE, HeaderValue::from_static(""));
response
.headers_mut()
.insert(header::SET_COOKIE, HeaderValue::from_static(""));
// this header is obsoleted, so hyper doesn't implement it, but it's still covered by the spec
response.headers_mut().insert(
HeaderName::from_static("set-cookie2"),
HeaderValue::from_bytes(&vec![]).unwrap()
HeaderValue::from_bytes(&vec![]).unwrap(),
);
*response.body_mut() = MESSAGE.to_vec().into();
@ -340,7 +408,9 @@ fn test_fetch_response_is_basic_filtered() {
let headers = fetch_response.headers;
assert!(!headers.contains_key(header::SET_COOKIE));
assert!(headers.get(HeaderName::from_static("set-cookie2")).is_none());
assert!(headers
.get(HeaderName::from_static("set-cookie2"))
.is_none());
}
#[test]
@ -349,28 +419,41 @@ fn test_fetch_response_is_cors_filtered() {
let handler = move |_: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
// this is mandatory for the Cors Check to pass
// TODO test using different url encodings with this value ie. punycode
response.headers_mut().typed_insert(AccessControlAllowOrigin::ANY);
response
.headers_mut()
.typed_insert(AccessControlAllowOrigin::ANY);
// these are the headers that should be kept after filtering
response.headers_mut().typed_insert(CacheControl::new());
response.headers_mut().insert(header::CONTENT_LANGUAGE, HeaderValue::from_bytes(&vec![]).unwrap());
response.headers_mut().typed_insert(ContentType::from(mime::TEXT_HTML));
response.headers_mut().typed_insert(Expires::from(SystemTime::now() + Duration::new(86400, 0)));
response.headers_mut().typed_insert(LastModified::from(SystemTime::now()));
response.headers_mut().insert(
header::CONTENT_LANGUAGE,
HeaderValue::from_bytes(&vec![]).unwrap(),
);
response
.headers_mut()
.typed_insert(ContentType::from(mime::TEXT_HTML));
response
.headers_mut()
.typed_insert(Expires::from(SystemTime::now() + Duration::new(86400, 0)));
response
.headers_mut()
.typed_insert(LastModified::from(SystemTime::now()));
response.headers_mut().typed_insert(Pragma::no_cache());
// these headers should not be kept after filtering, even though they are given a pass
response.headers_mut().insert(header::SET_COOKIE, HeaderValue::from_static(""));
response
.headers_mut()
.insert(header::SET_COOKIE, HeaderValue::from_static(""));
response.headers_mut().insert(
HeaderName::from_static("set-cookie2"),
HeaderValue::from_bytes(&vec![]).unwrap()
HeaderValue::from_bytes(&vec![]).unwrap(),
);
response.headers_mut().typed_insert(
AccessControlAllowHeaders::from_iter(vec![
response
.headers_mut()
.typed_insert(AccessControlAllowHeaders::from_iter(vec![
HeaderName::from_static("set-cookie"),
HeaderName::from_static("set-cookie2")
])
);
HeaderName::from_static("set-cookie2"),
]));
*response.body_mut() = MESSAGE.to_vec().into();
};
@ -397,7 +480,9 @@ fn test_fetch_response_is_cors_filtered() {
assert!(!headers.contains_key(header::ACCESS_CONTROL_ALLOW_ORIGIN));
assert!(!headers.contains_key(header::SET_COOKIE));
assert!(headers.get(HeaderName::from_static("set-cookie2")).is_none());
assert!(headers
.get(HeaderName::from_static("set-cookie2"))
.is_none());
}
#[test]
@ -424,12 +509,12 @@ fn test_fetch_response_is_opaque_filtered() {
assert!(fetch_response.status.is_none());
assert_eq!(fetch_response.headers, HeaderMap::new());
match *fetch_response.body.lock().unwrap() {
ResponseBody::Empty => { },
_ => panic!()
ResponseBody::Empty => {},
_ => panic!(),
}
match fetch_response.cache_state {
CacheState::None => { },
_ => panic!()
CacheState::None => {},
_ => panic!(),
}
}
@ -437,13 +522,21 @@ fn test_fetch_response_is_opaque_filtered() {
fn test_fetch_response_is_opaque_redirect_filtered() {
static MESSAGE: &'static [u8] = b"";
let handler = move |request: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
let redirects = request.uri().path().split("/").collect::<String>().parse::<u32>().unwrap_or(0);
let redirects = request
.uri()
.path()
.split("/")
.collect::<String>()
.parse::<u32>()
.unwrap_or(0);
if redirects == 1 {
*response.body_mut() = MESSAGE.to_vec().into();
} else {
*response.status_mut() = StatusCode::FOUND;
response.headers_mut().insert(header::LOCATION, HeaderValue::from_static("1"));
response
.headers_mut()
.insert(header::LOCATION, HeaderValue::from_static("1"));
}
};
@ -463,12 +556,12 @@ fn test_fetch_response_is_opaque_redirect_filtered() {
assert!(fetch_response.status.is_none());
assert_eq!(fetch_response.headers, HeaderMap::new());
match *fetch_response.body.lock().unwrap() {
ResponseBody::Empty => { },
_ => panic!()
ResponseBody::Empty => {},
_ => panic!(),
}
match fetch_response.cache_state {
CacheState::None => { },
_ => panic!()
CacheState::None => {},
_ => panic!(),
}
}
@ -516,12 +609,19 @@ fn test_fetch_with_hsts() {
*response.body_mut() = MESSAGE.to_vec().into();
};
let cert_path = Path::new("../../resources/self_signed_certificate_for_testing.crt").canonicalize().unwrap();
let key_path = Path::new("../../resources/privatekey_for_testing.key").canonicalize().unwrap();
let cert_path = Path::new("../../resources/self_signed_certificate_for_testing.crt")
.canonicalize()
.unwrap();
let key_path = Path::new("../../resources/privatekey_for_testing.key")
.canonicalize()
.unwrap();
let (server, url) = make_ssl_server(handler, cert_path.clone(), key_path.clone());
let mut ca_content = String::new();
File::open(cert_path).unwrap().read_to_string(&mut ca_content).unwrap();
File::open(cert_path)
.unwrap()
.read_to_string(&mut ca_content)
.unwrap();
let ssl_client = create_ssl_connector_builder(&ca_content);
let context = FetchContext {
@ -534,8 +634,9 @@ fn test_fetch_with_hsts() {
{
let mut list = context.state.hsts_list.write().unwrap();
list.push(HstsEntry::new("localhost".to_owned(), IncludeSubdomains::NotIncluded, None)
.unwrap());
list.push(
HstsEntry::new("localhost".to_owned(), IncludeSubdomains::NotIncluded, None).unwrap(),
);
}
let origin = Origin::Origin(url.origin());
let mut request = Request::new(url, Some(origin), None);
@ -544,8 +645,10 @@ fn test_fetch_with_hsts() {
request.local_urls_only = false;
let response = fetch_with_context(&mut request, &context);
server.close();
assert_eq!(response.internal_response.unwrap().url().unwrap().scheme(),
"https");
assert_eq!(
response.internal_response.unwrap().url().unwrap().scheme(),
"https"
);
}
#[test]
@ -562,7 +665,7 @@ fn test_fetch_with_sri_network_error() {
// To calulate hash use :
// echo -n "alert('Hello, Network Error');" | openssl dgst -sha384 -binary | openssl base64 -A
request.integrity_metadata =
"sha384-H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H16dQZngK7T62em8MUt1FLm52t+eX6xO".to_owned();
"sha384-H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H16dQZngK7T62em8MUt1FLm52t+eX6xO".to_owned();
// Set the flag.
request.local_urls_only = false;
@ -586,7 +689,7 @@ fn test_fetch_with_sri_sucess() {
// To calulate hash use :
// echo -n "alert('Hello, Network Error');" | openssl dgst -sha384 -binary | openssl base64 -A
request.integrity_metadata =
"sha384-H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H16dQZngK7T62em8MUt1FLm52t+eX6xO".to_owned();
"sha384-H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H16dQZngK7T62em8MUt1FLm52t+eX6xO".to_owned();
// Set the flag.
request.local_urls_only = false;
@ -600,9 +703,7 @@ fn test_fetch_with_sri_sucess() {
#[test]
fn test_fetch_blocked_nosniff() {
#[inline]
fn test_nosniff_request(destination: Destination,
mime: Mime,
should_error: bool) {
fn test_nosniff_request(destination: Destination, mime: Mime, should_error: bool) {
const MESSAGE: &'static [u8] = b"";
const HEADER: &'static str = "x-content-type-options";
const VALUE: &'static [u8] = b"nosniff";
@ -612,7 +713,10 @@ fn test_fetch_blocked_nosniff() {
response.headers_mut().typed_insert(mime_header);
assert!(response.headers().contains_key(header::CONTENT_TYPE));
// Add the nosniff header
response.headers_mut().insert(HeaderName::from_static(HEADER), HeaderValue::from_bytes(VALUE).unwrap());
response.headers_mut().insert(
HeaderName::from_static(HEADER),
HeaderValue::from_bytes(VALUE).unwrap(),
);
*response.body_mut() = MESSAGE.to_vec().into();
};
@ -631,7 +735,7 @@ fn test_fetch_blocked_nosniff() {
let tests = vec![
(Destination::Script, mime::TEXT_JAVASCRIPT, false),
(Destination::Script, mime::TEXT_CSS, true),
(Destination::Style, mime::TEXT_CSS, false),
(Destination::Style, mime::TEXT_CSS, false),
];
for test in tests {
@ -642,14 +746,22 @@ fn test_fetch_blocked_nosniff() {
fn setup_server_and_fetch(message: &'static [u8], redirect_cap: u32) -> Response {
let handler = move |request: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
let redirects = request.uri().path().split("/").collect::<String>().parse::<u32>().unwrap_or(0);
let redirects = request
.uri()
.path()
.split("/")
.collect::<String>()
.parse::<u32>()
.unwrap_or(0);
if redirects >= redirect_cap {
*response.body_mut() = message.to_vec().into();
} else {
*response.status_mut() = StatusCode::FOUND;
let url = format!("{redirects}", redirects = redirects + 1);
response.headers_mut().insert(header::LOCATION, HeaderValue::from_str(&url).unwrap());
response
.headers_mut()
.insert(header::LOCATION, HeaderValue::from_str(&url).unwrap());
}
};
@ -678,7 +790,7 @@ fn test_fetch_redirect_count_ceiling() {
ResponseBody::Done(ref body) => {
assert_eq!(&**body, MESSAGE);
},
_ => panic!()
_ => panic!(),
};
}
@ -694,31 +806,43 @@ fn test_fetch_redirect_count_failure() {
match *fetch_response.body.lock().unwrap() {
ResponseBody::Done(_) | ResponseBody::Receiving(_) => panic!(),
_ => { }
_ => {},
};
}
fn test_fetch_redirect_updates_method_runner(tx: Sender<bool>, status_code: StatusCode, method: Method) {
fn test_fetch_redirect_updates_method_runner(
tx: Sender<bool>,
status_code: StatusCode,
method: Method,
) {
let handler_method = method.clone();
let handler_tx = Arc::new(Mutex::new(tx));
let handler = move |request: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
let redirects = request.uri().path().split("/").collect::<String>().parse::<u32>().unwrap_or(0);
let redirects = request
.uri()
.path()
.split("/")
.collect::<String>()
.parse::<u32>()
.unwrap_or(0);
let mut test_pass = true;
if redirects == 0 {
*response.status_mut() = StatusCode::TEMPORARY_REDIRECT;
response.headers_mut().insert(header::LOCATION, HeaderValue::from_static("1"));
response
.headers_mut()
.insert(header::LOCATION, HeaderValue::from_static("1"));
} else if redirects == 1 {
// this makes sure that the request method does't change from the wrong status code
if handler_method != Method::GET && request.method() == Method::GET {
test_pass = false;
}
*response.status_mut() = status_code;
response.headers_mut().insert(header::LOCATION, HeaderValue::from_static("2"));
response
.headers_mut()
.insert(header::LOCATION, HeaderValue::from_static("2"));
} else if request.method() != Method::GET {
test_pass = false;
}
@ -727,7 +851,6 @@ fn test_fetch_redirect_updates_method_runner(tx: Sender<bool>, status_code: Stat
if redirects > 0 {
handler_tx.lock().unwrap().send(test_pass).unwrap();
}
};
let (server, url) = make_server(handler);
@ -745,7 +868,11 @@ fn test_fetch_redirect_updates_method_runner(tx: Sender<bool>, status_code: Stat
fn test_fetch_redirect_updates_method() {
let (tx, rx) = channel();
test_fetch_redirect_updates_method_runner(tx.clone(), StatusCode::MOVED_PERMANENTLY, Method::POST);
test_fetch_redirect_updates_method_runner(
tx.clone(),
StatusCode::MOVED_PERMANENTLY,
Method::POST,
);
assert_eq!(rx.recv().unwrap(), true);
assert_eq!(rx.recv().unwrap(), true);
// make sure the test doesn't send more data than expected
@ -763,7 +890,11 @@ fn test_fetch_redirect_updates_method() {
let extension = Method::from_bytes(b"FOO").unwrap();
test_fetch_redirect_updates_method_runner(tx.clone(), StatusCode::MOVED_PERMANENTLY, extension.clone());
test_fetch_redirect_updates_method_runner(
tx.clone(),
StatusCode::MOVED_PERMANENTLY,
extension.clone(),
);
assert_eq!(rx.recv().unwrap(), true);
// for MovedPermanently and Found, Method should only be changed if it was Post
assert_eq!(rx.recv().unwrap(), false);
@ -785,9 +916,9 @@ fn response_is_done(response: &Response) -> bool {
let response_complete = match response.response_type {
ResponseType::Default | ResponseType::Basic | ResponseType::Cors => {
(*response.body.lock().unwrap()).is_done()
}
},
// if the internal response cannot have a body, it shouldn't block the "done" state
ResponseType::Opaque | ResponseType::OpaqueRedirect | ResponseType::Error(..) => true
ResponseType::Opaque | ResponseType::OpaqueRedirect | ResponseType::Error(..) => true,
};
let internal_complete = if let Some(ref res) = response.internal_response {
@ -842,13 +973,21 @@ fn test_opaque_filtered_fetch_async_returns_complete_response() {
fn test_opaque_redirect_filtered_fetch_async_returns_complete_response() {
static MESSAGE: &'static [u8] = b"";
let handler = move |request: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
let redirects = request.uri().path().split("/").collect::<String>().parse::<u32>().unwrap_or(0);
let redirects = request
.uri()
.path()
.split("/")
.collect::<String>()
.parse::<u32>()
.unwrap_or(0);
if redirects == 1 {
*response.body_mut() = MESSAGE.to_vec().into();
} else {
*response.status_mut() = StatusCode::FOUND;
response.headers_mut().insert(header::LOCATION, HeaderValue::from_static("1"));
response
.headers_mut()
.insert(header::LOCATION, HeaderValue::from_static("1"));
}
};
@ -892,13 +1031,22 @@ fn test_fetch_with_devtools() {
//Creating default headers for request
let mut headers = HeaderMap::new();
headers.insert(header::ACCEPT_ENCODING, HeaderValue::from_static("gzip, deflate, br"));
headers.typed_insert(
Host::from(format!("{}:{}", url.host_str().unwrap(), url.port().unwrap()).parse::<Authority>().unwrap()));
headers.insert(
header::ACCEPT_ENCODING,
HeaderValue::from_static("gzip, deflate, br"),
);
headers.typed_insert(Host::from(
format!("{}:{}", url.host_str().unwrap(), url.port().unwrap())
.parse::<Authority>()
.unwrap(),
));
headers.insert(header::ACCEPT, HeaderValue::from_static("*/*"));
headers.insert(header::ACCEPT_LANGUAGE, HeaderValue::from_static("en-US, en; q=0.5"));
headers.insert(
header::ACCEPT_LANGUAGE,
HeaderValue::from_static("en-US, en; q=0.5"),
);
headers.typed_insert::<UserAgent>(DEFAULT_USER_AGENT.parse().unwrap());
@ -918,7 +1066,11 @@ fn test_fetch_with_devtools() {
let content = "Yay!";
let mut response_headers = HeaderMap::new();
response_headers.typed_insert(ContentLength(content.len() as u64));
devhttpresponse.headers.as_mut().unwrap().remove(header::DATE);
devhttpresponse
.headers
.as_mut()
.unwrap()
.remove(header::DATE);
let httpresponse = DevtoolsHttpResponse {
headers: Some(response_headers),

View file

@ -16,14 +16,18 @@ use std::path::PathBuf;
#[test]
fn test_filemanager() {
let filemanager = FileManager::new(create_embedder_proxy());
PREFS.set("dom.testing.htmlinputelement.select_files.enabled", PrefValue::Boolean(true));
PREFS.set(
"dom.testing.htmlinputelement.select_files.enabled",
PrefValue::Boolean(true),
);
// Try to open a dummy file "components/net/tests/test.jpeg" in tree
let mut handler = File::open("tests/test.jpeg").expect("test.jpeg is stolen");
let mut test_file_content = vec![];
handler.read_to_end(&mut test_file_content)
.expect("Read components/net/tests/test.jpeg error");
handler
.read_to_end(&mut test_file_content)
.expect("Read components/net/tests/test.jpeg error");
let patterns = vec![FilterPattern(".txt".to_string())];
let origin = "test.com".to_string();
@ -31,10 +35,16 @@ fn test_filemanager() {
{
// Try to select a dummy file "components/net/tests/test.jpeg"
let (tx, rx) = ipc::channel().unwrap();
filemanager.handle(FileManagerThreadMsg::SelectFile(patterns.clone(), tx, origin.clone(),
Some("tests/test.jpeg".to_string())));
let selected = rx.recv().expect("Broken channel")
.expect("The file manager failed to find test.jpeg");
filemanager.handle(FileManagerThreadMsg::SelectFile(
patterns.clone(),
tx,
origin.clone(),
Some("tests/test.jpeg".to_string()),
));
let selected = rx
.recv()
.expect("Broken channel")
.expect("The file manager failed to find test.jpeg");
// Expecting attributes conforming the spec
assert_eq!(selected.filename, PathBuf::from("test.jpeg"));
@ -43,24 +53,35 @@ fn test_filemanager() {
// Test by reading, expecting same content
{
let (tx2, rx2) = ipc::channel().unwrap();
filemanager.handle(FileManagerThreadMsg::ReadFile(tx2, selected.id.clone(), false, origin.clone()));
filemanager.handle(FileManagerThreadMsg::ReadFile(
tx2,
selected.id.clone(),
false,
origin.clone(),
));
let msg = rx2.recv().expect("Broken channel");
if let ReadFileProgress::Meta(blob_buf) = msg.expect("File manager reading failure is unexpected") {
if let ReadFileProgress::Meta(blob_buf) =
msg.expect("File manager reading failure is unexpected")
{
let mut bytes = blob_buf.bytes;
loop {
match rx2.recv().expect("Broken channel").expect("File manager reading failure is unexpected") {
match rx2
.recv()
.expect("Broken channel")
.expect("File manager reading failure is unexpected")
{
ReadFileProgress::Meta(_) => {
panic!("Invalid FileManager reply");
}
},
ReadFileProgress::Partial(mut bytes_in) => {
bytes.append(&mut bytes_in);
}
},
ReadFileProgress::EOF => {
break;
}
},
}
}
@ -73,7 +94,11 @@ fn test_filemanager() {
// Delete the id
{
let (tx2, rx2) = ipc::channel().unwrap();
filemanager.handle(FileManagerThreadMsg::DecRef(selected.id.clone(), origin.clone(), tx2));
filemanager.handle(FileManagerThreadMsg::DecRef(
selected.id.clone(),
origin.clone(),
tx2,
));
let ret = rx2.recv().expect("Broken channel");
assert!(ret.is_ok(), "DecRef is not okay");
@ -82,15 +107,26 @@ fn test_filemanager() {
// Test by reading again, expecting read error because we invalidated the id
{
let (tx2, rx2) = ipc::channel().unwrap();
filemanager.handle(FileManagerThreadMsg::ReadFile(tx2, selected.id.clone(), false, origin.clone()));
filemanager.handle(FileManagerThreadMsg::ReadFile(
tx2,
selected.id.clone(),
false,
origin.clone(),
));
let msg = rx2.recv().expect("Broken channel");
match msg {
Err(FileManagerThreadError::BlobURLStoreError(BlobURLStoreError::InvalidFileID)) => {},
Err(FileManagerThreadError::BlobURLStoreError(
BlobURLStoreError::InvalidFileID,
)) => {},
other => {
assert!(false, "Get unexpected response after deleting the id: {:?}", other);
}
assert!(
false,
"Get unexpected response after deleting the id: {:?}",
other
);
},
}
}
}

View file

@ -13,7 +13,7 @@ fn test_hsts_entry_is_not_expired_when_it_has_no_timestamp() {
host: "mozilla.org".to_owned(),
include_subdomains: false,
max_age: Some(20),
timestamp: None
timestamp: None,
};
assert!(!entry.is_expired());
@ -25,7 +25,7 @@ fn test_hsts_entry_is_not_expired_when_it_has_no_max_age() {
host: "mozilla.org".to_owned(),
include_subdomains: false,
max_age: None,
timestamp: Some(time::get_time().sec as u64)
timestamp: Some(time::get_time().sec as u64),
};
assert!(!entry.is_expired());
@ -37,7 +37,7 @@ fn test_hsts_entry_is_expired_when_it_has_reached_its_max_age() {
host: "mozilla.org".to_owned(),
include_subdomains: false,
max_age: Some(10),
timestamp: Some(time::get_time().sec as u64 - 20u64)
timestamp: Some(time::get_time().sec as u64 - 20u64),
};
assert!(entry.is_expired());
@ -46,7 +46,9 @@ fn test_hsts_entry_is_expired_when_it_has_reached_its_max_age() {
#[test]
fn test_hsts_entry_cant_be_created_with_ipv6_address_as_host() {
let entry = HstsEntry::new(
"2001:0db8:0000:0000:0000:ff00:0042:8329".to_owned(), IncludeSubdomains::NotIncluded, None
"2001:0db8:0000:0000:0000:ff00:0042:8329".to_owned(),
IncludeSubdomains::NotIncluded,
None,
);
assert!(entry.is_none(), "able to create HstsEntry with IPv6 host");
@ -54,9 +56,7 @@ fn test_hsts_entry_cant_be_created_with_ipv6_address_as_host() {
#[test]
fn test_hsts_entry_cant_be_created_with_ipv4_address_as_host() {
let entry = HstsEntry::new(
"4.4.4.4".to_owned(), IncludeSubdomains::NotIncluded, None
);
let entry = HstsEntry::new("4.4.4.4".to_owned(), IncludeSubdomains::NotIncluded, None);
assert!(entry.is_none(), "able to create HstsEntry with IPv4 host");
}
@ -66,15 +66,33 @@ fn test_base_domain_in_entries_map() {
let entries_map = HashMap::new();
let mut list = HstsList {
entries_map: entries_map
entries_map: entries_map,
};
list.push(HstsEntry::new("servo.mozilla.org".to_owned(),
IncludeSubdomains::NotIncluded, None).unwrap());
list.push(HstsEntry::new("firefox.mozilla.org".to_owned(),
IncludeSubdomains::NotIncluded, None).unwrap());
list.push(HstsEntry::new("bugzilla.org".to_owned(),
IncludeSubdomains::NotIncluded, None).unwrap());
list.push(
HstsEntry::new(
"servo.mozilla.org".to_owned(),
IncludeSubdomains::NotIncluded,
None,
)
.unwrap(),
);
list.push(
HstsEntry::new(
"firefox.mozilla.org".to_owned(),
IncludeSubdomains::NotIncluded,
None,
)
.unwrap(),
);
list.push(
HstsEntry::new(
"bugzilla.org".to_owned(),
IncludeSubdomains::NotIncluded,
None,
)
.unwrap(),
);
assert_eq!(list.entries_map.len(), 2);
assert_eq!(list.entries_map.get("mozilla.org").unwrap().len(), 2);
@ -83,14 +101,27 @@ fn test_base_domain_in_entries_map() {
#[test]
fn test_push_entry_with_0_max_age_evicts_entry_from_list() {
let mut entries_map = HashMap::new();
entries_map.insert("mozilla.org".to_owned(), vec!(HstsEntry::new("mozilla.org".to_owned(),
IncludeSubdomains::NotIncluded, Some(500000u64)).unwrap()));
entries_map.insert(
"mozilla.org".to_owned(),
vec![HstsEntry::new(
"mozilla.org".to_owned(),
IncludeSubdomains::NotIncluded,
Some(500000u64),
)
.unwrap()],
);
let mut list = HstsList {
entries_map: entries_map
entries_map: entries_map,
};
list.push(HstsEntry::new("mozilla.org".to_owned(),
IncludeSubdomains::NotIncluded, Some(0)).unwrap());
list.push(
HstsEntry::new(
"mozilla.org".to_owned(),
IncludeSubdomains::NotIncluded,
Some(0),
)
.unwrap(),
);
assert_eq!(list.is_host_secure("mozilla.org"), false)
}
@ -98,14 +129,22 @@ fn test_push_entry_with_0_max_age_evicts_entry_from_list() {
#[test]
fn test_push_entry_to_hsts_list_should_not_add_subdomains_whose_superdomain_is_already_matched() {
let mut entries_map = HashMap::new();
entries_map.insert("mozilla.org".to_owned(), vec!(HstsEntry::new("mozilla.org".to_owned(),
IncludeSubdomains::Included, None).unwrap()));
entries_map.insert(
"mozilla.org".to_owned(),
vec![HstsEntry::new("mozilla.org".to_owned(), IncludeSubdomains::Included, None).unwrap()],
);
let mut list = HstsList {
entries_map: entries_map
entries_map: entries_map,
};
list.push(HstsEntry::new("servo.mozilla.org".to_owned(),
IncludeSubdomains::NotIncluded, None).unwrap());
list.push(
HstsEntry::new(
"servo.mozilla.org".to_owned(),
IncludeSubdomains::NotIncluded,
None,
)
.unwrap(),
);
assert_eq!(list.entries_map.get("mozilla.org").unwrap().len(), 1)
}
@ -113,16 +152,24 @@ fn test_push_entry_to_hsts_list_should_not_add_subdomains_whose_superdomain_is_a
#[test]
fn test_push_entry_to_hsts_list_should_update_existing_domain_entrys_include_subdomains() {
let mut entries_map = HashMap::new();
entries_map.insert("mozilla.org".to_owned(), vec!(HstsEntry::new("mozilla.org".to_owned(),
IncludeSubdomains::Included, None).unwrap()));
entries_map.insert(
"mozilla.org".to_owned(),
vec![HstsEntry::new("mozilla.org".to_owned(), IncludeSubdomains::Included, None).unwrap()],
);
let mut list = HstsList {
entries_map: entries_map
entries_map: entries_map,
};
assert!(list.is_host_secure("servo.mozilla.org"));
list.push(HstsEntry::new("mozilla.org".to_owned(),
IncludeSubdomains::NotIncluded, None).unwrap());
list.push(
HstsEntry::new(
"mozilla.org".to_owned(),
IncludeSubdomains::NotIncluded,
None,
)
.unwrap(),
);
assert!(!list.is_host_secure("servo.mozilla.org"))
}
@ -130,14 +177,27 @@ fn test_push_entry_to_hsts_list_should_update_existing_domain_entrys_include_sub
#[test]
fn test_push_entry_to_hsts_list_should_not_create_duplicate_entry() {
let mut entries_map = HashMap::new();
entries_map.insert("mozilla.org".to_owned(), vec!(HstsEntry::new("mozilla.org".to_owned(),
IncludeSubdomains::NotIncluded, None).unwrap()));
entries_map.insert(
"mozilla.org".to_owned(),
vec![HstsEntry::new(
"mozilla.org".to_owned(),
IncludeSubdomains::NotIncluded,
None,
)
.unwrap()],
);
let mut list = HstsList {
entries_map: entries_map
entries_map: entries_map,
};
list.push(HstsEntry::new("mozilla.org".to_owned(),
IncludeSubdomains::NotIncluded, None).unwrap());
list.push(
HstsEntry::new(
"mozilla.org".to_owned(),
IncludeSubdomains::NotIncluded,
None,
)
.unwrap(),
);
assert_eq!(list.entries_map.get("mozilla.org").unwrap().len(), 1)
}
@ -145,16 +205,16 @@ fn test_push_entry_to_hsts_list_should_not_create_duplicate_entry() {
#[test]
fn test_push_multiple_entrie_to_hsts_list_should_add_them_all() {
let mut list = HstsList {
entries_map: HashMap::new()
entries_map: HashMap::new(),
};
assert!(!list.is_host_secure("mozilla.org"));
assert!(!list.is_host_secure("bugzilla.org"));
list.push(HstsEntry::new("mozilla.org".to_owned(),
IncludeSubdomains::Included, None).unwrap());
list.push(HstsEntry::new("bugzilla.org".to_owned(),
IncludeSubdomains::Included, None).unwrap());
list.push(HstsEntry::new("mozilla.org".to_owned(), IncludeSubdomains::Included, None).unwrap());
list.push(
HstsEntry::new("bugzilla.org".to_owned(), IncludeSubdomains::Included, None).unwrap(),
);
assert!(list.is_host_secure("mozilla.org"));
assert!(list.is_host_secure("bugzilla.org"));
@ -163,13 +223,12 @@ fn test_push_multiple_entrie_to_hsts_list_should_add_them_all() {
#[test]
fn test_push_entry_to_hsts_list_should_add_an_entry() {
let mut list = HstsList {
entries_map: HashMap::new()
entries_map: HashMap::new(),
};
assert!(!list.is_host_secure("mozilla.org"));
list.push(HstsEntry::new("mozilla.org".to_owned(),
IncludeSubdomains::Included, None).unwrap());
list.push(HstsEntry::new("mozilla.org".to_owned(), IncludeSubdomains::Included, None).unwrap());
assert!(list.is_host_secure("mozilla.org"));
}
@ -177,34 +236,43 @@ fn test_push_entry_to_hsts_list_should_add_an_entry() {
#[test]
fn test_parse_hsts_preload_should_return_none_when_json_invalid() {
let mock_preload_content = "derp";
assert!(HstsList::from_preload(mock_preload_content).is_none(), "invalid preload list should not have parsed")
assert!(
HstsList::from_preload(mock_preload_content).is_none(),
"invalid preload list should not have parsed"
)
}
#[test]
fn test_parse_hsts_preload_should_return_none_when_json_contains_no_entries_map_key() {
let mock_preload_content = "{\"nothing\": \"to see here\"}";
assert!(HstsList::from_preload(mock_preload_content).is_none(), "invalid preload list should not have parsed")
assert!(
HstsList::from_preload(mock_preload_content).is_none(),
"invalid preload list should not have parsed"
)
}
#[test]
fn test_parse_hsts_preload_should_decode_host_and_includes_subdomains() {
let mock_preload_content = "{\
\"entries\": [\
{\"host\": \"mozilla.org\",\
\"include_subdomains\": false}\
]\
\"entries\": [\
{\"host\": \"mozilla.org\",\
\"include_subdomains\": false}\
]\
}";
let hsts_list = HstsList::from_preload(mock_preload_content);
let entries_map = hsts_list.unwrap().entries_map;
assert_eq!(entries_map.get("mozilla.org").unwrap()[0].host, "mozilla.org");
assert_eq!(
entries_map.get("mozilla.org").unwrap()[0].host,
"mozilla.org"
);
assert!(!entries_map.get("mozilla.org").unwrap()[0].include_subdomains);
}
#[test]
fn test_hsts_list_with_no_entries_map_does_not_is_host_secure() {
let hsts_list = HstsList {
entries_map: HashMap::new()
entries_map: HashMap::new(),
};
assert!(!hsts_list.is_host_secure("mozilla.org"));
@ -213,11 +281,18 @@ fn test_hsts_list_with_no_entries_map_does_not_is_host_secure() {
#[test]
fn test_hsts_list_with_exact_domain_entry_is_is_host_secure() {
let mut entries_map = HashMap::new();
entries_map.insert("mozilla.org".to_owned(), vec![HstsEntry::new("mozilla.org".to_owned(),
IncludeSubdomains::NotIncluded, None).unwrap()]);
entries_map.insert(
"mozilla.org".to_owned(),
vec![HstsEntry::new(
"mozilla.org".to_owned(),
IncludeSubdomains::NotIncluded,
None,
)
.unwrap()],
);
let hsts_list = HstsList {
entries_map: entries_map
entries_map: entries_map,
};
assert!(hsts_list.is_host_secure("mozilla.org"));
@ -226,10 +301,12 @@ fn test_hsts_list_with_exact_domain_entry_is_is_host_secure() {
#[test]
fn test_hsts_list_with_subdomain_when_include_subdomains_is_true_is_is_host_secure() {
let mut entries_map = HashMap::new();
entries_map.insert("mozilla.org".to_owned(), vec![HstsEntry::new("mozilla.org".to_owned(),
IncludeSubdomains::Included, None).unwrap()]);
entries_map.insert(
"mozilla.org".to_owned(),
vec![HstsEntry::new("mozilla.org".to_owned(), IncludeSubdomains::Included, None).unwrap()],
);
let hsts_list = HstsList {
entries_map: entries_map
entries_map: entries_map,
};
assert!(hsts_list.is_host_secure("servo.mozilla.org"));
@ -238,10 +315,17 @@ fn test_hsts_list_with_subdomain_when_include_subdomains_is_true_is_is_host_secu
#[test]
fn test_hsts_list_with_subdomain_when_include_subdomains_is_false_is_not_is_host_secure() {
let mut entries_map = HashMap::new();
entries_map.insert("mozilla.org".to_owned(), vec![HstsEntry::new("mozilla.org".to_owned(),
IncludeSubdomains::NotIncluded, None).unwrap()]);
entries_map.insert(
"mozilla.org".to_owned(),
vec![HstsEntry::new(
"mozilla.org".to_owned(),
IncludeSubdomains::NotIncluded,
None,
)
.unwrap()],
);
let hsts_list = HstsList {
entries_map: entries_map
entries_map: entries_map,
};
assert!(!hsts_list.is_host_secure("servo.mozilla.org"));
@ -250,10 +334,12 @@ fn test_hsts_list_with_subdomain_when_include_subdomains_is_false_is_not_is_host
#[test]
fn test_hsts_list_with_subdomain_when_host_is_not_a_subdomain_is_not_is_host_secure() {
let mut entries_map = HashMap::new();
entries_map.insert("mozilla.org".to_owned(), vec![HstsEntry::new("mozilla.org".to_owned(),
IncludeSubdomains::Included, None).unwrap()]);
entries_map.insert(
"mozilla.org".to_owned(),
vec![HstsEntry::new("mozilla.org".to_owned(), IncludeSubdomains::Included, None).unwrap()],
);
let hsts_list = HstsList {
entries_map: entries_map
entries_map: entries_map,
};
assert!(!hsts_list.is_host_secure("servo-mozilla.org"));
@ -262,10 +348,12 @@ fn test_hsts_list_with_subdomain_when_host_is_not_a_subdomain_is_not_is_host_sec
#[test]
fn test_hsts_list_with_subdomain_when_host_is_exact_match_is_is_host_secure() {
let mut entries_map = HashMap::new();
entries_map.insert("mozilla.org".to_owned(), vec![HstsEntry::new("mozilla.org".to_owned(),
IncludeSubdomains::Included, None).unwrap()]);
entries_map.insert(
"mozilla.org".to_owned(),
vec![HstsEntry::new("mozilla.org".to_owned(), IncludeSubdomains::Included, None).unwrap()],
);
let hsts_list = HstsList {
entries_map: entries_map
entries_map: entries_map,
};
assert!(hsts_list.is_host_secure("mozilla.org"));
@ -274,14 +362,17 @@ fn test_hsts_list_with_subdomain_when_host_is_exact_match_is_is_host_secure() {
#[test]
fn test_hsts_list_with_expired_entry_is_not_is_host_secure() {
let mut entries_map = HashMap::new();
entries_map.insert("mozilla.org".to_owned(), vec![HstsEntry {
entries_map.insert(
"mozilla.org".to_owned(),
vec![HstsEntry {
host: "mozilla.org".to_owned(),
include_subdomains: false,
max_age: Some(20),
timestamp: Some(time::get_time().sec as u64 - 100u64)
}]);
timestamp: Some(time::get_time().sec as u64 - 100u64),
}],
);
let hsts_list = HstsList {
entries_map: entries_map
entries_map: entries_map,
};
assert!(!hsts_list.is_host_secure("mozilla.org"));

File diff suppressed because it is too large Load diff

View file

@ -70,9 +70,7 @@ use tokio::runtime::Runtime;
use tokio_openssl::SslAcceptorExt;
lazy_static! {
pub static ref HANDLE: Mutex<Runtime> = {
Mutex::new(Runtime::new().unwrap())
};
pub static ref HANDLE: Mutex<Runtime> = { Mutex::new(Runtime::new().unwrap()) };
}
const DEFAULT_USER_AGENT: &'static str = "Such Browser. Very Layout. Wow.";
@ -83,18 +81,17 @@ struct FetchResponseCollector {
fn create_embedder_proxy() -> EmbedderProxy {
let (sender, _) = channel();
let event_loop_waker = | | {
struct DummyEventLoopWaker {
}
let event_loop_waker = || {
struct DummyEventLoopWaker {}
impl DummyEventLoopWaker {
fn new() -> DummyEventLoopWaker {
DummyEventLoopWaker { }
DummyEventLoopWaker {}
}
}
impl EventLoopWaker for DummyEventLoopWaker {
fn wake(&self) { }
fn wake(&self) {}
fn clone(&self) -> Box<EventLoopWaker + Send> {
Box::new(DummyEventLoopWaker { })
Box::new(DummyEventLoopWaker {})
}
}
@ -103,12 +100,16 @@ fn create_embedder_proxy() -> EmbedderProxy {
EmbedderProxy {
sender: sender,
event_loop_waker: event_loop_waker()
event_loop_waker: event_loop_waker(),
}
}
fn new_fetch_context(dc: Option<Sender<DevtoolsControlMsg>>, fc: Option<EmbedderProxy>) -> FetchContext {
let ssl_connector = create_ssl_connector_builder(&resources::read_string(Resource::SSLCertificates));
fn new_fetch_context(
dc: Option<Sender<DevtoolsControlMsg>>,
fc: Option<EmbedderProxy>,
) -> FetchContext {
let ssl_connector =
create_ssl_connector_builder(&resources::read_string(Resource::SSLCertificates));
let sender = fc.unwrap_or_else(|| create_embedder_proxy());
FetchContext {
state: Arc::new(HttpState::new(ssl_connector)),
@ -135,9 +136,7 @@ fn fetch(request: &mut Request, dc: Option<Sender<DevtoolsControlMsg>>) -> Respo
fn fetch_with_context(request: &mut Request, context: &FetchContext) -> Response {
let (sender, receiver) = channel();
let mut target = FetchResponseCollector {
sender: sender,
};
let mut target = FetchResponseCollector { sender: sender };
methods::fetch(request, &mut target, context);
@ -146,9 +145,7 @@ fn fetch_with_context(request: &mut Request, context: &FetchContext) -> Response
fn fetch_with_cors_cache(request: &mut Request, cache: &mut CorsCache) -> Response {
let (sender, receiver) = channel();
let mut target = FetchResponseCollector {
sender: sender,
};
let mut target = FetchResponseCollector { sender: sender };
methods::fetch_with_cors_cache(request, cache, &mut target, &new_fetch_context(None, None));
@ -166,7 +163,7 @@ impl Server {
}
fn make_server<H>(handler: H) -> (Server, ServoUrl)
where
where
H: Fn(HyperRequest<Body>, &mut HyperResponse<Body>) + Send + Sync + 'static,
{
let handler = Arc::new(handler);
@ -174,18 +171,18 @@ fn make_server<H>(handler: H) -> (Server, ServoUrl)
let url_string = format!("http://localhost:{}", listener.local_addr().unwrap().port());
let url = ServoUrl::parse(&url_string).unwrap();
let (tx, rx) = futures::sync::oneshot::channel::<()>();
let server = HyperServer::from_tcp(listener).unwrap().serve(
move || {
let server = HyperServer::from_tcp(listener)
.unwrap()
.serve(move || {
let handler = handler.clone();
service_fn_ok(move |req: HyperRequest<Body>| {
let mut response = HyperResponse::new(Vec::<u8>::new().into());
handler(req, &mut response);
response
})
}
)
.with_graceful_shutdown(rx)
.map_err(|_|());
})
.with_graceful_shutdown(rx)
.map_err(|_| ());
HANDLE.lock().unwrap().spawn(server);
let server = Server { close_channel: tx };
@ -193,7 +190,7 @@ fn make_server<H>(handler: H) -> (Server, ServoUrl)
}
fn make_ssl_server<H>(handler: H, cert_path: PathBuf, key_path: PathBuf) -> (Server, ServoUrl)
where
where
H: Fn(HyperRequest<Body>, &mut HyperResponse<Body>) + Send + Sync + 'static,
{
let handler = Arc::new(handler);
@ -202,28 +199,39 @@ fn make_ssl_server<H>(handler: H, cert_path: PathBuf, key_path: PathBuf) -> (Ser
let url_string = format!("http://localhost:{}", listener.local_addr().unwrap().port());
let url = ServoUrl::parse(&url_string).unwrap();
let server = listener.incoming()
.map_err(|_| ())
.for_each(move |sock| {
let mut ssl_builder = SslAcceptor::mozilla_modern(SslMethod::tls()).unwrap();
ssl_builder.set_certificate_file(&cert_path, SslFiletype::PEM).unwrap();
ssl_builder.set_private_key_file(&key_path, SslFiletype::PEM).unwrap();
let server = listener.incoming().map_err(|_| ()).for_each(move |sock| {
let mut ssl_builder = SslAcceptor::mozilla_modern(SslMethod::tls()).unwrap();
ssl_builder
.set_certificate_file(&cert_path, SslFiletype::PEM)
.unwrap();
ssl_builder
.set_private_key_file(&key_path, SslFiletype::PEM)
.unwrap();
let handler = handler.clone();
ssl_builder.build().accept_async(sock).map_err(|_| ()).and_then(move |ssl| {
Http::new().serve_connection(ssl,
let handler = handler.clone();
ssl_builder
.build()
.accept_async(sock)
.map_err(|_| ())
.and_then(move |ssl| {
Http::new()
.serve_connection(
ssl,
service_fn_ok(move |req: HyperRequest<Body>| {
let mut response = HyperResponse::new(Vec::<u8>::new().into());
handler(req, &mut response);
response
})
)
.map_err(|_|())
}),
)
.map_err(|_| ())
})
});
});
let (tx, rx) = futures::sync::oneshot::channel::<()>();
let server = server.select(rx.map_err(|_| ())).map(|_| ()).map_err(|_| ());
let server = server
.select(rx.map_err(|_| ()))
.map(|_| ())
.map_err(|_| ());
HANDLE.lock().unwrap().spawn(server);

View file

@ -33,7 +33,7 @@ fn test_sniff_mp4_matcher() {
panic!("Didn't read mime type")
}
},
Err(e) => panic!("Couldn't read from file with error {}", e)
Err(e) => panic!("Couldn't read from file with error {}", e),
}
}
@ -43,9 +43,9 @@ fn test_sniff_mp4_matcher_long() {
let matcher = Mp4Matcher;
let mut data: [u8; 260] = [0; 260];
&data[.. 11].clone_from_slice(
&[0x00, 0x00, 0x01, 0x04, 0x66, 0x74, 0x79, 0x70, 0x6D, 0x70, 0x34]
);
&data[..11].clone_from_slice(&[
0x00, 0x00, 0x01, 0x04, 0x66, 0x74, 0x79, 0x70, 0x6D, 0x70, 0x34,
]);
assert!(matcher.matches(&data));
}
@ -57,13 +57,18 @@ fn test_validate_classifier() {
}
#[cfg(test)]
fn test_sniff_with_flags(filename_orig: &path::Path,
expected_mime: Mime,
supplied_type: Option<Mime>,
no_sniff_flag: NoSniffFlag,
apache_bug_flag: ApacheBugFlag) {
fn test_sniff_with_flags(
filename_orig: &path::Path,
expected_mime: Mime,
supplied_type: Option<Mime>,
no_sniff_flag: NoSniffFlag,
apache_bug_flag: ApacheBugFlag,
) {
let current_working_directory = env::current_dir().unwrap();
println!("The current directory is {}", current_working_directory.display());
println!(
"The current directory is {}",
current_working_directory.display()
);
let mut filename = PathBuf::from("tests/parsable_mime/");
filename.push(filename_orig);
@ -74,30 +79,35 @@ fn test_sniff_with_flags(filename_orig: &path::Path,
match read_result {
Ok(data) => {
let parsed_mime = classifier.classify(LoadContext::Browsing,
no_sniff_flag,
apache_bug_flag,
&supplied_type,
&data);
let parsed_mime = classifier.classify(
LoadContext::Browsing,
no_sniff_flag,
apache_bug_flag,
&supplied_type,
&data,
);
if (parsed_mime.type_() != expected_mime.type_()) ||
(parsed_mime.subtype() != expected_mime.subtype()) {
panic!("File {:?} parsed incorrectly should be {:?}, parsed as {:?}",
filename, expected_mime, parsed_mime);
(parsed_mime.subtype() != expected_mime.subtype())
{
panic!(
"File {:?} parsed incorrectly should be {:?}, parsed as {:?}",
filename, expected_mime, parsed_mime
);
}
}
Err(e) => panic!("Couldn't read from file {:?} with error {}",
filename, e),
},
Err(e) => panic!("Couldn't read from file {:?} with error {}", filename, e),
}
}
#[cfg(test)]
fn test_sniff_full(filename_orig: &path::Path, expected_mime: Mime,
supplied_type: Option<Mime>) {
test_sniff_with_flags(filename_orig,
expected_mime,
supplied_type,
NoSniffFlag::Off,
ApacheBugFlag::Off)
fn test_sniff_full(filename_orig: &path::Path, expected_mime: Mime, supplied_type: Option<Mime>) {
test_sniff_with_flags(
filename_orig,
expected_mime,
supplied_type,
NoSniffFlag::Off,
ApacheBugFlag::Off,
)
}
#[cfg(test)]
@ -198,32 +208,50 @@ fn test_sniff_wave() {
#[test]
fn test_sniff_ogg() {
test_sniff_classification("small.ogg", "application/ogg".parse().unwrap(), None);
test_sniff_classification("small.ogg", "application/ogg".parse().unwrap(), Some("audio/".parse().unwrap()));
test_sniff_classification(
"small.ogg",
"application/ogg".parse().unwrap(),
Some("audio/".parse().unwrap()),
);
}
#[test]
#[should_panic]
fn test_sniff_vsn_ms_fontobject() {
test_sniff_classification_sup("vnd.ms-fontobject", "application/vnd.ms-fontobject".parse().unwrap());
test_sniff_classification_sup(
"vnd.ms-fontobject",
"application/vnd.ms-fontobject".parse().unwrap(),
);
}
#[test]
#[should_panic]
fn test_sniff_true_type() {
test_sniff_full(&PathBuf::from("unknown/true_type.ttf"), "(TrueType)/".parse().unwrap(), None);
test_sniff_full(
&PathBuf::from("unknown/true_type.ttf"),
"(TrueType)/".parse().unwrap(),
None,
);
}
#[test]
#[should_panic]
fn test_sniff_open_type() {
test_sniff_full(&PathBuf::from("unknown/open_type"), "(OpenType)/".parse().unwrap(), None);
test_sniff_full(
&PathBuf::from("unknown/open_type"),
"(OpenType)/".parse().unwrap(),
None,
);
}
#[test]
#[should_panic]
fn test_sniff_true_type_collection() {
test_sniff_full(&PathBuf::from("unknown/true_type_collection.ttc"), "(TrueType Collection)/".parse().unwrap(),
None);
test_sniff_full(
&PathBuf::from("unknown/true_type_collection.ttc"),
"(TrueType Collection)/".parse().unwrap(),
None,
);
}
#[test]
@ -244,7 +272,11 @@ fn test_sniff_zip() {
#[test]
fn test_sniff_rar() {
test_sniff_classification("test.rar", "application/x-rar-compressed".parse().unwrap(), None);
test_sniff_classification(
"test.rar",
"application/x-rar-compressed".parse().unwrap(),
None,
);
}
#[test]
@ -466,86 +498,130 @@ fn test_sniff_utf_8_bom() {
#[test]
fn test_sniff_rss_feed() {
// RSS feeds
test_sniff_full(&PathBuf::from("text/xml/feed.rss"), "application/rss+xml".parse().unwrap(), Some(mime::TEXT_HTML));
test_sniff_full(&PathBuf::from("text/xml/rdf_rss.xml"), "application/rss+xml".parse().unwrap(),
Some(mime::TEXT_HTML));
test_sniff_full(
&PathBuf::from("text/xml/feed.rss"),
"application/rss+xml".parse().unwrap(),
Some(mime::TEXT_HTML),
);
test_sniff_full(
&PathBuf::from("text/xml/rdf_rss.xml"),
"application/rss+xml".parse().unwrap(),
Some(mime::TEXT_HTML),
);
// Not RSS feeds
test_sniff_full(&PathBuf::from("text/xml/rdf_rss_ko_1.xml"), mime::TEXT_HTML, Some(mime::TEXT_HTML));
test_sniff_full(&PathBuf::from("text/xml/rdf_rss_ko_2.xml"), mime::TEXT_HTML, Some(mime::TEXT_HTML));
test_sniff_full(&PathBuf::from("text/xml/rdf_rss_ko_3.xml"), mime::TEXT_HTML, Some(mime::TEXT_HTML));
test_sniff_full(&PathBuf::from("text/xml/rdf_rss_ko_4.xml"), mime::TEXT_HTML, Some(mime::TEXT_HTML));
test_sniff_full(
&PathBuf::from("text/xml/rdf_rss_ko_1.xml"),
mime::TEXT_HTML,
Some(mime::TEXT_HTML),
);
test_sniff_full(
&PathBuf::from("text/xml/rdf_rss_ko_2.xml"),
mime::TEXT_HTML,
Some(mime::TEXT_HTML),
);
test_sniff_full(
&PathBuf::from("text/xml/rdf_rss_ko_3.xml"),
mime::TEXT_HTML,
Some(mime::TEXT_HTML),
);
test_sniff_full(
&PathBuf::from("text/xml/rdf_rss_ko_4.xml"),
mime::TEXT_HTML,
Some(mime::TEXT_HTML),
);
}
#[test]
fn test_sniff_atom_feed() {
test_sniff_full(&PathBuf::from("text/xml/feed.atom"), "application/atom+xml".parse().unwrap(),
Some(mime::TEXT_HTML));
test_sniff_full(
&PathBuf::from("text/xml/feed.atom"),
"application/atom+xml".parse().unwrap(),
Some(mime::TEXT_HTML),
);
}
#[test]
fn test_sniff_binary_file() {
test_sniff_full(&PathBuf::from("unknown/binary_file"), mime::APPLICATION_OCTET_STREAM, None);
test_sniff_full(
&PathBuf::from("unknown/binary_file"),
mime::APPLICATION_OCTET_STREAM,
None,
);
}
#[test]
fn test_sniff_atom_feed_with_no_sniff_flag_on() {
test_sniff_with_flags(&PathBuf::from("text/xml/feed.atom"),
mime::TEXT_HTML,
Some(mime::TEXT_HTML),
NoSniffFlag::On,
ApacheBugFlag::Off);
test_sniff_with_flags(
&PathBuf::from("text/xml/feed.atom"),
mime::TEXT_HTML,
Some(mime::TEXT_HTML),
NoSniffFlag::On,
ApacheBugFlag::Off,
);
}
#[test]
fn test_sniff_with_no_sniff_flag_on_and_apache_flag_on() {
test_sniff_with_flags(&PathBuf::from("text/xml/feed.atom"),
mime::TEXT_HTML,
Some(mime::TEXT_HTML),
NoSniffFlag::On,
ApacheBugFlag::On);
test_sniff_with_flags(
&PathBuf::from("text/xml/feed.atom"),
mime::TEXT_HTML,
Some(mime::TEXT_HTML),
NoSniffFlag::On,
ApacheBugFlag::On,
);
}
#[test]
fn test_sniff_utf_8_bom_with_apache_flag_on() {
test_sniff_with_flags(&PathBuf::from("text/plain/utf8bom.txt"),
mime::TEXT_PLAIN,
Some("dummy/text".parse().unwrap()),
NoSniffFlag::Off,
ApacheBugFlag::On);
test_sniff_with_flags(
&PathBuf::from("text/plain/utf8bom.txt"),
mime::TEXT_PLAIN,
Some("dummy/text".parse().unwrap()),
NoSniffFlag::Off,
ApacheBugFlag::On,
);
}
#[test]
fn test_sniff_utf_16be_bom_with_apache_flag_on() {
test_sniff_with_flags(&PathBuf::from("text/plain/utf16bebom.txt"),
mime::TEXT_PLAIN,
Some("dummy/text".parse().unwrap()),
NoSniffFlag::Off,
ApacheBugFlag::On);
test_sniff_with_flags(
&PathBuf::from("text/plain/utf16bebom.txt"),
mime::TEXT_PLAIN,
Some("dummy/text".parse().unwrap()),
NoSniffFlag::Off,
ApacheBugFlag::On,
);
}
#[test]
fn test_sniff_utf_16le_bom_with_apache_flag_on() {
test_sniff_with_flags(&PathBuf::from("text/plain/utf16lebom.txt"),
mime::TEXT_PLAIN,
Some("dummy/text".parse().unwrap()),
NoSniffFlag::Off,
ApacheBugFlag::On);
test_sniff_with_flags(
&PathBuf::from("text/plain/utf16lebom.txt"),
mime::TEXT_PLAIN,
Some("dummy/text".parse().unwrap()),
NoSniffFlag::Off,
ApacheBugFlag::On,
);
}
#[test]
fn test_sniff_octet_stream_apache_flag_on() {
test_sniff_with_flags(&PathBuf::from("unknown/binary_file"),
mime::APPLICATION_OCTET_STREAM,
Some("dummy/binary".parse().unwrap()),
NoSniffFlag::Off,
ApacheBugFlag::On);
test_sniff_with_flags(
&PathBuf::from("unknown/binary_file"),
mime::APPLICATION_OCTET_STREAM,
Some("dummy/binary".parse().unwrap()),
NoSniffFlag::Off,
ApacheBugFlag::On,
);
}
#[test]
fn test_sniff_mp4_video_apache_flag_on() {
test_sniff_with_flags(&PathBuf::from("video/mp4/test.mp4"),
mime::APPLICATION_OCTET_STREAM,
Some("video/mp4".parse().unwrap()),
NoSniffFlag::Off,
ApacheBugFlag::On);
test_sniff_with_flags(
&PathBuf::from("video/mp4/test.mp4"),
mime::APPLICATION_OCTET_STREAM,
Some("video/mp4".parse().unwrap()),
NoSniffFlag::Off,
ApacheBugFlag::On,
);
}

View file

@ -21,7 +21,13 @@ fn test_exit() {
let (mtx, _mrx) = ipc::channel().unwrap();
let (sender, receiver) = ipc::channel().unwrap();
let (resource_thread, _private_resource_thread) = new_core_resource_thread(
"".into(), None, ProfilerChan(tx), MemProfilerChan(mtx), create_embedder_proxy(), None);
"".into(),
None,
ProfilerChan(tx),
MemProfilerChan(mtx),
create_embedder_proxy(),
None,
);
resource_thread.send(CoreResourceMsg::Exit(sender)).unwrap();
receiver.recv().unwrap();
}
@ -32,12 +38,16 @@ fn test_parse_hostsfile() {
let hosts_table = parse_hostsfile(mock_hosts_file_content);
assert_eq!(2, hosts_table.len());
assert_eq!(ip("127.0.0.1"), *hosts_table.get("foo.bar.com").unwrap());
assert_eq!(ip("127.0.0.2"), *hosts_table.get("servo.test.server").unwrap());
assert_eq!(
ip("127.0.0.2"),
*hosts_table.get("servo.test.server").unwrap()
);
}
#[test]
fn test_parse_malformed_hostsfile() {
let mock_hosts_file_content = "malformed file\n127.0.0.1 foo.bar.com\nservo.test.server 127.0.0.1";
let mock_hosts_file_content =
"malformed file\n127.0.0.1 foo.bar.com\nservo.test.server 127.0.0.1";
let hosts_table = parse_hostsfile(mock_hosts_file_content);
assert_eq!(1, hosts_table.len());
assert_eq!(ip("127.0.0.1"), *hosts_table.get("foo.bar.com").unwrap());
@ -45,7 +55,8 @@ fn test_parse_malformed_hostsfile() {
#[test]
fn test_parse_hostsfile_with_line_comment() {
let mock_hosts_file_content = "# this is a line comment\n127.0.0.1 foo.bar.com\n# anothercomment";
let mock_hosts_file_content =
"# this is a line comment\n127.0.0.1 foo.bar.com\n# anothercomment";
let hosts_table = parse_hostsfile(mock_hosts_file_content);
assert_eq!(1, hosts_table.len());
assert_eq!(ip("127.0.0.1"), *hosts_table.get("foo.bar.com").unwrap());
@ -53,11 +64,15 @@ fn test_parse_hostsfile_with_line_comment() {
#[test]
fn test_parse_hostsfile_with_end_of_line_comment() {
let mock_hosts_file_content = "127.0.0.1 foo.bar.com # line ending comment\n127.0.0.2 servo.test.server #comment";
let mock_hosts_file_content =
"127.0.0.1 foo.bar.com # line ending comment\n127.0.0.2 servo.test.server #comment";
let hosts_table = parse_hostsfile(mock_hosts_file_content);
assert_eq!(2, hosts_table.len());
assert_eq!(ip("127.0.0.1"), *hosts_table.get("foo.bar.com").unwrap());
assert_eq!(ip("127.0.0.2"), *hosts_table.get("servo.test.server").unwrap());
assert_eq!(
ip("127.0.0.2"),
*hosts_table.get("servo.test.server").unwrap()
);
}
#[test]
@ -86,12 +101,14 @@ fn test_parse_hostsfile_with_tabs_instead_spaces() {
let hosts_table = parse_hostsfile(mock_hosts_file_content);
assert_eq!(2, hosts_table.len());
assert_eq!(ip("127.0.0.1"), *hosts_table.get("foo.bar.com").unwrap());
assert_eq!(ip("127.0.0.2"), *hosts_table.get("servo.test.server").unwrap());
assert_eq!(
ip("127.0.0.2"),
*hosts_table.get("servo.test.server").unwrap()
);
}
#[test]
fn test_parse_hostsfile_with_valid_ipv4_addresses()
{
fn test_parse_hostsfile_with_valid_ipv4_addresses() {
let mock_hosts_file_content =
"255.255.255.255 foo.bar.com\n169.0.1.201 servo.test.server\n192.168.5.0 servo.foo.com";
let hosts_table = parse_hostsfile(mock_hosts_file_content);
@ -99,8 +116,7 @@ fn test_parse_hostsfile_with_valid_ipv4_addresses()
}
#[test]
fn test_parse_hostsfile_with_invalid_ipv4_addresses()
{
fn test_parse_hostsfile_with_invalid_ipv4_addresses() {
let mock_hosts_file_content = "256.255.255.255 foo.bar.com\n169.0.1000.201 servo.test.server \
\n192.168.5.500 servo.foo.com\n192.abc.100.2 test.servo.com";
let hosts_table = parse_hostsfile(mock_hosts_file_content);
@ -108,8 +124,7 @@ fn test_parse_hostsfile_with_invalid_ipv4_addresses()
}
#[test]
fn test_parse_hostsfile_with_valid_ipv6_addresses()
{
fn test_parse_hostsfile_with_valid_ipv6_addresses() {
let mock_hosts_file_content = "2001:0db8:0000:0000:0000:ff00:0042:8329 foo.bar.com\n\
2001:db8:0:0:0:ff00:42:8329 moz.foo.com\n\
2001:db8::ff00:42:8329 foo.moz.com moz.moz.com\n\
@ -122,8 +137,7 @@ fn test_parse_hostsfile_with_valid_ipv6_addresses()
}
#[test]
fn test_parse_hostsfile_with_invalid_ipv6_addresses()
{
fn test_parse_hostsfile_with_invalid_ipv6_addresses() {
let mock_hosts_file_content = "12001:0db8:0000:0000:0000:ff00:0042:8329 foo.bar.com\n\
2001:zdb8:0:0:0:gg00:42:t329 moz.foo.com\n\
2002:0DB8:85A3:0042:1000:8A2E:0370:7334/1289 baz3.bar.moz";
@ -132,14 +146,19 @@ fn test_parse_hostsfile_with_invalid_ipv6_addresses()
}
#[test]
fn test_parse_hostsfile_with_end_of_line_whitespace()
{
fn test_parse_hostsfile_with_end_of_line_whitespace() {
let mock_hosts_file_content = "127.0.0.1 foo.bar.com \n\
2001:db8:0:0:0:ff00:42:8329 moz.foo.com\n \
127.0.0.2 servo.test.server ";
let hosts_table = parse_hostsfile(mock_hosts_file_content);
assert_eq!(3, hosts_table.len());
assert_eq!(ip("127.0.0.1"), *hosts_table.get("foo.bar.com").unwrap());
assert_eq!(ip("2001:db8:0:0:0:ff00:42:8329"), *hosts_table.get("moz.foo.com").unwrap());
assert_eq!(ip("127.0.0.2"), *hosts_table.get("servo.test.server").unwrap());
assert_eq!(
ip("2001:db8:0:0:0:ff00:42:8329"),
*hosts_table.get("moz.foo.com").unwrap()
);
assert_eq!(
ip("127.0.0.2"),
*hosts_table.get("servo.test.server").unwrap()
);
}

View file

@ -72,7 +72,8 @@ fn test_response_integrity_valid() {
let url: ServoUrl = ServoUrl::parse("http://servo.org").unwrap();
let response: Response = Response::new(url);
let integrity_metadata = "sha384-H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H16dQZngK7T62em8MUt1FLm52t+eX6xO";
let integrity_metadata =
"sha384-H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H16dQZngK7T62em8MUt1FLm52t+eX6xO";
let response_body = "alert('Hello, world.');".to_owned().into_bytes();
*response.body.lock().unwrap() = ResponseBody::Done(response_body);
@ -84,7 +85,8 @@ fn test_response_integrity_invalid() {
let url: ServoUrl = ServoUrl::parse("http://servo.org").unwrap();
let response: Response = Response::new(url);
let integrity_metadata = "sha256-H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H16dQZngK7T62em8MUt1FLm52t+eX6xO";
let integrity_metadata =
"sha256-H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H16dQZngK7T62em8MUt1FLm52t+eX6xO";
let response_body = "alert('Hello, world.');".to_owned().into_bytes();
*response.body.lock().unwrap() = ResponseBody::Done(response_body);

View file

@ -51,20 +51,25 @@ impl<'a> Factory for Client<'a> {
}
}
impl<'a> Handler for Client<'a> {
fn build_request(&mut self, url: &Url) -> WebSocketResult<Request> {
let mut req = Request::from_url(url)?;
req.headers_mut().push(("Origin".to_string(), self.origin.as_bytes().to_owned()));
req.headers_mut().push(("Host".to_string(), format!("{}", self.host).as_bytes().to_owned()));
req.headers_mut()
.push(("Origin".to_string(), self.origin.as_bytes().to_owned()));
req.headers_mut().push((
"Host".to_string(),
format!("{}", self.host).as_bytes().to_owned(),
));
for protocol in self.protocols {
req.add_protocol(protocol);
};
}
let mut cookie_jar = self.http_state.cookie_jar.write().unwrap();
if let Some(cookie_list) = cookie_jar.cookies_for_url(self.resource_url, CookieSource::HTTP) {
req.headers_mut().push(("Cookie".into(), cookie_list.as_bytes().to_owned()))
if let Some(cookie_list) = cookie_jar.cookies_for_url(self.resource_url, CookieSource::HTTP)
{
req.headers_mut()
.push(("Cookie".into(), cookie_list.as_bytes().to_owned()))
}
Ok(req)
@ -83,41 +88,48 @@ impl<'a> Handler for Client<'a> {
// TODO(eijebong): Replace thise once typed headers settled on a cookie impl
for cookie in headers.get_all(header::SET_COOKIE) {
if let Ok(s) = cookie.to_str() {
if let Some(cookie) = Cookie::from_cookie_string(s.into(), self.resource_url, CookieSource::HTTP) {
if let Some(cookie) =
Cookie::from_cookie_string(s.into(), self.resource_url, CookieSource::HTTP)
{
jar.push(cookie, self.resource_url, CookieSource::HTTP);
}
}
}
let _ = self.event_sender.send(
WebSocketNetworkEvent::ConnectionEstablished { protocol_in_use: self.protocol_in_use.clone() });
let _ = self
.event_sender
.send(WebSocketNetworkEvent::ConnectionEstablished {
protocol_in_use: self.protocol_in_use.clone(),
});
Ok(())
}
fn on_message(&mut self, message: Message) -> WebSocketResult<()> {
fn on_message(&mut self, message: Message) -> WebSocketResult<()> {
let message = match message {
Message::Text(message) => MessageData::Text(message),
Message::Binary(message) => MessageData::Binary(message),
};
let _ = self.event_sender.send(WebSocketNetworkEvent::MessageReceived(message));
let _ = self
.event_sender
.send(WebSocketNetworkEvent::MessageReceived(message));
Ok(())
}
fn on_error(&mut self, err: WebSocketError) {
debug!("Error in WebSocket communication: {:?}", err);
let _ = self.event_sender.send(WebSocketNetworkEvent::Fail);
}
fn on_response(&mut self, res: &WsResponse) -> WebSocketResult<()> {
let protocol_in_use = res.protocol()?;
if let Some(protocol_name) = protocol_in_use {
if !self.protocols.is_empty() && !self.protocols.iter().any(|p| protocol_name == (*p)) {
let error = WebSocketError::new(WebSocketErrorKind::Protocol,
"Protocol in Use not in client-supplied protocol list");
let error = WebSocketError::new(
WebSocketErrorKind::Protocol,
"Protocol in Use not in client-supplied protocol list",
);
return Err(error);
}
self.protocol_in_use = Some(protocol_name.into());
@ -127,7 +139,10 @@ impl<'a> Handler for Client<'a> {
fn on_close(&mut self, code: CloseCode, reason: &str) {
debug!("Connection closing due to ({:?}) {}", code, reason);
let _ = self.event_sender.send(WebSocketNetworkEvent::Close(Some(code.into()), reason.to_owned()));
let _ = self.event_sender.send(WebSocketNetworkEvent::Close(
Some(code.into()),
reason.to_owned(),
));
}
fn upgrade_ssl_client(
@ -136,106 +151,120 @@ impl<'a> Handler for Client<'a> {
url: &Url,
) -> WebSocketResult<SslStream<TcpStream>> {
let certs = match opts::get().certificate_path {
Some(ref path) => {
fs::read_to_string(path).expect("Couldn't not find certificate file")
}
None => {
resources::read_string(Resource::SSLCertificates)
},
Some(ref path) => fs::read_to_string(path).expect("Couldn't not find certificate file"),
None => resources::read_string(Resource::SSLCertificates),
};
let domain = self.resource_url.as_url().domain().ok_or(WebSocketError::new(
WebSocketErrorKind::Protocol,
format!("Unable to parse domain from {}. Needed for SSL.", url),
))?;
let domain = self
.resource_url
.as_url()
.domain()
.ok_or(WebSocketError::new(
WebSocketErrorKind::Protocol,
format!("Unable to parse domain from {}. Needed for SSL.", url),
))?;
let connector = create_ssl_connector_builder(&certs).build();
connector.connect(domain, stream).map_err(WebSocketError::from)
connector
.connect(domain, stream)
.map_err(WebSocketError::from)
}
}
pub fn init(
req_init: RequestInit,
resource_event_sender: IpcSender<WebSocketNetworkEvent>,
dom_action_receiver: IpcReceiver<WebSocketDomAction>,
http_state: Arc<HttpState>
http_state: Arc<HttpState>,
) {
thread::Builder::new().name(format!("WebSocket connection to {}", req_init.url)).spawn(move || {
let protocols = match req_init.mode {
RequestMode::WebSocket { protocols } => protocols.clone(),
_ => panic!("Received a RequestInit with a non-websocket mode in websocket_loader"),
};
thread::Builder::new()
.name(format!("WebSocket connection to {}", req_init.url))
.spawn(move || {
let protocols = match req_init.mode {
RequestMode::WebSocket { protocols } => protocols.clone(),
_ => panic!("Received a RequestInit with a non-websocket mode in websocket_loader"),
};
let scheme = req_init.url.scheme();
let mut req_url = req_init.url.clone();
if scheme == "ws" {
req_url.as_mut_url().set_scheme("http").unwrap();
} else if scheme == "wss" {
req_url.as_mut_url().set_scheme("https").unwrap();
}
if should_be_blocked_due_to_bad_port(&req_url) {
debug!("Failed to establish a WebSocket connection: port blocked");
let _ = resource_event_sender.send(WebSocketNetworkEvent::Fail);
return;
}
let host = replace_host(req_init.url.host_str().unwrap());
let mut net_url = req_init.url.clone().into_url();
net_url.set_host(Some(&host)).unwrap();
let host = Host::from(
format!("{}{}", req_init.url.host_str().unwrap(),
req_init.url.port_or_known_default().map(|v| format!(":{}", v)).unwrap_or("".into())
).parse::<Authority>().unwrap()
);
let client = Client {
origin: &req_init.origin.ascii_serialization(),
host: &host,
protocols: &protocols,
http_state: &http_state,
resource_url: &req_init.url,
event_sender: &resource_event_sender,
protocol_in_use: None,
};
let mut ws = WebSocket::new(client).unwrap();
if let Err(e) = ws.connect(net_url) {
debug!("Failed to establish a WebSocket connection: {:?}", e);
return;
};
let ws_sender = ws.broadcaster();
let initiated_close = Arc::new(AtomicBool::new(false));
thread::spawn(move || {
while let Ok(dom_action) = dom_action_receiver.recv() {
match dom_action {
WebSocketDomAction::SendMessage(MessageData::Text(data)) => {
ws_sender.send(Message::text(data)).unwrap();
},
WebSocketDomAction::SendMessage(MessageData::Binary(data)) => {
ws_sender.send(Message::binary(data)).unwrap();
},
WebSocketDomAction::Close(code, reason) => {
if !initiated_close.fetch_or(true, Ordering::SeqCst) {
match code {
Some(code) => {
ws_sender.close_with_reason(code.into(), reason.unwrap_or("".to_owned())).unwrap()
},
None => ws_sender.close(CloseCode::Status).unwrap(),
};
}
},
}
let scheme = req_init.url.scheme();
let mut req_url = req_init.url.clone();
if scheme == "ws" {
req_url.as_mut_url().set_scheme("http").unwrap();
} else if scheme == "wss" {
req_url.as_mut_url().set_scheme("https").unwrap();
}
});
if let Err(e) = ws.run() {
debug!("Failed to run WebSocket: {:?}", e);
let _ = resource_event_sender.send(WebSocketNetworkEvent::Fail);
};
}).expect("Thread spawning failed");
if should_be_blocked_due_to_bad_port(&req_url) {
debug!("Failed to establish a WebSocket connection: port blocked");
let _ = resource_event_sender.send(WebSocketNetworkEvent::Fail);
return;
}
let host = replace_host(req_init.url.host_str().unwrap());
let mut net_url = req_init.url.clone().into_url();
net_url.set_host(Some(&host)).unwrap();
let host = Host::from(
format!(
"{}{}",
req_init.url.host_str().unwrap(),
req_init
.url
.port_or_known_default()
.map(|v| format!(":{}", v))
.unwrap_or("".into())
)
.parse::<Authority>()
.unwrap(),
);
let client = Client {
origin: &req_init.origin.ascii_serialization(),
host: &host,
protocols: &protocols,
http_state: &http_state,
resource_url: &req_init.url,
event_sender: &resource_event_sender,
protocol_in_use: None,
};
let mut ws = WebSocket::new(client).unwrap();
if let Err(e) = ws.connect(net_url) {
debug!("Failed to establish a WebSocket connection: {:?}", e);
return;
};
let ws_sender = ws.broadcaster();
let initiated_close = Arc::new(AtomicBool::new(false));
thread::spawn(move || {
while let Ok(dom_action) = dom_action_receiver.recv() {
match dom_action {
WebSocketDomAction::SendMessage(MessageData::Text(data)) => {
ws_sender.send(Message::text(data)).unwrap();
},
WebSocketDomAction::SendMessage(MessageData::Binary(data)) => {
ws_sender.send(Message::binary(data)).unwrap();
},
WebSocketDomAction::Close(code, reason) => {
if !initiated_close.fetch_or(true, Ordering::SeqCst) {
match code {
Some(code) => ws_sender
.close_with_reason(
code.into(),
reason.unwrap_or("".to_owned()),
)
.unwrap(),
None => ws_sender.close(CloseCode::Status).unwrap(),
};
}
},
}
}
});
if let Err(e) = ws.run() {
debug!("Failed to run WebSocket: {:?}", e);
let _ = resource_event_sender.send(WebSocketNetworkEvent::Fail);
};
})
.expect("Thread spawning failed");
}

View file

@ -114,21 +114,46 @@ pub struct SelectedFile {
#[derive(Debug, Deserialize, Serialize)]
pub enum FileManagerThreadMsg {
/// Select a single file. Last field is pre-selected file path for testing
SelectFile(Vec<FilterPattern>, IpcSender<FileManagerResult<SelectedFile>>, FileOrigin, Option<String>),
SelectFile(
Vec<FilterPattern>,
IpcSender<FileManagerResult<SelectedFile>>,
FileOrigin,
Option<String>,
),
/// Select multiple files. Last field is pre-selected file paths for testing
SelectFiles(Vec<FilterPattern>, IpcSender<FileManagerResult<Vec<SelectedFile>>>, FileOrigin, Option<Vec<String>>),
SelectFiles(
Vec<FilterPattern>,
IpcSender<FileManagerResult<Vec<SelectedFile>>>,
FileOrigin,
Option<Vec<String>>,
),
/// Read FileID-indexed file in chunks, optionally check URL validity based on boolean flag
ReadFile(IpcSender<FileManagerResult<ReadFileProgress>>, Uuid, bool, FileOrigin),
ReadFile(
IpcSender<FileManagerResult<ReadFileProgress>>,
Uuid,
bool,
FileOrigin,
),
/// Add an entry as promoted memory-based blob and send back the associated FileID
/// as part of a valid/invalid Blob URL depending on the boolean flag
PromoteMemory(BlobBuf, bool, IpcSender<Result<Uuid, BlobURLStoreError>>, FileOrigin),
PromoteMemory(
BlobBuf,
bool,
IpcSender<Result<Uuid, BlobURLStoreError>>,
FileOrigin,
),
/// Add a sliced entry pointing to the parent FileID, and send back the associated FileID
/// as part of a valid Blob URL
AddSlicedURLEntry(Uuid, RelativePos, IpcSender<Result<Uuid, BlobURLStoreError>>, FileOrigin),
AddSlicedURLEntry(
Uuid,
RelativePos,
IpcSender<Result<Uuid, BlobURLStoreError>>,
FileOrigin,
),
/// Decrease reference count and send back the acknowledgement
DecRef(Uuid, FileOrigin, IpcSender<Result<(), BlobURLStoreError>>),

View file

@ -33,8 +33,11 @@ pub struct Image {
impl fmt::Debug for Image {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Image {{ width: {}, height: {}, format: {:?}, ..., id: {:?} }}",
self.width, self.height, self.format, self.id)
write!(
f,
"Image {{ width: {}, height: {}, format: {:?}, ..., id: {:?} }}",
self.width, self.height, self.format, self.id
)
}
}
@ -58,32 +61,29 @@ pub fn load_from_memory(buffer: &[u8]) -> Option<Image> {
debug!("{}", msg);
None
},
Ok(_) => {
match piston_image::load_from_memory(buffer) {
Ok(image) => {
let mut rgba = match image {
DynamicImage::ImageRgba8(rgba) => rgba,
image => image.to_rgba(),
};
pixels::byte_swap_colors_inplace(&mut *rgba);
Some(Image {
width: rgba.width(),
height: rgba.height(),
format: PixelFormat::BGRA8,
bytes: IpcSharedMemory::from_bytes(&*rgba),
id: None,
})
},
Err(e) => {
debug!("Image decoding error: {:?}", e);
None
},
}
Ok(_) => match piston_image::load_from_memory(buffer) {
Ok(image) => {
let mut rgba = match image {
DynamicImage::ImageRgba8(rgba) => rgba,
image => image.to_rgba(),
};
pixels::byte_swap_colors_inplace(&mut *rgba);
Some(Image {
width: rgba.width(),
height: rgba.height(),
format: PixelFormat::BGRA8,
bytes: IpcSharedMemory::from_bytes(&*rgba),
id: None,
})
},
Err(e) => {
debug!("Image decoding error: {:?}", e);
None
},
},
}
}
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img
pub fn detect_image_format(buffer: &[u8]) -> Result<ImageFormat, &str> {
if is_gif(buffer) {

View file

@ -101,16 +101,19 @@ pub enum UsePlaceholder {
// ======================================================================
pub trait ImageCache: Sync + Send {
fn new(webrender_api: webrender_api::RenderApi) -> Self where Self: Sized;
fn new(webrender_api: webrender_api::RenderApi) -> Self
where
Self: Sized;
/// Return any available metadata or image for the given URL,
/// or an indication that the image is not yet available if it is in progress,
/// or else reserve a slot in the cache for the URL if the consumer can request images.
fn find_image_or_metadata(&self,
url: ServoUrl,
use_placeholder: UsePlaceholder,
can_request: CanRequestImages)
-> Result<ImageOrMetadataAvailable, ImageState>;
fn find_image_or_metadata(
&self,
url: ServoUrl,
use_placeholder: UsePlaceholder,
can_request: CanRequestImages,
) -> Result<ImageOrMetadataAvailable, ImageState>;
/// Add a new listener for the given pending image id. If the image is already present,
/// the responder will still receive the expected response.

View file

@ -2,7 +2,6 @@
* 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/. */
#![deny(unsafe_code)]
extern crate cookie as cookie_rs;
@ -14,18 +13,24 @@ extern crate hyper;
extern crate hyper_serde;
extern crate image as piston_image;
extern crate ipc_channel;
#[macro_use] extern crate lazy_static;
#[macro_use] extern crate log;
#[macro_use] extern crate malloc_size_of;
#[macro_use] extern crate malloc_size_of_derive;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
#[macro_use]
extern crate malloc_size_of;
#[macro_use]
extern crate malloc_size_of_derive;
extern crate mime;
extern crate msg;
extern crate num_traits;
extern crate pixels;
#[macro_use] extern crate serde;
#[macro_use]
extern crate serde;
extern crate servo_arc;
extern crate servo_url;
#[macro_use] extern crate url;
#[macro_use]
extern crate url;
extern crate uuid;
extern crate webrender_api;
@ -86,18 +91,26 @@ pub enum LoadContext {
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
pub struct CustomResponse {
#[ignore_malloc_size_of = "Defined in hyper"]
#[serde(deserialize_with = "::hyper_serde::deserialize",
serialize_with = "::hyper_serde::serialize")]
#[serde(
deserialize_with = "::hyper_serde::deserialize",
serialize_with = "::hyper_serde::serialize"
)]
pub headers: HeaderMap,
#[ignore_malloc_size_of = "Defined in hyper"]
#[serde(deserialize_with = "::hyper_serde::deserialize",
serialize_with = "::hyper_serde::serialize")]
#[serde(
deserialize_with = "::hyper_serde::deserialize",
serialize_with = "::hyper_serde::serialize"
)]
pub raw_status: (StatusCode, String),
pub body: Vec<u8>,
}
impl CustomResponse {
pub fn new(headers: HeaderMap, raw_status: (StatusCode, String), body: Vec<u8>) -> CustomResponse {
pub fn new(
headers: HeaderMap,
raw_status: (StatusCode, String),
body: Vec<u8>,
) -> CustomResponse {
CustomResponse {
headers: headers,
raw_status: raw_status,
@ -137,22 +150,18 @@ pub enum ReferrerPolicy {
impl From<ReferrerPolicyHeader> for ReferrerPolicy {
fn from(policy: ReferrerPolicyHeader) -> Self {
match policy {
ReferrerPolicyHeader::NO_REFERRER =>
ReferrerPolicy::NoReferrer,
ReferrerPolicyHeader::NO_REFERRER_WHEN_DOWNGRADE =>
ReferrerPolicy::NoReferrerWhenDowngrade,
ReferrerPolicyHeader::SAME_ORIGIN =>
ReferrerPolicy::SameOrigin,
ReferrerPolicyHeader::ORIGIN =>
ReferrerPolicy::Origin,
ReferrerPolicyHeader::ORIGIN_WHEN_CROSS_ORIGIN =>
ReferrerPolicy::OriginWhenCrossOrigin,
ReferrerPolicyHeader::UNSAFE_URL =>
ReferrerPolicy::UnsafeUrl,
ReferrerPolicyHeader::STRICT_ORIGIN =>
ReferrerPolicy::StrictOrigin,
ReferrerPolicyHeader::STRICT_ORIGIN_WHEN_CROSS_ORIGIN =>
ReferrerPolicy::StrictOriginWhenCrossOrigin,
ReferrerPolicyHeader::NO_REFERRER => ReferrerPolicy::NoReferrer,
ReferrerPolicyHeader::NO_REFERRER_WHEN_DOWNGRADE => {
ReferrerPolicy::NoReferrerWhenDowngrade
},
ReferrerPolicyHeader::SAME_ORIGIN => ReferrerPolicy::SameOrigin,
ReferrerPolicyHeader::ORIGIN => ReferrerPolicy::Origin,
ReferrerPolicyHeader::ORIGIN_WHEN_CROSS_ORIGIN => ReferrerPolicy::OriginWhenCrossOrigin,
ReferrerPolicyHeader::UNSAFE_URL => ReferrerPolicy::UnsafeUrl,
ReferrerPolicyHeader::STRICT_ORIGIN => ReferrerPolicy::StrictOrigin,
ReferrerPolicyHeader::STRICT_ORIGIN_WHEN_CROSS_ORIGIN => {
ReferrerPolicy::StrictOriginWhenCrossOrigin
},
}
}
}
@ -198,7 +207,7 @@ pub enum FilteredMetadata {
Basic(Metadata),
Cors(Metadata),
Opaque,
OpaqueRedirect
OpaqueRedirect,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
@ -244,7 +253,6 @@ impl FetchTaskTarget for IpcSender<FetchResponseMsg> {
}
}
pub trait Action<Listener> {
fn process(self, listener: &mut Listener);
}
@ -271,7 +279,8 @@ pub type IpcSendResult = Result<(), IpcError>;
/// used by net_traits::ResourceThreads to ease the use its IpcSender sub-fields
/// XXX: If this trait will be used more in future, some auto derive might be appealing
pub trait IpcSend<T>
where T: serde::Serialize + for<'de> serde::Deserialize<'de>,
where
T: serde::Serialize + for<'de> serde::Deserialize<'de>,
{
/// send message T
fn send(&self, T) -> IpcSendResult;
@ -342,9 +351,7 @@ pub enum WebSocketDomAction {
#[derive(Debug, Deserialize, Serialize)]
pub enum WebSocketNetworkEvent {
ConnectionEstablished {
protocol_in_use: Option<String>,
},
ConnectionEstablished { protocol_in_use: Option<String> },
MessageReceived(MessageData),
Close(Option<u16>, String),
Fail,
@ -353,18 +360,26 @@ pub enum WebSocketNetworkEvent {
#[derive(Debug, Deserialize, Serialize)]
/// IPC channels to communicate with the script thread about network or DOM events.
pub enum FetchChannels {
ResponseMsg(IpcSender<FetchResponseMsg>, /* cancel_chan */ Option<IpcReceiver<()>>),
ResponseMsg(
IpcSender<FetchResponseMsg>,
/* cancel_chan */ Option<IpcReceiver<()>>,
),
WebSocket {
event_sender: IpcSender<WebSocketNetworkEvent>,
action_receiver: IpcReceiver<WebSocketDomAction>,
}
},
}
#[derive(Debug, Deserialize, Serialize)]
pub enum CoreResourceMsg {
Fetch(RequestInit, FetchChannels),
/// Initiate a fetch in response to processing a redirection
FetchRedirect(RequestInit, ResponseInit, IpcSender<FetchResponseMsg>, /* cancel_chan */ Option<IpcReceiver<()>>),
FetchRedirect(
RequestInit,
ResponseInit,
IpcSender<FetchResponseMsg>,
/* cancel_chan */ Option<IpcReceiver<()>>,
),
/// Store a cookie for a given originating URL
SetCookieForUrl(ServoUrl, Serde<Cookie<'static>>, CookieSource),
/// Store a set of cookies for a given originating URL
@ -372,7 +387,11 @@ pub enum CoreResourceMsg {
/// Retrieve the stored cookies for a given URL
GetCookiesForUrl(ServoUrl, IpcSender<Option<String>>, CookieSource),
/// Get a cookie by name for a given originating URL
GetCookiesDataForUrl(ServoUrl, IpcSender<Vec<Serde<Cookie<'static>>>>, CookieSource),
GetCookiesDataForUrl(
ServoUrl,
IpcSender<Vec<Serde<Cookie<'static>>>>,
CookieSource,
),
/// Get a history state by a given history state id
GetHistoryState(HistoryStateId, IpcSender<Option<Vec<u8>>>),
/// Set a history state for a given history state id
@ -392,13 +411,20 @@ pub enum CoreResourceMsg {
/// Instruct the resource thread to make a new request.
pub fn fetch_async<F>(request: RequestInit, core_resource_thread: &CoreResourceThread, f: F)
where F: Fn(FetchResponseMsg) + Send + 'static,
where
F: Fn(FetchResponseMsg) + Send + 'static,
{
let (action_sender, action_receiver) = ipc::channel().unwrap();
ROUTER.add_route(action_receiver.to_opaque(),
Box::new(move |message| f(message.to().unwrap())));
core_resource_thread.send(
CoreResourceMsg::Fetch(request, FetchChannels::ResponseMsg(action_sender, None))).unwrap();
ROUTER.add_route(
action_receiver.to_opaque(),
Box::new(move |message| f(message.to().unwrap())),
);
core_resource_thread
.send(CoreResourceMsg::Fetch(
request,
FetchChannels::ResponseMsg(action_sender, None),
))
.unwrap();
}
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
@ -466,7 +492,10 @@ impl Metadata {
}
if let Some(mime) = content_type {
self.headers.as_mut().unwrap().typed_insert(ContentType::from(mime.clone()));
self.headers
.as_mut()
.unwrap()
.typed_insert(ContentType::from(mime.clone()));
self.content_type = Some(Serde(ContentType::from(mime.clone())));
for (name, value) in mime.params() {
if mime::CHARSET == name {
@ -487,19 +516,23 @@ pub enum CookieSource {
}
/// Convenience function for synchronously loading a whole resource.
pub fn load_whole_resource(request: RequestInit,
core_resource_thread: &CoreResourceThread)
-> Result<(Metadata, Vec<u8>), NetworkError> {
pub fn load_whole_resource(
request: RequestInit,
core_resource_thread: &CoreResourceThread,
) -> Result<(Metadata, Vec<u8>), NetworkError> {
let (action_sender, action_receiver) = ipc::channel().unwrap();
core_resource_thread.send(
CoreResourceMsg::Fetch(request, FetchChannels::ResponseMsg(action_sender, None))).unwrap();
core_resource_thread
.send(CoreResourceMsg::Fetch(
request,
FetchChannels::ResponseMsg(action_sender, None),
))
.unwrap();
let mut buf = vec![];
let mut metadata = None;
loop {
match action_receiver.recv().unwrap() {
FetchResponseMsg::ProcessRequestBody |
FetchResponseMsg::ProcessRequestEOF => (),
FetchResponseMsg::ProcessRequestBody | FetchResponseMsg::ProcessRequestEOF => (),
FetchResponseMsg::ProcessResponse(Ok(m)) => {
metadata = Some(match m {
FetchMetadata::Unfiltered(m) => m,
@ -534,7 +567,6 @@ impl NetworkError {
}
}
/// Normalize `slice`, as defined by
/// [the Fetch Spec](https://fetch.spec.whatwg.org/#concept-header-value-normalize).
pub fn trim_http_whitespace(mut slice: &[u8]) -> &[u8] {
@ -569,4 +601,3 @@ pub fn http_percent_encode(bytes: &[u8]) -> String {
url::percent_encoding::percent_encode(bytes, HTTP_VALUE).to_string()
}

View file

@ -32,7 +32,8 @@ lazy_static! {
impl<'a> FromIterator<&'a str> for PubDomainRules {
fn from_iter<T>(iter: T) -> Self
where T: IntoIterator<Item = &'a str>,
where
T: IntoIterator<Item = &'a str>,
{
let mut result = PubDomainRules::new();
for item in iter {
@ -57,7 +58,8 @@ impl PubDomainRules {
}
}
pub fn parse(content: &str) -> PubDomainRules {
content.lines()
content
.lines()
.map(str::trim)
.filter(|s| !s.is_empty())
.filter(|s| !s.starts_with("//"))
@ -99,7 +101,7 @@ impl PubDomainRules {
None => !domain.is_empty(),
Some(index) => {
!self.exceptions.contains(domain) && self.wildcards.contains(&domain[index + 1..]) ||
self.rules.contains(domain)
self.rules.contains(domain)
},
}
}
@ -112,8 +114,9 @@ impl PubDomainRules {
None => false,
Some(index) => {
self.exceptions.contains(domain) ||
!self.wildcards.contains(&domain[index + 1..]) && !self.rules.contains(domain) &&
self.is_public_suffix(&domain[index + 1..])
!self.wildcards.contains(&domain[index + 1..]) &&
!self.rules.contains(domain) &&
self.is_public_suffix(&domain[index + 1..])
},
}
}
@ -145,7 +148,9 @@ pub fn is_reg_domain(domain: &str) -> bool {
/// Leaves the host name alone if it is an IP address.
pub fn reg_host(url: &ServoUrl) -> Option<Host> {
match url.origin() {
ImmutableOrigin::Tuple(_, Host::Domain(domain), _) => Some(Host::Domain(String::from(reg_suffix(&*domain)))),
ImmutableOrigin::Tuple(_, Host::Domain(domain), _) => {
Some(Host::Domain(String::from(reg_suffix(&*domain))))
},
ImmutableOrigin::Tuple(_, ip, _) => Some(ip),
ImmutableOrigin::Opaque(_) => None,
}

View file

@ -70,11 +70,17 @@ where
let s = str::from_utf8(&digits[..]).unwrap();
fmt.write_str(s.trim_right_matches('0'))
}
},
}
}
}
pub fn quality_to_value(q: Vec<QualityItem<Mime>>) -> HeaderValue {
HeaderValue::from_str(&q.iter().map(|q| q.to_string()).collect::<Vec<String>>().join(", ")).unwrap()
HeaderValue::from_str(
&q.iter()
.map(|q| q.to_string())
.collect::<Vec<String>>()
.join(", "),
)
.unwrap()
}

View file

@ -46,9 +46,9 @@ impl Destination {
#[inline]
pub fn is_script_like(&self) -> bool {
*self == Destination::Script ||
*self == Destination::ServiceWorker ||
*self == Destination::SharedWorker ||
*self == Destination::Worker
*self == Destination::ServiceWorker ||
*self == Destination::SharedWorker ||
*self == Destination::Worker
}
}
@ -75,7 +75,7 @@ pub enum RequestMode {
SameOrigin,
NoCors,
CorsMode,
WebSocket { protocols: Vec<String> }
WebSocket { protocols: Vec<String> },
}
/// Request [credentials mode](https://fetch.spec.whatwg.org/#concept-request-credentials-mode)
@ -137,13 +137,17 @@ pub enum CorsSettings {
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
pub struct RequestInit {
#[serde(deserialize_with = "::hyper_serde::deserialize",
serialize_with = "::hyper_serde::serialize")]
#[serde(
deserialize_with = "::hyper_serde::deserialize",
serialize_with = "::hyper_serde::serialize"
)]
#[ignore_malloc_size_of = "Defined in hyper"]
pub method: Method,
pub url: ServoUrl,
#[serde(deserialize_with = "::hyper_serde::deserialize",
serialize_with = "::hyper_serde::serialize")]
#[serde(
deserialize_with = "::hyper_serde::deserialize",
serialize_with = "::hyper_serde::serialize"
)]
#[ignore_malloc_size_of = "Defined in hyper"]
pub headers: HeaderMap,
pub unsafe_request: bool,
@ -259,10 +263,7 @@ pub struct Request {
}
impl Request {
pub fn new(url: ServoUrl,
origin: Option<Origin>,
pipeline_id: Option<PipelineId>)
-> Request {
pub fn new(url: ServoUrl, origin: Option<Origin>, pipeline_id: Option<PipelineId>) -> Request {
Request {
method: Method::GET,
local_urls_only: false,
@ -294,9 +295,11 @@ impl Request {
}
pub fn from_init(init: RequestInit) -> Request {
let mut req = Request::new(init.url.clone(),
Some(Origin::Origin(init.origin)),
init.pipeline_id);
let mut req = Request::new(
init.url.clone(),
Some(Origin::Origin(init.origin)),
init.pipeline_id,
);
req.method = init.method;
req.headers = init.headers;
req.unsafe_request = init.unsafe_request;
@ -350,9 +353,16 @@ impl Request {
/// <https://fetch.spec.whatwg.org/#subresource-request>
pub fn is_subresource_request(&self) -> bool {
match self.destination {
Destination::Audio | Destination::Font | Destination::Image | Destination::Manifest |
Destination::Script | Destination::Style | Destination::Track | Destination::Video |
Destination::Xslt | Destination::None => true,
Destination::Audio |
Destination::Font |
Destination::Image |
Destination::Manifest |
Destination::Script |
Destination::Style |
Destination::Track |
Destination::Video |
Destination::Xslt |
Destination::None => true,
_ => false,
}
}

View file

@ -46,13 +46,11 @@ impl ResponseBody {
pub fn is_done(&self) -> bool {
match *self {
ResponseBody::Done(..) => true,
ResponseBody::Empty |
ResponseBody::Receiving(..) => false,
ResponseBody::Empty | ResponseBody::Receiving(..) => false,
}
}
}
/// [Cache state](https://fetch.spec.whatwg.org/#concept-response-cache-state)
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
pub enum CacheState {
@ -79,8 +77,10 @@ pub enum ResponseMsg {
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
pub struct ResponseInit {
pub url: ServoUrl,
#[serde(deserialize_with = "::hyper_serde::deserialize",
serialize_with = "::hyper_serde::serialize")]
#[serde(
deserialize_with = "::hyper_serde::deserialize",
serialize_with = "::hyper_serde::serialize"
)]
#[ignore_malloc_size_of = "Defined in hyper"]
pub headers: HeaderMap,
pub status_code: u16,
@ -149,7 +149,9 @@ impl Response {
res.location_url = init.location_url;
res.headers = init.headers;
res.referrer = init.referrer;
res.status = StatusCode::from_u16(init.status_code).map(|s| (s, s.to_string())).ok();
res.status = StatusCode::from_u16(init.status_code)
.map(|s| (s, s.to_string()))
.ok();
res
}
@ -292,7 +294,13 @@ impl Response {
pub fn metadata(&self) -> Result<FetchMetadata, NetworkError> {
fn init_metadata(response: &Response, url: &ServoUrl) -> Metadata {
let mut metadata = Metadata::default(url.clone());
metadata.set_content_type(response.headers.typed_get::<ContentType>().map(|v| v.into()).as_ref());
metadata.set_content_type(
response
.headers
.typed_get::<ContentType>()
.map(|v| v.into())
.as_ref(),
);
metadata.location_url = response.location_url.clone();
metadata.headers = Some(Serde(response.headers.clone()));
metadata.status = response.raw_status.clone();
@ -316,26 +324,27 @@ impl Response {
match self.response_type {
ResponseType::Basic => Ok(FetchMetadata::Filtered {
filtered: FilteredMetadata::Basic(metadata.unwrap()),
unsafe_: unsafe_metadata
unsafe_: unsafe_metadata,
}),
ResponseType::Cors => Ok(FetchMetadata::Filtered {
filtered: FilteredMetadata::Cors(metadata.unwrap()),
unsafe_: unsafe_metadata
unsafe_: unsafe_metadata,
}),
ResponseType::Default => unreachable!(),
ResponseType::Error(ref network_err) =>
Err(network_err.clone()),
ResponseType::Error(ref network_err) => Err(network_err.clone()),
ResponseType::Opaque => Ok(FetchMetadata::Filtered {
filtered: FilteredMetadata::Opaque,
unsafe_: unsafe_metadata
unsafe_: unsafe_metadata,
}),
ResponseType::OpaqueRedirect => Ok(FetchMetadata::Filtered {
filtered: FilteredMetadata::OpaqueRedirect,
unsafe_: unsafe_metadata
})
unsafe_: unsafe_metadata,
}),
}
},
None => Err(NetworkError::Internal("No url found in unsafe response".to_owned()))
None => Err(NetworkError::Internal(
"No url found in unsafe response".to_owned(),
)),
}
} else {
assert_eq!(self.response_type, ResponseType::Default);

View file

@ -27,7 +27,13 @@ pub enum StorageThreadMsg {
GetItem(IpcSender<Option<String>>, ServoUrl, StorageType, String),
/// sets the value of the given key in the associated storage data
SetItem(IpcSender<Result<(bool, Option<String>), ()>>, ServoUrl, StorageType, String, String),
SetItem(
IpcSender<Result<(bool, Option<String>), ()>>,
ServoUrl,
StorageType,
String,
String,
),
/// removes the key/value pair for the given key in the associated storage data
RemoveItem(IpcSender<Option<String>>, ServoUrl, StorageType, String),

View file

@ -116,6 +116,9 @@ fn test_reg_suffix() {
#[test]
fn test_weirdness() {
// These are weird results, but AFAICT they are spec-compliant.
assert_ne!(pub_suffix("city.yokohama.jp"), pub_suffix(pub_suffix("city.yokohama.jp")));
assert_ne!(
pub_suffix("city.yokohama.jp"),
pub_suffix(pub_suffix("city.yokohama.jp"))
);
assert!(!is_pub_domain(pub_suffix("city.yokohama.jp")));
}