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 let Ok(cookie_list_header_value) = HeaderValue::from_bytes(cookie_list.as_bytes())
345 {
346 headers.insert(header::COOKIE, cookie_list_header_value);
347 }
348}
349
350fn set_cookie_for_url(cookie_jar: &RwLock<CookieStorage>, request: &ServoUrl, cookie_val: &str) {
351 let mut cookie_jar = cookie_jar.write();
352 let source = CookieSource::HTTP;
353
354 if let Some(cookie) = ServoCookie::from_cookie_string(cookie_val, request, source) {
355 cookie_jar.push(cookie, request, source);
356 }
357}
358
359fn set_cookies_from_headers(
360 url: &ServoUrl,
361 headers: &HeaderMap,
362 cookie_jar: &RwLock<CookieStorage>,
363) {
364 for cookie in headers.get_all(header::SET_COOKIE) {
365 let cookie_bytes = cookie.as_bytes();
366 if !ServoCookie::is_valid_name_or_value(cookie_bytes) {
367 continue;
368 }
369 if let Ok(cookie_str) = std::str::from_utf8(cookie_bytes) {
370 set_cookie_for_url(cookie_jar, url, cookie_str);
371 }
372 }
373}
374
375fn build_tls_security_info(handshake: &TlsHandshakeInfo, hsts_enabled: bool) -> TlsSecurityInfo {
376 let state = if handshake.protocol_version.is_none() || handshake.cipher_suite.is_none() {
384 TlsSecurityState::Insecure
386 } else {
387 TlsSecurityState::Secure
389 };
390
391 TlsSecurityInfo {
392 state,
393 weakness_reasons: Vec::new(), protocol_version: handshake.protocol_version.clone(),
395 cipher_suite: handshake.cipher_suite.clone(),
396 kea_group_name: handshake.kea_group_name.clone(),
397 signature_scheme_name: handshake.signature_scheme_name.clone(),
398 alpn_protocol: handshake.alpn_protocol.clone(),
399 certificate_chain_der: handshake.certificate_chain_der.clone(),
400 certificate_transparency: None,
401 hsts: hsts_enabled,
402 hpkp: false,
403 used_ech: handshake.used_ech,
404 used_delegated_credentials: false,
405 used_ocsp: false,
406 used_private_dns: false,
407 }
408}
409
410fn auth_from_cache(
411 auth_cache: &RwLock<AuthCache>,
412 origin: &ImmutableOrigin,
413) -> Option<Authorization<Basic>> {
414 if let Some(auth_entry) = auth_cache.read().entries.get(&origin.ascii_serialization()) {
415 let user_name = &auth_entry.user_name;
416 let password = &auth_entry.password;
417 Some(Authorization::basic(user_name, password))
418 } else {
419 None
420 }
421}
422
423enum BodyChunk {
426 Chunk(GenericSharedMemory),
428 Done,
430}
431
432enum BodyStream {
434 Chunked(TokioReceiver<Result<Frame<Bytes>, hyper::Error>>),
437 Buffered(UnboundedReceiver<BodyChunk>),
440}
441
442enum BodySink {
445 Chunked(TokioSender<Result<Frame<Bytes>, hyper::Error>>),
447 Buffered(UnboundedSender<BodyChunk>),
451}
452
453impl BodySink {
454 fn transmit_bytes(&self, bytes: GenericSharedMemory) {
455 match self {
456 BodySink::Chunked(sender) => {
457 let sender = sender.clone();
458 spawn_task(async move {
459 let _ = sender
460 .send(Ok(Frame::data(Bytes::copy_from_slice(&bytes))))
461 .await;
462 });
463 },
464 BodySink::Buffered(sender) => {
465 let _ = sender.send(BodyChunk::Chunk(bytes));
466 },
467 }
468 }
469
470 fn close(self) {
471 match self {
472 BodySink::Chunked(_) => {},
473 BodySink::Buffered(sender) => {
474 let _ = sender.send(BodyChunk::Done);
475 },
476 }
477 }
478}
479
480fn request_body_stream_closed_error(action: &str) -> NetworkError {
481 NetworkError::Crash(format!(
482 "Request body stream has already been closed while trying to {action}."
483 ))
484}
485
486fn log_request_body_stream_closed(action: &str, error: Option<&IpcError>) {
487 match error {
488 Some(error) => {
489 error!("Request body stream has already been closed while trying to {action}: {error}")
490 },
491 None => error!("Request body stream has already been closed while trying to {action}."),
492 }
493}
494
495fn log_fetch_terminated_send_failure(terminated_with_error: bool, context: &str) {
496 warn!(
497 "Failed to notify request-body stream termination state ({terminated_with_error}) while {context} because the receiver was already dropped."
498 );
499}
500
501const FRAGMENT: &AsciiSet = &CONTROLS.add(b'|').add(b'{').add(b'}');
502
503#[allow(clippy::too_many_arguments)]
504#[servo_tracing::instrument(skip_all, fields(url=url.as_str()))]
505async fn obtain_response(
507 client: &ServoClient,
508 url: &ServoUrl,
509 method: &Method,
510 request_headers: &mut HeaderMap,
511 body_sender: Option<StdArc<Mutex<Option<IpcSender<BodyChunkRequest>>>>>,
512 source_is_null: bool,
513 pipeline_id: &Option<PipelineId>,
514 request_id: Option<&str>,
515 destination: Destination,
516 is_xhr: bool,
517 context: &FetchContext,
518 fetch_terminated: UnboundedSender<bool>,
519 browsing_context_id: Option<BrowsingContextId>,
520) -> Result<(HyperResponse<Decoder>, Option<ChromeToDevtoolsControlMsg>), NetworkError> {
521 let mut headers = request_headers.clone();
522
523 let devtools_bytes = StdArc::new(Mutex::new(vec![]));
524
525 let encoded_url = utf8_percent_encode(url.as_str(), FRAGMENT).to_string();
527
528 let request = if let Some(chunk_requester) = body_sender {
529 let (sink, stream) = if source_is_null {
530 headers.insert(TRANSFER_ENCODING, HeaderValue::from_static("chunked"));
533
534 let (sender, receiver) = channel(1);
535 (BodySink::Chunked(sender), BodyStream::Chunked(receiver))
536 } else {
537 let (sender, receiver) = unbounded_channel();
544 (BodySink::Buffered(sender), BodyStream::Buffered(receiver))
545 };
546
547 obtain_response_setup_router_callback(
548 devtools_bytes.clone(),
549 chunk_requester,
550 sink,
551 fetch_terminated,
552 )?;
553
554 let body = match stream {
555 BodyStream::Chunked(receiver) => {
556 let stream = ReceiverStream::new(receiver);
557 BoxBody::new(http_body_util::StreamBody::new(stream))
558 },
559 BodyStream::Buffered(mut receiver) => {
560 let mut body = vec![];
562 loop {
563 match receiver.recv().await {
564 Some(BodyChunk::Chunk(bytes)) => {
565 body.extend_from_slice(&bytes);
566 },
567 Some(BodyChunk::Done) => break,
568 None => warn!("Failed to read all chunks from request body."),
569 }
570 }
571 Full::new(body.into()).map_err(|_| unreachable!()).boxed()
572 },
573 };
574 HyperRequest::builder()
575 .method(method)
576 .uri(encoded_url)
577 .body(body)
578 } else {
579 HyperRequest::builder()
580 .method(method)
581 .uri(encoded_url)
582 .body(
583 http_body_util::Empty::new()
584 .map_err(|_| unreachable!())
585 .boxed(),
586 )
587 };
588
589 let connect_start = CrossProcessInstant::now();
592 context.timing.set_attributes(&[
593 ResourceAttribute::DomainLookupStart,
594 ResourceAttribute::ConnectStart(connect_start),
595 ]);
596
597 if url.scheme() == "https" {
601 context
602 .timing
603 .set_attribute(ResourceAttribute::SecureConnectionStart);
604 }
605
606 let mut request = match request {
607 Ok(request) => request,
608 Err(error) => return Err(NetworkError::HttpError(error.to_string())),
609 };
610 *request.headers_mut() = headers.clone();
611
612 let connect_end = CrossProcessInstant::now();
613 context
614 .timing
615 .set_attribute(ResourceAttribute::ConnectEnd(connect_end));
616
617 let request_id = request_id.map(|v| v.to_owned());
618 let pipeline_id = *pipeline_id;
619 let closure_url = url.clone();
620 let method = method.clone();
621 let send_start = CrossProcessInstant::now();
622
623 let host = request.uri().host().unwrap_or("").to_owned();
624 let override_manager = context.state.override_manager.clone();
625 let headers = headers.clone();
626 let is_secure_scheme = url.is_secure_scheme();
627
628 context
632 .timing
633 .set_attribute(ResourceAttribute::RequestStart);
634
635 let client_future = client
636 .request(request)
637 .and_then(move |res| {
638 let send_end = CrossProcessInstant::now();
639
640 let msg = if let Some(request_id) = request_id {
643 if let Some(pipeline_id) = pipeline_id {
644 if let Some(browsing_context_id) = browsing_context_id {
645 Some(prepare_devtools_request(
646 request_id,
647 closure_url,
648 method.clone(),
649 headers,
650 Some(devtools_bytes.lock().clone()),
651 pipeline_id,
652 (connect_end - connect_start).unsigned_abs(),
653 (send_end - send_start).unsigned_abs(),
654 destination,
655 is_xhr,
656 browsing_context_id,
657 ))
658 } else {
659 debug!("Not notifying devtools (no browsing_context_id)");
660 None
661 }
662 } else {
667 debug!("Not notifying devtools (no pipeline_id)");
668 None
669 }
670 } else {
671 debug!("Not notifying devtools (no request_id)");
672 None
673 };
674
675 future::ready(Ok((
676 Decoder::detect(res.map(|r| r.boxed()), is_secure_scheme),
677 msg,
678 )))
679 })
680 .map_err(move |error| {
681 warn!("network error: {error:?}");
682 NetworkError::from_hyper_error(
683 &error,
684 override_manager.remove_certificate_failing_verification(host.as_str()),
685 )
686 });
687
688 #[cfg(feature = "tracing")]
689 {
690 client_future.instrument(trace_span!("HyperRequest")).await
691 }
692
693 #[cfg(not(feature = "tracing"))]
694 {
695 client_future.await
696 }
697}
698
699fn obtain_response_setup_router_callback(
701 devtools_bytes: StdArc<Mutex<Vec<u8>>>,
702 chunk_requester: StdArc<Mutex<Option<IpcSender<BodyChunkRequest>>>>,
703 sink: BodySink,
704 fetch_terminated: UnboundedSender<bool>,
705) -> Result<(), NetworkError> {
706 let (body_chan, body_port) = ipc::channel().unwrap();
707
708 {
709 let mut lock = chunk_requester.lock();
710 if let Some(chunk_requester) = lock.as_mut() {
711 if let Err(error) = chunk_requester.send(BodyChunkRequest::Connect(body_chan)) {
712 log_request_body_stream_closed("connect to the request body stream", Some(&error));
713 return Err(request_body_stream_closed_error(
714 "connect to the request body stream",
715 ));
716 }
717
718 if let Err(error) = chunk_requester.send(BodyChunkRequest::Chunk) {
721 log_request_body_stream_closed(
722 "request the first request body chunk",
723 Some(&error),
724 );
725 return Err(request_body_stream_closed_error(
726 "request the first request body chunk",
727 ));
728 }
729 } else {
730 log_request_body_stream_closed("connect to the request body stream", None);
731 return Err(request_body_stream_closed_error(
732 "connect to the request body stream",
733 ));
734 }
735 }
736
737 let mut sink = Some(sink);
738
739 ROUTER.add_typed_route(
740 body_port,
741 Box::new(move |message| {
742 info!("Received message");
743 let bytes = match message.unwrap() {
744 BodyChunkResponse::Chunk(bytes) => bytes,
745 BodyChunkResponse::Done => {
746 if fetch_terminated.send(false).is_err() {
749 log_fetch_terminated_send_failure(
750 false,
751 "handling request body completion",
752 );
753 }
754 if let Some(sink) = sink.take() {
755 sink.close();
756 }
757
758 return;
759 },
760 BodyChunkResponse::Error => {
761 if fetch_terminated.send(true).is_err() {
765 log_fetch_terminated_send_failure(
766 true,
767 "handling request body stream error",
768 );
769 }
770 if let Some(sink) = sink.take() {
771 sink.close();
772 }
773
774 return;
775 },
776 };
777
778 devtools_bytes.lock().extend_from_slice(&bytes);
779
780 {
783 let Some(sink) = sink.as_ref() else {
784 return;
785 };
786 sink.transmit_bytes(bytes);
787 }
788
789 let mut chunk_requester = chunk_requester.lock();
792 if let Some(chunk_requester) = chunk_requester.as_mut() {
793 if let Err(error) = chunk_requester.send(BodyChunkRequest::Chunk) {
794 log_request_body_stream_closed(
795 "request the next request body chunk",
796 Some(&error),
797 );
798 if fetch_terminated.send(true).is_err() {
799 log_fetch_terminated_send_failure(
800 true,
801 "handling failure to request the next request body chunk",
802 );
803 }
804 if let Some(sink) = sink.take() {
805 sink.close();
806 }
807 }
808 } else {
809 log_request_body_stream_closed("request the next request body chunk", None);
810 if fetch_terminated.send(true).is_err() {
811 log_fetch_terminated_send_failure(
812 true,
813 "handling a closed request body stream while requesting the next chunk",
814 );
815 }
816 if let Some(sink) = sink.take() {
817 sink.close();
818 }
819 }
820 }),
821 );
822
823 Ok(())
824}
825
826#[async_recursion]
828#[allow(clippy::too_many_arguments)]
829pub(crate) async fn http_fetch(
830 fetch_params: &mut FetchParams,
831 cache: &mut CorsCache,
832 cors_flag: bool,
833 cors_preflight_flag: bool,
834 authentication_fetch_flag: bool,
835 target: Target<'async_recursion>,
836 done_chan: &mut DoneChannel,
837 context: &FetchContext,
838) -> Response {
839 *done_chan = None;
841 let request = &mut fetch_params.request;
843
844 let mut response: Option<Response> = None;
846
847 if request.service_workers_mode == ServiceWorkersMode::All {
849 if let Some(ref res) = response {
854 if (res.response_type == ResponseType::Opaque && request.mode != RequestMode::NoCors) ||
862 (res.response_type == ResponseType::OpaqueRedirect &&
863 request.redirect_mode != RedirectMode::Manual) ||
864 (res.url_list.len() > 1 && request.redirect_mode != RedirectMode::Follow) ||
865 res.is_network_error()
866 {
867 return Response::network_error(NetworkError::ConnectionFailure);
868 }
869
870 }
873 }
874
875 if response.is_none() {
877 if cors_preflight_flag {
879 let method_cache_match = cache.match_method(request, request.method.clone());
880
881 let method_mismatch = !method_cache_match &&
885 (!is_cors_safelisted_method(&request.method) || request.use_cors_preflight);
886
887 let header_mismatch = request.headers.iter().any(|(name, value)| {
890 !cache.match_header(request, name) &&
891 !is_cors_safelisted_request_header(&name, &value)
892 });
893
894 if method_mismatch || header_mismatch {
896 let preflight_response = cors_preflight_fetch(request, cache, context).await;
899 if let Some(error) = preflight_response.get_network_error() {
901 return Response::network_error(error.clone());
902 }
903 }
904 }
905
906 if request.redirect_mode == RedirectMode::Follow {
909 request.service_workers_mode = ServiceWorkersMode::None;
910 }
911
912 let mut fetch_result = http_network_or_cache_fetch(
915 fetch_params,
916 authentication_fetch_flag,
917 cors_flag,
918 done_chan,
919 context,
920 )
921 .await;
922
923 if cors_flag && cors_check(&fetch_params.request, &fetch_result).is_err() {
926 return Response::network_error(NetworkError::CorsGeneral);
927 }
928
929 if let Err(()) = tao_check(&fetch_params.request, &fetch_result) {
932 context.timing.inner().mark_timing_check_failed();
933 }
934 fetch_result.return_internal = false;
935 response = Some(fetch_result);
936 }
937
938 let request = &mut fetch_params.request;
939
940 let mut response = response.unwrap();
942
943 if (request.response_tainting == ResponseTainting::Opaque ||
947 response.response_type == ResponseType::Opaque) &&
948 request.client.as_ref().is_some_and(|client| {
949 cross_origin_resource_policy_check(
950 &request.origin,
951 client,
952 &response,
953 ForNavigation::No,
954 ) == CrossOriginResourcePolicy::Blocked
955 })
956 {
957 return Response::network_error(NetworkError::CrossOriginResponse);
958 }
959
960 if response
962 .actual_response()
963 .status
964 .try_code()
965 .is_some_and(is_redirect_status)
966 {
967 if response.actual_response().status != StatusCode::SEE_OTHER {
974 }
976
977 response = match request.redirect_mode {
979 RedirectMode::Error => Response::network_error(NetworkError::RedirectError),
981 RedirectMode::Manual => {
982 if request.mode == RequestMode::Navigate {
985 let location_url =
989 location_url_for_response(&response, request.current_url().fragment());
990 response.actual_response_mut().location_url = location_url;
991 response
992 } else {
993 response.to_filtered(ResponseType::OpaqueRedirect)
996 }
997 },
998 RedirectMode::Follow => {
999 response.return_internal = true;
1003
1004 http_redirect_fetch(
1007 fetch_params,
1008 cache,
1009 response,
1010 cors_flag,
1011 target,
1012 done_chan,
1013 context,
1014 )
1015 .await
1016 },
1017 };
1018 }
1019
1020 response.return_internal = true;
1022 context
1023 .timing
1024 .set_attribute(ResourceAttribute::RedirectCount(
1025 fetch_params.request.redirect_count as u16,
1026 ));
1027
1028 response.resource_timing = context.timing.clone();
1029
1030 response
1032}
1033
1034fn tao_check(request: &Request, response: &Response) -> Result<(), ()> {
1036 let Origin::Origin(ref request_origin) = request.origin else {
1038 unreachable!("origin cannot be \"client\" at this point");
1039 };
1040
1041 let values: Vec<&str> = response
1046 .headers
1047 .get_all("Timing-Allow-Origin")
1048 .iter()
1049 .map(|header_value| header_value.to_str().unwrap_or(""))
1050 .collect();
1051
1052 if values.contains(&"*") {
1054 return Ok(());
1055 }
1056
1057 if values
1060 .iter()
1061 .any(|header_str| *header_str == request_origin.ascii_serialization())
1062 {
1063 return Ok(());
1064 }
1065
1066 if request.mode == RequestMode::Navigate && request.current_url().origin() != *request_origin {
1069 return Err(());
1070 }
1071
1072 if request.response_tainting == ResponseTainting::Basic {
1074 return Ok(());
1075 }
1076
1077 Err(())
1079}
1080
1081struct RedirectEndTimer(Option<ResourceFetchTimingContainer>);
1083
1084impl RedirectEndTimer {
1085 fn neuter(&mut self) {
1086 self.0 = None;
1087 }
1088}
1089
1090impl Drop for RedirectEndTimer {
1091 fn drop(&mut self) {
1092 let RedirectEndTimer(resource_fetch_timing_opt) = self;
1093
1094 resource_fetch_timing_opt.as_ref().map_or((), |t| {
1095 t.set_attribute(ResourceAttribute::RedirectEnd(RedirectEndValue::Zero));
1096 })
1097 }
1098}
1099
1100static REQUEST_BODY_HEADER_NAMES: &[HeaderName] = &[
1102 CONTENT_ENCODING,
1103 CONTENT_LANGUAGE,
1104 CONTENT_LOCATION,
1105 CONTENT_TYPE,
1106];
1107
1108fn location_url_for_response(
1110 response: &Response,
1111 request_fragment: Option<&str>,
1112) -> Option<Result<ServoUrl, String>> {
1113 assert!(
1115 response
1116 .actual_response()
1117 .status
1118 .try_code()
1119 .is_some_and(is_redirect_status)
1120 );
1121 let mut location = response
1123 .actual_response()
1124 .headers
1125 .get(header::LOCATION)
1126 .and_then(|header_value| {
1127 HeaderValue::to_str(header_value)
1128 .map(|location_string| {
1129 ServoUrl::parse_with_base(response.actual_response().url(), location_string)
1131 .map_err(|error| error.to_string())
1132 })
1133 .ok()
1134 });
1135
1136 if let Some(Ok(ref mut location)) = location &&
1138 location.fragment().is_none()
1139 {
1140 location.set_fragment(request_fragment);
1141 }
1142 location
1144}
1145
1146#[async_recursion]
1148pub async fn http_redirect_fetch(
1149 fetch_params: &mut FetchParams,
1150 cache: &mut CorsCache,
1151 mut response: Response,
1152 cors_flag: bool,
1153 target: Target<'async_recursion>,
1154 done_chan: &mut DoneChannel,
1155 context: &FetchContext,
1156) -> Response {
1157 let mut redirect_end_timer = RedirectEndTimer(Some(context.timing.clone()));
1158
1159 let request = &mut fetch_params.request;
1161
1162 assert!(response.return_internal);
1164
1165 let location_url = location_url_for_response(&response, request.current_url().fragment());
1167 response.actual_response_mut().location_url = location_url.clone();
1168
1169 let location_url = match location_url {
1170 None => return response,
1172 Some(Err(err)) => {
1174 return Response::network_error(NetworkError::ResourceLoadError(
1175 "Location URL parse failure: ".to_owned() + &err,
1176 ));
1177 },
1178 Some(Ok(url)) if !matches!(url.scheme(), "http" | "https") => {
1180 return Response::network_error(NetworkError::UnsupportedScheme);
1181 },
1182 Some(Ok(url)) => url,
1183 };
1184
1185 context.timing.set_attributes(&[
1190 ResourceAttribute::RedirectStart(RedirectStartValue::FetchStart),
1191 ResourceAttribute::FetchStart,
1192 ResourceAttribute::StartTime(ResourceTimeValue::FetchStart),
1193 ResourceAttribute::StartTime(ResourceTimeValue::RedirectStart),
1194 ]);
1195
1196 if request.redirect_count >= 20 {
1198 return Response::network_error(NetworkError::TooManyRedirects);
1199 }
1200
1201 request.redirect_count += 1;
1203
1204 let same_origin = match request.origin {
1207 Origin::Origin(ref origin) => *origin == location_url.origin(),
1208 Origin::Client => panic!(
1209 "Request origin should not be client for {}",
1210 request.current_url()
1211 ),
1212 };
1213
1214 let has_credentials = has_credentials(&location_url);
1215
1216 if request.mode == RequestMode::CorsMode && !same_origin && has_credentials {
1217 return Response::network_error(NetworkError::CorsCredentials);
1218 }
1219
1220 if cors_flag && location_url.origin() != request.current_url().origin() {
1221 request.origin = Origin::Origin(ImmutableOrigin::new_opaque());
1222 }
1223
1224 if cors_flag && has_credentials {
1226 return Response::network_error(NetworkError::CorsCredentials);
1227 }
1228
1229 if response.actual_response().status != StatusCode::SEE_OTHER &&
1232 request.body.as_ref().is_some_and(|b| b.source_is_null())
1233 {
1234 return Response::network_error(NetworkError::ConnectionFailure);
1235 }
1236
1237 if response
1239 .actual_response()
1240 .status
1241 .try_code()
1242 .is_some_and(|code| {
1243 ((code == StatusCode::MOVED_PERMANENTLY || code == StatusCode::FOUND) &&
1245 request.method == Method::POST) ||
1246 (code == StatusCode::SEE_OTHER &&
1248 request.method != Method::HEAD &&
1249 request.method != Method::GET)
1250 })
1251 {
1252 request.method = Method::GET;
1254 request.body = None;
1255 for name in REQUEST_BODY_HEADER_NAMES {
1257 request.headers.remove(name);
1258 }
1259 }
1260
1261 if location_url.origin() != request.current_url().origin() {
1265 request.headers.remove(AUTHORIZATION);
1268 }
1269
1270 if let Some(body) = request.body.as_mut() {
1273 body.extract_source();
1274 }
1275
1276 request
1280 .url_list
1281 .push(UrlWithBlobClaim::from_url_without_having_claimed_blob(
1282 location_url,
1283 ));
1284
1285 set_requests_referrer_policy_on_redirect(request, response.actual_response());
1287
1288 let recursive_flag = request.redirect_mode != RedirectMode::Manual;
1291
1292 let fetch_response = main_fetch(
1294 fetch_params,
1295 cache,
1296 recursive_flag,
1297 target,
1298 done_chan,
1299 context,
1300 )
1301 .await;
1302
1303 context.timing.set_attribute(ResourceAttribute::RedirectEnd(
1305 RedirectEndValue::ResponseEnd,
1306 ));
1307 redirect_end_timer.neuter();
1308
1309 fetch_response
1310}
1311
1312#[async_recursion]
1314#[servo_tracing::instrument(skip_all,fields(url=fetch_params.request.url().as_str()))]
1315async fn http_network_or_cache_fetch(
1316 fetch_params: &mut FetchParams,
1317 authentication_fetch_flag: bool,
1318 cors_flag: bool,
1319 done_chan: &mut DoneChannel,
1320 context: &FetchContext,
1321) -> Response {
1322 let http_fetch_params: &mut FetchParams;
1324 let mut fetch_params_copy: FetchParams;
1325
1326 let mut response: Option<Response> = None;
1330
1331 let mut revalidating_flag = false;
1333
1334 let http_request = if fetch_params.request.traversable_for_user_prompts ==
1338 TraversableForUserPrompts::NoTraversable &&
1339 fetch_params.request.redirect_mode == RedirectMode::Error
1340 {
1341 http_fetch_params = fetch_params;
1342 &mut http_fetch_params.request
1343 }
1344 else {
1346 fetch_params_copy =
1349 std::mem::replace(fetch_params, FetchParams::new(fetch_params.request.clone()));
1350 http_fetch_params = &mut fetch_params_copy;
1351
1352 &mut http_fetch_params.request
1353 };
1354
1355 let include_credentials = match http_request.credentials_mode {
1357 CredentialsMode::Include => true,
1359 CredentialsMode::CredentialsSameOrigin
1361 if http_request.response_tainting == ResponseTainting::Basic =>
1362 {
1363 true
1364 },
1365 _ => false,
1366 };
1367
1368 let content_length = http_request
1375 .body
1376 .as_ref()
1377 .and_then(|body| body.len().map(|size| size as u64));
1378
1379 let mut content_length_header_value = None;
1381
1382 if http_request.body.is_none() && matches!(http_request.method, Method::POST | Method::PUT) {
1385 content_length_header_value = Some(0);
1386 }
1387
1388 if let Some(content_length) = content_length {
1392 content_length_header_value = Some(content_length);
1393 };
1394
1395 if let Some(content_length_header_value) = content_length_header_value {
1398 http_request
1399 .headers
1400 .typed_insert(ContentLength(content_length_header_value));
1401 }
1402
1403 if http_request.keep_alive &&
1405 let Some(content_length) = content_length
1406 {
1407 let in_flight_keep_alive_bytes: u64 = context
1412 .in_flight_keep_alive_records
1413 .lock()
1414 .get(
1415 &http_request
1416 .pipeline_id
1417 .expect("Must always set a pipeline ID for keep-alive requests"),
1418 )
1419 .map(|records| {
1420 records
1424 .iter()
1425 .map(|record| {
1426 if record.request_id == http_request.id {
1427 0
1430 } else {
1431 record.keep_alive_body_length
1432 }
1433 })
1434 .sum()
1435 })
1436 .unwrap_or_default();
1437 if content_length + in_flight_keep_alive_bytes > 64 * 1024 {
1439 return Response::network_error(NetworkError::TooManyInFlightKeepAliveRequests);
1440 }
1441 }
1442
1443 match http_request.referrer {
1445 Referrer::ReferrerUrl(ref http_request_referrer) |
1446 Referrer::Client(ref http_request_referrer) => {
1447 if let Ok(referer) = http_request_referrer.as_str().parse::<Referer>() {
1450 http_request.headers.typed_insert(referer);
1452 } else {
1453 error!("Failed to parse {} as referrer", http_request_referrer);
1457 }
1458 },
1459 _ => {},
1460 };
1461
1462 append_a_request_origin_header(http_request);
1464
1465 append_the_fetch_metadata_headers(http_request);
1467
1468 if http_request.initiator == Initiator::Prefetch &&
1471 let Ok(value) = HeaderValue::from_str("prefetch")
1472 {
1473 http_request.headers.insert("Sec-Purpose", value);
1474 }
1475
1476 if !http_request.headers.contains_key(header::USER_AGENT) {
1479 http_request
1480 .headers
1481 .typed_insert::<UserAgent>(context.user_agent.parse().unwrap());
1482 }
1483
1484 append_cache_data_to_headers(http_request);
1486
1487 if http_request.headers.contains_key(header::RANGE) &&
1490 let Ok(value) = HeaderValue::from_str("identity")
1491 {
1492 http_request.headers.insert("Accept-Encoding", value);
1493 }
1494
1495 http_request.headers.remove(header::HOST);
1499 set_default_accept_encoding(&mut http_request.headers);
1501
1502 let current_url = http_request.current_url();
1503
1504 if include_credentials {
1507 set_request_cookies(
1511 ¤t_url,
1512 &mut http_request.headers,
1513 &context.state.cookie_jar,
1514 );
1515 if !http_request.headers.contains_key(header::AUTHORIZATION) {
1517 let mut authorization_value = None;
1519
1520 if let Some(basic) = auth_from_cache(&context.state.auth_cache, ¤t_url.origin()) &&
1522 (!http_request.use_url_credentials || !has_credentials(¤t_url))
1523 {
1524 authorization_value = Some(basic);
1525 }
1526
1527 if authentication_fetch_flag &&
1529 authorization_value.is_none() &&
1530 has_credentials(¤t_url)
1531 {
1532 authorization_value = Some(Authorization::basic(
1533 current_url.username(),
1534 current_url.password().unwrap_or(""),
1535 ));
1536 }
1537
1538 if let Some(basic) = authorization_value {
1540 http_request.headers.typed_insert(basic);
1541 }
1542 }
1543 }
1544
1545 let should_wait = {
1547 let mut cache_guard = block_for_cache_ready(
1549 context,
1550 http_request,
1551 done_chan,
1552 &mut revalidating_flag,
1553 &mut response,
1554 )
1555 .await;
1556
1557 if response.is_none() {
1561 if http_request.cache_mode == CacheMode::OnlyIfCached {
1563 return Response::network_error(NetworkError::CacheError);
1565 }
1566
1567 drop(cache_guard);
1570 let forward_response =
1571 http_network_fetch(http_fetch_params, include_credentials, done_chan, context)
1572 .await;
1573
1574 let http_request = &mut http_fetch_params.request;
1575 let request_key = CacheKey::new(http_request);
1576 cache_guard = context
1577 .state
1578 .http_cache
1579 .get_or_guard(request_key.clone())
1580 .await;
1581 if forward_response.status.in_range(200..=399) && !http_request.method.is_safe() {
1585 if let Some(guard) = cache_guard.try_as_mut() {
1586 invalidate_cached_resources(guard);
1587 }
1588 context
1589 .state
1590 .http_cache
1591 .invalidate_related_urls(http_request, &forward_response, &request_key)
1592 .await;
1593 }
1594
1595 if revalidating_flag && forward_response.status == StatusCode::NOT_MODIFIED {
1597 *done_chan = None;
1600 if let Some(guard) = cache_guard.try_as_mut() {
1601 response = refresh(http_request, forward_response.clone(), done_chan, guard);
1602 }
1603
1604 if let Some(response) = &mut response {
1605 response.cache_state = CacheState::Validated;
1606 }
1607 }
1608
1609 if response.is_none() {
1611 let forward_response = response.insert(forward_response);
1613
1614 if http_request.cache_mode != CacheMode::NoStore {
1617 cache_guard.insert(http_request, forward_response);
1620 }
1621 }
1622 false
1623 } else {
1624 true
1625 }
1626 }; if should_wait {
1629 wait_for_inflight_requests(done_chan, &mut response).await;
1634 }
1635
1636 let http_request = &mut http_fetch_params.request;
1637 let mut response = response.unwrap();
1638
1639 response.url_list = http_request
1641 .url_list
1642 .iter()
1643 .map(|claimed_url| claimed_url.url())
1644 .collect();
1645
1646 if http_request.headers.contains_key(RANGE) {
1648 response.range_requested = true;
1649 }
1650
1651 response.request_includes_credentials = include_credentials;
1653
1654 if response.status.try_code() == Some(StatusCode::UNAUTHORIZED) &&
1660 !cors_flag &&
1661 include_credentials &&
1662 response.headers.contains_key(WWW_AUTHENTICATE)
1663 {
1664 let request = &mut fetch_params.request;
1667
1668 if request
1671 .body
1672 .as_ref()
1673 .is_some_and(|body| body.source_is_null())
1674 {
1675 return Response::network_error(NetworkError::ConnectionFailure);
1676 }
1677
1678 if !request.use_url_credentials || authentication_fetch_flag {
1680 let Some(credentials) = context
1681 .state
1682 .request_authentication(request, &response)
1683 .await
1684 else {
1685 return response;
1686 };
1687
1688 if let Err(err) = request
1689 .current_url_mut()
1690 .set_username(&credentials.username)
1691 {
1692 error!("error setting username for url: {:?}", err);
1693 return response;
1694 };
1695
1696 if let Err(err) = request
1697 .current_url_mut()
1698 .set_password(Some(&credentials.password))
1699 {
1700 error!("error setting password for url: {:?}", err);
1701 return response;
1702 };
1703 }
1704
1705 *done_chan = None;
1708
1709 response = http_network_or_cache_fetch(
1711 fetch_params,
1712 true, cors_flag,
1714 done_chan,
1715 context,
1716 )
1717 .await;
1718 }
1719
1720 if response.status == StatusCode::PROXY_AUTHENTICATION_REQUIRED {
1722 let request = &mut fetch_params.request;
1723 if request.traversable_for_user_prompts == TraversableForUserPrompts::NoTraversable {
1726 return Response::network_error(NetworkError::ResourceLoadError(
1727 "Can't find Window object".into(),
1728 ));
1729 }
1730
1731 let Some(credentials) = context
1739 .state
1740 .request_authentication(request, &response)
1741 .await
1742 else {
1743 return response;
1744 };
1745
1746 let entry = AuthCacheEntry {
1748 user_name: credentials.username,
1749 password: credentials.password,
1750 };
1751 {
1752 let mut auth_cache = context.state.auth_cache.write();
1753 let key = request.current_url().origin().ascii_serialization();
1754 auth_cache.entries.insert(key, entry);
1755 }
1756
1757 *done_chan = None;
1760
1761 response = http_network_or_cache_fetch(
1763 fetch_params,
1764 false, cors_flag,
1766 done_chan,
1767 context,
1768 )
1769 .await;
1770 }
1771
1772 if authentication_fetch_flag {
1780 }
1782
1783 response
1785}
1786
1787#[servo_tracing::instrument(skip_all)]
1795async fn block_for_cache_ready<'a>(
1796 context: &'a FetchContext,
1797 http_request: &mut Request,
1798 done_chan: &mut DoneChannel,
1799 revalidating_flag: &mut bool,
1800 response: &mut Option<Response>,
1801) -> CachedResourcesOrGuard<'a> {
1802 let entry_key = CacheKey::new(http_request);
1803 let guard_result = context.state.http_cache.get_or_guard(entry_key).await;
1804
1805 match guard_result {
1806 CachedResourcesOrGuard::Guard(_) => {
1807 *done_chan = None;
1808 },
1809 CachedResourcesOrGuard::Value(ref cached_resources) => {
1810 let stored_response = construct_response(http_request, done_chan, cached_resources);
1816 if let Some(response_from_cache) = stored_response {
1818 let response_headers = response_from_cache.response.headers.clone();
1819 let validation_status = response_from_cache.validation_status;
1820 let revalidation_guard = response_from_cache.revalidation_guard.clone();
1821
1822 let (cached_response, needs_synchronous_revalidation) =
1824 match (http_request.cache_mode, &http_request.mode) {
1825 (CacheMode::ForceCache, _) => (Some(response_from_cache.response), false),
1826 (CacheMode::OnlyIfCached, &RequestMode::SameOrigin) => {
1827 (Some(response_from_cache.response), false)
1828 },
1829 (CacheMode::OnlyIfCached, _) |
1830 (CacheMode::NoStore, _) |
1831 (CacheMode::Reload, _) => (None, false),
1832 (_, _) => (
1833 Some(response_from_cache.response),
1834 validation_status ==
1835 (ValidationStatus::Stale {
1836 revalidate_in_background: false,
1837 }),
1838 ),
1839 };
1840
1841 if needs_synchronous_revalidation {
1842 *revalidating_flag = true;
1843 if let Some(http_date) = response_headers.typed_get::<LastModified>() {
1845 let http_date: SystemTime = http_date.into();
1846 http_request
1847 .headers
1848 .typed_insert(IfModifiedSince::from(http_date));
1849 }
1850 if let Some(entity_tag) = response_headers.get(header::ETAG) {
1851 http_request
1852 .headers
1853 .insert(header::IF_NONE_MATCH, entity_tag.clone());
1854 }
1855 } else {
1856 let revalidate_in_background = validation_status ==
1859 (ValidationStatus::Stale {
1860 revalidate_in_background: true,
1861 });
1862 if revalidate_in_background && cached_response.is_some() {
1863 spawn_stale_while_revalidate(context, http_request, revalidation_guard);
1864 }
1865 *response = cached_response;
1866 if let Some(response) = response {
1867 response.cache_state = CacheState::Local;
1868 }
1869 }
1870 if response.is_none() {
1871 *done_chan = None;
1874 }
1875 }
1876 },
1877 }
1878 guard_result
1879}
1880
1881fn spawn_stale_while_revalidate(
1884 context: &FetchContext,
1885 http_request: &Request,
1886 revalidation_guard: StdArc<AtomicBool>,
1887) {
1888 if revalidation_guard
1890 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
1891 .is_err()
1892 {
1893 return;
1894 }
1895
1896 let mut revalidation_request = http_request.clone();
1899 revalidation_request.cache_mode = CacheMode::NoCache;
1900
1901 revalidation_request.service_workers_mode = ServiceWorkersMode::None;
1903
1904 let context = context.clone();
1905 debug!(
1906 "spawning stale-while-revalidate background revalidation for {:?}",
1907 revalidation_request.current_url()
1908 );
1909 spawn_task(async move {
1910 let mut target = DiscardFetch;
1911
1912 let _ = fetch(revalidation_request, &mut target, &context).await;
1913 revalidation_guard.store(false, Ordering::Release);
1914 });
1915}
1916
1917async fn wait_for_inflight_requests(done_chan: &mut DoneChannel, response: &mut Option<Response>) {
1920 if let Some(ref mut ch) = *done_chan {
1921 assert!(response.is_some());
1925
1926 loop {
1927 match ch.1.recv().await {
1928 Some(Data::ContentLength(_)) | Some(Data::Payload(_)) | Some(Data::Error(_)) => {},
1929 Some(Data::Done) => break, Some(Data::Cancelled) => {
1931 break;
1933 },
1934 None => panic!("HTTP cache should always send Done or Cancelled"),
1935 }
1936 }
1937 }
1938 *done_chan = None;
1940}
1941
1942#[derive(PartialEq)]
1946enum CrossOriginResourcePolicy {
1947 Allowed,
1948 Blocked,
1949}
1950
1951enum ForNavigation {
1952 #[expect(dead_code)]
1953 Yes,
1954 No,
1955}
1956
1957fn cross_origin_resource_policy_check(
1959 origin: &Origin,
1960 request_client: &RequestClient,
1961 response: &Response,
1962 for_navigation: ForNavigation,
1963) -> CrossOriginResourcePolicy {
1964 let embedder_policy = &request_client.policy_container.embedder_policy;
1970
1971 if cross_origin_resource_policy_internal_check(
1974 origin,
1975 EmbedderPolicyValue::UnsafeNone,
1976 response,
1977 &for_navigation,
1978 ) == CrossOriginResourcePolicy::Blocked
1979 {
1980 return CrossOriginResourcePolicy::Blocked;
1981 }
1982
1983 if cross_origin_resource_policy_internal_check(
1991 origin,
1992 embedder_policy.value,
1993 response,
1994 &for_navigation,
1995 ) == CrossOriginResourcePolicy::Allowed
1996 {
1997 return CrossOriginResourcePolicy::Allowed;
1998 }
1999
2000 CrossOriginResourcePolicy::Blocked
2005}
2006
2007fn cross_origin_resource_policy_internal_check(
2009 origin: &Origin,
2010 embedder_policy_value: EmbedderPolicyValue,
2011 response: &Response,
2012 for_navigation: &ForNavigation,
2013) -> CrossOriginResourcePolicy {
2014 if let ForNavigation::Yes = for_navigation &&
2016 let EmbedderPolicyValue::UnsafeNone = embedder_policy_value
2017 {
2018 return CrossOriginResourcePolicy::Allowed;
2019 }
2020
2021 let policy = response
2023 .headers
2024 .get(HeaderName::from_static("cross-origin-resource-policy"))
2025 .and_then(|h| h.to_str().ok());
2026
2027 let policy = policy
2029 .filter(|&s| s == "same-origin" || s == "same-site" || s == "cross-origin")
2030 .or(match embedder_policy_value {
2032 EmbedderPolicyValue::UnsafeNone => None,
2034 EmbedderPolicyValue::RequireCorp => Some("same-origin"),
2036 });
2037
2038 match policy {
2040 Some("same-origin") => {
2041 if let Origin::Origin(request_origin) = origin &&
2043 response
2044 .url()
2045 .is_some_and(|url| request_origin == &url.origin())
2046 {
2047 return CrossOriginResourcePolicy::Allowed;
2048 }
2049
2050 CrossOriginResourcePolicy::Blocked
2052 },
2053 Some("same-site") => {
2054 if let Some(response_url) = response.url() {
2055 if let Origin::Origin(request_origin) = origin &&
2059 is_schemelessy_same_site(request_origin, &response_url.origin()) &&
2060 (request_origin.scheme() == Some("https") ||
2061 response_url.scheme() != "https")
2062 {
2063 return CrossOriginResourcePolicy::Allowed;
2064 }
2065 }
2066 CrossOriginResourcePolicy::Blocked
2068 },
2069 _ => CrossOriginResourcePolicy::Allowed,
2072 }
2073}
2074
2075struct ResponseEndTimer(Option<ResourceFetchTimingContainer>);
2077
2078impl ResponseEndTimer {
2079 fn neuter(&mut self) {
2080 self.0 = None;
2081 }
2082}
2083
2084impl Drop for ResponseEndTimer {
2085 fn drop(&mut self) {
2086 let ResponseEndTimer(resource_fetch_timing_opt) = self;
2087
2088 resource_fetch_timing_opt.as_ref().map_or((), |t| {
2089 t.set_attribute(ResourceAttribute::ResponseEnd);
2090 })
2091 }
2092}
2093
2094#[servo_tracing::instrument(skip_all,fields(url=fetch_params.request.url().as_str()))]
2096async fn http_network_fetch(
2097 fetch_params: &mut FetchParams,
2098 credentials_flag: bool,
2099 done_chan: &mut DoneChannel,
2100 context: &FetchContext,
2101) -> Response {
2102 let mut response_end_timer = ResponseEndTimer(Some(context.timing.clone()));
2103
2104 let request = &mut fetch_params.request;
2106
2107 let url = request.current_url();
2117 let request_id = request.id.0.to_string();
2118 if log_enabled!(log::Level::Info) {
2119 info!("{:?} request for {}", request.method, url);
2120 for header in request.headers.iter() {
2121 debug!(" - {:?}", header);
2122 }
2123 }
2124
2125 let is_xhr = request.destination == Destination::None;
2129
2130 let (fetch_terminated_sender, mut fetch_terminated_receiver) = unbounded_channel();
2132
2133 let body = request.body.as_ref().map(|body| body.clone_stream());
2134
2135 if body.is_none() {
2136 let _ = fetch_terminated_sender.send(false);
2141 }
2142
2143 let browsing_context_id = request.target_webview_id.map(Into::into);
2144
2145 let (res, msg) = match &request.mode {
2149 RequestMode::WebSocket {
2151 protocols,
2152 original_url: _,
2153 } => {
2154 let (resource_event_sender, dom_action_receiver) = {
2157 let mut websocket_chan = context.websocket_chan.as_ref().unwrap().lock();
2158 (
2159 websocket_chan.sender.clone(),
2160 websocket_chan.receiver.take().unwrap(),
2161 )
2162 };
2163
2164 let mut tls_config = create_tls_config(
2165 context.ca_certificates.clone(),
2166 context.ignore_certificate_errors,
2167 context.state.override_manager.clone(),
2168 );
2169 tls_config.alpn_protocols = vec!["http/1.1".to_string().into()];
2170
2171 let response = match start_websocket(
2172 context.state.clone(),
2173 resource_event_sender,
2174 protocols,
2175 request,
2176 tls_config,
2177 dom_action_receiver,
2178 )
2179 .await
2180 {
2181 Ok(response) => response,
2182 Err(error) => {
2183 return Response::network_error(NetworkError::WebsocketConnectionFailure(
2184 format!("{error:?}"),
2185 ));
2186 },
2187 };
2188
2189 let response = response.map(|r| match r {
2190 Some(body) => Full::from(body).map_err(|_| unreachable!()).boxed(),
2191 None => http_body_util::Empty::new()
2192 .map_err(|_| unreachable!())
2193 .boxed(),
2194 });
2195 (Decoder::detect(response, url.is_secure_scheme()), None)
2196 },
2197 _ => {
2200 let response_future = obtain_response(
2201 &context.state.client,
2202 &url,
2203 &request.method,
2204 &mut request.headers,
2205 body,
2206 request
2207 .body
2208 .as_ref()
2209 .is_some_and(|body| body.source_is_null()),
2210 &request.pipeline_id,
2211 Some(&request_id),
2212 request.destination,
2213 is_xhr,
2214 context,
2215 fetch_terminated_sender,
2216 browsing_context_id,
2217 );
2218
2219 let (res, msg) = match response_future.await {
2221 Ok(wrapped_response) => wrapped_response,
2222 Err(error) => return Response::network_error(error),
2223 };
2224 (res, msg)
2225 },
2226 };
2227
2228 if log_enabled!(log::Level::Info) {
2229 debug!("{:?} response for {}", res.version(), url);
2230 for header in res.headers().iter() {
2231 debug!(" - {:?}", header);
2232 }
2233 }
2234
2235 match fetch_terminated_receiver.recv().await {
2238 Some(true) => return Response::network_error(NetworkError::ConnectionFailure),
2239 Some(false) => {},
2240 _ => warn!("Failed to receive confirmation request was streamed without error."),
2241 }
2242
2243 let timing = context.timing.inner().clone();
2244 let mut response = Response::new(url.clone(), timing);
2245
2246 if let Some(handshake_info) = res.extensions().get::<TlsHandshakeInfo>() {
2247 let mut hsts_enabled = url
2248 .host_str()
2249 .is_some_and(|host| context.state.hsts_list.read().is_host_secure(host));
2250
2251 if url.scheme() == "https" &&
2252 let Some(sts) = res.headers().typed_get::<StrictTransportSecurity>()
2253 {
2254 hsts_enabled = sts.max_age().as_secs() > 0;
2256 }
2257 response.tls_security_info = Some(build_tls_security_info(handshake_info, hsts_enabled));
2258 }
2259
2260 let status_text = res
2261 .extensions()
2262 .get::<ReasonPhrase>()
2263 .map(ReasonPhrase::as_bytes)
2264 .or_else(|| res.status().canonical_reason().map(str::as_bytes))
2265 .map(Vec::from)
2266 .unwrap_or_default();
2267 response.status = HttpStatus::new(res.status(), status_text);
2268
2269 info!("got {:?} response for {:?}", res.status(), request.url());
2270 response.headers = res.headers().clone();
2271 response.referrer = request.referrer.to_url().cloned();
2272 response.referrer_policy = request.referrer_policy;
2273
2274 let res_body = response.body.clone();
2275
2276 let (done_sender, done_receiver) = unbounded_channel();
2278 *done_chan = Some((done_sender.clone(), done_receiver));
2279
2280 let devtools_sender = context.devtools_chan.clone();
2281 let cancellation_listener = context.cancellation_listener.clone();
2282 if cancellation_listener.cancelled() {
2283 return Response::network_error(NetworkError::LoadCancelled);
2284 }
2285
2286 *res_body.lock() = ResponseBody::Receiving(vec![]);
2287 let res_body2 = res_body.clone();
2288
2289 if let Some(ref sender) = devtools_sender &&
2290 let Some(m) = msg
2291 {
2292 send_request_to_devtools(m, sender);
2293 }
2294
2295 let done_sender2 = done_sender.clone();
2296 let done_sender3 = done_sender.clone();
2297 let timing_ptr2 = context.timing.clone();
2298 let timing_ptr3 = context.timing.clone();
2299 let devtools_request = request.clone();
2300 let url1 = devtools_request.url();
2301 let url2 = url1.clone();
2302
2303 let status = response.status.clone();
2304 let headers = response.headers.clone();
2305 let devtools_chan = context.devtools_chan.clone();
2306
2307 if let Some(possible_length) = res
2308 .headers()
2309 .get(http::header::CONTENT_LENGTH)
2310 .and_then(|header_value| header_value.to_str().ok())
2311 .and_then(|s| s.parse().ok())
2312 .map(|length| min(length, pref!(network_max_content_length) as usize))
2313 {
2314 let _ = done_sender.send(Data::ContentLength(possible_length));
2315 }
2316
2317 spawn_task(
2318 res.into_body()
2319 .try_fold(res_body, move |res_body, chunk| {
2320 if cancellation_listener.cancelled() {
2321 *res_body.lock() = ResponseBody::Done(vec![]);
2322 let _ = done_sender.send(Data::Cancelled);
2323 return future::ready(Err(std::io::Error::new(
2324 std::io::ErrorKind::Interrupted,
2325 "Fetch aborted",
2326 )));
2327 }
2328 if let ResponseBody::Receiving(ref mut body) = *res_body.lock() {
2329 let bytes = chunk;
2330 body.extend_from_slice(&bytes);
2331 let _ = done_sender.send(Data::Payload(bytes.to_vec()));
2332 }
2333 future::ready(Ok(res_body))
2334 })
2335 .and_then(move |res_body| {
2336 debug!("successfully finished response for {:?}", url1);
2337 let mut body = res_body.lock();
2338 let completed_body = match *body {
2339 ResponseBody::Receiving(ref mut body) => std::mem::take(body),
2340 _ => vec![],
2341 };
2342 let devtools_response_body = completed_body.clone();
2343 *body = ResponseBody::Done(completed_body);
2344 send_response_values_to_devtools(
2345 Some(headers),
2346 status,
2347 Some(devtools_response_body),
2348 CacheState::None,
2349 &devtools_request,
2350 devtools_chan,
2351 );
2352 timing_ptr2.set_attribute(ResourceAttribute::ResponseEnd);
2353 let _ = done_sender2.send(Data::Done);
2354 future::ready(Ok(()))
2355 })
2356 .map_err(move |error| {
2357 if let std::io::ErrorKind::InvalidData = error.kind() {
2358 debug!("Content decompression error for {:?}", url2);
2359 let _ = done_sender3.send(Data::Error(NetworkError::DecompressionError));
2360 let mut body = res_body2.lock();
2361
2362 *body = ResponseBody::Done(vec![]);
2363 }
2364 debug!("finished response for {:?}", url2);
2365 let mut body = res_body2.lock();
2366 let completed_body = match *body {
2367 ResponseBody::Receiving(ref mut body) => std::mem::take(body),
2368 _ => vec![],
2369 };
2370 *body = ResponseBody::Done(completed_body);
2371 timing_ptr3.set_attribute(ResourceAttribute::ResponseEnd);
2372 let _ = done_sender3.send(Data::Done);
2373 }),
2374 );
2375
2376 if credentials_flag {
2394 set_cookies_from_headers(&url, &response.headers, &context.state.cookie_jar);
2395 }
2396 context
2397 .state
2398 .hsts_list
2399 .write()
2400 .update_hsts_list_from_response(&url, &response.headers);
2401
2402 response_end_timer.neuter();
2416 response
2417}
2418
2419async fn cors_preflight_fetch(
2421 request: &Request,
2422 cache: &mut CorsCache,
2423 context: &FetchContext,
2424) -> Response {
2425 let mut preflight = RequestBuilder::new(
2430 request.target_webview_id,
2431 request.current_url_with_blob_claim(),
2432 request.referrer.clone(),
2433 )
2434 .method(Method::OPTIONS)
2435 .origin(match &request.origin {
2436 Origin::Client => {
2437 unreachable!("We shouldn't get Client origin in cors_preflight_fetch.")
2438 },
2439 Origin::Origin(origin) => origin.clone(),
2440 })
2441 .pipeline_id(request.pipeline_id)
2442 .initiator(request.initiator)
2443 .destination(request.destination)
2444 .referrer_policy(request.referrer_policy)
2445 .mode(RequestMode::CorsMode)
2446 .response_tainting(ResponseTainting::CorsTainting)
2447 .policy_container(match &request.policy_container {
2448 RequestPolicyContainer::Client => {
2449 unreachable!("We should have a policy container for request in cors_preflight_fetch")
2450 },
2451 RequestPolicyContainer::PolicyContainer(policy_container) => policy_container.clone(),
2452 })
2453 .url_list(
2454 request
2455 .url_list
2456 .iter()
2457 .map(|claimed_url| claimed_url.url())
2458 .collect(),
2459 )
2460 .build();
2461
2462 preflight
2464 .headers
2465 .insert(ACCEPT, HeaderValue::from_static("*/*"));
2466
2467 preflight
2469 .headers
2470 .typed_insert::<AccessControlRequestMethod>(AccessControlRequestMethod::from(
2471 request.method.clone(),
2472 ));
2473
2474 let headers = get_cors_unsafe_header_names(&request.headers);
2476
2477 if !headers.is_empty() {
2479 preflight.headers.insert(
2482 ACCESS_CONTROL_REQUEST_HEADERS,
2483 HeaderValue::from_bytes(itertools::join(headers.iter(), ",").as_bytes())
2484 .unwrap_or(HeaderValue::from_static("")),
2485 );
2486 }
2487
2488 let mut fetch_params = FetchParams::new(preflight);
2491 let response =
2492 http_network_or_cache_fetch(&mut fetch_params, false, false, &mut None, context).await;
2493
2494 if cors_check(request, &response).is_ok() && response.status.code().is_success() {
2496 let mut methods = if response
2499 .headers
2500 .contains_key(header::ACCESS_CONTROL_ALLOW_METHODS)
2501 {
2502 match response.headers.typed_get::<AccessControlAllowMethods>() {
2503 Some(methods) => methods.iter().collect(),
2504 None => {
2506 return Response::network_error(NetworkError::CorsAllowMethods);
2507 },
2508 }
2509 } else {
2510 vec![]
2511 };
2512
2513 let header_names = if response
2516 .headers
2517 .contains_key(header::ACCESS_CONTROL_ALLOW_HEADERS)
2518 {
2519 match response.headers.typed_get::<AccessControlAllowHeaders>() {
2520 Some(names) => names.iter().collect(),
2521 None => {
2523 return Response::network_error(NetworkError::CorsAllowHeaders);
2524 },
2525 }
2526 } else {
2527 vec![]
2528 };
2529
2530 debug!(
2531 "CORS check: Allowed methods: {:?}, current method: {:?}",
2532 methods, request.method
2533 );
2534
2535 if methods.is_empty() && request.use_cors_preflight {
2538 methods = vec![request.method.clone()];
2539 }
2540
2541 if methods
2544 .iter()
2545 .all(|method| *method.as_str() != *request.method.as_ref()) &&
2546 !is_cors_safelisted_method(&request.method) &&
2547 (request.credentials_mode == CredentialsMode::Include ||
2548 methods.iter().all(|method| method.as_ref() != "*"))
2549 {
2550 return Response::network_error(NetworkError::CorsMethod);
2551 }
2552
2553 debug!(
2554 "CORS check: Allowed headers: {:?}, current headers: {:?}",
2555 header_names, request.headers
2556 );
2557
2558 if request.headers.iter().any(|(name, _)| {
2561 is_cors_non_wildcard_request_header_name(name) &&
2562 header_names.iter().all(|header_name| header_name != name)
2563 }) {
2564 return Response::network_error(NetworkError::CorsAuthorization);
2565 }
2566
2567 let unsafe_names = get_cors_unsafe_header_names(&request.headers);
2571 let header_names_set: HashSet<&HeaderName> = HashSet::from_iter(header_names.iter());
2572 let header_names_contains_star = header_names
2573 .iter()
2574 .any(|header_name| header_name.as_str() == "*");
2575 for unsafe_name in unsafe_names.iter() {
2576 if !header_names_set.contains(unsafe_name) &&
2577 (request.credentials_mode == CredentialsMode::Include ||
2578 !header_names_contains_star)
2579 {
2580 return Response::network_error(NetworkError::CorsHeaders);
2581 }
2582 }
2583
2584 let max_age: Option<Duration> = response
2587 .headers
2588 .typed_get::<AccessControlMaxAge>()
2589 .map(|acma| acma.into());
2590
2591 let max_age = max_age.unwrap_or(Duration::from_secs(5));
2593
2594 for method in &methods {
2605 cache.match_method_and_update(request, method.clone(), max_age);
2606 }
2607
2608 for header_name in &header_names {
2613 cache.match_header_and_update(request, header_name, max_age);
2614 }
2615
2616 return response;
2618 }
2619
2620 Response::network_error(NetworkError::CorsGeneral)
2622}
2623
2624fn cors_check(request: &Request, response: &Response) -> Result<(), ()> {
2626 let Some(origins) =
2628 get_value_from_header_list(ACCESS_CONTROL_ALLOW_ORIGIN.as_str(), &response.headers)
2629 else {
2630 return Err(());
2632 };
2633 let origin = origins.into_iter().map(char::from).collect::<String>();
2634
2635 if request.credentials_mode != CredentialsMode::Include && origin == "*" {
2637 return Ok(());
2638 }
2639
2640 if serialize_request_origin(request).to_string() != origin {
2642 return Err(());
2643 }
2644
2645 if request.credentials_mode != CredentialsMode::Include {
2647 return Ok(());
2648 }
2649
2650 let credentials = response
2652 .headers
2653 .typed_get::<AccessControlAllowCredentials>();
2654
2655 if credentials.is_some() {
2657 return Ok(());
2658 }
2659
2660 Err(())
2662}
2663
2664fn has_credentials(url: &ServoUrl) -> bool {
2665 !url.username().is_empty() || url.password().is_some()
2666}
2667
2668fn is_no_store_cache(headers: &HeaderMap) -> bool {
2669 headers.contains_key(header::IF_MODIFIED_SINCE) |
2670 headers.contains_key(header::IF_NONE_MATCH) |
2671 headers.contains_key(header::IF_UNMODIFIED_SINCE) |
2672 headers.contains_key(header::IF_MATCH) |
2673 headers.contains_key(header::IF_RANGE)
2674}
2675
2676fn is_redirect_status(status: StatusCode) -> bool {
2678 matches!(
2679 status,
2680 StatusCode::MOVED_PERMANENTLY |
2681 StatusCode::FOUND |
2682 StatusCode::SEE_OTHER |
2683 StatusCode::TEMPORARY_REDIRECT |
2684 StatusCode::PERMANENT_REDIRECT
2685 )
2686}
2687
2688fn serialize_request_origin(request: &Request) -> headers::Origin {
2690 let Origin::Origin(origin) = &request.origin else {
2692 panic!("origin cannot be \"client\" at this point in time");
2693 };
2694
2695 if request.redirect_taint_for_request() != RedirectTaint::SameOrigin {
2697 return headers::Origin::NULL;
2698 }
2699
2700 serialize_origin(origin)
2702}
2703
2704pub fn serialize_origin(origin: &ImmutableOrigin) -> headers::Origin {
2706 match origin {
2707 ImmutableOrigin::Opaque(_) => headers::Origin::NULL,
2708 ImmutableOrigin::Tuple(scheme, host, port) => {
2709 let port = match (scheme.as_ref(), port) {
2712 ("http" | "ws", 80) | ("https" | "wss", 443) | ("ftp", 21) => None,
2713 _ => Some(*port),
2714 };
2715
2716 headers::Origin::try_from_parts(scheme, &host.to_string(), port)
2718 .unwrap_or(headers::Origin::NULL)
2719 },
2720 }
2721}
2722
2723#[expect(
2725 clippy::collapsible_match,
2726 reason = "The current way follows the spec more closely"
2727)]
2728fn append_a_request_origin_header(request: &mut Request) {
2729 let Origin::Origin(request_origin) = &request.origin else {
2731 panic!("origin cannot be \"client\" at this point in time");
2732 };
2733
2734 let mut serialized_origin = serialize_request_origin(request);
2736
2737 if request.response_tainting == ResponseTainting::CorsTainting ||
2740 matches!(request.mode, RequestMode::WebSocket { .. })
2741 {
2742 request.headers.typed_insert(serialized_origin);
2743 }
2744 else if !matches!(request.method, Method::GET | Method::HEAD) {
2746 if request.mode != RequestMode::CorsMode {
2748 match request.referrer_policy {
2749 ReferrerPolicy::NoReferrer => {
2750 serialized_origin = headers::Origin::NULL;
2752 },
2753 ReferrerPolicy::NoReferrerWhenDowngrade |
2754 ReferrerPolicy::StrictOrigin |
2755 ReferrerPolicy::StrictOriginWhenCrossOrigin => {
2756 if let ImmutableOrigin::Tuple(scheme, _, _) = &request_origin &&
2759 scheme == "https" &&
2760 request.current_url().scheme() != "https"
2761 {
2762 serialized_origin = headers::Origin::NULL;
2763 }
2764 },
2765 ReferrerPolicy::SameOrigin => {
2766 if *request_origin != request.current_url().origin() {
2769 serialized_origin = headers::Origin::NULL;
2770 }
2771 },
2772 _ => {
2773 },
2775 };
2776 }
2777
2778 request.headers.typed_insert(serialized_origin);
2780 }
2781}
2782
2783fn append_the_fetch_metadata_headers(r: &mut Request) {
2785 if !r.url().is_potentially_trustworthy() {
2787 return;
2788 }
2789
2790 set_the_sec_fetch_dest_header(r);
2792
2793 set_the_sec_fetch_mode_header(r);
2795
2796 set_the_sec_fetch_site_header(r);
2798
2799 set_the_sec_fetch_user_header(r);
2801}
2802
2803fn append_cache_data_to_headers(http_request: &mut Request) {
2805 match http_request.cache_mode {
2806 CacheMode::Default if is_no_store_cache(&http_request.headers) => {
2810 http_request.cache_mode = CacheMode::NoStore;
2811 },
2812
2813 CacheMode::NoCache if !http_request.headers.contains_key(header::CACHE_CONTROL) => {
2823 http_request
2824 .headers
2825 .typed_insert(CacheControl::new().with_max_age(Duration::from_secs(0)));
2826 },
2827
2828 CacheMode::Reload | CacheMode::NoStore => {
2830 if !http_request.headers.contains_key(header::PRAGMA) {
2833 http_request.headers.typed_insert(Pragma::no_cache());
2834 }
2835
2836 if !http_request.headers.contains_key(header::CACHE_CONTROL) {
2839 http_request
2840 .headers
2841 .typed_insert(CacheControl::new().with_no_cache());
2842 }
2843 },
2844
2845 _ => {},
2846 }
2847}
2848
2849fn set_the_sec_fetch_dest_header(r: &mut Request) {
2851 debug_assert!(r.url().is_potentially_trustworthy());
2853
2854 let header = r.destination;
2858
2859 r.headers.typed_insert(SecFetchDest(header));
2861}
2862
2863fn set_the_sec_fetch_mode_header(r: &mut Request) {
2865 debug_assert!(r.url().is_potentially_trustworthy());
2867
2868 let header = &r.mode;
2871
2872 r.headers.typed_insert(SecFetchMode::from(header));
2874}
2875
2876fn set_the_sec_fetch_site_header(r: &mut Request) {
2878 let Origin::Origin(request_origin) = &r.origin else {
2881 panic!("request origin cannot be \"client\" at this point")
2882 };
2883
2884 debug_assert!(r.url().is_potentially_trustworthy());
2886
2887 let mut header = SecFetchSite::SameOrigin;
2890
2891 if header != SecFetchSite::None {
2896 for url in &r.url_list {
2897 if url.origin() == *request_origin {
2899 continue;
2900 }
2901
2902 header = SecFetchSite::CrossSite;
2904
2905 if !is_same_site(request_origin, &url.origin()) {
2907 break;
2908 }
2909
2910 header = SecFetchSite::SameSite;
2912 }
2913 }
2914
2915 r.headers.typed_insert(header);
2917}
2918
2919fn set_the_sec_fetch_user_header(r: &mut Request) {
2921 debug_assert!(r.url().is_potentially_trustworthy());
2923
2924 if !r.is_navigation_request() {
2927 return;
2928 }
2929
2930 let header = SecFetchUser;
2933
2934 r.headers.typed_insert(header);
2936}
2937
2938fn set_requests_referrer_policy_on_redirect(request: &mut Request, response: &Response) {
2940 let referrer_policy: ReferrerPolicy = response
2943 .headers
2944 .typed_get::<headers::ReferrerPolicy>()
2945 .into();
2946
2947 if referrer_policy != ReferrerPolicy::EmptyString {
2949 request.referrer_policy = referrer_policy;
2950 }
2951}