diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2016-04-28 13:36:05 -0700 |
---|---|---|
committer | bors-servo <lbergstrom+bors@mozilla.com> | 2016-04-28 13:36:05 -0700 |
commit | 3d38a60cee8a2e19ae8f04df7c2374fc9d97999c (patch) | |
tree | d05a733f3366cf6b0a77b255f0e9ba71be132f9f /components | |
parent | 3836c2c4cba1f49ea5ce3ce802b0bd604213c2a2 (diff) | |
parent | 483f07c8f012ded73347dd0307eb4d09dba96a94 (diff) | |
download | servo-3d38a60cee8a2e19ae8f04df7c2374fc9d97999c.tar.gz servo-3d38a60cee8a2e19ae8f04df7c2374fc9d97999c.zip |
Auto merge of #10867 - danlrobertson:sandbox, r=KiChjang
Fix logic for cors cache match
The current logic for a cors cache match does not consider "credentials is false and request's credentials mode is not "include" or credentials is true."
I could have missed something, but `CacheRequestDetails::credentials` is set to true if credentials mode is "include", and false otherwise. So `(!cors_cache.credentials && !cors_req.credentials) || cors_cache.credentials` would be directly following the spec, but unless I'm mistaken `cors_cache.credentials || !cors_req.credentials` is logically the same.
Fixes: #10525
<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="35" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/10867)
<!-- Reviewable:end -->
Diffstat (limited to 'components')
-rw-r--r-- | components/net/fetch/cors_cache.rs | 227 | ||||
-rw-r--r-- | components/net/fetch/methods.rs | 43 | ||||
-rw-r--r-- | components/net_traits/request.rs | 2 |
3 files changed, 60 insertions, 212 deletions
diff --git a/components/net/fetch/cors_cache.rs b/components/net/fetch/cors_cache.rs index dc640ce4c37..1f6d39f71b7 100644 --- a/components/net/fetch/cors_cache.rs +++ b/components/net/fetch/cors_cache.rs @@ -12,7 +12,6 @@ use hyper::method::Method; use net_traits::request::Origin; use std::ascii::AsciiExt; -use std::sync::mpsc::{Sender, Receiver, channel}; use time; use time::{now, Timespec}; use url::Url; @@ -20,7 +19,7 @@ use url::Url; /// Union type for CORS cache entries /// /// Each entry might pertain to a header or method -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum HeaderOrMethod { HeaderData(String), MethodData(Method) @@ -43,7 +42,7 @@ impl HeaderOrMethod { } /// An entry in the CORS cache -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct CORSCacheEntry { pub origin: Origin, pub url: Url, @@ -74,51 +73,20 @@ pub struct CacheRequestDetails { pub credentials: bool } -/// Trait for a generic CORS Cache -pub trait CORSCache { - /// [Clear the cache](https://fetch.spec.whatwg.org/#concept-cache-clear) - fn clear (&mut self, request: CacheRequestDetails); - - /// Remove old entries - fn cleanup(&mut self); - - /// Returns true if an entry with a - /// [matching header](https://fetch.spec.whatwg.org/#concept-cache-match-header) is found - fn match_header(&mut self, request: CacheRequestDetails, header_name: &str) -> bool; - - /// Updates max age if an entry for a - /// [matching header](https://fetch.spec.whatwg.org/#concept-cache-match-header) is found. - /// - /// If not, it will insert an equivalent entry - fn match_header_and_update(&mut self, request: CacheRequestDetails, header_name: &str, new_max_age: u32) -> bool; - - /// Returns true if an entry with a - /// [matching method](https://fetch.spec.whatwg.org/#concept-cache-match-method) is found - fn match_method(&mut self, request: CacheRequestDetails, method: Method) -> bool; - - /// Updates max age if an entry for - /// [a matching method](https://fetch.spec.whatwg.org/#concept-cache-match-method) is found. - /// - /// If not, it will insert an equivalent entry - fn match_method_and_update(&mut self, request: CacheRequestDetails, method: Method, new_max_age: u32) -> bool; - /// Insert an entry - fn insert(&mut self, entry: CORSCacheEntry); -} - -/// A simple, vector-based CORS Cache -#[derive(Clone)] -pub struct BasicCORSCache(Vec<CORSCacheEntry>); - fn match_headers(cors_cache: &CORSCacheEntry, cors_req: &CacheRequestDetails) -> bool { cors_cache.origin == cors_req.origin && cors_cache.url == cors_req.destination && - cors_cache.credentials == cors_req.credentials + (cors_cache.credentials || !cors_req.credentials) } -impl BasicCORSCache { +/// A simple, vector-based CORS Cache +#[derive(Clone)] +pub struct CORSCache(Vec<CORSCacheEntry>); + +impl CORSCache { - pub fn new() -> BasicCORSCache { - BasicCORSCache(vec![]) + pub fn new() -> CORSCache { + CORSCache(vec![]) } fn find_entry_by_header<'a>(&'a mut self, request: &CacheRequestDetails, @@ -133,33 +101,37 @@ impl BasicCORSCache { self.cleanup(); self.0.iter_mut().find(|e| match_headers(e, request) && e.header_or_method.match_method(&method)) } -} -impl CORSCache for BasicCORSCache { - /// https://fetch.spec.whatwg.org/#concept-cache-clear - #[allow(dead_code)] - fn clear (&mut self, request: CacheRequestDetails) { - let BasicCORSCache(buf) = self.clone(); + /// [Clear the cache](https://fetch.spec.whatwg.org/#concept-cache-clear) + pub fn clear (&mut self, request: CacheRequestDetails) { + let CORSCache(buf) = self.clone(); let new_buf: Vec<CORSCacheEntry> = buf.into_iter().filter(|e| e.origin == request.origin && request.destination == e.url).collect(); - *self = BasicCORSCache(new_buf); + *self = CORSCache(new_buf); } - // Remove old entries - fn cleanup(&mut self) { - let BasicCORSCache(buf) = self.clone(); + /// Remove old entries + pub fn cleanup(&mut self) { + let CORSCache(buf) = self.clone(); let now = time::now().to_timespec(); let new_buf: Vec<CORSCacheEntry> = buf.into_iter() - .filter(|e| now.sec > e.created.sec + e.max_age as i64) + .filter(|e| now.sec < e.created.sec + e.max_age as i64) .collect(); - *self = BasicCORSCache(new_buf); + *self = CORSCache(new_buf); } - fn match_header(&mut self, request: CacheRequestDetails, header_name: &str) -> bool { + /// Returns true if an entry with a + /// [matching header](https://fetch.spec.whatwg.org/#concept-cache-match-header) is found + pub fn match_header(&mut self, request: CacheRequestDetails, header_name: &str) -> bool { self.find_entry_by_header(&request, header_name).is_some() } - fn match_header_and_update(&mut self, request: CacheRequestDetails, header_name: &str, new_max_age: u32) -> bool { + /// Updates max age if an entry for a + /// [matching header](https://fetch.spec.whatwg.org/#concept-cache-match-header) is found. + /// + /// If not, it will insert an equivalent entry + pub fn match_header_and_update(&mut self, request: CacheRequestDetails, + header_name: &str, new_max_age: u32) -> bool { match self.find_entry_by_header(&request, header_name).map(|e| e.max_age = new_max_age) { Some(_) => true, None => { @@ -171,11 +143,17 @@ impl CORSCache for BasicCORSCache { } } - fn match_method(&mut self, request: CacheRequestDetails, method: Method) -> bool { + /// Returns true if an entry with a + /// [matching method](https://fetch.spec.whatwg.org/#concept-cache-match-method) is found + pub fn match_method(&mut self, request: CacheRequestDetails, method: Method) -> bool { self.find_entry_by_method(&request, method).is_some() } - fn match_method_and_update(&mut self, request: CacheRequestDetails, method: Method, new_max_age: u32) -> bool { + /// Updates max age if an entry for + /// [a matching method](https://fetch.spec.whatwg.org/#concept-cache-match-method) is found. + /// + /// If not, it will insert an equivalent entry + pub fn match_method_and_update(&mut self, request: CacheRequestDetails, method: Method, new_max_age: u32) -> bool { match self.find_entry_by_method(&request, method.clone()).map(|e| e.max_age = new_max_age) { Some(_) => true, None => { @@ -186,138 +164,9 @@ impl CORSCache for BasicCORSCache { } } - fn insert(&mut self, entry: CORSCacheEntry) { + /// Insert an entry + pub fn insert(&mut self, entry: CORSCacheEntry) { self.cleanup(); self.0.push(entry); } } - -/// Various messages that can be sent to a CORSCacheThread -pub enum CORSCacheThreadMsg { - Clear(CacheRequestDetails, Sender<()>), - Cleanup(Sender<()>), - MatchHeader(CacheRequestDetails, String, Sender<bool>), - MatchHeaderUpdate(CacheRequestDetails, String, u32, Sender<bool>), - MatchMethod(CacheRequestDetails, Method, Sender<bool>), - MatchMethodUpdate(CacheRequestDetails, Method, u32, Sender<bool>), - Insert(CORSCacheEntry, Sender<()>), - ExitMsg -} - -/// A Sender to a CORSCacheThread -/// -/// This can be used as a CORS Cache. -/// The methods on this type block until they can run, and it behaves similar to a mutex -pub type CORSCacheSender = Sender<CORSCacheThreadMsg>; - -impl CORSCache for CORSCacheSender { - fn clear (&mut self, request: CacheRequestDetails) { - let (tx, rx) = channel(); - let _ = self.send(CORSCacheThreadMsg::Clear(request, tx)); - let _ = rx.recv(); - } - - fn cleanup(&mut self) { - let (tx, rx) = channel(); - let _ = self.send(CORSCacheThreadMsg::Cleanup(tx)); - let _ = rx.recv(); - } - - fn match_header(&mut self, request: CacheRequestDetails, header_name: &str) -> bool { - let (tx, rx) = channel(); - let _ = self.send(CORSCacheThreadMsg::MatchHeader(request, header_name.to_owned(), tx)); - rx.recv().unwrap_or(false) - } - - fn match_header_and_update(&mut self, request: CacheRequestDetails, header_name: &str, new_max_age: u32) -> bool { - let (tx, rx) = channel(); - let _ = self.send(CORSCacheThreadMsg::MatchHeaderUpdate(request, header_name.to_owned(), new_max_age, tx)); - rx.recv().unwrap_or(false) - } - - fn match_method(&mut self, request: CacheRequestDetails, method: Method) -> bool { - let (tx, rx) = channel(); - let _ = self.send(CORSCacheThreadMsg::MatchMethod(request, method, tx)); - rx.recv().unwrap_or(false) - } - - fn match_method_and_update(&mut self, request: CacheRequestDetails, method: Method, new_max_age: u32) -> bool { - let (tx, rx) = channel(); - let _ = self.send(CORSCacheThreadMsg::MatchMethodUpdate(request, method, new_max_age, tx)); - rx.recv().unwrap_or(false) - } - - fn insert(&mut self, entry: CORSCacheEntry) { - let (tx, rx) = channel(); - let _ = self.send(CORSCacheThreadMsg::Insert(entry, tx)); - let _ = rx.recv(); - } -} - -/// A simple thread-based CORS Cache that can be sent messages -/// -/// #Example -/// ```ignore -/// let thread = CORSCacheThread::new(); -/// let builder = ThreadBuilder::new().named("XHRThread"); -/// let mut sender = thread.sender(); -/// builder.spawn(move || { thread.run() }); -/// sender.insert(CORSCacheEntry::new(/* parameters here */)); -/// ``` -pub struct CORSCacheThread { - receiver: Receiver<CORSCacheThreadMsg>, - cache: BasicCORSCache, - sender: CORSCacheSender -} - -impl CORSCacheThread { - pub fn new() -> CORSCacheThread { - let (tx, rx) = channel(); - CORSCacheThread { - receiver: rx, - cache: BasicCORSCache(vec![]), - sender: tx - } - } - - /// Provides a sender to the cache thread - pub fn sender(&self) -> CORSCacheSender { - self.sender.clone() - } - - /// Runs the cache thread - /// This blocks the current thread, so it is advised - /// to spawn a new thread for this - /// Send ExitMsg to the associated Sender to exit - pub fn run(&mut self) { - loop { - match self.receiver.recv().unwrap() { - CORSCacheThreadMsg::Clear(request, tx) => { - self.cache.clear(request); - let _ = tx.send(()); - }, - CORSCacheThreadMsg::Cleanup(tx) => { - self.cache.cleanup(); - let _ = tx.send(()); - }, - CORSCacheThreadMsg::MatchHeader(request, header, tx) => { - let _ = tx.send(self.cache.match_header(request, &header)); - }, - CORSCacheThreadMsg::MatchHeaderUpdate(request, header, new_max_age, tx) => { - let _ = tx.send(self.cache.match_header_and_update(request, &header, new_max_age)); - }, - CORSCacheThreadMsg::MatchMethod(request, method, tx) => { - let _ = tx.send(self.cache.match_method(request, method)); - }, - CORSCacheThreadMsg::MatchMethodUpdate(request, method, new_max_age, tx) => { - let _ = tx.send(self.cache.match_method_and_update(request, method, new_max_age)); - }, - CORSCacheThreadMsg::Insert(entry, tx) => { - self.cache.insert(entry); - let _ = tx.send(()); - }, - CORSCacheThreadMsg::ExitMsg => break - } - } - } -} diff --git a/components/net/fetch/methods.rs b/components/net/fetch/methods.rs index 479c9bc9951..868d284707f 100644 --- a/components/net/fetch/methods.rs +++ b/components/net/fetch/methods.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use data_loader::decode; -use fetch::cors_cache::{BasicCORSCache, CORSCache, CacheRequestDetails}; +use fetch::cors_cache::{CORSCache, CacheRequestDetails}; use http_loader::{NetworkHttpRequestFactory, create_http_connector, obtain_response}; use hyper::header::{Accept, AcceptLanguage, Authorization, AccessControlAllowCredentials}; use hyper::header::{AccessControlAllowOrigin, AccessControlAllowHeaders, AccessControlAllowMethods}; @@ -40,6 +40,10 @@ pub fn fetch_async(request: Request, listener: Box<AsyncFetchListener + Send>) { /// [Fetch](https://fetch.spec.whatwg.org#concept-fetch) pub fn fetch(request: Rc<Request>) -> Response { + fetch_with_cors_cache(request, &mut CORSCache::new()) +} + +pub fn fetch_with_cors_cache(request: Rc<Request>, cache: &mut CORSCache) -> Response { // Step 1 if request.window.get() == Window::Client { @@ -102,11 +106,11 @@ pub fn fetch(request: Rc<Request>) -> Response { // TODO: create a fetch record and append it to request's client's fetch group list } // Step 7 - main_fetch(request, false, false) + main_fetch(request, cache, false, false) } /// [Main fetch](https://fetch.spec.whatwg.org/#concept-main-fetch) -fn main_fetch(request: Rc<Request>, cors_flag: bool, recursive_flag: bool) -> Response { +fn main_fetch(request: Rc<Request>, cache: &mut CORSCache, cors_flag: bool, recursive_flag: bool) -> Response { // TODO: Implement main fetch spec // Step 1 @@ -156,14 +160,14 @@ fn main_fetch(request: Rc<Request>, cors_flag: bool, recursive_flag: bool) -> Re current_url.scheme() == "about" || request.mode == RequestMode::Navigate { - basic_fetch(request.clone()) + basic_fetch(request.clone(), cache) } else if request.mode == RequestMode::SameOrigin { Response::network_error() } else if request.mode == RequestMode::NoCORS { request.response_tainting.set(ResponseTainting::Opaque); - basic_fetch(request.clone()) + basic_fetch(request.clone(), cache) } else if !matches!(current_url.scheme(), "http" | "https") { Response::network_error() @@ -175,7 +179,7 @@ fn main_fetch(request: Rc<Request>, cors_flag: bool, recursive_flag: bool) -> Re request.response_tainting.set(ResponseTainting::CORSTainting); request.redirect_mode.set(RedirectMode::Error); - let response = http_fetch(request.clone(), BasicCORSCache::new(), true, true, false); + let response = http_fetch(request.clone(), cache, true, true, false); if response.is_network_error() { // TODO clear cache entries using request } @@ -183,7 +187,7 @@ fn main_fetch(request: Rc<Request>, cors_flag: bool, recursive_flag: bool) -> Re } else { request.response_tainting.set(ResponseTainting::CORSTainting); - http_fetch(request.clone(), BasicCORSCache::new(), true, false, false) + http_fetch(request.clone(), cache, true, false, false) } } }; @@ -280,7 +284,7 @@ fn main_fetch(request: Rc<Request>, cors_flag: bool, recursive_flag: bool) -> Re } /// [Basic fetch](https://fetch.spec.whatwg.org#basic-fetch) -fn basic_fetch(request: Rc<Request>) -> Response { +fn basic_fetch(request: Rc<Request>, cache: &mut CORSCache) -> Response { let url = request.current_url(); @@ -294,7 +298,7 @@ fn basic_fetch(request: Rc<Request>) -> Response { }, "http" | "https" => { - http_fetch(request.clone(), BasicCORSCache::new(), false, false, false) + http_fetch(request.clone(), cache, false, false, false) }, "data" => { @@ -324,7 +328,7 @@ fn basic_fetch(request: Rc<Request>) -> Response { /// [HTTP fetch](https://fetch.spec.whatwg.org#http-fetch) fn http_fetch(request: Rc<Request>, - mut cache: BasicCORSCache, + cache: &mut CORSCache, cors_flag: bool, cors_preflight_flag: bool, authentication_fetch_flag: bool) -> Response { @@ -394,7 +398,7 @@ fn http_fetch(request: Rc<Request>, // Sub-substep 1 if method_mismatch || header_mismatch { - let preflight_result = cors_preflight_fetch(request.clone(), Some(cache)); + let preflight_result = cors_preflight_fetch(request.clone(), cache); // Sub-substep 2 if preflight_result.response_type == ResponseType::Error { return Response::network_error(); @@ -443,7 +447,7 @@ fn http_fetch(request: Rc<Request>, RedirectMode::Follow => { // set back to default response.return_internal.set(true); - http_redirect_fetch(request, Rc::new(response), cors_flag) + http_redirect_fetch(request, cache, Rc::new(response), cors_flag) } } }, @@ -466,7 +470,7 @@ fn http_fetch(request: Rc<Request>, } // Step 4 - return http_fetch(request, BasicCORSCache::new(), cors_flag, cors_preflight_flag, true); + return http_fetch(request, cache, cors_flag, cors_preflight_flag, true); } // Code 407 @@ -482,7 +486,7 @@ fn http_fetch(request: Rc<Request>, // TODO: Prompt the user for proxy authentication credentials // Step 4 - return http_fetch(request, BasicCORSCache::new(), + return http_fetch(request, cache, cors_flag, cors_preflight_flag, authentication_fetch_flag); } @@ -503,6 +507,7 @@ fn http_fetch(request: Rc<Request>, /// [HTTP redirect fetch](https://fetch.spec.whatwg.org#http-redirect-fetch) fn http_redirect_fetch(request: Rc<Request>, + cache: &mut CORSCache, response: Rc<Response>, cors_flag: bool) -> Response { @@ -580,7 +585,7 @@ fn http_redirect_fetch(request: Rc<Request>, request.url_list.borrow_mut().push(location_url); // Step 15 - main_fetch(request, cors_flag, true) + main_fetch(request, cache, cors_flag, true) } /// [HTTP network or cache fetch](https://fetch.spec.whatwg.org#http-network-or-cache-fetch) @@ -917,7 +922,7 @@ fn http_network_fetch(request: Rc<Request>, } /// [CORS preflight fetch](https://fetch.spec.whatwg.org#cors-preflight-fetch) -fn cors_preflight_fetch(request: Rc<Request>, cache: Option<BasicCORSCache>) -> Response { +fn cors_preflight_fetch(request: Rc<Request>, cache: &mut CORSCache) -> Response { // Step 1 let mut preflight = Request::new(request.current_url(), Some(request.origin.borrow().clone()), false); *preflight.method.borrow_mut() = Method::Options; @@ -995,12 +1000,6 @@ fn cors_preflight_fetch(request: Rc<Request>, cache: Option<BasicCORSCache>) -> // TODO: Substep 9 - Need to define what an imposed limit on max-age is - // Substep 10 - let mut cache = match cache { - Some(c) => c, - None => return response - }; - // Substep 11, 12 for method in &methods { cache.match_method_and_update(CacheRequestDetails { diff --git a/components/net_traits/request.rs b/components/net_traits/request.rs index e02529263c5..675a78fd788 100644 --- a/components/net_traits/request.rs +++ b/components/net_traits/request.rs @@ -33,7 +33,7 @@ pub enum Destination { } /// A request [origin](https://fetch.spec.whatwg.org/#concept-request-origin) -#[derive(Clone, PartialEq)] +#[derive(Clone, PartialEq, Debug)] pub enum Origin { Client, Origin(UrlOrigin) |