From 074fc4a5e17da6796f73922001ea400e357d1bb9 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Sun, 17 Apr 2016 16:45:33 -0400 Subject: [PATCH] Process content blocker rules in the HTTP loader. --- components/net/http_loader.rs | 46 ++++++++++++++++++++++++--- components/servo/Cargo.lock | 1 + tests/unit/net/Cargo.toml | 1 + tests/unit/net/http_loader.rs | 60 +++++++++++++++++++++++++++++++++++ tests/unit/net/lib.rs | 1 + 5 files changed, 105 insertions(+), 4 deletions(-) diff --git a/components/net/http_loader.rs b/components/net/http_loader.rs index f47207f940d..a081140e9ec 100644 --- a/components/net/http_loader.rs +++ b/components/net/http_loader.rs @@ -4,7 +4,8 @@ use brotli::Decompressor; 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_storage::CookieStorage; use devtools_traits::{ChromeToDevtoolsControlMsg, DevtoolsControlMsg, HttpRequest as DevtoolsHttpRequest}; @@ -329,6 +330,7 @@ pub enum LoadErrorType { Cancelled, Connection { reason: String }, ConnectionAborted { reason: String }, + ContentBlocked, // Preflight fetch inconsistent with main fetch CorsPreflightFetchInconsistent, Decoding { reason: String }, @@ -351,6 +353,7 @@ impl Error for LoadErrorType { LoadErrorType::Cancelled => "load cancelled", LoadErrorType::Connection { ref reason } => reason, LoadErrorType::ConnectionAborted { ref reason } => reason, + LoadErrorType::ContentBlocked => "content blocked", LoadErrorType::CorsPreflightFetchInconsistent => "preflight fetch inconsistent with main fetch", LoadErrorType::Decoding { ref reason } => reason, LoadErrorType::InvalidRedirect { ref reason } => reason, @@ -592,7 +595,8 @@ pub fn modify_request_headers(headers: &mut Headers, user_agent: &str, cookie_jar: &Arc>, auth_cache: &Arc>, - load_data: &LoadData) { + load_data: &LoadData, + block_cookies: bool) { // Ensure that the host header is set from the original url let host = Host { 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 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 set_auth_header(headers, url, auth_cache); @@ -854,6 +860,24 @@ pub fn load(load_data: &LoadData, 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); // Avoid automatically preserving request headers when redirects occur. @@ -872,7 +896,7 @@ pub fn load(load_data: &LoadData, modify_request_headers(&mut request_headers, &doc_url, &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 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, + } +} diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index 0eb34048a2b..2a12a2d75d7 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -1414,6 +1414,7 @@ dependencies = [ name = "net_tests" version = "0.0.1" 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)", "devtools_traits 0.0.1", "flate2 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/tests/unit/net/Cargo.toml b/tests/unit/net/Cargo.toml index 798afe12af0..485c0897ee7 100644 --- a/tests/unit/net/Cargo.toml +++ b/tests/unit/net/Cargo.toml @@ -9,6 +9,7 @@ path = "lib.rs" doctest = false [dependencies] +content-blocker = "0.2" cookie = "0.2" devtools_traits = {path = "../../../components/devtools_traits"} flate2 = "0.2.0" diff --git a/tests/unit/net/http_loader.rs b/tests/unit/net/http_loader.rs index 68f677b46af..ba1a7d6f2b0 100644 --- a/tests/unit/net/http_loader.rs +++ b/tests/unit/net/http_loader.rs @@ -2,6 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use content_blocker::parse_list; use cookie_rs::Cookie as CookiePair; use devtools_traits::HttpRequest as DevtoolsHttpRequest; 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!(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 { + 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"), + } +} diff --git a/tests/unit/net/lib.rs b/tests/unit/net/lib.rs index 4a22213baa6..a20926967ca 100644 --- a/tests/unit/net/lib.rs +++ b/tests/unit/net/lib.rs @@ -5,6 +5,7 @@ #![feature(plugin)] #![plugin(plugins)] +extern crate content_blocker; extern crate cookie as cookie_rs; extern crate devtools_traits; extern crate flate2;