1use std::collections::{HashMap, HashSet};
6use std::iter::FromIterator;
7use std::sync::{Arc as StdArc, Condvar, Mutex, RwLock};
8use std::time::{Duration, SystemTime, UNIX_EPOCH};
9
10use async_recursion::async_recursion;
11use base::cross_process_instant::CrossProcessInstant;
12use base::generic_channel;
13use base::id::{BrowsingContextId, HistoryStateId, PipelineId};
14use crossbeam_channel::Sender;
15use devtools_traits::{
16 ChromeToDevtoolsControlMsg, DevtoolsControlMsg, HttpRequest as DevtoolsHttpRequest,
17 HttpResponse as DevtoolsHttpResponse, NetworkEvent,
18};
19use embedder_traits::{AuthenticationResponse, EmbedderMsg, EmbedderProxy};
20use futures::{TryFutureExt, TryStreamExt, future};
21use headers::authorization::Basic;
22use headers::{
23 AccessControlAllowCredentials, AccessControlAllowHeaders, AccessControlAllowMethods,
24 AccessControlAllowOrigin, AccessControlMaxAge, AccessControlRequestMethod, Authorization,
25 CacheControl, ContentLength, HeaderMapExt, IfModifiedSince, LastModified, Pragma, Referer,
26 UserAgent,
27};
28use http::header::{
29 self, ACCEPT, ACCESS_CONTROL_REQUEST_HEADERS, AUTHORIZATION, CONTENT_ENCODING,
30 CONTENT_LANGUAGE, CONTENT_LOCATION, CONTENT_TYPE, HeaderValue, RANGE, WWW_AUTHENTICATE,
31};
32use http::{HeaderMap, Method, Request as HyperRequest, StatusCode};
33use http_body_util::combinators::BoxBody;
34use http_body_util::{BodyExt, Full};
35use hyper::Response as HyperResponse;
36use hyper::body::{Bytes, Frame};
37use hyper::ext::ReasonPhrase;
38use hyper::header::{HeaderName, TRANSFER_ENCODING};
39use hyper_serde::Serde;
40use hyper_util::client::legacy::Client;
41use ipc_channel::ipc::{self, IpcSender, IpcSharedMemory};
42use ipc_channel::router::ROUTER;
43use log::{debug, error, info, log_enabled, warn};
44use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
45use net_traits::http_status::HttpStatus;
46use net_traits::pub_domains::reg_suffix;
47use net_traits::request::Origin::Origin as SpecificOrigin;
48use net_traits::request::{
49 BodyChunkRequest, BodyChunkResponse, CacheMode, CredentialsMode, Destination, Initiator,
50 Origin, RedirectMode, Referrer, Request, RequestBuilder, RequestMode, ResponseTainting,
51 ServiceWorkersMode, Window as RequestWindow, get_cors_unsafe_header_names,
52 is_cors_non_wildcard_request_header_name, is_cors_safelisted_method,
53 is_cors_safelisted_request_header,
54};
55use net_traits::response::{HttpsState, Response, ResponseBody, ResponseType};
56use net_traits::{
57 CookieSource, DOCUMENT_ACCEPT_HEADER_VALUE, FetchMetadata, NetworkError, RedirectEndValue,
58 RedirectStartValue, ReferrerPolicy, ResourceAttribute, ResourceFetchTiming, ResourceTimeValue,
59};
60use profile_traits::mem::{Report, ReportKind};
61use profile_traits::path;
62use rustc_hash::FxHashMap;
63use servo_arc::Arc;
64use servo_url::{Host, ImmutableOrigin, ServoUrl};
65use tokio::sync::mpsc::{
66 Receiver as TokioReceiver, Sender as TokioSender, UnboundedReceiver, UnboundedSender, channel,
67 unbounded_channel,
68};
69use tokio_stream::wrappers::ReceiverStream;
70
71use crate::async_runtime::spawn_task;
72use crate::connector::{CertificateErrorOverrideManager, Connector};
73use crate::cookie::ServoCookie;
74use crate::cookie_storage::CookieStorage;
75use crate::decoder::Decoder;
76use crate::fetch::cors_cache::CorsCache;
77use crate::fetch::fetch_params::FetchParams;
78use crate::fetch::headers::{SecFetchDest, SecFetchMode, SecFetchSite, SecFetchUser};
79use crate::fetch::methods::{Data, DoneChannel, FetchContext, Target, main_fetch};
80use crate::hsts::HstsList;
81use crate::http_cache::{CacheKey, HttpCache};
82use crate::resource_thread::{AuthCache, AuthCacheEntry};
83
84#[derive(Clone, Debug, Eq, PartialEq)]
86pub enum HttpCacheEntryState {
87 ReadyToConstruct,
91 PendingStore(usize),
93}
94
95type HttpCacheState = Mutex<HashMap<CacheKey, Arc<(Mutex<HttpCacheEntryState>, Condvar)>>>;
96
97pub struct HttpState {
98 pub hsts_list: RwLock<HstsList>,
99 pub cookie_jar: RwLock<CookieStorage>,
100 pub http_cache: RwLock<HttpCache>,
101 pub http_cache_state: HttpCacheState,
105 pub auth_cache: RwLock<AuthCache>,
106 pub history_states: RwLock<FxHashMap<HistoryStateId, Vec<u8>>>,
107 pub client: Client<Connector, crate::connector::BoxedBody>,
108 pub override_manager: CertificateErrorOverrideManager,
109 pub embedder_proxy: Mutex<EmbedderProxy>,
110}
111
112impl HttpState {
113 pub(crate) fn memory_reports(&self, suffix: &str, ops: &mut MallocSizeOfOps) -> Vec<Report> {
114 vec![
115 Report {
116 path: path!["memory-cache", suffix],
117 kind: ReportKind::ExplicitJemallocHeapSize,
118 size: self.http_cache.read().unwrap().size_of(ops),
119 },
120 Report {
121 path: path!["hsts-list", suffix],
122 kind: ReportKind::ExplicitJemallocHeapSize,
123 size: self.hsts_list.read().unwrap().size_of(ops),
124 },
125 ]
126 }
127
128 fn request_authentication(
129 &self,
130 request: &Request,
131 response: &Response,
132 ) -> Option<AuthenticationResponse> {
133 let webview_id = request.target_webview_id?;
135 let for_proxy = response.status == StatusCode::PROXY_AUTHENTICATION_REQUIRED;
136
137 if request.mode != RequestMode::Navigate {
139 return None;
140 }
141
142 let embedder_proxy = self.embedder_proxy.lock().unwrap();
143 let (ipc_sender, ipc_receiver) = generic_channel::channel().unwrap();
144 embedder_proxy.send(EmbedderMsg::RequestAuthentication(
145 webview_id,
146 request.url(),
147 for_proxy,
148 ipc_sender,
149 ));
150 ipc_receiver.recv().ok()?
151 }
152}
153
154pub(crate) fn set_default_accept(request: &mut Request) {
156 if request.headers.contains_key(header::ACCEPT) {
157 return;
158 }
159
160 let value = if request.initiator == Initiator::Prefetch {
161 DOCUMENT_ACCEPT_HEADER_VALUE
162 } else {
163 match request.destination {
164 Destination::Document | Destination::Frame | Destination::IFrame => {
165 DOCUMENT_ACCEPT_HEADER_VALUE
166 },
167 Destination::Image => {
168 HeaderValue::from_static("image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5")
169 },
170 Destination::Json => HeaderValue::from_static("application/json,*/*;q=0.5"),
171 Destination::Style => HeaderValue::from_static("text/css,*/*;q=0.1"),
172 _ => HeaderValue::from_static("*/*"),
173 }
174 };
175
176 request.headers.insert(header::ACCEPT, value);
177}
178
179fn set_default_accept_encoding(headers: &mut HeaderMap) {
180 if headers.contains_key(header::ACCEPT_ENCODING) {
181 return;
182 }
183
184 headers.insert(
186 header::ACCEPT_ENCODING,
187 HeaderValue::from_static("gzip, deflate, br, zstd"),
188 );
189}
190
191fn no_referrer_when_downgrade(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
193 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
195 return None;
196 }
197 strip_url_for_use_as_referrer(referrer_url, false)
199}
200
201fn strict_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
203 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
205 return None;
206 }
207 strip_url_for_use_as_referrer(referrer_url, true)
209}
210
211fn strict_origin_when_cross_origin(
213 referrer_url: ServoUrl,
214 current_url: ServoUrl,
215) -> Option<ServoUrl> {
216 if referrer_url.origin() == current_url.origin() {
218 return strip_url_for_use_as_referrer(referrer_url, false);
219 }
220 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
222 return None;
223 }
224 strip_url_for_use_as_referrer(referrer_url, true)
226}
227
228fn is_same_site(site_a: &ImmutableOrigin, site_b: &ImmutableOrigin) -> bool {
230 if !site_a.is_tuple() && !site_b.is_tuple() && site_a == site_b {
235 return true;
236 }
237
238 let ImmutableOrigin::Tuple(scheme_a, host_a, _) = site_a else {
240 return false;
241 };
242 let ImmutableOrigin::Tuple(scheme_b, host_b, _) = site_b else {
243 return false;
244 };
245
246 if scheme_a != scheme_b {
248 return false;
249 }
250
251 if let (Host::Domain(domain_a), Host::Domain(domain_b)) = (host_a, host_b) {
254 if reg_suffix(domain_a) != reg_suffix(domain_b) {
255 return false;
256 }
257 } else if host_a != host_b {
258 return false;
259 }
260
261 true
263}
264
265fn is_schemelessy_same_site(site_a: &ImmutableOrigin, site_b: &ImmutableOrigin) -> bool {
267 if !site_a.is_tuple() && !site_b.is_tuple() && site_a == site_b {
269 true
270 } else if site_a.is_tuple() && site_b.is_tuple() {
271 let host_a = site_a.host().map(|h| h.to_string()).unwrap_or_default();
273 let host_b = site_b.host().map(|h| h.to_string()).unwrap_or_default();
274
275 let host_a_reg = reg_suffix(&host_a);
276 let host_b_reg = reg_suffix(&host_b);
277
278 (site_a.host() == site_b.host() && host_a_reg.is_empty()) ||
280 (host_a_reg == host_b_reg && !host_a_reg.is_empty())
281 } else {
282 false
284 }
285}
286
287fn strip_url_for_use_as_referrer(mut url: ServoUrl, origin_only: bool) -> Option<ServoUrl> {
289 const MAX_REFERRER_URL_LENGTH: usize = 4096;
290 if url.is_local_scheme() {
292 return None;
293 }
294 {
296 let url = url.as_mut_url();
297 let _ = url.set_username("");
298 let _ = url.set_password(None);
299 url.set_fragment(None);
300 if origin_only || url.as_str().len() > MAX_REFERRER_URL_LENGTH {
304 url.set_path("");
305 url.set_query(None);
306 }
307 }
308 Some(url)
310}
311
312fn same_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
314 if referrer_url.origin() == current_url.origin() {
316 return strip_url_for_use_as_referrer(referrer_url, false);
317 }
318 None
320}
321
322fn origin_when_cross_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
324 if referrer_url.origin() == current_url.origin() {
326 return strip_url_for_use_as_referrer(referrer_url, false);
327 }
328 strip_url_for_use_as_referrer(referrer_url, true)
330}
331
332pub fn determine_requests_referrer(
334 referrer_policy: ReferrerPolicy,
335 referrer_source: ServoUrl,
336 current_url: ServoUrl,
337) -> Option<ServoUrl> {
338 match referrer_policy {
339 ReferrerPolicy::EmptyString | ReferrerPolicy::NoReferrer => None,
340 ReferrerPolicy::Origin => strip_url_for_use_as_referrer(referrer_source, true),
341 ReferrerPolicy::UnsafeUrl => strip_url_for_use_as_referrer(referrer_source, false),
342 ReferrerPolicy::StrictOrigin => strict_origin(referrer_source, current_url),
343 ReferrerPolicy::StrictOriginWhenCrossOrigin => {
344 strict_origin_when_cross_origin(referrer_source, current_url)
345 },
346 ReferrerPolicy::SameOrigin => same_origin(referrer_source, current_url),
347 ReferrerPolicy::OriginWhenCrossOrigin => {
348 origin_when_cross_origin(referrer_source, current_url)
349 },
350 ReferrerPolicy::NoReferrerWhenDowngrade => {
351 no_referrer_when_downgrade(referrer_source, current_url)
352 },
353 }
354}
355
356fn set_request_cookies(
357 url: &ServoUrl,
358 headers: &mut HeaderMap,
359 cookie_jar: &RwLock<CookieStorage>,
360) {
361 let mut cookie_jar = cookie_jar.write().unwrap();
362 cookie_jar.remove_expired_cookies_for_url(url);
363 if let Some(cookie_list) = cookie_jar.cookies_for_url(url, CookieSource::HTTP) {
364 headers.insert(
365 header::COOKIE,
366 HeaderValue::from_bytes(cookie_list.as_bytes()).unwrap(),
367 );
368 }
369}
370
371fn set_cookie_for_url(cookie_jar: &RwLock<CookieStorage>, request: &ServoUrl, cookie_val: &str) {
372 let mut cookie_jar = cookie_jar.write().unwrap();
373 let source = CookieSource::HTTP;
374
375 if let Some(cookie) = ServoCookie::from_cookie_string(cookie_val.into(), request, source) {
376 cookie_jar.push(cookie, request, source);
377 }
378}
379
380fn set_cookies_from_headers(
381 url: &ServoUrl,
382 headers: &HeaderMap,
383 cookie_jar: &RwLock<CookieStorage>,
384) {
385 for cookie in headers.get_all(header::SET_COOKIE) {
386 if let Ok(cookie_str) = std::str::from_utf8(cookie.as_bytes()) {
387 set_cookie_for_url(cookie_jar, url, cookie_str);
388 }
389 }
390}
391
392#[allow(clippy::too_many_arguments)]
393fn prepare_devtools_request(
394 request_id: String,
395 url: ServoUrl,
396 method: Method,
397 headers: HeaderMap,
398 body: Option<Vec<u8>>,
399 pipeline_id: PipelineId,
400 connect_time: Duration,
401 send_time: Duration,
402 destination: Destination,
403 is_xhr: bool,
404 browsing_context_id: BrowsingContextId,
405) -> ChromeToDevtoolsControlMsg {
406 let started_date_time = SystemTime::now();
407 let request = DevtoolsHttpRequest {
408 url,
409 method,
410 headers,
411 body,
412 pipeline_id,
413 started_date_time,
414 time_stamp: started_date_time
415 .duration_since(UNIX_EPOCH)
416 .unwrap_or_default()
417 .as_secs() as i64,
418 connect_time,
419 send_time,
420 destination,
421 is_xhr,
422 browsing_context_id,
423 };
424 let net_event = NetworkEvent::HttpRequestUpdate(request);
425
426 ChromeToDevtoolsControlMsg::NetworkEvent(request_id, net_event)
427}
428
429pub fn send_request_to_devtools(
430 msg: ChromeToDevtoolsControlMsg,
431 devtools_chan: &Sender<DevtoolsControlMsg>,
432) {
433 if matches!(msg, ChromeToDevtoolsControlMsg::NetworkEvent(_, ref network_event) if !network_event.forward_to_devtools())
434 {
435 return;
436 }
437 if let Err(e) = devtools_chan.send(DevtoolsControlMsg::FromChrome(msg)) {
438 error!("DevTools send failed: {e}");
439 }
440}
441
442pub fn send_response_to_devtools(
443 request: &Request,
444 context: &FetchContext,
445 response: &Response,
446 body_data: Option<Vec<u8>>,
447) {
448 let meta = match response.metadata() {
449 Ok(FetchMetadata::Unfiltered(m)) => m,
450 Ok(FetchMetadata::Filtered { unsafe_, .. }) => unsafe_,
451 Err(_) => {
452 log::warn!("No metadata available, skipping devtools response.");
453 return;
454 },
455 };
456 send_response_values_to_devtools(
457 meta.headers.map(Serde::into_inner),
458 meta.status,
459 body_data,
460 request,
461 context.devtools_chan.clone(),
462 );
463}
464
465#[allow(clippy::too_many_arguments)]
466pub fn send_response_values_to_devtools(
467 headers: Option<HeaderMap>,
468 status: HttpStatus,
469 body: Option<Vec<u8>>,
470 request: &Request,
471 devtools_chan: Option<StdArc<Mutex<Sender<DevtoolsControlMsg>>>>,
472) {
473 if let (Some(devtools_chan), Some(pipeline_id), Some(webview_id)) = (
474 devtools_chan,
475 request.pipeline_id,
476 request.target_webview_id,
477 ) {
478 let browsing_context_id = webview_id.into();
479
480 let devtoolsresponse = DevtoolsHttpResponse {
481 headers,
482 status,
483 body,
484 pipeline_id,
485 browsing_context_id,
486 };
487 let net_event_response = NetworkEvent::HttpResponse(devtoolsresponse);
488
489 let msg =
490 ChromeToDevtoolsControlMsg::NetworkEvent(request.id.0.to_string(), net_event_response);
491
492 let _ = devtools_chan
493 .lock()
494 .unwrap()
495 .send(DevtoolsControlMsg::FromChrome(msg));
496 }
497}
498
499pub fn send_early_httprequest_to_devtools(request: &Request, context: &FetchContext) {
500 if request.url().scheme() == "data" {
502 return;
503 }
504 if let (Some(devtools_chan), Some(browsing_context_id), Some(pipeline_id)) = (
505 context.devtools_chan.as_ref(),
506 request.target_webview_id.map(|id| id.into()),
507 request.pipeline_id,
508 ) {
509 let devtools_request = DevtoolsHttpRequest {
511 url: request.current_url().clone(),
512 method: request.method.clone(),
513 headers: request.headers.clone(),
514 body: None,
515 pipeline_id,
516 started_date_time: SystemTime::now(),
517 time_stamp: 0,
518 connect_time: Duration::from_millis(0),
519 send_time: Duration::from_millis(0),
520 destination: request.destination,
521 is_xhr: false,
522 browsing_context_id,
523 };
524
525 let msg = ChromeToDevtoolsControlMsg::NetworkEvent(
526 request.id.0.to_string(),
527 NetworkEvent::HttpRequest(devtools_request),
528 );
529
530 send_request_to_devtools(msg, &devtools_chan.lock().unwrap());
531 }
532}
533
534fn auth_from_cache(
535 auth_cache: &RwLock<AuthCache>,
536 origin: &ImmutableOrigin,
537) -> Option<Authorization<Basic>> {
538 if let Some(auth_entry) = auth_cache
539 .read()
540 .unwrap()
541 .entries
542 .get(&origin.ascii_serialization())
543 {
544 let user_name = &auth_entry.user_name;
545 let password = &auth_entry.password;
546 Some(Authorization::basic(user_name, password))
547 } else {
548 None
549 }
550}
551
552enum BodyChunk {
555 Chunk(IpcSharedMemory),
557 Done,
559}
560
561enum BodyStream {
563 Chunked(TokioReceiver<Result<Frame<Bytes>, hyper::Error>>),
566 Buffered(UnboundedReceiver<BodyChunk>),
569}
570
571enum BodySink {
574 Chunked(TokioSender<Result<Frame<Bytes>, hyper::Error>>),
576 Buffered(UnboundedSender<BodyChunk>),
580}
581
582impl BodySink {
583 fn transmit_bytes(&self, bytes: IpcSharedMemory) {
584 match self {
585 BodySink::Chunked(sender) => {
586 let sender = sender.clone();
587 spawn_task(async move {
588 let _ = sender
589 .send(Ok(Frame::data(Bytes::copy_from_slice(&bytes))))
590 .await;
591 });
592 },
593 BodySink::Buffered(sender) => {
594 let _ = sender.send(BodyChunk::Chunk(bytes));
595 },
596 }
597 }
598
599 fn close(&self) {
600 match self {
601 BodySink::Chunked(_) => { },
602 BodySink::Buffered(sender) => {
603 let _ = sender.send(BodyChunk::Done);
604 },
605 }
606 }
607}
608
609#[allow(clippy::too_many_arguments)]
610async fn obtain_response(
611 client: &Client<Connector, crate::connector::BoxedBody>,
612 url: &ServoUrl,
613 method: &Method,
614 request_headers: &mut HeaderMap,
615 body: Option<StdArc<Mutex<IpcSender<BodyChunkRequest>>>>,
616 source_is_null: bool,
617 pipeline_id: &Option<PipelineId>,
618 request_id: Option<&str>,
619 destination: Destination,
620 is_xhr: bool,
621 context: &FetchContext,
622 fetch_terminated: UnboundedSender<bool>,
623 browsing_context_id: Option<BrowsingContextId>,
624) -> Result<(HyperResponse<Decoder>, Option<ChromeToDevtoolsControlMsg>), NetworkError> {
625 {
626 let mut headers = request_headers.clone();
627
628 let devtools_bytes = StdArc::new(Mutex::new(vec![]));
629
630 let encoded_url = url
632 .clone()
633 .into_url()
634 .as_ref()
635 .replace('|', "%7C")
636 .replace('{', "%7B")
637 .replace('}', "%7D");
638
639 let request = if let Some(chunk_requester) = body {
640 let (sink, stream) = if source_is_null {
641 headers.insert(TRANSFER_ENCODING, HeaderValue::from_static("chunked"));
644
645 let (sender, receiver) = channel(1);
646 (BodySink::Chunked(sender), BodyStream::Chunked(receiver))
647 } else {
648 let (sender, receiver) = unbounded_channel();
655 (BodySink::Buffered(sender), BodyStream::Buffered(receiver))
656 };
657
658 let (body_chan, body_port) = ipc::channel().unwrap();
659
660 if let Ok(requester) = chunk_requester.lock() {
661 let _ = requester.send(BodyChunkRequest::Connect(body_chan));
662
663 let _ = requester.send(BodyChunkRequest::Chunk);
666 }
667
668 let devtools_bytes = devtools_bytes.clone();
669 let chunk_requester2 = chunk_requester.clone();
670
671 ROUTER.add_typed_route(
672 body_port,
673 Box::new(move |message| {
674 info!("Received message");
675 let bytes = match message.unwrap() {
676 BodyChunkResponse::Chunk(bytes) => bytes,
677 BodyChunkResponse::Done => {
678 let _ = fetch_terminated.send(false);
680 sink.close();
681
682 return;
683 },
684 BodyChunkResponse::Error => {
685 let _ = fetch_terminated.send(true);
689 sink.close();
690
691 return;
692 },
693 };
694
695 devtools_bytes.lock().unwrap().extend_from_slice(&bytes);
696
697 sink.transmit_bytes(bytes);
700
701 let _ = chunk_requester2
704 .lock()
705 .unwrap()
706 .send(BodyChunkRequest::Chunk);
707 }),
708 );
709
710 let body = match stream {
711 BodyStream::Chunked(receiver) => {
712 let stream = ReceiverStream::new(receiver);
713 BoxBody::new(http_body_util::StreamBody::new(stream))
714 },
715 BodyStream::Buffered(mut receiver) => {
716 let mut body = vec![];
718 loop {
719 match receiver.recv().await {
720 Some(BodyChunk::Chunk(bytes)) => {
721 body.extend_from_slice(&bytes);
722 },
723 Some(BodyChunk::Done) => break,
724 None => warn!("Failed to read all chunks from request body."),
725 }
726 }
727 Full::new(body.into()).map_err(|_| unreachable!()).boxed()
728 },
729 };
730 HyperRequest::builder()
731 .method(method)
732 .uri(encoded_url)
733 .body(body)
734 } else {
735 HyperRequest::builder()
736 .method(method)
737 .uri(encoded_url)
738 .body(
739 http_body_util::Empty::new()
740 .map_err(|_| unreachable!())
741 .boxed(),
742 )
743 };
744
745 context
746 .timing
747 .lock()
748 .unwrap()
749 .set_attribute(ResourceAttribute::DomainLookupStart);
750
751 let connect_start = CrossProcessInstant::now();
754 context
755 .timing
756 .lock()
757 .unwrap()
758 .set_attribute(ResourceAttribute::ConnectStart(connect_start));
759
760 if url.scheme() == "https" {
764 context
765 .timing
766 .lock()
767 .unwrap()
768 .set_attribute(ResourceAttribute::SecureConnectionStart);
769 }
770
771 let mut request = match request {
772 Ok(request) => request,
773 Err(e) => return Err(NetworkError::from_http_error(&e)),
774 };
775 *request.headers_mut() = headers.clone();
776
777 let connect_end = CrossProcessInstant::now();
778 context
779 .timing
780 .lock()
781 .unwrap()
782 .set_attribute(ResourceAttribute::ConnectEnd(connect_end));
783
784 let request_id = request_id.map(|v| v.to_owned());
785 let pipeline_id = *pipeline_id;
786 let closure_url = url.clone();
787 let method = method.clone();
788 let send_start = CrossProcessInstant::now();
789
790 let host = request.uri().host().unwrap_or("").to_owned();
791 let override_manager = context.state.override_manager.clone();
792 let headers = headers.clone();
793 let is_secure_scheme = url.is_secure_scheme();
794
795 client
796 .request(request)
797 .and_then(move |res| {
798 let send_end = CrossProcessInstant::now();
799
800 let msg = if let Some(request_id) = request_id {
803 if let Some(pipeline_id) = pipeline_id {
804 if let Some(browsing_context_id) = browsing_context_id {
805 Some(prepare_devtools_request(
806 request_id,
807 closure_url,
808 method.clone(),
809 headers,
810 Some(devtools_bytes.lock().unwrap().clone()),
811 pipeline_id,
812 (connect_end - connect_start).unsigned_abs(),
813 (send_end - send_start).unsigned_abs(),
814 destination,
815 is_xhr,
816 browsing_context_id,
817 ))
818 } else {
819 debug!("Not notifying devtools (no browsing_context_id)");
820 None
821 }
822 } else {
827 debug!("Not notifying devtools (no pipeline_id)");
828 None
829 }
830 } else {
831 debug!("Not notifying devtools (no request_id)");
832 None
833 };
834
835 future::ready(Ok((
836 Decoder::detect(res.map(|r| r.boxed()), is_secure_scheme),
837 msg,
838 )))
839 })
840 .map_err(move |error| {
841 NetworkError::from_hyper_error(
842 &error,
843 override_manager.remove_certificate_failing_verification(host.as_str()),
844 )
845 })
846 .await
847 }
848}
849
850#[async_recursion]
852#[allow(clippy::too_many_arguments)]
853pub async fn http_fetch(
854 fetch_params: &mut FetchParams,
855 cache: &mut CorsCache,
856 cors_flag: bool,
857 cors_preflight_flag: bool,
858 authentication_fetch_flag: bool,
859 target: Target<'async_recursion>,
860 done_chan: &mut DoneChannel,
861 context: &FetchContext,
862) -> Response {
863 *done_chan = None;
865 let request = &mut fetch_params.request;
867
868 let mut response: Option<Response> = None;
871
872 if request.service_workers_mode == ServiceWorkersMode::All {
874 if let Some(ref res) = response {
879 if (res.response_type == ResponseType::Opaque && request.mode != RequestMode::NoCors) ||
887 (res.response_type == ResponseType::OpaqueRedirect &&
888 request.redirect_mode != RedirectMode::Manual) ||
889 (res.url_list.len() > 1 && request.redirect_mode != RedirectMode::Follow) ||
890 res.is_network_error()
891 {
892 return Response::network_error(NetworkError::Internal("Request failed".into()));
893 }
894
895 }
898 }
899
900 if response.is_none() {
902 if cors_preflight_flag {
904 let method_cache_match = cache.match_method(request, request.method.clone());
905
906 let method_mismatch = !method_cache_match &&
907 (!is_cors_safelisted_method(&request.method) || request.use_cors_preflight);
908 let header_mismatch = request.headers.iter().any(|(name, value)| {
909 !cache.match_header(request, name) &&
910 !is_cors_safelisted_request_header(&name, &value)
911 });
912
913 if method_mismatch || header_mismatch {
915 let preflight_result = cors_preflight_fetch(request, cache, context).await;
916 if let Some(e) = preflight_result.get_network_error() {
918 return Response::network_error(e.clone());
919 }
920 }
921 }
922
923 if request.redirect_mode == RedirectMode::Follow {
925 request.service_workers_mode = ServiceWorkersMode::None;
926 }
927
928 context
932 .timing
933 .lock()
934 .unwrap()
935 .set_attribute(ResourceAttribute::RequestStart);
936
937 let mut fetch_result = http_network_or_cache_fetch(
938 fetch_params,
939 authentication_fetch_flag,
940 cors_flag,
941 done_chan,
942 context,
943 )
944 .await;
945
946 if cors_flag && cors_check(&fetch_params.request, &fetch_result).is_err() {
948 return Response::network_error(NetworkError::Internal("CORS check failed".into()));
949 }
950
951 fetch_result.return_internal = false;
952 response = Some(fetch_result);
953 }
954
955 let request = &mut fetch_params.request;
956
957 let mut response = response.unwrap();
959
960 if response
964 .actual_response()
965 .status
966 .try_code()
967 .is_some_and(is_redirect_status)
968 {
969 if response.actual_response().status != StatusCode::SEE_OTHER {
971 }
973
974 let mut location = response
976 .actual_response()
977 .headers
978 .get(header::LOCATION)
979 .and_then(|v| {
980 HeaderValue::to_str(v)
981 .map(|l| {
982 ServoUrl::parse_with_base(response.actual_response().url(), l)
983 .map_err(|err| err.to_string())
984 })
985 .ok()
986 });
987
988 if let Some(Ok(ref mut location)) = location {
990 if location.fragment().is_none() {
991 let current_url = request.current_url();
992 location.set_fragment(current_url.fragment());
993 }
994 }
995 response.actual_response_mut().location_url = location;
996
997 response = match request.redirect_mode {
999 RedirectMode::Error => {
1000 Response::network_error(NetworkError::Internal("Redirect mode error".into()))
1001 },
1002 RedirectMode::Manual => response.to_filtered(ResponseType::OpaqueRedirect),
1003 RedirectMode::Follow => {
1004 response.return_internal = true;
1006 http_redirect_fetch(
1007 fetch_params,
1008 cache,
1009 response,
1010 cors_flag,
1011 target,
1012 done_chan,
1013 context,
1014 )
1015 .await
1016 },
1017 };
1018 }
1019
1020 response.return_internal = true;
1022 context
1023 .timing
1024 .lock()
1025 .unwrap()
1026 .set_attribute(ResourceAttribute::RedirectCount(
1027 fetch_params.request.redirect_count as u16,
1028 ));
1029
1030 response.resource_timing = Arc::clone(&context.timing);
1031
1032 response
1034}
1035
1036struct RedirectEndTimer(Option<Arc<Mutex<ResourceFetchTiming>>>);
1038
1039impl RedirectEndTimer {
1040 fn neuter(&mut self) {
1041 self.0 = None;
1042 }
1043}
1044
1045impl Drop for RedirectEndTimer {
1046 fn drop(&mut self) {
1047 let RedirectEndTimer(resource_fetch_timing_opt) = self;
1048
1049 resource_fetch_timing_opt.as_ref().map_or((), |t| {
1050 t.lock()
1051 .unwrap()
1052 .set_attribute(ResourceAttribute::RedirectEnd(RedirectEndValue::Zero));
1053 })
1054 }
1055}
1056
1057#[async_recursion]
1059pub async fn http_redirect_fetch(
1060 fetch_params: &mut FetchParams,
1061 cache: &mut CorsCache,
1062 response: Response,
1063 cors_flag: bool,
1064 target: Target<'async_recursion>,
1065 done_chan: &mut DoneChannel,
1066 context: &FetchContext,
1067) -> Response {
1068 let mut redirect_end_timer = RedirectEndTimer(Some(context.timing.clone()));
1069
1070 let request = &mut fetch_params.request;
1072
1073 assert!(response.return_internal);
1074
1075 let location_url = response.actual_response().location_url.clone();
1076 let location_url = match location_url {
1077 None => return response,
1079 Some(Err(err)) => {
1081 return Response::network_error(NetworkError::Internal(
1082 "Location URL parse failure: ".to_owned() + &err,
1083 ));
1084 },
1085 Some(Ok(ref url)) if !matches!(url.scheme(), "http" | "https") => {
1087 return Response::network_error(NetworkError::Internal(
1088 "Location URL not an HTTP(S) scheme".into(),
1089 ));
1090 },
1091 Some(Ok(url)) => url,
1092 };
1093
1094 context
1097 .timing
1098 .lock()
1099 .unwrap()
1100 .set_attribute(ResourceAttribute::RedirectStart(
1101 RedirectStartValue::FetchStart,
1102 ));
1103
1104 context
1105 .timing
1106 .lock()
1107 .unwrap()
1108 .set_attribute(ResourceAttribute::FetchStart);
1109
1110 context
1112 .timing
1113 .lock()
1114 .unwrap()
1115 .set_attribute(ResourceAttribute::StartTime(ResourceTimeValue::FetchStart));
1116
1117 context
1118 .timing
1119 .lock()
1120 .unwrap()
1121 .set_attribute(ResourceAttribute::StartTime(
1122 ResourceTimeValue::RedirectStart,
1123 )); if request.redirect_count >= 20 {
1127 return Response::network_error(NetworkError::Internal("Too many redirects".into()));
1128 }
1129
1130 request.redirect_count += 1;
1132
1133 let same_origin = match request.origin {
1135 Origin::Origin(ref origin) => *origin == location_url.origin(),
1136 Origin::Client => panic!(
1137 "Request origin should not be client for {}",
1138 request.current_url()
1139 ),
1140 };
1141
1142 let has_credentials = has_credentials(&location_url);
1143
1144 if request.mode == RequestMode::CorsMode && !same_origin && has_credentials {
1145 return Response::network_error(NetworkError::Internal(
1146 "Cross-origin credentials check failed".into(),
1147 ));
1148 }
1149
1150 if cors_flag && location_url.origin() != request.current_url().origin() {
1152 request.origin = Origin::Origin(ImmutableOrigin::new_opaque());
1153 }
1154
1155 if cors_flag && has_credentials {
1157 return Response::network_error(NetworkError::Internal("Credentials check failed".into()));
1158 }
1159
1160 if response.actual_response().status != StatusCode::SEE_OTHER &&
1163 request.body.as_ref().is_some_and(|b| b.source_is_null())
1164 {
1165 return Response::network_error(NetworkError::Internal("Request body is not done".into()));
1166 }
1167
1168 if response
1170 .actual_response()
1171 .status
1172 .try_code()
1173 .is_some_and(|code| {
1174 ((code == StatusCode::MOVED_PERMANENTLY || code == StatusCode::FOUND) &&
1175 request.method == Method::POST) ||
1176 (code == StatusCode::SEE_OTHER &&
1177 request.method != Method::HEAD &&
1178 request.method != Method::GET)
1179 })
1180 {
1181 request.method = Method::GET;
1183 request.body = None;
1184 for name in &[
1186 CONTENT_ENCODING,
1187 CONTENT_LANGUAGE,
1188 CONTENT_LOCATION,
1189 CONTENT_TYPE,
1190 ] {
1191 request.headers.remove(name);
1192 }
1193 }
1194
1195 if location_url.origin() != request.current_url().origin() {
1199 request.headers.remove(AUTHORIZATION);
1202 }
1203
1204 if let Some(body) = request.body.as_mut() {
1207 body.extract_source();
1208 }
1209
1210 request.url_list.push(location_url);
1214
1215 set_requests_referrer_policy_on_redirect(request, response.actual_response());
1217
1218 let recursive_flag = request.redirect_mode != RedirectMode::Manual;
1221
1222 let fetch_response = main_fetch(
1224 fetch_params,
1225 cache,
1226 recursive_flag,
1227 target,
1228 done_chan,
1229 context,
1230 )
1231 .await;
1232
1233 context
1235 .timing
1236 .lock()
1237 .unwrap()
1238 .set_attribute(ResourceAttribute::RedirectEnd(
1239 RedirectEndValue::ResponseEnd,
1240 ));
1241 redirect_end_timer.neuter();
1242
1243 fetch_response
1244}
1245
1246#[async_recursion]
1248async fn http_network_or_cache_fetch(
1249 fetch_params: &mut FetchParams,
1250 authentication_fetch_flag: bool,
1251 cors_flag: bool,
1252 done_chan: &mut DoneChannel,
1253 context: &FetchContext,
1254) -> Response {
1255 let request = &mut fetch_params.request;
1257
1258 let http_fetch_params: &mut FetchParams;
1260 let mut fetch_params_copy: FetchParams;
1261
1262 let mut response: Option<Response> = None;
1266
1267 let mut revalidating_flag = false;
1269
1270 let request_has_no_window = request.window == RequestWindow::NoWindow;
1274
1275 let http_request = if request_has_no_window && request.redirect_mode == RedirectMode::Error {
1276 http_fetch_params = fetch_params;
1277 &mut http_fetch_params.request
1278 }
1279 else {
1281 fetch_params_copy = fetch_params.clone();
1284 http_fetch_params = &mut fetch_params_copy;
1285
1286 &mut http_fetch_params.request
1287 };
1288
1289 let include_credentials = match http_request.credentials_mode {
1291 CredentialsMode::Include => true,
1293 CredentialsMode::CredentialsSameOrigin
1295 if http_request.response_tainting == ResponseTainting::Basic =>
1296 {
1297 true
1298 },
1299 _ => false,
1300 };
1301
1302 let content_length = http_request
1309 .body
1310 .as_ref()
1311 .and_then(|body| body.len().map(|size| size as u64));
1312
1313 let mut content_length_header_value = None;
1315
1316 if http_request.body.is_none() && matches!(http_request.method, Method::POST | Method::PUT) {
1319 content_length_header_value = Some(0);
1320 }
1321
1322 if let Some(content_length) = content_length {
1326 content_length_header_value = Some(content_length);
1327 };
1328
1329 if let Some(content_length_header_value) = content_length_header_value {
1332 http_request
1333 .headers
1334 .typed_insert(ContentLength(content_length_header_value));
1335 }
1336
1337 if content_length.is_some() && http_request.keep_alive {
1339 }
1341
1342 match http_request.referrer {
1344 Referrer::ReferrerUrl(ref http_request_referrer) |
1345 Referrer::Client(ref http_request_referrer) => {
1346 if let Ok(referer) = http_request_referrer.to_string().parse::<Referer>() {
1349 http_request.headers.typed_insert(referer);
1351 } else {
1352 error!("Failed to parse {} as referrer", http_request_referrer);
1356 }
1357 },
1358 _ => {},
1359 };
1360
1361 append_a_request_origin_header(http_request);
1363
1364 append_the_fetch_metadata_headers(http_request);
1366
1367 if http_request.initiator == Initiator::Prefetch {
1370 if let Ok(value) = HeaderValue::from_str("prefetch") {
1371 http_request.headers.insert("Sec-Purpose", value);
1372 }
1373 }
1374
1375 if !http_request.headers.contains_key(header::USER_AGENT) {
1378 http_request
1379 .headers
1380 .typed_insert::<UserAgent>(context.user_agent.parse().unwrap());
1381 }
1382
1383 match http_request.cache_mode {
1385 CacheMode::Default if is_no_store_cache(&http_request.headers) => {
1389 http_request.cache_mode = CacheMode::NoStore;
1390 },
1391
1392 CacheMode::NoCache if !http_request.headers.contains_key(header::CACHE_CONTROL) => {
1402 http_request
1403 .headers
1404 .typed_insert(CacheControl::new().with_max_age(Duration::from_secs(0)));
1405 },
1406
1407 CacheMode::Reload | CacheMode::NoStore => {
1409 if !http_request.headers.contains_key(header::PRAGMA) {
1412 http_request.headers.typed_insert(Pragma::no_cache());
1413 }
1414
1415 if !http_request.headers.contains_key(header::CACHE_CONTROL) {
1418 http_request
1419 .headers
1420 .typed_insert(CacheControl::new().with_no_cache());
1421 }
1422 },
1423
1424 _ => {},
1425 }
1426
1427 if http_request.headers.contains_key(header::RANGE) {
1430 if let Ok(value) = HeaderValue::from_str("identity") {
1431 http_request.headers.insert("Accept-Encoding", value);
1432 }
1433 }
1434
1435 http_request.headers.remove(header::HOST);
1439 set_default_accept_encoding(&mut http_request.headers);
1441
1442 let current_url = http_request.current_url();
1443
1444 if include_credentials {
1447 set_request_cookies(
1451 ¤t_url,
1452 &mut http_request.headers,
1453 &context.state.cookie_jar,
1454 );
1455 if !http_request.headers.contains_key(header::AUTHORIZATION) {
1457 let mut authorization_value = None;
1459
1460 if let Some(basic) = auth_from_cache(&context.state.auth_cache, ¤t_url.origin()) {
1462 if !http_request.use_url_credentials || !has_credentials(¤t_url) {
1463 authorization_value = Some(basic);
1464 }
1465 }
1466
1467 if authentication_fetch_flag &&
1469 authorization_value.is_none() &&
1470 has_credentials(¤t_url)
1471 {
1472 authorization_value = Some(Authorization::basic(
1473 current_url.username(),
1474 current_url.password().unwrap_or(""),
1475 ));
1476 }
1477
1478 if let Some(basic) = authorization_value {
1480 http_request.headers.typed_insert(basic);
1481 }
1482 }
1483 }
1484
1485 {
1495 let (lock, cvar) = {
1496 let entry_key = CacheKey::new(http_request);
1497 let mut state_map = context.state.http_cache_state.lock().unwrap();
1498 &*state_map
1499 .entry(entry_key)
1500 .or_insert_with(|| {
1501 Arc::new((
1502 Mutex::new(HttpCacheEntryState::ReadyToConstruct),
1503 Condvar::new(),
1504 ))
1505 })
1506 .clone()
1507 };
1508
1509 let mut state = lock.lock().unwrap();
1511 while let HttpCacheEntryState::PendingStore(_) = *state {
1512 let (current_state, time_out) = cvar
1513 .wait_timeout(state, Duration::from_millis(500))
1514 .unwrap();
1515 state = current_state;
1516 if time_out.timed_out() {
1517 break;
1519 }
1520 }
1521
1522 if let Ok(http_cache) = context.state.http_cache.read() {
1525 let stored_response = http_cache.construct_response(http_request, done_chan);
1529
1530 if let Some(response_from_cache) = stored_response {
1532 let response_headers = response_from_cache.response.headers.clone();
1533 let (cached_response, needs_revalidation) =
1535 match (http_request.cache_mode, &http_request.mode) {
1536 (CacheMode::ForceCache, _) => (Some(response_from_cache.response), false),
1537 (CacheMode::OnlyIfCached, &RequestMode::SameOrigin) => {
1538 (Some(response_from_cache.response), false)
1539 },
1540 (CacheMode::OnlyIfCached, _) |
1541 (CacheMode::NoStore, _) |
1542 (CacheMode::Reload, _) => (None, false),
1543 (_, _) => (
1544 Some(response_from_cache.response),
1545 response_from_cache.needs_validation,
1546 ),
1547 };
1548
1549 if needs_revalidation {
1550 revalidating_flag = true;
1551 if let Some(http_date) = response_headers.typed_get::<LastModified>() {
1553 let http_date: SystemTime = http_date.into();
1554 http_request
1555 .headers
1556 .typed_insert(IfModifiedSince::from(http_date));
1557 }
1558 if let Some(entity_tag) = response_headers.get(header::ETAG) {
1559 http_request
1560 .headers
1561 .insert(header::IF_NONE_MATCH, entity_tag.clone());
1562 }
1563 } else {
1564 response = cached_response;
1566 }
1567 if response.is_none() {
1568 *done_chan = None;
1571
1572 if let HttpCacheEntryState::PendingStore(i) = *state {
1575 let new = i + 1;
1576 *state = HttpCacheEntryState::PendingStore(new);
1577 } else {
1578 *state = HttpCacheEntryState::PendingStore(1);
1579 }
1580 }
1581 }
1582 }
1583 if *state == HttpCacheEntryState::ReadyToConstruct {
1585 cvar.notify_one();
1586 }
1587 }
1589
1590 fn update_http_cache_state(context: &FetchContext, http_request: &Request) {
1594 let (lock, cvar) = {
1595 let entry_key = CacheKey::new(http_request);
1596 let mut state_map = context.state.http_cache_state.lock().unwrap();
1597 &*state_map
1598 .get_mut(&entry_key)
1599 .expect("Entry in http-cache state to have been previously inserted")
1600 .clone()
1601 };
1602 let mut state = lock.lock().unwrap();
1603 if let HttpCacheEntryState::PendingStore(i) = *state {
1604 let new = i - 1;
1605 if new == 0 {
1606 *state = HttpCacheEntryState::ReadyToConstruct;
1607 cvar.notify_one();
1609 } else {
1610 *state = HttpCacheEntryState::PendingStore(new);
1611 }
1612 }
1613 }
1614
1615 async fn wait_for_cached_response(
1616 done_chan: &mut DoneChannel,
1617 response: &mut Option<Response>,
1618 ) {
1619 if let Some(ref mut ch) = *done_chan {
1620 assert!(response.is_some());
1624
1625 loop {
1626 match ch.1.recv().await {
1627 Some(Data::Payload(_)) => {},
1628 Some(Data::Done) => break, Some(Data::Cancelled) => {
1630 *response = None;
1633 break;
1634 },
1635 _ => panic!("HTTP cache should always send Done or Cancelled"),
1636 }
1637 }
1638 }
1639 *done_chan = None;
1641 }
1642
1643 wait_for_cached_response(done_chan, &mut response).await;
1644
1645 if response.is_none() {
1649 if http_request.cache_mode == CacheMode::OnlyIfCached {
1651 update_http_cache_state(context, http_request);
1654 return Response::network_error(NetworkError::Internal(
1655 "Couldn't find response in cache".into(),
1656 ));
1657 }
1658
1659 let forward_response =
1662 http_network_fetch(http_fetch_params, include_credentials, done_chan, context).await;
1663
1664 let http_request = &mut http_fetch_params.request;
1665 if forward_response.status.in_range(200..=399) && !http_request.method.is_safe() {
1669 if let Ok(mut http_cache) = context.state.http_cache.write() {
1670 http_cache.invalidate(http_request, &forward_response);
1671 }
1672 }
1673
1674 if revalidating_flag && forward_response.status == StatusCode::NOT_MODIFIED {
1676 if let Ok(mut http_cache) = context.state.http_cache.write() {
1677 *done_chan = None;
1680 response = http_cache.refresh(http_request, forward_response.clone(), done_chan);
1681 }
1682 wait_for_cached_response(done_chan, &mut response).await;
1683 }
1684
1685 if response.is_none() {
1687 let forward_response = response.insert(forward_response);
1689
1690 if http_request.cache_mode != CacheMode::NoStore {
1693 if let Ok(mut http_cache) = context.state.http_cache.write() {
1696 http_cache.store(http_request, forward_response);
1697 }
1698 }
1699 }
1700 }
1701
1702 let http_request = &mut http_fetch_params.request;
1703 update_http_cache_state(context, http_request);
1705
1706 let mut response = response.unwrap();
1707
1708 if http_request.response_tainting != ResponseTainting::CorsTainting &&
1711 cross_origin_resource_policy_check(http_request, &response) ==
1712 CrossOriginResourcePolicy::Blocked
1713 {
1714 return Response::network_error(NetworkError::Internal(
1715 "Cross-origin resource policy check failed".into(),
1716 ));
1717 }
1718
1719 if http_request.headers.contains_key(RANGE) {
1723 response.range_requested = true;
1724 }
1725
1726 if response.status.try_code() == Some(StatusCode::UNAUTHORIZED) &&
1734 !cors_flag &&
1735 include_credentials &&
1736 response.headers.contains_key(WWW_AUTHENTICATE)
1737 {
1738 let request = &mut fetch_params.request;
1741
1742 if request.body.is_some() {
1744 }
1746
1747 if !request.use_url_credentials || authentication_fetch_flag {
1749 let Some(credentials) = context.state.request_authentication(request, &response) else {
1750 return response;
1751 };
1752
1753 if let Err(err) = request
1754 .current_url_mut()
1755 .set_username(&credentials.username)
1756 {
1757 error!("error setting username for url: {:?}", err);
1758 return response;
1759 };
1760
1761 if let Err(err) = request
1762 .current_url_mut()
1763 .set_password(Some(&credentials.password))
1764 {
1765 error!("error setting password for url: {:?}", err);
1766 return response;
1767 };
1768 }
1769
1770 *done_chan = None;
1773
1774 response = http_network_or_cache_fetch(
1776 fetch_params,
1777 true, cors_flag,
1779 done_chan,
1780 context,
1781 )
1782 .await;
1783 }
1784
1785 if response.status == StatusCode::PROXY_AUTHENTICATION_REQUIRED {
1787 let request = &mut fetch_params.request;
1788 if request_has_no_window {
1791 return Response::network_error(NetworkError::Internal(
1792 "Can't find Window object".into(),
1793 ));
1794 }
1795
1796 let Some(credentials) = context.state.request_authentication(request, &response) else {
1804 return response;
1805 };
1806
1807 let entry = AuthCacheEntry {
1809 user_name: credentials.username,
1810 password: credentials.password,
1811 };
1812 {
1813 let mut auth_cache = context.state.auth_cache.write().unwrap();
1814 let key = request.current_url().origin().ascii_serialization();
1815 auth_cache.entries.insert(key, entry);
1816 }
1817
1818 *done_chan = None;
1821
1822 response = http_network_or_cache_fetch(
1824 fetch_params,
1825 false, cors_flag,
1827 done_chan,
1828 context,
1829 )
1830 .await;
1831 }
1832
1833 if authentication_fetch_flag {
1841 }
1843
1844 response
1846}
1847
1848#[derive(PartialEq)]
1852enum CrossOriginResourcePolicy {
1853 Allowed,
1854 Blocked,
1855}
1856
1857fn cross_origin_resource_policy_check(
1860 request: &Request,
1861 response: &Response,
1862) -> CrossOriginResourcePolicy {
1863 if request.mode != RequestMode::NoCors {
1865 return CrossOriginResourcePolicy::Allowed;
1866 }
1867
1868 let current_url_origin = request.current_url().origin();
1870 let same_origin = if let Origin::Origin(ref origin) = request.origin {
1871 *origin == request.current_url().origin()
1872 } else {
1873 false
1874 };
1875
1876 if same_origin {
1877 return CrossOriginResourcePolicy::Allowed;
1878 }
1879
1880 let policy = response
1882 .headers
1883 .get(HeaderName::from_static("cross-origin-resource-policy"))
1884 .map(|h| h.to_str().unwrap_or(""))
1885 .unwrap_or("");
1886
1887 if policy == "same-origin" {
1889 return CrossOriginResourcePolicy::Blocked;
1890 }
1891
1892 if let Origin::Origin(ref request_origin) = request.origin {
1894 let schemeless_same_origin = is_schemelessy_same_site(request_origin, ¤t_url_origin);
1895 if schemeless_same_origin &&
1896 (request_origin.scheme() == Some("https") ||
1897 response.https_state == HttpsState::None)
1898 {
1899 return CrossOriginResourcePolicy::Allowed;
1900 }
1901 };
1902
1903 if policy == "same-site" {
1905 return CrossOriginResourcePolicy::Blocked;
1906 }
1907
1908 CrossOriginResourcePolicy::Allowed
1909}
1910
1911struct ResponseEndTimer(Option<Arc<Mutex<ResourceFetchTiming>>>);
1913
1914impl ResponseEndTimer {
1915 fn neuter(&mut self) {
1916 self.0 = None;
1917 }
1918}
1919
1920impl Drop for ResponseEndTimer {
1921 fn drop(&mut self) {
1922 let ResponseEndTimer(resource_fetch_timing_opt) = self;
1923
1924 resource_fetch_timing_opt.as_ref().map_or((), |t| {
1925 t.lock()
1926 .unwrap()
1927 .set_attribute(ResourceAttribute::ResponseEnd);
1928 })
1929 }
1930}
1931
1932async fn http_network_fetch(
1934 fetch_params: &mut FetchParams,
1935 credentials_flag: bool,
1936 done_chan: &mut DoneChannel,
1937 context: &FetchContext,
1938) -> Response {
1939 let mut response_end_timer = ResponseEndTimer(Some(context.timing.clone()));
1940
1941 let request = &mut fetch_params.request;
1943
1944 let url = request.current_url();
1955 let request_id = request.id.0.to_string();
1956 if log_enabled!(log::Level::Info) {
1957 info!("{:?} request for {}", request.method, url);
1958 for header in request.headers.iter() {
1959 debug!(" - {:?}", header);
1960 }
1961 }
1962
1963 let is_xhr = request.destination == Destination::None;
1967
1968 let (fetch_terminated_sender, mut fetch_terminated_receiver) = unbounded_channel();
1970
1971 let body = request.body.as_ref().map(|body| body.take_stream());
1972
1973 if body.is_none() {
1974 let _ = fetch_terminated_sender.send(false);
1979 }
1980
1981 let browsing_context_id = request.target_webview_id.map(Into::into);
1982
1983 let response_future = obtain_response(
1984 &context.state.client,
1985 &url,
1986 &request.method,
1987 &mut request.headers,
1988 body,
1989 request
1990 .body
1991 .as_ref()
1992 .map(|body| body.source_is_null())
1993 .unwrap_or(false),
1994 &request.pipeline_id,
1995 Some(&request_id),
1996 request.destination,
1997 is_xhr,
1998 context,
1999 fetch_terminated_sender,
2000 browsing_context_id,
2001 );
2002
2003 let (res, msg) = match response_future.await {
2005 Ok(wrapped_response) => wrapped_response,
2006 Err(error) => return Response::network_error(error),
2007 };
2008
2009 if log_enabled!(log::Level::Info) {
2010 debug!("{:?} response for {}", res.version(), url);
2011 for header in res.headers().iter() {
2012 debug!(" - {:?}", header);
2013 }
2014 }
2015
2016 match fetch_terminated_receiver.recv().await {
2019 Some(true) => {
2020 return Response::network_error(NetworkError::Internal(
2021 "Request body streaming failed.".into(),
2022 ));
2023 },
2024 Some(false) => {},
2025 _ => warn!("Failed to receive confirmation request was streamed without error."),
2026 }
2027
2028 let header_strings: Vec<&str> = res
2029 .headers()
2030 .get_all("Timing-Allow-Origin")
2031 .iter()
2032 .map(|header_value| header_value.to_str().unwrap_or(""))
2033 .collect();
2034 let wildcard_present = header_strings.contains(&"*");
2035 let req_origin_in_timing_allow = header_strings
2039 .iter()
2040 .any(|header_str| match request.origin {
2041 SpecificOrigin(ref immutable_request_origin) => {
2042 *header_str == immutable_request_origin.ascii_serialization()
2043 },
2044 _ => false,
2045 });
2046
2047 let is_same_origin = request.url_list.iter().all(|url| match request.origin {
2048 SpecificOrigin(ref immutable_request_origin) => url.origin() == *immutable_request_origin,
2049 _ => false,
2050 });
2051
2052 if !(is_same_origin || req_origin_in_timing_allow || wildcard_present) {
2053 context.timing.lock().unwrap().mark_timing_check_failed();
2054 }
2055
2056 let timing = context.timing.lock().unwrap().clone();
2057 let mut response = Response::new(url.clone(), timing);
2058
2059 let status_text = res
2060 .extensions()
2061 .get::<ReasonPhrase>()
2062 .map(ReasonPhrase::as_bytes)
2063 .or_else(|| res.status().canonical_reason().map(str::as_bytes))
2064 .map(Vec::from)
2065 .unwrap_or_default();
2066 response.status = HttpStatus::new(res.status(), status_text);
2067
2068 info!("got {:?} response for {:?}", res.status(), request.url());
2069 response.headers = res.headers().clone();
2070 response.referrer = request.referrer.to_url().cloned();
2071 response.referrer_policy = request.referrer_policy;
2072
2073 let res_body = response.body.clone();
2074
2075 let (done_sender, done_receiver) = unbounded_channel();
2077 *done_chan = Some((done_sender.clone(), done_receiver));
2078
2079 let devtools_sender = context.devtools_chan.clone();
2080 let cancellation_listener = context.cancellation_listener.clone();
2081 if cancellation_listener.cancelled() {
2082 return Response::network_error(NetworkError::Internal("Fetch aborted".into()));
2083 }
2084
2085 *res_body.lock().unwrap() = ResponseBody::Receiving(vec![]);
2086 let res_body2 = res_body.clone();
2087
2088 if let Some(ref sender) = devtools_sender {
2089 let sender = sender.lock().unwrap();
2090 if let Some(m) = msg {
2091 send_request_to_devtools(m, &sender);
2092 }
2093 }
2094
2095 let done_sender2 = done_sender.clone();
2096 let done_sender3 = done_sender.clone();
2097 let timing_ptr2 = context.timing.clone();
2098 let timing_ptr3 = context.timing.clone();
2099 let devtools_request = request.clone();
2100 let url1 = devtools_request.url();
2101 let url2 = url1.clone();
2102
2103 let status = response.status.clone();
2104 let headers = response.headers.clone();
2105 let devtools_chan = context.devtools_chan.clone();
2106
2107 spawn_task(
2108 res.into_body()
2109 .map_err(|e| {
2110 warn!("Error streaming response body: {:?}", e);
2111 })
2112 .try_fold(res_body, move |res_body, chunk| {
2113 if cancellation_listener.cancelled() {
2114 *res_body.lock().unwrap() = ResponseBody::Done(vec![]);
2115 let _ = done_sender.send(Data::Cancelled);
2116 return future::ready(Err(()));
2117 }
2118 if let ResponseBody::Receiving(ref mut body) = *res_body.lock().unwrap() {
2119 let bytes = chunk;
2120 body.extend_from_slice(&bytes);
2121 let _ = done_sender.send(Data::Payload(bytes.to_vec()));
2122 }
2123 future::ready(Ok(res_body))
2124 })
2125 .and_then(move |res_body| {
2126 debug!("successfully finished response for {:?}", url1);
2127 let mut body = res_body.lock().unwrap();
2128 let completed_body = match *body {
2129 ResponseBody::Receiving(ref mut body) => std::mem::take(body),
2130 _ => vec![],
2131 };
2132 let devtools_response_body = completed_body.clone();
2133 *body = ResponseBody::Done(completed_body);
2134 send_response_values_to_devtools(
2135 Some(headers),
2136 status,
2137 Some(devtools_response_body),
2138 &devtools_request,
2139 devtools_chan,
2140 );
2141 timing_ptr2
2142 .lock()
2143 .unwrap()
2144 .set_attribute(ResourceAttribute::ResponseEnd);
2145 let _ = done_sender2.send(Data::Done);
2146 future::ready(Ok(()))
2147 })
2148 .map_err(move |_| {
2149 debug!("finished response for {:?}", url2);
2150 let mut body = res_body2.lock().unwrap();
2151 let completed_body = match *body {
2152 ResponseBody::Receiving(ref mut body) => std::mem::take(body),
2153 _ => vec![],
2154 };
2155 *body = ResponseBody::Done(completed_body);
2156 timing_ptr3
2157 .lock()
2158 .unwrap()
2159 .set_attribute(ResourceAttribute::ResponseEnd);
2160 let _ = done_sender3.send(Data::Done);
2161 }),
2162 );
2163
2164 response.https_state = match url.scheme() {
2170 "https" => HttpsState::Modern,
2171 _ => HttpsState::None,
2172 };
2173
2174 if credentials_flag {
2187 set_cookies_from_headers(&url, &response.headers, &context.state.cookie_jar);
2188 }
2189 context
2190 .state
2191 .hsts_list
2192 .write()
2193 .unwrap()
2194 .update_hsts_list_from_response(&url, &response.headers);
2195
2196 response_end_timer.neuter();
2210
2211 response
2212}
2213
2214async fn cors_preflight_fetch(
2216 request: &Request,
2217 cache: &mut CorsCache,
2218 context: &FetchContext,
2219) -> Response {
2220 let mut preflight = RequestBuilder::new(
2225 request.target_webview_id,
2226 request.current_url(),
2227 request.referrer.clone(),
2228 )
2229 .method(Method::OPTIONS)
2230 .origin(match &request.origin {
2231 Origin::Client => {
2232 unreachable!("We shouldn't get Client origin in cors_preflight_fetch.")
2233 },
2234 Origin::Origin(origin) => origin.clone(),
2235 })
2236 .pipeline_id(request.pipeline_id)
2237 .initiator(request.initiator)
2238 .destination(request.destination)
2239 .referrer_policy(request.referrer_policy)
2240 .mode(RequestMode::CorsMode)
2241 .response_tainting(ResponseTainting::CorsTainting)
2242 .build();
2243
2244 preflight
2246 .headers
2247 .insert(ACCEPT, HeaderValue::from_static("*/*"));
2248
2249 preflight
2251 .headers
2252 .typed_insert::<AccessControlRequestMethod>(AccessControlRequestMethod::from(
2253 request.method.clone(),
2254 ));
2255
2256 let headers = get_cors_unsafe_header_names(&request.headers);
2258
2259 if !headers.is_empty() {
2261 preflight.headers.insert(
2264 ACCESS_CONTROL_REQUEST_HEADERS,
2265 HeaderValue::from_bytes(itertools::join(headers.iter(), ",").as_bytes())
2266 .unwrap_or(HeaderValue::from_static("")),
2267 );
2268 }
2269
2270 let mut fetch_params = FetchParams::new(preflight);
2273 let response =
2274 http_network_or_cache_fetch(&mut fetch_params, false, false, &mut None, context).await;
2275
2276 if cors_check(request, &response).is_ok() && response.status.code().is_success() {
2278 let mut methods = if response
2281 .headers
2282 .contains_key(header::ACCESS_CONTROL_ALLOW_METHODS)
2283 {
2284 match response.headers.typed_get::<AccessControlAllowMethods>() {
2285 Some(methods) => methods.iter().collect(),
2286 None => {
2288 return Response::network_error(NetworkError::Internal(
2289 "CORS ACAM check failed".into(),
2290 ));
2291 },
2292 }
2293 } else {
2294 vec![]
2295 };
2296
2297 let header_names = if response
2300 .headers
2301 .contains_key(header::ACCESS_CONTROL_ALLOW_HEADERS)
2302 {
2303 match response.headers.typed_get::<AccessControlAllowHeaders>() {
2304 Some(names) => names.iter().collect(),
2305 None => {
2307 return Response::network_error(NetworkError::Internal(
2308 "CORS ACAH check failed".into(),
2309 ));
2310 },
2311 }
2312 } else {
2313 vec![]
2314 };
2315
2316 debug!(
2317 "CORS check: Allowed methods: {:?}, current method: {:?}",
2318 methods, request.method
2319 );
2320
2321 if methods.is_empty() && request.use_cors_preflight {
2324 methods = vec![request.method.clone()];
2325 }
2326
2327 if methods
2330 .iter()
2331 .all(|method| *method.as_str() != *request.method.as_ref()) &&
2332 !is_cors_safelisted_method(&request.method) &&
2333 (request.credentials_mode == CredentialsMode::Include ||
2334 methods.iter().all(|method| method.as_ref() != "*"))
2335 {
2336 return Response::network_error(NetworkError::Internal(
2337 "CORS method check failed".into(),
2338 ));
2339 }
2340
2341 debug!(
2342 "CORS check: Allowed headers: {:?}, current headers: {:?}",
2343 header_names, request.headers
2344 );
2345
2346 if request.headers.iter().any(|(name, _)| {
2349 is_cors_non_wildcard_request_header_name(name) &&
2350 header_names.iter().all(|header_name| header_name != name)
2351 }) {
2352 return Response::network_error(NetworkError::Internal(
2353 "CORS authorization check failed".into(),
2354 ));
2355 }
2356
2357 let unsafe_names = get_cors_unsafe_header_names(&request.headers);
2361 let header_names_set: HashSet<&HeaderName> = HashSet::from_iter(header_names.iter());
2362 let header_names_contains_star = header_names
2363 .iter()
2364 .any(|header_name| header_name.as_str() == "*");
2365 for unsafe_name in unsafe_names.iter() {
2366 if !header_names_set.contains(unsafe_name) &&
2367 (request.credentials_mode == CredentialsMode::Include ||
2368 !header_names_contains_star)
2369 {
2370 return Response::network_error(NetworkError::Internal(
2371 "CORS headers check failed".into(),
2372 ));
2373 }
2374 }
2375
2376 let max_age: Option<Duration> = response
2379 .headers
2380 .typed_get::<AccessControlMaxAge>()
2381 .map(|acma| acma.into());
2382
2383 let max_age = max_age.unwrap_or(Duration::from_secs(5));
2385
2386 for method in &methods {
2397 cache.match_method_and_update(request, method.clone(), max_age);
2398 }
2399
2400 for header_name in &header_names {
2405 cache.match_header_and_update(request, header_name, max_age);
2406 }
2407
2408 return response;
2410 }
2411
2412 Response::network_error(NetworkError::Internal("CORS check failed".into()))
2414}
2415
2416fn cors_check(request: &Request, response: &Response) -> Result<(), ()> {
2418 let origin = response.headers.typed_get::<AccessControlAllowOrigin>();
2420
2421 let origin = origin.ok_or(())?;
2423
2424 if request.credentials_mode != CredentialsMode::Include &&
2426 origin == AccessControlAllowOrigin::ANY
2427 {
2428 return Ok(());
2429 }
2430
2431 let origin = match origin.origin() {
2433 Some(origin) => origin,
2434 None => return Err(()),
2436 };
2437
2438 match request.origin {
2439 Origin::Origin(ref o) if o.ascii_serialization() == origin.to_string().trim() => {},
2440 _ => return Err(()),
2441 }
2442
2443 if request.credentials_mode != CredentialsMode::Include {
2445 return Ok(());
2446 }
2447
2448 let credentials = response
2450 .headers
2451 .typed_get::<AccessControlAllowCredentials>();
2452
2453 if credentials.is_some() {
2455 return Ok(());
2456 }
2457
2458 Err(())
2460}
2461
2462fn has_credentials(url: &ServoUrl) -> bool {
2463 !url.username().is_empty() || url.password().is_some()
2464}
2465
2466fn is_no_store_cache(headers: &HeaderMap) -> bool {
2467 headers.contains_key(header::IF_MODIFIED_SINCE) |
2468 headers.contains_key(header::IF_NONE_MATCH) |
2469 headers.contains_key(header::IF_UNMODIFIED_SINCE) |
2470 headers.contains_key(header::IF_MATCH) |
2471 headers.contains_key(header::IF_RANGE)
2472}
2473
2474fn is_redirect_status(status: StatusCode) -> bool {
2476 matches!(
2477 status,
2478 StatusCode::MOVED_PERMANENTLY |
2479 StatusCode::FOUND |
2480 StatusCode::SEE_OTHER |
2481 StatusCode::TEMPORARY_REDIRECT |
2482 StatusCode::PERMANENT_REDIRECT
2483 )
2484}
2485
2486fn request_has_redirect_tainted_origin(request: &Request) -> bool {
2488 let Origin::Origin(request_origin) = &request.origin else {
2490 panic!("origin cannot be \"client\" at this point in time");
2491 };
2492
2493 let mut last_url = None;
2495
2496 for url in &request.url_list {
2498 let Some(last_url) = &mut last_url else {
2500 last_url = Some(url);
2501 continue;
2502 };
2503
2504 if url.origin() != last_url.origin() && *request_origin != last_url.origin() {
2507 return true;
2508 }
2509
2510 *last_url = url;
2512 }
2513
2514 false
2516}
2517
2518fn serialize_request_origin(request: &Request) -> headers::Origin {
2520 let Origin::Origin(origin) = &request.origin else {
2522 panic!("origin cannot be \"client\" at this point in time");
2523 };
2524
2525 if request_has_redirect_tainted_origin(request) {
2527 return headers::Origin::NULL;
2528 }
2529
2530 serialize_origin(origin)
2532}
2533
2534pub fn serialize_origin(origin: &ImmutableOrigin) -> headers::Origin {
2536 match origin {
2537 ImmutableOrigin::Opaque(_) => headers::Origin::NULL,
2538 ImmutableOrigin::Tuple(scheme, host, port) => {
2539 let port = match (scheme.as_ref(), port) {
2542 ("http" | "ws", 80) | ("https" | "wss", 443) | ("ftp", 21) => None,
2543 _ => Some(*port),
2544 };
2545
2546 headers::Origin::try_from_parts(scheme, &host.to_string(), port)
2548 .unwrap_or(headers::Origin::NULL)
2549 },
2550 }
2551}
2552
2553fn append_a_request_origin_header(request: &mut Request) {
2555 let Origin::Origin(request_origin) = &request.origin else {
2557 panic!("origin cannot be \"client\" at this point in time");
2558 };
2559
2560 let mut serialized_origin = serialize_request_origin(request);
2562
2563 if request.response_tainting == ResponseTainting::CorsTainting ||
2566 matches!(request.mode, RequestMode::WebSocket { .. })
2567 {
2568 request.headers.typed_insert(serialized_origin);
2569 }
2570 else if !matches!(request.method, Method::GET | Method::HEAD) {
2572 if request.mode != RequestMode::CorsMode {
2574 match request.referrer_policy {
2575 ReferrerPolicy::NoReferrer => {
2576 serialized_origin = headers::Origin::NULL;
2578 },
2579 ReferrerPolicy::NoReferrerWhenDowngrade |
2580 ReferrerPolicy::StrictOrigin |
2581 ReferrerPolicy::StrictOriginWhenCrossOrigin => {
2582 if let ImmutableOrigin::Tuple(scheme, _, _) = &request_origin {
2585 if scheme == "https" && request.current_url().scheme() != "https" {
2586 serialized_origin = headers::Origin::NULL;
2587 }
2588 }
2589 },
2590 ReferrerPolicy::SameOrigin => {
2591 if *request_origin != request.current_url().origin() {
2594 serialized_origin = headers::Origin::NULL;
2595 }
2596 },
2597 _ => {
2598 },
2600 };
2601 }
2602
2603 request.headers.typed_insert(serialized_origin);
2605 }
2606}
2607
2608fn append_the_fetch_metadata_headers(r: &mut Request) {
2610 if !r.url().is_potentially_trustworthy() {
2612 return;
2613 }
2614
2615 set_the_sec_fetch_dest_header(r);
2617
2618 set_the_sec_fetch_mode_header(r);
2620
2621 set_the_sec_fetch_site_header(r);
2623
2624 set_the_sec_fetch_user_header(r);
2626}
2627
2628fn set_the_sec_fetch_dest_header(r: &mut Request) {
2630 debug_assert!(r.url().is_potentially_trustworthy());
2632
2633 let header = r.destination;
2637
2638 r.headers.typed_insert(SecFetchDest(header));
2640}
2641
2642fn set_the_sec_fetch_mode_header(r: &mut Request) {
2644 debug_assert!(r.url().is_potentially_trustworthy());
2646
2647 let header = &r.mode;
2650
2651 r.headers.typed_insert(SecFetchMode::from(header));
2653}
2654
2655fn set_the_sec_fetch_site_header(r: &mut Request) {
2657 let Origin::Origin(request_origin) = &r.origin else {
2660 panic!("request origin cannot be \"client\" at this point")
2661 };
2662
2663 debug_assert!(r.url().is_potentially_trustworthy());
2665
2666 let mut header = SecFetchSite::SameOrigin;
2669
2670 if header != SecFetchSite::None {
2675 for url in &r.url_list {
2676 if url.origin() == *request_origin {
2678 continue;
2679 }
2680
2681 header = SecFetchSite::CrossSite;
2683
2684 if !is_same_site(request_origin, &url.origin()) {
2686 break;
2687 }
2688
2689 header = SecFetchSite::SameSite;
2691 }
2692 }
2693
2694 r.headers.typed_insert(header);
2696}
2697
2698fn set_the_sec_fetch_user_header(r: &mut Request) {
2700 debug_assert!(r.url().is_potentially_trustworthy());
2702
2703 if !r.is_navigation_request() {
2706 return;
2707 }
2708
2709 let header = SecFetchUser;
2712
2713 r.headers.typed_insert(header);
2715}
2716
2717fn set_requests_referrer_policy_on_redirect(request: &mut Request, response: &Response) {
2719 let referrer_policy: ReferrerPolicy = response
2722 .headers
2723 .typed_get::<headers::ReferrerPolicy>()
2724 .into();
2725
2726 if referrer_policy != ReferrerPolicy::EmptyString {
2728 request.referrer_policy = referrer_policy;
2729 }
2730}