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,
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 servo_arc::Arc;
63use servo_url::{Host, ImmutableOrigin, ServoUrl};
64use tokio::sync::mpsc::{
65 Receiver as TokioReceiver, Sender as TokioSender, UnboundedReceiver, UnboundedSender, channel,
66 unbounded_channel,
67};
68use tokio_stream::wrappers::ReceiverStream;
69
70use crate::async_runtime::spawn_task;
71use crate::connector::{CertificateErrorOverrideManager, Connector};
72use crate::cookie::ServoCookie;
73use crate::cookie_storage::CookieStorage;
74use crate::decoder::Decoder;
75use crate::fetch::cors_cache::CorsCache;
76use crate::fetch::fetch_params::FetchParams;
77use crate::fetch::headers::{SecFetchDest, SecFetchMode, SecFetchSite, SecFetchUser};
78use crate::fetch::methods::{Data, DoneChannel, FetchContext, Target, main_fetch};
79use crate::hsts::HstsList;
80use crate::http_cache::{CacheKey, HttpCache};
81use crate::resource_thread::{AuthCache, AuthCacheEntry};
82
83#[derive(Clone, Debug, Eq, PartialEq)]
85pub enum HttpCacheEntryState {
86 ReadyToConstruct,
90 PendingStore(usize),
92}
93
94type HttpCacheState = Mutex<HashMap<CacheKey, Arc<(Mutex<HttpCacheEntryState>, Condvar)>>>;
95
96pub struct HttpState {
97 pub hsts_list: RwLock<HstsList>,
98 pub cookie_jar: RwLock<CookieStorage>,
99 pub http_cache: RwLock<HttpCache>,
100 pub http_cache_state: HttpCacheState,
104 pub auth_cache: RwLock<AuthCache>,
105 pub history_states: RwLock<HashMap<HistoryStateId, Vec<u8>>>,
106 pub client: Client<Connector, crate::connector::BoxedBody>,
107 pub override_manager: CertificateErrorOverrideManager,
108 pub embedder_proxy: Mutex<EmbedderProxy>,
109}
110
111impl HttpState {
112 pub(crate) fn memory_reports(&self, suffix: &str, ops: &mut MallocSizeOfOps) -> Vec<Report> {
113 vec![
114 Report {
115 path: path!["memory-cache", suffix],
116 kind: ReportKind::ExplicitJemallocHeapSize,
117 size: self.http_cache.read().unwrap().size_of(ops),
118 },
119 Report {
120 path: path!["hsts-list", suffix],
121 kind: ReportKind::ExplicitJemallocHeapSize,
122 size: self.hsts_list.read().unwrap().size_of(ops),
123 },
124 ]
125 }
126
127 fn request_authentication(
128 &self,
129 request: &Request,
130 response: &Response,
131 ) -> Option<AuthenticationResponse> {
132 let webview_id = request.target_webview_id?;
134 let for_proxy = response.status == StatusCode::PROXY_AUTHENTICATION_REQUIRED;
135
136 if request.mode != RequestMode::Navigate {
138 return None;
139 }
140
141 let embedder_proxy = self.embedder_proxy.lock().unwrap();
142 let (ipc_sender, ipc_receiver) = generic_channel::channel().unwrap();
143 embedder_proxy.send(EmbedderMsg::RequestAuthentication(
144 webview_id,
145 request.url(),
146 for_proxy,
147 ipc_sender,
148 ));
149 ipc_receiver.recv().ok()?
150 }
151}
152
153pub(crate) fn set_default_accept(request: &mut Request) {
155 if request.headers.contains_key(header::ACCEPT) {
156 return;
157 }
158
159 let value = if request.initiator == Initiator::Prefetch {
160 DOCUMENT_ACCEPT_HEADER_VALUE
161 } else {
162 match request.destination {
163 Destination::Document | Destination::Frame | Destination::IFrame => {
164 DOCUMENT_ACCEPT_HEADER_VALUE
165 },
166 Destination::Image => {
167 HeaderValue::from_static("image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5")
168 },
169 Destination::Json => HeaderValue::from_static("application/json,*/*;q=0.5"),
170 Destination::Style => HeaderValue::from_static("text/css,*/*;q=0.1"),
171 _ => HeaderValue::from_static("*/*"),
172 }
173 };
174
175 request.headers.insert(header::ACCEPT, value);
176}
177
178fn set_default_accept_encoding(headers: &mut HeaderMap) {
179 if headers.contains_key(header::ACCEPT_ENCODING) {
180 return;
181 }
182
183 headers.insert(
185 header::ACCEPT_ENCODING,
186 HeaderValue::from_static("gzip, deflate, br"),
187 );
188}
189
190fn no_referrer_when_downgrade(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
192 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
194 return None;
195 }
196 strip_url_for_use_as_referrer(referrer_url, false)
198}
199
200fn strict_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
202 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
204 return None;
205 }
206 strip_url_for_use_as_referrer(referrer_url, true)
208}
209
210fn strict_origin_when_cross_origin(
212 referrer_url: ServoUrl,
213 current_url: ServoUrl,
214) -> Option<ServoUrl> {
215 if referrer_url.origin() == current_url.origin() {
217 return strip_url_for_use_as_referrer(referrer_url, false);
218 }
219 if referrer_url.is_potentially_trustworthy() && !current_url.is_potentially_trustworthy() {
221 return None;
222 }
223 strip_url_for_use_as_referrer(referrer_url, true)
225}
226
227fn is_same_site(site_a: &ImmutableOrigin, site_b: &ImmutableOrigin) -> bool {
229 if !site_a.is_tuple() && !site_b.is_tuple() && site_a == site_b {
234 return true;
235 }
236
237 let ImmutableOrigin::Tuple(scheme_a, host_a, _) = site_a else {
239 return false;
240 };
241 let ImmutableOrigin::Tuple(scheme_b, host_b, _) = site_b else {
242 return false;
243 };
244
245 if scheme_a != scheme_b {
247 return false;
248 }
249
250 if let (Host::Domain(domain_a), Host::Domain(domain_b)) = (host_a, host_b) {
253 if reg_suffix(domain_a) != reg_suffix(domain_b) {
254 return false;
255 }
256 } else if host_a != host_b {
257 return false;
258 }
259
260 true
262}
263
264fn is_schemelessy_same_site(site_a: &ImmutableOrigin, site_b: &ImmutableOrigin) -> bool {
266 if !site_a.is_tuple() && !site_b.is_tuple() && site_a == site_b {
268 true
269 } else if site_a.is_tuple() && site_b.is_tuple() {
270 let host_a = site_a.host().map(|h| h.to_string()).unwrap_or_default();
272 let host_b = site_b.host().map(|h| h.to_string()).unwrap_or_default();
273
274 let host_a_reg = reg_suffix(&host_a);
275 let host_b_reg = reg_suffix(&host_b);
276
277 (site_a.host() == site_b.host() && host_a_reg.is_empty()) ||
279 (host_a_reg == host_b_reg && !host_a_reg.is_empty())
280 } else {
281 false
283 }
284}
285
286fn strip_url_for_use_as_referrer(mut url: ServoUrl, origin_only: bool) -> Option<ServoUrl> {
288 const MAX_REFERRER_URL_LENGTH: usize = 4096;
289 if url.is_local_scheme() {
291 return None;
292 }
293 {
295 let url = url.as_mut_url();
296 let _ = url.set_username("");
297 let _ = url.set_password(None);
298 url.set_fragment(None);
299 if origin_only || url.as_str().len() > MAX_REFERRER_URL_LENGTH {
303 url.set_path("");
304 url.set_query(None);
305 }
306 }
307 Some(url)
309}
310
311fn same_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
313 if referrer_url.origin() == current_url.origin() {
315 return strip_url_for_use_as_referrer(referrer_url, false);
316 }
317 None
319}
320
321fn origin_when_cross_origin(referrer_url: ServoUrl, current_url: ServoUrl) -> Option<ServoUrl> {
323 if referrer_url.origin() == current_url.origin() {
325 return strip_url_for_use_as_referrer(referrer_url, false);
326 }
327 strip_url_for_use_as_referrer(referrer_url, true)
329}
330
331pub fn determine_requests_referrer(
333 referrer_policy: ReferrerPolicy,
334 referrer_source: ServoUrl,
335 current_url: ServoUrl,
336) -> Option<ServoUrl> {
337 match referrer_policy {
338 ReferrerPolicy::EmptyString | ReferrerPolicy::NoReferrer => None,
339 ReferrerPolicy::Origin => strip_url_for_use_as_referrer(referrer_source, true),
340 ReferrerPolicy::UnsafeUrl => strip_url_for_use_as_referrer(referrer_source, false),
341 ReferrerPolicy::StrictOrigin => strict_origin(referrer_source, current_url),
342 ReferrerPolicy::StrictOriginWhenCrossOrigin => {
343 strict_origin_when_cross_origin(referrer_source, current_url)
344 },
345 ReferrerPolicy::SameOrigin => same_origin(referrer_source, current_url),
346 ReferrerPolicy::OriginWhenCrossOrigin => {
347 origin_when_cross_origin(referrer_source, current_url)
348 },
349 ReferrerPolicy::NoReferrerWhenDowngrade => {
350 no_referrer_when_downgrade(referrer_source, current_url)
351 },
352 }
353}
354
355fn set_request_cookies(
356 url: &ServoUrl,
357 headers: &mut HeaderMap,
358 cookie_jar: &RwLock<CookieStorage>,
359) {
360 let mut cookie_jar = cookie_jar.write().unwrap();
361 cookie_jar.remove_expired_cookies_for_url(url);
362 if let Some(cookie_list) = cookie_jar.cookies_for_url(url, CookieSource::HTTP) {
363 headers.insert(
364 header::COOKIE,
365 HeaderValue::from_bytes(cookie_list.as_bytes()).unwrap(),
366 );
367 }
368}
369
370fn set_cookie_for_url(cookie_jar: &RwLock<CookieStorage>, request: &ServoUrl, cookie_val: &str) {
371 let mut cookie_jar = cookie_jar.write().unwrap();
372 let source = CookieSource::HTTP;
373
374 if let Some(cookie) = ServoCookie::from_cookie_string(cookie_val.into(), request, source) {
375 cookie_jar.push(cookie, request, source);
376 }
377}
378
379fn set_cookies_from_headers(
380 url: &ServoUrl,
381 headers: &HeaderMap,
382 cookie_jar: &RwLock<CookieStorage>,
383) {
384 for cookie in headers.get_all(header::SET_COOKIE) {
385 if let Ok(cookie_str) = std::str::from_utf8(cookie.as_bytes()) {
386 set_cookie_for_url(cookie_jar, url, cookie_str);
387 }
388 }
389}
390
391#[allow(clippy::too_many_arguments)]
392fn prepare_devtools_request(
393 request_id: String,
394 url: ServoUrl,
395 method: Method,
396 headers: HeaderMap,
397 body: Option<Vec<u8>>,
398 pipeline_id: PipelineId,
399 connect_time: Duration,
400 send_time: Duration,
401 destination: Destination,
402 is_xhr: bool,
403 browsing_context_id: BrowsingContextId,
404) -> ChromeToDevtoolsControlMsg {
405 let started_date_time = SystemTime::now();
406 let request = DevtoolsHttpRequest {
407 url,
408 method,
409 headers,
410 body,
411 pipeline_id,
412 started_date_time,
413 time_stamp: started_date_time
414 .duration_since(UNIX_EPOCH)
415 .unwrap_or_default()
416 .as_secs() as i64,
417 connect_time,
418 send_time,
419 destination,
420 is_xhr,
421 browsing_context_id,
422 };
423 let net_event = NetworkEvent::HttpRequestUpdate(request);
424
425 ChromeToDevtoolsControlMsg::NetworkEvent(request_id, net_event)
426}
427
428pub fn send_request_to_devtools(
429 msg: ChromeToDevtoolsControlMsg,
430 devtools_chan: &Sender<DevtoolsControlMsg>,
431) {
432 devtools_chan
433 .send(DevtoolsControlMsg::FromChrome(msg))
434 .unwrap();
435}
436
437pub fn send_response_to_devtools(
438 request: &Request,
439 context: &FetchContext,
440 response: &Response,
441 body_data: Option<Vec<u8>>,
442) {
443 let meta = match response.metadata() {
444 Ok(FetchMetadata::Unfiltered(m)) => m,
445 Ok(FetchMetadata::Filtered { unsafe_, .. }) => unsafe_,
446 Err(_) => {
447 log::warn!("No metadata available, skipping devtools response.");
448 return;
449 },
450 };
451 send_response_values_to_devtools(
452 meta.headers.map(Serde::into_inner),
453 meta.status,
454 body_data,
455 request,
456 context.devtools_chan.clone(),
457 );
458}
459
460#[allow(clippy::too_many_arguments)]
461pub fn send_response_values_to_devtools(
462 headers: Option<HeaderMap>,
463 status: HttpStatus,
464 body: Option<Vec<u8>>,
465 request: &Request,
466 devtools_chan: Option<StdArc<Mutex<Sender<DevtoolsControlMsg>>>>,
467) {
468 if let (Some(devtools_chan), Some(pipeline_id), Some(webview_id)) = (
469 devtools_chan,
470 request.pipeline_id,
471 request.target_webview_id,
472 ) {
473 let browsing_context_id = webview_id.into();
474
475 let devtoolsresponse = DevtoolsHttpResponse {
476 headers,
477 status,
478 body,
479 pipeline_id,
480 browsing_context_id,
481 };
482 let net_event_response = NetworkEvent::HttpResponse(devtoolsresponse);
483
484 let msg =
485 ChromeToDevtoolsControlMsg::NetworkEvent(request.id.0.to_string(), net_event_response);
486
487 let _ = devtools_chan
488 .lock()
489 .unwrap()
490 .send(DevtoolsControlMsg::FromChrome(msg));
491 }
492}
493
494pub fn send_early_httprequest_to_devtools(request: &Request, context: &FetchContext) {
495 if let (Some(devtools_chan), Some(browsing_context_id), Some(pipeline_id)) = (
496 context.devtools_chan.as_ref(),
497 request.target_webview_id.map(|id| id.into()),
498 request.pipeline_id,
499 ) {
500 let devtools_request = DevtoolsHttpRequest {
502 url: request.current_url().clone(),
503 method: request.method.clone(),
504 headers: request.headers.clone(),
505 body: None,
506 pipeline_id,
507 started_date_time: SystemTime::now(),
508 time_stamp: 0,
509 connect_time: Duration::from_millis(0),
510 send_time: Duration::from_millis(0),
511 destination: request.destination,
512 is_xhr: false,
513 browsing_context_id,
514 };
515
516 let msg = ChromeToDevtoolsControlMsg::NetworkEvent(
517 request.id.0.to_string(),
518 NetworkEvent::HttpRequest(devtools_request),
519 );
520
521 send_request_to_devtools(msg, &devtools_chan.lock().unwrap());
522 }
523}
524
525fn auth_from_cache(
526 auth_cache: &RwLock<AuthCache>,
527 origin: &ImmutableOrigin,
528) -> Option<Authorization<Basic>> {
529 if let Some(auth_entry) = auth_cache
530 .read()
531 .unwrap()
532 .entries
533 .get(&origin.ascii_serialization())
534 {
535 let user_name = &auth_entry.user_name;
536 let password = &auth_entry.password;
537 Some(Authorization::basic(user_name, password))
538 } else {
539 None
540 }
541}
542
543enum BodyChunk {
546 Chunk(IpcSharedMemory),
548 Done,
550}
551
552enum BodyStream {
554 Chunked(TokioReceiver<Result<Frame<Bytes>, hyper::Error>>),
557 Buffered(UnboundedReceiver<BodyChunk>),
560}
561
562enum BodySink {
565 Chunked(TokioSender<Result<Frame<Bytes>, hyper::Error>>),
567 Buffered(UnboundedSender<BodyChunk>),
571}
572
573impl BodySink {
574 fn transmit_bytes(&self, bytes: IpcSharedMemory) {
575 match self {
576 BodySink::Chunked(sender) => {
577 let sender = sender.clone();
578 spawn_task(async move {
579 let _ = sender
580 .send(Ok(Frame::data(Bytes::copy_from_slice(&bytes))))
581 .await;
582 });
583 },
584 BodySink::Buffered(sender) => {
585 let _ = sender.send(BodyChunk::Chunk(bytes));
586 },
587 }
588 }
589
590 fn close(&self) {
591 match self {
592 BodySink::Chunked(_) => { },
593 BodySink::Buffered(sender) => {
594 let _ = sender.send(BodyChunk::Done);
595 },
596 }
597 }
598}
599
600#[allow(clippy::too_many_arguments)]
601async fn obtain_response(
602 client: &Client<Connector, crate::connector::BoxedBody>,
603 url: &ServoUrl,
604 method: &Method,
605 request_headers: &mut HeaderMap,
606 body: Option<StdArc<Mutex<IpcSender<BodyChunkRequest>>>>,
607 source_is_null: bool,
608 pipeline_id: &Option<PipelineId>,
609 request_id: Option<&str>,
610 destination: Destination,
611 is_xhr: bool,
612 context: &FetchContext,
613 fetch_terminated: UnboundedSender<bool>,
614 browsing_context_id: Option<BrowsingContextId>,
615) -> Result<(HyperResponse<Decoder>, Option<ChromeToDevtoolsControlMsg>), NetworkError> {
616 {
617 let mut headers = request_headers.clone();
618
619 let devtools_bytes = StdArc::new(Mutex::new(vec![]));
620
621 let encoded_url = url
623 .clone()
624 .into_url()
625 .as_ref()
626 .replace('|', "%7C")
627 .replace('{', "%7B")
628 .replace('}', "%7D");
629
630 let request = if let Some(chunk_requester) = body {
631 let (sink, stream) = if source_is_null {
632 headers.insert(TRANSFER_ENCODING, HeaderValue::from_static("chunked"));
635
636 let (sender, receiver) = channel(1);
637 (BodySink::Chunked(sender), BodyStream::Chunked(receiver))
638 } else {
639 let (sender, receiver) = unbounded_channel();
646 (BodySink::Buffered(sender), BodyStream::Buffered(receiver))
647 };
648
649 let (body_chan, body_port) = ipc::channel().unwrap();
650
651 if let Ok(requester) = chunk_requester.lock() {
652 let _ = requester.send(BodyChunkRequest::Connect(body_chan));
653
654 let _ = requester.send(BodyChunkRequest::Chunk);
657 }
658
659 let devtools_bytes = devtools_bytes.clone();
660 let chunk_requester2 = chunk_requester.clone();
661
662 ROUTER.add_typed_route(
663 body_port,
664 Box::new(move |message| {
665 info!("Received message");
666 let bytes = match message.unwrap() {
667 BodyChunkResponse::Chunk(bytes) => bytes,
668 BodyChunkResponse::Done => {
669 let _ = fetch_terminated.send(false);
671 sink.close();
672
673 return;
674 },
675 BodyChunkResponse::Error => {
676 let _ = fetch_terminated.send(true);
680 sink.close();
681
682 return;
683 },
684 };
685
686 devtools_bytes.lock().unwrap().extend_from_slice(&bytes);
687
688 sink.transmit_bytes(bytes);
691
692 let _ = chunk_requester2
695 .lock()
696 .unwrap()
697 .send(BodyChunkRequest::Chunk);
698 }),
699 );
700
701 let body = match stream {
702 BodyStream::Chunked(receiver) => {
703 let stream = ReceiverStream::new(receiver);
704 BoxBody::new(http_body_util::StreamBody::new(stream))
705 },
706 BodyStream::Buffered(mut receiver) => {
707 let mut body = vec![];
709 loop {
710 match receiver.recv().await {
711 Some(BodyChunk::Chunk(bytes)) => {
712 body.extend_from_slice(&bytes);
713 },
714 Some(BodyChunk::Done) => break,
715 None => warn!("Failed to read all chunks from request body."),
716 }
717 }
718 Full::new(body.into()).map_err(|_| unreachable!()).boxed()
719 },
720 };
721 HyperRequest::builder()
722 .method(method)
723 .uri(encoded_url)
724 .body(body)
725 } else {
726 HyperRequest::builder()
727 .method(method)
728 .uri(encoded_url)
729 .body(
730 http_body_util::Empty::new()
731 .map_err(|_| unreachable!())
732 .boxed(),
733 )
734 };
735
736 context
737 .timing
738 .lock()
739 .unwrap()
740 .set_attribute(ResourceAttribute::DomainLookupStart);
741
742 let connect_start = CrossProcessInstant::now();
745 context
746 .timing
747 .lock()
748 .unwrap()
749 .set_attribute(ResourceAttribute::ConnectStart(connect_start));
750
751 if url.scheme() == "https" {
755 context
756 .timing
757 .lock()
758 .unwrap()
759 .set_attribute(ResourceAttribute::SecureConnectionStart);
760 }
761
762 let mut request = match request {
763 Ok(request) => request,
764 Err(e) => return Err(NetworkError::from_http_error(&e)),
765 };
766 *request.headers_mut() = headers.clone();
767
768 let connect_end = CrossProcessInstant::now();
769 context
770 .timing
771 .lock()
772 .unwrap()
773 .set_attribute(ResourceAttribute::ConnectEnd(connect_end));
774
775 let request_id = request_id.map(|v| v.to_owned());
776 let pipeline_id = *pipeline_id;
777 let closure_url = url.clone();
778 let method = method.clone();
779 let send_start = CrossProcessInstant::now();
780
781 let host = request.uri().host().unwrap_or("").to_owned();
782 let override_manager = context.state.override_manager.clone();
783 let headers = headers.clone();
784 let is_secure_scheme = url.is_secure_scheme();
785
786 client
787 .request(request)
788 .and_then(move |res| {
789 let send_end = CrossProcessInstant::now();
790
791 let msg = if let Some(request_id) = request_id {
794 if let Some(pipeline_id) = pipeline_id {
795 if let Some(browsing_context_id) = browsing_context_id {
796 Some(prepare_devtools_request(
797 request_id,
798 closure_url,
799 method.clone(),
800 headers,
801 Some(devtools_bytes.lock().unwrap().clone()),
802 pipeline_id,
803 (connect_end - connect_start).unsigned_abs(),
804 (send_end - send_start).unsigned_abs(),
805 destination,
806 is_xhr,
807 browsing_context_id,
808 ))
809 } else {
810 debug!("Not notifying devtools (no browsing_context_id)");
811 None
812 }
813 } else {
818 debug!("Not notifying devtools (no pipeline_id)");
819 None
820 }
821 } else {
822 debug!("Not notifying devtools (no request_id)");
823 None
824 };
825
826 future::ready(Ok((
827 Decoder::detect(res.map(|r| r.boxed()), is_secure_scheme),
828 msg,
829 )))
830 })
831 .map_err(move |error| {
832 NetworkError::from_hyper_error(
833 &error,
834 override_manager.remove_certificate_failing_verification(host.as_str()),
835 )
836 })
837 .await
838 }
839}
840
841#[async_recursion]
843#[allow(clippy::too_many_arguments)]
844pub async fn http_fetch(
845 fetch_params: &mut FetchParams,
846 cache: &mut CorsCache,
847 cors_flag: bool,
848 cors_preflight_flag: bool,
849 authentication_fetch_flag: bool,
850 target: Target<'async_recursion>,
851 done_chan: &mut DoneChannel,
852 context: &FetchContext,
853) -> Response {
854 *done_chan = None;
856 let request = &mut fetch_params.request;
858
859 let mut response: Option<Response> = None;
862
863 if request.service_workers_mode == ServiceWorkersMode::All {
865 if let Some(ref res) = response {
870 if (res.response_type == ResponseType::Opaque && request.mode != RequestMode::NoCors) ||
878 (res.response_type == ResponseType::OpaqueRedirect &&
879 request.redirect_mode != RedirectMode::Manual) ||
880 (res.url_list.len() > 1 && request.redirect_mode != RedirectMode::Follow) ||
881 res.is_network_error()
882 {
883 return Response::network_error(NetworkError::Internal("Request failed".into()));
884 }
885
886 }
889 }
890
891 if response.is_none() {
893 if cors_preflight_flag {
895 let method_cache_match = cache.match_method(request, request.method.clone());
896
897 let method_mismatch = !method_cache_match &&
898 (!is_cors_safelisted_method(&request.method) || request.use_cors_preflight);
899 let header_mismatch = request.headers.iter().any(|(name, value)| {
900 !cache.match_header(request, name) &&
901 !is_cors_safelisted_request_header(&name, &value)
902 });
903
904 if method_mismatch || header_mismatch {
906 let preflight_result = cors_preflight_fetch(request, cache, context).await;
907 if let Some(e) = preflight_result.get_network_error() {
909 return Response::network_error(e.clone());
910 }
911 }
912 }
913
914 if request.redirect_mode == RedirectMode::Follow {
916 request.service_workers_mode = ServiceWorkersMode::None;
917 }
918
919 context
923 .timing
924 .lock()
925 .unwrap()
926 .set_attribute(ResourceAttribute::RequestStart);
927
928 let mut fetch_result = http_network_or_cache_fetch(
929 fetch_params,
930 authentication_fetch_flag,
931 cors_flag,
932 done_chan,
933 context,
934 )
935 .await;
936
937 if cors_flag && cors_check(&fetch_params.request, &fetch_result).is_err() {
939 return Response::network_error(NetworkError::Internal("CORS check failed".into()));
940 }
941
942 fetch_result.return_internal = false;
943 response = Some(fetch_result);
944 }
945
946 let request = &mut fetch_params.request;
947
948 let mut response = response.unwrap();
950
951 if response
955 .actual_response()
956 .status
957 .try_code()
958 .is_some_and(is_redirect_status)
959 {
960 if response.actual_response().status != StatusCode::SEE_OTHER {
962 }
964
965 let mut location = response
967 .actual_response()
968 .headers
969 .get(header::LOCATION)
970 .and_then(|v| {
971 HeaderValue::to_str(v)
972 .map(|l| {
973 ServoUrl::parse_with_base(response.actual_response().url(), l)
974 .map_err(|err| err.to_string())
975 })
976 .ok()
977 });
978
979 if let Some(Ok(ref mut location)) = location {
981 if location.fragment().is_none() {
982 let current_url = request.current_url();
983 location.set_fragment(current_url.fragment());
984 }
985 }
986 response.actual_response_mut().location_url = location;
987
988 response = match request.redirect_mode {
990 RedirectMode::Error => {
991 Response::network_error(NetworkError::Internal("Redirect mode error".into()))
992 },
993 RedirectMode::Manual => response.to_filtered(ResponseType::OpaqueRedirect),
994 RedirectMode::Follow => {
995 response.return_internal = true;
997 http_redirect_fetch(
998 fetch_params,
999 cache,
1000 response,
1001 cors_flag,
1002 target,
1003 done_chan,
1004 context,
1005 )
1006 .await
1007 },
1008 };
1009 }
1010
1011 response.return_internal = true;
1013 context
1014 .timing
1015 .lock()
1016 .unwrap()
1017 .set_attribute(ResourceAttribute::RedirectCount(
1018 fetch_params.request.redirect_count as u16,
1019 ));
1020
1021 response.resource_timing = Arc::clone(&context.timing);
1022
1023 response
1025}
1026
1027struct RedirectEndTimer(Option<Arc<Mutex<ResourceFetchTiming>>>);
1029
1030impl RedirectEndTimer {
1031 fn neuter(&mut self) {
1032 self.0 = None;
1033 }
1034}
1035
1036impl Drop for RedirectEndTimer {
1037 fn drop(&mut self) {
1038 let RedirectEndTimer(resource_fetch_timing_opt) = self;
1039
1040 resource_fetch_timing_opt.as_ref().map_or((), |t| {
1041 t.lock()
1042 .unwrap()
1043 .set_attribute(ResourceAttribute::RedirectEnd(RedirectEndValue::Zero));
1044 })
1045 }
1046}
1047
1048#[async_recursion]
1050pub async fn http_redirect_fetch(
1051 fetch_params: &mut FetchParams,
1052 cache: &mut CorsCache,
1053 response: Response,
1054 cors_flag: bool,
1055 target: Target<'async_recursion>,
1056 done_chan: &mut DoneChannel,
1057 context: &FetchContext,
1058) -> Response {
1059 let mut redirect_end_timer = RedirectEndTimer(Some(context.timing.clone()));
1060
1061 let request = &mut fetch_params.request;
1063
1064 assert!(response.return_internal);
1065
1066 let location_url = response.actual_response().location_url.clone();
1067 let location_url = match location_url {
1068 None => return response,
1070 Some(Err(err)) => {
1072 return Response::network_error(NetworkError::Internal(
1073 "Location URL parse failure: ".to_owned() + &err,
1074 ));
1075 },
1076 Some(Ok(ref url)) if !matches!(url.scheme(), "http" | "https") => {
1078 return Response::network_error(NetworkError::Internal(
1079 "Location URL not an HTTP(S) scheme".into(),
1080 ));
1081 },
1082 Some(Ok(url)) => url,
1083 };
1084
1085 context
1088 .timing
1089 .lock()
1090 .unwrap()
1091 .set_attribute(ResourceAttribute::RedirectStart(
1092 RedirectStartValue::FetchStart,
1093 ));
1094
1095 context
1096 .timing
1097 .lock()
1098 .unwrap()
1099 .set_attribute(ResourceAttribute::FetchStart);
1100
1101 context
1103 .timing
1104 .lock()
1105 .unwrap()
1106 .set_attribute(ResourceAttribute::StartTime(ResourceTimeValue::FetchStart));
1107
1108 context
1109 .timing
1110 .lock()
1111 .unwrap()
1112 .set_attribute(ResourceAttribute::StartTime(
1113 ResourceTimeValue::RedirectStart,
1114 )); if request.redirect_count >= 20 {
1118 return Response::network_error(NetworkError::Internal("Too many redirects".into()));
1119 }
1120
1121 request.redirect_count += 1;
1123
1124 let same_origin = match request.origin {
1126 Origin::Origin(ref origin) => *origin == location_url.origin(),
1127 Origin::Client => panic!(
1128 "Request origin should not be client for {}",
1129 request.current_url()
1130 ),
1131 };
1132
1133 let has_credentials = has_credentials(&location_url);
1134
1135 if request.mode == RequestMode::CorsMode && !same_origin && has_credentials {
1136 return Response::network_error(NetworkError::Internal(
1137 "Cross-origin credentials check failed".into(),
1138 ));
1139 }
1140
1141 if cors_flag && location_url.origin() != request.current_url().origin() {
1143 request.origin = Origin::Origin(ImmutableOrigin::new_opaque());
1144 }
1145
1146 if cors_flag && has_credentials {
1148 return Response::network_error(NetworkError::Internal("Credentials check failed".into()));
1149 }
1150
1151 if response.actual_response().status != StatusCode::SEE_OTHER &&
1154 request.body.as_ref().is_some_and(|b| b.source_is_null())
1155 {
1156 return Response::network_error(NetworkError::Internal("Request body is not done".into()));
1157 }
1158
1159 if response
1161 .actual_response()
1162 .status
1163 .try_code()
1164 .is_some_and(|code| {
1165 ((code == StatusCode::MOVED_PERMANENTLY || code == StatusCode::FOUND) &&
1166 request.method == Method::POST) ||
1167 (code == StatusCode::SEE_OTHER &&
1168 request.method != Method::HEAD &&
1169 request.method != Method::GET)
1170 })
1171 {
1172 request.method = Method::GET;
1174 request.body = None;
1175 for name in &[
1177 CONTENT_ENCODING,
1178 CONTENT_LANGUAGE,
1179 CONTENT_LOCATION,
1180 CONTENT_TYPE,
1181 ] {
1182 request.headers.remove(name);
1183 }
1184 }
1185
1186 if location_url.origin() != request.current_url().origin() {
1190 request.headers.remove(AUTHORIZATION);
1193 }
1194
1195 if let Some(body) = request.body.as_mut() {
1198 body.extract_source();
1199 }
1200
1201 request.url_list.push(location_url);
1205
1206 set_requests_referrer_policy_on_redirect(request, response.actual_response());
1208
1209 let recursive_flag = request.redirect_mode != RedirectMode::Manual;
1212
1213 let fetch_response = main_fetch(
1215 fetch_params,
1216 cache,
1217 recursive_flag,
1218 target,
1219 done_chan,
1220 context,
1221 )
1222 .await;
1223
1224 context
1226 .timing
1227 .lock()
1228 .unwrap()
1229 .set_attribute(ResourceAttribute::RedirectEnd(
1230 RedirectEndValue::ResponseEnd,
1231 ));
1232 redirect_end_timer.neuter();
1233
1234 fetch_response
1235}
1236
1237#[async_recursion]
1239async fn http_network_or_cache_fetch(
1240 fetch_params: &mut FetchParams,
1241 authentication_fetch_flag: bool,
1242 cors_flag: bool,
1243 done_chan: &mut DoneChannel,
1244 context: &FetchContext,
1245) -> Response {
1246 let request = &mut fetch_params.request;
1248
1249 let http_fetch_params: &mut FetchParams;
1251 let mut fetch_params_copy: FetchParams;
1252
1253 let mut response: Option<Response> = None;
1257
1258 let mut revalidating_flag = false;
1260
1261 let request_has_no_window = request.window == RequestWindow::NoWindow;
1265
1266 let http_request = if request_has_no_window && request.redirect_mode == RedirectMode::Error {
1267 http_fetch_params = fetch_params;
1268 &mut http_fetch_params.request
1269 }
1270 else {
1272 fetch_params_copy = fetch_params.clone();
1275 http_fetch_params = &mut fetch_params_copy;
1276
1277 &mut http_fetch_params.request
1278 };
1279
1280 let include_credentials = match http_request.credentials_mode {
1282 CredentialsMode::Include => true,
1284 CredentialsMode::CredentialsSameOrigin
1286 if http_request.response_tainting == ResponseTainting::Basic =>
1287 {
1288 true
1289 },
1290 _ => false,
1291 };
1292
1293 let content_length = http_request
1300 .body
1301 .as_ref()
1302 .and_then(|body| body.len().map(|size| size as u64));
1303
1304 let mut content_length_header_value = None;
1306
1307 if http_request.body.is_none() && matches!(http_request.method, Method::POST | Method::PUT) {
1310 content_length_header_value = Some(0);
1311 }
1312
1313 if let Some(content_length) = content_length {
1317 content_length_header_value = Some(content_length);
1318 };
1319
1320 if let Some(content_length_header_value) = content_length_header_value {
1323 http_request
1324 .headers
1325 .typed_insert(ContentLength(content_length_header_value));
1326 }
1327
1328 if content_length.is_some() && http_request.keep_alive {
1330 }
1332
1333 match http_request.referrer {
1335 Referrer::ReferrerUrl(ref http_request_referrer) |
1336 Referrer::Client(ref http_request_referrer) => {
1337 if let Ok(referer) = http_request_referrer.to_string().parse::<Referer>() {
1340 http_request.headers.typed_insert(referer);
1342 } else {
1343 error!("Failed to parse {} as referrer", http_request_referrer);
1347 }
1348 },
1349 _ => {},
1350 };
1351
1352 append_a_request_origin_header(http_request);
1354
1355 append_the_fetch_metadata_headers(http_request);
1357
1358 if http_request.initiator == Initiator::Prefetch {
1361 if let Ok(value) = HeaderValue::from_str("prefetch") {
1362 http_request.headers.insert("Sec-Purpose", value);
1363 }
1364 }
1365
1366 if !http_request.headers.contains_key(header::USER_AGENT) {
1369 http_request
1370 .headers
1371 .typed_insert::<UserAgent>(context.user_agent.parse().unwrap());
1372 }
1373
1374 match http_request.cache_mode {
1376 CacheMode::Default if is_no_store_cache(&http_request.headers) => {
1380 http_request.cache_mode = CacheMode::NoStore;
1381 },
1382
1383 CacheMode::NoCache if !http_request.headers.contains_key(header::CACHE_CONTROL) => {
1393 http_request
1394 .headers
1395 .typed_insert(CacheControl::new().with_max_age(Duration::from_secs(0)));
1396 },
1397
1398 CacheMode::Reload | CacheMode::NoStore => {
1400 if !http_request.headers.contains_key(header::PRAGMA) {
1403 http_request.headers.typed_insert(Pragma::no_cache());
1404 }
1405
1406 if !http_request.headers.contains_key(header::CACHE_CONTROL) {
1409 http_request
1410 .headers
1411 .typed_insert(CacheControl::new().with_no_cache());
1412 }
1413 },
1414
1415 _ => {},
1416 }
1417
1418 if http_request.headers.contains_key(header::RANGE) {
1421 if let Ok(value) = HeaderValue::from_str("identity") {
1422 http_request.headers.insert("Accept-Encoding", value);
1423 }
1424 }
1425
1426 http_request.headers.remove(header::HOST);
1430 set_default_accept_encoding(&mut http_request.headers);
1432
1433 let current_url = http_request.current_url();
1434
1435 if include_credentials {
1438 set_request_cookies(
1442 ¤t_url,
1443 &mut http_request.headers,
1444 &context.state.cookie_jar,
1445 );
1446 if !http_request.headers.contains_key(header::AUTHORIZATION) {
1448 let mut authorization_value = None;
1450
1451 if let Some(basic) = auth_from_cache(&context.state.auth_cache, ¤t_url.origin()) {
1453 if !http_request.use_url_credentials || !has_credentials(¤t_url) {
1454 authorization_value = Some(basic);
1455 }
1456 }
1457
1458 if authentication_fetch_flag &&
1460 authorization_value.is_none() &&
1461 has_credentials(¤t_url)
1462 {
1463 authorization_value = Some(Authorization::basic(
1464 current_url.username(),
1465 current_url.password().unwrap_or(""),
1466 ));
1467 }
1468
1469 if let Some(basic) = authorization_value {
1471 http_request.headers.typed_insert(basic);
1472 }
1473 }
1474 }
1475
1476 {
1486 let (lock, cvar) = {
1487 let entry_key = CacheKey::new(http_request);
1488 let mut state_map = context.state.http_cache_state.lock().unwrap();
1489 &*state_map
1490 .entry(entry_key)
1491 .or_insert_with(|| {
1492 Arc::new((
1493 Mutex::new(HttpCacheEntryState::ReadyToConstruct),
1494 Condvar::new(),
1495 ))
1496 })
1497 .clone()
1498 };
1499
1500 let mut state = lock.lock().unwrap();
1502 while let HttpCacheEntryState::PendingStore(_) = *state {
1503 let (current_state, time_out) = cvar
1504 .wait_timeout(state, Duration::from_millis(500))
1505 .unwrap();
1506 state = current_state;
1507 if time_out.timed_out() {
1508 break;
1510 }
1511 }
1512
1513 if let Ok(http_cache) = context.state.http_cache.read() {
1516 let stored_response = http_cache.construct_response(http_request, done_chan);
1520
1521 if let Some(response_from_cache) = stored_response {
1523 let response_headers = response_from_cache.response.headers.clone();
1524 let (cached_response, needs_revalidation) =
1526 match (http_request.cache_mode, &http_request.mode) {
1527 (CacheMode::ForceCache, _) => (Some(response_from_cache.response), false),
1528 (CacheMode::OnlyIfCached, &RequestMode::SameOrigin) => {
1529 (Some(response_from_cache.response), false)
1530 },
1531 (CacheMode::OnlyIfCached, _) |
1532 (CacheMode::NoStore, _) |
1533 (CacheMode::Reload, _) => (None, false),
1534 (_, _) => (
1535 Some(response_from_cache.response),
1536 response_from_cache.needs_validation,
1537 ),
1538 };
1539
1540 if needs_revalidation {
1541 revalidating_flag = true;
1542 if let Some(http_date) = response_headers.typed_get::<LastModified>() {
1544 let http_date: SystemTime = http_date.into();
1545 http_request
1546 .headers
1547 .typed_insert(IfModifiedSince::from(http_date));
1548 }
1549 if let Some(entity_tag) = response_headers.get(header::ETAG) {
1550 http_request
1551 .headers
1552 .insert(header::IF_NONE_MATCH, entity_tag.clone());
1553 }
1554 } else {
1555 response = cached_response;
1557 }
1558 if response.is_none() {
1559 *done_chan = None;
1562
1563 if let HttpCacheEntryState::PendingStore(i) = *state {
1566 let new = i + 1;
1567 *state = HttpCacheEntryState::PendingStore(new);
1568 } else {
1569 *state = HttpCacheEntryState::PendingStore(1);
1570 }
1571 }
1572 }
1573 }
1574 if *state == HttpCacheEntryState::ReadyToConstruct {
1576 cvar.notify_one();
1577 }
1578 }
1580
1581 fn update_http_cache_state(context: &FetchContext, http_request: &Request) {
1585 let (lock, cvar) = {
1586 let entry_key = CacheKey::new(http_request);
1587 let mut state_map = context.state.http_cache_state.lock().unwrap();
1588 &*state_map
1589 .get_mut(&entry_key)
1590 .expect("Entry in http-cache state to have been previously inserted")
1591 .clone()
1592 };
1593 let mut state = lock.lock().unwrap();
1594 if let HttpCacheEntryState::PendingStore(i) = *state {
1595 let new = i - 1;
1596 if new == 0 {
1597 *state = HttpCacheEntryState::ReadyToConstruct;
1598 cvar.notify_one();
1600 } else {
1601 *state = HttpCacheEntryState::PendingStore(new);
1602 }
1603 }
1604 }
1605
1606 async fn wait_for_cached_response(
1607 done_chan: &mut DoneChannel,
1608 response: &mut Option<Response>,
1609 ) {
1610 if let Some(ref mut ch) = *done_chan {
1611 assert!(response.is_some());
1615
1616 loop {
1617 match ch.1.recv().await {
1618 Some(Data::Payload(_)) => {},
1619 Some(Data::Done) => break, Some(Data::Cancelled) => {
1621 *response = None;
1624 break;
1625 },
1626 _ => panic!("HTTP cache should always send Done or Cancelled"),
1627 }
1628 }
1629 }
1630 *done_chan = None;
1632 }
1633
1634 wait_for_cached_response(done_chan, &mut response).await;
1635
1636 if response.is_none() {
1640 if http_request.cache_mode == CacheMode::OnlyIfCached {
1642 update_http_cache_state(context, http_request);
1645 return Response::network_error(NetworkError::Internal(
1646 "Couldn't find response in cache".into(),
1647 ));
1648 }
1649
1650 let forward_response =
1653 http_network_fetch(http_fetch_params, include_credentials, done_chan, context).await;
1654
1655 let http_request = &mut http_fetch_params.request;
1656 if forward_response.status.in_range(200..=399) && !http_request.method.is_safe() {
1660 if let Ok(mut http_cache) = context.state.http_cache.write() {
1661 http_cache.invalidate(http_request, &forward_response);
1662 }
1663 }
1664
1665 if revalidating_flag && forward_response.status == StatusCode::NOT_MODIFIED {
1667 if let Ok(mut http_cache) = context.state.http_cache.write() {
1668 *done_chan = None;
1671 response = http_cache.refresh(http_request, forward_response.clone(), done_chan);
1672 }
1673 wait_for_cached_response(done_chan, &mut response).await;
1674 }
1675
1676 if response.is_none() {
1678 let forward_response = response.insert(forward_response);
1680
1681 if http_request.cache_mode != CacheMode::NoStore {
1684 if let Ok(mut http_cache) = context.state.http_cache.write() {
1687 http_cache.store(http_request, forward_response);
1688 }
1689 }
1690 }
1691 }
1692
1693 let http_request = &mut http_fetch_params.request;
1694 update_http_cache_state(context, http_request);
1696
1697 let mut response = response.unwrap();
1698
1699 if http_request.response_tainting != ResponseTainting::CorsTainting &&
1702 cross_origin_resource_policy_check(http_request, &response) ==
1703 CrossOriginResourcePolicy::Blocked
1704 {
1705 return Response::network_error(NetworkError::Internal(
1706 "Cross-origin resource policy check failed".into(),
1707 ));
1708 }
1709
1710 if http_request.headers.contains_key(RANGE) {
1714 response.range_requested = true;
1715 }
1716
1717 if let (Some(StatusCode::UNAUTHORIZED), false, true) =
1723 (response.status.try_code(), cors_flag, include_credentials)
1724 {
1725 let request = &mut fetch_params.request;
1728
1729 if request.body.is_some() {
1731 }
1733
1734 if !request.use_url_credentials || authentication_fetch_flag {
1736 let Some(credentials) = context.state.request_authentication(request, &response) else {
1737 return response;
1738 };
1739
1740 if let Err(err) = request
1741 .current_url_mut()
1742 .set_username(&credentials.username)
1743 {
1744 error!("error setting username for url: {:?}", err);
1745 return response;
1746 };
1747
1748 if let Err(err) = request
1749 .current_url_mut()
1750 .set_password(Some(&credentials.password))
1751 {
1752 error!("error setting password for url: {:?}", err);
1753 return response;
1754 };
1755 }
1756
1757 *done_chan = None;
1760
1761 response = http_network_or_cache_fetch(
1763 fetch_params,
1764 true, cors_flag,
1766 done_chan,
1767 context,
1768 )
1769 .await;
1770 }
1771
1772 if response.status == StatusCode::PROXY_AUTHENTICATION_REQUIRED {
1774 let request = &mut fetch_params.request;
1775 if request_has_no_window {
1778 return Response::network_error(NetworkError::Internal(
1779 "Can't find Window object".into(),
1780 ));
1781 }
1782
1783 let Some(credentials) = context.state.request_authentication(request, &response) else {
1791 return response;
1792 };
1793
1794 let entry = AuthCacheEntry {
1796 user_name: credentials.username,
1797 password: credentials.password,
1798 };
1799 {
1800 let mut auth_cache = context.state.auth_cache.write().unwrap();
1801 let key = request.current_url().origin().ascii_serialization();
1802 auth_cache.entries.insert(key, entry);
1803 }
1804
1805 *done_chan = None;
1808
1809 response = http_network_or_cache_fetch(
1811 fetch_params,
1812 false, cors_flag,
1814 done_chan,
1815 context,
1816 )
1817 .await;
1818 }
1819
1820 if authentication_fetch_flag {
1828 }
1830
1831 response
1833}
1834
1835#[derive(PartialEq)]
1839enum CrossOriginResourcePolicy {
1840 Allowed,
1841 Blocked,
1842}
1843
1844fn cross_origin_resource_policy_check(
1847 request: &Request,
1848 response: &Response,
1849) -> CrossOriginResourcePolicy {
1850 if request.mode != RequestMode::NoCors {
1852 return CrossOriginResourcePolicy::Allowed;
1853 }
1854
1855 let current_url_origin = request.current_url().origin();
1857 let same_origin = if let Origin::Origin(ref origin) = request.origin {
1858 *origin == request.current_url().origin()
1859 } else {
1860 false
1861 };
1862
1863 if same_origin {
1864 return CrossOriginResourcePolicy::Allowed;
1865 }
1866
1867 let policy = response
1869 .headers
1870 .get(HeaderName::from_static("cross-origin-resource-policy"))
1871 .map(|h| h.to_str().unwrap_or(""))
1872 .unwrap_or("");
1873
1874 if policy == "same-origin" {
1876 return CrossOriginResourcePolicy::Blocked;
1877 }
1878
1879 if let Origin::Origin(ref request_origin) = request.origin {
1881 let schemeless_same_origin = is_schemelessy_same_site(request_origin, ¤t_url_origin);
1882 if schemeless_same_origin &&
1883 (request_origin.scheme() == Some("https") ||
1884 response.https_state == HttpsState::None)
1885 {
1886 return CrossOriginResourcePolicy::Allowed;
1887 }
1888 };
1889
1890 if policy == "same-site" {
1892 return CrossOriginResourcePolicy::Blocked;
1893 }
1894
1895 CrossOriginResourcePolicy::Allowed
1896}
1897
1898struct ResponseEndTimer(Option<Arc<Mutex<ResourceFetchTiming>>>);
1900
1901impl ResponseEndTimer {
1902 fn neuter(&mut self) {
1903 self.0 = None;
1904 }
1905}
1906
1907impl Drop for ResponseEndTimer {
1908 fn drop(&mut self) {
1909 let ResponseEndTimer(resource_fetch_timing_opt) = self;
1910
1911 resource_fetch_timing_opt.as_ref().map_or((), |t| {
1912 t.lock()
1913 .unwrap()
1914 .set_attribute(ResourceAttribute::ResponseEnd);
1915 })
1916 }
1917}
1918
1919async fn http_network_fetch(
1921 fetch_params: &mut FetchParams,
1922 credentials_flag: bool,
1923 done_chan: &mut DoneChannel,
1924 context: &FetchContext,
1925) -> Response {
1926 let mut response_end_timer = ResponseEndTimer(Some(context.timing.clone()));
1927
1928 let request = &mut fetch_params.request;
1930
1931 let url = request.current_url();
1942 let request_id = request.id.0.to_string();
1943 if log_enabled!(log::Level::Info) {
1944 info!("{:?} request for {}", request.method, url);
1945 for header in request.headers.iter() {
1946 debug!(" - {:?}", header);
1947 }
1948 }
1949
1950 let is_xhr = request.destination == Destination::None;
1954
1955 let (fetch_terminated_sender, mut fetch_terminated_receiver) = unbounded_channel();
1957
1958 let body = request.body.as_ref().map(|body| body.take_stream());
1959
1960 if body.is_none() {
1961 let _ = fetch_terminated_sender.send(false);
1966 }
1967
1968 let browsing_context_id = request.target_webview_id.map(Into::into);
1969
1970 let response_future = obtain_response(
1971 &context.state.client,
1972 &url,
1973 &request.method,
1974 &mut request.headers,
1975 body,
1976 request
1977 .body
1978 .as_ref()
1979 .map(|body| body.source_is_null())
1980 .unwrap_or(false),
1981 &request.pipeline_id,
1982 Some(&request_id),
1983 request.destination,
1984 is_xhr,
1985 context,
1986 fetch_terminated_sender,
1987 browsing_context_id,
1988 );
1989
1990 let (res, msg) = match response_future.await {
1992 Ok(wrapped_response) => wrapped_response,
1993 Err(error) => return Response::network_error(error),
1994 };
1995
1996 if log_enabled!(log::Level::Info) {
1997 debug!("{:?} response for {}", res.version(), url);
1998 for header in res.headers().iter() {
1999 debug!(" - {:?}", header);
2000 }
2001 }
2002
2003 match fetch_terminated_receiver.recv().await {
2006 Some(true) => {
2007 return Response::network_error(NetworkError::Internal(
2008 "Request body streaming failed.".into(),
2009 ));
2010 },
2011 Some(false) => {},
2012 _ => warn!("Failed to receive confirmation request was streamed without error."),
2013 }
2014
2015 let header_strings: Vec<&str> = res
2016 .headers()
2017 .get_all("Timing-Allow-Origin")
2018 .iter()
2019 .map(|header_value| header_value.to_str().unwrap_or(""))
2020 .collect();
2021 let wildcard_present = header_strings.contains(&"*");
2022 let req_origin_in_timing_allow = header_strings
2026 .iter()
2027 .any(|header_str| match request.origin {
2028 SpecificOrigin(ref immutable_request_origin) => {
2029 *header_str == immutable_request_origin.ascii_serialization()
2030 },
2031 _ => false,
2032 });
2033
2034 let is_same_origin = request.url_list.iter().all(|url| match request.origin {
2035 SpecificOrigin(ref immutable_request_origin) => url.origin() == *immutable_request_origin,
2036 _ => false,
2037 });
2038
2039 if !(is_same_origin || req_origin_in_timing_allow || wildcard_present) {
2040 context.timing.lock().unwrap().mark_timing_check_failed();
2041 }
2042
2043 let timing = context.timing.lock().unwrap().clone();
2044 let mut response = Response::new(url.clone(), timing);
2045
2046 let status_text = res
2047 .extensions()
2048 .get::<ReasonPhrase>()
2049 .map(ReasonPhrase::as_bytes)
2050 .or_else(|| res.status().canonical_reason().map(str::as_bytes))
2051 .map(Vec::from)
2052 .unwrap_or_default();
2053 response.status = HttpStatus::new(res.status(), status_text);
2054
2055 info!("got {:?} response for {:?}", res.status(), request.url());
2056 response.headers = res.headers().clone();
2057 response.referrer = request.referrer.to_url().cloned();
2058 response.referrer_policy = request.referrer_policy;
2059
2060 let res_body = response.body.clone();
2061
2062 let (done_sender, done_receiver) = unbounded_channel();
2064 *done_chan = Some((done_sender.clone(), done_receiver));
2065
2066 let devtools_sender = context.devtools_chan.clone();
2067 let cancellation_listener = context.cancellation_listener.clone();
2068 if cancellation_listener.cancelled() {
2069 return Response::network_error(NetworkError::Internal("Fetch aborted".into()));
2070 }
2071
2072 *res_body.lock().unwrap() = ResponseBody::Receiving(vec![]);
2073 let res_body2 = res_body.clone();
2074
2075 if let Some(ref sender) = devtools_sender {
2076 let sender = sender.lock().unwrap();
2077 if let Some(m) = msg {
2078 send_request_to_devtools(m, &sender);
2079 }
2080 }
2081
2082 let done_sender2 = done_sender.clone();
2083 let done_sender3 = done_sender.clone();
2084 let timing_ptr2 = context.timing.clone();
2085 let timing_ptr3 = context.timing.clone();
2086 let devtools_request = request.clone();
2087 let url1 = devtools_request.url();
2088 let url2 = url1.clone();
2089
2090 let status = response.status.clone();
2091 let headers = response.headers.clone();
2092 let devtools_chan = context.devtools_chan.clone();
2093
2094 spawn_task(
2095 res.into_body()
2096 .map_err(|e| {
2097 warn!("Error streaming response body: {:?}", e);
2098 })
2099 .try_fold(res_body, move |res_body, chunk| {
2100 if cancellation_listener.cancelled() {
2101 *res_body.lock().unwrap() = ResponseBody::Done(vec![]);
2102 let _ = done_sender.send(Data::Cancelled);
2103 return future::ready(Err(()));
2104 }
2105 if let ResponseBody::Receiving(ref mut body) = *res_body.lock().unwrap() {
2106 let bytes = chunk;
2107 body.extend_from_slice(&bytes);
2108 let _ = done_sender.send(Data::Payload(bytes.to_vec()));
2109 }
2110 future::ready(Ok(res_body))
2111 })
2112 .and_then(move |res_body| {
2113 debug!("successfully finished response for {:?}", url1);
2114 let mut body = res_body.lock().unwrap();
2115 let completed_body = match *body {
2116 ResponseBody::Receiving(ref mut body) => std::mem::take(body),
2117 _ => vec![],
2118 };
2119 let devtools_response_body = completed_body.clone();
2120 *body = ResponseBody::Done(completed_body);
2121 send_response_values_to_devtools(
2122 Some(headers),
2123 status,
2124 Some(devtools_response_body),
2125 &devtools_request,
2126 devtools_chan,
2127 );
2128 timing_ptr2
2129 .lock()
2130 .unwrap()
2131 .set_attribute(ResourceAttribute::ResponseEnd);
2132 let _ = done_sender2.send(Data::Done);
2133 future::ready(Ok(()))
2134 })
2135 .map_err(move |_| {
2136 debug!("finished response for {:?}", url2);
2137 let mut body = res_body2.lock().unwrap();
2138 let completed_body = match *body {
2139 ResponseBody::Receiving(ref mut body) => std::mem::take(body),
2140 _ => vec![],
2141 };
2142 *body = ResponseBody::Done(completed_body);
2143 timing_ptr3
2144 .lock()
2145 .unwrap()
2146 .set_attribute(ResourceAttribute::ResponseEnd);
2147 let _ = done_sender3.send(Data::Done);
2148 }),
2149 );
2150
2151 response.https_state = match url.scheme() {
2157 "https" => HttpsState::Modern,
2158 _ => HttpsState::None,
2159 };
2160
2161 if credentials_flag {
2174 set_cookies_from_headers(&url, &response.headers, &context.state.cookie_jar);
2175 }
2176 context
2177 .state
2178 .hsts_list
2179 .write()
2180 .unwrap()
2181 .update_hsts_list_from_response(&url, &response.headers);
2182
2183 response_end_timer.neuter();
2197
2198 response
2199}
2200
2201async fn cors_preflight_fetch(
2203 request: &Request,
2204 cache: &mut CorsCache,
2205 context: &FetchContext,
2206) -> Response {
2207 let mut preflight = RequestBuilder::new(
2209 request.target_webview_id,
2210 request.current_url(),
2211 request.referrer.clone(),
2212 )
2213 .method(Method::OPTIONS)
2214 .origin(match &request.origin {
2215 Origin::Client => {
2216 unreachable!("We shouldn't get Client origin in cors_preflight_fetch.")
2217 },
2218 Origin::Origin(origin) => origin.clone(),
2219 })
2220 .pipeline_id(request.pipeline_id)
2221 .initiator(request.initiator)
2222 .destination(request.destination)
2223 .referrer_policy(request.referrer_policy)
2224 .mode(RequestMode::CorsMode)
2225 .response_tainting(ResponseTainting::CorsTainting)
2226 .build();
2227
2228 preflight
2230 .headers
2231 .insert(ACCEPT, HeaderValue::from_static("*/*"));
2232
2233 preflight
2235 .headers
2236 .typed_insert::<AccessControlRequestMethod>(AccessControlRequestMethod::from(
2237 request.method.clone(),
2238 ));
2239
2240 let headers = get_cors_unsafe_header_names(&request.headers);
2242
2243 if !headers.is_empty() {
2245 preflight.headers.insert(
2248 ACCESS_CONTROL_REQUEST_HEADERS,
2249 HeaderValue::from_bytes(itertools::join(headers.iter(), ",").as_bytes())
2250 .unwrap_or(HeaderValue::from_static("")),
2251 );
2252 }
2253
2254 let mut fetch_params = FetchParams::new(preflight);
2256 let response =
2257 http_network_or_cache_fetch(&mut fetch_params, false, false, &mut None, context).await;
2258 if cors_check(request, &response).is_ok() && response.status.code().is_success() {
2260 let mut methods = if response
2262 .headers
2263 .contains_key(header::ACCESS_CONTROL_ALLOW_METHODS)
2264 {
2265 match response.headers.typed_get::<AccessControlAllowMethods>() {
2266 Some(methods) => methods.iter().collect(),
2267 None => {
2269 return Response::network_error(NetworkError::Internal(
2270 "CORS ACAM check failed".into(),
2271 ));
2272 },
2273 }
2274 } else {
2275 vec![]
2276 };
2277
2278 let header_names = if response
2280 .headers
2281 .contains_key(header::ACCESS_CONTROL_ALLOW_HEADERS)
2282 {
2283 match response.headers.typed_get::<AccessControlAllowHeaders>() {
2284 Some(names) => names.iter().collect(),
2285 None => {
2287 return Response::network_error(NetworkError::Internal(
2288 "CORS ACAH check failed".into(),
2289 ));
2290 },
2291 }
2292 } else {
2293 vec![]
2294 };
2295
2296 debug!(
2297 "CORS check: Allowed methods: {:?}, current method: {:?}",
2298 methods, request.method
2299 );
2300
2301 if methods.is_empty() && request.use_cors_preflight {
2303 methods = vec![request.method.clone()];
2304 }
2305
2306 if methods
2308 .iter()
2309 .all(|m| *m.as_str() != *request.method.as_ref()) &&
2310 !is_cors_safelisted_method(&request.method) &&
2311 (request.credentials_mode == CredentialsMode::Include ||
2312 methods.iter().all(|m| m.as_ref() != "*"))
2313 {
2314 return Response::network_error(NetworkError::Internal(
2315 "CORS method check failed".into(),
2316 ));
2317 }
2318
2319 debug!(
2320 "CORS check: Allowed headers: {:?}, current headers: {:?}",
2321 header_names, request.headers
2322 );
2323
2324 if request.headers.iter().any(|(name, _)| {
2326 is_cors_non_wildcard_request_header_name(name) &&
2327 header_names.iter().all(|hn| hn != name)
2328 }) {
2329 return Response::network_error(NetworkError::Internal(
2330 "CORS authorization check failed".into(),
2331 ));
2332 }
2333
2334 let unsafe_names = get_cors_unsafe_header_names(&request.headers);
2336 #[allow(clippy::mutable_key_type)] let header_names_set: HashSet<&HeaderName> = HashSet::from_iter(header_names.iter());
2338 let header_names_contains_star = header_names.iter().any(|hn| hn.as_str() == "*");
2339 for unsafe_name in unsafe_names.iter() {
2340 if !header_names_set.contains(unsafe_name) &&
2341 (request.credentials_mode == CredentialsMode::Include ||
2342 !header_names_contains_star)
2343 {
2344 return Response::network_error(NetworkError::Internal(
2345 "CORS headers check failed".into(),
2346 ));
2347 }
2348 }
2349
2350 let max_age: Duration = response
2352 .headers
2353 .typed_get::<AccessControlMaxAge>()
2354 .map(|acma| acma.into())
2355 .unwrap_or(Duration::from_secs(5));
2356 for method in &methods {
2363 cache.match_method_and_update(request, method.clone(), max_age);
2364 }
2365
2366 for header_name in &header_names {
2368 cache.match_header_and_update(request, header_name, max_age);
2369 }
2370
2371 return response;
2373 }
2374
2375 Response::network_error(NetworkError::Internal("CORS check failed".into()))
2377}
2378
2379fn cors_check(request: &Request, response: &Response) -> Result<(), ()> {
2381 let origin = response.headers.typed_get::<AccessControlAllowOrigin>();
2383
2384 let origin = origin.ok_or(())?;
2386
2387 if request.credentials_mode != CredentialsMode::Include &&
2389 origin == AccessControlAllowOrigin::ANY
2390 {
2391 return Ok(());
2392 }
2393
2394 let origin = match origin.origin() {
2396 Some(origin) => origin,
2397 None => return Err(()),
2399 };
2400
2401 match request.origin {
2402 Origin::Origin(ref o) if o.ascii_serialization() == origin.to_string().trim() => {},
2403 _ => return Err(()),
2404 }
2405
2406 if request.credentials_mode != CredentialsMode::Include {
2408 return Ok(());
2409 }
2410
2411 let credentials = response
2413 .headers
2414 .typed_get::<AccessControlAllowCredentials>();
2415
2416 if credentials.is_some() {
2418 return Ok(());
2419 }
2420
2421 Err(())
2423}
2424
2425fn has_credentials(url: &ServoUrl) -> bool {
2426 !url.username().is_empty() || url.password().is_some()
2427}
2428
2429fn is_no_store_cache(headers: &HeaderMap) -> bool {
2430 headers.contains_key(header::IF_MODIFIED_SINCE) |
2431 headers.contains_key(header::IF_NONE_MATCH) |
2432 headers.contains_key(header::IF_UNMODIFIED_SINCE) |
2433 headers.contains_key(header::IF_MATCH) |
2434 headers.contains_key(header::IF_RANGE)
2435}
2436
2437fn is_redirect_status(status: StatusCode) -> bool {
2439 matches!(
2440 status,
2441 StatusCode::MOVED_PERMANENTLY |
2442 StatusCode::FOUND |
2443 StatusCode::SEE_OTHER |
2444 StatusCode::TEMPORARY_REDIRECT |
2445 StatusCode::PERMANENT_REDIRECT
2446 )
2447}
2448
2449fn request_has_redirect_tainted_origin(request: &Request) -> bool {
2451 let Origin::Origin(request_origin) = &request.origin else {
2453 panic!("origin cannot be \"client\" at this point in time");
2454 };
2455
2456 let mut last_url = None;
2458
2459 for url in &request.url_list {
2461 let Some(last_url) = &mut last_url else {
2463 last_url = Some(url);
2464 continue;
2465 };
2466
2467 if url.origin() != last_url.origin() && *request_origin != last_url.origin() {
2470 return true;
2471 }
2472
2473 *last_url = url;
2475 }
2476
2477 false
2479}
2480
2481fn serialize_request_origin(request: &Request) -> headers::Origin {
2483 let Origin::Origin(origin) = &request.origin else {
2485 panic!("origin cannot be \"client\" at this point in time");
2486 };
2487
2488 if request_has_redirect_tainted_origin(request) {
2490 return headers::Origin::NULL;
2491 }
2492
2493 serialize_origin(origin)
2495}
2496
2497pub fn serialize_origin(origin: &ImmutableOrigin) -> headers::Origin {
2499 match origin {
2500 ImmutableOrigin::Opaque(_) => headers::Origin::NULL,
2501 ImmutableOrigin::Tuple(scheme, host, port) => {
2502 let port = match (scheme.as_ref(), port) {
2505 ("http" | "ws", 80) | ("https" | "wss", 443) | ("ftp", 21) => None,
2506 _ => Some(*port),
2507 };
2508
2509 headers::Origin::try_from_parts(scheme, &host.to_string(), port)
2511 .unwrap_or(headers::Origin::NULL)
2512 },
2513 }
2514}
2515
2516fn append_a_request_origin_header(request: &mut Request) {
2518 let Origin::Origin(request_origin) = &request.origin else {
2520 panic!("origin cannot be \"client\" at this point in time");
2521 };
2522
2523 let mut serialized_origin = serialize_request_origin(request);
2525
2526 if request.response_tainting == ResponseTainting::CorsTainting ||
2529 matches!(request.mode, RequestMode::WebSocket { .. })
2530 {
2531 request.headers.typed_insert(serialized_origin);
2532 }
2533 else if !matches!(request.method, Method::GET | Method::HEAD) {
2535 if request.mode != RequestMode::CorsMode {
2537 match request.referrer_policy {
2538 ReferrerPolicy::NoReferrer => {
2539 serialized_origin = headers::Origin::NULL;
2541 },
2542 ReferrerPolicy::NoReferrerWhenDowngrade |
2543 ReferrerPolicy::StrictOrigin |
2544 ReferrerPolicy::StrictOriginWhenCrossOrigin => {
2545 if let ImmutableOrigin::Tuple(scheme, _, _) = &request_origin {
2548 if scheme == "https" && request.current_url().scheme() != "https" {
2549 serialized_origin = headers::Origin::NULL;
2550 }
2551 }
2552 },
2553 ReferrerPolicy::SameOrigin => {
2554 if *request_origin != request.current_url().origin() {
2557 serialized_origin = headers::Origin::NULL;
2558 }
2559 },
2560 _ => {
2561 },
2563 };
2564 }
2565
2566 request.headers.typed_insert(serialized_origin);
2568 }
2569}
2570
2571fn append_the_fetch_metadata_headers(r: &mut Request) {
2573 if !r.url().is_potentially_trustworthy() {
2575 return;
2576 }
2577
2578 set_the_sec_fetch_dest_header(r);
2580
2581 set_the_sec_fetch_mode_header(r);
2583
2584 set_the_sec_fetch_site_header(r);
2586
2587 set_the_sec_fetch_user_header(r);
2589}
2590
2591fn set_the_sec_fetch_dest_header(r: &mut Request) {
2593 debug_assert!(r.url().is_potentially_trustworthy());
2595
2596 let header = r.destination;
2600
2601 r.headers.typed_insert(SecFetchDest(header));
2603}
2604
2605fn set_the_sec_fetch_mode_header(r: &mut Request) {
2607 debug_assert!(r.url().is_potentially_trustworthy());
2609
2610 let header = &r.mode;
2613
2614 r.headers.typed_insert(SecFetchMode::from(header));
2616}
2617
2618fn set_the_sec_fetch_site_header(r: &mut Request) {
2620 let Origin::Origin(request_origin) = &r.origin else {
2623 panic!("request origin cannot be \"client\" at this point")
2624 };
2625
2626 debug_assert!(r.url().is_potentially_trustworthy());
2628
2629 let mut header = SecFetchSite::SameOrigin;
2632
2633 if header != SecFetchSite::None {
2638 for url in &r.url_list {
2639 if url.origin() == *request_origin {
2641 continue;
2642 }
2643
2644 header = SecFetchSite::CrossSite;
2646
2647 if !is_same_site(request_origin, &url.origin()) {
2649 break;
2650 }
2651
2652 header = SecFetchSite::SameSite;
2654 }
2655 }
2656
2657 r.headers.typed_insert(header);
2659}
2660
2661fn set_the_sec_fetch_user_header(r: &mut Request) {
2663 debug_assert!(r.url().is_potentially_trustworthy());
2665
2666 if !r.is_navigation_request() {
2669 return;
2670 }
2671
2672 let header = SecFetchUser;
2675
2676 r.headers.typed_insert(header);
2678}
2679
2680fn set_requests_referrer_policy_on_redirect(request: &mut Request, response: &Response) {
2682 let referrer_policy: ReferrerPolicy = response
2685 .headers
2686 .typed_get::<headers::ReferrerPolicy>()
2687 .into();
2688
2689 if referrer_policy != ReferrerPolicy::EmptyString {
2691 request.referrer_policy = referrer_policy;
2692 }
2693}