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