diff --git a/components/net/fetch/methods.rs b/components/net/fetch/methods.rs index 76f640ebb82..3286d1caad3 100644 --- a/components/net/fetch/methods.rs +++ b/components/net/fetch/methods.rs @@ -14,7 +14,7 @@ use crossbeam_channel::Sender; use devtools_traits::DevtoolsControlMsg; use embedder_traits::resources::{self, Resource}; use headers::{AccessControlExposeHeaders, ContentType, HeaderMapExt}; -use http::header::{self, HeaderMap, HeaderName}; +use http::header::{self, HeaderMap, HeaderName, RANGE}; use http::{HeaderValue, Method, StatusCode}; use ipc_channel::ipc; use log::{debug, trace, warn}; @@ -47,6 +47,9 @@ use crate::protocols::ProtocolRegistry; use crate::request_interceptor::RequestInterceptor; use crate::subresource_integrity::is_response_integrity_valid; +const PARTIAL_RESPONSE_TO_NON_RANGE_REQUEST_ERROR: &str = "Refusing to provide partial response\ +from earlier ranged request to API that did not make a range request"; + pub type Target<'a> = &'a mut (dyn FetchTaskTarget + Send); #[derive(Clone, Deserialize, Serialize)] @@ -484,21 +487,28 @@ pub async fn main_fetch( .get_network_error() .cloned() .map(Response::network_error); + + // Step 15. Let internalResponse be response, if response is a network error; + // otherwise response’s internal response. + let response_type = response.response_type.clone(); // Needed later after the mutable borrow let internal_response = if let Some(error_response) = network_error_response.as_mut() { error_response } else { response.actual_response_mut() }; - // Step 16. + // Step 16. If internalResponse’s URL list is empty, then set it to a clone of request’s URL list. if internal_response.url_list.is_empty() { internal_response.url_list.clone_from(&request.url_list) } - // Step 17. - // TODO: handle blocking as mixed content. - // TODO: handle blocking by content security policy. - let blocked_error_response; + // Step 19. If response is not a network error and any of the following returns blocked + // TODO: * should internalResponse to request be blocked as mixed content + // TODO: * should internalResponse to request be blocked by Content Security Policy + // * should internalResponse to request be blocked due to its MIME type + // * should internalResponse to request be blocked due to nosniff + let mut blocked_error_response; + let internal_response = if should_replace_with_nosniff_error { // Defer rebinding result blocked_error_response = @@ -513,9 +523,27 @@ pub async fn main_fetch( internal_response }; - // Step 18. - // We check `internal_response` since we did not mutate `response` - // in the previous step. + // Step 20. If response’s type is "opaque", internalResponse’s status is 206, internalResponse’s + // range-requested flag is set, and request’s header list does not contain `Range`, then set + // response and internalResponse to a network error. + let internal_response = if response_type == ResponseType::Opaque && + internal_response.status.code() == StatusCode::PARTIAL_CONTENT && + internal_response.range_requested && + !request.headers.contains_key(RANGE) + { + // Defer rebinding result + blocked_error_response = Response::network_error(NetworkError::Internal( + PARTIAL_RESPONSE_TO_NON_RANGE_REQUEST_ERROR.into(), + )); + &blocked_error_response + } else { + internal_response + }; + + // Step 21. If response is not a network error and either request’s method is `HEAD` or `CONNECT`, + // or internalResponse’s status is a null body status, set internalResponse’s body to null and + // disregard any enqueuing toward it (if any). + // NOTE: We check `internal_response` since we did not mutate `response` in the previous steps. let not_network_error = !response_is_network_error && !internal_response.is_network_error(); if not_network_error && (is_null_body_status(&internal_response.status) || diff --git a/components/net/http_loader.rs b/components/net/http_loader.rs index d92bec2bb5c..8549b11a101 100644 --- a/components/net/http_loader.rs +++ b/components/net/http_loader.rs @@ -26,7 +26,7 @@ use headers::{ }; use http::header::{ self, ACCEPT, AUTHORIZATION, CONTENT_ENCODING, CONTENT_LANGUAGE, CONTENT_LOCATION, - CONTENT_TYPE, HeaderValue, + CONTENT_TYPE, HeaderValue, RANGE, }; use http::{HeaderMap, Method, Request as HyperRequest, StatusCode}; use http_body_util::combinators::BoxBody; @@ -1123,7 +1123,7 @@ pub async fn http_redirect_fetch( fetch_response } -/// [HTTP network or cache fetch](https://fetch.spec.whatwg.org#http-network-or-cache-fetch) +/// [HTTP network or cache fetch](https://fetch.spec.whatwg.org/#concept-http-network-or-cache-fetch) #[async_recursion] async fn http_network_or_cache_fetch( fetch_params: &mut FetchParams, @@ -1598,8 +1598,12 @@ async fn http_network_or_cache_fetch( } // TODO(#33616): Step 11. Set response’s URL list to a clone of httpRequest’s URL list. - // TODO(#33616): Step 12. If httpRequest’s header list contains `Range`, - // then set response’s range-requested flag. + + // Step 12. If httpRequest’s header list contains `Range`, then set response’s range-requested flag. + if http_request.headers.contains_key(RANGE) { + response.range_requested = true; + } + // TODO(#33616): Step 13 Set response’s request-includes-credentials to includeCredentials. // Step 14. If response’s status is 401, httpRequest’s response tainting is not "cors", diff --git a/components/net/protocols/blob.rs b/components/net/protocols/blob.rs index 991cc2a6453..53c609c91a7 100644 --- a/components/net/protocols/blob.rs +++ b/components/net/protocols/blob.rs @@ -54,6 +54,7 @@ impl ProtocolHandler for BlobProtocolHander { response.status = HttpStatus::default(); if is_range_request { + response.range_requested = true; partial_content(&mut response); } diff --git a/components/shared/net/response.rs b/components/shared/net/response.rs index 06ca0687da2..f91993ddccb 100644 --- a/components/shared/net/response.rs +++ b/components/shared/net/response.rs @@ -120,6 +120,9 @@ pub struct Response { /// track network metrics #[ignore_malloc_size_of = "Mutex heap size undefined"] pub resource_timing: Arc>, + + /// + pub range_requested: bool, } impl Response { @@ -142,6 +145,7 @@ impl Response { return_internal: true, aborted: Arc::new(AtomicBool::new(false)), resource_timing: Arc::new(Mutex::new(resource_timing)), + range_requested: false, } } @@ -175,6 +179,7 @@ impl Response { resource_timing: Arc::new(Mutex::new(ResourceFetchTiming::new( ResourceTimingType::Error, ))), + range_requested: false, } }