1use std::collections::HashSet;
6use std::iter::FromIterator;
7use std::sync::Arc as StdArc;
8use std::time::{Duration, SystemTime, UNIX_EPOCH};
9
10use async_recursion::async_recursion;
11use crossbeam_channel::Sender;
12use devtools_traits::{
13 ChromeToDevtoolsControlMsg, DevtoolsControlMsg, HttpRequest as DevtoolsHttpRequest,
14 HttpResponse as DevtoolsHttpResponse, NetworkEvent, SecurityInfoUpdate,
15};
16use embedder_traits::{AuthenticationResponse, GenericEmbedderProxy};
17use futures::{TryFutureExt, TryStreamExt, future};
18use headers::authorization::Basic;
19use headers::{
20 AccessControlAllowCredentials, AccessControlAllowHeaders, AccessControlAllowMethods,
21 AccessControlMaxAge, AccessControlRequestMethod, Authorization, CacheControl, ContentLength,
22 HeaderMapExt, IfModifiedSince, LastModified, Pragma, Referer, StrictTransportSecurity,
23 UserAgent,
24};
25use http::header::{
26 self, ACCEPT, ACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_REQUEST_HEADERS, AUTHORIZATION,
27 CONTENT_ENCODING, CONTENT_LANGUAGE, CONTENT_LOCATION, CONTENT_TYPE, HeaderValue, RANGE,
28 WWW_AUTHENTICATE,
29};
30use http::{HeaderMap, Method, Request as HyperRequest, StatusCode};
31use http_body_util::combinators::BoxBody;
32use http_body_util::{BodyExt, Full};
33use hyper::Response as HyperResponse;
34use hyper::body::{Bytes, Frame};
35use hyper::ext::ReasonPhrase;
36use hyper::header::{HeaderName, TRANSFER_ENCODING};
37use hyper_serde::Serde;
38use ipc_channel::IpcError;
39use ipc_channel::ipc::{self, IpcSender};
40use ipc_channel::router::ROUTER;
41use log::{debug, error, info, log_enabled, warn};
42use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
43use net_traits::blob_url_store::UrlWithBlobClaim;
44use net_traits::fetch::headers::get_value_from_header_list;
45use net_traits::http_status::HttpStatus;
46use net_traits::policy_container::RequestPolicyContainer;
47use net_traits::pub_domains::{is_same_site, reg_suffix};
48use net_traits::request::Origin::Origin as SpecificOrigin;
49use net_traits::request::{
50 BodyChunkRequest, BodyChunkResponse, CacheMode, CredentialsMode, Destination, Initiator,
51 Origin, RedirectMode, Referrer, Request, RequestBuilder, RequestMode, ResponseTainting,
52 ServiceWorkersMode, TraversableForUserPrompts, get_cors_unsafe_header_names,
53 is_cors_non_wildcard_request_header_name, is_cors_safelisted_method,
54 is_cors_safelisted_request_header,
55};
56use net_traits::response::{
57 CacheState, HttpsState, RedirectTaint, Response, ResponseBody, ResponseType,
58};
59use net_traits::{
60 CookieSource, DOCUMENT_ACCEPT_HEADER_VALUE, DebugVec, FetchMetadata, NetworkError,
61 RedirectEndValue, RedirectStartValue, ReferrerPolicy, ResourceAttribute, ResourceFetchTiming,
62 ResourceTimeValue, TlsSecurityInfo, TlsSecurityState,
63};
64use parking_lot::{Mutex, RwLock};
65use profile_traits::mem::{Report, ReportKind};
66use profile_traits::path;
67use rustc_hash::FxHashMap;
68use servo_arc::Arc;
69use servo_base::cross_process_instant::CrossProcessInstant;
70use servo_base::generic_channel::GenericSharedMemory;
71use servo_base::id::{BrowsingContextId, HistoryStateId, PipelineId};
72use servo_url::{ImmutableOrigin, ServoUrl};
73use tokio::sync::mpsc::{
74 Receiver as TokioReceiver, Sender as TokioSender, UnboundedReceiver, UnboundedSender, channel,
75 unbounded_channel,
76};
77use tokio_stream::wrappers::ReceiverStream;
78
79use crate::async_runtime::spawn_task;
80use crate::connector::{
81 CertificateErrorOverrideManager, ServoClient, TlsHandshakeInfo, create_tls_config,
82};
83use crate::cookie::ServoCookie;
84use crate::cookie_storage::CookieStorage;
85use crate::decoder::Decoder;
86use crate::embedder::NetToEmbedderMsg;
87use crate::fetch::cors_cache::CorsCache;
88use crate::fetch::fetch_params::FetchParams;
89use crate::fetch::headers::{SecFetchDest, SecFetchMode, SecFetchSite, SecFetchUser};
90use crate::fetch::methods::{Data, DoneChannel, FetchContext, Target, main_fetch};
91use crate::hsts::HstsList;
92use crate::http_cache::{
93 CacheKey, CachedResourcesOrGuard, HttpCache, construct_response, invalidate, refresh,
94};
95use crate::resource_thread::{AuthCache, AuthCacheEntry};
96use crate::websocket_loader::start_websocket;
97
98#[derive(Clone, Debug, Eq, PartialEq)]
100pub enum HttpCacheEntryState {
101 ReadyToConstruct,
105 PendingStore(usize),
107}
108
109pub struct HttpState {
110 pub hsts_list: RwLock<HstsList>,
111 pub cookie_jar: RwLock<CookieStorage>,
112 pub http_cache: HttpCache,
113 pub auth_cache: RwLock<AuthCache>,
114 pub history_states: RwLock<FxHashMap<HistoryStateId, Vec<u8>>>,
115 pub client: ServoClient,
116 pub override_manager: CertificateErrorOverrideManager,
117 pub embedder_proxy: GenericEmbedderProxy<NetToEmbedderMsg>,
118}
119
120impl HttpState {
121 pub(crate) fn memory_reports(&self, suffix: &str, ops: &mut MallocSizeOfOps) -> Vec<Report> {
122 vec![
123 Report {
124 path: path!["memory-cache", suffix],
125 kind: ReportKind::ExplicitJemallocHeapSize,
126 size: self.http_cache.size_of(ops),
127 },
128 Report {
129 path: path!["hsts-list", suffix],
130 kind: ReportKind::ExplicitJemallocHeapSize,
131 size: self.hsts_list.read().size_of(ops),
132 },
133 ]
134 }
135
136 async fn request_authentication(
137 &self,
138 request: &Request,
139 response: &Response,
140 ) -> Option<AuthenticationResponse> {
141 let webview_id = request.target_webview_id?;
143 let for_proxy = response.status == StatusCode::PROXY_AUTHENTICATION_REQUIRED;
144
145 if request.mode != RequestMode::Navigate {
147 return None;
148 }
149
150 let (sender, receiver) = tokio::sync::oneshot::channel();
151 self.embedder_proxy
152 .send(NetToEmbedderMsg::RequestAuthentication(
153 webview_id,
154 request.url(),
155 for_proxy,
156 sender,
157 ));
158 receiver.await.ok()?
159 }
160}
161
162pub(crate) fn set_default_accept(request: &mut Request) {
164 if request.headers.contains_key(header::ACCEPT) {
166 return;
167 }
168
169 let value = if request.initiator == Initiator::Prefetch {
171 DOCUMENT_ACCEPT_HEADER_VALUE
172 } else {
173 match request.destination {
176 Destination::Document | Destination::Frame | Destination::IFrame => {
177 DOCUMENT_ACCEPT_HEADER_VALUE
178 },
179 Destination::Image => {
180 HeaderValue::from_static("image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5")
181 },
182 Destination::Json => HeaderValue::from_static("application/json,*/*;q=0.5"),
183 Destination::Style => HeaderValue::from_static("text/css,*/*;q=0.1"),
184 _ => HeaderValue::from_static("*/*"),
186 }
187 };
188
189 request.headers.insert(header::ACCEPT, value);
191}
192
193fn set_default_accept_encoding(headers: &mut HeaderMap) {
194 if headers.contains_key(header::ACCEPT_ENCODING) {
195 return;
196 }
197
198 headers.insert(
200 header::ACCEPT_ENCODING,
201 HeaderValue::from_static("gzip, deflate, br, zstd"),
202 );
203}
204
205fn no_referrer_when_downgrade(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
207 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
209 return None;
210 }
211 strip_url_for_use_as_referrer(referrer_url, false)
213}
214
215fn strict_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
217 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
219 return None;
220 }
221 strip_url_for_use_as_referrer(referrer_url, true)
223}
224
225fn strict_origin_when_cross_origin(
227 referrer_url: ServoUrl,
228 current_url: ServoUrl,
229) -> Option<ServoUrl> {
230 if referrer_url.origin() == current_url.origin() {
232 return strip_url_for_use_as_referrer(referrer_url, false);
233 }
234 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
236 return None;
237 }
238 strip_url_for_use_as_referrer(referrer_url, true)
240}
241
242fn is_schemelessy_same_site(site_a: &ImmutableOrigin, site_b: &ImmutableOrigin) -> bool {
244 if !site_a.is_tuple() && !site_b.is_tuple() && site_a == site_b {
246 true
247 } else if site_a.is_tuple() && site_b.is_tuple() {
248 let host_a = site_a.host().map(|h| h.to_string()).unwrap_or_default();
250 let host_b = site_b.host().map(|h| h.to_string()).unwrap_or_default();
251
252 let host_a_reg = reg_suffix(&host_a);
253 let host_b_reg = reg_suffix(&host_b);
254
255 (site_a.host() == site_b.host() && host_a_reg.is_empty()) ||
257 (host_a_reg == host_b_reg && !host_a_reg.is_empty())
258 } else {
259 false
261 }
262}
263
264fn strip_url_for_use_as_referrer(mut url: ServoUrl, origin_only: bool) -> Option<ServoUrl> {
266 const MAX_REFERRER_URL_LENGTH: usize = 4096;
267 if url.is_local_scheme() {
269 return None;
270 }
271 {
273 let url = url.as_mut_url();
274 let _ = url.set_username("");
275 let _ = url.set_password(None);
276 url.set_fragment(None);
277 if origin_only || url.as_str().len() > MAX_REFERRER_URL_LENGTH {
281 url.set_path("");
282 url.set_query(None);
283 }
284 }
285 Some(url)
287}
288
289fn same_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
291 if referrer_url.origin() == current_url.origin() {
293 return strip_url_for_use_as_referrer(referrer_url, false);
294 }
295 None
297}
298
299fn origin_when_cross_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
301 if referrer_url.origin() == current_url.origin() {
303 return strip_url_for_use_as_referrer(referrer_url, false);
304 }
305 strip_url_for_use_as_referrer(referrer_url, true)
307}
308
309pub fn determine_requests_referrer(
311 referrer_policy: ReferrerPolicy,
312 referrer_source: ServoUrl,
313 current_url: ServoUrl,
314) -> Option<ServoUrl> {
315 match referrer_policy {
316 ReferrerPolicy::EmptyString | ReferrerPolicy::NoReferrer => None,
317 ReferrerPolicy::Origin => strip_url_for_use_as_referrer(referrer_source, true),
318 ReferrerPolicy::UnsafeUrl => strip_url_for_use_as_referrer(referrer_source, false),
319 ReferrerPolicy::StrictOrigin => strict_origin(referrer_source, current_url),
320 ReferrerPolicy::StrictOriginWhenCrossOrigin => {
321 strict_origin_when_cross_origin(referrer_source, current_url)
322 },
323 ReferrerPolicy::SameOrigin => same_origin(referrer_source, current_url),
324 ReferrerPolicy::OriginWhenCrossOrigin => {
325 origin_when_cross_origin(referrer_source, current_url)
326 },
327 ReferrerPolicy::NoReferrerWhenDowngrade => {
328 no_referrer_when_downgrade(referrer_source, current_url)
329 },
330 }
331}
332
333fn set_request_cookies(
334 url: &ServoUrl,
335 headers: &mut HeaderMap,
336 cookie_jar: &RwLock<CookieStorage>,
337) {
338 let mut cookie_jar = cookie_jar.write();
339 cookie_jar.remove_expired_cookies_for_url(url);
340 if let Some(cookie_list) = cookie_jar.cookies_for_url(url, CookieSource::HTTP) {
341 headers.insert(
342 header::COOKIE,
343 HeaderValue::from_bytes(cookie_list.as_bytes()).unwrap(),
344 );
345 }
346}
347
348fn set_cookie_for_url(cookie_jar: &RwLock<CookieStorage>, request: &ServoUrl, cookie_val: &str) {
349 let mut cookie_jar = cookie_jar.write();
350 let source = CookieSource::HTTP;
351
352 if let Some(cookie) = ServoCookie::from_cookie_string(cookie_val, request, source) {
353 cookie_jar.push(cookie, request, source);
354 }
355}
356
357fn set_cookies_from_headers(
358 url: &ServoUrl,
359 headers: &HeaderMap,
360 cookie_jar: &RwLock<CookieStorage>,
361) {
362 for cookie in headers.get_all(header::SET_COOKIE) {
363 let cookie_bytes = cookie.as_bytes();
364 if !ServoCookie::is_valid_name_or_value(cookie_bytes) {
365 continue;
366 }
367 if let Ok(cookie_str) = std::str::from_utf8(cookie_bytes) {
368 set_cookie_for_url(cookie_jar, url, cookie_str);
369 }
370 }
371}
372
373fn build_tls_security_info(handshake: &TlsHandshakeInfo, hsts_enabled: bool) -> TlsSecurityInfo {
374 let state = if handshake.protocol_version.is_none() || handshake.cipher_suite.is_none() {
382 TlsSecurityState::Insecure
384 } else {
385 TlsSecurityState::Secure
387 };
388
389 TlsSecurityInfo {
390 state,
391 weakness_reasons: Vec::new(), protocol_version: handshake.protocol_version.clone(),
393 cipher_suite: handshake.cipher_suite.clone(),
394 kea_group_name: handshake.kea_group_name.clone(),
395 signature_scheme_name: handshake.signature_scheme_name.clone(),
396 alpn_protocol: handshake.alpn_protocol.clone(),
397 certificate_chain_der: handshake.certificate_chain_der.clone(),
398 certificate_transparency: None,
399 hsts: hsts_enabled,
400 hpkp: false,
401 used_ech: handshake.used_ech,
402 used_delegated_credentials: false,
403 used_ocsp: false,
404 used_private_dns: false,
405 }
406}
407
408#[allow(clippy::too_many_arguments)]
409fn prepare_devtools_request(
410 request_id: String,
411 url: ServoUrl,
412 method: Method,
413 headers: HeaderMap,
414 body: Option<Vec<u8>>,
415 pipeline_id: PipelineId,
416 connect_time: Duration,
417 send_time: Duration,
418 destination: Destination,
419 is_xhr: bool,
420 browsing_context_id: BrowsingContextId,
421) -> ChromeToDevtoolsControlMsg {
422 let started_date_time = SystemTime::now();
423 let request = DevtoolsHttpRequest {
424 url,
425 method,
426 headers,
427 body: body.map(DebugVec::from),
428 pipeline_id,
429 started_date_time,
430 time_stamp: started_date_time
431 .duration_since(UNIX_EPOCH)
432 .unwrap_or_default()
433 .as_secs() as i64,
434 connect_time,
435 send_time,
436 destination,
437 is_xhr,
438 browsing_context_id,
439 };
440 let net_event = NetworkEvent::HttpRequestUpdate(request);
441
442 ChromeToDevtoolsControlMsg::NetworkEvent(request_id, net_event)
443}
444
445pub fn send_request_to_devtools(
446 msg: ChromeToDevtoolsControlMsg,
447 devtools_chan: &Sender<DevtoolsControlMsg>,
448) {
449 if matches!(msg, ChromeToDevtoolsControlMsg::NetworkEvent(_, ref network_event) if !network_event.forward_to_devtools())
450 {
451 return;
452 }
453 if let Err(e) = devtools_chan.send(DevtoolsControlMsg::FromChrome(msg)) {
454 error!("DevTools send failed: {e}");
455 }
456}
457
458pub fn send_response_to_devtools(
459 request: &Request,
460 context: &FetchContext,
461 response: &Response,
462 body_data: Option<Vec<u8>>,
463) {
464 let meta = match response.metadata() {
465 Ok(FetchMetadata::Unfiltered(m)) => m,
466 Ok(FetchMetadata::Filtered { unsafe_, .. }) => unsafe_,
467 Err(_) => {
468 log::warn!("No metadata available, skipping devtools response.");
469 return;
470 },
471 };
472 send_response_values_to_devtools(
473 meta.headers.map(Serde::into_inner),
474 meta.status,
475 body_data,
476 response.cache_state,
477 request,
478 context.devtools_chan.clone(),
479 );
480}
481
482#[allow(clippy::too_many_arguments)]
483pub fn send_response_values_to_devtools(
484 headers: Option<HeaderMap>,
485 status: HttpStatus,
486 body: Option<Vec<u8>>,
487 cache_state: CacheState,
488 request: &Request,
489 devtools_chan: Option<Sender<DevtoolsControlMsg>>,
490) {
491 if let (Some(devtools_chan), Some(pipeline_id), Some(webview_id)) = (
492 devtools_chan,
493 request.pipeline_id,
494 request.target_webview_id,
495 ) {
496 let browsing_context_id = webview_id.into();
497 let from_cache = matches!(cache_state, CacheState::Local | CacheState::Validated);
498
499 let devtoolsresponse = DevtoolsHttpResponse {
500 headers,
501 status,
502 body: body.map(DebugVec::from),
503 from_cache,
504 pipeline_id,
505 browsing_context_id,
506 };
507 let net_event_response = NetworkEvent::HttpResponse(devtoolsresponse);
508
509 let msg =
510 ChromeToDevtoolsControlMsg::NetworkEvent(request.id.0.to_string(), net_event_response);
511
512 let _ = devtools_chan.send(DevtoolsControlMsg::FromChrome(msg));
513 }
514}
515
516pub fn send_security_info_to_devtools(
517 request: &Request,
518 context: &FetchContext,
519 response: &Response,
520) {
521 let meta = match response.metadata() {
522 Ok(FetchMetadata::Unfiltered(m)) => m,
523 Ok(FetchMetadata::Filtered { unsafe_, .. }) => unsafe_,
524 Err(_) => {
525 log::warn!("No metadata available, skipping devtools security info.");
526 return;
527 },
528 };
529
530 if let (Some(devtools_chan), Some(security_info), Some(webview_id)) = (
531 context.devtools_chan.clone(),
532 meta.tls_security_info,
533 request.target_webview_id,
534 ) {
535 let update = NetworkEvent::SecurityInfo(SecurityInfoUpdate {
536 browsing_context_id: webview_id.into(),
537 security_info: Some(security_info),
538 });
539
540 let msg = ChromeToDevtoolsControlMsg::NetworkEvent(request.id.0.to_string(), update);
541
542 let _ = devtools_chan.send(DevtoolsControlMsg::FromChrome(msg));
543 }
544}
545
546pub fn send_early_httprequest_to_devtools(request: &Request, context: &FetchContext) {
547 if request.url().scheme() == "data" {
549 return;
550 }
551 if let (Some(devtools_chan), Some(browsing_context_id), Some(pipeline_id)) = (
552 context.devtools_chan.as_ref(),
553 request.target_webview_id.map(|id| id.into()),
554 request.pipeline_id,
555 ) {
556 let devtools_request = DevtoolsHttpRequest {
558 url: request.current_url(),
559 method: request.method.clone(),
560 headers: request.headers.clone(),
561 body: None,
562 pipeline_id,
563 started_date_time: SystemTime::now(),
564 time_stamp: 0,
565 connect_time: Duration::from_millis(0),
566 send_time: Duration::from_millis(0),
567 destination: request.destination,
568 is_xhr: false,
569 browsing_context_id,
570 };
571
572 let msg = ChromeToDevtoolsControlMsg::NetworkEvent(
573 request.id.0.to_string(),
574 NetworkEvent::HttpRequest(devtools_request),
575 );
576
577 send_request_to_devtools(msg, devtools_chan);
578 }
579}
580
581fn auth_from_cache(
582 auth_cache: &RwLock<AuthCache>,
583 origin: &ImmutableOrigin,
584) -> Option<Authorization<Basic>> {
585 if let Some(auth_entry) = auth_cache.read().entries.get(&origin.ascii_serialization()) {
586 let user_name = &auth_entry.user_name;
587 let password = &auth_entry.password;
588 Some(Authorization::basic(user_name, password))
589 } else {
590 None
591 }
592}
593
594enum BodyChunk {
597 Chunk(GenericSharedMemory),
599 Done,
601}
602
603enum BodyStream {
605 Chunked(TokioReceiver<Result<Frame<Bytes>, hyper::Error>>),
608 Buffered(UnboundedReceiver<BodyChunk>),
611}
612
613enum BodySink {
616 Chunked(TokioSender<Result<Frame<Bytes>, hyper::Error>>),
618 Buffered(UnboundedSender<BodyChunk>),
622}
623
624impl BodySink {
625 fn transmit_bytes(&self, bytes: GenericSharedMemory) {
626 match self {
627 BodySink::Chunked(sender) => {
628 let sender = sender.clone();
629 spawn_task(async move {
630 let _ = sender
631 .send(Ok(Frame::data(Bytes::copy_from_slice(&bytes))))
632 .await;
633 });
634 },
635 BodySink::Buffered(sender) => {
636 let _ = sender.send(BodyChunk::Chunk(bytes));
637 },
638 }
639 }
640
641 fn close(&self) {
642 match self {
643 BodySink::Chunked(_) => { },
644 BodySink::Buffered(sender) => {
645 let _ = sender.send(BodyChunk::Done);
646 },
647 }
648 }
649}
650
651fn request_body_stream_closed_error(action: &str) -> NetworkError {
652 NetworkError::Crash(format!(
653 "Request body stream has already been closed while trying to {action}."
654 ))
655}
656
657fn log_request_body_stream_closed(action: &str, error: Option<&IpcError>) {
658 match error {
659 Some(error) => {
660 error!("Request body stream has already been closed while trying to {action}: {error}")
661 },
662 None => error!("Request body stream has already been closed while trying to {action}."),
663 }
664}
665
666fn log_fetch_terminated_send_failure(terminated_with_error: bool, context: &str) {
667 warn!(
668 "Failed to notify request-body stream termination state ({terminated_with_error}) while {context} because the receiver was already dropped."
669 );
670}
671
672#[allow(clippy::too_many_arguments)]
673async fn obtain_response(
674 client: &ServoClient,
675 url: &ServoUrl,
676 method: &Method,
677 request_headers: &mut HeaderMap,
678 body: Option<StdArc<Mutex<Option<IpcSender<BodyChunkRequest>>>>>,
679 source_is_null: bool,
680 pipeline_id: &Option<PipelineId>,
681 request_id: Option<&str>,
682 destination: Destination,
683 is_xhr: bool,
684 context: &FetchContext,
685 fetch_terminated: UnboundedSender<bool>,
686 browsing_context_id: Option<BrowsingContextId>,
687) -> Result<(HyperResponse<Decoder>, Option<ChromeToDevtoolsControlMsg>), NetworkError> {
688 {
689 let mut headers = request_headers.clone();
690
691 let devtools_bytes = StdArc::new(Mutex::new(vec![]));
692
693 let encoded_url = url
695 .clone()
696 .into_url()
697 .as_ref()
698 .replace('|', "%7C")
699 .replace('{', "%7B")
700 .replace('}', "%7D");
701
702 let request = if let Some(chunk_requester) = body {
703 let (sink, stream) = if source_is_null {
704 headers.insert(TRANSFER_ENCODING, HeaderValue::from_static("chunked"));
707
708 let (sender, receiver) = channel(1);
709 (BodySink::Chunked(sender), BodyStream::Chunked(receiver))
710 } else {
711 let (sender, receiver) = unbounded_channel();
718 (BodySink::Buffered(sender), BodyStream::Buffered(receiver))
719 };
720
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(
728 "connect to the request body stream",
729 Some(&error),
730 );
731 return Err(request_body_stream_closed_error(
732 "connect to the request body stream",
733 ));
734 }
735
736 if let Err(error) = chunk_requester.send(BodyChunkRequest::Chunk) {
739 log_request_body_stream_closed(
740 "request the first request body chunk",
741 Some(&error),
742 );
743 return Err(request_body_stream_closed_error(
744 "request the first request body chunk",
745 ));
746 }
747 } else {
748 log_request_body_stream_closed("connect to the request body stream", None);
749 return Err(request_body_stream_closed_error(
750 "connect to the request body stream",
751 ));
752 }
753 }
754
755 let devtools_bytes = devtools_bytes.clone();
756 let chunk_requester2 = chunk_requester.clone();
757
758 ROUTER.add_typed_route(
759 body_port,
760 Box::new(move |message| {
761 info!("Received message");
762 let bytes = match message.unwrap() {
763 BodyChunkResponse::Chunk(bytes) => bytes,
764 BodyChunkResponse::Done => {
765 if fetch_terminated.send(false).is_err() {
767 log_fetch_terminated_send_failure(
768 false,
769 "handling request body completion",
770 );
771 }
772 sink.close();
773
774 return;
775 },
776 BodyChunkResponse::Error => {
777 if fetch_terminated.send(true).is_err() {
781 log_fetch_terminated_send_failure(
782 true,
783 "handling request body stream error",
784 );
785 }
786 sink.close();
787
788 return;
789 },
790 };
791
792 devtools_bytes.lock().extend_from_slice(&bytes);
793
794 sink.transmit_bytes(bytes);
797
798 let mut chunk_requester2 = chunk_requester2.lock();
801 if let Some(chunk_requester2) = chunk_requester2.as_mut() {
802 if let Err(error) = chunk_requester2.send(BodyChunkRequest::Chunk) {
803 log_request_body_stream_closed(
804 "request the next request body chunk",
805 Some(&error),
806 );
807 if fetch_terminated.send(true).is_err() {
808 log_fetch_terminated_send_failure(
809 true,
810 "handling failure to request the next request body chunk",
811 );
812 }
813 sink.close();
814 }
815 } else {
816 log_request_body_stream_closed("request the next request body chunk", None);
817 if fetch_terminated.send(true).is_err() {
818 log_fetch_terminated_send_failure(
819 true,
820 "handling a closed request body stream while requesting the next chunk",
821 );
822 }
823 sink.close();
824 }
825 }),
826 );
827
828 let body = match stream {
829 BodyStream::Chunked(receiver) => {
830 let stream = ReceiverStream::new(receiver);
831 BoxBody::new(http_body_util::StreamBody::new(stream))
832 },
833 BodyStream::Buffered(mut receiver) => {
834 let mut body = vec![];
836 loop {
837 match receiver.recv().await {
838 Some(BodyChunk::Chunk(bytes)) => {
839 body.extend_from_slice(&bytes);
840 },
841 Some(BodyChunk::Done) => break,
842 None => warn!("Failed to read all chunks from request body."),
843 }
844 }
845 Full::new(body.into()).map_err(|_| unreachable!()).boxed()
846 },
847 };
848 HyperRequest::builder()
849 .method(method)
850 .uri(encoded_url)
851 .body(body)
852 } else {
853 HyperRequest::builder()
854 .method(method)
855 .uri(encoded_url)
856 .body(
857 http_body_util::Empty::new()
858 .map_err(|_| unreachable!())
859 .boxed(),
860 )
861 };
862
863 context
864 .timing
865 .lock()
866 .set_attribute(ResourceAttribute::DomainLookupStart);
867
868 let connect_start = CrossProcessInstant::now();
871 context
872 .timing
873 .lock()
874 .set_attribute(ResourceAttribute::ConnectStart(connect_start));
875
876 if url.scheme() == "https" {
880 context
881 .timing
882 .lock()
883 .set_attribute(ResourceAttribute::SecureConnectionStart);
884 }
885
886 let mut request = match request {
887 Ok(request) => request,
888 Err(error) => return Err(NetworkError::HttpError(error.to_string())),
889 };
890 *request.headers_mut() = headers.clone();
891
892 let connect_end = CrossProcessInstant::now();
893 context
894 .timing
895 .lock()
896 .set_attribute(ResourceAttribute::ConnectEnd(connect_end));
897
898 let request_id = request_id.map(|v| v.to_owned());
899 let pipeline_id = *pipeline_id;
900 let closure_url = url.clone();
901 let method = method.clone();
902 let send_start = CrossProcessInstant::now();
903
904 let host = request.uri().host().unwrap_or("").to_owned();
905 let override_manager = context.state.override_manager.clone();
906 let headers = headers.clone();
907 let is_secure_scheme = url.is_secure_scheme();
908
909 client
910 .request(request)
911 .and_then(move |res| {
912 let send_end = CrossProcessInstant::now();
913
914 let msg = if let Some(request_id) = request_id {
917 if let Some(pipeline_id) = pipeline_id {
918 if let Some(browsing_context_id) = browsing_context_id {
919 Some(prepare_devtools_request(
920 request_id,
921 closure_url,
922 method.clone(),
923 headers,
924 Some(devtools_bytes.lock().clone()),
925 pipeline_id,
926 (connect_end - connect_start).unsigned_abs(),
927 (send_end - send_start).unsigned_abs(),
928 destination,
929 is_xhr,
930 browsing_context_id,
931 ))
932 } else {
933 debug!("Not notifying devtools (no browsing_context_id)");
934 None
935 }
936 } else {
941 debug!("Not notifying devtools (no pipeline_id)");
942 None
943 }
944 } else {
945 debug!("Not notifying devtools (no request_id)");
946 None
947 };
948
949 future::ready(Ok((
950 Decoder::detect(res.map(|r| r.boxed()), is_secure_scheme),
951 msg,
952 )))
953 })
954 .map_err(move |error| {
955 warn!("network error: {error:?}");
956 NetworkError::from_hyper_error(
957 &error,
958 override_manager.remove_certificate_failing_verification(host.as_str()),
959 )
960 })
961 .await
962 }
963}
964
965#[async_recursion]
967#[allow(clippy::too_many_arguments)]
968pub async fn http_fetch(
969 fetch_params: &mut FetchParams,
970 cache: &mut CorsCache,
971 cors_flag: bool,
972 cors_preflight_flag: bool,
973 authentication_fetch_flag: bool,
974 target: Target<'async_recursion>,
975 done_chan: &mut DoneChannel,
976 context: &FetchContext,
977) -> Response {
978 *done_chan = None;
980 let request = &mut fetch_params.request;
982
983 let mut response: Option<Response> = None;
985
986 if request.service_workers_mode == ServiceWorkersMode::All {
988 if let Some(ref res) = response {
993 if (res.response_type == ResponseType::Opaque && request.mode != RequestMode::NoCors) ||
1001 (res.response_type == ResponseType::OpaqueRedirect &&
1002 request.redirect_mode != RedirectMode::Manual) ||
1003 (res.url_list.len() > 1 && request.redirect_mode != RedirectMode::Follow) ||
1004 res.is_network_error()
1005 {
1006 return Response::network_error(NetworkError::ConnectionFailure);
1007 }
1008
1009 }
1012 }
1013
1014 if response.is_none() {
1016 if cors_preflight_flag {
1018 let method_cache_match = cache.match_method(request, request.method.clone());
1019
1020 let method_mismatch = !method_cache_match &&
1021 (!is_cors_safelisted_method(&request.method) || request.use_cors_preflight);
1022 let header_mismatch = request.headers.iter().any(|(name, value)| {
1023 !cache.match_header(request, name) &&
1024 !is_cors_safelisted_request_header(&name, &value)
1025 });
1026
1027 if method_mismatch || header_mismatch {
1028 let preflight_response = cors_preflight_fetch(request, cache, context).await;
1031 if let Some(error) = preflight_response.get_network_error() {
1033 return Response::network_error(error.clone());
1034 }
1035 }
1036 }
1037
1038 if request.redirect_mode == RedirectMode::Follow {
1041 request.service_workers_mode = ServiceWorkersMode::None;
1042 }
1043
1044 context
1048 .timing
1049 .lock()
1050 .set_attribute(ResourceAttribute::RequestStart);
1051
1052 let mut fetch_result = http_network_or_cache_fetch(
1055 fetch_params,
1056 authentication_fetch_flag,
1057 cors_flag,
1058 done_chan,
1059 context,
1060 )
1061 .await;
1062
1063 if cors_flag && cors_check(&fetch_params.request, &fetch_result).is_err() {
1066 return Response::network_error(NetworkError::CorsGeneral);
1067 }
1068
1069 fetch_result.return_internal = false;
1072 response = Some(fetch_result);
1073 }
1074
1075 let request = &mut fetch_params.request;
1076
1077 let mut response = response.unwrap();
1079
1080 if (request.response_tainting == ResponseTainting::Opaque ||
1084 response.response_type == ResponseType::Opaque) &&
1085 cross_origin_resource_policy_check(request, &response) ==
1086 CrossOriginResourcePolicy::Blocked
1087 {
1088 return Response::network_error(NetworkError::CrossOriginResponse);
1089 }
1090
1091 if response
1093 .actual_response()
1094 .status
1095 .try_code()
1096 .is_some_and(is_redirect_status)
1097 {
1098 if response.actual_response().status != StatusCode::SEE_OTHER {
1102 }
1104
1105 response = match request.redirect_mode {
1107 RedirectMode::Error => Response::network_error(NetworkError::RedirectError),
1109 RedirectMode::Manual => {
1110 if request.mode == RequestMode::Navigate {
1113 let location_url =
1117 location_url_for_response(&response, request.current_url().fragment());
1118 response.actual_response_mut().location_url = location_url;
1119 response
1120 } else {
1121 response.to_filtered(ResponseType::OpaqueRedirect)
1123 }
1124 },
1125 RedirectMode::Follow => {
1126 response.return_internal = true;
1128 http_redirect_fetch(
1129 fetch_params,
1130 cache,
1131 response,
1132 cors_flag,
1133 target,
1134 done_chan,
1135 context,
1136 )
1137 .await
1138 },
1139 };
1140 }
1141
1142 response.return_internal = true;
1144 context
1145 .timing
1146 .lock()
1147 .set_attribute(ResourceAttribute::RedirectCount(
1148 fetch_params.request.redirect_count as u16,
1149 ));
1150
1151 response.resource_timing = Arc::clone(&context.timing);
1152
1153 response
1155}
1156
1157struct RedirectEndTimer(Option<Arc<Mutex<ResourceFetchTiming>>>);
1159
1160impl RedirectEndTimer {
1161 fn neuter(&mut self) {
1162 self.0 = None;
1163 }
1164}
1165
1166impl Drop for RedirectEndTimer {
1167 fn drop(&mut self) {
1168 let RedirectEndTimer(resource_fetch_timing_opt) = self;
1169
1170 resource_fetch_timing_opt.as_ref().map_or((), |t| {
1171 t.lock()
1172 .set_attribute(ResourceAttribute::RedirectEnd(RedirectEndValue::Zero));
1173 })
1174 }
1175}
1176
1177static REQUEST_BODY_HEADER_NAMES: &[HeaderName] = &[
1179 CONTENT_ENCODING,
1180 CONTENT_LANGUAGE,
1181 CONTENT_LOCATION,
1182 CONTENT_TYPE,
1183];
1184
1185fn location_url_for_response(
1187 response: &Response,
1188 request_fragment: Option<&str>,
1189) -> Option<Result<ServoUrl, String>> {
1190 assert!(
1192 response
1193 .actual_response()
1194 .status
1195 .try_code()
1196 .is_some_and(is_redirect_status)
1197 );
1198 let mut location = response
1200 .actual_response()
1201 .headers
1202 .get(header::LOCATION)
1203 .and_then(|header_value| {
1204 HeaderValue::to_str(header_value)
1205 .map(|location_string| {
1206 ServoUrl::parse_with_base(response.actual_response().url(), location_string)
1208 .map_err(|error| error.to_string())
1209 })
1210 .ok()
1211 });
1212
1213 if let Some(Ok(ref mut location)) = location {
1215 if location.fragment().is_none() {
1216 location.set_fragment(request_fragment);
1217 }
1218 }
1219 location
1221}
1222
1223#[async_recursion]
1225pub async fn http_redirect_fetch(
1226 fetch_params: &mut FetchParams,
1227 cache: &mut CorsCache,
1228 mut response: Response,
1229 cors_flag: bool,
1230 target: Target<'async_recursion>,
1231 done_chan: &mut DoneChannel,
1232 context: &FetchContext,
1233) -> Response {
1234 let mut redirect_end_timer = RedirectEndTimer(Some(context.timing.clone()));
1235
1236 let request = &mut fetch_params.request;
1238
1239 assert!(response.return_internal);
1241
1242 let location_url = location_url_for_response(&response, request.current_url().fragment());
1244 response.actual_response_mut().location_url = location_url.clone();
1245
1246 let location_url = match location_url {
1247 None => return response,
1249 Some(Err(err)) => {
1251 return Response::network_error(NetworkError::ResourceLoadError(
1252 "Location URL parse failure: ".to_owned() + &err,
1253 ));
1254 },
1255 Some(Ok(url)) if !matches!(url.scheme(), "http" | "https") => {
1257 return Response::network_error(NetworkError::UnsupportedScheme);
1258 },
1259 Some(Ok(url)) => url,
1260 };
1261
1262 context
1265 .timing
1266 .lock()
1267 .set_attribute(ResourceAttribute::RedirectStart(
1268 RedirectStartValue::FetchStart,
1269 ));
1270
1271 context
1272 .timing
1273 .lock()
1274 .set_attribute(ResourceAttribute::FetchStart);
1275
1276 context
1278 .timing
1279 .lock()
1280 .set_attribute(ResourceAttribute::StartTime(ResourceTimeValue::FetchStart));
1281
1282 context
1283 .timing
1284 .lock()
1285 .set_attribute(ResourceAttribute::StartTime(
1286 ResourceTimeValue::RedirectStart,
1287 )); if request.redirect_count >= 20 {
1291 return Response::network_error(NetworkError::TooManyRedirects);
1292 }
1293
1294 request.redirect_count += 1;
1296
1297 let same_origin = match request.origin {
1300 Origin::Origin(ref origin) => *origin == location_url.origin(),
1301 Origin::Client => panic!(
1302 "Request origin should not be client for {}",
1303 request.current_url()
1304 ),
1305 };
1306
1307 let has_credentials = has_credentials(&location_url);
1308
1309 if request.mode == RequestMode::CorsMode && !same_origin && has_credentials {
1310 return Response::network_error(NetworkError::CorsCredentials);
1311 }
1312
1313 if cors_flag && location_url.origin() != request.current_url().origin() {
1314 request.origin = Origin::Origin(ImmutableOrigin::new_opaque());
1315 }
1316
1317 if cors_flag && has_credentials {
1319 return Response::network_error(NetworkError::CorsCredentials);
1320 }
1321
1322 if response.actual_response().status != StatusCode::SEE_OTHER &&
1325 request.body.as_ref().is_some_and(|b| b.source_is_null())
1326 {
1327 return Response::network_error(NetworkError::ConnectionFailure);
1328 }
1329
1330 if response
1332 .actual_response()
1333 .status
1334 .try_code()
1335 .is_some_and(|code| {
1336 ((code == StatusCode::MOVED_PERMANENTLY || code == StatusCode::FOUND) &&
1338 request.method == Method::POST) ||
1339 (code == StatusCode::SEE_OTHER &&
1341 request.method != Method::HEAD &&
1342 request.method != Method::GET)
1343 })
1344 {
1345 request.method = Method::GET;
1347 request.body = None;
1348 for name in REQUEST_BODY_HEADER_NAMES {
1350 request.headers.remove(name);
1351 }
1352 }
1353
1354 if location_url.origin() != request.current_url().origin() {
1358 request.headers.remove(AUTHORIZATION);
1361 }
1362
1363 if let Some(body) = request.body.as_mut() {
1366 body.extract_source();
1367 }
1368
1369 request
1373 .url_list
1374 .push(UrlWithBlobClaim::from_url_without_having_claimed_blob(
1375 location_url,
1376 ));
1377
1378 set_requests_referrer_policy_on_redirect(request, response.actual_response());
1380
1381 let recursive_flag = request.redirect_mode != RedirectMode::Manual;
1384
1385 let fetch_response = main_fetch(
1387 fetch_params,
1388 cache,
1389 recursive_flag,
1390 target,
1391 done_chan,
1392 context,
1393 )
1394 .await;
1395
1396 context
1398 .timing
1399 .lock()
1400 .set_attribute(ResourceAttribute::RedirectEnd(
1401 RedirectEndValue::ResponseEnd,
1402 ));
1403 redirect_end_timer.neuter();
1404
1405 fetch_response
1406}
1407
1408#[async_recursion]
1410async fn http_network_or_cache_fetch(
1411 fetch_params: &mut FetchParams,
1412 authentication_fetch_flag: bool,
1413 cors_flag: bool,
1414 done_chan: &mut DoneChannel,
1415 context: &FetchContext,
1416) -> Response {
1417 let http_fetch_params: &mut FetchParams;
1419 let mut fetch_params_copy: FetchParams;
1420
1421 let mut response: Option<Response> = None;
1425
1426 let mut revalidating_flag = false;
1428
1429 let http_request = if fetch_params.request.traversable_for_user_prompts ==
1433 TraversableForUserPrompts::NoTraversable &&
1434 fetch_params.request.redirect_mode == RedirectMode::Error
1435 {
1436 http_fetch_params = fetch_params;
1437 &mut http_fetch_params.request
1438 }
1439 else {
1441 fetch_params_copy =
1444 std::mem::replace(fetch_params, FetchParams::new(fetch_params.request.clone()));
1445 http_fetch_params = &mut fetch_params_copy;
1446
1447 &mut http_fetch_params.request
1448 };
1449
1450 let include_credentials = match http_request.credentials_mode {
1452 CredentialsMode::Include => true,
1454 CredentialsMode::CredentialsSameOrigin
1456 if http_request.response_tainting == ResponseTainting::Basic =>
1457 {
1458 true
1459 },
1460 _ => false,
1461 };
1462
1463 let content_length = http_request
1470 .body
1471 .as_ref()
1472 .and_then(|body| body.len().map(|size| size as u64));
1473
1474 let mut content_length_header_value = None;
1476
1477 if http_request.body.is_none() && matches!(http_request.method, Method::POST | Method::PUT) {
1480 content_length_header_value = Some(0);
1481 }
1482
1483 if let Some(content_length) = content_length {
1487 content_length_header_value = Some(content_length);
1488 };
1489
1490 if let Some(content_length_header_value) = content_length_header_value {
1493 http_request
1494 .headers
1495 .typed_insert(ContentLength(content_length_header_value));
1496 }
1497
1498 if http_request.keep_alive {
1500 if let Some(content_length) = content_length {
1501 let in_flight_keep_alive_bytes: u64 = context
1506 .in_flight_keep_alive_records
1507 .lock()
1508 .get(
1509 &http_request
1510 .pipeline_id
1511 .expect("Must always set a pipeline ID for keep-alive requests"),
1512 )
1513 .map(|records| {
1514 records
1518 .iter()
1519 .map(|record| {
1520 if record.request_id == http_request.id {
1521 0
1524 } else {
1525 record.keep_alive_body_length
1526 }
1527 })
1528 .sum()
1529 })
1530 .unwrap_or_default();
1531 if content_length + in_flight_keep_alive_bytes > 64 * 1024 {
1533 return Response::network_error(NetworkError::TooManyInFlightKeepAliveRequests);
1534 }
1535 }
1536 }
1537
1538 match http_request.referrer {
1540 Referrer::ReferrerUrl(ref http_request_referrer) |
1541 Referrer::Client(ref http_request_referrer) => {
1542 if let Ok(referer) = http_request_referrer.as_str().parse::<Referer>() {
1545 http_request.headers.typed_insert(referer);
1547 } else {
1548 error!("Failed to parse {} as referrer", http_request_referrer);
1552 }
1553 },
1554 _ => {},
1555 };
1556
1557 append_a_request_origin_header(http_request);
1559
1560 append_the_fetch_metadata_headers(http_request);
1562
1563 if http_request.initiator == Initiator::Prefetch {
1566 if let Ok(value) = HeaderValue::from_str("prefetch") {
1567 http_request.headers.insert("Sec-Purpose", value);
1568 }
1569 }
1570
1571 if !http_request.headers.contains_key(header::USER_AGENT) {
1574 http_request
1575 .headers
1576 .typed_insert::<UserAgent>(context.user_agent.parse().unwrap());
1577 }
1578
1579 match http_request.cache_mode {
1581 CacheMode::Default if is_no_store_cache(&http_request.headers) => {
1585 http_request.cache_mode = CacheMode::NoStore;
1586 },
1587
1588 CacheMode::NoCache if !http_request.headers.contains_key(header::CACHE_CONTROL) => {
1598 http_request
1599 .headers
1600 .typed_insert(CacheControl::new().with_max_age(Duration::from_secs(0)));
1601 },
1602
1603 CacheMode::Reload | CacheMode::NoStore => {
1605 if !http_request.headers.contains_key(header::PRAGMA) {
1608 http_request.headers.typed_insert(Pragma::no_cache());
1609 }
1610
1611 if !http_request.headers.contains_key(header::CACHE_CONTROL) {
1614 http_request
1615 .headers
1616 .typed_insert(CacheControl::new().with_no_cache());
1617 }
1618 },
1619
1620 _ => {},
1621 }
1622
1623 if http_request.headers.contains_key(header::RANGE) {
1626 if let Ok(value) = HeaderValue::from_str("identity") {
1627 http_request.headers.insert("Accept-Encoding", value);
1628 }
1629 }
1630
1631 http_request.headers.remove(header::HOST);
1635 set_default_accept_encoding(&mut http_request.headers);
1637
1638 let current_url = http_request.current_url();
1639
1640 if include_credentials {
1643 set_request_cookies(
1647 ¤t_url,
1648 &mut http_request.headers,
1649 &context.state.cookie_jar,
1650 );
1651 if !http_request.headers.contains_key(header::AUTHORIZATION) {
1653 let mut authorization_value = None;
1655
1656 if let Some(basic) = auth_from_cache(&context.state.auth_cache, ¤t_url.origin()) {
1658 if !http_request.use_url_credentials || !has_credentials(¤t_url) {
1659 authorization_value = Some(basic);
1660 }
1661 }
1662
1663 if authentication_fetch_flag &&
1665 authorization_value.is_none() &&
1666 has_credentials(¤t_url)
1667 {
1668 authorization_value = Some(Authorization::basic(
1669 current_url.username(),
1670 current_url.password().unwrap_or(""),
1671 ));
1672 }
1673
1674 if let Some(basic) = authorization_value {
1676 http_request.headers.typed_insert(basic);
1677 }
1678 }
1679 }
1680
1681 let should_wait = {
1683 let mut cache_guard = block_for_cache_ready(
1685 context,
1686 http_request,
1687 done_chan,
1688 &mut revalidating_flag,
1689 &mut response,
1690 )
1691 .await;
1692
1693 if response.is_none() {
1697 if http_request.cache_mode == CacheMode::OnlyIfCached {
1699 return Response::network_error(NetworkError::CacheError);
1701 }
1702
1703 drop(cache_guard);
1706 let forward_response =
1707 http_network_fetch(http_fetch_params, include_credentials, done_chan, context)
1708 .await;
1709
1710 let http_request = &mut http_fetch_params.request;
1711 let request_key = CacheKey::new(http_request);
1712 cache_guard = context
1713 .state
1714 .http_cache
1715 .get_or_guard(request_key.clone())
1716 .await;
1717 if forward_response.status.in_range(200..=399) && !http_request.method.is_safe() {
1721 if let Some(guard) = cache_guard.try_as_mut() {
1722 invalidate(http_request, &forward_response, guard).await;
1723 }
1724 context
1725 .state
1726 .http_cache
1727 .invalidate_related_urls(http_request, &forward_response, &request_key)
1728 .await;
1729 }
1730
1731 if revalidating_flag && forward_response.status == StatusCode::NOT_MODIFIED {
1733 *done_chan = None;
1736 if let Some(guard) = cache_guard.try_as_mut() {
1737 response = refresh(http_request, forward_response.clone(), done_chan, guard);
1738 }
1739
1740 if let Some(response) = &mut response {
1741 response.cache_state = CacheState::Validated;
1742 }
1743 }
1744
1745 if response.is_none() {
1747 let forward_response = response.insert(forward_response);
1749
1750 if http_request.cache_mode != CacheMode::NoStore {
1753 cache_guard.insert(http_request, forward_response);
1756 }
1757 }
1758 false
1759 } else {
1760 true
1761 }
1762 }; if should_wait {
1765 wait_for_inflight_requests(done_chan, &mut response).await;
1770 }
1771
1772 let http_request = &mut http_fetch_params.request;
1773 let mut response = response.unwrap();
1774
1775 response.url_list = http_request
1777 .url_list
1778 .iter()
1779 .map(|claimed_url| claimed_url.url())
1780 .collect();
1781
1782 if http_request.headers.contains_key(RANGE) {
1784 response.range_requested = true;
1785 }
1786
1787 response.request_includes_credentials = include_credentials;
1789
1790 if response.status.try_code() == Some(StatusCode::UNAUTHORIZED) &&
1796 !cors_flag &&
1797 include_credentials &&
1798 response.headers.contains_key(WWW_AUTHENTICATE)
1799 {
1800 let request = &mut fetch_params.request;
1803
1804 if request.body.is_some() {
1806 }
1808
1809 if !request.use_url_credentials || authentication_fetch_flag {
1811 let Some(credentials) = context
1812 .state
1813 .request_authentication(request, &response)
1814 .await
1815 else {
1816 return response;
1817 };
1818
1819 if let Err(err) = request
1820 .current_url_mut()
1821 .set_username(&credentials.username)
1822 {
1823 error!("error setting username for url: {:?}", err);
1824 return response;
1825 };
1826
1827 if let Err(err) = request
1828 .current_url_mut()
1829 .set_password(Some(&credentials.password))
1830 {
1831 error!("error setting password for url: {:?}", err);
1832 return response;
1833 };
1834 }
1835
1836 *done_chan = None;
1839
1840 response = http_network_or_cache_fetch(
1842 fetch_params,
1843 true, cors_flag,
1845 done_chan,
1846 context,
1847 )
1848 .await;
1849 }
1850
1851 if response.status == StatusCode::PROXY_AUTHENTICATION_REQUIRED {
1853 let request = &mut fetch_params.request;
1854 if request.traversable_for_user_prompts == TraversableForUserPrompts::NoTraversable {
1857 return Response::network_error(NetworkError::ResourceLoadError(
1858 "Can't find Window object".into(),
1859 ));
1860 }
1861
1862 let Some(credentials) = context
1870 .state
1871 .request_authentication(request, &response)
1872 .await
1873 else {
1874 return response;
1875 };
1876
1877 let entry = AuthCacheEntry {
1879 user_name: credentials.username,
1880 password: credentials.password,
1881 };
1882 {
1883 let mut auth_cache = context.state.auth_cache.write();
1884 let key = request.current_url().origin().ascii_serialization();
1885 auth_cache.entries.insert(key, entry);
1886 }
1887
1888 *done_chan = None;
1891
1892 response = http_network_or_cache_fetch(
1894 fetch_params,
1895 false, cors_flag,
1897 done_chan,
1898 context,
1899 )
1900 .await;
1901 }
1902
1903 if authentication_fetch_flag {
1911 }
1913
1914 response
1916}
1917async fn block_for_cache_ready<'a>(
1926 context: &'a FetchContext,
1927 http_request: &mut Request,
1928 done_chan: &mut DoneChannel,
1929 revalidating_flag: &mut bool,
1930 response: &mut Option<Response>,
1931) -> CachedResourcesOrGuard<'a> {
1932 let entry_key = CacheKey::new(http_request);
1933 let guard_result = context.state.http_cache.get_or_guard(entry_key).await;
1934
1935 match guard_result {
1936 CachedResourcesOrGuard::Guard(_) => {
1937 *done_chan = None;
1938 },
1939 CachedResourcesOrGuard::Value(ref cached_resources) => {
1940 let stored_response = construct_response(http_request, done_chan, cached_resources);
1946 if let Some(response_from_cache) = stored_response {
1948 let response_headers = response_from_cache.response.headers.clone();
1949 let (cached_response, needs_revalidation) =
1951 match (http_request.cache_mode, &http_request.mode) {
1952 (CacheMode::ForceCache, _) => (Some(response_from_cache.response), false),
1953 (CacheMode::OnlyIfCached, &RequestMode::SameOrigin) => {
1954 (Some(response_from_cache.response), false)
1955 },
1956 (CacheMode::OnlyIfCached, _) |
1957 (CacheMode::NoStore, _) |
1958 (CacheMode::Reload, _) => (None, false),
1959 (_, _) => (
1960 Some(response_from_cache.response),
1961 response_from_cache.needs_validation,
1962 ),
1963 };
1964
1965 if needs_revalidation {
1966 *revalidating_flag = true;
1967 if let Some(http_date) = response_headers.typed_get::<LastModified>() {
1969 let http_date: SystemTime = http_date.into();
1970 http_request
1971 .headers
1972 .typed_insert(IfModifiedSince::from(http_date));
1973 }
1974 if let Some(entity_tag) = response_headers.get(header::ETAG) {
1975 http_request
1976 .headers
1977 .insert(header::IF_NONE_MATCH, entity_tag.clone());
1978 }
1979 } else {
1980 *response = cached_response;
1982 if let Some(response) = response {
1983 response.cache_state = CacheState::Local;
1984 }
1985 }
1986 if response.is_none() {
1987 *done_chan = None;
1990 }
1991 }
1992 },
1993 }
1994 guard_result
1995}
1996
1997async fn wait_for_inflight_requests(done_chan: &mut DoneChannel, response: &mut Option<Response>) {
2000 if let Some(ref mut ch) = *done_chan {
2001 assert!(response.is_some());
2005
2006 loop {
2007 match ch.1.recv().await {
2008 Some(Data::Payload(_)) => {},
2009 Some(Data::Done) => break, Some(Data::Cancelled) => {
2011 break;
2013 },
2014 _ => panic!("HTTP cache should always send Done or Cancelled"),
2015 }
2016 }
2017 }
2018 *done_chan = None;
2020}
2021
2022#[derive(PartialEq)]
2026enum CrossOriginResourcePolicy {
2027 Allowed,
2028 Blocked,
2029}
2030
2031fn cross_origin_resource_policy_check(
2034 request: &Request,
2035 response: &Response,
2036) -> CrossOriginResourcePolicy {
2037 if request.mode != RequestMode::NoCors {
2039 return CrossOriginResourcePolicy::Allowed;
2040 }
2041
2042 let current_url_origin = request.current_url().origin();
2044 let same_origin = if let Origin::Origin(ref origin) = request.origin {
2045 *origin == request.current_url().origin()
2046 } else {
2047 false
2048 };
2049
2050 if same_origin {
2051 return CrossOriginResourcePolicy::Allowed;
2052 }
2053
2054 let policy = response
2056 .headers
2057 .get(HeaderName::from_static("cross-origin-resource-policy"))
2058 .map(|h| h.to_str().unwrap_or(""))
2059 .unwrap_or("");
2060
2061 if policy == "same-origin" {
2063 return CrossOriginResourcePolicy::Blocked;
2064 }
2065
2066 if let Origin::Origin(ref request_origin) = request.origin {
2068 let schemeless_same_origin = is_schemelessy_same_site(request_origin, ¤t_url_origin);
2069 if schemeless_same_origin &&
2070 (request_origin.scheme() == Some("https") ||
2071 response.https_state == HttpsState::None)
2072 {
2073 return CrossOriginResourcePolicy::Allowed;
2074 }
2075 };
2076
2077 if policy == "same-site" {
2079 return CrossOriginResourcePolicy::Blocked;
2080 }
2081
2082 CrossOriginResourcePolicy::Allowed
2083}
2084
2085struct ResponseEndTimer(Option<Arc<Mutex<ResourceFetchTiming>>>);
2087
2088impl ResponseEndTimer {
2089 fn neuter(&mut self) {
2090 self.0 = None;
2091 }
2092}
2093
2094impl Drop for ResponseEndTimer {
2095 fn drop(&mut self) {
2096 let ResponseEndTimer(resource_fetch_timing_opt) = self;
2097
2098 resource_fetch_timing_opt.as_ref().map_or((), |t| {
2099 t.lock().set_attribute(ResourceAttribute::ResponseEnd);
2100 })
2101 }
2102}
2103
2104async fn http_network_fetch(
2106 fetch_params: &mut FetchParams,
2107 credentials_flag: bool,
2108 done_chan: &mut DoneChannel,
2109 context: &FetchContext,
2110) -> Response {
2111 let mut response_end_timer = ResponseEndTimer(Some(context.timing.clone()));
2112
2113 let request = &mut fetch_params.request;
2115
2116 let url = request.current_url();
2127 let request_id = request.id.0.to_string();
2128 if log_enabled!(log::Level::Info) {
2129 info!("{:?} request for {}", request.method, url);
2130 for header in request.headers.iter() {
2131 debug!(" - {:?}", header);
2132 }
2133 }
2134
2135 let is_xhr = request.destination == Destination::None;
2139
2140 let (fetch_terminated_sender, mut fetch_terminated_receiver) = unbounded_channel();
2142
2143 let body = request.body.as_ref().map(|body| body.clone_stream());
2144
2145 if body.is_none() {
2146 let _ = fetch_terminated_sender.send(false);
2151 }
2152
2153 let browsing_context_id = request.target_webview_id.map(Into::into);
2154
2155 let (res, msg) = match &request.mode {
2156 RequestMode::WebSocket {
2157 protocols,
2158 original_url: _,
2159 } => {
2160 let (resource_event_sender, dom_action_receiver) = {
2163 let mut websocket_chan = context.websocket_chan.as_ref().unwrap().lock();
2164 (
2165 websocket_chan.sender.clone(),
2166 websocket_chan.receiver.take().unwrap(),
2167 )
2168 };
2169
2170 let mut tls_config = create_tls_config(
2171 context.ca_certificates.clone(),
2172 context.ignore_certificate_errors,
2173 context.state.override_manager.clone(),
2174 );
2175 tls_config.alpn_protocols = vec!["http/1.1".to_string().into()];
2176
2177 let response = match start_websocket(
2178 context.state.clone(),
2179 resource_event_sender,
2180 protocols,
2181 request,
2182 tls_config,
2183 dom_action_receiver,
2184 )
2185 .await
2186 {
2187 Ok(response) => response,
2188 Err(error) => {
2189 return Response::network_error(NetworkError::WebsocketConnectionFailure(
2190 format!("{error:?}"),
2191 ));
2192 },
2193 };
2194
2195 let response = response.map(|r| match r {
2196 Some(body) => Full::from(body).map_err(|_| unreachable!()).boxed(),
2197 None => http_body_util::Empty::new()
2198 .map_err(|_| unreachable!())
2199 .boxed(),
2200 });
2201 (Decoder::detect(response, url.is_secure_scheme()), None)
2202 },
2203 _ => {
2204 let response_future = obtain_response(
2205 &context.state.client,
2206 &url,
2207 &request.method,
2208 &mut request.headers,
2209 body,
2210 request
2211 .body
2212 .as_ref()
2213 .is_some_and(|body| body.source_is_null()),
2214 &request.pipeline_id,
2215 Some(&request_id),
2216 request.destination,
2217 is_xhr,
2218 context,
2219 fetch_terminated_sender,
2220 browsing_context_id,
2221 );
2222
2223 let (res, msg) = match response_future.await {
2225 Ok(wrapped_response) => wrapped_response,
2226 Err(error) => return Response::network_error(error),
2227 };
2228 (res, msg)
2229 },
2230 };
2231
2232 if log_enabled!(log::Level::Info) {
2233 debug!("{:?} response for {}", res.version(), url);
2234 for header in res.headers().iter() {
2235 debug!(" - {:?}", header);
2236 }
2237 }
2238
2239 match fetch_terminated_receiver.recv().await {
2242 Some(true) => return Response::network_error(NetworkError::ConnectionFailure),
2243 Some(false) => {},
2244 _ => warn!("Failed to receive confirmation request was streamed without error."),
2245 }
2246
2247 let header_strings: Vec<&str> = res
2248 .headers()
2249 .get_all("Timing-Allow-Origin")
2250 .iter()
2251 .map(|header_value| header_value.to_str().unwrap_or(""))
2252 .collect();
2253 let wildcard_present = header_strings.contains(&"*");
2254 let req_origin_in_timing_allow = header_strings
2258 .iter()
2259 .any(|header_str| match request.origin {
2260 SpecificOrigin(ref immutable_request_origin) => {
2261 *header_str == immutable_request_origin.ascii_serialization()
2262 },
2263 _ => false,
2264 });
2265
2266 let is_same_origin = request.url_list.iter().all(|url| match request.origin {
2267 SpecificOrigin(ref immutable_request_origin) => url.origin() == *immutable_request_origin,
2268 _ => false,
2269 });
2270
2271 if !(is_same_origin || req_origin_in_timing_allow || wildcard_present) {
2272 context.timing.lock().mark_timing_check_failed();
2273 }
2274
2275 let timing = context.timing.lock().clone();
2276 let mut response = Response::new(url.clone(), timing);
2277
2278 if let Some(handshake_info) = res.extensions().get::<TlsHandshakeInfo>() {
2279 let mut hsts_enabled = url
2280 .host_str()
2281 .is_some_and(|host| context.state.hsts_list.read().is_host_secure(host));
2282
2283 if url.scheme() == "https" {
2284 if let Some(sts) = res.headers().typed_get::<StrictTransportSecurity>() {
2285 hsts_enabled = sts.max_age().as_secs() > 0;
2287 }
2288 }
2289 response.tls_security_info = Some(build_tls_security_info(handshake_info, hsts_enabled));
2290 }
2291
2292 let status_text = res
2293 .extensions()
2294 .get::<ReasonPhrase>()
2295 .map(ReasonPhrase::as_bytes)
2296 .or_else(|| res.status().canonical_reason().map(str::as_bytes))
2297 .map(Vec::from)
2298 .unwrap_or_default();
2299 response.status = HttpStatus::new(res.status(), status_text);
2300
2301 info!("got {:?} response for {:?}", res.status(), request.url());
2302 response.headers = res.headers().clone();
2303 response.referrer = request.referrer.to_url().cloned();
2304 response.referrer_policy = request.referrer_policy;
2305
2306 let res_body = response.body.clone();
2307
2308 let (done_sender, done_receiver) = unbounded_channel();
2310 *done_chan = Some((done_sender.clone(), done_receiver));
2311
2312 let devtools_sender = context.devtools_chan.clone();
2313 let cancellation_listener = context.cancellation_listener.clone();
2314 if cancellation_listener.cancelled() {
2315 return Response::network_error(NetworkError::LoadCancelled);
2316 }
2317
2318 *res_body.lock() = ResponseBody::Receiving(vec![]);
2319 let res_body2 = res_body.clone();
2320
2321 if let Some(ref sender) = devtools_sender {
2322 if let Some(m) = msg {
2323 send_request_to_devtools(m, sender);
2324 }
2325 }
2326
2327 let done_sender2 = done_sender.clone();
2328 let done_sender3 = done_sender.clone();
2329 let timing_ptr2 = context.timing.clone();
2330 let timing_ptr3 = context.timing.clone();
2331 let devtools_request = request.clone();
2332 let url1 = devtools_request.url();
2333 let url2 = url1.clone();
2334
2335 let status = response.status.clone();
2336 let headers = response.headers.clone();
2337 let devtools_chan = context.devtools_chan.clone();
2338
2339 spawn_task(
2340 res.into_body()
2341 .try_fold(res_body, move |res_body, chunk| {
2342 if cancellation_listener.cancelled() {
2343 *res_body.lock() = ResponseBody::Done(vec![]);
2344 let _ = done_sender.send(Data::Cancelled);
2345 return future::ready(Err(std::io::Error::new(
2346 std::io::ErrorKind::Interrupted,
2347 "Fetch aborted",
2348 )));
2349 }
2350 if let ResponseBody::Receiving(ref mut body) = *res_body.lock() {
2351 let bytes = chunk;
2352 body.extend_from_slice(&bytes);
2353 let _ = done_sender.send(Data::Payload(bytes.to_vec()));
2354 }
2355 future::ready(Ok(res_body))
2356 })
2357 .and_then(move |res_body| {
2358 debug!("successfully finished response for {:?}", url1);
2359 let mut body = res_body.lock();
2360 let completed_body = match *body {
2361 ResponseBody::Receiving(ref mut body) => std::mem::take(body),
2362 _ => vec![],
2363 };
2364 let devtools_response_body = completed_body.clone();
2365 *body = ResponseBody::Done(completed_body);
2366 send_response_values_to_devtools(
2367 Some(headers),
2368 status,
2369 Some(devtools_response_body),
2370 CacheState::None,
2371 &devtools_request,
2372 devtools_chan,
2373 );
2374 timing_ptr2
2375 .lock()
2376 .set_attribute(ResourceAttribute::ResponseEnd);
2377 let _ = done_sender2.send(Data::Done);
2378 future::ready(Ok(()))
2379 })
2380 .map_err(move |error| {
2381 if let std::io::ErrorKind::InvalidData = error.kind() {
2382 debug!("Content decompression error for {:?}", url2);
2383 let _ = done_sender3.send(Data::Error(NetworkError::DecompressionError));
2384 let mut body = res_body2.lock();
2385
2386 *body = ResponseBody::Done(vec![]);
2387 }
2388 debug!("finished response for {:?}", url2);
2389 let mut body = res_body2.lock();
2390 let completed_body = match *body {
2391 ResponseBody::Receiving(ref mut body) => std::mem::take(body),
2392 _ => vec![],
2393 };
2394 *body = ResponseBody::Done(completed_body);
2395 timing_ptr3
2396 .lock()
2397 .set_attribute(ResourceAttribute::ResponseEnd);
2398 let _ = done_sender3.send(Data::Done);
2399 }),
2400 );
2401
2402 response.https_state = match url.scheme() {
2408 "https" => HttpsState::Modern,
2409 _ => HttpsState::None,
2410 };
2411
2412 if credentials_flag {
2425 set_cookies_from_headers(&url, &response.headers, &context.state.cookie_jar);
2426 }
2427 context
2428 .state
2429 .hsts_list
2430 .write()
2431 .update_hsts_list_from_response(&url, &response.headers);
2432
2433 response_end_timer.neuter();
2447 response
2448}
2449
2450async fn cors_preflight_fetch(
2452 request: &Request,
2453 cache: &mut CorsCache,
2454 context: &FetchContext,
2455) -> Response {
2456 let mut preflight = RequestBuilder::new(
2461 request.target_webview_id,
2462 request.current_url_with_blob_claim(),
2463 request.referrer.clone(),
2464 )
2465 .method(Method::OPTIONS)
2466 .origin(match &request.origin {
2467 Origin::Client => {
2468 unreachable!("We shouldn't get Client origin in cors_preflight_fetch.")
2469 },
2470 Origin::Origin(origin) => origin.clone(),
2471 })
2472 .pipeline_id(request.pipeline_id)
2473 .initiator(request.initiator)
2474 .destination(request.destination)
2475 .referrer_policy(request.referrer_policy)
2476 .mode(RequestMode::CorsMode)
2477 .response_tainting(ResponseTainting::CorsTainting)
2478 .policy_container(match &request.policy_container {
2479 RequestPolicyContainer::Client => {
2480 unreachable!("We should have a policy container for request in cors_preflight_fetch")
2481 },
2482 RequestPolicyContainer::PolicyContainer(policy_container) => policy_container.clone(),
2483 })
2484 .url_list(
2485 request
2486 .url_list
2487 .iter()
2488 .map(|claimed_url| claimed_url.url())
2489 .collect(),
2490 )
2491 .build();
2492
2493 preflight
2495 .headers
2496 .insert(ACCEPT, HeaderValue::from_static("*/*"));
2497
2498 preflight
2500 .headers
2501 .typed_insert::<AccessControlRequestMethod>(AccessControlRequestMethod::from(
2502 request.method.clone(),
2503 ));
2504
2505 let headers = get_cors_unsafe_header_names(&request.headers);
2507
2508 if !headers.is_empty() {
2510 preflight.headers.insert(
2513 ACCESS_CONTROL_REQUEST_HEADERS,
2514 HeaderValue::from_bytes(itertools::join(headers.iter(), ",").as_bytes())
2515 .unwrap_or(HeaderValue::from_static("")),
2516 );
2517 }
2518
2519 let mut fetch_params = FetchParams::new(preflight);
2522 let response =
2523 http_network_or_cache_fetch(&mut fetch_params, false, false, &mut None, context).await;
2524
2525 if cors_check(request, &response).is_ok() && response.status.code().is_success() {
2527 let mut methods = if response
2530 .headers
2531 .contains_key(header::ACCESS_CONTROL_ALLOW_METHODS)
2532 {
2533 match response.headers.typed_get::<AccessControlAllowMethods>() {
2534 Some(methods) => methods.iter().collect(),
2535 None => {
2537 return Response::network_error(NetworkError::CorsAllowMethods);
2538 },
2539 }
2540 } else {
2541 vec![]
2542 };
2543
2544 let header_names = if response
2547 .headers
2548 .contains_key(header::ACCESS_CONTROL_ALLOW_HEADERS)
2549 {
2550 match response.headers.typed_get::<AccessControlAllowHeaders>() {
2551 Some(names) => names.iter().collect(),
2552 None => {
2554 return Response::network_error(NetworkError::CorsAllowHeaders);
2555 },
2556 }
2557 } else {
2558 vec![]
2559 };
2560
2561 debug!(
2562 "CORS check: Allowed methods: {:?}, current method: {:?}",
2563 methods, request.method
2564 );
2565
2566 if methods.is_empty() && request.use_cors_preflight {
2569 methods = vec![request.method.clone()];
2570 }
2571
2572 if methods
2575 .iter()
2576 .all(|method| *method.as_str() != *request.method.as_ref()) &&
2577 !is_cors_safelisted_method(&request.method) &&
2578 (request.credentials_mode == CredentialsMode::Include ||
2579 methods.iter().all(|method| method.as_ref() != "*"))
2580 {
2581 return Response::network_error(NetworkError::CorsMethod);
2582 }
2583
2584 debug!(
2585 "CORS check: Allowed headers: {:?}, current headers: {:?}",
2586 header_names, request.headers
2587 );
2588
2589 if request.headers.iter().any(|(name, _)| {
2592 is_cors_non_wildcard_request_header_name(name) &&
2593 header_names.iter().all(|header_name| header_name != name)
2594 }) {
2595 return Response::network_error(NetworkError::CorsAuthorization);
2596 }
2597
2598 let unsafe_names = get_cors_unsafe_header_names(&request.headers);
2602 let header_names_set: HashSet<&HeaderName> = HashSet::from_iter(header_names.iter());
2603 let header_names_contains_star = header_names
2604 .iter()
2605 .any(|header_name| header_name.as_str() == "*");
2606 for unsafe_name in unsafe_names.iter() {
2607 if !header_names_set.contains(unsafe_name) &&
2608 (request.credentials_mode == CredentialsMode::Include ||
2609 !header_names_contains_star)
2610 {
2611 return Response::network_error(NetworkError::CorsHeaders);
2612 }
2613 }
2614
2615 let max_age: Option<Duration> = response
2618 .headers
2619 .typed_get::<AccessControlMaxAge>()
2620 .map(|acma| acma.into());
2621
2622 let max_age = max_age.unwrap_or(Duration::from_secs(5));
2624
2625 for method in &methods {
2636 cache.match_method_and_update(request, method.clone(), max_age);
2637 }
2638
2639 for header_name in &header_names {
2644 cache.match_header_and_update(request, header_name, max_age);
2645 }
2646
2647 return response;
2649 }
2650
2651 Response::network_error(NetworkError::CorsGeneral)
2653}
2654
2655fn cors_check(request: &Request, response: &Response) -> Result<(), ()> {
2657 let Some(origins) =
2659 get_value_from_header_list(ACCESS_CONTROL_ALLOW_ORIGIN.as_str(), &response.headers)
2660 else {
2661 return Err(());
2663 };
2664 let origin = origins.into_iter().map(char::from).collect::<String>();
2665
2666 if request.credentials_mode != CredentialsMode::Include && origin == "*" {
2668 return Ok(());
2669 }
2670
2671 if serialize_request_origin(request).to_string() != origin {
2673 return Err(());
2674 }
2675
2676 if request.credentials_mode != CredentialsMode::Include {
2678 return Ok(());
2679 }
2680
2681 let credentials = response
2683 .headers
2684 .typed_get::<AccessControlAllowCredentials>();
2685
2686 if credentials.is_some() {
2688 return Ok(());
2689 }
2690
2691 Err(())
2693}
2694
2695fn has_credentials(url: &ServoUrl) -> bool {
2696 !url.username().is_empty() || url.password().is_some()
2697}
2698
2699fn is_no_store_cache(headers: &HeaderMap) -> bool {
2700 headers.contains_key(header::IF_MODIFIED_SINCE) |
2701 headers.contains_key(header::IF_NONE_MATCH) |
2702 headers.contains_key(header::IF_UNMODIFIED_SINCE) |
2703 headers.contains_key(header::IF_MATCH) |
2704 headers.contains_key(header::IF_RANGE)
2705}
2706
2707fn is_redirect_status(status: StatusCode) -> bool {
2709 matches!(
2710 status,
2711 StatusCode::MOVED_PERMANENTLY |
2712 StatusCode::FOUND |
2713 StatusCode::SEE_OTHER |
2714 StatusCode::TEMPORARY_REDIRECT |
2715 StatusCode::PERMANENT_REDIRECT
2716 )
2717}
2718
2719fn serialize_request_origin(request: &Request) -> headers::Origin {
2721 let Origin::Origin(origin) = &request.origin else {
2723 panic!("origin cannot be \"client\" at this point in time");
2724 };
2725
2726 if request.redirect_taint_for_request() != RedirectTaint::SameOrigin {
2728 return headers::Origin::NULL;
2729 }
2730
2731 serialize_origin(origin)
2733}
2734
2735pub fn serialize_origin(origin: &ImmutableOrigin) -> headers::Origin {
2737 match origin {
2738 ImmutableOrigin::Opaque(_) => headers::Origin::NULL,
2739 ImmutableOrigin::Tuple(scheme, host, port) => {
2740 let port = match (scheme.as_ref(), port) {
2743 ("http" | "ws", 80) | ("https" | "wss", 443) | ("ftp", 21) => None,
2744 _ => Some(*port),
2745 };
2746
2747 headers::Origin::try_from_parts(scheme, &host.to_string(), port)
2749 .unwrap_or(headers::Origin::NULL)
2750 },
2751 }
2752}
2753
2754fn append_a_request_origin_header(request: &mut Request) {
2756 let Origin::Origin(request_origin) = &request.origin else {
2758 panic!("origin cannot be \"client\" at this point in time");
2759 };
2760
2761 let mut serialized_origin = serialize_request_origin(request);
2763
2764 if request.response_tainting == ResponseTainting::CorsTainting ||
2767 matches!(request.mode, RequestMode::WebSocket { .. })
2768 {
2769 request.headers.typed_insert(serialized_origin);
2770 }
2771 else if !matches!(request.method, Method::GET | Method::HEAD) {
2773 if request.mode != RequestMode::CorsMode {
2775 match request.referrer_policy {
2776 ReferrerPolicy::NoReferrer => {
2777 serialized_origin = headers::Origin::NULL;
2779 },
2780 ReferrerPolicy::NoReferrerWhenDowngrade |
2781 ReferrerPolicy::StrictOrigin |
2782 ReferrerPolicy::StrictOriginWhenCrossOrigin => {
2783 if let ImmutableOrigin::Tuple(scheme, _, _) = &request_origin {
2786 if scheme == "https" && request.current_url().scheme() != "https" {
2787 serialized_origin = headers::Origin::NULL;
2788 }
2789 }
2790 },
2791 ReferrerPolicy::SameOrigin => {
2792 if *request_origin != request.current_url().origin() {
2795 serialized_origin = headers::Origin::NULL;
2796 }
2797 },
2798 _ => {
2799 },
2801 };
2802 }
2803
2804 request.headers.typed_insert(serialized_origin);
2806 }
2807}
2808
2809fn append_the_fetch_metadata_headers(r: &mut Request) {
2811 if !r.url().is_potentially_trustworthy() {
2813 return;
2814 }
2815
2816 set_the_sec_fetch_dest_header(r);
2818
2819 set_the_sec_fetch_mode_header(r);
2821
2822 set_the_sec_fetch_site_header(r);
2824
2825 set_the_sec_fetch_user_header(r);
2827}
2828
2829fn set_the_sec_fetch_dest_header(r: &mut Request) {
2831 debug_assert!(r.url().is_potentially_trustworthy());
2833
2834 let header = r.destination;
2838
2839 r.headers.typed_insert(SecFetchDest(header));
2841}
2842
2843fn set_the_sec_fetch_mode_header(r: &mut Request) {
2845 debug_assert!(r.url().is_potentially_trustworthy());
2847
2848 let header = &r.mode;
2851
2852 r.headers.typed_insert(SecFetchMode::from(header));
2854}
2855
2856fn set_the_sec_fetch_site_header(r: &mut Request) {
2858 let Origin::Origin(request_origin) = &r.origin else {
2861 panic!("request origin cannot be \"client\" at this point")
2862 };
2863
2864 debug_assert!(r.url().is_potentially_trustworthy());
2866
2867 let mut header = SecFetchSite::SameOrigin;
2870
2871 if header != SecFetchSite::None {
2876 for url in &r.url_list {
2877 if url.origin() == *request_origin {
2879 continue;
2880 }
2881
2882 header = SecFetchSite::CrossSite;
2884
2885 if !is_same_site(request_origin, &url.origin()) {
2887 break;
2888 }
2889
2890 header = SecFetchSite::SameSite;
2892 }
2893 }
2894
2895 r.headers.typed_insert(header);
2897}
2898
2899fn set_the_sec_fetch_user_header(r: &mut Request) {
2901 debug_assert!(r.url().is_potentially_trustworthy());
2903
2904 if !r.is_navigation_request() {
2907 return;
2908 }
2909
2910 let header = SecFetchUser;
2913
2914 r.headers.typed_insert(header);
2916}
2917
2918fn set_requests_referrer_policy_on_redirect(request: &mut Request, response: &Response) {
2920 let referrer_policy: ReferrerPolicy = response
2923 .headers
2924 .typed_get::<headers::ReferrerPolicy>()
2925 .into();
2926
2927 if referrer_policy != ReferrerPolicy::EmptyString {
2929 request.referrer_policy = referrer_policy;
2930 }
2931}