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;
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::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::{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 let mut builder =
113 RequestBuilder::new(request.target_webview_id, request.url(), request.referrer)
114 .method(request.method)
115 .headers(request.headers)
116 .unsafe_request(request.unsafe_request)
117 .body(request.body)
118 .destination(request.destination)
119 .synchronous(request.synchronous)
120 .mode(request.mode)
121 .cache_mode(request.cache_mode)
122 .use_cors_preflight(request.use_cors_preflight)
123 .credentials_mode(request.credentials_mode)
124 .use_url_credentials(request.use_url_credentials)
125 .referrer_policy(request.referrer_policy)
126 .pipeline_id(request.pipeline_id)
127 .redirect_mode(request.redirect_mode)
128 .integrity_metadata(request.integrity_metadata)
129 .cryptographic_nonce_metadata(request.cryptographic_nonce_metadata)
130 .parser_metadata(request.parser_metadata)
131 .initiator(request.initiator)
132 .client(global.request_client())
133 .insecure_requests_policy(request.insecure_requests_policy)
134 .has_trustworthy_ancestor_origin(request.has_trustworthy_ancestor_origin)
135 .https_state(request.https_state)
136 .response_tainting(request.response_tainting);
137 builder.id = request.id;
138 builder
139}
140
141fn abort_fetch_call(
143 promise: Rc<Promise>,
144 request: &Request,
145 response_object: Option<&Response>,
146 abort_reason: HandleValue,
147 global: &GlobalScope,
148 cx: SafeJSContext,
149 can_gc: CanGc,
150) {
151 promise.reject(cx, abort_reason, can_gc);
153 if let Some(body) = request.body() {
155 if body.is_readable() {
156 body.cancel(cx, global, abort_reason, can_gc);
157 }
158 }
159 let Some(response) = response_object else {
162 return;
163 };
164 if let Some(body) = response.body() {
166 if body.is_readable() {
167 body.error(abort_reason, can_gc);
168 }
169 }
170}
171
172#[allow(non_snake_case)]
174#[cfg_attr(crown, allow(crown::unrooted_must_root))]
175pub(crate) fn Fetch(
176 global: &GlobalScope,
177 input: RequestInfo,
178 init: RootedTraceableBox<RequestInit>,
179 comp: InRealm,
180 can_gc: CanGc,
181) -> Rc<Promise> {
182 let promise = Promise::new_in_current_realm(comp, can_gc);
184 let cx = GlobalScope::get_cx();
185
186 let response = Response::new(global, can_gc);
189 response.Headers(can_gc).set_guard(Guard::Immutable);
190
191 let request_object = match Request::Constructor(global, None, can_gc, input, init) {
194 Err(e) => {
195 response.error_stream(e.clone(), can_gc);
196 promise.reject_error(e, can_gc);
197 return promise;
198 },
199 Ok(r) => r,
200 };
201 let request = request_object.get_request();
203 let timing_type = request.timing_type();
204 let request_id = request.id;
205
206 let signal = request_object.Signal();
208 if signal.aborted() {
209 rooted!(in(*cx) let mut abort_reason = UndefinedValue());
211 signal.Reason(cx, abort_reason.handle_mut());
212 abort_fetch_call(
213 promise.clone(),
214 &request_object,
215 None,
216 abort_reason.handle(),
217 global,
218 cx,
219 can_gc,
220 );
221 return promise;
223 }
224
225 let mut request_init = request_init_from_request(request, global);
228
229 if global.is::<ServiceWorkerGlobalScope>() {
232 request_init.service_workers_mode = ServiceWorkersMode::None;
233 }
234
235 let fetch_context = Arc::new(Mutex::new(FetchContext {
242 fetch_promise: Some(TrustedPromise::new(promise.clone())),
243 response_object: Trusted::new(&*response),
244 request: Trusted::new(&*request_object),
245 global: Trusted::new(global),
246 resource_timing: ResourceFetchTiming::new(timing_type),
247 locally_aborted: false,
248 canceller: FetchCanceller::new(request_id, global.core_resource_thread()),
249 }));
250
251 signal.add(&AbortAlgorithm::Fetch(fetch_context.clone()));
253
254 global.fetch(
257 request_init,
258 fetch_context,
259 global.task_manager().networking_task_source().to_sendable(),
260 );
261
262 promise
264}
265
266fn queue_deferred_fetch(
268 request: NetTraitsRequest,
269 activate_after: Finite<f64>,
270 global: &GlobalScope,
271) -> Arc<Mutex<DeferredFetchRecord>> {
272 let trusted_global = Trusted::new(global);
273 let deferred_record = Arc::new(Mutex::new(DeferredFetchRecord {
281 request,
282 global: trusted_global.clone(),
283 invoke_state: Cell::new(DeferredFetchRecordInvokeState::Pending),
284 activated: Cell::new(false),
285 }));
286 let deferred_record_clone = deferred_record.clone();
290 global.schedule_timer(TimerEventRequest {
291 callback: Box::new(move || {
292 deferred_record_clone.lock().unwrap().process();
294
295 let deferred_record_clone = deferred_record_clone.clone();
300 trusted_global
301 .root()
302 .task_manager()
303 .deferred_fetch_task_source()
304 .queue(task!(notify_deferred_record: move || {
305 deferred_record_clone.lock().unwrap().activate();
306 }));
307 }),
308 duration: Duration::from_millis(*activate_after as u64),
310 });
311 deferred_record
313}
314
315#[allow(non_snake_case, unsafe_code)]
317pub(crate) fn FetchLater(
318 window: &Window,
319 input: RequestInfo,
320 init: RootedTraceableBox<DeferredRequestInit>,
321 can_gc: CanGc,
322) -> Fallible<DomRoot<FetchLaterResult>> {
323 let global_scope = window.upcast();
324 let request_object = Request::constructor(global_scope, None, can_gc, input, &init.parent)?;
327 let signal = request_object.Signal();
329 if signal.aborted() {
330 let cx = GlobalScope::get_cx();
331 rooted!(in(*cx) let mut abort_reason = UndefinedValue());
332 signal.Reason(cx, abort_reason.handle_mut());
333 unsafe {
334 assert!(!JS_IsExceptionPending(*cx));
335 JS_SetPendingException(*cx, abort_reason.handle(), ExceptionStackBehavior::Capture);
336 }
337 return Err(Error::JSFailed);
338 }
339 let request = request_object.get_request();
341 let mut activate_after = Finite::wrap(0_f64);
343 if let Some(init_activate_after) = init.activateAfter.as_ref() {
346 activate_after = *init_activate_after;
347 }
348 if *activate_after < 0.0 {
350 return Err(Error::Range("activateAfter must be at least 0".to_owned()));
351 }
352 if !window.Document().is_fully_active() {
354 return Err(Error::Type("Document is not fully active".to_owned()));
355 }
356 let url = request.url();
357 if !matches!(url.scheme(), "http" | "https") {
359 return Err(Error::Type("URL is not http(s)".to_owned()));
360 }
361 if !url.is_potentially_trustworthy() {
363 return Err(Error::Type("URL is not trustworthy".to_owned()));
364 }
365 if let Some(body) = request.body.as_ref() {
367 if body.len().is_none() {
368 return Err(Error::Type("Body is null".to_owned()));
369 }
370 }
371 let deferred_record = queue_deferred_fetch(request, activate_after, global_scope);
378 signal.add(&AbortAlgorithm::FetchLater(deferred_record.clone()));
380 Ok(FetchLaterResult::new(window, deferred_record, can_gc))
382}
383
384#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
386enum DeferredFetchRecordInvokeState {
387 Pending,
388 Sent,
389 Aborted,
390}
391
392#[derive(MallocSizeOf)]
394pub(crate) struct DeferredFetchRecord {
395 request: NetTraitsRequest,
397 invoke_state: Cell<DeferredFetchRecordInvokeState>,
399 global: Trusted<GlobalScope>,
400 activated: Cell<bool>,
401}
402
403impl DeferredFetchRecord {
404 fn activate(&self) {
406 self.activated.set(true);
408 }
409 pub(crate) fn abort(&self) {
411 self.invoke_state
413 .set(DeferredFetchRecordInvokeState::Aborted);
414 }
415 pub(crate) fn activated_getter_steps(&self) -> bool {
417 self.activated.get()
419 }
420 fn process(&self) {
422 if self.invoke_state.get() != DeferredFetchRecordInvokeState::Pending {
424 return;
425 }
426 self.invoke_state.set(DeferredFetchRecordInvokeState::Sent);
428 let url = self.request.url().clone();
430 let fetch_later_listener = Arc::new(Mutex::new(FetchLaterListener {
431 url,
432 resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource),
433 global: self.global.clone(),
434 }));
435 let global = self.global.root();
436 let request_init = request_init_from_request(self.request.clone(), &global);
437 global.fetch(
438 request_init,
439 fetch_later_listener,
440 global.task_manager().networking_task_source().to_sendable(),
441 );
442 }
444}
445
446#[derive(JSTraceable, MallocSizeOf)]
447pub(crate) struct FetchContext {
448 #[ignore_malloc_size_of = "unclear ownership semantics"]
449 fetch_promise: Option<TrustedPromise>,
450 response_object: Trusted<Response>,
451 request: Trusted<Request>,
452 global: Trusted<GlobalScope>,
453 #[no_trace]
454 resource_timing: ResourceFetchTiming,
455 locally_aborted: bool,
456 canceller: FetchCanceller,
457}
458
459impl PreInvoke for FetchContext {}
460
461impl FetchContext {
462 pub(crate) fn abort_fetch(
464 &mut self,
465 abort_reason: HandleValue,
466 cx: SafeJSContext,
467 can_gc: CanGc,
468 ) {
469 self.locally_aborted = true;
471 self.canceller.cancel();
477
478 let promise = self
481 .fetch_promise
482 .take()
483 .expect("fetch promise is missing")
484 .root();
485 abort_fetch_call(
486 promise,
487 &self.request.root(),
488 Some(&self.response_object.root()),
489 abort_reason,
490 &self.global.root(),
491 cx,
492 can_gc,
493 );
494 }
495}
496
497impl FetchResponseListener for FetchContext {
499 fn process_request_body(&mut self, _: RequestId) {
500 }
502
503 fn process_request_eof(&mut self, _: RequestId) {
504 }
506
507 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
508 fn process_response(
509 &mut self,
510 _: RequestId,
511 fetch_metadata: Result<FetchMetadata, NetworkError>,
512 ) {
513 if self.locally_aborted {
515 return;
516 }
517 let promise = self
518 .fetch_promise
519 .take()
520 .expect("fetch promise is missing")
521 .root();
522
523 let _ac = enter_realm(&*promise);
524 match fetch_metadata {
525 Err(_) => {
528 promise.reject_error(
529 Error::Type("Network error occurred".to_string()),
530 CanGc::note(),
531 );
532 self.fetch_promise = Some(TrustedPromise::new(promise));
533 let response = self.response_object.root();
534 response.set_type(DOMResponseType::Error, CanGc::note());
535 response.error_stream(
536 Error::Type("Network error occurred".to_string()),
537 CanGc::note(),
538 );
539 return;
540 },
541 Ok(metadata) => match metadata {
544 FetchMetadata::Unfiltered(m) => {
545 fill_headers_with_metadata(self.response_object.root(), m, CanGc::note());
546 self.response_object
547 .root()
548 .set_type(DOMResponseType::Default, CanGc::note());
549 },
550 FetchMetadata::Filtered { filtered, .. } => match filtered {
551 FilteredMetadata::Basic(m) => {
552 fill_headers_with_metadata(self.response_object.root(), m, CanGc::note());
553 self.response_object
554 .root()
555 .set_type(DOMResponseType::Basic, CanGc::note());
556 },
557 FilteredMetadata::Cors(m) => {
558 fill_headers_with_metadata(self.response_object.root(), m, CanGc::note());
559 self.response_object
560 .root()
561 .set_type(DOMResponseType::Cors, CanGc::note());
562 },
563 FilteredMetadata::Opaque => {
564 self.response_object
565 .root()
566 .set_type(DOMResponseType::Opaque, CanGc::note());
567 },
568 FilteredMetadata::OpaqueRedirect(url) => {
569 let r = self.response_object.root();
570 r.set_type(DOMResponseType::Opaqueredirect, CanGc::note());
571 r.set_final_url(url);
572 },
573 },
574 },
575 }
576
577 promise.resolve_native(&self.response_object.root(), CanGc::note());
579 self.fetch_promise = Some(TrustedPromise::new(promise));
580 }
581
582 fn process_response_chunk(&mut self, _: RequestId, chunk: Vec<u8>) {
583 let response = self.response_object.root();
584 response.stream_chunk(chunk, CanGc::note());
585 }
586
587 fn process_response_eof(
588 &mut self,
589 _: RequestId,
590 _response: Result<ResourceFetchTiming, NetworkError>,
591 ) {
592 let response = self.response_object.root();
593 let _ac = enter_realm(&*response);
594 response.finish(CanGc::note());
595 }
598
599 fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming {
600 &mut self.resource_timing
601 }
602
603 fn resource_timing(&self) -> &ResourceFetchTiming {
604 &self.resource_timing
605 }
606
607 fn submit_resource_timing(&mut self) {
608 if self.resource_timing.timing_type == ResourceTimingType::Resource {
610 network_listener::submit_timing(self, CanGc::note())
611 }
612 }
613
614 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
615 let global = &self.resource_timing_global();
616 global.report_csp_violations(violations, None, None);
617 }
618}
619
620impl ResourceTimingListener for FetchContext {
621 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
622 (
623 InitiatorType::Fetch,
624 self.resource_timing_global().get_url().clone(),
625 )
626 }
627
628 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
629 self.response_object.root().global()
630 }
631}
632
633struct FetchLaterListener {
634 url: ServoUrl,
636 resource_timing: ResourceFetchTiming,
638 global: Trusted<GlobalScope>,
640}
641
642impl FetchResponseListener for FetchLaterListener {
643 fn process_request_body(&mut self, _: RequestId) {}
644
645 fn process_request_eof(&mut self, _: RequestId) {}
646
647 fn process_response(
648 &mut self,
649 _: RequestId,
650 fetch_metadata: Result<FetchMetadata, NetworkError>,
651 ) {
652 _ = fetch_metadata;
653 }
654
655 fn process_response_chunk(&mut self, _: RequestId, chunk: Vec<u8>) {
656 _ = chunk;
657 }
658
659 fn process_response_eof(
660 &mut self,
661 _: RequestId,
662 response: Result<ResourceFetchTiming, NetworkError>,
663 ) {
664 _ = response;
665 }
666
667 fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming {
668 &mut self.resource_timing
669 }
670
671 fn resource_timing(&self) -> &ResourceFetchTiming {
672 &self.resource_timing
673 }
674
675 fn submit_resource_timing(&mut self) {
676 network_listener::submit_timing(self, CanGc::note())
677 }
678
679 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
680 let global = self.resource_timing_global();
681 global.report_csp_violations(violations, None, None);
682 }
683}
684
685impl ResourceTimingListener for FetchLaterListener {
686 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
687 (InitiatorType::Fetch, self.url.clone())
688 }
689
690 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
691 self.global.root()
692 }
693}
694
695impl PreInvoke for FetchLaterListener {
696 fn should_invoke(&self) -> bool {
697 true
698 }
699}
700
701fn fill_headers_with_metadata(r: DomRoot<Response>, m: Metadata, can_gc: CanGc) {
702 r.set_headers(m.headers, can_gc);
703 r.set_status(&m.status);
704 r.set_final_url(m.final_url);
705 r.set_redirected(m.redirected);
706}
707
708pub(crate) trait CspViolationsProcessor {
709 fn process_csp_violations(&self, violations: Vec<Violation>);
710}
711
712pub(crate) fn load_whole_resource(
714 request: RequestBuilder,
715 core_resource_thread: &CoreResourceThread,
716 global: &GlobalScope,
717 csp_violations_processor: &dyn CspViolationsProcessor,
718 can_gc: CanGc,
719) -> Result<(Metadata, Vec<u8>), NetworkError> {
720 let request = request.https_state(global.get_https_state());
721 let (action_sender, action_receiver) = ipc::channel().unwrap();
722 let url = request.url.clone();
723 core_resource_thread
724 .send(CoreResourceMsg::Fetch(
725 request,
726 FetchChannels::ResponseMsg(action_sender),
727 ))
728 .unwrap();
729
730 let mut buf = vec![];
731 let mut metadata = None;
732 loop {
733 match action_receiver.recv().unwrap() {
734 FetchResponseMsg::ProcessRequestBody(..) | FetchResponseMsg::ProcessRequestEOF(..) => {
735 },
736 FetchResponseMsg::ProcessResponse(_, Ok(m)) => {
737 metadata = Some(match m {
738 FetchMetadata::Unfiltered(m) => m,
739 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
740 })
741 },
742 FetchResponseMsg::ProcessResponseChunk(_, data) => buf.extend_from_slice(&data),
743 FetchResponseMsg::ProcessResponseEOF(_, Ok(_)) => {
744 let metadata = metadata.unwrap();
745 if let Some(timing) = &metadata.timing {
746 submit_timing_data(global, url, InitiatorType::Other, timing, can_gc);
747 }
748 return Ok((metadata, buf));
749 },
750 FetchResponseMsg::ProcessResponse(_, Err(e)) |
751 FetchResponseMsg::ProcessResponseEOF(_, Err(e)) => return Err(e),
752 FetchResponseMsg::ProcessCspViolations(_, violations) => {
753 csp_violations_processor.process_csp_violations(violations);
754 },
755 }
756 }
757}
758
759#[allow(clippy::too_many_arguments)]
761pub(crate) fn create_a_potential_cors_request(
762 webview_id: Option<WebViewId>,
763 url: ServoUrl,
764 destination: Destination,
765 cors_setting: Option<CorsSettings>,
766 same_origin_fallback: Option<bool>,
767 referrer: Referrer,
768 insecure_requests_policy: InsecureRequestsPolicy,
769 has_trustworthy_ancestor_origin: bool,
770 policy_container: PolicyContainer,
771) -> RequestBuilder {
772 RequestBuilder::new(webview_id, url, referrer)
773 .mode(match cors_setting {
776 Some(_) => RequestMode::CorsMode,
777 None if same_origin_fallback == Some(true) => RequestMode::SameOrigin,
778 None => RequestMode::NoCors,
779 })
780 .credentials_mode(match cors_setting {
783 Some(CorsSettings::Anonymous) => CredentialsMode::CredentialsSameOrigin,
784 _ => CredentialsMode::Include,
785 })
786 .destination(destination)
788 .use_url_credentials(true)
789 .insecure_requests_policy(insecure_requests_policy)
790 .has_trustworthy_ancestor_origin(has_trustworthy_ancestor_origin)
791 .policy_container(policy_container)
792}