1use std::cell::Cell;
6use std::rc::Rc;
7use std::sync::{Arc, Mutex};
8use std::time::Duration;
9
10use base::id::WebViewId;
11use ipc_channel::ipc;
12use js::jsapi::{ExceptionStackBehavior, JS_IsExceptionPending};
13use js::jsval::UndefinedValue;
14use js::rust::HandleValue;
15use js::rust::wrappers::JS_SetPendingException;
16use net_traits::policy_container::{PolicyContainer, RequestPolicyContainer};
17use net_traits::request::{
18 CorsSettings, CredentialsMode, Destination, InsecureRequestsPolicy, Referrer,
19 Request as NetTraitsRequest, RequestBuilder, RequestId, RequestMode, ServiceWorkersMode,
20};
21use net_traits::{
22 CoreResourceMsg, CoreResourceThread, FetchChannels, FetchMetadata, FetchResponseListener,
23 FetchResponseMsg, FilteredMetadata, Metadata, NetworkError, ResourceFetchTiming,
24 ResourceTimingType, cancel_async_fetch,
25};
26use servo_url::ServoUrl;
27use timers::TimerEventRequest;
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::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::{self, PreInvoke, ResourceTimingListener, submit_timing_data};
57use crate::realms::{InRealm, enter_realm};
58use crate::script_runtime::CanGc;
59
60#[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}
73
74impl FetchCanceller {
75 pub(crate) fn new(request_id: RequestId, core_resource_thread: CoreResourceThread) -> Self {
78 Self {
79 request_id: Some(request_id),
80 core_resource_thread: Some(core_resource_thread),
81 }
82 }
83
84 pub(crate) fn cancel(&mut self) {
86 if let Some(request_id) = self.request_id.take() {
87 if let Some(ref core_resource_thread) = self.core_resource_thread {
91 cancel_async_fetch(vec![request_id], core_resource_thread);
94 }
95 }
96 }
97
98 pub(crate) fn ignore(&mut self) {
101 let _ = self.request_id.take();
102 }
103}
104
105impl Drop for FetchCanceller {
106 fn drop(&mut self) {
107 self.cancel()
108 }
109}
110
111fn request_init_from_request(request: NetTraitsRequest, global: &GlobalScope) -> RequestBuilder {
112 RequestBuilder {
113 id: request.id,
114 method: request.method.clone(),
115 url: request.url(),
116 headers: request.headers.clone(),
117 unsafe_request: request.unsafe_request,
118 body: request.body.clone(),
119 service_workers_mode: ServiceWorkersMode::All,
120 destination: request.destination,
121 synchronous: request.synchronous,
122 mode: request.mode.clone(),
123 cache_mode: request.cache_mode,
124 use_cors_preflight: request.use_cors_preflight,
125 credentials_mode: request.credentials_mode,
126 use_url_credentials: request.use_url_credentials,
127 origin: global.origin().immutable().clone(),
128 referrer: request.referrer.clone(),
129 referrer_policy: request.referrer_policy,
130 pipeline_id: request.pipeline_id,
131 target_webview_id: request.target_webview_id,
132 redirect_mode: request.redirect_mode,
133 integrity_metadata: request.integrity_metadata.clone(),
134 cryptographic_nonce_metadata: request.cryptographic_nonce_metadata.clone(),
135 url_list: vec![],
136 parser_metadata: request.parser_metadata,
137 initiator: request.initiator,
138 policy_container: RequestPolicyContainer::PolicyContainer(global.policy_container()),
139 insecure_requests_policy: request.insecure_requests_policy,
140 has_trustworthy_ancestor_origin: request.has_trustworthy_ancestor_origin,
141 https_state: request.https_state,
142 response_tainting: request.response_tainting,
143 crash: None,
144 }
145}
146
147fn abort_fetch_call(
149 promise: Rc<Promise>,
150 request: &Request,
151 response_object: Option<&Response>,
152 abort_reason: HandleValue,
153 global: &GlobalScope,
154 cx: SafeJSContext,
155 can_gc: CanGc,
156) {
157 promise.reject(cx, abort_reason, can_gc);
159 if let Some(body) = request.body() {
161 if body.is_readable() {
162 body.cancel(cx, global, abort_reason, can_gc);
163 }
164 }
165 let Some(response) = response_object else {
168 return;
169 };
170 if let Some(body) = response.body() {
172 if body.is_readable() {
173 body.error(abort_reason, can_gc);
174 }
175 }
176}
177
178#[allow(non_snake_case)]
180#[cfg_attr(crown, allow(crown::unrooted_must_root))]
181pub(crate) fn Fetch(
182 global: &GlobalScope,
183 input: RequestInfo,
184 init: RootedTraceableBox<RequestInit>,
185 comp: InRealm,
186 can_gc: CanGc,
187) -> Rc<Promise> {
188 let promise = Promise::new_in_current_realm(comp, can_gc);
190 let cx = GlobalScope::get_cx();
191
192 let response = Response::new(global, can_gc);
195 response.Headers(can_gc).set_guard(Guard::Immutable);
196
197 let request_object = match Request::Constructor(global, None, can_gc, input, init) {
200 Err(e) => {
201 response.error_stream(e.clone(), can_gc);
202 promise.reject_error(e, can_gc);
203 return promise;
204 },
205 Ok(r) => r,
206 };
207 let request = request_object.get_request();
209 let timing_type = request.timing_type();
210 let request_id = request.id;
211
212 let signal = request_object.Signal();
214 if signal.aborted() {
215 rooted!(in(*cx) let mut abort_reason = UndefinedValue());
217 signal.Reason(cx, abort_reason.handle_mut());
218 abort_fetch_call(
219 promise.clone(),
220 &request_object,
221 None,
222 abort_reason.handle(),
223 global,
224 cx,
225 can_gc,
226 );
227 return promise;
229 }
230
231 let mut request_init = request_init_from_request(request, global);
234
235 if global.is::<ServiceWorkerGlobalScope>() {
238 request_init.service_workers_mode = ServiceWorkersMode::None;
239 }
240
241 let fetch_context = Arc::new(Mutex::new(FetchContext {
248 fetch_promise: Some(TrustedPromise::new(promise.clone())),
249 response_object: Trusted::new(&*response),
250 request: Trusted::new(&*request_object),
251 global: Trusted::new(global),
252 resource_timing: ResourceFetchTiming::new(timing_type),
253 locally_aborted: false,
254 canceller: FetchCanceller::new(request_id, global.core_resource_thread()),
255 }));
256
257 signal.add(&AbortAlgorithm::Fetch(fetch_context.clone()));
259
260 global.fetch(
263 request_init,
264 fetch_context,
265 global.task_manager().networking_task_source().to_sendable(),
266 );
267
268 promise
270}
271
272fn queue_deferred_fetch(
274 request: NetTraitsRequest,
275 activate_after: Finite<f64>,
276 global: &GlobalScope,
277) -> Arc<Mutex<DeferredFetchRecord>> {
278 let trusted_global = Trusted::new(global);
279 let deferred_record = Arc::new(Mutex::new(DeferredFetchRecord {
287 request,
288 global: trusted_global.clone(),
289 invoke_state: Cell::new(DeferredFetchRecordInvokeState::Pending),
290 activated: Cell::new(false),
291 }));
292 let deferred_record_clone = deferred_record.clone();
296 global.schedule_timer(TimerEventRequest {
297 callback: Box::new(move || {
298 deferred_record_clone.lock().unwrap().process();
300
301 let deferred_record_clone = deferred_record_clone.clone();
306 trusted_global
307 .root()
308 .task_manager()
309 .deferred_fetch_task_source()
310 .queue(task!(notify_deferred_record: move || {
311 deferred_record_clone.lock().unwrap().activate();
312 }));
313 }),
314 duration: Duration::from_millis(*activate_after as u64),
316 });
317 deferred_record
319}
320
321#[allow(non_snake_case, unsafe_code)]
323pub(crate) fn FetchLater(
324 window: &Window,
325 input: RequestInfo,
326 init: RootedTraceableBox<DeferredRequestInit>,
327 can_gc: CanGc,
328) -> Fallible<DomRoot<FetchLaterResult>> {
329 let global_scope = window.upcast();
330 let request_object = Request::constructor(global_scope, None, can_gc, input, &init.parent)?;
333 let signal = request_object.Signal();
335 if signal.aborted() {
336 let cx = GlobalScope::get_cx();
337 rooted!(in(*cx) let mut abort_reason = UndefinedValue());
338 signal.Reason(cx, abort_reason.handle_mut());
339 unsafe {
340 assert!(!JS_IsExceptionPending(*cx));
341 JS_SetPendingException(*cx, abort_reason.handle(), ExceptionStackBehavior::Capture);
342 }
343 return Err(Error::JSFailed);
344 }
345 let request = request_object.get_request();
347 let mut activate_after = Finite::wrap(0_f64);
349 if let Some(init_activate_after) = init.activateAfter.as_ref() {
352 activate_after = *init_activate_after;
353 }
354 if *activate_after < 0.0 {
356 return Err(Error::Range("activateAfter must be at least 0".to_owned()));
357 }
358 if !window.Document().is_fully_active() {
360 return Err(Error::Type("Document is not fully active".to_owned()));
361 }
362 let url = request.url();
363 if !matches!(url.scheme(), "http" | "https") {
365 return Err(Error::Type("URL is not http(s)".to_owned()));
366 }
367 if !url.is_potentially_trustworthy() {
369 return Err(Error::Type("URL is not trustworthy".to_owned()));
370 }
371 if let Some(body) = request.body.as_ref() {
373 if body.len().is_none() {
374 return Err(Error::Type("Body is null".to_owned()));
375 }
376 }
377 let deferred_record = queue_deferred_fetch(request, activate_after, global_scope);
384 signal.add(&AbortAlgorithm::FetchLater(deferred_record.clone()));
386 Ok(FetchLaterResult::new(window, deferred_record, can_gc))
388}
389
390#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
392enum DeferredFetchRecordInvokeState {
393 Pending,
394 Sent,
395 Aborted,
396}
397
398#[derive(MallocSizeOf)]
400pub(crate) struct DeferredFetchRecord {
401 request: NetTraitsRequest,
403 invoke_state: Cell<DeferredFetchRecordInvokeState>,
405 global: Trusted<GlobalScope>,
406 activated: Cell<bool>,
407}
408
409impl DeferredFetchRecord {
410 fn activate(&self) {
412 self.activated.set(true);
414 }
415 pub(crate) fn abort(&self) {
417 self.invoke_state
419 .set(DeferredFetchRecordInvokeState::Aborted);
420 }
421 pub(crate) fn activated_getter_steps(&self) -> bool {
423 self.activated.get()
425 }
426 fn process(&self) {
428 if self.invoke_state.get() != DeferredFetchRecordInvokeState::Pending {
430 return;
431 }
432 self.invoke_state.set(DeferredFetchRecordInvokeState::Sent);
434 let url = self.request.url().clone();
436 let fetch_later_listener = Arc::new(Mutex::new(FetchLaterListener {
437 url,
438 resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource),
439 global: self.global.clone(),
440 }));
441 let global = self.global.root();
442 let _realm = enter_realm(&*global);
443 let request_init = request_init_from_request(self.request.clone(), &global);
444 global.fetch(
445 request_init,
446 fetch_later_listener,
447 global.task_manager().networking_task_source().to_sendable(),
448 );
449 }
451}
452
453#[derive(JSTraceable, MallocSizeOf)]
454pub(crate) struct FetchContext {
455 #[ignore_malloc_size_of = "unclear ownership semantics"]
456 fetch_promise: Option<TrustedPromise>,
457 response_object: Trusted<Response>,
458 request: Trusted<Request>,
459 global: Trusted<GlobalScope>,
460 #[no_trace]
461 resource_timing: ResourceFetchTiming,
462 locally_aborted: bool,
463 canceller: FetchCanceller,
464}
465
466impl PreInvoke for FetchContext {}
467
468impl FetchContext {
469 pub(crate) fn abort_fetch(
471 &mut self,
472 abort_reason: HandleValue,
473 cx: SafeJSContext,
474 can_gc: CanGc,
475 ) {
476 self.locally_aborted = true;
478 self.canceller.cancel();
484
485 let promise = self
488 .fetch_promise
489 .take()
490 .expect("fetch promise is missing")
491 .root();
492 abort_fetch_call(
493 promise,
494 &self.request.root(),
495 Some(&self.response_object.root()),
496 abort_reason,
497 &self.global.root(),
498 cx,
499 can_gc,
500 );
501 }
502}
503
504impl FetchResponseListener for FetchContext {
506 fn process_request_body(&mut self, _: RequestId) {
507 }
509
510 fn process_request_eof(&mut self, _: RequestId) {
511 }
513
514 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
515 fn process_response(
516 &mut self,
517 _: RequestId,
518 fetch_metadata: Result<FetchMetadata, NetworkError>,
519 ) {
520 if self.locally_aborted {
522 return;
523 }
524 let promise = self
525 .fetch_promise
526 .take()
527 .expect("fetch promise is missing")
528 .root();
529
530 let _ac = enter_realm(&*promise);
531 match fetch_metadata {
532 Err(_) => {
535 promise.reject_error(
536 Error::Type("Network error occurred".to_string()),
537 CanGc::note(),
538 );
539 self.fetch_promise = Some(TrustedPromise::new(promise));
540 let response = self.response_object.root();
541 response.set_type(DOMResponseType::Error, CanGc::note());
542 response.error_stream(
543 Error::Type("Network error occurred".to_string()),
544 CanGc::note(),
545 );
546 return;
547 },
548 Ok(metadata) => match metadata {
551 FetchMetadata::Unfiltered(m) => {
552 fill_headers_with_metadata(self.response_object.root(), m, CanGc::note());
553 self.response_object
554 .root()
555 .set_type(DOMResponseType::Default, CanGc::note());
556 },
557 FetchMetadata::Filtered { filtered, .. } => match filtered {
558 FilteredMetadata::Basic(m) => {
559 fill_headers_with_metadata(self.response_object.root(), m, CanGc::note());
560 self.response_object
561 .root()
562 .set_type(DOMResponseType::Basic, CanGc::note());
563 },
564 FilteredMetadata::Cors(m) => {
565 fill_headers_with_metadata(self.response_object.root(), m, CanGc::note());
566 self.response_object
567 .root()
568 .set_type(DOMResponseType::Cors, CanGc::note());
569 },
570 FilteredMetadata::Opaque => {
571 self.response_object
572 .root()
573 .set_type(DOMResponseType::Opaque, CanGc::note());
574 },
575 FilteredMetadata::OpaqueRedirect(url) => {
576 let r = self.response_object.root();
577 r.set_type(DOMResponseType::Opaqueredirect, CanGc::note());
578 r.set_final_url(url);
579 },
580 },
581 },
582 }
583
584 promise.resolve_native(&self.response_object.root(), CanGc::note());
586 self.fetch_promise = Some(TrustedPromise::new(promise));
587 }
588
589 fn process_response_chunk(&mut self, _: RequestId, chunk: Vec<u8>) {
590 let response = self.response_object.root();
591 response.stream_chunk(chunk, CanGc::note());
592 }
593
594 fn process_response_eof(
595 &mut self,
596 _: RequestId,
597 _response: Result<ResourceFetchTiming, NetworkError>,
598 ) {
599 let response = self.response_object.root();
600 let _ac = enter_realm(&*response);
601 response.finish(CanGc::note());
602 }
605
606 fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming {
607 &mut self.resource_timing
608 }
609
610 fn resource_timing(&self) -> &ResourceFetchTiming {
611 &self.resource_timing
612 }
613
614 fn submit_resource_timing(&mut self) {
615 if self.resource_timing.timing_type == ResourceTimingType::Resource {
617 network_listener::submit_timing(self, CanGc::note())
618 }
619 }
620
621 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
622 let global = &self.resource_timing_global();
623 global.report_csp_violations(violations, None, None);
624 }
625}
626
627impl ResourceTimingListener for FetchContext {
628 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
629 (
630 InitiatorType::Fetch,
631 self.resource_timing_global().get_url().clone(),
632 )
633 }
634
635 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
636 self.response_object.root().global()
637 }
638}
639
640struct FetchLaterListener {
641 url: ServoUrl,
643 resource_timing: ResourceFetchTiming,
645 global: Trusted<GlobalScope>,
647}
648
649impl FetchResponseListener for FetchLaterListener {
650 fn process_request_body(&mut self, _: RequestId) {}
651
652 fn process_request_eof(&mut self, _: RequestId) {}
653
654 fn process_response(
655 &mut self,
656 _: RequestId,
657 fetch_metadata: Result<FetchMetadata, NetworkError>,
658 ) {
659 _ = fetch_metadata;
660 }
661
662 fn process_response_chunk(&mut self, _: RequestId, chunk: Vec<u8>) {
663 _ = chunk;
664 }
665
666 fn process_response_eof(
667 &mut self,
668 _: RequestId,
669 response: Result<ResourceFetchTiming, NetworkError>,
670 ) {
671 _ = response;
672 }
673
674 fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming {
675 &mut self.resource_timing
676 }
677
678 fn resource_timing(&self) -> &ResourceFetchTiming {
679 &self.resource_timing
680 }
681
682 fn submit_resource_timing(&mut self) {
683 network_listener::submit_timing(self, 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
702impl PreInvoke for FetchLaterListener {
703 fn should_invoke(&self) -> bool {
704 true
705 }
706}
707
708fn fill_headers_with_metadata(r: DomRoot<Response>, m: Metadata, can_gc: CanGc) {
709 r.set_headers(m.headers, can_gc);
710 r.set_status(&m.status);
711 r.set_final_url(m.final_url);
712 r.set_redirected(m.redirected);
713}
714
715pub(crate) trait CspViolationsProcessor {
716 fn process_csp_violations(&self, violations: Vec<Violation>);
717}
718
719pub(crate) fn load_whole_resource(
721 request: RequestBuilder,
722 core_resource_thread: &CoreResourceThread,
723 global: &GlobalScope,
724 csp_violations_processor: &dyn CspViolationsProcessor,
725 can_gc: CanGc,
726) -> Result<(Metadata, Vec<u8>), NetworkError> {
727 let request = request.https_state(global.get_https_state());
728 let (action_sender, action_receiver) = ipc::channel().unwrap();
729 let url = request.url.clone();
730 core_resource_thread
731 .send(CoreResourceMsg::Fetch(
732 request,
733 FetchChannels::ResponseMsg(action_sender),
734 ))
735 .unwrap();
736
737 let mut buf = vec![];
738 let mut metadata = None;
739 loop {
740 match action_receiver.recv().unwrap() {
741 FetchResponseMsg::ProcessRequestBody(..) | FetchResponseMsg::ProcessRequestEOF(..) => {
742 },
743 FetchResponseMsg::ProcessResponse(_, Ok(m)) => {
744 metadata = Some(match m {
745 FetchMetadata::Unfiltered(m) => m,
746 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
747 })
748 },
749 FetchResponseMsg::ProcessResponseChunk(_, data) => buf.extend_from_slice(&data),
750 FetchResponseMsg::ProcessResponseEOF(_, Ok(_)) => {
751 let metadata = metadata.unwrap();
752 if let Some(timing) = &metadata.timing {
753 submit_timing_data(global, url, InitiatorType::Other, timing, can_gc);
754 }
755 return Ok((metadata, buf));
756 },
757 FetchResponseMsg::ProcessResponse(_, Err(e)) |
758 FetchResponseMsg::ProcessResponseEOF(_, Err(e)) => return Err(e),
759 FetchResponseMsg::ProcessCspViolations(_, violations) => {
760 csp_violations_processor.process_csp_violations(violations);
761 },
762 }
763 }
764}
765
766#[allow(clippy::too_many_arguments)]
768pub(crate) fn create_a_potential_cors_request(
769 webview_id: Option<WebViewId>,
770 url: ServoUrl,
771 destination: Destination,
772 cors_setting: Option<CorsSettings>,
773 same_origin_fallback: Option<bool>,
774 referrer: Referrer,
775 insecure_requests_policy: InsecureRequestsPolicy,
776 has_trustworthy_ancestor_origin: bool,
777 policy_container: PolicyContainer,
778) -> RequestBuilder {
779 RequestBuilder::new(webview_id, url, referrer)
780 .mode(match cors_setting {
783 Some(_) => RequestMode::CorsMode,
784 None if same_origin_fallback == Some(true) => RequestMode::SameOrigin,
785 None => RequestMode::NoCors,
786 })
787 .credentials_mode(match cors_setting {
790 Some(CorsSettings::Anonymous) => CredentialsMode::CredentialsSameOrigin,
791 _ => CredentialsMode::Include,
792 })
793 .destination(destination)
795 .use_url_credentials(true)
796 .insecure_requests_policy(insecure_requests_policy)
797 .has_trustworthy_ancestor_origin(has_trustworthy_ancestor_origin)
798 .policy_container(policy_container)
799}