Append the Sec-Purpose header for prefetch requests (#33490)

Signed-off-by: Shane Handley <shanehandley@fastmail.com>
This commit is contained in:
shanehandley 2024-09-18 22:20:05 +10:00 committed by GitHub
parent aa5bf94b35
commit 777a3ec13f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -41,6 +41,7 @@ use net_traits::request::{
is_cors_safelisted_method, is_cors_safelisted_request_header, BodyChunkRequest, is_cors_safelisted_method, is_cors_safelisted_request_header, BodyChunkRequest,
BodyChunkResponse, CacheMode, CredentialsMode, Destination, Initiator, Origin, RedirectMode, BodyChunkResponse, CacheMode, CredentialsMode, Destination, Initiator, Origin, RedirectMode,
Referrer, Request, RequestBuilder, RequestMode, ResponseTainting, ServiceWorkersMode, Referrer, Request, RequestBuilder, RequestMode, ResponseTainting, ServiceWorkersMode,
Window as RequestWindow,
}; };
use net_traits::response::{HttpsState, Response, ResponseBody, ResponseType}; use net_traits::response::{HttpsState, Response, ResponseBody, ResponseType};
use net_traits::{ use net_traits::{
@ -1095,27 +1096,33 @@ async fn http_network_or_cache_fetch(
done_chan: &mut DoneChannel, done_chan: &mut DoneChannel,
context: &FetchContext, context: &FetchContext,
) -> Response { ) -> Response {
// Step 2 // Step 3: Let httpRequest be null.
let mut http_request;
// Step 4: Let response be null.
let mut response: Option<Response> = None; let mut response: Option<Response> = None;
// Step 4
// Step 7: Let the revalidatingFlag be unset.
let mut revalidating_flag = false; let mut revalidating_flag = false;
// TODO: Implement Window enum for Request // Step 8.1: If requests window is "no-window" and requests redirect mode is "error", then set
let request_has_no_window = true; // httpFetchParams to fetchParams and httpRequest to request.
let request_has_no_window = request.window == RequestWindow::NoWindow;
// Step 5.1
let mut http_request;
let http_request = if request_has_no_window && request.redirect_mode == RedirectMode::Error { let http_request = if request_has_no_window && request.redirect_mode == RedirectMode::Error {
request request
} else { } else {
// Step 5.2.1, .2.2 and .2.3 and 2.4 // Step 8.2.1: Set httpRequest to a clone of request.
http_request = request.clone(); http_request = request.clone();
&mut http_request &mut http_request
}; };
// Step 5.3 // Step 8.3: Let includeCredentials be true if one of:
let credentials_flag = match http_request.credentials_mode { let include_credentials = match http_request.credentials_mode {
// requests credentials mode is "include"
CredentialsMode::Include => true, CredentialsMode::Include => true,
// requests credentials mode is "same-origin" and requests response tainting is "basic"
CredentialsMode::CredentialsSameOrigin CredentialsMode::CredentialsSameOrigin
if http_request.response_tainting == ResponseTainting::Basic => if http_request.response_tainting == ResponseTainting::Basic =>
{ {
@ -1124,33 +1131,42 @@ async fn http_network_or_cache_fetch(
_ => false, _ => false,
}; };
// Step 8.4: If Cross-Origin-Embedder-Policy allows credentials with request returns false, then
// set includeCredentials to false.
// TODO: Requires request's client object
// Step 8.5: Let contentLength be httpRequests bodys length, if httpRequests body is
// non-null; otherwise null.
let content_length_value = match http_request.body { let content_length_value = match http_request.body {
Some(ref http_request_body) => http_request_body.len().map(|size| size as u64),
// Step 8.7: If httpRequests body is null and httpRequests method is `POST` or `PUT`, then
// set contentLengthHeaderValue to `0`.
None => match http_request.method { None => match http_request.method {
// Step 5.5
Method::POST | Method::PUT => Some(0), Method::POST | Method::PUT => Some(0),
// Step 5.4
_ => None, _ => None,
}, },
// Step 5.6
Some(ref http_request_body) => http_request_body.len().map(|size| size as u64),
}; };
// Step 5.7 // Step 8.9: If contentLengthHeaderValue is non-null, then append (`Content-Length`,
// contentLengthHeaderValue) to httpRequests header list.
if let Some(content_length_value) = content_length_value { if let Some(content_length_value) = content_length_value {
http_request http_request
.headers .headers
.typed_insert(ContentLength(content_length_value)); .typed_insert(ContentLength(content_length_value));
if http_request.keep_alive {
// Step 5.8 TODO: needs request's client object
}
} }
// Step 5.9 // Step 8.10: If contentLength is non-null and httpRequests keepalive is true, then:
// TODO Keepalive requires request's client object's fetch group
// Step 8.11: If httpRequests referrer is a URL, then:
match http_request.referrer { match http_request.referrer {
Referrer::NoReferrer => (), Referrer::NoReferrer => (),
Referrer::ReferrerUrl(ref http_request_referrer) | Referrer::ReferrerUrl(ref http_request_referrer) |
Referrer::Client(ref http_request_referrer) => { Referrer::Client(ref http_request_referrer) => {
// Step 8.11.1: Let referrerValue be httpRequests referrer, serialized and isomorphic
// encoded.
if let Ok(referer) = http_request_referrer.to_string().parse::<Referer>() { if let Ok(referer) = http_request_referrer.to_string().parse::<Referer>() {
// Step 8.11.2: Append (`Referer`, referrerValue) to httpRequests header list.
http_request.headers.typed_insert(referer); http_request.headers.typed_insert(referer);
} else { } else {
// This error should only happen in cases where hyper and rust-url disagree // This error should only happen in cases where hyper and rust-url disagree
@ -1161,7 +1177,7 @@ async fn http_network_or_cache_fetch(
}, },
}; };
// Step 5.10 // Step 8.12: Append a request `Origin` header for httpRequest.
if cors_flag || (http_request.method != Method::GET && http_request.method != Method::HEAD) { if cors_flag || (http_request.method != Method::GET && http_request.method != Method::HEAD) {
debug_assert_ne!(http_request.origin, Origin::Client); debug_assert_ne!(http_request.origin, Origin::Client);
if let Origin::Origin(ref url_origin) = http_request.origin { if let Origin::Origin(ref url_origin) = http_request.origin {
@ -1171,7 +1187,19 @@ async fn http_network_or_cache_fetch(
} }
} }
// Step 5.11 // Step 8.13: Append the Fetch metadata headers for httpRequest.
// TODO Implement Sec-Fetch-* headers
// Step 8.14: If httpRequests initiator is "prefetch", then set a structured field value given
// (`Sec-Purpose`, the token "prefetch") in httpRequests header list.
if http_request.initiator == Initiator::Prefetch {
if let Ok(value) = HeaderValue::from_str("prefetch") {
http_request.headers.insert("Sec-Purpose", value);
}
}
// Step 8.15: If httpRequests header list does not contain `User-Agent`, then user agents
// should append (`User-Agent`, default `User-Agent` value) to httpRequests header list.
if !http_request.headers.contains_key(header::USER_AGENT) { if !http_request.headers.contains_key(header::USER_AGENT) {
let user_agent = context.user_agent.clone().into_owned(); let user_agent = context.user_agent.clone().into_owned();
http_request http_request
@ -1223,7 +1251,7 @@ async fn http_network_or_cache_fetch(
// Step 5.17 // Step 5.17
// TODO some of this step can't be implemented yet // TODO some of this step can't be implemented yet
if credentials_flag { if include_credentials {
// Substep 1 // Substep 1
// TODO http://mxr.mozilla.org/servo/source/components/net/http_loader.rs#504 // TODO http://mxr.mozilla.org/servo/source/components/net/http_loader.rs#504
// XXXManishearth http_loader has block_cookies: support content blocking here too // XXXManishearth http_loader has block_cookies: support content blocking here too
@ -1436,7 +1464,7 @@ async fn http_network_or_cache_fetch(
if response.is_none() { if response.is_none() {
// Substep 2 // Substep 2
let forward_response = let forward_response =
http_network_fetch(http_request, credentials_flag, done_chan, context).await; http_network_fetch(http_request, include_credentials, done_chan, context).await;
// Substep 3 // Substep 3
if let Some((200..=399, _)) = forward_response.raw_status { if let Some((200..=399, _)) = forward_response.raw_status {
if !http_request.method.is_safe() { if !http_request.method.is_safe() {
@ -1555,7 +1583,7 @@ async fn http_network_or_cache_fetch(
// Step 10 // Step 10
// FIXME: Figure out what to do with request window objects // FIXME: Figure out what to do with request window objects
if let (Some((StatusCode::UNAUTHORIZED, _)), false, true) = if let (Some((StatusCode::UNAUTHORIZED, _)), false, true) =
(response.status.as_ref(), cors_flag, credentials_flag) (response.status.as_ref(), cors_flag, include_credentials)
{ {
// Substep 1 // Substep 1
// TODO: Spec says requires testing on multiple WWW-Authenticate headers // TODO: Spec says requires testing on multiple WWW-Authenticate headers