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 response_object.finish(CanGc::note());
630 network_listener::submit_timing(&self, &response, &timing, CanGc::note());
635 }
636
637 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
638 let global = &self.resource_timing_global();
639 global.report_csp_violations(violations, None, None);
640 }
641}
642
643impl ResourceTimingListener for FetchContext {
644 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
645 (InitiatorType::Fetch, self.url.clone())
646 }
647
648 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
649 self.response_object.root().global()
650 }
651}
652
653struct FetchLaterListener {
654 url: ServoUrl,
656 global: Trusted<GlobalScope>,
658}
659
660impl FetchResponseListener for FetchLaterListener {
661 fn process_request_body(&mut self, _: RequestId) {}
662
663 fn process_request_eof(&mut self, _: RequestId) {}
664
665 fn process_response(
666 &mut self,
667 _: RequestId,
668 fetch_metadata: Result<FetchMetadata, NetworkError>,
669 ) {
670 _ = fetch_metadata;
671 }
672
673 fn process_response_chunk(&mut self, _: RequestId, chunk: Vec<u8>) {
674 _ = chunk;
675 }
676
677 fn process_response_eof(
678 self,
679 _: RequestId,
680 response: Result<(), NetworkError>,
681 timing: ResourceFetchTiming,
682 ) {
683 network_listener::submit_timing(&self, &response, &timing, CanGc::note());
684 }
685
686 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
687 let global = self.resource_timing_global();
688 global.report_csp_violations(violations, None, None);
689 }
690}
691
692impl ResourceTimingListener for FetchLaterListener {
693 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
694 (InitiatorType::Fetch, self.url.clone())
695 }
696
697 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
698 self.global.root()
699 }
700}
701
702fn fill_headers_with_metadata(r: DomRoot<Response>, m: Metadata, can_gc: CanGc) {
703 r.set_headers(m.headers, can_gc);
704 r.set_status(&m.status);
705 r.set_final_url(m.final_url);
706 r.set_redirected(m.redirected);
707}
708
709pub(crate) trait CspViolationsProcessor {
710 fn process_csp_violations(&self, violations: Vec<Violation>);
711}
712
713pub(crate) fn load_whole_resource(
715 request: RequestBuilder,
716 core_resource_thread: &CoreResourceThread,
717 global: &GlobalScope,
718 csp_violations_processor: &dyn CspViolationsProcessor,
719 can_gc: CanGc,
720) -> Result<(Metadata, Vec<u8>, bool), NetworkError> {
721 let request = request.https_state(global.get_https_state());
722 let (action_sender, action_receiver) = ipc::channel().unwrap();
723 let url = request.url.clone();
724 core_resource_thread
725 .send(CoreResourceMsg::Fetch(
726 request,
727 FetchChannels::ResponseMsg(action_sender),
728 ))
729 .unwrap();
730
731 let mut buf = vec![];
732 let mut metadata = None;
733 let mut muted_errors = false;
734 loop {
735 match action_receiver.recv().unwrap() {
736 FetchResponseMsg::ProcessRequestBody(..) | FetchResponseMsg::ProcessRequestEOF(..) => {
737 },
738 FetchResponseMsg::ProcessResponse(_, Ok(m)) => {
739 muted_errors = m.is_cors_cross_origin();
740 metadata = Some(match m {
741 FetchMetadata::Unfiltered(m) => m,
742 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
743 })
744 },
745 FetchResponseMsg::ProcessResponseChunk(_, data) => buf.extend_from_slice(&data),
746 FetchResponseMsg::ProcessResponseEOF(_, Ok(_), _) => {
747 let metadata = metadata.unwrap();
748 if let Some(timing) = &metadata.timing {
749 submit_timing_data(global, url, InitiatorType::Other, timing, can_gc);
750 }
751 return Ok((metadata, buf, muted_errors));
752 },
753 FetchResponseMsg::ProcessResponse(_, Err(e)) |
754 FetchResponseMsg::ProcessResponseEOF(_, Err(e), _) => return Err(e),
755 FetchResponseMsg::ProcessCspViolations(_, violations) => {
756 csp_violations_processor.process_csp_violations(violations);
757 },
758 }
759 }
760}
761
762pub(crate) trait RequestWithGlobalScope {
763 fn with_global_scope(self, global: &GlobalScope) -> Self;
764}
765
766impl RequestWithGlobalScope for RequestBuilder {
767 fn with_global_scope(self, global: &GlobalScope) -> Self {
768 self.insecure_requests_policy(global.insecure_requests_policy())
769 .has_trustworthy_ancestor_origin(global.has_trustworthy_ancestor_or_current_origin())
770 .policy_container(global.policy_container())
771 .client(global.request_client())
772 .pipeline_id(Some(global.pipeline_id()))
773 .origin(global.origin().immutable().clone())
774 .https_state(global.get_https_state())
775 }
776}
777
778#[allow(clippy::too_many_arguments)]
780pub(crate) fn create_a_potential_cors_request(
781 webview_id: Option<WebViewId>,
782 url: ServoUrl,
783 destination: Destination,
784 cors_setting: Option<CorsSettings>,
785 same_origin_fallback: Option<bool>,
786 referrer: Referrer,
787) -> RequestBuilder {
788 RequestBuilder::new(webview_id, url, referrer)
789 .mode(match cors_setting {
791 Some(_) => RequestMode::CorsMode,
792 None if same_origin_fallback == Some(true) => RequestMode::SameOrigin,
794 None => RequestMode::NoCors,
795 })
796 .credentials_mode(match cors_setting {
797 Some(CorsSettings::Anonymous) => CredentialsMode::CredentialsSameOrigin,
799 _ => CredentialsMode::Include,
801 })
802 .destination(destination)
805 .use_url_credentials(true)
806}