1use std::collections::HashSet;
6use std::iter::FromIterator;
7use std::sync::Arc as StdArc;
8use std::sync::atomic::{AtomicBool, Ordering};
9use std::time::{Duration, SystemTime};
10
11use async_recursion::async_recursion;
12use content_security_policy::percent_encoding::{AsciiSet, CONTROLS, utf8_percent_encode};
13use devtools_traits::ChromeToDevtoolsControlMsg;
14use embedder_traits::{AuthenticationResponse, GenericEmbedderProxy};
15use futures::{TryFutureExt, TryStreamExt, future};
16use headers::authorization::Basic;
17use headers::{
18 AccessControlAllowCredentials, AccessControlAllowHeaders, AccessControlAllowMethods,
19 AccessControlMaxAge, AccessControlRequestMethod, Authorization, CacheControl, ContentLength,
20 HeaderMapExt, IfModifiedSince, LastModified, Pragma, Referer, StrictTransportSecurity,
21 UserAgent,
22};
23use http::header::{
24 self, ACCEPT, ACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_REQUEST_HEADERS, AUTHORIZATION,
25 CONTENT_ENCODING, CONTENT_LANGUAGE, CONTENT_LOCATION, CONTENT_TYPE, HeaderValue, RANGE,
26 WWW_AUTHENTICATE,
27};
28use http::{HeaderMap, Method, Request as HyperRequest, StatusCode};
29use http_body_util::combinators::BoxBody;
30use http_body_util::{BodyExt, Full};
31use hyper::Response as HyperResponse;
32use hyper::body::{Bytes, Frame};
33use hyper::ext::ReasonPhrase;
34use hyper::header::{HeaderName, TRANSFER_ENCODING};
35use ipc_channel::IpcError;
36use ipc_channel::ipc::{self, IpcSender};
37use ipc_channel::router::ROUTER;
38use log::{debug, error, info, log_enabled, warn};
39use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
40use net_traits::blob_url_store::UrlWithBlobClaim;
41use net_traits::fetch::headers::get_value_from_header_list;
42use net_traits::http_status::HttpStatus;
43use net_traits::policy_container::{EmbedderPolicyValue, RequestPolicyContainer};
44use net_traits::pub_domains::{is_same_site, reg_suffix};
45use net_traits::request::{
46 BodyChunkRequest, BodyChunkResponse, CacheMode, CredentialsMode, Destination, Initiator,
47 Origin, RedirectMode, Referrer, Request, RequestBuilder, RequestClient, RequestMode,
48 ResponseTainting, ServiceWorkersMode, TraversableForUserPrompts, get_cors_unsafe_header_names,
49 is_cors_non_wildcard_request_header_name, is_cors_safelisted_method,
50 is_cors_safelisted_request_header,
51};
52use net_traits::response::{CacheState, RedirectTaint, Response, ResponseBody, ResponseType};
53use net_traits::{
54 CookieSource, DOCUMENT_ACCEPT_HEADER_VALUE, DiscardFetch, NetworkError, RedirectEndValue,
55 RedirectStartValue, ReferrerPolicy, ResourceAttribute, ResourceFetchTimingContainer,
56 ResourceTimeValue, ServoCipherSuite, ServoNamedGroup, ServoProtocolVersion, TlsSecurityInfo,
57 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 Destination::Text => HeaderValue::from_static("text/plain,*/*;q=0.5"),
198 _ => HeaderValue::from_static("*/*"),
200 }
201 };
202
203 request.headers.insert(header::ACCEPT, value);
205}
206
207fn set_default_accept_encoding(headers: &mut HeaderMap) {
208 if headers.contains_key(header::ACCEPT_ENCODING) {
209 return;
210 }
211
212 headers.insert(
214 header::ACCEPT_ENCODING,
215 HeaderValue::from_static("gzip, deflate, br, zstd"),
216 );
217}
218
219fn no_referrer_when_downgrade(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
221 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
223 return None;
224 }
225 strip_url_for_use_as_referrer(referrer_url, false)
227}
228
229fn strict_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
231 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
233 return None;
234 }
235 strip_url_for_use_as_referrer(referrer_url, true)
237}
238
239fn strict_origin_when_cross_origin(
241 referrer_url: ServoUrl,
242 current_url: ServoUrl,
243) -> Option<ServoUrl> {
244 if referrer_url.origin() == current_url.origin() {
246 return strip_url_for_use_as_referrer(referrer_url, false);
247 }
248 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
250 return None;
251 }
252 strip_url_for_use_as_referrer(referrer_url, true)
254}
255
256fn is_schemelessy_same_site(site_a: &ImmutableOrigin, site_b: &ImmutableOrigin) -> bool {
258 if !site_a.is_tuple() && !site_b.is_tuple() && site_a == site_b {
260 true
261 } else if site_a.is_tuple() && site_b.is_tuple() {
262 let host_a = site_a.host().map(|h| h.to_string()).unwrap_or_default();
264 let host_b = site_b.host().map(|h| h.to_string()).unwrap_or_default();
265
266 let host_a_reg = reg_suffix(&host_a);
267 let host_b_reg = reg_suffix(&host_b);
268
269 (site_a.host() == site_b.host() && host_a_reg.is_empty()) ||
271 (host_a_reg == host_b_reg && !host_a_reg.is_empty())
272 } else {
273 false
275 }
276}
277
278fn strip_url_for_use_as_referrer(mut url: ServoUrl, origin_only: bool) -> Option<ServoUrl> {
280 const MAX_REFERRER_URL_LENGTH: usize = 4096;
281 if url.is_local_scheme() {
283 return None;
284 }
285 {
287 let url = url.as_mut_url();
288 let _ = url.set_username("");
289 let _ = url.set_password(None);
290 url.set_fragment(None);
291 if origin_only || url.as_str().len() > MAX_REFERRER_URL_LENGTH {
295 url.set_path("");
296 url.set_query(None);
297 }
298 }
299 Some(url)
301}
302
303fn same_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
305 if referrer_url.origin() == current_url.origin() {
307 return strip_url_for_use_as_referrer(referrer_url, false);
308 }
309 None
311}
312
313fn origin_when_cross_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
315 if referrer_url.origin() == current_url.origin() {
317 return strip_url_for_use_as_referrer(referrer_url, false);
318 }
319 strip_url_for_use_as_referrer(referrer_url, true)
321}
322
323pub fn determine_requests_referrer(
325 referrer_policy: ReferrerPolicy,
326 referrer_source: ServoUrl,
327 current_url: ServoUrl,
328) -> Option<ServoUrl> {
329 match referrer_policy {
330 ReferrerPolicy::EmptyString | ReferrerPolicy::NoReferrer => None,
331 ReferrerPolicy::Origin => strip_url_for_use_as_referrer(referrer_source, true),
332 ReferrerPolicy::UnsafeUrl => strip_url_for_use_as_referrer(referrer_source, false),
333 ReferrerPolicy::StrictOrigin => strict_origin(referrer_source, current_url),
334 ReferrerPolicy::StrictOriginWhenCrossOrigin => {
335 strict_origin_when_cross_origin(referrer_source, current_url)
336 },
337 ReferrerPolicy::SameOrigin => same_origin(referrer_source, current_url),
338 ReferrerPolicy::OriginWhenCrossOrigin => {
339 origin_when_cross_origin(referrer_source, current_url)
340 },
341 ReferrerPolicy::NoReferrerWhenDowngrade => {
342 no_referrer_when_downgrade(referrer_source, current_url)
343 },
344 }
345}
346
347fn set_request_cookies(
348 url: &ServoUrl,
349 headers: &mut HeaderMap,
350 cookie_jar: &RwLock<CookieStorage>,
351) {
352 let mut cookie_jar = cookie_jar.write();
353 cookie_jar.remove_expired_cookies_for_url(url);
354 if let Some(cookie_list) = cookie_jar.cookies_for_url(url, CookieSource::HTTP) &&
355 let Ok(cookie_list_header_value) = HeaderValue::from_bytes(cookie_list.as_bytes())
356 {
357 headers.insert(header::COOKIE, cookie_list_header_value);
358 }
359}
360
361fn set_cookie_for_url(cookie_jar: &RwLock<CookieStorage>, request: &ServoUrl, cookie_val: &str) {
362 let mut cookie_jar = cookie_jar.write();
363 let source = CookieSource::HTTP;
364
365 if let Some(cookie) = ServoCookie::from_cookie_string(cookie_val, request, source) {
366 cookie_jar.push(cookie, request, source);
367 }
368}
369
370fn set_cookies_from_headers(
371 url: &ServoUrl,
372 headers: &HeaderMap,
373 cookie_jar: &RwLock<CookieStorage>,
374) {
375 for cookie in headers.get_all(header::SET_COOKIE) {
376 let cookie_bytes = cookie.as_bytes();
377 if !ServoCookie::is_valid_name_or_value(cookie_bytes) {
378 continue;
379 }
380 if let Ok(cookie_str) = std::str::from_utf8(cookie_bytes) {
381 set_cookie_for_url(cookie_jar, url, cookie_str);
382 }
383 }
384}
385
386fn build_tls_security_info(handshake: &TlsHandshakeInfo, hsts_enabled: bool) -> TlsSecurityInfo {
387 let state = if handshake.protocol_version.is_none() || handshake.cipher_suite.is_none() {
395 TlsSecurityState::Insecure
397 } else {
398 TlsSecurityState::Secure
400 };
401
402 TlsSecurityInfo {
403 state,
404 weakness_reasons: Vec::new(), protocol_version: handshake.protocol_version.map(ServoProtocolVersion),
406 cipher_suite: handshake.cipher_suite.map(ServoCipherSuite),
407 kea_group_name: handshake.kea_group_name.map(ServoNamedGroup),
408 signature_scheme_name: handshake.signature_scheme_name.clone(),
409 alpn_protocol: handshake.alpn_protocol.clone(),
410 certificate_chain_der: handshake.certificate_chain_der.clone(),
411 certificate_transparency: None,
412 hsts: hsts_enabled,
413 hpkp: false,
414 used_ech: handshake.used_ech,
415 used_delegated_credentials: false,
416 used_ocsp: false,
417 used_private_dns: false,
418 }
419}
420
421fn auth_from_cache(
422 auth_cache: &RwLock<AuthCache>,
423 origin: &ImmutableOrigin,
424) -> Option<Authorization<Basic>> {
425 if let Some(auth_entry) = auth_cache
426 .read()
427 .entries
428 .get(origin.ascii_serialization().as_ref())
429 {
430 let user_name = &auth_entry.user_name;
431 let password = &auth_entry.password;
432 Some(Authorization::basic(user_name, password))
433 } else {
434 None
435 }
436}
437
438enum BodyChunk {
441 Chunk(GenericSharedMemory),
443 Done,
445}
446
447enum BodyStream {
449 Chunked(TokioReceiver<Result<Frame<Bytes>, hyper::Error>>),
452 Buffered(UnboundedReceiver<BodyChunk>),
455}
456
457enum BodySink {
460 Chunked(TokioSender<Result<Frame<Bytes>, hyper::Error>>),
462 Buffered(UnboundedSender<BodyChunk>),
466}
467
468impl BodySink {
469 fn transmit_bytes(&self, bytes: GenericSharedMemory) {
470 match self {
471 BodySink::Chunked(sender) => {
472 let sender = sender.clone();
473 spawn_task(async move {
474 let _ = sender
475 .send(Ok(Frame::data(Bytes::copy_from_slice(&bytes))))
476 .await;
477 });
478 },
479 BodySink::Buffered(sender) => {
480 let _ = sender.send(BodyChunk::Chunk(bytes));
481 },
482 }
483 }
484
485 fn close(self) {
486 match self {
487 BodySink::Chunked(_) => {},
488 BodySink::Buffered(sender) => {
489 let _ = sender.send(BodyChunk::Done);
490 },
491 }
492 }
493}
494
495fn request_body_stream_closed_error(action: &str) -> NetworkError {
496 NetworkError::Crash(format!(
497 "Request body stream has already been closed while trying to {action}."
498 ))
499}
500
501fn log_request_body_stream_closed(action: &str, error: Option<&IpcError>) {
502 match error {
503 Some(error) => {
504 error!("Request body stream has already been closed while trying to {action}: {error}")
505 },
506 None => error!("Request body stream has already been closed while trying to {action}."),
507 }
508}
509
510fn log_fetch_terminated_send_failure(terminated_with_error: bool, context: &str) {
511 warn!(
512 "Failed to notify request-body stream termination state ({terminated_with_error}) while {context} because the receiver was already dropped."
513 );
514}
515
516const FRAGMENT: &AsciiSet = &CONTROLS.add(b'|').add(b'{').add(b'}');
517
518#[allow(clippy::too_many_arguments)]
519#[servo_tracing::instrument(skip_all, fields(url=url.as_str()))]
520async fn obtain_response(
522 client: &ServoClient,
523 url: &ServoUrl,
524 method: &Method,
525 request_headers: &mut HeaderMap,
526 body_sender: Option<StdArc<Mutex<Option<IpcSender<BodyChunkRequest>>>>>,
527 source_is_null: bool,
528 pipeline_id: &Option<PipelineId>,
529 request_id: Option<&str>,
530 destination: Destination,
531 is_xhr: bool,
532 context: &FetchContext,
533 fetch_terminated: UnboundedSender<bool>,
534 browsing_context_id: Option<BrowsingContextId>,
535) -> Result<(HyperResponse<Decoder>, Option<ChromeToDevtoolsControlMsg>), NetworkError> {
536 let mut headers = request_headers.clone();
537
538 let devtools_bytes = StdArc::new(Mutex::new(vec![]));
539
540 let encoded_url = utf8_percent_encode(url.as_str(), FRAGMENT).to_string();
542
543 let request = if let Some(chunk_requester) = body_sender {
544 let (sink, stream) = if source_is_null {
545 headers.insert(TRANSFER_ENCODING, HeaderValue::from_static("chunked"));
548
549 let (sender, receiver) = channel(1);
550 (BodySink::Chunked(sender), BodyStream::Chunked(receiver))
551 } else {
552 let (sender, receiver) = unbounded_channel();
559 (BodySink::Buffered(sender), BodyStream::Buffered(receiver))
560 };
561
562 obtain_response_setup_router_callback(
563 devtools_bytes.clone(),
564 chunk_requester,
565 sink,
566 fetch_terminated,
567 )?;
568
569 let body = match stream {
570 BodyStream::Chunked(receiver) => {
571 let stream = ReceiverStream::new(receiver);
572 BoxBody::new(http_body_util::StreamBody::new(stream))
573 },
574 BodyStream::Buffered(mut receiver) => {
575 let mut body = vec![];
577 loop {
578 match receiver.recv().await {
579 Some(BodyChunk::Chunk(bytes)) => {
580 body.extend_from_slice(&bytes);
581 },
582 Some(BodyChunk::Done) => break,
583 None => warn!("Failed to read all chunks from request body."),
584 }
585 }
586 Full::new(body.into()).map_err(|_| unreachable!()).boxed()
587 },
588 };
589 HyperRequest::builder()
590 .method(method)
591 .uri(encoded_url)
592 .body(body)
593 } else {
594 HyperRequest::builder()
595 .method(method)
596 .uri(encoded_url)
597 .body(
598 http_body_util::Empty::new()
599 .map_err(|_| unreachable!())
600 .boxed(),
601 )
602 };
603
604 let connect_start = CrossProcessInstant::now();
607 context.timing.set_attributes(&[
608 ResourceAttribute::DomainLookupStart,
609 ResourceAttribute::ConnectStart(connect_start),
610 ]);
611
612 if url.scheme() == "https" {
616 context
617 .timing
618 .set_attribute(ResourceAttribute::SecureConnectionStart);
619 }
620
621 let mut request = match request {
622 Ok(request) => request,
623 Err(error) => return Err(NetworkError::HttpError(error.to_string())),
624 };
625 *request.headers_mut() = headers.clone();
626
627 let connect_end = CrossProcessInstant::now();
628 context
629 .timing
630 .set_attribute(ResourceAttribute::ConnectEnd(connect_end));
631
632 let request_id = request_id.map(|v| v.to_owned());
633 let pipeline_id = *pipeline_id;
634 let closure_url = url.clone();
635 let method = method.clone();
636 let send_start = CrossProcessInstant::now();
637
638 let host = request.uri().host().unwrap_or("").to_owned();
639 let override_manager = context.state.override_manager.clone();
640 let headers = headers.clone();
641 let is_secure_scheme = url.is_secure_scheme();
642
643 context
647 .timing
648 .set_attribute(ResourceAttribute::RequestStart);
649
650 let client_future = client
651 .request(request)
652 .and_then(move |res| {
653 let send_end = CrossProcessInstant::now();
654
655 let msg = if let Some(request_id) = request_id {
658 if let Some(pipeline_id) = pipeline_id {
659 if let Some(browsing_context_id) = browsing_context_id {
660 Some(prepare_devtools_request(
661 request_id,
662 closure_url,
663 method.clone(),
664 headers,
665 Some(devtools_bytes.lock().clone()),
666 pipeline_id,
667 (connect_end - connect_start).unsigned_abs(),
668 (send_end - send_start).unsigned_abs(),
669 destination,
670 is_xhr,
671 browsing_context_id,
672 ))
673 } else {
674 debug!("Not notifying devtools (no browsing_context_id)");
675 None
676 }
677 } else {
682 debug!("Not notifying devtools (no pipeline_id)");
683 None
684 }
685 } else {
686 debug!("Not notifying devtools (no request_id)");
687 None
688 };
689
690 future::ready(Ok((
691 Decoder::detect(res.map(|r| r.boxed()), is_secure_scheme),
692 msg,
693 )))
694 })
695 .map_err(move |error| {
696 warn!("network error: {error:?}");
697 NetworkError::from_hyper_error(
698 &error,
699 override_manager.remove_certificate_failing_verification(host.as_str()),
700 )
701 });
702
703 #[cfg(feature = "tracing")]
704 {
705 client_future.instrument(trace_span!("HyperRequest")).await
706 }
707
708 #[cfg(not(feature = "tracing"))]
709 {
710 client_future.await
711 }
712}
713
714fn obtain_response_setup_router_callback(
716 devtools_bytes: StdArc<Mutex<Vec<u8>>>,
717 chunk_requester: StdArc<Mutex<Option<IpcSender<BodyChunkRequest>>>>,
718 sink: BodySink,
719 fetch_terminated: UnboundedSender<bool>,
720) -> Result<(), NetworkError> {
721 let (body_chan, body_port) = ipc::channel().unwrap();
722
723 {
724 let mut lock = chunk_requester.lock();
725 if let Some(chunk_requester) = lock.as_mut() {
726 if let Err(error) = chunk_requester.send(BodyChunkRequest::Connect(body_chan)) {
727 log_request_body_stream_closed("connect to the request body stream", Some(&error));
728 return Err(request_body_stream_closed_error(
729 "connect to the request body stream",
730 ));
731 }
732
733 if let Err(error) = chunk_requester.send(BodyChunkRequest::Chunk) {
736 log_request_body_stream_closed(
737 "request the first request body chunk",
738 Some(&error),
739 );
740 return Err(request_body_stream_closed_error(
741 "request the first request body chunk",
742 ));
743 }
744 } else {
745 log_request_body_stream_closed("connect to the request body stream", None);
746 return Err(request_body_stream_closed_error(
747 "connect to the request body stream",
748 ));
749 }
750 }
751
752 let mut sink = Some(sink);
753
754 ROUTER.add_typed_route(
755 body_port,
756 Box::new(move |message| {
757 info!("Received message");
758 let bytes = match message.unwrap() {
759 BodyChunkResponse::Chunk(bytes) => bytes,
760 BodyChunkResponse::Done => {
761 if fetch_terminated.send(false).is_err() {
764 log_fetch_terminated_send_failure(
765 false,
766 "handling request body completion",
767 );
768 }
769 if let Some(sink) = sink.take() {
770 sink.close();
771 }
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 if let Some(sink) = sink.take() {
786 sink.close();
787 }
788
789 return;
790 },
791 };
792
793 devtools_bytes.lock().extend_from_slice(&bytes);
794
795 {
798 let Some(sink) = sink.as_ref() else {
799 return;
800 };
801 sink.transmit_bytes(bytes);
802 }
803
804 let mut chunk_requester = chunk_requester.lock();
807 if let Some(chunk_requester) = chunk_requester.as_mut() {
808 if let Err(error) = chunk_requester.send(BodyChunkRequest::Chunk) {
809 log_request_body_stream_closed(
810 "request the next request body chunk",
811 Some(&error),
812 );
813 if fetch_terminated.send(true).is_err() {
814 log_fetch_terminated_send_failure(
815 true,
816 "handling failure to request the next request body chunk",
817 );
818 }
819 if let Some(sink) = sink.take() {
820 sink.close();
821 }
822 }
823 } else {
824 log_request_body_stream_closed("request the next request body chunk", None);
825 if fetch_terminated.send(true).is_err() {
826 log_fetch_terminated_send_failure(
827 true,
828 "handling a closed request body stream while requesting the next chunk",
829 );
830 }
831 if let Some(sink) = sink.take() {
832 sink.close();
833 }
834 }
835 }),
836 );
837
838 Ok(())
839}
840
841#[async_recursion]
843#[allow(clippy::too_many_arguments)]
844pub(crate) async fn http_fetch(
845 fetch_params: &mut FetchParams,
846 cache: &mut CorsCache,
847 cors_flag: bool,
848 cors_preflight_flag: bool,
849 authentication_fetch_flag: bool,
850 target: Target<'async_recursion>,
851 done_chan: &mut DoneChannel,
852 context: &FetchContext,
853) -> Response {
854 *done_chan = None;
856 let request = &mut fetch_params.request;
858
859 let mut response: Option<Response> = None;
861
862 if request.service_workers_mode == ServiceWorkersMode::All {
864 if let Some(ref res) = response {
869 if (res.response_type == ResponseType::Opaque && request.mode != RequestMode::NoCors) ||
877 (res.response_type == ResponseType::OpaqueRedirect &&
878 request.redirect_mode != RedirectMode::Manual) ||
879 (res.url_list.len() > 1 && request.redirect_mode != RedirectMode::Follow) ||
880 res.is_network_error()
881 {
882 return Response::network_error(NetworkError::ConnectionFailure);
883 }
884
885 }
888 }
889
890 if response.is_none() {
892 if cors_preflight_flag {
894 let method_cache_match = cache.match_method(request, request.method.clone());
895
896 let method_mismatch = !method_cache_match &&
900 (!is_cors_safelisted_method(&request.method) || request.use_cors_preflight);
901
902 let header_mismatch = request.headers.iter().any(|(name, value)| {
905 !cache.match_header(request, name) &&
906 !is_cors_safelisted_request_header(&name, &value)
907 });
908
909 if method_mismatch || header_mismatch {
911 let preflight_response = cors_preflight_fetch(request, cache, context).await;
914 if let Some(error) = preflight_response.get_network_error() {
916 return Response::network_error(error.clone());
917 }
918 }
919 }
920
921 if request.redirect_mode == RedirectMode::Follow {
924 request.service_workers_mode = ServiceWorkersMode::None;
925 }
926
927 let mut fetch_result = http_network_or_cache_fetch(
930 fetch_params,
931 authentication_fetch_flag,
932 cors_flag,
933 done_chan,
934 context,
935 )
936 .await;
937
938 if cors_flag && cors_check(&fetch_params.request, &fetch_result).is_err() {
941 return Response::network_error(NetworkError::CorsGeneral);
942 }
943
944 if let Err(()) = tao_check(&fetch_params.request, &fetch_result) {
947 context.timing.inner().mark_timing_check_failed();
948 }
949 fetch_result.return_internal = false;
950 response = Some(fetch_result);
951 }
952
953 let request = &mut fetch_params.request;
954
955 let mut response = response.unwrap();
957
958 if (request.response_tainting == ResponseTainting::Opaque ||
962 response.response_type == ResponseType::Opaque) &&
963 request.client.as_ref().is_some_and(|client| {
964 cross_origin_resource_policy_check(
965 &request.origin,
966 client,
967 &response,
968 ForNavigation::No,
969 ) == CrossOriginResourcePolicy::Blocked
970 })
971 {
972 return Response::network_error(NetworkError::CrossOriginResponse);
973 }
974
975 if response
977 .actual_response()
978 .status
979 .try_code()
980 .is_some_and(is_redirect_status)
981 {
982 if response.actual_response().status != StatusCode::SEE_OTHER {
989 }
991
992 response = match request.redirect_mode {
994 RedirectMode::Error => Response::network_error(NetworkError::RedirectError),
996 RedirectMode::Manual => {
997 if request.mode == RequestMode::Navigate {
1000 let location_url =
1004 location_url_for_response(&response, request.current_url().fragment());
1005 response.actual_response_mut().location_url = location_url;
1006 response
1007 } else {
1008 response.to_filtered(ResponseType::OpaqueRedirect)
1011 }
1012 },
1013 RedirectMode::Follow => {
1014 response.return_internal = true;
1018
1019 http_redirect_fetch(
1022 fetch_params,
1023 cache,
1024 response,
1025 cors_flag,
1026 target,
1027 done_chan,
1028 context,
1029 )
1030 .await
1031 },
1032 };
1033 }
1034
1035 response.return_internal = true;
1037 context
1038 .timing
1039 .set_attribute(ResourceAttribute::RedirectCount(
1040 fetch_params.request.redirect_count as u16,
1041 ));
1042
1043 response.resource_timing = context.timing.clone();
1044
1045 response
1047}
1048
1049fn tao_check(request: &Request, response: &Response) -> Result<(), ()> {
1051 let Origin::Origin(ref request_origin) = request.origin else {
1053 unreachable!("origin cannot be \"client\" at this point");
1054 };
1055
1056 let values: Vec<&str> = response
1061 .headers
1062 .get_all("Timing-Allow-Origin")
1063 .iter()
1064 .map(|header_value| header_value.to_str().unwrap_or(""))
1065 .collect();
1066
1067 if values.contains(&"*") {
1069 return Ok(());
1070 }
1071
1072 if values
1075 .iter()
1076 .any(|header_str| *header_str == request_origin.ascii_serialization().as_ref())
1077 {
1078 return Ok(());
1079 }
1080
1081 if request.mode == RequestMode::Navigate && request.current_url().origin() != *request_origin {
1084 return Err(());
1085 }
1086
1087 if request.response_tainting == ResponseTainting::Basic {
1089 return Ok(());
1090 }
1091
1092 Err(())
1094}
1095
1096struct RedirectEndTimer(Option<ResourceFetchTimingContainer>);
1098
1099impl RedirectEndTimer {
1100 fn neuter(&mut self) {
1101 self.0 = None;
1102 }
1103}
1104
1105impl Drop for RedirectEndTimer {
1106 fn drop(&mut self) {
1107 let RedirectEndTimer(resource_fetch_timing_opt) = self;
1108
1109 resource_fetch_timing_opt.as_ref().map_or((), |t| {
1110 t.set_attribute(ResourceAttribute::RedirectEnd(RedirectEndValue::Zero));
1111 })
1112 }
1113}
1114
1115static REQUEST_BODY_HEADER_NAMES: &[HeaderName] = &[
1117 CONTENT_ENCODING,
1118 CONTENT_LANGUAGE,
1119 CONTENT_LOCATION,
1120 CONTENT_TYPE,
1121];
1122
1123fn location_url_for_response(
1125 response: &Response,
1126 request_fragment: Option<&str>,
1127) -> Option<Result<ServoUrl, String>> {
1128 assert!(
1130 response
1131 .actual_response()
1132 .status
1133 .try_code()
1134 .is_some_and(is_redirect_status)
1135 );
1136 let mut location = response
1138 .actual_response()
1139 .headers
1140 .get(header::LOCATION)
1141 .and_then(|header_value| {
1142 HeaderValue::to_str(header_value)
1143 .map(|location_string| {
1144 ServoUrl::parse_with_base(response.actual_response().url(), location_string)
1146 .map_err(|error| error.to_string())
1147 })
1148 .ok()
1149 });
1150
1151 if let Some(Ok(ref mut location)) = location &&
1153 location.fragment().is_none()
1154 {
1155 location.set_fragment(request_fragment);
1156 }
1157 location
1159}
1160
1161#[async_recursion]
1163pub async fn http_redirect_fetch(
1164 fetch_params: &mut FetchParams,
1165 cache: &mut CorsCache,
1166 mut response: Response,
1167 cors_flag: bool,
1168 target: Target<'async_recursion>,
1169 done_chan: &mut DoneChannel,
1170 context: &FetchContext,
1171) -> Response {
1172 let mut redirect_end_timer = RedirectEndTimer(Some(context.timing.clone()));
1173
1174 let request = &mut fetch_params.request;
1176
1177 assert!(response.return_internal);
1179
1180 let location_url = location_url_for_response(&response, request.current_url().fragment());
1182 response.actual_response_mut().location_url = location_url.clone();
1183
1184 let location_url = match location_url {
1185 None => return response,
1187 Some(Err(err)) => {
1189 return Response::network_error(NetworkError::ResourceLoadError(
1190 "Location URL parse failure: ".to_owned() + &err,
1191 ));
1192 },
1193 Some(Ok(url)) if !matches!(url.scheme(), "http" | "https") => {
1195 return Response::network_error(NetworkError::UnsupportedScheme);
1196 },
1197 Some(Ok(url)) => url,
1198 };
1199
1200 context.timing.set_attributes(&[
1205 ResourceAttribute::RedirectStart(RedirectStartValue::FetchStart),
1206 ResourceAttribute::FetchStart,
1207 ResourceAttribute::StartTime(ResourceTimeValue::FetchStart),
1208 ResourceAttribute::StartTime(ResourceTimeValue::RedirectStart),
1209 ]);
1210
1211 if request.redirect_count >= 20 {
1213 return Response::network_error(NetworkError::TooManyRedirects);
1214 }
1215
1216 request.redirect_count += 1;
1218
1219 let same_origin = match request.origin {
1222 Origin::Origin(ref origin) => *origin == location_url.origin(),
1223 Origin::Client => panic!(
1224 "Request origin should not be client for {}",
1225 request.current_url()
1226 ),
1227 };
1228
1229 let has_credentials = has_credentials(&location_url);
1230
1231 if request.mode == RequestMode::CorsMode && !same_origin && has_credentials {
1232 return Response::network_error(NetworkError::CorsCredentials);
1233 }
1234
1235 if cors_flag && location_url.origin() != request.current_url().origin() {
1236 request.origin = Origin::Origin(ImmutableOrigin::new_opaque());
1237 }
1238
1239 if cors_flag && has_credentials {
1241 return Response::network_error(NetworkError::CorsCredentials);
1242 }
1243
1244 if response.actual_response().status != StatusCode::SEE_OTHER &&
1247 request.body.as_ref().is_some_and(|b| b.source_is_null())
1248 {
1249 return Response::network_error(NetworkError::ConnectionFailure);
1250 }
1251
1252 if response
1254 .actual_response()
1255 .status
1256 .try_code()
1257 .is_some_and(|code| {
1258 ((code == StatusCode::MOVED_PERMANENTLY || code == StatusCode::FOUND) &&
1260 request.method == Method::POST) ||
1261 (code == StatusCode::SEE_OTHER &&
1263 request.method != Method::HEAD &&
1264 request.method != Method::GET)
1265 })
1266 {
1267 request.method = Method::GET;
1269 request.body = None;
1270 for name in REQUEST_BODY_HEADER_NAMES {
1272 request.headers.remove(name);
1273 }
1274 }
1275
1276 if location_url.origin() != request.current_url().origin() {
1280 request.headers.remove(AUTHORIZATION);
1283 }
1284
1285 if let Some(body) = request.body.as_mut() {
1288 body.extract_source();
1289 }
1290
1291 request
1295 .url_list
1296 .push(UrlWithBlobClaim::from_url_without_having_claimed_blob(
1297 location_url,
1298 ));
1299
1300 set_requests_referrer_policy_on_redirect(request, response.actual_response());
1302
1303 let recursive_flag = request.redirect_mode != RedirectMode::Manual;
1306
1307 let fetch_response = main_fetch(
1309 fetch_params,
1310 cache,
1311 recursive_flag,
1312 target,
1313 done_chan,
1314 context,
1315 )
1316 .await;
1317
1318 context.timing.set_attribute(ResourceAttribute::RedirectEnd(
1320 RedirectEndValue::ResponseEnd,
1321 ));
1322 redirect_end_timer.neuter();
1323
1324 fetch_response
1325}
1326
1327#[async_recursion]
1329#[servo_tracing::instrument(skip_all,fields(url=fetch_params.request.url().as_str()))]
1330async fn http_network_or_cache_fetch(
1331 fetch_params: &mut FetchParams,
1332 authentication_fetch_flag: bool,
1333 cors_flag: bool,
1334 done_chan: &mut DoneChannel,
1335 context: &FetchContext,
1336) -> Response {
1337 let http_fetch_params: &mut FetchParams;
1339 let mut fetch_params_copy: FetchParams;
1340
1341 let mut response: Option<Response> = None;
1345
1346 let mut revalidating_flag = false;
1348
1349 let http_request = if fetch_params.request.traversable_for_user_prompts ==
1353 TraversableForUserPrompts::NoTraversable &&
1354 fetch_params.request.redirect_mode == RedirectMode::Error
1355 {
1356 http_fetch_params = fetch_params;
1357 &mut http_fetch_params.request
1358 }
1359 else {
1361 fetch_params_copy =
1364 std::mem::replace(fetch_params, FetchParams::new(fetch_params.request.clone()));
1365 http_fetch_params = &mut fetch_params_copy;
1366
1367 &mut http_fetch_params.request
1368 };
1369
1370 let include_credentials = match http_request.credentials_mode {
1372 CredentialsMode::Include => true,
1374 CredentialsMode::CredentialsSameOrigin
1376 if http_request.response_tainting == ResponseTainting::Basic =>
1377 {
1378 true
1379 },
1380 _ => false,
1381 };
1382
1383 let content_length = http_request
1390 .body
1391 .as_ref()
1392 .and_then(|body| body.len().map(|size| size as u64));
1393
1394 let mut content_length_header_value = None;
1396
1397 if http_request.body.is_none() && matches!(http_request.method, Method::POST | Method::PUT) {
1400 content_length_header_value = Some(0);
1401 }
1402
1403 if let Some(content_length) = content_length {
1407 content_length_header_value = Some(content_length);
1408 };
1409
1410 if let Some(content_length_header_value) = content_length_header_value {
1413 http_request
1414 .headers
1415 .typed_insert(ContentLength(content_length_header_value));
1416 }
1417
1418 if http_request.keep_alive &&
1420 let Some(content_length) = content_length
1421 {
1422 let in_flight_keep_alive_bytes: u64 = context
1427 .in_flight_keep_alive_records
1428 .lock()
1429 .get(
1430 &http_request
1431 .pipeline_id
1432 .expect("Must always set a pipeline ID for keep-alive requests"),
1433 )
1434 .map(|records| {
1435 records
1439 .iter()
1440 .map(|record| {
1441 if record.request_id == http_request.id {
1442 0
1445 } else {
1446 record.keep_alive_body_length
1447 }
1448 })
1449 .sum()
1450 })
1451 .unwrap_or_default();
1452 if content_length + in_flight_keep_alive_bytes > 64 * 1024 {
1454 return Response::network_error(NetworkError::TooManyInFlightKeepAliveRequests);
1455 }
1456 }
1457
1458 match http_request.referrer {
1460 Referrer::ReferrerUrl(ref http_request_referrer) |
1461 Referrer::Client(ref http_request_referrer) => {
1462 if let Ok(referer) = http_request_referrer.as_str().parse::<Referer>() {
1465 http_request.headers.typed_insert(referer);
1467 } else {
1468 error!("Failed to parse {} as referrer", http_request_referrer);
1472 }
1473 },
1474 _ => {},
1475 };
1476
1477 append_a_request_origin_header(http_request);
1479
1480 append_the_fetch_metadata_headers(http_request);
1482
1483 if http_request.initiator == Initiator::Prefetch &&
1486 let Ok(value) = HeaderValue::from_str("prefetch")
1487 {
1488 http_request.headers.insert("Sec-Purpose", value);
1489 }
1490
1491 if !http_request.headers.contains_key(header::USER_AGENT) {
1494 http_request
1495 .headers
1496 .typed_insert::<UserAgent>(context.user_agent.parse().unwrap());
1497 }
1498
1499 append_cache_data_to_headers(http_request);
1501
1502 if http_request.headers.contains_key(header::RANGE) &&
1505 let Ok(value) = HeaderValue::from_str("identity")
1506 {
1507 http_request.headers.insert("Accept-Encoding", value);
1508 }
1509
1510 http_request.headers.remove(header::HOST);
1514 set_default_accept_encoding(&mut http_request.headers);
1516
1517 let current_url = http_request.current_url();
1518
1519 if include_credentials {
1522 set_request_cookies(
1526 ¤t_url,
1527 &mut http_request.headers,
1528 &context.state.cookie_jar,
1529 );
1530 if !http_request.headers.contains_key(header::AUTHORIZATION) {
1532 let mut authorization_value = None;
1534
1535 if let Some(basic) = auth_from_cache(&context.state.auth_cache, ¤t_url.origin()) &&
1537 (!http_request.use_url_credentials || !has_credentials(¤t_url))
1538 {
1539 authorization_value = Some(basic);
1540 }
1541
1542 if authentication_fetch_flag &&
1544 authorization_value.is_none() &&
1545 has_credentials(¤t_url)
1546 {
1547 authorization_value = Some(Authorization::basic(
1548 current_url.username(),
1549 current_url.password().unwrap_or(""),
1550 ));
1551 }
1552
1553 if let Some(basic) = authorization_value {
1555 http_request.headers.typed_insert(basic);
1556 }
1557 }
1558 }
1559
1560 let should_wait = {
1562 let mut cache_guard = block_for_cache_ready(
1564 context,
1565 http_request,
1566 done_chan,
1567 &mut revalidating_flag,
1568 &mut response,
1569 )
1570 .await;
1571
1572 if response.is_none() {
1576 if http_request.cache_mode == CacheMode::OnlyIfCached {
1578 return Response::network_error(NetworkError::CacheError);
1580 }
1581
1582 drop(cache_guard);
1585 let forward_response =
1586 http_network_fetch(http_fetch_params, include_credentials, done_chan, context)
1587 .await;
1588
1589 let http_request = &mut http_fetch_params.request;
1590 let request_key = CacheKey::new(http_request);
1591 cache_guard = context
1592 .state
1593 .http_cache
1594 .get_or_guard(request_key.clone())
1595 .await;
1596 if forward_response.status.in_range(200..=399) && !http_request.method.is_safe() {
1600 if let Some(guard) = cache_guard.try_as_mut() {
1601 invalidate_cached_resources(guard);
1602 }
1603 context
1604 .state
1605 .http_cache
1606 .invalidate_related_urls(http_request, &forward_response, &request_key)
1607 .await;
1608 }
1609
1610 if revalidating_flag && forward_response.status == StatusCode::NOT_MODIFIED {
1612 *done_chan = None;
1615 if let Some(guard) = cache_guard.try_as_mut() {
1616 response = refresh(http_request, forward_response.clone(), done_chan, guard);
1617 }
1618
1619 if let Some(response) = &mut response {
1620 response.cache_state = CacheState::Validated;
1621 }
1622 }
1623
1624 if response.is_none() {
1626 let forward_response = response.insert(forward_response);
1628
1629 if http_request.cache_mode != CacheMode::NoStore {
1632 cache_guard.insert(http_request, forward_response);
1635 }
1636 }
1637 false
1638 } else {
1639 true
1640 }
1641 }; if should_wait {
1644 wait_for_inflight_requests(done_chan, &mut response).await;
1649 }
1650
1651 let http_request = &mut http_fetch_params.request;
1652 let mut response = response.unwrap();
1653
1654 response.url_list = http_request
1656 .url_list
1657 .iter()
1658 .map(|claimed_url| claimed_url.url())
1659 .collect();
1660
1661 if http_request.headers.contains_key(RANGE) {
1663 response.range_requested = true;
1664 }
1665
1666 response.request_includes_credentials = include_credentials;
1668
1669 if response.status.try_code() == Some(StatusCode::UNAUTHORIZED) &&
1675 !cors_flag &&
1676 include_credentials &&
1677 response.headers.contains_key(WWW_AUTHENTICATE)
1678 {
1679 let request = &mut fetch_params.request;
1682
1683 if request
1686 .body
1687 .as_ref()
1688 .is_some_and(|body| body.source_is_null())
1689 {
1690 return Response::network_error(NetworkError::ConnectionFailure);
1691 }
1692
1693 if !request.use_url_credentials || authentication_fetch_flag {
1695 let Some(credentials) = context
1696 .state
1697 .request_authentication(request, &response)
1698 .await
1699 else {
1700 return response;
1701 };
1702
1703 if let Err(err) = request
1704 .current_url_mut()
1705 .set_username(&credentials.username)
1706 {
1707 error!("error setting username for url: {:?}", err);
1708 return response;
1709 };
1710
1711 if let Err(err) = request
1712 .current_url_mut()
1713 .set_password(Some(&credentials.password))
1714 {
1715 error!("error setting password for url: {:?}", err);
1716 return response;
1717 };
1718 }
1719
1720 *done_chan = None;
1723
1724 response = http_network_or_cache_fetch(
1726 fetch_params,
1727 true, cors_flag,
1729 done_chan,
1730 context,
1731 )
1732 .await;
1733 }
1734
1735 if response.status == StatusCode::PROXY_AUTHENTICATION_REQUIRED {
1737 let request = &mut fetch_params.request;
1738 if request.traversable_for_user_prompts == TraversableForUserPrompts::NoTraversable {
1741 return Response::network_error(NetworkError::ResourceLoadError(
1742 "Can't find Window object".into(),
1743 ));
1744 }
1745
1746 let Some(credentials) = context
1754 .state
1755 .request_authentication(request, &response)
1756 .await
1757 else {
1758 return response;
1759 };
1760
1761 let entry = AuthCacheEntry {
1763 user_name: credentials.username,
1764 password: credentials.password,
1765 };
1766 {
1767 let mut auth_cache = context.state.auth_cache.write();
1768 let key = request
1769 .current_url()
1770 .origin()
1771 .ascii_serialization()
1772 .into_owned();
1773 auth_cache.entries.insert(key, entry);
1774 }
1775
1776 *done_chan = None;
1779
1780 response = http_network_or_cache_fetch(
1782 fetch_params,
1783 false, cors_flag,
1785 done_chan,
1786 context,
1787 )
1788 .await;
1789 }
1790
1791 if authentication_fetch_flag {
1799 }
1801
1802 response
1804}
1805
1806#[servo_tracing::instrument(skip_all)]
1814async fn block_for_cache_ready<'a>(
1815 context: &'a FetchContext,
1816 http_request: &mut Request,
1817 done_chan: &mut DoneChannel,
1818 revalidating_flag: &mut bool,
1819 response: &mut Option<Response>,
1820) -> CachedResourcesOrGuard<'a> {
1821 let entry_key = CacheKey::new(http_request);
1822 let guard_result = context.state.http_cache.get_or_guard(entry_key).await;
1823
1824 match guard_result {
1825 CachedResourcesOrGuard::Guard(_) => {
1826 *done_chan = None;
1827 },
1828 CachedResourcesOrGuard::Value(ref cached_resources) => {
1829 let stored_response = construct_response(http_request, done_chan, cached_resources);
1835 if let Some(response_from_cache) = stored_response {
1837 let response_headers = response_from_cache.response.headers.clone();
1838 let validation_status = response_from_cache.validation_status;
1839 let revalidation_guard = response_from_cache.revalidation_guard.clone();
1840
1841 let (cached_response, needs_synchronous_revalidation) =
1843 match (http_request.cache_mode, &http_request.mode) {
1844 (CacheMode::ForceCache, _) => (Some(response_from_cache.response), false),
1845 (CacheMode::OnlyIfCached, &RequestMode::SameOrigin) => {
1846 (Some(response_from_cache.response), false)
1847 },
1848 (CacheMode::OnlyIfCached, _) |
1849 (CacheMode::NoStore, _) |
1850 (CacheMode::Reload, _) => (None, false),
1851 (_, _) => (
1852 Some(response_from_cache.response),
1853 validation_status ==
1854 (ValidationStatus::Stale {
1855 revalidate_in_background: false,
1856 }),
1857 ),
1858 };
1859
1860 if needs_synchronous_revalidation {
1861 *revalidating_flag = true;
1862 if let Some(http_date) = response_headers.typed_get::<LastModified>() {
1864 let http_date: SystemTime = http_date.into();
1865 http_request
1866 .headers
1867 .typed_insert(IfModifiedSince::from(http_date));
1868 }
1869 if let Some(entity_tag) = response_headers.get(header::ETAG) {
1870 http_request
1871 .headers
1872 .insert(header::IF_NONE_MATCH, entity_tag.clone());
1873 }
1874 } else {
1875 let revalidate_in_background = validation_status ==
1878 (ValidationStatus::Stale {
1879 revalidate_in_background: true,
1880 });
1881 if revalidate_in_background && cached_response.is_some() {
1882 spawn_stale_while_revalidate(context, http_request, revalidation_guard);
1883 }
1884 *response = cached_response;
1885 if let Some(response) = response {
1886 response.cache_state = CacheState::Local;
1887 }
1888 }
1889 if response.is_none() {
1890 *done_chan = None;
1893 }
1894 }
1895 },
1896 }
1897 guard_result
1898}
1899
1900fn spawn_stale_while_revalidate(
1903 context: &FetchContext,
1904 http_request: &Request,
1905 revalidation_guard: StdArc<AtomicBool>,
1906) {
1907 if revalidation_guard
1909 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
1910 .is_err()
1911 {
1912 return;
1913 }
1914
1915 let mut revalidation_request = http_request.clone();
1918 revalidation_request.cache_mode = CacheMode::NoCache;
1919
1920 revalidation_request.service_workers_mode = ServiceWorkersMode::None;
1922
1923 let context = context.clone();
1924 debug!(
1925 "spawning stale-while-revalidate background revalidation for {:?}",
1926 revalidation_request.current_url()
1927 );
1928 spawn_task(async move {
1929 let mut target = DiscardFetch;
1930
1931 let _ = fetch(revalidation_request, &mut target, &context).await;
1932 revalidation_guard.store(false, Ordering::Release);
1933 });
1934}
1935
1936async fn wait_for_inflight_requests(done_chan: &mut DoneChannel, response: &mut Option<Response>) {
1939 if let Some(ref mut ch) = *done_chan {
1940 assert!(response.is_some());
1944
1945 loop {
1946 match ch.1.recv().await {
1947 Some(Data::ContentLength(_)) | Some(Data::Payload(_)) | Some(Data::Error(_)) => {},
1948 Some(Data::Done) => break, Some(Data::Cancelled) => {
1950 break;
1952 },
1953 None => panic!("HTTP cache should always send Done or Cancelled"),
1954 }
1955 }
1956 }
1957 *done_chan = None;
1959}
1960
1961#[derive(PartialEq)]
1965enum CrossOriginResourcePolicy {
1966 Allowed,
1967 Blocked,
1968}
1969
1970enum ForNavigation {
1971 #[expect(dead_code)]
1972 Yes,
1973 No,
1974}
1975
1976fn cross_origin_resource_policy_check(
1978 origin: &Origin,
1979 request_client: &RequestClient,
1980 response: &Response,
1981 for_navigation: ForNavigation,
1982) -> CrossOriginResourcePolicy {
1983 let embedder_policy = &request_client.policy_container.embedder_policy;
1989
1990 if cross_origin_resource_policy_internal_check(
1993 origin,
1994 EmbedderPolicyValue::UnsafeNone,
1995 response,
1996 &for_navigation,
1997 ) == CrossOriginResourcePolicy::Blocked
1998 {
1999 return CrossOriginResourcePolicy::Blocked;
2000 }
2001
2002 if cross_origin_resource_policy_internal_check(
2010 origin,
2011 embedder_policy.value,
2012 response,
2013 &for_navigation,
2014 ) == CrossOriginResourcePolicy::Allowed
2015 {
2016 return CrossOriginResourcePolicy::Allowed;
2017 }
2018
2019 CrossOriginResourcePolicy::Blocked
2024}
2025
2026fn cross_origin_resource_policy_internal_check(
2028 origin: &Origin,
2029 embedder_policy_value: EmbedderPolicyValue,
2030 response: &Response,
2031 for_navigation: &ForNavigation,
2032) -> CrossOriginResourcePolicy {
2033 if let ForNavigation::Yes = for_navigation &&
2035 let EmbedderPolicyValue::UnsafeNone = embedder_policy_value
2036 {
2037 return CrossOriginResourcePolicy::Allowed;
2038 }
2039
2040 let policy = response
2042 .headers
2043 .get(HeaderName::from_static("cross-origin-resource-policy"))
2044 .and_then(|h| h.to_str().ok());
2045
2046 let policy = policy
2048 .filter(|&s| s == "same-origin" || s == "same-site" || s == "cross-origin")
2049 .or(match embedder_policy_value {
2051 EmbedderPolicyValue::UnsafeNone => None,
2053 EmbedderPolicyValue::RequireCorp => Some("same-origin"),
2055 });
2056
2057 match policy {
2059 Some("same-origin") => {
2060 if let Origin::Origin(request_origin) = origin &&
2062 response
2063 .url()
2064 .is_some_and(|url| request_origin == &url.origin())
2065 {
2066 return CrossOriginResourcePolicy::Allowed;
2067 }
2068
2069 CrossOriginResourcePolicy::Blocked
2071 },
2072 Some("same-site") => {
2073 if let Some(response_url) = response.url() {
2074 if let Origin::Origin(request_origin) = origin &&
2078 is_schemelessy_same_site(request_origin, &response_url.origin()) &&
2079 (request_origin.scheme() == Some("https") ||
2080 response_url.scheme() != "https")
2081 {
2082 return CrossOriginResourcePolicy::Allowed;
2083 }
2084 }
2085 CrossOriginResourcePolicy::Blocked
2087 },
2088 _ => CrossOriginResourcePolicy::Allowed,
2091 }
2092}
2093
2094struct ResponseEndTimer(Option<ResourceFetchTimingContainer>);
2096
2097impl ResponseEndTimer {
2098 fn neuter(&mut self) {
2099 self.0 = None;
2100 }
2101}
2102
2103impl Drop for ResponseEndTimer {
2104 fn drop(&mut self) {
2105 let ResponseEndTimer(resource_fetch_timing_opt) = self;
2106
2107 resource_fetch_timing_opt.as_ref().map_or((), |t| {
2108 t.set_attribute(ResourceAttribute::ResponseEnd);
2109 })
2110 }
2111}
2112
2113#[servo_tracing::instrument(skip_all,fields(url=fetch_params.request.url().as_str()))]
2115async fn http_network_fetch(
2116 fetch_params: &mut FetchParams,
2117 credentials_flag: bool,
2118 done_chan: &mut DoneChannel,
2119 context: &FetchContext,
2120) -> Response {
2121 let mut response_end_timer = ResponseEndTimer(Some(context.timing.clone()));
2122
2123 let request = &mut fetch_params.request;
2125
2126 let url = request.current_url();
2136 let request_id = request.id.0.to_string();
2137 if log_enabled!(log::Level::Info) {
2138 info!("{:?} request for {}", request.method, url);
2139 for header in request.headers.iter() {
2140 debug!(" - {:?}", header);
2141 }
2142 }
2143
2144 let is_xhr = request.destination == Destination::None;
2148
2149 let (fetch_terminated_sender, mut fetch_terminated_receiver) = unbounded_channel();
2151
2152 let body = request.body.as_ref().map(|body| body.clone_stream());
2153
2154 if body.is_none() {
2155 let _ = fetch_terminated_sender.send(false);
2160 }
2161
2162 let browsing_context_id = request.target_webview_id.map(Into::into);
2163
2164 let (response_stream, msg) = match &request.mode {
2168 RequestMode::WebSocket {
2170 protocols,
2171 original_url: _,
2172 } => {
2173 let (resource_event_sender, dom_action_receiver) = {
2176 let mut websocket_chan = context.websocket_chan.as_ref().unwrap().lock();
2177 (
2178 websocket_chan.sender.clone(),
2179 websocket_chan.receiver.take().unwrap(),
2180 )
2181 };
2182
2183 let mut tls_config = create_tls_config(
2184 context.ca_certificates.clone(),
2185 context.ignore_certificate_errors,
2186 context.state.override_manager.clone(),
2187 );
2188 tls_config.alpn_protocols = vec!["http/1.1".to_string().into()];
2189
2190 let response = match start_websocket(
2191 context.state.clone(),
2192 resource_event_sender,
2193 protocols,
2194 request,
2195 tls_config,
2196 dom_action_receiver,
2197 )
2198 .await
2199 {
2200 Ok(response) => response,
2201 Err(error) => {
2202 return Response::network_error(NetworkError::WebsocketConnectionFailure(
2203 format!("{error:?}"),
2204 ));
2205 },
2206 };
2207
2208 let response = response.map(|r| match r {
2209 Some(body) => Full::from(body).map_err(|_| unreachable!()).boxed(),
2210 None => http_body_util::Empty::new()
2211 .map_err(|_| unreachable!())
2212 .boxed(),
2213 });
2214 (Decoder::detect(response, url.is_secure_scheme()), None)
2215 },
2216 _ => {
2219 let response_future = obtain_response(
2220 &context.state.client,
2221 &url,
2222 &request.method,
2223 &mut request.headers,
2224 body,
2225 request
2226 .body
2227 .as_ref()
2228 .is_some_and(|body| body.source_is_null()),
2229 &request.pipeline_id,
2230 Some(&request_id),
2231 request.destination,
2232 is_xhr,
2233 context,
2234 fetch_terminated_sender,
2235 browsing_context_id,
2236 );
2237
2238 let (response_stream, msg) = match response_future.await {
2240 Ok(wrapped_response) => wrapped_response,
2241 Err(error) => return Response::network_error(error),
2242 };
2243 (response_stream, msg)
2244 },
2245 };
2246
2247 if log_enabled!(log::Level::Info) {
2248 debug!("{:?} response for {}", response_stream.version(), url);
2249 for header in response_stream.headers().iter() {
2250 debug!(" - {:?}", header);
2251 }
2252 }
2253
2254 match fetch_terminated_receiver.recv().await {
2257 Some(true) => return Response::network_error(NetworkError::ConnectionFailure),
2258 Some(false) => {},
2259 _ => warn!("Failed to receive confirmation request was streamed without error."),
2260 }
2261
2262 let timing = context.timing.inner().clone();
2263 let mut response = Response::new(url.clone(), timing);
2264
2265 if let Some(handshake_info) = response_stream.extensions().get::<TlsHandshakeInfo>() {
2266 let mut hsts_enabled = url
2267 .host_str()
2268 .is_some_and(|host| context.state.hsts_list.read().is_host_secure(host));
2269
2270 if url.scheme() == "https" &&
2271 let Some(strict_transport_security) = response_stream
2272 .headers()
2273 .typed_get::<StrictTransportSecurity>()
2274 {
2275 hsts_enabled = strict_transport_security.max_age().as_secs() > 0;
2277 }
2278 response.tls_security_info = Some(build_tls_security_info(handshake_info, hsts_enabled));
2279 }
2280
2281 let status_text = response_stream
2282 .extensions()
2283 .get::<ReasonPhrase>()
2284 .map(ReasonPhrase::as_bytes)
2285 .or_else(|| {
2286 response_stream
2287 .status()
2288 .canonical_reason()
2289 .map(str::as_bytes)
2290 })
2291 .map(Vec::from)
2292 .unwrap_or_default();
2293 response.status = HttpStatus::new(response_stream.status(), status_text);
2294
2295 info!(
2296 "got {:?} response for {:?}",
2297 response_stream.status(),
2298 request.url()
2299 );
2300 response.headers = response_stream.headers().clone();
2301 response.referrer = request.referrer.to_url().cloned();
2302 response.referrer_policy = request.referrer_policy;
2303
2304 let response_body = response.body.clone();
2305
2306 let (done_sender, done_receiver) = unbounded_channel();
2308 *done_chan = Some((done_sender.clone(), done_receiver));
2309
2310 let devtools_sender = context.devtools_chan.clone();
2311 let cancellation_listener = context.cancellation_listener.clone();
2312 if cancellation_listener.cancelled() {
2313 return Response::network_error(NetworkError::LoadCancelled);
2314 }
2315
2316 if let Some(ref sender) = devtools_sender &&
2317 let Some(m) = msg
2318 {
2319 send_request_to_devtools(m, sender);
2320 }
2321
2322 let done_sender2 = done_sender.clone();
2323 let done_sender3 = done_sender.clone();
2324 let timing_ptr2 = context.timing.clone();
2325 let timing_ptr3 = context.timing.clone();
2326 let devtools_request = request.clone();
2327 let url1 = devtools_request.url();
2328 let url2 = url1.clone();
2329
2330 let status = response.status.clone();
2331 let headers = response.headers.clone();
2332 let devtools_chan = context.devtools_chan.clone();
2333
2334 let prealloc_size: usize = if let Some(possible_length) = response_stream
2335 .headers()
2336 .get(http::header::CONTENT_LENGTH)
2337 .and_then(|header_value| header_value.to_str().ok())
2338 .and_then(|s| s.parse::<usize>().ok())
2339 {
2340 let multiplier: usize = if response_stream.body().is_encoded() {
2343 5
2344 } else {
2345 1
2346 };
2347 possible_length.saturating_mul(multiplier)
2348 } else {
2349 4096
2352 }
2353 .min(pref!(network_max_content_length) as usize);
2354 let _ = done_sender.send(Data::ContentLength(prealloc_size));
2355 *response_body.lock() = ResponseBody::Receiving(Vec::with_capacity(prealloc_size));
2356 let response_body2 = response_body.clone();
2357
2358 spawn_task(
2359 response_stream
2360 .into_body()
2361 .try_fold(response_body, move |response_body_accumulator, chunk| {
2362 if cancellation_listener.cancelled() {
2363 *response_body_accumulator.lock() = ResponseBody::Done(vec![]);
2364 let _ = done_sender.send(Data::Cancelled);
2365 return future::ready(Err(std::io::Error::new(
2366 std::io::ErrorKind::Interrupted,
2367 "Fetch aborted",
2368 )));
2369 }
2370 if let ResponseBody::Receiving(ref mut body) = *response_body_accumulator.lock() {
2371 body.extend_from_slice(&chunk);
2372 let _ = done_sender.send(Data::Payload(chunk));
2373 }
2374 future::ready(Ok(response_body_accumulator))
2375 })
2376 .and_then(move |complete_response_body| {
2377 debug!("successfully finished response for {:?}", url1);
2378 let mut body = complete_response_body.lock();
2379 let mut completed_body = match *body {
2380 ResponseBody::Receiving(ref mut body) => std::mem::take(body),
2381 _ => vec![],
2382 };
2383 completed_body.shrink_to_fit();
2385 let devtools_response_body =
2388 devtools_chan.is_some().then(|| completed_body.clone());
2389 *body = ResponseBody::Done(completed_body);
2390 send_response_values_to_devtools(
2391 Some(headers),
2392 status,
2393 devtools_response_body,
2394 CacheState::None,
2395 &devtools_request,
2396 devtools_chan,
2397 );
2398 timing_ptr2.set_attribute(ResourceAttribute::ResponseEnd);
2399 let _ = done_sender2.send(Data::Done);
2400 future::ready(Ok(()))
2401 })
2402 .map_err(move |error| {
2403 if let std::io::ErrorKind::InvalidData = error.kind() {
2404 debug!("Content decompression error for {:?}", url2);
2405 let _ = done_sender3.send(Data::Error(NetworkError::DecompressionError));
2406 let mut body = response_body2.lock();
2407
2408 *body = ResponseBody::Done(vec![]);
2409 }
2410 debug!("finished response for {:?}", url2);
2411 let mut body = response_body2.lock();
2412 let completed_body = match *body {
2413 ResponseBody::Receiving(ref mut body) => std::mem::take(body),
2414 _ => vec![],
2415 };
2416 *body = ResponseBody::Done(completed_body);
2417 timing_ptr3.set_attribute(ResourceAttribute::ResponseEnd);
2418 let _ = done_sender3.send(Data::Done);
2419 }),
2420 );
2421
2422 if credentials_flag {
2440 set_cookies_from_headers(&url, &response.headers, &context.state.cookie_jar);
2441 }
2442 context
2443 .state
2444 .hsts_list
2445 .write()
2446 .update_hsts_list_from_response(&url, &response.headers);
2447
2448 response_end_timer.neuter();
2462 response
2463}
2464
2465async fn cors_preflight_fetch(
2467 request: &Request,
2468 cache: &mut CorsCache,
2469 context: &FetchContext,
2470) -> Response {
2471 let mut preflight = RequestBuilder::new(
2476 request.target_webview_id,
2477 request.current_url_with_blob_claim(),
2478 request.referrer.clone(),
2479 )
2480 .method(Method::OPTIONS)
2481 .origin(match &request.origin {
2482 Origin::Client => {
2483 unreachable!("We shouldn't get Client origin in cors_preflight_fetch.")
2484 },
2485 Origin::Origin(origin) => origin.clone(),
2486 })
2487 .pipeline_id(request.pipeline_id)
2488 .initiator(request.initiator)
2489 .destination(request.destination)
2490 .referrer_policy(request.referrer_policy)
2491 .mode(RequestMode::CorsMode)
2492 .response_tainting(ResponseTainting::CorsTainting)
2493 .policy_container(match &request.policy_container {
2494 RequestPolicyContainer::Client => {
2495 unreachable!("We should have a policy container for request in cors_preflight_fetch")
2496 },
2497 RequestPolicyContainer::PolicyContainer(policy_container) => policy_container.clone(),
2498 })
2499 .url_list(
2500 request
2501 .url_list
2502 .iter()
2503 .map(|claimed_url| claimed_url.url())
2504 .collect(),
2505 )
2506 .build();
2507
2508 preflight
2510 .headers
2511 .insert(ACCEPT, HeaderValue::from_static("*/*"));
2512
2513 preflight
2515 .headers
2516 .typed_insert::<AccessControlRequestMethod>(AccessControlRequestMethod::from(
2517 request.method.clone(),
2518 ));
2519
2520 let headers = get_cors_unsafe_header_names(&request.headers);
2522
2523 if !headers.is_empty() {
2525 preflight.headers.insert(
2528 ACCESS_CONTROL_REQUEST_HEADERS,
2529 HeaderValue::from_bytes(itertools::join(headers.iter(), ",").as_bytes())
2530 .unwrap_or(HeaderValue::from_static("")),
2531 );
2532 }
2533
2534 let mut fetch_params = FetchParams::new(preflight);
2537 let response =
2538 http_network_or_cache_fetch(&mut fetch_params, false, false, &mut None, context).await;
2539
2540 if cors_check(request, &response).is_ok() && response.status.code().is_success() {
2542 let mut methods = if response
2545 .headers
2546 .contains_key(header::ACCESS_CONTROL_ALLOW_METHODS)
2547 {
2548 match response.headers.typed_get::<AccessControlAllowMethods>() {
2549 Some(methods) => methods.iter().collect(),
2550 None => {
2552 return Response::network_error(NetworkError::CorsAllowMethods);
2553 },
2554 }
2555 } else {
2556 vec![]
2557 };
2558
2559 let header_names = if response
2562 .headers
2563 .contains_key(header::ACCESS_CONTROL_ALLOW_HEADERS)
2564 {
2565 match response.headers.typed_get::<AccessControlAllowHeaders>() {
2566 Some(names) => names.iter().collect(),
2567 None => {
2569 return Response::network_error(NetworkError::CorsAllowHeaders);
2570 },
2571 }
2572 } else {
2573 vec![]
2574 };
2575
2576 debug!(
2577 "CORS check: Allowed methods: {:?}, current method: {:?}",
2578 methods, request.method
2579 );
2580
2581 if methods.is_empty() && request.use_cors_preflight {
2584 methods = vec![request.method.clone()];
2585 }
2586
2587 if methods
2590 .iter()
2591 .all(|method| *method.as_str() != *request.method.as_ref()) &&
2592 !is_cors_safelisted_method(&request.method) &&
2593 (request.credentials_mode == CredentialsMode::Include ||
2594 methods.iter().all(|method| method.as_ref() != "*"))
2595 {
2596 return Response::network_error(NetworkError::CorsMethod);
2597 }
2598
2599 debug!(
2600 "CORS check: Allowed headers: {:?}, current headers: {:?}",
2601 header_names, request.headers
2602 );
2603
2604 let header_names_set: HashSet<&HeaderName> = HashSet::from_iter(header_names.iter());
2610 if request.headers.iter().any(|(name, _)| {
2611 is_cors_non_wildcard_request_header_name(name) &&
2612 !header_names_set.contains(name) &&
2613 !header_names_set.contains(&HeaderName::from_static("*"))
2614 }) {
2615 return Response::network_error(NetworkError::CorsAuthorization);
2616 }
2617
2618 let unsafe_names = get_cors_unsafe_header_names(&request.headers);
2622 for unsafe_name in unsafe_names.iter() {
2623 if !header_names_set.contains(unsafe_name) &&
2624 (request.credentials_mode == CredentialsMode::Include ||
2625 !header_names_set.contains(&HeaderName::from_static("*")))
2626 {
2627 return Response::network_error(NetworkError::CorsHeaders);
2628 }
2629 }
2630
2631 let max_age: Option<Duration> = response
2634 .headers
2635 .typed_get::<AccessControlMaxAge>()
2636 .map(|acma| acma.into());
2637
2638 let max_age = max_age.unwrap_or(Duration::from_secs(5));
2640
2641 for method in &methods {
2652 cache.match_method_and_update(request, method.clone(), max_age);
2653 }
2654
2655 for header_name in &header_names {
2660 cache.match_header_and_update(request, header_name, max_age);
2661 }
2662
2663 return response;
2665 }
2666
2667 Response::network_error(NetworkError::CorsGeneral)
2669}
2670
2671fn cors_check(request: &Request, response: &Response) -> Result<(), ()> {
2673 let Some(origins) =
2675 get_value_from_header_list(ACCESS_CONTROL_ALLOW_ORIGIN.as_str(), &response.headers)
2676 else {
2677 return Err(());
2679 };
2680 let origin = origins.into_iter().map(char::from).collect::<String>();
2681
2682 if request.credentials_mode != CredentialsMode::Include && origin == "*" {
2684 return Ok(());
2685 }
2686
2687 if serialize_request_origin(request).to_string() != origin {
2689 return Err(());
2690 }
2691
2692 if request.credentials_mode != CredentialsMode::Include {
2694 return Ok(());
2695 }
2696
2697 let credentials = response
2699 .headers
2700 .typed_get::<AccessControlAllowCredentials>();
2701
2702 if credentials.is_some() {
2704 return Ok(());
2705 }
2706
2707 Err(())
2709}
2710
2711fn has_credentials(url: &ServoUrl) -> bool {
2712 !url.username().is_empty() || url.password().is_some()
2713}
2714
2715fn is_no_store_cache(headers: &HeaderMap) -> bool {
2716 headers.contains_key(header::IF_MODIFIED_SINCE) |
2717 headers.contains_key(header::IF_NONE_MATCH) |
2718 headers.contains_key(header::IF_UNMODIFIED_SINCE) |
2719 headers.contains_key(header::IF_MATCH) |
2720 headers.contains_key(header::IF_RANGE)
2721}
2722
2723fn is_redirect_status(status: StatusCode) -> bool {
2725 matches!(
2726 status,
2727 StatusCode::MOVED_PERMANENTLY |
2728 StatusCode::FOUND |
2729 StatusCode::SEE_OTHER |
2730 StatusCode::TEMPORARY_REDIRECT |
2731 StatusCode::PERMANENT_REDIRECT
2732 )
2733}
2734
2735fn serialize_request_origin(request: &Request) -> headers::Origin {
2737 let Origin::Origin(origin) = &request.origin else {
2739 panic!("origin cannot be \"client\" at this point in time");
2740 };
2741
2742 if request.redirect_taint_for_request() != RedirectTaint::SameOrigin {
2744 return headers::Origin::NULL;
2745 }
2746
2747 serialize_origin(origin)
2749}
2750
2751pub fn serialize_origin(origin: &ImmutableOrigin) -> headers::Origin {
2753 match origin {
2754 ImmutableOrigin::Opaque(_) => headers::Origin::NULL,
2755 ImmutableOrigin::Tuple(scheme, host, port) => {
2756 let port = match (scheme.as_ref(), port) {
2759 ("http" | "ws", 80) | ("https" | "wss", 443) | ("ftp", 21) => None,
2760 _ => Some(*port),
2761 };
2762
2763 headers::Origin::try_from_parts(scheme, &host.to_string(), port)
2765 .unwrap_or(headers::Origin::NULL)
2766 },
2767 }
2768}
2769
2770#[expect(
2772 clippy::collapsible_match,
2773 reason = "The current way follows the spec more closely"
2774)]
2775fn append_a_request_origin_header(request: &mut Request) {
2776 let Origin::Origin(request_origin) = &request.origin else {
2778 panic!("origin cannot be \"client\" at this point in time");
2779 };
2780
2781 let mut serialized_origin = serialize_request_origin(request);
2783
2784 if request.response_tainting == ResponseTainting::CorsTainting ||
2787 matches!(request.mode, RequestMode::WebSocket { .. })
2788 {
2789 request.headers.typed_insert(serialized_origin);
2790 }
2791 else if !matches!(request.method, Method::GET | Method::HEAD) {
2793 if request.mode != RequestMode::CorsMode {
2795 match request.referrer_policy {
2796 ReferrerPolicy::NoReferrer => {
2797 serialized_origin = headers::Origin::NULL;
2799 },
2800 ReferrerPolicy::NoReferrerWhenDowngrade |
2801 ReferrerPolicy::StrictOrigin |
2802 ReferrerPolicy::StrictOriginWhenCrossOrigin => {
2803 if let ImmutableOrigin::Tuple(scheme, _, _) = &request_origin &&
2806 scheme == "https" &&
2807 request.current_url().scheme() != "https"
2808 {
2809 serialized_origin = headers::Origin::NULL;
2810 }
2811 },
2812 ReferrerPolicy::SameOrigin => {
2813 if *request_origin != request.current_url().origin() {
2816 serialized_origin = headers::Origin::NULL;
2817 }
2818 },
2819 _ => {
2820 },
2822 };
2823 }
2824
2825 request.headers.typed_insert(serialized_origin);
2827 }
2828}
2829
2830fn append_the_fetch_metadata_headers(r: &mut Request) {
2832 if !r.current_url().is_potentially_trustworthy() {
2834 return;
2835 }
2836
2837 set_the_sec_fetch_dest_header(r);
2839
2840 set_the_sec_fetch_mode_header(r);
2842
2843 set_the_sec_fetch_site_header(r);
2845
2846 set_the_sec_fetch_user_header(r);
2848}
2849
2850fn append_cache_data_to_headers(http_request: &mut Request) {
2852 match http_request.cache_mode {
2853 CacheMode::Default if is_no_store_cache(&http_request.headers) => {
2857 http_request.cache_mode = CacheMode::NoStore;
2858 },
2859
2860 CacheMode::NoCache if !http_request.headers.contains_key(header::CACHE_CONTROL) => {
2870 http_request
2871 .headers
2872 .typed_insert(CacheControl::new().with_max_age(Duration::from_secs(0)));
2873 },
2874
2875 CacheMode::Reload | CacheMode::NoStore => {
2877 if !http_request.headers.contains_key(header::PRAGMA) {
2880 http_request.headers.typed_insert(Pragma::no_cache());
2881 }
2882
2883 if !http_request.headers.contains_key(header::CACHE_CONTROL) {
2886 http_request
2887 .headers
2888 .typed_insert(CacheControl::new().with_no_cache());
2889 }
2890 },
2891
2892 _ => {},
2893 }
2894}
2895
2896fn set_the_sec_fetch_dest_header(r: &mut Request) {
2898 debug_assert!(r.current_url().is_potentially_trustworthy());
2900
2901 let header = r.destination;
2905
2906 r.headers.typed_insert(SecFetchDest(header));
2908}
2909
2910fn set_the_sec_fetch_mode_header(r: &mut Request) {
2912 debug_assert!(r.current_url().is_potentially_trustworthy());
2914
2915 let header = &r.mode;
2918
2919 r.headers.typed_insert(SecFetchMode::from(header));
2921}
2922
2923fn set_the_sec_fetch_site_header(r: &mut Request) {
2925 let Origin::Origin(request_origin) = &r.origin else {
2928 panic!("request origin cannot be \"client\" at this point")
2929 };
2930
2931 debug_assert!(r.current_url().is_potentially_trustworthy());
2933
2934 let mut header = SecFetchSite::SameOrigin;
2937
2938 if header != SecFetchSite::None {
2943 for url in &r.url_list {
2944 if url.origin() == *request_origin {
2946 continue;
2947 }
2948
2949 header = SecFetchSite::CrossSite;
2951
2952 if !is_same_site(request_origin, &url.origin()) {
2954 break;
2955 }
2956
2957 header = SecFetchSite::SameSite;
2959 }
2960 }
2961
2962 r.headers.typed_insert(header);
2964}
2965
2966fn set_the_sec_fetch_user_header(r: &mut Request) {
2968 debug_assert!(r.current_url().is_potentially_trustworthy());
2970
2971 if !r.is_navigation_request() {
2974 return;
2975 }
2976
2977 let header = SecFetchUser;
2980
2981 r.headers.typed_insert(header);
2983}
2984
2985fn set_requests_referrer_policy_on_redirect(request: &mut Request, response: &Response) {
2987 let referrer_policy: ReferrerPolicy = response
2990 .headers
2991 .typed_get::<headers::ReferrerPolicy>()
2992 .into();
2993
2994 if referrer_policy != ReferrerPolicy::EmptyString {
2996 request.referrer_policy = referrer_policy;
2997 }
2998}