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, DebugVec, FetchMetadata, NetworkError,
59 RedirectEndValue, RedirectStartValue, ReferrerPolicy, ResourceAttribute, ResourceFetchTiming,
60 ResourceTimeValue,
61};
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, Connector, 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: Client<Connector, crate::connector::BoxedBody>,
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().unwrap().size_of(ops),
122 },
123 Report {
124 path: path!["hsts-list", suffix],
125 kind: ReportKind::ExplicitJemallocHeapSize,
126 size: self.hsts_list.read().unwrap().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().unwrap();
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().unwrap();
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().unwrap();
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 .unwrap()
512 .send(DevtoolsControlMsg::FromChrome(msg));
513 }
514}
515
516pub fn send_early_httprequest_to_devtools(request: &Request, context: &FetchContext) {
517 if request.url().scheme() == "data" {
519 return;
520 }
521 if let (Some(devtools_chan), Some(browsing_context_id), Some(pipeline_id)) = (
522 context.devtools_chan.as_ref(),
523 request.target_webview_id.map(|id| id.into()),
524 request.pipeline_id,
525 ) {
526 let devtools_request = DevtoolsHttpRequest {
528 url: request.current_url().clone(),
529 method: request.method.clone(),
530 headers: request.headers.clone(),
531 body: None,
532 pipeline_id,
533 started_date_time: SystemTime::now(),
534 time_stamp: 0,
535 connect_time: Duration::from_millis(0),
536 send_time: Duration::from_millis(0),
537 destination: request.destination,
538 is_xhr: false,
539 browsing_context_id,
540 };
541
542 let msg = ChromeToDevtoolsControlMsg::NetworkEvent(
543 request.id.0.to_string(),
544 NetworkEvent::HttpRequest(devtools_request),
545 );
546
547 send_request_to_devtools(msg, &devtools_chan.lock().unwrap());
548 }
549}
550
551fn auth_from_cache(
552 auth_cache: &RwLock<AuthCache>,
553 origin: &ImmutableOrigin,
554) -> Option<Authorization<Basic>> {
555 if let Some(auth_entry) = auth_cache
556 .read()
557 .unwrap()
558 .entries
559 .get(&origin.ascii_serialization())
560 {
561 let user_name = &auth_entry.user_name;
562 let password = &auth_entry.password;
563 Some(Authorization::basic(user_name, password))
564 } else {
565 None
566 }
567}
568
569enum BodyChunk {
572 Chunk(IpcSharedMemory),
574 Done,
576}
577
578enum BodyStream {
580 Chunked(TokioReceiver<Result<Frame<Bytes>, hyper::Error>>),
583 Buffered(UnboundedReceiver<BodyChunk>),
586}
587
588enum BodySink {
591 Chunked(TokioSender<Result<Frame<Bytes>, hyper::Error>>),
593 Buffered(UnboundedSender<BodyChunk>),
597}
598
599impl BodySink {
600 fn transmit_bytes(&self, bytes: IpcSharedMemory) {
601 match self {
602 BodySink::Chunked(sender) => {
603 let sender = sender.clone();
604 spawn_task(async move {
605 let _ = sender
606 .send(Ok(Frame::data(Bytes::copy_from_slice(&bytes))))
607 .await;
608 });
609 },
610 BodySink::Buffered(sender) => {
611 let _ = sender.send(BodyChunk::Chunk(bytes));
612 },
613 }
614 }
615
616 fn close(&self) {
617 match self {
618 BodySink::Chunked(_) => { },
619 BodySink::Buffered(sender) => {
620 let _ = sender.send(BodyChunk::Done);
621 },
622 }
623 }
624}
625
626#[allow(clippy::too_many_arguments)]
627async fn obtain_response(
628 client: &Client<Connector, crate::connector::BoxedBody>,
629 url: &ServoUrl,
630 method: &Method,
631 request_headers: &mut HeaderMap,
632 body: Option<StdArc<Mutex<IpcSender<BodyChunkRequest>>>>,
633 source_is_null: bool,
634 pipeline_id: &Option<PipelineId>,
635 request_id: Option<&str>,
636 destination: Destination,
637 is_xhr: bool,
638 context: &FetchContext,
639 fetch_terminated: UnboundedSender<bool>,
640 browsing_context_id: Option<BrowsingContextId>,
641) -> Result<(HyperResponse<Decoder>, Option<ChromeToDevtoolsControlMsg>), NetworkError> {
642 {
643 let mut headers = request_headers.clone();
644
645 let devtools_bytes = StdArc::new(Mutex::new(vec![]));
646
647 let encoded_url = url
649 .clone()
650 .into_url()
651 .as_ref()
652 .replace('|', "%7C")
653 .replace('{', "%7B")
654 .replace('}', "%7D");
655
656 let request = if let Some(chunk_requester) = body {
657 let (sink, stream) = if source_is_null {
658 headers.insert(TRANSFER_ENCODING, HeaderValue::from_static("chunked"));
661
662 let (sender, receiver) = channel(1);
663 (BodySink::Chunked(sender), BodyStream::Chunked(receiver))
664 } else {
665 let (sender, receiver) = unbounded_channel();
672 (BodySink::Buffered(sender), BodyStream::Buffered(receiver))
673 };
674
675 let (body_chan, body_port) = ipc::channel().unwrap();
676
677 if let Ok(requester) = chunk_requester.lock() {
678 let _ = requester.send(BodyChunkRequest::Connect(body_chan));
679
680 let _ = requester.send(BodyChunkRequest::Chunk);
683 }
684
685 let devtools_bytes = devtools_bytes.clone();
686 let chunk_requester2 = chunk_requester.clone();
687
688 ROUTER.add_typed_route(
689 body_port,
690 Box::new(move |message| {
691 info!("Received message");
692 let bytes = match message.unwrap() {
693 BodyChunkResponse::Chunk(bytes) => bytes,
694 BodyChunkResponse::Done => {
695 let _ = fetch_terminated.send(false);
697 sink.close();
698
699 return;
700 },
701 BodyChunkResponse::Error => {
702 let _ = fetch_terminated.send(true);
706 sink.close();
707
708 return;
709 },
710 };
711
712 devtools_bytes.lock().unwrap().extend_from_slice(&bytes);
713
714 sink.transmit_bytes(bytes);
717
718 let _ = chunk_requester2
721 .lock()
722 .unwrap()
723 .send(BodyChunkRequest::Chunk);
724 }),
725 );
726
727 let body = match stream {
728 BodyStream::Chunked(receiver) => {
729 let stream = ReceiverStream::new(receiver);
730 BoxBody::new(http_body_util::StreamBody::new(stream))
731 },
732 BodyStream::Buffered(mut receiver) => {
733 let mut body = vec![];
735 loop {
736 match receiver.recv().await {
737 Some(BodyChunk::Chunk(bytes)) => {
738 body.extend_from_slice(&bytes);
739 },
740 Some(BodyChunk::Done) => break,
741 None => warn!("Failed to read all chunks from request body."),
742 }
743 }
744 Full::new(body.into()).map_err(|_| unreachable!()).boxed()
745 },
746 };
747 HyperRequest::builder()
748 .method(method)
749 .uri(encoded_url)
750 .body(body)
751 } else {
752 HyperRequest::builder()
753 .method(method)
754 .uri(encoded_url)
755 .body(
756 http_body_util::Empty::new()
757 .map_err(|_| unreachable!())
758 .boxed(),
759 )
760 };
761
762 context
763 .timing
764 .lock()
765 .unwrap()
766 .set_attribute(ResourceAttribute::DomainLookupStart);
767
768 let connect_start = CrossProcessInstant::now();
771 context
772 .timing
773 .lock()
774 .unwrap()
775 .set_attribute(ResourceAttribute::ConnectStart(connect_start));
776
777 if url.scheme() == "https" {
781 context
782 .timing
783 .lock()
784 .unwrap()
785 .set_attribute(ResourceAttribute::SecureConnectionStart);
786 }
787
788 let mut request = match request {
789 Ok(request) => request,
790 Err(e) => return Err(NetworkError::from_http_error(&e)),
791 };
792 *request.headers_mut() = headers.clone();
793
794 let connect_end = CrossProcessInstant::now();
795 context
796 .timing
797 .lock()
798 .unwrap()
799 .set_attribute(ResourceAttribute::ConnectEnd(connect_end));
800
801 let request_id = request_id.map(|v| v.to_owned());
802 let pipeline_id = *pipeline_id;
803 let closure_url = url.clone();
804 let method = method.clone();
805 let send_start = CrossProcessInstant::now();
806
807 let host = request.uri().host().unwrap_or("").to_owned();
808 let override_manager = context.state.override_manager.clone();
809 let headers = headers.clone();
810 let is_secure_scheme = url.is_secure_scheme();
811
812 client
813 .request(request)
814 .and_then(move |res| {
815 let send_end = CrossProcessInstant::now();
816
817 let msg = if let Some(request_id) = request_id {
820 if let Some(pipeline_id) = pipeline_id {
821 if let Some(browsing_context_id) = browsing_context_id {
822 Some(prepare_devtools_request(
823 request_id,
824 closure_url,
825 method.clone(),
826 headers,
827 Some(devtools_bytes.lock().unwrap().clone()),
828 pipeline_id,
829 (connect_end - connect_start).unsigned_abs(),
830 (send_end - send_start).unsigned_abs(),
831 destination,
832 is_xhr,
833 browsing_context_id,
834 ))
835 } else {
836 debug!("Not notifying devtools (no browsing_context_id)");
837 None
838 }
839 } else {
844 debug!("Not notifying devtools (no pipeline_id)");
845 None
846 }
847 } else {
848 debug!("Not notifying devtools (no request_id)");
849 None
850 };
851
852 future::ready(Ok((
853 Decoder::detect(res.map(|r| r.boxed()), is_secure_scheme),
854 msg,
855 )))
856 })
857 .map_err(move |error| {
858 warn!("network error: {error:?}");
859 NetworkError::from_hyper_error(
860 &error,
861 override_manager.remove_certificate_failing_verification(host.as_str()),
862 )
863 })
864 .await
865 }
866}
867
868#[async_recursion]
870#[allow(clippy::too_many_arguments)]
871pub async fn http_fetch(
872 fetch_params: &mut FetchParams,
873 cache: &mut CorsCache,
874 cors_flag: bool,
875 cors_preflight_flag: bool,
876 authentication_fetch_flag: bool,
877 target: Target<'async_recursion>,
878 done_chan: &mut DoneChannel,
879 context: &FetchContext,
880) -> Response {
881 *done_chan = None;
883 let request = &mut fetch_params.request;
885
886 let mut response: Option<Response> = None;
889
890 if request.service_workers_mode == ServiceWorkersMode::All {
892 if let Some(ref res) = response {
897 if (res.response_type == ResponseType::Opaque && request.mode != RequestMode::NoCors) ||
905 (res.response_type == ResponseType::OpaqueRedirect &&
906 request.redirect_mode != RedirectMode::Manual) ||
907 (res.url_list.len() > 1 && request.redirect_mode != RedirectMode::Follow) ||
908 res.is_network_error()
909 {
910 return Response::network_error(NetworkError::Internal("Request failed".into()));
911 }
912
913 }
916 }
917
918 if response.is_none() {
920 if cors_preflight_flag {
922 let method_cache_match = cache.match_method(request, request.method.clone());
923
924 let method_mismatch = !method_cache_match &&
925 (!is_cors_safelisted_method(&request.method) || request.use_cors_preflight);
926 let header_mismatch = request.headers.iter().any(|(name, value)| {
927 !cache.match_header(request, name) &&
928 !is_cors_safelisted_request_header(&name, &value)
929 });
930
931 if method_mismatch || header_mismatch {
933 let preflight_result = cors_preflight_fetch(request, cache, context).await;
934 if let Some(e) = preflight_result.get_network_error() {
936 return Response::network_error(e.clone());
937 }
938 }
939 }
940
941 if request.redirect_mode == RedirectMode::Follow {
943 request.service_workers_mode = ServiceWorkersMode::None;
944 }
945
946 context
950 .timing
951 .lock()
952 .unwrap()
953 .set_attribute(ResourceAttribute::RequestStart);
954
955 let mut fetch_result = http_network_or_cache_fetch(
956 fetch_params,
957 authentication_fetch_flag,
958 cors_flag,
959 done_chan,
960 context,
961 )
962 .await;
963
964 if cors_flag && cors_check(&fetch_params.request, &fetch_result).is_err() {
966 return Response::network_error(NetworkError::Internal("CORS check failed".into()));
967 }
968
969 fetch_result.return_internal = false;
970 response = Some(fetch_result);
971 }
972
973 let request = &mut fetch_params.request;
974
975 let mut response = response.unwrap();
977
978 if response
982 .actual_response()
983 .status
984 .try_code()
985 .is_some_and(is_redirect_status)
986 {
987 if response.actual_response().status != StatusCode::SEE_OTHER {
989 }
991
992 let mut location = response
994 .actual_response()
995 .headers
996 .get(header::LOCATION)
997 .and_then(|v| {
998 HeaderValue::to_str(v)
999 .map(|l| {
1000 ServoUrl::parse_with_base(response.actual_response().url(), l)
1001 .map_err(|err| err.to_string())
1002 })
1003 .ok()
1004 });
1005
1006 if let Some(Ok(ref mut location)) = location {
1008 if location.fragment().is_none() {
1009 let current_url = request.current_url();
1010 location.set_fragment(current_url.fragment());
1011 }
1012 }
1013 response.actual_response_mut().location_url = location;
1014
1015 response = match request.redirect_mode {
1017 RedirectMode::Error => {
1018 Response::network_error(NetworkError::Internal("Redirect mode error".into()))
1019 },
1020 RedirectMode::Manual => response.to_filtered(ResponseType::OpaqueRedirect),
1021 RedirectMode::Follow => {
1022 response.return_internal = true;
1024 http_redirect_fetch(
1025 fetch_params,
1026 cache,
1027 response,
1028 cors_flag,
1029 target,
1030 done_chan,
1031 context,
1032 )
1033 .await
1034 },
1035 };
1036 }
1037
1038 response.return_internal = true;
1040 context
1041 .timing
1042 .lock()
1043 .unwrap()
1044 .set_attribute(ResourceAttribute::RedirectCount(
1045 fetch_params.request.redirect_count as u16,
1046 ));
1047
1048 response.resource_timing = Arc::clone(&context.timing);
1049
1050 response
1052}
1053
1054struct RedirectEndTimer(Option<Arc<Mutex<ResourceFetchTiming>>>);
1056
1057impl RedirectEndTimer {
1058 fn neuter(&mut self) {
1059 self.0 = None;
1060 }
1061}
1062
1063impl Drop for RedirectEndTimer {
1064 fn drop(&mut self) {
1065 let RedirectEndTimer(resource_fetch_timing_opt) = self;
1066
1067 resource_fetch_timing_opt.as_ref().map_or((), |t| {
1068 t.lock()
1069 .unwrap()
1070 .set_attribute(ResourceAttribute::RedirectEnd(RedirectEndValue::Zero));
1071 })
1072 }
1073}
1074
1075#[async_recursion]
1077pub async fn http_redirect_fetch(
1078 fetch_params: &mut FetchParams,
1079 cache: &mut CorsCache,
1080 response: Response,
1081 cors_flag: bool,
1082 target: Target<'async_recursion>,
1083 done_chan: &mut DoneChannel,
1084 context: &FetchContext,
1085) -> Response {
1086 let mut redirect_end_timer = RedirectEndTimer(Some(context.timing.clone()));
1087
1088 let request = &mut fetch_params.request;
1090
1091 assert!(response.return_internal);
1092
1093 let location_url = response.actual_response().location_url.clone();
1094 let location_url = match location_url {
1095 None => return response,
1097 Some(Err(err)) => {
1099 return Response::network_error(NetworkError::Internal(
1100 "Location URL parse failure: ".to_owned() + &err,
1101 ));
1102 },
1103 Some(Ok(ref url)) if !matches!(url.scheme(), "http" | "https") => {
1105 return Response::network_error(NetworkError::Internal(
1106 "Location URL not an HTTP(S) scheme".into(),
1107 ));
1108 },
1109 Some(Ok(url)) => url,
1110 };
1111
1112 context
1115 .timing
1116 .lock()
1117 .unwrap()
1118 .set_attribute(ResourceAttribute::RedirectStart(
1119 RedirectStartValue::FetchStart,
1120 ));
1121
1122 context
1123 .timing
1124 .lock()
1125 .unwrap()
1126 .set_attribute(ResourceAttribute::FetchStart);
1127
1128 context
1130 .timing
1131 .lock()
1132 .unwrap()
1133 .set_attribute(ResourceAttribute::StartTime(ResourceTimeValue::FetchStart));
1134
1135 context
1136 .timing
1137 .lock()
1138 .unwrap()
1139 .set_attribute(ResourceAttribute::StartTime(
1140 ResourceTimeValue::RedirectStart,
1141 )); if request.redirect_count >= 20 {
1145 return Response::network_error(NetworkError::Internal("Too many redirects".into()));
1146 }
1147
1148 request.redirect_count += 1;
1150
1151 let same_origin = match request.origin {
1153 Origin::Origin(ref origin) => *origin == location_url.origin(),
1154 Origin::Client => panic!(
1155 "Request origin should not be client for {}",
1156 request.current_url()
1157 ),
1158 };
1159
1160 let has_credentials = has_credentials(&location_url);
1161
1162 if request.mode == RequestMode::CorsMode && !same_origin && has_credentials {
1163 return Response::network_error(NetworkError::Internal(
1164 "Cross-origin credentials check failed".into(),
1165 ));
1166 }
1167
1168 if cors_flag && location_url.origin() != request.current_url().origin() {
1170 request.origin = Origin::Origin(ImmutableOrigin::new_opaque());
1171 }
1172
1173 if cors_flag && has_credentials {
1175 return Response::network_error(NetworkError::Internal("Credentials check failed".into()));
1176 }
1177
1178 if response.actual_response().status != StatusCode::SEE_OTHER &&
1181 request.body.as_ref().is_some_and(|b| b.source_is_null())
1182 {
1183 return Response::network_error(NetworkError::Internal("Request body is not done".into()));
1184 }
1185
1186 if response
1188 .actual_response()
1189 .status
1190 .try_code()
1191 .is_some_and(|code| {
1192 ((code == StatusCode::MOVED_PERMANENTLY || code == StatusCode::FOUND) &&
1193 request.method == Method::POST) ||
1194 (code == StatusCode::SEE_OTHER &&
1195 request.method != Method::HEAD &&
1196 request.method != Method::GET)
1197 })
1198 {
1199 request.method = Method::GET;
1201 request.body = None;
1202 for name in &[
1204 CONTENT_ENCODING,
1205 CONTENT_LANGUAGE,
1206 CONTENT_LOCATION,
1207 CONTENT_TYPE,
1208 ] {
1209 request.headers.remove(name);
1210 }
1211 }
1212
1213 if location_url.origin() != request.current_url().origin() {
1217 request.headers.remove(AUTHORIZATION);
1220 }
1221
1222 if let Some(body) = request.body.as_mut() {
1225 body.extract_source();
1226 }
1227
1228 request.url_list.push(location_url);
1232
1233 set_requests_referrer_policy_on_redirect(request, response.actual_response());
1235
1236 let recursive_flag = request.redirect_mode != RedirectMode::Manual;
1239
1240 let fetch_response = main_fetch(
1242 fetch_params,
1243 cache,
1244 recursive_flag,
1245 target,
1246 done_chan,
1247 context,
1248 )
1249 .await;
1250
1251 context
1253 .timing
1254 .lock()
1255 .unwrap()
1256 .set_attribute(ResourceAttribute::RedirectEnd(
1257 RedirectEndValue::ResponseEnd,
1258 ));
1259 redirect_end_timer.neuter();
1260
1261 fetch_response
1262}
1263
1264#[async_recursion]
1266async fn http_network_or_cache_fetch(
1267 fetch_params: &mut FetchParams,
1268 authentication_fetch_flag: bool,
1269 cors_flag: bool,
1270 done_chan: &mut DoneChannel,
1271 context: &FetchContext,
1272) -> Response {
1273 let http_fetch_params: &mut FetchParams;
1275 let mut fetch_params_copy: FetchParams;
1276
1277 let mut response: Option<Response> = None;
1281
1282 let mut revalidating_flag = false;
1284
1285 let http_request = if fetch_params.request.traversable_for_user_prompts ==
1289 TraversableForUserPrompts::NoTraversable &&
1290 fetch_params.request.redirect_mode == RedirectMode::Error
1291 {
1292 http_fetch_params = fetch_params;
1293 &mut http_fetch_params.request
1294 }
1295 else {
1297 fetch_params_copy = fetch_params.clone();
1300 http_fetch_params = &mut fetch_params_copy;
1301
1302 &mut http_fetch_params.request
1303 };
1304
1305 let include_credentials = match http_request.credentials_mode {
1307 CredentialsMode::Include => true,
1309 CredentialsMode::CredentialsSameOrigin
1311 if http_request.response_tainting == ResponseTainting::Basic =>
1312 {
1313 true
1314 },
1315 _ => false,
1316 };
1317
1318 let content_length = http_request
1325 .body
1326 .as_ref()
1327 .and_then(|body| body.len().map(|size| size as u64));
1328
1329 let mut content_length_header_value = None;
1331
1332 if http_request.body.is_none() && matches!(http_request.method, Method::POST | Method::PUT) {
1335 content_length_header_value = Some(0);
1336 }
1337
1338 if let Some(content_length) = content_length {
1342 content_length_header_value = Some(content_length);
1343 };
1344
1345 if let Some(content_length_header_value) = content_length_header_value {
1348 http_request
1349 .headers
1350 .typed_insert(ContentLength(content_length_header_value));
1351 }
1352
1353 if content_length.is_some() && http_request.keep_alive {
1355 }
1357
1358 match http_request.referrer {
1360 Referrer::ReferrerUrl(ref http_request_referrer) |
1361 Referrer::Client(ref http_request_referrer) => {
1362 if let Ok(referer) = http_request_referrer.as_str().parse::<Referer>() {
1365 http_request.headers.typed_insert(referer);
1367 } else {
1368 error!("Failed to parse {} as referrer", http_request_referrer);
1372 }
1373 },
1374 _ => {},
1375 };
1376
1377 append_a_request_origin_header(http_request);
1379
1380 append_the_fetch_metadata_headers(http_request);
1382
1383 if http_request.initiator == Initiator::Prefetch {
1386 if let Ok(value) = HeaderValue::from_str("prefetch") {
1387 http_request.headers.insert("Sec-Purpose", value);
1388 }
1389 }
1390
1391 if !http_request.headers.contains_key(header::USER_AGENT) {
1394 http_request
1395 .headers
1396 .typed_insert::<UserAgent>(context.user_agent.parse().unwrap());
1397 }
1398
1399 match http_request.cache_mode {
1401 CacheMode::Default if is_no_store_cache(&http_request.headers) => {
1405 http_request.cache_mode = CacheMode::NoStore;
1406 },
1407
1408 CacheMode::NoCache if !http_request.headers.contains_key(header::CACHE_CONTROL) => {
1418 http_request
1419 .headers
1420 .typed_insert(CacheControl::new().with_max_age(Duration::from_secs(0)));
1421 },
1422
1423 CacheMode::Reload | CacheMode::NoStore => {
1425 if !http_request.headers.contains_key(header::PRAGMA) {
1428 http_request.headers.typed_insert(Pragma::no_cache());
1429 }
1430
1431 if !http_request.headers.contains_key(header::CACHE_CONTROL) {
1434 http_request
1435 .headers
1436 .typed_insert(CacheControl::new().with_no_cache());
1437 }
1438 },
1439
1440 _ => {},
1441 }
1442
1443 if http_request.headers.contains_key(header::RANGE) {
1446 if let Ok(value) = HeaderValue::from_str("identity") {
1447 http_request.headers.insert("Accept-Encoding", value);
1448 }
1449 }
1450
1451 http_request.headers.remove(header::HOST);
1455 set_default_accept_encoding(&mut http_request.headers);
1457
1458 let current_url = http_request.current_url();
1459
1460 if include_credentials {
1463 set_request_cookies(
1467 ¤t_url,
1468 &mut http_request.headers,
1469 &context.state.cookie_jar,
1470 );
1471 if !http_request.headers.contains_key(header::AUTHORIZATION) {
1473 let mut authorization_value = None;
1475
1476 if let Some(basic) = auth_from_cache(&context.state.auth_cache, ¤t_url.origin()) {
1478 if !http_request.use_url_credentials || !has_credentials(¤t_url) {
1479 authorization_value = Some(basic);
1480 }
1481 }
1482
1483 if authentication_fetch_flag &&
1485 authorization_value.is_none() &&
1486 has_credentials(¤t_url)
1487 {
1488 authorization_value = Some(Authorization::basic(
1489 current_url.username(),
1490 current_url.password().unwrap_or(""),
1491 ));
1492 }
1493
1494 if let Some(basic) = authorization_value {
1496 http_request.headers.typed_insert(basic);
1497 }
1498 }
1499 }
1500
1501 block_for_cache_ready(
1504 context,
1505 http_request,
1506 done_chan,
1507 &mut revalidating_flag,
1508 &mut response,
1509 );
1510
1511 wait_for_cached_response(done_chan, &mut response).await;
1512
1513 if response.is_none() {
1517 if http_request.cache_mode == CacheMode::OnlyIfCached {
1519 update_http_cache_state(context, http_request);
1522 return Response::network_error(NetworkError::Internal(
1523 "Couldn't find response in cache".into(),
1524 ));
1525 }
1526
1527 let forward_response =
1530 http_network_fetch(http_fetch_params, include_credentials, done_chan, context).await;
1531
1532 let http_request = &mut http_fetch_params.request;
1533 if forward_response.status.in_range(200..=399) && !http_request.method.is_safe() {
1537 if let Ok(mut http_cache) = context.state.http_cache.write() {
1538 http_cache.invalidate(http_request, &forward_response);
1539 }
1540 }
1541
1542 if revalidating_flag && forward_response.status == StatusCode::NOT_MODIFIED {
1544 if let Ok(mut http_cache) = context.state.http_cache.write() {
1545 *done_chan = None;
1548 response = http_cache.refresh(http_request, forward_response.clone(), done_chan);
1549 }
1550 wait_for_cached_response(done_chan, &mut response).await;
1551 if let Some(response) = &mut response {
1552 response.cache_state = CacheState::Validated;
1553 }
1554 }
1555
1556 if response.is_none() {
1558 let forward_response = response.insert(forward_response);
1560
1561 if http_request.cache_mode != CacheMode::NoStore {
1564 if let Ok(mut http_cache) = context.state.http_cache.write() {
1567 http_cache.store(http_request, forward_response);
1568 }
1569 }
1570 }
1571 }
1572
1573 let http_request = &mut http_fetch_params.request;
1574 update_http_cache_state(context, http_request);
1576
1577 let mut response = response.unwrap();
1578
1579 if http_request.response_tainting != ResponseTainting::CorsTainting &&
1582 cross_origin_resource_policy_check(http_request, &response) ==
1583 CrossOriginResourcePolicy::Blocked
1584 {
1585 return Response::network_error(NetworkError::Internal(
1586 "Cross-origin resource policy check failed".into(),
1587 ));
1588 }
1589
1590 if http_request.headers.contains_key(RANGE) {
1594 response.range_requested = true;
1595 }
1596
1597 if response.status.try_code() == Some(StatusCode::UNAUTHORIZED) &&
1605 !cors_flag &&
1606 include_credentials &&
1607 response.headers.contains_key(WWW_AUTHENTICATE)
1608 {
1609 let request = &mut fetch_params.request;
1612
1613 if request.body.is_some() {
1615 }
1617
1618 if !request.use_url_credentials || authentication_fetch_flag {
1620 let Some(credentials) = context.state.request_authentication(request, &response) else {
1621 return response;
1622 };
1623
1624 if let Err(err) = request
1625 .current_url_mut()
1626 .set_username(&credentials.username)
1627 {
1628 error!("error setting username for url: {:?}", err);
1629 return response;
1630 };
1631
1632 if let Err(err) = request
1633 .current_url_mut()
1634 .set_password(Some(&credentials.password))
1635 {
1636 error!("error setting password for url: {:?}", err);
1637 return response;
1638 };
1639 }
1640
1641 *done_chan = None;
1644
1645 response = http_network_or_cache_fetch(
1647 fetch_params,
1648 true, cors_flag,
1650 done_chan,
1651 context,
1652 )
1653 .await;
1654 }
1655
1656 if response.status == StatusCode::PROXY_AUTHENTICATION_REQUIRED {
1658 let request = &mut fetch_params.request;
1659 if request.traversable_for_user_prompts == TraversableForUserPrompts::NoTraversable {
1662 return Response::network_error(NetworkError::Internal(
1663 "Can't find Window object".into(),
1664 ));
1665 }
1666
1667 let Some(credentials) = context.state.request_authentication(request, &response) else {
1675 return response;
1676 };
1677
1678 let entry = AuthCacheEntry {
1680 user_name: credentials.username,
1681 password: credentials.password,
1682 };
1683 {
1684 let mut auth_cache = context.state.auth_cache.write().unwrap();
1685 let key = request.current_url().origin().ascii_serialization();
1686 auth_cache.entries.insert(key, entry);
1687 }
1688
1689 *done_chan = None;
1692
1693 response = http_network_or_cache_fetch(
1695 fetch_params,
1696 false, cors_flag,
1698 done_chan,
1699 context,
1700 )
1701 .await;
1702 }
1703
1704 if authentication_fetch_flag {
1712 }
1714
1715 response
1717}
1718
1719fn block_for_cache_ready(
1728 context: &FetchContext,
1729 http_request: &mut Request,
1730 done_chan: &mut DoneChannel,
1731 revalidating_flag: &mut bool,
1732 response: &mut Option<Response>,
1733) {
1734 let (lock, cvar) = {
1735 let entry_key = CacheKey::new(http_request);
1736 let mut state_map = context.state.http_cache_state.lock().unwrap();
1737 &*state_map
1738 .entry(entry_key)
1739 .or_insert_with(|| {
1740 Arc::new((
1741 Mutex::new(HttpCacheEntryState::ReadyToConstruct),
1742 Condvar::new(),
1743 ))
1744 })
1745 .clone()
1746 };
1747
1748 let mut state = lock.lock().unwrap();
1750 while let HttpCacheEntryState::PendingStore(_) = *state {
1751 let (current_state, time_out) = cvar
1752 .wait_timeout(state, Duration::from_millis(500))
1753 .unwrap();
1754 state = current_state;
1755 if time_out.timed_out() {
1756 break;
1758 }
1759 }
1760
1761 if let Ok(http_cache) = context.state.http_cache.read() {
1764 let stored_response = http_cache.construct_response(http_request, done_chan);
1768
1769 if let Some(response_from_cache) = stored_response {
1771 let response_headers = response_from_cache.response.headers.clone();
1772 let (cached_response, needs_revalidation) =
1774 match (http_request.cache_mode, &http_request.mode) {
1775 (CacheMode::ForceCache, _) => (Some(response_from_cache.response), false),
1776 (CacheMode::OnlyIfCached, &RequestMode::SameOrigin) => {
1777 (Some(response_from_cache.response), false)
1778 },
1779 (CacheMode::OnlyIfCached, _) |
1780 (CacheMode::NoStore, _) |
1781 (CacheMode::Reload, _) => (None, false),
1782 (_, _) => (
1783 Some(response_from_cache.response),
1784 response_from_cache.needs_validation,
1785 ),
1786 };
1787
1788 if needs_revalidation {
1789 *revalidating_flag = true;
1790 if let Some(http_date) = response_headers.typed_get::<LastModified>() {
1792 let http_date: SystemTime = http_date.into();
1793 http_request
1794 .headers
1795 .typed_insert(IfModifiedSince::from(http_date));
1796 }
1797 if let Some(entity_tag) = response_headers.get(header::ETAG) {
1798 http_request
1799 .headers
1800 .insert(header::IF_NONE_MATCH, entity_tag.clone());
1801 }
1802 } else {
1803 *response = cached_response;
1805 if let Some(response) = response {
1806 response.cache_state = CacheState::Local;
1807 }
1808 }
1809 if response.is_none() {
1810 *done_chan = None;
1813
1814 if let HttpCacheEntryState::PendingStore(i) = *state {
1817 let new = i + 1;
1818 *state = HttpCacheEntryState::PendingStore(new);
1819 } else {
1820 *state = HttpCacheEntryState::PendingStore(1);
1821 }
1822 }
1823 }
1824 }
1825 if *state == HttpCacheEntryState::ReadyToConstruct {
1827 cvar.notify_one();
1828 }
1829 }
1831
1832fn update_http_cache_state(context: &FetchContext, http_request: &Request) {
1837 let (lock, cvar) = {
1838 let entry_key = CacheKey::new(http_request);
1839 let mut state_map = context.state.http_cache_state.lock().unwrap();
1840 &*state_map
1841 .get_mut(&entry_key)
1842 .expect("Entry in http-cache state to have been previously inserted")
1843 .clone()
1844 };
1845 let mut state = lock.lock().unwrap();
1846 if let HttpCacheEntryState::PendingStore(i) = *state {
1847 let new = i - 1;
1848 if new == 0 {
1849 *state = HttpCacheEntryState::ReadyToConstruct;
1850 cvar.notify_one();
1852 } else {
1853 *state = HttpCacheEntryState::PendingStore(new);
1854 }
1855 }
1856}
1857
1858async fn wait_for_cached_response(done_chan: &mut DoneChannel, response: &mut Option<Response>) {
1861 if let Some(ref mut ch) = *done_chan {
1862 assert!(response.is_some());
1866
1867 loop {
1868 match ch.1.recv().await {
1869 Some(Data::Payload(_)) => {},
1870 Some(Data::Done) => break, Some(Data::Cancelled) => {
1872 *response = None;
1875 break;
1876 },
1877 _ => panic!("HTTP cache should always send Done or Cancelled"),
1878 }
1879 }
1880 }
1881 *done_chan = None;
1883}
1884
1885#[derive(PartialEq)]
1889enum CrossOriginResourcePolicy {
1890 Allowed,
1891 Blocked,
1892}
1893
1894fn cross_origin_resource_policy_check(
1897 request: &Request,
1898 response: &Response,
1899) -> CrossOriginResourcePolicy {
1900 if request.mode != RequestMode::NoCors {
1902 return CrossOriginResourcePolicy::Allowed;
1903 }
1904
1905 let current_url_origin = request.current_url().origin();
1907 let same_origin = if let Origin::Origin(ref origin) = request.origin {
1908 *origin == request.current_url().origin()
1909 } else {
1910 false
1911 };
1912
1913 if same_origin {
1914 return CrossOriginResourcePolicy::Allowed;
1915 }
1916
1917 let policy = response
1919 .headers
1920 .get(HeaderName::from_static("cross-origin-resource-policy"))
1921 .map(|h| h.to_str().unwrap_or(""))
1922 .unwrap_or("");
1923
1924 if policy == "same-origin" {
1926 return CrossOriginResourcePolicy::Blocked;
1927 }
1928
1929 if let Origin::Origin(ref request_origin) = request.origin {
1931 let schemeless_same_origin = is_schemelessy_same_site(request_origin, ¤t_url_origin);
1932 if schemeless_same_origin &&
1933 (request_origin.scheme() == Some("https") ||
1934 response.https_state == HttpsState::None)
1935 {
1936 return CrossOriginResourcePolicy::Allowed;
1937 }
1938 };
1939
1940 if policy == "same-site" {
1942 return CrossOriginResourcePolicy::Blocked;
1943 }
1944
1945 CrossOriginResourcePolicy::Allowed
1946}
1947
1948struct ResponseEndTimer(Option<Arc<Mutex<ResourceFetchTiming>>>);
1950
1951impl ResponseEndTimer {
1952 fn neuter(&mut self) {
1953 self.0 = None;
1954 }
1955}
1956
1957impl Drop for ResponseEndTimer {
1958 fn drop(&mut self) {
1959 let ResponseEndTimer(resource_fetch_timing_opt) = self;
1960
1961 resource_fetch_timing_opt.as_ref().map_or((), |t| {
1962 t.lock()
1963 .unwrap()
1964 .set_attribute(ResourceAttribute::ResponseEnd);
1965 })
1966 }
1967}
1968
1969async fn http_network_fetch(
1971 fetch_params: &mut FetchParams,
1972 credentials_flag: bool,
1973 done_chan: &mut DoneChannel,
1974 context: &FetchContext,
1975) -> Response {
1976 let mut response_end_timer = ResponseEndTimer(Some(context.timing.clone()));
1977
1978 let request = &mut fetch_params.request;
1980
1981 let url = request.current_url();
1992 let request_id = request.id.0.to_string();
1993 if log_enabled!(log::Level::Info) {
1994 info!("{:?} request for {}", request.method, url);
1995 for header in request.headers.iter() {
1996 debug!(" - {:?}", header);
1997 }
1998 }
1999
2000 let is_xhr = request.destination == Destination::None;
2004
2005 let (fetch_terminated_sender, mut fetch_terminated_receiver) = unbounded_channel();
2007
2008 let body = request.body.as_ref().map(|body| body.take_stream());
2009
2010 if body.is_none() {
2011 let _ = fetch_terminated_sender.send(false);
2016 }
2017
2018 let browsing_context_id = request.target_webview_id.map(Into::into);
2019
2020 let (res, msg) = match &request.mode {
2021 RequestMode::WebSocket {
2022 protocols,
2023 original_url: _,
2024 } => {
2025 let (resource_event_sender, dom_action_receiver) = {
2028 let mut websocket_chan = context.websocket_chan.as_ref().unwrap().lock().unwrap();
2029 (
2030 websocket_chan.sender.clone(),
2031 websocket_chan.receiver.take().unwrap(),
2032 )
2033 };
2034
2035 let mut tls_config = create_tls_config(
2036 context.ca_certificates.clone(),
2037 context.ignore_certificate_errors,
2038 context.state.override_manager.clone(),
2039 );
2040 tls_config.alpn_protocols = vec!["http/1.1".to_string().into()];
2041
2042 let response = match start_websocket(
2043 context.state.clone(),
2044 resource_event_sender,
2045 protocols,
2046 request,
2047 tls_config,
2048 dom_action_receiver,
2049 )
2050 .await
2051 {
2052 Ok(response) => response,
2053 Err(e) => {
2054 return Response::network_internal_error(e.to_string());
2055 },
2056 };
2057
2058 let response = response.map(|r| match r {
2059 Some(body) => Full::from(body).map_err(|_| unreachable!()).boxed(),
2060 None => http_body_util::Empty::new()
2061 .map_err(|_| unreachable!())
2062 .boxed(),
2063 });
2064 (Decoder::detect(response, url.is_secure_scheme()), None)
2065 },
2066 _ => {
2067 let response_future = obtain_response(
2068 &context.state.client,
2069 &url,
2070 &request.method,
2071 &mut request.headers,
2072 body,
2073 request
2074 .body
2075 .as_ref()
2076 .is_some_and(|body| body.source_is_null()),
2077 &request.pipeline_id,
2078 Some(&request_id),
2079 request.destination,
2080 is_xhr,
2081 context,
2082 fetch_terminated_sender,
2083 browsing_context_id,
2084 );
2085
2086 let (res, msg) = match response_future.await {
2088 Ok(wrapped_response) => wrapped_response,
2089 Err(error) => return Response::network_error(error),
2090 };
2091 (res, msg)
2092 },
2093 };
2094
2095 if log_enabled!(log::Level::Info) {
2096 debug!("{:?} response for {}", res.version(), url);
2097 for header in res.headers().iter() {
2098 debug!(" - {:?}", header);
2099 }
2100 }
2101
2102 match fetch_terminated_receiver.recv().await {
2105 Some(true) => {
2106 return Response::network_error(NetworkError::Internal(
2107 "Request body streaming failed.".into(),
2108 ));
2109 },
2110 Some(false) => {},
2111 _ => warn!("Failed to receive confirmation request was streamed without error."),
2112 }
2113
2114 let header_strings: Vec<&str> = res
2115 .headers()
2116 .get_all("Timing-Allow-Origin")
2117 .iter()
2118 .map(|header_value| header_value.to_str().unwrap_or(""))
2119 .collect();
2120 let wildcard_present = header_strings.contains(&"*");
2121 let req_origin_in_timing_allow = header_strings
2125 .iter()
2126 .any(|header_str| match request.origin {
2127 SpecificOrigin(ref immutable_request_origin) => {
2128 *header_str == immutable_request_origin.ascii_serialization()
2129 },
2130 _ => false,
2131 });
2132
2133 let is_same_origin = request.url_list.iter().all(|url| match request.origin {
2134 SpecificOrigin(ref immutable_request_origin) => url.origin() == *immutable_request_origin,
2135 _ => false,
2136 });
2137
2138 if !(is_same_origin || req_origin_in_timing_allow || wildcard_present) {
2139 context.timing.lock().unwrap().mark_timing_check_failed();
2140 }
2141
2142 let timing = context.timing.lock().unwrap().clone();
2143 let mut response = Response::new(url.clone(), timing);
2144
2145 let status_text = res
2146 .extensions()
2147 .get::<ReasonPhrase>()
2148 .map(ReasonPhrase::as_bytes)
2149 .or_else(|| res.status().canonical_reason().map(str::as_bytes))
2150 .map(Vec::from)
2151 .unwrap_or_default();
2152 response.status = HttpStatus::new(res.status(), status_text);
2153
2154 info!("got {:?} response for {:?}", res.status(), request.url());
2155 response.headers = res.headers().clone();
2156 response.referrer = request.referrer.to_url().cloned();
2157 response.referrer_policy = request.referrer_policy;
2158
2159 let res_body = response.body.clone();
2160
2161 let (done_sender, done_receiver) = unbounded_channel();
2163 *done_chan = Some((done_sender.clone(), done_receiver));
2164
2165 let devtools_sender = context.devtools_chan.clone();
2166 let cancellation_listener = context.cancellation_listener.clone();
2167 if cancellation_listener.cancelled() {
2168 return Response::network_error(NetworkError::Internal("Fetch aborted".into()));
2169 }
2170
2171 *res_body.lock().unwrap() = ResponseBody::Receiving(vec![]);
2172 let res_body2 = res_body.clone();
2173
2174 if let Some(ref sender) = devtools_sender {
2175 let sender = sender.lock().unwrap();
2176 if let Some(m) = msg {
2177 send_request_to_devtools(m, &sender);
2178 }
2179 }
2180
2181 let done_sender2 = done_sender.clone();
2182 let done_sender3 = done_sender.clone();
2183 let timing_ptr2 = context.timing.clone();
2184 let timing_ptr3 = context.timing.clone();
2185 let devtools_request = request.clone();
2186 let url1 = devtools_request.url();
2187 let url2 = url1.clone();
2188
2189 let status = response.status.clone();
2190 let headers = response.headers.clone();
2191 let devtools_chan = context.devtools_chan.clone();
2192
2193 spawn_task(
2194 res.into_body()
2195 .map_err(|e| {
2196 warn!("Error streaming response body: {:?}", e);
2197 })
2198 .try_fold(res_body, move |res_body, chunk| {
2199 if cancellation_listener.cancelled() {
2200 *res_body.lock().unwrap() = ResponseBody::Done(vec![]);
2201 let _ = done_sender.send(Data::Cancelled);
2202 return future::ready(Err(()));
2203 }
2204 if let ResponseBody::Receiving(ref mut body) = *res_body.lock().unwrap() {
2205 let bytes = chunk;
2206 body.extend_from_slice(&bytes);
2207 let _ = done_sender.send(Data::Payload(bytes.to_vec()));
2208 }
2209 future::ready(Ok(res_body))
2210 })
2211 .and_then(move |res_body| {
2212 debug!("successfully finished response for {:?}", url1);
2213 let mut body = res_body.lock().unwrap();
2214 let completed_body = match *body {
2215 ResponseBody::Receiving(ref mut body) => std::mem::take(body),
2216 _ => vec![],
2217 };
2218 let devtools_response_body = completed_body.clone();
2219 *body = ResponseBody::Done(completed_body);
2220 send_response_values_to_devtools(
2221 Some(headers),
2222 status,
2223 Some(devtools_response_body),
2224 CacheState::None,
2225 &devtools_request,
2226 devtools_chan,
2227 );
2228 timing_ptr2
2229 .lock()
2230 .unwrap()
2231 .set_attribute(ResourceAttribute::ResponseEnd);
2232 let _ = done_sender2.send(Data::Done);
2233 future::ready(Ok(()))
2234 })
2235 .map_err(move |_| {
2236 debug!("finished response for {:?}", url2);
2237 let mut body = res_body2.lock().unwrap();
2238 let completed_body = match *body {
2239 ResponseBody::Receiving(ref mut body) => std::mem::take(body),
2240 _ => vec![],
2241 };
2242 *body = ResponseBody::Done(completed_body);
2243 timing_ptr3
2244 .lock()
2245 .unwrap()
2246 .set_attribute(ResourceAttribute::ResponseEnd);
2247 let _ = done_sender3.send(Data::Done);
2248 }),
2249 );
2250
2251 response.https_state = match url.scheme() {
2257 "https" => HttpsState::Modern,
2258 _ => HttpsState::None,
2259 };
2260
2261 if credentials_flag {
2274 set_cookies_from_headers(&url, &response.headers, &context.state.cookie_jar);
2275 }
2276 context
2277 .state
2278 .hsts_list
2279 .write()
2280 .unwrap()
2281 .update_hsts_list_from_response(&url, &response.headers);
2282
2283 response_end_timer.neuter();
2297
2298 response
2299}
2300
2301async fn cors_preflight_fetch(
2303 request: &Request,
2304 cache: &mut CorsCache,
2305 context: &FetchContext,
2306) -> Response {
2307 let mut preflight = RequestBuilder::new(
2312 request.target_webview_id,
2313 request.current_url(),
2314 request.referrer.clone(),
2315 )
2316 .method(Method::OPTIONS)
2317 .origin(match &request.origin {
2318 Origin::Client => {
2319 unreachable!("We shouldn't get Client origin in cors_preflight_fetch.")
2320 },
2321 Origin::Origin(origin) => origin.clone(),
2322 })
2323 .pipeline_id(request.pipeline_id)
2324 .initiator(request.initiator)
2325 .destination(request.destination)
2326 .referrer_policy(request.referrer_policy)
2327 .mode(RequestMode::CorsMode)
2328 .response_tainting(ResponseTainting::CorsTainting)
2329 .policy_container(match &request.policy_container {
2330 RequestPolicyContainer::Client => {
2331 unreachable!("We should have a policy container for request in cors_preflight_fetch")
2332 },
2333 RequestPolicyContainer::PolicyContainer(policy_container) => policy_container.clone(),
2334 })
2335 .build();
2336
2337 preflight
2339 .headers
2340 .insert(ACCEPT, HeaderValue::from_static("*/*"));
2341
2342 preflight
2344 .headers
2345 .typed_insert::<AccessControlRequestMethod>(AccessControlRequestMethod::from(
2346 request.method.clone(),
2347 ));
2348
2349 let headers = get_cors_unsafe_header_names(&request.headers);
2351
2352 if !headers.is_empty() {
2354 preflight.headers.insert(
2357 ACCESS_CONTROL_REQUEST_HEADERS,
2358 HeaderValue::from_bytes(itertools::join(headers.iter(), ",").as_bytes())
2359 .unwrap_or(HeaderValue::from_static("")),
2360 );
2361 }
2362
2363 let mut fetch_params = FetchParams::new(preflight);
2366 let response =
2367 http_network_or_cache_fetch(&mut fetch_params, false, false, &mut None, context).await;
2368
2369 if cors_check(request, &response).is_ok() && response.status.code().is_success() {
2371 let mut methods = if response
2374 .headers
2375 .contains_key(header::ACCESS_CONTROL_ALLOW_METHODS)
2376 {
2377 match response.headers.typed_get::<AccessControlAllowMethods>() {
2378 Some(methods) => methods.iter().collect(),
2379 None => {
2381 return Response::network_error(NetworkError::Internal(
2382 "CORS ACAM check failed".into(),
2383 ));
2384 },
2385 }
2386 } else {
2387 vec![]
2388 };
2389
2390 let header_names = if response
2393 .headers
2394 .contains_key(header::ACCESS_CONTROL_ALLOW_HEADERS)
2395 {
2396 match response.headers.typed_get::<AccessControlAllowHeaders>() {
2397 Some(names) => names.iter().collect(),
2398 None => {
2400 return Response::network_error(NetworkError::Internal(
2401 "CORS ACAH check failed".into(),
2402 ));
2403 },
2404 }
2405 } else {
2406 vec![]
2407 };
2408
2409 debug!(
2410 "CORS check: Allowed methods: {:?}, current method: {:?}",
2411 methods, request.method
2412 );
2413
2414 if methods.is_empty() && request.use_cors_preflight {
2417 methods = vec![request.method.clone()];
2418 }
2419
2420 if methods
2423 .iter()
2424 .all(|method| *method.as_str() != *request.method.as_ref()) &&
2425 !is_cors_safelisted_method(&request.method) &&
2426 (request.credentials_mode == CredentialsMode::Include ||
2427 methods.iter().all(|method| method.as_ref() != "*"))
2428 {
2429 return Response::network_error(NetworkError::Internal(
2430 "CORS method check failed".into(),
2431 ));
2432 }
2433
2434 debug!(
2435 "CORS check: Allowed headers: {:?}, current headers: {:?}",
2436 header_names, request.headers
2437 );
2438
2439 if request.headers.iter().any(|(name, _)| {
2442 is_cors_non_wildcard_request_header_name(name) &&
2443 header_names.iter().all(|header_name| header_name != name)
2444 }) {
2445 return Response::network_error(NetworkError::Internal(
2446 "CORS authorization check failed".into(),
2447 ));
2448 }
2449
2450 let unsafe_names = get_cors_unsafe_header_names(&request.headers);
2454 let header_names_set: HashSet<&HeaderName> = HashSet::from_iter(header_names.iter());
2455 let header_names_contains_star = header_names
2456 .iter()
2457 .any(|header_name| header_name.as_str() == "*");
2458 for unsafe_name in unsafe_names.iter() {
2459 if !header_names_set.contains(unsafe_name) &&
2460 (request.credentials_mode == CredentialsMode::Include ||
2461 !header_names_contains_star)
2462 {
2463 return Response::network_error(NetworkError::Internal(
2464 "CORS headers check failed".into(),
2465 ));
2466 }
2467 }
2468
2469 let max_age: Option<Duration> = response
2472 .headers
2473 .typed_get::<AccessControlMaxAge>()
2474 .map(|acma| acma.into());
2475
2476 let max_age = max_age.unwrap_or(Duration::from_secs(5));
2478
2479 for method in &methods {
2490 cache.match_method_and_update(request, method.clone(), max_age);
2491 }
2492
2493 for header_name in &header_names {
2498 cache.match_header_and_update(request, header_name, max_age);
2499 }
2500
2501 return response;
2503 }
2504
2505 Response::network_error(NetworkError::Internal("CORS check failed".into()))
2507}
2508
2509fn cors_check(request: &Request, response: &Response) -> Result<(), ()> {
2511 let origin = response.headers.typed_get::<AccessControlAllowOrigin>();
2513
2514 let origin = origin.ok_or(())?;
2516
2517 if request.credentials_mode != CredentialsMode::Include &&
2519 origin == AccessControlAllowOrigin::ANY
2520 {
2521 return Ok(());
2522 }
2523
2524 let origin = match origin.origin() {
2526 Some(origin) => origin,
2527 None => return Err(()),
2529 };
2530
2531 match request.origin {
2532 Origin::Origin(ref o) if o.ascii_serialization() == origin.to_string().trim() => {},
2533 _ => return Err(()),
2534 }
2535
2536 if request.credentials_mode != CredentialsMode::Include {
2538 return Ok(());
2539 }
2540
2541 let credentials = response
2543 .headers
2544 .typed_get::<AccessControlAllowCredentials>();
2545
2546 if credentials.is_some() {
2548 return Ok(());
2549 }
2550
2551 Err(())
2553}
2554
2555fn has_credentials(url: &ServoUrl) -> bool {
2556 !url.username().is_empty() || url.password().is_some()
2557}
2558
2559fn is_no_store_cache(headers: &HeaderMap) -> bool {
2560 headers.contains_key(header::IF_MODIFIED_SINCE) |
2561 headers.contains_key(header::IF_NONE_MATCH) |
2562 headers.contains_key(header::IF_UNMODIFIED_SINCE) |
2563 headers.contains_key(header::IF_MATCH) |
2564 headers.contains_key(header::IF_RANGE)
2565}
2566
2567fn is_redirect_status(status: StatusCode) -> bool {
2569 matches!(
2570 status,
2571 StatusCode::MOVED_PERMANENTLY |
2572 StatusCode::FOUND |
2573 StatusCode::SEE_OTHER |
2574 StatusCode::TEMPORARY_REDIRECT |
2575 StatusCode::PERMANENT_REDIRECT
2576 )
2577}
2578
2579fn request_has_redirect_tainted_origin(request: &Request) -> bool {
2581 let Origin::Origin(request_origin) = &request.origin else {
2583 panic!("origin cannot be \"client\" at this point in time");
2584 };
2585
2586 let mut last_url = None;
2588
2589 for url in &request.url_list {
2591 let Some(last_url) = &mut last_url else {
2593 last_url = Some(url);
2594 continue;
2595 };
2596
2597 if url.origin() != last_url.origin() && *request_origin != last_url.origin() {
2600 return true;
2601 }
2602
2603 *last_url = url;
2605 }
2606
2607 false
2609}
2610
2611fn serialize_request_origin(request: &Request) -> headers::Origin {
2613 let Origin::Origin(origin) = &request.origin else {
2615 panic!("origin cannot be \"client\" at this point in time");
2616 };
2617
2618 if request_has_redirect_tainted_origin(request) {
2620 return headers::Origin::NULL;
2621 }
2622
2623 serialize_origin(origin)
2625}
2626
2627pub fn serialize_origin(origin: &ImmutableOrigin) -> headers::Origin {
2629 match origin {
2630 ImmutableOrigin::Opaque(_) => headers::Origin::NULL,
2631 ImmutableOrigin::Tuple(scheme, host, port) => {
2632 let port = match (scheme.as_ref(), port) {
2635 ("http" | "ws", 80) | ("https" | "wss", 443) | ("ftp", 21) => None,
2636 _ => Some(*port),
2637 };
2638
2639 headers::Origin::try_from_parts(scheme, &host.to_string(), port)
2641 .unwrap_or(headers::Origin::NULL)
2642 },
2643 }
2644}
2645
2646fn append_a_request_origin_header(request: &mut Request) {
2648 let Origin::Origin(request_origin) = &request.origin else {
2650 panic!("origin cannot be \"client\" at this point in time");
2651 };
2652
2653 let mut serialized_origin = serialize_request_origin(request);
2655
2656 if request.response_tainting == ResponseTainting::CorsTainting ||
2659 matches!(request.mode, RequestMode::WebSocket { .. })
2660 {
2661 request.headers.typed_insert(serialized_origin);
2662 }
2663 else if !matches!(request.method, Method::GET | Method::HEAD) {
2665 if request.mode != RequestMode::CorsMode {
2667 match request.referrer_policy {
2668 ReferrerPolicy::NoReferrer => {
2669 serialized_origin = headers::Origin::NULL;
2671 },
2672 ReferrerPolicy::NoReferrerWhenDowngrade |
2673 ReferrerPolicy::StrictOrigin |
2674 ReferrerPolicy::StrictOriginWhenCrossOrigin => {
2675 if let ImmutableOrigin::Tuple(scheme, _, _) = &request_origin {
2678 if scheme == "https" && request.current_url().scheme() != "https" {
2679 serialized_origin = headers::Origin::NULL;
2680 }
2681 }
2682 },
2683 ReferrerPolicy::SameOrigin => {
2684 if *request_origin != request.current_url().origin() {
2687 serialized_origin = headers::Origin::NULL;
2688 }
2689 },
2690 _ => {
2691 },
2693 };
2694 }
2695
2696 request.headers.typed_insert(serialized_origin);
2698 }
2699}
2700
2701fn append_the_fetch_metadata_headers(r: &mut Request) {
2703 if !r.url().is_potentially_trustworthy() {
2705 return;
2706 }
2707
2708 set_the_sec_fetch_dest_header(r);
2710
2711 set_the_sec_fetch_mode_header(r);
2713
2714 set_the_sec_fetch_site_header(r);
2716
2717 set_the_sec_fetch_user_header(r);
2719}
2720
2721fn set_the_sec_fetch_dest_header(r: &mut Request) {
2723 debug_assert!(r.url().is_potentially_trustworthy());
2725
2726 let header = r.destination;
2730
2731 r.headers.typed_insert(SecFetchDest(header));
2733}
2734
2735fn set_the_sec_fetch_mode_header(r: &mut Request) {
2737 debug_assert!(r.url().is_potentially_trustworthy());
2739
2740 let header = &r.mode;
2743
2744 r.headers.typed_insert(SecFetchMode::from(header));
2746}
2747
2748fn set_the_sec_fetch_site_header(r: &mut Request) {
2750 let Origin::Origin(request_origin) = &r.origin else {
2753 panic!("request origin cannot be \"client\" at this point")
2754 };
2755
2756 debug_assert!(r.url().is_potentially_trustworthy());
2758
2759 let mut header = SecFetchSite::SameOrigin;
2762
2763 if header != SecFetchSite::None {
2768 for url in &r.url_list {
2769 if url.origin() == *request_origin {
2771 continue;
2772 }
2773
2774 header = SecFetchSite::CrossSite;
2776
2777 if !is_same_site(request_origin, &url.origin()) {
2779 break;
2780 }
2781
2782 header = SecFetchSite::SameSite;
2784 }
2785 }
2786
2787 r.headers.typed_insert(header);
2789}
2790
2791fn set_the_sec_fetch_user_header(r: &mut Request) {
2793 debug_assert!(r.url().is_potentially_trustworthy());
2795
2796 if !r.is_navigation_request() {
2799 return;
2800 }
2801
2802 let header = SecFetchUser;
2805
2806 r.headers.typed_insert(header);
2808}
2809
2810fn set_requests_referrer_policy_on_redirect(request: &mut Request, response: &Response) {
2812 let referrer_policy: ReferrerPolicy = response
2815 .headers
2816 .typed_get::<headers::ReferrerPolicy>()
2817 .into();
2818
2819 if referrer_policy != ReferrerPolicy::EmptyString {
2821 request.referrer_policy = referrer_policy;
2822 }
2823}