diff --git a/components/net/http_loader.rs b/components/net/http_loader.rs index 225751e2cd5..fa936501fe5 100644 --- a/components/net/http_loader.rs +++ b/components/net/http_loader.rs @@ -37,6 +37,7 @@ use hyper::{Body, Client, Method, Response as HyperResponse, StatusCode}; use hyper_serde::Serde; use msg::constellation_msg::{HistoryStateId, PipelineId}; use net_traits::quality::{quality_to_value, Quality, QualityItem}; +use net_traits::request::Origin::Origin as SpecificOrigin; use net_traits::request::{CacheMode, CredentialsMode, Destination, Origin}; use net_traits::request::{RedirectMode, Referrer, Request, RequestMode}; use net_traits::request::{ResponseTainting, ServiceWorkersMode}; @@ -44,6 +45,7 @@ use net_traits::response::{HttpsState, Response, ResponseBody, ResponseType}; use net_traits::{CookieSource, FetchMetadata, NetworkError, ReferrerPolicy}; use net_traits::{RedirectEndValue, RedirectStartValue, ResourceAttribute, ResourceFetchTiming}; use openssl::ssl::SslConnectorBuilder; +use servo_arc::Arc; use servo_url::{ImmutableOrigin, ServoUrl}; use std::collections::{HashMap, HashSet}; use std::error::Error; @@ -51,7 +53,7 @@ use std::iter::FromIterator; use std::mem; use std::ops::Deref; use std::str::FromStr; -use std::sync::{Arc, Mutex, RwLock}; +use std::sync::{Mutex, RwLock}; use std::time::{Duration, SystemTime}; use time::{self, Tm}; use tokio::prelude::{future, Future, Stream}; @@ -631,7 +633,7 @@ pub fn http_fetch( request.redirect_count as u16, )); - response.resource_timing = context.timing.lock().unwrap().clone(); + response.resource_timing = Arc::clone(&context.timing); // Step 6 response @@ -828,7 +830,6 @@ fn http_network_or_cache_fetch( ) -> Response { // Step 2 let mut response: Option = None; - // Step 4 let mut revalidating_flag = false; @@ -1302,8 +1303,32 @@ fn http_network_fetch( } } + let header_strings: Vec<&str> = res + .headers() + .get_all("Timing-Allow-Origin") + .iter() + .map(|header_value| header_value.to_str().unwrap_or("")) + .collect(); + let wildcard_present = header_strings.iter().any(|header_str| *header_str == "*"); + // The spec: https://www.w3.org/TR/resource-timing-2/#sec-timing-allow-origin + // says that a header string is either an origin or a wildcard so we can just do a straight + // check against the document origin + let req_origin_in_timing_allow = header_strings + .iter() + .any(|header_str| match request.origin { + SpecificOrigin(ref immutable_request_origin) => { + *header_str == immutable_request_origin.ascii_serialization() + }, + _ => false, + }); + + if !req_origin_in_timing_allow && !wildcard_present { + context.timing.lock().unwrap().mark_timing_check_failed(); + } + let timing = context.timing.lock().unwrap().clone(); let mut response = Response::new(url.clone(), timing); + response.status = Some(( res.status(), res.status().canonical_reason().unwrap_or("").into(), diff --git a/components/net_traits/lib.rs b/components/net_traits/lib.rs index 369c1efedaa..46a48af443e 100644 --- a/components/net_traits/lib.rs +++ b/components/net_traits/lib.rs @@ -232,6 +232,8 @@ impl FetchTaskTarget for IpcSender { } else { let _ = self.send(FetchResponseMsg::ProcessResponseEOF(Ok(response .get_resource_timing() + .lock() + .unwrap() .clone()))); } } @@ -459,6 +461,7 @@ pub struct ResourceCorsData { #[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] pub struct ResourceFetchTiming { pub domain_lookup_start: u64, + pub timing_check_passed: bool, pub timing_type: ResourceTimingType, /// Number of redirects until final resource (currently limited to 20) pub redirect_count: u16, @@ -508,6 +511,7 @@ impl ResourceFetchTiming { pub fn new(timing_type: ResourceTimingType) -> ResourceFetchTiming { ResourceFetchTiming { timing_type: timing_type, + timing_check_passed: true, domain_lookup_start: 0, redirect_count: 0, request_start: 0, @@ -524,6 +528,13 @@ impl ResourceFetchTiming { // TODO currently this is being set with precise time ns when it should be time since // time origin (as described in Performance::now) pub fn set_attribute(&mut self, attribute: ResourceAttribute) { + let should_attribute_always_be_updated = match attribute { + ResourceAttribute::FetchStart | ResourceAttribute::ResponseEnd => true, + _ => false, + }; + if !self.timing_check_passed && !should_attribute_always_be_updated { + return; + } match attribute { ResourceAttribute::DomainLookupStart => self.domain_lookup_start = precise_time_ns(), ResourceAttribute::RedirectCount(count) => self.redirect_count = count, @@ -547,6 +558,17 @@ impl ResourceFetchTiming { ResourceAttribute::ResponseEnd => self.response_end = precise_time_ns(), } } + + pub fn mark_timing_check_failed(&mut self) { + self.timing_check_passed = false; + self.domain_lookup_start = 0; + self.redirect_count = 0; + self.request_start = 0; + self.response_start = 0; + self.redirect_start = 0; + self.connect_start = 0; + self.connect_end = 0; + } } /// Metadata about a loaded resource, such as is obtained from HTTP headers.