1use std::cmp::min;
6use std::collections::HashSet;
7use std::iter::FromIterator;
8use std::sync::Arc as StdArc;
9use std::sync::atomic::{AtomicBool, Ordering};
10use std::time::{Duration, SystemTime};
11
12use async_recursion::async_recursion;
13use content_security_policy::percent_encoding::{AsciiSet, CONTROLS, utf8_percent_encode};
14use devtools_traits::ChromeToDevtoolsControlMsg;
15use embedder_traits::{AuthenticationResponse, GenericEmbedderProxy};
16use futures::{TryFutureExt, TryStreamExt, future};
17use headers::authorization::Basic;
18use headers::{
19 AccessControlAllowCredentials, AccessControlAllowHeaders, AccessControlAllowMethods,
20 AccessControlMaxAge, AccessControlRequestMethod, Authorization, CacheControl, ContentLength,
21 HeaderMapExt, IfModifiedSince, LastModified, Pragma, Referer, StrictTransportSecurity,
22 UserAgent,
23};
24use http::header::{
25 self, ACCEPT, ACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_REQUEST_HEADERS, AUTHORIZATION,
26 CONTENT_ENCODING, CONTENT_LANGUAGE, CONTENT_LOCATION, CONTENT_TYPE, HeaderValue, RANGE,
27 WWW_AUTHENTICATE,
28};
29use http::{HeaderMap, Method, Request as HyperRequest, StatusCode};
30use http_body_util::combinators::BoxBody;
31use http_body_util::{BodyExt, Full};
32use hyper::Response as HyperResponse;
33use hyper::body::{Bytes, Frame};
34use hyper::ext::ReasonPhrase;
35use hyper::header::{HeaderName, TRANSFER_ENCODING};
36use ipc_channel::IpcError;
37use ipc_channel::ipc::{self, IpcSender};
38use ipc_channel::router::ROUTER;
39use log::{debug, error, info, log_enabled, warn};
40use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
41use net_traits::blob_url_store::UrlWithBlobClaim;
42use net_traits::fetch::headers::get_value_from_header_list;
43use net_traits::http_status::HttpStatus;
44use net_traits::policy_container::{EmbedderPolicyValue, RequestPolicyContainer};
45use net_traits::pub_domains::{is_same_site, reg_suffix};
46use net_traits::request::{
47 BodyChunkRequest, BodyChunkResponse, CacheMode, CredentialsMode, Destination, Initiator,
48 Origin, RedirectMode, Referrer, Request, RequestBuilder, RequestClient, RequestMode,
49 ResponseTainting, ServiceWorkersMode, TraversableForUserPrompts, get_cors_unsafe_header_names,
50 is_cors_non_wildcard_request_header_name, is_cors_safelisted_method,
51 is_cors_safelisted_request_header,
52};
53use net_traits::response::{CacheState, RedirectTaint, Response, ResponseBody, ResponseType};
54use net_traits::{
55 CookieSource, DOCUMENT_ACCEPT_HEADER_VALUE, DiscardFetch, NetworkError, RedirectEndValue,
56 RedirectStartValue, ReferrerPolicy, ResourceAttribute, ResourceFetchTimingContainer,
57 ResourceTimeValue, TlsSecurityInfo, TlsSecurityState,
58};
59use parking_lot::{Mutex, RwLock};
60use profile_traits::mem::{Report, ReportKind};
61use profile_traits::path;
62#[cfg(feature = "tracing")]
63use profile_traits::trace_span;
64use rustc_hash::FxHashMap;
65use servo_base::cross_process_instant::CrossProcessInstant;
66use servo_base::generic_channel::GenericSharedMemory;
67use servo_base::id::{BrowsingContextId, HistoryStateId, PipelineId};
68use servo_config::pref;
69use servo_url::{ImmutableOrigin, ServoUrl};
70use tokio::sync::mpsc::{
71 Receiver as TokioReceiver, Sender as TokioSender, UnboundedReceiver, UnboundedSender, channel,
72 unbounded_channel,
73};
74use tokio_stream::wrappers::ReceiverStream;
75#[cfg(feature = "tracing")]
76use tracing::Instrument;
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::devtools::{
86 prepare_devtools_request, send_request_to_devtools, send_response_values_to_devtools,
87};
88use crate::embedder::NetToEmbedderMsg;
89use crate::fetch::cors_cache::CorsCache;
90use crate::fetch::fetch_params::FetchParams;
91use crate::fetch::headers::{SecFetchDest, SecFetchMode, SecFetchSite, SecFetchUser};
92use crate::fetch::methods::{Data, DoneChannel, FetchContext, Target, fetch, main_fetch};
93use crate::hsts::HstsList;
94use crate::http_cache::{
95 CacheKey, CachedResourcesOrGuard, HttpCache, ValidationStatus, construct_response,
96 invalidate_cached_resources, refresh,
97};
98use crate::resource_thread::{AuthCache, AuthCacheEntry};
99use crate::websocket_loader::start_websocket;
100
101#[derive(Clone, Debug, Eq, PartialEq)]
103pub enum HttpCacheEntryState {
104 ReadyToConstruct,
108 PendingStore(usize),
110}
111
112pub struct HttpState {
113 pub hsts_list: RwLock<HstsList>,
114 pub cookie_jar: RwLock<CookieStorage>,
115 pub http_cache: HttpCache,
116 pub auth_cache: RwLock<AuthCache>,
117 pub history_states: RwLock<FxHashMap<HistoryStateId, Vec<u8>>>,
118 pub client: ServoClient,
119 pub override_manager: CertificateErrorOverrideManager,
120 pub embedder_proxy: GenericEmbedderProxy<NetToEmbedderMsg>,
121}
122
123impl HttpState {
124 pub(crate) fn memory_reports(&self, suffix: &str, ops: &mut MallocSizeOfOps) -> Vec<Report> {
125 vec![
126 Report {
127 path: path!["memory-cache", suffix],
128 kind: ReportKind::ExplicitJemallocHeapSize,
129 size: self.http_cache.size_of(ops),
130 },
131 Report {
132 path: path!["hsts-list", suffix],
133 kind: ReportKind::ExplicitJemallocHeapSize,
134 size: self.hsts_list.read().size_of(ops),
135 },
136 Report {
137 path: path!["auth cache", suffix],
138 kind: ReportKind::ExplicitJemallocHeapSize,
139 size: self.auth_cache.read().size_of(ops),
140 },
141 Report {
142 path: path!["cookie storage", suffix],
143 kind: ReportKind::ExplicitJemallocHeapSize,
144 size: self.cookie_jar.read().size_of(ops),
145 },
146 ]
147 }
148
149 async fn request_authentication(
150 &self,
151 request: &Request,
152 response: &Response,
153 ) -> Option<AuthenticationResponse> {
154 let webview_id = request.target_webview_id?;
156 let for_proxy = response.status == StatusCode::PROXY_AUTHENTICATION_REQUIRED;
157
158 if request.mode != RequestMode::Navigate {
160 return None;
161 }
162
163 let (sender, receiver) = tokio::sync::oneshot::channel();
164 self.embedder_proxy
165 .send(NetToEmbedderMsg::RequestAuthentication(
166 webview_id,
167 request.url(),
168 for_proxy,
169 sender,
170 ));
171 receiver.await.ok()?
172 }
173}
174
175pub(crate) fn set_default_accept(request: &mut Request) {
177 if request.headers.contains_key(header::ACCEPT) {
179 return;
180 }
181
182 let value = if request.initiator == Initiator::Prefetch {
184 DOCUMENT_ACCEPT_HEADER_VALUE
185 } else {
186 match request.destination {
189 Destination::Document | Destination::Frame | Destination::IFrame => {
190 DOCUMENT_ACCEPT_HEADER_VALUE
191 },
192 Destination::Image => {
193 HeaderValue::from_static("image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5")
194 },
195 Destination::Json => HeaderValue::from_static("application/json,*/*;q=0.5"),
196 Destination::Style => HeaderValue::from_static("text/css,*/*;q=0.1"),
197 _ => HeaderValue::from_static("*/*"),
199 }
200 };
201
202 request.headers.insert(header::ACCEPT, value);
204}
205
206fn set_default_accept_encoding(headers: &mut HeaderMap) {
207 if headers.contains_key(header::ACCEPT_ENCODING) {
208 return;
209 }
210
211 headers.insert(
213 header::ACCEPT_ENCODING,
214 HeaderValue::from_static("gzip, deflate, br, zstd"),
215 );
216}
217
218fn no_referrer_when_downgrade(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
220 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
222 return None;
223 }
224 strip_url_for_use_as_referrer(referrer_url, false)
226}
227
228fn strict_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
230 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
232 return None;
233 }
234 strip_url_for_use_as_referrer(referrer_url, true)
236}
237
238fn strict_origin_when_cross_origin(
240 referrer_url: ServoUrl,
241 current_url: ServoUrl,
242) -> Option<ServoUrl> {
243 if referrer_url.origin() == current_url.origin() {
245 return strip_url_for_use_as_referrer(referrer_url, false);
246 }
247 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
249 return None;
250 }
251 strip_url_for_use_as_referrer(referrer_url, true)
253}
254
255fn is_schemelessy_same_site(site_a: &ImmutableOrigin, site_b: &ImmutableOrigin) -> bool {
257 if !site_a.is_tuple() && !site_b.is_tuple() && site_a == site_b {
259 true
260 } else if site_a.is_tuple() && site_b.is_tuple() {
261 let host_a = site_a.host().map(|h| h.to_string()).unwrap_or_default();
263 let host_b = site_b.host().map(|h| h.to_string()).unwrap_or_default();
264
265 let host_a_reg = reg_suffix(&host_a);
266 let host_b_reg = reg_suffix(&host_b);
267
268 (site_a.host() == site_b.host() && host_a_reg.is_empty()) ||
270 (host_a_reg == host_b_reg && !host_a_reg.is_empty())
271 } else {
272 false
274 }
275}
276
277fn strip_url_for_use_as_referrer(mut url: ServoUrl, origin_only: bool) -> Option<ServoUrl> {
279 const MAX_REFERRER_URL_LENGTH: usize = 4096;
280 if url.is_local_scheme() {
282 return None;
283 }
284 {
286 let url = url.as_mut_url();
287 let _ = url.set_username("");
288 let _ = url.set_password(None);
289 url.set_fragment(None);
290 if origin_only || url.as_str().len() > MAX_REFERRER_URL_LENGTH {
294 url.set_path("");
295 url.set_query(None);
296 }
297 }
298 Some(url)
300}
301
302fn same_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
304 if referrer_url.origin() == current_url.origin() {
306 return strip_url_for_use_as_referrer(referrer_url, false);
307 }
308 None
310}
311
312fn origin_when_cross_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
314 if referrer_url.origin() == current_url.origin() {
316 return strip_url_for_use_as_referrer(referrer_url, false);
317 }
318 strip_url_for_use_as_referrer(referrer_url, true)
320}
321
322pub fn determine_requests_referrer(
324 referrer_policy: ReferrerPolicy,
325 referrer_source: ServoUrl,
326 current_url: ServoUrl,
327) -> Option<ServoUrl> {
328 match referrer_policy {
329 ReferrerPolicy::EmptyString | ReferrerPolicy::NoReferrer => None,
330 ReferrerPolicy::Origin => strip_url_for_use_as_referrer(referrer_source, true),
331 ReferrerPolicy::UnsafeUrl => strip_url_for_use_as_referrer(referrer_source, false),
332 ReferrerPolicy::StrictOrigin => strict_origin(referrer_source, current_url),
333 ReferrerPolicy::StrictOriginWhenCrossOrigin => {
334 strict_origin_when_cross_origin(referrer_source, current_url)
335 },
336 ReferrerPolicy::SameOrigin => same_origin(referrer_source, current_url),
337 ReferrerPolicy::OriginWhenCrossOrigin => {
338 origin_when_cross_origin(referrer_source, current_url)
339 },
340 ReferrerPolicy::NoReferrerWhenDowngrade => {
341 no_referrer_when_downgrade(referrer_source, current_url)
342 },
343 }
344}
345
346fn set_request_cookies(
347 url: &ServoUrl,
348 headers: &mut HeaderMap,
349 cookie_jar: &RwLock<CookieStorage>,
350) {
351 let mut cookie_jar = cookie_jar.write();
352 cookie_jar.remove_expired_cookies_for_url(url);
353 if let Some(cookie_list) = cookie_jar.cookies_for_url(url, CookieSource::HTTP) &&
354 let Ok(cookie_list_header_value) = HeaderValue::from_bytes(cookie_list.as_bytes())
355 {
356 headers.insert(header::COOKIE, cookie_list_header_value);
357 }
358}
359
360fn set_cookie_for_url(cookie_jar: &RwLock<CookieStorage>, request: &ServoUrl, cookie_val: &str) {
361 let mut cookie_jar = cookie_jar.write();
362 let source = CookieSource::HTTP;
363
364 if let Some(cookie) = ServoCookie::from_cookie_string(cookie_val, request, source) {
365 cookie_jar.push(cookie, request, source);
366 }
367}
368
369fn set_cookies_from_headers(
370 url: &ServoUrl,
371 headers: &HeaderMap,
372 cookie_jar: &RwLock<CookieStorage>,
373) {
374 for cookie in headers.get_all(header::SET_COOKIE) {
375 let cookie_bytes = cookie.as_bytes();
376 if !ServoCookie::is_valid_name_or_value(cookie_bytes) {
377 continue;
378 }
379 if let Ok(cookie_str) = std::str::from_utf8(cookie_bytes) {
380 set_cookie_for_url(cookie_jar, url, cookie_str);
381 }
382 }
383}
384
385fn build_tls_security_info(handshake: &TlsHandshakeInfo, hsts_enabled: bool) -> TlsSecurityInfo {
386 let state = if handshake.protocol_version.is_none() || handshake.cipher_suite.is_none() {
394 TlsSecurityState::Insecure
396 } else {
397 TlsSecurityState::Secure
399 };
400
401 TlsSecurityInfo {
402 state,
403 weakness_reasons: Vec::new(), protocol_version: handshake.protocol_version.clone(),
405 cipher_suite: handshake.cipher_suite.clone(),
406 kea_group_name: handshake.kea_group_name.clone(),
407 signature_scheme_name: handshake.signature_scheme_name.clone(),
408 alpn_protocol: handshake.alpn_protocol.clone(),
409 certificate_chain_der: handshake.certificate_chain_der.clone(),
410 certificate_transparency: None,
411 hsts: hsts_enabled,
412 hpkp: false,
413 used_ech: handshake.used_ech,
414 used_delegated_credentials: false,
415 used_ocsp: false,
416 used_private_dns: false,
417 }
418}
419
420fn auth_from_cache(
421 auth_cache: &RwLock<AuthCache>,
422 origin: &ImmutableOrigin,
423) -> Option<Authorization<Basic>> {
424 if let Some(auth_entry) = auth_cache
425 .read()
426 .entries
427 .get(origin.ascii_serialization().as_ref())
428 {
429 let user_name = &auth_entry.user_name;
430 let password = &auth_entry.password;
431 Some(Authorization::basic(user_name, password))
432 } else {
433 None
434 }
435}
436
437enum BodyChunk {
440 Chunk(GenericSharedMemory),
442 Done,
444}
445
446enum BodyStream {
448 Chunked(TokioReceiver<Result<Frame<Bytes>, hyper::Error>>),
451 Buffered(UnboundedReceiver<BodyChunk>),
454}
455
456enum BodySink {
459 Chunked(TokioSender<Result<Frame<Bytes>, hyper::Error>>),
461 Buffered(UnboundedSender<BodyChunk>),
465}
466
467impl BodySink {
468 fn transmit_bytes(&self, bytes: GenericSharedMemory) {
469 match self {
470 BodySink::Chunked(sender) => {
471 let sender = sender.clone();
472 spawn_task(async move {
473 let _ = sender
474 .send(Ok(Frame::data(Bytes::copy_from_slice(&bytes))))
475 .await;
476 });
477 },
478 BodySink::Buffered(sender) => {
479 let _ = sender.send(BodyChunk::Chunk(bytes));
480 },
481 }
482 }
483
484 fn close(self) {
485 match self {
486 BodySink::Chunked(_) => {},
487 BodySink::Buffered(sender) => {
488 let _ = sender.send(BodyChunk::Done);
489 },
490 }
491 }
492}
493
494fn request_body_stream_closed_error(action: &str) -> NetworkError {
495 NetworkError::Crash(format!(
496 "Request body stream has already been closed while trying to {action}."
497 ))
498}
499
500fn log_request_body_stream_closed(action: &str, error: Option<&IpcError>) {
501 match error {
502 Some(error) => {
503 error!("Request body stream has already been closed while trying to {action}: {error}")
504 },
505 None => error!("Request body stream has already been closed while trying to {action}."),
506 }
507}
508
509fn log_fetch_terminated_send_failure(terminated_with_error: bool, context: &str) {
510 warn!(
511 "Failed to notify request-body stream termination state ({terminated_with_error}) while {context} because the receiver was already dropped."
512 );
513}
514
515const FRAGMENT: &AsciiSet = &CONTROLS.add(b'|').add(b'{').add(b'}');
516
517#[allow(clippy::too_many_arguments)]
518#[servo_tracing::instrument(skip_all, fields(url=url.as_str()))]
519async fn obtain_response(
521 client: &ServoClient,
522 url: &ServoUrl,
523 method: &Method,
524 request_headers: &mut HeaderMap,
525 body_sender: Option<StdArc<Mutex<Option<IpcSender<BodyChunkRequest>>>>>,
526 source_is_null: bool,
527 pipeline_id: &Option<PipelineId>,
528 request_id: Option<&str>,
529 destination: Destination,
530 is_xhr: bool,
531 context: &FetchContext,
532 fetch_terminated: UnboundedSender<bool>,
533 browsing_context_id: Option<BrowsingContextId>,
534) -> Result<(HyperResponse<Decoder>, Option<ChromeToDevtoolsControlMsg>), NetworkError> {
535 let mut headers = request_headers.clone();
536
537 let devtools_bytes = StdArc::new(Mutex::new(vec![]));
538
539 let encoded_url = utf8_percent_encode(url.as_str(), FRAGMENT).to_string();
541
542 let request = if let Some(chunk_requester) = body_sender {
543 let (sink, stream) = if source_is_null {
544 headers.insert(TRANSFER_ENCODING, HeaderValue::from_static("chunked"));
547
548 let (sender, receiver) = channel(1);
549 (BodySink::Chunked(sender), BodyStream::Chunked(receiver))
550 } else {
551 let (sender, receiver) = unbounded_channel();
558 (BodySink::Buffered(sender), BodyStream::Buffered(receiver))
559 };
560
561 obtain_response_setup_router_callback(
562 devtools_bytes.clone(),
563 chunk_requester,
564 sink,
565 fetch_terminated,
566 )?;
567
568 let body = match stream {
569 BodyStream::Chunked(receiver) => {
570 let stream = ReceiverStream::new(receiver);
571 BoxBody::new(http_body_util::StreamBody::new(stream))
572 },
573 BodyStream::Buffered(mut receiver) => {
574 let mut body = vec![];
576 loop {
577 match receiver.recv().await {
578 Some(BodyChunk::Chunk(bytes)) => {
579 body.extend_from_slice(&bytes);
580 },
581 Some(BodyChunk::Done) => break,
582 None => warn!("Failed to read all chunks from request body."),
583 }
584 }
585 Full::new(body.into()).map_err(|_| unreachable!()).boxed()
586 },
587 };
588 HyperRequest::builder()
589 .method(method)
590 .uri(encoded_url)
591 .body(body)
592 } else {
593 HyperRequest::builder()
594 .method(method)
595 .uri(encoded_url)
596 .body(
597 http_body_util::Empty::new()
598 .map_err(|_| unreachable!())
599 .boxed(),
600 )
601 };
602
603 let connect_start = CrossProcessInstant::now();
606 context.timing.set_attributes(&[
607 ResourceAttribute::DomainLookupStart,
608 ResourceAttribute::ConnectStart(connect_start),
609 ]);
610
611 if url.scheme() == "https" {
615 context
616 .timing
617 .set_attribute(ResourceAttribute::SecureConnectionStart);
618 }
619
620 let mut request = match request {
621 Ok(request) => request,
622 Err(error) => return Err(NetworkError::HttpError(error.to_string())),
623 };
624 *request.headers_mut() = headers.clone();
625
626 let connect_end = CrossProcessInstant::now();
627 context
628 .timing
629 .set_attribute(ResourceAttribute::ConnectEnd(connect_end));
630
631 let request_id = request_id.map(|v| v.to_owned());
632 let pipeline_id = *pipeline_id;
633 let closure_url = url.clone();
634 let method = method.clone();
635 let send_start = CrossProcessInstant::now();
636
637 let host = request.uri().host().unwrap_or("").to_owned();
638 let override_manager = context.state.override_manager.clone();
639 let headers = headers.clone();
640 let is_secure_scheme = url.is_secure_scheme();
641
642 context
646 .timing
647 .set_attribute(ResourceAttribute::RequestStart);
648
649 let client_future = client
650 .request(request)
651 .and_then(move |res| {
652 let send_end = CrossProcessInstant::now();
653
654 let msg = if let Some(request_id) = request_id {
657 if let Some(pipeline_id) = pipeline_id {
658 if let Some(browsing_context_id) = browsing_context_id {
659 Some(prepare_devtools_request(
660 request_id,
661 closure_url,
662 method.clone(),
663 headers,
664 Some(devtools_bytes.lock().clone()),
665 pipeline_id,
666 (connect_end - connect_start).unsigned_abs(),
667 (send_end - send_start).unsigned_abs(),
668 destination,
669 is_xhr,
670 browsing_context_id,
671 ))
672 } else {
673 debug!("Not notifying devtools (no browsing_context_id)");
674 None
675 }
676 } else {
681 debug!("Not notifying devtools (no pipeline_id)");
682 None
683 }
684 } else {
685 debug!("Not notifying devtools (no request_id)");
686 None
687 };
688
689 future::ready(Ok((
690 Decoder::detect(res.map(|r| r.boxed()), is_secure_scheme),
691 msg,
692 )))
693 })
694 .map_err(move |error| {
695 warn!("network error: {error:?}");
696 NetworkError::from_hyper_error(
697 &error,
698 override_manager.remove_certificate_failing_verification(host.as_str()),
699 )
700 });
701
702 #[cfg(feature = "tracing")]
703 {
704 client_future.instrument(trace_span!("HyperRequest")).await
705 }
706
707 #[cfg(not(feature = "tracing"))]
708 {
709 client_future.await
710 }
711}
712
713fn obtain_response_setup_router_callback(
715 devtools_bytes: StdArc<Mutex<Vec<u8>>>,
716 chunk_requester: StdArc<Mutex<Option<IpcSender<BodyChunkRequest>>>>,
717 sink: BodySink,
718 fetch_terminated: UnboundedSender<bool>,
719) -> Result<(), NetworkError> {
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("connect to the request body stream", Some(&error));
727 return Err(request_body_stream_closed_error(
728 "connect to the request body stream",
729 ));
730 }
731
732 if let Err(error) = chunk_requester.send(BodyChunkRequest::Chunk) {
735 log_request_body_stream_closed(
736 "request the first request body chunk",
737 Some(&error),
738 );
739 return Err(request_body_stream_closed_error(
740 "request the first request body chunk",
741 ));
742 }
743 } else {
744 log_request_body_stream_closed("connect to the request body stream", None);
745 return Err(request_body_stream_closed_error(
746 "connect to the request body stream",
747 ));
748 }
749 }
750
751 let mut sink = Some(sink);
752
753 ROUTER.add_typed_route(
754 body_port,
755 Box::new(move |message| {
756 info!("Received message");
757 let bytes = match message.unwrap() {
758 BodyChunkResponse::Chunk(bytes) => bytes,
759 BodyChunkResponse::Done => {
760 if fetch_terminated.send(false).is_err() {
763 log_fetch_terminated_send_failure(
764 false,
765 "handling request body completion",
766 );
767 }
768 if let Some(sink) = sink.take() {
769 sink.close();
770 }
771
772 return;
773 },
774 BodyChunkResponse::Error => {
775 if fetch_terminated.send(true).is_err() {
779 log_fetch_terminated_send_failure(
780 true,
781 "handling request body stream error",
782 );
783 }
784 if let Some(sink) = sink.take() {
785 sink.close();
786 }
787
788 return;
789 },
790 };
791
792 devtools_bytes.lock().extend_from_slice(&bytes);
793
794 {
797 let Some(sink) = sink.as_ref() else {
798 return;
799 };
800 sink.transmit_bytes(bytes);
801 }
802
803 let mut chunk_requester = chunk_requester.lock();
806 if let Some(chunk_requester) = chunk_requester.as_mut() {
807 if let Err(error) = chunk_requester.send(BodyChunkRequest::Chunk) {
808 log_request_body_stream_closed(
809 "request the next request body chunk",
810 Some(&error),
811 );
812 if fetch_terminated.send(true).is_err() {
813 log_fetch_terminated_send_failure(
814 true,
815 "handling failure to request the next request body chunk",
816 );
817 }
818 if let Some(sink) = sink.take() {
819 sink.close();
820 }
821 }
822 } else {
823 log_request_body_stream_closed("request the next request body chunk", None);
824 if fetch_terminated.send(true).is_err() {
825 log_fetch_terminated_send_failure(
826 true,
827 "handling a closed request body stream while requesting the next chunk",
828 );
829 }
830 if let Some(sink) = sink.take() {
831 sink.close();
832 }
833 }
834 }),
835 );
836
837 Ok(())
838}
839
840#[async_recursion]
842#[allow(clippy::too_many_arguments)]
843pub(crate) async fn http_fetch(
844 fetch_params: &mut FetchParams,
845 cache: &mut CorsCache,
846 cors_flag: bool,
847 cors_preflight_flag: bool,
848 authentication_fetch_flag: bool,
849 target: Target<'async_recursion>,
850 done_chan: &mut DoneChannel,
851 context: &FetchContext,
852) -> Response {
853 *done_chan = None;
855 let request = &mut fetch_params.request;
857
858 let mut response: Option<Response> = None;
860
861 if request.service_workers_mode == ServiceWorkersMode::All {
863 if let Some(ref res) = response {
868 if (res.response_type == ResponseType::Opaque && request.mode != RequestMode::NoCors) ||
876 (res.response_type == ResponseType::OpaqueRedirect &&
877 request.redirect_mode != RedirectMode::Manual) ||
878 (res.url_list.len() > 1 && request.redirect_mode != RedirectMode::Follow) ||
879 res.is_network_error()
880 {
881 return Response::network_error(NetworkError::ConnectionFailure);
882 }
883
884 }
887 }
888
889 if response.is_none() {
891 if cors_preflight_flag {
893 let method_cache_match = cache.match_method(request, request.method.clone());
894
895 let method_mismatch = !method_cache_match &&
899 (!is_cors_safelisted_method(&request.method) || request.use_cors_preflight);
900
901 let header_mismatch = request.headers.iter().any(|(name, value)| {
904 !cache.match_header(request, name) &&
905 !is_cors_safelisted_request_header(&name, &value)
906 });
907
908 if method_mismatch || header_mismatch {
910 let preflight_response = cors_preflight_fetch(request, cache, context).await;
913 if let Some(error) = preflight_response.get_network_error() {
915 return Response::network_error(error.clone());
916 }
917 }
918 }
919
920 if request.redirect_mode == RedirectMode::Follow {
923 request.service_workers_mode = ServiceWorkersMode::None;
924 }
925
926 let mut fetch_result = http_network_or_cache_fetch(
929 fetch_params,
930 authentication_fetch_flag,
931 cors_flag,
932 done_chan,
933 context,
934 )
935 .await;
936
937 if cors_flag && cors_check(&fetch_params.request, &fetch_result).is_err() {
940 return Response::network_error(NetworkError::CorsGeneral);
941 }
942
943 if let Err(()) = tao_check(&fetch_params.request, &fetch_result) {
946 context.timing.inner().mark_timing_check_failed();
947 }
948 fetch_result.return_internal = false;
949 response = Some(fetch_result);
950 }
951
952 let request = &mut fetch_params.request;
953
954 let mut response = response.unwrap();
956
957 if (request.response_tainting == ResponseTainting::Opaque ||
961 response.response_type == ResponseType::Opaque) &&
962 request.client.as_ref().is_some_and(|client| {
963 cross_origin_resource_policy_check(
964 &request.origin,
965 client,
966 &response,
967 ForNavigation::No,
968 ) == CrossOriginResourcePolicy::Blocked
969 })
970 {
971 return Response::network_error(NetworkError::CrossOriginResponse);
972 }
973
974 if response
976 .actual_response()
977 .status
978 .try_code()
979 .is_some_and(is_redirect_status)
980 {
981 if response.actual_response().status != StatusCode::SEE_OTHER {
988 }
990
991 response = match request.redirect_mode {
993 RedirectMode::Error => Response::network_error(NetworkError::RedirectError),
995 RedirectMode::Manual => {
996 if request.mode == RequestMode::Navigate {
999 let location_url =
1003 location_url_for_response(&response, request.current_url().fragment());
1004 response.actual_response_mut().location_url = location_url;
1005 response
1006 } else {
1007 response.to_filtered(ResponseType::OpaqueRedirect)
1010 }
1011 },
1012 RedirectMode::Follow => {
1013 response.return_internal = true;
1017
1018 http_redirect_fetch(
1021 fetch_params,
1022 cache,
1023 response,
1024 cors_flag,
1025 target,
1026 done_chan,
1027 context,
1028 )
1029 .await
1030 },
1031 };
1032 }
1033
1034 response.return_internal = true;
1036 context
1037 .timing
1038 .set_attribute(ResourceAttribute::RedirectCount(
1039 fetch_params.request.redirect_count as u16,
1040 ));
1041
1042 response.resource_timing = context.timing.clone();
1043
1044 response
1046}
1047
1048fn tao_check(request: &Request, response: &Response) -> Result<(), ()> {
1050 let Origin::Origin(ref request_origin) = request.origin else {
1052 unreachable!("origin cannot be \"client\" at this point");
1053 };
1054
1055 let values: Vec<&str> = response
1060 .headers
1061 .get_all("Timing-Allow-Origin")
1062 .iter()
1063 .map(|header_value| header_value.to_str().unwrap_or(""))
1064 .collect();
1065
1066 if values.contains(&"*") {
1068 return Ok(());
1069 }
1070
1071 if values
1074 .iter()
1075 .any(|header_str| *header_str == request_origin.ascii_serialization().as_ref())
1076 {
1077 return Ok(());
1078 }
1079
1080 if request.mode == RequestMode::Navigate && request.current_url().origin() != *request_origin {
1083 return Err(());
1084 }
1085
1086 if request.response_tainting == ResponseTainting::Basic {
1088 return Ok(());
1089 }
1090
1091 Err(())
1093}
1094
1095struct RedirectEndTimer(Option<ResourceFetchTimingContainer>);
1097
1098impl RedirectEndTimer {
1099 fn neuter(&mut self) {
1100 self.0 = None;
1101 }
1102}
1103
1104impl Drop for RedirectEndTimer {
1105 fn drop(&mut self) {
1106 let RedirectEndTimer(resource_fetch_timing_opt) = self;
1107
1108 resource_fetch_timing_opt.as_ref().map_or((), |t| {
1109 t.set_attribute(ResourceAttribute::RedirectEnd(RedirectEndValue::Zero));
1110 })
1111 }
1112}
1113
1114static REQUEST_BODY_HEADER_NAMES: &[HeaderName] = &[
1116 CONTENT_ENCODING,
1117 CONTENT_LANGUAGE,
1118 CONTENT_LOCATION,
1119 CONTENT_TYPE,
1120];
1121
1122fn location_url_for_response(
1124 response: &Response,
1125 request_fragment: Option<&str>,
1126) -> Option<Result<ServoUrl, String>> {
1127 assert!(
1129 response
1130 .actual_response()
1131 .status
1132 .try_code()
1133 .is_some_and(is_redirect_status)
1134 );
1135 let mut location = response
1137 .actual_response()
1138 .headers
1139 .get(header::LOCATION)
1140 .and_then(|header_value| {
1141 HeaderValue::to_str(header_value)
1142 .map(|location_string| {
1143 ServoUrl::parse_with_base(response.actual_response().url(), location_string)
1145 .map_err(|error| error.to_string())
1146 })
1147 .ok()
1148 });
1149
1150 if let Some(Ok(ref mut location)) = location &&
1152 location.fragment().is_none()
1153 {
1154 location.set_fragment(request_fragment);
1155 }
1156 location
1158}
1159
1160#[async_recursion]
1162pub async fn http_redirect_fetch(
1163 fetch_params: &mut FetchParams,
1164 cache: &mut CorsCache,
1165 mut response: Response,
1166 cors_flag: bool,
1167 target: Target<'async_recursion>,
1168 done_chan: &mut DoneChannel,
1169 context: &FetchContext,
1170) -> Response {
1171 let mut redirect_end_timer = RedirectEndTimer(Some(context.timing.clone()));
1172
1173 let request = &mut fetch_params.request;
1175
1176 assert!(response.return_internal);
1178
1179 let location_url = location_url_for_response(&response, request.current_url().fragment());
1181 response.actual_response_mut().location_url = location_url.clone();
1182
1183 let location_url = match location_url {
1184 None => return response,
1186 Some(Err(err)) => {
1188 return Response::network_error(NetworkError::ResourceLoadError(
1189 "Location URL parse failure: ".to_owned() + &err,
1190 ));
1191 },
1192 Some(Ok(url)) if !matches!(url.scheme(), "http" | "https") => {
1194 return Response::network_error(NetworkError::UnsupportedScheme);
1195 },
1196 Some(Ok(url)) => url,
1197 };
1198
1199 context.timing.set_attributes(&[
1204 ResourceAttribute::RedirectStart(RedirectStartValue::FetchStart),
1205 ResourceAttribute::FetchStart,
1206 ResourceAttribute::StartTime(ResourceTimeValue::FetchStart),
1207 ResourceAttribute::StartTime(ResourceTimeValue::RedirectStart),
1208 ]);
1209
1210 if request.redirect_count >= 20 {
1212 return Response::network_error(NetworkError::TooManyRedirects);
1213 }
1214
1215 request.redirect_count += 1;
1217
1218 let same_origin = match request.origin {
1221 Origin::Origin(ref origin) => *origin == location_url.origin(),
1222 Origin::Client => panic!(
1223 "Request origin should not be client for {}",
1224 request.current_url()
1225 ),
1226 };
1227
1228 let has_credentials = has_credentials(&location_url);
1229
1230 if request.mode == RequestMode::CorsMode && !same_origin && has_credentials {
1231 return Response::network_error(NetworkError::CorsCredentials);
1232 }
1233
1234 if cors_flag && location_url.origin() != request.current_url().origin() {
1235 request.origin = Origin::Origin(ImmutableOrigin::new_opaque());
1236 }
1237
1238 if cors_flag && has_credentials {
1240 return Response::network_error(NetworkError::CorsCredentials);
1241 }
1242
1243 if response.actual_response().status != StatusCode::SEE_OTHER &&
1246 request.body.as_ref().is_some_and(|b| b.source_is_null())
1247 {
1248 return Response::network_error(NetworkError::ConnectionFailure);
1249 }
1250
1251 if response
1253 .actual_response()
1254 .status
1255 .try_code()
1256 .is_some_and(|code| {
1257 ((code == StatusCode::MOVED_PERMANENTLY || code == StatusCode::FOUND) &&
1259 request.method == Method::POST) ||
1260 (code == StatusCode::SEE_OTHER &&
1262 request.method != Method::HEAD &&
1263 request.method != Method::GET)
1264 })
1265 {
1266 request.method = Method::GET;
1268 request.body = None;
1269 for name in REQUEST_BODY_HEADER_NAMES {
1271 request.headers.remove(name);
1272 }
1273 }
1274
1275 if location_url.origin() != request.current_url().origin() {
1279 request.headers.remove(AUTHORIZATION);
1282 }
1283
1284 if let Some(body) = request.body.as_mut() {
1287 body.extract_source();
1288 }
1289
1290 request
1294 .url_list
1295 .push(UrlWithBlobClaim::from_url_without_having_claimed_blob(
1296 location_url,
1297 ));
1298
1299 set_requests_referrer_policy_on_redirect(request, response.actual_response());
1301
1302 let recursive_flag = request.redirect_mode != RedirectMode::Manual;
1305
1306 let fetch_response = main_fetch(
1308 fetch_params,
1309 cache,
1310 recursive_flag,
1311 target,
1312 done_chan,
1313 context,
1314 )
1315 .await;
1316
1317 context.timing.set_attribute(ResourceAttribute::RedirectEnd(
1319 RedirectEndValue::ResponseEnd,
1320 ));
1321 redirect_end_timer.neuter();
1322
1323 fetch_response
1324}
1325
1326#[async_recursion]
1328#[servo_tracing::instrument(skip_all,fields(url=fetch_params.request.url().as_str()))]
1329async fn http_network_or_cache_fetch(
1330 fetch_params: &mut FetchParams,
1331 authentication_fetch_flag: bool,
1332 cors_flag: bool,
1333 done_chan: &mut DoneChannel,
1334 context: &FetchContext,
1335) -> Response {
1336 let http_fetch_params: &mut FetchParams;
1338 let mut fetch_params_copy: FetchParams;
1339
1340 let mut response: Option<Response> = None;
1344
1345 let mut revalidating_flag = false;
1347
1348 let http_request = if fetch_params.request.traversable_for_user_prompts ==
1352 TraversableForUserPrompts::NoTraversable &&
1353 fetch_params.request.redirect_mode == RedirectMode::Error
1354 {
1355 http_fetch_params = fetch_params;
1356 &mut http_fetch_params.request
1357 }
1358 else {
1360 fetch_params_copy =
1363 std::mem::replace(fetch_params, FetchParams::new(fetch_params.request.clone()));
1364 http_fetch_params = &mut fetch_params_copy;
1365
1366 &mut http_fetch_params.request
1367 };
1368
1369 let include_credentials = match http_request.credentials_mode {
1371 CredentialsMode::Include => true,
1373 CredentialsMode::CredentialsSameOrigin
1375 if http_request.response_tainting == ResponseTainting::Basic =>
1376 {
1377 true
1378 },
1379 _ => false,
1380 };
1381
1382 let content_length = http_request
1389 .body
1390 .as_ref()
1391 .and_then(|body| body.len().map(|size| size as u64));
1392
1393 let mut content_length_header_value = None;
1395
1396 if http_request.body.is_none() && matches!(http_request.method, Method::POST | Method::PUT) {
1399 content_length_header_value = Some(0);
1400 }
1401
1402 if let Some(content_length) = content_length {
1406 content_length_header_value = Some(content_length);
1407 };
1408
1409 if let Some(content_length_header_value) = content_length_header_value {
1412 http_request
1413 .headers
1414 .typed_insert(ContentLength(content_length_header_value));
1415 }
1416
1417 if http_request.keep_alive &&
1419 let Some(content_length) = content_length
1420 {
1421 let in_flight_keep_alive_bytes: u64 = context
1426 .in_flight_keep_alive_records
1427 .lock()
1428 .get(
1429 &http_request
1430 .pipeline_id
1431 .expect("Must always set a pipeline ID for keep-alive requests"),
1432 )
1433 .map(|records| {
1434 records
1438 .iter()
1439 .map(|record| {
1440 if record.request_id == http_request.id {
1441 0
1444 } else {
1445 record.keep_alive_body_length
1446 }
1447 })
1448 .sum()
1449 })
1450 .unwrap_or_default();
1451 if content_length + in_flight_keep_alive_bytes > 64 * 1024 {
1453 return Response::network_error(NetworkError::TooManyInFlightKeepAliveRequests);
1454 }
1455 }
1456
1457 match http_request.referrer {
1459 Referrer::ReferrerUrl(ref http_request_referrer) |
1460 Referrer::Client(ref http_request_referrer) => {
1461 if let Ok(referer) = http_request_referrer.as_str().parse::<Referer>() {
1464 http_request.headers.typed_insert(referer);
1466 } else {
1467 error!("Failed to parse {} as referrer", http_request_referrer);
1471 }
1472 },
1473 _ => {},
1474 };
1475
1476 append_a_request_origin_header(http_request);
1478
1479 append_the_fetch_metadata_headers(http_request);
1481
1482 if http_request.initiator == Initiator::Prefetch &&
1485 let Ok(value) = HeaderValue::from_str("prefetch")
1486 {
1487 http_request.headers.insert("Sec-Purpose", value);
1488 }
1489
1490 if !http_request.headers.contains_key(header::USER_AGENT) {
1493 http_request
1494 .headers
1495 .typed_insert::<UserAgent>(context.user_agent.parse().unwrap());
1496 }
1497
1498 append_cache_data_to_headers(http_request);
1500
1501 if http_request.headers.contains_key(header::RANGE) &&
1504 let Ok(value) = HeaderValue::from_str("identity")
1505 {
1506 http_request.headers.insert("Accept-Encoding", value);
1507 }
1508
1509 http_request.headers.remove(header::HOST);
1513 set_default_accept_encoding(&mut http_request.headers);
1515
1516 let current_url = http_request.current_url();
1517
1518 if include_credentials {
1521 set_request_cookies(
1525 ¤t_url,
1526 &mut http_request.headers,
1527 &context.state.cookie_jar,
1528 );
1529 if !http_request.headers.contains_key(header::AUTHORIZATION) {
1531 let mut authorization_value = None;
1533
1534 if let Some(basic) = auth_from_cache(&context.state.auth_cache, ¤t_url.origin()) &&
1536 (!http_request.use_url_credentials || !has_credentials(¤t_url))
1537 {
1538 authorization_value = Some(basic);
1539 }
1540
1541 if authentication_fetch_flag &&
1543 authorization_value.is_none() &&
1544 has_credentials(¤t_url)
1545 {
1546 authorization_value = Some(Authorization::basic(
1547 current_url.username(),
1548 current_url.password().unwrap_or(""),
1549 ));
1550 }
1551
1552 if let Some(basic) = authorization_value {
1554 http_request.headers.typed_insert(basic);
1555 }
1556 }
1557 }
1558
1559 let should_wait = {
1561 let mut cache_guard = block_for_cache_ready(
1563 context,
1564 http_request,
1565 done_chan,
1566 &mut revalidating_flag,
1567 &mut response,
1568 )
1569 .await;
1570
1571 if response.is_none() {
1575 if http_request.cache_mode == CacheMode::OnlyIfCached {
1577 return Response::network_error(NetworkError::CacheError);
1579 }
1580
1581 drop(cache_guard);
1584 let forward_response =
1585 http_network_fetch(http_fetch_params, include_credentials, done_chan, context)
1586 .await;
1587
1588 let http_request = &mut http_fetch_params.request;
1589 let request_key = CacheKey::new(http_request);
1590 cache_guard = context
1591 .state
1592 .http_cache
1593 .get_or_guard(request_key.clone())
1594 .await;
1595 if forward_response.status.in_range(200..=399) && !http_request.method.is_safe() {
1599 if let Some(guard) = cache_guard.try_as_mut() {
1600 invalidate_cached_resources(guard);
1601 }
1602 context
1603 .state
1604 .http_cache
1605 .invalidate_related_urls(http_request, &forward_response, &request_key)
1606 .await;
1607 }
1608
1609 if revalidating_flag && forward_response.status == StatusCode::NOT_MODIFIED {
1611 *done_chan = None;
1614 if let Some(guard) = cache_guard.try_as_mut() {
1615 response = refresh(http_request, forward_response.clone(), done_chan, guard);
1616 }
1617
1618 if let Some(response) = &mut response {
1619 response.cache_state = CacheState::Validated;
1620 }
1621 }
1622
1623 if response.is_none() {
1625 let forward_response = response.insert(forward_response);
1627
1628 if http_request.cache_mode != CacheMode::NoStore {
1631 cache_guard.insert(http_request, forward_response);
1634 }
1635 }
1636 false
1637 } else {
1638 true
1639 }
1640 }; if should_wait {
1643 wait_for_inflight_requests(done_chan, &mut response).await;
1648 }
1649
1650 let http_request = &mut http_fetch_params.request;
1651 let mut response = response.unwrap();
1652
1653 response.url_list = http_request
1655 .url_list
1656 .iter()
1657 .map(|claimed_url| claimed_url.url())
1658 .collect();
1659
1660 if http_request.headers.contains_key(RANGE) {
1662 response.range_requested = true;
1663 }
1664
1665 response.request_includes_credentials = include_credentials;
1667
1668 if response.status.try_code() == Some(StatusCode::UNAUTHORIZED) &&
1674 !cors_flag &&
1675 include_credentials &&
1676 response.headers.contains_key(WWW_AUTHENTICATE)
1677 {
1678 let request = &mut fetch_params.request;
1681
1682 if request
1685 .body
1686 .as_ref()
1687 .is_some_and(|body| body.source_is_null())
1688 {
1689 return Response::network_error(NetworkError::ConnectionFailure);
1690 }
1691
1692 if !request.use_url_credentials || authentication_fetch_flag {
1694 let Some(credentials) = context
1695 .state
1696 .request_authentication(request, &response)
1697 .await
1698 else {
1699 return response;
1700 };
1701
1702 if let Err(err) = request
1703 .current_url_mut()
1704 .set_username(&credentials.username)
1705 {
1706 error!("error setting username for url: {:?}", err);
1707 return response;
1708 };
1709
1710 if let Err(err) = request
1711 .current_url_mut()
1712 .set_password(Some(&credentials.password))
1713 {
1714 error!("error setting password for url: {:?}", err);
1715 return response;
1716 };
1717 }
1718
1719 *done_chan = None;
1722
1723 response = http_network_or_cache_fetch(
1725 fetch_params,
1726 true, cors_flag,
1728 done_chan,
1729 context,
1730 )
1731 .await;
1732 }
1733
1734 if response.status == StatusCode::PROXY_AUTHENTICATION_REQUIRED {
1736 let request = &mut fetch_params.request;
1737 if request.traversable_for_user_prompts == TraversableForUserPrompts::NoTraversable {
1740 return Response::network_error(NetworkError::ResourceLoadError(
1741 "Can't find Window object".into(),
1742 ));
1743 }
1744
1745 let Some(credentials) = context
1753 .state
1754 .request_authentication(request, &response)
1755 .await
1756 else {
1757 return response;
1758 };
1759
1760 let entry = AuthCacheEntry {
1762 user_name: credentials.username,
1763 password: credentials.password,
1764 };
1765 {
1766 let mut auth_cache = context.state.auth_cache.write();
1767 let key = request
1768 .current_url()
1769 .origin()
1770 .ascii_serialization()
1771 .into_owned();
1772 auth_cache.entries.insert(key, entry);
1773 }
1774
1775 *done_chan = None;
1778
1779 response = http_network_or_cache_fetch(
1781 fetch_params,
1782 false, cors_flag,
1784 done_chan,
1785 context,
1786 )
1787 .await;
1788 }
1789
1790 if authentication_fetch_flag {
1798 }
1800
1801 response
1803}
1804
1805#[servo_tracing::instrument(skip_all)]
1813async fn block_for_cache_ready<'a>(
1814 context: &'a FetchContext,
1815 http_request: &mut Request,
1816 done_chan: &mut DoneChannel,
1817 revalidating_flag: &mut bool,
1818 response: &mut Option<Response>,
1819) -> CachedResourcesOrGuard<'a> {
1820 let entry_key = CacheKey::new(http_request);
1821 let guard_result = context.state.http_cache.get_or_guard(entry_key).await;
1822
1823 match guard_result {
1824 CachedResourcesOrGuard::Guard(_) => {
1825 *done_chan = None;
1826 },
1827 CachedResourcesOrGuard::Value(ref cached_resources) => {
1828 let stored_response = construct_response(http_request, done_chan, cached_resources);
1834 if let Some(response_from_cache) = stored_response {
1836 let response_headers = response_from_cache.response.headers.clone();
1837 let validation_status = response_from_cache.validation_status;
1838 let revalidation_guard = response_from_cache.revalidation_guard.clone();
1839
1840 let (cached_response, needs_synchronous_revalidation) =
1842 match (http_request.cache_mode, &http_request.mode) {
1843 (CacheMode::ForceCache, _) => (Some(response_from_cache.response), false),
1844 (CacheMode::OnlyIfCached, &RequestMode::SameOrigin) => {
1845 (Some(response_from_cache.response), false)
1846 },
1847 (CacheMode::OnlyIfCached, _) |
1848 (CacheMode::NoStore, _) |
1849 (CacheMode::Reload, _) => (None, false),
1850 (_, _) => (
1851 Some(response_from_cache.response),
1852 validation_status ==
1853 (ValidationStatus::Stale {
1854 revalidate_in_background: false,
1855 }),
1856 ),
1857 };
1858
1859 if needs_synchronous_revalidation {
1860 *revalidating_flag = true;
1861 if let Some(http_date) = response_headers.typed_get::<LastModified>() {
1863 let http_date: SystemTime = http_date.into();
1864 http_request
1865 .headers
1866 .typed_insert(IfModifiedSince::from(http_date));
1867 }
1868 if let Some(entity_tag) = response_headers.get(header::ETAG) {
1869 http_request
1870 .headers
1871 .insert(header::IF_NONE_MATCH, entity_tag.clone());
1872 }
1873 } else {
1874 let revalidate_in_background = validation_status ==
1877 (ValidationStatus::Stale {
1878 revalidate_in_background: true,
1879 });
1880 if revalidate_in_background && cached_response.is_some() {
1881 spawn_stale_while_revalidate(context, http_request, revalidation_guard);
1882 }
1883 *response = cached_response;
1884 if let Some(response) = response {
1885 response.cache_state = CacheState::Local;
1886 }
1887 }
1888 if response.is_none() {
1889 *done_chan = None;
1892 }
1893 }
1894 },
1895 }
1896 guard_result
1897}
1898
1899fn spawn_stale_while_revalidate(
1902 context: &FetchContext,
1903 http_request: &Request,
1904 revalidation_guard: StdArc<AtomicBool>,
1905) {
1906 if revalidation_guard
1908 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
1909 .is_err()
1910 {
1911 return;
1912 }
1913
1914 let mut revalidation_request = http_request.clone();
1917 revalidation_request.cache_mode = CacheMode::NoCache;
1918
1919 revalidation_request.service_workers_mode = ServiceWorkersMode::None;
1921
1922 let context = context.clone();
1923 debug!(
1924 "spawning stale-while-revalidate background revalidation for {:?}",
1925 revalidation_request.current_url()
1926 );
1927 spawn_task(async move {
1928 let mut target = DiscardFetch;
1929
1930 let _ = fetch(revalidation_request, &mut target, &context).await;
1931 revalidation_guard.store(false, Ordering::Release);
1932 });
1933}
1934
1935async fn wait_for_inflight_requests(done_chan: &mut DoneChannel, response: &mut Option<Response>) {
1938 if let Some(ref mut ch) = *done_chan {
1939 assert!(response.is_some());
1943
1944 loop {
1945 match ch.1.recv().await {
1946 Some(Data::ContentLength(_)) | Some(Data::Payload(_)) | Some(Data::Error(_)) => {},
1947 Some(Data::Done) => break, Some(Data::Cancelled) => {
1949 break;
1951 },
1952 None => panic!("HTTP cache should always send Done or Cancelled"),
1953 }
1954 }
1955 }
1956 *done_chan = None;
1958}
1959
1960#[derive(PartialEq)]
1964enum CrossOriginResourcePolicy {
1965 Allowed,
1966 Blocked,
1967}
1968
1969enum ForNavigation {
1970 #[expect(dead_code)]
1971 Yes,
1972 No,
1973}
1974
1975fn cross_origin_resource_policy_check(
1977 origin: &Origin,
1978 request_client: &RequestClient,
1979 response: &Response,
1980 for_navigation: ForNavigation,
1981) -> CrossOriginResourcePolicy {
1982 let embedder_policy = &request_client.policy_container.embedder_policy;
1988
1989 if cross_origin_resource_policy_internal_check(
1992 origin,
1993 EmbedderPolicyValue::UnsafeNone,
1994 response,
1995 &for_navigation,
1996 ) == CrossOriginResourcePolicy::Blocked
1997 {
1998 return CrossOriginResourcePolicy::Blocked;
1999 }
2000
2001 if cross_origin_resource_policy_internal_check(
2009 origin,
2010 embedder_policy.value,
2011 response,
2012 &for_navigation,
2013 ) == CrossOriginResourcePolicy::Allowed
2014 {
2015 return CrossOriginResourcePolicy::Allowed;
2016 }
2017
2018 CrossOriginResourcePolicy::Blocked
2023}
2024
2025fn cross_origin_resource_policy_internal_check(
2027 origin: &Origin,
2028 embedder_policy_value: EmbedderPolicyValue,
2029 response: &Response,
2030 for_navigation: &ForNavigation,
2031) -> CrossOriginResourcePolicy {
2032 if let ForNavigation::Yes = for_navigation &&
2034 let EmbedderPolicyValue::UnsafeNone = embedder_policy_value
2035 {
2036 return CrossOriginResourcePolicy::Allowed;
2037 }
2038
2039 let policy = response
2041 .headers
2042 .get(HeaderName::from_static("cross-origin-resource-policy"))
2043 .and_then(|h| h.to_str().ok());
2044
2045 let policy = policy
2047 .filter(|&s| s == "same-origin" || s == "same-site" || s == "cross-origin")
2048 .or(match embedder_policy_value {
2050 EmbedderPolicyValue::UnsafeNone => None,
2052 EmbedderPolicyValue::RequireCorp => Some("same-origin"),
2054 });
2055
2056 match policy {
2058 Some("same-origin") => {
2059 if let Origin::Origin(request_origin) = origin &&
2061 response
2062 .url()
2063 .is_some_and(|url| request_origin == &url.origin())
2064 {
2065 return CrossOriginResourcePolicy::Allowed;
2066 }
2067
2068 CrossOriginResourcePolicy::Blocked
2070 },
2071 Some("same-site") => {
2072 if let Some(response_url) = response.url() {
2073 if let Origin::Origin(request_origin) = origin &&
2077 is_schemelessy_same_site(request_origin, &response_url.origin()) &&
2078 (request_origin.scheme() == Some("https") ||
2079 response_url.scheme() != "https")
2080 {
2081 return CrossOriginResourcePolicy::Allowed;
2082 }
2083 }
2084 CrossOriginResourcePolicy::Blocked
2086 },
2087 _ => CrossOriginResourcePolicy::Allowed,
2090 }
2091}
2092
2093struct ResponseEndTimer(Option<ResourceFetchTimingContainer>);
2095
2096impl ResponseEndTimer {
2097 fn neuter(&mut self) {
2098 self.0 = None;
2099 }
2100}
2101
2102impl Drop for ResponseEndTimer {
2103 fn drop(&mut self) {
2104 let ResponseEndTimer(resource_fetch_timing_opt) = self;
2105
2106 resource_fetch_timing_opt.as_ref().map_or((), |t| {
2107 t.set_attribute(ResourceAttribute::ResponseEnd);
2108 })
2109 }
2110}
2111
2112#[servo_tracing::instrument(skip_all,fields(url=fetch_params.request.url().as_str()))]
2114async fn http_network_fetch(
2115 fetch_params: &mut FetchParams,
2116 credentials_flag: bool,
2117 done_chan: &mut DoneChannel,
2118 context: &FetchContext,
2119) -> Response {
2120 let mut response_end_timer = ResponseEndTimer(Some(context.timing.clone()));
2121
2122 let request = &mut fetch_params.request;
2124
2125 let url = request.current_url();
2135 let request_id = request.id.0.to_string();
2136 if log_enabled!(log::Level::Info) {
2137 info!("{:?} request for {}", request.method, url);
2138 for header in request.headers.iter() {
2139 debug!(" - {:?}", header);
2140 }
2141 }
2142
2143 let is_xhr = request.destination == Destination::None;
2147
2148 let (fetch_terminated_sender, mut fetch_terminated_receiver) = unbounded_channel();
2150
2151 let body = request.body.as_ref().map(|body| body.clone_stream());
2152
2153 if body.is_none() {
2154 let _ = fetch_terminated_sender.send(false);
2159 }
2160
2161 let browsing_context_id = request.target_webview_id.map(Into::into);
2162
2163 let (response_stream, msg) = match &request.mode {
2167 RequestMode::WebSocket {
2169 protocols,
2170 original_url: _,
2171 } => {
2172 let (resource_event_sender, dom_action_receiver) = {
2175 let mut websocket_chan = context.websocket_chan.as_ref().unwrap().lock();
2176 (
2177 websocket_chan.sender.clone(),
2178 websocket_chan.receiver.take().unwrap(),
2179 )
2180 };
2181
2182 let mut tls_config = create_tls_config(
2183 context.ca_certificates.clone(),
2184 context.ignore_certificate_errors,
2185 context.state.override_manager.clone(),
2186 );
2187 tls_config.alpn_protocols = vec!["http/1.1".to_string().into()];
2188
2189 let response = match start_websocket(
2190 context.state.clone(),
2191 resource_event_sender,
2192 protocols,
2193 request,
2194 tls_config,
2195 dom_action_receiver,
2196 )
2197 .await
2198 {
2199 Ok(response) => response,
2200 Err(error) => {
2201 return Response::network_error(NetworkError::WebsocketConnectionFailure(
2202 format!("{error:?}"),
2203 ));
2204 },
2205 };
2206
2207 let response = response.map(|r| match r {
2208 Some(body) => Full::from(body).map_err(|_| unreachable!()).boxed(),
2209 None => http_body_util::Empty::new()
2210 .map_err(|_| unreachable!())
2211 .boxed(),
2212 });
2213 (Decoder::detect(response, url.is_secure_scheme()), None)
2214 },
2215 _ => {
2218 let response_future = obtain_response(
2219 &context.state.client,
2220 &url,
2221 &request.method,
2222 &mut request.headers,
2223 body,
2224 request
2225 .body
2226 .as_ref()
2227 .is_some_and(|body| body.source_is_null()),
2228 &request.pipeline_id,
2229 Some(&request_id),
2230 request.destination,
2231 is_xhr,
2232 context,
2233 fetch_terminated_sender,
2234 browsing_context_id,
2235 );
2236
2237 let (response_stream, msg) = match response_future.await {
2239 Ok(wrapped_response) => wrapped_response,
2240 Err(error) => return Response::network_error(error),
2241 };
2242 (response_stream, msg)
2243 },
2244 };
2245
2246 if log_enabled!(log::Level::Info) {
2247 debug!("{:?} response for {}", response_stream.version(), url);
2248 for header in response_stream.headers().iter() {
2249 debug!(" - {:?}", header);
2250 }
2251 }
2252
2253 match fetch_terminated_receiver.recv().await {
2256 Some(true) => return Response::network_error(NetworkError::ConnectionFailure),
2257 Some(false) => {},
2258 _ => warn!("Failed to receive confirmation request was streamed without error."),
2259 }
2260
2261 let timing = context.timing.inner().clone();
2262 let mut response = Response::new(url.clone(), timing);
2263
2264 if let Some(handshake_info) = response_stream.extensions().get::<TlsHandshakeInfo>() {
2265 let mut hsts_enabled = url
2266 .host_str()
2267 .is_some_and(|host| context.state.hsts_list.read().is_host_secure(host));
2268
2269 if url.scheme() == "https" &&
2270 let Some(strict_transport_security) = response_stream
2271 .headers()
2272 .typed_get::<StrictTransportSecurity>()
2273 {
2274 hsts_enabled = strict_transport_security.max_age().as_secs() > 0;
2276 }
2277 response.tls_security_info = Some(build_tls_security_info(handshake_info, hsts_enabled));
2278 }
2279
2280 let status_text = response_stream
2281 .extensions()
2282 .get::<ReasonPhrase>()
2283 .map(ReasonPhrase::as_bytes)
2284 .or_else(|| {
2285 response_stream
2286 .status()
2287 .canonical_reason()
2288 .map(str::as_bytes)
2289 })
2290 .map(Vec::from)
2291 .unwrap_or_default();
2292 response.status = HttpStatus::new(response_stream.status(), status_text);
2293
2294 info!(
2295 "got {:?} response for {:?}",
2296 response_stream.status(),
2297 request.url()
2298 );
2299 response.headers = response_stream.headers().clone();
2300 response.referrer = request.referrer.to_url().cloned();
2301 response.referrer_policy = request.referrer_policy;
2302
2303 let response_body = response.body.clone();
2304
2305 let (done_sender, done_receiver) = unbounded_channel();
2307 *done_chan = Some((done_sender.clone(), done_receiver));
2308
2309 let devtools_sender = context.devtools_chan.clone();
2310 let cancellation_listener = context.cancellation_listener.clone();
2311 if cancellation_listener.cancelled() {
2312 return Response::network_error(NetworkError::LoadCancelled);
2313 }
2314
2315 *response_body.lock() = ResponseBody::Receiving(vec![]);
2316 let response_body2 = response_body.clone();
2317
2318 if let Some(ref sender) = devtools_sender &&
2319 let Some(m) = msg
2320 {
2321 send_request_to_devtools(m, sender);
2322 }
2323
2324 let done_sender2 = done_sender.clone();
2325 let done_sender3 = done_sender.clone();
2326 let timing_ptr2 = context.timing.clone();
2327 let timing_ptr3 = context.timing.clone();
2328 let devtools_request = request.clone();
2329 let url1 = devtools_request.url();
2330 let url2 = url1.clone();
2331
2332 let status = response.status.clone();
2333 let headers = response.headers.clone();
2334 let devtools_chan = context.devtools_chan.clone();
2335
2336 if let Some(possible_length) = response_stream
2337 .headers()
2338 .get(http::header::CONTENT_LENGTH)
2339 .and_then(|header_value| header_value.to_str().ok())
2340 .and_then(|s| s.parse().ok())
2341 .map(|length| min(length, pref!(network_max_content_length) as usize))
2342 {
2343 let _ = done_sender.send(Data::ContentLength(possible_length));
2344 }
2345
2346 spawn_task(
2347 response_stream
2348 .into_body()
2349 .try_fold(response_body, move |response_body_accumulator, chunk| {
2350 if cancellation_listener.cancelled() {
2351 *response_body_accumulator.lock() = ResponseBody::Done(vec![]);
2352 let _ = done_sender.send(Data::Cancelled);
2353 return future::ready(Err(std::io::Error::new(
2354 std::io::ErrorKind::Interrupted,
2355 "Fetch aborted",
2356 )));
2357 }
2358 if let ResponseBody::Receiving(ref mut body) = *response_body_accumulator.lock() {
2359 body.extend_from_slice(&chunk);
2360 let _ = done_sender.send(Data::Payload(chunk));
2361 }
2362 future::ready(Ok(response_body_accumulator))
2363 })
2364 .and_then(move |complete_response_body| {
2365 debug!("successfully finished response for {:?}", url1);
2366 let mut body = complete_response_body.lock();
2367 let mut completed_body = match *body {
2368 ResponseBody::Receiving(ref mut body) => std::mem::take(body),
2369 _ => vec![],
2370 };
2371 completed_body.shrink_to_fit();
2373 let devtools_response_body =
2376 devtools_chan.is_some().then(|| completed_body.clone());
2377 *body = ResponseBody::Done(completed_body);
2378 send_response_values_to_devtools(
2379 Some(headers),
2380 status,
2381 devtools_response_body,
2382 CacheState::None,
2383 &devtools_request,
2384 devtools_chan,
2385 );
2386 timing_ptr2.set_attribute(ResourceAttribute::ResponseEnd);
2387 let _ = done_sender2.send(Data::Done);
2388 future::ready(Ok(()))
2389 })
2390 .map_err(move |error| {
2391 if let std::io::ErrorKind::InvalidData = error.kind() {
2392 debug!("Content decompression error for {:?}", url2);
2393 let _ = done_sender3.send(Data::Error(NetworkError::DecompressionError));
2394 let mut body = response_body2.lock();
2395
2396 *body = ResponseBody::Done(vec![]);
2397 }
2398 debug!("finished response for {:?}", url2);
2399 let mut body = response_body2.lock();
2400 let completed_body = match *body {
2401 ResponseBody::Receiving(ref mut body) => std::mem::take(body),
2402 _ => vec![],
2403 };
2404 *body = ResponseBody::Done(completed_body);
2405 timing_ptr3.set_attribute(ResourceAttribute::ResponseEnd);
2406 let _ = done_sender3.send(Data::Done);
2407 }),
2408 );
2409
2410 if credentials_flag {
2428 set_cookies_from_headers(&url, &response.headers, &context.state.cookie_jar);
2429 }
2430 context
2431 .state
2432 .hsts_list
2433 .write()
2434 .update_hsts_list_from_response(&url, &response.headers);
2435
2436 response_end_timer.neuter();
2450 response
2451}
2452
2453async fn cors_preflight_fetch(
2455 request: &Request,
2456 cache: &mut CorsCache,
2457 context: &FetchContext,
2458) -> Response {
2459 let mut preflight = RequestBuilder::new(
2464 request.target_webview_id,
2465 request.current_url_with_blob_claim(),
2466 request.referrer.clone(),
2467 )
2468 .method(Method::OPTIONS)
2469 .origin(match &request.origin {
2470 Origin::Client => {
2471 unreachable!("We shouldn't get Client origin in cors_preflight_fetch.")
2472 },
2473 Origin::Origin(origin) => origin.clone(),
2474 })
2475 .pipeline_id(request.pipeline_id)
2476 .initiator(request.initiator)
2477 .destination(request.destination)
2478 .referrer_policy(request.referrer_policy)
2479 .mode(RequestMode::CorsMode)
2480 .response_tainting(ResponseTainting::CorsTainting)
2481 .policy_container(match &request.policy_container {
2482 RequestPolicyContainer::Client => {
2483 unreachable!("We should have a policy container for request in cors_preflight_fetch")
2484 },
2485 RequestPolicyContainer::PolicyContainer(policy_container) => policy_container.clone(),
2486 })
2487 .url_list(
2488 request
2489 .url_list
2490 .iter()
2491 .map(|claimed_url| claimed_url.url())
2492 .collect(),
2493 )
2494 .build();
2495
2496 preflight
2498 .headers
2499 .insert(ACCEPT, HeaderValue::from_static("*/*"));
2500
2501 preflight
2503 .headers
2504 .typed_insert::<AccessControlRequestMethod>(AccessControlRequestMethod::from(
2505 request.method.clone(),
2506 ));
2507
2508 let headers = get_cors_unsafe_header_names(&request.headers);
2510
2511 if !headers.is_empty() {
2513 preflight.headers.insert(
2516 ACCESS_CONTROL_REQUEST_HEADERS,
2517 HeaderValue::from_bytes(itertools::join(headers.iter(), ",").as_bytes())
2518 .unwrap_or(HeaderValue::from_static("")),
2519 );
2520 }
2521
2522 let mut fetch_params = FetchParams::new(preflight);
2525 let response =
2526 http_network_or_cache_fetch(&mut fetch_params, false, false, &mut None, context).await;
2527
2528 if cors_check(request, &response).is_ok() && response.status.code().is_success() {
2530 let mut methods = if response
2533 .headers
2534 .contains_key(header::ACCESS_CONTROL_ALLOW_METHODS)
2535 {
2536 match response.headers.typed_get::<AccessControlAllowMethods>() {
2537 Some(methods) => methods.iter().collect(),
2538 None => {
2540 return Response::network_error(NetworkError::CorsAllowMethods);
2541 },
2542 }
2543 } else {
2544 vec![]
2545 };
2546
2547 let header_names = if response
2550 .headers
2551 .contains_key(header::ACCESS_CONTROL_ALLOW_HEADERS)
2552 {
2553 match response.headers.typed_get::<AccessControlAllowHeaders>() {
2554 Some(names) => names.iter().collect(),
2555 None => {
2557 return Response::network_error(NetworkError::CorsAllowHeaders);
2558 },
2559 }
2560 } else {
2561 vec![]
2562 };
2563
2564 debug!(
2565 "CORS check: Allowed methods: {:?}, current method: {:?}",
2566 methods, request.method
2567 );
2568
2569 if methods.is_empty() && request.use_cors_preflight {
2572 methods = vec![request.method.clone()];
2573 }
2574
2575 if methods
2578 .iter()
2579 .all(|method| *method.as_str() != *request.method.as_ref()) &&
2580 !is_cors_safelisted_method(&request.method) &&
2581 (request.credentials_mode == CredentialsMode::Include ||
2582 methods.iter().all(|method| method.as_ref() != "*"))
2583 {
2584 return Response::network_error(NetworkError::CorsMethod);
2585 }
2586
2587 debug!(
2588 "CORS check: Allowed headers: {:?}, current headers: {:?}",
2589 header_names, request.headers
2590 );
2591
2592 let header_names_set: HashSet<&HeaderName> = HashSet::from_iter(header_names.iter());
2598 if request.headers.iter().any(|(name, _)| {
2599 is_cors_non_wildcard_request_header_name(name) &&
2600 !header_names_set.contains(name) &&
2601 !header_names_set.contains(&HeaderName::from_static("*"))
2602 }) {
2603 return Response::network_error(NetworkError::CorsAuthorization);
2604 }
2605
2606 let unsafe_names = get_cors_unsafe_header_names(&request.headers);
2610 for unsafe_name in unsafe_names.iter() {
2611 if !header_names_set.contains(unsafe_name) &&
2612 (request.credentials_mode == CredentialsMode::Include ||
2613 !header_names_set.contains(&HeaderName::from_static("*")))
2614 {
2615 return Response::network_error(NetworkError::CorsHeaders);
2616 }
2617 }
2618
2619 let max_age: Option<Duration> = response
2622 .headers
2623 .typed_get::<AccessControlMaxAge>()
2624 .map(|acma| acma.into());
2625
2626 let max_age = max_age.unwrap_or(Duration::from_secs(5));
2628
2629 for method in &methods {
2640 cache.match_method_and_update(request, method.clone(), max_age);
2641 }
2642
2643 for header_name in &header_names {
2648 cache.match_header_and_update(request, header_name, max_age);
2649 }
2650
2651 return response;
2653 }
2654
2655 Response::network_error(NetworkError::CorsGeneral)
2657}
2658
2659fn cors_check(request: &Request, response: &Response) -> Result<(), ()> {
2661 let Some(origins) =
2663 get_value_from_header_list(ACCESS_CONTROL_ALLOW_ORIGIN.as_str(), &response.headers)
2664 else {
2665 return Err(());
2667 };
2668 let origin = origins.into_iter().map(char::from).collect::<String>();
2669
2670 if request.credentials_mode != CredentialsMode::Include && origin == "*" {
2672 return Ok(());
2673 }
2674
2675 if serialize_request_origin(request).to_string() != origin {
2677 return Err(());
2678 }
2679
2680 if request.credentials_mode != CredentialsMode::Include {
2682 return Ok(());
2683 }
2684
2685 let credentials = response
2687 .headers
2688 .typed_get::<AccessControlAllowCredentials>();
2689
2690 if credentials.is_some() {
2692 return Ok(());
2693 }
2694
2695 Err(())
2697}
2698
2699fn has_credentials(url: &ServoUrl) -> bool {
2700 !url.username().is_empty() || url.password().is_some()
2701}
2702
2703fn is_no_store_cache(headers: &HeaderMap) -> bool {
2704 headers.contains_key(header::IF_MODIFIED_SINCE) |
2705 headers.contains_key(header::IF_NONE_MATCH) |
2706 headers.contains_key(header::IF_UNMODIFIED_SINCE) |
2707 headers.contains_key(header::IF_MATCH) |
2708 headers.contains_key(header::IF_RANGE)
2709}
2710
2711fn is_redirect_status(status: StatusCode) -> bool {
2713 matches!(
2714 status,
2715 StatusCode::MOVED_PERMANENTLY |
2716 StatusCode::FOUND |
2717 StatusCode::SEE_OTHER |
2718 StatusCode::TEMPORARY_REDIRECT |
2719 StatusCode::PERMANENT_REDIRECT
2720 )
2721}
2722
2723fn serialize_request_origin(request: &Request) -> headers::Origin {
2725 let Origin::Origin(origin) = &request.origin else {
2727 panic!("origin cannot be \"client\" at this point in time");
2728 };
2729
2730 if request.redirect_taint_for_request() != RedirectTaint::SameOrigin {
2732 return headers::Origin::NULL;
2733 }
2734
2735 serialize_origin(origin)
2737}
2738
2739pub fn serialize_origin(origin: &ImmutableOrigin) -> headers::Origin {
2741 match origin {
2742 ImmutableOrigin::Opaque(_) => headers::Origin::NULL,
2743 ImmutableOrigin::Tuple(scheme, host, port) => {
2744 let port = match (scheme.as_ref(), port) {
2747 ("http" | "ws", 80) | ("https" | "wss", 443) | ("ftp", 21) => None,
2748 _ => Some(*port),
2749 };
2750
2751 headers::Origin::try_from_parts(scheme, &host.to_string(), port)
2753 .unwrap_or(headers::Origin::NULL)
2754 },
2755 }
2756}
2757
2758#[expect(
2760 clippy::collapsible_match,
2761 reason = "The current way follows the spec more closely"
2762)]
2763fn append_a_request_origin_header(request: &mut Request) {
2764 let Origin::Origin(request_origin) = &request.origin else {
2766 panic!("origin cannot be \"client\" at this point in time");
2767 };
2768
2769 let mut serialized_origin = serialize_request_origin(request);
2771
2772 if request.response_tainting == ResponseTainting::CorsTainting ||
2775 matches!(request.mode, RequestMode::WebSocket { .. })
2776 {
2777 request.headers.typed_insert(serialized_origin);
2778 }
2779 else if !matches!(request.method, Method::GET | Method::HEAD) {
2781 if request.mode != RequestMode::CorsMode {
2783 match request.referrer_policy {
2784 ReferrerPolicy::NoReferrer => {
2785 serialized_origin = headers::Origin::NULL;
2787 },
2788 ReferrerPolicy::NoReferrerWhenDowngrade |
2789 ReferrerPolicy::StrictOrigin |
2790 ReferrerPolicy::StrictOriginWhenCrossOrigin => {
2791 if let ImmutableOrigin::Tuple(scheme, _, _) = &request_origin &&
2794 scheme == "https" &&
2795 request.current_url().scheme() != "https"
2796 {
2797 serialized_origin = headers::Origin::NULL;
2798 }
2799 },
2800 ReferrerPolicy::SameOrigin => {
2801 if *request_origin != request.current_url().origin() {
2804 serialized_origin = headers::Origin::NULL;
2805 }
2806 },
2807 _ => {
2808 },
2810 };
2811 }
2812
2813 request.headers.typed_insert(serialized_origin);
2815 }
2816}
2817
2818fn append_the_fetch_metadata_headers(r: &mut Request) {
2820 if !r.url().is_potentially_trustworthy() {
2822 return;
2823 }
2824
2825 set_the_sec_fetch_dest_header(r);
2827
2828 set_the_sec_fetch_mode_header(r);
2830
2831 set_the_sec_fetch_site_header(r);
2833
2834 set_the_sec_fetch_user_header(r);
2836}
2837
2838fn append_cache_data_to_headers(http_request: &mut Request) {
2840 match http_request.cache_mode {
2841 CacheMode::Default if is_no_store_cache(&http_request.headers) => {
2845 http_request.cache_mode = CacheMode::NoStore;
2846 },
2847
2848 CacheMode::NoCache if !http_request.headers.contains_key(header::CACHE_CONTROL) => {
2858 http_request
2859 .headers
2860 .typed_insert(CacheControl::new().with_max_age(Duration::from_secs(0)));
2861 },
2862
2863 CacheMode::Reload | CacheMode::NoStore => {
2865 if !http_request.headers.contains_key(header::PRAGMA) {
2868 http_request.headers.typed_insert(Pragma::no_cache());
2869 }
2870
2871 if !http_request.headers.contains_key(header::CACHE_CONTROL) {
2874 http_request
2875 .headers
2876 .typed_insert(CacheControl::new().with_no_cache());
2877 }
2878 },
2879
2880 _ => {},
2881 }
2882}
2883
2884fn set_the_sec_fetch_dest_header(r: &mut Request) {
2886 debug_assert!(r.url().is_potentially_trustworthy());
2888
2889 let header = r.destination;
2893
2894 r.headers.typed_insert(SecFetchDest(header));
2896}
2897
2898fn set_the_sec_fetch_mode_header(r: &mut Request) {
2900 debug_assert!(r.url().is_potentially_trustworthy());
2902
2903 let header = &r.mode;
2906
2907 r.headers.typed_insert(SecFetchMode::from(header));
2909}
2910
2911fn set_the_sec_fetch_site_header(r: &mut Request) {
2913 let Origin::Origin(request_origin) = &r.origin else {
2916 panic!("request origin cannot be \"client\" at this point")
2917 };
2918
2919 debug_assert!(r.url().is_potentially_trustworthy());
2921
2922 let mut header = SecFetchSite::SameOrigin;
2925
2926 if header != SecFetchSite::None {
2931 for url in &r.url_list {
2932 if url.origin() == *request_origin {
2934 continue;
2935 }
2936
2937 header = SecFetchSite::CrossSite;
2939
2940 if !is_same_site(request_origin, &url.origin()) {
2942 break;
2943 }
2944
2945 header = SecFetchSite::SameSite;
2947 }
2948 }
2949
2950 r.headers.typed_insert(header);
2952}
2953
2954fn set_the_sec_fetch_user_header(r: &mut Request) {
2956 debug_assert!(r.url().is_potentially_trustworthy());
2958
2959 if !r.is_navigation_request() {
2962 return;
2963 }
2964
2965 let header = SecFetchUser;
2968
2969 r.headers.typed_insert(header);
2971}
2972
2973fn set_requests_referrer_policy_on_redirect(request: &mut Request, response: &Response) {
2975 let referrer_policy: ReferrerPolicy = response
2978 .headers
2979 .typed_get::<headers::ReferrerPolicy>()
2980 .into();
2981
2982 if referrer_policy != ReferrerPolicy::EmptyString {
2984 request.referrer_policy = referrer_policy;
2985 }
2986}