diff options
Diffstat (limited to 'components/script/cors.rs')
-rw-r--r-- | components/script/cors.rs | 141 |
1 files changed, 83 insertions, 58 deletions
diff --git a/components/script/cors.rs b/components/script/cors.rs index f23925b29ed..463d2378009 100644 --- a/components/script/cors.rs +++ b/components/script/cors.rs @@ -46,7 +46,7 @@ pub struct CORSRequest { pub headers: Headers, /// CORS preflight flag (https://fetch.spec.whatwg.org/#concept-http-fetch) /// Indicates that a CORS preflight request and/or cache check is to be performed - pub preflight_flag: bool + pub preflight_flag: bool, } /// https://fetch.spec.whatwg.org/#concept-request-mode @@ -55,15 +55,18 @@ pub struct CORSRequest { #[derive(PartialEq, Copy, Clone, HeapSizeOf)] pub enum RequestMode { CORS, // CORS - ForcedPreflight // CORS-with-forced-preflight + ForcedPreflight, // CORS-with-forced-preflight } impl CORSRequest { /// Creates a CORS request if necessary. Will return an error when fetching is forbidden - pub fn maybe_new(referer: Url, destination: Url, mode: RequestMode, - method: Method, headers: Headers) -> Result<Option<CORSRequest>, ()> { - if referer.scheme == destination.scheme && - referer.host() == destination.host() && + pub fn maybe_new(referer: Url, + destination: Url, + mode: RequestMode, + method: Method, + headers: Headers) + -> Result<Option<CORSRequest>, ()> { + if referer.scheme == destination.scheme && referer.host() == destination.host() && referer.port() == destination.port() { return Ok(None); // Not cross-origin, proceed with a normal fetch } @@ -72,7 +75,8 @@ impl CORSRequest { // we can fetch a data URL normally. about:blank can also be fetched by XHR "http" | "https" => { let mut req = CORSRequest::new(referer, destination, mode, method, headers); - req.preflight_flag = !is_simple_method(&req.method) || mode == RequestMode::ForcedPreflight; + req.preflight_flag = !is_simple_method(&req.method) || + mode == RequestMode::ForcedPreflight; if req.headers.iter().all(|h| is_simple_header(&h)) { req.preflight_flag = true; } @@ -82,10 +86,14 @@ impl CORSRequest { } } - fn new(mut referer: Url, destination: Url, mode: RequestMode, method: Method, - headers: Headers) -> CORSRequest { + fn new(mut referer: Url, + destination: Url, + mode: RequestMode, + method: Method, + headers: Headers) + -> CORSRequest { match referer.scheme_data { - SchemeData::Relative(ref mut data) => data.path = vec!(), + SchemeData::Relative(ref mut data) => data.path = vec![], _ => {} }; referer.fragment = None; @@ -96,7 +104,7 @@ impl CORSRequest { mode: mode, method: method, headers: headers, - preflight_flag: false + preflight_flag: false, } } @@ -181,12 +189,13 @@ impl CORSRequest { preflight.headers.set(AccessControlRequestMethod(self.method.clone())); // Step 5 - 7 - let mut header_names = vec!(); + let mut header_names = vec![]; for header in self.headers.iter() { header_names.push(header.name().to_owned()); } header_names.sort(); - preflight.headers.set(AccessControlRequestHeaders(header_names.into_iter().map(UniCase).collect())); + preflight.headers + .set(AccessControlRequestHeaders(header_names.into_iter().map(UniCase).collect())); // Step 8 unnecessary, we don't use the request body // Step 9, 10 unnecessary, we're writing our own fetch code @@ -195,7 +204,7 @@ impl CORSRequest { let preflight_request = Request::new(preflight.method, preflight.destination); let mut req = match preflight_request { Ok(req) => req, - Err(_) => return error + Err(_) => return error, }; let host = req.headers().get::<Host>().unwrap().clone(); @@ -203,37 +212,36 @@ impl CORSRequest { req.headers_mut().set(host); let stream = match req.start() { Ok(s) => s, - Err(_) => return error + Err(_) => return error, }; let response = match stream.send() { Ok(r) => r, - Err(_) => return error + Err(_) => return error, }; // Step 12 match response.status.class() { Success => {} - _ => return error + _ => return error, } cors_response.headers = response.headers.clone(); // Substeps 1-3 (parsing rules: https://fetch.spec.whatwg.org/#http-new-header-syntax) let methods_substep4 = [self.method.clone()]; let mut methods = match response.headers.get() { Some(&AccessControlAllowMethods(ref v)) => &**v, - _ => return error + _ => return error, }; let headers = match response.headers.get() { Some(&AccessControlAllowHeaders(ref h)) => h, - _ => return error + _ => return error, }; // Substep 4 if methods.is_empty() || preflight.mode == RequestMode::ForcedPreflight { methods = &methods_substep4; } // Substep 5 - if !is_simple_method(&self.method) && - !methods.iter().any(|m| m == &self.method) { - return error; + if !is_simple_method(&self.method) && !methods.iter().any(|m| m == &self.method) { + return error; } // Substep 6 for h in self.headers.iter() { @@ -247,25 +255,31 @@ impl CORSRequest { // Substep 7, 8 let max_age = match response.headers.get() { Some(&AccessControlMaxAge(num)) => num, - None => 0 + None => 0, }; // Substep 9: Impose restrictions on max-age, if any (unimplemented) // Substeps 10-12: Add a cache (partially implemented, XXXManishearth) // This cache should come from the user agent, creating a new one here to check // for compile time errors - let cache = &mut CORSCache(vec!()); + let cache = &mut CORSCache(vec![]); for m in methods { let cache_match = cache.match_method_and_update(self, m, max_age); if !cache_match { - cache.insert(CORSCacheEntry::new(self.origin.clone(), self.destination.clone(), - max_age, false, HeaderOrMethod::MethodData(m.clone()))); + cache.insert(CORSCacheEntry::new(self.origin.clone(), + self.destination.clone(), + max_age, + false, + HeaderOrMethod::MethodData(m.clone()))); } } for h in response.headers.iter() { let cache_match = cache.match_header_and_update(self, h.name(), max_age); if !cache_match { - cache.insert(CORSCacheEntry::new(self.origin.clone(), self.destination.clone(), - max_age, false, HeaderOrMethod::HeaderData(h.to_string()))); + cache.insert(CORSCacheEntry::new(self.origin.clone(), + self.destination.clone(), + max_age, + false, + HeaderOrMethod::HeaderData(h.to_string()))); } } cors_response @@ -275,21 +289,21 @@ impl CORSRequest { pub struct CORSResponse { pub network_error: bool, - pub headers: Headers + pub headers: Headers, } impl CORSResponse { fn new() -> CORSResponse { CORSResponse { network_error: false, - headers: Headers::new() + headers: Headers::new(), } } fn new_error() -> CORSResponse { - CORSResponse { + CORSResponse { network_error: true, - headers: Headers::new() + headers: Headers::new(), } } } @@ -305,21 +319,21 @@ pub struct CORSCache(Vec<CORSCacheEntry>); #[derive(Clone)] pub enum HeaderOrMethod { HeaderData(String), - MethodData(Method) + MethodData(Method), } impl HeaderOrMethod { fn match_header(&self, header_name: &str) -> bool { match *self { HeaderOrMethod::HeaderData(ref s) => s.eq_ignore_ascii_case(header_name), - _ => false + _ => false, } } fn match_method(&self, method: &Method) -> bool { match *self { HeaderOrMethod::MethodData(ref m) => m == method, - _ => false + _ => false, } } } @@ -332,7 +346,7 @@ pub struct CORSCacheEntry { pub max_age: u32, pub credentials: bool, pub header_or_method: HeaderOrMethod, - created: Timespec + created: Timespec, } impl CORSCacheEntry { @@ -340,14 +354,15 @@ impl CORSCacheEntry { url: Url, max_age: u32, credentials: bool, - header_or_method: HeaderOrMethod) -> CORSCacheEntry { + header_or_method: HeaderOrMethod) + -> CORSCacheEntry { CORSCacheEntry { origin: origin, url: url, max_age: max_age, credentials: credentials, header_or_method: header_or_method, - created: time::now().to_timespec() + created: time::now().to_timespec(), } } } @@ -377,15 +392,16 @@ impl CORSCache { /// https://fetch.spec.whatwg.org/#concept-cache-match-header fn find_entry_by_header<'a>(&'a mut self, request: &CORSRequest, - header_name: &str) -> Option<&'a mut CORSCacheEntry> { + header_name: &str) + -> Option<&'a mut CORSCacheEntry> { self.cleanup(); let CORSCache(ref mut buf) = *self; // Credentials are not yet implemented here - let entry = buf.iter_mut().find(|e| e.origin.scheme == request.origin.scheme && - e.origin.host() == request.origin.host() && - e.origin.port() == request.origin.port() && - e.url == request.destination && - e.header_or_method.match_header(header_name)); + let entry = buf.iter_mut().find(|e| { + e.origin.scheme == request.origin.scheme && e.origin.host() == request.origin.host() && + e.origin.port() == request.origin.port() && e.url == request.destination && + e.header_or_method.match_header(header_name) + }); entry } @@ -393,22 +409,27 @@ impl CORSCache { self.find_entry_by_header(request, header_name).is_some() } - fn match_header_and_update(&mut self, request: &CORSRequest, header_name: &str, new_max_age: u32) -> bool { + fn match_header_and_update(&mut self, + request: &CORSRequest, + header_name: &str, + new_max_age: u32) + -> bool { self.find_entry_by_header(request, header_name).map(|e| e.max_age = new_max_age).is_some() } fn find_entry_by_method<'a>(&'a mut self, request: &CORSRequest, - method: &Method) -> Option<&'a mut CORSCacheEntry> { + method: &Method) + -> Option<&'a mut CORSCacheEntry> { // we can take the method from CORSRequest itself self.cleanup(); let CORSCache(ref mut buf) = *self; // Credentials are not yet implemented here - let entry = buf.iter_mut().find(|e| e.origin.scheme == request.origin.scheme && - e.origin.host() == request.origin.host() && - e.origin.port() == request.origin.port() && - e.url == request.destination && - e.header_or_method.match_method(method)); + let entry = buf.iter_mut().find(|e| { + e.origin.scheme == request.origin.scheme && e.origin.host() == request.origin.host() && + e.origin.port() == request.origin.port() && e.url == request.destination && + e.header_or_method.match_method(method) + }); entry } @@ -417,7 +438,11 @@ impl CORSCache { self.find_entry_by_method(request, method).is_some() } - fn match_method_and_update(&mut self, request: &CORSRequest, method: &Method, new_max_age: u32) -> bool { + fn match_method_and_update(&mut self, + request: &CORSRequest, + method: &Method, + new_max_age: u32) + -> bool { self.find_entry_by_method(request, method).map(|e| e.max_age = new_max_age).is_some() } @@ -429,8 +454,8 @@ impl CORSCache { } fn is_simple_header(h: &HeaderView) -> bool { - //FIXME: use h.is::<HeaderType>() when AcceptLanguage and - //ContentLanguage headers exist + // FIXME: use h.is::<HeaderType>() when AcceptLanguage and + // ContentLanguage headers exist match &*h.name().to_ascii_lowercase() { "accept" | "accept-language" | "content-language" => true, "content-type" => match h.value() { @@ -438,17 +463,17 @@ fn is_simple_header(h: &HeaderView) -> bool { Some(&ContentType(Mime(TopLevel::Application, SubLevel::WwwFormUrlEncoded, _))) | Some(&ContentType(Mime(TopLevel::Multipart, SubLevel::FormData, _))) => true, - _ => false + _ => false, }, - _ => false + _ => false, } } fn is_simple_method(m: &Method) -> bool { match *m { Method::Get | Method::Head | Method::Post => true, - _ => false + _ => false, } } @@ -459,6 +484,6 @@ pub fn allow_cross_origin_request(req: &CORSRequest, headers: &Headers) -> bool Some(&AccessControlAllowOrigin::Any) => true, // Not always true, depends on credentials mode Some(&AccessControlAllowOrigin::Value(ref url)) => req.origin.serialize() == *url, Some(&AccessControlAllowOrigin::Null) | - None => false + None => false, } } |