1use std::collections::{HashMap, 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;
13use base::id::{BrowsingContextId, HistoryStateId, PipelineId};
14use crossbeam_channel::Sender;
15use devtools_traits::{
16 ChromeToDevtoolsControlMsg, DevtoolsControlMsg, HttpRequest as DevtoolsHttpRequest,
17 HttpResponse as DevtoolsHttpResponse, NetworkEvent,
18};
19use embedder_traits::{AuthenticationResponse, EmbedderMsg, EmbedderProxy};
20use futures::{TryFutureExt, TryStreamExt, future};
21use headers::authorization::Basic;
22use headers::{
23 AccessControlAllowCredentials, AccessControlAllowHeaders, AccessControlAllowMethods,
24 AccessControlAllowOrigin, AccessControlMaxAge, AccessControlRequestMethod, Authorization,
25 CacheControl, ContentLength, HeaderMapExt, IfModifiedSince, LastModified, Pragma, Referer,
26 UserAgent,
27};
28use http::header::{
29 self, ACCEPT, ACCESS_CONTROL_REQUEST_HEADERS, AUTHORIZATION, CONTENT_ENCODING,
30 CONTENT_LANGUAGE, CONTENT_LOCATION, CONTENT_TYPE, HeaderValue, RANGE, WWW_AUTHENTICATE,
31};
32use http::{HeaderMap, Method, Request as HyperRequest, StatusCode};
33use http_body_util::combinators::BoxBody;
34use http_body_util::{BodyExt, Full};
35use hyper::Response as HyperResponse;
36use hyper::body::{Bytes, Frame};
37use hyper::ext::ReasonPhrase;
38use hyper::header::{HeaderName, TRANSFER_ENCODING};
39use hyper_serde::Serde;
40use ipc_channel::ipc::{self, IpcSender, IpcSharedMemory};
41use ipc_channel::router::ROUTER;
42use log::{debug, error, info, log_enabled, warn};
43use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
44use net_traits::http_status::HttpStatus;
45use net_traits::policy_container::RequestPolicyContainer;
46use net_traits::pub_domains::reg_suffix;
47use net_traits::request::Origin::Origin as SpecificOrigin;
48use net_traits::request::{
49 BodyChunkRequest, BodyChunkResponse, CacheMode, CredentialsMode, Destination, Initiator,
50 Origin, RedirectMode, Referrer, Request, RequestBuilder, RequestMode, ResponseTainting,
51 ServiceWorkersMode, TraversableForUserPrompts, get_cors_unsafe_header_names,
52 is_cors_non_wildcard_request_header_name, is_cors_safelisted_method,
53 is_cors_safelisted_request_header,
54};
55use net_traits::response::{CacheState, HttpsState, Response, ResponseBody, ResponseType};
56use net_traits::{
57 CookieSource, DOCUMENT_ACCEPT_HEADER_VALUE, DebugVec, FetchMetadata, NetworkError,
58 RedirectEndValue, RedirectStartValue, ReferrerPolicy, ResourceAttribute, ResourceFetchTiming,
59 ResourceTimeValue,
60};
61use parking_lot::{Condvar, Mutex, RwLock};
62use profile_traits::mem::{Report, ReportKind};
63use profile_traits::path;
64use rustc_hash::FxHashMap;
65use servo_arc::Arc;
66use servo_url::{Host, ImmutableOrigin, ServoUrl};
67use tokio::sync::mpsc::{
68 Receiver as TokioReceiver, Sender as TokioSender, UnboundedReceiver, UnboundedSender, channel,
69 unbounded_channel,
70};
71use tokio_stream::wrappers::ReceiverStream;
72
73use crate::async_runtime::spawn_task;
74use crate::connector::{CertificateErrorOverrideManager, ServoClient, create_tls_config};
75use crate::cookie::ServoCookie;
76use crate::cookie_storage::CookieStorage;
77use crate::decoder::Decoder;
78use crate::fetch::cors_cache::CorsCache;
79use crate::fetch::fetch_params::FetchParams;
80use crate::fetch::headers::{SecFetchDest, SecFetchMode, SecFetchSite, SecFetchUser};
81use crate::fetch::methods::{Data, DoneChannel, FetchContext, Target, main_fetch};
82use crate::hsts::HstsList;
83use crate::http_cache::{CacheKey, HttpCache};
84use crate::resource_thread::{AuthCache, AuthCacheEntry};
85use crate::websocket_loader::start_websocket;
86
87#[derive(Clone, Debug, Eq, PartialEq)]
89pub enum HttpCacheEntryState {
90 ReadyToConstruct,
94 PendingStore(usize),
96}
97
98type HttpCacheState = Mutex<HashMap<CacheKey, Arc<(Mutex<HttpCacheEntryState>, Condvar)>>>;
99
100pub struct HttpState {
101 pub hsts_list: RwLock<HstsList>,
102 pub cookie_jar: RwLock<CookieStorage>,
103 pub http_cache: RwLock<HttpCache>,
104 pub http_cache_state: HttpCacheState,
108 pub auth_cache: RwLock<AuthCache>,
109 pub history_states: RwLock<FxHashMap<HistoryStateId, Vec<u8>>>,
110 pub client: ServoClient,
111 pub override_manager: CertificateErrorOverrideManager,
112 pub embedder_proxy: Mutex<EmbedderProxy>,
113}
114
115impl HttpState {
116 pub(crate) fn memory_reports(&self, suffix: &str, ops: &mut MallocSizeOfOps) -> Vec<Report> {
117 vec![
118 Report {
119 path: path!["memory-cache", suffix],
120 kind: ReportKind::ExplicitJemallocHeapSize,
121 size: self.http_cache.read().size_of(ops),
122 },
123 Report {
124 path: path!["hsts-list", suffix],
125 kind: ReportKind::ExplicitJemallocHeapSize,
126 size: self.hsts_list.read().size_of(ops),
127 },
128 ]
129 }
130
131 fn request_authentication(
132 &self,
133 request: &Request,
134 response: &Response,
135 ) -> Option<AuthenticationResponse> {
136 let webview_id = request.target_webview_id?;
138 let for_proxy = response.status == StatusCode::PROXY_AUTHENTICATION_REQUIRED;
139
140 if request.mode != RequestMode::Navigate {
142 return None;
143 }
144
145 let embedder_proxy = self.embedder_proxy.lock();
146 let (ipc_sender, ipc_receiver) = generic_channel::channel().unwrap();
147 embedder_proxy.send(EmbedderMsg::RequestAuthentication(
148 webview_id,
149 request.url(),
150 for_proxy,
151 ipc_sender,
152 ));
153 ipc_receiver.recv().ok()?
154 }
155}
156
157pub(crate) fn set_default_accept(request: &mut Request) {
159 if request.headers.contains_key(header::ACCEPT) {
161 return;
162 }
163
164 let value = if request.initiator == Initiator::Prefetch {
166 DOCUMENT_ACCEPT_HEADER_VALUE
167 } else {
168 match request.destination {
171 Destination::Document | Destination::Frame | Destination::IFrame => {
172 DOCUMENT_ACCEPT_HEADER_VALUE
173 },
174 Destination::Image => {
175 HeaderValue::from_static("image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5")
176 },
177 Destination::Json => HeaderValue::from_static("application/json,*/*;q=0.5"),
178 Destination::Style => HeaderValue::from_static("text/css,*/*;q=0.1"),
179 _ => HeaderValue::from_static("*/*"),
181 }
182 };
183
184 request.headers.insert(header::ACCEPT, value);
186}
187
188fn set_default_accept_encoding(headers: &mut HeaderMap) {
189 if headers.contains_key(header::ACCEPT_ENCODING) {
190 return;
191 }
192
193 headers.insert(
195 header::ACCEPT_ENCODING,
196 HeaderValue::from_static("gzip, deflate, br, zstd"),
197 );
198}
199
200fn no_referrer_when_downgrade(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
202 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
204 return None;
205 }
206 strip_url_for_use_as_referrer(referrer_url, false)
208}
209
210fn strict_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
212 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
214 return None;
215 }
216 strip_url_for_use_as_referrer(referrer_url, true)
218}
219
220fn strict_origin_when_cross_origin(
222 referrer_url: ServoUrl,
223 current_url: ServoUrl,
224) -> Option<ServoUrl> {
225 if referrer_url.origin() == current_url.origin() {
227 return strip_url_for_use_as_referrer(referrer_url, false);
228 }
229 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
231 return None;
232 }
233 strip_url_for_use_as_referrer(referrer_url, true)
235}
236
237fn is_same_site(site_a: &ImmutableOrigin, site_b: &ImmutableOrigin) -> bool {
239 if !site_a.is_tuple() && !site_b.is_tuple() && site_a == site_b {
244 return true;
245 }
246
247 let ImmutableOrigin::Tuple(scheme_a, host_a, _) = site_a else {
249 return false;
250 };
251 let ImmutableOrigin::Tuple(scheme_b, host_b, _) = site_b else {
252 return false;
253 };
254
255 if scheme_a != scheme_b {
257 return false;
258 }
259
260 if let (Host::Domain(domain_a), Host::Domain(domain_b)) = (host_a, host_b) {
263 if reg_suffix(domain_a) != reg_suffix(domain_b) {
264 return false;
265 }
266 } else if host_a != host_b {
267 return false;
268 }
269
270 true
272}
273
274fn is_schemelessy_same_site(site_a: &ImmutableOrigin, site_b: &ImmutableOrigin) -> bool {
276 if !site_a.is_tuple() && !site_b.is_tuple() && site_a == site_b {
278 true
279 } else if site_a.is_tuple() && site_b.is_tuple() {
280 let host_a = site_a.host().map(|h| h.to_string()).unwrap_or_default();
282 let host_b = site_b.host().map(|h| h.to_string()).unwrap_or_default();
283
284 let host_a_reg = reg_suffix(&host_a);
285 let host_b_reg = reg_suffix(&host_b);
286
287 (site_a.host() == site_b.host() && host_a_reg.is_empty()) ||
289 (host_a_reg == host_b_reg && !host_a_reg.is_empty())
290 } else {
291 false
293 }
294}
295
296fn strip_url_for_use_as_referrer(mut url: ServoUrl, origin_only: bool) -> Option<ServoUrl> {
298 const MAX_REFERRER_URL_LENGTH: usize = 4096;
299 if url.is_local_scheme() {
301 return None;
302 }
303 {
305 let url = url.as_mut_url();
306 let _ = url.set_username("");
307 let _ = url.set_password(None);
308 url.set_fragment(None);
309 if origin_only || url.as_str().len() > MAX_REFERRER_URL_LENGTH {
313 url.set_path("");
314 url.set_query(None);
315 }
316 }
317 Some(url)
319}
320
321fn same_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
323 if referrer_url.origin() == current_url.origin() {
325 return strip_url_for_use_as_referrer(referrer_url, false);
326 }
327 None
329}
330
331fn origin_when_cross_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
333 if referrer_url.origin() == current_url.origin() {
335 return strip_url_for_use_as_referrer(referrer_url, false);
336 }
337 strip_url_for_use_as_referrer(referrer_url, true)
339}
340
341pub fn determine_requests_referrer(
343 referrer_policy: ReferrerPolicy,
344 referrer_source: ServoUrl,
345 current_url: ServoUrl,
346) -> Option<ServoUrl> {
347 match referrer_policy {
348 ReferrerPolicy::EmptyString | ReferrerPolicy::NoReferrer => None,
349 ReferrerPolicy::Origin => strip_url_for_use_as_referrer(referrer_source, true),
350 ReferrerPolicy::UnsafeUrl => strip_url_for_use_as_referrer(referrer_source, false),
351 ReferrerPolicy::StrictOrigin => strict_origin(referrer_source, current_url),
352 ReferrerPolicy::StrictOriginWhenCrossOrigin => {
353 strict_origin_when_cross_origin(referrer_source, current_url)
354 },
355 ReferrerPolicy::SameOrigin => same_origin(referrer_source, current_url),
356 ReferrerPolicy::OriginWhenCrossOrigin => {
357 origin_when_cross_origin(referrer_source, current_url)
358 },
359 ReferrerPolicy::NoReferrerWhenDowngrade => {
360 no_referrer_when_downgrade(referrer_source, current_url)
361 },
362 }
363}
364
365fn set_request_cookies(
366 url: &ServoUrl,
367 headers: &mut HeaderMap,
368 cookie_jar: &RwLock<CookieStorage>,
369) {
370 let mut cookie_jar = cookie_jar.write();
371 cookie_jar.remove_expired_cookies_for_url(url);
372 if let Some(cookie_list) = cookie_jar.cookies_for_url(url, CookieSource::HTTP) {
373 headers.insert(
374 header::COOKIE,
375 HeaderValue::from_bytes(cookie_list.as_bytes()).unwrap(),
376 );
377 }
378}
379
380fn set_cookie_for_url(cookie_jar: &RwLock<CookieStorage>, request: &ServoUrl, cookie_val: &str) {
381 let mut cookie_jar = cookie_jar.write();
382 let source = CookieSource::HTTP;
383
384 if let Some(cookie) = ServoCookie::from_cookie_string(cookie_val, request, source) {
385 cookie_jar.push(cookie, request, source);
386 }
387}
388
389fn set_cookies_from_headers(
390 url: &ServoUrl,
391 headers: &HeaderMap,
392 cookie_jar: &RwLock<CookieStorage>,
393) {
394 for cookie in headers.get_all(header::SET_COOKIE) {
395 let cookie_bytes = cookie.as_bytes();
396 if !ServoCookie::is_valid_name_or_value(cookie_bytes) {
397 continue;
398 }
399 if let Ok(cookie_str) = std::str::from_utf8(cookie_bytes) {
400 set_cookie_for_url(cookie_jar, url, cookie_str);
401 }
402 }
403}
404
405#[allow(clippy::too_many_arguments)]
406fn prepare_devtools_request(
407 request_id: String,
408 url: ServoUrl,
409 method: Method,
410 headers: HeaderMap,
411 body: Option<Vec<u8>>,
412 pipeline_id: PipelineId,
413 connect_time: Duration,
414 send_time: Duration,
415 destination: Destination,
416 is_xhr: bool,
417 browsing_context_id: BrowsingContextId,
418) -> ChromeToDevtoolsControlMsg {
419 let started_date_time = SystemTime::now();
420 let request = DevtoolsHttpRequest {
421 url,
422 method,
423 headers,
424 body: body.map(DebugVec::from),
425 pipeline_id,
426 started_date_time,
427 time_stamp: started_date_time
428 .duration_since(UNIX_EPOCH)
429 .unwrap_or_default()
430 .as_secs() as i64,
431 connect_time,
432 send_time,
433 destination,
434 is_xhr,
435 browsing_context_id,
436 };
437 let net_event = NetworkEvent::HttpRequestUpdate(request);
438
439 ChromeToDevtoolsControlMsg::NetworkEvent(request_id, net_event)
440}
441
442pub fn send_request_to_devtools(
443 msg: ChromeToDevtoolsControlMsg,
444 devtools_chan: &Sender<DevtoolsControlMsg>,
445) {
446 if matches!(msg, ChromeToDevtoolsControlMsg::NetworkEvent(_, ref network_event) if !network_event.forward_to_devtools())
447 {
448 return;
449 }
450 if let Err(e) = devtools_chan.send(DevtoolsControlMsg::FromChrome(msg)) {
451 error!("DevTools send failed: {e}");
452 }
453}
454
455pub fn send_response_to_devtools(
456 request: &Request,
457 context: &FetchContext,
458 response: &Response,
459 body_data: Option<Vec<u8>>,
460) {
461 let meta = match response.metadata() {
462 Ok(FetchMetadata::Unfiltered(m)) => m,
463 Ok(FetchMetadata::Filtered { unsafe_, .. }) => unsafe_,
464 Err(_) => {
465 log::warn!("No metadata available, skipping devtools response.");
466 return;
467 },
468 };
469 send_response_values_to_devtools(
470 meta.headers.map(Serde::into_inner),
471 meta.status,
472 body_data,
473 response.cache_state,
474 request,
475 context.devtools_chan.clone(),
476 );
477}
478
479#[allow(clippy::too_many_arguments)]
480pub fn send_response_values_to_devtools(
481 headers: Option<HeaderMap>,
482 status: HttpStatus,
483 body: Option<Vec<u8>>,
484 cache_state: CacheState,
485 request: &Request,
486 devtools_chan: Option<StdArc<Mutex<Sender<DevtoolsControlMsg>>>>,
487) {
488 if let (Some(devtools_chan), Some(pipeline_id), Some(webview_id)) = (
489 devtools_chan,
490 request.pipeline_id,
491 request.target_webview_id,
492 ) {
493 let browsing_context_id = webview_id.into();
494 let from_cache = matches!(cache_state, CacheState::Local | CacheState::Validated);
495
496 let devtoolsresponse = DevtoolsHttpResponse {
497 headers,
498 status,
499 body: body.map(DebugVec::from),
500 from_cache,
501 pipeline_id,
502 browsing_context_id,
503 };
504 let net_event_response = NetworkEvent::HttpResponse(devtoolsresponse);
505
506 let msg =
507 ChromeToDevtoolsControlMsg::NetworkEvent(request.id.0.to_string(), net_event_response);
508
509 let _ = devtools_chan
510 .lock()
511 .send(DevtoolsControlMsg::FromChrome(msg));
512 }
513}
514
515pub fn send_early_httprequest_to_devtools(request: &Request, context: &FetchContext) {
516 if request.url().scheme() == "data" {
518 return;
519 }
520 if let (Some(devtools_chan), Some(browsing_context_id), Some(pipeline_id)) = (
521 context.devtools_chan.as_ref(),
522 request.target_webview_id.map(|id| id.into()),
523 request.pipeline_id,
524 ) {
525 let devtools_request = DevtoolsHttpRequest {
527 url: request.current_url().clone(),
528 method: request.method.clone(),
529 headers: request.headers.clone(),
530 body: None,
531 pipeline_id,
532 started_date_time: SystemTime::now(),
533 time_stamp: 0,
534 connect_time: Duration::from_millis(0),
535 send_time: Duration::from_millis(0),
536 destination: request.destination,
537 is_xhr: false,
538 browsing_context_id,
539 };
540
541 let msg = ChromeToDevtoolsControlMsg::NetworkEvent(
542 request.id.0.to_string(),
543 NetworkEvent::HttpRequest(devtools_request),
544 );
545
546 send_request_to_devtools(msg, &devtools_chan.lock());
547 }
548}
549
550fn auth_from_cache(
551 auth_cache: &RwLock<AuthCache>,
552 origin: &ImmutableOrigin,
553) -> Option<Authorization<Basic>> {
554 if let Some(auth_entry) = auth_cache.read().entries.get(&origin.ascii_serialization()) {
555 let user_name = &auth_entry.user_name;
556 let password = &auth_entry.password;
557 Some(Authorization::basic(user_name, password))
558 } else {
559 None
560 }
561}
562
563enum BodyChunk {
566 Chunk(IpcSharedMemory),
568 Done,
570}
571
572enum BodyStream {
574 Chunked(TokioReceiver<Result<Frame<Bytes>, hyper::Error>>),
577 Buffered(UnboundedReceiver<BodyChunk>),
580}
581
582enum BodySink {
585 Chunked(TokioSender<Result<Frame<Bytes>, hyper::Error>>),
587 Buffered(UnboundedSender<BodyChunk>),
591}
592
593impl BodySink {
594 fn transmit_bytes(&self, bytes: IpcSharedMemory) {
595 match self {
596 BodySink::Chunked(sender) => {
597 let sender = sender.clone();
598 spawn_task(async move {
599 let _ = sender
600 .send(Ok(Frame::data(Bytes::copy_from_slice(&bytes))))
601 .await;
602 });
603 },
604 BodySink::Buffered(sender) => {
605 let _ = sender.send(BodyChunk::Chunk(bytes));
606 },
607 }
608 }
609
610 fn close(&self) {
611 match self {
612 BodySink::Chunked(_) => { },
613 BodySink::Buffered(sender) => {
614 let _ = sender.send(BodyChunk::Done);
615 },
616 }
617 }
618}
619
620#[allow(clippy::too_many_arguments)]
621async fn obtain_response(
622 client: &ServoClient,
623 url: &ServoUrl,
624 method: &Method,
625 request_headers: &mut HeaderMap,
626 body: Option<StdArc<Mutex<IpcSender<BodyChunkRequest>>>>,
627 source_is_null: bool,
628 pipeline_id: &Option<PipelineId>,
629 request_id: Option<&str>,
630 destination: Destination,
631 is_xhr: bool,
632 context: &FetchContext,
633 fetch_terminated: UnboundedSender<bool>,
634 browsing_context_id: Option<BrowsingContextId>,
635) -> Result<(HyperResponse<Decoder>, Option<ChromeToDevtoolsControlMsg>), NetworkError> {
636 {
637 let mut headers = request_headers.clone();
638
639 let devtools_bytes = StdArc::new(Mutex::new(vec![]));
640
641 let encoded_url = url
643 .clone()
644 .into_url()
645 .as_ref()
646 .replace('|', "%7C")
647 .replace('{', "%7B")
648 .replace('}', "%7D");
649
650 let request = if let Some(chunk_requester) = body {
651 let (sink, stream) = if source_is_null {
652 headers.insert(TRANSFER_ENCODING, HeaderValue::from_static("chunked"));
655
656 let (sender, receiver) = channel(1);
657 (BodySink::Chunked(sender), BodyStream::Chunked(receiver))
658 } else {
659 let (sender, receiver) = unbounded_channel();
666 (BodySink::Buffered(sender), BodyStream::Buffered(receiver))
667 };
668
669 let (body_chan, body_port) = ipc::channel().unwrap();
670
671 {
672 let requester = chunk_requester.lock();
673 let _ = requester.send(BodyChunkRequest::Connect(body_chan));
674
675 let _ = requester.send(BodyChunkRequest::Chunk);
678 }
679
680 let devtools_bytes = devtools_bytes.clone();
681 let chunk_requester2 = chunk_requester.clone();
682
683 ROUTER.add_typed_route(
684 body_port,
685 Box::new(move |message| {
686 info!("Received message");
687 let bytes = match message.unwrap() {
688 BodyChunkResponse::Chunk(bytes) => bytes,
689 BodyChunkResponse::Done => {
690 let _ = fetch_terminated.send(false);
692 sink.close();
693
694 return;
695 },
696 BodyChunkResponse::Error => {
697 let _ = fetch_terminated.send(true);
701 sink.close();
702
703 return;
704 },
705 };
706
707 devtools_bytes.lock().extend_from_slice(&bytes);
708
709 sink.transmit_bytes(bytes);
712
713 let _ = chunk_requester2.lock().send(BodyChunkRequest::Chunk);
716 }),
717 );
718
719 let body = match stream {
720 BodyStream::Chunked(receiver) => {
721 let stream = ReceiverStream::new(receiver);
722 BoxBody::new(http_body_util::StreamBody::new(stream))
723 },
724 BodyStream::Buffered(mut receiver) => {
725 let mut body = vec![];
727 loop {
728 match receiver.recv().await {
729 Some(BodyChunk::Chunk(bytes)) => {
730 body.extend_from_slice(&bytes);
731 },
732 Some(BodyChunk::Done) => break,
733 None => warn!("Failed to read all chunks from request body."),
734 }
735 }
736 Full::new(body.into()).map_err(|_| unreachable!()).boxed()
737 },
738 };
739 HyperRequest::builder()
740 .method(method)
741 .uri(encoded_url)
742 .body(body)
743 } else {
744 HyperRequest::builder()
745 .method(method)
746 .uri(encoded_url)
747 .body(
748 http_body_util::Empty::new()
749 .map_err(|_| unreachable!())
750 .boxed(),
751 )
752 };
753
754 context
755 .timing
756 .lock()
757 .set_attribute(ResourceAttribute::DomainLookupStart);
758
759 let connect_start = CrossProcessInstant::now();
762 context
763 .timing
764 .lock()
765 .set_attribute(ResourceAttribute::ConnectStart(connect_start));
766
767 if url.scheme() == "https" {
771 context
772 .timing
773 .lock()
774 .set_attribute(ResourceAttribute::SecureConnectionStart);
775 }
776
777 let mut request = match request {
778 Ok(request) => request,
779 Err(e) => return Err(NetworkError::from_http_error(&e)),
780 };
781 *request.headers_mut() = headers.clone();
782
783 let connect_end = CrossProcessInstant::now();
784 context
785 .timing
786 .lock()
787 .set_attribute(ResourceAttribute::ConnectEnd(connect_end));
788
789 let request_id = request_id.map(|v| v.to_owned());
790 let pipeline_id = *pipeline_id;
791 let closure_url = url.clone();
792 let method = method.clone();
793 let send_start = CrossProcessInstant::now();
794
795 let host = request.uri().host().unwrap_or("").to_owned();
796 let override_manager = context.state.override_manager.clone();
797 let headers = headers.clone();
798 let is_secure_scheme = url.is_secure_scheme();
799
800 client
801 .request(request)
802 .and_then(move |res| {
803 let send_end = CrossProcessInstant::now();
804
805 let msg = if let Some(request_id) = request_id {
808 if let Some(pipeline_id) = pipeline_id {
809 if let Some(browsing_context_id) = browsing_context_id {
810 Some(prepare_devtools_request(
811 request_id,
812 closure_url,
813 method.clone(),
814 headers,
815 Some(devtools_bytes.lock().clone()),
816 pipeline_id,
817 (connect_end - connect_start).unsigned_abs(),
818 (send_end - send_start).unsigned_abs(),
819 destination,
820 is_xhr,
821 browsing_context_id,
822 ))
823 } else {
824 debug!("Not notifying devtools (no browsing_context_id)");
825 None
826 }
827 } else {
832 debug!("Not notifying devtools (no pipeline_id)");
833 None
834 }
835 } else {
836 debug!("Not notifying devtools (no request_id)");
837 None
838 };
839
840 future::ready(Ok((
841 Decoder::detect(res.map(|r| r.boxed()), is_secure_scheme),
842 msg,
843 )))
844 })
845 .map_err(move |error| {
846 warn!("network error: {error:?}");
847 NetworkError::from_hyper_error(
848 &error,
849 override_manager.remove_certificate_failing_verification(host.as_str()),
850 )
851 })
852 .await
853 }
854}
855
856#[async_recursion]
858#[allow(clippy::too_many_arguments)]
859pub async fn http_fetch(
860 fetch_params: &mut FetchParams,
861 cache: &mut CorsCache,
862 cors_flag: bool,
863 cors_preflight_flag: bool,
864 authentication_fetch_flag: bool,
865 target: Target<'async_recursion>,
866 done_chan: &mut DoneChannel,
867 context: &FetchContext,
868) -> Response {
869 *done_chan = None;
871 let request = &mut fetch_params.request;
873
874 let mut response: Option<Response> = None;
877
878 if request.service_workers_mode == ServiceWorkersMode::All {
880 if let Some(ref res) = response {
885 if (res.response_type == ResponseType::Opaque && request.mode != RequestMode::NoCors) ||
893 (res.response_type == ResponseType::OpaqueRedirect &&
894 request.redirect_mode != RedirectMode::Manual) ||
895 (res.url_list.len() > 1 && request.redirect_mode != RedirectMode::Follow) ||
896 res.is_network_error()
897 {
898 return Response::network_error(NetworkError::Internal("Request failed".into()));
899 }
900
901 }
904 }
905
906 if response.is_none() {
908 if cors_preflight_flag {
910 let method_cache_match = cache.match_method(request, request.method.clone());
911
912 let method_mismatch = !method_cache_match &&
913 (!is_cors_safelisted_method(&request.method) || request.use_cors_preflight);
914 let header_mismatch = request.headers.iter().any(|(name, value)| {
915 !cache.match_header(request, name) &&
916 !is_cors_safelisted_request_header(&name, &value)
917 });
918
919 if method_mismatch || header_mismatch {
921 let preflight_result = cors_preflight_fetch(request, cache, context).await;
922 if let Some(e) = preflight_result.get_network_error() {
924 return Response::network_error(e.clone());
925 }
926 }
927 }
928
929 if request.redirect_mode == RedirectMode::Follow {
931 request.service_workers_mode = ServiceWorkersMode::None;
932 }
933
934 context
938 .timing
939 .lock()
940 .set_attribute(ResourceAttribute::RequestStart);
941
942 let mut fetch_result = http_network_or_cache_fetch(
943 fetch_params,
944 authentication_fetch_flag,
945 cors_flag,
946 done_chan,
947 context,
948 )
949 .await;
950
951 if cors_flag && cors_check(&fetch_params.request, &fetch_result).is_err() {
953 return Response::network_error(NetworkError::Internal("CORS check failed".into()));
954 }
955
956 fetch_result.return_internal = false;
957 response = Some(fetch_result);
958 }
959
960 let request = &mut fetch_params.request;
961
962 let mut response = response.unwrap();
964
965 if response
969 .actual_response()
970 .status
971 .try_code()
972 .is_some_and(is_redirect_status)
973 {
974 if response.actual_response().status != StatusCode::SEE_OTHER {
976 }
978
979 let mut location = response
981 .actual_response()
982 .headers
983 .get(header::LOCATION)
984 .and_then(|v| {
985 HeaderValue::to_str(v)
986 .map(|l| {
987 ServoUrl::parse_with_base(response.actual_response().url(), l)
988 .map_err(|err| err.to_string())
989 })
990 .ok()
991 });
992
993 if let Some(Ok(ref mut location)) = location {
995 if location.fragment().is_none() {
996 let current_url = request.current_url();
997 location.set_fragment(current_url.fragment());
998 }
999 }
1000 response.actual_response_mut().location_url = location;
1001
1002 response = match request.redirect_mode {
1004 RedirectMode::Error => {
1005 Response::network_error(NetworkError::Internal("Redirect mode error".into()))
1006 },
1007 RedirectMode::Manual => response.to_filtered(ResponseType::OpaqueRedirect),
1008 RedirectMode::Follow => {
1009 response.return_internal = true;
1011 http_redirect_fetch(
1012 fetch_params,
1013 cache,
1014 response,
1015 cors_flag,
1016 target,
1017 done_chan,
1018 context,
1019 )
1020 .await
1021 },
1022 };
1023 }
1024
1025 response.return_internal = true;
1027 context
1028 .timing
1029 .lock()
1030 .set_attribute(ResourceAttribute::RedirectCount(
1031 fetch_params.request.redirect_count as u16,
1032 ));
1033
1034 response.resource_timing = Arc::clone(&context.timing);
1035
1036 response
1038}
1039
1040struct RedirectEndTimer(Option<Arc<Mutex<ResourceFetchTiming>>>);
1042
1043impl RedirectEndTimer {
1044 fn neuter(&mut self) {
1045 self.0 = None;
1046 }
1047}
1048
1049impl Drop for RedirectEndTimer {
1050 fn drop(&mut self) {
1051 let RedirectEndTimer(resource_fetch_timing_opt) = self;
1052
1053 resource_fetch_timing_opt.as_ref().map_or((), |t| {
1054 t.lock()
1055 .set_attribute(ResourceAttribute::RedirectEnd(RedirectEndValue::Zero));
1056 })
1057 }
1058}
1059
1060#[async_recursion]
1062pub async fn http_redirect_fetch(
1063 fetch_params: &mut FetchParams,
1064 cache: &mut CorsCache,
1065 response: Response,
1066 cors_flag: bool,
1067 target: Target<'async_recursion>,
1068 done_chan: &mut DoneChannel,
1069 context: &FetchContext,
1070) -> Response {
1071 let mut redirect_end_timer = RedirectEndTimer(Some(context.timing.clone()));
1072
1073 let request = &mut fetch_params.request;
1075
1076 assert!(response.return_internal);
1077
1078 let location_url = response.actual_response().location_url.clone();
1079 let location_url = match location_url {
1080 None => return response,
1082 Some(Err(err)) => {
1084 return Response::network_error(NetworkError::Internal(
1085 "Location URL parse failure: ".to_owned() + &err,
1086 ));
1087 },
1088 Some(Ok(ref url)) if !matches!(url.scheme(), "http" | "https") => {
1090 return Response::network_error(NetworkError::Internal(
1091 "Location URL not an HTTP(S) scheme".into(),
1092 ));
1093 },
1094 Some(Ok(url)) => url,
1095 };
1096
1097 context
1100 .timing
1101 .lock()
1102 .set_attribute(ResourceAttribute::RedirectStart(
1103 RedirectStartValue::FetchStart,
1104 ));
1105
1106 context
1107 .timing
1108 .lock()
1109 .set_attribute(ResourceAttribute::FetchStart);
1110
1111 context
1113 .timing
1114 .lock()
1115 .set_attribute(ResourceAttribute::StartTime(ResourceTimeValue::FetchStart));
1116
1117 context
1118 .timing
1119 .lock()
1120 .set_attribute(ResourceAttribute::StartTime(
1121 ResourceTimeValue::RedirectStart,
1122 )); if request.redirect_count >= 20 {
1126 return Response::network_error(NetworkError::Internal("Too many redirects".into()));
1127 }
1128
1129 request.redirect_count += 1;
1131
1132 let same_origin = match request.origin {
1134 Origin::Origin(ref origin) => *origin == location_url.origin(),
1135 Origin::Client => panic!(
1136 "Request origin should not be client for {}",
1137 request.current_url()
1138 ),
1139 };
1140
1141 let has_credentials = has_credentials(&location_url);
1142
1143 if request.mode == RequestMode::CorsMode && !same_origin && has_credentials {
1144 return Response::network_error(NetworkError::Internal(
1145 "Cross-origin credentials check failed".into(),
1146 ));
1147 }
1148
1149 if cors_flag && location_url.origin() != request.current_url().origin() {
1151 request.origin = Origin::Origin(ImmutableOrigin::new_opaque());
1152 }
1153
1154 if cors_flag && has_credentials {
1156 return Response::network_error(NetworkError::Internal("Credentials check failed".into()));
1157 }
1158
1159 if response.actual_response().status != StatusCode::SEE_OTHER &&
1162 request.body.as_ref().is_some_and(|b| b.source_is_null())
1163 {
1164 return Response::network_error(NetworkError::Internal("Request body is not done".into()));
1165 }
1166
1167 if response
1169 .actual_response()
1170 .status
1171 .try_code()
1172 .is_some_and(|code| {
1173 ((code == StatusCode::MOVED_PERMANENTLY || code == StatusCode::FOUND) &&
1174 request.method == Method::POST) ||
1175 (code == StatusCode::SEE_OTHER &&
1176 request.method != Method::HEAD &&
1177 request.method != Method::GET)
1178 })
1179 {
1180 request.method = Method::GET;
1182 request.body = None;
1183 for name in &[
1185 CONTENT_ENCODING,
1186 CONTENT_LANGUAGE,
1187 CONTENT_LOCATION,
1188 CONTENT_TYPE,
1189 ] {
1190 request.headers.remove(name);
1191 }
1192 }
1193
1194 if location_url.origin() != request.current_url().origin() {
1198 request.headers.remove(AUTHORIZATION);
1201 }
1202
1203 if let Some(body) = request.body.as_mut() {
1206 body.extract_source();
1207 }
1208
1209 request.url_list.push(location_url);
1213
1214 set_requests_referrer_policy_on_redirect(request, response.actual_response());
1216
1217 let recursive_flag = request.redirect_mode != RedirectMode::Manual;
1220
1221 let fetch_response = main_fetch(
1223 fetch_params,
1224 cache,
1225 recursive_flag,
1226 target,
1227 done_chan,
1228 context,
1229 )
1230 .await;
1231
1232 context
1234 .timing
1235 .lock()
1236 .set_attribute(ResourceAttribute::RedirectEnd(
1237 RedirectEndValue::ResponseEnd,
1238 ));
1239 redirect_end_timer.neuter();
1240
1241 fetch_response
1242}
1243
1244#[async_recursion]
1246async fn http_network_or_cache_fetch(
1247 fetch_params: &mut FetchParams,
1248 authentication_fetch_flag: bool,
1249 cors_flag: bool,
1250 done_chan: &mut DoneChannel,
1251 context: &FetchContext,
1252) -> Response {
1253 let http_fetch_params: &mut FetchParams;
1255 let mut fetch_params_copy: FetchParams;
1256
1257 let mut response: Option<Response> = None;
1261
1262 let mut revalidating_flag = false;
1264
1265 let http_request = if fetch_params.request.traversable_for_user_prompts ==
1269 TraversableForUserPrompts::NoTraversable &&
1270 fetch_params.request.redirect_mode == RedirectMode::Error
1271 {
1272 http_fetch_params = fetch_params;
1273 &mut http_fetch_params.request
1274 }
1275 else {
1277 fetch_params_copy = fetch_params.clone();
1280 http_fetch_params = &mut fetch_params_copy;
1281
1282 &mut http_fetch_params.request
1283 };
1284
1285 let include_credentials = match http_request.credentials_mode {
1287 CredentialsMode::Include => true,
1289 CredentialsMode::CredentialsSameOrigin
1291 if http_request.response_tainting == ResponseTainting::Basic =>
1292 {
1293 true
1294 },
1295 _ => false,
1296 };
1297
1298 let content_length = http_request
1305 .body
1306 .as_ref()
1307 .and_then(|body| body.len().map(|size| size as u64));
1308
1309 let mut content_length_header_value = None;
1311
1312 if http_request.body.is_none() && matches!(http_request.method, Method::POST | Method::PUT) {
1315 content_length_header_value = Some(0);
1316 }
1317
1318 if let Some(content_length) = content_length {
1322 content_length_header_value = Some(content_length);
1323 };
1324
1325 if let Some(content_length_header_value) = content_length_header_value {
1328 http_request
1329 .headers
1330 .typed_insert(ContentLength(content_length_header_value));
1331 }
1332
1333 if content_length.is_some() && http_request.keep_alive {
1335 }
1337
1338 match http_request.referrer {
1340 Referrer::ReferrerUrl(ref http_request_referrer) |
1341 Referrer::Client(ref http_request_referrer) => {
1342 if let Ok(referer) = http_request_referrer.as_str().parse::<Referer>() {
1345 http_request.headers.typed_insert(referer);
1347 } else {
1348 error!("Failed to parse {} as referrer", http_request_referrer);
1352 }
1353 },
1354 _ => {},
1355 };
1356
1357 append_a_request_origin_header(http_request);
1359
1360 append_the_fetch_metadata_headers(http_request);
1362
1363 if http_request.initiator == Initiator::Prefetch {
1366 if let Ok(value) = HeaderValue::from_str("prefetch") {
1367 http_request.headers.insert("Sec-Purpose", value);
1368 }
1369 }
1370
1371 if !http_request.headers.contains_key(header::USER_AGENT) {
1374 http_request
1375 .headers
1376 .typed_insert::<UserAgent>(context.user_agent.parse().unwrap());
1377 }
1378
1379 match http_request.cache_mode {
1381 CacheMode::Default if is_no_store_cache(&http_request.headers) => {
1385 http_request.cache_mode = CacheMode::NoStore;
1386 },
1387
1388 CacheMode::NoCache if !http_request.headers.contains_key(header::CACHE_CONTROL) => {
1398 http_request
1399 .headers
1400 .typed_insert(CacheControl::new().with_max_age(Duration::from_secs(0)));
1401 },
1402
1403 CacheMode::Reload | CacheMode::NoStore => {
1405 if !http_request.headers.contains_key(header::PRAGMA) {
1408 http_request.headers.typed_insert(Pragma::no_cache());
1409 }
1410
1411 if !http_request.headers.contains_key(header::CACHE_CONTROL) {
1414 http_request
1415 .headers
1416 .typed_insert(CacheControl::new().with_no_cache());
1417 }
1418 },
1419
1420 _ => {},
1421 }
1422
1423 if http_request.headers.contains_key(header::RANGE) {
1426 if let Ok(value) = HeaderValue::from_str("identity") {
1427 http_request.headers.insert("Accept-Encoding", value);
1428 }
1429 }
1430
1431 http_request.headers.remove(header::HOST);
1435 set_default_accept_encoding(&mut http_request.headers);
1437
1438 let current_url = http_request.current_url();
1439
1440 if include_credentials {
1443 set_request_cookies(
1447 ¤t_url,
1448 &mut http_request.headers,
1449 &context.state.cookie_jar,
1450 );
1451 if !http_request.headers.contains_key(header::AUTHORIZATION) {
1453 let mut authorization_value = None;
1455
1456 if let Some(basic) = auth_from_cache(&context.state.auth_cache, ¤t_url.origin()) {
1458 if !http_request.use_url_credentials || !has_credentials(¤t_url) {
1459 authorization_value = Some(basic);
1460 }
1461 }
1462
1463 if authentication_fetch_flag &&
1465 authorization_value.is_none() &&
1466 has_credentials(¤t_url)
1467 {
1468 authorization_value = Some(Authorization::basic(
1469 current_url.username(),
1470 current_url.password().unwrap_or(""),
1471 ));
1472 }
1473
1474 if let Some(basic) = authorization_value {
1476 http_request.headers.typed_insert(basic);
1477 }
1478 }
1479 }
1480
1481 block_for_cache_ready(
1484 context,
1485 http_request,
1486 done_chan,
1487 &mut revalidating_flag,
1488 &mut response,
1489 );
1490
1491 wait_for_cached_response(done_chan, &mut response).await;
1492
1493 if response.is_none() {
1497 if http_request.cache_mode == CacheMode::OnlyIfCached {
1499 update_http_cache_state(context, http_request);
1502 return Response::network_error(NetworkError::Internal(
1503 "Couldn't find response in cache".into(),
1504 ));
1505 }
1506
1507 let forward_response =
1510 http_network_fetch(http_fetch_params, include_credentials, done_chan, context).await;
1511
1512 let http_request = &mut http_fetch_params.request;
1513 if forward_response.status.in_range(200..=399) && !http_request.method.is_safe() {
1517 let mut http_cache = context.state.http_cache.write();
1518 http_cache.invalidate(http_request, &forward_response);
1519 }
1520
1521 if revalidating_flag && forward_response.status == StatusCode::NOT_MODIFIED {
1523 *done_chan = None;
1526 {
1527 let mut http_cache = context.state.http_cache.write();
1528 response = http_cache.refresh(http_request, forward_response.clone(), done_chan);
1529 }
1530 wait_for_cached_response(done_chan, &mut response).await;
1531 if let Some(response) = &mut response {
1532 response.cache_state = CacheState::Validated;
1533 }
1534 }
1535
1536 if response.is_none() {
1538 let forward_response = response.insert(forward_response);
1540
1541 if http_request.cache_mode != CacheMode::NoStore {
1544 let mut http_cache = context.state.http_cache.write();
1547 http_cache.store(http_request, forward_response);
1548 }
1549 }
1550 }
1551
1552 let http_request = &mut http_fetch_params.request;
1553 update_http_cache_state(context, http_request);
1555
1556 let mut response = response.unwrap();
1557
1558 if http_request.response_tainting != ResponseTainting::CorsTainting &&
1561 cross_origin_resource_policy_check(http_request, &response) ==
1562 CrossOriginResourcePolicy::Blocked
1563 {
1564 return Response::network_error(NetworkError::Internal(
1565 "Cross-origin resource policy check failed".into(),
1566 ));
1567 }
1568
1569 if http_request.headers.contains_key(RANGE) {
1573 response.range_requested = true;
1574 }
1575
1576 if response.status.try_code() == Some(StatusCode::UNAUTHORIZED) &&
1584 !cors_flag &&
1585 include_credentials &&
1586 response.headers.contains_key(WWW_AUTHENTICATE)
1587 {
1588 let request = &mut fetch_params.request;
1591
1592 if request.body.is_some() {
1594 }
1596
1597 if !request.use_url_credentials || authentication_fetch_flag {
1599 let Some(credentials) = context.state.request_authentication(request, &response) else {
1600 return response;
1601 };
1602
1603 if let Err(err) = request
1604 .current_url_mut()
1605 .set_username(&credentials.username)
1606 {
1607 error!("error setting username for url: {:?}", err);
1608 return response;
1609 };
1610
1611 if let Err(err) = request
1612 .current_url_mut()
1613 .set_password(Some(&credentials.password))
1614 {
1615 error!("error setting password for url: {:?}", err);
1616 return response;
1617 };
1618 }
1619
1620 *done_chan = None;
1623
1624 response = http_network_or_cache_fetch(
1626 fetch_params,
1627 true, cors_flag,
1629 done_chan,
1630 context,
1631 )
1632 .await;
1633 }
1634
1635 if response.status == StatusCode::PROXY_AUTHENTICATION_REQUIRED {
1637 let request = &mut fetch_params.request;
1638 if request.traversable_for_user_prompts == TraversableForUserPrompts::NoTraversable {
1641 return Response::network_error(NetworkError::Internal(
1642 "Can't find Window object".into(),
1643 ));
1644 }
1645
1646 let Some(credentials) = context.state.request_authentication(request, &response) else {
1654 return response;
1655 };
1656
1657 let entry = AuthCacheEntry {
1659 user_name: credentials.username,
1660 password: credentials.password,
1661 };
1662 {
1663 let mut auth_cache = context.state.auth_cache.write();
1664 let key = request.current_url().origin().ascii_serialization();
1665 auth_cache.entries.insert(key, entry);
1666 }
1667
1668 *done_chan = None;
1671
1672 response = http_network_or_cache_fetch(
1674 fetch_params,
1675 false, cors_flag,
1677 done_chan,
1678 context,
1679 )
1680 .await;
1681 }
1682
1683 if authentication_fetch_flag {
1691 }
1693
1694 response
1696}
1697
1698fn block_for_cache_ready(
1707 context: &FetchContext,
1708 http_request: &mut Request,
1709 done_chan: &mut DoneChannel,
1710 revalidating_flag: &mut bool,
1711 response: &mut Option<Response>,
1712) {
1713 let (lock, cvar) = {
1714 let entry_key = CacheKey::new(http_request);
1715 let mut state_map = context.state.http_cache_state.lock();
1716 &*state_map
1717 .entry(entry_key)
1718 .or_insert_with(|| {
1719 Arc::new((
1720 Mutex::new(HttpCacheEntryState::ReadyToConstruct),
1721 Condvar::new(),
1722 ))
1723 })
1724 .clone()
1725 };
1726
1727 let mut state = lock.lock();
1729 while let HttpCacheEntryState::PendingStore(_) = *state {
1730 let time_out = cvar.wait_for(&mut state, Duration::from_millis(500));
1731 if time_out.timed_out() {
1732 break;
1734 }
1735 }
1736
1737 let http_cache = context.state.http_cache.read();
1740 let stored_response = http_cache.construct_response(http_request, done_chan);
1744
1745 if let Some(response_from_cache) = stored_response {
1747 let response_headers = response_from_cache.response.headers.clone();
1748 let (cached_response, needs_revalidation) =
1750 match (http_request.cache_mode, &http_request.mode) {
1751 (CacheMode::ForceCache, _) => (Some(response_from_cache.response), false),
1752 (CacheMode::OnlyIfCached, &RequestMode::SameOrigin) => {
1753 (Some(response_from_cache.response), false)
1754 },
1755 (CacheMode::OnlyIfCached, _) | (CacheMode::NoStore, _) | (CacheMode::Reload, _) => {
1756 (None, false)
1757 },
1758 (_, _) => (
1759 Some(response_from_cache.response),
1760 response_from_cache.needs_validation,
1761 ),
1762 };
1763
1764 if needs_revalidation {
1765 *revalidating_flag = true;
1766 if let Some(http_date) = response_headers.typed_get::<LastModified>() {
1768 let http_date: SystemTime = http_date.into();
1769 http_request
1770 .headers
1771 .typed_insert(IfModifiedSince::from(http_date));
1772 }
1773 if let Some(entity_tag) = response_headers.get(header::ETAG) {
1774 http_request
1775 .headers
1776 .insert(header::IF_NONE_MATCH, entity_tag.clone());
1777 }
1778 } else {
1779 *response = cached_response;
1781 if let Some(response) = response {
1782 response.cache_state = CacheState::Local;
1783 }
1784 }
1785 if response.is_none() {
1786 *done_chan = None;
1789
1790 if let HttpCacheEntryState::PendingStore(i) = *state {
1793 let new = i + 1;
1794 *state = HttpCacheEntryState::PendingStore(new);
1795 } else {
1796 *state = HttpCacheEntryState::PendingStore(1);
1797 }
1798 }
1799 }
1800
1801 if *state == HttpCacheEntryState::ReadyToConstruct {
1803 cvar.notify_one();
1804 }
1805 }
1807
1808fn update_http_cache_state(context: &FetchContext, http_request: &Request) {
1813 let (lock, cvar) = {
1814 let entry_key = CacheKey::new(http_request);
1815 let mut state_map = context.state.http_cache_state.lock();
1816 &*state_map
1817 .get_mut(&entry_key)
1818 .expect("Entry in http-cache state to have been previously inserted")
1819 .clone()
1820 };
1821 let mut state = lock.lock();
1822 if let HttpCacheEntryState::PendingStore(i) = *state {
1823 let new = i - 1;
1824 if new == 0 {
1825 *state = HttpCacheEntryState::ReadyToConstruct;
1826 cvar.notify_one();
1828 } else {
1829 *state = HttpCacheEntryState::PendingStore(new);
1830 }
1831 }
1832}
1833
1834async fn wait_for_cached_response(done_chan: &mut DoneChannel, response: &mut Option<Response>) {
1837 if let Some(ref mut ch) = *done_chan {
1838 assert!(response.is_some());
1842
1843 loop {
1844 match ch.1.recv().await {
1845 Some(Data::Payload(_)) => {},
1846 Some(Data::Done) => break, Some(Data::Cancelled) => {
1848 *response = None;
1851 break;
1852 },
1853 _ => panic!("HTTP cache should always send Done or Cancelled"),
1854 }
1855 }
1856 }
1857 *done_chan = None;
1859}
1860
1861#[derive(PartialEq)]
1865enum CrossOriginResourcePolicy {
1866 Allowed,
1867 Blocked,
1868}
1869
1870fn cross_origin_resource_policy_check(
1873 request: &Request,
1874 response: &Response,
1875) -> CrossOriginResourcePolicy {
1876 if request.mode != RequestMode::NoCors {
1878 return CrossOriginResourcePolicy::Allowed;
1879 }
1880
1881 let current_url_origin = request.current_url().origin();
1883 let same_origin = if let Origin::Origin(ref origin) = request.origin {
1884 *origin == request.current_url().origin()
1885 } else {
1886 false
1887 };
1888
1889 if same_origin {
1890 return CrossOriginResourcePolicy::Allowed;
1891 }
1892
1893 let policy = response
1895 .headers
1896 .get(HeaderName::from_static("cross-origin-resource-policy"))
1897 .map(|h| h.to_str().unwrap_or(""))
1898 .unwrap_or("");
1899
1900 if policy == "same-origin" {
1902 return CrossOriginResourcePolicy::Blocked;
1903 }
1904
1905 if let Origin::Origin(ref request_origin) = request.origin {
1907 let schemeless_same_origin = is_schemelessy_same_site(request_origin, ¤t_url_origin);
1908 if schemeless_same_origin &&
1909 (request_origin.scheme() == Some("https") ||
1910 response.https_state == HttpsState::None)
1911 {
1912 return CrossOriginResourcePolicy::Allowed;
1913 }
1914 };
1915
1916 if policy == "same-site" {
1918 return CrossOriginResourcePolicy::Blocked;
1919 }
1920
1921 CrossOriginResourcePolicy::Allowed
1922}
1923
1924struct ResponseEndTimer(Option<Arc<Mutex<ResourceFetchTiming>>>);
1926
1927impl ResponseEndTimer {
1928 fn neuter(&mut self) {
1929 self.0 = None;
1930 }
1931}
1932
1933impl Drop for ResponseEndTimer {
1934 fn drop(&mut self) {
1935 let ResponseEndTimer(resource_fetch_timing_opt) = self;
1936
1937 resource_fetch_timing_opt.as_ref().map_or((), |t| {
1938 t.lock().set_attribute(ResourceAttribute::ResponseEnd);
1939 })
1940 }
1941}
1942
1943async fn http_network_fetch(
1945 fetch_params: &mut FetchParams,
1946 credentials_flag: bool,
1947 done_chan: &mut DoneChannel,
1948 context: &FetchContext,
1949) -> Response {
1950 let mut response_end_timer = ResponseEndTimer(Some(context.timing.clone()));
1951
1952 let request = &mut fetch_params.request;
1954
1955 let url = request.current_url();
1966 let request_id = request.id.0.to_string();
1967 if log_enabled!(log::Level::Info) {
1968 info!("{:?} request for {}", request.method, url);
1969 for header in request.headers.iter() {
1970 debug!(" - {:?}", header);
1971 }
1972 }
1973
1974 let is_xhr = request.destination == Destination::None;
1978
1979 let (fetch_terminated_sender, mut fetch_terminated_receiver) = unbounded_channel();
1981
1982 let body = request.body.as_ref().map(|body| body.take_stream());
1983
1984 if body.is_none() {
1985 let _ = fetch_terminated_sender.send(false);
1990 }
1991
1992 let browsing_context_id = request.target_webview_id.map(Into::into);
1993
1994 let (res, msg) = match &request.mode {
1995 RequestMode::WebSocket {
1996 protocols,
1997 original_url: _,
1998 } => {
1999 let (resource_event_sender, dom_action_receiver) = {
2002 let mut websocket_chan = context.websocket_chan.as_ref().unwrap().lock();
2003 (
2004 websocket_chan.sender.clone(),
2005 websocket_chan.receiver.take().unwrap(),
2006 )
2007 };
2008
2009 let mut tls_config = create_tls_config(
2010 context.ca_certificates.clone(),
2011 context.ignore_certificate_errors,
2012 context.state.override_manager.clone(),
2013 );
2014 tls_config.alpn_protocols = vec!["http/1.1".to_string().into()];
2015
2016 let response = match start_websocket(
2017 context.state.clone(),
2018 resource_event_sender,
2019 protocols,
2020 request,
2021 tls_config,
2022 dom_action_receiver,
2023 )
2024 .await
2025 {
2026 Ok(response) => response,
2027 Err(e) => {
2028 return Response::network_internal_error(e.to_string());
2029 },
2030 };
2031
2032 let response = response.map(|r| match r {
2033 Some(body) => Full::from(body).map_err(|_| unreachable!()).boxed(),
2034 None => http_body_util::Empty::new()
2035 .map_err(|_| unreachable!())
2036 .boxed(),
2037 });
2038 (Decoder::detect(response, url.is_secure_scheme()), None)
2039 },
2040 _ => {
2041 let response_future = obtain_response(
2042 &context.state.client,
2043 &url,
2044 &request.method,
2045 &mut request.headers,
2046 body,
2047 request
2048 .body
2049 .as_ref()
2050 .is_some_and(|body| body.source_is_null()),
2051 &request.pipeline_id,
2052 Some(&request_id),
2053 request.destination,
2054 is_xhr,
2055 context,
2056 fetch_terminated_sender,
2057 browsing_context_id,
2058 );
2059
2060 let (res, msg) = match response_future.await {
2062 Ok(wrapped_response) => wrapped_response,
2063 Err(error) => return Response::network_error(error),
2064 };
2065 (res, msg)
2066 },
2067 };
2068
2069 if log_enabled!(log::Level::Info) {
2070 debug!("{:?} response for {}", res.version(), url);
2071 for header in res.headers().iter() {
2072 debug!(" - {:?}", header);
2073 }
2074 }
2075
2076 match fetch_terminated_receiver.recv().await {
2079 Some(true) => {
2080 return Response::network_error(NetworkError::Internal(
2081 "Request body streaming failed.".into(),
2082 ));
2083 },
2084 Some(false) => {},
2085 _ => warn!("Failed to receive confirmation request was streamed without error."),
2086 }
2087
2088 let header_strings: Vec<&str> = res
2089 .headers()
2090 .get_all("Timing-Allow-Origin")
2091 .iter()
2092 .map(|header_value| header_value.to_str().unwrap_or(""))
2093 .collect();
2094 let wildcard_present = header_strings.contains(&"*");
2095 let req_origin_in_timing_allow = header_strings
2099 .iter()
2100 .any(|header_str| match request.origin {
2101 SpecificOrigin(ref immutable_request_origin) => {
2102 *header_str == immutable_request_origin.ascii_serialization()
2103 },
2104 _ => false,
2105 });
2106
2107 let is_same_origin = request.url_list.iter().all(|url| match request.origin {
2108 SpecificOrigin(ref immutable_request_origin) => url.origin() == *immutable_request_origin,
2109 _ => false,
2110 });
2111
2112 if !(is_same_origin || req_origin_in_timing_allow || wildcard_present) {
2113 context.timing.lock().mark_timing_check_failed();
2114 }
2115
2116 let timing = context.timing.lock().clone();
2117 let mut response = Response::new(url.clone(), timing);
2118
2119 let status_text = res
2120 .extensions()
2121 .get::<ReasonPhrase>()
2122 .map(ReasonPhrase::as_bytes)
2123 .or_else(|| res.status().canonical_reason().map(str::as_bytes))
2124 .map(Vec::from)
2125 .unwrap_or_default();
2126 response.status = HttpStatus::new(res.status(), status_text);
2127
2128 info!("got {:?} response for {:?}", res.status(), request.url());
2129 response.headers = res.headers().clone();
2130 response.referrer = request.referrer.to_url().cloned();
2131 response.referrer_policy = request.referrer_policy;
2132
2133 let res_body = response.body.clone();
2134
2135 let (done_sender, done_receiver) = unbounded_channel();
2137 *done_chan = Some((done_sender.clone(), done_receiver));
2138
2139 let devtools_sender = context.devtools_chan.clone();
2140 let cancellation_listener = context.cancellation_listener.clone();
2141 if cancellation_listener.cancelled() {
2142 return Response::network_error(NetworkError::Internal("Fetch aborted".into()));
2143 }
2144
2145 *res_body.lock() = ResponseBody::Receiving(vec![]);
2146 let res_body2 = res_body.clone();
2147
2148 if let Some(ref sender) = devtools_sender {
2149 let sender = sender.lock();
2150 if let Some(m) = msg {
2151 send_request_to_devtools(m, &sender);
2152 }
2153 }
2154
2155 let done_sender2 = done_sender.clone();
2156 let done_sender3 = done_sender.clone();
2157 let timing_ptr2 = context.timing.clone();
2158 let timing_ptr3 = context.timing.clone();
2159 let devtools_request = request.clone();
2160 let url1 = devtools_request.url();
2161 let url2 = url1.clone();
2162
2163 let status = response.status.clone();
2164 let headers = response.headers.clone();
2165 let devtools_chan = context.devtools_chan.clone();
2166
2167 spawn_task(
2168 res.into_body()
2169 .map_err(|e| {
2170 warn!("Error streaming response body: {:?}", e);
2171 })
2172 .try_fold(res_body, move |res_body, chunk| {
2173 if cancellation_listener.cancelled() {
2174 *res_body.lock() = ResponseBody::Done(vec![]);
2175 let _ = done_sender.send(Data::Cancelled);
2176 return future::ready(Err(()));
2177 }
2178 if let ResponseBody::Receiving(ref mut body) = *res_body.lock() {
2179 let bytes = chunk;
2180 body.extend_from_slice(&bytes);
2181 let _ = done_sender.send(Data::Payload(bytes.to_vec()));
2182 }
2183 future::ready(Ok(res_body))
2184 })
2185 .and_then(move |res_body| {
2186 debug!("successfully finished response for {:?}", url1);
2187 let mut body = res_body.lock();
2188 let completed_body = match *body {
2189 ResponseBody::Receiving(ref mut body) => std::mem::take(body),
2190 _ => vec![],
2191 };
2192 let devtools_response_body = completed_body.clone();
2193 *body = ResponseBody::Done(completed_body);
2194 send_response_values_to_devtools(
2195 Some(headers),
2196 status,
2197 Some(devtools_response_body),
2198 CacheState::None,
2199 &devtools_request,
2200 devtools_chan,
2201 );
2202 timing_ptr2
2203 .lock()
2204 .set_attribute(ResourceAttribute::ResponseEnd);
2205 let _ = done_sender2.send(Data::Done);
2206 future::ready(Ok(()))
2207 })
2208 .map_err(move |_| {
2209 debug!("finished response for {:?}", url2);
2210 let mut body = res_body2.lock();
2211 let completed_body = match *body {
2212 ResponseBody::Receiving(ref mut body) => std::mem::take(body),
2213 _ => vec![],
2214 };
2215 *body = ResponseBody::Done(completed_body);
2216 timing_ptr3
2217 .lock()
2218 .set_attribute(ResourceAttribute::ResponseEnd);
2219 let _ = done_sender3.send(Data::Done);
2220 }),
2221 );
2222
2223 response.https_state = match url.scheme() {
2229 "https" => HttpsState::Modern,
2230 _ => HttpsState::None,
2231 };
2232
2233 if credentials_flag {
2246 set_cookies_from_headers(&url, &response.headers, &context.state.cookie_jar);
2247 }
2248 context
2249 .state
2250 .hsts_list
2251 .write()
2252 .update_hsts_list_from_response(&url, &response.headers);
2253
2254 response_end_timer.neuter();
2268
2269 response
2270}
2271
2272async fn cors_preflight_fetch(
2274 request: &Request,
2275 cache: &mut CorsCache,
2276 context: &FetchContext,
2277) -> Response {
2278 let mut preflight = RequestBuilder::new(
2283 request.target_webview_id,
2284 request.current_url(),
2285 request.referrer.clone(),
2286 )
2287 .method(Method::OPTIONS)
2288 .origin(match &request.origin {
2289 Origin::Client => {
2290 unreachable!("We shouldn't get Client origin in cors_preflight_fetch.")
2291 },
2292 Origin::Origin(origin) => origin.clone(),
2293 })
2294 .pipeline_id(request.pipeline_id)
2295 .initiator(request.initiator)
2296 .destination(request.destination)
2297 .referrer_policy(request.referrer_policy)
2298 .mode(RequestMode::CorsMode)
2299 .response_tainting(ResponseTainting::CorsTainting)
2300 .policy_container(match &request.policy_container {
2301 RequestPolicyContainer::Client => {
2302 unreachable!("We should have a policy container for request in cors_preflight_fetch")
2303 },
2304 RequestPolicyContainer::PolicyContainer(policy_container) => policy_container.clone(),
2305 })
2306 .build();
2307
2308 preflight
2310 .headers
2311 .insert(ACCEPT, HeaderValue::from_static("*/*"));
2312
2313 preflight
2315 .headers
2316 .typed_insert::<AccessControlRequestMethod>(AccessControlRequestMethod::from(
2317 request.method.clone(),
2318 ));
2319
2320 let headers = get_cors_unsafe_header_names(&request.headers);
2322
2323 if !headers.is_empty() {
2325 preflight.headers.insert(
2328 ACCESS_CONTROL_REQUEST_HEADERS,
2329 HeaderValue::from_bytes(itertools::join(headers.iter(), ",").as_bytes())
2330 .unwrap_or(HeaderValue::from_static("")),
2331 );
2332 }
2333
2334 let mut fetch_params = FetchParams::new(preflight);
2337 let response =
2338 http_network_or_cache_fetch(&mut fetch_params, false, false, &mut None, context).await;
2339
2340 if cors_check(request, &response).is_ok() && response.status.code().is_success() {
2342 let mut methods = if response
2345 .headers
2346 .contains_key(header::ACCESS_CONTROL_ALLOW_METHODS)
2347 {
2348 match response.headers.typed_get::<AccessControlAllowMethods>() {
2349 Some(methods) => methods.iter().collect(),
2350 None => {
2352 return Response::network_error(NetworkError::Internal(
2353 "CORS ACAM check failed".into(),
2354 ));
2355 },
2356 }
2357 } else {
2358 vec![]
2359 };
2360
2361 let header_names = if response
2364 .headers
2365 .contains_key(header::ACCESS_CONTROL_ALLOW_HEADERS)
2366 {
2367 match response.headers.typed_get::<AccessControlAllowHeaders>() {
2368 Some(names) => names.iter().collect(),
2369 None => {
2371 return Response::network_error(NetworkError::Internal(
2372 "CORS ACAH check failed".into(),
2373 ));
2374 },
2375 }
2376 } else {
2377 vec![]
2378 };
2379
2380 debug!(
2381 "CORS check: Allowed methods: {:?}, current method: {:?}",
2382 methods, request.method
2383 );
2384
2385 if methods.is_empty() && request.use_cors_preflight {
2388 methods = vec![request.method.clone()];
2389 }
2390
2391 if methods
2394 .iter()
2395 .all(|method| *method.as_str() != *request.method.as_ref()) &&
2396 !is_cors_safelisted_method(&request.method) &&
2397 (request.credentials_mode == CredentialsMode::Include ||
2398 methods.iter().all(|method| method.as_ref() != "*"))
2399 {
2400 return Response::network_error(NetworkError::Internal(
2401 "CORS method check failed".into(),
2402 ));
2403 }
2404
2405 debug!(
2406 "CORS check: Allowed headers: {:?}, current headers: {:?}",
2407 header_names, request.headers
2408 );
2409
2410 if request.headers.iter().any(|(name, _)| {
2413 is_cors_non_wildcard_request_header_name(name) &&
2414 header_names.iter().all(|header_name| header_name != name)
2415 }) {
2416 return Response::network_error(NetworkError::Internal(
2417 "CORS authorization check failed".into(),
2418 ));
2419 }
2420
2421 let unsafe_names = get_cors_unsafe_header_names(&request.headers);
2425 let header_names_set: HashSet<&HeaderName> = HashSet::from_iter(header_names.iter());
2426 let header_names_contains_star = header_names
2427 .iter()
2428 .any(|header_name| header_name.as_str() == "*");
2429 for unsafe_name in unsafe_names.iter() {
2430 if !header_names_set.contains(unsafe_name) &&
2431 (request.credentials_mode == CredentialsMode::Include ||
2432 !header_names_contains_star)
2433 {
2434 return Response::network_error(NetworkError::Internal(
2435 "CORS headers check failed".into(),
2436 ));
2437 }
2438 }
2439
2440 let max_age: Option<Duration> = response
2443 .headers
2444 .typed_get::<AccessControlMaxAge>()
2445 .map(|acma| acma.into());
2446
2447 let max_age = max_age.unwrap_or(Duration::from_secs(5));
2449
2450 for method in &methods {
2461 cache.match_method_and_update(request, method.clone(), max_age);
2462 }
2463
2464 for header_name in &header_names {
2469 cache.match_header_and_update(request, header_name, max_age);
2470 }
2471
2472 return response;
2474 }
2475
2476 Response::network_error(NetworkError::Internal("CORS check failed".into()))
2478}
2479
2480fn cors_check(request: &Request, response: &Response) -> Result<(), ()> {
2482 let origin = response.headers.typed_get::<AccessControlAllowOrigin>();
2484
2485 let origin = origin.ok_or(())?;
2487
2488 if request.credentials_mode != CredentialsMode::Include &&
2490 origin == AccessControlAllowOrigin::ANY
2491 {
2492 return Ok(());
2493 }
2494
2495 let origin = match origin.origin() {
2497 Some(origin) => origin,
2498 None => return Err(()),
2500 };
2501
2502 match request.origin {
2503 Origin::Origin(ref o) if o.ascii_serialization() == origin.to_string().trim() => {},
2504 _ => return Err(()),
2505 }
2506
2507 if request.credentials_mode != CredentialsMode::Include {
2509 return Ok(());
2510 }
2511
2512 let credentials = response
2514 .headers
2515 .typed_get::<AccessControlAllowCredentials>();
2516
2517 if credentials.is_some() {
2519 return Ok(());
2520 }
2521
2522 Err(())
2524}
2525
2526fn has_credentials(url: &ServoUrl) -> bool {
2527 !url.username().is_empty() || url.password().is_some()
2528}
2529
2530fn is_no_store_cache(headers: &HeaderMap) -> bool {
2531 headers.contains_key(header::IF_MODIFIED_SINCE) |
2532 headers.contains_key(header::IF_NONE_MATCH) |
2533 headers.contains_key(header::IF_UNMODIFIED_SINCE) |
2534 headers.contains_key(header::IF_MATCH) |
2535 headers.contains_key(header::IF_RANGE)
2536}
2537
2538fn is_redirect_status(status: StatusCode) -> bool {
2540 matches!(
2541 status,
2542 StatusCode::MOVED_PERMANENTLY |
2543 StatusCode::FOUND |
2544 StatusCode::SEE_OTHER |
2545 StatusCode::TEMPORARY_REDIRECT |
2546 StatusCode::PERMANENT_REDIRECT
2547 )
2548}
2549
2550fn request_has_redirect_tainted_origin(request: &Request) -> bool {
2552 let Origin::Origin(request_origin) = &request.origin else {
2554 panic!("origin cannot be \"client\" at this point in time");
2555 };
2556
2557 let mut last_url = None;
2559
2560 for url in &request.url_list {
2562 let Some(last_url) = &mut last_url else {
2564 last_url = Some(url);
2565 continue;
2566 };
2567
2568 if url.origin() != last_url.origin() && *request_origin != last_url.origin() {
2571 return true;
2572 }
2573
2574 *last_url = url;
2576 }
2577
2578 false
2580}
2581
2582fn serialize_request_origin(request: &Request) -> headers::Origin {
2584 let Origin::Origin(origin) = &request.origin else {
2586 panic!("origin cannot be \"client\" at this point in time");
2587 };
2588
2589 if request_has_redirect_tainted_origin(request) {
2591 return headers::Origin::NULL;
2592 }
2593
2594 serialize_origin(origin)
2596}
2597
2598pub fn serialize_origin(origin: &ImmutableOrigin) -> headers::Origin {
2600 match origin {
2601 ImmutableOrigin::Opaque(_) => headers::Origin::NULL,
2602 ImmutableOrigin::Tuple(scheme, host, port) => {
2603 let port = match (scheme.as_ref(), port) {
2606 ("http" | "ws", 80) | ("https" | "wss", 443) | ("ftp", 21) => None,
2607 _ => Some(*port),
2608 };
2609
2610 headers::Origin::try_from_parts(scheme, &host.to_string(), port)
2612 .unwrap_or(headers::Origin::NULL)
2613 },
2614 }
2615}
2616
2617fn append_a_request_origin_header(request: &mut Request) {
2619 let Origin::Origin(request_origin) = &request.origin else {
2621 panic!("origin cannot be \"client\" at this point in time");
2622 };
2623
2624 let mut serialized_origin = serialize_request_origin(request);
2626
2627 if request.response_tainting == ResponseTainting::CorsTainting ||
2630 matches!(request.mode, RequestMode::WebSocket { .. })
2631 {
2632 request.headers.typed_insert(serialized_origin);
2633 }
2634 else if !matches!(request.method, Method::GET | Method::HEAD) {
2636 if request.mode != RequestMode::CorsMode {
2638 match request.referrer_policy {
2639 ReferrerPolicy::NoReferrer => {
2640 serialized_origin = headers::Origin::NULL;
2642 },
2643 ReferrerPolicy::NoReferrerWhenDowngrade |
2644 ReferrerPolicy::StrictOrigin |
2645 ReferrerPolicy::StrictOriginWhenCrossOrigin => {
2646 if let ImmutableOrigin::Tuple(scheme, _, _) = &request_origin {
2649 if scheme == "https" && request.current_url().scheme() != "https" {
2650 serialized_origin = headers::Origin::NULL;
2651 }
2652 }
2653 },
2654 ReferrerPolicy::SameOrigin => {
2655 if *request_origin != request.current_url().origin() {
2658 serialized_origin = headers::Origin::NULL;
2659 }
2660 },
2661 _ => {
2662 },
2664 };
2665 }
2666
2667 request.headers.typed_insert(serialized_origin);
2669 }
2670}
2671
2672fn append_the_fetch_metadata_headers(r: &mut Request) {
2674 if !r.url().is_potentially_trustworthy() {
2676 return;
2677 }
2678
2679 set_the_sec_fetch_dest_header(r);
2681
2682 set_the_sec_fetch_mode_header(r);
2684
2685 set_the_sec_fetch_site_header(r);
2687
2688 set_the_sec_fetch_user_header(r);
2690}
2691
2692fn set_the_sec_fetch_dest_header(r: &mut Request) {
2694 debug_assert!(r.url().is_potentially_trustworthy());
2696
2697 let header = r.destination;
2701
2702 r.headers.typed_insert(SecFetchDest(header));
2704}
2705
2706fn set_the_sec_fetch_mode_header(r: &mut Request) {
2708 debug_assert!(r.url().is_potentially_trustworthy());
2710
2711 let header = &r.mode;
2714
2715 r.headers.typed_insert(SecFetchMode::from(header));
2717}
2718
2719fn set_the_sec_fetch_site_header(r: &mut Request) {
2721 let Origin::Origin(request_origin) = &r.origin else {
2724 panic!("request origin cannot be \"client\" at this point")
2725 };
2726
2727 debug_assert!(r.url().is_potentially_trustworthy());
2729
2730 let mut header = SecFetchSite::SameOrigin;
2733
2734 if header != SecFetchSite::None {
2739 for url in &r.url_list {
2740 if url.origin() == *request_origin {
2742 continue;
2743 }
2744
2745 header = SecFetchSite::CrossSite;
2747
2748 if !is_same_site(request_origin, &url.origin()) {
2750 break;
2751 }
2752
2753 header = SecFetchSite::SameSite;
2755 }
2756 }
2757
2758 r.headers.typed_insert(header);
2760}
2761
2762fn set_the_sec_fetch_user_header(r: &mut Request) {
2764 debug_assert!(r.url().is_potentially_trustworthy());
2766
2767 if !r.is_navigation_request() {
2770 return;
2771 }
2772
2773 let header = SecFetchUser;
2776
2777 r.headers.typed_insert(header);
2779}
2780
2781fn set_requests_referrer_policy_on_redirect(request: &mut Request, response: &Response) {
2783 let referrer_policy: ReferrerPolicy = response
2786 .headers
2787 .typed_get::<headers::ReferrerPolicy>()
2788 .into();
2789
2790 if referrer_policy != ReferrerPolicy::EmptyString {
2792 request.referrer_policy = referrer_policy;
2793 }
2794}