1use std::cmp::min;
6use std::collections::HashSet;
7use std::iter::FromIterator;
8use std::sync::Arc as StdArc;
9use std::sync::atomic::{AtomicBool, Ordering};
10use std::time::{Duration, SystemTime};
11
12use async_recursion::async_recursion;
13use content_security_policy::percent_encoding::{AsciiSet, CONTROLS, utf8_percent_encode};
14use devtools_traits::ChromeToDevtoolsControlMsg;
15use embedder_traits::{AuthenticationResponse, GenericEmbedderProxy};
16use futures::{TryFutureExt, TryStreamExt, future};
17use headers::authorization::Basic;
18use headers::{
19 AccessControlAllowCredentials, AccessControlAllowHeaders, AccessControlAllowMethods,
20 AccessControlMaxAge, AccessControlRequestMethod, Authorization, CacheControl, ContentLength,
21 HeaderMapExt, IfModifiedSince, LastModified, Pragma, Referer, StrictTransportSecurity,
22 UserAgent,
23};
24use http::header::{
25 self, ACCEPT, ACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_REQUEST_HEADERS, AUTHORIZATION,
26 CONTENT_ENCODING, CONTENT_LANGUAGE, CONTENT_LOCATION, CONTENT_TYPE, HeaderValue, RANGE,
27 WWW_AUTHENTICATE,
28};
29use http::{HeaderMap, Method, Request as HyperRequest, StatusCode};
30use http_body_util::combinators::BoxBody;
31use http_body_util::{BodyExt, Full};
32use hyper::Response as HyperResponse;
33use hyper::body::{Bytes, Frame};
34use hyper::ext::ReasonPhrase;
35use hyper::header::{HeaderName, TRANSFER_ENCODING};
36use ipc_channel::IpcError;
37use ipc_channel::ipc::{self, IpcSender};
38use ipc_channel::router::ROUTER;
39use log::{debug, error, info, log_enabled, warn};
40use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
41use net_traits::blob_url_store::UrlWithBlobClaim;
42use net_traits::fetch::headers::get_value_from_header_list;
43use net_traits::http_status::HttpStatus;
44use net_traits::policy_container::{EmbedderPolicyValue, RequestPolicyContainer};
45use net_traits::pub_domains::{is_same_site, reg_suffix};
46use net_traits::request::{
47 BodyChunkRequest, BodyChunkResponse, CacheMode, CredentialsMode, Destination, Initiator,
48 Origin, RedirectMode, Referrer, Request, RequestBuilder, RequestClient, RequestMode,
49 ResponseTainting, ServiceWorkersMode, TraversableForUserPrompts, get_cors_unsafe_header_names,
50 is_cors_non_wildcard_request_header_name, is_cors_safelisted_method,
51 is_cors_safelisted_request_header,
52};
53use net_traits::response::{CacheState, RedirectTaint, Response, ResponseBody, ResponseType};
54use net_traits::{
55 CookieSource, DOCUMENT_ACCEPT_HEADER_VALUE, DiscardFetch, NetworkError, RedirectEndValue,
56 RedirectStartValue, ReferrerPolicy, ResourceAttribute, ResourceFetchTimingContainer,
57 ResourceTimeValue, TlsSecurityInfo, TlsSecurityState,
58};
59use parking_lot::{Mutex, RwLock};
60use profile_traits::mem::{Report, ReportKind};
61use profile_traits::path;
62#[cfg(feature = "tracing")]
63use profile_traits::trace_span;
64use rustc_hash::FxHashMap;
65use servo_base::cross_process_instant::CrossProcessInstant;
66use servo_base::generic_channel::GenericSharedMemory;
67use servo_base::id::{BrowsingContextId, HistoryStateId, PipelineId};
68use servo_config::pref;
69use servo_url::{ImmutableOrigin, ServoUrl};
70use tokio::sync::mpsc::{
71 Receiver as TokioReceiver, Sender as TokioSender, UnboundedReceiver, UnboundedSender, channel,
72 unbounded_channel,
73};
74use tokio_stream::wrappers::ReceiverStream;
75#[cfg(feature = "tracing")]
76use tracing::Instrument;
77
78use crate::async_runtime::spawn_task;
79use crate::connector::{
80 CertificateErrorOverrideManager, ServoClient, TlsHandshakeInfo, create_tls_config,
81};
82use crate::cookie::ServoCookie;
83use crate::cookie_storage::CookieStorage;
84use crate::decoder::Decoder;
85use crate::devtools::{
86 prepare_devtools_request, send_request_to_devtools, send_response_values_to_devtools,
87};
88use crate::embedder::NetToEmbedderMsg;
89use crate::fetch::cors_cache::CorsCache;
90use crate::fetch::fetch_params::FetchParams;
91use crate::fetch::headers::{SecFetchDest, SecFetchMode, SecFetchSite, SecFetchUser};
92use crate::fetch::methods::{Data, DoneChannel, FetchContext, Target, fetch, main_fetch};
93use crate::hsts::HstsList;
94use crate::http_cache::{
95 CacheKey, CachedResourcesOrGuard, HttpCache, ValidationStatus, construct_response,
96 invalidate_cached_resources, refresh,
97};
98use crate::resource_thread::{AuthCache, AuthCacheEntry};
99use crate::websocket_loader::start_websocket;
100
101#[derive(Clone, Debug, Eq, PartialEq)]
103pub enum HttpCacheEntryState {
104 ReadyToConstruct,
108 PendingStore(usize),
110}
111
112pub struct HttpState {
113 pub hsts_list: RwLock<HstsList>,
114 pub cookie_jar: RwLock<CookieStorage>,
115 pub http_cache: HttpCache,
116 pub auth_cache: RwLock<AuthCache>,
117 pub history_states: RwLock<FxHashMap<HistoryStateId, Vec<u8>>>,
118 pub client: ServoClient,
119 pub override_manager: CertificateErrorOverrideManager,
120 pub embedder_proxy: GenericEmbedderProxy<NetToEmbedderMsg>,
121}
122
123impl HttpState {
124 pub(crate) fn memory_reports(&self, suffix: &str, ops: &mut MallocSizeOfOps) -> Vec<Report> {
125 vec![
126 Report {
127 path: path!["memory-cache", suffix],
128 kind: ReportKind::ExplicitJemallocHeapSize,
129 size: self.http_cache.size_of(ops),
130 },
131 Report {
132 path: path!["hsts-list", suffix],
133 kind: ReportKind::ExplicitJemallocHeapSize,
134 size: self.hsts_list.read().size_of(ops),
135 },
136 ]
137 }
138
139 async fn request_authentication(
140 &self,
141 request: &Request,
142 response: &Response,
143 ) -> Option<AuthenticationResponse> {
144 let webview_id = request.target_webview_id?;
146 let for_proxy = response.status == StatusCode::PROXY_AUTHENTICATION_REQUIRED;
147
148 if request.mode != RequestMode::Navigate {
150 return None;
151 }
152
153 let (sender, receiver) = tokio::sync::oneshot::channel();
154 self.embedder_proxy
155 .send(NetToEmbedderMsg::RequestAuthentication(
156 webview_id,
157 request.url(),
158 for_proxy,
159 sender,
160 ));
161 receiver.await.ok()?
162 }
163}
164
165pub(crate) fn set_default_accept(request: &mut Request) {
167 if request.headers.contains_key(header::ACCEPT) {
169 return;
170 }
171
172 let value = if request.initiator == Initiator::Prefetch {
174 DOCUMENT_ACCEPT_HEADER_VALUE
175 } else {
176 match request.destination {
179 Destination::Document | Destination::Frame | Destination::IFrame => {
180 DOCUMENT_ACCEPT_HEADER_VALUE
181 },
182 Destination::Image => {
183 HeaderValue::from_static("image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5")
184 },
185 Destination::Json => HeaderValue::from_static("application/json,*/*;q=0.5"),
186 Destination::Style => HeaderValue::from_static("text/css,*/*;q=0.1"),
187 _ => HeaderValue::from_static("*/*"),
189 }
190 };
191
192 request.headers.insert(header::ACCEPT, value);
194}
195
196fn set_default_accept_encoding(headers: &mut HeaderMap) {
197 if headers.contains_key(header::ACCEPT_ENCODING) {
198 return;
199 }
200
201 headers.insert(
203 header::ACCEPT_ENCODING,
204 HeaderValue::from_static("gzip, deflate, br, zstd"),
205 );
206}
207
208fn no_referrer_when_downgrade(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
210 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
212 return None;
213 }
214 strip_url_for_use_as_referrer(referrer_url, false)
216}
217
218fn strict_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
220 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
222 return None;
223 }
224 strip_url_for_use_as_referrer(referrer_url, true)
226}
227
228fn strict_origin_when_cross_origin(
230 referrer_url: ServoUrl,
231 current_url: ServoUrl,
232) -> Option<ServoUrl> {
233 if referrer_url.origin() == current_url.origin() {
235 return strip_url_for_use_as_referrer(referrer_url, false);
236 }
237 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
239 return None;
240 }
241 strip_url_for_use_as_referrer(referrer_url, true)
243}
244
245fn is_schemelessy_same_site(site_a: &ImmutableOrigin, site_b: &ImmutableOrigin) -> bool {
247 if !site_a.is_tuple() && !site_b.is_tuple() && site_a == site_b {
249 true
250 } else if site_a.is_tuple() && site_b.is_tuple() {
251 let host_a = site_a.host().map(|h| h.to_string()).unwrap_or_default();
253 let host_b = site_b.host().map(|h| h.to_string()).unwrap_or_default();
254
255 let host_a_reg = reg_suffix(&host_a);
256 let host_b_reg = reg_suffix(&host_b);
257
258 (site_a.host() == site_b.host() && host_a_reg.is_empty()) ||
260 (host_a_reg == host_b_reg && !host_a_reg.is_empty())
261 } else {
262 false
264 }
265}
266
267fn strip_url_for_use_as_referrer(mut url: ServoUrl, origin_only: bool) -> Option<ServoUrl> {
269 const MAX_REFERRER_URL_LENGTH: usize = 4096;
270 if url.is_local_scheme() {
272 return None;
273 }
274 {
276 let url = url.as_mut_url();
277 let _ = url.set_username("");
278 let _ = url.set_password(None);
279 url.set_fragment(None);
280 if origin_only || url.as_str().len() > MAX_REFERRER_URL_LENGTH {
284 url.set_path("");
285 url.set_query(None);
286 }
287 }
288 Some(url)
290}
291
292fn same_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
294 if referrer_url.origin() == current_url.origin() {
296 return strip_url_for_use_as_referrer(referrer_url, false);
297 }
298 None
300}
301
302fn origin_when_cross_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
304 if referrer_url.origin() == current_url.origin() {
306 return strip_url_for_use_as_referrer(referrer_url, false);
307 }
308 strip_url_for_use_as_referrer(referrer_url, true)
310}
311
312pub fn determine_requests_referrer(
314 referrer_policy: ReferrerPolicy,
315 referrer_source: ServoUrl,
316 current_url: ServoUrl,
317) -> Option<ServoUrl> {
318 match referrer_policy {
319 ReferrerPolicy::EmptyString | ReferrerPolicy::NoReferrer => None,
320 ReferrerPolicy::Origin => strip_url_for_use_as_referrer(referrer_source, true),
321 ReferrerPolicy::UnsafeUrl => strip_url_for_use_as_referrer(referrer_source, false),
322 ReferrerPolicy::StrictOrigin => strict_origin(referrer_source, current_url),
323 ReferrerPolicy::StrictOriginWhenCrossOrigin => {
324 strict_origin_when_cross_origin(referrer_source, current_url)
325 },
326 ReferrerPolicy::SameOrigin => same_origin(referrer_source, current_url),
327 ReferrerPolicy::OriginWhenCrossOrigin => {
328 origin_when_cross_origin(referrer_source, current_url)
329 },
330 ReferrerPolicy::NoReferrerWhenDowngrade => {
331 no_referrer_when_downgrade(referrer_source, current_url)
332 },
333 }
334}
335
336fn set_request_cookies(
337 url: &ServoUrl,
338 headers: &mut HeaderMap,
339 cookie_jar: &RwLock<CookieStorage>,
340) {
341 let mut cookie_jar = cookie_jar.write();
342 cookie_jar.remove_expired_cookies_for_url(url);
343 if let Some(cookie_list) = cookie_jar.cookies_for_url(url, CookieSource::HTTP) {
344 headers.insert(
345 header::COOKIE,
346 HeaderValue::from_bytes(cookie_list.as_bytes()).unwrap(),
347 );
348 }
349}
350
351fn set_cookie_for_url(cookie_jar: &RwLock<CookieStorage>, request: &ServoUrl, cookie_val: &str) {
352 let mut cookie_jar = cookie_jar.write();
353 let source = CookieSource::HTTP;
354
355 if let Some(cookie) = ServoCookie::from_cookie_string(cookie_val, request, source) {
356 cookie_jar.push(cookie, request, source);
357 }
358}
359
360fn set_cookies_from_headers(
361 url: &ServoUrl,
362 headers: &HeaderMap,
363 cookie_jar: &RwLock<CookieStorage>,
364) {
365 for cookie in headers.get_all(header::SET_COOKIE) {
366 let cookie_bytes = cookie.as_bytes();
367 if !ServoCookie::is_valid_name_or_value(cookie_bytes) {
368 continue;
369 }
370 if let Ok(cookie_str) = std::str::from_utf8(cookie_bytes) {
371 set_cookie_for_url(cookie_jar, url, cookie_str);
372 }
373 }
374}
375
376fn build_tls_security_info(handshake: &TlsHandshakeInfo, hsts_enabled: bool) -> TlsSecurityInfo {
377 let state = if handshake.protocol_version.is_none() || handshake.cipher_suite.is_none() {
385 TlsSecurityState::Insecure
387 } else {
388 TlsSecurityState::Secure
390 };
391
392 TlsSecurityInfo {
393 state,
394 weakness_reasons: Vec::new(), protocol_version: handshake.protocol_version.clone(),
396 cipher_suite: handshake.cipher_suite.clone(),
397 kea_group_name: handshake.kea_group_name.clone(),
398 signature_scheme_name: handshake.signature_scheme_name.clone(),
399 alpn_protocol: handshake.alpn_protocol.clone(),
400 certificate_chain_der: handshake.certificate_chain_der.clone(),
401 certificate_transparency: None,
402 hsts: hsts_enabled,
403 hpkp: false,
404 used_ech: handshake.used_ech,
405 used_delegated_credentials: false,
406 used_ocsp: false,
407 used_private_dns: false,
408 }
409}
410
411fn auth_from_cache(
412 auth_cache: &RwLock<AuthCache>,
413 origin: &ImmutableOrigin,
414) -> Option<Authorization<Basic>> {
415 if let Some(auth_entry) = auth_cache.read().entries.get(&origin.ascii_serialization()) {
416 let user_name = &auth_entry.user_name;
417 let password = &auth_entry.password;
418 Some(Authorization::basic(user_name, password))
419 } else {
420 None
421 }
422}
423
424enum BodyChunk {
427 Chunk(GenericSharedMemory),
429 Done,
431}
432
433enum BodyStream {
435 Chunked(TokioReceiver<Result<Frame<Bytes>, hyper::Error>>),
438 Buffered(UnboundedReceiver<BodyChunk>),
441}
442
443enum BodySink {
446 Chunked(TokioSender<Result<Frame<Bytes>, hyper::Error>>),
448 Buffered(UnboundedSender<BodyChunk>),
452}
453
454impl BodySink {
455 fn transmit_bytes(&self, bytes: GenericSharedMemory) {
456 match self {
457 BodySink::Chunked(sender) => {
458 let sender = sender.clone();
459 spawn_task(async move {
460 let _ = sender
461 .send(Ok(Frame::data(Bytes::copy_from_slice(&bytes))))
462 .await;
463 });
464 },
465 BodySink::Buffered(sender) => {
466 let _ = sender.send(BodyChunk::Chunk(bytes));
467 },
468 }
469 }
470
471 fn close(&self) {
472 match self {
473 BodySink::Chunked(_) => { },
474 BodySink::Buffered(sender) => {
475 let _ = sender.send(BodyChunk::Done);
476 },
477 }
478 }
479}
480
481fn request_body_stream_closed_error(action: &str) -> NetworkError {
482 NetworkError::Crash(format!(
483 "Request body stream has already been closed while trying to {action}."
484 ))
485}
486
487fn log_request_body_stream_closed(action: &str, error: Option<&IpcError>) {
488 match error {
489 Some(error) => {
490 error!("Request body stream has already been closed while trying to {action}: {error}")
491 },
492 None => error!("Request body stream has already been closed while trying to {action}."),
493 }
494}
495
496fn log_fetch_terminated_send_failure(terminated_with_error: bool, context: &str) {
497 warn!(
498 "Failed to notify request-body stream termination state ({terminated_with_error}) while {context} because the receiver was already dropped."
499 );
500}
501
502const FRAGMENT: &AsciiSet = &CONTROLS.add(b'|').add(b'{').add(b'}');
503
504#[allow(clippy::too_many_arguments)]
505#[servo_tracing::instrument(skip_all, fields(url=url.as_str()))]
506async fn obtain_response(
508 client: &ServoClient,
509 url: &ServoUrl,
510 method: &Method,
511 request_headers: &mut HeaderMap,
512 body_sender: Option<StdArc<Mutex<Option<IpcSender<BodyChunkRequest>>>>>,
513 source_is_null: bool,
514 pipeline_id: &Option<PipelineId>,
515 request_id: Option<&str>,
516 destination: Destination,
517 is_xhr: bool,
518 context: &FetchContext,
519 fetch_terminated: UnboundedSender<bool>,
520 browsing_context_id: Option<BrowsingContextId>,
521) -> Result<(HyperResponse<Decoder>, Option<ChromeToDevtoolsControlMsg>), NetworkError> {
522 let mut headers = request_headers.clone();
523
524 let devtools_bytes = StdArc::new(Mutex::new(vec![]));
525
526 let encoded_url = utf8_percent_encode(url.as_str(), FRAGMENT).to_string();
528
529 let request = if let Some(chunk_requester) = body_sender {
530 let (sink, stream) = if source_is_null {
531 headers.insert(TRANSFER_ENCODING, HeaderValue::from_static("chunked"));
534
535 let (sender, receiver) = channel(1);
536 (BodySink::Chunked(sender), BodyStream::Chunked(receiver))
537 } else {
538 let (sender, receiver) = unbounded_channel();
545 (BodySink::Buffered(sender), BodyStream::Buffered(receiver))
546 };
547
548 obtain_response_setup_router_callback(
549 devtools_bytes.clone(),
550 chunk_requester,
551 sink,
552 fetch_terminated,
553 )?;
554
555 let body = match stream {
556 BodyStream::Chunked(receiver) => {
557 let stream = ReceiverStream::new(receiver);
558 BoxBody::new(http_body_util::StreamBody::new(stream))
559 },
560 BodyStream::Buffered(mut receiver) => {
561 let mut body = vec![];
563 loop {
564 match receiver.recv().await {
565 Some(BodyChunk::Chunk(bytes)) => {
566 body.extend_from_slice(&bytes);
567 },
568 Some(BodyChunk::Done) => break,
569 None => warn!("Failed to read all chunks from request body."),
570 }
571 }
572 Full::new(body.into()).map_err(|_| unreachable!()).boxed()
573 },
574 };
575 HyperRequest::builder()
576 .method(method)
577 .uri(encoded_url)
578 .body(body)
579 } else {
580 HyperRequest::builder()
581 .method(method)
582 .uri(encoded_url)
583 .body(
584 http_body_util::Empty::new()
585 .map_err(|_| unreachable!())
586 .boxed(),
587 )
588 };
589
590 let connect_start = CrossProcessInstant::now();
593 context.timing.set_attributes(&[
594 ResourceAttribute::DomainLookupStart,
595 ResourceAttribute::ConnectStart(connect_start),
596 ]);
597
598 if url.scheme() == "https" {
602 context
603 .timing
604 .set_attribute(ResourceAttribute::SecureConnectionStart);
605 }
606
607 let mut request = match request {
608 Ok(request) => request,
609 Err(error) => return Err(NetworkError::HttpError(error.to_string())),
610 };
611 *request.headers_mut() = headers.clone();
612
613 let connect_end = CrossProcessInstant::now();
614 context
615 .timing
616 .set_attribute(ResourceAttribute::ConnectEnd(connect_end));
617
618 let request_id = request_id.map(|v| v.to_owned());
619 let pipeline_id = *pipeline_id;
620 let closure_url = url.clone();
621 let method = method.clone();
622 let send_start = CrossProcessInstant::now();
623
624 let host = request.uri().host().unwrap_or("").to_owned();
625 let override_manager = context.state.override_manager.clone();
626 let headers = headers.clone();
627 let is_secure_scheme = url.is_secure_scheme();
628
629 context
633 .timing
634 .set_attribute(ResourceAttribute::RequestStart);
635
636 let client_future = client
637 .request(request)
638 .and_then(move |res| {
639 let send_end = CrossProcessInstant::now();
640
641 let msg = if let Some(request_id) = request_id {
644 if let Some(pipeline_id) = pipeline_id {
645 if let Some(browsing_context_id) = browsing_context_id {
646 Some(prepare_devtools_request(
647 request_id,
648 closure_url,
649 method.clone(),
650 headers,
651 Some(devtools_bytes.lock().clone()),
652 pipeline_id,
653 (connect_end - connect_start).unsigned_abs(),
654 (send_end - send_start).unsigned_abs(),
655 destination,
656 is_xhr,
657 browsing_context_id,
658 ))
659 } else {
660 debug!("Not notifying devtools (no browsing_context_id)");
661 None
662 }
663 } else {
668 debug!("Not notifying devtools (no pipeline_id)");
669 None
670 }
671 } else {
672 debug!("Not notifying devtools (no request_id)");
673 None
674 };
675
676 future::ready(Ok((
677 Decoder::detect(res.map(|r| r.boxed()), is_secure_scheme),
678 msg,
679 )))
680 })
681 .map_err(move |error| {
682 warn!("network error: {error:?}");
683 NetworkError::from_hyper_error(
684 &error,
685 override_manager.remove_certificate_failing_verification(host.as_str()),
686 )
687 });
688
689 #[cfg(feature = "tracing")]
690 {
691 client_future.instrument(trace_span!("HyperRequest")).await
692 }
693
694 #[cfg(not(feature = "tracing"))]
695 {
696 client_future.await
697 }
698}
699
700fn obtain_response_setup_router_callback(
702 devtools_bytes: StdArc<Mutex<Vec<u8>>>,
703 chunk_requester: StdArc<Mutex<Option<IpcSender<BodyChunkRequest>>>>,
704 sink: BodySink,
705 fetch_terminated: UnboundedSender<bool>,
706) -> Result<(), NetworkError> {
707 let (body_chan, body_port) = ipc::channel().unwrap();
708
709 {
710 let mut lock = chunk_requester.lock();
711 if let Some(chunk_requester) = lock.as_mut() {
712 if let Err(error) = chunk_requester.send(BodyChunkRequest::Connect(body_chan)) {
713 log_request_body_stream_closed("connect to the request body stream", Some(&error));
714 return Err(request_body_stream_closed_error(
715 "connect to the request body stream",
716 ));
717 }
718
719 if let Err(error) = chunk_requester.send(BodyChunkRequest::Chunk) {
722 log_request_body_stream_closed(
723 "request the first request body chunk",
724 Some(&error),
725 );
726 return Err(request_body_stream_closed_error(
727 "request the first request body chunk",
728 ));
729 }
730 } else {
731 log_request_body_stream_closed("connect to the request body stream", None);
732 return Err(request_body_stream_closed_error(
733 "connect to the request body stream",
734 ));
735 }
736 }
737
738 ROUTER.add_typed_route(
739 body_port,
740 Box::new(move |message| {
741 info!("Received message");
742 let bytes = match message.unwrap() {
743 BodyChunkResponse::Chunk(bytes) => bytes,
744 BodyChunkResponse::Done => {
745 if fetch_terminated.send(false).is_err() {
747 log_fetch_terminated_send_failure(
748 false,
749 "handling request body completion",
750 );
751 }
752 sink.close();
753
754 return;
755 },
756 BodyChunkResponse::Error => {
757 if fetch_terminated.send(true).is_err() {
761 log_fetch_terminated_send_failure(
762 true,
763 "handling request body stream error",
764 );
765 }
766 sink.close();
767
768 return;
769 },
770 };
771
772 devtools_bytes.lock().extend_from_slice(&bytes);
773
774 sink.transmit_bytes(bytes);
777
778 let mut chunk_requester = chunk_requester.lock();
781 if let Some(chunk_requester) = chunk_requester.as_mut() {
782 if let Err(error) = chunk_requester.send(BodyChunkRequest::Chunk) {
783 log_request_body_stream_closed(
784 "request the next request body chunk",
785 Some(&error),
786 );
787 if fetch_terminated.send(true).is_err() {
788 log_fetch_terminated_send_failure(
789 true,
790 "handling failure to request the next request body chunk",
791 );
792 }
793 sink.close();
794 }
795 } else {
796 log_request_body_stream_closed("request the next request body chunk", None);
797 if fetch_terminated.send(true).is_err() {
798 log_fetch_terminated_send_failure(
799 true,
800 "handling a closed request body stream while requesting the next chunk",
801 );
802 }
803 sink.close();
804 }
805 }),
806 );
807
808 Ok(())
809}
810
811#[async_recursion]
813#[allow(clippy::too_many_arguments)]
814pub(crate) async fn http_fetch(
815 fetch_params: &mut FetchParams,
816 cache: &mut CorsCache,
817 cors_flag: bool,
818 cors_preflight_flag: bool,
819 authentication_fetch_flag: bool,
820 target: Target<'async_recursion>,
821 done_chan: &mut DoneChannel,
822 context: &FetchContext,
823) -> Response {
824 *done_chan = None;
826 let request = &mut fetch_params.request;
828
829 let mut response: Option<Response> = None;
831
832 if request.service_workers_mode == ServiceWorkersMode::All {
834 if let Some(ref res) = response {
839 if (res.response_type == ResponseType::Opaque && request.mode != RequestMode::NoCors) ||
847 (res.response_type == ResponseType::OpaqueRedirect &&
848 request.redirect_mode != RedirectMode::Manual) ||
849 (res.url_list.len() > 1 && request.redirect_mode != RedirectMode::Follow) ||
850 res.is_network_error()
851 {
852 return Response::network_error(NetworkError::ConnectionFailure);
853 }
854
855 }
858 }
859
860 if response.is_none() {
862 if cors_preflight_flag {
864 let method_cache_match = cache.match_method(request, request.method.clone());
865
866 let method_mismatch = !method_cache_match &&
870 (!is_cors_safelisted_method(&request.method) || request.use_cors_preflight);
871
872 let header_mismatch = request.headers.iter().any(|(name, value)| {
875 !cache.match_header(request, name) &&
876 !is_cors_safelisted_request_header(&name, &value)
877 });
878
879 if method_mismatch || header_mismatch {
881 let preflight_response = cors_preflight_fetch(request, cache, context).await;
884 if let Some(error) = preflight_response.get_network_error() {
886 return Response::network_error(error.clone());
887 }
888 }
889 }
890
891 if request.redirect_mode == RedirectMode::Follow {
894 request.service_workers_mode = ServiceWorkersMode::None;
895 }
896
897 let mut fetch_result = http_network_or_cache_fetch(
900 fetch_params,
901 authentication_fetch_flag,
902 cors_flag,
903 done_chan,
904 context,
905 )
906 .await;
907
908 if cors_flag && cors_check(&fetch_params.request, &fetch_result).is_err() {
911 return Response::network_error(NetworkError::CorsGeneral);
912 }
913
914 if let Err(()) = tao_check(&fetch_params.request, &fetch_result) {
917 context.timing.inner().mark_timing_check_failed();
918 }
919 fetch_result.return_internal = false;
920 response = Some(fetch_result);
921 }
922
923 let request = &mut fetch_params.request;
924
925 let mut response = response.unwrap();
927
928 if (request.response_tainting == ResponseTainting::Opaque ||
932 response.response_type == ResponseType::Opaque) &&
933 request.client.as_ref().is_some_and(|client| {
934 cross_origin_resource_policy_check(
935 &request.origin,
936 client,
937 &response,
938 ForNavigation::No,
939 ) == CrossOriginResourcePolicy::Blocked
940 })
941 {
942 return Response::network_error(NetworkError::CrossOriginResponse);
943 }
944
945 if response
947 .actual_response()
948 .status
949 .try_code()
950 .is_some_and(is_redirect_status)
951 {
952 if response.actual_response().status != StatusCode::SEE_OTHER {
959 }
961
962 response = match request.redirect_mode {
964 RedirectMode::Error => Response::network_error(NetworkError::RedirectError),
966 RedirectMode::Manual => {
967 if request.mode == RequestMode::Navigate {
970 let location_url =
974 location_url_for_response(&response, request.current_url().fragment());
975 response.actual_response_mut().location_url = location_url;
976 response
977 } else {
978 response.to_filtered(ResponseType::OpaqueRedirect)
981 }
982 },
983 RedirectMode::Follow => {
984 response.return_internal = true;
988
989 http_redirect_fetch(
992 fetch_params,
993 cache,
994 response,
995 cors_flag,
996 target,
997 done_chan,
998 context,
999 )
1000 .await
1001 },
1002 };
1003 }
1004
1005 response.return_internal = true;
1007 context
1008 .timing
1009 .set_attribute(ResourceAttribute::RedirectCount(
1010 fetch_params.request.redirect_count as u16,
1011 ));
1012
1013 response.resource_timing = context.timing.clone();
1014
1015 response
1017}
1018
1019fn tao_check(request: &Request, response: &Response) -> Result<(), ()> {
1021 let Origin::Origin(ref request_origin) = request.origin else {
1023 unreachable!("origin cannot be \"client\" at this point");
1024 };
1025
1026 let values: Vec<&str> = response
1031 .headers
1032 .get_all("Timing-Allow-Origin")
1033 .iter()
1034 .map(|header_value| header_value.to_str().unwrap_or(""))
1035 .collect();
1036
1037 if values.contains(&"*") {
1039 return Ok(());
1040 }
1041
1042 if values
1045 .iter()
1046 .any(|header_str| *header_str == request_origin.ascii_serialization())
1047 {
1048 return Ok(());
1049 }
1050
1051 if request.mode == RequestMode::Navigate && request.current_url().origin() != *request_origin {
1054 return Err(());
1055 }
1056
1057 if request.response_tainting == ResponseTainting::Basic {
1059 return Ok(());
1060 }
1061
1062 Err(())
1064}
1065
1066struct RedirectEndTimer(Option<ResourceFetchTimingContainer>);
1068
1069impl RedirectEndTimer {
1070 fn neuter(&mut self) {
1071 self.0 = None;
1072 }
1073}
1074
1075impl Drop for RedirectEndTimer {
1076 fn drop(&mut self) {
1077 let RedirectEndTimer(resource_fetch_timing_opt) = self;
1078
1079 resource_fetch_timing_opt.as_ref().map_or((), |t| {
1080 t.set_attribute(ResourceAttribute::RedirectEnd(RedirectEndValue::Zero));
1081 })
1082 }
1083}
1084
1085static REQUEST_BODY_HEADER_NAMES: &[HeaderName] = &[
1087 CONTENT_ENCODING,
1088 CONTENT_LANGUAGE,
1089 CONTENT_LOCATION,
1090 CONTENT_TYPE,
1091];
1092
1093fn location_url_for_response(
1095 response: &Response,
1096 request_fragment: Option<&str>,
1097) -> Option<Result<ServoUrl, String>> {
1098 assert!(
1100 response
1101 .actual_response()
1102 .status
1103 .try_code()
1104 .is_some_and(is_redirect_status)
1105 );
1106 let mut location = response
1108 .actual_response()
1109 .headers
1110 .get(header::LOCATION)
1111 .and_then(|header_value| {
1112 HeaderValue::to_str(header_value)
1113 .map(|location_string| {
1114 ServoUrl::parse_with_base(response.actual_response().url(), location_string)
1116 .map_err(|error| error.to_string())
1117 })
1118 .ok()
1119 });
1120
1121 if let Some(Ok(ref mut location)) = location &&
1123 location.fragment().is_none()
1124 {
1125 location.set_fragment(request_fragment);
1126 }
1127 location
1129}
1130
1131#[async_recursion]
1133pub async fn http_redirect_fetch(
1134 fetch_params: &mut FetchParams,
1135 cache: &mut CorsCache,
1136 mut response: Response,
1137 cors_flag: bool,
1138 target: Target<'async_recursion>,
1139 done_chan: &mut DoneChannel,
1140 context: &FetchContext,
1141) -> Response {
1142 let mut redirect_end_timer = RedirectEndTimer(Some(context.timing.clone()));
1143
1144 let request = &mut fetch_params.request;
1146
1147 assert!(response.return_internal);
1149
1150 let location_url = location_url_for_response(&response, request.current_url().fragment());
1152 response.actual_response_mut().location_url = location_url.clone();
1153
1154 let location_url = match location_url {
1155 None => return response,
1157 Some(Err(err)) => {
1159 return Response::network_error(NetworkError::ResourceLoadError(
1160 "Location URL parse failure: ".to_owned() + &err,
1161 ));
1162 },
1163 Some(Ok(url)) if !matches!(url.scheme(), "http" | "https") => {
1165 return Response::network_error(NetworkError::UnsupportedScheme);
1166 },
1167 Some(Ok(url)) => url,
1168 };
1169
1170 context.timing.set_attributes(&[
1175 ResourceAttribute::RedirectStart(RedirectStartValue::FetchStart),
1176 ResourceAttribute::FetchStart,
1177 ResourceAttribute::StartTime(ResourceTimeValue::FetchStart),
1178 ResourceAttribute::StartTime(ResourceTimeValue::RedirectStart),
1179 ]);
1180
1181 if request.redirect_count >= 20 {
1183 return Response::network_error(NetworkError::TooManyRedirects);
1184 }
1185
1186 request.redirect_count += 1;
1188
1189 let same_origin = match request.origin {
1192 Origin::Origin(ref origin) => *origin == location_url.origin(),
1193 Origin::Client => panic!(
1194 "Request origin should not be client for {}",
1195 request.current_url()
1196 ),
1197 };
1198
1199 let has_credentials = has_credentials(&location_url);
1200
1201 if request.mode == RequestMode::CorsMode && !same_origin && has_credentials {
1202 return Response::network_error(NetworkError::CorsCredentials);
1203 }
1204
1205 if cors_flag && location_url.origin() != request.current_url().origin() {
1206 request.origin = Origin::Origin(ImmutableOrigin::new_opaque());
1207 }
1208
1209 if cors_flag && has_credentials {
1211 return Response::network_error(NetworkError::CorsCredentials);
1212 }
1213
1214 if response.actual_response().status != StatusCode::SEE_OTHER &&
1217 request.body.as_ref().is_some_and(|b| b.source_is_null())
1218 {
1219 return Response::network_error(NetworkError::ConnectionFailure);
1220 }
1221
1222 if response
1224 .actual_response()
1225 .status
1226 .try_code()
1227 .is_some_and(|code| {
1228 ((code == StatusCode::MOVED_PERMANENTLY || code == StatusCode::FOUND) &&
1230 request.method == Method::POST) ||
1231 (code == StatusCode::SEE_OTHER &&
1233 request.method != Method::HEAD &&
1234 request.method != Method::GET)
1235 })
1236 {
1237 request.method = Method::GET;
1239 request.body = None;
1240 for name in REQUEST_BODY_HEADER_NAMES {
1242 request.headers.remove(name);
1243 }
1244 }
1245
1246 if location_url.origin() != request.current_url().origin() {
1250 request.headers.remove(AUTHORIZATION);
1253 }
1254
1255 if let Some(body) = request.body.as_mut() {
1258 body.extract_source();
1259 }
1260
1261 request
1265 .url_list
1266 .push(UrlWithBlobClaim::from_url_without_having_claimed_blob(
1267 location_url,
1268 ));
1269
1270 set_requests_referrer_policy_on_redirect(request, response.actual_response());
1272
1273 let recursive_flag = request.redirect_mode != RedirectMode::Manual;
1276
1277 let fetch_response = main_fetch(
1279 fetch_params,
1280 cache,
1281 recursive_flag,
1282 target,
1283 done_chan,
1284 context,
1285 )
1286 .await;
1287
1288 context.timing.set_attribute(ResourceAttribute::RedirectEnd(
1290 RedirectEndValue::ResponseEnd,
1291 ));
1292 redirect_end_timer.neuter();
1293
1294 fetch_response
1295}
1296
1297#[async_recursion]
1299#[servo_tracing::instrument(skip_all,fields(url=fetch_params.request.url().as_str()))]
1300async fn http_network_or_cache_fetch(
1301 fetch_params: &mut FetchParams,
1302 authentication_fetch_flag: bool,
1303 cors_flag: bool,
1304 done_chan: &mut DoneChannel,
1305 context: &FetchContext,
1306) -> Response {
1307 let http_fetch_params: &mut FetchParams;
1309 let mut fetch_params_copy: FetchParams;
1310
1311 let mut response: Option<Response> = None;
1315
1316 let mut revalidating_flag = false;
1318
1319 let http_request = if fetch_params.request.traversable_for_user_prompts ==
1323 TraversableForUserPrompts::NoTraversable &&
1324 fetch_params.request.redirect_mode == RedirectMode::Error
1325 {
1326 http_fetch_params = fetch_params;
1327 &mut http_fetch_params.request
1328 }
1329 else {
1331 fetch_params_copy =
1334 std::mem::replace(fetch_params, FetchParams::new(fetch_params.request.clone()));
1335 http_fetch_params = &mut fetch_params_copy;
1336
1337 &mut http_fetch_params.request
1338 };
1339
1340 let include_credentials = match http_request.credentials_mode {
1342 CredentialsMode::Include => true,
1344 CredentialsMode::CredentialsSameOrigin
1346 if http_request.response_tainting == ResponseTainting::Basic =>
1347 {
1348 true
1349 },
1350 _ => false,
1351 };
1352
1353 let content_length = http_request
1360 .body
1361 .as_ref()
1362 .and_then(|body| body.len().map(|size| size as u64));
1363
1364 let mut content_length_header_value = None;
1366
1367 if http_request.body.is_none() && matches!(http_request.method, Method::POST | Method::PUT) {
1370 content_length_header_value = Some(0);
1371 }
1372
1373 if let Some(content_length) = content_length {
1377 content_length_header_value = Some(content_length);
1378 };
1379
1380 if let Some(content_length_header_value) = content_length_header_value {
1383 http_request
1384 .headers
1385 .typed_insert(ContentLength(content_length_header_value));
1386 }
1387
1388 if http_request.keep_alive &&
1390 let Some(content_length) = content_length
1391 {
1392 let in_flight_keep_alive_bytes: u64 = context
1397 .in_flight_keep_alive_records
1398 .lock()
1399 .get(
1400 &http_request
1401 .pipeline_id
1402 .expect("Must always set a pipeline ID for keep-alive requests"),
1403 )
1404 .map(|records| {
1405 records
1409 .iter()
1410 .map(|record| {
1411 if record.request_id == http_request.id {
1412 0
1415 } else {
1416 record.keep_alive_body_length
1417 }
1418 })
1419 .sum()
1420 })
1421 .unwrap_or_default();
1422 if content_length + in_flight_keep_alive_bytes > 64 * 1024 {
1424 return Response::network_error(NetworkError::TooManyInFlightKeepAliveRequests);
1425 }
1426 }
1427
1428 match http_request.referrer {
1430 Referrer::ReferrerUrl(ref http_request_referrer) |
1431 Referrer::Client(ref http_request_referrer) => {
1432 if let Ok(referer) = http_request_referrer.as_str().parse::<Referer>() {
1435 http_request.headers.typed_insert(referer);
1437 } else {
1438 error!("Failed to parse {} as referrer", http_request_referrer);
1442 }
1443 },
1444 _ => {},
1445 };
1446
1447 append_a_request_origin_header(http_request);
1449
1450 append_the_fetch_metadata_headers(http_request);
1452
1453 if http_request.initiator == Initiator::Prefetch &&
1456 let Ok(value) = HeaderValue::from_str("prefetch")
1457 {
1458 http_request.headers.insert("Sec-Purpose", value);
1459 }
1460
1461 if !http_request.headers.contains_key(header::USER_AGENT) {
1464 http_request
1465 .headers
1466 .typed_insert::<UserAgent>(context.user_agent.parse().unwrap());
1467 }
1468
1469 append_cache_data_to_headers(http_request);
1471
1472 if http_request.headers.contains_key(header::RANGE) &&
1475 let Ok(value) = HeaderValue::from_str("identity")
1476 {
1477 http_request.headers.insert("Accept-Encoding", value);
1478 }
1479
1480 http_request.headers.remove(header::HOST);
1484 set_default_accept_encoding(&mut http_request.headers);
1486
1487 let current_url = http_request.current_url();
1488
1489 if include_credentials {
1492 set_request_cookies(
1496 ¤t_url,
1497 &mut http_request.headers,
1498 &context.state.cookie_jar,
1499 );
1500 if !http_request.headers.contains_key(header::AUTHORIZATION) {
1502 let mut authorization_value = None;
1504
1505 if let Some(basic) = auth_from_cache(&context.state.auth_cache, ¤t_url.origin()) &&
1507 (!http_request.use_url_credentials || !has_credentials(¤t_url))
1508 {
1509 authorization_value = Some(basic);
1510 }
1511
1512 if authentication_fetch_flag &&
1514 authorization_value.is_none() &&
1515 has_credentials(¤t_url)
1516 {
1517 authorization_value = Some(Authorization::basic(
1518 current_url.username(),
1519 current_url.password().unwrap_or(""),
1520 ));
1521 }
1522
1523 if let Some(basic) = authorization_value {
1525 http_request.headers.typed_insert(basic);
1526 }
1527 }
1528 }
1529
1530 let should_wait = {
1532 let mut cache_guard = block_for_cache_ready(
1534 context,
1535 http_request,
1536 done_chan,
1537 &mut revalidating_flag,
1538 &mut response,
1539 )
1540 .await;
1541
1542 if response.is_none() {
1546 if http_request.cache_mode == CacheMode::OnlyIfCached {
1548 return Response::network_error(NetworkError::CacheError);
1550 }
1551
1552 drop(cache_guard);
1555 let forward_response =
1556 http_network_fetch(http_fetch_params, include_credentials, done_chan, context)
1557 .await;
1558
1559 let http_request = &mut http_fetch_params.request;
1560 let request_key = CacheKey::new(http_request);
1561 cache_guard = context
1562 .state
1563 .http_cache
1564 .get_or_guard(request_key.clone())
1565 .await;
1566 if forward_response.status.in_range(200..=399) && !http_request.method.is_safe() {
1570 if let Some(guard) = cache_guard.try_as_mut() {
1571 invalidate_cached_resources(guard);
1572 }
1573 context
1574 .state
1575 .http_cache
1576 .invalidate_related_urls(http_request, &forward_response, &request_key)
1577 .await;
1578 }
1579
1580 if revalidating_flag && forward_response.status == StatusCode::NOT_MODIFIED {
1582 *done_chan = None;
1585 if let Some(guard) = cache_guard.try_as_mut() {
1586 response = refresh(http_request, forward_response.clone(), done_chan, guard);
1587 }
1588
1589 if let Some(response) = &mut response {
1590 response.cache_state = CacheState::Validated;
1591 }
1592 }
1593
1594 if response.is_none() {
1596 let forward_response = response.insert(forward_response);
1598
1599 if http_request.cache_mode != CacheMode::NoStore {
1602 cache_guard.insert(http_request, forward_response);
1605 }
1606 }
1607 false
1608 } else {
1609 true
1610 }
1611 }; if should_wait {
1614 wait_for_inflight_requests(done_chan, &mut response).await;
1619 }
1620
1621 let http_request = &mut http_fetch_params.request;
1622 let mut response = response.unwrap();
1623
1624 response.url_list = http_request
1626 .url_list
1627 .iter()
1628 .map(|claimed_url| claimed_url.url())
1629 .collect();
1630
1631 if http_request.headers.contains_key(RANGE) {
1633 response.range_requested = true;
1634 }
1635
1636 response.request_includes_credentials = include_credentials;
1638
1639 if response.status.try_code() == Some(StatusCode::UNAUTHORIZED) &&
1645 !cors_flag &&
1646 include_credentials &&
1647 response.headers.contains_key(WWW_AUTHENTICATE)
1648 {
1649 let request = &mut fetch_params.request;
1652
1653 if request.body.is_some() {
1655 }
1657
1658 if !request.use_url_credentials || authentication_fetch_flag {
1660 let Some(credentials) = context
1661 .state
1662 .request_authentication(request, &response)
1663 .await
1664 else {
1665 return response;
1666 };
1667
1668 if let Err(err) = request
1669 .current_url_mut()
1670 .set_username(&credentials.username)
1671 {
1672 error!("error setting username for url: {:?}", err);
1673 return response;
1674 };
1675
1676 if let Err(err) = request
1677 .current_url_mut()
1678 .set_password(Some(&credentials.password))
1679 {
1680 error!("error setting password for url: {:?}", err);
1681 return response;
1682 };
1683 }
1684
1685 *done_chan = None;
1688
1689 response = http_network_or_cache_fetch(
1691 fetch_params,
1692 true, cors_flag,
1694 done_chan,
1695 context,
1696 )
1697 .await;
1698 }
1699
1700 if response.status == StatusCode::PROXY_AUTHENTICATION_REQUIRED {
1702 let request = &mut fetch_params.request;
1703 if request.traversable_for_user_prompts == TraversableForUserPrompts::NoTraversable {
1706 return Response::network_error(NetworkError::ResourceLoadError(
1707 "Can't find Window object".into(),
1708 ));
1709 }
1710
1711 let Some(credentials) = context
1719 .state
1720 .request_authentication(request, &response)
1721 .await
1722 else {
1723 return response;
1724 };
1725
1726 let entry = AuthCacheEntry {
1728 user_name: credentials.username,
1729 password: credentials.password,
1730 };
1731 {
1732 let mut auth_cache = context.state.auth_cache.write();
1733 let key = request.current_url().origin().ascii_serialization();
1734 auth_cache.entries.insert(key, entry);
1735 }
1736
1737 *done_chan = None;
1740
1741 response = http_network_or_cache_fetch(
1743 fetch_params,
1744 false, cors_flag,
1746 done_chan,
1747 context,
1748 )
1749 .await;
1750 }
1751
1752 if authentication_fetch_flag {
1760 }
1762
1763 response
1765}
1766
1767#[servo_tracing::instrument(skip_all)]
1775async fn block_for_cache_ready<'a>(
1776 context: &'a FetchContext,
1777 http_request: &mut Request,
1778 done_chan: &mut DoneChannel,
1779 revalidating_flag: &mut bool,
1780 response: &mut Option<Response>,
1781) -> CachedResourcesOrGuard<'a> {
1782 let entry_key = CacheKey::new(http_request);
1783 let guard_result = context.state.http_cache.get_or_guard(entry_key).await;
1784
1785 match guard_result {
1786 CachedResourcesOrGuard::Guard(_) => {
1787 *done_chan = None;
1788 },
1789 CachedResourcesOrGuard::Value(ref cached_resources) => {
1790 let stored_response = construct_response(http_request, done_chan, cached_resources);
1796 if let Some(response_from_cache) = stored_response {
1798 let response_headers = response_from_cache.response.headers.clone();
1799 let validation_status = response_from_cache.validation_status;
1800 let revalidation_guard = response_from_cache.revalidation_guard.clone();
1801
1802 let (cached_response, needs_synchronous_revalidation) =
1804 match (http_request.cache_mode, &http_request.mode) {
1805 (CacheMode::ForceCache, _) => (Some(response_from_cache.response), false),
1806 (CacheMode::OnlyIfCached, &RequestMode::SameOrigin) => {
1807 (Some(response_from_cache.response), false)
1808 },
1809 (CacheMode::OnlyIfCached, _) |
1810 (CacheMode::NoStore, _) |
1811 (CacheMode::Reload, _) => (None, false),
1812 (_, _) => (
1813 Some(response_from_cache.response),
1814 validation_status ==
1815 (ValidationStatus::Stale {
1816 revalidate_in_background: false,
1817 }),
1818 ),
1819 };
1820
1821 if needs_synchronous_revalidation {
1822 *revalidating_flag = true;
1823 if let Some(http_date) = response_headers.typed_get::<LastModified>() {
1825 let http_date: SystemTime = http_date.into();
1826 http_request
1827 .headers
1828 .typed_insert(IfModifiedSince::from(http_date));
1829 }
1830 if let Some(entity_tag) = response_headers.get(header::ETAG) {
1831 http_request
1832 .headers
1833 .insert(header::IF_NONE_MATCH, entity_tag.clone());
1834 }
1835 } else {
1836 let revalidate_in_background = validation_status ==
1839 (ValidationStatus::Stale {
1840 revalidate_in_background: true,
1841 });
1842 if revalidate_in_background && cached_response.is_some() {
1843 spawn_stale_while_revalidate(context, http_request, revalidation_guard);
1844 }
1845 *response = cached_response;
1846 if let Some(response) = response {
1847 response.cache_state = CacheState::Local;
1848 }
1849 }
1850 if response.is_none() {
1851 *done_chan = None;
1854 }
1855 }
1856 },
1857 }
1858 guard_result
1859}
1860
1861fn spawn_stale_while_revalidate(
1864 context: &FetchContext,
1865 http_request: &Request,
1866 revalidation_guard: StdArc<AtomicBool>,
1867) {
1868 if revalidation_guard
1870 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
1871 .is_err()
1872 {
1873 return;
1874 }
1875
1876 let mut revalidation_request = http_request.clone();
1879 revalidation_request.cache_mode = CacheMode::NoCache;
1880
1881 revalidation_request.service_workers_mode = ServiceWorkersMode::None;
1883
1884 let context = context.clone();
1885 debug!(
1886 "spawning stale-while-revalidate background revalidation for {:?}",
1887 revalidation_request.current_url()
1888 );
1889 spawn_task(async move {
1890 let mut target = DiscardFetch;
1891
1892 let _ = fetch(revalidation_request, &mut target, &context).await;
1893 revalidation_guard.store(false, Ordering::Release);
1894 });
1895}
1896
1897async fn wait_for_inflight_requests(done_chan: &mut DoneChannel, response: &mut Option<Response>) {
1900 if let Some(ref mut ch) = *done_chan {
1901 assert!(response.is_some());
1905
1906 loop {
1907 match ch.1.recv().await {
1908 Some(Data::ContentLength(_)) | Some(Data::Payload(_)) | Some(Data::Error(_)) => {},
1909 Some(Data::Done) => break, Some(Data::Cancelled) => {
1911 break;
1913 },
1914 None => panic!("HTTP cache should always send Done or Cancelled"),
1915 }
1916 }
1917 }
1918 *done_chan = None;
1920}
1921
1922#[derive(PartialEq)]
1926enum CrossOriginResourcePolicy {
1927 Allowed,
1928 Blocked,
1929}
1930
1931enum ForNavigation {
1932 #[expect(dead_code)]
1933 Yes,
1934 No,
1935}
1936
1937fn cross_origin_resource_policy_check(
1939 origin: &Origin,
1940 request_client: &RequestClient,
1941 response: &Response,
1942 for_navigation: ForNavigation,
1943) -> CrossOriginResourcePolicy {
1944 let embedder_policy = &request_client.policy_container.embedder_policy;
1950
1951 if cross_origin_resource_policy_internal_check(
1954 origin,
1955 EmbedderPolicyValue::UnsafeNone,
1956 response,
1957 &for_navigation,
1958 ) == CrossOriginResourcePolicy::Blocked
1959 {
1960 return CrossOriginResourcePolicy::Blocked;
1961 }
1962
1963 if cross_origin_resource_policy_internal_check(
1971 origin,
1972 embedder_policy.value,
1973 response,
1974 &for_navigation,
1975 ) == CrossOriginResourcePolicy::Allowed
1976 {
1977 return CrossOriginResourcePolicy::Allowed;
1978 }
1979
1980 CrossOriginResourcePolicy::Blocked
1985}
1986
1987fn cross_origin_resource_policy_internal_check(
1989 origin: &Origin,
1990 embedder_policy_value: EmbedderPolicyValue,
1991 response: &Response,
1992 for_navigation: &ForNavigation,
1993) -> CrossOriginResourcePolicy {
1994 if let ForNavigation::Yes = for_navigation &&
1996 let EmbedderPolicyValue::UnsafeNone = embedder_policy_value
1997 {
1998 return CrossOriginResourcePolicy::Allowed;
1999 }
2000
2001 let policy = response
2003 .headers
2004 .get(HeaderName::from_static("cross-origin-resource-policy"))
2005 .and_then(|h| h.to_str().ok());
2006
2007 let policy = policy
2009 .filter(|&s| s == "same-origin" || s == "same-site" || s == "cross-origin")
2010 .or(match embedder_policy_value {
2012 EmbedderPolicyValue::UnsafeNone => None,
2014 EmbedderPolicyValue::RequireCorp => Some("same-origin"),
2016 });
2017
2018 match policy {
2020 Some("same-origin") => {
2021 if let Origin::Origin(request_origin) = origin &&
2023 response
2024 .url()
2025 .is_some_and(|url| request_origin == &url.origin())
2026 {
2027 return CrossOriginResourcePolicy::Allowed;
2028 }
2029
2030 CrossOriginResourcePolicy::Blocked
2032 },
2033 Some("same-site") => {
2034 if let Some(response_url) = response.url() {
2035 if let Origin::Origin(request_origin) = origin &&
2039 is_schemelessy_same_site(request_origin, &response_url.origin()) &&
2040 (request_origin.scheme() == Some("https") ||
2041 response_url.scheme() != "https")
2042 {
2043 return CrossOriginResourcePolicy::Allowed;
2044 }
2045 }
2046 CrossOriginResourcePolicy::Blocked
2048 },
2049 _ => CrossOriginResourcePolicy::Allowed,
2052 }
2053}
2054
2055struct ResponseEndTimer(Option<ResourceFetchTimingContainer>);
2057
2058impl ResponseEndTimer {
2059 fn neuter(&mut self) {
2060 self.0 = None;
2061 }
2062}
2063
2064impl Drop for ResponseEndTimer {
2065 fn drop(&mut self) {
2066 let ResponseEndTimer(resource_fetch_timing_opt) = self;
2067
2068 resource_fetch_timing_opt.as_ref().map_or((), |t| {
2069 t.set_attribute(ResourceAttribute::ResponseEnd);
2070 })
2071 }
2072}
2073
2074#[servo_tracing::instrument(skip_all,fields(url=fetch_params.request.url().as_str()))]
2076async fn http_network_fetch(
2077 fetch_params: &mut FetchParams,
2078 credentials_flag: bool,
2079 done_chan: &mut DoneChannel,
2080 context: &FetchContext,
2081) -> Response {
2082 let mut response_end_timer = ResponseEndTimer(Some(context.timing.clone()));
2083
2084 let request = &mut fetch_params.request;
2086
2087 let url = request.current_url();
2097 let request_id = request.id.0.to_string();
2098 if log_enabled!(log::Level::Info) {
2099 info!("{:?} request for {}", request.method, url);
2100 for header in request.headers.iter() {
2101 debug!(" - {:?}", header);
2102 }
2103 }
2104
2105 let is_xhr = request.destination == Destination::None;
2109
2110 let (fetch_terminated_sender, mut fetch_terminated_receiver) = unbounded_channel();
2112
2113 let body = request.body.as_ref().map(|body| body.clone_stream());
2114
2115 if body.is_none() {
2116 let _ = fetch_terminated_sender.send(false);
2121 }
2122
2123 let browsing_context_id = request.target_webview_id.map(Into::into);
2124
2125 let (res, msg) = match &request.mode {
2129 RequestMode::WebSocket {
2131 protocols,
2132 original_url: _,
2133 } => {
2134 let (resource_event_sender, dom_action_receiver) = {
2137 let mut websocket_chan = context.websocket_chan.as_ref().unwrap().lock();
2138 (
2139 websocket_chan.sender.clone(),
2140 websocket_chan.receiver.take().unwrap(),
2141 )
2142 };
2143
2144 let mut tls_config = create_tls_config(
2145 context.ca_certificates.clone(),
2146 context.ignore_certificate_errors,
2147 context.state.override_manager.clone(),
2148 );
2149 tls_config.alpn_protocols = vec!["http/1.1".to_string().into()];
2150
2151 let response = match start_websocket(
2152 context.state.clone(),
2153 resource_event_sender,
2154 protocols,
2155 request,
2156 tls_config,
2157 dom_action_receiver,
2158 )
2159 .await
2160 {
2161 Ok(response) => response,
2162 Err(error) => {
2163 return Response::network_error(NetworkError::WebsocketConnectionFailure(
2164 format!("{error:?}"),
2165 ));
2166 },
2167 };
2168
2169 let response = response.map(|r| match r {
2170 Some(body) => Full::from(body).map_err(|_| unreachable!()).boxed(),
2171 None => http_body_util::Empty::new()
2172 .map_err(|_| unreachable!())
2173 .boxed(),
2174 });
2175 (Decoder::detect(response, url.is_secure_scheme()), None)
2176 },
2177 _ => {
2180 let response_future = obtain_response(
2181 &context.state.client,
2182 &url,
2183 &request.method,
2184 &mut request.headers,
2185 body,
2186 request
2187 .body
2188 .as_ref()
2189 .is_some_and(|body| body.source_is_null()),
2190 &request.pipeline_id,
2191 Some(&request_id),
2192 request.destination,
2193 is_xhr,
2194 context,
2195 fetch_terminated_sender,
2196 browsing_context_id,
2197 );
2198
2199 let (res, msg) = match response_future.await {
2201 Ok(wrapped_response) => wrapped_response,
2202 Err(error) => return Response::network_error(error),
2203 };
2204 (res, msg)
2205 },
2206 };
2207
2208 if log_enabled!(log::Level::Info) {
2209 debug!("{:?} response for {}", res.version(), url);
2210 for header in res.headers().iter() {
2211 debug!(" - {:?}", header);
2212 }
2213 }
2214
2215 match fetch_terminated_receiver.recv().await {
2218 Some(true) => return Response::network_error(NetworkError::ConnectionFailure),
2219 Some(false) => {},
2220 _ => warn!("Failed to receive confirmation request was streamed without error."),
2221 }
2222
2223 let timing = context.timing.inner().clone();
2224 let mut response = Response::new(url.clone(), timing);
2225
2226 if let Some(handshake_info) = res.extensions().get::<TlsHandshakeInfo>() {
2227 let mut hsts_enabled = url
2228 .host_str()
2229 .is_some_and(|host| context.state.hsts_list.read().is_host_secure(host));
2230
2231 if url.scheme() == "https" &&
2232 let Some(sts) = res.headers().typed_get::<StrictTransportSecurity>()
2233 {
2234 hsts_enabled = sts.max_age().as_secs() > 0;
2236 }
2237 response.tls_security_info = Some(build_tls_security_info(handshake_info, hsts_enabled));
2238 }
2239
2240 let status_text = res
2241 .extensions()
2242 .get::<ReasonPhrase>()
2243 .map(ReasonPhrase::as_bytes)
2244 .or_else(|| res.status().canonical_reason().map(str::as_bytes))
2245 .map(Vec::from)
2246 .unwrap_or_default();
2247 response.status = HttpStatus::new(res.status(), status_text);
2248
2249 info!("got {:?} response for {:?}", res.status(), request.url());
2250 response.headers = res.headers().clone();
2251 response.referrer = request.referrer.to_url().cloned();
2252 response.referrer_policy = request.referrer_policy;
2253
2254 let res_body = response.body.clone();
2255
2256 let (done_sender, done_receiver) = unbounded_channel();
2258 *done_chan = Some((done_sender.clone(), done_receiver));
2259
2260 let devtools_sender = context.devtools_chan.clone();
2261 let cancellation_listener = context.cancellation_listener.clone();
2262 if cancellation_listener.cancelled() {
2263 return Response::network_error(NetworkError::LoadCancelled);
2264 }
2265
2266 *res_body.lock() = ResponseBody::Receiving(vec![]);
2267 let res_body2 = res_body.clone();
2268
2269 if let Some(ref sender) = devtools_sender &&
2270 let Some(m) = msg
2271 {
2272 send_request_to_devtools(m, sender);
2273 }
2274
2275 let done_sender2 = done_sender.clone();
2276 let done_sender3 = done_sender.clone();
2277 let timing_ptr2 = context.timing.clone();
2278 let timing_ptr3 = context.timing.clone();
2279 let devtools_request = request.clone();
2280 let url1 = devtools_request.url();
2281 let url2 = url1.clone();
2282
2283 let status = response.status.clone();
2284 let headers = response.headers.clone();
2285 let devtools_chan = context.devtools_chan.clone();
2286
2287 if let Some(possible_length) = res
2288 .headers()
2289 .get(http::header::CONTENT_LENGTH)
2290 .and_then(|header_value| header_value.to_str().ok())
2291 .and_then(|s| s.parse().ok())
2292 .map(|length| min(length, pref!(network_max_content_length) as usize))
2293 {
2294 let _ = done_sender.send(Data::ContentLength(possible_length));
2295 }
2296
2297 spawn_task(
2298 res.into_body()
2299 .try_fold(res_body, move |res_body, chunk| {
2300 if cancellation_listener.cancelled() {
2301 *res_body.lock() = ResponseBody::Done(vec![]);
2302 let _ = done_sender.send(Data::Cancelled);
2303 return future::ready(Err(std::io::Error::new(
2304 std::io::ErrorKind::Interrupted,
2305 "Fetch aborted",
2306 )));
2307 }
2308 if let ResponseBody::Receiving(ref mut body) = *res_body.lock() {
2309 let bytes = chunk;
2310 body.extend_from_slice(&bytes);
2311 let _ = done_sender.send(Data::Payload(bytes.to_vec()));
2312 }
2313 future::ready(Ok(res_body))
2314 })
2315 .and_then(move |res_body| {
2316 debug!("successfully finished response for {:?}", url1);
2317 let mut body = res_body.lock();
2318 let completed_body = match *body {
2319 ResponseBody::Receiving(ref mut body) => std::mem::take(body),
2320 _ => vec![],
2321 };
2322 let devtools_response_body = completed_body.clone();
2323 *body = ResponseBody::Done(completed_body);
2324 send_response_values_to_devtools(
2325 Some(headers),
2326 status,
2327 Some(devtools_response_body),
2328 CacheState::None,
2329 &devtools_request,
2330 devtools_chan,
2331 );
2332 timing_ptr2.set_attribute(ResourceAttribute::ResponseEnd);
2333 let _ = done_sender2.send(Data::Done);
2334 future::ready(Ok(()))
2335 })
2336 .map_err(move |error| {
2337 if let std::io::ErrorKind::InvalidData = error.kind() {
2338 debug!("Content decompression error for {:?}", url2);
2339 let _ = done_sender3.send(Data::Error(NetworkError::DecompressionError));
2340 let mut body = res_body2.lock();
2341
2342 *body = ResponseBody::Done(vec![]);
2343 }
2344 debug!("finished response for {:?}", url2);
2345 let mut body = res_body2.lock();
2346 let completed_body = match *body {
2347 ResponseBody::Receiving(ref mut body) => std::mem::take(body),
2348 _ => vec![],
2349 };
2350 *body = ResponseBody::Done(completed_body);
2351 timing_ptr3.set_attribute(ResourceAttribute::ResponseEnd);
2352 let _ = done_sender3.send(Data::Done);
2353 }),
2354 );
2355
2356 if credentials_flag {
2374 set_cookies_from_headers(&url, &response.headers, &context.state.cookie_jar);
2375 }
2376 context
2377 .state
2378 .hsts_list
2379 .write()
2380 .update_hsts_list_from_response(&url, &response.headers);
2381
2382 response_end_timer.neuter();
2396 response
2397}
2398
2399async fn cors_preflight_fetch(
2401 request: &Request,
2402 cache: &mut CorsCache,
2403 context: &FetchContext,
2404) -> Response {
2405 let mut preflight = RequestBuilder::new(
2410 request.target_webview_id,
2411 request.current_url_with_blob_claim(),
2412 request.referrer.clone(),
2413 )
2414 .method(Method::OPTIONS)
2415 .origin(match &request.origin {
2416 Origin::Client => {
2417 unreachable!("We shouldn't get Client origin in cors_preflight_fetch.")
2418 },
2419 Origin::Origin(origin) => origin.clone(),
2420 })
2421 .pipeline_id(request.pipeline_id)
2422 .initiator(request.initiator)
2423 .destination(request.destination)
2424 .referrer_policy(request.referrer_policy)
2425 .mode(RequestMode::CorsMode)
2426 .response_tainting(ResponseTainting::CorsTainting)
2427 .policy_container(match &request.policy_container {
2428 RequestPolicyContainer::Client => {
2429 unreachable!("We should have a policy container for request in cors_preflight_fetch")
2430 },
2431 RequestPolicyContainer::PolicyContainer(policy_container) => policy_container.clone(),
2432 })
2433 .url_list(
2434 request
2435 .url_list
2436 .iter()
2437 .map(|claimed_url| claimed_url.url())
2438 .collect(),
2439 )
2440 .build();
2441
2442 preflight
2444 .headers
2445 .insert(ACCEPT, HeaderValue::from_static("*/*"));
2446
2447 preflight
2449 .headers
2450 .typed_insert::<AccessControlRequestMethod>(AccessControlRequestMethod::from(
2451 request.method.clone(),
2452 ));
2453
2454 let headers = get_cors_unsafe_header_names(&request.headers);
2456
2457 if !headers.is_empty() {
2459 preflight.headers.insert(
2462 ACCESS_CONTROL_REQUEST_HEADERS,
2463 HeaderValue::from_bytes(itertools::join(headers.iter(), ",").as_bytes())
2464 .unwrap_or(HeaderValue::from_static("")),
2465 );
2466 }
2467
2468 let mut fetch_params = FetchParams::new(preflight);
2471 let response =
2472 http_network_or_cache_fetch(&mut fetch_params, false, false, &mut None, context).await;
2473
2474 if cors_check(request, &response).is_ok() && response.status.code().is_success() {
2476 let mut methods = if response
2479 .headers
2480 .contains_key(header::ACCESS_CONTROL_ALLOW_METHODS)
2481 {
2482 match response.headers.typed_get::<AccessControlAllowMethods>() {
2483 Some(methods) => methods.iter().collect(),
2484 None => {
2486 return Response::network_error(NetworkError::CorsAllowMethods);
2487 },
2488 }
2489 } else {
2490 vec![]
2491 };
2492
2493 let header_names = if response
2496 .headers
2497 .contains_key(header::ACCESS_CONTROL_ALLOW_HEADERS)
2498 {
2499 match response.headers.typed_get::<AccessControlAllowHeaders>() {
2500 Some(names) => names.iter().collect(),
2501 None => {
2503 return Response::network_error(NetworkError::CorsAllowHeaders);
2504 },
2505 }
2506 } else {
2507 vec![]
2508 };
2509
2510 debug!(
2511 "CORS check: Allowed methods: {:?}, current method: {:?}",
2512 methods, request.method
2513 );
2514
2515 if methods.is_empty() && request.use_cors_preflight {
2518 methods = vec![request.method.clone()];
2519 }
2520
2521 if methods
2524 .iter()
2525 .all(|method| *method.as_str() != *request.method.as_ref()) &&
2526 !is_cors_safelisted_method(&request.method) &&
2527 (request.credentials_mode == CredentialsMode::Include ||
2528 methods.iter().all(|method| method.as_ref() != "*"))
2529 {
2530 return Response::network_error(NetworkError::CorsMethod);
2531 }
2532
2533 debug!(
2534 "CORS check: Allowed headers: {:?}, current headers: {:?}",
2535 header_names, request.headers
2536 );
2537
2538 if request.headers.iter().any(|(name, _)| {
2541 is_cors_non_wildcard_request_header_name(name) &&
2542 header_names.iter().all(|header_name| header_name != name)
2543 }) {
2544 return Response::network_error(NetworkError::CorsAuthorization);
2545 }
2546
2547 let unsafe_names = get_cors_unsafe_header_names(&request.headers);
2551 let header_names_set: HashSet<&HeaderName> = HashSet::from_iter(header_names.iter());
2552 let header_names_contains_star = header_names
2553 .iter()
2554 .any(|header_name| header_name.as_str() == "*");
2555 for unsafe_name in unsafe_names.iter() {
2556 if !header_names_set.contains(unsafe_name) &&
2557 (request.credentials_mode == CredentialsMode::Include ||
2558 !header_names_contains_star)
2559 {
2560 return Response::network_error(NetworkError::CorsHeaders);
2561 }
2562 }
2563
2564 let max_age: Option<Duration> = response
2567 .headers
2568 .typed_get::<AccessControlMaxAge>()
2569 .map(|acma| acma.into());
2570
2571 let max_age = max_age.unwrap_or(Duration::from_secs(5));
2573
2574 for method in &methods {
2585 cache.match_method_and_update(request, method.clone(), max_age);
2586 }
2587
2588 for header_name in &header_names {
2593 cache.match_header_and_update(request, header_name, max_age);
2594 }
2595
2596 return response;
2598 }
2599
2600 Response::network_error(NetworkError::CorsGeneral)
2602}
2603
2604fn cors_check(request: &Request, response: &Response) -> Result<(), ()> {
2606 let Some(origins) =
2608 get_value_from_header_list(ACCESS_CONTROL_ALLOW_ORIGIN.as_str(), &response.headers)
2609 else {
2610 return Err(());
2612 };
2613 let origin = origins.into_iter().map(char::from).collect::<String>();
2614
2615 if request.credentials_mode != CredentialsMode::Include && origin == "*" {
2617 return Ok(());
2618 }
2619
2620 if serialize_request_origin(request).to_string() != origin {
2622 return Err(());
2623 }
2624
2625 if request.credentials_mode != CredentialsMode::Include {
2627 return Ok(());
2628 }
2629
2630 let credentials = response
2632 .headers
2633 .typed_get::<AccessControlAllowCredentials>();
2634
2635 if credentials.is_some() {
2637 return Ok(());
2638 }
2639
2640 Err(())
2642}
2643
2644fn has_credentials(url: &ServoUrl) -> bool {
2645 !url.username().is_empty() || url.password().is_some()
2646}
2647
2648fn is_no_store_cache(headers: &HeaderMap) -> bool {
2649 headers.contains_key(header::IF_MODIFIED_SINCE) |
2650 headers.contains_key(header::IF_NONE_MATCH) |
2651 headers.contains_key(header::IF_UNMODIFIED_SINCE) |
2652 headers.contains_key(header::IF_MATCH) |
2653 headers.contains_key(header::IF_RANGE)
2654}
2655
2656fn is_redirect_status(status: StatusCode) -> bool {
2658 matches!(
2659 status,
2660 StatusCode::MOVED_PERMANENTLY |
2661 StatusCode::FOUND |
2662 StatusCode::SEE_OTHER |
2663 StatusCode::TEMPORARY_REDIRECT |
2664 StatusCode::PERMANENT_REDIRECT
2665 )
2666}
2667
2668fn serialize_request_origin(request: &Request) -> headers::Origin {
2670 let Origin::Origin(origin) = &request.origin else {
2672 panic!("origin cannot be \"client\" at this point in time");
2673 };
2674
2675 if request.redirect_taint_for_request() != RedirectTaint::SameOrigin {
2677 return headers::Origin::NULL;
2678 }
2679
2680 serialize_origin(origin)
2682}
2683
2684pub fn serialize_origin(origin: &ImmutableOrigin) -> headers::Origin {
2686 match origin {
2687 ImmutableOrigin::Opaque(_) => headers::Origin::NULL,
2688 ImmutableOrigin::Tuple(scheme, host, port) => {
2689 let port = match (scheme.as_ref(), port) {
2692 ("http" | "ws", 80) | ("https" | "wss", 443) | ("ftp", 21) => None,
2693 _ => Some(*port),
2694 };
2695
2696 headers::Origin::try_from_parts(scheme, &host.to_string(), port)
2698 .unwrap_or(headers::Origin::NULL)
2699 },
2700 }
2701}
2702
2703#[expect(
2705 clippy::collapsible_match,
2706 reason = "The current way follows the spec more closely"
2707)]
2708fn append_a_request_origin_header(request: &mut Request) {
2709 let Origin::Origin(request_origin) = &request.origin else {
2711 panic!("origin cannot be \"client\" at this point in time");
2712 };
2713
2714 let mut serialized_origin = serialize_request_origin(request);
2716
2717 if request.response_tainting == ResponseTainting::CorsTainting ||
2720 matches!(request.mode, RequestMode::WebSocket { .. })
2721 {
2722 request.headers.typed_insert(serialized_origin);
2723 }
2724 else if !matches!(request.method, Method::GET | Method::HEAD) {
2726 if request.mode != RequestMode::CorsMode {
2728 match request.referrer_policy {
2729 ReferrerPolicy::NoReferrer => {
2730 serialized_origin = headers::Origin::NULL;
2732 },
2733 ReferrerPolicy::NoReferrerWhenDowngrade |
2734 ReferrerPolicy::StrictOrigin |
2735 ReferrerPolicy::StrictOriginWhenCrossOrigin => {
2736 if let ImmutableOrigin::Tuple(scheme, _, _) = &request_origin &&
2739 scheme == "https" &&
2740 request.current_url().scheme() != "https"
2741 {
2742 serialized_origin = headers::Origin::NULL;
2743 }
2744 },
2745 ReferrerPolicy::SameOrigin => {
2746 if *request_origin != request.current_url().origin() {
2749 serialized_origin = headers::Origin::NULL;
2750 }
2751 },
2752 _ => {
2753 },
2755 };
2756 }
2757
2758 request.headers.typed_insert(serialized_origin);
2760 }
2761}
2762
2763fn append_the_fetch_metadata_headers(r: &mut Request) {
2765 if !r.url().is_potentially_trustworthy() {
2767 return;
2768 }
2769
2770 set_the_sec_fetch_dest_header(r);
2772
2773 set_the_sec_fetch_mode_header(r);
2775
2776 set_the_sec_fetch_site_header(r);
2778
2779 set_the_sec_fetch_user_header(r);
2781}
2782
2783fn append_cache_data_to_headers(http_request: &mut Request) {
2785 match http_request.cache_mode {
2786 CacheMode::Default if is_no_store_cache(&http_request.headers) => {
2790 http_request.cache_mode = CacheMode::NoStore;
2791 },
2792
2793 CacheMode::NoCache if !http_request.headers.contains_key(header::CACHE_CONTROL) => {
2803 http_request
2804 .headers
2805 .typed_insert(CacheControl::new().with_max_age(Duration::from_secs(0)));
2806 },
2807
2808 CacheMode::Reload | CacheMode::NoStore => {
2810 if !http_request.headers.contains_key(header::PRAGMA) {
2813 http_request.headers.typed_insert(Pragma::no_cache());
2814 }
2815
2816 if !http_request.headers.contains_key(header::CACHE_CONTROL) {
2819 http_request
2820 .headers
2821 .typed_insert(CacheControl::new().with_no_cache());
2822 }
2823 },
2824
2825 _ => {},
2826 }
2827}
2828
2829fn set_the_sec_fetch_dest_header(r: &mut Request) {
2831 debug_assert!(r.url().is_potentially_trustworthy());
2833
2834 let header = r.destination;
2838
2839 r.headers.typed_insert(SecFetchDest(header));
2841}
2842
2843fn set_the_sec_fetch_mode_header(r: &mut Request) {
2845 debug_assert!(r.url().is_potentially_trustworthy());
2847
2848 let header = &r.mode;
2851
2852 r.headers.typed_insert(SecFetchMode::from(header));
2854}
2855
2856fn set_the_sec_fetch_site_header(r: &mut Request) {
2858 let Origin::Origin(request_origin) = &r.origin else {
2861 panic!("request origin cannot be \"client\" at this point")
2862 };
2863
2864 debug_assert!(r.url().is_potentially_trustworthy());
2866
2867 let mut header = SecFetchSite::SameOrigin;
2870
2871 if header != SecFetchSite::None {
2876 for url in &r.url_list {
2877 if url.origin() == *request_origin {
2879 continue;
2880 }
2881
2882 header = SecFetchSite::CrossSite;
2884
2885 if !is_same_site(request_origin, &url.origin()) {
2887 break;
2888 }
2889
2890 header = SecFetchSite::SameSite;
2892 }
2893 }
2894
2895 r.headers.typed_insert(header);
2897}
2898
2899fn set_the_sec_fetch_user_header(r: &mut Request) {
2901 debug_assert!(r.url().is_potentially_trustworthy());
2903
2904 if !r.is_navigation_request() {
2907 return;
2908 }
2909
2910 let header = SecFetchUser;
2913
2914 r.headers.typed_insert(header);
2916}
2917
2918fn set_requests_referrer_policy_on_redirect(request: &mut Request, response: &Response) {
2920 let referrer_policy: ReferrerPolicy = response
2923 .headers
2924 .typed_get::<headers::ReferrerPolicy>()
2925 .into();
2926
2927 if referrer_policy != ReferrerPolicy::EmptyString {
2929 request.referrer_policy = referrer_policy;
2930 }
2931}