1use std::collections::{HashMap, HashSet};
6use std::iter::FromIterator;
7use std::sync::{Arc as StdArc, Condvar, Mutex, RwLock};
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 hyper_util::client::legacy::Client;
41use ipc_channel::ipc::{self, IpcSender, IpcSharedMemory};
42use ipc_channel::router::ROUTER;
43use log::{debug, error, info, log_enabled, warn};
44use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
45use net_traits::http_status::HttpStatus;
46use net_traits::policy_container::RequestPolicyContainer;
47use net_traits::pub_domains::reg_suffix;
48use net_traits::request::Origin::Origin as SpecificOrigin;
49use net_traits::request::{
50 BodyChunkRequest, BodyChunkResponse, CacheMode, CredentialsMode, Destination, Initiator,
51 Origin, RedirectMode, Referrer, Request, RequestBuilder, RequestMode, ResponseTainting,
52 ServiceWorkersMode, TraversableForUserPrompts, get_cors_unsafe_header_names,
53 is_cors_non_wildcard_request_header_name, is_cors_safelisted_method,
54 is_cors_safelisted_request_header,
55};
56use net_traits::response::{CacheState, HttpsState, Response, ResponseBody, ResponseType};
57use net_traits::{
58 CookieSource, DOCUMENT_ACCEPT_HEADER_VALUE, FetchMetadata, NetworkError, RedirectEndValue,
59 RedirectStartValue, ReferrerPolicy, ResourceAttribute, ResourceFetchTiming, ResourceTimeValue,
60};
61use profile_traits::mem::{Report, ReportKind};
62use profile_traits::path;
63use rustc_hash::FxHashMap;
64use servo_arc::Arc;
65use servo_url::{Host, ImmutableOrigin, ServoUrl};
66use tokio::sync::mpsc::{
67 Receiver as TokioReceiver, Sender as TokioSender, UnboundedReceiver, UnboundedSender, channel,
68 unbounded_channel,
69};
70use tokio_stream::wrappers::ReceiverStream;
71
72use crate::async_runtime::spawn_task;
73use crate::connector::{CertificateErrorOverrideManager, Connector, create_tls_config};
74use crate::cookie::ServoCookie;
75use crate::cookie_storage::CookieStorage;
76use crate::decoder::Decoder;
77use crate::fetch::cors_cache::CorsCache;
78use crate::fetch::fetch_params::FetchParams;
79use crate::fetch::headers::{SecFetchDest, SecFetchMode, SecFetchSite, SecFetchUser};
80use crate::fetch::methods::{Data, DoneChannel, FetchContext, Target, main_fetch};
81use crate::hsts::HstsList;
82use crate::http_cache::{CacheKey, HttpCache};
83use crate::resource_thread::{AuthCache, AuthCacheEntry};
84use crate::websocket_loader::start_websocket;
85
86#[derive(Clone, Debug, Eq, PartialEq)]
88pub enum HttpCacheEntryState {
89 ReadyToConstruct,
93 PendingStore(usize),
95}
96
97type HttpCacheState = Mutex<HashMap<CacheKey, Arc<(Mutex<HttpCacheEntryState>, Condvar)>>>;
98
99pub struct HttpState {
100 pub hsts_list: RwLock<HstsList>,
101 pub cookie_jar: RwLock<CookieStorage>,
102 pub http_cache: RwLock<HttpCache>,
103 pub http_cache_state: HttpCacheState,
107 pub auth_cache: RwLock<AuthCache>,
108 pub history_states: RwLock<FxHashMap<HistoryStateId, Vec<u8>>>,
109 pub client: Client<Connector, crate::connector::BoxedBody>,
110 pub override_manager: CertificateErrorOverrideManager,
111 pub embedder_proxy: Mutex<EmbedderProxy>,
112}
113
114impl HttpState {
115 pub(crate) fn memory_reports(&self, suffix: &str, ops: &mut MallocSizeOfOps) -> Vec<Report> {
116 vec![
117 Report {
118 path: path!["memory-cache", suffix],
119 kind: ReportKind::ExplicitJemallocHeapSize,
120 size: self.http_cache.read().unwrap().size_of(ops),
121 },
122 Report {
123 path: path!["hsts-list", suffix],
124 kind: ReportKind::ExplicitJemallocHeapSize,
125 size: self.hsts_list.read().unwrap().size_of(ops),
126 },
127 ]
128 }
129
130 fn request_authentication(
131 &self,
132 request: &Request,
133 response: &Response,
134 ) -> Option<AuthenticationResponse> {
135 let webview_id = request.target_webview_id?;
137 let for_proxy = response.status == StatusCode::PROXY_AUTHENTICATION_REQUIRED;
138
139 if request.mode != RequestMode::Navigate {
141 return None;
142 }
143
144 let embedder_proxy = self.embedder_proxy.lock().unwrap();
145 let (ipc_sender, ipc_receiver) = generic_channel::channel().unwrap();
146 embedder_proxy.send(EmbedderMsg::RequestAuthentication(
147 webview_id,
148 request.url(),
149 for_proxy,
150 ipc_sender,
151 ));
152 ipc_receiver.recv().ok()?
153 }
154}
155
156pub(crate) fn set_default_accept(request: &mut Request) {
158 if request.headers.contains_key(header::ACCEPT) {
160 return;
161 }
162
163 let value = if request.initiator == Initiator::Prefetch {
165 DOCUMENT_ACCEPT_HEADER_VALUE
166 } else {
167 match request.destination {
170 Destination::Document | Destination::Frame | Destination::IFrame => {
171 DOCUMENT_ACCEPT_HEADER_VALUE
172 },
173 Destination::Image => {
174 HeaderValue::from_static("image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5")
175 },
176 Destination::Json => HeaderValue::from_static("application/json,*/*;q=0.5"),
177 Destination::Style => HeaderValue::from_static("text/css,*/*;q=0.1"),
178 _ => HeaderValue::from_static("*/*"),
180 }
181 };
182
183 request.headers.insert(header::ACCEPT, value);
185}
186
187fn set_default_accept_encoding(headers: &mut HeaderMap) {
188 if headers.contains_key(header::ACCEPT_ENCODING) {
189 return;
190 }
191
192 headers.insert(
194 header::ACCEPT_ENCODING,
195 HeaderValue::from_static("gzip, deflate, br, zstd"),
196 );
197}
198
199fn no_referrer_when_downgrade(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
201 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
203 return None;
204 }
205 strip_url_for_use_as_referrer(referrer_url, false)
207}
208
209fn strict_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
211 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
213 return None;
214 }
215 strip_url_for_use_as_referrer(referrer_url, true)
217}
218
219fn strict_origin_when_cross_origin(
221 referrer_url: ServoUrl,
222 current_url: ServoUrl,
223) -> Option<ServoUrl> {
224 if referrer_url.origin() == current_url.origin() {
226 return strip_url_for_use_as_referrer(referrer_url, false);
227 }
228 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
230 return None;
231 }
232 strip_url_for_use_as_referrer(referrer_url, true)
234}
235
236fn is_same_site(site_a: &ImmutableOrigin, site_b: &ImmutableOrigin) -> bool {
238 if !site_a.is_tuple() && !site_b.is_tuple() && site_a == site_b {
243 return true;
244 }
245
246 let ImmutableOrigin::Tuple(scheme_a, host_a, _) = site_a else {
248 return false;
249 };
250 let ImmutableOrigin::Tuple(scheme_b, host_b, _) = site_b else {
251 return false;
252 };
253
254 if scheme_a != scheme_b {
256 return false;
257 }
258
259 if let (Host::Domain(domain_a), Host::Domain(domain_b)) = (host_a, host_b) {
262 if reg_suffix(domain_a) != reg_suffix(domain_b) {
263 return false;
264 }
265 } else if host_a != host_b {
266 return false;
267 }
268
269 true
271}
272
273fn is_schemelessy_same_site(site_a: &ImmutableOrigin, site_b: &ImmutableOrigin) -> bool {
275 if !site_a.is_tuple() && !site_b.is_tuple() && site_a == site_b {
277 true
278 } else if site_a.is_tuple() && site_b.is_tuple() {
279 let host_a = site_a.host().map(|h| h.to_string()).unwrap_or_default();
281 let host_b = site_b.host().map(|h| h.to_string()).unwrap_or_default();
282
283 let host_a_reg = reg_suffix(&host_a);
284 let host_b_reg = reg_suffix(&host_b);
285
286 (site_a.host() == site_b.host() && host_a_reg.is_empty()) ||
288 (host_a_reg == host_b_reg && !host_a_reg.is_empty())
289 } else {
290 false
292 }
293}
294
295fn strip_url_for_use_as_referrer(mut url: ServoUrl, origin_only: bool) -> Option<ServoUrl> {
297 const MAX_REFERRER_URL_LENGTH: usize = 4096;
298 if url.is_local_scheme() {
300 return None;
301 }
302 {
304 let url = url.as_mut_url();
305 let _ = url.set_username("");
306 let _ = url.set_password(None);
307 url.set_fragment(None);
308 if origin_only || url.as_str().len() > MAX_REFERRER_URL_LENGTH {
312 url.set_path("");
313 url.set_query(None);
314 }
315 }
316 Some(url)
318}
319
320fn same_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
322 if referrer_url.origin() == current_url.origin() {
324 return strip_url_for_use_as_referrer(referrer_url, false);
325 }
326 None
328}
329
330fn origin_when_cross_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
332 if referrer_url.origin() == current_url.origin() {
334 return strip_url_for_use_as_referrer(referrer_url, false);
335 }
336 strip_url_for_use_as_referrer(referrer_url, true)
338}
339
340pub fn determine_requests_referrer(
342 referrer_policy: ReferrerPolicy,
343 referrer_source: ServoUrl,
344 current_url: ServoUrl,
345) -> Option<ServoUrl> {
346 match referrer_policy {
347 ReferrerPolicy::EmptyString | ReferrerPolicy::NoReferrer => None,
348 ReferrerPolicy::Origin => strip_url_for_use_as_referrer(referrer_source, true),
349 ReferrerPolicy::UnsafeUrl => strip_url_for_use_as_referrer(referrer_source, false),
350 ReferrerPolicy::StrictOrigin => strict_origin(referrer_source, current_url),
351 ReferrerPolicy::StrictOriginWhenCrossOrigin => {
352 strict_origin_when_cross_origin(referrer_source, current_url)
353 },
354 ReferrerPolicy::SameOrigin => same_origin(referrer_source, current_url),
355 ReferrerPolicy::OriginWhenCrossOrigin => {
356 origin_when_cross_origin(referrer_source, current_url)
357 },
358 ReferrerPolicy::NoReferrerWhenDowngrade => {
359 no_referrer_when_downgrade(referrer_source, current_url)
360 },
361 }
362}
363
364fn set_request_cookies(
365 url: &ServoUrl,
366 headers: &mut HeaderMap,
367 cookie_jar: &RwLock<CookieStorage>,
368) {
369 let mut cookie_jar = cookie_jar.write().unwrap();
370 cookie_jar.remove_expired_cookies_for_url(url);
371 if let Some(cookie_list) = cookie_jar.cookies_for_url(url, CookieSource::HTTP) {
372 headers.insert(
373 header::COOKIE,
374 HeaderValue::from_bytes(cookie_list.as_bytes()).unwrap(),
375 );
376 }
377}
378
379fn set_cookie_for_url(cookie_jar: &RwLock<CookieStorage>, request: &ServoUrl, cookie_val: &str) {
380 let mut cookie_jar = cookie_jar.write().unwrap();
381 let source = CookieSource::HTTP;
382
383 if let Some(cookie) = ServoCookie::from_cookie_string(cookie_val.into(), request, source) {
384 cookie_jar.push(cookie, request, source);
385 }
386}
387
388fn set_cookies_from_headers(
389 url: &ServoUrl,
390 headers: &HeaderMap,
391 cookie_jar: &RwLock<CookieStorage>,
392) {
393 for cookie in headers.get_all(header::SET_COOKIE) {
394 if let Ok(cookie_str) = std::str::from_utf8(cookie.as_bytes()) {
395 set_cookie_for_url(cookie_jar, url, cookie_str);
396 }
397 }
398}
399
400#[allow(clippy::too_many_arguments)]
401fn prepare_devtools_request(
402 request_id: String,
403 url: ServoUrl,
404 method: Method,
405 headers: HeaderMap,
406 body: Option<Vec<u8>>,
407 pipeline_id: PipelineId,
408 connect_time: Duration,
409 send_time: Duration,
410 destination: Destination,
411 is_xhr: bool,
412 browsing_context_id: BrowsingContextId,
413) -> ChromeToDevtoolsControlMsg {
414 let started_date_time = SystemTime::now();
415 let request = DevtoolsHttpRequest {
416 url,
417 method,
418 headers,
419 body,
420 pipeline_id,
421 started_date_time,
422 time_stamp: started_date_time
423 .duration_since(UNIX_EPOCH)
424 .unwrap_or_default()
425 .as_secs() as i64,
426 connect_time,
427 send_time,
428 destination,
429 is_xhr,
430 browsing_context_id,
431 };
432 let net_event = NetworkEvent::HttpRequestUpdate(request);
433
434 ChromeToDevtoolsControlMsg::NetworkEvent(request_id, net_event)
435}
436
437pub fn send_request_to_devtools(
438 msg: ChromeToDevtoolsControlMsg,
439 devtools_chan: &Sender<DevtoolsControlMsg>,
440) {
441 if matches!(msg, ChromeToDevtoolsControlMsg::NetworkEvent(_, ref network_event) if !network_event.forward_to_devtools())
442 {
443 return;
444 }
445 if let Err(e) = devtools_chan.send(DevtoolsControlMsg::FromChrome(msg)) {
446 error!("DevTools send failed: {e}");
447 }
448}
449
450pub fn send_response_to_devtools(
451 request: &Request,
452 context: &FetchContext,
453 response: &Response,
454 body_data: Option<Vec<u8>>,
455) {
456 let meta = match response.metadata() {
457 Ok(FetchMetadata::Unfiltered(m)) => m,
458 Ok(FetchMetadata::Filtered { unsafe_, .. }) => unsafe_,
459 Err(_) => {
460 log::warn!("No metadata available, skipping devtools response.");
461 return;
462 },
463 };
464 send_response_values_to_devtools(
465 meta.headers.map(Serde::into_inner),
466 meta.status,
467 body_data,
468 response.cache_state,
469 request,
470 context.devtools_chan.clone(),
471 );
472}
473
474#[allow(clippy::too_many_arguments)]
475pub fn send_response_values_to_devtools(
476 headers: Option<HeaderMap>,
477 status: HttpStatus,
478 body: Option<Vec<u8>>,
479 cache_state: CacheState,
480 request: &Request,
481 devtools_chan: Option<StdArc<Mutex<Sender<DevtoolsControlMsg>>>>,
482) {
483 if let (Some(devtools_chan), Some(pipeline_id), Some(webview_id)) = (
484 devtools_chan,
485 request.pipeline_id,
486 request.target_webview_id,
487 ) {
488 let browsing_context_id = webview_id.into();
489 let from_cache = matches!(cache_state, CacheState::Local | CacheState::Validated);
490
491 let devtoolsresponse = DevtoolsHttpResponse {
492 headers,
493 status,
494 body,
495 from_cache,
496 pipeline_id,
497 browsing_context_id,
498 };
499 let net_event_response = NetworkEvent::HttpResponse(devtoolsresponse);
500
501 let msg =
502 ChromeToDevtoolsControlMsg::NetworkEvent(request.id.0.to_string(), net_event_response);
503
504 let _ = devtools_chan
505 .lock()
506 .unwrap()
507 .send(DevtoolsControlMsg::FromChrome(msg));
508 }
509}
510
511pub fn send_early_httprequest_to_devtools(request: &Request, context: &FetchContext) {
512 if request.url().scheme() == "data" {
514 return;
515 }
516 if let (Some(devtools_chan), Some(browsing_context_id), Some(pipeline_id)) = (
517 context.devtools_chan.as_ref(),
518 request.target_webview_id.map(|id| id.into()),
519 request.pipeline_id,
520 ) {
521 let devtools_request = DevtoolsHttpRequest {
523 url: request.current_url().clone(),
524 method: request.method.clone(),
525 headers: request.headers.clone(),
526 body: None,
527 pipeline_id,
528 started_date_time: SystemTime::now(),
529 time_stamp: 0,
530 connect_time: Duration::from_millis(0),
531 send_time: Duration::from_millis(0),
532 destination: request.destination,
533 is_xhr: false,
534 browsing_context_id,
535 };
536
537 let msg = ChromeToDevtoolsControlMsg::NetworkEvent(
538 request.id.0.to_string(),
539 NetworkEvent::HttpRequest(devtools_request),
540 );
541
542 send_request_to_devtools(msg, &devtools_chan.lock().unwrap());
543 }
544}
545
546fn auth_from_cache(
547 auth_cache: &RwLock<AuthCache>,
548 origin: &ImmutableOrigin,
549) -> Option<Authorization<Basic>> {
550 if let Some(auth_entry) = auth_cache
551 .read()
552 .unwrap()
553 .entries
554 .get(&origin.ascii_serialization())
555 {
556 let user_name = &auth_entry.user_name;
557 let password = &auth_entry.password;
558 Some(Authorization::basic(user_name, password))
559 } else {
560 None
561 }
562}
563
564enum BodyChunk {
567 Chunk(IpcSharedMemory),
569 Done,
571}
572
573enum BodyStream {
575 Chunked(TokioReceiver<Result<Frame<Bytes>, hyper::Error>>),
578 Buffered(UnboundedReceiver<BodyChunk>),
581}
582
583enum BodySink {
586 Chunked(TokioSender<Result<Frame<Bytes>, hyper::Error>>),
588 Buffered(UnboundedSender<BodyChunk>),
592}
593
594impl BodySink {
595 fn transmit_bytes(&self, bytes: IpcSharedMemory) {
596 match self {
597 BodySink::Chunked(sender) => {
598 let sender = sender.clone();
599 spawn_task(async move {
600 let _ = sender
601 .send(Ok(Frame::data(Bytes::copy_from_slice(&bytes))))
602 .await;
603 });
604 },
605 BodySink::Buffered(sender) => {
606 let _ = sender.send(BodyChunk::Chunk(bytes));
607 },
608 }
609 }
610
611 fn close(&self) {
612 match self {
613 BodySink::Chunked(_) => { },
614 BodySink::Buffered(sender) => {
615 let _ = sender.send(BodyChunk::Done);
616 },
617 }
618 }
619}
620
621#[allow(clippy::too_many_arguments)]
622async fn obtain_response(
623 client: &Client<Connector, crate::connector::BoxedBody>,
624 url: &ServoUrl,
625 method: &Method,
626 request_headers: &mut HeaderMap,
627 body: Option<StdArc<Mutex<IpcSender<BodyChunkRequest>>>>,
628 source_is_null: bool,
629 pipeline_id: &Option<PipelineId>,
630 request_id: Option<&str>,
631 destination: Destination,
632 is_xhr: bool,
633 context: &FetchContext,
634 fetch_terminated: UnboundedSender<bool>,
635 browsing_context_id: Option<BrowsingContextId>,
636) -> Result<(HyperResponse<Decoder>, Option<ChromeToDevtoolsControlMsg>), NetworkError> {
637 {
638 let mut headers = request_headers.clone();
639
640 let devtools_bytes = StdArc::new(Mutex::new(vec![]));
641
642 let encoded_url = url
644 .clone()
645 .into_url()
646 .as_ref()
647 .replace('|', "%7C")
648 .replace('{', "%7B")
649 .replace('}', "%7D");
650
651 let request = if let Some(chunk_requester) = body {
652 let (sink, stream) = if source_is_null {
653 headers.insert(TRANSFER_ENCODING, HeaderValue::from_static("chunked"));
656
657 let (sender, receiver) = channel(1);
658 (BodySink::Chunked(sender), BodyStream::Chunked(receiver))
659 } else {
660 let (sender, receiver) = unbounded_channel();
667 (BodySink::Buffered(sender), BodyStream::Buffered(receiver))
668 };
669
670 let (body_chan, body_port) = ipc::channel().unwrap();
671
672 if let Ok(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().unwrap().extend_from_slice(&bytes);
708
709 sink.transmit_bytes(bytes);
712
713 let _ = chunk_requester2
716 .lock()
717 .unwrap()
718 .send(BodyChunkRequest::Chunk);
719 }),
720 );
721
722 let body = match stream {
723 BodyStream::Chunked(receiver) => {
724 let stream = ReceiverStream::new(receiver);
725 BoxBody::new(http_body_util::StreamBody::new(stream))
726 },
727 BodyStream::Buffered(mut receiver) => {
728 let mut body = vec![];
730 loop {
731 match receiver.recv().await {
732 Some(BodyChunk::Chunk(bytes)) => {
733 body.extend_from_slice(&bytes);
734 },
735 Some(BodyChunk::Done) => break,
736 None => warn!("Failed to read all chunks from request body."),
737 }
738 }
739 Full::new(body.into()).map_err(|_| unreachable!()).boxed()
740 },
741 };
742 HyperRequest::builder()
743 .method(method)
744 .uri(encoded_url)
745 .body(body)
746 } else {
747 HyperRequest::builder()
748 .method(method)
749 .uri(encoded_url)
750 .body(
751 http_body_util::Empty::new()
752 .map_err(|_| unreachable!())
753 .boxed(),
754 )
755 };
756
757 context
758 .timing
759 .lock()
760 .unwrap()
761 .set_attribute(ResourceAttribute::DomainLookupStart);
762
763 let connect_start = CrossProcessInstant::now();
766 context
767 .timing
768 .lock()
769 .unwrap()
770 .set_attribute(ResourceAttribute::ConnectStart(connect_start));
771
772 if url.scheme() == "https" {
776 context
777 .timing
778 .lock()
779 .unwrap()
780 .set_attribute(ResourceAttribute::SecureConnectionStart);
781 }
782
783 let mut request = match request {
784 Ok(request) => request,
785 Err(e) => return Err(NetworkError::from_http_error(&e)),
786 };
787 *request.headers_mut() = headers.clone();
788
789 let connect_end = CrossProcessInstant::now();
790 context
791 .timing
792 .lock()
793 .unwrap()
794 .set_attribute(ResourceAttribute::ConnectEnd(connect_end));
795
796 let request_id = request_id.map(|v| v.to_owned());
797 let pipeline_id = *pipeline_id;
798 let closure_url = url.clone();
799 let method = method.clone();
800 let send_start = CrossProcessInstant::now();
801
802 let host = request.uri().host().unwrap_or("").to_owned();
803 let override_manager = context.state.override_manager.clone();
804 let headers = headers.clone();
805 let is_secure_scheme = url.is_secure_scheme();
806
807 client
808 .request(request)
809 .and_then(move |res| {
810 let send_end = CrossProcessInstant::now();
811
812 let msg = if let Some(request_id) = request_id {
815 if let Some(pipeline_id) = pipeline_id {
816 if let Some(browsing_context_id) = browsing_context_id {
817 Some(prepare_devtools_request(
818 request_id,
819 closure_url,
820 method.clone(),
821 headers,
822 Some(devtools_bytes.lock().unwrap().clone()),
823 pipeline_id,
824 (connect_end - connect_start).unsigned_abs(),
825 (send_end - send_start).unsigned_abs(),
826 destination,
827 is_xhr,
828 browsing_context_id,
829 ))
830 } else {
831 debug!("Not notifying devtools (no browsing_context_id)");
832 None
833 }
834 } else {
839 debug!("Not notifying devtools (no pipeline_id)");
840 None
841 }
842 } else {
843 debug!("Not notifying devtools (no request_id)");
844 None
845 };
846
847 future::ready(Ok((
848 Decoder::detect(res.map(|r| r.boxed()), is_secure_scheme),
849 msg,
850 )))
851 })
852 .map_err(move |error| {
853 warn!("network error: {error:?}");
854 NetworkError::from_hyper_error(
855 &error,
856 override_manager.remove_certificate_failing_verification(host.as_str()),
857 )
858 })
859 .await
860 }
861}
862
863#[async_recursion]
865#[allow(clippy::too_many_arguments)]
866pub async fn http_fetch(
867 fetch_params: &mut FetchParams,
868 cache: &mut CorsCache,
869 cors_flag: bool,
870 cors_preflight_flag: bool,
871 authentication_fetch_flag: bool,
872 target: Target<'async_recursion>,
873 done_chan: &mut DoneChannel,
874 context: &FetchContext,
875) -> Response {
876 *done_chan = None;
878 let request = &mut fetch_params.request;
880
881 let mut response: Option<Response> = None;
884
885 if request.service_workers_mode == ServiceWorkersMode::All {
887 if let Some(ref res) = response {
892 if (res.response_type == ResponseType::Opaque && request.mode != RequestMode::NoCors) ||
900 (res.response_type == ResponseType::OpaqueRedirect &&
901 request.redirect_mode != RedirectMode::Manual) ||
902 (res.url_list.len() > 1 && request.redirect_mode != RedirectMode::Follow) ||
903 res.is_network_error()
904 {
905 return Response::network_error(NetworkError::Internal("Request failed".into()));
906 }
907
908 }
911 }
912
913 if response.is_none() {
915 if cors_preflight_flag {
917 let method_cache_match = cache.match_method(request, request.method.clone());
918
919 let method_mismatch = !method_cache_match &&
920 (!is_cors_safelisted_method(&request.method) || request.use_cors_preflight);
921 let header_mismatch = request.headers.iter().any(|(name, value)| {
922 !cache.match_header(request, name) &&
923 !is_cors_safelisted_request_header(&name, &value)
924 });
925
926 if method_mismatch || header_mismatch {
928 let preflight_result = cors_preflight_fetch(request, cache, context).await;
929 if let Some(e) = preflight_result.get_network_error() {
931 return Response::network_error(e.clone());
932 }
933 }
934 }
935
936 if request.redirect_mode == RedirectMode::Follow {
938 request.service_workers_mode = ServiceWorkersMode::None;
939 }
940
941 context
945 .timing
946 .lock()
947 .unwrap()
948 .set_attribute(ResourceAttribute::RequestStart);
949
950 let mut fetch_result = http_network_or_cache_fetch(
951 fetch_params,
952 authentication_fetch_flag,
953 cors_flag,
954 done_chan,
955 context,
956 )
957 .await;
958
959 if cors_flag && cors_check(&fetch_params.request, &fetch_result).is_err() {
961 return Response::network_error(NetworkError::Internal("CORS check failed".into()));
962 }
963
964 fetch_result.return_internal = false;
965 response = Some(fetch_result);
966 }
967
968 let request = &mut fetch_params.request;
969
970 let mut response = response.unwrap();
972
973 if response
977 .actual_response()
978 .status
979 .try_code()
980 .is_some_and(is_redirect_status)
981 {
982 if response.actual_response().status != StatusCode::SEE_OTHER {
984 }
986
987 let mut location = response
989 .actual_response()
990 .headers
991 .get(header::LOCATION)
992 .and_then(|v| {
993 HeaderValue::to_str(v)
994 .map(|l| {
995 ServoUrl::parse_with_base(response.actual_response().url(), l)
996 .map_err(|err| err.to_string())
997 })
998 .ok()
999 });
1000
1001 if let Some(Ok(ref mut location)) = location {
1003 if location.fragment().is_none() {
1004 let current_url = request.current_url();
1005 location.set_fragment(current_url.fragment());
1006 }
1007 }
1008 response.actual_response_mut().location_url = location;
1009
1010 response = match request.redirect_mode {
1012 RedirectMode::Error => {
1013 Response::network_error(NetworkError::Internal("Redirect mode error".into()))
1014 },
1015 RedirectMode::Manual => response.to_filtered(ResponseType::OpaqueRedirect),
1016 RedirectMode::Follow => {
1017 response.return_internal = true;
1019 http_redirect_fetch(
1020 fetch_params,
1021 cache,
1022 response,
1023 cors_flag,
1024 target,
1025 done_chan,
1026 context,
1027 )
1028 .await
1029 },
1030 };
1031 }
1032
1033 response.return_internal = true;
1035 context
1036 .timing
1037 .lock()
1038 .unwrap()
1039 .set_attribute(ResourceAttribute::RedirectCount(
1040 fetch_params.request.redirect_count as u16,
1041 ));
1042
1043 response.resource_timing = Arc::clone(&context.timing);
1044
1045 response
1047}
1048
1049struct RedirectEndTimer(Option<Arc<Mutex<ResourceFetchTiming>>>);
1051
1052impl RedirectEndTimer {
1053 fn neuter(&mut self) {
1054 self.0 = None;
1055 }
1056}
1057
1058impl Drop for RedirectEndTimer {
1059 fn drop(&mut self) {
1060 let RedirectEndTimer(resource_fetch_timing_opt) = self;
1061
1062 resource_fetch_timing_opt.as_ref().map_or((), |t| {
1063 t.lock()
1064 .unwrap()
1065 .set_attribute(ResourceAttribute::RedirectEnd(RedirectEndValue::Zero));
1066 })
1067 }
1068}
1069
1070#[async_recursion]
1072pub async fn http_redirect_fetch(
1073 fetch_params: &mut FetchParams,
1074 cache: &mut CorsCache,
1075 response: Response,
1076 cors_flag: bool,
1077 target: Target<'async_recursion>,
1078 done_chan: &mut DoneChannel,
1079 context: &FetchContext,
1080) -> Response {
1081 let mut redirect_end_timer = RedirectEndTimer(Some(context.timing.clone()));
1082
1083 let request = &mut fetch_params.request;
1085
1086 assert!(response.return_internal);
1087
1088 let location_url = response.actual_response().location_url.clone();
1089 let location_url = match location_url {
1090 None => return response,
1092 Some(Err(err)) => {
1094 return Response::network_error(NetworkError::Internal(
1095 "Location URL parse failure: ".to_owned() + &err,
1096 ));
1097 },
1098 Some(Ok(ref url)) if !matches!(url.scheme(), "http" | "https") => {
1100 return Response::network_error(NetworkError::Internal(
1101 "Location URL not an HTTP(S) scheme".into(),
1102 ));
1103 },
1104 Some(Ok(url)) => url,
1105 };
1106
1107 context
1110 .timing
1111 .lock()
1112 .unwrap()
1113 .set_attribute(ResourceAttribute::RedirectStart(
1114 RedirectStartValue::FetchStart,
1115 ));
1116
1117 context
1118 .timing
1119 .lock()
1120 .unwrap()
1121 .set_attribute(ResourceAttribute::FetchStart);
1122
1123 context
1125 .timing
1126 .lock()
1127 .unwrap()
1128 .set_attribute(ResourceAttribute::StartTime(ResourceTimeValue::FetchStart));
1129
1130 context
1131 .timing
1132 .lock()
1133 .unwrap()
1134 .set_attribute(ResourceAttribute::StartTime(
1135 ResourceTimeValue::RedirectStart,
1136 )); if request.redirect_count >= 20 {
1140 return Response::network_error(NetworkError::Internal("Too many redirects".into()));
1141 }
1142
1143 request.redirect_count += 1;
1145
1146 let same_origin = match request.origin {
1148 Origin::Origin(ref origin) => *origin == location_url.origin(),
1149 Origin::Client => panic!(
1150 "Request origin should not be client for {}",
1151 request.current_url()
1152 ),
1153 };
1154
1155 let has_credentials = has_credentials(&location_url);
1156
1157 if request.mode == RequestMode::CorsMode && !same_origin && has_credentials {
1158 return Response::network_error(NetworkError::Internal(
1159 "Cross-origin credentials check failed".into(),
1160 ));
1161 }
1162
1163 if cors_flag && location_url.origin() != request.current_url().origin() {
1165 request.origin = Origin::Origin(ImmutableOrigin::new_opaque());
1166 }
1167
1168 if cors_flag && has_credentials {
1170 return Response::network_error(NetworkError::Internal("Credentials check failed".into()));
1171 }
1172
1173 if response.actual_response().status != StatusCode::SEE_OTHER &&
1176 request.body.as_ref().is_some_and(|b| b.source_is_null())
1177 {
1178 return Response::network_error(NetworkError::Internal("Request body is not done".into()));
1179 }
1180
1181 if response
1183 .actual_response()
1184 .status
1185 .try_code()
1186 .is_some_and(|code| {
1187 ((code == StatusCode::MOVED_PERMANENTLY || code == StatusCode::FOUND) &&
1188 request.method == Method::POST) ||
1189 (code == StatusCode::SEE_OTHER &&
1190 request.method != Method::HEAD &&
1191 request.method != Method::GET)
1192 })
1193 {
1194 request.method = Method::GET;
1196 request.body = None;
1197 for name in &[
1199 CONTENT_ENCODING,
1200 CONTENT_LANGUAGE,
1201 CONTENT_LOCATION,
1202 CONTENT_TYPE,
1203 ] {
1204 request.headers.remove(name);
1205 }
1206 }
1207
1208 if location_url.origin() != request.current_url().origin() {
1212 request.headers.remove(AUTHORIZATION);
1215 }
1216
1217 if let Some(body) = request.body.as_mut() {
1220 body.extract_source();
1221 }
1222
1223 request.url_list.push(location_url);
1227
1228 set_requests_referrer_policy_on_redirect(request, response.actual_response());
1230
1231 let recursive_flag = request.redirect_mode != RedirectMode::Manual;
1234
1235 let fetch_response = main_fetch(
1237 fetch_params,
1238 cache,
1239 recursive_flag,
1240 target,
1241 done_chan,
1242 context,
1243 )
1244 .await;
1245
1246 context
1248 .timing
1249 .lock()
1250 .unwrap()
1251 .set_attribute(ResourceAttribute::RedirectEnd(
1252 RedirectEndValue::ResponseEnd,
1253 ));
1254 redirect_end_timer.neuter();
1255
1256 fetch_response
1257}
1258
1259#[async_recursion]
1261async fn http_network_or_cache_fetch(
1262 fetch_params: &mut FetchParams,
1263 authentication_fetch_flag: bool,
1264 cors_flag: bool,
1265 done_chan: &mut DoneChannel,
1266 context: &FetchContext,
1267) -> Response {
1268 let request = &mut fetch_params.request;
1270
1271 let http_fetch_params: &mut FetchParams;
1273 let mut fetch_params_copy: FetchParams;
1274
1275 let mut response: Option<Response> = None;
1279
1280 let mut revalidating_flag = false;
1282
1283 let http_request = if request.traversable_for_user_prompts ==
1287 TraversableForUserPrompts::NoTraversable &&
1288 request.redirect_mode == RedirectMode::Error
1289 {
1290 http_fetch_params = fetch_params;
1291 &mut http_fetch_params.request
1292 }
1293 else {
1295 fetch_params_copy = fetch_params.clone();
1298 http_fetch_params = &mut fetch_params_copy;
1299
1300 &mut http_fetch_params.request
1301 };
1302
1303 let include_credentials = match http_request.credentials_mode {
1305 CredentialsMode::Include => true,
1307 CredentialsMode::CredentialsSameOrigin
1309 if http_request.response_tainting == ResponseTainting::Basic =>
1310 {
1311 true
1312 },
1313 _ => false,
1314 };
1315
1316 let content_length = http_request
1323 .body
1324 .as_ref()
1325 .and_then(|body| body.len().map(|size| size as u64));
1326
1327 let mut content_length_header_value = None;
1329
1330 if http_request.body.is_none() && matches!(http_request.method, Method::POST | Method::PUT) {
1333 content_length_header_value = Some(0);
1334 }
1335
1336 if let Some(content_length) = content_length {
1340 content_length_header_value = Some(content_length);
1341 };
1342
1343 if let Some(content_length_header_value) = content_length_header_value {
1346 http_request
1347 .headers
1348 .typed_insert(ContentLength(content_length_header_value));
1349 }
1350
1351 if content_length.is_some() && http_request.keep_alive {
1353 }
1355
1356 match http_request.referrer {
1358 Referrer::ReferrerUrl(ref http_request_referrer) |
1359 Referrer::Client(ref http_request_referrer) => {
1360 if let Ok(referer) = http_request_referrer.to_string().parse::<Referer>() {
1363 http_request.headers.typed_insert(referer);
1365 } else {
1366 error!("Failed to parse {} as referrer", http_request_referrer);
1370 }
1371 },
1372 _ => {},
1373 };
1374
1375 append_a_request_origin_header(http_request);
1377
1378 append_the_fetch_metadata_headers(http_request);
1380
1381 if http_request.initiator == Initiator::Prefetch {
1384 if let Ok(value) = HeaderValue::from_str("prefetch") {
1385 http_request.headers.insert("Sec-Purpose", value);
1386 }
1387 }
1388
1389 if !http_request.headers.contains_key(header::USER_AGENT) {
1392 http_request
1393 .headers
1394 .typed_insert::<UserAgent>(context.user_agent.parse().unwrap());
1395 }
1396
1397 match http_request.cache_mode {
1399 CacheMode::Default if is_no_store_cache(&http_request.headers) => {
1403 http_request.cache_mode = CacheMode::NoStore;
1404 },
1405
1406 CacheMode::NoCache if !http_request.headers.contains_key(header::CACHE_CONTROL) => {
1416 http_request
1417 .headers
1418 .typed_insert(CacheControl::new().with_max_age(Duration::from_secs(0)));
1419 },
1420
1421 CacheMode::Reload | CacheMode::NoStore => {
1423 if !http_request.headers.contains_key(header::PRAGMA) {
1426 http_request.headers.typed_insert(Pragma::no_cache());
1427 }
1428
1429 if !http_request.headers.contains_key(header::CACHE_CONTROL) {
1432 http_request
1433 .headers
1434 .typed_insert(CacheControl::new().with_no_cache());
1435 }
1436 },
1437
1438 _ => {},
1439 }
1440
1441 if http_request.headers.contains_key(header::RANGE) {
1444 if let Ok(value) = HeaderValue::from_str("identity") {
1445 http_request.headers.insert("Accept-Encoding", value);
1446 }
1447 }
1448
1449 http_request.headers.remove(header::HOST);
1453 set_default_accept_encoding(&mut http_request.headers);
1455
1456 let current_url = http_request.current_url();
1457
1458 if include_credentials {
1461 set_request_cookies(
1465 ¤t_url,
1466 &mut http_request.headers,
1467 &context.state.cookie_jar,
1468 );
1469 if !http_request.headers.contains_key(header::AUTHORIZATION) {
1471 let mut authorization_value = None;
1473
1474 if let Some(basic) = auth_from_cache(&context.state.auth_cache, ¤t_url.origin()) {
1476 if !http_request.use_url_credentials || !has_credentials(¤t_url) {
1477 authorization_value = Some(basic);
1478 }
1479 }
1480
1481 if authentication_fetch_flag &&
1483 authorization_value.is_none() &&
1484 has_credentials(¤t_url)
1485 {
1486 authorization_value = Some(Authorization::basic(
1487 current_url.username(),
1488 current_url.password().unwrap_or(""),
1489 ));
1490 }
1491
1492 if let Some(basic) = authorization_value {
1494 http_request.headers.typed_insert(basic);
1495 }
1496 }
1497 }
1498
1499 {
1509 let (lock, cvar) = {
1510 let entry_key = CacheKey::new(http_request);
1511 let mut state_map = context.state.http_cache_state.lock().unwrap();
1512 &*state_map
1513 .entry(entry_key)
1514 .or_insert_with(|| {
1515 Arc::new((
1516 Mutex::new(HttpCacheEntryState::ReadyToConstruct),
1517 Condvar::new(),
1518 ))
1519 })
1520 .clone()
1521 };
1522
1523 let mut state = lock.lock().unwrap();
1525 while let HttpCacheEntryState::PendingStore(_) = *state {
1526 let (current_state, time_out) = cvar
1527 .wait_timeout(state, Duration::from_millis(500))
1528 .unwrap();
1529 state = current_state;
1530 if time_out.timed_out() {
1531 break;
1533 }
1534 }
1535
1536 if let Ok(http_cache) = context.state.http_cache.read() {
1539 let stored_response = http_cache.construct_response(http_request, done_chan);
1543
1544 if let Some(response_from_cache) = stored_response {
1546 let response_headers = response_from_cache.response.headers.clone();
1547 let (cached_response, needs_revalidation) =
1549 match (http_request.cache_mode, &http_request.mode) {
1550 (CacheMode::ForceCache, _) => (Some(response_from_cache.response), false),
1551 (CacheMode::OnlyIfCached, &RequestMode::SameOrigin) => {
1552 (Some(response_from_cache.response), false)
1553 },
1554 (CacheMode::OnlyIfCached, _) |
1555 (CacheMode::NoStore, _) |
1556 (CacheMode::Reload, _) => (None, false),
1557 (_, _) => (
1558 Some(response_from_cache.response),
1559 response_from_cache.needs_validation,
1560 ),
1561 };
1562
1563 if needs_revalidation {
1564 revalidating_flag = true;
1565 if let Some(http_date) = response_headers.typed_get::<LastModified>() {
1567 let http_date: SystemTime = http_date.into();
1568 http_request
1569 .headers
1570 .typed_insert(IfModifiedSince::from(http_date));
1571 }
1572 if let Some(entity_tag) = response_headers.get(header::ETAG) {
1573 http_request
1574 .headers
1575 .insert(header::IF_NONE_MATCH, entity_tag.clone());
1576 }
1577 } else {
1578 response = cached_response;
1580 if let Some(response) = &mut response {
1581 response.cache_state = CacheState::Local;
1582 }
1583 }
1584 if response.is_none() {
1585 *done_chan = None;
1588
1589 if let HttpCacheEntryState::PendingStore(i) = *state {
1592 let new = i + 1;
1593 *state = HttpCacheEntryState::PendingStore(new);
1594 } else {
1595 *state = HttpCacheEntryState::PendingStore(1);
1596 }
1597 }
1598 }
1599 }
1600 if *state == HttpCacheEntryState::ReadyToConstruct {
1602 cvar.notify_one();
1603 }
1604 }
1606
1607 fn update_http_cache_state(context: &FetchContext, http_request: &Request) {
1611 let (lock, cvar) = {
1612 let entry_key = CacheKey::new(http_request);
1613 let mut state_map = context.state.http_cache_state.lock().unwrap();
1614 &*state_map
1615 .get_mut(&entry_key)
1616 .expect("Entry in http-cache state to have been previously inserted")
1617 .clone()
1618 };
1619 let mut state = lock.lock().unwrap();
1620 if let HttpCacheEntryState::PendingStore(i) = *state {
1621 let new = i - 1;
1622 if new == 0 {
1623 *state = HttpCacheEntryState::ReadyToConstruct;
1624 cvar.notify_one();
1626 } else {
1627 *state = HttpCacheEntryState::PendingStore(new);
1628 }
1629 }
1630 }
1631
1632 async fn wait_for_cached_response(
1633 done_chan: &mut DoneChannel,
1634 response: &mut Option<Response>,
1635 ) {
1636 if let Some(ref mut ch) = *done_chan {
1637 assert!(response.is_some());
1641
1642 loop {
1643 match ch.1.recv().await {
1644 Some(Data::Payload(_)) => {},
1645 Some(Data::Done) => break, Some(Data::Cancelled) => {
1647 *response = None;
1650 break;
1651 },
1652 _ => panic!("HTTP cache should always send Done or Cancelled"),
1653 }
1654 }
1655 }
1656 *done_chan = None;
1658 }
1659
1660 wait_for_cached_response(done_chan, &mut response).await;
1661
1662 if response.is_none() {
1666 if http_request.cache_mode == CacheMode::OnlyIfCached {
1668 update_http_cache_state(context, http_request);
1671 return Response::network_error(NetworkError::Internal(
1672 "Couldn't find response in cache".into(),
1673 ));
1674 }
1675
1676 let forward_response =
1679 http_network_fetch(http_fetch_params, include_credentials, done_chan, context).await;
1680
1681 let http_request = &mut http_fetch_params.request;
1682 if forward_response.status.in_range(200..=399) && !http_request.method.is_safe() {
1686 if let Ok(mut http_cache) = context.state.http_cache.write() {
1687 http_cache.invalidate(http_request, &forward_response);
1688 }
1689 }
1690
1691 if revalidating_flag && forward_response.status == StatusCode::NOT_MODIFIED {
1693 if let Ok(mut http_cache) = context.state.http_cache.write() {
1694 *done_chan = None;
1697 response = http_cache.refresh(http_request, forward_response.clone(), done_chan);
1698 }
1699 wait_for_cached_response(done_chan, &mut response).await;
1700 if let Some(response) = &mut response {
1701 response.cache_state = CacheState::Validated;
1702 }
1703 }
1704
1705 if response.is_none() {
1707 let forward_response = response.insert(forward_response);
1709
1710 if http_request.cache_mode != CacheMode::NoStore {
1713 if let Ok(mut http_cache) = context.state.http_cache.write() {
1716 http_cache.store(http_request, forward_response);
1717 }
1718 }
1719 }
1720 }
1721
1722 let http_request = &mut http_fetch_params.request;
1723 update_http_cache_state(context, http_request);
1725
1726 let mut response = response.unwrap();
1727
1728 if http_request.response_tainting != ResponseTainting::CorsTainting &&
1731 cross_origin_resource_policy_check(http_request, &response) ==
1732 CrossOriginResourcePolicy::Blocked
1733 {
1734 return Response::network_error(NetworkError::Internal(
1735 "Cross-origin resource policy check failed".into(),
1736 ));
1737 }
1738
1739 if http_request.headers.contains_key(RANGE) {
1743 response.range_requested = true;
1744 }
1745
1746 if response.status.try_code() == Some(StatusCode::UNAUTHORIZED) &&
1754 !cors_flag &&
1755 include_credentials &&
1756 response.headers.contains_key(WWW_AUTHENTICATE)
1757 {
1758 let request = &mut fetch_params.request;
1761
1762 if request.body.is_some() {
1764 }
1766
1767 if !request.use_url_credentials || authentication_fetch_flag {
1769 let Some(credentials) = context.state.request_authentication(request, &response) else {
1770 return response;
1771 };
1772
1773 if let Err(err) = request
1774 .current_url_mut()
1775 .set_username(&credentials.username)
1776 {
1777 error!("error setting username for url: {:?}", err);
1778 return response;
1779 };
1780
1781 if let Err(err) = request
1782 .current_url_mut()
1783 .set_password(Some(&credentials.password))
1784 {
1785 error!("error setting password for url: {:?}", err);
1786 return response;
1787 };
1788 }
1789
1790 *done_chan = None;
1793
1794 response = http_network_or_cache_fetch(
1796 fetch_params,
1797 true, cors_flag,
1799 done_chan,
1800 context,
1801 )
1802 .await;
1803 }
1804
1805 if response.status == StatusCode::PROXY_AUTHENTICATION_REQUIRED {
1807 let request = &mut fetch_params.request;
1808 if request.traversable_for_user_prompts == TraversableForUserPrompts::NoTraversable {
1811 return Response::network_error(NetworkError::Internal(
1812 "Can't find Window object".into(),
1813 ));
1814 }
1815
1816 let Some(credentials) = context.state.request_authentication(request, &response) else {
1824 return response;
1825 };
1826
1827 let entry = AuthCacheEntry {
1829 user_name: credentials.username,
1830 password: credentials.password,
1831 };
1832 {
1833 let mut auth_cache = context.state.auth_cache.write().unwrap();
1834 let key = request.current_url().origin().ascii_serialization();
1835 auth_cache.entries.insert(key, entry);
1836 }
1837
1838 *done_chan = None;
1841
1842 response = http_network_or_cache_fetch(
1844 fetch_params,
1845 false, cors_flag,
1847 done_chan,
1848 context,
1849 )
1850 .await;
1851 }
1852
1853 if authentication_fetch_flag {
1861 }
1863
1864 response
1866}
1867
1868#[derive(PartialEq)]
1872enum CrossOriginResourcePolicy {
1873 Allowed,
1874 Blocked,
1875}
1876
1877fn cross_origin_resource_policy_check(
1880 request: &Request,
1881 response: &Response,
1882) -> CrossOriginResourcePolicy {
1883 if request.mode != RequestMode::NoCors {
1885 return CrossOriginResourcePolicy::Allowed;
1886 }
1887
1888 let current_url_origin = request.current_url().origin();
1890 let same_origin = if let Origin::Origin(ref origin) = request.origin {
1891 *origin == request.current_url().origin()
1892 } else {
1893 false
1894 };
1895
1896 if same_origin {
1897 return CrossOriginResourcePolicy::Allowed;
1898 }
1899
1900 let policy = response
1902 .headers
1903 .get(HeaderName::from_static("cross-origin-resource-policy"))
1904 .map(|h| h.to_str().unwrap_or(""))
1905 .unwrap_or("");
1906
1907 if policy == "same-origin" {
1909 return CrossOriginResourcePolicy::Blocked;
1910 }
1911
1912 if let Origin::Origin(ref request_origin) = request.origin {
1914 let schemeless_same_origin = is_schemelessy_same_site(request_origin, ¤t_url_origin);
1915 if schemeless_same_origin &&
1916 (request_origin.scheme() == Some("https") ||
1917 response.https_state == HttpsState::None)
1918 {
1919 return CrossOriginResourcePolicy::Allowed;
1920 }
1921 };
1922
1923 if policy == "same-site" {
1925 return CrossOriginResourcePolicy::Blocked;
1926 }
1927
1928 CrossOriginResourcePolicy::Allowed
1929}
1930
1931struct ResponseEndTimer(Option<Arc<Mutex<ResourceFetchTiming>>>);
1933
1934impl ResponseEndTimer {
1935 fn neuter(&mut self) {
1936 self.0 = None;
1937 }
1938}
1939
1940impl Drop for ResponseEndTimer {
1941 fn drop(&mut self) {
1942 let ResponseEndTimer(resource_fetch_timing_opt) = self;
1943
1944 resource_fetch_timing_opt.as_ref().map_or((), |t| {
1945 t.lock()
1946 .unwrap()
1947 .set_attribute(ResourceAttribute::ResponseEnd);
1948 })
1949 }
1950}
1951
1952async fn http_network_fetch(
1954 fetch_params: &mut FetchParams,
1955 credentials_flag: bool,
1956 done_chan: &mut DoneChannel,
1957 context: &FetchContext,
1958) -> Response {
1959 let mut response_end_timer = ResponseEndTimer(Some(context.timing.clone()));
1960
1961 let request = &mut fetch_params.request;
1963
1964 let url = request.current_url();
1975 let request_id = request.id.0.to_string();
1976 if log_enabled!(log::Level::Info) {
1977 info!("{:?} request for {}", request.method, url);
1978 for header in request.headers.iter() {
1979 debug!(" - {:?}", header);
1980 }
1981 }
1982
1983 let is_xhr = request.destination == Destination::None;
1987
1988 let (fetch_terminated_sender, mut fetch_terminated_receiver) = unbounded_channel();
1990
1991 let body = request.body.as_ref().map(|body| body.take_stream());
1992
1993 if body.is_none() {
1994 let _ = fetch_terminated_sender.send(false);
1999 }
2000
2001 let browsing_context_id = request.target_webview_id.map(Into::into);
2002
2003 let (res, msg) = match &request.mode {
2004 RequestMode::WebSocket {
2005 protocols,
2006 original_url: _,
2007 } => {
2008 let (resource_event_sender, dom_action_receiver) = {
2011 let mut websocket_chan = context.websocket_chan.as_ref().unwrap().lock().unwrap();
2012 (
2013 websocket_chan.sender.clone(),
2014 websocket_chan.receiver.take().unwrap(),
2015 )
2016 };
2017
2018 let mut tls_config = create_tls_config(
2019 context.ca_certificates.clone(),
2020 context.ignore_certificate_errors,
2021 context.state.override_manager.clone(),
2022 );
2023 tls_config.alpn_protocols = vec!["http/1.1".to_string().into()];
2024
2025 let response = match start_websocket(
2026 context.state.clone(),
2027 resource_event_sender,
2028 protocols,
2029 request,
2030 tls_config,
2031 dom_action_receiver,
2032 )
2033 .await
2034 {
2035 Ok(response) => response,
2036 Err(e) => {
2037 return Response::network_internal_error(e.to_string());
2038 },
2039 };
2040
2041 let response = response.map(|r| match r {
2042 Some(body) => Full::from(body).map_err(|_| unreachable!()).boxed(),
2043 None => http_body_util::Empty::new()
2044 .map_err(|_| unreachable!())
2045 .boxed(),
2046 });
2047 (Decoder::detect(response, url.is_secure_scheme()), None)
2048 },
2049 _ => {
2050 let response_future = obtain_response(
2051 &context.state.client,
2052 &url,
2053 &request.method,
2054 &mut request.headers,
2055 body,
2056 request
2057 .body
2058 .as_ref()
2059 .is_some_and(|body| body.source_is_null()),
2060 &request.pipeline_id,
2061 Some(&request_id),
2062 request.destination,
2063 is_xhr,
2064 context,
2065 fetch_terminated_sender,
2066 browsing_context_id,
2067 );
2068
2069 let (res, msg) = match response_future.await {
2071 Ok(wrapped_response) => wrapped_response,
2072 Err(error) => return Response::network_error(error),
2073 };
2074 (res, msg)
2075 },
2076 };
2077
2078 if log_enabled!(log::Level::Info) {
2079 debug!("{:?} response for {}", res.version(), url);
2080 for header in res.headers().iter() {
2081 debug!(" - {:?}", header);
2082 }
2083 }
2084
2085 match fetch_terminated_receiver.recv().await {
2088 Some(true) => {
2089 return Response::network_error(NetworkError::Internal(
2090 "Request body streaming failed.".into(),
2091 ));
2092 },
2093 Some(false) => {},
2094 _ => warn!("Failed to receive confirmation request was streamed without error."),
2095 }
2096
2097 let header_strings: Vec<&str> = res
2098 .headers()
2099 .get_all("Timing-Allow-Origin")
2100 .iter()
2101 .map(|header_value| header_value.to_str().unwrap_or(""))
2102 .collect();
2103 let wildcard_present = header_strings.contains(&"*");
2104 let req_origin_in_timing_allow = header_strings
2108 .iter()
2109 .any(|header_str| match request.origin {
2110 SpecificOrigin(ref immutable_request_origin) => {
2111 *header_str == immutable_request_origin.ascii_serialization()
2112 },
2113 _ => false,
2114 });
2115
2116 let is_same_origin = request.url_list.iter().all(|url| match request.origin {
2117 SpecificOrigin(ref immutable_request_origin) => url.origin() == *immutable_request_origin,
2118 _ => false,
2119 });
2120
2121 if !(is_same_origin || req_origin_in_timing_allow || wildcard_present) {
2122 context.timing.lock().unwrap().mark_timing_check_failed();
2123 }
2124
2125 let timing = context.timing.lock().unwrap().clone();
2126 let mut response = Response::new(url.clone(), timing);
2127
2128 let status_text = res
2129 .extensions()
2130 .get::<ReasonPhrase>()
2131 .map(ReasonPhrase::as_bytes)
2132 .or_else(|| res.status().canonical_reason().map(str::as_bytes))
2133 .map(Vec::from)
2134 .unwrap_or_default();
2135 response.status = HttpStatus::new(res.status(), status_text);
2136
2137 info!("got {:?} response for {:?}", res.status(), request.url());
2138 response.headers = res.headers().clone();
2139 response.referrer = request.referrer.to_url().cloned();
2140 response.referrer_policy = request.referrer_policy;
2141
2142 let res_body = response.body.clone();
2143
2144 let (done_sender, done_receiver) = unbounded_channel();
2146 *done_chan = Some((done_sender.clone(), done_receiver));
2147
2148 let devtools_sender = context.devtools_chan.clone();
2149 let cancellation_listener = context.cancellation_listener.clone();
2150 if cancellation_listener.cancelled() {
2151 return Response::network_error(NetworkError::Internal("Fetch aborted".into()));
2152 }
2153
2154 *res_body.lock().unwrap() = ResponseBody::Receiving(vec![]);
2155 let res_body2 = res_body.clone();
2156
2157 if let Some(ref sender) = devtools_sender {
2158 let sender = sender.lock().unwrap();
2159 if let Some(m) = msg {
2160 send_request_to_devtools(m, &sender);
2161 }
2162 }
2163
2164 let done_sender2 = done_sender.clone();
2165 let done_sender3 = done_sender.clone();
2166 let timing_ptr2 = context.timing.clone();
2167 let timing_ptr3 = context.timing.clone();
2168 let devtools_request = request.clone();
2169 let url1 = devtools_request.url();
2170 let url2 = url1.clone();
2171
2172 let status = response.status.clone();
2173 let headers = response.headers.clone();
2174 let devtools_chan = context.devtools_chan.clone();
2175
2176 spawn_task(
2177 res.into_body()
2178 .map_err(|e| {
2179 warn!("Error streaming response body: {:?}", e);
2180 })
2181 .try_fold(res_body, move |res_body, chunk| {
2182 if cancellation_listener.cancelled() {
2183 *res_body.lock().unwrap() = ResponseBody::Done(vec![]);
2184 let _ = done_sender.send(Data::Cancelled);
2185 return future::ready(Err(()));
2186 }
2187 if let ResponseBody::Receiving(ref mut body) = *res_body.lock().unwrap() {
2188 let bytes = chunk;
2189 body.extend_from_slice(&bytes);
2190 let _ = done_sender.send(Data::Payload(bytes.to_vec()));
2191 }
2192 future::ready(Ok(res_body))
2193 })
2194 .and_then(move |res_body| {
2195 debug!("successfully finished response for {:?}", url1);
2196 let mut body = res_body.lock().unwrap();
2197 let completed_body = match *body {
2198 ResponseBody::Receiving(ref mut body) => std::mem::take(body),
2199 _ => vec![],
2200 };
2201 let devtools_response_body = completed_body.clone();
2202 *body = ResponseBody::Done(completed_body);
2203 send_response_values_to_devtools(
2204 Some(headers),
2205 status,
2206 Some(devtools_response_body),
2207 CacheState::None,
2208 &devtools_request,
2209 devtools_chan,
2210 );
2211 timing_ptr2
2212 .lock()
2213 .unwrap()
2214 .set_attribute(ResourceAttribute::ResponseEnd);
2215 let _ = done_sender2.send(Data::Done);
2216 future::ready(Ok(()))
2217 })
2218 .map_err(move |_| {
2219 debug!("finished response for {:?}", url2);
2220 let mut body = res_body2.lock().unwrap();
2221 let completed_body = match *body {
2222 ResponseBody::Receiving(ref mut body) => std::mem::take(body),
2223 _ => vec![],
2224 };
2225 *body = ResponseBody::Done(completed_body);
2226 timing_ptr3
2227 .lock()
2228 .unwrap()
2229 .set_attribute(ResourceAttribute::ResponseEnd);
2230 let _ = done_sender3.send(Data::Done);
2231 }),
2232 );
2233
2234 response.https_state = match url.scheme() {
2240 "https" => HttpsState::Modern,
2241 _ => HttpsState::None,
2242 };
2243
2244 if credentials_flag {
2257 set_cookies_from_headers(&url, &response.headers, &context.state.cookie_jar);
2258 }
2259 context
2260 .state
2261 .hsts_list
2262 .write()
2263 .unwrap()
2264 .update_hsts_list_from_response(&url, &response.headers);
2265
2266 response_end_timer.neuter();
2280
2281 response
2282}
2283
2284async fn cors_preflight_fetch(
2286 request: &Request,
2287 cache: &mut CorsCache,
2288 context: &FetchContext,
2289) -> Response {
2290 let mut preflight = RequestBuilder::new(
2295 request.target_webview_id,
2296 request.current_url(),
2297 request.referrer.clone(),
2298 )
2299 .method(Method::OPTIONS)
2300 .origin(match &request.origin {
2301 Origin::Client => {
2302 unreachable!("We shouldn't get Client origin in cors_preflight_fetch.")
2303 },
2304 Origin::Origin(origin) => origin.clone(),
2305 })
2306 .pipeline_id(request.pipeline_id)
2307 .initiator(request.initiator)
2308 .destination(request.destination)
2309 .referrer_policy(request.referrer_policy)
2310 .mode(RequestMode::CorsMode)
2311 .response_tainting(ResponseTainting::CorsTainting)
2312 .policy_container(match &request.policy_container {
2313 RequestPolicyContainer::Client => {
2314 unreachable!("We should have a policy container for request in cors_preflight_fetch")
2315 },
2316 RequestPolicyContainer::PolicyContainer(policy_container) => policy_container.clone(),
2317 })
2318 .build();
2319
2320 preflight
2322 .headers
2323 .insert(ACCEPT, HeaderValue::from_static("*/*"));
2324
2325 preflight
2327 .headers
2328 .typed_insert::<AccessControlRequestMethod>(AccessControlRequestMethod::from(
2329 request.method.clone(),
2330 ));
2331
2332 let headers = get_cors_unsafe_header_names(&request.headers);
2334
2335 if !headers.is_empty() {
2337 preflight.headers.insert(
2340 ACCESS_CONTROL_REQUEST_HEADERS,
2341 HeaderValue::from_bytes(itertools::join(headers.iter(), ",").as_bytes())
2342 .unwrap_or(HeaderValue::from_static("")),
2343 );
2344 }
2345
2346 let mut fetch_params = FetchParams::new(preflight);
2349 let response =
2350 http_network_or_cache_fetch(&mut fetch_params, false, false, &mut None, context).await;
2351
2352 if cors_check(request, &response).is_ok() && response.status.code().is_success() {
2354 let mut methods = if response
2357 .headers
2358 .contains_key(header::ACCESS_CONTROL_ALLOW_METHODS)
2359 {
2360 match response.headers.typed_get::<AccessControlAllowMethods>() {
2361 Some(methods) => methods.iter().collect(),
2362 None => {
2364 return Response::network_error(NetworkError::Internal(
2365 "CORS ACAM check failed".into(),
2366 ));
2367 },
2368 }
2369 } else {
2370 vec![]
2371 };
2372
2373 let header_names = if response
2376 .headers
2377 .contains_key(header::ACCESS_CONTROL_ALLOW_HEADERS)
2378 {
2379 match response.headers.typed_get::<AccessControlAllowHeaders>() {
2380 Some(names) => names.iter().collect(),
2381 None => {
2383 return Response::network_error(NetworkError::Internal(
2384 "CORS ACAH check failed".into(),
2385 ));
2386 },
2387 }
2388 } else {
2389 vec![]
2390 };
2391
2392 debug!(
2393 "CORS check: Allowed methods: {:?}, current method: {:?}",
2394 methods, request.method
2395 );
2396
2397 if methods.is_empty() && request.use_cors_preflight {
2400 methods = vec![request.method.clone()];
2401 }
2402
2403 if methods
2406 .iter()
2407 .all(|method| *method.as_str() != *request.method.as_ref()) &&
2408 !is_cors_safelisted_method(&request.method) &&
2409 (request.credentials_mode == CredentialsMode::Include ||
2410 methods.iter().all(|method| method.as_ref() != "*"))
2411 {
2412 return Response::network_error(NetworkError::Internal(
2413 "CORS method check failed".into(),
2414 ));
2415 }
2416
2417 debug!(
2418 "CORS check: Allowed headers: {:?}, current headers: {:?}",
2419 header_names, request.headers
2420 );
2421
2422 if request.headers.iter().any(|(name, _)| {
2425 is_cors_non_wildcard_request_header_name(name) &&
2426 header_names.iter().all(|header_name| header_name != name)
2427 }) {
2428 return Response::network_error(NetworkError::Internal(
2429 "CORS authorization check failed".into(),
2430 ));
2431 }
2432
2433 let unsafe_names = get_cors_unsafe_header_names(&request.headers);
2437 let header_names_set: HashSet<&HeaderName> = HashSet::from_iter(header_names.iter());
2438 let header_names_contains_star = header_names
2439 .iter()
2440 .any(|header_name| header_name.as_str() == "*");
2441 for unsafe_name in unsafe_names.iter() {
2442 if !header_names_set.contains(unsafe_name) &&
2443 (request.credentials_mode == CredentialsMode::Include ||
2444 !header_names_contains_star)
2445 {
2446 return Response::network_error(NetworkError::Internal(
2447 "CORS headers check failed".into(),
2448 ));
2449 }
2450 }
2451
2452 let max_age: Option<Duration> = response
2455 .headers
2456 .typed_get::<AccessControlMaxAge>()
2457 .map(|acma| acma.into());
2458
2459 let max_age = max_age.unwrap_or(Duration::from_secs(5));
2461
2462 for method in &methods {
2473 cache.match_method_and_update(request, method.clone(), max_age);
2474 }
2475
2476 for header_name in &header_names {
2481 cache.match_header_and_update(request, header_name, max_age);
2482 }
2483
2484 return response;
2486 }
2487
2488 Response::network_error(NetworkError::Internal("CORS check failed".into()))
2490}
2491
2492fn cors_check(request: &Request, response: &Response) -> Result<(), ()> {
2494 let origin = response.headers.typed_get::<AccessControlAllowOrigin>();
2496
2497 let origin = origin.ok_or(())?;
2499
2500 if request.credentials_mode != CredentialsMode::Include &&
2502 origin == AccessControlAllowOrigin::ANY
2503 {
2504 return Ok(());
2505 }
2506
2507 let origin = match origin.origin() {
2509 Some(origin) => origin,
2510 None => return Err(()),
2512 };
2513
2514 match request.origin {
2515 Origin::Origin(ref o) if o.ascii_serialization() == origin.to_string().trim() => {},
2516 _ => return Err(()),
2517 }
2518
2519 if request.credentials_mode != CredentialsMode::Include {
2521 return Ok(());
2522 }
2523
2524 let credentials = response
2526 .headers
2527 .typed_get::<AccessControlAllowCredentials>();
2528
2529 if credentials.is_some() {
2531 return Ok(());
2532 }
2533
2534 Err(())
2536}
2537
2538fn has_credentials(url: &ServoUrl) -> bool {
2539 !url.username().is_empty() || url.password().is_some()
2540}
2541
2542fn is_no_store_cache(headers: &HeaderMap) -> bool {
2543 headers.contains_key(header::IF_MODIFIED_SINCE) |
2544 headers.contains_key(header::IF_NONE_MATCH) |
2545 headers.contains_key(header::IF_UNMODIFIED_SINCE) |
2546 headers.contains_key(header::IF_MATCH) |
2547 headers.contains_key(header::IF_RANGE)
2548}
2549
2550fn is_redirect_status(status: StatusCode) -> bool {
2552 matches!(
2553 status,
2554 StatusCode::MOVED_PERMANENTLY |
2555 StatusCode::FOUND |
2556 StatusCode::SEE_OTHER |
2557 StatusCode::TEMPORARY_REDIRECT |
2558 StatusCode::PERMANENT_REDIRECT
2559 )
2560}
2561
2562fn request_has_redirect_tainted_origin(request: &Request) -> bool {
2564 let Origin::Origin(request_origin) = &request.origin else {
2566 panic!("origin cannot be \"client\" at this point in time");
2567 };
2568
2569 let mut last_url = None;
2571
2572 for url in &request.url_list {
2574 let Some(last_url) = &mut last_url else {
2576 last_url = Some(url);
2577 continue;
2578 };
2579
2580 if url.origin() != last_url.origin() && *request_origin != last_url.origin() {
2583 return true;
2584 }
2585
2586 *last_url = url;
2588 }
2589
2590 false
2592}
2593
2594fn serialize_request_origin(request: &Request) -> headers::Origin {
2596 let Origin::Origin(origin) = &request.origin else {
2598 panic!("origin cannot be \"client\" at this point in time");
2599 };
2600
2601 if request_has_redirect_tainted_origin(request) {
2603 return headers::Origin::NULL;
2604 }
2605
2606 serialize_origin(origin)
2608}
2609
2610pub fn serialize_origin(origin: &ImmutableOrigin) -> headers::Origin {
2612 match origin {
2613 ImmutableOrigin::Opaque(_) => headers::Origin::NULL,
2614 ImmutableOrigin::Tuple(scheme, host, port) => {
2615 let port = match (scheme.as_ref(), port) {
2618 ("http" | "ws", 80) | ("https" | "wss", 443) | ("ftp", 21) => None,
2619 _ => Some(*port),
2620 };
2621
2622 headers::Origin::try_from_parts(scheme, &host.to_string(), port)
2624 .unwrap_or(headers::Origin::NULL)
2625 },
2626 }
2627}
2628
2629fn append_a_request_origin_header(request: &mut Request) {
2631 let Origin::Origin(request_origin) = &request.origin else {
2633 panic!("origin cannot be \"client\" at this point in time");
2634 };
2635
2636 let mut serialized_origin = serialize_request_origin(request);
2638
2639 if request.response_tainting == ResponseTainting::CorsTainting ||
2642 matches!(request.mode, RequestMode::WebSocket { .. })
2643 {
2644 request.headers.typed_insert(serialized_origin);
2645 }
2646 else if !matches!(request.method, Method::GET | Method::HEAD) {
2648 if request.mode != RequestMode::CorsMode {
2650 match request.referrer_policy {
2651 ReferrerPolicy::NoReferrer => {
2652 serialized_origin = headers::Origin::NULL;
2654 },
2655 ReferrerPolicy::NoReferrerWhenDowngrade |
2656 ReferrerPolicy::StrictOrigin |
2657 ReferrerPolicy::StrictOriginWhenCrossOrigin => {
2658 if let ImmutableOrigin::Tuple(scheme, _, _) = &request_origin {
2661 if scheme == "https" && request.current_url().scheme() != "https" {
2662 serialized_origin = headers::Origin::NULL;
2663 }
2664 }
2665 },
2666 ReferrerPolicy::SameOrigin => {
2667 if *request_origin != request.current_url().origin() {
2670 serialized_origin = headers::Origin::NULL;
2671 }
2672 },
2673 _ => {
2674 },
2676 };
2677 }
2678
2679 request.headers.typed_insert(serialized_origin);
2681 }
2682}
2683
2684fn append_the_fetch_metadata_headers(r: &mut Request) {
2686 if !r.url().is_potentially_trustworthy() {
2688 return;
2689 }
2690
2691 set_the_sec_fetch_dest_header(r);
2693
2694 set_the_sec_fetch_mode_header(r);
2696
2697 set_the_sec_fetch_site_header(r);
2699
2700 set_the_sec_fetch_user_header(r);
2702}
2703
2704fn set_the_sec_fetch_dest_header(r: &mut Request) {
2706 debug_assert!(r.url().is_potentially_trustworthy());
2708
2709 let header = r.destination;
2713
2714 r.headers.typed_insert(SecFetchDest(header));
2716}
2717
2718fn set_the_sec_fetch_mode_header(r: &mut Request) {
2720 debug_assert!(r.url().is_potentially_trustworthy());
2722
2723 let header = &r.mode;
2726
2727 r.headers.typed_insert(SecFetchMode::from(header));
2729}
2730
2731fn set_the_sec_fetch_site_header(r: &mut Request) {
2733 let Origin::Origin(request_origin) = &r.origin else {
2736 panic!("request origin cannot be \"client\" at this point")
2737 };
2738
2739 debug_assert!(r.url().is_potentially_trustworthy());
2741
2742 let mut header = SecFetchSite::SameOrigin;
2745
2746 if header != SecFetchSite::None {
2751 for url in &r.url_list {
2752 if url.origin() == *request_origin {
2754 continue;
2755 }
2756
2757 header = SecFetchSite::CrossSite;
2759
2760 if !is_same_site(request_origin, &url.origin()) {
2762 break;
2763 }
2764
2765 header = SecFetchSite::SameSite;
2767 }
2768 }
2769
2770 r.headers.typed_insert(header);
2772}
2773
2774fn set_the_sec_fetch_user_header(r: &mut Request) {
2776 debug_assert!(r.url().is_potentially_trustworthy());
2778
2779 if !r.is_navigation_request() {
2782 return;
2783 }
2784
2785 let header = SecFetchUser;
2788
2789 r.headers.typed_insert(header);
2791}
2792
2793fn set_requests_referrer_policy_on_redirect(request: &mut Request, response: &Response) {
2795 let referrer_policy: ReferrerPolicy = response
2798 .headers
2799 .typed_get::<headers::ReferrerPolicy>()
2800 .into();
2801
2802 if referrer_policy != ReferrerPolicy::EmptyString {
2804 request.referrer_policy = referrer_policy;
2805 }
2806}