mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
Process content blocker rules in the HTTP loader.
This commit is contained in:
parent
50fea8554e
commit
074fc4a5e1
5 changed files with 105 additions and 4 deletions
|
@ -4,7 +4,8 @@
|
||||||
|
|
||||||
use brotli::Decompressor;
|
use brotli::Decompressor;
|
||||||
use connector::Connector;
|
use connector::Connector;
|
||||||
use content_blocker_parser::RuleList;
|
use content_blocker_parser::{LoadType, Reaction, Request as CBRequest, ResourceType};
|
||||||
|
use content_blocker_parser::{RuleList, process_rules_for_request};
|
||||||
use cookie;
|
use cookie;
|
||||||
use cookie_storage::CookieStorage;
|
use cookie_storage::CookieStorage;
|
||||||
use devtools_traits::{ChromeToDevtoolsControlMsg, DevtoolsControlMsg, HttpRequest as DevtoolsHttpRequest};
|
use devtools_traits::{ChromeToDevtoolsControlMsg, DevtoolsControlMsg, HttpRequest as DevtoolsHttpRequest};
|
||||||
|
@ -329,6 +330,7 @@ pub enum LoadErrorType {
|
||||||
Cancelled,
|
Cancelled,
|
||||||
Connection { reason: String },
|
Connection { reason: String },
|
||||||
ConnectionAborted { reason: String },
|
ConnectionAborted { reason: String },
|
||||||
|
ContentBlocked,
|
||||||
// Preflight fetch inconsistent with main fetch
|
// Preflight fetch inconsistent with main fetch
|
||||||
CorsPreflightFetchInconsistent,
|
CorsPreflightFetchInconsistent,
|
||||||
Decoding { reason: String },
|
Decoding { reason: String },
|
||||||
|
@ -351,6 +353,7 @@ impl Error for LoadErrorType {
|
||||||
LoadErrorType::Cancelled => "load cancelled",
|
LoadErrorType::Cancelled => "load cancelled",
|
||||||
LoadErrorType::Connection { ref reason } => reason,
|
LoadErrorType::Connection { ref reason } => reason,
|
||||||
LoadErrorType::ConnectionAborted { ref reason } => reason,
|
LoadErrorType::ConnectionAborted { ref reason } => reason,
|
||||||
|
LoadErrorType::ContentBlocked => "content blocked",
|
||||||
LoadErrorType::CorsPreflightFetchInconsistent => "preflight fetch inconsistent with main fetch",
|
LoadErrorType::CorsPreflightFetchInconsistent => "preflight fetch inconsistent with main fetch",
|
||||||
LoadErrorType::Decoding { ref reason } => reason,
|
LoadErrorType::Decoding { ref reason } => reason,
|
||||||
LoadErrorType::InvalidRedirect { ref reason } => reason,
|
LoadErrorType::InvalidRedirect { ref reason } => reason,
|
||||||
|
@ -592,7 +595,8 @@ pub fn modify_request_headers(headers: &mut Headers,
|
||||||
user_agent: &str,
|
user_agent: &str,
|
||||||
cookie_jar: &Arc<RwLock<CookieStorage>>,
|
cookie_jar: &Arc<RwLock<CookieStorage>>,
|
||||||
auth_cache: &Arc<RwLock<AuthCache>>,
|
auth_cache: &Arc<RwLock<AuthCache>>,
|
||||||
load_data: &LoadData) {
|
load_data: &LoadData,
|
||||||
|
block_cookies: bool) {
|
||||||
// Ensure that the host header is set from the original url
|
// Ensure that the host header is set from the original url
|
||||||
let host = Host {
|
let host = Host {
|
||||||
hostname: url.host_str().unwrap().to_owned(),
|
hostname: url.host_str().unwrap().to_owned(),
|
||||||
|
@ -622,7 +626,9 @@ pub fn modify_request_headers(headers: &mut Headers,
|
||||||
|
|
||||||
// https://fetch.spec.whatwg.org/#concept-http-network-or-cache-fetch step 11
|
// https://fetch.spec.whatwg.org/#concept-http-network-or-cache-fetch step 11
|
||||||
if load_data.credentials_flag {
|
if load_data.credentials_flag {
|
||||||
set_request_cookies(url.clone(), headers, cookie_jar);
|
if !block_cookies {
|
||||||
|
set_request_cookies(url.clone(), headers, cookie_jar);
|
||||||
|
}
|
||||||
|
|
||||||
// https://fetch.spec.whatwg.org/#http-network-or-cache-fetch step 12
|
// https://fetch.spec.whatwg.org/#http-network-or-cache-fetch step 12
|
||||||
set_auth_header(headers, url, auth_cache);
|
set_auth_header(headers, url, auth_cache);
|
||||||
|
@ -854,6 +860,24 @@ pub fn load<A, B>(load_data: &LoadData,
|
||||||
return Err(LoadError::new(doc_url, LoadErrorType::Cancelled));
|
return Err(LoadError::new(doc_url, LoadErrorType::Cancelled));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut block_cookies = false;
|
||||||
|
if let Some(ref rules) = *http_state.blocked_content {
|
||||||
|
let actions = process_rules_for_request(rules, &CBRequest {
|
||||||
|
url: &doc_url,
|
||||||
|
resource_type: to_resource_type(&load_data.context),
|
||||||
|
load_type: LoadType::FirstParty, //FIXME need request origin
|
||||||
|
});
|
||||||
|
for action in actions {
|
||||||
|
match action {
|
||||||
|
Reaction::Block => {
|
||||||
|
return Err(LoadError::new(doc_url, LoadErrorType::ContentBlocked));
|
||||||
|
},
|
||||||
|
Reaction::BlockCookies => block_cookies = true,
|
||||||
|
Reaction::HideMatchingElements(_) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
info!("requesting {}", doc_url);
|
info!("requesting {}", doc_url);
|
||||||
|
|
||||||
// Avoid automatically preserving request headers when redirects occur.
|
// Avoid automatically preserving request headers when redirects occur.
|
||||||
|
@ -872,7 +896,7 @@ pub fn load<A, B>(load_data: &LoadData,
|
||||||
|
|
||||||
modify_request_headers(&mut request_headers, &doc_url,
|
modify_request_headers(&mut request_headers, &doc_url,
|
||||||
&user_agent, &http_state.cookie_jar,
|
&user_agent, &http_state.cookie_jar,
|
||||||
&http_state.auth_cache, &load_data);
|
&http_state.auth_cache, &load_data, block_cookies);
|
||||||
|
|
||||||
//if there is a new auth header then set the request headers with it
|
//if there is a new auth header then set the request headers with it
|
||||||
if let Some(ref auth_header) = new_auth_header {
|
if let Some(ref auth_header) = new_auth_header {
|
||||||
|
@ -1034,3 +1058,17 @@ fn is_cert_verify_error(error: &OpensslError) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn to_resource_type(context: &LoadContext) -> ResourceType {
|
||||||
|
match *context {
|
||||||
|
LoadContext::Browsing => ResourceType::Document,
|
||||||
|
LoadContext::Image => ResourceType::Image,
|
||||||
|
LoadContext::AudioVideo => ResourceType::Media,
|
||||||
|
LoadContext::Plugin => ResourceType::Raw,
|
||||||
|
LoadContext::Style => ResourceType::StyleSheet,
|
||||||
|
LoadContext::Script => ResourceType::Script,
|
||||||
|
LoadContext::Font => ResourceType::Font,
|
||||||
|
LoadContext::TextTrack => ResourceType::Media,
|
||||||
|
LoadContext::CacheManifest => ResourceType::Raw,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
1
components/servo/Cargo.lock
generated
1
components/servo/Cargo.lock
generated
|
@ -1414,6 +1414,7 @@ dependencies = [
|
||||||
name = "net_tests"
|
name = "net_tests"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"content-blocker 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"cookie 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cookie 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"devtools_traits 0.0.1",
|
"devtools_traits 0.0.1",
|
||||||
"flate2 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"flate2 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
|
@ -9,6 +9,7 @@ path = "lib.rs"
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
content-blocker = "0.2"
|
||||||
cookie = "0.2"
|
cookie = "0.2"
|
||||||
devtools_traits = {path = "../../../components/devtools_traits"}
|
devtools_traits = {path = "../../../components/devtools_traits"}
|
||||||
flate2 = "0.2.0"
|
flate2 = "0.2.0"
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use content_blocker::parse_list;
|
||||||
use cookie_rs::Cookie as CookiePair;
|
use cookie_rs::Cookie as CookiePair;
|
||||||
use devtools_traits::HttpRequest as DevtoolsHttpRequest;
|
use devtools_traits::HttpRequest as DevtoolsHttpRequest;
|
||||||
use devtools_traits::HttpResponse as DevtoolsHttpResponse;
|
use devtools_traits::HttpResponse as DevtoolsHttpResponse;
|
||||||
|
@ -1858,3 +1859,62 @@ fn test_custom_response_from_worker() {
|
||||||
assert_eq!(metadata.status, Some(RawStatus(200, Cow::Borrowed("OK"))));
|
assert_eq!(metadata.status, Some(RawStatus(200, Cow::Borrowed("OK"))));
|
||||||
assert_eq!(body, String::from_utf8(expected_body).unwrap());
|
assert_eq!(body, String::from_utf8(expected_body).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_content_blocked() {
|
||||||
|
struct Factory;
|
||||||
|
impl HttpRequestFactory for Factory {
|
||||||
|
type R = MockRequest;
|
||||||
|
|
||||||
|
fn create(&self, _url: Url, _method: Method, _: Headers) -> Result<MockRequest, LoadError> {
|
||||||
|
Ok(MockRequest::new(ResponseType::Text(<[_]>::to_vec("Yay!".as_bytes()))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let blocked_url = Url::parse("http://mozilla.com").unwrap();
|
||||||
|
let url_without_cookies = Url::parse("http://mozilla2.com").unwrap();
|
||||||
|
let mut http_state = HttpState::new();
|
||||||
|
|
||||||
|
let blocked_content_list = "[{ \"trigger\": { \"url-filter\": \"https?://mozilla.com\" }, \
|
||||||
|
\"action\": { \"type\": \"block\" } },\
|
||||||
|
{ \"trigger\": { \"url-filter\": \"https?://mozilla2.com\" }, \
|
||||||
|
\"action\": { \"type\": \"block-cookies\" } }]";
|
||||||
|
http_state.blocked_content = Arc::new(parse_list(blocked_content_list).ok());
|
||||||
|
assert!(http_state.blocked_content.is_some());
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut cookie_jar = http_state.cookie_jar.write().unwrap();
|
||||||
|
let cookie = Cookie::new_wrapped(
|
||||||
|
CookiePair::parse("mozillaIs=theBest;").unwrap(),
|
||||||
|
&url_without_cookies,
|
||||||
|
CookieSource::HTTP
|
||||||
|
).unwrap();
|
||||||
|
cookie_jar.push(cookie, CookieSource::HTTP);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ui_provider = TestProvider::new();
|
||||||
|
|
||||||
|
let load_data = LoadData::new(LoadContext::Browsing, url_without_cookies, &HttpTest);
|
||||||
|
|
||||||
|
let response = load(
|
||||||
|
&load_data, &ui_provider, &http_state,
|
||||||
|
None, &AssertMustNotIncludeHeadersRequestFactory {
|
||||||
|
headers_not_expected: vec!["Cookie".to_owned()],
|
||||||
|
body: b"hi".to_vec(),
|
||||||
|
}, DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None));
|
||||||
|
match response {
|
||||||
|
Ok(_) => {},
|
||||||
|
_ => panic!("request should have succeeded without cookies"),
|
||||||
|
}
|
||||||
|
|
||||||
|
let load_data = LoadData::new(LoadContext::Browsing, blocked_url, &HttpTest);
|
||||||
|
|
||||||
|
let response = load(
|
||||||
|
&load_data, &ui_provider, &http_state,
|
||||||
|
None, &Factory,
|
||||||
|
DEFAULT_USER_AGENT.to_owned(), &CancellationListener::new(None));
|
||||||
|
match response {
|
||||||
|
Err(LoadError { error: LoadErrorType::ContentBlocked, .. }) => {},
|
||||||
|
_ => panic!("request should have been blocked"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#![feature(plugin)]
|
#![feature(plugin)]
|
||||||
#![plugin(plugins)]
|
#![plugin(plugins)]
|
||||||
|
|
||||||
|
extern crate content_blocker;
|
||||||
extern crate cookie as cookie_rs;
|
extern crate cookie as cookie_rs;
|
||||||
extern crate devtools_traits;
|
extern crate devtools_traits;
|
||||||
extern crate flate2;
|
extern crate flate2;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue