1use std::cell::Cell;
6use std::rc::Rc;
7use std::time::Duration;
8
9use base::id::WebViewId;
10use ipc_channel::ipc;
11use js::jsapi::{ExceptionStackBehavior, JS_IsExceptionPending};
12use js::jsval::UndefinedValue;
13use js::rust::HandleValue;
14use js::rust::wrappers::JS_SetPendingException;
15use net_traits::request::{
16 CorsSettings, CredentialsMode, Destination, Referrer, Request as NetTraitsRequest,
17 RequestBuilder, RequestId, RequestMode, ServiceWorkersMode,
18};
19use net_traits::{
20 CoreResourceMsg, CoreResourceThread, FetchChannels, FetchMetadata, FetchResponseMsg,
21 FilteredMetadata, Metadata, NetworkError, ResourceFetchTiming, cancel_async_fetch,
22};
23use rustc_hash::FxHashMap;
24use serde::{Deserialize, Serialize};
25use servo_url::ServoUrl;
26use timers::TimerEventRequest;
27use uuid::Uuid;
28
29use crate::body::BodyMixin;
30use crate::dom::abortsignal::AbortAlgorithm;
31use crate::dom::bindings::codegen::Bindings::AbortSignalBinding::AbortSignalMethods;
32use crate::dom::bindings::codegen::Bindings::RequestBinding::{
33 RequestInfo, RequestInit, RequestMethods,
34};
35use crate::dom::bindings::codegen::Bindings::ResponseBinding::Response_Binding::ResponseMethods;
36use crate::dom::bindings::codegen::Bindings::ResponseBinding::ResponseType as DOMResponseType;
37use crate::dom::bindings::codegen::Bindings::WindowBinding::{DeferredRequestInit, WindowMethods};
38use crate::dom::bindings::error::{Error, Fallible};
39use crate::dom::bindings::import::module::SafeJSContext;
40use crate::dom::bindings::inheritance::Castable;
41use crate::dom::bindings::num::Finite;
42use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
43use crate::dom::bindings::reflector::DomGlobal;
44use crate::dom::bindings::root::DomRoot;
45use crate::dom::bindings::trace::RootedTraceableBox;
46use crate::dom::csp::{GlobalCspReporting, Violation};
47use crate::dom::fetchlaterresult::FetchLaterResult;
48use crate::dom::globalscope::GlobalScope;
49use crate::dom::headers::Guard;
50use crate::dom::performance::performanceresourcetiming::InitiatorType;
51use crate::dom::promise::Promise;
52use crate::dom::request::Request;
53use crate::dom::response::Response;
54use crate::dom::serviceworkerglobalscope::ServiceWorkerGlobalScope;
55use crate::dom::window::Window;
56use crate::network_listener::{
57 self, FetchResponseListener, NetworkListener, ResourceTimingListener, submit_timing_data,
58};
59use crate::realms::{InRealm, enter_realm};
60use crate::script_runtime::CanGc;
61
62#[derive(Default, JSTraceable, MallocSizeOf)]
67pub(crate) struct FetchCanceller {
68 #[no_trace]
69 request_id: Option<RequestId>,
70 #[no_trace]
71 core_resource_thread: Option<CoreResourceThread>,
72 keep_alive: bool,
73}
74
75impl FetchCanceller {
76 pub(crate) fn new(
79 request_id: RequestId,
80 keep_alive: bool,
81 core_resource_thread: CoreResourceThread,
82 ) -> Self {
83 Self {
84 request_id: Some(request_id),
85 core_resource_thread: Some(core_resource_thread),
86 keep_alive,
87 }
88 }
89
90 pub(crate) fn keep_alive(&self) -> bool {
91 self.keep_alive
92 }
93
94 fn cancel(&mut self) {
95 if let Some(request_id) = self.request_id.take() {
96 if let Some(ref core_resource_thread) = self.core_resource_thread {
100 cancel_async_fetch(vec![request_id], core_resource_thread);
103 }
104 }
105 }
106
107 pub(crate) fn ignore(&mut self) {
110 let _ = self.request_id.take();
111 }
112
113 pub(crate) fn abort(&mut self) {
115 self.cancel();
116 }
117
118 pub(crate) fn terminate(&mut self) {
120 self.cancel();
121 }
122}
123
124#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
125pub(crate) struct DeferredFetchRecordId(Uuid);
127
128impl Default for DeferredFetchRecordId {
129 fn default() -> Self {
130 Self(Uuid::new_v4())
131 }
132}
133
134pub(crate) type QueuedDeferredFetchRecord = Rc<DeferredFetchRecord>;
135
136#[derive(Default, MallocSizeOf)]
138pub(crate) struct FetchGroup {
139 #[conditional_malloc_size_of]
141 pub(crate) deferred_fetch_records: FxHashMap<DeferredFetchRecordId, QueuedDeferredFetchRecord>,
142}
143
144fn request_init_from_request(request: NetTraitsRequest, global: &GlobalScope) -> RequestBuilder {
145 let mut builder =
146 RequestBuilder::new(request.target_webview_id, request.url(), request.referrer)
147 .method(request.method)
148 .headers(request.headers)
149 .unsafe_request(request.unsafe_request)
150 .body(request.body)
151 .destination(request.destination)
152 .synchronous(request.synchronous)
153 .mode(request.mode)
154 .cache_mode(request.cache_mode)
155 .use_cors_preflight(request.use_cors_preflight)
156 .credentials_mode(request.credentials_mode)
157 .use_url_credentials(request.use_url_credentials)
158 .referrer_policy(request.referrer_policy)
159 .pipeline_id(request.pipeline_id)
160 .redirect_mode(request.redirect_mode)
161 .integrity_metadata(request.integrity_metadata)
162 .cryptographic_nonce_metadata(request.cryptographic_nonce_metadata)
163 .parser_metadata(request.parser_metadata)
164 .initiator(request.initiator)
165 .client(global.request_client())
166 .insecure_requests_policy(request.insecure_requests_policy)
167 .has_trustworthy_ancestor_origin(request.has_trustworthy_ancestor_origin)
168 .https_state(request.https_state)
169 .response_tainting(request.response_tainting);
170 builder.id = request.id;
171 builder
172}
173
174fn abort_fetch_call(
176 promise: Rc<Promise>,
177 request: &Request,
178 response_object: Option<&Response>,
179 abort_reason: HandleValue,
180 global: &GlobalScope,
181 cx: SafeJSContext,
182 can_gc: CanGc,
183) {
184 promise.reject(cx, abort_reason, can_gc);
186 if let Some(body) = request.body() {
188 if body.is_readable() {
189 body.cancel(cx, global, abort_reason, can_gc);
190 }
191 }
192 let Some(response) = response_object else {
195 return;
196 };
197 if let Some(body) = response.body() {
199 if body.is_readable() {
200 body.error(abort_reason, can_gc);
201 }
202 }
203}
204
205#[expect(non_snake_case)]
207pub(crate) fn Fetch(
208 global: &GlobalScope,
209 input: RequestInfo,
210 init: RootedTraceableBox<RequestInit>,
211 comp: InRealm,
212 can_gc: CanGc,
213) -> Rc<Promise> {
214 let promise = Promise::new_in_current_realm(comp, can_gc);
216 let cx = GlobalScope::get_cx();
217
218 let response = Response::new(global, can_gc);
221 response.Headers(can_gc).set_guard(Guard::Immutable);
222
223 let request_object = match Request::Constructor(global, None, can_gc, input, init) {
226 Err(e) => {
227 response.error_stream(e.clone(), can_gc);
228 promise.reject_error(e, can_gc);
229 return promise;
230 },
231 Ok(r) => r,
232 };
233 let request = request_object.get_request();
235 let request_id = request.id;
236
237 let signal = request_object.Signal();
239 if signal.aborted() {
240 rooted!(in(*cx) let mut abort_reason = UndefinedValue());
242 signal.Reason(cx, abort_reason.handle_mut());
243 abort_fetch_call(
244 promise.clone(),
245 &request_object,
246 None,
247 abort_reason.handle(),
248 global,
249 cx,
250 can_gc,
251 );
252 return promise;
254 }
255
256 let keep_alive = request.keep_alive;
257 let mut request_init = request_init_from_request(request, global);
260
261 if global.is::<ServiceWorkerGlobalScope>() {
264 request_init.service_workers_mode = ServiceWorkersMode::None;
265 }
266
267 let fetch_context = FetchContext {
274 fetch_promise: Some(TrustedPromise::new(promise.clone())),
275 response_object: Trusted::new(&*response),
276 request: Trusted::new(&*request_object),
277 global: Trusted::new(global),
278 locally_aborted: false,
279 canceller: FetchCanceller::new(request_id, keep_alive, global.core_resource_thread()),
280 url: request_init.url.clone(),
281 };
282 let network_listener = NetworkListener::new(
283 fetch_context,
284 global.task_manager().networking_task_source().to_sendable(),
285 );
286 let fetch_context = network_listener.context.clone();
287
288 signal.add(&AbortAlgorithm::Fetch(fetch_context));
290
291 global.fetch_with_network_listener(request_init, network_listener);
294
295 promise
297}
298
299fn queue_deferred_fetch(
301 request: NetTraitsRequest,
302 activate_after: Finite<f64>,
303 global: &GlobalScope,
304) -> DeferredFetchRecordId {
305 let trusted_global = Trusted::new(global);
306 let mut request = request;
307 request.client = Some(global.request_client());
309 request.populate_request_from_client();
310 request.service_workers_mode = ServiceWorkersMode::None;
312 request.keep_alive = true;
314 let deferred_record = Rc::new(DeferredFetchRecord {
316 request,
317 invoke_state: Cell::new(DeferredFetchRecordInvokeState::Pending),
318 activated: Cell::new(false),
319 });
320 let deferred_fetch_record_id = global.append_deferred_fetch(deferred_record);
322 global.schedule_timer(TimerEventRequest {
324 callback: Box::new(move || {
325 let global = trusted_global.root();
327 global.deferred_fetch_record_for_id(&deferred_fetch_record_id).process(&global);
328
329 let trusted_global = trusted_global.clone();
334 global.task_manager().deferred_fetch_task_source().queue(
335 task!(notify_deferred_record: move || {
336 trusted_global.root().deferred_fetch_record_for_id(&deferred_fetch_record_id).activate();
337 }),
338 );
339 }),
340 duration: Duration::from_millis(*activate_after as u64),
342 });
343 deferred_fetch_record_id
345}
346
347#[expect(non_snake_case, unsafe_code)]
349pub(crate) fn FetchLater(
350 window: &Window,
351 input: RequestInfo,
352 init: RootedTraceableBox<DeferredRequestInit>,
353 can_gc: CanGc,
354) -> Fallible<DomRoot<FetchLaterResult>> {
355 let global_scope = window.upcast();
356 let document = window.Document();
357 let request_object = Request::constructor(global_scope, None, can_gc, input, &init.parent)?;
360 let signal = request_object.Signal();
362 if signal.aborted() {
363 let cx = GlobalScope::get_cx();
364 rooted!(in(*cx) let mut abort_reason = UndefinedValue());
365 signal.Reason(cx, abort_reason.handle_mut());
366 unsafe {
367 assert!(!JS_IsExceptionPending(*cx));
368 JS_SetPendingException(*cx, abort_reason.handle(), ExceptionStackBehavior::Capture);
369 }
370 return Err(Error::JSFailed);
371 }
372 let request = request_object.get_request();
374 let mut activate_after = Finite::wrap(0_f64);
376 if let Some(init_activate_after) = init.activateAfter.as_ref() {
379 activate_after = *init_activate_after;
380 }
381 if *activate_after < 0.0 {
383 return Err(Error::Range("activateAfter must be at least 0".to_owned()));
384 }
385 if !document.is_fully_active() {
387 return Err(Error::Type("Document is not fully active".to_owned()));
388 }
389 let url = request.url();
390 if !matches!(url.scheme(), "http" | "https") {
392 return Err(Error::Type("URL is not http(s)".to_owned()));
393 }
394 if !url.is_potentially_trustworthy() {
396 return Err(Error::Type("URL is not trustworthy".to_owned()));
397 }
398 if let Some(body) = request.body.as_ref() {
400 if body.len().is_none_or(|len| len == 0) {
401 return Err(Error::Type("Body is empty".to_owned()));
402 }
403 }
404 let quota = document.available_deferred_fetch_quota(request.url().origin());
407 let requested = request.total_request_length() as isize;
408 if quota < requested {
409 return Err(Error::QuotaExceeded {
410 quota: Some(Finite::wrap(quota as f64)),
411 requested: Some(Finite::wrap(requested as f64)),
412 });
413 }
414 let deferred_record_id = queue_deferred_fetch(request, activate_after, global_scope);
418 signal.add(&AbortAlgorithm::FetchLater(deferred_record_id));
420 Ok(FetchLaterResult::new(window, deferred_record_id, can_gc))
422}
423
424#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
426pub(crate) enum DeferredFetchRecordInvokeState {
427 Pending,
428 Sent,
429 Aborted,
430}
431
432#[derive(MallocSizeOf)]
434pub(crate) struct DeferredFetchRecord {
435 pub(crate) request: NetTraitsRequest,
437 pub(crate) invoke_state: Cell<DeferredFetchRecordInvokeState>,
439 activated: Cell<bool>,
440}
441
442impl DeferredFetchRecord {
443 fn activate(&self) {
445 self.activated.set(true);
447 }
448 pub(crate) fn abort(&self) {
450 self.invoke_state
452 .set(DeferredFetchRecordInvokeState::Aborted);
453 }
454 pub(crate) fn activated_getter_steps(&self) -> bool {
456 self.activated.get()
458 }
459 pub(crate) fn process(&self, global: &GlobalScope) {
461 if self.invoke_state.get() != DeferredFetchRecordInvokeState::Pending {
463 return;
464 }
465 self.invoke_state.set(DeferredFetchRecordInvokeState::Sent);
467 let url = self.request.url().clone();
469 let fetch_later_listener = FetchLaterListener {
470 url,
471 global: Trusted::new(global),
472 };
473 let request_init = request_init_from_request(self.request.clone(), global);
474 global.fetch(
475 request_init,
476 fetch_later_listener,
477 global.task_manager().networking_task_source().to_sendable(),
478 );
479 }
481}
482
483#[derive(JSTraceable, MallocSizeOf)]
484pub(crate) struct FetchContext {
485 #[ignore_malloc_size_of = "unclear ownership semantics"]
486 fetch_promise: Option<TrustedPromise>,
487 response_object: Trusted<Response>,
488 request: Trusted<Request>,
489 global: Trusted<GlobalScope>,
490 locally_aborted: bool,
491 canceller: FetchCanceller,
492 #[no_trace]
493 url: ServoUrl,
494}
495
496impl FetchContext {
497 pub(crate) fn abort_fetch(
499 &mut self,
500 abort_reason: HandleValue,
501 cx: SafeJSContext,
502 can_gc: CanGc,
503 ) {
504 self.locally_aborted = true;
506 self.canceller.abort();
512
513 let promise = self
516 .fetch_promise
517 .take()
518 .expect("fetch promise is missing")
519 .root();
520 abort_fetch_call(
521 promise,
522 &self.request.root(),
523 Some(&self.response_object.root()),
524 abort_reason,
525 &self.global.root(),
526 cx,
527 can_gc,
528 );
529 }
530}
531
532impl FetchResponseListener for FetchContext {
534 fn process_request_body(&mut self, _: RequestId) {
535 }
537
538 fn process_request_eof(&mut self, _: RequestId) {
539 }
541
542 fn process_response(
543 &mut self,
544 _: RequestId,
545 fetch_metadata: Result<FetchMetadata, NetworkError>,
546 ) {
547 if self.locally_aborted {
549 return;
550 }
551 let promise = self
552 .fetch_promise
553 .take()
554 .expect("fetch promise is missing")
555 .root();
556
557 let _ac = enter_realm(&*promise);
558 match fetch_metadata {
559 Err(error) => {
562 promise.reject_error(
563 Error::Type(format!("Network error: {:?}", error)),
564 CanGc::note(),
565 );
566 self.fetch_promise = Some(TrustedPromise::new(promise));
567 let response = self.response_object.root();
568 response.set_type(DOMResponseType::Error, CanGc::note());
569 response.error_stream(
570 Error::Type("Network error occurred".to_string()),
571 CanGc::note(),
572 );
573 return;
574 },
575 Ok(metadata) => match metadata {
578 FetchMetadata::Unfiltered(m) => {
579 fill_headers_with_metadata(self.response_object.root(), m, CanGc::note());
580 self.response_object
581 .root()
582 .set_type(DOMResponseType::Default, CanGc::note());
583 },
584 FetchMetadata::Filtered { filtered, .. } => match filtered {
585 FilteredMetadata::Basic(m) => {
586 fill_headers_with_metadata(self.response_object.root(), m, CanGc::note());
587 self.response_object
588 .root()
589 .set_type(DOMResponseType::Basic, CanGc::note());
590 },
591 FilteredMetadata::Cors(m) => {
592 fill_headers_with_metadata(self.response_object.root(), m, CanGc::note());
593 self.response_object
594 .root()
595 .set_type(DOMResponseType::Cors, CanGc::note());
596 },
597 FilteredMetadata::Opaque => {
598 self.response_object
599 .root()
600 .set_type(DOMResponseType::Opaque, CanGc::note());
601 },
602 FilteredMetadata::OpaqueRedirect(url) => {
603 let r = self.response_object.root();
604 r.set_type(DOMResponseType::Opaqueredirect, CanGc::note());
605 r.set_final_url(url);
606 },
607 },
608 },
609 }
610
611 promise.resolve_native(&self.response_object.root(), CanGc::note());
613 self.fetch_promise = Some(TrustedPromise::new(promise));
614 }
615
616 fn process_response_chunk(&mut self, _: RequestId, chunk: Vec<u8>) {
617 let response = self.response_object.root();
618 response.stream_chunk(chunk, CanGc::note());
619 }
620
621 fn process_response_eof(
622 self,
623 _: RequestId,
624 response: Result<(), NetworkError>,
625 timing: ResourceFetchTiming,
626 ) {
627 let response_object = self.response_object.root();
628 let _ac = enter_realm(&*response_object);
629 if let Err(ref error) = response {
630 if *error == NetworkError::DecompressionError {
631 response_object.error_stream(
632 Error::Type("Network error occurred".to_string()),
633 CanGc::note(),
634 );
635 }
636 }
637 response_object.finish(CanGc::note());
638 network_listener::submit_timing(&self, &response, &timing, CanGc::note());
643 }
644
645 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
646 let global = &self.resource_timing_global();
647 global.report_csp_violations(violations, None, None);
648 }
649}
650
651impl ResourceTimingListener for FetchContext {
652 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
653 (InitiatorType::Fetch, self.url.clone())
654 }
655
656 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
657 self.response_object.root().global()
658 }
659}
660
661struct FetchLaterListener {
662 url: ServoUrl,
664 global: Trusted<GlobalScope>,
666}
667
668impl FetchResponseListener for FetchLaterListener {
669 fn process_request_body(&mut self, _: RequestId) {}
670
671 fn process_request_eof(&mut self, _: RequestId) {}
672
673 fn process_response(
674 &mut self,
675 _: RequestId,
676 fetch_metadata: Result<FetchMetadata, NetworkError>,
677 ) {
678 _ = fetch_metadata;
679 }
680
681 fn process_response_chunk(&mut self, _: RequestId, chunk: Vec<u8>) {
682 _ = chunk;
683 }
684
685 fn process_response_eof(
686 self,
687 _: RequestId,
688 response: Result<(), NetworkError>,
689 timing: ResourceFetchTiming,
690 ) {
691 network_listener::submit_timing(&self, &response, &timing, CanGc::note());
692 }
693
694 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
695 let global = self.resource_timing_global();
696 global.report_csp_violations(violations, None, None);
697 }
698}
699
700impl ResourceTimingListener for FetchLaterListener {
701 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
702 (InitiatorType::Fetch, self.url.clone())
703 }
704
705 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
706 self.global.root()
707 }
708}
709
710fn fill_headers_with_metadata(r: DomRoot<Response>, m: Metadata, can_gc: CanGc) {
711 r.set_headers(m.headers, can_gc);
712 r.set_status(&m.status);
713 r.set_final_url(m.final_url);
714 r.set_redirected(m.redirected);
715}
716
717pub(crate) trait CspViolationsProcessor {
718 fn process_csp_violations(&self, violations: Vec<Violation>);
719}
720
721pub(crate) fn load_whole_resource(
723 request: RequestBuilder,
724 core_resource_thread: &CoreResourceThread,
725 global: &GlobalScope,
726 csp_violations_processor: &dyn CspViolationsProcessor,
727 can_gc: CanGc,
728) -> Result<(Metadata, Vec<u8>, bool), NetworkError> {
729 let request = request.https_state(global.get_https_state());
730 let (action_sender, action_receiver) = ipc::channel().unwrap();
731 let url = request.url.clone();
732 core_resource_thread
733 .send(CoreResourceMsg::Fetch(
734 request,
735 FetchChannels::ResponseMsg(action_sender),
736 ))
737 .unwrap();
738
739 let mut buf = vec![];
740 let mut metadata = None;
741 let mut muted_errors = false;
742 loop {
743 match action_receiver.recv().unwrap() {
744 FetchResponseMsg::ProcessRequestBody(..) | FetchResponseMsg::ProcessRequestEOF(..) => {
745 },
746 FetchResponseMsg::ProcessResponse(_, Ok(m)) => {
747 muted_errors = m.is_cors_cross_origin();
748 metadata = Some(match m {
749 FetchMetadata::Unfiltered(m) => m,
750 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
751 })
752 },
753 FetchResponseMsg::ProcessResponseChunk(_, data) => buf.extend_from_slice(&data),
754 FetchResponseMsg::ProcessResponseEOF(_, Ok(_), _) => {
755 let metadata = metadata.unwrap();
756 if let Some(timing) = &metadata.timing {
757 submit_timing_data(global, url, InitiatorType::Other, timing, can_gc);
758 }
759 return Ok((metadata, buf, muted_errors));
760 },
761 FetchResponseMsg::ProcessResponse(_, Err(e)) |
762 FetchResponseMsg::ProcessResponseEOF(_, Err(e), _) => return Err(e),
763 FetchResponseMsg::ProcessCspViolations(_, violations) => {
764 csp_violations_processor.process_csp_violations(violations);
765 },
766 }
767 }
768}
769
770pub(crate) trait RequestWithGlobalScope {
771 fn with_global_scope(self, global: &GlobalScope) -> Self;
772}
773
774impl RequestWithGlobalScope for RequestBuilder {
775 fn with_global_scope(self, global: &GlobalScope) -> Self {
776 self.insecure_requests_policy(global.insecure_requests_policy())
777 .has_trustworthy_ancestor_origin(global.has_trustworthy_ancestor_or_current_origin())
778 .policy_container(global.policy_container())
779 .client(global.request_client())
780 .pipeline_id(Some(global.pipeline_id()))
781 .origin(global.origin().immutable().clone())
782 .https_state(global.get_https_state())
783 }
784}
785
786#[allow(clippy::too_many_arguments)]
788pub(crate) fn create_a_potential_cors_request(
789 webview_id: Option<WebViewId>,
790 url: ServoUrl,
791 destination: Destination,
792 cors_setting: Option<CorsSettings>,
793 same_origin_fallback: Option<bool>,
794 referrer: Referrer,
795) -> RequestBuilder {
796 RequestBuilder::new(webview_id, url, referrer)
797 .mode(match cors_setting {
799 Some(_) => RequestMode::CorsMode,
800 None if same_origin_fallback == Some(true) => RequestMode::SameOrigin,
802 None => RequestMode::NoCors,
803 })
804 .credentials_mode(match cors_setting {
805 Some(CorsSettings::Anonymous) => CredentialsMode::CredentialsSameOrigin,
807 _ => CredentialsMode::Include,
809 })
810 .destination(destination)
813 .use_url_credentials(true)
814}