1use std::collections::HashSet;
6use std::iter::FromIterator;
7use std::sync::Arc as StdArc;
8use std::time::{Duration, SystemTime, UNIX_EPOCH};
9
10use async_recursion::async_recursion;
11use crossbeam_channel::Sender;
12use devtools_traits::{
13 ChromeToDevtoolsControlMsg, DevtoolsControlMsg, HttpRequest as DevtoolsHttpRequest,
14 HttpResponse as DevtoolsHttpResponse, NetworkEvent, SecurityInfoUpdate,
15};
16use embedder_traits::{AuthenticationResponse, GenericEmbedderProxy};
17use futures::{TryFutureExt, TryStreamExt, future};
18use headers::authorization::Basic;
19use headers::{
20 AccessControlAllowCredentials, AccessControlAllowHeaders, AccessControlAllowMethods,
21 AccessControlMaxAge, AccessControlRequestMethod, Authorization, CacheControl, ContentLength,
22 HeaderMapExt, IfModifiedSince, LastModified, Pragma, Referer, StrictTransportSecurity,
23 UserAgent,
24};
25use http::header::{
26 self, ACCEPT, ACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_REQUEST_HEADERS, AUTHORIZATION,
27 CONTENT_ENCODING, CONTENT_LANGUAGE, CONTENT_LOCATION, CONTENT_TYPE, HeaderValue, RANGE,
28 WWW_AUTHENTICATE,
29};
30use http::{HeaderMap, Method, Request as HyperRequest, StatusCode};
31use http_body_util::combinators::BoxBody;
32use http_body_util::{BodyExt, Full};
33use hyper::Response as HyperResponse;
34use hyper::body::{Bytes, Frame};
35use hyper::ext::ReasonPhrase;
36use hyper::header::{HeaderName, TRANSFER_ENCODING};
37use hyper_serde::Serde;
38use ipc_channel::IpcError;
39use ipc_channel::ipc::{self, IpcSender};
40use ipc_channel::router::ROUTER;
41use log::{debug, error, info, log_enabled, warn};
42use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
43use net_traits::fetch::headers::get_value_from_header_list;
44use net_traits::http_status::HttpStatus;
45use net_traits::policy_container::RequestPolicyContainer;
46use net_traits::pub_domains::{is_same_site, reg_suffix};
47use net_traits::request::Origin::Origin as SpecificOrigin;
48use net_traits::request::{
49 BodyChunkRequest, BodyChunkResponse, CacheMode, CredentialsMode, Destination, Initiator,
50 Origin, RedirectMode, Referrer, Request, RequestBuilder, RequestMode, ResponseTainting,
51 ServiceWorkersMode, TraversableForUserPrompts, get_cors_unsafe_header_names,
52 is_cors_non_wildcard_request_header_name, is_cors_safelisted_method,
53 is_cors_safelisted_request_header,
54};
55use net_traits::response::{
56 CacheState, HttpsState, RedirectTaint, Response, ResponseBody, ResponseType,
57};
58use net_traits::{
59 CookieSource, DOCUMENT_ACCEPT_HEADER_VALUE, DebugVec, FetchMetadata, NetworkError,
60 RedirectEndValue, RedirectStartValue, ReferrerPolicy, ResourceAttribute, ResourceFetchTiming,
61 ResourceTimeValue, TlsSecurityInfo, TlsSecurityState,
62};
63use parking_lot::{Mutex, RwLock};
64use profile_traits::mem::{Report, ReportKind};
65use profile_traits::path;
66use rustc_hash::FxHashMap;
67use servo_arc::Arc;
68use servo_base::cross_process_instant::CrossProcessInstant;
69use servo_base::generic_channel::GenericSharedMemory;
70use servo_base::id::{BrowsingContextId, HistoryStateId, PipelineId};
71use servo_url::{ImmutableOrigin, ServoUrl};
72use tokio::sync::mpsc::{
73 Receiver as TokioReceiver, Sender as TokioSender, UnboundedReceiver, UnboundedSender, channel,
74 unbounded_channel,
75};
76use tokio_stream::wrappers::ReceiverStream;
77
78use crate::async_runtime::spawn_task;
79use crate::connector::{
80 CertificateErrorOverrideManager, ServoClient, TlsHandshakeInfo, create_tls_config,
81};
82use crate::cookie::ServoCookie;
83use crate::cookie_storage::CookieStorage;
84use crate::decoder::Decoder;
85use crate::embedder::NetToEmbedderMsg;
86use crate::fetch::cors_cache::CorsCache;
87use crate::fetch::fetch_params::FetchParams;
88use crate::fetch::headers::{SecFetchDest, SecFetchMode, SecFetchSite, SecFetchUser};
89use crate::fetch::methods::{Data, DoneChannel, FetchContext, Target, main_fetch};
90use crate::hsts::HstsList;
91use crate::http_cache::{
92 CacheKey, CachedResourcesOrGuard, HttpCache, construct_response, invalidate, refresh,
93};
94use crate::resource_thread::{AuthCache, AuthCacheEntry};
95use crate::websocket_loader::start_websocket;
96
97#[derive(Clone, Debug, Eq, PartialEq)]
99pub enum HttpCacheEntryState {
100 ReadyToConstruct,
104 PendingStore(usize),
106}
107
108pub struct HttpState {
109 pub hsts_list: RwLock<HstsList>,
110 pub cookie_jar: RwLock<CookieStorage>,
111 pub http_cache: HttpCache,
112 pub auth_cache: RwLock<AuthCache>,
113 pub history_states: RwLock<FxHashMap<HistoryStateId, Vec<u8>>>,
114 pub client: ServoClient,
115 pub override_manager: CertificateErrorOverrideManager,
116 pub embedder_proxy: GenericEmbedderProxy<NetToEmbedderMsg>,
117}
118
119impl HttpState {
120 pub(crate) fn memory_reports(&self, suffix: &str, ops: &mut MallocSizeOfOps) -> Vec<Report> {
121 vec![
122 Report {
123 path: path!["memory-cache", suffix],
124 kind: ReportKind::ExplicitJemallocHeapSize,
125 size: self.http_cache.size_of(ops),
126 },
127 Report {
128 path: path!["hsts-list", suffix],
129 kind: ReportKind::ExplicitJemallocHeapSize,
130 size: self.hsts_list.read().size_of(ops),
131 },
132 ]
133 }
134
135 async fn request_authentication(
136 &self,
137 request: &Request,
138 response: &Response,
139 ) -> Option<AuthenticationResponse> {
140 let webview_id = request.target_webview_id?;
142 let for_proxy = response.status == StatusCode::PROXY_AUTHENTICATION_REQUIRED;
143
144 if request.mode != RequestMode::Navigate {
146 return None;
147 }
148
149 let (sender, receiver) = tokio::sync::oneshot::channel();
150 self.embedder_proxy
151 .send(NetToEmbedderMsg::RequestAuthentication(
152 webview_id,
153 request.url(),
154 for_proxy,
155 sender,
156 ));
157 receiver.await.ok()?
158 }
159}
160
161pub(crate) fn set_default_accept(request: &mut Request) {
163 if request.headers.contains_key(header::ACCEPT) {
165 return;
166 }
167
168 let value = if request.initiator == Initiator::Prefetch {
170 DOCUMENT_ACCEPT_HEADER_VALUE
171 } else {
172 match request.destination {
175 Destination::Document | Destination::Frame | Destination::IFrame => {
176 DOCUMENT_ACCEPT_HEADER_VALUE
177 },
178 Destination::Image => {
179 HeaderValue::from_static("image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5")
180 },
181 Destination::Json => HeaderValue::from_static("application/json,*/*;q=0.5"),
182 Destination::Style => HeaderValue::from_static("text/css,*/*;q=0.1"),
183 _ => HeaderValue::from_static("*/*"),
185 }
186 };
187
188 request.headers.insert(header::ACCEPT, value);
190}
191
192fn set_default_accept_encoding(headers: &mut HeaderMap) {
193 if headers.contains_key(header::ACCEPT_ENCODING) {
194 return;
195 }
196
197 headers.insert(
199 header::ACCEPT_ENCODING,
200 HeaderValue::from_static("gzip, deflate, br, zstd"),
201 );
202}
203
204fn no_referrer_when_downgrade(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
206 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
208 return None;
209 }
210 strip_url_for_use_as_referrer(referrer_url, false)
212}
213
214fn strict_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
216 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
218 return None;
219 }
220 strip_url_for_use_as_referrer(referrer_url, true)
222}
223
224fn strict_origin_when_cross_origin(
226 referrer_url: ServoUrl,
227 current_url: ServoUrl,
228) -> Option<ServoUrl> {
229 if referrer_url.origin() == current_url.origin() {
231 return strip_url_for_use_as_referrer(referrer_url, false);
232 }
233 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
235 return None;
236 }
237 strip_url_for_use_as_referrer(referrer_url, true)
239}
240
241fn is_schemelessy_same_site(site_a: &ImmutableOrigin, site_b: &ImmutableOrigin) -> bool {
243 if !site_a.is_tuple() && !site_b.is_tuple() && site_a == site_b {
245 true
246 } else if site_a.is_tuple() && site_b.is_tuple() {
247 let host_a = site_a.host().map(|h| h.to_string()).unwrap_or_default();
249 let host_b = site_b.host().map(|h| h.to_string()).unwrap_or_default();
250
251 let host_a_reg = reg_suffix(&host_a);
252 let host_b_reg = reg_suffix(&host_b);
253
254 (site_a.host() == site_b.host() && host_a_reg.is_empty()) ||
256 (host_a_reg == host_b_reg && !host_a_reg.is_empty())
257 } else {
258 false
260 }
261}
262
263fn strip_url_for_use_as_referrer(mut url: ServoUrl, origin_only: bool) -> Option<ServoUrl> {
265 const MAX_REFERRER_URL_LENGTH: usize = 4096;
266 if url.is_local_scheme() {
268 return None;
269 }
270 {
272 let url = url.as_mut_url();
273 let _ = url.set_username("");
274 let _ = url.set_password(None);
275 url.set_fragment(None);
276 if origin_only || url.as_str().len() > MAX_REFERRER_URL_LENGTH {
280 url.set_path("");
281 url.set_query(None);
282 }
283 }
284 Some(url)
286}
287
288fn same_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
290 if referrer_url.origin() == current_url.origin() {
292 return strip_url_for_use_as_referrer(referrer_url, false);
293 }
294 None
296}
297
298fn origin_when_cross_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
300 if referrer_url.origin() == current_url.origin() {
302 return strip_url_for_use_as_referrer(referrer_url, false);
303 }
304 strip_url_for_use_as_referrer(referrer_url, true)
306}
307
308pub fn determine_requests_referrer(
310 referrer_policy: ReferrerPolicy,
311 referrer_source: ServoUrl,
312 current_url: ServoUrl,
313) -> Option<ServoUrl> {
314 match referrer_policy {
315 ReferrerPolicy::EmptyString | ReferrerPolicy::NoReferrer => None,
316 ReferrerPolicy::Origin => strip_url_for_use_as_referrer(referrer_source, true),
317 ReferrerPolicy::UnsafeUrl => strip_url_for_use_as_referrer(referrer_source, false),
318 ReferrerPolicy::StrictOrigin => strict_origin(referrer_source, current_url),
319 ReferrerPolicy::StrictOriginWhenCrossOrigin => {
320 strict_origin_when_cross_origin(referrer_source, current_url)
321 },
322 ReferrerPolicy::SameOrigin => same_origin(referrer_source, current_url),
323 ReferrerPolicy::OriginWhenCrossOrigin => {
324 origin_when_cross_origin(referrer_source, current_url)
325 },
326 ReferrerPolicy::NoReferrerWhenDowngrade => {
327 no_referrer_when_downgrade(referrer_source, current_url)
328 },
329 }
330}
331
332fn set_request_cookies(
333 url: &ServoUrl,
334 headers: &mut HeaderMap,
335 cookie_jar: &RwLock<CookieStorage>,
336) {
337 let mut cookie_jar = cookie_jar.write();
338 cookie_jar.remove_expired_cookies_for_url(url);
339 if let Some(cookie_list) = cookie_jar.cookies_for_url(url, CookieSource::HTTP) {
340 headers.insert(
341 header::COOKIE,
342 HeaderValue::from_bytes(cookie_list.as_bytes()).unwrap(),
343 );
344 }
345}
346
347fn set_cookie_for_url(cookie_jar: &RwLock<CookieStorage>, request: &ServoUrl, cookie_val: &str) {
348 let mut cookie_jar = cookie_jar.write();
349 let source = CookieSource::HTTP;
350
351 if let Some(cookie) = ServoCookie::from_cookie_string(cookie_val, request, source) {
352 cookie_jar.push(cookie, request, source);
353 }
354}
355
356fn set_cookies_from_headers(
357 url: &ServoUrl,
358 headers: &HeaderMap,
359 cookie_jar: &RwLock<CookieStorage>,
360) {
361 for cookie in headers.get_all(header::SET_COOKIE) {
362 let cookie_bytes = cookie.as_bytes();
363 if !ServoCookie::is_valid_name_or_value(cookie_bytes) {
364 continue;
365 }
366 if let Ok(cookie_str) = std::str::from_utf8(cookie_bytes) {
367 set_cookie_for_url(cookie_jar, url, cookie_str);
368 }
369 }
370}
371
372fn build_tls_security_info(handshake: &TlsHandshakeInfo, hsts_enabled: bool) -> TlsSecurityInfo {
373 let state = if handshake.protocol_version.is_none() || handshake.cipher_suite.is_none() {
381 TlsSecurityState::Insecure
383 } else {
384 TlsSecurityState::Secure
386 };
387
388 TlsSecurityInfo {
389 state,
390 weakness_reasons: Vec::new(), protocol_version: handshake.protocol_version.clone(),
392 cipher_suite: handshake.cipher_suite.clone(),
393 kea_group_name: handshake.kea_group_name.clone(),
394 signature_scheme_name: handshake.signature_scheme_name.clone(),
395 alpn_protocol: handshake.alpn_protocol.clone(),
396 certificate_chain_der: handshake.certificate_chain_der.clone(),
397 certificate_transparency: None,
398 hsts: hsts_enabled,
399 hpkp: false,
400 used_ech: handshake.used_ech,
401 used_delegated_credentials: false,
402 used_ocsp: false,
403 used_private_dns: false,
404 }
405}
406
407#[allow(clippy::too_many_arguments)]
408fn prepare_devtools_request(
409 request_id: String,
410 url: ServoUrl,
411 method: Method,
412 headers: HeaderMap,
413 body: Option<Vec<u8>>,
414 pipeline_id: PipelineId,
415 connect_time: Duration,
416 send_time: Duration,
417 destination: Destination,
418 is_xhr: bool,
419 browsing_context_id: BrowsingContextId,
420) -> ChromeToDevtoolsControlMsg {
421 let started_date_time = SystemTime::now();
422 let request = DevtoolsHttpRequest {
423 url,
424 method,
425 headers,
426 body: body.map(DebugVec::from),
427 pipeline_id,
428 started_date_time,
429 time_stamp: started_date_time
430 .duration_since(UNIX_EPOCH)
431 .unwrap_or_default()
432 .as_secs() as i64,
433 connect_time,
434 send_time,
435 destination,
436 is_xhr,
437 browsing_context_id,
438 };
439 let net_event = NetworkEvent::HttpRequestUpdate(request);
440
441 ChromeToDevtoolsControlMsg::NetworkEvent(request_id, net_event)
442}
443
444pub fn send_request_to_devtools(
445 msg: ChromeToDevtoolsControlMsg,
446 devtools_chan: &Sender<DevtoolsControlMsg>,
447) {
448 if matches!(msg, ChromeToDevtoolsControlMsg::NetworkEvent(_, ref network_event) if !network_event.forward_to_devtools())
449 {
450 return;
451 }
452 if let Err(e) = devtools_chan.send(DevtoolsControlMsg::FromChrome(msg)) {
453 error!("DevTools send failed: {e}");
454 }
455}
456
457pub fn send_response_to_devtools(
458 request: &Request,
459 context: &FetchContext,
460 response: &Response,
461 body_data: Option<Vec<u8>>,
462) {
463 let meta = match response.metadata() {
464 Ok(FetchMetadata::Unfiltered(m)) => m,
465 Ok(FetchMetadata::Filtered { unsafe_, .. }) => unsafe_,
466 Err(_) => {
467 log::warn!("No metadata available, skipping devtools response.");
468 return;
469 },
470 };
471 send_response_values_to_devtools(
472 meta.headers.map(Serde::into_inner),
473 meta.status,
474 body_data,
475 response.cache_state,
476 request,
477 context.devtools_chan.clone(),
478 );
479}
480
481#[allow(clippy::too_many_arguments)]
482pub fn send_response_values_to_devtools(
483 headers: Option<HeaderMap>,
484 status: HttpStatus,
485 body: Option<Vec<u8>>,
486 cache_state: CacheState,
487 request: &Request,
488 devtools_chan: Option<Sender<DevtoolsControlMsg>>,
489) {
490 if let (Some(devtools_chan), Some(pipeline_id), Some(webview_id)) = (
491 devtools_chan,
492 request.pipeline_id,
493 request.target_webview_id,
494 ) {
495 let browsing_context_id = webview_id.into();
496 let from_cache = matches!(cache_state, CacheState::Local | CacheState::Validated);
497
498 let devtoolsresponse = DevtoolsHttpResponse {
499 headers,
500 status,
501 body: body.map(DebugVec::from),
502 from_cache,
503 pipeline_id,
504 browsing_context_id,
505 };
506 let net_event_response = NetworkEvent::HttpResponse(devtoolsresponse);
507
508 let msg =
509 ChromeToDevtoolsControlMsg::NetworkEvent(request.id.0.to_string(), net_event_response);
510
511 let _ = devtools_chan.send(DevtoolsControlMsg::FromChrome(msg));
512 }
513}
514
515pub fn send_security_info_to_devtools(
516 request: &Request,
517 context: &FetchContext,
518 response: &Response,
519) {
520 let meta = match response.metadata() {
521 Ok(FetchMetadata::Unfiltered(m)) => m,
522 Ok(FetchMetadata::Filtered { unsafe_, .. }) => unsafe_,
523 Err(_) => {
524 log::warn!("No metadata available, skipping devtools security info.");
525 return;
526 },
527 };
528
529 if let (Some(devtools_chan), Some(security_info), Some(webview_id)) = (
530 context.devtools_chan.clone(),
531 meta.tls_security_info,
532 request.target_webview_id,
533 ) {
534 let update = NetworkEvent::SecurityInfo(SecurityInfoUpdate {
535 browsing_context_id: webview_id.into(),
536 security_info: Some(security_info),
537 });
538
539 let msg = ChromeToDevtoolsControlMsg::NetworkEvent(request.id.0.to_string(), update);
540
541 let _ = devtools_chan.send(DevtoolsControlMsg::FromChrome(msg));
542 }
543}
544
545pub fn send_early_httprequest_to_devtools(request: &Request, context: &FetchContext) {
546 if request.url().scheme() == "data" {
548 return;
549 }
550 if let (Some(devtools_chan), Some(browsing_context_id), Some(pipeline_id)) = (
551 context.devtools_chan.as_ref(),
552 request.target_webview_id.map(|id| id.into()),
553 request.pipeline_id,
554 ) {
555 let devtools_request = DevtoolsHttpRequest {
557 url: request.current_url(),
558 method: request.method.clone(),
559 headers: request.headers.clone(),
560 body: None,
561 pipeline_id,
562 started_date_time: SystemTime::now(),
563 time_stamp: 0,
564 connect_time: Duration::from_millis(0),
565 send_time: Duration::from_millis(0),
566 destination: request.destination,
567 is_xhr: false,
568 browsing_context_id,
569 };
570
571 let msg = ChromeToDevtoolsControlMsg::NetworkEvent(
572 request.id.0.to_string(),
573 NetworkEvent::HttpRequest(devtools_request),
574 );
575
576 send_request_to_devtools(msg, devtools_chan);
577 }
578}
579
580fn auth_from_cache(
581 auth_cache: &RwLock<AuthCache>,
582 origin: &ImmutableOrigin,
583) -> Option<Authorization<Basic>> {
584 if let Some(auth_entry) = auth_cache.read().entries.get(&origin.ascii_serialization()) {
585 let user_name = &auth_entry.user_name;
586 let password = &auth_entry.password;
587 Some(Authorization::basic(user_name, password))
588 } else {
589 None
590 }
591}
592
593enum BodyChunk {
596 Chunk(GenericSharedMemory),
598 Done,
600}
601
602enum BodyStream {
604 Chunked(TokioReceiver<Result<Frame<Bytes>, hyper::Error>>),
607 Buffered(UnboundedReceiver<BodyChunk>),
610}
611
612enum BodySink {
615 Chunked(TokioSender<Result<Frame<Bytes>, hyper::Error>>),
617 Buffered(UnboundedSender<BodyChunk>),
621}
622
623impl BodySink {
624 fn transmit_bytes(&self, bytes: GenericSharedMemory) {
625 match self {
626 BodySink::Chunked(sender) => {
627 let sender = sender.clone();
628 spawn_task(async move {
629 let _ = sender
630 .send(Ok(Frame::data(Bytes::copy_from_slice(&bytes))))
631 .await;
632 });
633 },
634 BodySink::Buffered(sender) => {
635 let _ = sender.send(BodyChunk::Chunk(bytes));
636 },
637 }
638 }
639
640 fn close(&self) {
641 match self {
642 BodySink::Chunked(_) => { },
643 BodySink::Buffered(sender) => {
644 let _ = sender.send(BodyChunk::Done);
645 },
646 }
647 }
648}
649
650fn request_body_stream_closed_error(action: &str) -> NetworkError {
651 NetworkError::Crash(format!(
652 "Request body stream has already been closed while trying to {action}."
653 ))
654}
655
656fn log_request_body_stream_closed(action: &str, error: Option<&IpcError>) {
657 match error {
658 Some(error) => {
659 error!("Request body stream has already been closed while trying to {action}: {error}")
660 },
661 None => error!("Request body stream has already been closed while trying to {action}."),
662 }
663}
664
665fn log_fetch_terminated_send_failure(terminated_with_error: bool, context: &str) {
666 warn!(
667 "Failed to notify request-body stream termination state ({terminated_with_error}) while {context} because the receiver was already dropped."
668 );
669}
670
671#[allow(clippy::too_many_arguments)]
672async fn obtain_response(
673 client: &ServoClient,
674 url: &ServoUrl,
675 method: &Method,
676 request_headers: &mut HeaderMap,
677 body: Option<StdArc<Mutex<Option<IpcSender<BodyChunkRequest>>>>>,
678 source_is_null: bool,
679 pipeline_id: &Option<PipelineId>,
680 request_id: Option<&str>,
681 destination: Destination,
682 is_xhr: bool,
683 context: &FetchContext,
684 fetch_terminated: UnboundedSender<bool>,
685 browsing_context_id: Option<BrowsingContextId>,
686) -> Result<(HyperResponse<Decoder>, Option<ChromeToDevtoolsControlMsg>), NetworkError> {
687 {
688 let mut headers = request_headers.clone();
689
690 let devtools_bytes = StdArc::new(Mutex::new(vec![]));
691
692 let encoded_url = url
694 .clone()
695 .into_url()
696 .as_ref()
697 .replace('|', "%7C")
698 .replace('{', "%7B")
699 .replace('}', "%7D");
700
701 let request = if let Some(chunk_requester) = body {
702 let (sink, stream) = if source_is_null {
703 headers.insert(TRANSFER_ENCODING, HeaderValue::from_static("chunked"));
706
707 let (sender, receiver) = channel(1);
708 (BodySink::Chunked(sender), BodyStream::Chunked(receiver))
709 } else {
710 let (sender, receiver) = unbounded_channel();
717 (BodySink::Buffered(sender), BodyStream::Buffered(receiver))
718 };
719
720 let (body_chan, body_port) = ipc::channel().unwrap();
721
722 {
723 let mut lock = chunk_requester.lock();
724 if let Some(chunk_requester) = lock.as_mut() {
725 if let Err(error) = chunk_requester.send(BodyChunkRequest::Connect(body_chan)) {
726 log_request_body_stream_closed(
727 "connect to the request body stream",
728 Some(&error),
729 );
730 return Err(request_body_stream_closed_error(
731 "connect to the request body stream",
732 ));
733 }
734
735 if let Err(error) = chunk_requester.send(BodyChunkRequest::Chunk) {
738 log_request_body_stream_closed(
739 "request the first request body chunk",
740 Some(&error),
741 );
742 return Err(request_body_stream_closed_error(
743 "request the first request body chunk",
744 ));
745 }
746 } else {
747 log_request_body_stream_closed("connect to the request body stream", None);
748 return Err(request_body_stream_closed_error(
749 "connect to the request body stream",
750 ));
751 }
752 }
753
754 let devtools_bytes = devtools_bytes.clone();
755 let chunk_requester2 = chunk_requester.clone();
756
757 ROUTER.add_typed_route(
758 body_port,
759 Box::new(move |message| {
760 info!("Received message");
761 let bytes = match message.unwrap() {
762 BodyChunkResponse::Chunk(bytes) => bytes,
763 BodyChunkResponse::Done => {
764 if fetch_terminated.send(false).is_err() {
766 log_fetch_terminated_send_failure(
767 false,
768 "handling request body completion",
769 );
770 }
771 sink.close();
772
773 return;
774 },
775 BodyChunkResponse::Error => {
776 if fetch_terminated.send(true).is_err() {
780 log_fetch_terminated_send_failure(
781 true,
782 "handling request body stream error",
783 );
784 }
785 sink.close();
786
787 return;
788 },
789 };
790
791 devtools_bytes.lock().extend_from_slice(&bytes);
792
793 sink.transmit_bytes(bytes);
796
797 let mut chunk_requester2 = chunk_requester2.lock();
800 if let Some(chunk_requester2) = chunk_requester2.as_mut() {
801 if let Err(error) = chunk_requester2.send(BodyChunkRequest::Chunk) {
802 log_request_body_stream_closed(
803 "request the next request body chunk",
804 Some(&error),
805 );
806 if fetch_terminated.send(true).is_err() {
807 log_fetch_terminated_send_failure(
808 true,
809 "handling failure to request the next request body chunk",
810 );
811 }
812 sink.close();
813 }
814 } else {
815 log_request_body_stream_closed("request the next request body chunk", None);
816 if fetch_terminated.send(true).is_err() {
817 log_fetch_terminated_send_failure(
818 true,
819 "handling a closed request body stream while requesting the next chunk",
820 );
821 }
822 sink.close();
823 }
824 }),
825 );
826
827 let body = match stream {
828 BodyStream::Chunked(receiver) => {
829 let stream = ReceiverStream::new(receiver);
830 BoxBody::new(http_body_util::StreamBody::new(stream))
831 },
832 BodyStream::Buffered(mut receiver) => {
833 let mut body = vec![];
835 loop {
836 match receiver.recv().await {
837 Some(BodyChunk::Chunk(bytes)) => {
838 body.extend_from_slice(&bytes);
839 },
840 Some(BodyChunk::Done) => break,
841 None => warn!("Failed to read all chunks from request body."),
842 }
843 }
844 Full::new(body.into()).map_err(|_| unreachable!()).boxed()
845 },
846 };
847 HyperRequest::builder()
848 .method(method)
849 .uri(encoded_url)
850 .body(body)
851 } else {
852 HyperRequest::builder()
853 .method(method)
854 .uri(encoded_url)
855 .body(
856 http_body_util::Empty::new()
857 .map_err(|_| unreachable!())
858 .boxed(),
859 )
860 };
861
862 context
863 .timing
864 .lock()
865 .set_attribute(ResourceAttribute::DomainLookupStart);
866
867 let connect_start = CrossProcessInstant::now();
870 context
871 .timing
872 .lock()
873 .set_attribute(ResourceAttribute::ConnectStart(connect_start));
874
875 if url.scheme() == "https" {
879 context
880 .timing
881 .lock()
882 .set_attribute(ResourceAttribute::SecureConnectionStart);
883 }
884
885 let mut request = match request {
886 Ok(request) => request,
887 Err(error) => return Err(NetworkError::HttpError(error.to_string())),
888 };
889 *request.headers_mut() = headers.clone();
890
891 let connect_end = CrossProcessInstant::now();
892 context
893 .timing
894 .lock()
895 .set_attribute(ResourceAttribute::ConnectEnd(connect_end));
896
897 let request_id = request_id.map(|v| v.to_owned());
898 let pipeline_id = *pipeline_id;
899 let closure_url = url.clone();
900 let method = method.clone();
901 let send_start = CrossProcessInstant::now();
902
903 let host = request.uri().host().unwrap_or("").to_owned();
904 let override_manager = context.state.override_manager.clone();
905 let headers = headers.clone();
906 let is_secure_scheme = url.is_secure_scheme();
907
908 client
909 .request(request)
910 .and_then(move |res| {
911 let send_end = CrossProcessInstant::now();
912
913 let msg = if let Some(request_id) = request_id {
916 if let Some(pipeline_id) = pipeline_id {
917 if let Some(browsing_context_id) = browsing_context_id {
918 Some(prepare_devtools_request(
919 request_id,
920 closure_url,
921 method.clone(),
922 headers,
923 Some(devtools_bytes.lock().clone()),
924 pipeline_id,
925 (connect_end - connect_start).unsigned_abs(),
926 (send_end - send_start).unsigned_abs(),
927 destination,
928 is_xhr,
929 browsing_context_id,
930 ))
931 } else {
932 debug!("Not notifying devtools (no browsing_context_id)");
933 None
934 }
935 } else {
940 debug!("Not notifying devtools (no pipeline_id)");
941 None
942 }
943 } else {
944 debug!("Not notifying devtools (no request_id)");
945 None
946 };
947
948 future::ready(Ok((
949 Decoder::detect(res.map(|r| r.boxed()), is_secure_scheme),
950 msg,
951 )))
952 })
953 .map_err(move |error| {
954 warn!("network error: {error:?}");
955 NetworkError::from_hyper_error(
956 &error,
957 override_manager.remove_certificate_failing_verification(host.as_str()),
958 )
959 })
960 .await
961 }
962}
963
964#[async_recursion]
966#[allow(clippy::too_many_arguments)]
967pub async fn http_fetch(
968 fetch_params: &mut FetchParams,
969 cache: &mut CorsCache,
970 cors_flag: bool,
971 cors_preflight_flag: bool,
972 authentication_fetch_flag: bool,
973 target: Target<'async_recursion>,
974 done_chan: &mut DoneChannel,
975 context: &FetchContext,
976) -> Response {
977 *done_chan = None;
979 let request = &mut fetch_params.request;
981
982 let mut response: Option<Response> = None;
985
986 if request.service_workers_mode == ServiceWorkersMode::All {
988 if let Some(ref res) = response {
993 if (res.response_type == ResponseType::Opaque && request.mode != RequestMode::NoCors) ||
1001 (res.response_type == ResponseType::OpaqueRedirect &&
1002 request.redirect_mode != RedirectMode::Manual) ||
1003 (res.url_list.len() > 1 && request.redirect_mode != RedirectMode::Follow) ||
1004 res.is_network_error()
1005 {
1006 return Response::network_error(NetworkError::ConnectionFailure);
1007 }
1008
1009 }
1012 }
1013
1014 if response.is_none() {
1016 if cors_preflight_flag {
1018 let method_cache_match = cache.match_method(request, request.method.clone());
1019
1020 let method_mismatch = !method_cache_match &&
1021 (!is_cors_safelisted_method(&request.method) || request.use_cors_preflight);
1022 let header_mismatch = request.headers.iter().any(|(name, value)| {
1023 !cache.match_header(request, name) &&
1024 !is_cors_safelisted_request_header(&name, &value)
1025 });
1026
1027 if method_mismatch || header_mismatch {
1029 let preflight_result = cors_preflight_fetch(request, cache, context).await;
1030 if let Some(e) = preflight_result.get_network_error() {
1032 return Response::network_error(e.clone());
1033 }
1034 }
1035 }
1036
1037 if request.redirect_mode == RedirectMode::Follow {
1039 request.service_workers_mode = ServiceWorkersMode::None;
1040 }
1041
1042 context
1046 .timing
1047 .lock()
1048 .set_attribute(ResourceAttribute::RequestStart);
1049
1050 let mut fetch_result = http_network_or_cache_fetch(
1051 fetch_params,
1052 authentication_fetch_flag,
1053 cors_flag,
1054 done_chan,
1055 context,
1056 )
1057 .await;
1058
1059 if cors_flag && cors_check(&fetch_params.request, &fetch_result).is_err() {
1061 return Response::network_error(NetworkError::CorsGeneral);
1062 }
1063
1064 fetch_result.return_internal = false;
1065 response = Some(fetch_result);
1066 }
1067
1068 let request = &mut fetch_params.request;
1069
1070 let mut response = response.unwrap();
1072
1073 if response
1077 .actual_response()
1078 .status
1079 .try_code()
1080 .is_some_and(is_redirect_status)
1081 {
1082 if response.actual_response().status != StatusCode::SEE_OTHER {
1086 }
1088
1089 response = match request.redirect_mode {
1091 RedirectMode::Error => Response::network_error(NetworkError::RedirectError),
1093 RedirectMode::Manual => {
1094 if request.mode == RequestMode::Navigate {
1097 let location_url =
1101 location_url_for_response(&response, request.current_url().fragment());
1102 response.actual_response_mut().location_url = location_url;
1103 response
1104 } else {
1105 response.to_filtered(ResponseType::OpaqueRedirect)
1107 }
1108 },
1109 RedirectMode::Follow => {
1110 response.return_internal = true;
1112 http_redirect_fetch(
1113 fetch_params,
1114 cache,
1115 response,
1116 cors_flag,
1117 target,
1118 done_chan,
1119 context,
1120 )
1121 .await
1122 },
1123 };
1124 }
1125
1126 response.return_internal = true;
1128 context
1129 .timing
1130 .lock()
1131 .set_attribute(ResourceAttribute::RedirectCount(
1132 fetch_params.request.redirect_count as u16,
1133 ));
1134
1135 response.resource_timing = Arc::clone(&context.timing);
1136
1137 response
1139}
1140
1141struct RedirectEndTimer(Option<Arc<Mutex<ResourceFetchTiming>>>);
1143
1144impl RedirectEndTimer {
1145 fn neuter(&mut self) {
1146 self.0 = None;
1147 }
1148}
1149
1150impl Drop for RedirectEndTimer {
1151 fn drop(&mut self) {
1152 let RedirectEndTimer(resource_fetch_timing_opt) = self;
1153
1154 resource_fetch_timing_opt.as_ref().map_or((), |t| {
1155 t.lock()
1156 .set_attribute(ResourceAttribute::RedirectEnd(RedirectEndValue::Zero));
1157 })
1158 }
1159}
1160
1161static REQUEST_BODY_HEADER_NAMES: &[HeaderName] = &[
1163 CONTENT_ENCODING,
1164 CONTENT_LANGUAGE,
1165 CONTENT_LOCATION,
1166 CONTENT_TYPE,
1167];
1168
1169fn location_url_for_response(
1171 response: &Response,
1172 request_fragment: Option<&str>,
1173) -> Option<Result<ServoUrl, String>> {
1174 assert!(
1176 response
1177 .actual_response()
1178 .status
1179 .try_code()
1180 .is_some_and(is_redirect_status)
1181 );
1182 let mut location = response
1184 .actual_response()
1185 .headers
1186 .get(header::LOCATION)
1187 .and_then(|header_value| {
1188 HeaderValue::to_str(header_value)
1189 .map(|location_string| {
1190 ServoUrl::parse_with_base(response.actual_response().url(), location_string)
1192 .map_err(|error| error.to_string())
1193 })
1194 .ok()
1195 });
1196
1197 if let Some(Ok(ref mut location)) = location {
1199 if location.fragment().is_none() {
1200 location.set_fragment(request_fragment);
1201 }
1202 }
1203 location
1205}
1206
1207#[async_recursion]
1209pub async fn http_redirect_fetch(
1210 fetch_params: &mut FetchParams,
1211 cache: &mut CorsCache,
1212 mut response: Response,
1213 cors_flag: bool,
1214 target: Target<'async_recursion>,
1215 done_chan: &mut DoneChannel,
1216 context: &FetchContext,
1217) -> Response {
1218 let mut redirect_end_timer = RedirectEndTimer(Some(context.timing.clone()));
1219
1220 let request = &mut fetch_params.request;
1222
1223 assert!(response.return_internal);
1225
1226 let location_url = location_url_for_response(&response, request.current_url().fragment());
1228 response.actual_response_mut().location_url = location_url.clone();
1229
1230 let location_url = match location_url {
1231 None => return response,
1233 Some(Err(err)) => {
1235 return Response::network_error(NetworkError::ResourceLoadError(
1236 "Location URL parse failure: ".to_owned() + &err,
1237 ));
1238 },
1239 Some(Ok(url)) if !matches!(url.scheme(), "http" | "https") => {
1241 return Response::network_error(NetworkError::UnsupportedScheme);
1242 },
1243 Some(Ok(url)) => url,
1244 };
1245
1246 context
1249 .timing
1250 .lock()
1251 .set_attribute(ResourceAttribute::RedirectStart(
1252 RedirectStartValue::FetchStart,
1253 ));
1254
1255 context
1256 .timing
1257 .lock()
1258 .set_attribute(ResourceAttribute::FetchStart);
1259
1260 context
1262 .timing
1263 .lock()
1264 .set_attribute(ResourceAttribute::StartTime(ResourceTimeValue::FetchStart));
1265
1266 context
1267 .timing
1268 .lock()
1269 .set_attribute(ResourceAttribute::StartTime(
1270 ResourceTimeValue::RedirectStart,
1271 )); if request.redirect_count >= 20 {
1275 return Response::network_error(NetworkError::TooManyRedirects);
1276 }
1277
1278 request.redirect_count += 1;
1280
1281 let same_origin = match request.origin {
1284 Origin::Origin(ref origin) => *origin == location_url.origin(),
1285 Origin::Client => panic!(
1286 "Request origin should not be client for {}",
1287 request.current_url()
1288 ),
1289 };
1290
1291 let has_credentials = has_credentials(&location_url);
1292
1293 if request.mode == RequestMode::CorsMode && !same_origin && has_credentials {
1294 return Response::network_error(NetworkError::CorsCredentials);
1295 }
1296
1297 if cors_flag && location_url.origin() != request.current_url().origin() {
1298 request.origin = Origin::Origin(ImmutableOrigin::new_opaque());
1299 }
1300
1301 if cors_flag && has_credentials {
1303 return Response::network_error(NetworkError::CorsCredentials);
1304 }
1305
1306 if response.actual_response().status != StatusCode::SEE_OTHER &&
1309 request.body.as_ref().is_some_and(|b| b.source_is_null())
1310 {
1311 return Response::network_error(NetworkError::ConnectionFailure);
1312 }
1313
1314 if response
1316 .actual_response()
1317 .status
1318 .try_code()
1319 .is_some_and(|code| {
1320 ((code == StatusCode::MOVED_PERMANENTLY || code == StatusCode::FOUND) &&
1322 request.method == Method::POST) ||
1323 (code == StatusCode::SEE_OTHER &&
1325 request.method != Method::HEAD &&
1326 request.method != Method::GET)
1327 })
1328 {
1329 request.method = Method::GET;
1331 request.body = None;
1332 for name in REQUEST_BODY_HEADER_NAMES {
1334 request.headers.remove(name);
1335 }
1336 }
1337
1338 if location_url.origin() != request.current_url().origin() {
1342 request.headers.remove(AUTHORIZATION);
1345 }
1346
1347 if let Some(body) = request.body.as_mut() {
1350 body.extract_source();
1351 }
1352
1353 request.url_list.push(location_url);
1357
1358 set_requests_referrer_policy_on_redirect(request, response.actual_response());
1360
1361 let recursive_flag = request.redirect_mode != RedirectMode::Manual;
1364
1365 let fetch_response = main_fetch(
1367 fetch_params,
1368 cache,
1369 recursive_flag,
1370 target,
1371 done_chan,
1372 context,
1373 )
1374 .await;
1375
1376 context
1378 .timing
1379 .lock()
1380 .set_attribute(ResourceAttribute::RedirectEnd(
1381 RedirectEndValue::ResponseEnd,
1382 ));
1383 redirect_end_timer.neuter();
1384
1385 fetch_response
1386}
1387
1388#[async_recursion]
1390async fn http_network_or_cache_fetch(
1391 fetch_params: &mut FetchParams,
1392 authentication_fetch_flag: bool,
1393 cors_flag: bool,
1394 done_chan: &mut DoneChannel,
1395 context: &FetchContext,
1396) -> Response {
1397 let http_fetch_params: &mut FetchParams;
1399 let mut fetch_params_copy: FetchParams;
1400
1401 let mut response: Option<Response> = None;
1405
1406 let mut revalidating_flag = false;
1408
1409 let http_request = if fetch_params.request.traversable_for_user_prompts ==
1413 TraversableForUserPrompts::NoTraversable &&
1414 fetch_params.request.redirect_mode == RedirectMode::Error
1415 {
1416 http_fetch_params = fetch_params;
1417 &mut http_fetch_params.request
1418 }
1419 else {
1421 fetch_params_copy =
1424 std::mem::replace(fetch_params, FetchParams::new(fetch_params.request.clone()));
1425 http_fetch_params = &mut fetch_params_copy;
1426
1427 &mut http_fetch_params.request
1428 };
1429
1430 let include_credentials = match http_request.credentials_mode {
1432 CredentialsMode::Include => true,
1434 CredentialsMode::CredentialsSameOrigin
1436 if http_request.response_tainting == ResponseTainting::Basic =>
1437 {
1438 true
1439 },
1440 _ => false,
1441 };
1442
1443 let content_length = http_request
1450 .body
1451 .as_ref()
1452 .and_then(|body| body.len().map(|size| size as u64));
1453
1454 let mut content_length_header_value = None;
1456
1457 if http_request.body.is_none() && matches!(http_request.method, Method::POST | Method::PUT) {
1460 content_length_header_value = Some(0);
1461 }
1462
1463 if let Some(content_length) = content_length {
1467 content_length_header_value = Some(content_length);
1468 };
1469
1470 if let Some(content_length_header_value) = content_length_header_value {
1473 http_request
1474 .headers
1475 .typed_insert(ContentLength(content_length_header_value));
1476 }
1477
1478 if http_request.keep_alive {
1480 if let Some(content_length) = content_length {
1481 let in_flight_keep_alive_bytes: u64 = context
1486 .in_flight_keep_alive_records
1487 .lock()
1488 .get(
1489 &http_request
1490 .pipeline_id
1491 .expect("Must always set a pipeline ID for keep-alive requests"),
1492 )
1493 .map(|records| {
1494 records
1498 .iter()
1499 .map(|record| {
1500 if record.request_id == http_request.id {
1501 0
1504 } else {
1505 record.keep_alive_body_length
1506 }
1507 })
1508 .sum()
1509 })
1510 .unwrap_or_default();
1511 if content_length + in_flight_keep_alive_bytes > 64 * 1024 {
1513 return Response::network_error(NetworkError::TooManyInFlightKeepAliveRequests);
1514 }
1515 }
1516 }
1517
1518 match http_request.referrer {
1520 Referrer::ReferrerUrl(ref http_request_referrer) |
1521 Referrer::Client(ref http_request_referrer) => {
1522 if let Ok(referer) = http_request_referrer.as_str().parse::<Referer>() {
1525 http_request.headers.typed_insert(referer);
1527 } else {
1528 error!("Failed to parse {} as referrer", http_request_referrer);
1532 }
1533 },
1534 _ => {},
1535 };
1536
1537 append_a_request_origin_header(http_request);
1539
1540 append_the_fetch_metadata_headers(http_request);
1542
1543 if http_request.initiator == Initiator::Prefetch {
1546 if let Ok(value) = HeaderValue::from_str("prefetch") {
1547 http_request.headers.insert("Sec-Purpose", value);
1548 }
1549 }
1550
1551 if !http_request.headers.contains_key(header::USER_AGENT) {
1554 http_request
1555 .headers
1556 .typed_insert::<UserAgent>(context.user_agent.parse().unwrap());
1557 }
1558
1559 match http_request.cache_mode {
1561 CacheMode::Default if is_no_store_cache(&http_request.headers) => {
1565 http_request.cache_mode = CacheMode::NoStore;
1566 },
1567
1568 CacheMode::NoCache if !http_request.headers.contains_key(header::CACHE_CONTROL) => {
1578 http_request
1579 .headers
1580 .typed_insert(CacheControl::new().with_max_age(Duration::from_secs(0)));
1581 },
1582
1583 CacheMode::Reload | CacheMode::NoStore => {
1585 if !http_request.headers.contains_key(header::PRAGMA) {
1588 http_request.headers.typed_insert(Pragma::no_cache());
1589 }
1590
1591 if !http_request.headers.contains_key(header::CACHE_CONTROL) {
1594 http_request
1595 .headers
1596 .typed_insert(CacheControl::new().with_no_cache());
1597 }
1598 },
1599
1600 _ => {},
1601 }
1602
1603 if http_request.headers.contains_key(header::RANGE) {
1606 if let Ok(value) = HeaderValue::from_str("identity") {
1607 http_request.headers.insert("Accept-Encoding", value);
1608 }
1609 }
1610
1611 http_request.headers.remove(header::HOST);
1615 set_default_accept_encoding(&mut http_request.headers);
1617
1618 let current_url = http_request.current_url();
1619
1620 if include_credentials {
1623 set_request_cookies(
1627 ¤t_url,
1628 &mut http_request.headers,
1629 &context.state.cookie_jar,
1630 );
1631 if !http_request.headers.contains_key(header::AUTHORIZATION) {
1633 let mut authorization_value = None;
1635
1636 if let Some(basic) = auth_from_cache(&context.state.auth_cache, ¤t_url.origin()) {
1638 if !http_request.use_url_credentials || !has_credentials(¤t_url) {
1639 authorization_value = Some(basic);
1640 }
1641 }
1642
1643 if authentication_fetch_flag &&
1645 authorization_value.is_none() &&
1646 has_credentials(¤t_url)
1647 {
1648 authorization_value = Some(Authorization::basic(
1649 current_url.username(),
1650 current_url.password().unwrap_or(""),
1651 ));
1652 }
1653
1654 if let Some(basic) = authorization_value {
1656 http_request.headers.typed_insert(basic);
1657 }
1658 }
1659 }
1660
1661 let should_wait = {
1663 let mut cache_guard = block_for_cache_ready(
1665 context,
1666 http_request,
1667 done_chan,
1668 &mut revalidating_flag,
1669 &mut response,
1670 )
1671 .await;
1672
1673 if response.is_none() {
1677 if http_request.cache_mode == CacheMode::OnlyIfCached {
1679 return Response::network_error(NetworkError::CacheError);
1681 }
1682
1683 drop(cache_guard);
1686 let forward_response =
1687 http_network_fetch(http_fetch_params, include_credentials, done_chan, context)
1688 .await;
1689
1690 let http_request = &mut http_fetch_params.request;
1691 let request_key = CacheKey::new(http_request);
1692 cache_guard = context
1693 .state
1694 .http_cache
1695 .get_or_guard(request_key.clone())
1696 .await;
1697 if forward_response.status.in_range(200..=399) && !http_request.method.is_safe() {
1701 if let Some(guard) = cache_guard.try_as_mut() {
1702 invalidate(http_request, &forward_response, guard).await;
1703 }
1704 context
1705 .state
1706 .http_cache
1707 .invalidate_related_urls(http_request, &forward_response, &request_key)
1708 .await;
1709 }
1710
1711 if revalidating_flag && forward_response.status == StatusCode::NOT_MODIFIED {
1713 *done_chan = None;
1716 if let Some(guard) = cache_guard.try_as_mut() {
1717 response = refresh(http_request, forward_response.clone(), done_chan, guard);
1718 }
1719
1720 if let Some(response) = &mut response {
1721 response.cache_state = CacheState::Validated;
1722 }
1723 }
1724
1725 if response.is_none() {
1727 let forward_response = response.insert(forward_response);
1729
1730 if http_request.cache_mode != CacheMode::NoStore {
1733 cache_guard.insert(http_request, forward_response);
1736 }
1737 }
1738 false
1739 } else {
1740 true
1741 }
1742 }; if should_wait {
1745 wait_for_inflight_requests(done_chan, &mut response).await;
1750 }
1751
1752 let http_request = &mut http_fetch_params.request;
1753 let mut response = response.unwrap();
1754
1755 if http_request.response_tainting != ResponseTainting::CorsTainting &&
1758 cross_origin_resource_policy_check(http_request, &response) ==
1759 CrossOriginResourcePolicy::Blocked
1760 {
1761 return Response::network_error(NetworkError::CorsGeneral);
1762 }
1763
1764 if http_request.headers.contains_key(RANGE) {
1768 response.range_requested = true;
1769 }
1770
1771 if response.status.try_code() == Some(StatusCode::UNAUTHORIZED) &&
1779 !cors_flag &&
1780 include_credentials &&
1781 response.headers.contains_key(WWW_AUTHENTICATE)
1782 {
1783 let request = &mut fetch_params.request;
1786
1787 if request.body.is_some() {
1789 }
1791
1792 if !request.use_url_credentials || authentication_fetch_flag {
1794 let Some(credentials) = context
1795 .state
1796 .request_authentication(request, &response)
1797 .await
1798 else {
1799 return response;
1800 };
1801
1802 if let Err(err) = request
1803 .current_url_mut()
1804 .set_username(&credentials.username)
1805 {
1806 error!("error setting username for url: {:?}", err);
1807 return response;
1808 };
1809
1810 if let Err(err) = request
1811 .current_url_mut()
1812 .set_password(Some(&credentials.password))
1813 {
1814 error!("error setting password for url: {:?}", err);
1815 return response;
1816 };
1817 }
1818
1819 *done_chan = None;
1822
1823 response = http_network_or_cache_fetch(
1825 fetch_params,
1826 true, cors_flag,
1828 done_chan,
1829 context,
1830 )
1831 .await;
1832 }
1833
1834 if response.status == StatusCode::PROXY_AUTHENTICATION_REQUIRED {
1836 let request = &mut fetch_params.request;
1837 if request.traversable_for_user_prompts == TraversableForUserPrompts::NoTraversable {
1840 return Response::network_error(NetworkError::ResourceLoadError(
1841 "Can't find Window object".into(),
1842 ));
1843 }
1844
1845 let Some(credentials) = context
1853 .state
1854 .request_authentication(request, &response)
1855 .await
1856 else {
1857 return response;
1858 };
1859
1860 let entry = AuthCacheEntry {
1862 user_name: credentials.username,
1863 password: credentials.password,
1864 };
1865 {
1866 let mut auth_cache = context.state.auth_cache.write();
1867 let key = request.current_url().origin().ascii_serialization();
1868 auth_cache.entries.insert(key, entry);
1869 }
1870
1871 *done_chan = None;
1874
1875 response = http_network_or_cache_fetch(
1877 fetch_params,
1878 false, cors_flag,
1880 done_chan,
1881 context,
1882 )
1883 .await;
1884 }
1885
1886 if authentication_fetch_flag {
1894 }
1896
1897 response
1899}
1900async fn block_for_cache_ready<'a>(
1909 context: &'a FetchContext,
1910 http_request: &mut Request,
1911 done_chan: &mut DoneChannel,
1912 revalidating_flag: &mut bool,
1913 response: &mut Option<Response>,
1914) -> CachedResourcesOrGuard<'a> {
1915 let entry_key = CacheKey::new(http_request);
1916 let guard_result = context.state.http_cache.get_or_guard(entry_key).await;
1917
1918 match guard_result {
1919 CachedResourcesOrGuard::Guard(_) => {
1920 *done_chan = None;
1921 },
1922 CachedResourcesOrGuard::Value(ref cached_resources) => {
1923 let stored_response = construct_response(http_request, done_chan, cached_resources);
1929 if let Some(response_from_cache) = stored_response {
1931 let response_headers = response_from_cache.response.headers.clone();
1932 let (cached_response, needs_revalidation) =
1934 match (http_request.cache_mode, &http_request.mode) {
1935 (CacheMode::ForceCache, _) => (Some(response_from_cache.response), false),
1936 (CacheMode::OnlyIfCached, &RequestMode::SameOrigin) => {
1937 (Some(response_from_cache.response), false)
1938 },
1939 (CacheMode::OnlyIfCached, _) |
1940 (CacheMode::NoStore, _) |
1941 (CacheMode::Reload, _) => (None, false),
1942 (_, _) => (
1943 Some(response_from_cache.response),
1944 response_from_cache.needs_validation,
1945 ),
1946 };
1947
1948 if needs_revalidation {
1949 *revalidating_flag = true;
1950 if let Some(http_date) = response_headers.typed_get::<LastModified>() {
1952 let http_date: SystemTime = http_date.into();
1953 http_request
1954 .headers
1955 .typed_insert(IfModifiedSince::from(http_date));
1956 }
1957 if let Some(entity_tag) = response_headers.get(header::ETAG) {
1958 http_request
1959 .headers
1960 .insert(header::IF_NONE_MATCH, entity_tag.clone());
1961 }
1962 } else {
1963 *response = cached_response;
1965 if let Some(response) = response {
1966 response.cache_state = CacheState::Local;
1967 }
1968 }
1969 if response.is_none() {
1970 *done_chan = None;
1973 }
1974 }
1975 },
1976 }
1977 guard_result
1978}
1979
1980async fn wait_for_inflight_requests(done_chan: &mut DoneChannel, response: &mut Option<Response>) {
1983 if let Some(ref mut ch) = *done_chan {
1984 assert!(response.is_some());
1988
1989 loop {
1990 match ch.1.recv().await {
1991 Some(Data::Payload(_)) => {},
1992 Some(Data::Done) => break, Some(Data::Cancelled) => {
1994 break;
1996 },
1997 _ => panic!("HTTP cache should always send Done or Cancelled"),
1998 }
1999 }
2000 }
2001 *done_chan = None;
2003}
2004
2005#[derive(PartialEq)]
2009enum CrossOriginResourcePolicy {
2010 Allowed,
2011 Blocked,
2012}
2013
2014fn cross_origin_resource_policy_check(
2017 request: &Request,
2018 response: &Response,
2019) -> CrossOriginResourcePolicy {
2020 if request.mode != RequestMode::NoCors {
2022 return CrossOriginResourcePolicy::Allowed;
2023 }
2024
2025 let current_url_origin = request.current_url().origin();
2027 let same_origin = if let Origin::Origin(ref origin) = request.origin {
2028 *origin == request.current_url().origin()
2029 } else {
2030 false
2031 };
2032
2033 if same_origin {
2034 return CrossOriginResourcePolicy::Allowed;
2035 }
2036
2037 let policy = response
2039 .headers
2040 .get(HeaderName::from_static("cross-origin-resource-policy"))
2041 .map(|h| h.to_str().unwrap_or(""))
2042 .unwrap_or("");
2043
2044 if policy == "same-origin" {
2046 return CrossOriginResourcePolicy::Blocked;
2047 }
2048
2049 if let Origin::Origin(ref request_origin) = request.origin {
2051 let schemeless_same_origin = is_schemelessy_same_site(request_origin, ¤t_url_origin);
2052 if schemeless_same_origin &&
2053 (request_origin.scheme() == Some("https") ||
2054 response.https_state == HttpsState::None)
2055 {
2056 return CrossOriginResourcePolicy::Allowed;
2057 }
2058 };
2059
2060 if policy == "same-site" {
2062 return CrossOriginResourcePolicy::Blocked;
2063 }
2064
2065 CrossOriginResourcePolicy::Allowed
2066}
2067
2068struct ResponseEndTimer(Option<Arc<Mutex<ResourceFetchTiming>>>);
2070
2071impl ResponseEndTimer {
2072 fn neuter(&mut self) {
2073 self.0 = None;
2074 }
2075}
2076
2077impl Drop for ResponseEndTimer {
2078 fn drop(&mut self) {
2079 let ResponseEndTimer(resource_fetch_timing_opt) = self;
2080
2081 resource_fetch_timing_opt.as_ref().map_or((), |t| {
2082 t.lock().set_attribute(ResourceAttribute::ResponseEnd);
2083 })
2084 }
2085}
2086
2087async fn http_network_fetch(
2089 fetch_params: &mut FetchParams,
2090 credentials_flag: bool,
2091 done_chan: &mut DoneChannel,
2092 context: &FetchContext,
2093) -> Response {
2094 let mut response_end_timer = ResponseEndTimer(Some(context.timing.clone()));
2095
2096 let request = &mut fetch_params.request;
2098
2099 let url = request.current_url();
2110 let request_id = request.id.0.to_string();
2111 if log_enabled!(log::Level::Info) {
2112 info!("{:?} request for {}", request.method, url);
2113 for header in request.headers.iter() {
2114 debug!(" - {:?}", header);
2115 }
2116 }
2117
2118 let is_xhr = request.destination == Destination::None;
2122
2123 let (fetch_terminated_sender, mut fetch_terminated_receiver) = unbounded_channel();
2125
2126 let body = request.body.as_ref().map(|body| body.clone_stream());
2127
2128 if body.is_none() {
2129 let _ = fetch_terminated_sender.send(false);
2134 }
2135
2136 let browsing_context_id = request.target_webview_id.map(Into::into);
2137
2138 let (res, msg) = match &request.mode {
2139 RequestMode::WebSocket {
2140 protocols,
2141 original_url: _,
2142 } => {
2143 let (resource_event_sender, dom_action_receiver) = {
2146 let mut websocket_chan = context.websocket_chan.as_ref().unwrap().lock();
2147 (
2148 websocket_chan.sender.clone(),
2149 websocket_chan.receiver.take().unwrap(),
2150 )
2151 };
2152
2153 let mut tls_config = create_tls_config(
2154 context.ca_certificates.clone(),
2155 context.ignore_certificate_errors,
2156 context.state.override_manager.clone(),
2157 );
2158 tls_config.alpn_protocols = vec!["http/1.1".to_string().into()];
2159
2160 let response = match start_websocket(
2161 context.state.clone(),
2162 resource_event_sender,
2163 protocols,
2164 request,
2165 tls_config,
2166 dom_action_receiver,
2167 )
2168 .await
2169 {
2170 Ok(response) => response,
2171 Err(error) => {
2172 return Response::network_error(NetworkError::WebsocketConnectionFailure(
2173 format!("{error:?}"),
2174 ));
2175 },
2176 };
2177
2178 let response = response.map(|r| match r {
2179 Some(body) => Full::from(body).map_err(|_| unreachable!()).boxed(),
2180 None => http_body_util::Empty::new()
2181 .map_err(|_| unreachable!())
2182 .boxed(),
2183 });
2184 (Decoder::detect(response, url.is_secure_scheme()), None)
2185 },
2186 _ => {
2187 let response_future = obtain_response(
2188 &context.state.client,
2189 &url,
2190 &request.method,
2191 &mut request.headers,
2192 body,
2193 request
2194 .body
2195 .as_ref()
2196 .is_some_and(|body| body.source_is_null()),
2197 &request.pipeline_id,
2198 Some(&request_id),
2199 request.destination,
2200 is_xhr,
2201 context,
2202 fetch_terminated_sender,
2203 browsing_context_id,
2204 );
2205
2206 let (res, msg) = match response_future.await {
2208 Ok(wrapped_response) => wrapped_response,
2209 Err(error) => return Response::network_error(error),
2210 };
2211 (res, msg)
2212 },
2213 };
2214
2215 if log_enabled!(log::Level::Info) {
2216 debug!("{:?} response for {}", res.version(), url);
2217 for header in res.headers().iter() {
2218 debug!(" - {:?}", header);
2219 }
2220 }
2221
2222 match fetch_terminated_receiver.recv().await {
2225 Some(true) => return Response::network_error(NetworkError::ConnectionFailure),
2226 Some(false) => {},
2227 _ => warn!("Failed to receive confirmation request was streamed without error."),
2228 }
2229
2230 let header_strings: Vec<&str> = res
2231 .headers()
2232 .get_all("Timing-Allow-Origin")
2233 .iter()
2234 .map(|header_value| header_value.to_str().unwrap_or(""))
2235 .collect();
2236 let wildcard_present = header_strings.contains(&"*");
2237 let req_origin_in_timing_allow = header_strings
2241 .iter()
2242 .any(|header_str| match request.origin {
2243 SpecificOrigin(ref immutable_request_origin) => {
2244 *header_str == immutable_request_origin.ascii_serialization()
2245 },
2246 _ => false,
2247 });
2248
2249 let is_same_origin = request.url_list.iter().all(|url| match request.origin {
2250 SpecificOrigin(ref immutable_request_origin) => url.origin() == *immutable_request_origin,
2251 _ => false,
2252 });
2253
2254 if !(is_same_origin || req_origin_in_timing_allow || wildcard_present) {
2255 context.timing.lock().mark_timing_check_failed();
2256 }
2257
2258 let timing = context.timing.lock().clone();
2259 let mut response = Response::new(url.clone(), timing);
2260
2261 if let Some(handshake_info) = res.extensions().get::<TlsHandshakeInfo>() {
2262 let mut hsts_enabled = url
2263 .host_str()
2264 .is_some_and(|host| context.state.hsts_list.read().is_host_secure(host));
2265
2266 if url.scheme() == "https" {
2267 if let Some(sts) = res.headers().typed_get::<StrictTransportSecurity>() {
2268 hsts_enabled = sts.max_age().as_secs() > 0;
2270 }
2271 }
2272 response.tls_security_info = Some(build_tls_security_info(handshake_info, hsts_enabled));
2273 }
2274
2275 let status_text = res
2276 .extensions()
2277 .get::<ReasonPhrase>()
2278 .map(ReasonPhrase::as_bytes)
2279 .or_else(|| res.status().canonical_reason().map(str::as_bytes))
2280 .map(Vec::from)
2281 .unwrap_or_default();
2282 response.status = HttpStatus::new(res.status(), status_text);
2283
2284 info!("got {:?} response for {:?}", res.status(), request.url());
2285 response.headers = res.headers().clone();
2286 response.referrer = request.referrer.to_url().cloned();
2287 response.referrer_policy = request.referrer_policy;
2288
2289 let res_body = response.body.clone();
2290
2291 let (done_sender, done_receiver) = unbounded_channel();
2293 *done_chan = Some((done_sender.clone(), done_receiver));
2294
2295 let devtools_sender = context.devtools_chan.clone();
2296 let cancellation_listener = context.cancellation_listener.clone();
2297 if cancellation_listener.cancelled() {
2298 return Response::network_error(NetworkError::LoadCancelled);
2299 }
2300
2301 *res_body.lock() = ResponseBody::Receiving(vec![]);
2302 let res_body2 = res_body.clone();
2303
2304 if let Some(ref sender) = devtools_sender {
2305 if let Some(m) = msg {
2306 send_request_to_devtools(m, sender);
2307 }
2308 }
2309
2310 let done_sender2 = done_sender.clone();
2311 let done_sender3 = done_sender.clone();
2312 let timing_ptr2 = context.timing.clone();
2313 let timing_ptr3 = context.timing.clone();
2314 let devtools_request = request.clone();
2315 let url1 = devtools_request.url();
2316 let url2 = url1.clone();
2317
2318 let status = response.status.clone();
2319 let headers = response.headers.clone();
2320 let devtools_chan = context.devtools_chan.clone();
2321
2322 spawn_task(
2323 res.into_body()
2324 .try_fold(res_body, move |res_body, chunk| {
2325 if cancellation_listener.cancelled() {
2326 *res_body.lock() = ResponseBody::Done(vec![]);
2327 let _ = done_sender.send(Data::Cancelled);
2328 return future::ready(Err(std::io::Error::new(
2329 std::io::ErrorKind::Interrupted,
2330 "Fetch aborted",
2331 )));
2332 }
2333 if let ResponseBody::Receiving(ref mut body) = *res_body.lock() {
2334 let bytes = chunk;
2335 body.extend_from_slice(&bytes);
2336 let _ = done_sender.send(Data::Payload(bytes.to_vec()));
2337 }
2338 future::ready(Ok(res_body))
2339 })
2340 .and_then(move |res_body| {
2341 debug!("successfully finished response for {:?}", url1);
2342 let mut body = res_body.lock();
2343 let completed_body = match *body {
2344 ResponseBody::Receiving(ref mut body) => std::mem::take(body),
2345 _ => vec![],
2346 };
2347 let devtools_response_body = completed_body.clone();
2348 *body = ResponseBody::Done(completed_body);
2349 send_response_values_to_devtools(
2350 Some(headers),
2351 status,
2352 Some(devtools_response_body),
2353 CacheState::None,
2354 &devtools_request,
2355 devtools_chan,
2356 );
2357 timing_ptr2
2358 .lock()
2359 .set_attribute(ResourceAttribute::ResponseEnd);
2360 let _ = done_sender2.send(Data::Done);
2361 future::ready(Ok(()))
2362 })
2363 .map_err(move |error| {
2364 if let std::io::ErrorKind::InvalidData = error.kind() {
2365 debug!("Content decompression error for {:?}", url2);
2366 let _ = done_sender3.send(Data::Error(NetworkError::DecompressionError));
2367 let mut body = res_body2.lock();
2368
2369 *body = ResponseBody::Done(vec![]);
2370 }
2371 debug!("finished response for {:?}", url2);
2372 let mut body = res_body2.lock();
2373 let completed_body = match *body {
2374 ResponseBody::Receiving(ref mut body) => std::mem::take(body),
2375 _ => vec![],
2376 };
2377 *body = ResponseBody::Done(completed_body);
2378 timing_ptr3
2379 .lock()
2380 .set_attribute(ResourceAttribute::ResponseEnd);
2381 let _ = done_sender3.send(Data::Done);
2382 }),
2383 );
2384
2385 response.https_state = match url.scheme() {
2391 "https" => HttpsState::Modern,
2392 _ => HttpsState::None,
2393 };
2394
2395 if credentials_flag {
2408 set_cookies_from_headers(&url, &response.headers, &context.state.cookie_jar);
2409 }
2410 context
2411 .state
2412 .hsts_list
2413 .write()
2414 .update_hsts_list_from_response(&url, &response.headers);
2415
2416 response_end_timer.neuter();
2430 response
2431}
2432
2433async fn cors_preflight_fetch(
2435 request: &Request,
2436 cache: &mut CorsCache,
2437 context: &FetchContext,
2438) -> Response {
2439 let mut preflight = RequestBuilder::new(
2444 request.target_webview_id,
2445 request.current_url(),
2446 request.referrer.clone(),
2447 )
2448 .method(Method::OPTIONS)
2449 .origin(match &request.origin {
2450 Origin::Client => {
2451 unreachable!("We shouldn't get Client origin in cors_preflight_fetch.")
2452 },
2453 Origin::Origin(origin) => origin.clone(),
2454 })
2455 .pipeline_id(request.pipeline_id)
2456 .initiator(request.initiator)
2457 .destination(request.destination)
2458 .referrer_policy(request.referrer_policy)
2459 .mode(RequestMode::CorsMode)
2460 .response_tainting(ResponseTainting::CorsTainting)
2461 .policy_container(match &request.policy_container {
2462 RequestPolicyContainer::Client => {
2463 unreachable!("We should have a policy container for request in cors_preflight_fetch")
2464 },
2465 RequestPolicyContainer::PolicyContainer(policy_container) => policy_container.clone(),
2466 })
2467 .build();
2468
2469 preflight
2471 .headers
2472 .insert(ACCEPT, HeaderValue::from_static("*/*"));
2473
2474 preflight
2476 .headers
2477 .typed_insert::<AccessControlRequestMethod>(AccessControlRequestMethod::from(
2478 request.method.clone(),
2479 ));
2480
2481 let headers = get_cors_unsafe_header_names(&request.headers);
2483
2484 if !headers.is_empty() {
2486 preflight.headers.insert(
2489 ACCESS_CONTROL_REQUEST_HEADERS,
2490 HeaderValue::from_bytes(itertools::join(headers.iter(), ",").as_bytes())
2491 .unwrap_or(HeaderValue::from_static("")),
2492 );
2493 }
2494
2495 let mut fetch_params = FetchParams::new(preflight);
2498 let response =
2499 http_network_or_cache_fetch(&mut fetch_params, false, false, &mut None, context).await;
2500
2501 if cors_check(request, &response).is_ok() && response.status.code().is_success() {
2503 let mut methods = if response
2506 .headers
2507 .contains_key(header::ACCESS_CONTROL_ALLOW_METHODS)
2508 {
2509 match response.headers.typed_get::<AccessControlAllowMethods>() {
2510 Some(methods) => methods.iter().collect(),
2511 None => {
2513 return Response::network_error(NetworkError::CorsAllowMethods);
2514 },
2515 }
2516 } else {
2517 vec![]
2518 };
2519
2520 let header_names = if response
2523 .headers
2524 .contains_key(header::ACCESS_CONTROL_ALLOW_HEADERS)
2525 {
2526 match response.headers.typed_get::<AccessControlAllowHeaders>() {
2527 Some(names) => names.iter().collect(),
2528 None => {
2530 return Response::network_error(NetworkError::CorsAllowHeaders);
2531 },
2532 }
2533 } else {
2534 vec![]
2535 };
2536
2537 debug!(
2538 "CORS check: Allowed methods: {:?}, current method: {:?}",
2539 methods, request.method
2540 );
2541
2542 if methods.is_empty() && request.use_cors_preflight {
2545 methods = vec![request.method.clone()];
2546 }
2547
2548 if methods
2551 .iter()
2552 .all(|method| *method.as_str() != *request.method.as_ref()) &&
2553 !is_cors_safelisted_method(&request.method) &&
2554 (request.credentials_mode == CredentialsMode::Include ||
2555 methods.iter().all(|method| method.as_ref() != "*"))
2556 {
2557 return Response::network_error(NetworkError::CorsMethod);
2558 }
2559
2560 debug!(
2561 "CORS check: Allowed headers: {:?}, current headers: {:?}",
2562 header_names, request.headers
2563 );
2564
2565 if request.headers.iter().any(|(name, _)| {
2568 is_cors_non_wildcard_request_header_name(name) &&
2569 header_names.iter().all(|header_name| header_name != name)
2570 }) {
2571 return Response::network_error(NetworkError::CorsAuthorization);
2572 }
2573
2574 let unsafe_names = get_cors_unsafe_header_names(&request.headers);
2578 let header_names_set: HashSet<&HeaderName> = HashSet::from_iter(header_names.iter());
2579 let header_names_contains_star = header_names
2580 .iter()
2581 .any(|header_name| header_name.as_str() == "*");
2582 for unsafe_name in unsafe_names.iter() {
2583 if !header_names_set.contains(unsafe_name) &&
2584 (request.credentials_mode == CredentialsMode::Include ||
2585 !header_names_contains_star)
2586 {
2587 return Response::network_error(NetworkError::CorsHeaders);
2588 }
2589 }
2590
2591 let max_age: Option<Duration> = response
2594 .headers
2595 .typed_get::<AccessControlMaxAge>()
2596 .map(|acma| acma.into());
2597
2598 let max_age = max_age.unwrap_or(Duration::from_secs(5));
2600
2601 for method in &methods {
2612 cache.match_method_and_update(request, method.clone(), max_age);
2613 }
2614
2615 for header_name in &header_names {
2620 cache.match_header_and_update(request, header_name, max_age);
2621 }
2622
2623 return response;
2625 }
2626
2627 Response::network_error(NetworkError::CorsGeneral)
2629}
2630
2631fn cors_check(request: &Request, response: &Response) -> Result<(), ()> {
2633 let Some(origins) =
2635 get_value_from_header_list(ACCESS_CONTROL_ALLOW_ORIGIN.as_str(), &response.headers)
2636 else {
2637 return Err(());
2639 };
2640 let origin = origins.into_iter().map(char::from).collect::<String>();
2641
2642 if request.credentials_mode != CredentialsMode::Include && origin == "*" {
2644 return Ok(());
2645 }
2646
2647 if serialize_request_origin(request).to_string() != origin {
2649 return Err(());
2650 }
2651
2652 if request.credentials_mode != CredentialsMode::Include {
2654 return Ok(());
2655 }
2656
2657 let credentials = response
2659 .headers
2660 .typed_get::<AccessControlAllowCredentials>();
2661
2662 if credentials.is_some() {
2664 return Ok(());
2665 }
2666
2667 Err(())
2669}
2670
2671fn has_credentials(url: &ServoUrl) -> bool {
2672 !url.username().is_empty() || url.password().is_some()
2673}
2674
2675fn is_no_store_cache(headers: &HeaderMap) -> bool {
2676 headers.contains_key(header::IF_MODIFIED_SINCE) |
2677 headers.contains_key(header::IF_NONE_MATCH) |
2678 headers.contains_key(header::IF_UNMODIFIED_SINCE) |
2679 headers.contains_key(header::IF_MATCH) |
2680 headers.contains_key(header::IF_RANGE)
2681}
2682
2683fn is_redirect_status(status: StatusCode) -> bool {
2685 matches!(
2686 status,
2687 StatusCode::MOVED_PERMANENTLY |
2688 StatusCode::FOUND |
2689 StatusCode::SEE_OTHER |
2690 StatusCode::TEMPORARY_REDIRECT |
2691 StatusCode::PERMANENT_REDIRECT
2692 )
2693}
2694
2695fn serialize_request_origin(request: &Request) -> headers::Origin {
2697 let Origin::Origin(origin) = &request.origin else {
2699 panic!("origin cannot be \"client\" at this point in time");
2700 };
2701
2702 if request.redirect_taint_for_request() != RedirectTaint::SameOrigin {
2704 return headers::Origin::NULL;
2705 }
2706
2707 serialize_origin(origin)
2709}
2710
2711pub fn serialize_origin(origin: &ImmutableOrigin) -> headers::Origin {
2713 match origin {
2714 ImmutableOrigin::Opaque(_) => headers::Origin::NULL,
2715 ImmutableOrigin::Tuple(scheme, host, port) => {
2716 let port = match (scheme.as_ref(), port) {
2719 ("http" | "ws", 80) | ("https" | "wss", 443) | ("ftp", 21) => None,
2720 _ => Some(*port),
2721 };
2722
2723 headers::Origin::try_from_parts(scheme, &host.to_string(), port)
2725 .unwrap_or(headers::Origin::NULL)
2726 },
2727 }
2728}
2729
2730fn append_a_request_origin_header(request: &mut Request) {
2732 let Origin::Origin(request_origin) = &request.origin else {
2734 panic!("origin cannot be \"client\" at this point in time");
2735 };
2736
2737 let mut serialized_origin = serialize_request_origin(request);
2739
2740 if request.response_tainting == ResponseTainting::CorsTainting ||
2743 matches!(request.mode, RequestMode::WebSocket { .. })
2744 {
2745 request.headers.typed_insert(serialized_origin);
2746 }
2747 else if !matches!(request.method, Method::GET | Method::HEAD) {
2749 if request.mode != RequestMode::CorsMode {
2751 match request.referrer_policy {
2752 ReferrerPolicy::NoReferrer => {
2753 serialized_origin = headers::Origin::NULL;
2755 },
2756 ReferrerPolicy::NoReferrerWhenDowngrade |
2757 ReferrerPolicy::StrictOrigin |
2758 ReferrerPolicy::StrictOriginWhenCrossOrigin => {
2759 if let ImmutableOrigin::Tuple(scheme, _, _) = &request_origin {
2762 if scheme == "https" && request.current_url().scheme() != "https" {
2763 serialized_origin = headers::Origin::NULL;
2764 }
2765 }
2766 },
2767 ReferrerPolicy::SameOrigin => {
2768 if *request_origin != request.current_url().origin() {
2771 serialized_origin = headers::Origin::NULL;
2772 }
2773 },
2774 _ => {
2775 },
2777 };
2778 }
2779
2780 request.headers.typed_insert(serialized_origin);
2782 }
2783}
2784
2785fn append_the_fetch_metadata_headers(r: &mut Request) {
2787 if !r.url().is_potentially_trustworthy() {
2789 return;
2790 }
2791
2792 set_the_sec_fetch_dest_header(r);
2794
2795 set_the_sec_fetch_mode_header(r);
2797
2798 set_the_sec_fetch_site_header(r);
2800
2801 set_the_sec_fetch_user_header(r);
2803}
2804
2805fn set_the_sec_fetch_dest_header(r: &mut Request) {
2807 debug_assert!(r.url().is_potentially_trustworthy());
2809
2810 let header = r.destination;
2814
2815 r.headers.typed_insert(SecFetchDest(header));
2817}
2818
2819fn set_the_sec_fetch_mode_header(r: &mut Request) {
2821 debug_assert!(r.url().is_potentially_trustworthy());
2823
2824 let header = &r.mode;
2827
2828 r.headers.typed_insert(SecFetchMode::from(header));
2830}
2831
2832fn set_the_sec_fetch_site_header(r: &mut Request) {
2834 let Origin::Origin(request_origin) = &r.origin else {
2837 panic!("request origin cannot be \"client\" at this point")
2838 };
2839
2840 debug_assert!(r.url().is_potentially_trustworthy());
2842
2843 let mut header = SecFetchSite::SameOrigin;
2846
2847 if header != SecFetchSite::None {
2852 for url in &r.url_list {
2853 if url.origin() == *request_origin {
2855 continue;
2856 }
2857
2858 header = SecFetchSite::CrossSite;
2860
2861 if !is_same_site(request_origin, &url.origin()) {
2863 break;
2864 }
2865
2866 header = SecFetchSite::SameSite;
2868 }
2869 }
2870
2871 r.headers.typed_insert(header);
2873}
2874
2875fn set_the_sec_fetch_user_header(r: &mut Request) {
2877 debug_assert!(r.url().is_potentially_trustworthy());
2879
2880 if !r.is_navigation_request() {
2883 return;
2884 }
2885
2886 let header = SecFetchUser;
2889
2890 r.headers.typed_insert(header);
2892}
2893
2894fn set_requests_referrer_policy_on_redirect(request: &mut Request, response: &Response) {
2896 let referrer_policy: ReferrerPolicy = response
2899 .headers
2900 .typed_get::<headers::ReferrerPolicy>()
2901 .into();
2902
2903 if referrer_policy != ReferrerPolicy::EmptyString {
2905 request.referrer_policy = referrer_policy;
2906 }
2907}