1use std::collections::HashSet;
6use std::iter::FromIterator;
7use std::sync::Arc as StdArc;
8use std::time::{Duration, SystemTime, UNIX_EPOCH};
9
10use async_recursion::async_recursion;
11use base::cross_process_instant::CrossProcessInstant;
12use base::generic_channel::GenericSharedMemory;
13use base::id::{BrowsingContextId, HistoryStateId, PipelineId};
14use crossbeam_channel::Sender;
15use devtools_traits::{
16 ChromeToDevtoolsControlMsg, DevtoolsControlMsg, HttpRequest as DevtoolsHttpRequest,
17 HttpResponse as DevtoolsHttpResponse, NetworkEvent, SecurityInfoUpdate,
18};
19use embedder_traits::{AuthenticationResponse, GenericEmbedderProxy};
20use futures::{TryFutureExt, TryStreamExt, future};
21use headers::authorization::Basic;
22use headers::{
23 AccessControlAllowCredentials, AccessControlAllowHeaders, AccessControlAllowMethods,
24 AccessControlMaxAge, AccessControlRequestMethod, Authorization, CacheControl, ContentLength,
25 HeaderMapExt, IfModifiedSince, LastModified, Pragma, Referer, StrictTransportSecurity,
26 UserAgent,
27};
28use http::header::{
29 self, ACCEPT, ACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_REQUEST_HEADERS, AUTHORIZATION,
30 CONTENT_ENCODING, CONTENT_LANGUAGE, CONTENT_LOCATION, CONTENT_TYPE, HeaderValue, RANGE,
31 WWW_AUTHENTICATE,
32};
33use http::{HeaderMap, Method, Request as HyperRequest, StatusCode};
34use http_body_util::combinators::BoxBody;
35use http_body_util::{BodyExt, Full};
36use hyper::Response as HyperResponse;
37use hyper::body::{Bytes, Frame};
38use hyper::ext::ReasonPhrase;
39use hyper::header::{HeaderName, TRANSFER_ENCODING};
40use hyper_serde::Serde;
41use ipc_channel::ipc::{self, IpcSender};
42use ipc_channel::router::ROUTER;
43use log::{debug, error, info, log_enabled, warn};
44use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
45use net_traits::fetch::headers::get_value_from_header_list;
46use net_traits::http_status::HttpStatus;
47use net_traits::policy_container::RequestPolicyContainer;
48use net_traits::pub_domains::{is_same_site, reg_suffix};
49use net_traits::request::Origin::Origin as SpecificOrigin;
50use net_traits::request::{
51 BodyChunkRequest, BodyChunkResponse, CacheMode, CredentialsMode, Destination, Initiator,
52 Origin, RedirectMode, Referrer, Request, RequestBuilder, RequestMode, ResponseTainting,
53 ServiceWorkersMode, TraversableForUserPrompts, get_cors_unsafe_header_names,
54 is_cors_non_wildcard_request_header_name, is_cors_safelisted_method,
55 is_cors_safelisted_request_header,
56};
57use net_traits::response::{
58 CacheState, HttpsState, RedirectTaint, Response, ResponseBody, ResponseType, TerminationReason,
59};
60use net_traits::{
61 CookieSource, DOCUMENT_ACCEPT_HEADER_VALUE, DebugVec, FetchMetadata, NetworkError,
62 RedirectEndValue, RedirectStartValue, ReferrerPolicy, ResourceAttribute, ResourceFetchTiming,
63 ResourceTimeValue, TlsSecurityInfo, TlsSecurityState,
64};
65use parking_lot::{Mutex, RwLock};
66use profile_traits::mem::{Report, ReportKind};
67use profile_traits::path;
68use rustc_hash::FxHashMap;
69use servo_arc::Arc;
70use servo_url::{ImmutableOrigin, ServoUrl};
71use tokio::sync::mpsc::{
72 Receiver as TokioReceiver, Sender as TokioSender, UnboundedReceiver, UnboundedSender, channel,
73 unbounded_channel,
74};
75use tokio_stream::wrappers::ReceiverStream;
76
77use crate::async_runtime::spawn_task;
78use crate::connector::{
79 CertificateErrorOverrideManager, ServoClient, TlsHandshakeInfo, create_tls_config,
80};
81use crate::cookie::ServoCookie;
82use crate::cookie_storage::CookieStorage;
83use crate::decoder::Decoder;
84use crate::embedder::NetToEmbedderMsg;
85use crate::fetch::cors_cache::CorsCache;
86use crate::fetch::fetch_params::FetchParams;
87use crate::fetch::headers::{SecFetchDest, SecFetchMode, SecFetchSite, SecFetchUser};
88use crate::fetch::methods::{Data, DoneChannel, FetchContext, Target, main_fetch};
89use crate::hsts::HstsList;
90use crate::http_cache::{
91 CacheKey, CachedResourcesOrGuard, HttpCache, construct_response, invalidate, refresh,
92};
93use crate::resource_thread::{AuthCache, AuthCacheEntry};
94use crate::websocket_loader::start_websocket;
95
96#[derive(Clone, Debug, Eq, PartialEq)]
98pub enum HttpCacheEntryState {
99 ReadyToConstruct,
103 PendingStore(usize),
105}
106
107pub struct HttpState {
108 pub hsts_list: RwLock<HstsList>,
109 pub cookie_jar: RwLock<CookieStorage>,
110 pub http_cache: HttpCache,
111 pub auth_cache: RwLock<AuthCache>,
112 pub history_states: RwLock<FxHashMap<HistoryStateId, Vec<u8>>>,
113 pub client: ServoClient,
114 pub override_manager: CertificateErrorOverrideManager,
115 pub embedder_proxy: GenericEmbedderProxy<NetToEmbedderMsg>,
116}
117
118impl HttpState {
119 pub(crate) fn memory_reports(&self, suffix: &str, ops: &mut MallocSizeOfOps) -> Vec<Report> {
120 vec![
121 Report {
122 path: path!["memory-cache", suffix],
123 kind: ReportKind::ExplicitJemallocHeapSize,
124 size: self.http_cache.size_of(ops),
125 },
126 Report {
127 path: path!["hsts-list", suffix],
128 kind: ReportKind::ExplicitJemallocHeapSize,
129 size: self.hsts_list.read().size_of(ops),
130 },
131 ]
132 }
133
134 async fn request_authentication(
135 &self,
136 request: &Request,
137 response: &Response,
138 ) -> Option<AuthenticationResponse> {
139 let webview_id = request.target_webview_id?;
141 let for_proxy = response.status == StatusCode::PROXY_AUTHENTICATION_REQUIRED;
142
143 if request.mode != RequestMode::Navigate {
145 return None;
146 }
147
148 let (sender, receiver) = tokio::sync::oneshot::channel();
149 self.embedder_proxy
150 .send(NetToEmbedderMsg::RequestAuthentication(
151 webview_id,
152 request.url(),
153 for_proxy,
154 sender,
155 ));
156 receiver.await.ok()?
157 }
158}
159
160pub(crate) fn set_default_accept(request: &mut Request) {
162 if request.headers.contains_key(header::ACCEPT) {
164 return;
165 }
166
167 let value = if request.initiator == Initiator::Prefetch {
169 DOCUMENT_ACCEPT_HEADER_VALUE
170 } else {
171 match request.destination {
174 Destination::Document | Destination::Frame | Destination::IFrame => {
175 DOCUMENT_ACCEPT_HEADER_VALUE
176 },
177 Destination::Image => {
178 HeaderValue::from_static("image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5")
179 },
180 Destination::Json => HeaderValue::from_static("application/json,*/*;q=0.5"),
181 Destination::Style => HeaderValue::from_static("text/css,*/*;q=0.1"),
182 _ => HeaderValue::from_static("*/*"),
184 }
185 };
186
187 request.headers.insert(header::ACCEPT, value);
189}
190
191fn set_default_accept_encoding(headers: &mut HeaderMap) {
192 if headers.contains_key(header::ACCEPT_ENCODING) {
193 return;
194 }
195
196 headers.insert(
198 header::ACCEPT_ENCODING,
199 HeaderValue::from_static("gzip, deflate, br, zstd"),
200 );
201}
202
203fn no_referrer_when_downgrade(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
205 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
207 return None;
208 }
209 strip_url_for_use_as_referrer(referrer_url, false)
211}
212
213fn strict_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
215 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
217 return None;
218 }
219 strip_url_for_use_as_referrer(referrer_url, true)
221}
222
223fn strict_origin_when_cross_origin(
225 referrer_url: ServoUrl,
226 current_url: ServoUrl,
227) -> Option<ServoUrl> {
228 if referrer_url.origin() == current_url.origin() {
230 return strip_url_for_use_as_referrer(referrer_url, false);
231 }
232 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
234 return None;
235 }
236 strip_url_for_use_as_referrer(referrer_url, true)
238}
239
240fn is_schemelessy_same_site(site_a: &ImmutableOrigin, site_b: &ImmutableOrigin) -> bool {
242 if !site_a.is_tuple() && !site_b.is_tuple() && site_a == site_b {
244 true
245 } else if site_a.is_tuple() && site_b.is_tuple() {
246 let host_a = site_a.host().map(|h| h.to_string()).unwrap_or_default();
248 let host_b = site_b.host().map(|h| h.to_string()).unwrap_or_default();
249
250 let host_a_reg = reg_suffix(&host_a);
251 let host_b_reg = reg_suffix(&host_b);
252
253 (site_a.host() == site_b.host() && host_a_reg.is_empty()) ||
255 (host_a_reg == host_b_reg && !host_a_reg.is_empty())
256 } else {
257 false
259 }
260}
261
262fn strip_url_for_use_as_referrer(mut url: ServoUrl, origin_only: bool) -> Option<ServoUrl> {
264 const MAX_REFERRER_URL_LENGTH: usize = 4096;
265 if url.is_local_scheme() {
267 return None;
268 }
269 {
271 let url = url.as_mut_url();
272 let _ = url.set_username("");
273 let _ = url.set_password(None);
274 url.set_fragment(None);
275 if origin_only || url.as_str().len() > MAX_REFERRER_URL_LENGTH {
279 url.set_path("");
280 url.set_query(None);
281 }
282 }
283 Some(url)
285}
286
287fn same_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
289 if referrer_url.origin() == current_url.origin() {
291 return strip_url_for_use_as_referrer(referrer_url, false);
292 }
293 None
295}
296
297fn origin_when_cross_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
299 if referrer_url.origin() == current_url.origin() {
301 return strip_url_for_use_as_referrer(referrer_url, false);
302 }
303 strip_url_for_use_as_referrer(referrer_url, true)
305}
306
307pub fn determine_requests_referrer(
309 referrer_policy: ReferrerPolicy,
310 referrer_source: ServoUrl,
311 current_url: ServoUrl,
312) -> Option<ServoUrl> {
313 match referrer_policy {
314 ReferrerPolicy::EmptyString | ReferrerPolicy::NoReferrer => None,
315 ReferrerPolicy::Origin => strip_url_for_use_as_referrer(referrer_source, true),
316 ReferrerPolicy::UnsafeUrl => strip_url_for_use_as_referrer(referrer_source, false),
317 ReferrerPolicy::StrictOrigin => strict_origin(referrer_source, current_url),
318 ReferrerPolicy::StrictOriginWhenCrossOrigin => {
319 strict_origin_when_cross_origin(referrer_source, current_url)
320 },
321 ReferrerPolicy::SameOrigin => same_origin(referrer_source, current_url),
322 ReferrerPolicy::OriginWhenCrossOrigin => {
323 origin_when_cross_origin(referrer_source, current_url)
324 },
325 ReferrerPolicy::NoReferrerWhenDowngrade => {
326 no_referrer_when_downgrade(referrer_source, current_url)
327 },
328 }
329}
330
331fn set_request_cookies(
332 url: &ServoUrl,
333 headers: &mut HeaderMap,
334 cookie_jar: &RwLock<CookieStorage>,
335) {
336 let mut cookie_jar = cookie_jar.write();
337 cookie_jar.remove_expired_cookies_for_url(url);
338 if let Some(cookie_list) = cookie_jar.cookies_for_url(url, CookieSource::HTTP) {
339 headers.insert(
340 header::COOKIE,
341 HeaderValue::from_bytes(cookie_list.as_bytes()).unwrap(),
342 );
343 }
344}
345
346fn set_cookie_for_url(cookie_jar: &RwLock<CookieStorage>, request: &ServoUrl, cookie_val: &str) {
347 let mut cookie_jar = cookie_jar.write();
348 let source = CookieSource::HTTP;
349
350 if let Some(cookie) = ServoCookie::from_cookie_string(cookie_val, request, source) {
351 cookie_jar.push(cookie, request, source);
352 }
353}
354
355fn set_cookies_from_headers(
356 url: &ServoUrl,
357 headers: &HeaderMap,
358 cookie_jar: &RwLock<CookieStorage>,
359) {
360 for cookie in headers.get_all(header::SET_COOKIE) {
361 let cookie_bytes = cookie.as_bytes();
362 if !ServoCookie::is_valid_name_or_value(cookie_bytes) {
363 continue;
364 }
365 if let Ok(cookie_str) = std::str::from_utf8(cookie_bytes) {
366 set_cookie_for_url(cookie_jar, url, cookie_str);
367 }
368 }
369}
370
371fn build_tls_security_info(handshake: &TlsHandshakeInfo, hsts_enabled: bool) -> TlsSecurityInfo {
372 let state = if handshake.protocol_version.is_none() || handshake.cipher_suite.is_none() {
380 TlsSecurityState::Insecure
382 } else {
383 TlsSecurityState::Secure
385 };
386
387 TlsSecurityInfo {
388 state,
389 weakness_reasons: Vec::new(), protocol_version: handshake.protocol_version.clone(),
391 cipher_suite: handshake.cipher_suite.clone(),
392 kea_group_name: handshake.kea_group_name.clone(),
393 signature_scheme_name: handshake.signature_scheme_name.clone(),
394 alpn_protocol: handshake.alpn_protocol.clone(),
395 certificate_chain_der: handshake.certificate_chain_der.clone(),
396 certificate_transparency: None,
397 hsts: hsts_enabled,
398 hpkp: false,
399 used_ech: handshake.used_ech,
400 used_delegated_credentials: false,
401 used_ocsp: false,
402 used_private_dns: false,
403 }
404}
405
406#[allow(clippy::too_many_arguments)]
407fn prepare_devtools_request(
408 request_id: String,
409 url: ServoUrl,
410 method: Method,
411 headers: HeaderMap,
412 body: Option<Vec<u8>>,
413 pipeline_id: PipelineId,
414 connect_time: Duration,
415 send_time: Duration,
416 destination: Destination,
417 is_xhr: bool,
418 browsing_context_id: BrowsingContextId,
419) -> ChromeToDevtoolsControlMsg {
420 let started_date_time = SystemTime::now();
421 let request = DevtoolsHttpRequest {
422 url,
423 method,
424 headers,
425 body: body.map(DebugVec::from),
426 pipeline_id,
427 started_date_time,
428 time_stamp: started_date_time
429 .duration_since(UNIX_EPOCH)
430 .unwrap_or_default()
431 .as_secs() as i64,
432 connect_time,
433 send_time,
434 destination,
435 is_xhr,
436 browsing_context_id,
437 };
438 let net_event = NetworkEvent::HttpRequestUpdate(request);
439
440 ChromeToDevtoolsControlMsg::NetworkEvent(request_id, net_event)
441}
442
443pub fn send_request_to_devtools(
444 msg: ChromeToDevtoolsControlMsg,
445 devtools_chan: &Sender<DevtoolsControlMsg>,
446) {
447 if matches!(msg, ChromeToDevtoolsControlMsg::NetworkEvent(_, ref network_event) if !network_event.forward_to_devtools())
448 {
449 return;
450 }
451 if let Err(e) = devtools_chan.send(DevtoolsControlMsg::FromChrome(msg)) {
452 error!("DevTools send failed: {e}");
453 }
454}
455
456pub fn send_response_to_devtools(
457 request: &Request,
458 context: &FetchContext,
459 response: &Response,
460 body_data: Option<Vec<u8>>,
461) {
462 let meta = match response.metadata() {
463 Ok(FetchMetadata::Unfiltered(m)) => m,
464 Ok(FetchMetadata::Filtered { unsafe_, .. }) => unsafe_,
465 Err(_) => {
466 log::warn!("No metadata available, skipping devtools response.");
467 return;
468 },
469 };
470 send_response_values_to_devtools(
471 meta.headers.map(Serde::into_inner),
472 meta.status,
473 body_data,
474 response.cache_state,
475 request,
476 context.devtools_chan.clone(),
477 );
478}
479
480#[allow(clippy::too_many_arguments)]
481pub fn send_response_values_to_devtools(
482 headers: Option<HeaderMap>,
483 status: HttpStatus,
484 body: Option<Vec<u8>>,
485 cache_state: CacheState,
486 request: &Request,
487 devtools_chan: Option<Sender<DevtoolsControlMsg>>,
488) {
489 if let (Some(devtools_chan), Some(pipeline_id), Some(webview_id)) = (
490 devtools_chan,
491 request.pipeline_id,
492 request.target_webview_id,
493 ) {
494 let browsing_context_id = webview_id.into();
495 let from_cache = matches!(cache_state, CacheState::Local | CacheState::Validated);
496
497 let devtoolsresponse = DevtoolsHttpResponse {
498 headers,
499 status,
500 body: body.map(DebugVec::from),
501 from_cache,
502 pipeline_id,
503 browsing_context_id,
504 };
505 let net_event_response = NetworkEvent::HttpResponse(devtoolsresponse);
506
507 let msg =
508 ChromeToDevtoolsControlMsg::NetworkEvent(request.id.0.to_string(), net_event_response);
509
510 let _ = devtools_chan.send(DevtoolsControlMsg::FromChrome(msg));
511 }
512}
513
514pub fn send_security_info_to_devtools(
515 request: &Request,
516 context: &FetchContext,
517 response: &Response,
518) {
519 let meta = match response.metadata() {
520 Ok(FetchMetadata::Unfiltered(m)) => m,
521 Ok(FetchMetadata::Filtered { unsafe_, .. }) => unsafe_,
522 Err(_) => {
523 log::warn!("No metadata available, skipping devtools security info.");
524 return;
525 },
526 };
527
528 if let (Some(devtools_chan), Some(security_info), Some(webview_id)) = (
529 context.devtools_chan.clone(),
530 meta.tls_security_info.clone(),
531 request.target_webview_id,
532 ) {
533 let update = NetworkEvent::SecurityInfo(SecurityInfoUpdate {
534 browsing_context_id: webview_id.into(),
535 security_info: Some(security_info),
536 });
537
538 let msg = ChromeToDevtoolsControlMsg::NetworkEvent(request.id.0.to_string(), update);
539
540 let _ = devtools_chan.send(DevtoolsControlMsg::FromChrome(msg));
541 }
542}
543
544pub fn send_early_httprequest_to_devtools(request: &Request, context: &FetchContext) {
545 if request.url().scheme() == "data" {
547 return;
548 }
549 if let (Some(devtools_chan), Some(browsing_context_id), Some(pipeline_id)) = (
550 context.devtools_chan.as_ref(),
551 request.target_webview_id.map(|id| id.into()),
552 request.pipeline_id,
553 ) {
554 let devtools_request = DevtoolsHttpRequest {
556 url: request.current_url().clone(),
557 method: request.method.clone(),
558 headers: request.headers.clone(),
559 body: None,
560 pipeline_id,
561 started_date_time: SystemTime::now(),
562 time_stamp: 0,
563 connect_time: Duration::from_millis(0),
564 send_time: Duration::from_millis(0),
565 destination: request.destination,
566 is_xhr: false,
567 browsing_context_id,
568 };
569
570 let msg = ChromeToDevtoolsControlMsg::NetworkEvent(
571 request.id.0.to_string(),
572 NetworkEvent::HttpRequest(devtools_request),
573 );
574
575 send_request_to_devtools(msg, devtools_chan);
576 }
577}
578
579fn auth_from_cache(
580 auth_cache: &RwLock<AuthCache>,
581 origin: &ImmutableOrigin,
582) -> Option<Authorization<Basic>> {
583 if let Some(auth_entry) = auth_cache.read().entries.get(&origin.ascii_serialization()) {
584 let user_name = &auth_entry.user_name;
585 let password = &auth_entry.password;
586 Some(Authorization::basic(user_name, password))
587 } else {
588 None
589 }
590}
591
592enum BodyChunk {
595 Chunk(GenericSharedMemory),
597 Done,
599}
600
601enum BodyStream {
603 Chunked(TokioReceiver<Result<Frame<Bytes>, hyper::Error>>),
606 Buffered(UnboundedReceiver<BodyChunk>),
609}
610
611enum BodySink {
614 Chunked(TokioSender<Result<Frame<Bytes>, hyper::Error>>),
616 Buffered(UnboundedSender<BodyChunk>),
620}
621
622impl BodySink {
623 fn transmit_bytes(&self, bytes: GenericSharedMemory) {
624 match self {
625 BodySink::Chunked(sender) => {
626 let sender = sender.clone();
627 spawn_task(async move {
628 let _ = sender
629 .send(Ok(Frame::data(Bytes::copy_from_slice(&bytes))))
630 .await;
631 });
632 },
633 BodySink::Buffered(sender) => {
634 let _ = sender.send(BodyChunk::Chunk(bytes));
635 },
636 }
637 }
638
639 fn close(&self) {
640 match self {
641 BodySink::Chunked(_) => { },
642 BodySink::Buffered(sender) => {
643 let _ = sender.send(BodyChunk::Done);
644 },
645 }
646 }
647}
648
649#[allow(clippy::too_many_arguments)]
650async fn obtain_response(
651 client: &ServoClient,
652 url: &ServoUrl,
653 method: &Method,
654 request_headers: &mut HeaderMap,
655 body: Option<StdArc<Mutex<IpcSender<BodyChunkRequest>>>>,
656 source_is_null: bool,
657 pipeline_id: &Option<PipelineId>,
658 request_id: Option<&str>,
659 destination: Destination,
660 is_xhr: bool,
661 context: &FetchContext,
662 fetch_terminated: UnboundedSender<bool>,
663 browsing_context_id: Option<BrowsingContextId>,
664) -> Result<(HyperResponse<Decoder>, Option<ChromeToDevtoolsControlMsg>), NetworkError> {
665 {
666 let mut headers = request_headers.clone();
667
668 let devtools_bytes = StdArc::new(Mutex::new(vec![]));
669
670 let encoded_url = url
672 .clone()
673 .into_url()
674 .as_ref()
675 .replace('|', "%7C")
676 .replace('{', "%7B")
677 .replace('}', "%7D");
678
679 let request = if let Some(chunk_requester) = body {
680 let (sink, stream) = if source_is_null {
681 headers.insert(TRANSFER_ENCODING, HeaderValue::from_static("chunked"));
684
685 let (sender, receiver) = channel(1);
686 (BodySink::Chunked(sender), BodyStream::Chunked(receiver))
687 } else {
688 let (sender, receiver) = unbounded_channel();
695 (BodySink::Buffered(sender), BodyStream::Buffered(receiver))
696 };
697
698 let (body_chan, body_port) = ipc::channel().unwrap();
699
700 {
701 let requester = chunk_requester.lock();
702 let _ = requester.send(BodyChunkRequest::Connect(body_chan));
703
704 let _ = requester.send(BodyChunkRequest::Chunk);
707 }
708
709 let devtools_bytes = devtools_bytes.clone();
710 let chunk_requester2 = chunk_requester.clone();
711
712 ROUTER.add_typed_route(
713 body_port,
714 Box::new(move |message| {
715 info!("Received message");
716 let bytes = match message.unwrap() {
717 BodyChunkResponse::Chunk(bytes) => bytes,
718 BodyChunkResponse::Done => {
719 let _ = fetch_terminated.send(false);
721 sink.close();
722
723 return;
724 },
725 BodyChunkResponse::Error => {
726 let _ = fetch_terminated.send(true);
730 sink.close();
731
732 return;
733 },
734 };
735
736 devtools_bytes.lock().extend_from_slice(&bytes);
737
738 sink.transmit_bytes(bytes);
741
742 let _ = chunk_requester2.lock().send(BodyChunkRequest::Chunk);
745 }),
746 );
747
748 let body = match stream {
749 BodyStream::Chunked(receiver) => {
750 let stream = ReceiverStream::new(receiver);
751 BoxBody::new(http_body_util::StreamBody::new(stream))
752 },
753 BodyStream::Buffered(mut receiver) => {
754 let mut body = vec![];
756 loop {
757 match receiver.recv().await {
758 Some(BodyChunk::Chunk(bytes)) => {
759 body.extend_from_slice(&bytes);
760 },
761 Some(BodyChunk::Done) => break,
762 None => warn!("Failed to read all chunks from request body."),
763 }
764 }
765 Full::new(body.into()).map_err(|_| unreachable!()).boxed()
766 },
767 };
768 HyperRequest::builder()
769 .method(method)
770 .uri(encoded_url)
771 .body(body)
772 } else {
773 HyperRequest::builder()
774 .method(method)
775 .uri(encoded_url)
776 .body(
777 http_body_util::Empty::new()
778 .map_err(|_| unreachable!())
779 .boxed(),
780 )
781 };
782
783 context
784 .timing
785 .lock()
786 .set_attribute(ResourceAttribute::DomainLookupStart);
787
788 let connect_start = CrossProcessInstant::now();
791 context
792 .timing
793 .lock()
794 .set_attribute(ResourceAttribute::ConnectStart(connect_start));
795
796 if url.scheme() == "https" {
800 context
801 .timing
802 .lock()
803 .set_attribute(ResourceAttribute::SecureConnectionStart);
804 }
805
806 let mut request = match request {
807 Ok(request) => request,
808 Err(error) => return Err(NetworkError::HttpError(error.to_string())),
809 };
810 *request.headers_mut() = headers.clone();
811
812 let connect_end = CrossProcessInstant::now();
813 context
814 .timing
815 .lock()
816 .set_attribute(ResourceAttribute::ConnectEnd(connect_end));
817
818 let request_id = request_id.map(|v| v.to_owned());
819 let pipeline_id = *pipeline_id;
820 let closure_url = url.clone();
821 let method = method.clone();
822 let send_start = CrossProcessInstant::now();
823
824 let host = request.uri().host().unwrap_or("").to_owned();
825 let override_manager = context.state.override_manager.clone();
826 let headers = headers.clone();
827 let is_secure_scheme = url.is_secure_scheme();
828
829 client
830 .request(request)
831 .and_then(move |res| {
832 let send_end = CrossProcessInstant::now();
833
834 let msg = if let Some(request_id) = request_id {
837 if let Some(pipeline_id) = pipeline_id {
838 if let Some(browsing_context_id) = browsing_context_id {
839 Some(prepare_devtools_request(
840 request_id,
841 closure_url,
842 method.clone(),
843 headers,
844 Some(devtools_bytes.lock().clone()),
845 pipeline_id,
846 (connect_end - connect_start).unsigned_abs(),
847 (send_end - send_start).unsigned_abs(),
848 destination,
849 is_xhr,
850 browsing_context_id,
851 ))
852 } else {
853 debug!("Not notifying devtools (no browsing_context_id)");
854 None
855 }
856 } else {
861 debug!("Not notifying devtools (no pipeline_id)");
862 None
863 }
864 } else {
865 debug!("Not notifying devtools (no request_id)");
866 None
867 };
868
869 future::ready(Ok((
870 Decoder::detect(res.map(|r| r.boxed()), is_secure_scheme),
871 msg,
872 )))
873 })
874 .map_err(move |error| {
875 warn!("network error: {error:?}");
876 NetworkError::from_hyper_error(
877 &error,
878 override_manager.remove_certificate_failing_verification(host.as_str()),
879 )
880 })
881 .await
882 }
883}
884
885#[async_recursion]
887#[allow(clippy::too_many_arguments)]
888pub async fn http_fetch(
889 fetch_params: &mut FetchParams,
890 cache: &mut CorsCache,
891 cors_flag: bool,
892 cors_preflight_flag: bool,
893 authentication_fetch_flag: bool,
894 target: Target<'async_recursion>,
895 done_chan: &mut DoneChannel,
896 context: &FetchContext,
897) -> Response {
898 *done_chan = None;
900 let request = &mut fetch_params.request;
902
903 let mut response: Option<Response> = None;
906
907 if request.service_workers_mode == ServiceWorkersMode::All {
909 if let Some(ref res) = response {
914 if (res.response_type == ResponseType::Opaque && request.mode != RequestMode::NoCors) ||
922 (res.response_type == ResponseType::OpaqueRedirect &&
923 request.redirect_mode != RedirectMode::Manual) ||
924 (res.url_list.len() > 1 && request.redirect_mode != RedirectMode::Follow) ||
925 res.is_network_error()
926 {
927 return Response::network_error(NetworkError::ConnectionFailure);
928 }
929
930 }
933 }
934
935 if response.is_none() {
937 if cors_preflight_flag {
939 let method_cache_match = cache.match_method(request, request.method.clone());
940
941 let method_mismatch = !method_cache_match &&
942 (!is_cors_safelisted_method(&request.method) || request.use_cors_preflight);
943 let header_mismatch = request.headers.iter().any(|(name, value)| {
944 !cache.match_header(request, name) &&
945 !is_cors_safelisted_request_header(&name, &value)
946 });
947
948 if method_mismatch || header_mismatch {
950 let preflight_result = cors_preflight_fetch(request, cache, context).await;
951 if let Some(e) = preflight_result.get_network_error() {
953 return Response::network_error(e.clone());
954 }
955 }
956 }
957
958 if request.redirect_mode == RedirectMode::Follow {
960 request.service_workers_mode = ServiceWorkersMode::None;
961 }
962
963 context
967 .timing
968 .lock()
969 .set_attribute(ResourceAttribute::RequestStart);
970
971 let mut fetch_result = http_network_or_cache_fetch(
972 fetch_params,
973 authentication_fetch_flag,
974 cors_flag,
975 done_chan,
976 context,
977 )
978 .await;
979
980 if cors_flag && cors_check(&fetch_params.request, &fetch_result).is_err() {
982 return Response::network_error(NetworkError::CorsGeneral);
983 }
984
985 fetch_result.return_internal = false;
986 response = Some(fetch_result);
987 }
988
989 let request = &mut fetch_params.request;
990
991 let mut response = response.unwrap();
993
994 if response
998 .actual_response()
999 .status
1000 .try_code()
1001 .is_some_and(is_redirect_status)
1002 {
1003 if response.actual_response().status != StatusCode::SEE_OTHER {
1007 }
1009
1010 response = match request.redirect_mode {
1012 RedirectMode::Error => Response::network_error(NetworkError::RedirectError),
1014 RedirectMode::Manual => {
1015 if request.mode == RequestMode::Navigate {
1018 let location_url =
1022 location_url_for_response(&response, request.current_url().fragment());
1023 response.actual_response_mut().location_url = location_url;
1024 response
1025 } else {
1026 response.to_filtered(ResponseType::OpaqueRedirect)
1028 }
1029 },
1030 RedirectMode::Follow => {
1031 response.return_internal = true;
1033 http_redirect_fetch(
1034 fetch_params,
1035 cache,
1036 response,
1037 cors_flag,
1038 target,
1039 done_chan,
1040 context,
1041 )
1042 .await
1043 },
1044 };
1045 }
1046
1047 response.return_internal = true;
1049 context
1050 .timing
1051 .lock()
1052 .set_attribute(ResourceAttribute::RedirectCount(
1053 fetch_params.request.redirect_count as u16,
1054 ));
1055
1056 response.resource_timing = Arc::clone(&context.timing);
1057
1058 response
1060}
1061
1062struct RedirectEndTimer(Option<Arc<Mutex<ResourceFetchTiming>>>);
1064
1065impl RedirectEndTimer {
1066 fn neuter(&mut self) {
1067 self.0 = None;
1068 }
1069}
1070
1071impl Drop for RedirectEndTimer {
1072 fn drop(&mut self) {
1073 let RedirectEndTimer(resource_fetch_timing_opt) = self;
1074
1075 resource_fetch_timing_opt.as_ref().map_or((), |t| {
1076 t.lock()
1077 .set_attribute(ResourceAttribute::RedirectEnd(RedirectEndValue::Zero));
1078 })
1079 }
1080}
1081
1082static REQUEST_BODY_HEADER_NAMES: &[HeaderName] = &[
1084 CONTENT_ENCODING,
1085 CONTENT_LANGUAGE,
1086 CONTENT_LOCATION,
1087 CONTENT_TYPE,
1088];
1089
1090fn location_url_for_response(
1092 response: &Response,
1093 request_fragment: Option<&str>,
1094) -> Option<Result<ServoUrl, String>> {
1095 assert!(
1097 response
1098 .actual_response()
1099 .status
1100 .try_code()
1101 .is_some_and(is_redirect_status)
1102 );
1103 let mut location = response
1105 .actual_response()
1106 .headers
1107 .get(header::LOCATION)
1108 .and_then(|header_value| {
1109 HeaderValue::to_str(header_value)
1110 .map(|location_string| {
1111 ServoUrl::parse_with_base(response.actual_response().url(), location_string)
1113 .map_err(|error| error.to_string())
1114 })
1115 .ok()
1116 });
1117
1118 if let Some(Ok(ref mut location)) = location {
1120 if location.fragment().is_none() {
1121 location.set_fragment(request_fragment);
1122 }
1123 }
1124 location
1126}
1127
1128#[async_recursion]
1130pub async fn http_redirect_fetch(
1131 fetch_params: &mut FetchParams,
1132 cache: &mut CorsCache,
1133 mut response: Response,
1134 cors_flag: bool,
1135 target: Target<'async_recursion>,
1136 done_chan: &mut DoneChannel,
1137 context: &FetchContext,
1138) -> Response {
1139 let mut redirect_end_timer = RedirectEndTimer(Some(context.timing.clone()));
1140
1141 let request = &mut fetch_params.request;
1143
1144 assert!(response.return_internal);
1146
1147 let location_url = location_url_for_response(&response, request.current_url().fragment());
1149 response.actual_response_mut().location_url = location_url.clone();
1150
1151 let location_url = match location_url {
1152 None => return response,
1154 Some(Err(err)) => {
1156 return Response::network_error(NetworkError::ResourceLoadError(
1157 "Location URL parse failure: ".to_owned() + &err,
1158 ));
1159 },
1160 Some(Ok(url)) if !matches!(url.scheme(), "http" | "https") => {
1162 return Response::network_error(NetworkError::UnsupportedScheme);
1163 },
1164 Some(Ok(url)) => url,
1165 };
1166
1167 context
1170 .timing
1171 .lock()
1172 .set_attribute(ResourceAttribute::RedirectStart(
1173 RedirectStartValue::FetchStart,
1174 ));
1175
1176 context
1177 .timing
1178 .lock()
1179 .set_attribute(ResourceAttribute::FetchStart);
1180
1181 context
1183 .timing
1184 .lock()
1185 .set_attribute(ResourceAttribute::StartTime(ResourceTimeValue::FetchStart));
1186
1187 context
1188 .timing
1189 .lock()
1190 .set_attribute(ResourceAttribute::StartTime(
1191 ResourceTimeValue::RedirectStart,
1192 )); if request.redirect_count >= 20 {
1196 return Response::network_error(NetworkError::TooManyRedirects);
1197 }
1198
1199 request.redirect_count += 1;
1201
1202 let same_origin = match request.origin {
1205 Origin::Origin(ref origin) => *origin == location_url.origin(),
1206 Origin::Client => panic!(
1207 "Request origin should not be client for {}",
1208 request.current_url()
1209 ),
1210 };
1211
1212 let has_credentials = has_credentials(&location_url);
1213
1214 if request.mode == RequestMode::CorsMode && !same_origin && has_credentials {
1215 return Response::network_error(NetworkError::CorsCredentials);
1216 }
1217
1218 if cors_flag && location_url.origin() != request.current_url().origin() {
1219 request.origin = Origin::Origin(ImmutableOrigin::new_opaque());
1220 }
1221
1222 if cors_flag && has_credentials {
1224 return Response::network_error(NetworkError::CorsCredentials);
1225 }
1226
1227 if response.actual_response().status != StatusCode::SEE_OTHER &&
1230 request.body.as_ref().is_some_and(|b| b.source_is_null())
1231 {
1232 return Response::network_error(NetworkError::ConnectionFailure);
1233 }
1234
1235 if response
1237 .actual_response()
1238 .status
1239 .try_code()
1240 .is_some_and(|code| {
1241 ((code == StatusCode::MOVED_PERMANENTLY || code == StatusCode::FOUND) &&
1243 request.method == Method::POST) ||
1244 (code == StatusCode::SEE_OTHER &&
1246 request.method != Method::HEAD &&
1247 request.method != Method::GET)
1248 })
1249 {
1250 request.method = Method::GET;
1252 request.body = None;
1253 for name in REQUEST_BODY_HEADER_NAMES {
1255 request.headers.remove(name);
1256 }
1257 }
1258
1259 if location_url.origin() != request.current_url().origin() {
1263 request.headers.remove(AUTHORIZATION);
1266 }
1267
1268 if let Some(body) = request.body.as_mut() {
1271 body.extract_source();
1272 }
1273
1274 request.url_list.push(location_url);
1278
1279 set_requests_referrer_policy_on_redirect(request, response.actual_response());
1281
1282 let recursive_flag = request.redirect_mode != RedirectMode::Manual;
1285
1286 let fetch_response = main_fetch(
1288 fetch_params,
1289 cache,
1290 recursive_flag,
1291 target,
1292 done_chan,
1293 context,
1294 )
1295 .await;
1296
1297 context
1299 .timing
1300 .lock()
1301 .set_attribute(ResourceAttribute::RedirectEnd(
1302 RedirectEndValue::ResponseEnd,
1303 ));
1304 redirect_end_timer.neuter();
1305
1306 fetch_response
1307}
1308
1309#[async_recursion]
1311async fn http_network_or_cache_fetch(
1312 fetch_params: &mut FetchParams,
1313 authentication_fetch_flag: bool,
1314 cors_flag: bool,
1315 done_chan: &mut DoneChannel,
1316 context: &FetchContext,
1317) -> Response {
1318 let http_fetch_params: &mut FetchParams;
1320 let mut fetch_params_copy: FetchParams;
1321
1322 let mut response: Option<Response> = None;
1326
1327 let mut revalidating_flag = false;
1329
1330 let http_request = if fetch_params.request.traversable_for_user_prompts ==
1334 TraversableForUserPrompts::NoTraversable &&
1335 fetch_params.request.redirect_mode == RedirectMode::Error
1336 {
1337 http_fetch_params = fetch_params;
1338 &mut http_fetch_params.request
1339 }
1340 else {
1342 fetch_params_copy =
1345 std::mem::replace(fetch_params, FetchParams::new(fetch_params.request.clone()));
1346 http_fetch_params = &mut fetch_params_copy;
1347
1348 &mut http_fetch_params.request
1349 };
1350
1351 let include_credentials = match http_request.credentials_mode {
1353 CredentialsMode::Include => true,
1355 CredentialsMode::CredentialsSameOrigin
1357 if http_request.response_tainting == ResponseTainting::Basic =>
1358 {
1359 true
1360 },
1361 _ => false,
1362 };
1363
1364 let content_length = http_request
1371 .body
1372 .as_ref()
1373 .and_then(|body| body.len().map(|size| size as u64));
1374
1375 let mut content_length_header_value = None;
1377
1378 if http_request.body.is_none() && matches!(http_request.method, Method::POST | Method::PUT) {
1381 content_length_header_value = Some(0);
1382 }
1383
1384 if let Some(content_length) = content_length {
1388 content_length_header_value = Some(content_length);
1389 };
1390
1391 if let Some(content_length_header_value) = content_length_header_value {
1394 http_request
1395 .headers
1396 .typed_insert(ContentLength(content_length_header_value));
1397 }
1398
1399 if http_request.keep_alive {
1401 if let Some(content_length) = content_length {
1402 let in_flight_keep_alive_bytes: u64 = context
1407 .in_flight_keep_alive_records
1408 .lock()
1409 .get(
1410 &http_request
1411 .pipeline_id
1412 .expect("Must always set a pipeline ID for keep-alive requests"),
1413 )
1414 .map(|records| {
1415 records
1419 .iter()
1420 .map(|record| {
1421 if record.request_id == http_request.id {
1422 0
1425 } else {
1426 record.keep_alive_body_length
1427 }
1428 })
1429 .sum()
1430 })
1431 .unwrap_or_default();
1432 if content_length + in_flight_keep_alive_bytes > 64 * 1024 {
1434 return Response::network_error(NetworkError::TooManyInFlightKeepAliveRequests);
1435 }
1436 }
1437 }
1438
1439 match http_request.referrer {
1441 Referrer::ReferrerUrl(ref http_request_referrer) |
1442 Referrer::Client(ref http_request_referrer) => {
1443 if let Ok(referer) = http_request_referrer.as_str().parse::<Referer>() {
1446 http_request.headers.typed_insert(referer);
1448 } else {
1449 error!("Failed to parse {} as referrer", http_request_referrer);
1453 }
1454 },
1455 _ => {},
1456 };
1457
1458 append_a_request_origin_header(http_request);
1460
1461 append_the_fetch_metadata_headers(http_request);
1463
1464 if http_request.initiator == Initiator::Prefetch {
1467 if let Ok(value) = HeaderValue::from_str("prefetch") {
1468 http_request.headers.insert("Sec-Purpose", value);
1469 }
1470 }
1471
1472 if !http_request.headers.contains_key(header::USER_AGENT) {
1475 http_request
1476 .headers
1477 .typed_insert::<UserAgent>(context.user_agent.parse().unwrap());
1478 }
1479
1480 match http_request.cache_mode {
1482 CacheMode::Default if is_no_store_cache(&http_request.headers) => {
1486 http_request.cache_mode = CacheMode::NoStore;
1487 },
1488
1489 CacheMode::NoCache if !http_request.headers.contains_key(header::CACHE_CONTROL) => {
1499 http_request
1500 .headers
1501 .typed_insert(CacheControl::new().with_max_age(Duration::from_secs(0)));
1502 },
1503
1504 CacheMode::Reload | CacheMode::NoStore => {
1506 if !http_request.headers.contains_key(header::PRAGMA) {
1509 http_request.headers.typed_insert(Pragma::no_cache());
1510 }
1511
1512 if !http_request.headers.contains_key(header::CACHE_CONTROL) {
1515 http_request
1516 .headers
1517 .typed_insert(CacheControl::new().with_no_cache());
1518 }
1519 },
1520
1521 _ => {},
1522 }
1523
1524 if http_request.headers.contains_key(header::RANGE) {
1527 if let Ok(value) = HeaderValue::from_str("identity") {
1528 http_request.headers.insert("Accept-Encoding", value);
1529 }
1530 }
1531
1532 http_request.headers.remove(header::HOST);
1536 set_default_accept_encoding(&mut http_request.headers);
1538
1539 let current_url = http_request.current_url();
1540
1541 if include_credentials {
1544 set_request_cookies(
1548 ¤t_url,
1549 &mut http_request.headers,
1550 &context.state.cookie_jar,
1551 );
1552 if !http_request.headers.contains_key(header::AUTHORIZATION) {
1554 let mut authorization_value = None;
1556
1557 if let Some(basic) = auth_from_cache(&context.state.auth_cache, ¤t_url.origin()) {
1559 if !http_request.use_url_credentials || !has_credentials(¤t_url) {
1560 authorization_value = Some(basic);
1561 }
1562 }
1563
1564 if authentication_fetch_flag &&
1566 authorization_value.is_none() &&
1567 has_credentials(¤t_url)
1568 {
1569 authorization_value = Some(Authorization::basic(
1570 current_url.username(),
1571 current_url.password().unwrap_or(""),
1572 ));
1573 }
1574
1575 if let Some(basic) = authorization_value {
1577 http_request.headers.typed_insert(basic);
1578 }
1579 }
1580 }
1581
1582 let should_wait = {
1584 let mut cache_guard = block_for_cache_ready(
1586 context,
1587 http_request,
1588 done_chan,
1589 &mut revalidating_flag,
1590 &mut response,
1591 )
1592 .await;
1593
1594 if response.is_none() {
1598 if http_request.cache_mode == CacheMode::OnlyIfCached {
1600 return Response::network_error(NetworkError::CacheError);
1602 }
1603
1604 drop(cache_guard);
1607 let forward_response =
1608 http_network_fetch(http_fetch_params, include_credentials, done_chan, context)
1609 .await;
1610
1611 let http_request = &mut http_fetch_params.request;
1612 let request_key = CacheKey::new(http_request);
1613 cache_guard = context
1614 .state
1615 .http_cache
1616 .get_or_guard(request_key.clone())
1617 .await;
1618 if forward_response.status.in_range(200..=399) && !http_request.method.is_safe() {
1622 if let Some(guard) = cache_guard.try_as_mut() {
1623 invalidate(http_request, &forward_response, guard).await;
1624 }
1625 context
1626 .state
1627 .http_cache
1628 .invalidate_related_urls(http_request, &forward_response, &request_key)
1629 .await;
1630 }
1631
1632 if revalidating_flag && forward_response.status == StatusCode::NOT_MODIFIED {
1634 *done_chan = None;
1637 if let Some(guard) = cache_guard.try_as_mut() {
1638 response = refresh(http_request, forward_response.clone(), done_chan, guard);
1639 }
1640
1641 if let Some(response) = &mut response {
1642 response.cache_state = CacheState::Validated;
1643 }
1644 }
1645
1646 if response.is_none() {
1648 let forward_response = response.insert(forward_response);
1650
1651 if http_request.cache_mode != CacheMode::NoStore {
1654 cache_guard.insert(http_request, forward_response);
1657 }
1658 }
1659 false
1660 } else {
1661 true
1662 }
1663 }; if should_wait {
1666 wait_for_inflight_requests(done_chan, &mut response).await;
1671 }
1672
1673 let http_request = &mut http_fetch_params.request;
1674 let mut response = response.unwrap();
1675
1676 if http_request.response_tainting != ResponseTainting::CorsTainting &&
1679 cross_origin_resource_policy_check(http_request, &response) ==
1680 CrossOriginResourcePolicy::Blocked
1681 {
1682 return Response::network_error(NetworkError::CorsGeneral);
1683 }
1684
1685 if http_request.headers.contains_key(RANGE) {
1689 response.range_requested = true;
1690 }
1691
1692 if response.status.try_code() == Some(StatusCode::UNAUTHORIZED) &&
1700 !cors_flag &&
1701 include_credentials &&
1702 response.headers.contains_key(WWW_AUTHENTICATE)
1703 {
1704 let request = &mut fetch_params.request;
1707
1708 if request.body.is_some() {
1710 }
1712
1713 if !request.use_url_credentials || authentication_fetch_flag {
1715 let Some(credentials) = context
1716 .state
1717 .request_authentication(request, &response)
1718 .await
1719 else {
1720 return response;
1721 };
1722
1723 if let Err(err) = request
1724 .current_url_mut()
1725 .set_username(&credentials.username)
1726 {
1727 error!("error setting username for url: {:?}", err);
1728 return response;
1729 };
1730
1731 if let Err(err) = request
1732 .current_url_mut()
1733 .set_password(Some(&credentials.password))
1734 {
1735 error!("error setting password for url: {:?}", err);
1736 return response;
1737 };
1738 }
1739
1740 *done_chan = None;
1743
1744 response = http_network_or_cache_fetch(
1746 fetch_params,
1747 true, cors_flag,
1749 done_chan,
1750 context,
1751 )
1752 .await;
1753 }
1754
1755 if response.status == StatusCode::PROXY_AUTHENTICATION_REQUIRED {
1757 let request = &mut fetch_params.request;
1758 if request.traversable_for_user_prompts == TraversableForUserPrompts::NoTraversable {
1761 return Response::network_error(NetworkError::ResourceLoadError(
1762 "Can't find Window object".into(),
1763 ));
1764 }
1765
1766 let Some(credentials) = context
1774 .state
1775 .request_authentication(request, &response)
1776 .await
1777 else {
1778 return response;
1779 };
1780
1781 let entry = AuthCacheEntry {
1783 user_name: credentials.username,
1784 password: credentials.password,
1785 };
1786 {
1787 let mut auth_cache = context.state.auth_cache.write();
1788 let key = request.current_url().origin().ascii_serialization();
1789 auth_cache.entries.insert(key, entry);
1790 }
1791
1792 *done_chan = None;
1795
1796 response = http_network_or_cache_fetch(
1798 fetch_params,
1799 false, cors_flag,
1801 done_chan,
1802 context,
1803 )
1804 .await;
1805 }
1806
1807 if authentication_fetch_flag {
1815 }
1817
1818 response
1820}
1821async fn block_for_cache_ready<'a>(
1830 context: &'a FetchContext,
1831 http_request: &mut Request,
1832 done_chan: &mut DoneChannel,
1833 revalidating_flag: &mut bool,
1834 response: &mut Option<Response>,
1835) -> CachedResourcesOrGuard<'a> {
1836 let entry_key = CacheKey::new(http_request);
1837 let guard_result = context.state.http_cache.get_or_guard(entry_key).await;
1838
1839 match guard_result {
1840 CachedResourcesOrGuard::Guard(_) => {
1841 *done_chan = None;
1842 },
1843 CachedResourcesOrGuard::Value(ref cached_resources) => {
1844 let stored_response = construct_response(http_request, done_chan, cached_resources);
1850 if let Some(response_from_cache) = stored_response {
1852 let response_headers = response_from_cache.response.headers.clone();
1853 let (cached_response, needs_revalidation) =
1855 match (http_request.cache_mode, &http_request.mode) {
1856 (CacheMode::ForceCache, _) => (Some(response_from_cache.response), false),
1857 (CacheMode::OnlyIfCached, &RequestMode::SameOrigin) => {
1858 (Some(response_from_cache.response), false)
1859 },
1860 (CacheMode::OnlyIfCached, _) |
1861 (CacheMode::NoStore, _) |
1862 (CacheMode::Reload, _) => (None, false),
1863 (_, _) => (
1864 Some(response_from_cache.response),
1865 response_from_cache.needs_validation,
1866 ),
1867 };
1868
1869 if needs_revalidation {
1870 *revalidating_flag = true;
1871 if let Some(http_date) = response_headers.typed_get::<LastModified>() {
1873 let http_date: SystemTime = http_date.into();
1874 http_request
1875 .headers
1876 .typed_insert(IfModifiedSince::from(http_date));
1877 }
1878 if let Some(entity_tag) = response_headers.get(header::ETAG) {
1879 http_request
1880 .headers
1881 .insert(header::IF_NONE_MATCH, entity_tag.clone());
1882 }
1883 } else {
1884 *response = cached_response;
1886 if let Some(response) = response {
1887 response.cache_state = CacheState::Local;
1888 }
1889 }
1890 if response.is_none() {
1891 *done_chan = None;
1894 }
1895 }
1896 },
1897 }
1898 guard_result
1899}
1900
1901async fn wait_for_inflight_requests(done_chan: &mut DoneChannel, response: &mut Option<Response>) {
1904 if let Some(ref mut ch) = *done_chan {
1905 assert!(response.is_some());
1909
1910 loop {
1911 match ch.1.recv().await {
1912 Some(Data::Payload(_)) => {},
1913 Some(Data::Done) => break, Some(Data::Cancelled) => {
1915 break;
1917 },
1918 _ => panic!("HTTP cache should always send Done or Cancelled"),
1919 }
1920 }
1921 }
1922 *done_chan = None;
1924}
1925
1926#[derive(PartialEq)]
1930enum CrossOriginResourcePolicy {
1931 Allowed,
1932 Blocked,
1933}
1934
1935fn cross_origin_resource_policy_check(
1938 request: &Request,
1939 response: &Response,
1940) -> CrossOriginResourcePolicy {
1941 if request.mode != RequestMode::NoCors {
1943 return CrossOriginResourcePolicy::Allowed;
1944 }
1945
1946 let current_url_origin = request.current_url().origin();
1948 let same_origin = if let Origin::Origin(ref origin) = request.origin {
1949 *origin == request.current_url().origin()
1950 } else {
1951 false
1952 };
1953
1954 if same_origin {
1955 return CrossOriginResourcePolicy::Allowed;
1956 }
1957
1958 let policy = response
1960 .headers
1961 .get(HeaderName::from_static("cross-origin-resource-policy"))
1962 .map(|h| h.to_str().unwrap_or(""))
1963 .unwrap_or("");
1964
1965 if policy == "same-origin" {
1967 return CrossOriginResourcePolicy::Blocked;
1968 }
1969
1970 if let Origin::Origin(ref request_origin) = request.origin {
1972 let schemeless_same_origin = is_schemelessy_same_site(request_origin, ¤t_url_origin);
1973 if schemeless_same_origin &&
1974 (request_origin.scheme() == Some("https") ||
1975 response.https_state == HttpsState::None)
1976 {
1977 return CrossOriginResourcePolicy::Allowed;
1978 }
1979 };
1980
1981 if policy == "same-site" {
1983 return CrossOriginResourcePolicy::Blocked;
1984 }
1985
1986 CrossOriginResourcePolicy::Allowed
1987}
1988
1989struct ResponseEndTimer(Option<Arc<Mutex<ResourceFetchTiming>>>);
1991
1992impl ResponseEndTimer {
1993 fn neuter(&mut self) {
1994 self.0 = None;
1995 }
1996}
1997
1998impl Drop for ResponseEndTimer {
1999 fn drop(&mut self) {
2000 let ResponseEndTimer(resource_fetch_timing_opt) = self;
2001
2002 resource_fetch_timing_opt.as_ref().map_or((), |t| {
2003 t.lock().set_attribute(ResourceAttribute::ResponseEnd);
2004 })
2005 }
2006}
2007
2008async fn http_network_fetch(
2010 fetch_params: &mut FetchParams,
2011 credentials_flag: bool,
2012 done_chan: &mut DoneChannel,
2013 context: &FetchContext,
2014) -> Response {
2015 let mut response_end_timer = ResponseEndTimer(Some(context.timing.clone()));
2016
2017 let request = &mut fetch_params.request;
2019
2020 let url = request.current_url();
2031 let request_id = request.id.0.to_string();
2032 if log_enabled!(log::Level::Info) {
2033 info!("{:?} request for {}", request.method, url);
2034 for header in request.headers.iter() {
2035 debug!(" - {:?}", header);
2036 }
2037 }
2038
2039 let is_xhr = request.destination == Destination::None;
2043
2044 let (fetch_terminated_sender, mut fetch_terminated_receiver) = unbounded_channel();
2046
2047 let body = request.body.as_ref().map(|body| body.take_stream());
2048
2049 if body.is_none() {
2050 let _ = fetch_terminated_sender.send(false);
2055 }
2056
2057 let browsing_context_id = request.target_webview_id.map(Into::into);
2058
2059 let (res, msg) = match &request.mode {
2060 RequestMode::WebSocket {
2061 protocols,
2062 original_url: _,
2063 } => {
2064 let (resource_event_sender, dom_action_receiver) = {
2067 let mut websocket_chan = context.websocket_chan.as_ref().unwrap().lock();
2068 (
2069 websocket_chan.sender.clone(),
2070 websocket_chan.receiver.take().unwrap(),
2071 )
2072 };
2073
2074 let mut tls_config = create_tls_config(
2075 context.ca_certificates.clone(),
2076 context.ignore_certificate_errors,
2077 context.state.override_manager.clone(),
2078 );
2079 tls_config.alpn_protocols = vec!["http/1.1".to_string().into()];
2080
2081 let response = match start_websocket(
2082 context.state.clone(),
2083 resource_event_sender,
2084 protocols,
2085 request,
2086 tls_config,
2087 dom_action_receiver,
2088 )
2089 .await
2090 {
2091 Ok(response) => response,
2092 Err(error) => {
2093 return Response::network_error(NetworkError::WebsocketConnectionFailure(
2094 format!("{error:?}"),
2095 ));
2096 },
2097 };
2098
2099 let response = response.map(|r| match r {
2100 Some(body) => Full::from(body).map_err(|_| unreachable!()).boxed(),
2101 None => http_body_util::Empty::new()
2102 .map_err(|_| unreachable!())
2103 .boxed(),
2104 });
2105 (Decoder::detect(response, url.is_secure_scheme()), None)
2106 },
2107 _ => {
2108 let response_future = obtain_response(
2109 &context.state.client,
2110 &url,
2111 &request.method,
2112 &mut request.headers,
2113 body,
2114 request
2115 .body
2116 .as_ref()
2117 .is_some_and(|body| body.source_is_null()),
2118 &request.pipeline_id,
2119 Some(&request_id),
2120 request.destination,
2121 is_xhr,
2122 context,
2123 fetch_terminated_sender,
2124 browsing_context_id,
2125 );
2126
2127 let (res, msg) = match response_future.await {
2129 Ok(wrapped_response) => wrapped_response,
2130 Err(error) => return Response::network_error(error),
2131 };
2132 (res, msg)
2133 },
2134 };
2135
2136 if log_enabled!(log::Level::Info) {
2137 debug!("{:?} response for {}", res.version(), url);
2138 for header in res.headers().iter() {
2139 debug!(" - {:?}", header);
2140 }
2141 }
2142
2143 match fetch_terminated_receiver.recv().await {
2146 Some(true) => {
2147 return Response::network_error(NetworkError::ConnectionFailure);
2148 },
2149 Some(false) => {},
2150 _ => warn!("Failed to receive confirmation request was streamed without error."),
2151 }
2152
2153 let header_strings: Vec<&str> = res
2154 .headers()
2155 .get_all("Timing-Allow-Origin")
2156 .iter()
2157 .map(|header_value| header_value.to_str().unwrap_or(""))
2158 .collect();
2159 let wildcard_present = header_strings.contains(&"*");
2160 let req_origin_in_timing_allow = header_strings
2164 .iter()
2165 .any(|header_str| match request.origin {
2166 SpecificOrigin(ref immutable_request_origin) => {
2167 *header_str == immutable_request_origin.ascii_serialization()
2168 },
2169 _ => false,
2170 });
2171
2172 let is_same_origin = request.url_list.iter().all(|url| match request.origin {
2173 SpecificOrigin(ref immutable_request_origin) => url.origin() == *immutable_request_origin,
2174 _ => false,
2175 });
2176
2177 if !(is_same_origin || req_origin_in_timing_allow || wildcard_present) {
2178 context.timing.lock().mark_timing_check_failed();
2179 }
2180
2181 let timing = context.timing.lock().clone();
2182 let mut response = Response::new(url.clone(), timing);
2183
2184 if let Some(handshake_info) = res.extensions().get::<TlsHandshakeInfo>() {
2185 let mut hsts_enabled = url
2186 .host_str()
2187 .is_some_and(|host| context.state.hsts_list.read().is_host_secure(host));
2188
2189 if url.scheme() == "https" {
2190 if let Some(sts) = res.headers().typed_get::<StrictTransportSecurity>() {
2191 hsts_enabled = sts.max_age().as_secs() > 0;
2193 }
2194 }
2195 response.tls_security_info = Some(build_tls_security_info(handshake_info, hsts_enabled));
2196 }
2197
2198 let status_text = res
2199 .extensions()
2200 .get::<ReasonPhrase>()
2201 .map(ReasonPhrase::as_bytes)
2202 .or_else(|| res.status().canonical_reason().map(str::as_bytes))
2203 .map(Vec::from)
2204 .unwrap_or_default();
2205 response.status = HttpStatus::new(res.status(), status_text);
2206
2207 info!("got {:?} response for {:?}", res.status(), request.url());
2208 response.headers = res.headers().clone();
2209 response.referrer = request.referrer.to_url().cloned();
2210 response.referrer_policy = request.referrer_policy;
2211
2212 let res_body = response.body.clone();
2213
2214 let (done_sender, done_receiver) = unbounded_channel();
2216 *done_chan = Some((done_sender.clone(), done_receiver));
2217
2218 let devtools_sender = context.devtools_chan.clone();
2219 let cancellation_listener = context.cancellation_listener.clone();
2220 if cancellation_listener.cancelled() {
2221 return Response::network_error(NetworkError::LoadCancelled);
2222 }
2223
2224 *res_body.lock() = ResponseBody::Receiving(vec![]);
2225 let res_body2 = res_body.clone();
2226
2227 if let Some(ref sender) = devtools_sender {
2228 if let Some(m) = msg {
2229 send_request_to_devtools(m, sender);
2230 }
2231 }
2232
2233 let done_sender2 = done_sender.clone();
2234 let done_sender3 = done_sender.clone();
2235 let timing_ptr2 = context.timing.clone();
2236 let timing_ptr3 = context.timing.clone();
2237 let devtools_request = request.clone();
2238 let url1 = devtools_request.url();
2239 let url2 = url1.clone();
2240
2241 let status = response.status.clone();
2242 let headers = response.headers.clone();
2243 let devtools_chan = context.devtools_chan.clone();
2244
2245 spawn_task(
2246 res.into_body()
2247 .try_fold(res_body, move |res_body, chunk| {
2248 if cancellation_listener.cancelled() {
2249 *res_body.lock() = ResponseBody::Done(vec![]);
2250 let _ = done_sender.send(Data::Cancelled);
2251 return future::ready(Err(std::io::Error::new(
2252 std::io::ErrorKind::Interrupted,
2253 "Fetch aborted",
2254 )));
2255 }
2256 if let ResponseBody::Receiving(ref mut body) = *res_body.lock() {
2257 let bytes = chunk;
2258 body.extend_from_slice(&bytes);
2259 let _ = done_sender.send(Data::Payload(bytes.to_vec()));
2260 }
2261 future::ready(Ok(res_body))
2262 })
2263 .and_then(move |res_body| {
2264 debug!("successfully finished response for {:?}", url1);
2265 let mut body = res_body.lock();
2266 let completed_body = match *body {
2267 ResponseBody::Receiving(ref mut body) => std::mem::take(body),
2268 _ => vec![],
2269 };
2270 let devtools_response_body = completed_body.clone();
2271 *body = ResponseBody::Done(completed_body);
2272 send_response_values_to_devtools(
2273 Some(headers),
2274 status,
2275 Some(devtools_response_body),
2276 CacheState::None,
2277 &devtools_request,
2278 devtools_chan,
2279 );
2280 timing_ptr2
2281 .lock()
2282 .set_attribute(ResourceAttribute::ResponseEnd);
2283 let _ = done_sender2.send(Data::Done);
2284 future::ready(Ok(()))
2285 })
2286 .map_err(move |error| {
2287 if let std::io::ErrorKind::InvalidData = error.kind() {
2288 debug!("Content decompression error for {:?}", url2);
2289 let _ = done_sender3.send(Data::Error(NetworkError::DecompressionError));
2290 let mut body = res_body2.lock();
2291 response.termination_reason = Some(TerminationReason::Fatal);
2292
2293 *body = ResponseBody::Done(vec![]);
2294 }
2295 debug!("finished response for {:?}", url2);
2296 let mut body = res_body2.lock();
2297 let completed_body = match *body {
2298 ResponseBody::Receiving(ref mut body) => std::mem::take(body),
2299 _ => vec![],
2300 };
2301 *body = ResponseBody::Done(completed_body);
2302 timing_ptr3
2303 .lock()
2304 .set_attribute(ResourceAttribute::ResponseEnd);
2305 let _ = done_sender3.send(Data::Done);
2306 }),
2307 );
2308
2309 response.https_state = match url.scheme() {
2315 "https" => HttpsState::Modern,
2316 _ => HttpsState::None,
2317 };
2318
2319 if credentials_flag {
2332 set_cookies_from_headers(&url, &response.headers, &context.state.cookie_jar);
2333 }
2334 context
2335 .state
2336 .hsts_list
2337 .write()
2338 .update_hsts_list_from_response(&url, &response.headers);
2339
2340 response_end_timer.neuter();
2354
2355 response
2356}
2357
2358async fn cors_preflight_fetch(
2360 request: &Request,
2361 cache: &mut CorsCache,
2362 context: &FetchContext,
2363) -> Response {
2364 let mut preflight = RequestBuilder::new(
2369 request.target_webview_id,
2370 request.current_url(),
2371 request.referrer.clone(),
2372 )
2373 .method(Method::OPTIONS)
2374 .origin(match &request.origin {
2375 Origin::Client => {
2376 unreachable!("We shouldn't get Client origin in cors_preflight_fetch.")
2377 },
2378 Origin::Origin(origin) => origin.clone(),
2379 })
2380 .pipeline_id(request.pipeline_id)
2381 .initiator(request.initiator)
2382 .destination(request.destination)
2383 .referrer_policy(request.referrer_policy)
2384 .mode(RequestMode::CorsMode)
2385 .response_tainting(ResponseTainting::CorsTainting)
2386 .policy_container(match &request.policy_container {
2387 RequestPolicyContainer::Client => {
2388 unreachable!("We should have a policy container for request in cors_preflight_fetch")
2389 },
2390 RequestPolicyContainer::PolicyContainer(policy_container) => policy_container.clone(),
2391 })
2392 .build();
2393
2394 preflight
2396 .headers
2397 .insert(ACCEPT, HeaderValue::from_static("*/*"));
2398
2399 preflight
2401 .headers
2402 .typed_insert::<AccessControlRequestMethod>(AccessControlRequestMethod::from(
2403 request.method.clone(),
2404 ));
2405
2406 let headers = get_cors_unsafe_header_names(&request.headers);
2408
2409 if !headers.is_empty() {
2411 preflight.headers.insert(
2414 ACCESS_CONTROL_REQUEST_HEADERS,
2415 HeaderValue::from_bytes(itertools::join(headers.iter(), ",").as_bytes())
2416 .unwrap_or(HeaderValue::from_static("")),
2417 );
2418 }
2419
2420 let mut fetch_params = FetchParams::new(preflight);
2423 let response =
2424 http_network_or_cache_fetch(&mut fetch_params, false, false, &mut None, context).await;
2425
2426 if cors_check(request, &response).is_ok() && response.status.code().is_success() {
2428 let mut methods = if response
2431 .headers
2432 .contains_key(header::ACCESS_CONTROL_ALLOW_METHODS)
2433 {
2434 match response.headers.typed_get::<AccessControlAllowMethods>() {
2435 Some(methods) => methods.iter().collect(),
2436 None => {
2438 return Response::network_error(NetworkError::CorsAllowMethods);
2439 },
2440 }
2441 } else {
2442 vec![]
2443 };
2444
2445 let header_names = if response
2448 .headers
2449 .contains_key(header::ACCESS_CONTROL_ALLOW_HEADERS)
2450 {
2451 match response.headers.typed_get::<AccessControlAllowHeaders>() {
2452 Some(names) => names.iter().collect(),
2453 None => {
2455 return Response::network_error(NetworkError::CorsAllowHeaders);
2456 },
2457 }
2458 } else {
2459 vec![]
2460 };
2461
2462 debug!(
2463 "CORS check: Allowed methods: {:?}, current method: {:?}",
2464 methods, request.method
2465 );
2466
2467 if methods.is_empty() && request.use_cors_preflight {
2470 methods = vec![request.method.clone()];
2471 }
2472
2473 if methods
2476 .iter()
2477 .all(|method| *method.as_str() != *request.method.as_ref()) &&
2478 !is_cors_safelisted_method(&request.method) &&
2479 (request.credentials_mode == CredentialsMode::Include ||
2480 methods.iter().all(|method| method.as_ref() != "*"))
2481 {
2482 return Response::network_error(NetworkError::CorsMethod);
2483 }
2484
2485 debug!(
2486 "CORS check: Allowed headers: {:?}, current headers: {:?}",
2487 header_names, request.headers
2488 );
2489
2490 if request.headers.iter().any(|(name, _)| {
2493 is_cors_non_wildcard_request_header_name(name) &&
2494 header_names.iter().all(|header_name| header_name != name)
2495 }) {
2496 return Response::network_error(NetworkError::CorsAuthorization);
2497 }
2498
2499 let unsafe_names = get_cors_unsafe_header_names(&request.headers);
2503 let header_names_set: HashSet<&HeaderName> = HashSet::from_iter(header_names.iter());
2504 let header_names_contains_star = header_names
2505 .iter()
2506 .any(|header_name| header_name.as_str() == "*");
2507 for unsafe_name in unsafe_names.iter() {
2508 if !header_names_set.contains(unsafe_name) &&
2509 (request.credentials_mode == CredentialsMode::Include ||
2510 !header_names_contains_star)
2511 {
2512 return Response::network_error(NetworkError::CorsHeaders);
2513 }
2514 }
2515
2516 let max_age: Option<Duration> = response
2519 .headers
2520 .typed_get::<AccessControlMaxAge>()
2521 .map(|acma| acma.into());
2522
2523 let max_age = max_age.unwrap_or(Duration::from_secs(5));
2525
2526 for method in &methods {
2537 cache.match_method_and_update(request, method.clone(), max_age);
2538 }
2539
2540 for header_name in &header_names {
2545 cache.match_header_and_update(request, header_name, max_age);
2546 }
2547
2548 return response;
2550 }
2551
2552 Response::network_error(NetworkError::CorsGeneral)
2554}
2555
2556fn cors_check(request: &Request, response: &Response) -> Result<(), ()> {
2558 let Some(origins) =
2560 get_value_from_header_list(ACCESS_CONTROL_ALLOW_ORIGIN.as_str(), &response.headers)
2561 else {
2562 return Err(());
2564 };
2565 let origin = origins.into_iter().map(char::from).collect::<String>();
2566
2567 if request.credentials_mode != CredentialsMode::Include && origin == "*" {
2569 return Ok(());
2570 }
2571
2572 if serialize_request_origin(request).to_string() != origin {
2574 return Err(());
2575 }
2576
2577 if request.credentials_mode != CredentialsMode::Include {
2579 return Ok(());
2580 }
2581
2582 let credentials = response
2584 .headers
2585 .typed_get::<AccessControlAllowCredentials>();
2586
2587 if credentials.is_some() {
2589 return Ok(());
2590 }
2591
2592 Err(())
2594}
2595
2596fn has_credentials(url: &ServoUrl) -> bool {
2597 !url.username().is_empty() || url.password().is_some()
2598}
2599
2600fn is_no_store_cache(headers: &HeaderMap) -> bool {
2601 headers.contains_key(header::IF_MODIFIED_SINCE) |
2602 headers.contains_key(header::IF_NONE_MATCH) |
2603 headers.contains_key(header::IF_UNMODIFIED_SINCE) |
2604 headers.contains_key(header::IF_MATCH) |
2605 headers.contains_key(header::IF_RANGE)
2606}
2607
2608fn is_redirect_status(status: StatusCode) -> bool {
2610 matches!(
2611 status,
2612 StatusCode::MOVED_PERMANENTLY |
2613 StatusCode::FOUND |
2614 StatusCode::SEE_OTHER |
2615 StatusCode::TEMPORARY_REDIRECT |
2616 StatusCode::PERMANENT_REDIRECT
2617 )
2618}
2619
2620fn serialize_request_origin(request: &Request) -> headers::Origin {
2622 let Origin::Origin(origin) = &request.origin else {
2624 panic!("origin cannot be \"client\" at this point in time");
2625 };
2626
2627 if request.redirect_taint_for_request() != RedirectTaint::SameOrigin {
2629 return headers::Origin::NULL;
2630 }
2631
2632 serialize_origin(origin)
2634}
2635
2636pub fn serialize_origin(origin: &ImmutableOrigin) -> headers::Origin {
2638 match origin {
2639 ImmutableOrigin::Opaque(_) => headers::Origin::NULL,
2640 ImmutableOrigin::Tuple(scheme, host, port) => {
2641 let port = match (scheme.as_ref(), port) {
2644 ("http" | "ws", 80) | ("https" | "wss", 443) | ("ftp", 21) => None,
2645 _ => Some(*port),
2646 };
2647
2648 headers::Origin::try_from_parts(scheme, &host.to_string(), port)
2650 .unwrap_or(headers::Origin::NULL)
2651 },
2652 }
2653}
2654
2655fn append_a_request_origin_header(request: &mut Request) {
2657 let Origin::Origin(request_origin) = &request.origin else {
2659 panic!("origin cannot be \"client\" at this point in time");
2660 };
2661
2662 let mut serialized_origin = serialize_request_origin(request);
2664
2665 if request.response_tainting == ResponseTainting::CorsTainting ||
2668 matches!(request.mode, RequestMode::WebSocket { .. })
2669 {
2670 request.headers.typed_insert(serialized_origin);
2671 }
2672 else if !matches!(request.method, Method::GET | Method::HEAD) {
2674 if request.mode != RequestMode::CorsMode {
2676 match request.referrer_policy {
2677 ReferrerPolicy::NoReferrer => {
2678 serialized_origin = headers::Origin::NULL;
2680 },
2681 ReferrerPolicy::NoReferrerWhenDowngrade |
2682 ReferrerPolicy::StrictOrigin |
2683 ReferrerPolicy::StrictOriginWhenCrossOrigin => {
2684 if let ImmutableOrigin::Tuple(scheme, _, _) = &request_origin {
2687 if scheme == "https" && request.current_url().scheme() != "https" {
2688 serialized_origin = headers::Origin::NULL;
2689 }
2690 }
2691 },
2692 ReferrerPolicy::SameOrigin => {
2693 if *request_origin != request.current_url().origin() {
2696 serialized_origin = headers::Origin::NULL;
2697 }
2698 },
2699 _ => {
2700 },
2702 };
2703 }
2704
2705 request.headers.typed_insert(serialized_origin);
2707 }
2708}
2709
2710fn append_the_fetch_metadata_headers(r: &mut Request) {
2712 if !r.url().is_potentially_trustworthy() {
2714 return;
2715 }
2716
2717 set_the_sec_fetch_dest_header(r);
2719
2720 set_the_sec_fetch_mode_header(r);
2722
2723 set_the_sec_fetch_site_header(r);
2725
2726 set_the_sec_fetch_user_header(r);
2728}
2729
2730fn set_the_sec_fetch_dest_header(r: &mut Request) {
2732 debug_assert!(r.url().is_potentially_trustworthy());
2734
2735 let header = r.destination;
2739
2740 r.headers.typed_insert(SecFetchDest(header));
2742}
2743
2744fn set_the_sec_fetch_mode_header(r: &mut Request) {
2746 debug_assert!(r.url().is_potentially_trustworthy());
2748
2749 let header = &r.mode;
2752
2753 r.headers.typed_insert(SecFetchMode::from(header));
2755}
2756
2757fn set_the_sec_fetch_site_header(r: &mut Request) {
2759 let Origin::Origin(request_origin) = &r.origin else {
2762 panic!("request origin cannot be \"client\" at this point")
2763 };
2764
2765 debug_assert!(r.url().is_potentially_trustworthy());
2767
2768 let mut header = SecFetchSite::SameOrigin;
2771
2772 if header != SecFetchSite::None {
2777 for url in &r.url_list {
2778 if url.origin() == *request_origin {
2780 continue;
2781 }
2782
2783 header = SecFetchSite::CrossSite;
2785
2786 if !is_same_site(request_origin, &url.origin()) {
2788 break;
2789 }
2790
2791 header = SecFetchSite::SameSite;
2793 }
2794 }
2795
2796 r.headers.typed_insert(header);
2798}
2799
2800fn set_the_sec_fetch_user_header(r: &mut Request) {
2802 debug_assert!(r.url().is_potentially_trustworthy());
2804
2805 if !r.is_navigation_request() {
2808 return;
2809 }
2810
2811 let header = SecFetchUser;
2814
2815 r.headers.typed_insert(header);
2817}
2818
2819fn set_requests_referrer_policy_on_redirect(request: &mut Request, response: &Response) {
2821 let referrer_policy: ReferrerPolicy = response
2824 .headers
2825 .typed_get::<headers::ReferrerPolicy>()
2826 .into();
2827
2828 if referrer_policy != ReferrerPolicy::EmptyString {
2830 request.referrer_policy = referrer_policy;
2831 }
2832}