mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
Rustfmt net crate
This commit is contained in:
parent
ba1ed11ced
commit
2481ad25f8
30 changed files with 4957 additions and 2870 deletions
|
@ -19,16 +19,16 @@ use servo_url::ServoUrl;
|
||||||
|
|
||||||
/// https://fetch.spec.whatwg.org/#concept-basic-fetch (partial)
|
/// https://fetch.spec.whatwg.org/#concept-basic-fetch (partial)
|
||||||
// TODO: make async.
|
// TODO: make async.
|
||||||
pub fn load_blob_sync
|
pub fn load_blob_sync(
|
||||||
(url: ServoUrl,
|
url: ServoUrl,
|
||||||
filemanager: FileManager)
|
filemanager: FileManager,
|
||||||
-> Result<(HeaderMap, Vec<u8>), NetworkError> {
|
) -> Result<(HeaderMap, Vec<u8>), NetworkError> {
|
||||||
let (id, origin) = match parse_blob_url(&url) {
|
let (id, origin) = match parse_blob_url(&url) {
|
||||||
Ok((id, origin)) => (id, origin),
|
Ok((id, origin)) => (id, origin),
|
||||||
Err(()) => {
|
Err(()) => {
|
||||||
let e = format!("Invalid blob URL format {:?}", url);
|
let e = format!("Invalid blob URL format {:?}", url);
|
||||||
return Err(NetworkError::Internal(e));
|
return Err(NetworkError::Internal(e));
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let (sender, receiver) = ipc::channel().unwrap();
|
let (sender, receiver) = ipc::channel().unwrap();
|
||||||
|
@ -38,11 +38,13 @@ pub fn load_blob_sync
|
||||||
let blob_buf = match receiver.recv().unwrap() {
|
let blob_buf = match receiver.recv().unwrap() {
|
||||||
Ok(ReadFileProgress::Meta(blob_buf)) => blob_buf,
|
Ok(ReadFileProgress::Meta(blob_buf)) => blob_buf,
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
return Err(NetworkError::Internal("Invalid filemanager reply".to_string()));
|
return Err(NetworkError::Internal(
|
||||||
}
|
"Invalid filemanager reply".to_string(),
|
||||||
|
));
|
||||||
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(NetworkError::Internal(format!("{:?}", e)));
|
return Err(NetworkError::Internal(format!("{:?}", e)));
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let content_type: Mime = blob_buf.type_string.parse().unwrap_or(mime::TEXT_PLAIN);
|
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();
|
let mut headers = HeaderMap::new();
|
||||||
|
|
||||||
if let Some(name) = blob_buf.filename {
|
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
|
// TODO(eijebong): Replace this once the typed header is there
|
||||||
headers.insert(
|
headers.insert(
|
||||||
header::CONTENT_DISPOSITION,
|
header::CONTENT_DISPOSITION,
|
||||||
HeaderValue::from_bytes(
|
HeaderValue::from_bytes(
|
||||||
format!("inline; {}",
|
format!(
|
||||||
|
"inline; {}",
|
||||||
if charset.to_lowercase() == "utf-8" {
|
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 {
|
} 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() {
|
match receiver.recv().unwrap() {
|
||||||
Ok(ReadFileProgress::Partial(ref mut new_bytes)) => {
|
Ok(ReadFileProgress::Partial(ref mut new_bytes)) => {
|
||||||
bytes.append(new_bytes);
|
bytes.append(new_bytes);
|
||||||
}
|
},
|
||||||
Ok(ReadFileProgress::EOF) => {
|
Ok(ReadFileProgress::EOF) => {
|
||||||
return Ok((headers, bytes));
|
return Ok((headers, bytes));
|
||||||
}
|
},
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
return Err(NetworkError::Internal("Invalid filemanager reply".to_string()));
|
return Err(NetworkError::Internal(
|
||||||
}
|
"Invalid filemanager reply".to_string(),
|
||||||
|
));
|
||||||
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(NetworkError::Internal(format!("{:?}", e)));
|
return Err(NetworkError::Internal(format!("{:?}", e)));
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ use tokio::prelude::future::Executor;
|
||||||
pub const BUF_SIZE: usize = 32768;
|
pub const BUF_SIZE: usize = 32768;
|
||||||
|
|
||||||
pub struct HttpConnector {
|
pub struct HttpConnector {
|
||||||
inner: HyperHttpConnector
|
inner: HyperHttpConnector,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HttpConnector {
|
impl HttpConnector {
|
||||||
|
@ -28,9 +28,7 @@ impl HttpConnector {
|
||||||
let mut inner = HyperHttpConnector::new(4);
|
let mut inner = HyperHttpConnector::new(4);
|
||||||
inner.enforce_http(false);
|
inner.enforce_http(false);
|
||||||
inner.set_happy_eyeballs_timeout(None);
|
inner.set_happy_eyeballs_timeout(None);
|
||||||
HttpConnector {
|
HttpConnector { inner }
|
||||||
inner
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,10 +58,7 @@ impl WrappedBody {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_with_decoder(body: Body, decoder: Decoder) -> Self {
|
pub fn new_with_decoder(body: Body, decoder: Decoder) -> Self {
|
||||||
WrappedBody {
|
WrappedBody { body, decoder }
|
||||||
body,
|
|
||||||
decoder,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +85,7 @@ impl Stream for WrappedBody {
|
||||||
let len = decoder.read(&mut buf).ok()?;
|
let len = decoder.read(&mut buf).ok()?;
|
||||||
buf.truncate(len);
|
buf.truncate(len);
|
||||||
Some(buf.into())
|
Some(buf.into())
|
||||||
}
|
},
|
||||||
Decoder::Gzip(None) => {
|
Decoder::Gzip(None) => {
|
||||||
let mut buf = vec![0; BUF_SIZE];
|
let mut buf = vec![0; BUF_SIZE];
|
||||||
let mut decoder = GzDecoder::new(Cursor::new(chunk.into_bytes()));
|
let mut decoder = GzDecoder::new(Cursor::new(chunk.into_bytes()));
|
||||||
|
@ -98,21 +93,21 @@ impl Stream for WrappedBody {
|
||||||
buf.truncate(len);
|
buf.truncate(len);
|
||||||
self.decoder = Decoder::Gzip(Some(decoder));
|
self.decoder = Decoder::Gzip(Some(decoder));
|
||||||
Some(buf.into())
|
Some(buf.into())
|
||||||
}
|
},
|
||||||
Decoder::Deflate(ref mut decoder) => {
|
Decoder::Deflate(ref mut decoder) => {
|
||||||
let mut buf = vec![0; BUF_SIZE];
|
let mut buf = vec![0; BUF_SIZE];
|
||||||
*decoder.get_mut() = Cursor::new(chunk.into_bytes());
|
*decoder.get_mut() = Cursor::new(chunk.into_bytes());
|
||||||
let len = decoder.read(&mut buf).ok()?;
|
let len = decoder.read(&mut buf).ok()?;
|
||||||
buf.truncate(len);
|
buf.truncate(len);
|
||||||
Some(buf.into())
|
Some(buf.into())
|
||||||
}
|
},
|
||||||
Decoder::Brotli(ref mut decoder) => {
|
Decoder::Brotli(ref mut decoder) => {
|
||||||
let mut buf = vec![0; BUF_SIZE];
|
let mut buf = vec![0; BUF_SIZE];
|
||||||
decoder.get_mut().get_mut().extend(&chunk.into_bytes());
|
decoder.get_mut().get_mut().extend(&chunk.into_bytes());
|
||||||
let len = decoder.read(&mut buf).ok()?;
|
let len = decoder.read(&mut buf).ok()?;
|
||||||
buf.truncate(len);
|
buf.truncate(len);
|
||||||
Some(buf.into())
|
Some(buf.into())
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -134,7 +129,10 @@ pub fn create_ssl_connector_builder(certs: &str) -> SslConnectorBuilder {
|
||||||
let (cert, rest) = certs.split_at(index + token.len());
|
let (cert, rest) = certs.split_at(index + token.len());
|
||||||
certs = rest;
|
certs = rest;
|
||||||
let cert = x509::X509::from_pem(cert.as_bytes()).unwrap();
|
let cert = x509::X509::from_pem(cert.as_bytes()).unwrap();
|
||||||
ssl_connector_builder.cert_store_mut().add_cert(cert).or_else(|e| {
|
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());
|
let v: Option<Option<&str>> = e.errors().iter().nth(0).map(|e| e.reason());
|
||||||
if v == Some(Some("cert already in hash table")) {
|
if v == Some(Some("cert already in hash table")) {
|
||||||
warn!("Cert already in hash table. Ignoring.");
|
warn!("Cert already in hash table. Ignoring.");
|
||||||
|
@ -144,23 +142,33 @@ pub fn create_ssl_connector_builder(certs: &str) -> SslConnectorBuilder {
|
||||||
} else {
|
} else {
|
||||||
Err(e)
|
Err(e)
|
||||||
}
|
}
|
||||||
}).expect("could not set CA file");
|
})
|
||||||
|
.expect("could not set CA file");
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ssl_connector_builder.set_cipher_list(DEFAULT_CIPHERS).expect("could not set ciphers");
|
ssl_connector_builder
|
||||||
ssl_connector_builder.set_options(SslOptions::NO_SSLV2 | SslOptions::NO_SSLV3 | SslOptions::NO_COMPRESSION);
|
.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
|
ssl_connector_builder
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_http_client<E>(ssl_connector_builder: SslConnectorBuilder, executor: E)
|
pub fn create_http_client<E>(
|
||||||
-> Client<Connector, WrappedBody>
|
ssl_connector_builder: SslConnectorBuilder,
|
||||||
|
executor: E,
|
||||||
|
) -> Client<Connector, WrappedBody>
|
||||||
where
|
where
|
||||||
E: Executor<Box<Future<Error=(), Item=()> + Send + 'static>> + Sync + Send + 'static
|
E: Executor<Box<Future<Error = (), Item = ()> + Send + 'static>> + Sync + Send + 'static,
|
||||||
{
|
{
|
||||||
let connector = HttpsConnector::with_connector(HttpConnector::new(), ssl_connector_builder).unwrap();
|
let connector =
|
||||||
Client::builder().http1_title_case_headers(true).executor(executor).build(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
|
// The basic logic here is to prefer ciphers with ECDSA certificates, Forward
|
||||||
|
|
|
@ -19,23 +19,32 @@ use time::{Tm, now, at, Duration};
|
||||||
/// which cookie-rs and hyper's header parsing do not support.
|
/// which cookie-rs and hyper's header parsing do not support.
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
pub struct Cookie {
|
pub struct Cookie {
|
||||||
#[serde(deserialize_with = "hyper_serde::deserialize",
|
#[serde(
|
||||||
serialize_with = "hyper_serde::serialize")]
|
deserialize_with = "hyper_serde::deserialize",
|
||||||
|
serialize_with = "hyper_serde::serialize"
|
||||||
|
)]
|
||||||
pub cookie: cookie_rs::Cookie<'static>,
|
pub cookie: cookie_rs::Cookie<'static>,
|
||||||
pub host_only: bool,
|
pub host_only: bool,
|
||||||
pub persistent: bool,
|
pub persistent: bool,
|
||||||
#[serde(deserialize_with = "hyper_serde::deserialize",
|
#[serde(
|
||||||
serialize_with = "hyper_serde::serialize")]
|
deserialize_with = "hyper_serde::deserialize",
|
||||||
|
serialize_with = "hyper_serde::serialize"
|
||||||
|
)]
|
||||||
pub creation_time: Tm,
|
pub creation_time: Tm,
|
||||||
#[serde(deserialize_with = "hyper_serde::deserialize",
|
#[serde(
|
||||||
serialize_with = "hyper_serde::serialize")]
|
deserialize_with = "hyper_serde::deserialize",
|
||||||
|
serialize_with = "hyper_serde::serialize"
|
||||||
|
)]
|
||||||
pub last_access: Tm,
|
pub last_access: Tm,
|
||||||
pub expiry_time: Option<Serde<Tm>>,
|
pub expiry_time: Option<Serde<Tm>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cookie {
|
impl Cookie {
|
||||||
pub fn from_cookie_string(cookie_str: String, request: &ServoUrl,
|
pub fn from_cookie_string(
|
||||||
source: CookieSource) -> Option<Cookie> {
|
cookie_str: String,
|
||||||
|
request: &ServoUrl,
|
||||||
|
source: CookieSource,
|
||||||
|
) -> Option<Cookie> {
|
||||||
cookie_rs::Cookie::parse(cookie_str)
|
cookie_rs::Cookie::parse(cookie_str)
|
||||||
.ok()
|
.ok()
|
||||||
.map(|cookie| Cookie::new_wrapped(cookie, request, source))
|
.map(|cookie| Cookie::new_wrapped(cookie, request, source))
|
||||||
|
@ -43,15 +52,21 @@ impl Cookie {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <http://tools.ietf.org/html/rfc6265#section-5.3>
|
/// <http://tools.ietf.org/html/rfc6265#section-5.3>
|
||||||
pub fn new_wrapped(mut cookie: cookie_rs::Cookie<'static>, request: &ServoUrl, source: CookieSource)
|
pub fn new_wrapped(
|
||||||
-> Option<Cookie> {
|
mut cookie: cookie_rs::Cookie<'static>,
|
||||||
|
request: &ServoUrl,
|
||||||
|
source: CookieSource,
|
||||||
|
) -> Option<Cookie> {
|
||||||
// Step 3
|
// Step 3
|
||||||
let (persistent, expiry_time) = match (cookie.max_age(), cookie.expires()) {
|
let (persistent, expiry_time) = match (cookie.max_age(), cookie.expires()) {
|
||||||
(Some(max_age), _) => {
|
(Some(max_age), _) => (
|
||||||
(true, Some(at(now().to_timespec() + Duration::seconds(max_age.num_seconds()))))
|
true,
|
||||||
}
|
Some(at(
|
||||||
|
now().to_timespec() + Duration::seconds(max_age.num_seconds())
|
||||||
|
)),
|
||||||
|
),
|
||||||
(_, Some(expires)) => (true, Some(expires)),
|
(_, Some(expires)) => (true, Some(expires)),
|
||||||
_ => (false, None)
|
_ => (false, None),
|
||||||
};
|
};
|
||||||
|
|
||||||
let url_host = request.host_str().unwrap_or("").to_owned();
|
let url_host = request.host_str().unwrap_or("").to_owned();
|
||||||
|
@ -64,7 +79,7 @@ impl Cookie {
|
||||||
if domain == url_host {
|
if domain == url_host {
|
||||||
domain = "".to_string();
|
domain = "".to_string();
|
||||||
} else {
|
} else {
|
||||||
return None
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,16 +98,18 @@ impl Cookie {
|
||||||
|
|
||||||
// Step 7
|
// Step 7
|
||||||
let mut has_path_specified = true;
|
let mut has_path_specified = true;
|
||||||
let mut path = cookie.path().unwrap_or_else(|| {
|
let mut path = cookie
|
||||||
|
.path()
|
||||||
|
.unwrap_or_else(|| {
|
||||||
has_path_specified = false;
|
has_path_specified = false;
|
||||||
""
|
""
|
||||||
}).to_owned();
|
})
|
||||||
|
.to_owned();
|
||||||
if path.chars().next() != Some('/') {
|
if path.chars().next() != Some('/') {
|
||||||
path = Cookie::default_path(&request.path().to_owned()).to_string();
|
path = Cookie::default_path(&request.path().to_owned()).to_string();
|
||||||
}
|
}
|
||||||
cookie.set_path(path);
|
cookie.set_path(path);
|
||||||
|
|
||||||
|
|
||||||
// Step 10
|
// Step 10
|
||||||
if cookie.http_only().unwrap_or(false) && source == CookieSource::NonHTTP {
|
if cookie.http_only().unwrap_or(false) && source == CookieSource::NonHTTP {
|
||||||
return None;
|
return None;
|
||||||
|
@ -152,8 +169,8 @@ impl Cookie {
|
||||||
|
|
||||||
// The cookie-path and the request-path are identical.
|
// The cookie-path and the request-path are identical.
|
||||||
request_path == cookie_path ||
|
request_path == cookie_path ||
|
||||||
|
(request_path.starts_with(cookie_path) &&
|
||||||
(request_path.starts_with(cookie_path) && (
|
(
|
||||||
// The cookie-path is a prefix of the request-path, and the last
|
// The cookie-path is a prefix of the request-path, and the last
|
||||||
// character of the cookie-path is %x2F ("/").
|
// character of the cookie-path is %x2F ("/").
|
||||||
cookie_path.ends_with("/") ||
|
cookie_path.ends_with("/") ||
|
||||||
|
|
|
@ -33,7 +33,12 @@ impl CookieStorage {
|
||||||
}
|
}
|
||||||
|
|
||||||
// http://tools.ietf.org/html/rfc6265#section-5.3
|
// 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 domain = reg_host(cookie.cookie.domain().as_ref().unwrap_or(&""));
|
||||||
let cookies = self.cookies_map.entry(domain).or_insert(vec![]);
|
let cookies = self.cookies_map.entry(domain).or_insert(vec![]);
|
||||||
|
|
||||||
|
@ -111,7 +116,9 @@ impl CookieStorage {
|
||||||
let new_len = cookies.len();
|
let new_len = cookies.len();
|
||||||
|
|
||||||
// https://www.ietf.org/id/draft-ietf-httpbis-cookie-alone-01.txt
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,7 +133,7 @@ impl CookieStorage {
|
||||||
let a_creation_time = a.creation_time.to_timespec();
|
let a_creation_time = a.creation_time.to_timespec();
|
||||||
let b_creation_time = b.creation_time.to_timespec();
|
let b_creation_time = b.creation_time.to_timespec();
|
||||||
a_creation_time.cmp(&b_creation_time)
|
a_creation_time.cmp(&b_creation_time)
|
||||||
}
|
},
|
||||||
// Ensure that longer paths are sorted earlier than shorter paths
|
// Ensure that longer paths are sorted earlier than shorter paths
|
||||||
Ordering::Greater => Ordering::Less,
|
Ordering::Greater => Ordering::Less,
|
||||||
Ordering::Less => Ordering::Greater,
|
Ordering::Less => Ordering::Greater,
|
||||||
|
@ -136,13 +143,17 @@ impl CookieStorage {
|
||||||
// http://tools.ietf.org/html/rfc6265#section-5.4
|
// http://tools.ietf.org/html/rfc6265#section-5.4
|
||||||
pub fn cookies_for_url(&mut self, url: &ServoUrl, source: CookieSource) -> Option<String> {
|
pub fn cookies_for_url(&mut self, url: &ServoUrl, source: CookieSource) -> Option<String> {
|
||||||
let filterer = |c: &&mut Cookie| -> bool {
|
let filterer = |c: &&mut Cookie| -> bool {
|
||||||
info!(" === SENT COOKIE : {} {} {:?} {:?}",
|
info!(
|
||||||
|
" === SENT COOKIE : {} {} {:?} {:?}",
|
||||||
c.cookie.name(),
|
c.cookie.name(),
|
||||||
c.cookie.value(),
|
c.cookie.value(),
|
||||||
c.cookie.domain(),
|
c.cookie.domain(),
|
||||||
c.cookie.path());
|
c.cookie.path()
|
||||||
info!(" === SENT COOKIE RESULT {}",
|
);
|
||||||
c.appropriate_for_url(url, source));
|
info!(
|
||||||
|
" === SENT COOKIE RESULT {}",
|
||||||
|
c.appropriate_for_url(url, source)
|
||||||
|
);
|
||||||
// Step 1
|
// Step 1
|
||||||
c.appropriate_for_url(url, source)
|
c.appropriate_for_url(url, source)
|
||||||
};
|
};
|
||||||
|
@ -161,7 +172,9 @@ impl CookieStorage {
|
||||||
(match acc.len() {
|
(match acc.len() {
|
||||||
0 => acc,
|
0 => acc,
|
||||||
_ => acc + "; ",
|
_ => acc + "; ",
|
||||||
}) + &c.cookie.name() + "=" + &c.cookie.value()
|
}) + &c.cookie.name() +
|
||||||
|
"=" +
|
||||||
|
&c.cookie.value()
|
||||||
};
|
};
|
||||||
let result = url_cookies.iter_mut().fold("".to_owned(), reducer);
|
let result = url_cookies.iter_mut().fold("".to_owned(), reducer);
|
||||||
|
|
||||||
|
@ -172,14 +185,18 @@ impl CookieStorage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cookies_data_for_url<'a>(&'a mut self,
|
pub fn cookies_data_for_url<'a>(
|
||||||
|
&'a mut self,
|
||||||
url: &'a ServoUrl,
|
url: &'a ServoUrl,
|
||||||
source: CookieSource)
|
source: CookieSource,
|
||||||
-> impl Iterator<Item = cookie_rs::Cookie<'static>> + 'a {
|
) -> impl Iterator<Item = cookie_rs::Cookie<'static>> + 'a {
|
||||||
let domain = reg_host(url.host_str().unwrap_or(""));
|
let domain = reg_host(url.host_str().unwrap_or(""));
|
||||||
let cookies = self.cookies_map.entry(domain).or_insert(vec![]);
|
let cookies = self.cookies_map.entry(domain).or_insert(vec![]);
|
||||||
|
|
||||||
cookies.iter_mut().filter(move |c| c.appropriate_for_url(url, source)).map(|c| {
|
cookies
|
||||||
|
.iter_mut()
|
||||||
|
.filter(move |c| c.appropriate_for_url(url, source))
|
||||||
|
.map(|c| {
|
||||||
c.touch();
|
c.touch();
|
||||||
c.cookie.clone()
|
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;
|
let mut oldest_accessed: Option<(usize, Tm)> = None;
|
||||||
for (i, c) in cookies.iter().enumerate() {
|
for (i, c) in cookies.iter().enumerate() {
|
||||||
if (c.cookie.secure().unwrap_or(false) == is_secure_cookie) &&
|
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));
|
oldest_accessed = Some((i, c.last_access));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,9 @@ pub type DecodeData = (Mime, Vec<u8>);
|
||||||
pub fn decode(url: &ServoUrl) -> Result<DecodeData, DecodeError> {
|
pub fn decode(url: &ServoUrl) -> Result<DecodeData, DecodeError> {
|
||||||
assert_eq!(url.scheme(), "data");
|
assert_eq!(url.scheme(), "data");
|
||||||
// Split out content type and 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 {
|
if parts.len() != 2 {
|
||||||
return Err(DecodeError::InvalidDataUri);
|
return Err(DecodeError::InvalidDataUri);
|
||||||
}
|
}
|
||||||
|
@ -36,15 +38,18 @@ pub fn decode(url: &ServoUrl) -> Result<DecodeData, DecodeError> {
|
||||||
ct_str.to_owned()
|
ct_str.to_owned()
|
||||||
};
|
};
|
||||||
|
|
||||||
let content_type = ct_str.parse().unwrap_or_else(|_| {
|
let content_type = ct_str
|
||||||
"text/plain; charset=US-ASCII".parse().unwrap()
|
.parse()
|
||||||
});
|
.unwrap_or_else(|_| "text/plain; charset=US-ASCII".parse().unwrap());
|
||||||
|
|
||||||
let mut bytes = percent_decode(parts[1].as_bytes()).collect::<Vec<_>>();
|
let mut bytes = percent_decode(parts[1].as_bytes()).collect::<Vec<_>>();
|
||||||
if is_base64 {
|
if is_base64 {
|
||||||
// FIXME(#2909): It’s unclear what to do with non-alphabet characters,
|
// FIXME(#2909): It’s unclear what to do with non-alphabet characters,
|
||||||
// but Acid 3 apparently depends on spaces being ignored.
|
// 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) {
|
match base64::decode(&bytes) {
|
||||||
Err(..) => return Err(DecodeError::NonBase64DataUri),
|
Err(..) => return Err(DecodeError::NonBase64DataUri),
|
||||||
Ok(data) => bytes = data,
|
Ok(data) => bytes = data,
|
||||||
|
|
|
@ -21,21 +21,21 @@ use time::{self, Timespec};
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum HeaderOrMethod {
|
pub enum HeaderOrMethod {
|
||||||
HeaderData(HeaderName),
|
HeaderData(HeaderName),
|
||||||
MethodData(Method)
|
MethodData(Method),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HeaderOrMethod {
|
impl HeaderOrMethod {
|
||||||
fn match_header(&self, header_name: &HeaderName) -> bool {
|
fn match_header(&self, header_name: &HeaderName) -> bool {
|
||||||
match *self {
|
match *self {
|
||||||
HeaderOrMethod::HeaderData(ref n) => n == header_name,
|
HeaderOrMethod::HeaderData(ref n) => n == header_name,
|
||||||
_ => false
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn match_method(&self, method: &Method) -> bool {
|
fn match_method(&self, method: &Method) -> bool {
|
||||||
match *self {
|
match *self {
|
||||||
HeaderOrMethod::MethodData(ref m) => m == method,
|
HeaderOrMethod::MethodData(ref m) => m == method,
|
||||||
_ => false
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,19 +48,24 @@ pub struct CorsCacheEntry {
|
||||||
pub max_age: u32,
|
pub max_age: u32,
|
||||||
pub credentials: bool,
|
pub credentials: bool,
|
||||||
pub header_or_method: HeaderOrMethod,
|
pub header_or_method: HeaderOrMethod,
|
||||||
created: Timespec
|
created: Timespec,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CorsCacheEntry {
|
impl CorsCacheEntry {
|
||||||
fn new(origin: Origin, url: ServoUrl, max_age: u32, credentials: bool,
|
fn new(
|
||||||
header_or_method: HeaderOrMethod) -> CorsCacheEntry {
|
origin: Origin,
|
||||||
|
url: ServoUrl,
|
||||||
|
max_age: u32,
|
||||||
|
credentials: bool,
|
||||||
|
header_or_method: HeaderOrMethod,
|
||||||
|
) -> CorsCacheEntry {
|
||||||
CorsCacheEntry {
|
CorsCacheEntry {
|
||||||
origin: origin,
|
origin: origin,
|
||||||
url: url,
|
url: url,
|
||||||
max_age: max_age,
|
max_age: max_age,
|
||||||
credentials: credentials,
|
credentials: credentials,
|
||||||
header_or_method: header_or_method,
|
header_or_method: header_or_method,
|
||||||
created: time::now().to_timespec()
|
created: time::now().to_timespec(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,25 +85,36 @@ impl CorsCache {
|
||||||
CorsCache(vec![])
|
CorsCache(vec![])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_entry_by_header<'a>(&'a mut self, request: &Request,
|
fn find_entry_by_header<'a>(
|
||||||
header_name: &HeaderName) -> Option<&'a mut CorsCacheEntry> {
|
&'a mut self,
|
||||||
|
request: &Request,
|
||||||
|
header_name: &HeaderName,
|
||||||
|
) -> Option<&'a mut CorsCacheEntry> {
|
||||||
self.cleanup();
|
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,
|
fn find_entry_by_method<'a>(
|
||||||
method: Method) -> Option<&'a mut CorsCacheEntry> {
|
&'a mut self,
|
||||||
|
request: &Request,
|
||||||
|
method: Method,
|
||||||
|
) -> Option<&'a mut CorsCacheEntry> {
|
||||||
// we can take the method from CorSRequest itself
|
// we can take the method from CorSRequest itself
|
||||||
self.cleanup();
|
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)
|
/// [Clear the cache](https://fetch.spec.whatwg.org/#concept-cache-clear)
|
||||||
pub fn clear(&mut self, request: &Request) {
|
pub fn clear(&mut self, request: &Request) {
|
||||||
let CorsCache(buf) = self.clone();
|
let CorsCache(buf) = self.clone();
|
||||||
let new_buf: Vec<CorsCacheEntry> =
|
let new_buf: Vec<CorsCacheEntry> = buf
|
||||||
buf.into_iter().filter(|e| e.origin == request.origin &&
|
.into_iter()
|
||||||
request.current_url() == e.url).collect();
|
.filter(|e| e.origin == request.origin && request.current_url() == e.url)
|
||||||
|
.collect();
|
||||||
*self = CorsCache(new_buf);
|
*self = CorsCache(new_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +122,8 @@ impl CorsCache {
|
||||||
pub fn cleanup(&mut self) {
|
pub fn cleanup(&mut self) {
|
||||||
let CorsCache(buf) = self.clone();
|
let CorsCache(buf) = self.clone();
|
||||||
let now = time::now().to_timespec();
|
let now = time::now().to_timespec();
|
||||||
let new_buf: Vec<CorsCacheEntry> = buf.into_iter()
|
let new_buf: Vec<CorsCacheEntry> = buf
|
||||||
|
.into_iter()
|
||||||
.filter(|e| now.sec < e.created.sec + e.max_age as i64)
|
.filter(|e| now.sec < e.created.sec + e.max_age as i64)
|
||||||
.collect();
|
.collect();
|
||||||
*self = CorsCache(new_buf);
|
*self = CorsCache(new_buf);
|
||||||
|
@ -122,16 +139,27 @@ impl CorsCache {
|
||||||
/// [matching header](https://fetch.spec.whatwg.org/#concept-cache-match-header) is found.
|
/// [matching header](https://fetch.spec.whatwg.org/#concept-cache-match-header) is found.
|
||||||
///
|
///
|
||||||
/// If not, it will insert an equivalent entry
|
/// If not, it will insert an equivalent entry
|
||||||
pub fn match_header_and_update(&mut self, request: &Request,
|
pub fn match_header_and_update(
|
||||||
header_name: &HeaderName, new_max_age: u32) -> bool {
|
&mut self,
|
||||||
match self.find_entry_by_header(&request, header_name).map(|e| e.max_age = new_max_age) {
|
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,
|
Some(_) => true,
|
||||||
None => {
|
None => {
|
||||||
self.insert(CorsCacheEntry::new(request.origin.clone(), request.current_url(), new_max_age,
|
self.insert(CorsCacheEntry::new(
|
||||||
|
request.origin.clone(),
|
||||||
|
request.current_url(),
|
||||||
|
new_max_age,
|
||||||
request.credentials_mode == CredentialsMode::Include,
|
request.credentials_mode == CredentialsMode::Include,
|
||||||
HeaderOrMethod::HeaderData(header_name.clone())));
|
HeaderOrMethod::HeaderData(header_name.clone()),
|
||||||
|
));
|
||||||
false
|
false
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,15 +173,27 @@ impl CorsCache {
|
||||||
/// [a matching method](https://fetch.spec.whatwg.org/#concept-cache-match-method) is found.
|
/// [a matching method](https://fetch.spec.whatwg.org/#concept-cache-match-method) is found.
|
||||||
///
|
///
|
||||||
/// If not, it will insert an equivalent entry
|
/// 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 {
|
pub fn match_method_and_update(
|
||||||
match self.find_entry_by_method(&request, method.clone()).map(|e| e.max_age = new_max_age) {
|
&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,
|
Some(_) => true,
|
||||||
None => {
|
None => {
|
||||||
self.insert(CorsCacheEntry::new(request.origin.clone(), request.current_url(), new_max_age,
|
self.insert(CorsCacheEntry::new(
|
||||||
|
request.origin.clone(),
|
||||||
|
request.current_url(),
|
||||||
|
new_max_age,
|
||||||
request.credentials_mode == CredentialsMode::Include,
|
request.credentials_mode == CredentialsMode::Include,
|
||||||
HeaderOrMethod::MethodData(method)));
|
HeaderOrMethod::MethodData(method),
|
||||||
|
));
|
||||||
false
|
false
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,8 @@ use std::thread;
|
||||||
use subresource_integrity::is_response_integrity_valid;
|
use subresource_integrity::is_response_integrity_valid;
|
||||||
|
|
||||||
lazy_static! {
|
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
|
const FILE_CHUNK_SIZE: usize = 32768; //32 KB
|
||||||
|
@ -87,16 +88,16 @@ impl CancellationListener {
|
||||||
pub type DoneChannel = Option<(Sender<Data>, Receiver<Data>)>;
|
pub type DoneChannel = Option<(Sender<Data>, Receiver<Data>)>;
|
||||||
|
|
||||||
/// [Fetch](https://fetch.spec.whatwg.org#concept-fetch)
|
/// [Fetch](https://fetch.spec.whatwg.org#concept-fetch)
|
||||||
pub fn fetch(request: &mut Request,
|
pub fn fetch(request: &mut Request, target: Target, context: &FetchContext) {
|
||||||
target: Target,
|
|
||||||
context: &FetchContext) {
|
|
||||||
fetch_with_cors_cache(request, &mut CorsCache::new(), target, context);
|
fetch_with_cors_cache(request, &mut CorsCache::new(), target, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fetch_with_cors_cache(request: &mut Request,
|
pub fn fetch_with_cors_cache(
|
||||||
|
request: &mut Request,
|
||||||
cache: &mut CorsCache,
|
cache: &mut CorsCache,
|
||||||
target: Target,
|
target: Target,
|
||||||
context: &FetchContext) {
|
context: &FetchContext,
|
||||||
|
) {
|
||||||
// Step 1.
|
// Step 1.
|
||||||
if request.window == Window::Client {
|
if request.window == Window::Client {
|
||||||
// TODO: Set window to request's client object if client is a Window object
|
// 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)
|
/// [Main fetch](https://fetch.spec.whatwg.org/#concept-main-fetch)
|
||||||
pub fn main_fetch(request: &mut Request,
|
pub fn main_fetch(
|
||||||
|
request: &mut Request,
|
||||||
cache: &mut CorsCache,
|
cache: &mut CorsCache,
|
||||||
cors_flag: bool,
|
cors_flag: bool,
|
||||||
recursive_flag: bool,
|
recursive_flag: bool,
|
||||||
target: Target,
|
target: Target,
|
||||||
done_chan: &mut DoneChannel,
|
done_chan: &mut DoneChannel,
|
||||||
context: &FetchContext)
|
context: &FetchContext,
|
||||||
-> Response {
|
) -> Response {
|
||||||
// Step 1.
|
// Step 1.
|
||||||
let mut response = None;
|
let mut response = None;
|
||||||
|
|
||||||
// Step 2.
|
// Step 2.
|
||||||
if request.local_urls_only {
|
if request.local_urls_only {
|
||||||
if !matches!(request.current_url().scheme(), "about" | "blob" | "data" | "filesystem") {
|
if !matches!(
|
||||||
response = Some(Response::network_error(NetworkError::Internal("Non-local scheme".into())));
|
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.
|
// Step 5.
|
||||||
if should_be_blocked_due_to_bad_port(&request.current_url()) {
|
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 as mixed content.
|
||||||
// TODO: handle blocking by content security policy.
|
// 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.
|
// TODO: handle request's client's referrer policy.
|
||||||
|
|
||||||
// Step 7.
|
// Step 7.
|
||||||
request.referrer_policy = request.referrer_policy.or(Some(ReferrerPolicy::NoReferrerWhenDowngrade));
|
request.referrer_policy = request
|
||||||
|
.referrer_policy
|
||||||
|
.or(Some(ReferrerPolicy::NoReferrerWhenDowngrade));
|
||||||
|
|
||||||
// Step 8.
|
// Step 8.
|
||||||
{
|
{
|
||||||
|
@ -182,11 +193,13 @@ pub fn main_fetch(request: &mut Request,
|
||||||
Referrer::ReferrerUrl(url) => {
|
Referrer::ReferrerUrl(url) => {
|
||||||
request.headers.remove(header::REFERER);
|
request.headers.remove(header::REFERER);
|
||||||
let current_url = request.current_url().clone();
|
let current_url = request.current_url().clone();
|
||||||
determine_request_referrer(&mut request.headers,
|
determine_request_referrer(
|
||||||
|
&mut request.headers,
|
||||||
request.referrer_policy.unwrap(),
|
request.referrer_policy.unwrap(),
|
||||||
url,
|
url,
|
||||||
current_url)
|
current_url,
|
||||||
}
|
)
|
||||||
|
},
|
||||||
};
|
};
|
||||||
if let Some(referrer_url) = referrer_url {
|
if let Some(referrer_url) = referrer_url {
|
||||||
request.referrer = Referrer::ReferrerUrl(referrer_url);
|
request.referrer = Referrer::ReferrerUrl(referrer_url);
|
||||||
|
@ -197,8 +210,12 @@ pub fn main_fetch(request: &mut Request,
|
||||||
// TODO: handle FTP URLs.
|
// TODO: handle FTP URLs.
|
||||||
|
|
||||||
// Step 10.
|
// Step 10.
|
||||||
context.state.hsts_list.read().unwrap().switch_known_hsts_host_domain_url_to_https(
|
context
|
||||||
request.current_url_mut());
|
.state
|
||||||
|
.hsts_list
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.switch_known_hsts_host_domain_url_to_https(request.current_url_mut());
|
||||||
|
|
||||||
// Step 11.
|
// Step 11.
|
||||||
// Not applicable: see fetch_async.
|
// 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
|
// and about: schemes, but CSS tests will break on loading Ahem
|
||||||
// since we load them through a file: URL.
|
// since we load them through a file: URL.
|
||||||
current_url.scheme() == "about" ||
|
current_url.scheme() == "about" ||
|
||||||
request.mode == RequestMode::Navigate {
|
request.mode == RequestMode::Navigate
|
||||||
|
{
|
||||||
// Substep 1.
|
// Substep 1.
|
||||||
request.response_tainting = ResponseTainting::Basic;
|
request.response_tainting = ResponseTainting::Basic;
|
||||||
|
|
||||||
// Substep 2.
|
// Substep 2.
|
||||||
scheme_fetch(request, cache, target, done_chan, context)
|
scheme_fetch(request, cache, target, done_chan, context)
|
||||||
|
|
||||||
} else if request.mode == RequestMode::SameOrigin {
|
} else if request.mode == RequestMode::SameOrigin {
|
||||||
Response::network_error(NetworkError::Internal("Cross-origin response".into()))
|
Response::network_error(NetworkError::Internal("Cross-origin response".into()))
|
||||||
|
|
||||||
} else if request.mode == RequestMode::NoCors {
|
} else if request.mode == RequestMode::NoCors {
|
||||||
// Substep 1.
|
// Substep 1.
|
||||||
request.response_tainting = ResponseTainting::Opaque;
|
request.response_tainting = ResponseTainting::Opaque;
|
||||||
|
|
||||||
// Substep 2.
|
// Substep 2.
|
||||||
scheme_fetch(request, cache, target, done_chan, context)
|
scheme_fetch(request, cache, target, done_chan, context)
|
||||||
|
|
||||||
} else if !matches!(current_url.scheme(), "http" | "https") {
|
} else if !matches!(current_url.scheme(), "http" | "https") {
|
||||||
Response::network_error(NetworkError::Internal("Non-http scheme".into()))
|
Response::network_error(NetworkError::Internal("Non-http scheme".into()))
|
||||||
|
|
||||||
} else if request.use_cors_preflight ||
|
} else if request.use_cors_preflight ||
|
||||||
(request.unsafe_request &&
|
(request.unsafe_request &&
|
||||||
(!is_cors_safelisted_method(&request.method) ||
|
(!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.
|
// Substep 1.
|
||||||
request.response_tainting = ResponseTainting::CorsTainting;
|
request.response_tainting = ResponseTainting::CorsTainting;
|
||||||
// Substep 2.
|
// Substep 2.
|
||||||
let response = http_fetch(request, cache, true, true, false,
|
let response = http_fetch(
|
||||||
target, done_chan, context);
|
request, cache, true, true, false, target, done_chan, context,
|
||||||
|
);
|
||||||
// Substep 3.
|
// Substep 3.
|
||||||
if response.is_network_error() {
|
if response.is_network_error() {
|
||||||
// TODO clear cache entries using request
|
// TODO clear cache entries using request
|
||||||
}
|
}
|
||||||
// Substep 4.
|
// Substep 4.
|
||||||
response
|
response
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Substep 1.
|
// Substep 1.
|
||||||
request.response_tainting = ResponseTainting::CorsTainting;
|
request.response_tainting = ResponseTainting::CorsTainting;
|
||||||
// Substep 2.
|
// 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.
|
// Substep 1.
|
||||||
if request.response_tainting == ResponseTainting::CorsTainting {
|
if request.response_tainting == ResponseTainting::CorsTainting {
|
||||||
// Subsubstep 1.
|
// 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());
|
.map(|v| v.iter().collect());
|
||||||
match header_names {
|
match header_names {
|
||||||
// Subsubstep 2.
|
// Subsubstep 2.
|
||||||
Some(ref list) if request.credentials_mode != CredentialsMode::Include => {
|
Some(ref list) if request.credentials_mode != CredentialsMode::Include => {
|
||||||
if list.len() == 1 && list[0] == "*" {
|
if list.len() == 1 && list[0] == "*" {
|
||||||
response.cors_exposed_header_name_list =
|
response.cors_exposed_header_name_list = response
|
||||||
response.headers.iter().map(|(name, _)| name.as_str().to_owned()).collect();
|
.headers
|
||||||
|
.iter()
|
||||||
|
.map(|(name, _)| name.as_str().to_owned())
|
||||||
|
.collect();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Subsubstep 3.
|
// Subsubstep 3.
|
||||||
Some(list) => {
|
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 = {
|
let internal_error = {
|
||||||
// Tests for steps 17 and 18, before step 15 for borrowing concerns.
|
// Tests for steps 17 and 18, before step 15 for borrowing concerns.
|
||||||
let response_is_network_error = response.is_network_error();
|
let response_is_network_error = response.is_network_error();
|
||||||
let should_replace_with_nosniff_error =
|
let should_replace_with_nosniff_error = !response_is_network_error &&
|
||||||
!response_is_network_error && should_be_blocked_due_to_nosniff(request.destination, &response.headers);
|
should_be_blocked_due_to_nosniff(request.destination, &response.headers);
|
||||||
let should_replace_with_mime_type_error =
|
let should_replace_with_mime_type_error = !response_is_network_error &&
|
||||||
!response_is_network_error && should_be_blocked_due_to_mime_type(request.destination, &response.headers);
|
should_be_blocked_due_to_mime_type(request.destination, &response.headers);
|
||||||
|
|
||||||
// Step 15.
|
// 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() {
|
let internal_response = if let Some(error_response) = network_error_response.as_mut() {
|
||||||
error_response
|
error_response
|
||||||
} else {
|
} else {
|
||||||
|
@ -326,14 +353,15 @@ pub fn main_fetch(request: &mut Request,
|
||||||
// TODO: handle blocking as mixed content.
|
// TODO: handle blocking as mixed content.
|
||||||
// TODO: handle blocking by content security policy.
|
// TODO: handle blocking by content security policy.
|
||||||
let blocked_error_response;
|
let blocked_error_response;
|
||||||
let internal_response =
|
let internal_response = if should_replace_with_nosniff_error {
|
||||||
if should_replace_with_nosniff_error {
|
|
||||||
// Defer rebinding result
|
// Defer rebinding result
|
||||||
blocked_error_response = Response::network_error(NetworkError::Internal("Blocked by nosniff".into()));
|
blocked_error_response =
|
||||||
|
Response::network_error(NetworkError::Internal("Blocked by nosniff".into()));
|
||||||
&blocked_error_response
|
&blocked_error_response
|
||||||
} else if should_replace_with_mime_type_error {
|
} else if should_replace_with_mime_type_error {
|
||||||
// Defer rebinding result
|
// Defer rebinding result
|
||||||
blocked_error_response = Response::network_error(NetworkError::Internal("Blocked by mime type".into()));
|
blocked_error_response =
|
||||||
|
Response::network_error(NetworkError::Internal("Blocked by mime type".into()));
|
||||||
&blocked_error_response
|
&blocked_error_response
|
||||||
} else {
|
} else {
|
||||||
internal_response
|
internal_response
|
||||||
|
@ -343,10 +371,12 @@ pub fn main_fetch(request: &mut Request,
|
||||||
// We check `internal_response` since we did not mutate `response`
|
// We check `internal_response` since we did not mutate `response`
|
||||||
// in the previous step.
|
// in the previous step.
|
||||||
let not_network_error = !response_is_network_error && !internal_response.is_network_error();
|
let not_network_error = !response_is_network_error && !internal_response.is_network_error();
|
||||||
if not_network_error && (is_null_body_status(&internal_response.status) ||
|
if not_network_error &&
|
||||||
|
(is_null_body_status(&internal_response.status) ||
|
||||||
match request.method {
|
match request.method {
|
||||||
Method::HEAD | Method::CONNECT => true,
|
Method::HEAD | Method::CONNECT => true,
|
||||||
_ => false }) {
|
_ => false,
|
||||||
|
}) {
|
||||||
// when Fetch is used only asynchronously, we will need to make sure
|
// when Fetch is used only asynchronously, we will need to make sure
|
||||||
// that nothing tries to write to the body at this point
|
// that nothing tries to write to the body at this point
|
||||||
let mut body = internal_response.body.lock().unwrap();
|
let mut body = internal_response.body.lock().unwrap();
|
||||||
|
@ -373,8 +403,11 @@ pub fn main_fetch(request: &mut Request,
|
||||||
// Step 19.2.
|
// Step 19.2.
|
||||||
let ref integrity_metadata = &request.integrity_metadata;
|
let ref integrity_metadata = &request.integrity_metadata;
|
||||||
if response.termination_reason.is_none() &&
|
if response.termination_reason.is_none() &&
|
||||||
!is_response_integrity_valid(integrity_metadata, &response) {
|
!is_response_integrity_valid(integrity_metadata, &response)
|
||||||
Response::network_error(NetworkError::Internal("Subresource integrity validation failed".into()))
|
{
|
||||||
|
Response::network_error(NetworkError::Internal(
|
||||||
|
"Subresource integrity validation failed".into(),
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
response
|
response
|
||||||
}
|
}
|
||||||
|
@ -430,8 +463,11 @@ pub fn main_fetch(request: &mut Request,
|
||||||
fn wait_for_response(response: &mut Response, target: Target, done_chan: &mut DoneChannel) {
|
fn wait_for_response(response: &mut Response, target: Target, done_chan: &mut DoneChannel) {
|
||||||
if let Some(ref ch) = *done_chan {
|
if let Some(ref ch) = *done_chan {
|
||||||
loop {
|
loop {
|
||||||
match ch.1.recv()
|
match ch
|
||||||
.expect("fetch worker should always send Done before terminating") {
|
.1
|
||||||
|
.recv()
|
||||||
|
.expect("fetch worker should always send Done before terminating")
|
||||||
|
{
|
||||||
Data::Payload(vec) => {
|
Data::Payload(vec) => {
|
||||||
target.process_response_chunk(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 => {
|
Data::Cancelled => {
|
||||||
response.aborted.store(true, Ordering::Relaxed);
|
response.aborted.store(true, Ordering::Relaxed);
|
||||||
break;
|
break;
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -456,36 +492,39 @@ fn wait_for_response(response: &mut Response, target: Target, done_chan: &mut Do
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [Scheme fetch](https://fetch.spec.whatwg.org#scheme-fetch)
|
/// [Scheme fetch](https://fetch.spec.whatwg.org#scheme-fetch)
|
||||||
fn scheme_fetch(request: &mut Request,
|
fn scheme_fetch(
|
||||||
|
request: &mut Request,
|
||||||
cache: &mut CorsCache,
|
cache: &mut CorsCache,
|
||||||
target: Target,
|
target: Target,
|
||||||
done_chan: &mut DoneChannel,
|
done_chan: &mut DoneChannel,
|
||||||
context: &FetchContext)
|
context: &FetchContext,
|
||||||
-> Response {
|
) -> Response {
|
||||||
let url = request.current_url();
|
let url = request.current_url();
|
||||||
|
|
||||||
match url.scheme() {
|
match url.scheme() {
|
||||||
"about" if url.path() == "blank" => {
|
"about" if url.path() == "blank" => {
|
||||||
let mut response = Response::new(url);
|
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.body.lock().unwrap() = ResponseBody::Done(vec![]);
|
||||||
response
|
response
|
||||||
},
|
},
|
||||||
|
|
||||||
"http" | "https" => {
|
"http" | "https" => http_fetch(
|
||||||
http_fetch(request, cache, false, false, false, target, done_chan, context)
|
request, cache, false, false, false, target, done_chan, context,
|
||||||
},
|
),
|
||||||
|
|
||||||
"data" => {
|
"data" => match decode(&url) {
|
||||||
match decode(&url) {
|
|
||||||
Ok((mime, bytes)) => {
|
Ok((mime, bytes)) => {
|
||||||
let mut response = Response::new(url);
|
let mut response = Response::new(url);
|
||||||
*response.body.lock().unwrap() = ResponseBody::Done(bytes);
|
*response.body.lock().unwrap() = ResponseBody::Done(bytes);
|
||||||
response.headers.typed_insert(ContentType::from(mime));
|
response.headers.typed_insert(ContentType::from(mime));
|
||||||
response
|
response
|
||||||
},
|
},
|
||||||
Err(_) => Response::network_error(NetworkError::Internal("Decoding data URL failed".into()))
|
Err(_) => {
|
||||||
}
|
Response::network_error(NetworkError::Internal("Decoding data URL failed".into()))
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
"file" => {
|
"file" => {
|
||||||
|
@ -507,9 +546,17 @@ fn scheme_fetch(request: &mut Request,
|
||||||
|
|
||||||
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>() {
|
let (start, end) = if let Some(ref range) =
|
||||||
match range.iter().collect::<Vec<(Bound<u64>, Bound<u64>)>>().first() {
|
request.headers.typed_get::<Range>()
|
||||||
Some(&(Bound::Included(start), Bound::Unbounded)) => (start, None),
|
{
|
||||||
|
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))) => {
|
Some(&(Bound::Included(start), Bound::Included(end))) => {
|
||||||
// `end` should be less or equal to `start`.
|
// `end` should be less or equal to `start`.
|
||||||
(start, Some(u64::max(start, end)))
|
(start, Some(u64::max(start, end)))
|
||||||
|
@ -517,38 +564,53 @@ fn scheme_fetch(request: &mut Request,
|
||||||
Some(&(Bound::Unbounded, Bound::Included(offset))) => {
|
Some(&(Bound::Unbounded, Bound::Included(offset))) => {
|
||||||
if let Ok(metadata) = file.metadata() {
|
if let Ok(metadata) = file.metadata() {
|
||||||
// `offset` cannot be bigger than the file size.
|
// `offset` cannot be bigger than the file size.
|
||||||
(metadata.len() - u64::min(metadata.len(), offset), None)
|
(
|
||||||
|
metadata.len() -
|
||||||
|
u64::min(metadata.len(), offset),
|
||||||
|
None,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
(0, None)
|
(0, None)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => (0, None)
|
_ => (0, None),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
(0, None)
|
(0, None)
|
||||||
};
|
};
|
||||||
|
|
||||||
thread::Builder::new().name("fetch file worker thread".to_string()).spawn(move || {
|
thread::Builder::new()
|
||||||
let mut reader = BufReader::with_capacity(FILE_CHUNK_SIZE, file);
|
.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() {
|
if reader.seek(SeekFrom::Start(start)).is_err() {
|
||||||
warn!("Fetch - could not seek to {:?}", start);
|
warn!("Fetch - could not seek to {:?}", start);
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if cancellation_listener.lock().unwrap().cancelled() {
|
if cancellation_listener.lock().unwrap().cancelled() {
|
||||||
*res_body.lock().unwrap() = ResponseBody::Done(vec![]);
|
*res_body.lock().unwrap() =
|
||||||
|
ResponseBody::Done(vec![]);
|
||||||
let _ = done_sender.send(Data::Cancelled);
|
let _ = done_sender.send(Data::Cancelled);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let length = {
|
let length = {
|
||||||
let mut buffer = reader.fill_buf().unwrap().to_vec();
|
let mut buffer =
|
||||||
|
reader.fill_buf().unwrap().to_vec();
|
||||||
let mut buffer_len = buffer.len();
|
let mut buffer_len = buffer.len();
|
||||||
if let ResponseBody::Receiving(ref mut body) = *res_body.lock().unwrap() {
|
if let ResponseBody::Receiving(ref mut body) =
|
||||||
let offset = usize::min({
|
*res_body.lock().unwrap()
|
||||||
|
{
|
||||||
|
let offset = usize::min(
|
||||||
|
{
|
||||||
if let Some(end) = end {
|
if let Some(end) = end {
|
||||||
let remaining_bytes =
|
let remaining_bytes = end as usize -
|
||||||
end as usize - start as usize - body.len();
|
start as usize -
|
||||||
if remaining_bytes <= FILE_CHUNK_SIZE {
|
body.len();
|
||||||
|
if remaining_bytes <=
|
||||||
|
FILE_CHUNK_SIZE
|
||||||
|
{
|
||||||
// This is the last chunk so we set buffer len to 0 to break
|
// This is the last chunk so we set buffer len to 0 to break
|
||||||
// the reading loop.
|
// the reading loop.
|
||||||
buffer_len = 0;
|
buffer_len = 0;
|
||||||
|
@ -559,7 +621,9 @@ fn scheme_fetch(request: &mut Request,
|
||||||
} else {
|
} else {
|
||||||
FILE_CHUNK_SIZE
|
FILE_CHUNK_SIZE
|
||||||
}
|
}
|
||||||
}, buffer.len());
|
},
|
||||||
|
buffer.len(),
|
||||||
|
);
|
||||||
body.extend_from_slice(&buffer[0..offset]);
|
body.extend_from_slice(&buffer[0..offset]);
|
||||||
let _ = done_sender.send(Data::Payload(buffer));
|
let _ = done_sender.send(Data::Payload(buffer));
|
||||||
}
|
}
|
||||||
|
@ -579,13 +643,18 @@ fn scheme_fetch(request: &mut Request,
|
||||||
}
|
}
|
||||||
reader.consume(length);
|
reader.consume(length);
|
||||||
}
|
}
|
||||||
}).expect("Failed to create fetch file worker thread");
|
})
|
||||||
|
.expect("Failed to create fetch file worker thread");
|
||||||
response
|
response
|
||||||
},
|
},
|
||||||
_ => Response::network_error(NetworkError::Internal("Opening file failed".into())),
|
_ => Response::network_error(NetworkError::Internal(
|
||||||
|
"Opening file failed".into(),
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => Response::network_error(NetworkError::Internal("Constructing file path failed".into()))
|
_ => Response::network_error(NetworkError::Internal(
|
||||||
|
"Constructing file path failed".into(),
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Response::network_error(NetworkError::Internal("Unexpected method for file".into()))
|
Response::network_error(NetworkError::Internal("Unexpected method for file".into()))
|
||||||
|
@ -596,7 +665,9 @@ fn scheme_fetch(request: &mut Request,
|
||||||
println!("Loading blob {}", url.as_str());
|
println!("Loading blob {}", url.as_str());
|
||||||
// Step 2.
|
// Step 2.
|
||||||
if request.method != Method::GET {
|
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()) {
|
match load_blob_sync(url.clone(), context.filemanager.clone()) {
|
||||||
|
@ -618,7 +689,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()))
|
_ => Response::network_error(NetworkError::Internal("Unexpected scheme".into())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -633,7 +704,9 @@ pub fn is_cors_safelisted_request_header(name: &HeaderName, value: &HeaderValue)
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
} else {
|
} 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 +714,35 @@ pub fn is_cors_safelisted_request_header(name: &HeaderName, value: &HeaderValue)
|
||||||
pub fn is_cors_safelisted_method(m: &Method) -> bool {
|
pub fn is_cors_safelisted_method(m: &Method) -> bool {
|
||||||
match *m {
|
match *m {
|
||||||
Method::GET | Method::HEAD | Method::POST => true,
|
Method::GET | Method::HEAD | Method::POST => true,
|
||||||
_ => false
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_null_body_status(status: &Option<(StatusCode, String)>) -> bool {
|
fn is_null_body_status(status: &Option<(StatusCode, String)>) -> bool {
|
||||||
match *status {
|
match *status {
|
||||||
Some((status, _)) => match status {
|
Some((status, _)) => match status {
|
||||||
StatusCode::SWITCHING_PROTOCOLS | StatusCode::NO_CONTENT |
|
StatusCode::SWITCHING_PROTOCOLS |
|
||||||
StatusCode::RESET_CONTENT | StatusCode::NOT_MODIFIED => true,
|
StatusCode::NO_CONTENT |
|
||||||
_ => false
|
StatusCode::RESET_CONTENT |
|
||||||
|
StatusCode::NOT_MODIFIED => true,
|
||||||
|
_ => false,
|
||||||
},
|
},
|
||||||
_ => false
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <https://fetch.spec.whatwg.org/#should-response-to-request-be-blocked-due-to-nosniff?>
|
/// <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.
|
// Steps 1-3.
|
||||||
// TODO(eijebong): Replace this once typed headers allow custom ones...
|
// TODO(eijebong): Replace this once typed headers allow custom ones...
|
||||||
if response_headers.get("x-content-type-options")
|
if response_headers
|
||||||
.map_or(true, |val| val.to_str().unwrap_or("").to_lowercase() != "nosniff")
|
.get("x-content-type-options")
|
||||||
{
|
.map_or(true, |val| {
|
||||||
|
val.to_str().unwrap_or("").to_lowercase() != "nosniff"
|
||||||
|
}) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -692,30 +772,34 @@ pub fn should_be_blocked_due_to_nosniff(destination: Destination, response_heade
|
||||||
"text/x-javascript".parse().unwrap(),
|
"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())
|
.any(|mime| mime.type_() == mime_type.type_() && mime.subtype() == mime_type.subtype())
|
||||||
}
|
}
|
||||||
|
|
||||||
match content_type_header {
|
match content_type_header {
|
||||||
// Step 6
|
// Step 6
|
||||||
Some(ref ct) if destination.is_script_like()
|
Some(ref ct) if destination.is_script_like() => {
|
||||||
=> !is_javascript_mime_type(&ct.clone().into()),
|
!is_javascript_mime_type(&ct.clone().into())
|
||||||
|
},
|
||||||
|
|
||||||
// Step 7
|
// Step 7
|
||||||
Some(ref ct) if destination == Destination::Style
|
Some(ref ct) if destination == Destination::Style => {
|
||||||
=> {
|
|
||||||
let m: mime::Mime = ct.clone().into();
|
let m: mime::Mime = ct.clone().into();
|
||||||
m.type_() != mime::TEXT && m.subtype() != mime::CSS
|
m.type_() != mime::TEXT && m.subtype() != mime::CSS
|
||||||
},
|
},
|
||||||
|
|
||||||
None if destination == Destination::Style || destination.is_script_like() => true,
|
None if destination == Destination::Style || destination.is_script_like() => true,
|
||||||
// Step 8
|
// Step 8
|
||||||
_ => false
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <https://fetch.spec.whatwg.org/#should-response-to-request-be-blocked-due-to-mime-type?>
|
/// <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
|
// Step 1
|
||||||
let mime_type: mime::Mime = match response_headers.typed_get::<ContentType>() {
|
let mime_type: mime::Mime = match response_headers.typed_get::<ContentType>() {
|
||||||
Some(header) => header.into(),
|
Some(header) => header.into(),
|
||||||
|
@ -725,12 +809,10 @@ fn should_be_blocked_due_to_mime_type(destination: Destination, response_headers
|
||||||
// Step 2-3
|
// Step 2-3
|
||||||
destination.is_script_like() &&
|
destination.is_script_like() &&
|
||||||
match mime_type.type_() {
|
match mime_type.type_() {
|
||||||
mime::AUDIO |
|
mime::AUDIO | mime::VIDEO | mime::IMAGE => true,
|
||||||
mime::VIDEO |
|
|
||||||
mime::IMAGE => true,
|
|
||||||
mime::TEXT if mime_type.subtype() == mime::CSV => true,
|
mime::TEXT if mime_type.subtype() == mime::CSV => true,
|
||||||
// Step 4
|
// Step 4
|
||||||
_ => false
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -745,14 +827,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
|
// 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
|
// the given scheme, and thus this means the request should not be blocked
|
||||||
// due to a bad port.
|
// 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.
|
// Step 4.
|
||||||
if scheme == "ftp" && (port == 20 || port == 21) {
|
if scheme == "ftp" && (port == 20 || port == 21) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Step 5.
|
// Step 5.
|
||||||
if is_network_scheme(scheme) && is_bad_port(port) {
|
if is_network_scheme(scheme) && is_bad_port(port) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -770,12 +855,10 @@ fn is_network_scheme(scheme: &str) -> bool {
|
||||||
/// <https://fetch.spec.whatwg.org/#bad-port>
|
/// <https://fetch.spec.whatwg.org/#bad-port>
|
||||||
fn is_bad_port(port: u16) -> bool {
|
fn is_bad_port(port: u16) -> bool {
|
||||||
static BAD_PORTS: [u16; 64] = [
|
static BAD_PORTS: [u16; 64] = [
|
||||||
1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42,
|
1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, 43, 53, 77, 79, 87, 95, 101, 102,
|
||||||
43, 53, 77, 79, 87, 95, 101, 102, 103, 104, 109, 110, 111,
|
103, 104, 109, 110, 111, 113, 115, 117, 119, 123, 135, 139, 143, 179, 389, 465, 512, 513,
|
||||||
113, 115, 117, 119, 123, 135, 139, 143, 179, 389, 465, 512,
|
514, 515, 526, 530, 531, 532, 540, 556, 563, 587, 601, 636, 993, 995, 2049, 3659, 4045,
|
||||||
513, 514, 515, 526, 530, 531, 532, 540, 556, 563, 587, 601,
|
6000, 6665, 6666, 6667, 6668, 6669,
|
||||||
636, 993, 995, 2049, 3659, 4045, 6000, 6665, 6666, 6667,
|
|
||||||
6668, 6669
|
|
||||||
];
|
];
|
||||||
|
|
||||||
BAD_PORTS.binary_search(&port).is_ok()
|
BAD_PORTS.binary_search(&port).is_ok()
|
||||||
|
|
|
@ -34,7 +34,7 @@ struct FileStoreEntry {
|
||||||
/// UUIDs only become valid blob URIs when explicitly requested
|
/// UUIDs only become valid blob URIs when explicitly requested
|
||||||
/// by the user with createObjectURL. Validity can be revoked as well.
|
/// by the user with createObjectURL. Validity can be revoked as well.
|
||||||
/// (The UUID is the one that maps to this entry in `FileManagerStore`)
|
/// (The UUID is the one that maps to this entry in `FileManagerStore`)
|
||||||
is_valid_url: AtomicBool
|
is_valid_url: AtomicBool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -71,29 +71,38 @@ impl FileManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_file(&self,
|
pub fn read_file(
|
||||||
|
&self,
|
||||||
sender: IpcSender<FileManagerResult<ReadFileProgress>>,
|
sender: IpcSender<FileManagerResult<ReadFileProgress>>,
|
||||||
id: Uuid,
|
id: Uuid,
|
||||||
check_url_validity: bool,
|
check_url_validity: bool,
|
||||||
origin: FileOrigin) {
|
origin: FileOrigin,
|
||||||
|
) {
|
||||||
let store = self.store.clone();
|
let store = self.store.clone();
|
||||||
thread::Builder::new().name("read file".to_owned()).spawn(move || {
|
thread::Builder::new()
|
||||||
if let Err(e) = store.try_read_file(&sender, id, check_url_validity,
|
.name("read file".to_owned())
|
||||||
origin) {
|
.spawn(move || {
|
||||||
|
if let Err(e) = store.try_read_file(&sender, id, check_url_validity, origin) {
|
||||||
let _ = sender.send(Err(FileManagerThreadError::BlobURLStoreError(e)));
|
let _ = sender.send(Err(FileManagerThreadError::BlobURLStoreError(e)));
|
||||||
}
|
}
|
||||||
}).expect("Thread spawning failed");
|
})
|
||||||
|
.expect("Thread spawning failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn promote_memory(&self,
|
pub fn promote_memory(
|
||||||
|
&self,
|
||||||
blob_buf: BlobBuf,
|
blob_buf: BlobBuf,
|
||||||
set_valid: bool,
|
set_valid: bool,
|
||||||
sender: IpcSender<Result<Uuid, BlobURLStoreError>>,
|
sender: IpcSender<Result<Uuid, BlobURLStoreError>>,
|
||||||
origin: FileOrigin) {
|
origin: FileOrigin,
|
||||||
|
) {
|
||||||
let store = self.store.clone();
|
let store = self.store.clone();
|
||||||
thread::Builder::new().name("transfer memory".to_owned()).spawn(move || {
|
thread::Builder::new()
|
||||||
|
.name("transfer memory".to_owned())
|
||||||
|
.spawn(move || {
|
||||||
store.promote_memory(blob_buf, set_valid, sender, origin);
|
store.promote_memory(blob_buf, set_valid, sender, origin);
|
||||||
}).expect("Thread spawning failed");
|
})
|
||||||
|
.expect("Thread spawning failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Message handler
|
/// Message handler
|
||||||
|
@ -102,35 +111,41 @@ impl FileManager {
|
||||||
FileManagerThreadMsg::SelectFile(filter, sender, origin, opt_test_path) => {
|
FileManagerThreadMsg::SelectFile(filter, sender, origin, opt_test_path) => {
|
||||||
let store = self.store.clone();
|
let store = self.store.clone();
|
||||||
let embedder = self.embedder_proxy.clone();
|
let embedder = self.embedder_proxy.clone();
|
||||||
thread::Builder::new().name("select file".to_owned()).spawn(move || {
|
thread::Builder::new()
|
||||||
|
.name("select file".to_owned())
|
||||||
|
.spawn(move || {
|
||||||
store.select_file(filter, sender, origin, opt_test_path, embedder);
|
store.select_file(filter, sender, origin, opt_test_path, embedder);
|
||||||
}).expect("Thread spawning failed");
|
})
|
||||||
}
|
.expect("Thread spawning failed");
|
||||||
|
},
|
||||||
FileManagerThreadMsg::SelectFiles(filter, sender, origin, opt_test_paths) => {
|
FileManagerThreadMsg::SelectFiles(filter, sender, origin, opt_test_paths) => {
|
||||||
let store = self.store.clone();
|
let store = self.store.clone();
|
||||||
let embedder = self.embedder_proxy.clone();
|
let embedder = self.embedder_proxy.clone();
|
||||||
thread::Builder::new().name("select files".to_owned()).spawn(move || {
|
thread::Builder::new()
|
||||||
|
.name("select files".to_owned())
|
||||||
|
.spawn(move || {
|
||||||
store.select_files(filter, sender, origin, opt_test_paths, embedder);
|
store.select_files(filter, sender, origin, opt_test_paths, embedder);
|
||||||
}).expect("Thread spawning failed");
|
})
|
||||||
}
|
.expect("Thread spawning failed");
|
||||||
|
},
|
||||||
FileManagerThreadMsg::ReadFile(sender, id, check_url_validity, origin) => {
|
FileManagerThreadMsg::ReadFile(sender, id, check_url_validity, origin) => {
|
||||||
self.read_file(sender, id, check_url_validity, origin);
|
self.read_file(sender, id, check_url_validity, origin);
|
||||||
}
|
},
|
||||||
FileManagerThreadMsg::PromoteMemory(blob_buf, set_valid, sender, origin) => {
|
FileManagerThreadMsg::PromoteMemory(blob_buf, set_valid, sender, origin) => {
|
||||||
self.promote_memory(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);
|
self.store.add_sliced_url_entry(id, rel_pos, sender, origin);
|
||||||
}
|
},
|
||||||
FileManagerThreadMsg::DecRef(id, origin, sender) => {
|
FileManagerThreadMsg::DecRef(id, origin, sender) => {
|
||||||
let _ = sender.send(self.store.dec_ref(&id, &origin));
|
let _ = sender.send(self.store.dec_ref(&id, &origin));
|
||||||
}
|
},
|
||||||
FileManagerThreadMsg::RevokeBlobURL(id, origin, sender) => {
|
FileManagerThreadMsg::RevokeBlobURL(id, origin, sender) => {
|
||||||
let _ = sender.send(self.store.set_blob_url_validity(false, &id, &origin));
|
let _ = sender.send(self.store.set_blob_url_validity(false, &id, &origin));
|
||||||
}
|
},
|
||||||
FileManagerThreadMsg::ActivateBlobURL(id, sender, origin) => {
|
FileManagerThreadMsg::ActivateBlobURL(id, sender, origin) => {
|
||||||
let _ = sender.send(self.store.set_blob_url_validity(true, &id, &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
|
/// Copy out the file backend implementation content
|
||||||
fn get_impl(&self, id: &Uuid, origin_in: &FileOrigin,
|
fn get_impl(
|
||||||
check_url_validity: bool) -> Result<FileImpl, BlobURLStoreError> {
|
&self,
|
||||||
|
id: &Uuid,
|
||||||
|
origin_in: &FileOrigin,
|
||||||
|
check_url_validity: bool,
|
||||||
|
) -> Result<FileImpl, BlobURLStoreError> {
|
||||||
match self.entries.read().unwrap().get(id) {
|
match self.entries.read().unwrap().get(id) {
|
||||||
Some(ref entry) => {
|
Some(ref entry) => {
|
||||||
if *origin_in != *entry.origin {
|
if *origin_in != *entry.origin {
|
||||||
|
@ -164,7 +183,7 @@ impl FileManagerStore {
|
||||||
Ok(entry.file_impl.clone())
|
Ok(entry.file_impl.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
None => Err(BlobURLStoreError::InvalidFileID),
|
None => Err(BlobURLStoreError::InvalidFileID),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -186,41 +205,53 @@ impl FileManagerStore {
|
||||||
} else {
|
} else {
|
||||||
Err(BlobURLStoreError::InvalidOrigin)
|
Err(BlobURLStoreError::InvalidOrigin)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
None => Err(BlobURLStoreError::InvalidFileID),
|
None => Err(BlobURLStoreError::InvalidFileID),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_sliced_url_entry(&self, parent_id: Uuid, rel_pos: RelativePos,
|
fn add_sliced_url_entry(
|
||||||
|
&self,
|
||||||
|
parent_id: Uuid,
|
||||||
|
rel_pos: RelativePos,
|
||||||
sender: IpcSender<Result<Uuid, BlobURLStoreError>>,
|
sender: IpcSender<Result<Uuid, BlobURLStoreError>>,
|
||||||
origin_in: FileOrigin) {
|
origin_in: FileOrigin,
|
||||||
|
) {
|
||||||
match self.inc_ref(&parent_id, &origin_in) {
|
match self.inc_ref(&parent_id, &origin_in) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let new_id = Uuid::new_v4();
|
let new_id = Uuid::new_v4();
|
||||||
self.insert(new_id, FileStoreEntry {
|
self.insert(
|
||||||
|
new_id,
|
||||||
|
FileStoreEntry {
|
||||||
origin: origin_in,
|
origin: origin_in,
|
||||||
file_impl: FileImpl::Sliced(parent_id, rel_pos),
|
file_impl: FileImpl::Sliced(parent_id, rel_pos),
|
||||||
refs: AtomicUsize::new(1),
|
refs: AtomicUsize::new(1),
|
||||||
// Valid here since AddSlicedURLEntry implies URL creation
|
// Valid here since AddSlicedURLEntry implies URL creation
|
||||||
// from a BlobImpl::Sliced
|
// from a BlobImpl::Sliced
|
||||||
is_valid_url: AtomicBool::new(true),
|
is_valid_url: AtomicBool::new(true),
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// We assume that the returned id will be held by BlobImpl::File
|
// We assume that the returned id will be held by BlobImpl::File
|
||||||
let _ = sender.send(Ok(new_id));
|
let _ = sender.send(Ok(new_id));
|
||||||
}
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let _ = sender.send(Err(e));
|
let _ = sender.send(Err(e));
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn query_files_from_embedder(&self,
|
fn query_files_from_embedder(
|
||||||
|
&self,
|
||||||
patterns: Vec<FilterPattern>,
|
patterns: Vec<FilterPattern>,
|
||||||
multiple_files: bool,
|
multiple_files: bool,
|
||||||
embedder_proxy: EmbedderProxy) -> Option<Vec<String>> {
|
embedder_proxy: EmbedderProxy,
|
||||||
|
) -> Option<Vec<String>> {
|
||||||
let (ipc_sender, ipc_receiver) = ipc::channel().expect("Failed to create IPC channel!");
|
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);
|
embedder_proxy.send(msg);
|
||||||
match ipc_receiver.recv() {
|
match ipc_receiver.recv() {
|
||||||
|
@ -228,23 +259,26 @@ impl FileManagerStore {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("Failed to receive files from embedder ({}).", e);
|
warn!("Failed to receive files from embedder ({}).", e);
|
||||||
None
|
None
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_file(&self,
|
fn select_file(
|
||||||
|
&self,
|
||||||
patterns: Vec<FilterPattern>,
|
patterns: Vec<FilterPattern>,
|
||||||
sender: IpcSender<FileManagerResult<SelectedFile>>,
|
sender: IpcSender<FileManagerResult<SelectedFile>>,
|
||||||
origin: FileOrigin,
|
origin: FileOrigin,
|
||||||
opt_test_path: Option<String>,
|
opt_test_path: Option<String>,
|
||||||
embedder_proxy: EmbedderProxy) {
|
embedder_proxy: EmbedderProxy,
|
||||||
|
) {
|
||||||
// Check if the select_files preference is enabled
|
// Check if the select_files preference is enabled
|
||||||
// to ensure process-level security against compromised script;
|
// to ensure process-level security against compromised script;
|
||||||
// Then try applying opt_test_path directly for testing convenience
|
// Then try applying opt_test_path directly for testing convenience
|
||||||
let opt_s = if select_files_pref_enabled() {
|
let opt_s = if select_files_pref_enabled() {
|
||||||
opt_test_path
|
opt_test_path
|
||||||
} else {
|
} 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 {
|
match opt_s {
|
||||||
|
@ -252,20 +286,22 @@ impl FileManagerStore {
|
||||||
let selected_path = Path::new(&s);
|
let selected_path = Path::new(&s);
|
||||||
let result = self.create_entry(selected_path, &origin);
|
let result = self.create_entry(selected_path, &origin);
|
||||||
let _ = sender.send(result);
|
let _ = sender.send(result);
|
||||||
}
|
},
|
||||||
None => {
|
None => {
|
||||||
let _ = sender.send(Err(FileManagerThreadError::UserCancelled));
|
let _ = sender.send(Err(FileManagerThreadError::UserCancelled));
|
||||||
return;
|
return;
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_files(&self,
|
fn select_files(
|
||||||
|
&self,
|
||||||
patterns: Vec<FilterPattern>,
|
patterns: Vec<FilterPattern>,
|
||||||
sender: IpcSender<FileManagerResult<Vec<SelectedFile>>>,
|
sender: IpcSender<FileManagerResult<Vec<SelectedFile>>>,
|
||||||
origin: FileOrigin,
|
origin: FileOrigin,
|
||||||
opt_test_paths: Option<Vec<String>>,
|
opt_test_paths: Option<Vec<String>>,
|
||||||
embedder_proxy: EmbedderProxy) {
|
embedder_proxy: EmbedderProxy,
|
||||||
|
) {
|
||||||
// Check if the select_files preference is enabled
|
// Check if the select_files preference is enabled
|
||||||
// to ensure process-level security against compromised script;
|
// to ensure process-level security against compromised script;
|
||||||
// Then try applying opt_test_paths directly for testing convenience
|
// Then try applying opt_test_paths directly for testing convenience
|
||||||
|
@ -291,30 +327,42 @@ impl FileManagerStore {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let _ = sender.send(Err(e));
|
let _ = sender.send(Err(e));
|
||||||
return;
|
return;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = sender.send(Ok(replies));
|
let _ = sender.send(Ok(replies));
|
||||||
}
|
},
|
||||||
None => {
|
None => {
|
||||||
let _ = sender.send(Err(FileManagerThreadError::UserCancelled));
|
let _ = sender.send(Err(FileManagerThreadError::UserCancelled));
|
||||||
return;
|
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;
|
use net_traits::filemanager_thread::FileManagerThreadError::FileSystemError;
|
||||||
|
|
||||||
let file = File::open(file_path).map_err(|e| FileSystemError(e.to_string()))?;
|
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 metadata = file
|
||||||
let modified = metadata.modified().map_err(|e| FileSystemError(e.to_string()))?;
|
.metadata()
|
||||||
let elapsed = modified.elapsed().map_err(|e| FileSystemError(e.to_string()))?;
|
.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
|
// 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 modified_epoch = elapsed.as_secs() * 1000 + elapsed.subsec_nanos() as u64 / 1000000;
|
||||||
let file_size = metadata.len();
|
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 {
|
let file_impl = FileImpl::MetaDataOnly(FileMetaData {
|
||||||
path: file_path.to_path_buf(),
|
path: file_path.to_path_buf(),
|
||||||
|
@ -324,13 +372,16 @@ impl FileManagerStore {
|
||||||
|
|
||||||
let id = Uuid::new_v4();
|
let id = Uuid::new_v4();
|
||||||
|
|
||||||
self.insert(id, FileStoreEntry {
|
self.insert(
|
||||||
|
id,
|
||||||
|
FileStoreEntry {
|
||||||
origin: origin.to_string(),
|
origin: origin.to_string(),
|
||||||
file_impl: file_impl,
|
file_impl: file_impl,
|
||||||
refs: AtomicUsize::new(1),
|
refs: AtomicUsize::new(1),
|
||||||
// Invalid here since create_entry is called by file selection
|
// Invalid here since create_entry is called by file selection
|
||||||
is_valid_url: AtomicBool::new(false),
|
is_valid_url: AtomicBool::new(false),
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
let filename_path = Path::new(file_name);
|
let filename_path = Path::new(file_name);
|
||||||
let type_string = match guess_mime_type_opt(filename_path) {
|
let type_string = match guess_mime_type_opt(filename_path) {
|
||||||
|
@ -347,9 +398,14 @@ impl FileManagerStore {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_blob_buf(&self, sender: &IpcSender<FileManagerResult<ReadFileProgress>>,
|
fn get_blob_buf(
|
||||||
id: &Uuid, origin_in: &FileOrigin, rel_pos: RelativePos,
|
&self,
|
||||||
check_url_validity: bool) -> Result<(), BlobURLStoreError> {
|
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)?;
|
let file_impl = self.get_impl(id, origin_in, check_url_validity)?;
|
||||||
match file_impl {
|
match file_impl {
|
||||||
FileImpl::Memory(buf) => {
|
FileImpl::Memory(buf) => {
|
||||||
|
@ -365,7 +421,7 @@ impl FileManagerStore {
|
||||||
let _ = sender.send(Ok(ReadFileProgress::EOF));
|
let _ = sender.send(Ok(ReadFileProgress::EOF));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
},
|
||||||
FileImpl::MetaDataOnly(metadata) => {
|
FileImpl::MetaDataOnly(metadata) => {
|
||||||
/* XXX: Snapshot state check (optional) https://w3c.github.io/FileAPI/#snapshot-state.
|
/* XXX: Snapshot state check (optional) https://w3c.github.io/FileAPI/#snapshot-state.
|
||||||
Concretely, here we create another file, and this file might not
|
Concretely, here we create another file, and this file might not
|
||||||
|
@ -373,7 +429,9 @@ impl FileManagerStore {
|
||||||
create_entry is called.
|
create_entry is called.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let opt_filename = metadata.path.file_name()
|
let opt_filename = metadata
|
||||||
|
.path
|
||||||
|
.file_name()
|
||||||
.and_then(|osstr| osstr.to_str())
|
.and_then(|osstr| osstr.to_str())
|
||||||
.map(|s| s.to_string());
|
.map(|s| s.to_string());
|
||||||
|
|
||||||
|
@ -382,7 +440,8 @@ impl FileManagerStore {
|
||||||
|
|
||||||
let mut file = File::open(&metadata.path)
|
let mut file = File::open(&metadata.path)
|
||||||
.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))
|
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()))?;
|
||||||
|
|
||||||
if seeked_start == (range.start as u64) {
|
if seeked_start == (range.start as u64) {
|
||||||
|
@ -391,27 +450,41 @@ impl FileManagerStore {
|
||||||
None => "".to_string(),
|
None => "".to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
chunked_read(sender, &mut file, range.len(), opt_filename,
|
chunked_read(sender, &mut file, range.len(), opt_filename, type_string);
|
||||||
type_string);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(BlobURLStoreError::InvalidEntry)
|
Err(BlobURLStoreError::InvalidEntry)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
FileImpl::Sliced(parent_id, inner_rel_pos) => {
|
FileImpl::Sliced(parent_id, inner_rel_pos) => {
|
||||||
// Next time we don't need to check validity since
|
// Next time we don't need to check validity since
|
||||||
// we have already done that for requesting URL if necessary
|
// we have already done that for requesting URL if necessary
|
||||||
self.get_blob_buf(sender, &parent_id, origin_in,
|
self.get_blob_buf(
|
||||||
rel_pos.slice_inner(&inner_rel_pos), false)
|
sender,
|
||||||
}
|
&parent_id,
|
||||||
|
origin_in,
|
||||||
|
rel_pos.slice_inner(&inner_rel_pos),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convenient wrapper over get_blob_buf
|
// Convenient wrapper over get_blob_buf
|
||||||
fn try_read_file(&self, sender: &IpcSender<FileManagerResult<ReadFileProgress>>,
|
fn try_read_file(
|
||||||
id: Uuid, check_url_validity: bool, origin_in: FileOrigin)
|
&self,
|
||||||
-> Result<(), BlobURLStoreError> {
|
sender: &IpcSender<FileManagerResult<ReadFileProgress>>,
|
||||||
self.get_blob_buf(sender, &id, &origin_in, RelativePos::full_range(), check_url_validity)
|
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> {
|
fn dec_ref(&self, id: &Uuid, origin_in: &FileOrigin) -> Result<(), BlobURLStoreError> {
|
||||||
|
@ -436,7 +509,7 @@ impl FileManagerStore {
|
||||||
} else {
|
} else {
|
||||||
return Err(BlobURLStoreError::InvalidOrigin);
|
return Err(BlobURLStoreError::InvalidOrigin);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
None => return Err(BlobURLStoreError::InvalidFileID),
|
None => return Err(BlobURLStoreError::InvalidFileID),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -454,28 +527,41 @@ impl FileManagerStore {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn promote_memory(&self, blob_buf: BlobBuf, set_valid: bool,
|
fn promote_memory(
|
||||||
sender: IpcSender<Result<Uuid, BlobURLStoreError>>, origin: FileOrigin) {
|
&self,
|
||||||
match Url::parse(&origin) { // parse to check sanity
|
blob_buf: BlobBuf,
|
||||||
|
set_valid: bool,
|
||||||
|
sender: IpcSender<Result<Uuid, BlobURLStoreError>>,
|
||||||
|
origin: FileOrigin,
|
||||||
|
) {
|
||||||
|
match Url::parse(&origin) {
|
||||||
|
// parse to check sanity
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let id = Uuid::new_v4();
|
let id = Uuid::new_v4();
|
||||||
self.insert(id, FileStoreEntry {
|
self.insert(
|
||||||
|
id,
|
||||||
|
FileStoreEntry {
|
||||||
origin: origin.clone(),
|
origin: origin.clone(),
|
||||||
file_impl: FileImpl::Memory(blob_buf),
|
file_impl: FileImpl::Memory(blob_buf),
|
||||||
refs: AtomicUsize::new(1),
|
refs: AtomicUsize::new(1),
|
||||||
is_valid_url: AtomicBool::new(set_valid),
|
is_valid_url: AtomicBool::new(set_valid),
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
let _ = sender.send(Ok(id));
|
let _ = sender.send(Ok(id));
|
||||||
}
|
},
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
let _ = sender.send(Err(BlobURLStoreError::InvalidOrigin));
|
let _ = sender.send(Err(BlobURLStoreError::InvalidOrigin));
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_blob_url_validity(&self, validity: bool, id: &Uuid,
|
fn set_blob_url_validity(
|
||||||
origin_in: &FileOrigin) -> Result<(), BlobURLStoreError> {
|
&self,
|
||||||
|
validity: bool,
|
||||||
|
id: &Uuid,
|
||||||
|
origin_in: &FileOrigin,
|
||||||
|
) -> Result<(), BlobURLStoreError> {
|
||||||
let (do_remove, opt_parent_id, res) = match self.entries.read().unwrap().get(id) {
|
let (do_remove, opt_parent_id, res) = match self.entries.read().unwrap().get(id) {
|
||||||
Some(entry) => {
|
Some(entry) => {
|
||||||
if *entry.origin == *origin_in {
|
if *entry.origin == *origin_in {
|
||||||
|
@ -498,8 +584,8 @@ impl FileManagerStore {
|
||||||
} else {
|
} else {
|
||||||
(false, None, Err(BlobURLStoreError::InvalidOrigin))
|
(false, None, Err(BlobURLStoreError::InvalidOrigin))
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
None => (false, None, Err(BlobURLStoreError::InvalidFileID))
|
None => (false, None, Err(BlobURLStoreError::InvalidFileID)),
|
||||||
};
|
};
|
||||||
|
|
||||||
if do_remove {
|
if do_remove {
|
||||||
|
@ -515,15 +601,21 @@ impl FileManagerStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_files_pref_enabled() -> bool {
|
fn select_files_pref_enabled() -> bool {
|
||||||
PREFS.get("dom.testing.htmlinputelement.select_files.enabled")
|
PREFS
|
||||||
.as_boolean().unwrap_or(false)
|
.get("dom.testing.htmlinputelement.select_files.enabled")
|
||||||
|
.as_boolean()
|
||||||
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
const CHUNK_SIZE: usize = 8192;
|
const CHUNK_SIZE: usize = 8192;
|
||||||
|
|
||||||
fn chunked_read(sender: &IpcSender<FileManagerResult<ReadFileProgress>>,
|
fn chunked_read(
|
||||||
file: &mut File, size: usize, opt_filename: Option<String>,
|
sender: &IpcSender<FileManagerResult<ReadFileProgress>>,
|
||||||
type_string: String) {
|
file: &mut File,
|
||||||
|
size: usize,
|
||||||
|
opt_filename: Option<String>,
|
||||||
|
type_string: String,
|
||||||
|
) {
|
||||||
// First chunk
|
// First chunk
|
||||||
let mut buf = vec![0; CHUNK_SIZE];
|
let mut buf = vec![0; CHUNK_SIZE];
|
||||||
match file.read(&mut buf) {
|
match file.read(&mut buf) {
|
||||||
|
@ -536,11 +628,11 @@ fn chunked_read(sender: &IpcSender<FileManagerResult<ReadFileProgress>>,
|
||||||
bytes: buf,
|
bytes: buf,
|
||||||
};
|
};
|
||||||
let _ = sender.send(Ok(ReadFileProgress::Meta(blob_buf)));
|
let _ = sender.send(Ok(ReadFileProgress::Meta(blob_buf)));
|
||||||
}
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let _ = sender.send(Err(FileManagerThreadError::FileSystemError(e.to_string())));
|
let _ = sender.send(Err(FileManagerThreadError::FileSystemError(e.to_string())));
|
||||||
return;
|
return;
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the remaining chunks
|
// Send the remaining chunks
|
||||||
|
@ -550,15 +642,15 @@ fn chunked_read(sender: &IpcSender<FileManagerResult<ReadFileProgress>>,
|
||||||
Ok(0) => {
|
Ok(0) => {
|
||||||
let _ = sender.send(Ok(ReadFileProgress::EOF));
|
let _ = sender.send(Ok(ReadFileProgress::EOF));
|
||||||
return;
|
return;
|
||||||
}
|
},
|
||||||
Ok(n) => {
|
Ok(n) => {
|
||||||
buf.truncate(n);
|
buf.truncate(n);
|
||||||
let _ = sender.send(Ok(ReadFileProgress::Partial(buf)));
|
let _ = sender.send(Ok(ReadFileProgress::Partial(buf)));
|
||||||
}
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let _ = sender.send(Err(FileManagerThreadError::FileSystemError(e.to_string())));
|
let _ = sender.send(Err(FileManagerThreadError::FileSystemError(e.to_string())));
|
||||||
return;
|
return;
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,21 +31,32 @@ pub fn replace_host_table(table: HashMap<String, IpAddr>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_hostsfile(hostsfile_content: &str) -> HashMap<String, IpAddr> {
|
pub fn parse_hostsfile(hostsfile_content: &str) -> HashMap<String, IpAddr> {
|
||||||
hostsfile_content.lines().filter_map(|line| {
|
hostsfile_content
|
||||||
|
.lines()
|
||||||
|
.filter_map(|line| {
|
||||||
let mut iter = line.split('#').next().unwrap().split_whitespace();
|
let mut iter = line.split('#').next().unwrap().split_whitespace();
|
||||||
Some((iter.next()?.parse().ok()?, iter))
|
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)
|
|
||||||
})
|
})
|
||||||
}).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> {
|
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))
|
.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()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,15 @@ pub struct HstsEntry {
|
||||||
pub host: String,
|
pub host: String,
|
||||||
pub include_subdomains: bool,
|
pub include_subdomains: bool,
|
||||||
pub max_age: Option<u64>,
|
pub max_age: Option<u64>,
|
||||||
pub timestamp: Option<u64>
|
pub timestamp: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HstsEntry {
|
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() {
|
if host.parse::<Ipv4Addr>().is_ok() || host.parse::<Ipv6Addr>().is_ok() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
|
@ -28,7 +32,7 @@ impl HstsEntry {
|
||||||
host: host,
|
host: host,
|
||||||
include_subdomains: (subdomains == IncludeSubdomains::Included),
|
include_subdomains: (subdomains == IncludeSubdomains::Included),
|
||||||
max_age: max_age,
|
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) {
|
match (self.max_age, self.timestamp) {
|
||||||
(Some(max_age), Some(timestamp)) => {
|
(Some(max_age), Some(timestamp)) => {
|
||||||
(time::get_time().sec as u64) - timestamp >= max_age
|
(time::get_time().sec as u64) - timestamp >= max_age
|
||||||
}
|
},
|
||||||
|
|
||||||
_ => false
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +63,9 @@ pub struct HstsList {
|
||||||
|
|
||||||
impl HstsList {
|
impl HstsList {
|
||||||
pub fn new() -> 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.
|
/// Create an `HstsList` from the bytes of a JSON preload file.
|
||||||
|
@ -118,7 +124,10 @@ impl HstsList {
|
||||||
let have_domain = self.has_domain(&entry.host, base_domain);
|
let have_domain = self.has_domain(&entry.host, base_domain);
|
||||||
let have_subdomain = self.has_subdomain(&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 {
|
if !have_domain && !have_subdomain {
|
||||||
entries.push(entry);
|
entries.push(entry);
|
||||||
} else if !have_subdomain {
|
} else if !have_subdomain {
|
||||||
|
@ -136,7 +145,10 @@ impl HstsList {
|
||||||
if url.scheme() != "http" {
|
if url.scheme() != "http" {
|
||||||
return;
|
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();
|
url.as_mut_url().set_scheme("https").unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,23 +30,22 @@ use std::time::SystemTime;
|
||||||
use time;
|
use time;
|
||||||
use time::{Duration, Timespec, Tm};
|
use time::{Duration, Timespec, Tm};
|
||||||
|
|
||||||
|
|
||||||
/// The key used to differentiate requests in the cache.
|
/// The key used to differentiate requests in the cache.
|
||||||
#[derive(Clone, Eq, Hash, MallocSizeOf, PartialEq)]
|
#[derive(Clone, Eq, Hash, MallocSizeOf, PartialEq)]
|
||||||
pub struct CacheKey {
|
pub struct CacheKey {
|
||||||
url: ServoUrl
|
url: ServoUrl,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CacheKey {
|
impl CacheKey {
|
||||||
fn new(request: Request) -> CacheKey {
|
fn new(request: Request) -> CacheKey {
|
||||||
CacheKey {
|
CacheKey {
|
||||||
url: request.current_url().clone()
|
url: request.current_url().clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_servo_url(servo_url: &ServoUrl) -> CacheKey {
|
fn from_servo_url(servo_url: &ServoUrl) -> CacheKey {
|
||||||
CacheKey {
|
CacheKey {
|
||||||
url: servo_url.clone()
|
url: servo_url.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +62,7 @@ struct CachedResource {
|
||||||
body: Arc<Mutex<ResponseBody>>,
|
body: Arc<Mutex<ResponseBody>>,
|
||||||
aborted: Arc<AtomicBool>,
|
aborted: Arc<AtomicBool>,
|
||||||
awaiting_body: Arc<Mutex<Vec<Sender<Data>>>>,
|
awaiting_body: Arc<Mutex<Vec<Sender<Data>>>>,
|
||||||
data: Measurable<MeasurableCachedResource>
|
data: Measurable<MeasurableCachedResource>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, MallocSizeOf)]
|
#[derive(Clone, MallocSizeOf)]
|
||||||
|
@ -94,7 +93,7 @@ struct CachedMetadata {
|
||||||
/// Headers
|
/// Headers
|
||||||
pub headers: Arc<Mutex<HeaderMap>>,
|
pub headers: Arc<Mutex<HeaderMap>>,
|
||||||
/// Fields that implement MallocSizeOf
|
/// Fields that implement MallocSizeOf
|
||||||
pub data: Measurable<MeasurableCachedMetadata>
|
pub data: Measurable<MeasurableCachedMetadata>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, MallocSizeOf)]
|
#[derive(Clone, MallocSizeOf)]
|
||||||
|
@ -106,7 +105,7 @@ struct MeasurableCachedMetadata {
|
||||||
/// Character set.
|
/// Character set.
|
||||||
pub charset: Option<String>,
|
pub charset: Option<String>,
|
||||||
/// HTTP Status
|
/// HTTP Status
|
||||||
pub status: Option<(u16, Vec<u8>)>
|
pub status: Option<(u16, Vec<u8>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MallocSizeOf for CachedMetadata {
|
impl MallocSizeOf for CachedMetadata {
|
||||||
|
@ -122,7 +121,7 @@ pub struct CachedResponse {
|
||||||
/// The response constructed from the cached resource
|
/// The response constructed from the cached resource
|
||||||
pub response: Response,
|
pub response: Response,
|
||||||
/// The revalidation flag for the stored response
|
/// The revalidation flag for the stored response
|
||||||
pub needs_validation: bool
|
pub needs_validation: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A memory cache.
|
/// A memory cache.
|
||||||
|
@ -132,7 +131,6 @@ pub struct HttpCache {
|
||||||
entries: HashMap<CacheKey, Vec<CachedResource>>,
|
entries: HashMap<CacheKey, Vec<CachedResource>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Determine if a given response is cacheable based on the initial metadata received.
|
/// Determine if a given response is cacheable based on the initial metadata received.
|
||||||
/// Based on <https://tools.ietf.org/html/rfc7234#section-3>
|
/// Based on <https://tools.ietf.org/html/rfc7234#section-3>
|
||||||
fn response_is_cacheable(metadata: &Metadata) -> bool {
|
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();
|
let headers = metadata.headers.as_ref().unwrap();
|
||||||
if headers.contains_key(header::EXPIRES) ||
|
if headers.contains_key(header::EXPIRES) ||
|
||||||
headers.contains_key(header::LAST_MODIFIED) ||
|
headers.contains_key(header::LAST_MODIFIED) ||
|
||||||
headers.contains_key(header::ETAG) {
|
headers.contains_key(header::ETAG)
|
||||||
|
{
|
||||||
is_cacheable = true;
|
is_cacheable = true;
|
||||||
}
|
}
|
||||||
if let Some(ref directive) = headers.typed_get::<CacheControl>() {
|
if let Some(ref directive) = headers.typed_get::<CacheControl>() {
|
||||||
if directive.no_store() {
|
if directive.no_store() {
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
if directive.public() || directive.s_max_age().is_some() ||
|
if directive.public() ||
|
||||||
directive.max_age().is_some() || directive.no_cache()
|
directive.s_max_age().is_some() ||
|
||||||
|
directive.max_age().is_some() ||
|
||||||
|
directive.no_cache()
|
||||||
{
|
{
|
||||||
is_cacheable = true;
|
is_cacheable = true;
|
||||||
}
|
}
|
||||||
|
@ -245,12 +246,11 @@ fn get_response_expiry(response: &Response) -> Duration {
|
||||||
match *code {
|
match *code {
|
||||||
200 | 203 | 204 | 206 | 300 | 301 | 404 | 405 | 410 | 414 | 501 => {
|
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>
|
// 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.
|
// 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() {
|
if directives.public() {
|
||||||
return heuristic_freshness;
|
return heuristic_freshness;
|
||||||
}
|
}
|
||||||
|
@ -288,25 +288,30 @@ fn get_expiry_adjustment_from_request_headers(request: &Request, expires: Durati
|
||||||
return expires - min_fresh;
|
return expires - min_fresh;
|
||||||
}
|
}
|
||||||
if directive.no_cache() || directive.no_store() {
|
if directive.no_cache() || directive.no_store() {
|
||||||
return Duration::min_value()
|
return Duration::min_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
expires
|
expires
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a CachedResponse from a request and a CachedResource.
|
/// 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_resource: &CachedResource,
|
||||||
cached_headers: &HeaderMap,
|
cached_headers: &HeaderMap,
|
||||||
done_chan: &mut DoneChannel)
|
done_chan: &mut DoneChannel,
|
||||||
-> CachedResponse {
|
) -> CachedResponse {
|
||||||
let mut response = Response::new(cached_resource.data.metadata.data.final_url.clone());
|
let mut response = Response::new(cached_resource.data.metadata.data.final_url.clone());
|
||||||
response.headers = cached_headers.clone();
|
response.headers = cached_headers.clone();
|
||||||
response.body = cached_resource.body.clone();
|
response.body = cached_resource.body.clone();
|
||||||
if let ResponseBody::Receiving(_) = *cached_resource.body.lock().unwrap() {
|
if let ResponseBody::Receiving(_) = *cached_resource.body.lock().unwrap() {
|
||||||
let (done_sender, done_receiver) = channel();
|
let (done_sender, done_receiver) = channel();
|
||||||
*done_chan = Some((done_sender.clone(), 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);
|
||||||
}
|
}
|
||||||
response.location_url = cached_resource.data.location_url.clone();
|
response.location_url = cached_resource.data.location_url.clone();
|
||||||
response.status = cached_resource.data.status.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: 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
|
// 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>
|
// <https://tools.ietf.org/html/rfc7234#section-5.2.2.7>
|
||||||
let has_expired = (adjusted_expires < time_since_validated) ||
|
let has_expired =
|
||||||
(adjusted_expires == time_since_validated);
|
(adjusted_expires < time_since_validated) || (adjusted_expires == time_since_validated);
|
||||||
CachedResponse { response: response, needs_validation: has_expired }
|
CachedResponse {
|
||||||
|
response: response,
|
||||||
|
needs_validation: has_expired,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new resource, based on the bytes requested, and an existing resource,
|
/// Create a new resource, based on the bytes requested, and an existing resource,
|
||||||
/// with a status-code of 206.
|
/// with a status-code of 206.
|
||||||
fn create_resource_with_bytes_from_resource(bytes: &[u8], resource: &CachedResource)
|
fn create_resource_with_bytes_from_resource(
|
||||||
-> CachedResource {
|
bytes: &[u8],
|
||||||
|
resource: &CachedResource,
|
||||||
|
) -> CachedResource {
|
||||||
CachedResource {
|
CachedResource {
|
||||||
request_headers: resource.request_headers.clone(),
|
request_headers: resource.request_headers.clone(),
|
||||||
body: Arc::new(Mutex::new(ResponseBody::Done(bytes.to_owned()))),
|
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(),
|
url_list: resource.data.url_list.clone(),
|
||||||
expires: resource.data.expires.clone(),
|
expires: resource.data.expires.clone(),
|
||||||
last_validated: resource.data.last_validated.clone(),
|
last_validated: resource.data.last_validated.clone(),
|
||||||
})
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Support for range requests <https://tools.ietf.org/html/rfc7233>.
|
/// 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>,
|
candidates: Vec<&CachedResource>,
|
||||||
range_spec: Vec<(Bound<u64>, Bound<u64>)>,
|
range_spec: Vec<(Bound<u64>, Bound<u64>)>,
|
||||||
done_chan: &mut DoneChannel)
|
done_chan: &mut DoneChannel,
|
||||||
-> Option<CachedResponse> {
|
) -> Option<CachedResponse> {
|
||||||
let mut complete_cached_resources = candidates.iter().filter(|resource| {
|
let mut complete_cached_resources =
|
||||||
match resource.data.raw_status {
|
candidates
|
||||||
|
.iter()
|
||||||
|
.filter(|resource| match resource.data.raw_status {
|
||||||
Some((ref code, _)) => *code == 200,
|
Some((ref code, _)) => *code == 200,
|
||||||
None => false
|
None => false,
|
||||||
}
|
|
||||||
});
|
});
|
||||||
let partial_cached_resources = candidates.iter().filter(|resource| {
|
let partial_cached_resources =
|
||||||
match resource.data.raw_status {
|
candidates
|
||||||
|
.iter()
|
||||||
|
.filter(|resource| match resource.data.raw_status {
|
||||||
Some((ref code, _)) => *code == 206,
|
Some((ref code, _)) => *code == 206,
|
||||||
None => false
|
None => false,
|
||||||
}
|
|
||||||
});
|
});
|
||||||
match (range_spec.first().unwrap(), complete_cached_resources.next()) {
|
match (
|
||||||
|
range_spec.first().unwrap(),
|
||||||
|
complete_cached_resources.next(),
|
||||||
|
) {
|
||||||
// TODO: take the full range spec into account.
|
// TODO: take the full range spec into account.
|
||||||
// If we have a complete resource, take the request range from the body.
|
// 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,
|
// 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 e = end as usize + 1;
|
||||||
let requested = body.get(b..e);
|
let requested = body.get(b..e);
|
||||||
if let Some(bytes) = requested {
|
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_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);
|
return Some(cached_response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -400,9 +418,9 @@ fn handle_range_request(request: &Request,
|
||||||
if let Some(bytes_range) = range.bytes_range() {
|
if let Some(bytes_range) = range.bytes_range() {
|
||||||
bytes_range
|
bytes_range
|
||||||
} else {
|
} else {
|
||||||
continue
|
continue;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
_ => continue,
|
_ => continue,
|
||||||
};
|
};
|
||||||
if res_beginning - 1 < beginning && res_end + 1 > end {
|
if res_beginning - 1 < beginning && res_end + 1 > end {
|
||||||
|
@ -416,8 +434,10 @@ fn handle_range_request(request: &Request,
|
||||||
_ => continue,
|
_ => continue,
|
||||||
};
|
};
|
||||||
if let Some(bytes) = requested {
|
if let Some(bytes) = requested {
|
||||||
let new_resource = create_resource_with_bytes_from_resource(&bytes, partial_resource);
|
let new_resource =
|
||||||
let cached_response = create_cached_response(request, &new_resource, &*headers, done_chan);
|
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);
|
return Some(cached_response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -428,9 +448,11 @@ fn handle_range_request(request: &Request,
|
||||||
let b = beginning as usize;
|
let b = beginning as usize;
|
||||||
let requested = body.get(b..);
|
let requested = body.get(b..);
|
||||||
if let Some(bytes) = requested {
|
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_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);
|
return Some(cached_response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -457,8 +479,10 @@ fn handle_range_request(request: &Request,
|
||||||
_ => continue,
|
_ => continue,
|
||||||
};
|
};
|
||||||
if let Some(bytes) = requested {
|
if let Some(bytes) = requested {
|
||||||
let new_resource = create_resource_with_bytes_from_resource(&bytes, partial_resource);
|
let new_resource =
|
||||||
let cached_response = create_cached_response(request, &new_resource, &*headers, done_chan);
|
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);
|
return Some(cached_response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -469,9 +493,11 @@ fn handle_range_request(request: &Request,
|
||||||
let from_byte = body.len() - offset as usize;
|
let from_byte = body.len() - offset as usize;
|
||||||
let requested = body.get(from_byte..);
|
let requested = body.get(from_byte..);
|
||||||
if let Some(bytes) = requested {
|
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_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);
|
return Some(cached_response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -498,38 +524,47 @@ fn handle_range_request(request: &Request,
|
||||||
_ => continue,
|
_ => continue,
|
||||||
};
|
};
|
||||||
if let Some(bytes) = requested {
|
if let Some(bytes) = requested {
|
||||||
let new_resource = create_resource_with_bytes_from_resource(&bytes, partial_resource);
|
let new_resource =
|
||||||
let cached_response = create_cached_response(request, &new_resource, &*headers, done_chan);
|
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);
|
return Some(cached_response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// All the cases with Bound::Excluded should be unreachable anyway
|
// All the cases with Bound::Excluded should be unreachable anyway
|
||||||
_ => return None
|
_ => return None,
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl HttpCache {
|
impl HttpCache {
|
||||||
/// Create a new memory cache instance.
|
/// Create a new memory cache instance.
|
||||||
pub fn new() -> HttpCache {
|
pub fn new() -> HttpCache {
|
||||||
HttpCache {
|
HttpCache {
|
||||||
entries: HashMap::new()
|
entries: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructing Responses from Caches.
|
/// Constructing Responses from Caches.
|
||||||
/// <https://tools.ietf.org/html/rfc7234#section-4>
|
/// <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>
|
// TODO: generate warning headers as appropriate <https://tools.ietf.org/html/rfc7234#section-5.5>
|
||||||
if request.method != Method::GET {
|
if request.method != Method::GET {
|
||||||
// Only Get requests are cached, avoid a url based match for others.
|
// Only Get requests are cached, avoid a url based match for others.
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let entry_key = CacheKey::new(request.clone());
|
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![];
|
let mut candidates = vec![];
|
||||||
for cached_resource in resources {
|
for cached_resource in resources {
|
||||||
let mut can_be_constructed = true;
|
let mut can_be_constructed = true;
|
||||||
|
@ -545,7 +580,9 @@ impl HttpCache {
|
||||||
match request.headers.get(vary_val) {
|
match request.headers.get(vary_val) {
|
||||||
Some(header_data) => {
|
Some(header_data) => {
|
||||||
// If the header is present in the request.
|
// 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,
|
// Check that the value of the nominated header field,
|
||||||
// in the original request, matches the value in the current request.
|
// in the original request, matches the value in the current request.
|
||||||
if original_header_data != header_data {
|
if original_header_data != header_data {
|
||||||
|
@ -558,7 +595,8 @@ impl HttpCache {
|
||||||
// If a header field is absent from a request,
|
// If a header field is absent from a request,
|
||||||
// it can only match a stored response if those headers,
|
// it can only match a stored response if those headers,
|
||||||
// were also absent in the original request.
|
// 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 {
|
if !can_be_constructed {
|
||||||
|
@ -573,7 +611,12 @@ impl HttpCache {
|
||||||
}
|
}
|
||||||
// Support for range requests
|
// Support for range requests
|
||||||
if let Some(range_spec) = request.headers.typed_get::<Range>() {
|
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 {
|
} else {
|
||||||
// Not a Range request.
|
// Not a Range request.
|
||||||
if let Some(ref cached_resource) = candidates.first() {
|
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,
|
// 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.
|
// or using the Date header to return the most recent one.
|
||||||
let cached_headers = cached_resource.data.metadata.headers.lock().unwrap();
|
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);
|
return Some(cached_response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -602,7 +646,7 @@ impl HttpCache {
|
||||||
let _ = done_sender.send(Data::Payload(completed_body.clone()));
|
let _ = done_sender.send(Data::Payload(completed_body.clone()));
|
||||||
let _ = done_sender.send(Data::Done);
|
let _ = done_sender.send(Data::Done);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -610,7 +654,12 @@ impl HttpCache {
|
||||||
|
|
||||||
/// Freshening Stored Responses upon Validation.
|
/// Freshening Stored Responses upon Validation.
|
||||||
/// <https://tools.ietf.org/html/rfc7234#section-4.3.4>
|
/// <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));
|
assert_eq!(response.status.map(|s| s.0), Some(StatusCode::NOT_MODIFIED));
|
||||||
let entry_key = CacheKey::new(request.clone());
|
let entry_key = CacheKey::new(request.clone());
|
||||||
if let Some(cached_resources) = self.entries.get_mut(&entry_key) {
|
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.
|
// Otherwise, create a new dedicated channel to update the consumer.
|
||||||
// The response constructed here will replace the 304 one from the network.
|
// The response constructed here will replace the 304 one from the network.
|
||||||
let in_progress_channel = match *cached_resource.body.lock().unwrap() {
|
let in_progress_channel = match *cached_resource.body.lock().unwrap() {
|
||||||
ResponseBody::Receiving(..) => {
|
ResponseBody::Receiving(..) => Some(channel()),
|
||||||
Some(channel())
|
ResponseBody::Empty | ResponseBody::Done(..) => None,
|
||||||
},
|
|
||||||
ResponseBody::Empty | ResponseBody::Done(..) => None
|
|
||||||
};
|
};
|
||||||
match in_progress_channel {
|
match in_progress_channel {
|
||||||
Some((done_sender, done_receiver)) => {
|
Some((done_sender, done_receiver)) => {
|
||||||
*done_chan = Some((done_sender.clone(), 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.
|
// 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.
|
// 1. update the headers of the cached resource.
|
||||||
// 2. return a response, constructed from 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.body = cached_resource.body.clone();
|
||||||
constructed_response.status = cached_resource.data.status.clone();
|
constructed_response.status = cached_resource.data.status.clone();
|
||||||
constructed_response.https_state = cached_resource.data.https_state.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>
|
/// <https://tools.ietf.org/html/rfc7234#section-4.4>
|
||||||
pub fn invalidate(&mut self, request: &Request, response: &Response) {
|
pub fn invalidate(&mut self, request: &Request, response: &Response) {
|
||||||
// TODO(eijebong): Once headers support typed_get, update this to use them
|
// 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) {
|
if let Ok(url) = request.current_url().join(location) {
|
||||||
self.invalidate_for_url(&url);
|
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) {
|
if let Ok(url) = request.current_url().join(&content_location) {
|
||||||
self.invalidate_for_url(&url);
|
self.invalidate_for_url(&url);
|
||||||
|
@ -683,18 +742,23 @@ impl HttpCache {
|
||||||
/// Storing Responses in Caches.
|
/// Storing Responses in Caches.
|
||||||
/// <https://tools.ietf.org/html/rfc7234#section-3>
|
/// <https://tools.ietf.org/html/rfc7234#section-3>
|
||||||
pub fn store(&mut self, request: &Request, response: &Response) {
|
pub fn store(&mut self, request: &Request, response: &Response) {
|
||||||
if PREFS.get("network.http-cache.disabled").as_boolean().unwrap_or(false) {
|
if PREFS
|
||||||
return
|
.get("network.http-cache.disabled")
|
||||||
|
.as_boolean()
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if request.method != Method::GET {
|
if request.method != Method::GET {
|
||||||
// Only Get requests are cached.
|
// Only Get requests are cached.
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
let entry_key = CacheKey::new(request.clone());
|
let entry_key = CacheKey::new(request.clone());
|
||||||
let metadata = match response.metadata() {
|
let metadata = match response.metadata() {
|
||||||
Ok(FetchMetadata::Filtered {
|
Ok(FetchMetadata::Filtered {
|
||||||
filtered: _,
|
filtered: _,
|
||||||
unsafe_: metadata }) |
|
unsafe_: metadata,
|
||||||
|
}) |
|
||||||
Ok(FetchMetadata::Unfiltered(metadata)) => metadata,
|
Ok(FetchMetadata::Unfiltered(metadata)) => metadata,
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
@ -708,8 +772,8 @@ impl HttpCache {
|
||||||
final_url: metadata.final_url,
|
final_url: metadata.final_url,
|
||||||
content_type: metadata.content_type.map(|v| v.0.to_string()),
|
content_type: metadata.content_type.map(|v| v.0.to_string()),
|
||||||
charset: metadata.charset,
|
charset: metadata.charset,
|
||||||
status: metadata.status
|
status: metadata.status,
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
let entry_resource = CachedResource {
|
let entry_resource = CachedResource {
|
||||||
request_headers: Arc::new(Mutex::new(request.headers.clone())),
|
request_headers: Arc::new(Mutex::new(request.headers.clone())),
|
||||||
|
@ -724,11 +788,10 @@ impl HttpCache {
|
||||||
raw_status: response.raw_status.clone(),
|
raw_status: response.raw_status.clone(),
|
||||||
url_list: response.url_list.clone(),
|
url_list: response.url_list.clone(),
|
||||||
expires: expiry,
|
expires: expiry,
|
||||||
last_validated: time::now()
|
last_validated: time::now(),
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
let entry = self.entries.entry(entry_key).or_insert(vec![]);
|
let entry = self.entries.entry(entry_key).or_insert(vec![]);
|
||||||
entry.push(entry_resource);
|
entry.push(entry_resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -37,39 +37,39 @@ fn decode_bytes_sync(key: LoadKey, bytes: &[u8]) -> DecoderMsg {
|
||||||
let image = load_from_memory(bytes);
|
let image = load_from_memory(bytes);
|
||||||
DecoderMsg {
|
DecoderMsg {
|
||||||
key: key,
|
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();
|
let mut image = load_from_memory(&data).unwrap();
|
||||||
set_webrender_image_key(webrender_api, &mut image);
|
set_webrender_image_key(webrender_api, &mut image);
|
||||||
Ok(Arc::new(image))
|
Ok(Arc::new(image))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_webrender_image_key(webrender_api: &webrender_api::RenderApi, image: &mut 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 mut bytes = Vec::new();
|
||||||
let is_opaque = match image.format {
|
let is_opaque = match image.format {
|
||||||
PixelFormat::BGRA8 => {
|
PixelFormat::BGRA8 => {
|
||||||
bytes.extend_from_slice(&*image.bytes);
|
bytes.extend_from_slice(&*image.bytes);
|
||||||
pixels::premultiply_inplace(bytes.as_mut_slice())
|
pixels::premultiply_inplace(bytes.as_mut_slice())
|
||||||
}
|
},
|
||||||
PixelFormat::RGB8 => {
|
PixelFormat::RGB8 => {
|
||||||
for bgr in image.bytes.chunks(3) {
|
for bgr in image.bytes.chunks(3) {
|
||||||
bytes.extend_from_slice(&[
|
bytes.extend_from_slice(&[bgr[2], bgr[1], bgr[0], 0xff]);
|
||||||
bgr[2],
|
|
||||||
bgr[1],
|
|
||||||
bgr[0],
|
|
||||||
0xff
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
},
|
||||||
PixelFormat::K8 | PixelFormat::KA8 => {
|
PixelFormat::K8 | PixelFormat::KA8 => {
|
||||||
panic!("Not support by webrender yet");
|
panic!("Not support by webrender yet");
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
let descriptor = webrender_api::ImageDescriptor {
|
let descriptor = webrender_api::ImageDescriptor {
|
||||||
size: webrender_api::DeviceUintSize::new(image.width, image.height),
|
size: webrender_api::DeviceUintSize::new(image.width, image.height),
|
||||||
|
@ -121,20 +121,22 @@ impl AllPendingLoads {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove(&mut self, key: &LoadKey) -> Option<PendingLoad> {
|
fn remove(&mut self, key: &LoadKey) -> Option<PendingLoad> {
|
||||||
self.loads.remove(key).
|
self.loads.remove(key).and_then(|pending_load| {
|
||||||
and_then(|pending_load| {
|
|
||||||
self.url_to_load_key.remove(&pending_load.url).unwrap();
|
self.url_to_load_key.remove(&pending_load.url).unwrap();
|
||||||
Some(pending_load)
|
Some(pending_load)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_cached<'a>(&'a mut self, url: ServoUrl, can_request: CanRequestImages)
|
fn get_cached<'a>(
|
||||||
-> CacheResult<'a> {
|
&'a mut self,
|
||||||
|
url: ServoUrl,
|
||||||
|
can_request: CanRequestImages,
|
||||||
|
) -> CacheResult<'a> {
|
||||||
match self.url_to_load_key.entry(url.clone()) {
|
match self.url_to_load_key.entry(url.clone()) {
|
||||||
Occupied(url_entry) => {
|
Occupied(url_entry) => {
|
||||||
let load_key = url_entry.get();
|
let load_key = url_entry.get();
|
||||||
CacheResult::Hit(*load_key, self.loads.get_mut(load_key).unwrap())
|
CacheResult::Hit(*load_key, self.loads.get_mut(load_key).unwrap())
|
||||||
}
|
},
|
||||||
Vacant(url_entry) => {
|
Vacant(url_entry) => {
|
||||||
if can_request == CanRequestImages::No {
|
if can_request == CanRequestImages::No {
|
||||||
return CacheResult::Miss(None);
|
return CacheResult::Miss(None);
|
||||||
|
@ -149,9 +151,9 @@ impl AllPendingLoads {
|
||||||
Vacant(load_entry) => {
|
Vacant(load_entry) => {
|
||||||
let mut_load = load_entry.insert(pending_load);
|
let mut_load = load_entry.insert(pending_load);
|
||||||
CacheResult::Miss(Some((load_key, mut_load)))
|
CacheResult::Miss(Some((load_key, mut_load)))
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -226,14 +228,12 @@ impl ImageBytes {
|
||||||
type LoadKey = PendingImageId;
|
type LoadKey = PendingImageId;
|
||||||
|
|
||||||
struct LoadKeyGenerator {
|
struct LoadKeyGenerator {
|
||||||
counter: u64
|
counter: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LoadKeyGenerator {
|
impl LoadKeyGenerator {
|
||||||
fn new() -> LoadKeyGenerator {
|
fn new() -> LoadKeyGenerator {
|
||||||
LoadKeyGenerator {
|
LoadKeyGenerator { counter: 0 }
|
||||||
counter: 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fn next(&mut self) -> PendingImageId {
|
fn next(&mut self) -> PendingImageId {
|
||||||
self.counter += 1;
|
self.counter += 1;
|
||||||
|
@ -244,7 +244,7 @@ impl LoadKeyGenerator {
|
||||||
enum LoadResult {
|
enum LoadResult {
|
||||||
Loaded(Image),
|
Loaded(Image),
|
||||||
PlaceholderLoaded(Arc<Image>),
|
PlaceholderLoaded(Arc<Image>),
|
||||||
None
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents an image that is either being loaded
|
/// Represents an image that is either being loaded
|
||||||
|
@ -271,10 +271,10 @@ struct PendingLoad {
|
||||||
impl PendingLoad {
|
impl PendingLoad {
|
||||||
fn new(url: ServoUrl) -> PendingLoad {
|
fn new(url: ServoUrl) -> PendingLoad {
|
||||||
PendingLoad {
|
PendingLoad {
|
||||||
bytes: ImageBytes::InProgress(vec!()),
|
bytes: ImageBytes::InProgress(vec![]),
|
||||||
metadata: None,
|
metadata: None,
|
||||||
result: None,
|
result: None,
|
||||||
listeners: vec!(),
|
listeners: vec![],
|
||||||
url: url,
|
url: url,
|
||||||
final_url: None,
|
final_url: None,
|
||||||
}
|
}
|
||||||
|
@ -314,20 +314,24 @@ impl ImageCacheStore {
|
||||||
};
|
};
|
||||||
|
|
||||||
match load_result {
|
match load_result {
|
||||||
LoadResult::Loaded(ref mut image) => set_webrender_image_key(&self.webrender_api, image),
|
LoadResult::Loaded(ref mut image) => {
|
||||||
LoadResult::PlaceholderLoaded(..) | LoadResult::None => {}
|
set_webrender_image_key(&self.webrender_api, image)
|
||||||
|
},
|
||||||
|
LoadResult::PlaceholderLoaded(..) | LoadResult::None => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
let url = pending_load.final_url.clone();
|
let url = pending_load.final_url.clone();
|
||||||
let image_response = match load_result {
|
let image_response = match load_result {
|
||||||
LoadResult::Loaded(image) => ImageResponse::Loaded(Arc::new(image), url.unwrap()),
|
LoadResult::Loaded(image) => ImageResponse::Loaded(Arc::new(image), url.unwrap()),
|
||||||
LoadResult::PlaceholderLoaded(image) =>
|
LoadResult::PlaceholderLoaded(image) => {
|
||||||
ImageResponse::PlaceholderLoaded(image, self.placeholder_url.clone()),
|
ImageResponse::PlaceholderLoaded(image, self.placeholder_url.clone())
|
||||||
|
},
|
||||||
LoadResult::None => ImageResponse::None,
|
LoadResult::None => ImageResponse::None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let completed_load = CompletedLoad::new(image_response.clone(), key);
|
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 {
|
for listener in pending_load.listeners {
|
||||||
listener.respond(image_response.clone());
|
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
|
/// 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.
|
/// or the complete load is not fully decoded or is unavailable.
|
||||||
fn get_completed_image_if_available(&self,
|
fn get_completed_image_if_available(
|
||||||
|
&self,
|
||||||
url: &ServoUrl,
|
url: &ServoUrl,
|
||||||
placeholder: UsePlaceholder)
|
placeholder: UsePlaceholder,
|
||||||
-> Option<Result<ImageOrMetadataAvailable, ImageState>> {
|
) -> Option<Result<ImageOrMetadataAvailable, ImageState>> {
|
||||||
self.completed_loads.get(url).map(|completed_load| {
|
self.completed_loads.get(url).map(|completed_load| {
|
||||||
match (&completed_load.image_response, placeholder) {
|
match (&completed_load.image_response, placeholder) {
|
||||||
(&ImageResponse::Loaded(ref image, ref url), _) |
|
(&ImageResponse::Loaded(ref image, ref url), _) |
|
||||||
(&ImageResponse::PlaceholderLoaded(ref image, ref url), UsePlaceholder::Yes) => {
|
(&ImageResponse::PlaceholderLoaded(ref image, ref url), UsePlaceholder::Yes) => Ok(
|
||||||
Ok(ImageOrMetadataAvailable::ImageAvailable(image.clone(), url.clone()))
|
ImageOrMetadataAvailable::ImageAvailable(image.clone(), url.clone()),
|
||||||
}
|
),
|
||||||
(&ImageResponse::PlaceholderLoaded(_, _), UsePlaceholder::No) |
|
(&ImageResponse::PlaceholderLoaded(_, _), UsePlaceholder::No) |
|
||||||
(&ImageResponse::None, _) |
|
(&ImageResponse::None, _) |
|
||||||
(&ImageResponse::MetadataLoaded(_), _) => {
|
(&ImageResponse::MetadataLoaded(_), _) => Err(ImageState::LoadError),
|
||||||
Err(ImageState::LoadError)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -383,18 +386,19 @@ impl ImageCache for ImageCacheImpl {
|
||||||
placeholder_image: get_placeholder_image(&webrender_api, &rippy_data).ok(),
|
placeholder_image: get_placeholder_image(&webrender_api, &rippy_data).ok(),
|
||||||
placeholder_url: ServoUrl::parse("chrome://resources/rippy.png").unwrap(),
|
placeholder_url: ServoUrl::parse("chrome://resources/rippy.png").unwrap(),
|
||||||
webrender_api: webrender_api,
|
webrender_api: webrender_api,
|
||||||
}))
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return any available metadata or image for the given URL,
|
/// 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 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.
|
/// or else reserve a slot in the cache for the URL if the consumer can request images.
|
||||||
fn find_image_or_metadata(&self,
|
fn find_image_or_metadata(
|
||||||
|
&self,
|
||||||
url: ServoUrl,
|
url: ServoUrl,
|
||||||
use_placeholder: UsePlaceholder,
|
use_placeholder: UsePlaceholder,
|
||||||
can_request: CanRequestImages)
|
can_request: CanRequestImages,
|
||||||
-> Result<ImageOrMetadataAvailable, ImageState> {
|
) -> Result<ImageOrMetadataAvailable, ImageState> {
|
||||||
debug!("Find image or metadata for {}", url);
|
debug!("Find image or metadata for {}", url);
|
||||||
let mut store = self.store.lock().unwrap();
|
let mut store = self.store.lock().unwrap();
|
||||||
if let Some(result) = store.get_completed_image_if_available(&url, use_placeholder) {
|
if let Some(result) = store.get_completed_image_if_available(&url, use_placeholder) {
|
||||||
|
@ -409,24 +413,24 @@ impl ImageCache for ImageCacheImpl {
|
||||||
(&Some(Ok(_)), _) => {
|
(&Some(Ok(_)), _) => {
|
||||||
debug!("Sync decoding {} ({:?})", url, key);
|
debug!("Sync decoding {} ({:?})", url, key);
|
||||||
decode_bytes_sync(key, &pl.bytes.as_slice())
|
decode_bytes_sync(key, &pl.bytes.as_slice())
|
||||||
}
|
},
|
||||||
(&None, &Some(ref meta)) => {
|
(&None, &Some(ref meta)) => {
|
||||||
debug!("Metadata available for {} ({:?})", url, key);
|
debug!("Metadata available for {} ({:?})", url, key);
|
||||||
return Ok(ImageOrMetadataAvailable::MetadataAvailable(meta.clone()))
|
return Ok(ImageOrMetadataAvailable::MetadataAvailable(meta.clone()));
|
||||||
}
|
},
|
||||||
(&Some(Err(_)), _) | (&None, &None) => {
|
(&Some(Err(_)), _) | (&None, &None) => {
|
||||||
debug!("{} ({:?}) is still pending", url, key);
|
debug!("{} ({:?}) is still pending", url, key);
|
||||||
return Err(ImageState::Pending(key));
|
return Err(ImageState::Pending(key));
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
CacheResult::Miss(Some((key, _pl))) => {
|
CacheResult::Miss(Some((key, _pl))) => {
|
||||||
debug!("Should be requesting {} ({:?})", url, key);
|
debug!("Should be requesting {} ({:?})", url, key);
|
||||||
return Err(ImageState::NotRequested(key));
|
return Err(ImageState::NotRequested(key));
|
||||||
}
|
},
|
||||||
CacheResult::Miss(None) => {
|
CacheResult::Miss(None) => {
|
||||||
debug!("Couldn't find an entry for {}", url);
|
debug!("Couldn't find an entry for {}", url);
|
||||||
return Err(ImageState::LoadError);
|
return Err(ImageState::LoadError);
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -468,17 +472,15 @@ impl ImageCache for ImageCacheImpl {
|
||||||
let mut store = self.store.lock().unwrap();
|
let mut store = self.store.lock().unwrap();
|
||||||
let pending_load = store.pending_loads.get_by_key_mut(&id).unwrap();
|
let pending_load = store.pending_loads.get_by_key_mut(&id).unwrap();
|
||||||
let metadata = match response {
|
let metadata = match response {
|
||||||
Ok(meta) => {
|
Ok(meta) => Some(match meta {
|
||||||
Some(match meta {
|
|
||||||
FetchMetadata::Unfiltered(m) => m,
|
FetchMetadata::Unfiltered(m) => m,
|
||||||
FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
|
FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
|
||||||
})
|
}),
|
||||||
},
|
|
||||||
Err(_) => None,
|
Err(_) => None,
|
||||||
};
|
};
|
||||||
let final_url = metadata.as_ref().map(|m| m.final_url.clone());
|
let final_url = metadata.as_ref().map(|m| m.final_url.clone());
|
||||||
pending_load.final_url = final_url;
|
pending_load.final_url = final_url;
|
||||||
}
|
},
|
||||||
(FetchResponseMsg::ProcessResponseChunk(data), _) => {
|
(FetchResponseMsg::ProcessResponseChunk(data), _) => {
|
||||||
debug!("Got some data for {:?}", id);
|
debug!("Got some data for {:?}", id);
|
||||||
let mut store = self.store.lock().unwrap();
|
let mut store = self.store.lock().unwrap();
|
||||||
|
@ -488,16 +490,17 @@ impl ImageCache for ImageCacheImpl {
|
||||||
if let None = pending_load.metadata {
|
if let None = pending_load.metadata {
|
||||||
if let Ok(metadata) = load_from_buf(&pending_load.bytes.as_slice()) {
|
if let Ok(metadata) = load_from_buf(&pending_load.bytes.as_slice()) {
|
||||||
let dimensions = metadata.dimensions();
|
let dimensions = metadata.dimensions();
|
||||||
let img_metadata = ImageMetadata { width: dimensions.width,
|
let img_metadata = ImageMetadata {
|
||||||
height: dimensions.height };
|
width: dimensions.width,
|
||||||
|
height: dimensions.height,
|
||||||
|
};
|
||||||
for listener in &pending_load.listeners {
|
for listener in &pending_load.listeners {
|
||||||
listener.respond(
|
listener.respond(ImageResponse::MetadataLoaded(img_metadata.clone()));
|
||||||
ImageResponse::MetadataLoaded(img_metadata.clone()));
|
|
||||||
}
|
}
|
||||||
pending_load.metadata = Some(img_metadata);
|
pending_load.metadata = Some(img_metadata);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
(FetchResponseMsg::ProcessResponseEOF(result), key) => {
|
(FetchResponseMsg::ProcessResponseEOF(result), key) => {
|
||||||
debug!("Received EOF for {:?}", key);
|
debug!("Received EOF for {:?}", key);
|
||||||
match result {
|
match result {
|
||||||
|
@ -516,20 +519,20 @@ impl ImageCache for ImageCacheImpl {
|
||||||
debug!("Image decoded");
|
debug!("Image decoded");
|
||||||
local_store.lock().unwrap().handle_decoder(msg);
|
local_store.lock().unwrap().handle_decoder(msg);
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
debug!("Processing error for {:?}", key);
|
debug!("Processing error for {:?}", key);
|
||||||
let mut store = self.store.lock().unwrap();
|
let mut store = self.store.lock().unwrap();
|
||||||
match store.placeholder_image.clone() {
|
match store.placeholder_image.clone() {
|
||||||
Some(placeholder_image) => {
|
Some(placeholder_image) => store.complete_load(
|
||||||
store.complete_load(
|
id,
|
||||||
id, LoadResult::PlaceholderLoaded(placeholder_image))
|
LoadResult::PlaceholderLoaded(placeholder_image),
|
||||||
}
|
),
|
||||||
None => store.complete_load(id, LoadResult::None),
|
None => store.complete_load(id, LoadResult::None),
|
||||||
}
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,10 +21,14 @@ extern crate immeta;
|
||||||
extern crate ipc_channel;
|
extern crate ipc_channel;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
#[macro_use] extern crate log;
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
extern crate malloc_size_of;
|
extern crate malloc_size_of;
|
||||||
#[macro_use] extern crate malloc_size_of_derive;
|
#[macro_use]
|
||||||
#[macro_use] #[no_link] extern crate matches;
|
extern crate malloc_size_of_derive;
|
||||||
|
#[macro_use]
|
||||||
|
#[no_link]
|
||||||
|
extern crate matches;
|
||||||
extern crate mime;
|
extern crate mime;
|
||||||
extern crate mime_guess;
|
extern crate mime_guess;
|
||||||
extern crate msg;
|
extern crate msg;
|
||||||
|
@ -33,7 +37,8 @@ extern crate openssl;
|
||||||
extern crate pixels;
|
extern crate pixels;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate profile_traits;
|
extern crate profile_traits;
|
||||||
#[macro_use] extern crate serde;
|
#[macro_use]
|
||||||
|
extern crate serde;
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
extern crate servo_allocator;
|
extern crate servo_allocator;
|
||||||
extern crate servo_arc;
|
extern crate servo_arc;
|
||||||
|
|
|
@ -25,16 +25,17 @@ pub enum MediaType {
|
||||||
|
|
||||||
pub enum ApacheBugFlag {
|
pub enum ApacheBugFlag {
|
||||||
On,
|
On,
|
||||||
Off
|
Off,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ApacheBugFlag {
|
impl ApacheBugFlag {
|
||||||
/// <https://mimesniff.spec.whatwg.org/#supplied-mime-type-detection-algorithm>
|
/// <https://mimesniff.spec.whatwg.org/#supplied-mime-type-detection-algorithm>
|
||||||
pub fn from_content_type(last_raw_content_type: &[u8]) -> ApacheBugFlag {
|
pub fn from_content_type(last_raw_content_type: &[u8]) -> ApacheBugFlag {
|
||||||
if last_raw_content_type == b"text/plain"
|
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=iso-8859-1"
|
last_raw_content_type == b"text/plain; charset=iso-8859-1" ||
|
||||||
|| last_raw_content_type == b"text/plain; charset=UTF-8" {
|
last_raw_content_type == b"text/plain; charset=UTF-8"
|
||||||
|
{
|
||||||
ApacheBugFlag::On
|
ApacheBugFlag::On
|
||||||
} else {
|
} else {
|
||||||
ApacheBugFlag::Off
|
ApacheBugFlag::Off
|
||||||
|
@ -45,19 +46,22 @@ impl ApacheBugFlag {
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub enum NoSniffFlag {
|
pub enum NoSniffFlag {
|
||||||
On,
|
On,
|
||||||
Off
|
Off,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl MimeClassifier {
|
impl MimeClassifier {
|
||||||
//Performs MIME Type Sniffing Algorithm (sections 7 and 8)
|
//Performs MIME Type Sniffing Algorithm (sections 7 and 8)
|
||||||
pub fn classify<'a>(&'a self,
|
pub fn classify<'a>(
|
||||||
|
&'a self,
|
||||||
context: LoadContext,
|
context: LoadContext,
|
||||||
no_sniff_flag: NoSniffFlag,
|
no_sniff_flag: NoSniffFlag,
|
||||||
apache_bug_flag: ApacheBugFlag,
|
apache_bug_flag: ApacheBugFlag,
|
||||||
supplied_type: &Option<Mime>,
|
supplied_type: &Option<Mime>,
|
||||||
data: &'a [u8]) -> Mime {
|
data: &'a [u8],
|
||||||
let supplied_type_or_octet_stream = supplied_type.clone().unwrap_or(mime::APPLICATION_OCTET_STREAM);
|
) -> Mime {
|
||||||
|
let supplied_type_or_octet_stream = supplied_type
|
||||||
|
.clone()
|
||||||
|
.unwrap_or(mime::APPLICATION_OCTET_STREAM);
|
||||||
match context {
|
match context {
|
||||||
LoadContext::Browsing => match *supplied_type {
|
LoadContext::Browsing => match *supplied_type {
|
||||||
None => self.sniff_unknown_type(no_sniff_flag, data),
|
None => self.sniff_unknown_type(no_sniff_flag, data),
|
||||||
|
@ -69,30 +73,41 @@ impl MimeClassifier {
|
||||||
NoSniffFlag::On => supplied_type.clone(),
|
NoSniffFlag::On => supplied_type.clone(),
|
||||||
NoSniffFlag::Off => match apache_bug_flag {
|
NoSniffFlag::Off => match apache_bug_flag {
|
||||||
ApacheBugFlag::On => self.sniff_text_or_data(data),
|
ApacheBugFlag::On => self.sniff_text_or_data(data),
|
||||||
ApacheBugFlag::Off => match MimeClassifier::get_media_type(supplied_type) {
|
ApacheBugFlag::Off => {
|
||||||
Some(MediaType::Html) => self.feeds_classifier.classify(data),
|
match MimeClassifier::get_media_type(supplied_type) {
|
||||||
Some(MediaType::Image) => self.image_classifier.classify(data),
|
Some(MediaType::Html) => {
|
||||||
Some(MediaType::AudioVideo) => self.audio_video_classifier.classify(data),
|
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,
|
Some(MediaType::Xml) | None => None,
|
||||||
}.unwrap_or(supplied_type.clone())
|
}
|
||||||
}
|
.unwrap_or(supplied_type.clone())
|
||||||
}
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
},
|
||||||
LoadContext::Image => {
|
LoadContext::Image => {
|
||||||
// Section 8.2 Sniffing an image context
|
// Section 8.2 Sniffing an image context
|
||||||
match MimeClassifier::maybe_get_media_type(supplied_type) {
|
match MimeClassifier::maybe_get_media_type(supplied_type) {
|
||||||
Some(MediaType::Xml) => None,
|
Some(MediaType::Xml) => None,
|
||||||
_ => self.image_classifier.classify(data),
|
_ => self.image_classifier.classify(data),
|
||||||
}.unwrap_or(supplied_type_or_octet_stream)
|
}
|
||||||
|
.unwrap_or(supplied_type_or_octet_stream)
|
||||||
},
|
},
|
||||||
LoadContext::AudioVideo => {
|
LoadContext::AudioVideo => {
|
||||||
// Section 8.3 Sniffing an image context
|
// Section 8.3 Sniffing an image context
|
||||||
match MimeClassifier::maybe_get_media_type(supplied_type) {
|
match MimeClassifier::maybe_get_media_type(supplied_type) {
|
||||||
Some(MediaType::Xml) => None,
|
Some(MediaType::Xml) => None,
|
||||||
_ => self.audio_video_classifier.classify(data),
|
_ => self.audio_video_classifier.classify(data),
|
||||||
}.unwrap_or(supplied_type_or_octet_stream)
|
}
|
||||||
|
.unwrap_or(supplied_type_or_octet_stream)
|
||||||
},
|
},
|
||||||
LoadContext::Plugin => {
|
LoadContext::Plugin => {
|
||||||
// 8.4 Sniffing in a plugin context
|
// 8.4 Sniffing in a plugin context
|
||||||
|
@ -129,7 +144,8 @@ impl MimeClassifier {
|
||||||
match MimeClassifier::maybe_get_media_type(supplied_type) {
|
match MimeClassifier::maybe_get_media_type(supplied_type) {
|
||||||
Some(MediaType::Xml) => None,
|
Some(MediaType::Xml) => None,
|
||||||
_ => self.font_classifier.classify(data),
|
_ => self.font_classifier.classify(data),
|
||||||
}.unwrap_or(supplied_type_or_octet_stream)
|
}
|
||||||
|
.unwrap_or(supplied_type_or_octet_stream)
|
||||||
},
|
},
|
||||||
LoadContext::TextTrack => {
|
LoadContext::TextTrack => {
|
||||||
// 8.8 Sniffing in a text track context
|
// 8.8 Sniffing in a text track context
|
||||||
|
@ -157,7 +173,7 @@ impl MimeClassifier {
|
||||||
archive_classifier: GroupedClassifier::archive_classifier(),
|
archive_classifier: GroupedClassifier::archive_classifier(),
|
||||||
binary_or_plaintext: BinaryOrPlaintextClassifier,
|
binary_or_plaintext: BinaryOrPlaintextClassifier,
|
||||||
feeds_classifier: FeedsClassifier,
|
feeds_classifier: FeedsClassifier,
|
||||||
font_classifier: GroupedClassifier::font_classifier()
|
font_classifier: GroupedClassifier::font_classifier(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,7 +198,8 @@ impl MimeClassifier {
|
||||||
None
|
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.image_classifier.classify(data))
|
||||||
.or_else(|| self.audio_video_classifier.classify(data))
|
.or_else(|| self.audio_video_classifier.classify(data))
|
||||||
.or_else(|| self.archive_classifier.classify(data))
|
.or_else(|| self.archive_classifier.classify(data))
|
||||||
|
@ -191,7 +208,9 @@ impl MimeClassifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sniff_text_or_data<'a>(&'a self, data: &'a [u8]) -> Mime {
|
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 {
|
fn is_xml(mt: &Mime) -> bool {
|
||||||
|
@ -235,9 +254,9 @@ impl MimeClassifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn maybe_get_media_type(supplied_type: &Option<Mime>) -> Option<MediaType> {
|
fn maybe_get_media_type(supplied_type: &Option<Mime>) -> Option<MediaType> {
|
||||||
supplied_type.as_ref().and_then(|ref mime| {
|
supplied_type
|
||||||
MimeClassifier::get_media_type(mime)
|
.as_ref()
|
||||||
})
|
.and_then(|ref mime| MimeClassifier::get_media_type(mime))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,7 +289,7 @@ impl <'a, T: Iterator<Item=&'a u8> + Clone> Matches for T {
|
||||||
fn matches(&mut self, matches: &[u8]) -> bool {
|
fn matches(&mut self, matches: &[u8]) -> bool {
|
||||||
if self.clone().nth(matches.len()).is_none() {
|
if self.clone().nth(matches.len()).is_none() {
|
||||||
// there are less than matches.len() elements in self
|
// there are less than matches.len() elements in self
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
let result = self.clone().zip(matches).all(|(s, m)| *s == *m);
|
let result = self.clone().zip(matches).all(|(s, m)| *s == *m);
|
||||||
if result {
|
if result {
|
||||||
|
@ -294,15 +313,20 @@ impl ByteMatcher {
|
||||||
} else if data == self.pattern {
|
} else if data == self.pattern {
|
||||||
Some(self.pattern.len())
|
Some(self.pattern.len())
|
||||||
} else {
|
} else {
|
||||||
data[..data.len() - self.pattern.len() + 1].iter()
|
data[..data.len() - self.pattern.len() + 1]
|
||||||
|
.iter()
|
||||||
.position(|x| !self.leading_ignore.contains(x))
|
.position(|x| !self.leading_ignore.contains(x))
|
||||||
.and_then(|start|
|
.and_then(|start| {
|
||||||
if data[start..].iter()
|
if data[start..]
|
||||||
.zip(self.pattern.iter()).zip(self.mask.iter())
|
.iter()
|
||||||
.all(|((&data, &pattern), &mask)| (data & mask) == pattern) {
|
.zip(self.pattern.iter())
|
||||||
|
.zip(self.mask.iter())
|
||||||
|
.all(|((&data, &pattern), &mask)| (data & mask) == pattern)
|
||||||
|
{
|
||||||
Some(start + self.pattern.len())
|
Some(start + self.pattern.len())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -310,47 +334,46 @@ impl ByteMatcher {
|
||||||
|
|
||||||
impl MIMEChecker for ByteMatcher {
|
impl MIMEChecker for ByteMatcher {
|
||||||
fn classify(&self, data: &[u8]) -> Option<Mime> {
|
fn classify(&self, data: &[u8]) -> Option<Mime> {
|
||||||
self.matches(data).map(|_| {
|
self.matches(data).map(|_| self.content_type.clone())
|
||||||
self.content_type.clone()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate(&self) -> Result<(), String> {
|
fn validate(&self) -> Result<(), String> {
|
||||||
if self.pattern.len() == 0 {
|
if self.pattern.len() == 0 {
|
||||||
return Err(format!(
|
return Err(format!("Zero length pattern for {:?}", self.content_type));
|
||||||
"Zero length pattern for {:?}",
|
|
||||||
self.content_type
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
if self.pattern.len() != self.mask.len() {
|
if self.pattern.len() != self.mask.len() {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Unequal pattern and mask length for {:?}",
|
"Unequal pattern and mask length for {:?}",
|
||||||
self.content_type
|
self.content_type
|
||||||
))
|
));
|
||||||
}
|
}
|
||||||
if self.pattern.iter().zip(self.mask.iter()).any(
|
if self
|
||||||
|(&pattern, &mask)| pattern & mask != pattern
|
.pattern
|
||||||
) {
|
.iter()
|
||||||
|
.zip(self.mask.iter())
|
||||||
|
.any(|(&pattern, &mask)| pattern & mask != pattern)
|
||||||
|
{
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Pattern not pre-masked for {:?}",
|
"Pattern not pre-masked for {:?}",
|
||||||
self.content_type
|
self.content_type
|
||||||
))
|
));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TagTerminatedByteMatcher {
|
struct TagTerminatedByteMatcher {
|
||||||
matcher: ByteMatcher
|
matcher: ByteMatcher,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MIMEChecker for TagTerminatedByteMatcher {
|
impl MIMEChecker for TagTerminatedByteMatcher {
|
||||||
fn classify(&self, data: &[u8]) -> Option<Mime> {
|
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'>') {
|
if j < data.len() && (data[j] == b' ' || data[j] == b'>') {
|
||||||
Some(self.matcher.content_type.clone())
|
Some(self.matcher.content_type.clone())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -367,8 +390,10 @@ impl Mp4Matcher {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let box_size = ((data[0] as u32) << 24 | (data[1] as u32) << 16 |
|
let box_size = ((data[0] as u32) << 24 |
|
||||||
(data[2] as u32) << 8 | (data[3] as u32)) as usize;
|
(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) {
|
if (data.len() < box_size) || (box_size % 4 != 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -380,9 +405,10 @@ impl Mp4Matcher {
|
||||||
|
|
||||||
let mp4 = [0x6D, 0x70, 0x34];
|
let mp4 = [0x6D, 0x70, 0x34];
|
||||||
data[8..].starts_with(&mp4) ||
|
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 {
|
impl MIMEChecker for Mp4Matcher {
|
||||||
fn classify(&self, data: &[u8]) -> Option<Mime> {
|
fn classify(&self, data: &[u8]) -> Option<Mime> {
|
||||||
|
@ -407,10 +433,12 @@ impl BinaryOrPlaintextClassifier {
|
||||||
data.starts_with(&[0xEFu8, 0xBBu8, 0xBFu8])
|
data.starts_with(&[0xEFu8, 0xBBu8, 0xBFu8])
|
||||||
{
|
{
|
||||||
mime::TEXT_PLAIN
|
mime::TEXT_PLAIN
|
||||||
} else if data.iter().any(|&x| x <= 0x08u8 ||
|
} else if data.iter().any(|&x| {
|
||||||
|
x <= 0x08u8 ||
|
||||||
x == 0x0Bu8 ||
|
x == 0x0Bu8 ||
|
||||||
(x >= 0x0Eu8 && x <= 0x1Au8) ||
|
(x >= 0x0Eu8 && x <= 0x1Au8) ||
|
||||||
(x >= 0x1Cu8 && x <= 0x1Fu8)) {
|
(x >= 0x1Cu8 && x <= 0x1Fu8)
|
||||||
|
}) {
|
||||||
mime::APPLICATION_OCTET_STREAM
|
mime::APPLICATION_OCTET_STREAM
|
||||||
} else {
|
} else {
|
||||||
mime::TEXT_PLAIN
|
mime::TEXT_PLAIN
|
||||||
|
@ -425,7 +453,6 @@ impl MIMEChecker for BinaryOrPlaintextClassifier {
|
||||||
fn validate(&self) -> Result<(), String> {
|
fn validate(&self) -> Result<(), String> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
struct GroupedClassifier {
|
struct GroupedClassifier {
|
||||||
byte_matchers: Vec<Box<MIMEChecker + Send + Sync>>,
|
byte_matchers: Vec<Box<MIMEChecker + Send + Sync>>,
|
||||||
|
@ -442,7 +469,7 @@ impl GroupedClassifier {
|
||||||
Box::new(ByteMatcher::image_webp()),
|
Box::new(ByteMatcher::image_webp()),
|
||||||
Box::new(ByteMatcher::image_png()),
|
Box::new(ByteMatcher::image_png()),
|
||||||
Box::new(ByteMatcher::image_jpeg()),
|
Box::new(ByteMatcher::image_jpeg()),
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn audio_video_classifier() -> GroupedClassifier {
|
fn audio_video_classifier() -> GroupedClassifier {
|
||||||
|
@ -456,8 +483,8 @@ impl GroupedClassifier {
|
||||||
Box::new(ByteMatcher::audio_midi()),
|
Box::new(ByteMatcher::audio_midi()),
|
||||||
Box::new(ByteMatcher::video_avi()),
|
Box::new(ByteMatcher::video_avi()),
|
||||||
Box::new(ByteMatcher::audio_wave()),
|
Box::new(ByteMatcher::audio_wave()),
|
||||||
Box::new(Mp4Matcher)
|
Box::new(Mp4Matcher),
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn scriptable_classifier() -> GroupedClassifier {
|
fn scriptable_classifier() -> GroupedClassifier {
|
||||||
|
@ -481,8 +508,8 @@ impl GroupedClassifier {
|
||||||
Box::new(ByteMatcher::text_html_p()),
|
Box::new(ByteMatcher::text_html_p()),
|
||||||
Box::new(ByteMatcher::text_html_comment()),
|
Box::new(ByteMatcher::text_html_comment()),
|
||||||
Box::new(ByteMatcher::text_xml()),
|
Box::new(ByteMatcher::text_xml()),
|
||||||
Box::new(ByteMatcher::application_pdf())
|
Box::new(ByteMatcher::application_pdf()),
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn plaintext_classifier() -> GroupedClassifier {
|
fn plaintext_classifier() -> GroupedClassifier {
|
||||||
|
@ -491,8 +518,8 @@ impl GroupedClassifier {
|
||||||
Box::new(ByteMatcher::text_plain_utf_8_bom()),
|
Box::new(ByteMatcher::text_plain_utf_8_bom()),
|
||||||
Box::new(ByteMatcher::text_plain_utf_16le_bom()),
|
Box::new(ByteMatcher::text_plain_utf_16le_bom()),
|
||||||
Box::new(ByteMatcher::text_plain_utf_16be_bom()),
|
Box::new(ByteMatcher::text_plain_utf_16be_bom()),
|
||||||
Box::new(ByteMatcher::application_postscript())
|
Box::new(ByteMatcher::application_postscript()),
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn archive_classifier() -> GroupedClassifier {
|
fn archive_classifier() -> GroupedClassifier {
|
||||||
|
@ -500,8 +527,8 @@ impl GroupedClassifier {
|
||||||
byte_matchers: vec![
|
byte_matchers: vec![
|
||||||
Box::new(ByteMatcher::application_x_gzip()),
|
Box::new(ByteMatcher::application_x_gzip()),
|
||||||
Box::new(ByteMatcher::application_zip()),
|
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::open_type()),
|
||||||
Box::new(ByteMatcher::true_type()),
|
Box::new(ByteMatcher::true_type()),
|
||||||
Box::new(ByteMatcher::application_vnd_ms_font_object()),
|
Box::new(ByteMatcher::application_vnd_ms_font_object()),
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -536,7 +563,7 @@ impl MIMEChecker for GroupedClassifier {
|
||||||
enum Match {
|
enum Match {
|
||||||
Start,
|
Start,
|
||||||
DidNotMatch,
|
DidNotMatch,
|
||||||
StartAndEnd
|
StartAndEnd,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Match {
|
impl Match {
|
||||||
|
@ -549,7 +576,9 @@ impl Match {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eats_until<'a, T>(matcher: &mut T, start: &[u8], end: &[u8]) -> 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) {
|
if !matcher.matches(start) {
|
||||||
Match::DidNotMatch
|
Match::DidNotMatch
|
||||||
} else if end.len() == 1 {
|
} else if end.len() == 1 {
|
||||||
|
@ -594,10 +623,11 @@ impl FeedsClassifier {
|
||||||
// Steps 5.2.1 to 5.2.4
|
// Steps 5.2.1 to 5.2.4
|
||||||
match eats_until(&mut matcher, b"?", b"?>")
|
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::StartAndEnd => continue,
|
||||||
Match::DidNotMatch => {},
|
Match::DidNotMatch => {},
|
||||||
Match::Start => return None
|
Match::Start => return None,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 5.2.5
|
// Step 5.2.5
|
||||||
|
@ -611,15 +641,21 @@ impl FeedsClassifier {
|
||||||
// Step 5.2.7
|
// Step 5.2.7
|
||||||
if matcher.matches(b"rdf:RDF") {
|
if matcher.matches(b"rdf:RDF") {
|
||||||
while matcher.next().is_some() {
|
while matcher.next().is_some() {
|
||||||
match eats_until(&mut matcher,
|
match eats_until(
|
||||||
|
&mut matcher,
|
||||||
b"http://purl.org/rss/1.0/",
|
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://www.w3.org/1999/02/22-rdf-syntax-ns#",
|
||||||
b"http://purl.org/rss/1.0/")) {
|
)
|
||||||
|
.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::StartAndEnd => return Some("application/rss+xml".parse().unwrap()),
|
||||||
Match::DidNotMatch => {},
|
Match::DidNotMatch => {},
|
||||||
Match::Start => return None
|
Match::Start => return None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return None;
|
return None;
|
||||||
|
@ -647,7 +683,7 @@ impl ByteMatcher {
|
||||||
pattern: b"\x00\x00\x01\x00",
|
pattern: b"\x00\x00\x01\x00",
|
||||||
mask: b"\xFF\xFF\xFF\xFF",
|
mask: b"\xFF\xFF\xFF\xFF",
|
||||||
content_type: "image/x-icon".parse().unwrap(),
|
content_type: "image/x-icon".parse().unwrap(),
|
||||||
leading_ignore: &[]
|
leading_ignore: &[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//A Windows Cursor signature.
|
//A Windows Cursor signature.
|
||||||
|
@ -656,7 +692,7 @@ impl ByteMatcher {
|
||||||
pattern: b"\x00\x00\x02\x00",
|
pattern: b"\x00\x00\x02\x00",
|
||||||
mask: b"\xFF\xFF\xFF\xFF",
|
mask: b"\xFF\xFF\xFF\xFF",
|
||||||
content_type: "image/x-icon".parse().unwrap(),
|
content_type: "image/x-icon".parse().unwrap(),
|
||||||
leading_ignore: &[]
|
leading_ignore: &[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//The string "BM", a BMP signature.
|
//The string "BM", a BMP signature.
|
||||||
|
@ -665,7 +701,7 @@ impl ByteMatcher {
|
||||||
pattern: b"BM",
|
pattern: b"BM",
|
||||||
mask: b"\xFF\xFF",
|
mask: b"\xFF\xFF",
|
||||||
content_type: mime::IMAGE_BMP,
|
content_type: mime::IMAGE_BMP,
|
||||||
leading_ignore: &[]
|
leading_ignore: &[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//The string "GIF89a", a GIF signature.
|
//The string "GIF89a", a GIF signature.
|
||||||
|
@ -674,7 +710,7 @@ impl ByteMatcher {
|
||||||
pattern: b"GIF89a",
|
pattern: b"GIF89a",
|
||||||
mask: b"\xFF\xFF\xFF\xFF\xFF\xFF",
|
mask: b"\xFF\xFF\xFF\xFF\xFF\xFF",
|
||||||
content_type: mime::IMAGE_GIF,
|
content_type: mime::IMAGE_GIF,
|
||||||
leading_ignore: &[]
|
leading_ignore: &[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//The string "GIF87a", a GIF signature.
|
//The string "GIF87a", a GIF signature.
|
||||||
|
@ -683,7 +719,7 @@ impl ByteMatcher {
|
||||||
pattern: b"GIF87a",
|
pattern: b"GIF87a",
|
||||||
mask: b"\xFF\xFF\xFF\xFF\xFF\xFF",
|
mask: b"\xFF\xFF\xFF\xFF\xFF\xFF",
|
||||||
content_type: mime::IMAGE_GIF,
|
content_type: mime::IMAGE_GIF,
|
||||||
leading_ignore: &[]
|
leading_ignore: &[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//The string "RIFF" followed by four bytes followed by the string "WEBPVP".
|
//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",
|
pattern: b"RIFF\x00\x00\x00\x00WEBPVP",
|
||||||
mask: b"\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF",
|
mask: b"\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF",
|
||||||
content_type: "image/webp".parse().unwrap(),
|
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
|
//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",
|
pattern: b"\x89PNG\r\n\x1A\n",
|
||||||
mask: b"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
|
mask: b"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
|
||||||
content_type: mime::IMAGE_PNG,
|
content_type: mime::IMAGE_PNG,
|
||||||
leading_ignore: &[]
|
leading_ignore: &[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The JPEG Start of Image marker followed by the indicator byte of another marker.
|
// 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",
|
pattern: b"\xFF\xD8\xFF",
|
||||||
mask: b"\xFF\xFF\xFF",
|
mask: b"\xFF\xFF\xFF",
|
||||||
content_type: mime::IMAGE_JPEG,
|
content_type: mime::IMAGE_JPEG,
|
||||||
leading_ignore: &[]
|
leading_ignore: &[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//The WebM signature. [TODO: Use more bytes?]
|
//The WebM signature. [TODO: Use more bytes?]
|
||||||
|
@ -720,7 +756,7 @@ impl ByteMatcher {
|
||||||
pattern: b"\x1A\x45\xDF\xA3",
|
pattern: b"\x1A\x45\xDF\xA3",
|
||||||
mask: b"\xFF\xFF\xFF\xFF",
|
mask: b"\xFF\xFF\xFF\xFF",
|
||||||
content_type: "video/webm".parse().unwrap(),
|
content_type: "video/webm".parse().unwrap(),
|
||||||
leading_ignore: &[]
|
leading_ignore: &[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//The string ".snd", the basic audio signature.
|
//The string ".snd", the basic audio signature.
|
||||||
|
@ -729,7 +765,7 @@ impl ByteMatcher {
|
||||||
pattern: b".snd",
|
pattern: b".snd",
|
||||||
mask: b"\xFF\xFF\xFF\xFF",
|
mask: b"\xFF\xFF\xFF\xFF",
|
||||||
content_type: "audio/basic".parse().unwrap(),
|
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.
|
//The string "FORM" followed by four bytes followed by the string "AIFF", the AIFF signature.
|
||||||
|
@ -738,7 +774,7 @@ impl ByteMatcher {
|
||||||
pattern: b"FORM\x00\x00\x00\x00AIFF",
|
pattern: b"FORM\x00\x00\x00\x00AIFF",
|
||||||
mask: b"\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF",
|
mask: b"\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF",
|
||||||
content_type: "audio/aiff".parse().unwrap(),
|
content_type: "audio/aiff".parse().unwrap(),
|
||||||
leading_ignore: &[]
|
leading_ignore: &[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//The string "ID3", the ID3v2-tagged MP3 signature.
|
//The string "ID3", the ID3v2-tagged MP3 signature.
|
||||||
|
@ -747,7 +783,7 @@ impl ByteMatcher {
|
||||||
pattern: b"ID3",
|
pattern: b"ID3",
|
||||||
mask: b"\xFF\xFF\xFF",
|
mask: b"\xFF\xFF\xFF",
|
||||||
content_type: "audio/mpeg".parse().unwrap(),
|
content_type: "audio/mpeg".parse().unwrap(),
|
||||||
leading_ignore: &[]
|
leading_ignore: &[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//The string "OggS" followed by NUL, the Ogg container signature.
|
//The string "OggS" followed by NUL, the Ogg container signature.
|
||||||
|
@ -756,7 +792,7 @@ impl ByteMatcher {
|
||||||
pattern: b"OggS\x00",
|
pattern: b"OggS\x00",
|
||||||
mask: b"\xFF\xFF\xFF\xFF\xFF",
|
mask: b"\xFF\xFF\xFF\xFF\xFF",
|
||||||
content_type: "application/ogg".parse().unwrap(),
|
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),
|
//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",
|
pattern: b"MThd\x00\x00\x00\x06",
|
||||||
mask: b"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
|
mask: b"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
|
||||||
content_type: "audio/midi".parse().unwrap(),
|
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.
|
//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 ",
|
pattern: b"RIFF\x00\x00\x00\x00AVI ",
|
||||||
mask: b"\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF",
|
mask: b"\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF",
|
||||||
content_type: "video/avi".parse().unwrap(),
|
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.
|
// 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",
|
pattern: b"RIFF\x00\x00\x00\x00WAVE",
|
||||||
mask: b"\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF",
|
mask: b"\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF",
|
||||||
content_type: "audio/wave".parse().unwrap(),
|
content_type: "audio/wave".parse().unwrap(),
|
||||||
leading_ignore: &[]
|
leading_ignore: &[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// doctype terminated with Tag terminating (TT) Byte
|
// doctype terminated with Tag terminating (TT) Byte
|
||||||
|
@ -794,8 +830,8 @@ impl ByteMatcher {
|
||||||
pattern: b"<!DOCTYPE HTML",
|
pattern: b"<!DOCTYPE HTML",
|
||||||
mask: b"\xFF\xFF\xDF\xDF\xDF\xDF\xDF\xDF\xDF\xFF\xDF\xDF\xDF\xDF",
|
mask: b"\xFF\xFF\xDF\xDF\xDF\xDF\xDF\xDF\xDF\xFF\xDF\xDF\xDF\xDF",
|
||||||
content_type: mime::TEXT_HTML,
|
content_type: mime::TEXT_HTML,
|
||||||
leading_ignore: b"\t\n\x0C\r "
|
leading_ignore: b"\t\n\x0C\r ",
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -806,8 +842,8 @@ impl ByteMatcher {
|
||||||
pattern: b"<HTML",
|
pattern: b"<HTML",
|
||||||
mask: b"\xFF\xDF\xDF\xDF\xDF",
|
mask: b"\xFF\xDF\xDF\xDF\xDF",
|
||||||
content_type: mime::TEXT_HTML,
|
content_type: mime::TEXT_HTML,
|
||||||
leading_ignore: b"\t\n\x0C\r "
|
leading_ignore: b"\t\n\x0C\r ",
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -818,8 +854,8 @@ impl ByteMatcher {
|
||||||
pattern: b"<HEAD",
|
pattern: b"<HEAD",
|
||||||
mask: b"\xFF\xDF\xDF\xDF\xDF",
|
mask: b"\xFF\xDF\xDF\xDF\xDF",
|
||||||
content_type: mime::TEXT_HTML,
|
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",
|
pattern: b"<SCRIPT",
|
||||||
mask: b"\xFF\xDF\xDF\xDF\xDF\xDF\xDF",
|
mask: b"\xFF\xDF\xDF\xDF\xDF\xDF\xDF",
|
||||||
content_type: mime::TEXT_HTML,
|
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",
|
pattern: b"<IFRAME",
|
||||||
mask: b"\xFF\xDF\xDF\xDF\xDF\xDF\xDF",
|
mask: b"\xFF\xDF\xDF\xDF\xDF\xDF\xDF",
|
||||||
content_type: mime::TEXT_HTML,
|
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",
|
pattern: b"<H1",
|
||||||
mask: b"\xFF\xDF\xFF",
|
mask: b"\xFF\xDF\xFF",
|
||||||
content_type: mime::TEXT_HTML,
|
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",
|
pattern: b"<DIV",
|
||||||
mask: b"\xFF\xDF\xDF\xDF",
|
mask: b"\xFF\xDF\xDF\xDF",
|
||||||
content_type: mime::TEXT_HTML,
|
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",
|
pattern: b"<FONT",
|
||||||
mask: b"\xFF\xDF\xDF\xDF\xDF",
|
mask: b"\xFF\xDF\xDF\xDF\xDF",
|
||||||
content_type: mime::TEXT_HTML,
|
content_type: mime::TEXT_HTML,
|
||||||
leading_ignore: b"\t\n\x0C\r "
|
leading_ignore: b"\t\n\x0C\r ",
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -890,8 +926,8 @@ impl ByteMatcher {
|
||||||
pattern: b"<TABLE",
|
pattern: b"<TABLE",
|
||||||
mask: b"\xFF\xDF\xDF\xDF\xDF\xDF",
|
mask: b"\xFF\xDF\xDF\xDF\xDF\xDF",
|
||||||
content_type: mime::TEXT_HTML,
|
content_type: mime::TEXT_HTML,
|
||||||
leading_ignore: b"\t\n\x0C\r "
|
leading_ignore: b"\t\n\x0C\r ",
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -902,8 +938,8 @@ impl ByteMatcher {
|
||||||
pattern: b"<A",
|
pattern: b"<A",
|
||||||
mask: b"\xFF\xDF",
|
mask: b"\xFF\xDF",
|
||||||
content_type: mime::TEXT_HTML,
|
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",
|
pattern: b"<STYLE",
|
||||||
mask: b"\xFF\xDF\xDF\xDF\xDF\xDF",
|
mask: b"\xFF\xDF\xDF\xDF\xDF\xDF",
|
||||||
content_type: mime::TEXT_HTML,
|
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",
|
pattern: b"<TITLE",
|
||||||
mask: b"\xFF\xDF\xDF\xDF\xDF\xDF",
|
mask: b"\xFF\xDF\xDF\xDF\xDF\xDF",
|
||||||
content_type: mime::TEXT_HTML,
|
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",
|
pattern: b"<B",
|
||||||
mask: b"\xFF\xDF",
|
mask: b"\xFF\xDF",
|
||||||
content_type: mime::TEXT_HTML,
|
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",
|
pattern: b"<BODY",
|
||||||
mask: b"\xFF\xDF\xDF\xDF\xDF",
|
mask: b"\xFF\xDF\xDF\xDF\xDF",
|
||||||
content_type: mime::TEXT_HTML,
|
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",
|
pattern: b"<BR",
|
||||||
mask: b"\xFF\xDF\xDF",
|
mask: b"\xFF\xDF\xDF",
|
||||||
content_type: mime::TEXT_HTML,
|
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",
|
pattern: b"<P",
|
||||||
mask: b"\xFF\xDF",
|
mask: b"\xFF\xDF",
|
||||||
content_type: mime::TEXT_HTML,
|
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"<!--",
|
pattern: b"<!--",
|
||||||
mask: b"\xFF\xFF\xFF\xFF",
|
mask: b"\xFF\xFF\xFF\xFF",
|
||||||
content_type: mime::TEXT_HTML,
|
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",
|
pattern: b"<?xml",
|
||||||
mask: b"\xFF\xFF\xFF\xFF\xFF",
|
mask: b"\xFF\xFF\xFF\xFF\xFF",
|
||||||
content_type: mime::TEXT_XML,
|
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.
|
//The string "%PDF-", the PDF signature.
|
||||||
|
@ -1006,7 +1042,7 @@ impl ByteMatcher {
|
||||||
pattern: b"%PDF-",
|
pattern: b"%PDF-",
|
||||||
mask: b"\xFF\xFF\xFF\xFF\xFF",
|
mask: b"\xFF\xFF\xFF\xFF\xFF",
|
||||||
content_type: mime::APPLICATION_PDF,
|
content_type: mime::APPLICATION_PDF,
|
||||||
leading_ignore: &[]
|
leading_ignore: &[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//34 bytes followed by the string "LP", the Embedded OpenType signature.
|
//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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||||
\x00\x00\xFF\xFF",
|
\x00\x00\xFF\xFF",
|
||||||
content_type: "application/vnd.ms-fontobject".parse().unwrap(),
|
content_type: "application/vnd.ms-fontobject".parse().unwrap(),
|
||||||
leading_ignore: &[]
|
leading_ignore: &[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//4 bytes representing the version number 1.0, a TrueType signature.
|
//4 bytes representing the version number 1.0, a TrueType signature.
|
||||||
|
@ -1028,7 +1064,7 @@ impl ByteMatcher {
|
||||||
pattern: b"\x00\x01\x00\x00",
|
pattern: b"\x00\x01\x00\x00",
|
||||||
mask: b"\xFF\xFF\xFF\xFF",
|
mask: b"\xFF\xFF\xFF\xFF",
|
||||||
content_type: "application/font-sfnt".parse().unwrap(),
|
content_type: "application/font-sfnt".parse().unwrap(),
|
||||||
leading_ignore: &[]
|
leading_ignore: &[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//The string "OTTO", the OpenType signature.
|
//The string "OTTO", the OpenType signature.
|
||||||
|
@ -1037,7 +1073,7 @@ impl ByteMatcher {
|
||||||
pattern: b"OTTO",
|
pattern: b"OTTO",
|
||||||
mask: b"\xFF\xFF\xFF\xFF",
|
mask: b"\xFF\xFF\xFF\xFF",
|
||||||
content_type: "application/font-sfnt".parse().unwrap(),
|
content_type: "application/font-sfnt".parse().unwrap(),
|
||||||
leading_ignore: &[]
|
leading_ignore: &[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The string "ttcf", the TrueType Collection signature.
|
// The string "ttcf", the TrueType Collection signature.
|
||||||
|
@ -1046,7 +1082,7 @@ impl ByteMatcher {
|
||||||
pattern: b"ttcf",
|
pattern: b"ttcf",
|
||||||
mask: b"\xFF\xFF\xFF\xFF",
|
mask: b"\xFF\xFF\xFF\xFF",
|
||||||
content_type: "application/font-sfnt".parse().unwrap(),
|
content_type: "application/font-sfnt".parse().unwrap(),
|
||||||
leading_ignore: &[]
|
leading_ignore: &[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The string "wOFF", the Web Open Font Format signature.
|
// The string "wOFF", the Web Open Font Format signature.
|
||||||
|
@ -1055,7 +1091,7 @@ impl ByteMatcher {
|
||||||
pattern: b"wOFF",
|
pattern: b"wOFF",
|
||||||
mask: b"\xFF\xFF\xFF\xFF",
|
mask: b"\xFF\xFF\xFF\xFF",
|
||||||
content_type: "application/font-woff".parse().unwrap(),
|
content_type: "application/font-woff".parse().unwrap(),
|
||||||
leading_ignore: &[]
|
leading_ignore: &[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//The GZIP archive signature.
|
//The GZIP archive signature.
|
||||||
|
@ -1064,7 +1100,7 @@ impl ByteMatcher {
|
||||||
pattern: b"\x1F\x8B\x08",
|
pattern: b"\x1F\x8B\x08",
|
||||||
mask: b"\xFF\xFF\xFF",
|
mask: b"\xFF\xFF\xFF",
|
||||||
content_type: "application/x-gzip".parse().unwrap(),
|
content_type: "application/x-gzip".parse().unwrap(),
|
||||||
leading_ignore: &[]
|
leading_ignore: &[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//The string "PK" followed by ETX EOT, the ZIP archive signature.
|
//The string "PK" followed by ETX EOT, the ZIP archive signature.
|
||||||
|
@ -1073,7 +1109,7 @@ impl ByteMatcher {
|
||||||
pattern: b"PK\x03\x04",
|
pattern: b"PK\x03\x04",
|
||||||
mask: b"\xFF\xFF\xFF\xFF",
|
mask: b"\xFF\xFF\xFF\xFF",
|
||||||
content_type: "application/zip".parse().unwrap(),
|
content_type: "application/zip".parse().unwrap(),
|
||||||
leading_ignore: &[]
|
leading_ignore: &[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//The string "Rar " followed by SUB BEL NUL, the RAR archive signature.
|
//The string "Rar " followed by SUB BEL NUL, the RAR archive signature.
|
||||||
|
@ -1082,7 +1118,7 @@ impl ByteMatcher {
|
||||||
pattern: b"Rar \x1A\x07\x00",
|
pattern: b"Rar \x1A\x07\x00",
|
||||||
mask: b"\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
|
mask: b"\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
|
||||||
content_type: "application/x-rar-compressed".parse().unwrap(),
|
content_type: "application/x-rar-compressed".parse().unwrap(),
|
||||||
leading_ignore: &[]
|
leading_ignore: &[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The string "%!PS-Adobe-", the PostScript signature.
|
// The string "%!PS-Adobe-", the PostScript signature.
|
||||||
|
@ -1091,7 +1127,7 @@ impl ByteMatcher {
|
||||||
pattern: b"%!PS-Adobe-",
|
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(),
|
content_type: "application/postscript".parse().unwrap(),
|
||||||
leading_ignore: &[]
|
leading_ignore: &[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// UTF-16BE BOM
|
// UTF-16BE BOM
|
||||||
|
@ -1100,7 +1136,7 @@ impl ByteMatcher {
|
||||||
pattern: b"\xFE\xFF\x00\x00",
|
pattern: b"\xFE\xFF\x00\x00",
|
||||||
mask: b"\xFF\xFF\x00\x00",
|
mask: b"\xFF\xFF\x00\x00",
|
||||||
content_type: mime::TEXT_PLAIN,
|
content_type: mime::TEXT_PLAIN,
|
||||||
leading_ignore: &[]
|
leading_ignore: &[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//UTF-16LE BOM
|
//UTF-16LE BOM
|
||||||
|
@ -1109,7 +1145,7 @@ impl ByteMatcher {
|
||||||
pattern: b"\xFF\xFE\x00\x00",
|
pattern: b"\xFF\xFE\x00\x00",
|
||||||
mask: b"\xFF\xFF\x00\x00",
|
mask: b"\xFF\xFF\x00\x00",
|
||||||
content_type: mime::TEXT_PLAIN,
|
content_type: mime::TEXT_PLAIN,
|
||||||
leading_ignore: &[]
|
leading_ignore: &[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//UTF-8 BOM
|
//UTF-8 BOM
|
||||||
|
@ -1118,7 +1154,7 @@ impl ByteMatcher {
|
||||||
pattern: b"\xEF\xBB\xBF\x00",
|
pattern: b"\xEF\xBB\xBF\x00",
|
||||||
mask: b"\xFF\xFF\xFF\x00",
|
mask: b"\xFF\xFF\xFF\x00",
|
||||||
content_type: mime::TEXT_PLAIN,
|
content_type: mime::TEXT_PLAIN,
|
||||||
leading_ignore: &[]
|
leading_ignore: &[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,41 +48,50 @@ use storage_thread::StorageThreadFactory;
|
||||||
use websocket_loader;
|
use websocket_loader;
|
||||||
|
|
||||||
/// Returns a tuple of (public, private) senders to the new threads.
|
/// Returns a tuple of (public, private) senders to the new threads.
|
||||||
pub fn new_resource_threads(user_agent: Cow<'static, str>,
|
pub fn new_resource_threads(
|
||||||
|
user_agent: Cow<'static, str>,
|
||||||
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
|
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
|
||||||
time_profiler_chan: ProfilerChan,
|
time_profiler_chan: ProfilerChan,
|
||||||
mem_profiler_chan: MemProfilerChan,
|
mem_profiler_chan: MemProfilerChan,
|
||||||
embedder_proxy: EmbedderProxy,
|
embedder_proxy: EmbedderProxy,
|
||||||
config_dir: Option<PathBuf>)
|
config_dir: Option<PathBuf>,
|
||||||
-> (ResourceThreads, ResourceThreads) {
|
) -> (ResourceThreads, ResourceThreads) {
|
||||||
let (public_core, private_core) = new_core_resource_thread(
|
let (public_core, private_core) = new_core_resource_thread(
|
||||||
user_agent,
|
user_agent,
|
||||||
devtools_chan,
|
devtools_chan,
|
||||||
time_profiler_chan,
|
time_profiler_chan,
|
||||||
mem_profiler_chan,
|
mem_profiler_chan,
|
||||||
embedder_proxy,
|
embedder_proxy,
|
||||||
config_dir.clone());
|
config_dir.clone(),
|
||||||
|
);
|
||||||
let storage: IpcSender<StorageThreadMsg> = StorageThreadFactory::new(config_dir);
|
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
|
/// Create a CoreResourceThread
|
||||||
pub fn new_core_resource_thread(user_agent: Cow<'static, str>,
|
pub fn new_core_resource_thread(
|
||||||
|
user_agent: Cow<'static, str>,
|
||||||
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
|
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
|
||||||
time_profiler_chan: ProfilerChan,
|
time_profiler_chan: ProfilerChan,
|
||||||
mem_profiler_chan: MemProfilerChan,
|
mem_profiler_chan: MemProfilerChan,
|
||||||
embedder_proxy: EmbedderProxy,
|
embedder_proxy: EmbedderProxy,
|
||||||
config_dir: Option<PathBuf>)
|
config_dir: Option<PathBuf>,
|
||||||
-> (CoreResourceThread, CoreResourceThread) {
|
) -> (CoreResourceThread, CoreResourceThread) {
|
||||||
let (public_setup_chan, public_setup_port) = ipc::channel().unwrap();
|
let (public_setup_chan, public_setup_port) = ipc::channel().unwrap();
|
||||||
let (private_setup_chan, private_setup_port) = ipc::channel().unwrap();
|
let (private_setup_chan, private_setup_port) = ipc::channel().unwrap();
|
||||||
let (report_chan, report_port) = ipc::channel().unwrap();
|
let (report_chan, report_port) = ipc::channel().unwrap();
|
||||||
|
|
||||||
thread::Builder::new().name("ResourceManager".to_owned()).spawn(move || {
|
thread::Builder::new()
|
||||||
|
.name("ResourceManager".to_owned())
|
||||||
|
.spawn(move || {
|
||||||
let resource_manager = CoreResourceManager::new(
|
let resource_manager = CoreResourceManager::new(
|
||||||
user_agent, devtools_chan, time_profiler_chan, embedder_proxy
|
user_agent,
|
||||||
|
devtools_chan,
|
||||||
|
time_profiler_chan,
|
||||||
|
embedder_proxy,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut channel_manager = ResourceChannelManager {
|
let mut channel_manager = ResourceChannelManager {
|
||||||
|
@ -90,17 +99,14 @@ pub fn new_core_resource_thread(user_agent: Cow<'static, str>,
|
||||||
config_dir: config_dir,
|
config_dir: config_dir,
|
||||||
};
|
};
|
||||||
|
|
||||||
mem_profiler_chan.run_with_memory_reporting(|| (
|
mem_profiler_chan.run_with_memory_reporting(
|
||||||
channel_manager.start(
|
|| (channel_manager.start(public_setup_port, private_setup_port, report_port)),
|
||||||
public_setup_port,
|
|
||||||
private_setup_port,
|
|
||||||
report_port)
|
|
||||||
),
|
|
||||||
String::from("network-cache-reporter"),
|
String::from("network-cache-reporter"),
|
||||||
report_chan,
|
report_chan,
|
||||||
|report_chan| report_chan);
|
|report_chan| report_chan,
|
||||||
|
);
|
||||||
}).expect("Thread spawning failed");
|
})
|
||||||
|
.expect("Thread spawning failed");
|
||||||
(public_setup_chan, private_setup_chan)
|
(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 {
|
let certs = match opts::get().certificate_path {
|
||||||
Some(ref path) => {
|
Some(ref path) => fs::read_to_string(path).expect("Couldn't not find certificate file"),
|
||||||
fs::read_to_string(path).expect("Couldn't not find certificate file")
|
None => resources::read_string(Resource::SSLCertificates),
|
||||||
}
|
|
||||||
None => {
|
|
||||||
resources::read_string(Resource::SSLCertificates)
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let ssl_connector_builder = create_ssl_connector_builder(&certs);
|
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 {
|
impl ResourceChannelManager {
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
fn start(&mut self,
|
fn start(
|
||||||
|
&mut self,
|
||||||
public_receiver: IpcReceiver<CoreResourceMsg>,
|
public_receiver: IpcReceiver<CoreResourceMsg>,
|
||||||
private_receiver: IpcReceiver<CoreResourceMsg>,
|
private_receiver: IpcReceiver<CoreResourceMsg>,
|
||||||
memory_reporter: IpcReceiver<ReportsChan>) {
|
memory_reporter: IpcReceiver<ReportsChan>,
|
||||||
|
) {
|
||||||
let (public_http_state, private_http_state) =
|
let (public_http_state, private_http_state) =
|
||||||
create_http_states(self.config_dir.as_ref().map(Deref::deref));
|
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.
|
// Handles case where profiler thread shuts down before resource thread.
|
||||||
match receiver {
|
match receiver {
|
||||||
ipc::IpcSelectionResult::ChannelClosed(..) => continue,
|
ipc::IpcSelectionResult::ChannelClosed(..) => continue,
|
||||||
_ => {}
|
_ => {},
|
||||||
}
|
}
|
||||||
let (id, data) = receiver.unwrap();
|
let (id, data) = receiver.unwrap();
|
||||||
// If message is memory report, get the size_of of public and private http caches
|
// 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,
|
fn process_report(
|
||||||
|
&mut self,
|
||||||
msg: ReportsChan,
|
msg: ReportsChan,
|
||||||
public_http_state: &Arc<HttpState>,
|
public_http_state: &Arc<HttpState>,
|
||||||
private_http_state: &Arc<HttpState>) {
|
private_http_state: &Arc<HttpState>,
|
||||||
|
) {
|
||||||
let mut ops = MallocSizeOfOps::new(servo_allocator::usable_size, None, None);
|
let mut ops = MallocSizeOfOps::new(servo_allocator::usable_size, None, None);
|
||||||
let public_cache = public_http_state.http_cache.read().unwrap();
|
let public_cache = public_http_state.http_cache.read().unwrap();
|
||||||
let private_cache = private_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 {
|
let public_report = Report {
|
||||||
path: path!["memory-cache", "public"],
|
path: path!["memory-cache", "public"],
|
||||||
kind: ReportKind::ExplicitJemallocHeapSize,
|
kind: ReportKind::ExplicitJemallocHeapSize,
|
||||||
size: public_cache.size_of(&mut ops)
|
size: public_cache.size_of(&mut ops),
|
||||||
};
|
};
|
||||||
|
|
||||||
let private_report = Report {
|
let private_report = Report {
|
||||||
path: path!["memory-cache", "private"],
|
path: path!["memory-cache", "private"],
|
||||||
kind: ReportKind::ExplicitJemallocHeapSize,
|
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.
|
/// Returns false if the thread should exit.
|
||||||
fn process_msg(&mut self,
|
fn process_msg(&mut self, msg: CoreResourceMsg, http_state: &Arc<HttpState>) -> bool {
|
||||||
msg: CoreResourceMsg,
|
|
||||||
http_state: &Arc<HttpState>) -> bool {
|
|
||||||
match msg {
|
match msg {
|
||||||
CoreResourceMsg::Fetch(req_init, channels) => {
|
CoreResourceMsg::Fetch(req_init, channels) => match channels {
|
||||||
match channels {
|
FetchChannels::ResponseMsg(sender, cancel_chan) => {
|
||||||
FetchChannels::ResponseMsg(sender, cancel_chan) =>
|
self.resource_manager
|
||||||
self.resource_manager.fetch(req_init, None, sender, http_state, cancel_chan),
|
.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),
|
FetchChannels::WebSocket {
|
||||||
}
|
event_sender,
|
||||||
}
|
action_receiver,
|
||||||
CoreResourceMsg::FetchRedirect(req_init, res_init, sender, cancel_chan) =>
|
} => self.resource_manager.websocket_connect(
|
||||||
self.resource_manager.fetch(req_init, Some(res_init), sender, http_state, cancel_chan),
|
req_init,
|
||||||
CoreResourceMsg::SetCookieForUrl(request, cookie, source) =>
|
event_sender,
|
||||||
self.resource_manager.set_cookie_for_url(&request, cookie.into_inner(), source, http_state),
|
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) => {
|
CoreResourceMsg::SetCookiesForUrl(request, cookies, source) => {
|
||||||
for cookie in cookies {
|
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) => {
|
CoreResourceMsg::GetCookiesForUrl(url, consumer, source) => {
|
||||||
let mut cookie_jar = http_state.cookie_jar.write().unwrap();
|
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) => {
|
CoreResourceMsg::NetworkMediator(mediator_chan) => {
|
||||||
self.resource_manager.swmanager_chan = Some(mediator_chan)
|
self.resource_manager.swmanager_chan = Some(mediator_chan)
|
||||||
}
|
},
|
||||||
CoreResourceMsg::GetCookiesDataForUrl(url, consumer, source) => {
|
CoreResourceMsg::GetCookiesDataForUrl(url, consumer, source) => {
|
||||||
let mut cookie_jar = http_state.cookie_jar.write().unwrap();
|
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();
|
consumer.send(cookies).unwrap();
|
||||||
}
|
},
|
||||||
CoreResourceMsg::GetHistoryState(history_state_id, consumer) => {
|
CoreResourceMsg::GetHistoryState(history_state_id, consumer) => {
|
||||||
let history_states = http_state.history_states.read().unwrap();
|
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) => {
|
CoreResourceMsg::SetHistoryState(history_state_id, history_state) => {
|
||||||
let mut history_states = http_state.history_states.write().unwrap();
|
let mut history_states = http_state.history_states.write().unwrap();
|
||||||
history_states.insert(history_state_id, history_state);
|
history_states.insert(history_state_id, history_state);
|
||||||
}
|
},
|
||||||
CoreResourceMsg::RemoveHistoryStates(states_to_remove) => {
|
CoreResourceMsg::RemoveHistoryStates(states_to_remove) => {
|
||||||
let mut history_states = http_state.history_states.write().unwrap();
|
let mut history_states = http_state.history_states.write().unwrap();
|
||||||
for history_state in states_to_remove {
|
for history_state in states_to_remove {
|
||||||
history_states.remove(&history_state);
|
history_states.remove(&history_state);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
CoreResourceMsg::Synchronize(sender) => {
|
CoreResourceMsg::Synchronize(sender) => {
|
||||||
let _ = sender.send(());
|
let _ = sender.send(());
|
||||||
}
|
},
|
||||||
CoreResourceMsg::ToFileManager(msg) => self.resource_manager.filemanager.handle(msg),
|
CoreResourceMsg::ToFileManager(msg) => self.resource_manager.filemanager.handle(msg),
|
||||||
CoreResourceMsg::Exit(sender) => {
|
CoreResourceMsg::Exit(sender) => {
|
||||||
if let Some(ref config_dir) = self.config_dir {
|
if let Some(ref config_dir) = self.config_dir {
|
||||||
match http_state.auth_cache.read() {
|
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"),
|
Err(_) => warn!("Error writing auth cache to disk"),
|
||||||
}
|
}
|
||||||
match http_state.cookie_jar.read() {
|
match http_state.cookie_jar.read() {
|
||||||
|
@ -282,14 +309,15 @@ impl ResourceChannelManager {
|
||||||
}
|
}
|
||||||
let _ = sender.send(());
|
let _ = sender.send(());
|
||||||
return false;
|
return false;
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_json_from_file<T>(data: &mut T, config_dir: &Path, filename: &str)
|
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 path = config_dir.join(filename);
|
||||||
let display = path.display();
|
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();
|
let mut string_buffer: String = String::new();
|
||||||
match file.read_to_string(&mut string_buffer) {
|
match file.read_to_string(&mut string_buffer) {
|
||||||
Err(why) => {
|
Err(why) => panic!(
|
||||||
panic!("couldn't read from {}: {}", display,
|
"couldn't read from {}: {}",
|
||||||
Error::description(&why))
|
display,
|
||||||
},
|
Error::description(&why)
|
||||||
|
),
|
||||||
Ok(_) => println!("successfully read from {}", display),
|
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)
|
pub fn write_json_to_file<T>(data: &T, config_dir: &Path, filename: &str)
|
||||||
where T: Serialize
|
where
|
||||||
|
T: Serialize,
|
||||||
{
|
{
|
||||||
let json_encoded: String;
|
let json_encoded: String;
|
||||||
match serde_json::to_string_pretty(&data) {
|
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 display = path.display();
|
||||||
|
|
||||||
let mut file = match File::create(&path) {
|
let mut file = match File::create(&path) {
|
||||||
Err(why) => panic!("couldn't create {}: {}",
|
Err(why) => panic!("couldn't create {}: {}", display, Error::description(&why)),
|
||||||
display,
|
|
||||||
Error::description(&why)),
|
|
||||||
Ok(file) => file,
|
Ok(file) => file,
|
||||||
};
|
};
|
||||||
|
|
||||||
match file.write_all(json_encoded.as_bytes()) {
|
match file.write_all(json_encoded.as_bytes()) {
|
||||||
Err(why) => {
|
Err(why) => panic!(
|
||||||
panic!("couldn't write to {}: {}", display,
|
"couldn't write to {}: {}",
|
||||||
Error::description(&why))
|
display,
|
||||||
},
|
Error::description(&why)
|
||||||
|
),
|
||||||
Ok(_) => println!("successfully wrote to {}", display),
|
Ok(_) => println!("successfully wrote to {}", display),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -354,7 +383,7 @@ impl AuthCache {
|
||||||
pub fn new() -> AuthCache {
|
pub fn new() -> AuthCache {
|
||||||
AuthCache {
|
AuthCache {
|
||||||
version: 1,
|
version: 1,
|
||||||
entries: HashMap::new()
|
entries: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -373,10 +402,12 @@ pub struct CoreResourceManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CoreResourceManager {
|
impl CoreResourceManager {
|
||||||
pub fn new(user_agent: Cow<'static, str>,
|
pub fn new(
|
||||||
|
user_agent: Cow<'static, str>,
|
||||||
devtools_channel: Option<Sender<DevtoolsControlMsg>>,
|
devtools_channel: Option<Sender<DevtoolsControlMsg>>,
|
||||||
_profiler_chan: ProfilerChan,
|
_profiler_chan: ProfilerChan,
|
||||||
embedder_proxy: EmbedderProxy) -> CoreResourceManager {
|
embedder_proxy: EmbedderProxy,
|
||||||
|
) -> CoreResourceManager {
|
||||||
CoreResourceManager {
|
CoreResourceManager {
|
||||||
user_agent: user_agent,
|
user_agent: user_agent,
|
||||||
devtools_chan: devtools_channel,
|
devtools_chan: devtools_channel,
|
||||||
|
@ -385,28 +416,35 @@ impl CoreResourceManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_cookie_for_url(&mut self, request: &ServoUrl,
|
fn set_cookie_for_url(
|
||||||
|
&mut self,
|
||||||
|
request: &ServoUrl,
|
||||||
cookie: cookie_rs::Cookie<'static>,
|
cookie: cookie_rs::Cookie<'static>,
|
||||||
source: CookieSource,
|
source: CookieSource,
|
||||||
http_state: &Arc<HttpState>) {
|
http_state: &Arc<HttpState>,
|
||||||
|
) {
|
||||||
if let Some(cookie) = cookie::Cookie::new_wrapped(cookie, request, source) {
|
if let Some(cookie) = cookie::Cookie::new_wrapped(cookie, request, source) {
|
||||||
let mut cookie_jar = http_state.cookie_jar.write().unwrap();
|
let mut cookie_jar = http_state.cookie_jar.write().unwrap();
|
||||||
cookie_jar.push(cookie, request, source)
|
cookie_jar.push(cookie, request, source)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch(&self,
|
fn fetch(
|
||||||
|
&self,
|
||||||
req_init: RequestInit,
|
req_init: RequestInit,
|
||||||
res_init_: Option<ResponseInit>,
|
res_init_: Option<ResponseInit>,
|
||||||
mut sender: IpcSender<FetchResponseMsg>,
|
mut sender: IpcSender<FetchResponseMsg>,
|
||||||
http_state: &Arc<HttpState>,
|
http_state: &Arc<HttpState>,
|
||||||
cancel_chan: Option<IpcReceiver<()>>) {
|
cancel_chan: Option<IpcReceiver<()>>,
|
||||||
|
) {
|
||||||
let http_state = http_state.clone();
|
let http_state = http_state.clone();
|
||||||
let ua = self.user_agent.clone();
|
let ua = self.user_agent.clone();
|
||||||
let dc = self.devtools_chan.clone();
|
let dc = self.devtools_chan.clone();
|
||||||
let filemanager = self.filemanager.clone();
|
let filemanager = self.filemanager.clone();
|
||||||
|
|
||||||
thread::Builder::new().name(format!("fetch thread for {}", req_init.url)).spawn(move || {
|
thread::Builder::new()
|
||||||
|
.name(format!("fetch thread for {}", req_init.url))
|
||||||
|
.spawn(move || {
|
||||||
let mut request = Request::from_init(req_init);
|
let mut request = Request::from_init(req_init);
|
||||||
// XXXManishearth: Check origin against pipeline id (also ensure that the mode is allowed)
|
// XXXManishearth: Check origin against pipeline id (also ensure that the mode is allowed)
|
||||||
// todo load context / mimesniff in fetch
|
// todo load context / mimesniff in fetch
|
||||||
|
@ -417,23 +455,28 @@ impl CoreResourceManager {
|
||||||
user_agent: ua,
|
user_agent: ua,
|
||||||
devtools_chan: dc,
|
devtools_chan: dc,
|
||||||
filemanager: filemanager,
|
filemanager: filemanager,
|
||||||
cancellation_listener: Arc::new(Mutex::new(CancellationListener::new(cancel_chan))),
|
cancellation_listener: Arc::new(Mutex::new(CancellationListener::new(
|
||||||
|
cancel_chan,
|
||||||
|
))),
|
||||||
};
|
};
|
||||||
|
|
||||||
match res_init_ {
|
match res_init_ {
|
||||||
Some(res_init) => {
|
Some(res_init) => {
|
||||||
let response = Response::from_init(res_init);
|
let response = Response::from_init(res_init);
|
||||||
http_redirect_fetch(&mut request,
|
http_redirect_fetch(
|
||||||
|
&mut request,
|
||||||
&mut CorsCache::new(),
|
&mut CorsCache::new(),
|
||||||
response,
|
response,
|
||||||
true,
|
true,
|
||||||
&mut sender,
|
&mut sender,
|
||||||
&mut None,
|
&mut None,
|
||||||
&context);
|
&context,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
None => fetch(&mut request, &mut sender, &context),
|
None => fetch(&mut request, &mut sender, &context),
|
||||||
};
|
};
|
||||||
}).expect("Thread spawning failed");
|
})
|
||||||
|
.expect("Thread spawning failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn websocket_connect(
|
fn websocket_connect(
|
||||||
|
@ -441,7 +484,7 @@ impl CoreResourceManager {
|
||||||
request: RequestInit,
|
request: RequestInit,
|
||||||
event_sender: IpcSender<WebSocketNetworkEvent>,
|
event_sender: IpcSender<WebSocketNetworkEvent>,
|
||||||
action_receiver: IpcReceiver<WebSocketDomAction>,
|
action_receiver: IpcReceiver<WebSocketDomAction>,
|
||||||
http_state: &Arc<HttpState>
|
http_state: &Arc<HttpState>,
|
||||||
) {
|
) {
|
||||||
websocket_loader::init(request, event_sender, action_receiver, http_state.clone());
|
websocket_loader::init(request, event_sender, action_receiver, http_state.clone());
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,9 +22,12 @@ impl StorageThreadFactory for IpcSender<StorageThreadMsg> {
|
||||||
/// Create a storage thread
|
/// Create a storage thread
|
||||||
fn new(config_dir: Option<PathBuf>) -> IpcSender<StorageThreadMsg> {
|
fn new(config_dir: Option<PathBuf>) -> IpcSender<StorageThreadMsg> {
|
||||||
let (chan, port) = ipc::channel().unwrap();
|
let (chan, port) = ipc::channel().unwrap();
|
||||||
thread::Builder::new().name("StorageManager".to_owned()).spawn(move || {
|
thread::Builder::new()
|
||||||
|
.name("StorageManager".to_owned())
|
||||||
|
.spawn(move || {
|
||||||
StorageManager::new(port, config_dir).start();
|
StorageManager::new(port, config_dir).start();
|
||||||
}).expect("Thread spawning failed");
|
})
|
||||||
|
.expect("Thread spawning failed");
|
||||||
chan
|
chan
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,9 +40,7 @@ struct StorageManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StorageManager {
|
impl StorageManager {
|
||||||
fn new(port: IpcReceiver<StorageThreadMsg>,
|
fn new(port: IpcReceiver<StorageThreadMsg>, config_dir: Option<PathBuf>) -> StorageManager {
|
||||||
config_dir: Option<PathBuf>)
|
|
||||||
-> StorageManager {
|
|
||||||
let mut local_data = HashMap::new();
|
let mut local_data = HashMap::new();
|
||||||
if let Some(ref config_dir) = config_dir {
|
if let Some(ref config_dir) = config_dir {
|
||||||
resource_thread::read_json_from_file(&mut local_data, config_dir, "local_data.json");
|
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() {
|
match self.port.recv().unwrap() {
|
||||||
StorageThreadMsg::Length(sender, url, storage_type) => {
|
StorageThreadMsg::Length(sender, url, storage_type) => {
|
||||||
self.length(sender, url, storage_type)
|
self.length(sender, url, storage_type)
|
||||||
}
|
},
|
||||||
StorageThreadMsg::Key(sender, url, storage_type, index) => {
|
StorageThreadMsg::Key(sender, url, storage_type, index) => {
|
||||||
self.key(sender, url, storage_type, index)
|
self.key(sender, url, storage_type, index)
|
||||||
}
|
},
|
||||||
StorageThreadMsg::Keys(sender, url, storage_type) => {
|
StorageThreadMsg::Keys(sender, url, storage_type) => {
|
||||||
self.keys(sender, url, storage_type)
|
self.keys(sender, url, storage_type)
|
||||||
}
|
},
|
||||||
StorageThreadMsg::SetItem(sender, url, storage_type, name, value) => {
|
StorageThreadMsg::SetItem(sender, url, storage_type, name, value) => {
|
||||||
self.set_item(sender, url, storage_type, name, value);
|
self.set_item(sender, url, storage_type, name, value);
|
||||||
self.save_state()
|
self.save_state()
|
||||||
}
|
},
|
||||||
StorageThreadMsg::GetItem(sender, url, storage_type, name) => {
|
StorageThreadMsg::GetItem(sender, url, storage_type, name) => {
|
||||||
self.request_item(sender, url, storage_type, name)
|
self.request_item(sender, url, storage_type, name)
|
||||||
}
|
},
|
||||||
StorageThreadMsg::RemoveItem(sender, url, storage_type, name) => {
|
StorageThreadMsg::RemoveItem(sender, url, storage_type, name) => {
|
||||||
self.remove_item(sender, url, storage_type, name);
|
self.remove_item(sender, url, storage_type, name);
|
||||||
self.save_state()
|
self.save_state()
|
||||||
}
|
},
|
||||||
StorageThreadMsg::Clear(sender, url, storage_type) => {
|
StorageThreadMsg::Clear(sender, url, storage_type) => {
|
||||||
self.clear(sender, url, storage_type);
|
self.clear(sender, url, storage_type);
|
||||||
self.save_state()
|
self.save_state()
|
||||||
}
|
},
|
||||||
StorageThreadMsg::Exit(sender) => {
|
StorageThreadMsg::Exit(sender) => {
|
||||||
// Nothing to do since we save localstorage set eagerly.
|
// Nothing to do since we save localstorage set eagerly.
|
||||||
let _ = sender.send(());
|
let _ = sender.send(());
|
||||||
break
|
break;
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,48 +97,55 @@ impl StorageManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_data(&self, storage_type: StorageType)
|
fn select_data(
|
||||||
-> &HashMap<String, (usize, BTreeMap<String, String>)> {
|
&self,
|
||||||
|
storage_type: StorageType,
|
||||||
|
) -> &HashMap<String, (usize, BTreeMap<String, String>)> {
|
||||||
match storage_type {
|
match storage_type {
|
||||||
StorageType::Session => &self.session_data,
|
StorageType::Session => &self.session_data,
|
||||||
StorageType::Local => &self.local_data
|
StorageType::Local => &self.local_data,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_data_mut(&mut self, storage_type: StorageType)
|
fn select_data_mut(
|
||||||
-> &mut HashMap<String, (usize, BTreeMap<String, String>)> {
|
&mut self,
|
||||||
|
storage_type: StorageType,
|
||||||
|
) -> &mut HashMap<String, (usize, BTreeMap<String, String>)> {
|
||||||
match storage_type {
|
match storage_type {
|
||||||
StorageType::Session => &mut self.session_data,
|
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) {
|
fn length(&self, sender: IpcSender<usize>, url: ServoUrl, storage_type: StorageType) {
|
||||||
let origin = self.origin_as_string(url);
|
let origin = self.origin_as_string(url);
|
||||||
let data = self.select_data(storage_type);
|
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,
|
fn key(
|
||||||
|
&self,
|
||||||
sender: IpcSender<Option<String>>,
|
sender: IpcSender<Option<String>>,
|
||||||
url: ServoUrl,
|
url: ServoUrl,
|
||||||
storage_type: StorageType,
|
storage_type: StorageType,
|
||||||
index: u32) {
|
index: u32,
|
||||||
|
) {
|
||||||
let origin = self.origin_as_string(url);
|
let origin = self.origin_as_string(url);
|
||||||
let data = self.select_data(storage_type);
|
let data = self.select_data(storage_type);
|
||||||
let key = data.get(&origin)
|
let key = data
|
||||||
|
.get(&origin)
|
||||||
.and_then(|&(_, ref entry)| entry.keys().nth(index as usize))
|
.and_then(|&(_, ref entry)| entry.keys().nth(index as usize))
|
||||||
.cloned();
|
.cloned();
|
||||||
sender.send(key).unwrap();
|
sender.send(key).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn keys(&self,
|
fn keys(&self, sender: IpcSender<Vec<String>>, url: ServoUrl, storage_type: StorageType) {
|
||||||
sender: IpcSender<Vec<String>>,
|
|
||||||
url: ServoUrl,
|
|
||||||
storage_type: StorageType) {
|
|
||||||
let origin = self.origin_as_string(url);
|
let origin = self.origin_as_string(url);
|
||||||
let data = self.select_data(storage_type);
|
let data = self.select_data(storage_type);
|
||||||
let keys = data.get(&origin)
|
let keys = data
|
||||||
|
.get(&origin)
|
||||||
.map_or(vec![], |&(_, ref entry)| entry.keys().cloned().collect());
|
.map_or(vec![], |&(_, ref entry)| entry.keys().cloned().collect());
|
||||||
|
|
||||||
sender.send(keys).unwrap();
|
sender.send(keys).unwrap();
|
||||||
|
@ -147,12 +155,14 @@ impl StorageManager {
|
||||||
/// value with the same key name but with different value name
|
/// value with the same key name but with different value name
|
||||||
/// otherwise sends Err(()) to indicate that the operation would result in
|
/// otherwise sends Err(()) to indicate that the operation would result in
|
||||||
/// exceeding the quota limit
|
/// exceeding the quota limit
|
||||||
fn set_item(&mut self,
|
fn set_item(
|
||||||
|
&mut self,
|
||||||
sender: IpcSender<Result<(bool, Option<String>), ()>>,
|
sender: IpcSender<Result<(bool, Option<String>), ()>>,
|
||||||
url: ServoUrl,
|
url: ServoUrl,
|
||||||
storage_type: StorageType,
|
storage_type: StorageType,
|
||||||
name: String,
|
name: String,
|
||||||
value: String) {
|
value: String,
|
||||||
|
) {
|
||||||
let origin = self.origin_as_string(url);
|
let origin = self.origin_as_string(url);
|
||||||
|
|
||||||
let (this_storage_size, other_storage_size) = {
|
let (this_storage_size, other_storage_size) = {
|
||||||
|
@ -171,7 +181,9 @@ impl StorageManager {
|
||||||
data.insert(origin.clone(), (0, BTreeMap::new()));
|
data.insert(origin.clone(), (0, BTreeMap::new()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let message = data.get_mut(&origin).map(|&mut (ref mut total, ref mut entry)| {
|
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();
|
let mut new_total_size = this_storage_size + value.as_bytes().len();
|
||||||
if let Some(old_value) = entry.get(&name) {
|
if let Some(old_value) = entry.get(&name) {
|
||||||
new_total_size -= old_value.as_bytes().len();
|
new_total_size -= old_value.as_bytes().len();
|
||||||
|
@ -183,40 +195,54 @@ impl StorageManager {
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let message = entry.insert(name.clone(), value.clone()).map_or(
|
let message =
|
||||||
Ok((true, None)),
|
entry
|
||||||
|old| if old == value {
|
.insert(name.clone(), value.clone())
|
||||||
|
.map_or(Ok((true, None)), |old| {
|
||||||
|
if old == value {
|
||||||
Ok((false, None))
|
Ok((false, None))
|
||||||
} else {
|
} else {
|
||||||
Ok((true, Some(old)))
|
Ok((true, Some(old)))
|
||||||
|
}
|
||||||
});
|
});
|
||||||
*total = new_total_size;
|
*total = new_total_size;
|
||||||
message
|
message
|
||||||
}).unwrap();
|
})
|
||||||
|
.unwrap();
|
||||||
sender.send(message).unwrap();
|
sender.send(message).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn request_item(&self,
|
fn request_item(
|
||||||
|
&self,
|
||||||
sender: IpcSender<Option<String>>,
|
sender: IpcSender<Option<String>>,
|
||||||
url: ServoUrl,
|
url: ServoUrl,
|
||||||
storage_type: StorageType,
|
storage_type: StorageType,
|
||||||
name: String) {
|
name: String,
|
||||||
|
) {
|
||||||
let origin = self.origin_as_string(url);
|
let origin = self.origin_as_string(url);
|
||||||
let data = self.select_data(storage_type);
|
let data = self.select_data(storage_type);
|
||||||
sender.send(data.get(&origin)
|
sender
|
||||||
|
.send(
|
||||||
|
data.get(&origin)
|
||||||
.and_then(|&(_, ref entry)| entry.get(&name))
|
.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
|
/// Sends Some(old_value) in case there was a previous value with the key name, otherwise sends None
|
||||||
fn remove_item(&mut self,
|
fn remove_item(
|
||||||
|
&mut self,
|
||||||
sender: IpcSender<Option<String>>,
|
sender: IpcSender<Option<String>>,
|
||||||
url: ServoUrl,
|
url: ServoUrl,
|
||||||
storage_type: StorageType,
|
storage_type: StorageType,
|
||||||
name: String) {
|
name: String,
|
||||||
|
) {
|
||||||
let origin = self.origin_as_string(url);
|
let origin = self.origin_as_string(url);
|
||||||
let data = self.select_data_mut(storage_type);
|
let data = self.select_data_mut(storage_type);
|
||||||
let old_value = data.get_mut(&origin).and_then(|&mut (ref mut total, ref mut entry)| {
|
let old_value = data
|
||||||
|
.get_mut(&origin)
|
||||||
|
.and_then(|&mut (ref mut total, ref mut entry)| {
|
||||||
entry.remove(&name).and_then(|old| {
|
entry.remove(&name).and_then(|old| {
|
||||||
*total -= name.as_bytes().len() + old.as_bytes().len();
|
*total -= name.as_bytes().len() + old.as_bytes().len();
|
||||||
Some(old)
|
Some(old)
|
||||||
|
@ -228,7 +254,9 @@ impl StorageManager {
|
||||||
fn clear(&mut self, sender: IpcSender<bool>, url: ServoUrl, storage_type: StorageType) {
|
fn clear(&mut self, sender: IpcSender<bool>, url: ServoUrl, storage_type: StorageType) {
|
||||||
let origin = self.origin_as_string(url);
|
let origin = self.origin_as_string(url);
|
||||||
let data = self.select_data_mut(storage_type);
|
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)| {
|
.map_or(false, |&mut (ref mut total, ref mut entry)| {
|
||||||
if !entry.is_empty() {
|
if !entry.is_empty() {
|
||||||
entry.clear();
|
entry.clear();
|
||||||
|
@ -236,7 +264,10 @@ impl StorageManager {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}})).unwrap();
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn origin_as_string(&self, url: ServoUrl) -> String {
|
fn origin_as_string(&self, url: ServoUrl) -> String {
|
||||||
|
|
|
@ -9,22 +9,13 @@ use std::iter::Filter;
|
||||||
use std::str::Split;
|
use std::str::Split;
|
||||||
use std::sync::MutexGuard;
|
use std::sync::MutexGuard;
|
||||||
|
|
||||||
const SUPPORTED_ALGORITHM: &'static [&'static str] = &[
|
const SUPPORTED_ALGORITHM: &'static [&'static str] = &["sha256", "sha384", "sha512"];
|
||||||
"sha256",
|
|
||||||
"sha384",
|
|
||||||
"sha512",
|
|
||||||
];
|
|
||||||
pub type StaticCharVec = &'static [char];
|
pub type StaticCharVec = &'static [char];
|
||||||
/// A "space character" according to:
|
/// A "space character" according to:
|
||||||
///
|
///
|
||||||
/// <https://html.spec.whatwg.org/multipage/#space-character>
|
/// <https://html.spec.whatwg.org/multipage/#space-character>
|
||||||
pub static HTML_SPACE_CHARACTERS: StaticCharVec = &[
|
pub static HTML_SPACE_CHARACTERS: StaticCharVec =
|
||||||
'\u{0020}',
|
&['\u{0020}', '\u{0009}', '\u{000a}', '\u{000c}', '\u{000d}'];
|
||||||
'\u{0009}',
|
|
||||||
'\u{000a}',
|
|
||||||
'\u{000c}',
|
|
||||||
'\u{000d}',
|
|
||||||
];
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SriEntry {
|
pub struct SriEntry {
|
||||||
pub alg: String,
|
pub alg: String,
|
||||||
|
@ -79,9 +70,18 @@ pub fn parsed_metadata(integrity_metadata: &str) -> Vec<SriEntry> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <https://w3c.github.io/webappsec-subresource-integrity/#getprioritizedhashfunction>
|
/// <https://w3c.github.io/webappsec-subresource-integrity/#getprioritizedhashfunction>
|
||||||
pub fn get_prioritized_hash_function(hash_func_left: &str, hash_func_right: &str) -> Option<String> {
|
pub fn get_prioritized_hash_function(
|
||||||
let left_priority = SUPPORTED_ALGORITHM.iter().position(|s| s.to_owned() == hash_func_left).unwrap();
|
hash_func_left: &str,
|
||||||
let right_priority = SUPPORTED_ALGORITHM.iter().position(|s| s.to_owned() == hash_func_right).unwrap();
|
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 {
|
if left_priority == right_priority {
|
||||||
return None;
|
return None;
|
||||||
|
@ -91,7 +91,6 @@ pub fn get_prioritized_hash_function(hash_func_left: &str, hash_func_right: &str
|
||||||
} else {
|
} else {
|
||||||
Some(hash_func_right.to_owned())
|
Some(hash_func_right.to_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <https://w3c.github.io/webappsec-subresource-integrity/#get-the-strongest-metadata>
|
/// <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();
|
let mut current_algorithm = result[0].alg.clone();
|
||||||
|
|
||||||
for integrity_metadata in &integrity_metadata_list[1..] {
|
for integrity_metadata in &integrity_metadata_list[1..] {
|
||||||
let prioritized_hash = get_prioritized_hash_function(&integrity_metadata.alg,
|
let prioritized_hash =
|
||||||
&*current_algorithm);
|
get_prioritized_hash_function(&integrity_metadata.alg, &*current_algorithm);
|
||||||
if prioritized_hash.is_none() {
|
if prioritized_hash.is_none() {
|
||||||
result.push(integrity_metadata.clone());
|
result.push(integrity_metadata.clone());
|
||||||
} else if let Some(algorithm) = prioritized_hash {
|
} 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>
|
/// <https://w3c.github.io/webappsec-subresource-integrity/#apply-algorithm-to-response>
|
||||||
fn apply_algorithm_to_response(body: MutexGuard<ResponseBody>,
|
fn apply_algorithm_to_response(
|
||||||
message_digest: MessageDigest)
|
body: MutexGuard<ResponseBody>,
|
||||||
-> String {
|
message_digest: MessageDigest,
|
||||||
|
) -> String {
|
||||||
if let ResponseBody::Done(ref vec) = *body {
|
if let ResponseBody::Done(ref vec) = *body {
|
||||||
let response_digest = hash(message_digest, vec).unwrap(); //Now hash
|
let response_digest = hash(message_digest, vec).unwrap(); //Now hash
|
||||||
base64::encode(&response_digest)
|
base64::encode(&response_digest)
|
||||||
|
@ -171,8 +171,12 @@ pub fn is_response_integrity_valid(integrity_metadata: &str, response: &Response
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn split_html_space_chars<'a>(s: &'a str) ->
|
pub fn split_html_space_chars<'a>(
|
||||||
Filter<Split<'a, StaticCharVec>, fn(&&str) -> bool> {
|
s: &'a str,
|
||||||
fn not_empty(&split: &&str) -> bool { !split.is_empty() }
|
) -> Filter<Split<'a, StaticCharVec>, fn(&&str) -> bool> {
|
||||||
s.split(HTML_SPACE_CHARACTERS).filter(not_empty as fn(&&str) -> bool)
|
fn not_empty(&split: &&str) -> bool {
|
||||||
|
!split.is_empty()
|
||||||
|
}
|
||||||
|
s.split(HTML_SPACE_CHARACTERS)
|
||||||
|
.filter(not_empty as fn(&&str) -> bool)
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,11 +118,13 @@ fn test_cookie_secure_prefix() {
|
||||||
assert!(Cookie::new_wrapped(cookie, url, CookieSource::HTTP).is_none());
|
assert!(Cookie::new_wrapped(cookie, url, CookieSource::HTTP).is_none());
|
||||||
|
|
||||||
let url = &ServoUrl::parse("http://example.com").unwrap();
|
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());
|
assert!(Cookie::new_wrapped(cookie, url, CookieSource::HTTP).is_none());
|
||||||
|
|
||||||
let url = &ServoUrl::parse("https://example.com").unwrap();
|
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());
|
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());
|
assert!(Cookie::new_wrapped(cookie, url, CookieSource::HTTP).is_none());
|
||||||
|
|
||||||
let url = &ServoUrl::parse("https://example.com").unwrap();
|
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());
|
assert!(Cookie::new_wrapped(cookie, url, CookieSource::HTTP).is_none());
|
||||||
|
|
||||||
let url = &ServoUrl::parse("https://example.com").unwrap();
|
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!(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(&a, &b), Ordering::Greater);
|
||||||
assert_eq!(CookieStorage::cookie_comparator(&b, &a), Ordering::Less);
|
assert_eq!(CookieStorage::cookie_comparator(&b, &a), Ordering::Less);
|
||||||
assert_eq!(CookieStorage::cookie_comparator(&a, &a_prime), Ordering::Less);
|
assert_eq!(
|
||||||
assert_eq!(CookieStorage::cookie_comparator(&a_prime, &a), Ordering::Greater);
|
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);
|
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 source = CookieSource::HTTP;
|
||||||
let cookie = cookie_rs::Cookie::parse(cookie_str.to_owned()).unwrap();
|
let cookie = cookie_rs::Cookie::parse(cookie_str.to_owned()).unwrap();
|
||||||
let cookie = Cookie::new_wrapped(cookie, url, source).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();
|
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(
|
||||||
add_cookie_to_storage(&mut storage, &insecure_url, "foo2=value; Domain=.example.org");
|
&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, "foo3=value; Path=/foo/bar");
|
||||||
add_cookie_to_storage(&mut storage, &insecure_url, "foo4=value; Path=/foo");
|
add_cookie_to_storage(&mut storage, &insecure_url, "foo4=value; Path=/foo");
|
||||||
|
|
||||||
let source = CookieSource::HTTP;
|
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;
|
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;
|
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]
|
#[test]
|
||||||
|
@ -267,14 +294,21 @@ fn test_secure_cookies_eviction() {
|
||||||
let source = CookieSource::HTTP;
|
let source = CookieSource::HTTP;
|
||||||
assert_eq!(storage.cookies_for_url(&url, source).unwrap(), "foo2=value");
|
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;
|
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;
|
let source = CookieSource::HTTP;
|
||||||
assert_eq!(storage.cookies_for_url(&url, source).unwrap(),
|
assert_eq!(
|
||||||
"foo4=bar; foo3=value; foo3=bar; foo4=value; foo2=value");
|
storage.cookies_for_url(&url, source).unwrap(),
|
||||||
|
"foo4=bar; foo3=value; foo3=bar; foo4=value; foo2=value"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -302,21 +336,28 @@ fn test_secure_cookies_eviction_non_http_source() {
|
||||||
let source = CookieSource::HTTP;
|
let source = CookieSource::HTTP;
|
||||||
assert_eq!(storage.cookies_for_url(&url, source).unwrap(), "foo2=value");
|
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;
|
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;
|
let source = CookieSource::HTTP;
|
||||||
assert_eq!(storage.cookies_for_url(&url, source).unwrap(),
|
assert_eq!(
|
||||||
"foo4=bar; foo3=value; foo3=bar; foo4=value; foo2=value");
|
storage.cookies_for_url(&url, source).unwrap(),
|
||||||
|
"foo4=bar; foo3=value; foo3=bar; foo4=value; foo2=value"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn add_retrieve_cookies(
|
||||||
fn add_retrieve_cookies(set_location: &str,
|
set_location: &str,
|
||||||
set_cookies: &[String],
|
set_cookies: &[String],
|
||||||
final_location: &str)
|
final_location: &str,
|
||||||
-> String {
|
) -> String {
|
||||||
let mut storage = CookieStorage::new(5);
|
let mut storage = CookieStorage::new(5);
|
||||||
let url = ServoUrl::parse(set_location).unwrap();
|
let url = ServoUrl::parse(set_location).unwrap();
|
||||||
let source = CookieSource::HTTP;
|
let source = CookieSource::HTTP;
|
||||||
|
@ -329,56 +370,75 @@ fn add_retrieve_cookies(set_location: &str,
|
||||||
|
|
||||||
// Get cookies for the test location
|
// Get cookies for the test location
|
||||||
let url = ServoUrl::parse(final_location).unwrap();
|
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]
|
#[test]
|
||||||
fn test_cookie_eviction_expired() {
|
fn test_cookie_eviction_expired() {
|
||||||
let mut vec = Vec::new();
|
let mut vec = Vec::new();
|
||||||
for i in 1..6 {
|
for i in 1..6 {
|
||||||
let st = format!("extra{}=bar; Secure; expires=Sun, 18-Apr-2000 21:06:29 GMT",
|
let st = format!(
|
||||||
i);
|
"extra{}=bar; Secure; expires=Sun, 18-Apr-2000 21:06:29 GMT",
|
||||||
|
i
|
||||||
|
);
|
||||||
vec.push(st);
|
vec.push(st);
|
||||||
}
|
}
|
||||||
vec.push("foo=bar; Secure; expires=Sun, 18-Apr-2027 21:06:29 GMT".to_owned());
|
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",
|
let r = add_retrieve_cookies(
|
||||||
&vec, "https://home.example.org:8888/cookie-parser-result?0001");
|
"https://home.example.org:8888/cookie-parser?0001",
|
||||||
|
&vec,
|
||||||
|
"https://home.example.org:8888/cookie-parser-result?0001",
|
||||||
|
);
|
||||||
assert_eq!(&r, "foo=bar");
|
assert_eq!(&r, "foo=bar");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_cookie_eviction_all_secure_one_nonsecure() {
|
fn test_cookie_eviction_all_secure_one_nonsecure() {
|
||||||
let mut vec = Vec::new();
|
let mut vec = Vec::new();
|
||||||
for i in 1..5 {
|
for i in 1..5 {
|
||||||
let st = format!("extra{}=bar; Secure; expires=Sun, 18-Apr-2026 21:06:29 GMT",
|
let st = format!(
|
||||||
i);
|
"extra{}=bar; Secure; expires=Sun, 18-Apr-2026 21:06:29 GMT",
|
||||||
|
i
|
||||||
|
);
|
||||||
vec.push(st);
|
vec.push(st);
|
||||||
}
|
}
|
||||||
vec.push("foo=bar; expires=Sun, 18-Apr-2026 21:06:29 GMT".to_owned());
|
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());
|
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",
|
let r = add_retrieve_cookies(
|
||||||
&vec, "https://home.example.org:8888/cookie-parser-result?0001");
|
"https://home.example.org:8888/cookie-parser?0001",
|
||||||
assert_eq!(&r, "extra1=bar; extra2=bar; extra3=bar; extra4=bar; foo2=bar");
|
&vec,
|
||||||
|
"https://home.example.org:8888/cookie-parser-result?0001",
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
&r,
|
||||||
|
"extra1=bar; extra2=bar; extra3=bar; extra4=bar; foo2=bar"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_cookie_eviction_all_secure_new_nonsecure() {
|
fn test_cookie_eviction_all_secure_new_nonsecure() {
|
||||||
let mut vec = Vec::new();
|
let mut vec = Vec::new();
|
||||||
for i in 1..6 {
|
for i in 1..6 {
|
||||||
let st = format!("extra{}=bar; Secure; expires=Sun, 18-Apr-2026 21:06:29 GMT",
|
let st = format!(
|
||||||
i);
|
"extra{}=bar; Secure; expires=Sun, 18-Apr-2026 21:06:29 GMT",
|
||||||
|
i
|
||||||
|
);
|
||||||
vec.push(st);
|
vec.push(st);
|
||||||
}
|
}
|
||||||
vec.push("foo=bar; expires=Sun, 18-Apr-2077 21:06:29 GMT".to_owned());
|
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",
|
let r = add_retrieve_cookies(
|
||||||
&vec, "https://home.example.org:8888/cookie-parser-result?0001");
|
"https://home.example.org:8888/cookie-parser?0001",
|
||||||
assert_eq!(&r, "extra1=bar; extra2=bar; extra3=bar; extra4=bar; extra5=bar");
|
&vec,
|
||||||
|
"https://home.example.org:8888/cookie-parser-result?0001",
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
&r,
|
||||||
|
"extra1=bar; extra2=bar; extra3=bar; extra4=bar; extra5=bar"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_cookie_eviction_all_nonsecure_new_secure() {
|
fn test_cookie_eviction_all_nonsecure_new_secure() {
|
||||||
let mut vec = Vec::new();
|
let mut vec = Vec::new();
|
||||||
|
@ -387,12 +447,17 @@ fn test_cookie_eviction_all_nonsecure_new_secure() {
|
||||||
vec.push(st);
|
vec.push(st);
|
||||||
}
|
}
|
||||||
vec.push("foo=bar; Secure; expires=Sun, 18-Apr-2077 21:06:29 GMT".to_owned());
|
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",
|
let r = add_retrieve_cookies(
|
||||||
&vec, "https://home.example.org:8888/cookie-parser-result?0001");
|
"https://home.example.org:8888/cookie-parser?0001",
|
||||||
assert_eq!(&r, "extra2=bar; extra3=bar; extra4=bar; extra5=bar; foo=bar");
|
&vec,
|
||||||
|
"https://home.example.org:8888/cookie-parser-result?0001",
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
&r,
|
||||||
|
"extra2=bar; extra3=bar; extra4=bar; extra5=bar; foo=bar"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_cookie_eviction_all_nonsecure_new_nonsecure() {
|
fn test_cookie_eviction_all_nonsecure_new_nonsecure() {
|
||||||
let mut vec = Vec::new();
|
let mut vec = Vec::new();
|
||||||
|
@ -401,7 +466,13 @@ fn test_cookie_eviction_all_nonsecure_new_nonsecure() {
|
||||||
vec.push(st);
|
vec.push(st);
|
||||||
}
|
}
|
||||||
vec.push("foo=bar; expires=Sun, 18-Apr-2077 21:06:29 GMT".to_owned());
|
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",
|
let r = add_retrieve_cookies(
|
||||||
&vec, "https://home.example.org:8888/cookie-parser-result?0001");
|
"https://home.example.org:8888/cookie-parser?0001",
|
||||||
assert_eq!(&r, "extra2=bar; extra3=bar; extra4=bar; extra5=bar; foo=bar");
|
&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
|
@ -14,10 +14,12 @@ use servo_url::ServoUrl;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn assert_parse(url: &'static str,
|
fn assert_parse(
|
||||||
|
url: &'static str,
|
||||||
content_type: Option<ContentType>,
|
content_type: Option<ContentType>,
|
||||||
charset: Option<&str>,
|
charset: Option<&str>,
|
||||||
data: Option<&[u8]>) {
|
data: Option<&[u8]>,
|
||||||
|
) {
|
||||||
let url = ServoUrl::parse(url).unwrap();
|
let url = ServoUrl::parse(url).unwrap();
|
||||||
let origin = Origin::Origin(url.origin());
|
let origin = Origin::Origin(url.origin());
|
||||||
let mut request = Request::new(url, Some(origin), None);
|
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);
|
assert_eq!(header_content_type, content_type);
|
||||||
|
|
||||||
let metadata = match response.metadata() {
|
let metadata = match response.metadata() {
|
||||||
Ok(FetchMetadata::Filtered { filtered: FilteredMetadata::Basic(m), .. }) => m,
|
Ok(FetchMetadata::Filtered {
|
||||||
|
filtered: FilteredMetadata::Basic(m),
|
||||||
|
..
|
||||||
|
}) => m,
|
||||||
result => panic!(result),
|
result => panic!(result),
|
||||||
};
|
};
|
||||||
assert_eq!(metadata.content_type.map(Serde::into_inner), content_type);
|
assert_eq!(metadata.content_type.map(Serde::into_inner), content_type);
|
||||||
|
@ -49,7 +54,12 @@ fn assert_parse(url: &'static str,
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
assert!(response.is_network_error());
|
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() {
|
fn plain() {
|
||||||
assert_parse(
|
assert_parse(
|
||||||
"data:,hello%20world",
|
"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("us-ascii"),
|
||||||
Some(b"hello world"));
|
Some(b"hello world"),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -74,7 +87,8 @@ fn plain_ct() {
|
||||||
"data:text/plain,hello",
|
"data:text/plain,hello",
|
||||||
Some(ContentType::from(mime::TEXT_PLAIN)),
|
Some(ContentType::from(mime::TEXT_PLAIN)),
|
||||||
None,
|
None,
|
||||||
Some(b"hello"));
|
Some(b"hello"),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -83,16 +97,20 @@ fn plain_html() {
|
||||||
"data:text/html,<p>Servo</p>",
|
"data:text/html,<p>Servo</p>",
|
||||||
Some(ContentType::from(mime::TEXT_HTML)),
|
Some(ContentType::from(mime::TEXT_HTML)),
|
||||||
None,
|
None,
|
||||||
Some(b"<p>Servo</p>"));
|
Some(b"<p>Servo</p>"),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn plain_charset() {
|
fn plain_charset() {
|
||||||
assert_parse(
|
assert_parse(
|
||||||
"data:text/plain;charset=latin1,hello",
|
"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("latin1"),
|
||||||
Some(b"hello"));
|
Some(b"hello"),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -101,16 +119,20 @@ fn plain_only_charset() {
|
||||||
"data:;charset=utf-8,hello",
|
"data:;charset=utf-8,hello",
|
||||||
Some(ContentType::from(mime::TEXT_PLAIN_UTF_8)),
|
Some(ContentType::from(mime::TEXT_PLAIN_UTF_8)),
|
||||||
Some("utf-8"),
|
Some("utf-8"),
|
||||||
Some(b"hello"));
|
Some(b"hello"),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn base64() {
|
fn base64() {
|
||||||
assert_parse(
|
assert_parse(
|
||||||
"data:;base64,C62+7w==",
|
"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("us-ascii"),
|
||||||
Some(&[0x0B, 0xAD, 0xBE, 0xEF]));
|
Some(&[0x0B, 0xAD, 0xBE, 0xEF]),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -119,14 +141,20 @@ fn base64_ct() {
|
||||||
"data:application/octet-stream;base64,C62+7w==",
|
"data:application/octet-stream;base64,C62+7w==",
|
||||||
Some(ContentType::from(mime::APPLICATION_OCTET_STREAM)),
|
Some(ContentType::from(mime::APPLICATION_OCTET_STREAM)),
|
||||||
None,
|
None,
|
||||||
Some(&[0x0B, 0xAD, 0xBE, 0xEF]));
|
Some(&[0x0B, 0xAD, 0xBE, 0xEF]),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn base64_charset() {
|
fn base64_charset() {
|
||||||
assert_parse(
|
assert_parse(
|
||||||
"data:text/plain;charset=koi8-r;base64,8PLl9+XkIO3l5Pfl5A==",
|
"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("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,
|
||||||
|
]),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,10 @@ fn test_fetch_on_bad_port_is_network_error() {
|
||||||
let fetch_response = fetch(&mut request, None);
|
let fetch_response = fetch(&mut request, None);
|
||||||
assert!(fetch_response.is_network_error());
|
assert!(fetch_response.is_network_error());
|
||||||
let fetch_error = fetch_response.get_network_error().unwrap();
|
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]
|
#[test]
|
||||||
|
@ -94,7 +97,7 @@ fn test_fetch_response_body_matches_const_message() {
|
||||||
ResponseBody::Done(ref body) => {
|
ResponseBody::Done(ref body) => {
|
||||||
assert_eq!(&**body, MESSAGE);
|
assert_eq!(&**body, MESSAGE);
|
||||||
},
|
},
|
||||||
_ => panic!()
|
_ => panic!(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +109,10 @@ fn test_fetch_aboutblank() {
|
||||||
request.referrer = Referrer::NoReferrer;
|
request.referrer = Referrer::NoReferrer;
|
||||||
let fetch_response = fetch(&mut request, None);
|
let fetch_response = fetch(&mut request, None);
|
||||||
assert!(!fetch_response.is_network_error());
|
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]
|
#[test]
|
||||||
|
@ -127,11 +133,12 @@ fn test_fetch_blob() {
|
||||||
let origin = ServoUrl::parse("http://www.example.org/").unwrap();
|
let origin = ServoUrl::parse("http://www.example.org/").unwrap();
|
||||||
|
|
||||||
let (sender, receiver) = ipc::channel().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 id = receiver.recv().unwrap().unwrap();
|
||||||
let url = ServoUrl::parse(&format!("blob:{}{}", origin.as_str(), id.simple())).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 mut request = Request::new(url, Some(Origin::Origin(origin.origin())), None);
|
||||||
let fetch_response = fetch_with_context(&mut request, &context);
|
let fetch_response = fetch_with_context(&mut request, &context);
|
||||||
|
|
||||||
|
@ -139,19 +146,27 @@ fn test_fetch_blob() {
|
||||||
|
|
||||||
assert_eq!(fetch_response.headers.len(), 2);
|
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);
|
assert_eq!(content_type, mime::TEXT_PLAIN);
|
||||||
|
|
||||||
let content_length: ContentLength = fetch_response.headers.typed_get().unwrap();
|
let content_length: ContentLength = fetch_response.headers.typed_get().unwrap();
|
||||||
assert_eq!(content_length.0, bytes.len() as u64);
|
assert_eq!(content_length.0, bytes.len() as u64);
|
||||||
|
|
||||||
assert_eq!(*fetch_response.body.lock().unwrap(),
|
assert_eq!(
|
||||||
ResponseBody::Done(bytes.to_vec()));
|
*fetch_response.body.lock().unwrap(),
|
||||||
|
ResponseBody::Done(bytes.to_vec())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fetch_file() {
|
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 url = ServoUrl::from_file_path(path.clone()).unwrap();
|
||||||
let origin = Origin::Origin(url.origin());
|
let origin = Origin::Origin(url.origin());
|
||||||
let mut request = Request::new(url, Some(origin), None);
|
let mut request = Request::new(url, Some(origin), None);
|
||||||
|
@ -159,7 +174,11 @@ fn test_fetch_file() {
|
||||||
let fetch_response = fetch(&mut request, None);
|
let fetch_response = fetch(&mut request, None);
|
||||||
assert!(!fetch_response.is_network_error());
|
assert!(!fetch_response.is_network_error());
|
||||||
assert_eq!(fetch_response.headers.len(), 1);
|
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);
|
assert_eq!(content_type, mime::TEXT_CSS);
|
||||||
|
|
||||||
let resp_body = fetch_response.body.lock().unwrap();
|
let resp_body = fetch_response.body.lock().unwrap();
|
||||||
|
@ -171,7 +190,7 @@ fn test_fetch_file() {
|
||||||
ResponseBody::Done(ref val) => {
|
ResponseBody::Done(ref val) => {
|
||||||
assert_eq!(val, &bytes);
|
assert_eq!(val, &bytes);
|
||||||
},
|
},
|
||||||
_ => panic!()
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,15 +219,34 @@ fn test_cors_preflight_fetch() {
|
||||||
static ACK: &'static [u8] = b"ACK";
|
static ACK: &'static [u8] = b"ACK";
|
||||||
let state = Arc::new(AtomicUsize::new(0));
|
let state = Arc::new(AtomicUsize::new(0));
|
||||||
let handler = move |request: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
let handler = move |request: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
||||||
if request.method() == Method::OPTIONS && state.clone().fetch_add(1, Ordering::SeqCst) == 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));
|
assert!(request
|
||||||
assert!(!request.headers().get(header::REFERER).unwrap().to_str().unwrap().contains("a.html"));
|
.headers()
|
||||||
response.headers_mut().typed_insert(AccessControlAllowOrigin::ANY);
|
.contains_key(header::ACCESS_CONTROL_REQUEST_METHOD));
|
||||||
response.headers_mut().typed_insert(AccessControlAllowCredentials);
|
assert!(!request
|
||||||
response.headers_mut().typed_insert(AccessControlAllowMethods::from_iter(vec![Method::GET]));
|
.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 {
|
} else {
|
||||||
response.headers_mut().typed_insert(AccessControlAllowOrigin::ANY);
|
response
|
||||||
|
.headers_mut()
|
||||||
|
.typed_insert(AccessControlAllowOrigin::ANY);
|
||||||
*response.body_mut() = ACK.to_vec().into();
|
*response.body_mut() = ACK.to_vec().into();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -228,7 +266,7 @@ fn test_cors_preflight_fetch() {
|
||||||
assert!(!fetch_response.is_network_error());
|
assert!(!fetch_response.is_network_error());
|
||||||
match *fetch_response.body.lock().unwrap() {
|
match *fetch_response.body.lock().unwrap() {
|
||||||
ResponseBody::Done(ref body) => assert_eq!(&**body, ACK),
|
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 counter = state.clone();
|
||||||
let mut cache = CorsCache::new();
|
let mut cache = CorsCache::new();
|
||||||
let handler = move |request: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
let handler = move |request: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
||||||
if request.method() == Method::OPTIONS && state.clone().fetch_add(1, Ordering::SeqCst) == 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));
|
assert!(request
|
||||||
response.headers_mut().typed_insert(AccessControlAllowOrigin::ANY);
|
.headers()
|
||||||
response.headers_mut().typed_insert(AccessControlAllowCredentials);
|
.contains_key(header::ACCESS_CONTROL_REQUEST_METHOD));
|
||||||
response.headers_mut().typed_insert(AccessControlAllowMethods::from_iter(vec![Method::GET]));
|
assert!(!request
|
||||||
response.headers_mut().typed_insert(AccessControlMaxAge::from(Duration::new(6000, 0)));
|
.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 {
|
} else {
|
||||||
response.headers_mut().typed_insert(AccessControlAllowOrigin::ANY);
|
response
|
||||||
|
.headers_mut()
|
||||||
|
.typed_insert(AccessControlAllowOrigin::ANY);
|
||||||
*response.body_mut() = ACK.to_vec().into();
|
*response.body_mut() = ACK.to_vec().into();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -276,11 +329,11 @@ fn test_cors_preflight_cache_fetch() {
|
||||||
|
|
||||||
match *fetch_response0.body.lock().unwrap() {
|
match *fetch_response0.body.lock().unwrap() {
|
||||||
ResponseBody::Done(ref body) => assert_eq!(&**body, ACK),
|
ResponseBody::Done(ref body) => assert_eq!(&**body, ACK),
|
||||||
_ => panic!()
|
_ => panic!(),
|
||||||
};
|
};
|
||||||
match *fetch_response1.body.lock().unwrap() {
|
match *fetch_response1.body.lock().unwrap() {
|
||||||
ResponseBody::Done(ref body) => assert_eq!(&**body, ACK),
|
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";
|
static ACK: &'static [u8] = b"ACK";
|
||||||
let state = Arc::new(AtomicUsize::new(0));
|
let state = Arc::new(AtomicUsize::new(0));
|
||||||
let handler = move |request: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
let handler = move |request: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
||||||
if request.method() == Method::OPTIONS && state.clone().fetch_add(1, Ordering::SeqCst) == 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));
|
assert!(request
|
||||||
response.headers_mut().typed_insert(AccessControlAllowOrigin::ANY);
|
.headers()
|
||||||
response.headers_mut().typed_insert(AccessControlAllowCredentials);
|
.contains_key(header::ACCESS_CONTROL_REQUEST_METHOD));
|
||||||
response.headers_mut().typed_insert(AccessControlAllowMethods::from_iter(vec![Method::GET]));
|
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 {
|
} else {
|
||||||
response.headers_mut().typed_insert(AccessControlAllowOrigin::ANY);
|
response
|
||||||
|
.headers_mut()
|
||||||
|
.typed_insert(AccessControlAllowOrigin::ANY);
|
||||||
*response.body_mut() = ACK.to_vec().into();
|
*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() {
|
fn test_fetch_response_is_basic_filtered() {
|
||||||
static MESSAGE: &'static [u8] = b"";
|
static MESSAGE: &'static [u8] = b"";
|
||||||
let handler = move |_: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
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
|
// this header is obsoleted, so hyper doesn't implement it, but it's still covered by the spec
|
||||||
response.headers_mut().insert(
|
response.headers_mut().insert(
|
||||||
HeaderName::from_static("set-cookie2"),
|
HeaderName::from_static("set-cookie2"),
|
||||||
HeaderValue::from_bytes(&vec![]).unwrap()
|
HeaderValue::from_bytes(&vec![]).unwrap(),
|
||||||
);
|
);
|
||||||
|
|
||||||
*response.body_mut() = MESSAGE.to_vec().into();
|
*response.body_mut() = MESSAGE.to_vec().into();
|
||||||
|
@ -340,7 +408,9 @@ fn test_fetch_response_is_basic_filtered() {
|
||||||
|
|
||||||
let headers = fetch_response.headers;
|
let headers = fetch_response.headers;
|
||||||
assert!(!headers.contains_key(header::SET_COOKIE));
|
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]
|
#[test]
|
||||||
|
@ -349,28 +419,41 @@ fn test_fetch_response_is_cors_filtered() {
|
||||||
let handler = move |_: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
let handler = move |_: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
||||||
// this is mandatory for the Cors Check to pass
|
// this is mandatory for the Cors Check to pass
|
||||||
// TODO test using different url encodings with this value ie. punycode
|
// 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
|
// these are the headers that should be kept after filtering
|
||||||
response.headers_mut().typed_insert(CacheControl::new());
|
response.headers_mut().typed_insert(CacheControl::new());
|
||||||
response.headers_mut().insert(header::CONTENT_LANGUAGE, HeaderValue::from_bytes(&vec![]).unwrap());
|
response.headers_mut().insert(
|
||||||
response.headers_mut().typed_insert(ContentType::from(mime::TEXT_HTML));
|
header::CONTENT_LANGUAGE,
|
||||||
response.headers_mut().typed_insert(Expires::from(SystemTime::now() + Duration::new(86400, 0)));
|
HeaderValue::from_bytes(&vec![]).unwrap(),
|
||||||
response.headers_mut().typed_insert(LastModified::from(SystemTime::now()));
|
);
|
||||||
|
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());
|
response.headers_mut().typed_insert(Pragma::no_cache());
|
||||||
|
|
||||||
// these headers should not be kept after filtering, even though they are given a pass
|
// 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(
|
response.headers_mut().insert(
|
||||||
HeaderName::from_static("set-cookie2"),
|
HeaderName::from_static("set-cookie2"),
|
||||||
HeaderValue::from_bytes(&vec![]).unwrap()
|
HeaderValue::from_bytes(&vec![]).unwrap(),
|
||||||
);
|
);
|
||||||
response.headers_mut().typed_insert(
|
response
|
||||||
AccessControlAllowHeaders::from_iter(vec![
|
.headers_mut()
|
||||||
|
.typed_insert(AccessControlAllowHeaders::from_iter(vec![
|
||||||
HeaderName::from_static("set-cookie"),
|
HeaderName::from_static("set-cookie"),
|
||||||
HeaderName::from_static("set-cookie2")
|
HeaderName::from_static("set-cookie2"),
|
||||||
])
|
]));
|
||||||
);
|
|
||||||
|
|
||||||
*response.body_mut() = MESSAGE.to_vec().into();
|
*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::ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
assert!(!headers.contains_key(header::SET_COOKIE));
|
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]
|
#[test]
|
||||||
|
@ -425,11 +510,11 @@ fn test_fetch_response_is_opaque_filtered() {
|
||||||
assert_eq!(fetch_response.headers, HeaderMap::new());
|
assert_eq!(fetch_response.headers, HeaderMap::new());
|
||||||
match *fetch_response.body.lock().unwrap() {
|
match *fetch_response.body.lock().unwrap() {
|
||||||
ResponseBody::Empty => {},
|
ResponseBody::Empty => {},
|
||||||
_ => panic!()
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
match fetch_response.cache_state {
|
match fetch_response.cache_state {
|
||||||
CacheState::None => {},
|
CacheState::None => {},
|
||||||
_ => panic!()
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -437,13 +522,21 @@ fn test_fetch_response_is_opaque_filtered() {
|
||||||
fn test_fetch_response_is_opaque_redirect_filtered() {
|
fn test_fetch_response_is_opaque_redirect_filtered() {
|
||||||
static MESSAGE: &'static [u8] = b"";
|
static MESSAGE: &'static [u8] = b"";
|
||||||
let handler = move |request: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
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 {
|
if redirects == 1 {
|
||||||
*response.body_mut() = MESSAGE.to_vec().into();
|
*response.body_mut() = MESSAGE.to_vec().into();
|
||||||
} else {
|
} else {
|
||||||
*response.status_mut() = StatusCode::FOUND;
|
*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"));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -464,11 +557,11 @@ fn test_fetch_response_is_opaque_redirect_filtered() {
|
||||||
assert_eq!(fetch_response.headers, HeaderMap::new());
|
assert_eq!(fetch_response.headers, HeaderMap::new());
|
||||||
match *fetch_response.body.lock().unwrap() {
|
match *fetch_response.body.lock().unwrap() {
|
||||||
ResponseBody::Empty => {},
|
ResponseBody::Empty => {},
|
||||||
_ => panic!()
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
match fetch_response.cache_state {
|
match fetch_response.cache_state {
|
||||||
CacheState::None => {},
|
CacheState::None => {},
|
||||||
_ => panic!()
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -516,12 +609,19 @@ fn test_fetch_with_hsts() {
|
||||||
*response.body_mut() = MESSAGE.to_vec().into();
|
*response.body_mut() = MESSAGE.to_vec().into();
|
||||||
};
|
};
|
||||||
|
|
||||||
let cert_path = Path::new("../../resources/self_signed_certificate_for_testing.crt").canonicalize().unwrap();
|
let cert_path = Path::new("../../resources/self_signed_certificate_for_testing.crt")
|
||||||
let key_path = Path::new("../../resources/privatekey_for_testing.key").canonicalize().unwrap();
|
.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 (server, url) = make_ssl_server(handler, cert_path.clone(), key_path.clone());
|
||||||
|
|
||||||
let mut ca_content = String::new();
|
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 ssl_client = create_ssl_connector_builder(&ca_content);
|
||||||
|
|
||||||
let context = FetchContext {
|
let context = FetchContext {
|
||||||
|
@ -534,8 +634,9 @@ fn test_fetch_with_hsts() {
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut list = context.state.hsts_list.write().unwrap();
|
let mut list = context.state.hsts_list.write().unwrap();
|
||||||
list.push(HstsEntry::new("localhost".to_owned(), IncludeSubdomains::NotIncluded, None)
|
list.push(
|
||||||
.unwrap());
|
HstsEntry::new("localhost".to_owned(), IncludeSubdomains::NotIncluded, None).unwrap(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
let origin = Origin::Origin(url.origin());
|
let origin = Origin::Origin(url.origin());
|
||||||
let mut request = Request::new(url, Some(origin), None);
|
let mut request = Request::new(url, Some(origin), None);
|
||||||
|
@ -544,8 +645,10 @@ fn test_fetch_with_hsts() {
|
||||||
request.local_urls_only = false;
|
request.local_urls_only = false;
|
||||||
let response = fetch_with_context(&mut request, &context);
|
let response = fetch_with_context(&mut request, &context);
|
||||||
server.close();
|
server.close();
|
||||||
assert_eq!(response.internal_response.unwrap().url().unwrap().scheme(),
|
assert_eq!(
|
||||||
"https");
|
response.internal_response.unwrap().url().unwrap().scheme(),
|
||||||
|
"https"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -600,9 +703,7 @@ fn test_fetch_with_sri_sucess() {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fetch_blocked_nosniff() {
|
fn test_fetch_blocked_nosniff() {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn test_nosniff_request(destination: Destination,
|
fn test_nosniff_request(destination: Destination, mime: Mime, should_error: bool) {
|
||||||
mime: Mime,
|
|
||||||
should_error: bool) {
|
|
||||||
const MESSAGE: &'static [u8] = b"";
|
const MESSAGE: &'static [u8] = b"";
|
||||||
const HEADER: &'static str = "x-content-type-options";
|
const HEADER: &'static str = "x-content-type-options";
|
||||||
const VALUE: &'static [u8] = b"nosniff";
|
const VALUE: &'static [u8] = b"nosniff";
|
||||||
|
@ -612,7 +713,10 @@ fn test_fetch_blocked_nosniff() {
|
||||||
response.headers_mut().typed_insert(mime_header);
|
response.headers_mut().typed_insert(mime_header);
|
||||||
assert!(response.headers().contains_key(header::CONTENT_TYPE));
|
assert!(response.headers().contains_key(header::CONTENT_TYPE));
|
||||||
// Add the nosniff header
|
// 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();
|
*response.body_mut() = MESSAGE.to_vec().into();
|
||||||
};
|
};
|
||||||
|
@ -642,14 +746,22 @@ fn test_fetch_blocked_nosniff() {
|
||||||
|
|
||||||
fn setup_server_and_fetch(message: &'static [u8], redirect_cap: u32) -> Response {
|
fn setup_server_and_fetch(message: &'static [u8], redirect_cap: u32) -> Response {
|
||||||
let handler = move |request: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
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 {
|
if redirects >= redirect_cap {
|
||||||
*response.body_mut() = message.to_vec().into();
|
*response.body_mut() = message.to_vec().into();
|
||||||
} else {
|
} else {
|
||||||
*response.status_mut() = StatusCode::FOUND;
|
*response.status_mut() = StatusCode::FOUND;
|
||||||
let url = format!("{redirects}", redirects = redirects + 1);
|
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) => {
|
ResponseBody::Done(ref body) => {
|
||||||
assert_eq!(&**body, MESSAGE);
|
assert_eq!(&**body, MESSAGE);
|
||||||
},
|
},
|
||||||
_ => panic!()
|
_ => panic!(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -694,31 +806,43 @@ fn test_fetch_redirect_count_failure() {
|
||||||
|
|
||||||
match *fetch_response.body.lock().unwrap() {
|
match *fetch_response.body.lock().unwrap() {
|
||||||
ResponseBody::Done(_) | ResponseBody::Receiving(_) => panic!(),
|
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_method = method.clone();
|
||||||
let handler_tx = Arc::new(Mutex::new(tx));
|
let handler_tx = Arc::new(Mutex::new(tx));
|
||||||
|
|
||||||
let handler = move |request: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
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;
|
let mut test_pass = true;
|
||||||
|
|
||||||
if redirects == 0 {
|
if redirects == 0 {
|
||||||
*response.status_mut() = StatusCode::TEMPORARY_REDIRECT;
|
*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 {
|
} else if redirects == 1 {
|
||||||
// this makes sure that the request method does't change from the wrong status code
|
// this makes sure that the request method does't change from the wrong status code
|
||||||
if handler_method != Method::GET && request.method() == Method::GET {
|
if handler_method != Method::GET && request.method() == Method::GET {
|
||||||
test_pass = false;
|
test_pass = false;
|
||||||
}
|
}
|
||||||
*response.status_mut() = status_code;
|
*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 {
|
} else if request.method() != Method::GET {
|
||||||
test_pass = false;
|
test_pass = false;
|
||||||
}
|
}
|
||||||
|
@ -727,7 +851,6 @@ fn test_fetch_redirect_updates_method_runner(tx: Sender<bool>, status_code: Stat
|
||||||
if redirects > 0 {
|
if redirects > 0 {
|
||||||
handler_tx.lock().unwrap().send(test_pass).unwrap();
|
handler_tx.lock().unwrap().send(test_pass).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let (server, url) = make_server(handler);
|
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() {
|
fn test_fetch_redirect_updates_method() {
|
||||||
let (tx, rx) = channel();
|
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);
|
||||||
assert_eq!(rx.recv().unwrap(), true);
|
assert_eq!(rx.recv().unwrap(), true);
|
||||||
// make sure the test doesn't send more data than expected
|
// 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();
|
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);
|
assert_eq!(rx.recv().unwrap(), true);
|
||||||
// for MovedPermanently and Found, Method should only be changed if it was Post
|
// for MovedPermanently and Found, Method should only be changed if it was Post
|
||||||
assert_eq!(rx.recv().unwrap(), false);
|
assert_eq!(rx.recv().unwrap(), false);
|
||||||
|
@ -785,9 +916,9 @@ fn response_is_done(response: &Response) -> bool {
|
||||||
let response_complete = match response.response_type {
|
let response_complete = match response.response_type {
|
||||||
ResponseType::Default | ResponseType::Basic | ResponseType::Cors => {
|
ResponseType::Default | ResponseType::Basic | ResponseType::Cors => {
|
||||||
(*response.body.lock().unwrap()).is_done()
|
(*response.body.lock().unwrap()).is_done()
|
||||||
}
|
},
|
||||||
// if the internal response cannot have a body, it shouldn't block the "done" state
|
// 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 {
|
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() {
|
fn test_opaque_redirect_filtered_fetch_async_returns_complete_response() {
|
||||||
static MESSAGE: &'static [u8] = b"";
|
static MESSAGE: &'static [u8] = b"";
|
||||||
let handler = move |request: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
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 {
|
if redirects == 1 {
|
||||||
*response.body_mut() = MESSAGE.to_vec().into();
|
*response.body_mut() = MESSAGE.to_vec().into();
|
||||||
} else {
|
} else {
|
||||||
*response.status_mut() = StatusCode::FOUND;
|
*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
|
//Creating default headers for request
|
||||||
let mut headers = HeaderMap::new();
|
let mut headers = HeaderMap::new();
|
||||||
|
|
||||||
headers.insert(header::ACCEPT_ENCODING, HeaderValue::from_static("gzip, deflate, br"));
|
headers.insert(
|
||||||
headers.typed_insert(
|
header::ACCEPT_ENCODING,
|
||||||
Host::from(format!("{}:{}", url.host_str().unwrap(), url.port().unwrap()).parse::<Authority>().unwrap()));
|
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, 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());
|
headers.typed_insert::<UserAgent>(DEFAULT_USER_AGENT.parse().unwrap());
|
||||||
|
|
||||||
|
@ -918,7 +1066,11 @@ fn test_fetch_with_devtools() {
|
||||||
let content = "Yay!";
|
let content = "Yay!";
|
||||||
let mut response_headers = HeaderMap::new();
|
let mut response_headers = HeaderMap::new();
|
||||||
response_headers.typed_insert(ContentLength(content.len() as u64));
|
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 {
|
let httpresponse = DevtoolsHttpResponse {
|
||||||
headers: Some(response_headers),
|
headers: Some(response_headers),
|
||||||
|
|
|
@ -16,13 +16,17 @@ use std::path::PathBuf;
|
||||||
#[test]
|
#[test]
|
||||||
fn test_filemanager() {
|
fn test_filemanager() {
|
||||||
let filemanager = FileManager::new(create_embedder_proxy());
|
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
|
// 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 handler = File::open("tests/test.jpeg").expect("test.jpeg is stolen");
|
||||||
let mut test_file_content = vec![];
|
let mut test_file_content = vec![];
|
||||||
|
|
||||||
handler.read_to_end(&mut test_file_content)
|
handler
|
||||||
|
.read_to_end(&mut test_file_content)
|
||||||
.expect("Read components/net/tests/test.jpeg error");
|
.expect("Read components/net/tests/test.jpeg error");
|
||||||
|
|
||||||
let patterns = vec![FilterPattern(".txt".to_string())];
|
let patterns = vec![FilterPattern(".txt".to_string())];
|
||||||
|
@ -31,9 +35,15 @@ fn test_filemanager() {
|
||||||
{
|
{
|
||||||
// Try to select a dummy file "components/net/tests/test.jpeg"
|
// Try to select a dummy file "components/net/tests/test.jpeg"
|
||||||
let (tx, rx) = ipc::channel().unwrap();
|
let (tx, rx) = ipc::channel().unwrap();
|
||||||
filemanager.handle(FileManagerThreadMsg::SelectFile(patterns.clone(), tx, origin.clone(),
|
filemanager.handle(FileManagerThreadMsg::SelectFile(
|
||||||
Some("tests/test.jpeg".to_string())));
|
patterns.clone(),
|
||||||
let selected = rx.recv().expect("Broken channel")
|
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");
|
.expect("The file manager failed to find test.jpeg");
|
||||||
|
|
||||||
// Expecting attributes conforming the spec
|
// Expecting attributes conforming the spec
|
||||||
|
@ -43,24 +53,35 @@ fn test_filemanager() {
|
||||||
// Test by reading, expecting same content
|
// Test by reading, expecting same content
|
||||||
{
|
{
|
||||||
let (tx2, rx2) = ipc::channel().unwrap();
|
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");
|
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;
|
let mut bytes = blob_buf.bytes;
|
||||||
|
|
||||||
loop {
|
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(_) => {
|
ReadFileProgress::Meta(_) => {
|
||||||
panic!("Invalid FileManager reply");
|
panic!("Invalid FileManager reply");
|
||||||
}
|
},
|
||||||
ReadFileProgress::Partial(mut bytes_in) => {
|
ReadFileProgress::Partial(mut bytes_in) => {
|
||||||
bytes.append(&mut bytes_in);
|
bytes.append(&mut bytes_in);
|
||||||
}
|
},
|
||||||
ReadFileProgress::EOF => {
|
ReadFileProgress::EOF => {
|
||||||
break;
|
break;
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +94,11 @@ fn test_filemanager() {
|
||||||
// Delete the id
|
// Delete the id
|
||||||
{
|
{
|
||||||
let (tx2, rx2) = ipc::channel().unwrap();
|
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");
|
let ret = rx2.recv().expect("Broken channel");
|
||||||
assert!(ret.is_ok(), "DecRef is not okay");
|
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
|
// Test by reading again, expecting read error because we invalidated the id
|
||||||
{
|
{
|
||||||
let (tx2, rx2) = ipc::channel().unwrap();
|
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");
|
let msg = rx2.recv().expect("Broken channel");
|
||||||
|
|
||||||
match msg {
|
match msg {
|
||||||
Err(FileManagerThreadError::BlobURLStoreError(BlobURLStoreError::InvalidFileID)) => {},
|
Err(FileManagerThreadError::BlobURLStoreError(
|
||||||
|
BlobURLStoreError::InvalidFileID,
|
||||||
|
)) => {},
|
||||||
other => {
|
other => {
|
||||||
assert!(false, "Get unexpected response after deleting the id: {:?}", other);
|
assert!(
|
||||||
}
|
false,
|
||||||
|
"Get unexpected response after deleting the id: {:?}",
|
||||||
|
other
|
||||||
|
);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ fn test_hsts_entry_is_not_expired_when_it_has_no_timestamp() {
|
||||||
host: "mozilla.org".to_owned(),
|
host: "mozilla.org".to_owned(),
|
||||||
include_subdomains: false,
|
include_subdomains: false,
|
||||||
max_age: Some(20),
|
max_age: Some(20),
|
||||||
timestamp: None
|
timestamp: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(!entry.is_expired());
|
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(),
|
host: "mozilla.org".to_owned(),
|
||||||
include_subdomains: false,
|
include_subdomains: false,
|
||||||
max_age: None,
|
max_age: None,
|
||||||
timestamp: Some(time::get_time().sec as u64)
|
timestamp: Some(time::get_time().sec as u64),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(!entry.is_expired());
|
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(),
|
host: "mozilla.org".to_owned(),
|
||||||
include_subdomains: false,
|
include_subdomains: false,
|
||||||
max_age: Some(10),
|
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());
|
assert!(entry.is_expired());
|
||||||
|
@ -46,7 +46,9 @@ fn test_hsts_entry_is_expired_when_it_has_reached_its_max_age() {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_hsts_entry_cant_be_created_with_ipv6_address_as_host() {
|
fn test_hsts_entry_cant_be_created_with_ipv6_address_as_host() {
|
||||||
let entry = HstsEntry::new(
|
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");
|
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]
|
#[test]
|
||||||
fn test_hsts_entry_cant_be_created_with_ipv4_address_as_host() {
|
fn test_hsts_entry_cant_be_created_with_ipv4_address_as_host() {
|
||||||
let entry = HstsEntry::new(
|
let entry = HstsEntry::new("4.4.4.4".to_owned(), IncludeSubdomains::NotIncluded, None);
|
||||||
"4.4.4.4".to_owned(), IncludeSubdomains::NotIncluded, None
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(entry.is_none(), "able to create HstsEntry with IPv4 host");
|
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 entries_map = HashMap::new();
|
||||||
|
|
||||||
let mut list = HstsList {
|
let mut list = HstsList {
|
||||||
entries_map: entries_map
|
entries_map: entries_map,
|
||||||
};
|
};
|
||||||
|
|
||||||
list.push(HstsEntry::new("servo.mozilla.org".to_owned(),
|
list.push(
|
||||||
IncludeSubdomains::NotIncluded, None).unwrap());
|
HstsEntry::new(
|
||||||
list.push(HstsEntry::new("firefox.mozilla.org".to_owned(),
|
"servo.mozilla.org".to_owned(),
|
||||||
IncludeSubdomains::NotIncluded, None).unwrap());
|
IncludeSubdomains::NotIncluded,
|
||||||
list.push(HstsEntry::new("bugzilla.org".to_owned(),
|
None,
|
||||||
IncludeSubdomains::NotIncluded, None).unwrap());
|
)
|
||||||
|
.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.len(), 2);
|
||||||
assert_eq!(list.entries_map.get("mozilla.org").unwrap().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]
|
#[test]
|
||||||
fn test_push_entry_with_0_max_age_evicts_entry_from_list() {
|
fn test_push_entry_with_0_max_age_evicts_entry_from_list() {
|
||||||
let mut entries_map = HashMap::new();
|
let mut entries_map = HashMap::new();
|
||||||
entries_map.insert("mozilla.org".to_owned(), vec!(HstsEntry::new("mozilla.org".to_owned(),
|
entries_map.insert(
|
||||||
IncludeSubdomains::NotIncluded, Some(500000u64)).unwrap()));
|
"mozilla.org".to_owned(),
|
||||||
|
vec![HstsEntry::new(
|
||||||
|
"mozilla.org".to_owned(),
|
||||||
|
IncludeSubdomains::NotIncluded,
|
||||||
|
Some(500000u64),
|
||||||
|
)
|
||||||
|
.unwrap()],
|
||||||
|
);
|
||||||
let mut list = HstsList {
|
let mut list = HstsList {
|
||||||
entries_map: entries_map
|
entries_map: entries_map,
|
||||||
};
|
};
|
||||||
|
|
||||||
list.push(HstsEntry::new("mozilla.org".to_owned(),
|
list.push(
|
||||||
IncludeSubdomains::NotIncluded, Some(0)).unwrap());
|
HstsEntry::new(
|
||||||
|
"mozilla.org".to_owned(),
|
||||||
|
IncludeSubdomains::NotIncluded,
|
||||||
|
Some(0),
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(list.is_host_secure("mozilla.org"), false)
|
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]
|
#[test]
|
||||||
fn test_push_entry_to_hsts_list_should_not_add_subdomains_whose_superdomain_is_already_matched() {
|
fn test_push_entry_to_hsts_list_should_not_add_subdomains_whose_superdomain_is_already_matched() {
|
||||||
let mut entries_map = HashMap::new();
|
let mut entries_map = HashMap::new();
|
||||||
entries_map.insert("mozilla.org".to_owned(), vec!(HstsEntry::new("mozilla.org".to_owned(),
|
entries_map.insert(
|
||||||
IncludeSubdomains::Included, None).unwrap()));
|
"mozilla.org".to_owned(),
|
||||||
|
vec![HstsEntry::new("mozilla.org".to_owned(), IncludeSubdomains::Included, None).unwrap()],
|
||||||
|
);
|
||||||
let mut list = HstsList {
|
let mut list = HstsList {
|
||||||
entries_map: entries_map
|
entries_map: entries_map,
|
||||||
};
|
};
|
||||||
|
|
||||||
list.push(HstsEntry::new("servo.mozilla.org".to_owned(),
|
list.push(
|
||||||
IncludeSubdomains::NotIncluded, None).unwrap());
|
HstsEntry::new(
|
||||||
|
"servo.mozilla.org".to_owned(),
|
||||||
|
IncludeSubdomains::NotIncluded,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(list.entries_map.get("mozilla.org").unwrap().len(), 1)
|
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]
|
#[test]
|
||||||
fn test_push_entry_to_hsts_list_should_update_existing_domain_entrys_include_subdomains() {
|
fn test_push_entry_to_hsts_list_should_update_existing_domain_entrys_include_subdomains() {
|
||||||
let mut entries_map = HashMap::new();
|
let mut entries_map = HashMap::new();
|
||||||
entries_map.insert("mozilla.org".to_owned(), vec!(HstsEntry::new("mozilla.org".to_owned(),
|
entries_map.insert(
|
||||||
IncludeSubdomains::Included, None).unwrap()));
|
"mozilla.org".to_owned(),
|
||||||
|
vec![HstsEntry::new("mozilla.org".to_owned(), IncludeSubdomains::Included, None).unwrap()],
|
||||||
|
);
|
||||||
let mut list = HstsList {
|
let mut list = HstsList {
|
||||||
entries_map: entries_map
|
entries_map: entries_map,
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(list.is_host_secure("servo.mozilla.org"));
|
assert!(list.is_host_secure("servo.mozilla.org"));
|
||||||
|
|
||||||
list.push(HstsEntry::new("mozilla.org".to_owned(),
|
list.push(
|
||||||
IncludeSubdomains::NotIncluded, None).unwrap());
|
HstsEntry::new(
|
||||||
|
"mozilla.org".to_owned(),
|
||||||
|
IncludeSubdomains::NotIncluded,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
assert!(!list.is_host_secure("servo.mozilla.org"))
|
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]
|
#[test]
|
||||||
fn test_push_entry_to_hsts_list_should_not_create_duplicate_entry() {
|
fn test_push_entry_to_hsts_list_should_not_create_duplicate_entry() {
|
||||||
let mut entries_map = HashMap::new();
|
let mut entries_map = HashMap::new();
|
||||||
entries_map.insert("mozilla.org".to_owned(), vec!(HstsEntry::new("mozilla.org".to_owned(),
|
entries_map.insert(
|
||||||
IncludeSubdomains::NotIncluded, None).unwrap()));
|
"mozilla.org".to_owned(),
|
||||||
|
vec![HstsEntry::new(
|
||||||
|
"mozilla.org".to_owned(),
|
||||||
|
IncludeSubdomains::NotIncluded,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap()],
|
||||||
|
);
|
||||||
let mut list = HstsList {
|
let mut list = HstsList {
|
||||||
entries_map: entries_map
|
entries_map: entries_map,
|
||||||
};
|
};
|
||||||
|
|
||||||
list.push(HstsEntry::new("mozilla.org".to_owned(),
|
list.push(
|
||||||
IncludeSubdomains::NotIncluded, None).unwrap());
|
HstsEntry::new(
|
||||||
|
"mozilla.org".to_owned(),
|
||||||
|
IncludeSubdomains::NotIncluded,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(list.entries_map.get("mozilla.org").unwrap().len(), 1)
|
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]
|
#[test]
|
||||||
fn test_push_multiple_entrie_to_hsts_list_should_add_them_all() {
|
fn test_push_multiple_entrie_to_hsts_list_should_add_them_all() {
|
||||||
let mut list = HstsList {
|
let mut list = HstsList {
|
||||||
entries_map: HashMap::new()
|
entries_map: HashMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(!list.is_host_secure("mozilla.org"));
|
assert!(!list.is_host_secure("mozilla.org"));
|
||||||
assert!(!list.is_host_secure("bugzilla.org"));
|
assert!(!list.is_host_secure("bugzilla.org"));
|
||||||
|
|
||||||
list.push(HstsEntry::new("mozilla.org".to_owned(),
|
list.push(HstsEntry::new("mozilla.org".to_owned(), IncludeSubdomains::Included, None).unwrap());
|
||||||
IncludeSubdomains::Included, None).unwrap());
|
list.push(
|
||||||
list.push(HstsEntry::new("bugzilla.org".to_owned(),
|
HstsEntry::new("bugzilla.org".to_owned(), IncludeSubdomains::Included, None).unwrap(),
|
||||||
IncludeSubdomains::Included, None).unwrap());
|
);
|
||||||
|
|
||||||
assert!(list.is_host_secure("mozilla.org"));
|
assert!(list.is_host_secure("mozilla.org"));
|
||||||
assert!(list.is_host_secure("bugzilla.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]
|
#[test]
|
||||||
fn test_push_entry_to_hsts_list_should_add_an_entry() {
|
fn test_push_entry_to_hsts_list_should_add_an_entry() {
|
||||||
let mut list = HstsList {
|
let mut list = HstsList {
|
||||||
entries_map: HashMap::new()
|
entries_map: HashMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(!list.is_host_secure("mozilla.org"));
|
assert!(!list.is_host_secure("mozilla.org"));
|
||||||
|
|
||||||
list.push(HstsEntry::new("mozilla.org".to_owned(),
|
list.push(HstsEntry::new("mozilla.org".to_owned(), IncludeSubdomains::Included, None).unwrap());
|
||||||
IncludeSubdomains::Included, None).unwrap());
|
|
||||||
|
|
||||||
assert!(list.is_host_secure("mozilla.org"));
|
assert!(list.is_host_secure("mozilla.org"));
|
||||||
}
|
}
|
||||||
|
@ -177,13 +236,19 @@ fn test_push_entry_to_hsts_list_should_add_an_entry() {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_hsts_preload_should_return_none_when_json_invalid() {
|
fn test_parse_hsts_preload_should_return_none_when_json_invalid() {
|
||||||
let mock_preload_content = "derp";
|
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]
|
#[test]
|
||||||
fn test_parse_hsts_preload_should_return_none_when_json_contains_no_entries_map_key() {
|
fn test_parse_hsts_preload_should_return_none_when_json_contains_no_entries_map_key() {
|
||||||
let mock_preload_content = "{\"nothing\": \"to see here\"}";
|
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]
|
#[test]
|
||||||
|
@ -197,14 +262,17 @@ fn test_parse_hsts_preload_should_decode_host_and_includes_subdomains() {
|
||||||
let hsts_list = HstsList::from_preload(mock_preload_content);
|
let hsts_list = HstsList::from_preload(mock_preload_content);
|
||||||
let entries_map = hsts_list.unwrap().entries_map;
|
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);
|
assert!(!entries_map.get("mozilla.org").unwrap()[0].include_subdomains);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_hsts_list_with_no_entries_map_does_not_is_host_secure() {
|
fn test_hsts_list_with_no_entries_map_does_not_is_host_secure() {
|
||||||
let hsts_list = HstsList {
|
let hsts_list = HstsList {
|
||||||
entries_map: HashMap::new()
|
entries_map: HashMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(!hsts_list.is_host_secure("mozilla.org"));
|
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]
|
#[test]
|
||||||
fn test_hsts_list_with_exact_domain_entry_is_is_host_secure() {
|
fn test_hsts_list_with_exact_domain_entry_is_is_host_secure() {
|
||||||
let mut entries_map = HashMap::new();
|
let mut entries_map = HashMap::new();
|
||||||
entries_map.insert("mozilla.org".to_owned(), vec![HstsEntry::new("mozilla.org".to_owned(),
|
entries_map.insert(
|
||||||
IncludeSubdomains::NotIncluded, None).unwrap()]);
|
"mozilla.org".to_owned(),
|
||||||
|
vec![HstsEntry::new(
|
||||||
|
"mozilla.org".to_owned(),
|
||||||
|
IncludeSubdomains::NotIncluded,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap()],
|
||||||
|
);
|
||||||
|
|
||||||
let hsts_list = HstsList {
|
let hsts_list = HstsList {
|
||||||
entries_map: entries_map
|
entries_map: entries_map,
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(hsts_list.is_host_secure("mozilla.org"));
|
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]
|
#[test]
|
||||||
fn test_hsts_list_with_subdomain_when_include_subdomains_is_true_is_is_host_secure() {
|
fn test_hsts_list_with_subdomain_when_include_subdomains_is_true_is_is_host_secure() {
|
||||||
let mut entries_map = HashMap::new();
|
let mut entries_map = HashMap::new();
|
||||||
entries_map.insert("mozilla.org".to_owned(), vec![HstsEntry::new("mozilla.org".to_owned(),
|
entries_map.insert(
|
||||||
IncludeSubdomains::Included, None).unwrap()]);
|
"mozilla.org".to_owned(),
|
||||||
|
vec![HstsEntry::new("mozilla.org".to_owned(), IncludeSubdomains::Included, None).unwrap()],
|
||||||
|
);
|
||||||
let hsts_list = HstsList {
|
let hsts_list = HstsList {
|
||||||
entries_map: entries_map
|
entries_map: entries_map,
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(hsts_list.is_host_secure("servo.mozilla.org"));
|
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]
|
#[test]
|
||||||
fn test_hsts_list_with_subdomain_when_include_subdomains_is_false_is_not_is_host_secure() {
|
fn test_hsts_list_with_subdomain_when_include_subdomains_is_false_is_not_is_host_secure() {
|
||||||
let mut entries_map = HashMap::new();
|
let mut entries_map = HashMap::new();
|
||||||
entries_map.insert("mozilla.org".to_owned(), vec![HstsEntry::new("mozilla.org".to_owned(),
|
entries_map.insert(
|
||||||
IncludeSubdomains::NotIncluded, None).unwrap()]);
|
"mozilla.org".to_owned(),
|
||||||
|
vec![HstsEntry::new(
|
||||||
|
"mozilla.org".to_owned(),
|
||||||
|
IncludeSubdomains::NotIncluded,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap()],
|
||||||
|
);
|
||||||
let hsts_list = HstsList {
|
let hsts_list = HstsList {
|
||||||
entries_map: entries_map
|
entries_map: entries_map,
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(!hsts_list.is_host_secure("servo.mozilla.org"));
|
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]
|
#[test]
|
||||||
fn test_hsts_list_with_subdomain_when_host_is_not_a_subdomain_is_not_is_host_secure() {
|
fn test_hsts_list_with_subdomain_when_host_is_not_a_subdomain_is_not_is_host_secure() {
|
||||||
let mut entries_map = HashMap::new();
|
let mut entries_map = HashMap::new();
|
||||||
entries_map.insert("mozilla.org".to_owned(), vec![HstsEntry::new("mozilla.org".to_owned(),
|
entries_map.insert(
|
||||||
IncludeSubdomains::Included, None).unwrap()]);
|
"mozilla.org".to_owned(),
|
||||||
|
vec![HstsEntry::new("mozilla.org".to_owned(), IncludeSubdomains::Included, None).unwrap()],
|
||||||
|
);
|
||||||
let hsts_list = HstsList {
|
let hsts_list = HstsList {
|
||||||
entries_map: entries_map
|
entries_map: entries_map,
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(!hsts_list.is_host_secure("servo-mozilla.org"));
|
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]
|
#[test]
|
||||||
fn test_hsts_list_with_subdomain_when_host_is_exact_match_is_is_host_secure() {
|
fn test_hsts_list_with_subdomain_when_host_is_exact_match_is_is_host_secure() {
|
||||||
let mut entries_map = HashMap::new();
|
let mut entries_map = HashMap::new();
|
||||||
entries_map.insert("mozilla.org".to_owned(), vec![HstsEntry::new("mozilla.org".to_owned(),
|
entries_map.insert(
|
||||||
IncludeSubdomains::Included, None).unwrap()]);
|
"mozilla.org".to_owned(),
|
||||||
|
vec![HstsEntry::new("mozilla.org".to_owned(), IncludeSubdomains::Included, None).unwrap()],
|
||||||
|
);
|
||||||
let hsts_list = HstsList {
|
let hsts_list = HstsList {
|
||||||
entries_map: entries_map
|
entries_map: entries_map,
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(hsts_list.is_host_secure("mozilla.org"));
|
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]
|
#[test]
|
||||||
fn test_hsts_list_with_expired_entry_is_not_is_host_secure() {
|
fn test_hsts_list_with_expired_entry_is_not_is_host_secure() {
|
||||||
let mut entries_map = HashMap::new();
|
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(),
|
host: "mozilla.org".to_owned(),
|
||||||
include_subdomains: false,
|
include_subdomains: false,
|
||||||
max_age: Some(20),
|
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 {
|
let hsts_list = HstsList {
|
||||||
entries_map: entries_map
|
entries_map: entries_map,
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(!hsts_list.is_host_secure("mozilla.org"));
|
assert!(!hsts_list.is_host_secure("mozilla.org"));
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -70,9 +70,7 @@ use tokio::runtime::Runtime;
|
||||||
use tokio_openssl::SslAcceptorExt;
|
use tokio_openssl::SslAcceptorExt;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref HANDLE: Mutex<Runtime> = {
|
pub static ref HANDLE: Mutex<Runtime> = { Mutex::new(Runtime::new().unwrap()) };
|
||||||
Mutex::new(Runtime::new().unwrap())
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_USER_AGENT: &'static str = "Such Browser. Very Layout. Wow.";
|
const DEFAULT_USER_AGENT: &'static str = "Such Browser. Very Layout. Wow.";
|
||||||
|
@ -84,8 +82,7 @@ struct FetchResponseCollector {
|
||||||
fn create_embedder_proxy() -> EmbedderProxy {
|
fn create_embedder_proxy() -> EmbedderProxy {
|
||||||
let (sender, _) = channel();
|
let (sender, _) = channel();
|
||||||
let event_loop_waker = || {
|
let event_loop_waker = || {
|
||||||
struct DummyEventLoopWaker {
|
struct DummyEventLoopWaker {}
|
||||||
}
|
|
||||||
impl DummyEventLoopWaker {
|
impl DummyEventLoopWaker {
|
||||||
fn new() -> DummyEventLoopWaker {
|
fn new() -> DummyEventLoopWaker {
|
||||||
DummyEventLoopWaker {}
|
DummyEventLoopWaker {}
|
||||||
|
@ -103,12 +100,16 @@ fn create_embedder_proxy() -> EmbedderProxy {
|
||||||
|
|
||||||
EmbedderProxy {
|
EmbedderProxy {
|
||||||
sender: sender,
|
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 {
|
fn new_fetch_context(
|
||||||
let ssl_connector = create_ssl_connector_builder(&resources::read_string(Resource::SSLCertificates));
|
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());
|
let sender = fc.unwrap_or_else(|| create_embedder_proxy());
|
||||||
FetchContext {
|
FetchContext {
|
||||||
state: Arc::new(HttpState::new(ssl_connector)),
|
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 {
|
fn fetch_with_context(request: &mut Request, context: &FetchContext) -> Response {
|
||||||
let (sender, receiver) = channel();
|
let (sender, receiver) = channel();
|
||||||
let mut target = FetchResponseCollector {
|
let mut target = FetchResponseCollector { sender: sender };
|
||||||
sender: sender,
|
|
||||||
};
|
|
||||||
|
|
||||||
methods::fetch(request, &mut target, context);
|
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 {
|
fn fetch_with_cors_cache(request: &mut Request, cache: &mut CorsCache) -> Response {
|
||||||
let (sender, receiver) = channel();
|
let (sender, receiver) = channel();
|
||||||
let mut target = FetchResponseCollector {
|
let mut target = FetchResponseCollector { sender: sender };
|
||||||
sender: sender,
|
|
||||||
};
|
|
||||||
|
|
||||||
methods::fetch_with_cors_cache(request, cache, &mut target, &new_fetch_context(None, None));
|
methods::fetch_with_cors_cache(request, cache, &mut target, &new_fetch_context(None, None));
|
||||||
|
|
||||||
|
@ -174,16 +171,16 @@ fn make_server<H>(handler: H) -> (Server, ServoUrl)
|
||||||
let url_string = format!("http://localhost:{}", listener.local_addr().unwrap().port());
|
let url_string = format!("http://localhost:{}", listener.local_addr().unwrap().port());
|
||||||
let url = ServoUrl::parse(&url_string).unwrap();
|
let url = ServoUrl::parse(&url_string).unwrap();
|
||||||
let (tx, rx) = futures::sync::oneshot::channel::<()>();
|
let (tx, rx) = futures::sync::oneshot::channel::<()>();
|
||||||
let server = HyperServer::from_tcp(listener).unwrap().serve(
|
let server = HyperServer::from_tcp(listener)
|
||||||
move || {
|
.unwrap()
|
||||||
|
.serve(move || {
|
||||||
let handler = handler.clone();
|
let handler = handler.clone();
|
||||||
service_fn_ok(move |req: HyperRequest<Body>| {
|
service_fn_ok(move |req: HyperRequest<Body>| {
|
||||||
let mut response = HyperResponse::new(Vec::<u8>::new().into());
|
let mut response = HyperResponse::new(Vec::<u8>::new().into());
|
||||||
handler(req, &mut response);
|
handler(req, &mut response);
|
||||||
response
|
response
|
||||||
})
|
})
|
||||||
}
|
})
|
||||||
)
|
|
||||||
.with_graceful_shutdown(rx)
|
.with_graceful_shutdown(rx)
|
||||||
.map_err(|_| ());
|
.map_err(|_| ());
|
||||||
|
|
||||||
|
@ -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_string = format!("http://localhost:{}", listener.local_addr().unwrap().port());
|
||||||
let url = ServoUrl::parse(&url_string).unwrap();
|
let url = ServoUrl::parse(&url_string).unwrap();
|
||||||
|
|
||||||
let server = listener.incoming()
|
let server = listener.incoming().map_err(|_| ()).for_each(move |sock| {
|
||||||
.map_err(|_| ())
|
|
||||||
.for_each(move |sock| {
|
|
||||||
let mut ssl_builder = SslAcceptor::mozilla_modern(SslMethod::tls()).unwrap();
|
let mut ssl_builder = SslAcceptor::mozilla_modern(SslMethod::tls()).unwrap();
|
||||||
ssl_builder.set_certificate_file(&cert_path, SslFiletype::PEM).unwrap();
|
ssl_builder
|
||||||
ssl_builder.set_private_key_file(&key_path, SslFiletype::PEM).unwrap();
|
.set_certificate_file(&cert_path, SslFiletype::PEM)
|
||||||
|
.unwrap();
|
||||||
|
ssl_builder
|
||||||
|
.set_private_key_file(&key_path, SslFiletype::PEM)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let handler = handler.clone();
|
let handler = handler.clone();
|
||||||
ssl_builder.build().accept_async(sock).map_err(|_| ()).and_then(move |ssl| {
|
ssl_builder
|
||||||
Http::new().serve_connection(ssl,
|
.build()
|
||||||
|
.accept_async(sock)
|
||||||
|
.map_err(|_| ())
|
||||||
|
.and_then(move |ssl| {
|
||||||
|
Http::new()
|
||||||
|
.serve_connection(
|
||||||
|
ssl,
|
||||||
service_fn_ok(move |req: HyperRequest<Body>| {
|
service_fn_ok(move |req: HyperRequest<Body>| {
|
||||||
let mut response = HyperResponse::new(Vec::<u8>::new().into());
|
let mut response = HyperResponse::new(Vec::<u8>::new().into());
|
||||||
handler(req, &mut response);
|
handler(req, &mut response);
|
||||||
response
|
response
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.map_err(|_| ())
|
.map_err(|_| ())
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let (tx, rx) = futures::sync::oneshot::channel::<()>();
|
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);
|
HANDLE.lock().unwrap().spawn(server);
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ fn test_sniff_mp4_matcher() {
|
||||||
panic!("Didn't read mime type")
|
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 matcher = Mp4Matcher;
|
||||||
|
|
||||||
let mut data: [u8; 260] = [0; 260];
|
let mut data: [u8; 260] = [0; 260];
|
||||||
&data[.. 11].clone_from_slice(
|
&data[..11].clone_from_slice(&[
|
||||||
&[0x00, 0x00, 0x01, 0x04, 0x66, 0x74, 0x79, 0x70, 0x6D, 0x70, 0x34]
|
0x00, 0x00, 0x01, 0x04, 0x66, 0x74, 0x79, 0x70, 0x6D, 0x70, 0x34,
|
||||||
);
|
]);
|
||||||
|
|
||||||
assert!(matcher.matches(&data));
|
assert!(matcher.matches(&data));
|
||||||
}
|
}
|
||||||
|
@ -57,13 +57,18 @@ fn test_validate_classifier() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn test_sniff_with_flags(filename_orig: &path::Path,
|
fn test_sniff_with_flags(
|
||||||
|
filename_orig: &path::Path,
|
||||||
expected_mime: Mime,
|
expected_mime: Mime,
|
||||||
supplied_type: Option<Mime>,
|
supplied_type: Option<Mime>,
|
||||||
no_sniff_flag: NoSniffFlag,
|
no_sniff_flag: NoSniffFlag,
|
||||||
apache_bug_flag: ApacheBugFlag) {
|
apache_bug_flag: ApacheBugFlag,
|
||||||
|
) {
|
||||||
let current_working_directory = env::current_dir().unwrap();
|
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/");
|
let mut filename = PathBuf::from("tests/parsable_mime/");
|
||||||
filename.push(filename_orig);
|
filename.push(filename_orig);
|
||||||
|
@ -74,30 +79,35 @@ fn test_sniff_with_flags(filename_orig: &path::Path,
|
||||||
|
|
||||||
match read_result {
|
match read_result {
|
||||||
Ok(data) => {
|
Ok(data) => {
|
||||||
let parsed_mime = classifier.classify(LoadContext::Browsing,
|
let parsed_mime = classifier.classify(
|
||||||
|
LoadContext::Browsing,
|
||||||
no_sniff_flag,
|
no_sniff_flag,
|
||||||
apache_bug_flag,
|
apache_bug_flag,
|
||||||
&supplied_type,
|
&supplied_type,
|
||||||
&data);
|
&data,
|
||||||
|
);
|
||||||
if (parsed_mime.type_() != expected_mime.type_()) ||
|
if (parsed_mime.type_() != expected_mime.type_()) ||
|
||||||
(parsed_mime.subtype() != expected_mime.subtype()) {
|
(parsed_mime.subtype() != expected_mime.subtype())
|
||||||
panic!("File {:?} parsed incorrectly should be {:?}, parsed as {:?}",
|
{
|
||||||
filename, expected_mime, parsed_mime);
|
panic!(
|
||||||
|
"File {:?} parsed incorrectly should be {:?}, parsed as {:?}",
|
||||||
|
filename, expected_mime, parsed_mime
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
Err(e) => panic!("Couldn't read from file {:?} with error {}",
|
Err(e) => panic!("Couldn't read from file {:?} with error {}", filename, e),
|
||||||
filename, e),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn test_sniff_full(filename_orig: &path::Path, expected_mime: Mime,
|
fn test_sniff_full(filename_orig: &path::Path, expected_mime: Mime, supplied_type: Option<Mime>) {
|
||||||
supplied_type: Option<Mime>) {
|
test_sniff_with_flags(
|
||||||
test_sniff_with_flags(filename_orig,
|
filename_orig,
|
||||||
expected_mime,
|
expected_mime,
|
||||||
supplied_type,
|
supplied_type,
|
||||||
NoSniffFlag::Off,
|
NoSniffFlag::Off,
|
||||||
ApacheBugFlag::Off)
|
ApacheBugFlag::Off,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -198,32 +208,50 @@ fn test_sniff_wave() {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sniff_ogg() {
|
fn test_sniff_ogg() {
|
||||||
test_sniff_classification("small.ogg", "application/ogg".parse().unwrap(), None);
|
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]
|
#[test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn test_sniff_vsn_ms_fontobject() {
|
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]
|
#[test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn test_sniff_true_type() {
|
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]
|
#[test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn test_sniff_open_type() {
|
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]
|
#[test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn test_sniff_true_type_collection() {
|
fn test_sniff_true_type_collection() {
|
||||||
test_sniff_full(&PathBuf::from("unknown/true_type_collection.ttc"), "(TrueType Collection)/".parse().unwrap(),
|
test_sniff_full(
|
||||||
None);
|
&PathBuf::from("unknown/true_type_collection.ttc"),
|
||||||
|
"(TrueType Collection)/".parse().unwrap(),
|
||||||
|
None,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -244,7 +272,11 @@ fn test_sniff_zip() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sniff_rar() {
|
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]
|
#[test]
|
||||||
|
@ -466,86 +498,130 @@ fn test_sniff_utf_8_bom() {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sniff_rss_feed() {
|
fn test_sniff_rss_feed() {
|
||||||
// RSS feeds
|
// RSS feeds
|
||||||
test_sniff_full(&PathBuf::from("text/xml/feed.rss"), "application/rss+xml".parse().unwrap(), Some(mime::TEXT_HTML));
|
test_sniff_full(
|
||||||
test_sniff_full(&PathBuf::from("text/xml/rdf_rss.xml"), "application/rss+xml".parse().unwrap(),
|
&PathBuf::from("text/xml/feed.rss"),
|
||||||
Some(mime::TEXT_HTML));
|
"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
|
// 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(
|
||||||
test_sniff_full(&PathBuf::from("text/xml/rdf_rss_ko_2.xml"), mime::TEXT_HTML, Some(mime::TEXT_HTML));
|
&PathBuf::from("text/xml/rdf_rss_ko_1.xml"),
|
||||||
test_sniff_full(&PathBuf::from("text/xml/rdf_rss_ko_3.xml"), mime::TEXT_HTML, Some(mime::TEXT_HTML));
|
mime::TEXT_HTML,
|
||||||
test_sniff_full(&PathBuf::from("text/xml/rdf_rss_ko_4.xml"), mime::TEXT_HTML, Some(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]
|
#[test]
|
||||||
fn test_sniff_atom_feed() {
|
fn test_sniff_atom_feed() {
|
||||||
test_sniff_full(&PathBuf::from("text/xml/feed.atom"), "application/atom+xml".parse().unwrap(),
|
test_sniff_full(
|
||||||
Some(mime::TEXT_HTML));
|
&PathBuf::from("text/xml/feed.atom"),
|
||||||
|
"application/atom+xml".parse().unwrap(),
|
||||||
|
Some(mime::TEXT_HTML),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sniff_binary_file() {
|
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]
|
#[test]
|
||||||
fn test_sniff_atom_feed_with_no_sniff_flag_on() {
|
fn test_sniff_atom_feed_with_no_sniff_flag_on() {
|
||||||
test_sniff_with_flags(&PathBuf::from("text/xml/feed.atom"),
|
test_sniff_with_flags(
|
||||||
|
&PathBuf::from("text/xml/feed.atom"),
|
||||||
mime::TEXT_HTML,
|
mime::TEXT_HTML,
|
||||||
Some(mime::TEXT_HTML),
|
Some(mime::TEXT_HTML),
|
||||||
NoSniffFlag::On,
|
NoSniffFlag::On,
|
||||||
ApacheBugFlag::Off);
|
ApacheBugFlag::Off,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sniff_with_no_sniff_flag_on_and_apache_flag_on() {
|
fn test_sniff_with_no_sniff_flag_on_and_apache_flag_on() {
|
||||||
test_sniff_with_flags(&PathBuf::from("text/xml/feed.atom"),
|
test_sniff_with_flags(
|
||||||
|
&PathBuf::from("text/xml/feed.atom"),
|
||||||
mime::TEXT_HTML,
|
mime::TEXT_HTML,
|
||||||
Some(mime::TEXT_HTML),
|
Some(mime::TEXT_HTML),
|
||||||
NoSniffFlag::On,
|
NoSniffFlag::On,
|
||||||
ApacheBugFlag::On);
|
ApacheBugFlag::On,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sniff_utf_8_bom_with_apache_flag_on() {
|
fn test_sniff_utf_8_bom_with_apache_flag_on() {
|
||||||
test_sniff_with_flags(&PathBuf::from("text/plain/utf8bom.txt"),
|
test_sniff_with_flags(
|
||||||
|
&PathBuf::from("text/plain/utf8bom.txt"),
|
||||||
mime::TEXT_PLAIN,
|
mime::TEXT_PLAIN,
|
||||||
Some("dummy/text".parse().unwrap()),
|
Some("dummy/text".parse().unwrap()),
|
||||||
NoSniffFlag::Off,
|
NoSniffFlag::Off,
|
||||||
ApacheBugFlag::On);
|
ApacheBugFlag::On,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sniff_utf_16be_bom_with_apache_flag_on() {
|
fn test_sniff_utf_16be_bom_with_apache_flag_on() {
|
||||||
test_sniff_with_flags(&PathBuf::from("text/plain/utf16bebom.txt"),
|
test_sniff_with_flags(
|
||||||
|
&PathBuf::from("text/plain/utf16bebom.txt"),
|
||||||
mime::TEXT_PLAIN,
|
mime::TEXT_PLAIN,
|
||||||
Some("dummy/text".parse().unwrap()),
|
Some("dummy/text".parse().unwrap()),
|
||||||
NoSniffFlag::Off,
|
NoSniffFlag::Off,
|
||||||
ApacheBugFlag::On);
|
ApacheBugFlag::On,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sniff_utf_16le_bom_with_apache_flag_on() {
|
fn test_sniff_utf_16le_bom_with_apache_flag_on() {
|
||||||
test_sniff_with_flags(&PathBuf::from("text/plain/utf16lebom.txt"),
|
test_sniff_with_flags(
|
||||||
|
&PathBuf::from("text/plain/utf16lebom.txt"),
|
||||||
mime::TEXT_PLAIN,
|
mime::TEXT_PLAIN,
|
||||||
Some("dummy/text".parse().unwrap()),
|
Some("dummy/text".parse().unwrap()),
|
||||||
NoSniffFlag::Off,
|
NoSniffFlag::Off,
|
||||||
ApacheBugFlag::On);
|
ApacheBugFlag::On,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sniff_octet_stream_apache_flag_on() {
|
fn test_sniff_octet_stream_apache_flag_on() {
|
||||||
test_sniff_with_flags(&PathBuf::from("unknown/binary_file"),
|
test_sniff_with_flags(
|
||||||
|
&PathBuf::from("unknown/binary_file"),
|
||||||
mime::APPLICATION_OCTET_STREAM,
|
mime::APPLICATION_OCTET_STREAM,
|
||||||
Some("dummy/binary".parse().unwrap()),
|
Some("dummy/binary".parse().unwrap()),
|
||||||
NoSniffFlag::Off,
|
NoSniffFlag::Off,
|
||||||
ApacheBugFlag::On);
|
ApacheBugFlag::On,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sniff_mp4_video_apache_flag_on() {
|
fn test_sniff_mp4_video_apache_flag_on() {
|
||||||
test_sniff_with_flags(&PathBuf::from("video/mp4/test.mp4"),
|
test_sniff_with_flags(
|
||||||
|
&PathBuf::from("video/mp4/test.mp4"),
|
||||||
mime::APPLICATION_OCTET_STREAM,
|
mime::APPLICATION_OCTET_STREAM,
|
||||||
Some("video/mp4".parse().unwrap()),
|
Some("video/mp4".parse().unwrap()),
|
||||||
NoSniffFlag::Off,
|
NoSniffFlag::Off,
|
||||||
ApacheBugFlag::On);
|
ApacheBugFlag::On,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,13 @@ fn test_exit() {
|
||||||
let (mtx, _mrx) = ipc::channel().unwrap();
|
let (mtx, _mrx) = ipc::channel().unwrap();
|
||||||
let (sender, receiver) = ipc::channel().unwrap();
|
let (sender, receiver) = ipc::channel().unwrap();
|
||||||
let (resource_thread, _private_resource_thread) = new_core_resource_thread(
|
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();
|
resource_thread.send(CoreResourceMsg::Exit(sender)).unwrap();
|
||||||
receiver.recv().unwrap();
|
receiver.recv().unwrap();
|
||||||
}
|
}
|
||||||
|
@ -32,12 +38,16 @@ fn test_parse_hostsfile() {
|
||||||
let hosts_table = parse_hostsfile(mock_hosts_file_content);
|
let hosts_table = parse_hostsfile(mock_hosts_file_content);
|
||||||
assert_eq!(2, hosts_table.len());
|
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.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]
|
#[test]
|
||||||
fn test_parse_malformed_hostsfile() {
|
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);
|
let hosts_table = parse_hostsfile(mock_hosts_file_content);
|
||||||
assert_eq!(1, hosts_table.len());
|
assert_eq!(1, hosts_table.len());
|
||||||
assert_eq!(ip("127.0.0.1"), *hosts_table.get("foo.bar.com").unwrap());
|
assert_eq!(ip("127.0.0.1"), *hosts_table.get("foo.bar.com").unwrap());
|
||||||
|
@ -45,7 +55,8 @@ fn test_parse_malformed_hostsfile() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_hostsfile_with_line_comment() {
|
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);
|
let hosts_table = parse_hostsfile(mock_hosts_file_content);
|
||||||
assert_eq!(1, hosts_table.len());
|
assert_eq!(1, hosts_table.len());
|
||||||
assert_eq!(ip("127.0.0.1"), *hosts_table.get("foo.bar.com").unwrap());
|
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]
|
#[test]
|
||||||
fn test_parse_hostsfile_with_end_of_line_comment() {
|
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);
|
let hosts_table = parse_hostsfile(mock_hosts_file_content);
|
||||||
assert_eq!(2, hosts_table.len());
|
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.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]
|
#[test]
|
||||||
|
@ -86,12 +101,14 @@ fn test_parse_hostsfile_with_tabs_instead_spaces() {
|
||||||
let hosts_table = parse_hostsfile(mock_hosts_file_content);
|
let hosts_table = parse_hostsfile(mock_hosts_file_content);
|
||||||
assert_eq!(2, hosts_table.len());
|
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.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]
|
#[test]
|
||||||
fn test_parse_hostsfile_with_valid_ipv4_addresses()
|
fn test_parse_hostsfile_with_valid_ipv4_addresses() {
|
||||||
{
|
|
||||||
let mock_hosts_file_content =
|
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";
|
"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);
|
let hosts_table = parse_hostsfile(mock_hosts_file_content);
|
||||||
|
@ -99,8 +116,7 @@ fn test_parse_hostsfile_with_valid_ipv4_addresses()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[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 \
|
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";
|
\n192.168.5.500 servo.foo.com\n192.abc.100.2 test.servo.com";
|
||||||
let hosts_table = parse_hostsfile(mock_hosts_file_content);
|
let hosts_table = parse_hostsfile(mock_hosts_file_content);
|
||||||
|
@ -108,8 +124,7 @@ fn test_parse_hostsfile_with_invalid_ipv4_addresses()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[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\
|
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:0:0:0:ff00:42:8329 moz.foo.com\n\
|
||||||
2001:db8::ff00:42:8329 foo.moz.com moz.moz.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]
|
#[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\
|
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\
|
2001:zdb8:0:0:0:gg00:42:t329 moz.foo.com\n\
|
||||||
2002:0DB8:85A3:0042:1000:8A2E:0370:7334/1289 baz3.bar.moz";
|
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]
|
#[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\
|
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 \
|
2001:db8:0:0:0:ff00:42:8329 moz.foo.com\n \
|
||||||
127.0.0.2 servo.test.server ";
|
127.0.0.2 servo.test.server ";
|
||||||
let hosts_table = parse_hostsfile(mock_hosts_file_content);
|
let hosts_table = parse_hostsfile(mock_hosts_file_content);
|
||||||
assert_eq!(3, hosts_table.len());
|
assert_eq!(3, hosts_table.len());
|
||||||
assert_eq!(ip("127.0.0.1"), *hosts_table.get("foo.bar.com").unwrap());
|
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!(
|
||||||
assert_eq!(ip("127.0.0.2"), *hosts_table.get("servo.test.server").unwrap());
|
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()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,8 @@ fn test_response_integrity_valid() {
|
||||||
let url: ServoUrl = ServoUrl::parse("http://servo.org").unwrap();
|
let url: ServoUrl = ServoUrl::parse("http://servo.org").unwrap();
|
||||||
let response: Response = Response::new(url);
|
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();
|
let response_body = "alert('Hello, world.');".to_owned().into_bytes();
|
||||||
|
|
||||||
*response.body.lock().unwrap() = ResponseBody::Done(response_body);
|
*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 url: ServoUrl = ServoUrl::parse("http://servo.org").unwrap();
|
||||||
let response: Response = Response::new(url);
|
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();
|
let response_body = "alert('Hello, world.');".to_owned().into_bytes();
|
||||||
|
|
||||||
*response.body.lock().unwrap() = ResponseBody::Done(response_body);
|
*response.body.lock().unwrap() = ResponseBody::Done(response_body);
|
||||||
|
|
|
@ -51,20 +51,25 @@ impl<'a> Factory for Client<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<'a> Handler for Client<'a> {
|
impl<'a> Handler for Client<'a> {
|
||||||
fn build_request(&mut self, url: &Url) -> WebSocketResult<Request> {
|
fn build_request(&mut self, url: &Url) -> WebSocketResult<Request> {
|
||||||
let mut req = Request::from_url(url)?;
|
let mut req = Request::from_url(url)?;
|
||||||
req.headers_mut().push(("Origin".to_string(), self.origin.as_bytes().to_owned()));
|
req.headers_mut()
|
||||||
req.headers_mut().push(("Host".to_string(), format!("{}", self.host).as_bytes().to_owned()));
|
.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 {
|
for protocol in self.protocols {
|
||||||
req.add_protocol(protocol);
|
req.add_protocol(protocol);
|
||||||
};
|
}
|
||||||
|
|
||||||
let mut cookie_jar = self.http_state.cookie_jar.write().unwrap();
|
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) {
|
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()))
|
{
|
||||||
|
req.headers_mut()
|
||||||
|
.push(("Cookie".into(), cookie_list.as_bytes().to_owned()))
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(req)
|
Ok(req)
|
||||||
|
@ -83,14 +88,19 @@ impl<'a> Handler for Client<'a> {
|
||||||
// TODO(eijebong): Replace thise once typed headers settled on a cookie impl
|
// TODO(eijebong): Replace thise once typed headers settled on a cookie impl
|
||||||
for cookie in headers.get_all(header::SET_COOKIE) {
|
for cookie in headers.get_all(header::SET_COOKIE) {
|
||||||
if let Ok(s) = cookie.to_str() {
|
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);
|
jar.push(cookie, self.resource_url, CookieSource::HTTP);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = self.event_sender.send(
|
let _ = self
|
||||||
WebSocketNetworkEvent::ConnectionEstablished { protocol_in_use: self.protocol_in_use.clone() });
|
.event_sender
|
||||||
|
.send(WebSocketNetworkEvent::ConnectionEstablished {
|
||||||
|
protocol_in_use: self.protocol_in_use.clone(),
|
||||||
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,25 +109,27 @@ impl<'a> Handler for Client<'a> {
|
||||||
Message::Text(message) => MessageData::Text(message),
|
Message::Text(message) => MessageData::Text(message),
|
||||||
Message::Binary(message) => MessageData::Binary(message),
|
Message::Binary(message) => MessageData::Binary(message),
|
||||||
};
|
};
|
||||||
let _ = self.event_sender.send(WebSocketNetworkEvent::MessageReceived(message));
|
let _ = self
|
||||||
|
.event_sender
|
||||||
|
.send(WebSocketNetworkEvent::MessageReceived(message));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn on_error(&mut self, err: WebSocketError) {
|
fn on_error(&mut self, err: WebSocketError) {
|
||||||
debug!("Error in WebSocket communication: {:?}", err);
|
debug!("Error in WebSocket communication: {:?}", err);
|
||||||
let _ = self.event_sender.send(WebSocketNetworkEvent::Fail);
|
let _ = self.event_sender.send(WebSocketNetworkEvent::Fail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn on_response(&mut self, res: &WsResponse) -> WebSocketResult<()> {
|
fn on_response(&mut self, res: &WsResponse) -> WebSocketResult<()> {
|
||||||
let protocol_in_use = res.protocol()?;
|
let protocol_in_use = res.protocol()?;
|
||||||
|
|
||||||
if let Some(protocol_name) = protocol_in_use {
|
if let Some(protocol_name) = protocol_in_use {
|
||||||
if !self.protocols.is_empty() && !self.protocols.iter().any(|p| protocol_name == (*p)) {
|
if !self.protocols.is_empty() && !self.protocols.iter().any(|p| protocol_name == (*p)) {
|
||||||
let error = WebSocketError::new(WebSocketErrorKind::Protocol,
|
let error = WebSocketError::new(
|
||||||
"Protocol in Use not in client-supplied protocol list");
|
WebSocketErrorKind::Protocol,
|
||||||
|
"Protocol in Use not in client-supplied protocol list",
|
||||||
|
);
|
||||||
return Err(error);
|
return Err(error);
|
||||||
}
|
}
|
||||||
self.protocol_in_use = Some(protocol_name.into());
|
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) {
|
fn on_close(&mut self, code: CloseCode, reason: &str) {
|
||||||
debug!("Connection closing due to ({:?}) {}", code, reason);
|
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(
|
fn upgrade_ssl_client(
|
||||||
|
@ -136,31 +151,34 @@ impl<'a> Handler for Client<'a> {
|
||||||
url: &Url,
|
url: &Url,
|
||||||
) -> WebSocketResult<SslStream<TcpStream>> {
|
) -> WebSocketResult<SslStream<TcpStream>> {
|
||||||
let certs = match opts::get().certificate_path {
|
let certs = match opts::get().certificate_path {
|
||||||
Some(ref path) => {
|
Some(ref path) => fs::read_to_string(path).expect("Couldn't not find certificate file"),
|
||||||
fs::read_to_string(path).expect("Couldn't not find certificate file")
|
None => resources::read_string(Resource::SSLCertificates),
|
||||||
}
|
|
||||||
None => {
|
|
||||||
resources::read_string(Resource::SSLCertificates)
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let domain = self.resource_url.as_url().domain().ok_or(WebSocketError::new(
|
let domain = self
|
||||||
|
.resource_url
|
||||||
|
.as_url()
|
||||||
|
.domain()
|
||||||
|
.ok_or(WebSocketError::new(
|
||||||
WebSocketErrorKind::Protocol,
|
WebSocketErrorKind::Protocol,
|
||||||
format!("Unable to parse domain from {}. Needed for SSL.", url),
|
format!("Unable to parse domain from {}. Needed for SSL.", url),
|
||||||
))?;
|
))?;
|
||||||
let connector = create_ssl_connector_builder(&certs).build();
|
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(
|
pub fn init(
|
||||||
req_init: RequestInit,
|
req_init: RequestInit,
|
||||||
resource_event_sender: IpcSender<WebSocketNetworkEvent>,
|
resource_event_sender: IpcSender<WebSocketNetworkEvent>,
|
||||||
dom_action_receiver: IpcReceiver<WebSocketDomAction>,
|
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 || {
|
thread::Builder::new()
|
||||||
|
.name(format!("WebSocket connection to {}", req_init.url))
|
||||||
|
.spawn(move || {
|
||||||
let protocols = match req_init.mode {
|
let protocols = match req_init.mode {
|
||||||
RequestMode::WebSocket { protocols } => protocols.clone(),
|
RequestMode::WebSocket { protocols } => protocols.clone(),
|
||||||
_ => panic!("Received a RequestInit with a non-websocket mode in websocket_loader"),
|
_ => panic!("Received a RequestInit with a non-websocket mode in websocket_loader"),
|
||||||
|
@ -185,9 +203,17 @@ pub fn init(
|
||||||
net_url.set_host(Some(&host)).unwrap();
|
net_url.set_host(Some(&host)).unwrap();
|
||||||
|
|
||||||
let host = Host::from(
|
let host = Host::from(
|
||||||
format!("{}{}", req_init.url.host_str().unwrap(),
|
format!(
|
||||||
req_init.url.port_or_known_default().map(|v| format!(":{}", v)).unwrap_or("".into())
|
"{}{}",
|
||||||
).parse::<Authority>().unwrap()
|
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 {
|
let client = Client {
|
||||||
|
@ -221,9 +247,12 @@ pub fn init(
|
||||||
WebSocketDomAction::Close(code, reason) => {
|
WebSocketDomAction::Close(code, reason) => {
|
||||||
if !initiated_close.fetch_or(true, Ordering::SeqCst) {
|
if !initiated_close.fetch_or(true, Ordering::SeqCst) {
|
||||||
match code {
|
match code {
|
||||||
Some(code) => {
|
Some(code) => ws_sender
|
||||||
ws_sender.close_with_reason(code.into(), reason.unwrap_or("".to_owned())).unwrap()
|
.close_with_reason(
|
||||||
},
|
code.into(),
|
||||||
|
reason.unwrap_or("".to_owned()),
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
None => ws_sender.close(CloseCode::Status).unwrap(),
|
None => ws_sender.close(CloseCode::Status).unwrap(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -236,6 +265,6 @@ pub fn init(
|
||||||
debug!("Failed to run WebSocket: {:?}", e);
|
debug!("Failed to run WebSocket: {:?}", e);
|
||||||
let _ = resource_event_sender.send(WebSocketNetworkEvent::Fail);
|
let _ = resource_event_sender.send(WebSocketNetworkEvent::Fail);
|
||||||
};
|
};
|
||||||
}).expect("Thread spawning failed");
|
})
|
||||||
|
.expect("Thread spawning failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue