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 embedder_traits::{AuthenticationResponse, GenericEmbedderProxy};
14use futures::{TryFutureExt, TryStreamExt, future};
15use headers::authorization::Basic;
16use headers::{
17 AccessControlAllowCredentials, AccessControlAllowHeaders, AccessControlAllowMethods,
18 AccessControlMaxAge, AccessControlRequestMethod, Authorization, CacheControl, ContentLength,
19 HeaderMapExt, IfModifiedSince, LastModified, Pragma, Referer, StrictTransportSecurity,
20 UserAgent,
21};
22use http::header::{
23 self, ACCEPT, ACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_REQUEST_HEADERS, AUTHORIZATION,
24 CONTENT_ENCODING, CONTENT_LANGUAGE, CONTENT_LOCATION, CONTENT_TYPE, HeaderValue, RANGE,
25 WWW_AUTHENTICATE,
26};
27use http::{HeaderMap, Method, Request as HyperRequest, StatusCode};
28use http_body_util::combinators::BoxBody;
29use http_body_util::{BodyExt, Full};
30use hyper::Response as HyperResponse;
31use hyper::body::{Bytes, Frame};
32use hyper::ext::ReasonPhrase;
33use hyper::header::{HeaderName, TRANSFER_ENCODING};
34use ipc_channel::IpcError;
35use ipc_channel::ipc::{self, IpcSender};
36use ipc_channel::router::ROUTER;
37use log::{debug, error, info, log_enabled, warn};
38use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
39use net_traits::blob_url_store::UrlWithBlobClaim;
40use net_traits::fetch::headers::get_value_from_header_list;
41use net_traits::http_status::HttpStatus;
42use net_traits::policy_container::{EmbedderPolicyValue, RequestPolicyContainer};
43use net_traits::pub_domains::{is_same_site, reg_suffix};
44use net_traits::request::{
45 BodyChunkRequest, BodyChunkResponse, CacheMode, CredentialsMode, Destination, Initiator,
46 Origin, RedirectMode, Referrer, Request, RequestBuilder, RequestClient, RequestMode,
47 ResponseTainting, ServiceWorkersMode, TraversableForUserPrompts, get_cors_unsafe_header_names,
48 is_cors_non_wildcard_request_header_name, is_cors_safelisted_method,
49 is_cors_safelisted_request_header,
50};
51use net_traits::response::{CacheState, RedirectTaint, Response, ResponseBody, ResponseType};
52use net_traits::{
53 CookieSource, DOCUMENT_ACCEPT_HEADER_VALUE, DiscardFetch, NetworkError, RedirectEndValue,
54 RedirectStartValue, ReferrerPolicy, ResourceAttribute, ResourceFetchTiming,
55 ResourceFetchTimingContainer, ResourceTimeValue, ResourceTimingType, ServoCipherSuite,
56 ServoNamedGroup, ServoProtocolVersion, TlsSecurityInfo, TlsSecurityState,
57};
58use parking_lot::{Mutex, RwLock};
59use profile_traits::mem::{Report, ReportKind};
60use profile_traits::path;
61#[cfg(feature = "tracing")]
62use profile_traits::trace_span;
63use rustc_hash::FxHashMap;
64use servo_base::cross_process_instant::CrossProcessInstant;
65use servo_base::generic_channel::GenericSharedMemory;
66use servo_base::id::HistoryStateId;
67use servo_config::pref;
68use servo_url::{ImmutableOrigin, ServoUrl};
69use tokio::sync::mpsc::{
70 Receiver as TokioReceiver, Sender as TokioSender, UnboundedReceiver, UnboundedSender, channel,
71 unbounded_channel,
72};
73use tokio_stream::wrappers::ReceiverStream;
74#[cfg(feature = "tracing")]
75use tracing::Instrument;
76use uuid::Uuid;
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;
85#[cfg(feature = "devtools")]
86use crate::devtools::{
87 prepare_devtools_request, send_request_to_devtools, send_response_values_to_devtools,
88};
89use crate::devtools::{
90 send_early_httprequest_to_devtools, send_response_to_devtools, send_security_info_to_devtools,
91};
92use crate::embedder::NetToEmbedderMsg;
93use crate::fetch::cors_cache::CorsCache;
94use crate::fetch::fetch_params::FetchParams;
95use crate::fetch::headers::{SecFetchDest, SecFetchMode, SecFetchSite, SecFetchUser};
96use crate::fetch::methods::{Data, DoneChannel, FetchContext, Target, fetch, main_fetch};
97use crate::hsts::HstsList;
98use crate::http_cache::{
99 CacheKey, CachedResourcesOrGuard, HttpCache, ValidationStatus, construct_response,
100 invalidate_cached_resources, refresh,
101};
102use crate::resource_thread::{AuthCache, AuthCacheEntry};
103use crate::websocket_loader::start_websocket;
104
105#[cfg(feature = "devtools")]
106type DevtoolsResponse = Option<devtools_traits::ChromeToDevtoolsControlMsg>;
107
108#[cfg(not(feature = "devtools"))]
109type DevtoolsResponse = Option<()>;
110
111#[derive(Clone, Debug, Eq, PartialEq)]
113pub enum HttpCacheEntryState {
114 ReadyToConstruct,
118 PendingStore(usize),
120}
121
122pub struct HttpState {
123 pub hsts_list: RwLock<HstsList>,
124 pub cookie_jar: RwLock<CookieStorage>,
125 pub http_cache: HttpCache,
126 pub auth_cache: RwLock<AuthCache>,
127 pub cors_cache: RwLock<CorsCache>,
128 pub history_states: RwLock<FxHashMap<HistoryStateId, Vec<u8>>>,
129 pub client: ServoClient,
130 pub override_manager: CertificateErrorOverrideManager,
131 pub embedder_proxy: GenericEmbedderProxy<NetToEmbedderMsg>,
132}
133
134impl HttpState {
135 pub(crate) fn memory_reports(&self, suffix: &str, ops: &mut MallocSizeOfOps) -> Vec<Report> {
136 vec![
137 Report {
138 path: path!["memory-cache", suffix],
139 kind: ReportKind::ExplicitJemallocHeapSize,
140 size: self.http_cache.size_of(ops),
141 },
142 Report {
143 path: path!["hsts-list", suffix],
144 kind: ReportKind::ExplicitJemallocHeapSize,
145 size: self.hsts_list.read().size_of(ops),
146 },
147 Report {
148 path: path!["auth cache", suffix],
149 kind: ReportKind::ExplicitJemallocHeapSize,
150 size: self.auth_cache.read().size_of(ops),
151 },
152 Report {
153 path: path!["cookie storage", suffix],
154 kind: ReportKind::ExplicitJemallocHeapSize,
155 size: self.cookie_jar.read().size_of(ops),
156 },
157 ]
158 }
159
160 async fn request_authentication(
161 &self,
162 request: &Request,
163 response: &Response,
164 ) -> Option<AuthenticationResponse> {
165 let webview_id = request.target_webview_id?;
167 let for_proxy = response.status == StatusCode::PROXY_AUTHENTICATION_REQUIRED;
168
169 if request.mode != RequestMode::Navigate {
171 return None;
172 }
173
174 let (sender, receiver) = tokio::sync::oneshot::channel();
175 self.embedder_proxy
176 .send(NetToEmbedderMsg::RequestAuthentication(
177 webview_id,
178 request.url(),
179 for_proxy,
180 sender,
181 ));
182 receiver.await.ok()?
183 }
184}
185
186pub(crate) fn set_default_accept(request: &mut Request) {
188 if request.headers.contains_key(header::ACCEPT) {
190 return;
191 }
192
193 let value = if request.initiator == Initiator::Prefetch {
195 DOCUMENT_ACCEPT_HEADER_VALUE
196 } else {
197 match request.destination {
200 Destination::Document | Destination::Frame | Destination::IFrame => {
201 DOCUMENT_ACCEPT_HEADER_VALUE
202 },
203 Destination::Image => {
204 HeaderValue::from_static("image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5")
205 },
206 Destination::Json => HeaderValue::from_static("application/json,*/*;q=0.5"),
207 Destination::Style => HeaderValue::from_static("text/css,*/*;q=0.1"),
208 Destination::Text => HeaderValue::from_static("text/plain,*/*;q=0.5"),
209 _ => HeaderValue::from_static("*/*"),
211 }
212 };
213
214 request.headers.insert(header::ACCEPT, value);
216}
217
218fn set_default_accept_encoding(headers: &mut HeaderMap) {
219 if headers.contains_key(header::ACCEPT_ENCODING) {
220 return;
221 }
222
223 headers.insert(
225 header::ACCEPT_ENCODING,
226 HeaderValue::from_static("gzip, deflate, br, zstd"),
227 );
228}
229
230fn no_referrer_when_downgrade(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
232 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
234 return None;
235 }
236 strip_url_for_use_as_referrer(referrer_url, false)
238}
239
240fn strict_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
242 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
244 return None;
245 }
246 strip_url_for_use_as_referrer(referrer_url, true)
248}
249
250fn strict_origin_when_cross_origin(
252 referrer_url: ServoUrl,
253 current_url: ServoUrl,
254) -> Option<ServoUrl> {
255 if referrer_url.origin() == current_url.origin() {
257 return strip_url_for_use_as_referrer(referrer_url, false);
258 }
259 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
261 return None;
262 }
263 strip_url_for_use_as_referrer(referrer_url, true)
265}
266
267fn is_schemelessy_same_site(site_a: &ImmutableOrigin, site_b: &ImmutableOrigin) -> bool {
269 if !site_a.is_tuple() && !site_b.is_tuple() && site_a == site_b {
271 true
272 } else if site_a.is_tuple() && site_b.is_tuple() {
273 let host_a = site_a.host().map(|h| h.to_string()).unwrap_or_default();
275 let host_b = site_b.host().map(|h| h.to_string()).unwrap_or_default();
276
277 let host_a_reg = reg_suffix(&host_a);
278 let host_b_reg = reg_suffix(&host_b);
279
280 (site_a.host() == site_b.host() && host_a_reg.is_empty()) ||
282 (host_a_reg == host_b_reg && !host_a_reg.is_empty())
283 } else {
284 false
286 }
287}
288
289fn strip_url_for_use_as_referrer(mut url: ServoUrl, origin_only: bool) -> Option<ServoUrl> {
291 const MAX_REFERRER_URL_LENGTH: usize = 4096;
292 if url.is_local_scheme() {
294 return None;
295 }
296 {
298 let url = url.as_mut_url();
299 let _ = url.set_username("");
300 let _ = url.set_password(None);
301 url.set_fragment(None);
302 if origin_only || url.as_str().len() > MAX_REFERRER_URL_LENGTH {
306 url.set_path("");
307 url.set_query(None);
308 }
309 }
310 Some(url)
312}
313
314fn same_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
316 if referrer_url.origin() == current_url.origin() {
318 return strip_url_for_use_as_referrer(referrer_url, false);
319 }
320 None
322}
323
324fn origin_when_cross_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
326 if referrer_url.origin() == current_url.origin() {
328 return strip_url_for_use_as_referrer(referrer_url, false);
329 }
330 strip_url_for_use_as_referrer(referrer_url, true)
332}
333
334pub fn determine_requests_referrer(
336 referrer_policy: ReferrerPolicy,
337 referrer_source: ServoUrl,
338 current_url: ServoUrl,
339) -> Option<ServoUrl> {
340 match referrer_policy {
341 ReferrerPolicy::EmptyString | ReferrerPolicy::NoReferrer => None,
342 ReferrerPolicy::Origin => strip_url_for_use_as_referrer(referrer_source, true),
343 ReferrerPolicy::UnsafeUrl => strip_url_for_use_as_referrer(referrer_source, false),
344 ReferrerPolicy::StrictOrigin => strict_origin(referrer_source, current_url),
345 ReferrerPolicy::StrictOriginWhenCrossOrigin => {
346 strict_origin_when_cross_origin(referrer_source, current_url)
347 },
348 ReferrerPolicy::SameOrigin => same_origin(referrer_source, current_url),
349 ReferrerPolicy::OriginWhenCrossOrigin => {
350 origin_when_cross_origin(referrer_source, current_url)
351 },
352 ReferrerPolicy::NoReferrerWhenDowngrade => {
353 no_referrer_when_downgrade(referrer_source, current_url)
354 },
355 }
356}
357
358fn set_request_cookies(
359 url: &ServoUrl,
360 headers: &mut HeaderMap,
361 cookie_jar: &RwLock<CookieStorage>,
362) {
363 let mut cookie_jar = cookie_jar.write();
364 cookie_jar.remove_expired_cookies_for_url(url);
365 if let Some(cookie_list) = cookie_jar.cookies_for_url(url, CookieSource::HTTP) &&
366 let Ok(cookie_list_header_value) = HeaderValue::from_bytes(cookie_list.as_bytes())
367 {
368 headers.insert(header::COOKIE, cookie_list_header_value);
369 }
370}
371
372fn set_cookie_for_url(cookie_jar: &RwLock<CookieStorage>, request: &ServoUrl, cookie_val: &str) {
373 let mut cookie_jar = cookie_jar.write();
374 let source = CookieSource::HTTP;
375
376 if let Some(cookie) = ServoCookie::from_cookie_string(cookie_val, request, source) {
377 cookie_jar.push(cookie, request, source);
378 }
379}
380
381fn set_cookies_from_headers(
382 url: &ServoUrl,
383 headers: &HeaderMap,
384 cookie_jar: &RwLock<CookieStorage>,
385) {
386 for cookie in headers.get_all(header::SET_COOKIE) {
387 let cookie_bytes = cookie.as_bytes();
388 if !ServoCookie::is_valid_name_or_value(cookie_bytes) {
389 continue;
390 }
391 if let Ok(cookie_str) = std::str::from_utf8(cookie_bytes) {
392 set_cookie_for_url(cookie_jar, url, cookie_str);
393 }
394 }
395}
396
397fn build_tls_security_info(handshake: &TlsHandshakeInfo, hsts_enabled: bool) -> TlsSecurityInfo {
398 let state = if handshake.protocol_version.is_none() || handshake.cipher_suite.is_none() {
406 TlsSecurityState::Insecure
408 } else {
409 TlsSecurityState::Secure
411 };
412
413 TlsSecurityInfo {
414 state,
415 weakness_reasons: Vec::new(), protocol_version: handshake.protocol_version.map(ServoProtocolVersion),
417 cipher_suite: handshake.cipher_suite.map(ServoCipherSuite),
418 kea_group_name: handshake.kea_group_name.map(ServoNamedGroup),
419 signature_scheme_name: handshake.signature_scheme_name.clone(),
420 alpn_protocol: handshake.alpn_protocol.clone(),
421 certificate_chain_der: handshake.certificate_chain_der.clone(),
422 certificate_transparency: None,
423 hsts: hsts_enabled,
424 hpkp: false,
425 used_ech: handshake.used_ech,
426 used_delegated_credentials: false,
427 used_ocsp: false,
428 used_private_dns: false,
429 }
430}
431
432fn auth_from_cache(
433 auth_cache: &RwLock<AuthCache>,
434 origin: &ImmutableOrigin,
435) -> Option<Authorization<Basic>> {
436 if let Some(auth_entry) = auth_cache
437 .read()
438 .entries
439 .get(origin.ascii_serialization().as_ref())
440 {
441 let user_name = &auth_entry.user_name;
442 let password = &auth_entry.password;
443 Some(Authorization::basic(user_name, password))
444 } else {
445 None
446 }
447}
448
449enum BodyChunk {
452 Chunk(GenericSharedMemory),
454 Done,
456}
457
458enum BodyStream {
460 Chunked(TokioReceiver<Result<Frame<Bytes>, hyper::Error>>),
463 Buffered(UnboundedReceiver<BodyChunk>),
466}
467
468enum BodySink {
471 Chunked(TokioSender<Result<Frame<Bytes>, hyper::Error>>),
473 Buffered(UnboundedSender<BodyChunk>),
477}
478
479impl BodySink {
480 fn transmit_bytes(&self, bytes: GenericSharedMemory) {
481 match self {
482 BodySink::Chunked(sender) => {
483 let sender = sender.clone();
484 spawn_task(async move {
485 let _ = sender
486 .send(Ok(Frame::data(Bytes::copy_from_slice(&bytes))))
487 .await;
488 });
489 },
490 BodySink::Buffered(sender) => {
491 let _ = sender.send(BodyChunk::Chunk(bytes));
492 },
493 }
494 }
495
496 fn close(self) {
497 match self {
498 BodySink::Chunked(_) => {},
499 BodySink::Buffered(sender) => {
500 let _ = sender.send(BodyChunk::Done);
501 },
502 }
503 }
504}
505
506fn request_body_stream_closed_error(action: &str) -> NetworkError {
507 NetworkError::Crash(format!(
508 "Request body stream has already been closed while trying to {action}."
509 ))
510}
511
512fn log_request_body_stream_closed(action: &str, error: Option<&IpcError>) {
513 match error {
514 Some(error) => {
515 error!("Request body stream has already been closed while trying to {action}: {error}")
516 },
517 None => error!("Request body stream has already been closed while trying to {action}."),
518 }
519}
520
521fn log_fetch_terminated_send_failure(terminated_with_error: bool, context: &str) {
522 warn!(
523 "Failed to notify request-body stream termination state ({terminated_with_error}) while {context} because the receiver was already dropped."
524 );
525}
526
527const FRAGMENT: &AsciiSet = &CONTROLS.add(b'|').add(b'{').add(b'}');
528
529#[servo_tracing::instrument(skip_all, fields(url=url.as_str()))]
530async fn obtain_response(
532 fetch_params: &mut FetchParams,
533 client: &ServoClient,
534 url: &ServoUrl,
535 body_sender: Option<StdArc<Mutex<Option<IpcSender<BodyChunkRequest>>>>>,
536 request_id: Option<&Uuid>,
537 context: &FetchContext,
538 fetch_terminated: UnboundedSender<bool>,
539) -> Result<(HyperResponse<Decoder>, DevtoolsResponse), NetworkError> {
540 #[cfg(feature = "devtools")]
541 let (destination, pipeline_id, browsing_context_id) = {
542 let request = &fetch_params.request;
543
544 (
545 request.destination,
546 request.pipeline_id,
547 request.target_webview_id.map(Into::into),
548 )
549 };
550
551 let method = &fetch_params.request.method;
552 let source_is_null = fetch_params
553 .request
554 .body
555 .as_ref()
556 .is_some_and(|body| body.source_is_null());
557 let headers = &mut fetch_params.request.headers;
558
559 #[cfg(feature = "devtools")]
560 let devtools_bytes = StdArc::new(Mutex::new(vec![]));
561
562 let encoded_url = utf8_percent_encode(url.as_str(), FRAGMENT).to_string();
564
565 let request = if let Some(chunk_requester) = body_sender {
566 let (sink, stream) = if source_is_null {
567 headers.insert(TRANSFER_ENCODING, HeaderValue::from_static("chunked"));
570
571 let (sender, receiver) = channel(1);
572 (BodySink::Chunked(sender), BodyStream::Chunked(receiver))
573 } else {
574 let (sender, receiver) = unbounded_channel();
581 (BodySink::Buffered(sender), BodyStream::Buffered(receiver))
582 };
583
584 #[cfg(feature = "devtools")]
585 obtain_response_setup_router_callback(
586 Some(devtools_bytes.clone()),
587 chunk_requester,
588 sink,
589 fetch_terminated,
590 )?;
591
592 #[cfg(not(feature = "devtools"))]
593 obtain_response_setup_router_callback(None, chunk_requester, sink, fetch_terminated)?;
594
595 let body = match stream {
596 BodyStream::Chunked(receiver) => {
597 let stream = ReceiverStream::new(receiver);
598 BoxBody::new(http_body_util::StreamBody::new(stream))
599 },
600 BodyStream::Buffered(mut receiver) => {
601 let mut body = vec![];
603 loop {
604 match receiver.recv().await {
605 Some(BodyChunk::Chunk(bytes)) => {
606 body.extend_from_slice(&bytes);
607 },
608 Some(BodyChunk::Done) => break,
609 None => warn!("Failed to read all chunks from request body."),
610 }
611 }
612 Full::new(body.into()).map_err(|_| unreachable!()).boxed()
613 },
614 };
615 HyperRequest::builder()
616 .method(method)
617 .uri(encoded_url)
618 .body(body)
619 } else {
620 HyperRequest::builder()
621 .method(method)
622 .uri(encoded_url)
623 .body(
624 http_body_util::Empty::new()
625 .map_err(|_| unreachable!())
626 .boxed(),
627 )
628 };
629
630 let connect_start = CrossProcessInstant::now();
633 fetch_params.timing_info.set_attributes(&[
634 ResourceAttribute::DomainLookupStart,
635 ResourceAttribute::ConnectStart(connect_start),
636 ]);
637
638 if url.scheme() == "https" {
642 fetch_params
643 .timing_info
644 .set_attribute(ResourceAttribute::SecureConnectionStart);
645 }
646
647 let mut request = match request {
648 Ok(request) => request,
649 Err(error) => return Err(NetworkError::HttpError(error.to_string())),
650 };
651 *request.headers_mut() = headers.clone();
652
653 let connect_end = CrossProcessInstant::now();
654 fetch_params
655 .timing_info
656 .set_attribute(ResourceAttribute::ConnectEnd(connect_end));
657
658 let host = request.uri().host().unwrap_or("").to_owned();
659 let override_manager = context.state.override_manager.clone();
660 #[cfg(feature = "devtools")]
661 let (request_id, pipeline_id, closure_url, method, send_start, headers) = (
662 request_id.map(|value| value.to_owned()),
663 pipeline_id,
664 url.clone(),
665 method.clone(),
666 CrossProcessInstant::now(),
667 headers.clone(),
668 );
669 let is_secure_scheme = url.is_secure_scheme();
670
671 fetch_params
675 .timing_info
676 .set_attribute(ResourceAttribute::RequestStart);
677
678 let client_future = client
679 .request(request)
680 .and_then(move |res| {
681 #[cfg(feature = "devtools")]
683 let msg = if let Some(request_id) = request_id {
684 let send_end = CrossProcessInstant::now();
685 if let Some(pipeline_id) = pipeline_id {
686 if let Some(browsing_context_id) = browsing_context_id {
687 let is_xhr = destination == Destination::None;
692
693 Some(prepare_devtools_request(
694 request_id,
695 closure_url,
696 method.clone(),
697 headers,
698 Some(devtools_bytes.lock().clone()),
699 pipeline_id,
700 (connect_end - connect_start).unsigned_abs(),
701 (send_end - send_start).unsigned_abs(),
702 destination,
703 is_xhr,
704 browsing_context_id,
705 ))
706 } else {
707 debug!("Not notifying devtools (no browsing_context_id)");
708 None
709 }
710 } else {
715 debug!("Not notifying devtools (no pipeline_id)");
716 None
717 }
718 } else {
719 debug!("Not notifying devtools (no request_id)");
720 None
721 };
722
723 future::ready(Ok((
724 Decoder::detect(res.map(|r| r.boxed()), is_secure_scheme),
725 #[cfg(feature = "devtools")]
726 msg,
727 #[cfg(not(feature = "devtools"))]
728 None,
729 )))
730 })
731 .map_err(move |error| {
732 warn!("network error: {error:?}");
733 NetworkError::from_hyper_error(
734 &error,
735 override_manager.remove_certificate_failing_verification(host.as_str()),
736 )
737 });
738
739 #[cfg(feature = "tracing")]
740 {
741 client_future.instrument(trace_span!("HyperRequest")).await
742 }
743
744 #[cfg(not(feature = "tracing"))]
745 {
746 client_future.await
747 }
748}
749
750fn obtain_response_setup_router_callback(
752 devtools_bytes: Option<StdArc<Mutex<Vec<u8>>>>,
753 chunk_requester: StdArc<Mutex<Option<IpcSender<BodyChunkRequest>>>>,
754 sink: BodySink,
755 fetch_terminated: UnboundedSender<bool>,
756) -> Result<(), NetworkError> {
757 let (body_chan, body_port) = ipc::channel().unwrap();
758
759 {
760 let mut lock = chunk_requester.lock();
761 if let Some(chunk_requester) = lock.as_mut() {
762 if let Err(error) = chunk_requester.send(BodyChunkRequest::Connect(body_chan)) {
763 log_request_body_stream_closed("connect to the request body stream", Some(&error));
764 return Err(request_body_stream_closed_error(
765 "connect to the request body stream",
766 ));
767 }
768
769 if let Err(error) = chunk_requester.send(BodyChunkRequest::Chunk) {
772 log_request_body_stream_closed(
773 "request the first request body chunk",
774 Some(&error),
775 );
776 return Err(request_body_stream_closed_error(
777 "request the first request body chunk",
778 ));
779 }
780 } else {
781 log_request_body_stream_closed("connect to the request body stream", None);
782 return Err(request_body_stream_closed_error(
783 "connect to the request body stream",
784 ));
785 }
786 }
787
788 let mut sink = Some(sink);
789
790 ROUTER.add_typed_route(
791 body_port,
792 Box::new(move |message| {
793 info!("Received message");
794 let bytes = match message.unwrap() {
795 BodyChunkResponse::Chunk(bytes) => bytes,
796 BodyChunkResponse::Done => {
797 if fetch_terminated.send(false).is_err() {
800 log_fetch_terminated_send_failure(
801 false,
802 "handling request body completion",
803 );
804 }
805 if let Some(sink) = sink.take() {
806 sink.close();
807 }
808
809 return;
810 },
811 BodyChunkResponse::Error => {
812 if fetch_terminated.send(true).is_err() {
816 log_fetch_terminated_send_failure(
817 true,
818 "handling request body stream error",
819 );
820 }
821 if let Some(sink) = sink.take() {
822 sink.close();
823 }
824
825 return;
826 },
827 };
828
829 #[cfg(feature = "devtools")]
830 if let Some(devtools_bytes) = &devtools_bytes {
831 devtools_bytes.lock().extend_from_slice(&bytes);
832 }
833
834 {
837 let Some(sink) = sink.as_ref() else {
838 return;
839 };
840 sink.transmit_bytes(bytes);
841 }
842
843 let mut chunk_requester = chunk_requester.lock();
846 if let Some(chunk_requester) = chunk_requester.as_mut() {
847 if let Err(error) = chunk_requester.send(BodyChunkRequest::Chunk) {
848 log_request_body_stream_closed(
849 "request the next request body chunk",
850 Some(&error),
851 );
852 if fetch_terminated.send(true).is_err() {
853 log_fetch_terminated_send_failure(
854 true,
855 "handling failure to request the next request body chunk",
856 );
857 }
858 if let Some(sink) = sink.take() {
859 sink.close();
860 }
861 }
862 } else {
863 log_request_body_stream_closed("request the next request body chunk", None);
864 if fetch_terminated.send(true).is_err() {
865 log_fetch_terminated_send_failure(
866 true,
867 "handling a closed request body stream while requesting the next chunk",
868 );
869 }
870 if let Some(sink) = sink.take() {
871 sink.close();
872 }
873 }
874 }),
875 );
876
877 Ok(())
878}
879
880#[async_recursion]
882pub(crate) async fn http_fetch(
883 fetch_params: &mut FetchParams,
884 cors_flag: bool,
885 cors_preflight_flag: bool,
886 authentication_fetch_flag: bool,
887 target: Target<'async_recursion>,
888 done_chan: &mut DoneChannel,
889 context: &FetchContext,
890) -> Response {
891 *done_chan = None;
893 let request = &mut fetch_params.request;
895
896 let mut response: Option<Response> = None;
898
899 if request.service_workers_mode == ServiceWorkersMode::All {
901 if let Some(ref res) = response {
906 if (res.response_type == ResponseType::Opaque && request.mode != RequestMode::NoCors) ||
914 (res.response_type == ResponseType::OpaqueRedirect &&
915 request.redirect_mode != RedirectMode::Manual) ||
916 (res.url_list.len() > 1 && request.redirect_mode != RedirectMode::Follow) ||
917 res.is_network_error()
918 {
919 return Response::network_error(NetworkError::ConnectionFailure);
920 }
921
922 }
925 }
926
927 if response.is_none() {
929 if cors_preflight_flag {
931 let (method_mismatch, header_mismatch) = {
932 let mut cors_cache = context.state.cors_cache.write();
933 let method_cache_match = cors_cache.match_method(request, &request.method);
934
935 let method_mismatch = !method_cache_match &&
939 (!is_cors_safelisted_method(&request.method) || request.use_cors_preflight);
940
941 let header_mismatch = request.headers.iter().any(|(name, value)| {
944 !cors_cache.match_header(request, name) &&
945 !is_cors_safelisted_request_header(&name, &value)
946 });
947 (method_mismatch, header_mismatch)
948 };
949
950 if method_mismatch || header_mismatch {
952 let preflight_response = cors_preflight_fetch(request, context).await;
955 if let Some(error) = preflight_response.get_network_error() {
957 return Response::network_error(error.clone());
958 }
959 }
960 }
961
962 if request.redirect_mode == RedirectMode::Follow {
965 request.service_workers_mode = ServiceWorkersMode::None;
966 }
967
968 let mut fetch_result = http_network_or_cache_fetch(
971 fetch_params,
972 authentication_fetch_flag,
973 cors_flag,
974 done_chan,
975 context,
976 )
977 .await;
978
979 if cors_flag && cors_check(&fetch_params.request, &fetch_result).is_err() {
982 return Response::network_error(NetworkError::CorsGeneral);
983 }
984
985 if let Err(()) = tao_check(&fetch_params.request, &fetch_result) {
988 fetch_params.timing_info.inner().mark_timing_check_failed();
989 }
990 fetch_result.return_internal = false;
991 response = Some(fetch_result);
992 }
993
994 let request = &mut fetch_params.request;
995
996 let mut response = response.unwrap();
998
999 if (request.response_tainting == ResponseTainting::Opaque ||
1003 response.response_type == ResponseType::Opaque) &&
1004 request.client.as_ref().is_some_and(|client| {
1005 cross_origin_resource_policy_check(
1006 &request.origin,
1007 client,
1008 &response,
1009 ForNavigation::No,
1010 ) == CrossOriginResourcePolicy::Blocked
1011 })
1012 {
1013 return Response::network_error(NetworkError::CrossOriginResponse);
1014 }
1015
1016 if response
1018 .actual_response()
1019 .status
1020 .try_code()
1021 .is_some_and(is_redirect_status)
1022 {
1023 if response.actual_response().status != StatusCode::SEE_OTHER {
1030 }
1032
1033 response = match request.redirect_mode {
1035 RedirectMode::Error => Response::network_error(NetworkError::RedirectError),
1037 RedirectMode::Manual => {
1038 if request.mode == RequestMode::Navigate {
1041 let location_url =
1045 location_url_for_response(&response, request.current_url().fragment());
1046 response.actual_response_mut().location_url = location_url;
1047 response
1048 } else {
1049 response.to_filtered(ResponseType::OpaqueRedirect)
1052 }
1053 },
1054 RedirectMode::Follow => {
1055 response.return_internal = true;
1059
1060 http_redirect_fetch(
1063 fetch_params,
1064 response,
1065 cors_flag,
1066 target,
1067 done_chan,
1068 context,
1069 )
1070 .await
1071 },
1072 };
1073 }
1074
1075 response.return_internal = true;
1077 fetch_params
1078 .timing_info
1079 .set_attribute(ResourceAttribute::RedirectCount(
1080 fetch_params.request.redirect_count as u16,
1081 ));
1082
1083 response.resource_timing = fetch_params.timing_info.clone();
1084
1085 response
1087}
1088
1089fn tao_check(request: &Request, response: &Response) -> Result<(), ()> {
1091 let Origin::Origin(ref request_origin) = request.origin else {
1093 unreachable!("origin cannot be \"client\" at this point");
1094 };
1095
1096 let values: Vec<&str> = response
1101 .headers
1102 .get_all("Timing-Allow-Origin")
1103 .iter()
1104 .map(|header_value| header_value.to_str().unwrap_or(""))
1105 .collect();
1106
1107 if values.contains(&"*") {
1109 return Ok(());
1110 }
1111
1112 if values
1115 .iter()
1116 .any(|header_str| *header_str == request_origin.ascii_serialization().as_ref())
1117 {
1118 return Ok(());
1119 }
1120
1121 if request.mode == RequestMode::Navigate && request.current_url().origin() != *request_origin {
1124 return Err(());
1125 }
1126
1127 if request.response_tainting == ResponseTainting::Basic {
1129 return Ok(());
1130 }
1131
1132 Err(())
1134}
1135
1136struct RedirectEndTimer(Option<ResourceFetchTimingContainer>);
1138
1139impl RedirectEndTimer {
1140 fn neuter(&mut self) {
1141 self.0 = None;
1142 }
1143}
1144
1145impl Drop for RedirectEndTimer {
1146 fn drop(&mut self) {
1147 let RedirectEndTimer(resource_fetch_timing_opt) = self;
1148
1149 resource_fetch_timing_opt.as_ref().map_or((), |t| {
1150 t.set_attribute(ResourceAttribute::RedirectEnd(RedirectEndValue::Zero));
1151 })
1152 }
1153}
1154
1155static REQUEST_BODY_HEADER_NAMES: &[HeaderName] = &[
1157 CONTENT_ENCODING,
1158 CONTENT_LANGUAGE,
1159 CONTENT_LOCATION,
1160 CONTENT_TYPE,
1161];
1162
1163fn location_url_for_response(
1165 response: &Response,
1166 request_fragment: Option<&str>,
1167) -> Option<Result<ServoUrl, String>> {
1168 assert!(
1170 response
1171 .actual_response()
1172 .status
1173 .try_code()
1174 .is_some_and(is_redirect_status)
1175 );
1176 let mut location = response
1178 .actual_response()
1179 .headers
1180 .get(header::LOCATION)
1181 .and_then(|header_value| {
1182 HeaderValue::to_str(header_value)
1183 .map(|location_string| {
1184 ServoUrl::parse_with_base(response.actual_response().url(), location_string)
1186 .map_err(|error| error.to_string())
1187 })
1188 .ok()
1189 });
1190
1191 if let Some(Ok(ref mut location)) = location &&
1193 location.fragment().is_none()
1194 {
1195 location.set_fragment(request_fragment);
1196 }
1197 location
1199}
1200
1201#[async_recursion]
1203pub async fn http_redirect_fetch(
1204 fetch_params: &mut FetchParams,
1205 mut response: Response,
1206 cors_flag: bool,
1207 target: Target<'async_recursion>,
1208 done_chan: &mut DoneChannel,
1209 context: &FetchContext,
1210) -> Response {
1211 let mut redirect_end_timer = RedirectEndTimer(Some(fetch_params.timing_info.clone()));
1212
1213 let request = &mut fetch_params.request;
1215
1216 assert!(response.return_internal);
1218
1219 let location_url = location_url_for_response(&response, request.current_url().fragment());
1221 response.actual_response_mut().location_url = location_url.clone();
1222
1223 let location_url = match location_url {
1224 None => return response,
1226 Some(Err(err)) => {
1228 return Response::network_error(NetworkError::ResourceLoadError(
1229 "Location URL parse failure: ".to_owned() + &err,
1230 ));
1231 },
1232 Some(Ok(url)) if !matches!(url.scheme(), "http" | "https") => {
1234 return Response::network_error(NetworkError::UnsupportedScheme);
1235 },
1236 Some(Ok(url)) => url,
1237 };
1238
1239 fetch_params.timing_info.set_attributes(&[
1244 ResourceAttribute::RedirectStart(RedirectStartValue::FetchStart),
1245 ResourceAttribute::FetchStart,
1246 ResourceAttribute::StartTime(ResourceTimeValue::FetchStart),
1247 ResourceAttribute::StartTime(ResourceTimeValue::RedirectStart),
1248 ]);
1249
1250 if request.redirect_count >= 20 {
1252 return Response::network_error(NetworkError::TooManyRedirects);
1253 }
1254
1255 request.redirect_count += 1;
1257
1258 let has_credentials = has_credentials(&location_url);
1261 if request.mode == RequestMode::CorsMode {
1262 let same_origin = match request.origin {
1263 Origin::Origin(ref origin) => *origin == location_url.origin(),
1264 Origin::Client => panic!(
1265 "Request origin should not be client for {}",
1266 request.current_url()
1267 ),
1268 };
1269 if !same_origin && has_credentials {
1270 return Response::network_error(NetworkError::CorsCredentials);
1271 }
1272 }
1273
1274 if cors_flag && has_credentials {
1276 return Response::network_error(NetworkError::CorsCredentials);
1277 }
1278
1279 if response.actual_response().status != StatusCode::SEE_OTHER &&
1282 request.body.as_ref().is_some_and(|b| b.source_is_null())
1283 {
1284 return Response::network_error(NetworkError::ConnectionFailure);
1285 }
1286
1287 if response
1289 .actual_response()
1290 .status
1291 .try_code()
1292 .is_some_and(|code| {
1293 ((code == StatusCode::MOVED_PERMANENTLY || code == StatusCode::FOUND) &&
1295 request.method == Method::POST) ||
1296 (code == StatusCode::SEE_OTHER &&
1298 request.method != Method::HEAD &&
1299 request.method != Method::GET)
1300 })
1301 {
1302 request.method = Method::GET;
1304 request.body = None;
1305 for name in REQUEST_BODY_HEADER_NAMES {
1307 request.headers.remove(name);
1308 }
1309 }
1310
1311 if location_url.origin() != request.current_url().origin() {
1315 request.headers.remove(AUTHORIZATION);
1318 }
1319
1320 if let Some(body) = request.body.as_mut() {
1323 body.extract_source();
1324 }
1325
1326 request
1330 .url_list
1331 .push(UrlWithBlobClaim::from_url_without_having_claimed_blob(
1332 location_url,
1333 ));
1334
1335 set_requests_referrer_policy_on_redirect(request, response.actual_response());
1337
1338 let recursive_flag = request.redirect_mode != RedirectMode::Manual;
1341
1342 let fetch_response = main_fetch(fetch_params, recursive_flag, target, done_chan, context).await;
1344
1345 fetch_params
1347 .timing_info
1348 .set_attribute(ResourceAttribute::RedirectEnd(
1349 RedirectEndValue::ResponseEnd,
1350 ));
1351 redirect_end_timer.neuter();
1352
1353 fetch_response
1354}
1355
1356#[async_recursion]
1358#[servo_tracing::instrument(skip_all,fields(url=fetch_params.request.url().as_str()))]
1359async fn http_network_or_cache_fetch(
1360 fetch_params: &mut FetchParams,
1361 authentication_fetch_flag: bool,
1362 cors_flag: bool,
1363 done_chan: &mut DoneChannel,
1364 context: &FetchContext,
1365) -> Response {
1366 let http_fetch_params: &mut FetchParams;
1368 let mut fetch_params_copy: FetchParams;
1369
1370 let mut response: Option<Response> = None;
1374
1375 let mut revalidating_flag = false;
1377
1378 let http_request = if fetch_params.request.traversable_for_user_prompts ==
1382 TraversableForUserPrompts::NoTraversable &&
1383 fetch_params.request.redirect_mode == RedirectMode::Error
1384 {
1385 http_fetch_params = fetch_params;
1386 &mut http_fetch_params.request
1387 }
1388 else {
1390 fetch_params_copy = std::mem::replace(
1393 fetch_params,
1394 FetchParams::new(
1395 fetch_params.request.clone(),
1396 fetch_params.timing_info.clone(),
1397 ),
1398 );
1399 http_fetch_params = &mut fetch_params_copy;
1400
1401 &mut http_fetch_params.request
1402 };
1403
1404 let include_credentials = match http_request.credentials_mode {
1406 CredentialsMode::Include => true,
1408 CredentialsMode::CredentialsSameOrigin
1410 if http_request.response_tainting == ResponseTainting::Basic =>
1411 {
1412 true
1413 },
1414 _ => false,
1415 };
1416
1417 let content_length = http_request
1424 .body
1425 .as_ref()
1426 .and_then(|body| body.len().map(|size| size as u64));
1427
1428 let mut content_length_header_value = None;
1430
1431 if http_request.body.is_none() && matches!(http_request.method, Method::POST | Method::PUT) {
1434 content_length_header_value = Some(0);
1435 }
1436
1437 if let Some(content_length) = content_length {
1441 content_length_header_value = Some(content_length);
1442 };
1443
1444 if let Some(content_length_header_value) = content_length_header_value {
1447 http_request
1448 .headers
1449 .typed_insert(ContentLength(content_length_header_value));
1450 }
1451
1452 if http_request.keep_alive &&
1454 let Some(content_length) = content_length
1455 {
1456 let in_flight_keep_alive_bytes: u64 = context
1461 .in_flight_keep_alive_records
1462 .lock()
1463 .get(
1464 &http_request
1465 .pipeline_id
1466 .expect("Must always set a pipeline ID for keep-alive requests"),
1467 )
1468 .map(|records| {
1469 records
1473 .iter()
1474 .map(|record| {
1475 if record.request_id == http_request.id {
1476 0
1479 } else {
1480 record.keep_alive_body_length
1481 }
1482 })
1483 .sum()
1484 })
1485 .unwrap_or_default();
1486 if content_length + in_flight_keep_alive_bytes > 64 * 1024 {
1488 return Response::network_error(NetworkError::TooManyInFlightKeepAliveRequests);
1489 }
1490 }
1491
1492 match http_request.referrer {
1494 Referrer::ReferrerUrl(ref http_request_referrer) |
1495 Referrer::Client(ref http_request_referrer) => {
1496 if let Ok(referer) = http_request_referrer.as_str().parse::<Referer>() {
1499 http_request.headers.typed_insert(referer);
1501 } else {
1502 error!("Failed to parse {} as referrer", http_request_referrer);
1506 }
1507 },
1508 _ => {},
1509 };
1510
1511 append_a_request_origin_header(http_request);
1513
1514 append_the_fetch_metadata_headers(http_request);
1516
1517 if http_request.initiator == Initiator::Prefetch &&
1520 let Ok(value) = HeaderValue::from_str("prefetch")
1521 {
1522 http_request.headers.insert("Sec-Purpose", value);
1523 }
1524
1525 if !http_request.headers.contains_key(header::USER_AGENT) {
1528 http_request
1529 .headers
1530 .typed_insert::<UserAgent>(context.user_agent.parse().unwrap());
1531 }
1532
1533 append_cache_data_to_headers(http_request);
1535
1536 if http_request.headers.contains_key(header::RANGE) &&
1539 let Ok(value) = HeaderValue::from_str("identity")
1540 {
1541 http_request.headers.insert("Accept-Encoding", value);
1542 }
1543
1544 http_request.headers.remove(header::HOST);
1548 set_default_accept_encoding(&mut http_request.headers);
1550
1551 let current_url = http_request.current_url();
1552
1553 if include_credentials {
1556 set_request_cookies(
1560 ¤t_url,
1561 &mut http_request.headers,
1562 &context.state.cookie_jar,
1563 );
1564 if !http_request.headers.contains_key(header::AUTHORIZATION) {
1566 let mut authorization_value = None;
1568
1569 if let Some(basic) = auth_from_cache(&context.state.auth_cache, ¤t_url.origin()) &&
1571 (!http_request.use_url_credentials || !has_credentials(¤t_url))
1572 {
1573 authorization_value = Some(basic);
1574 }
1575
1576 if authentication_fetch_flag &&
1578 authorization_value.is_none() &&
1579 has_credentials(¤t_url)
1580 {
1581 authorization_value = Some(Authorization::basic(
1582 current_url.username(),
1583 current_url.password().unwrap_or(""),
1584 ));
1585 }
1586
1587 if let Some(basic) = authorization_value {
1589 http_request.headers.typed_insert(basic);
1590 }
1591 }
1592 }
1593
1594 let should_wait = {
1596 let mut cache_guard = block_for_cache_ready(
1598 context,
1599 http_request,
1600 done_chan,
1601 &mut revalidating_flag,
1602 &mut response,
1603 )
1604 .await;
1605
1606 if response.is_none() {
1610 if http_request.cache_mode == CacheMode::OnlyIfCached {
1612 return Response::network_error(NetworkError::CacheError);
1614 }
1615
1616 drop(cache_guard);
1619 let forward_response =
1620 http_network_fetch(http_fetch_params, include_credentials, done_chan, context)
1621 .await;
1622
1623 let http_request = &mut http_fetch_params.request;
1624 let request_key = CacheKey::new(http_request);
1625 cache_guard = context
1626 .state
1627 .http_cache
1628 .get_or_guard(request_key.clone())
1629 .await;
1630 if forward_response.status.in_range(200..=399) && !http_request.method.is_safe() {
1634 if let Some(guard) = cache_guard.try_as_mut() {
1635 invalidate_cached_resources(guard);
1636 }
1637 context
1638 .state
1639 .http_cache
1640 .invalidate_related_urls(http_request, &forward_response, &request_key)
1641 .await;
1642 }
1643
1644 if revalidating_flag && forward_response.status == StatusCode::NOT_MODIFIED {
1646 *done_chan = None;
1649 if let Some(guard) = cache_guard.try_as_mut() {
1650 response = refresh(http_request, forward_response.clone(), done_chan, guard);
1651 }
1652
1653 match &mut response {
1654 Some(response) => {
1655 response.cache_state = CacheState::Validated;
1656 },
1657 None => {
1659 *done_chan = None;
1660 return Response::network_error(NetworkError::CacheError);
1661 },
1662 }
1663 }
1664
1665 if response.is_none() {
1667 let forward_response = response.insert(forward_response);
1669
1670 if http_request.cache_mode != CacheMode::NoStore {
1673 cache_guard.insert(http_request, forward_response);
1676 }
1677 }
1678 false
1679 } else {
1680 true
1681 }
1682 }; if should_wait {
1685 wait_for_inflight_requests(done_chan, &mut response).await;
1690 }
1691
1692 let http_request = &mut http_fetch_params.request;
1693 let mut response = response.unwrap();
1694
1695 response.url_list = http_request
1697 .url_list
1698 .iter()
1699 .map(|claimed_url| claimed_url.url())
1700 .collect();
1701
1702 if http_request.headers.contains_key(RANGE) {
1704 response.range_requested = true;
1705 }
1706
1707 response.request_includes_credentials = include_credentials;
1709
1710 if response.status.try_code() == Some(StatusCode::UNAUTHORIZED) &&
1716 !cors_flag &&
1717 include_credentials &&
1718 response.headers.contains_key(WWW_AUTHENTICATE)
1719 {
1720 let request = &mut fetch_params.request;
1723
1724 if request
1727 .body
1728 .as_ref()
1729 .is_some_and(|body| body.source_is_null())
1730 {
1731 return Response::network_error(NetworkError::ConnectionFailure);
1732 }
1733
1734 if !request.use_url_credentials || authentication_fetch_flag {
1736 let Some(credentials) = context
1737 .state
1738 .request_authentication(request, &response)
1739 .await
1740 else {
1741 return response;
1742 };
1743
1744 if let Err(err) = request
1745 .current_url_mut()
1746 .set_username(&credentials.username)
1747 {
1748 error!("error setting username for url: {:?}", err);
1749 return response;
1750 };
1751
1752 if let Err(err) = request
1753 .current_url_mut()
1754 .set_password(Some(&credentials.password))
1755 {
1756 error!("error setting password for url: {:?}", err);
1757 return response;
1758 };
1759 }
1760
1761 *done_chan = None;
1764
1765 response = http_network_or_cache_fetch(
1767 fetch_params,
1768 true, cors_flag,
1770 done_chan,
1771 context,
1772 )
1773 .await;
1774 }
1775
1776 if response.status == StatusCode::PROXY_AUTHENTICATION_REQUIRED {
1778 let request = &mut fetch_params.request;
1779 if request.traversable_for_user_prompts == TraversableForUserPrompts::NoTraversable {
1782 return Response::network_error(NetworkError::ResourceLoadError(
1783 "Can't find Window object".into(),
1784 ));
1785 }
1786
1787 let Some(credentials) = context
1795 .state
1796 .request_authentication(request, &response)
1797 .await
1798 else {
1799 return response;
1800 };
1801
1802 let entry = AuthCacheEntry {
1804 user_name: credentials.username,
1805 password: credentials.password,
1806 };
1807 {
1808 let mut auth_cache = context.state.auth_cache.write();
1809 let key = request
1810 .current_url()
1811 .origin()
1812 .ascii_serialization()
1813 .into_owned();
1814 auth_cache.entries.insert(key, entry);
1815 }
1816
1817 *done_chan = None;
1820
1821 response = http_network_or_cache_fetch(
1823 fetch_params,
1824 false, cors_flag,
1826 done_chan,
1827 context,
1828 )
1829 .await;
1830 }
1831
1832 if authentication_fetch_flag {
1840 }
1842
1843 response
1845}
1846
1847#[servo_tracing::instrument(skip_all)]
1855async fn block_for_cache_ready<'a>(
1856 context: &'a FetchContext,
1857 http_request: &mut Request,
1858 done_chan: &mut DoneChannel,
1859 revalidating_flag: &mut bool,
1860 response: &mut Option<Response>,
1861) -> CachedResourcesOrGuard<'a> {
1862 let entry_key = CacheKey::new(http_request);
1863 let guard_result = context.state.http_cache.get_or_guard(entry_key).await;
1864
1865 match guard_result {
1866 CachedResourcesOrGuard::Guard(_) => {
1867 *done_chan = None;
1868 },
1869 CachedResourcesOrGuard::Value(ref cached_resources) => {
1870 let stored_response = construct_response(http_request, done_chan, cached_resources);
1876 if let Some(response_from_cache) = stored_response {
1878 let validation_status = &response_from_cache.validation_status;
1879 let revalidation_guard = &response_from_cache.revalidation_guard;
1880
1881 let (cached_response, needs_synchronous_revalidation) =
1883 match (http_request.cache_mode, &http_request.mode) {
1884 (CacheMode::ForceCache, _) => (Some(response_from_cache.response), false),
1885 (CacheMode::OnlyIfCached, &RequestMode::SameOrigin) => {
1886 (Some(response_from_cache.response), false)
1887 },
1888 (CacheMode::OnlyIfCached, _) |
1889 (CacheMode::NoStore, _) |
1890 (CacheMode::Reload, _) => (None, false),
1891 (_, _) => (
1892 Some(response_from_cache.response),
1893 *validation_status ==
1894 (ValidationStatus::Stale {
1895 revalidate_in_background: false,
1896 }),
1897 ),
1898 };
1899
1900 if needs_synchronous_revalidation {
1901 *revalidating_flag = true;
1902 let response_headers = &cached_response
1903 .as_ref()
1904 .expect("needs_synchronous_revalidation implies a cached response")
1905 .headers;
1906 if let Some(http_date) = response_headers.typed_get::<LastModified>() {
1908 let http_date: SystemTime = http_date.into();
1909 http_request
1910 .headers
1911 .typed_insert(IfModifiedSince::from(http_date));
1912 }
1913 if let Some(entity_tag) = response_headers.get(header::ETAG) {
1914 http_request
1915 .headers
1916 .insert(header::IF_NONE_MATCH, entity_tag.clone());
1917 }
1918 } else {
1919 let revalidate_in_background = *validation_status ==
1922 (ValidationStatus::Stale {
1923 revalidate_in_background: true,
1924 });
1925 if revalidate_in_background && cached_response.is_some() {
1926 spawn_stale_while_revalidate(
1927 context,
1928 http_request,
1929 revalidation_guard.clone(),
1930 );
1931 }
1932 *response = cached_response;
1933 if let Some(response) = response {
1934 response.cache_state = CacheState::Local;
1935 }
1936 }
1937 if response.is_none() {
1938 *done_chan = None;
1941 }
1942 }
1943 },
1944 }
1945 guard_result
1946}
1947
1948fn spawn_stale_while_revalidate(
1951 context: &FetchContext,
1952 http_request: &Request,
1953 revalidation_guard: StdArc<AtomicBool>,
1954) {
1955 if revalidation_guard
1957 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
1958 .is_err()
1959 {
1960 return;
1961 }
1962
1963 let mut revalidation_request = http_request.clone();
1966 revalidation_request.cache_mode = CacheMode::NoCache;
1967
1968 revalidation_request.service_workers_mode = ServiceWorkersMode::None;
1970
1971 let context = context.clone();
1972 debug!(
1973 "spawning stale-while-revalidate background revalidation for {:?}",
1974 revalidation_request.current_url()
1975 );
1976 spawn_task(async move {
1977 let mut target = DiscardFetch;
1978
1979 let _ = fetch(revalidation_request, &mut target, &context).await;
1980 revalidation_guard.store(false, Ordering::Release);
1981 });
1982}
1983
1984async fn wait_for_inflight_requests(done_chan: &mut DoneChannel, response: &mut Option<Response>) {
1987 if let Some(ref mut ch) = *done_chan {
1988 assert!(response.is_some());
1992
1993 loop {
1994 match ch.1.recv().await {
1995 Some(Data::ContentLength(_)) | Some(Data::Payload(_)) | Some(Data::Error(_)) => {},
1996 Some(Data::Done) => break, Some(Data::Cancelled) => {
1998 break;
2000 },
2001 None => panic!("HTTP cache should always send Done or Cancelled"),
2002 }
2003 }
2004 }
2005 *done_chan = None;
2007}
2008
2009#[derive(PartialEq)]
2013enum CrossOriginResourcePolicy {
2014 Allowed,
2015 Blocked,
2016}
2017
2018enum ForNavigation {
2019 #[expect(dead_code)]
2020 Yes,
2021 No,
2022}
2023
2024fn cross_origin_resource_policy_check(
2026 origin: &Origin,
2027 request_client: &RequestClient,
2028 response: &Response,
2029 for_navigation: ForNavigation,
2030) -> CrossOriginResourcePolicy {
2031 let embedder_policy = &request_client.policy_container.embedder_policy;
2037
2038 if cross_origin_resource_policy_internal_check(
2041 origin,
2042 EmbedderPolicyValue::UnsafeNone,
2043 response,
2044 &for_navigation,
2045 ) == CrossOriginResourcePolicy::Blocked
2046 {
2047 return CrossOriginResourcePolicy::Blocked;
2048 }
2049
2050 if cross_origin_resource_policy_internal_check(
2058 origin,
2059 embedder_policy.value,
2060 response,
2061 &for_navigation,
2062 ) == CrossOriginResourcePolicy::Allowed
2063 {
2064 return CrossOriginResourcePolicy::Allowed;
2065 }
2066
2067 CrossOriginResourcePolicy::Blocked
2072}
2073
2074fn cross_origin_resource_policy_internal_check(
2076 origin: &Origin,
2077 embedder_policy_value: EmbedderPolicyValue,
2078 response: &Response,
2079 for_navigation: &ForNavigation,
2080) -> CrossOriginResourcePolicy {
2081 if let ForNavigation::Yes = for_navigation &&
2083 let EmbedderPolicyValue::UnsafeNone = embedder_policy_value
2084 {
2085 return CrossOriginResourcePolicy::Allowed;
2086 }
2087
2088 let policy = response
2090 .headers
2091 .get(HeaderName::from_static("cross-origin-resource-policy"))
2092 .and_then(|h| h.to_str().ok());
2093
2094 let policy = policy
2096 .filter(|&s| s == "same-origin" || s == "same-site" || s == "cross-origin")
2097 .or(match embedder_policy_value {
2099 EmbedderPolicyValue::UnsafeNone => None,
2101 EmbedderPolicyValue::RequireCorp => Some("same-origin"),
2103 });
2104
2105 match policy {
2107 Some("same-origin") => {
2108 if let Origin::Origin(request_origin) = origin &&
2110 response
2111 .url()
2112 .is_some_and(|url| request_origin == &url.origin())
2113 {
2114 return CrossOriginResourcePolicy::Allowed;
2115 }
2116
2117 CrossOriginResourcePolicy::Blocked
2119 },
2120 Some("same-site") => {
2121 if let Some(response_url) = response.url() {
2122 if let Origin::Origin(request_origin) = origin &&
2126 is_schemelessy_same_site(request_origin, &response_url.origin()) &&
2127 (request_origin.scheme() == Some("https") ||
2128 response_url.scheme() != "https")
2129 {
2130 return CrossOriginResourcePolicy::Allowed;
2131 }
2132 }
2133 CrossOriginResourcePolicy::Blocked
2135 },
2136 _ => CrossOriginResourcePolicy::Allowed,
2139 }
2140}
2141
2142struct ResponseEndTimer(Option<ResourceFetchTimingContainer>);
2144
2145impl ResponseEndTimer {
2146 fn neuter(&mut self) {
2147 self.0 = None;
2148 }
2149}
2150
2151impl Drop for ResponseEndTimer {
2152 fn drop(&mut self) {
2153 let ResponseEndTimer(resource_fetch_timing_opt) = self;
2154
2155 resource_fetch_timing_opt.as_ref().map_or((), |t| {
2156 t.set_attribute(ResourceAttribute::ResponseEnd);
2157 })
2158 }
2159}
2160
2161#[servo_tracing::instrument(skip_all,fields(url=fetch_params.request.url().as_str()))]
2163async fn http_network_fetch(
2164 fetch_params: &mut FetchParams,
2165 credentials_flag: bool,
2166 done_chan: &mut DoneChannel,
2167 context: &FetchContext,
2168) -> Response {
2169 let mut response_end_timer = ResponseEndTimer(Some(fetch_params.timing_info.clone()));
2170
2171 let request = &mut fetch_params.request;
2173
2174 let url = request.current_url();
2184 let request_id = request.id.0;
2185 if log_enabled!(log::Level::Info) {
2186 info!("{:?} request for {}", request.method, url);
2187 for header in request.headers.iter() {
2188 debug!(" - {:?}", header);
2189 }
2190 }
2191
2192 let (fetch_terminated_sender, mut fetch_terminated_receiver) = unbounded_channel();
2194
2195 let body = request.body.as_ref().map(|body| body.clone_stream());
2196
2197 if body.is_none() {
2198 let _ = fetch_terminated_sender.send(false);
2203 }
2204
2205 let (response_stream, msg) = match &request.mode {
2209 RequestMode::WebSocket {
2211 protocols,
2212 original_url: _,
2213 } => {
2214 let (resource_event_sender, dom_action_receiver) = {
2217 let mut websocket_chan = context.websocket_chan.as_ref().unwrap().lock();
2218 (
2219 websocket_chan.sender.clone(),
2220 websocket_chan.receiver.take().unwrap(),
2221 )
2222 };
2223
2224 let mut tls_config = create_tls_config(
2225 context.ca_certificates.clone(),
2226 context.ignore_certificate_errors,
2227 context.state.override_manager.clone(),
2228 );
2229 tls_config.alpn_protocols = vec!["http/1.1".into()];
2230
2231 let response = match start_websocket(
2232 context.state.clone(),
2233 resource_event_sender,
2234 protocols,
2235 request,
2236 tls_config,
2237 dom_action_receiver,
2238 )
2239 .await
2240 {
2241 Ok(response) => response,
2242 Err(error) => {
2243 return Response::network_error(NetworkError::WebsocketConnectionFailure(
2244 format!("{error:?}"),
2245 ));
2246 },
2247 };
2248
2249 let response = response.map(|r| match r {
2250 Some(body) => Full::from(body).map_err(|_| unreachable!()).boxed(),
2251 None => http_body_util::Empty::new()
2252 .map_err(|_| unreachable!())
2253 .boxed(),
2254 });
2255 (Decoder::detect(response, url.is_secure_scheme()), None)
2256 },
2257 _ => {
2260 let response_future = obtain_response(
2261 fetch_params,
2262 &context.state.client,
2263 &url,
2264 body,
2265 Some(&request_id),
2266 context,
2267 fetch_terminated_sender,
2268 );
2269
2270 let (response_stream, msg) = match response_future.await {
2272 Ok(wrapped_response) => wrapped_response,
2273 Err(error) => return Response::network_error(error),
2274 };
2275 (response_stream, msg)
2276 },
2277 };
2278
2279 let request = &mut fetch_params.request;
2280
2281 if log_enabled!(log::Level::Info) {
2282 debug!("{:?} response for {}", response_stream.version(), url);
2283 for header in response_stream.headers().iter() {
2284 debug!(" - {:?}", header);
2285 }
2286 }
2287
2288 match fetch_terminated_receiver.recv().await {
2291 Some(true) => return Response::network_error(NetworkError::ConnectionFailure),
2292 Some(false) => {},
2293 _ => warn!("Failed to receive confirmation request was streamed without error."),
2294 }
2295
2296 let timing = fetch_params.timing_info.inner().clone();
2297 let mut response = Response::new(url.clone(), timing);
2298
2299 if let Some(handshake_info) = response_stream.extensions().get::<TlsHandshakeInfo>() {
2300 let mut hsts_enabled = url
2301 .host_str()
2302 .is_some_and(|host| context.state.hsts_list.read().is_host_secure(host));
2303
2304 if url.scheme() == "https" &&
2305 let Some(strict_transport_security) = response_stream
2306 .headers()
2307 .typed_get::<StrictTransportSecurity>()
2308 {
2309 hsts_enabled = strict_transport_security.max_age().as_secs() > 0;
2311 }
2312 response.tls_security_info = Some(build_tls_security_info(handshake_info, hsts_enabled));
2313 }
2314
2315 let status_text = response_stream
2316 .extensions()
2317 .get::<ReasonPhrase>()
2318 .map(ReasonPhrase::as_bytes)
2319 .or_else(|| {
2320 response_stream
2321 .status()
2322 .canonical_reason()
2323 .map(str::as_bytes)
2324 })
2325 .map(Vec::from)
2326 .unwrap_or_default();
2327 response.status = HttpStatus::new(response_stream.status(), status_text);
2328
2329 info!(
2330 "got {:?} response for {:?}",
2331 response_stream.status(),
2332 request.url()
2333 );
2334 response.headers = response_stream.headers().clone();
2335 response.referrer = request.referrer.to_url().cloned();
2336 response.referrer_policy = request.referrer_policy;
2337
2338 let response_body = response.body.clone();
2339
2340 let (done_sender, done_receiver) = unbounded_channel();
2342 *done_chan = Some((done_sender.clone(), done_receiver));
2343
2344 let cancellation_listener = context.cancellation_listener.clone();
2345 if cancellation_listener.cancelled() {
2346 return Response::network_error(NetworkError::LoadCancelled);
2347 }
2348
2349 #[cfg(feature = "devtools")]
2350 let (devtools_request, status, headers, devtools_chan) = {
2351 if let Some(ref sender) = context.devtools_chan &&
2352 let Some(msg) = msg
2353 {
2354 send_request_to_devtools(msg, sender);
2355 }
2356 (
2357 request.clone(),
2358 response.status.clone(),
2359 response.headers.clone(),
2360 context.devtools_chan.clone(),
2361 )
2362 };
2363
2364 let done_sender2 = done_sender.clone();
2365 let done_sender3 = done_sender.clone();
2366 let timing_ptr2 = fetch_params.timing_info.clone();
2367 let timing_ptr3 = fetch_params.timing_info.clone();
2368 let url1 = request.url();
2369 let url2 = url1.clone();
2370
2371 let prealloc_size: usize = if let Some(possible_length) = response_stream
2372 .headers()
2373 .get(http::header::CONTENT_LENGTH)
2374 .and_then(|header_value| header_value.to_str().ok())
2375 .and_then(|s| s.parse::<usize>().ok())
2376 {
2377 let multiplier: usize = if response_stream.body().is_encoded() {
2380 5
2381 } else {
2382 1
2383 };
2384 possible_length.saturating_mul(multiplier)
2385 } else {
2386 4096
2389 }
2390 .min(pref!(network_max_content_length) as usize);
2391 let _ = done_sender.send(Data::ContentLength(prealloc_size));
2392 *response_body.lock() = ResponseBody::Receiving(Vec::with_capacity(prealloc_size));
2393 let response_body2 = response_body.clone();
2394
2395 spawn_task(
2396 response_stream
2397 .into_body()
2398 .try_fold(response_body, move |response_body_accumulator, chunk| {
2399 if cancellation_listener.cancelled() {
2400 *response_body_accumulator.lock() = ResponseBody::Done(vec![]);
2401 let _ = done_sender.send(Data::Cancelled);
2402 return future::ready(Err(std::io::Error::new(
2403 std::io::ErrorKind::Interrupted,
2404 "Fetch aborted",
2405 )));
2406 }
2407 if let ResponseBody::Receiving(ref mut body) = *response_body_accumulator.lock() {
2408 body.extend_from_slice(&chunk);
2409 let _ = done_sender.send(Data::Payload(chunk));
2410 }
2411 future::ready(Ok(response_body_accumulator))
2412 })
2413 .and_then(move |complete_response_body| {
2414 debug!("successfully finished response for {:?}", url1);
2415 let mut body = complete_response_body.lock();
2416 let mut completed_body = match *body {
2417 ResponseBody::Receiving(ref mut body) => std::mem::take(body),
2418 _ => vec![],
2419 };
2420 completed_body.shrink_to_fit();
2422 #[cfg(feature = "devtools")]
2425 let devtools_response_body =
2426 devtools_chan.is_some().then(|| completed_body.clone());
2427 *body = ResponseBody::Done(completed_body);
2428 #[cfg(feature = "devtools")]
2429 send_response_values_to_devtools(
2430 Some(headers),
2431 status,
2432 devtools_response_body,
2433 CacheState::None,
2434 &devtools_request,
2435 devtools_chan,
2436 );
2437 timing_ptr2.set_attribute(ResourceAttribute::ResponseEnd);
2438 let _ = done_sender2.send(Data::Done);
2439 future::ready(Ok(()))
2440 })
2441 .map_err(move |error| {
2442 if let std::io::ErrorKind::InvalidData = error.kind() {
2443 debug!("Content decompression error for {:?}", url2);
2444 let _ = done_sender3.send(Data::Error(NetworkError::DecompressionError));
2445 let mut body = response_body2.lock();
2446
2447 *body = ResponseBody::Done(vec![]);
2448 }
2449 debug!("finished response for {:?}", url2);
2450 let mut body = response_body2.lock();
2451 let completed_body = match *body {
2452 ResponseBody::Receiving(ref mut body) => std::mem::take(body),
2453 _ => vec![],
2454 };
2455 *body = ResponseBody::Done(completed_body);
2456 timing_ptr3.set_attribute(ResourceAttribute::ResponseEnd);
2457 let _ = done_sender3.send(Data::Done);
2458 }),
2459 );
2460
2461 if credentials_flag {
2479 set_cookies_from_headers(&url, &response.headers, &context.state.cookie_jar);
2480 }
2481 context
2482 .state
2483 .hsts_list
2484 .write()
2485 .update_hsts_list_from_response(&url, &response.headers);
2486
2487 response_end_timer.neuter();
2501 response
2502}
2503
2504async fn cors_preflight_fetch(request: &Request, context: &FetchContext) -> Response {
2506 let mut preflight = RequestBuilder::new(
2511 request.target_webview_id,
2512 request.current_url_with_blob_claim(),
2513 request.referrer.clone(),
2514 )
2515 .method(Method::OPTIONS)
2516 .origin(match &request.origin {
2517 Origin::Client => {
2518 unreachable!("We shouldn't get Client origin in cors_preflight_fetch.")
2519 },
2520 Origin::Origin(origin) => origin.clone(),
2521 })
2522 .pipeline_id(request.pipeline_id)
2523 .initiator(request.initiator)
2524 .destination(request.destination)
2525 .referrer_policy(request.referrer_policy)
2526 .mode(RequestMode::CorsMode)
2527 .response_tainting(ResponseTainting::CorsTainting)
2528 .policy_container(match &request.policy_container {
2529 RequestPolicyContainer::Client => {
2530 unreachable!("We should have a policy container for request in cors_preflight_fetch")
2531 },
2532 RequestPolicyContainer::PolicyContainer(policy_container) => policy_container.clone(),
2533 })
2534 .url_list(request.url_list.clone())
2535 .build();
2536
2537 preflight
2539 .headers
2540 .insert(ACCEPT, HeaderValue::from_static("*/*"));
2541
2542 preflight
2544 .headers
2545 .typed_insert::<AccessControlRequestMethod>(AccessControlRequestMethod::from(
2546 request.method.clone(),
2547 ));
2548
2549 let headers = get_cors_unsafe_header_names(&request.headers);
2551
2552 if !headers.is_empty() {
2554 preflight.headers.insert(
2557 ACCESS_CONTROL_REQUEST_HEADERS,
2558 HeaderValue::from_bytes(itertools::join(headers.iter(), ",").as_bytes())
2559 .unwrap_or(HeaderValue::from_static("")),
2560 );
2561 }
2562
2563 let mut fetch_params = FetchParams::new(
2566 preflight.clone(),
2567 ResourceFetchTiming::new(ResourceTimingType::None).into(),
2568 );
2569
2570 send_early_httprequest_to_devtools(&preflight, context);
2571
2572 let response =
2573 http_network_or_cache_fetch(&mut fetch_params, false, false, &mut None, context).await;
2574
2575 send_response_to_devtools(&preflight, context, &response, None);
2576 send_security_info_to_devtools(&preflight, context, &response);
2577
2578 if cors_check(request, &response).is_ok() && response.status.code().is_success() {
2580 let mut methods = if response
2583 .headers
2584 .contains_key(header::ACCESS_CONTROL_ALLOW_METHODS)
2585 {
2586 match response.headers.typed_get::<AccessControlAllowMethods>() {
2587 Some(methods) => methods.iter().collect(),
2588 None => {
2590 return Response::network_error(NetworkError::CorsAllowMethods);
2591 },
2592 }
2593 } else {
2594 vec![]
2595 };
2596
2597 let header_names = if response
2600 .headers
2601 .contains_key(header::ACCESS_CONTROL_ALLOW_HEADERS)
2602 {
2603 match response.headers.typed_get::<AccessControlAllowHeaders>() {
2604 Some(names) => names.iter().collect(),
2605 None => {
2607 return Response::network_error(NetworkError::CorsAllowHeaders);
2608 },
2609 }
2610 } else {
2611 vec![]
2612 };
2613
2614 debug!(
2615 "CORS check: Allowed methods: {:?}, current method: {:?}",
2616 methods, request.method
2617 );
2618
2619 if methods.is_empty() && request.use_cors_preflight {
2622 methods = vec![request.method.clone()];
2623 }
2624
2625 if methods
2628 .iter()
2629 .all(|method| *method.as_str() != *request.method.as_ref()) &&
2630 !is_cors_safelisted_method(&request.method) &&
2631 (request.credentials_mode == CredentialsMode::Include ||
2632 methods.iter().all(|method| method.as_ref() != "*"))
2633 {
2634 return Response::network_error(NetworkError::CorsMethod);
2635 }
2636
2637 debug!(
2638 "CORS check: Allowed headers: {:?}, current headers: {:?}",
2639 header_names, request.headers
2640 );
2641
2642 let header_names_set: HashSet<&HeaderName> = HashSet::from_iter(header_names.iter());
2648 if request.headers.iter().any(|(name, _)| {
2649 is_cors_non_wildcard_request_header_name(name) &&
2650 !header_names_set.contains(name) &&
2651 !header_names_set.contains(&HeaderName::from_static("*"))
2652 }) {
2653 return Response::network_error(NetworkError::CorsAuthorization);
2654 }
2655
2656 let unsafe_names = get_cors_unsafe_header_names(&request.headers);
2660 for unsafe_name in unsafe_names.iter() {
2661 if !header_names_set.contains(unsafe_name) &&
2662 (request.credentials_mode == CredentialsMode::Include ||
2663 !header_names_set.contains(&HeaderName::from_static("*")))
2664 {
2665 return Response::network_error(NetworkError::CorsHeaders);
2666 }
2667 }
2668
2669 let max_age: Option<Duration> = response
2672 .headers
2673 .typed_get::<AccessControlMaxAge>()
2674 .map(|acma| acma.into());
2675
2676 let max_age = max_age.unwrap_or(Duration::from_secs(5));
2678
2679 {
2686 let mut cors_cache = context.state.cors_cache.write();
2687 for method in &methods {
2692 cors_cache.match_method_and_update(request, method, max_age);
2693 }
2694
2695 for header_name in &header_names {
2700 cors_cache.match_header_and_update(request, header_name, max_age);
2701 }
2702 }
2703
2704 return response;
2706 }
2707
2708 Response::network_error(NetworkError::CorsGeneral)
2710}
2711
2712fn cors_check(request: &Request, response: &Response) -> Result<(), ()> {
2714 let Some(origins) =
2716 get_value_from_header_list(ACCESS_CONTROL_ALLOW_ORIGIN.as_str(), &response.headers)
2717 else {
2718 return Err(());
2720 };
2721 let origin = String::from_utf8(origins).map_err(|_| ())?;
2722
2723 if request.credentials_mode != CredentialsMode::Include && origin == "*" {
2725 return Ok(());
2726 }
2727
2728 if serialize_request_origin(request).to_string() != origin {
2730 return Err(());
2731 }
2732
2733 if request.credentials_mode != CredentialsMode::Include {
2735 return Ok(());
2736 }
2737
2738 let credentials = response
2740 .headers
2741 .typed_get::<AccessControlAllowCredentials>();
2742
2743 if credentials.is_some() {
2745 return Ok(());
2746 }
2747
2748 Err(())
2750}
2751
2752fn has_credentials(url: &ServoUrl) -> bool {
2753 !url.username().is_empty() || url.password().is_some()
2754}
2755
2756fn is_no_store_cache(headers: &HeaderMap) -> bool {
2757 headers.contains_key(header::IF_MODIFIED_SINCE) |
2758 headers.contains_key(header::IF_NONE_MATCH) |
2759 headers.contains_key(header::IF_UNMODIFIED_SINCE) |
2760 headers.contains_key(header::IF_MATCH) |
2761 headers.contains_key(header::IF_RANGE)
2762}
2763
2764fn is_redirect_status(status: StatusCode) -> bool {
2766 matches!(
2767 status,
2768 StatusCode::MOVED_PERMANENTLY |
2769 StatusCode::FOUND |
2770 StatusCode::SEE_OTHER |
2771 StatusCode::TEMPORARY_REDIRECT |
2772 StatusCode::PERMANENT_REDIRECT
2773 )
2774}
2775
2776fn serialize_request_origin(request: &Request) -> headers::Origin {
2778 let Origin::Origin(origin) = &request.origin else {
2780 panic!("origin cannot be \"client\" at this point in time");
2781 };
2782
2783 if request.redirect_taint_for_request() != RedirectTaint::SameOrigin {
2785 return headers::Origin::NULL;
2786 }
2787
2788 serialize_origin(origin)
2790}
2791
2792pub fn serialize_origin(origin: &ImmutableOrigin) -> headers::Origin {
2794 match origin {
2795 ImmutableOrigin::Opaque(_) => headers::Origin::NULL,
2796 ImmutableOrigin::Tuple(scheme, host, port) => {
2797 let port = match (scheme.as_ref(), port) {
2800 ("http" | "ws", 80) | ("https" | "wss", 443) | ("ftp", 21) => None,
2801 _ => Some(*port),
2802 };
2803
2804 headers::Origin::try_from_parts(scheme, &host.to_string(), port)
2806 .unwrap_or(headers::Origin::NULL)
2807 },
2808 }
2809}
2810
2811#[expect(
2813 clippy::collapsible_match,
2814 reason = "The current way follows the spec more closely"
2815)]
2816fn append_a_request_origin_header(request: &mut Request) {
2817 let Origin::Origin(request_origin) = &request.origin else {
2819 panic!("origin cannot be \"client\" at this point in time");
2820 };
2821
2822 let mut serialized_origin = serialize_request_origin(request);
2824
2825 if request.response_tainting == ResponseTainting::CorsTainting ||
2828 matches!(request.mode, RequestMode::WebSocket { .. })
2829 {
2830 request.headers.typed_insert(serialized_origin);
2831 }
2832 else if !matches!(request.method, Method::GET | Method::HEAD) {
2834 if request.mode != RequestMode::CorsMode {
2836 match request.referrer_policy {
2837 ReferrerPolicy::NoReferrer => {
2838 serialized_origin = headers::Origin::NULL;
2840 },
2841 ReferrerPolicy::NoReferrerWhenDowngrade |
2842 ReferrerPolicy::StrictOrigin |
2843 ReferrerPolicy::StrictOriginWhenCrossOrigin => {
2844 if let ImmutableOrigin::Tuple(scheme, _, _) = &request_origin &&
2847 scheme == "https" &&
2848 request.current_url().scheme() != "https"
2849 {
2850 serialized_origin = headers::Origin::NULL;
2851 }
2852 },
2853 ReferrerPolicy::SameOrigin => {
2854 if *request_origin != request.current_url().origin() {
2857 serialized_origin = headers::Origin::NULL;
2858 }
2859 },
2860 _ => {
2861 },
2863 };
2864 }
2865
2866 request.headers.typed_insert(serialized_origin);
2868 }
2869}
2870
2871fn append_the_fetch_metadata_headers(r: &mut Request) {
2873 if !r.current_url().is_potentially_trustworthy() {
2875 return;
2876 }
2877
2878 set_the_sec_fetch_dest_header(r);
2880
2881 set_the_sec_fetch_mode_header(r);
2883
2884 set_the_sec_fetch_site_header(r);
2886
2887 set_the_sec_fetch_user_header(r);
2889}
2890
2891fn append_cache_data_to_headers(http_request: &mut Request) {
2893 match http_request.cache_mode {
2894 CacheMode::Default if is_no_store_cache(&http_request.headers) => {
2898 http_request.cache_mode = CacheMode::NoStore;
2899 },
2900
2901 CacheMode::NoCache if !http_request.headers.contains_key(header::CACHE_CONTROL) => {
2911 http_request
2912 .headers
2913 .typed_insert(CacheControl::new().with_max_age(Duration::from_secs(0)));
2914 },
2915
2916 CacheMode::Reload | CacheMode::NoStore => {
2918 if !http_request.headers.contains_key(header::PRAGMA) {
2921 http_request.headers.typed_insert(Pragma::no_cache());
2922 }
2923
2924 if !http_request.headers.contains_key(header::CACHE_CONTROL) {
2927 http_request
2928 .headers
2929 .typed_insert(CacheControl::new().with_no_cache());
2930 }
2931 },
2932
2933 _ => {},
2934 }
2935}
2936
2937fn set_the_sec_fetch_dest_header(r: &mut Request) {
2939 debug_assert!(r.current_url().is_potentially_trustworthy());
2941
2942 let header = r.destination;
2946
2947 r.headers.typed_insert(SecFetchDest(header));
2949}
2950
2951fn set_the_sec_fetch_mode_header(r: &mut Request) {
2953 debug_assert!(r.current_url().is_potentially_trustworthy());
2955
2956 let header = &r.mode;
2959
2960 r.headers.typed_insert(SecFetchMode::from(header));
2962}
2963
2964fn set_the_sec_fetch_site_header(r: &mut Request) {
2966 let Origin::Origin(request_origin) = &r.origin else {
2969 panic!("request origin cannot be \"client\" at this point")
2970 };
2971
2972 debug_assert!(r.current_url().is_potentially_trustworthy());
2974
2975 let mut header = SecFetchSite::SameOrigin;
2978
2979 if header != SecFetchSite::None {
2984 for url in &r.url_list {
2985 if url.origin() == *request_origin {
2987 continue;
2988 }
2989
2990 header = SecFetchSite::CrossSite;
2992
2993 if !is_same_site(request_origin, &url.origin()) {
2995 break;
2996 }
2997
2998 header = SecFetchSite::SameSite;
3000 }
3001 }
3002
3003 r.headers.typed_insert(header);
3005}
3006
3007fn set_the_sec_fetch_user_header(r: &mut Request) {
3009 debug_assert!(r.current_url().is_potentially_trustworthy());
3011
3012 if !r.is_navigation_request() || !r.user_activation {
3014 return;
3015 }
3016
3017 let header = SecFetchUser;
3020
3021 r.headers.typed_insert(header);
3023}
3024
3025fn set_requests_referrer_policy_on_redirect(request: &mut Request, response: &Response) {
3027 let referrer_policy: ReferrerPolicy = response
3030 .headers
3031 .typed_get::<headers::ReferrerPolicy>()
3032 .into();
3033
3034 if referrer_policy != ReferrerPolicy::EmptyString {
3036 request.referrer_policy = referrer_policy;
3037 }
3038}