1use std::rc::Rc;
6use std::sync::{Arc, Mutex};
7
8use base::id::WebViewId;
9use ipc_channel::ipc;
10use js::jsval::UndefinedValue;
11use js::rust::HandleValue;
12use net_traits::policy_container::{PolicyContainer, RequestPolicyContainer};
13use net_traits::request::{
14 CorsSettings, CredentialsMode, Destination, InsecureRequestsPolicy, Referrer,
15 Request as NetTraitsRequest, RequestBuilder, RequestId, RequestMode, ServiceWorkersMode,
16};
17use net_traits::{
18 CoreResourceMsg, CoreResourceThread, FetchChannels, FetchMetadata, FetchResponseListener,
19 FetchResponseMsg, FilteredMetadata, Metadata, NetworkError, ResourceFetchTiming,
20 ResourceTimingType, cancel_async_fetch,
21};
22use servo_url::ServoUrl;
23
24use crate::body::BodyMixin;
25use crate::dom::abortsignal::AbortAlgorithm;
26use crate::dom::bindings::codegen::Bindings::AbortSignalBinding::AbortSignalMethods;
27use crate::dom::bindings::codegen::Bindings::RequestBinding::{
28 RequestInfo, RequestInit, RequestMethods,
29};
30use crate::dom::bindings::codegen::Bindings::ResponseBinding::Response_Binding::ResponseMethods;
31use crate::dom::bindings::codegen::Bindings::ResponseBinding::ResponseType as DOMResponseType;
32use crate::dom::bindings::error::Error;
33use crate::dom::bindings::import::module::SafeJSContext;
34use crate::dom::bindings::inheritance::Castable;
35use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
36use crate::dom::bindings::reflector::DomGlobal;
37use crate::dom::bindings::root::DomRoot;
38use crate::dom::bindings::trace::RootedTraceableBox;
39use crate::dom::csp::{GlobalCspReporting, Violation};
40use crate::dom::globalscope::GlobalScope;
41use crate::dom::headers::Guard;
42use crate::dom::performanceresourcetiming::InitiatorType;
43use crate::dom::promise::Promise;
44use crate::dom::request::Request;
45use crate::dom::response::Response;
46use crate::dom::serviceworkerglobalscope::ServiceWorkerGlobalScope;
47use crate::network_listener::{self, PreInvoke, ResourceTimingListener, submit_timing_data};
48use crate::realms::{InRealm, enter_realm};
49use crate::script_runtime::CanGc;
50
51#[derive(Default, JSTraceable, MallocSizeOf)]
58pub(crate) struct FetchCanceller {
59 #[no_trace]
60 request_id: Option<RequestId>,
61 #[no_trace]
62 core_resource_thread: Option<CoreResourceThread>,
63}
64
65impl FetchCanceller {
66 pub(crate) fn new(request_id: RequestId, core_resource_thread: CoreResourceThread) -> Self {
69 Self {
70 request_id: Some(request_id),
71 core_resource_thread: Some(core_resource_thread),
72 }
73 }
74
75 pub(crate) fn cancel(&mut self) {
77 if let Some(request_id) = self.request_id.take() {
78 if let Some(ref core_resource_thread) = self.core_resource_thread {
82 cancel_async_fetch(vec![request_id], core_resource_thread);
85 }
86 }
87 }
88
89 pub(crate) fn ignore(&mut self) {
92 let _ = self.request_id.take();
93 }
94}
95
96impl Drop for FetchCanceller {
97 fn drop(&mut self) {
98 self.cancel()
99 }
100}
101
102fn request_init_from_request(request: NetTraitsRequest) -> RequestBuilder {
103 RequestBuilder {
104 id: request.id,
105 method: request.method.clone(),
106 url: request.url(),
107 headers: request.headers.clone(),
108 unsafe_request: request.unsafe_request,
109 body: request.body.clone(),
110 service_workers_mode: ServiceWorkersMode::All,
111 destination: request.destination,
112 synchronous: request.synchronous,
113 mode: request.mode.clone(),
114 cache_mode: request.cache_mode,
115 use_cors_preflight: request.use_cors_preflight,
116 credentials_mode: request.credentials_mode,
117 use_url_credentials: request.use_url_credentials,
118 origin: GlobalScope::current()
119 .expect("No current global object")
120 .origin()
121 .immutable()
122 .clone(),
123 referrer: request.referrer.clone(),
124 referrer_policy: request.referrer_policy,
125 pipeline_id: request.pipeline_id,
126 target_webview_id: request.target_webview_id,
127 redirect_mode: request.redirect_mode,
128 integrity_metadata: request.integrity_metadata.clone(),
129 cryptographic_nonce_metadata: request.cryptographic_nonce_metadata.clone(),
130 url_list: vec![],
131 parser_metadata: request.parser_metadata,
132 initiator: request.initiator,
133 policy_container: request.policy_container,
134 insecure_requests_policy: request.insecure_requests_policy,
135 has_trustworthy_ancestor_origin: request.has_trustworthy_ancestor_origin,
136 https_state: request.https_state,
137 response_tainting: request.response_tainting,
138 crash: None,
139 }
140}
141
142fn abort_fetch_call(
144 promise: Rc<Promise>,
145 request: &Request,
146 response_object: Option<&Response>,
147 abort_reason: HandleValue,
148 global: &GlobalScope,
149 cx: SafeJSContext,
150 can_gc: CanGc,
151) {
152 promise.reject(cx, abort_reason, can_gc);
154 if let Some(body) = request.body() {
156 if body.is_readable() {
157 body.cancel(cx, global, abort_reason, can_gc);
158 }
159 }
160 let Some(response) = response_object else {
163 return;
164 };
165 if let Some(body) = response.body() {
167 if body.is_readable() {
168 body.error(abort_reason, can_gc);
169 }
170 }
171}
172
173#[allow(non_snake_case)]
175#[cfg_attr(crown, allow(crown::unrooted_must_root))]
176pub(crate) fn Fetch(
177 global: &GlobalScope,
178 input: RequestInfo,
179 init: RootedTraceableBox<RequestInit>,
180 comp: InRealm,
181 can_gc: CanGc,
182) -> Rc<Promise> {
183 let promise = Promise::new_in_current_realm(comp, can_gc);
185 let cx = GlobalScope::get_cx();
186
187 let response = Response::new(global, can_gc);
190 response.Headers(can_gc).set_guard(Guard::Immutable);
191
192 let request_object = match Request::Constructor(global, None, can_gc, input, init) {
195 Err(e) => {
196 response.error_stream(e.clone(), can_gc);
197 promise.reject_error(e, can_gc);
198 return promise;
199 },
200 Ok(r) => r,
201 };
202 let request = request_object.get_request();
204 let timing_type = request.timing_type();
205 let request_id = request.id;
206
207 let signal = request_object.Signal();
209 if signal.aborted() {
210 rooted!(in(*cx) let mut abort_reason = UndefinedValue());
212 signal.Reason(cx, abort_reason.handle_mut());
213 abort_fetch_call(
214 promise.clone(),
215 &request_object,
216 None,
217 abort_reason.handle(),
218 global,
219 cx,
220 can_gc,
221 );
222 return promise;
224 }
225
226 let mut request_init = request_init_from_request(request);
229 request_init.policy_container =
230 RequestPolicyContainer::PolicyContainer(global.policy_container());
231
232 if global.is::<ServiceWorkerGlobalScope>() {
235 request_init.service_workers_mode = ServiceWorkersMode::None;
236 }
237
238 let fetch_context = Arc::new(Mutex::new(FetchContext {
245 fetch_promise: Some(TrustedPromise::new(promise.clone())),
246 response_object: Trusted::new(&*response),
247 request: Trusted::new(&*request_object),
248 global: Trusted::new(global),
249 resource_timing: ResourceFetchTiming::new(timing_type),
250 locally_aborted: false,
251 canceller: FetchCanceller::new(request_id, global.core_resource_thread()),
252 }));
253
254 signal.add(&AbortAlgorithm::Fetch(fetch_context.clone()));
256
257 global.fetch(
260 request_init,
261 fetch_context,
262 global.task_manager().networking_task_source().to_sendable(),
263 );
264
265 promise
267}
268
269#[derive(JSTraceable, MallocSizeOf)]
270pub(crate) struct FetchContext {
271 #[ignore_malloc_size_of = "unclear ownership semantics"]
272 fetch_promise: Option<TrustedPromise>,
273 response_object: Trusted<Response>,
274 request: Trusted<Request>,
275 global: Trusted<GlobalScope>,
276 #[no_trace]
277 resource_timing: ResourceFetchTiming,
278 locally_aborted: bool,
279 canceller: FetchCanceller,
280}
281
282impl PreInvoke for FetchContext {}
283
284impl FetchContext {
285 pub(crate) fn abort_fetch(
287 &mut self,
288 abort_reason: HandleValue,
289 cx: SafeJSContext,
290 can_gc: CanGc,
291 ) {
292 self.locally_aborted = true;
294 self.canceller.cancel();
300
301 let promise = self
304 .fetch_promise
305 .take()
306 .expect("fetch promise is missing")
307 .root();
308 abort_fetch_call(
309 promise,
310 &self.request.root(),
311 Some(&self.response_object.root()),
312 abort_reason,
313 &self.global.root(),
314 cx,
315 can_gc,
316 );
317 }
318}
319
320impl FetchResponseListener for FetchContext {
322 fn process_request_body(&mut self, _: RequestId) {
323 }
325
326 fn process_request_eof(&mut self, _: RequestId) {
327 }
329
330 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
331 fn process_response(
332 &mut self,
333 _: RequestId,
334 fetch_metadata: Result<FetchMetadata, NetworkError>,
335 ) {
336 if self.locally_aborted {
338 return;
339 }
340 let promise = self
341 .fetch_promise
342 .take()
343 .expect("fetch promise is missing")
344 .root();
345
346 let _ac = enter_realm(&*promise);
347 match fetch_metadata {
348 Err(_) => {
351 promise.reject_error(
352 Error::Type("Network error occurred".to_string()),
353 CanGc::note(),
354 );
355 self.fetch_promise = Some(TrustedPromise::new(promise));
356 let response = self.response_object.root();
357 response.set_type(DOMResponseType::Error, CanGc::note());
358 response.error_stream(
359 Error::Type("Network error occurred".to_string()),
360 CanGc::note(),
361 );
362 return;
363 },
364 Ok(metadata) => match metadata {
367 FetchMetadata::Unfiltered(m) => {
368 fill_headers_with_metadata(self.response_object.root(), m, CanGc::note());
369 self.response_object
370 .root()
371 .set_type(DOMResponseType::Default, CanGc::note());
372 },
373 FetchMetadata::Filtered { filtered, .. } => match filtered {
374 FilteredMetadata::Basic(m) => {
375 fill_headers_with_metadata(self.response_object.root(), m, CanGc::note());
376 self.response_object
377 .root()
378 .set_type(DOMResponseType::Basic, CanGc::note());
379 },
380 FilteredMetadata::Cors(m) => {
381 fill_headers_with_metadata(self.response_object.root(), m, CanGc::note());
382 self.response_object
383 .root()
384 .set_type(DOMResponseType::Cors, CanGc::note());
385 },
386 FilteredMetadata::Opaque => {
387 self.response_object
388 .root()
389 .set_type(DOMResponseType::Opaque, CanGc::note());
390 },
391 FilteredMetadata::OpaqueRedirect(url) => {
392 let r = self.response_object.root();
393 r.set_type(DOMResponseType::Opaqueredirect, CanGc::note());
394 r.set_final_url(url);
395 },
396 },
397 },
398 }
399
400 promise.resolve_native(&self.response_object.root(), CanGc::note());
402 self.fetch_promise = Some(TrustedPromise::new(promise));
403 }
404
405 fn process_response_chunk(&mut self, _: RequestId, chunk: Vec<u8>) {
406 let response = self.response_object.root();
407 response.stream_chunk(chunk, CanGc::note());
408 }
409
410 fn process_response_eof(
411 &mut self,
412 _: RequestId,
413 _response: Result<ResourceFetchTiming, NetworkError>,
414 ) {
415 let response = self.response_object.root();
416 let _ac = enter_realm(&*response);
417 response.finish(CanGc::note());
418 }
421
422 fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming {
423 &mut self.resource_timing
424 }
425
426 fn resource_timing(&self) -> &ResourceFetchTiming {
427 &self.resource_timing
428 }
429
430 fn submit_resource_timing(&mut self) {
431 if self.resource_timing.timing_type == ResourceTimingType::Resource {
433 network_listener::submit_timing(self, CanGc::note())
434 }
435 }
436
437 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
438 let global = &self.resource_timing_global();
439 global.report_csp_violations(violations, None, None);
440 }
441}
442
443impl ResourceTimingListener for FetchContext {
444 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
445 (
446 InitiatorType::Fetch,
447 self.resource_timing_global().get_url().clone(),
448 )
449 }
450
451 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
452 self.response_object.root().global()
453 }
454}
455
456fn fill_headers_with_metadata(r: DomRoot<Response>, m: Metadata, can_gc: CanGc) {
457 r.set_headers(m.headers, can_gc);
458 r.set_status(&m.status);
459 r.set_final_url(m.final_url);
460 r.set_redirected(m.redirected);
461}
462
463pub(crate) trait CspViolationsProcessor {
464 fn process_csp_violations(&self, violations: Vec<Violation>);
465}
466
467pub(crate) fn load_whole_resource(
469 request: RequestBuilder,
470 core_resource_thread: &CoreResourceThread,
471 global: &GlobalScope,
472 csp_violations_processor: &dyn CspViolationsProcessor,
473 can_gc: CanGc,
474) -> Result<(Metadata, Vec<u8>), NetworkError> {
475 let request = request.https_state(global.get_https_state());
476 let (action_sender, action_receiver) = ipc::channel().unwrap();
477 let url = request.url.clone();
478 core_resource_thread
479 .send(CoreResourceMsg::Fetch(
480 request,
481 FetchChannels::ResponseMsg(action_sender),
482 ))
483 .unwrap();
484
485 let mut buf = vec![];
486 let mut metadata = None;
487 loop {
488 match action_receiver.recv().unwrap() {
489 FetchResponseMsg::ProcessRequestBody(..) | FetchResponseMsg::ProcessRequestEOF(..) => {
490 },
491 FetchResponseMsg::ProcessResponse(_, Ok(m)) => {
492 metadata = Some(match m {
493 FetchMetadata::Unfiltered(m) => m,
494 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
495 })
496 },
497 FetchResponseMsg::ProcessResponseChunk(_, data) => buf.extend_from_slice(&data),
498 FetchResponseMsg::ProcessResponseEOF(_, Ok(_)) => {
499 let metadata = metadata.unwrap();
500 if let Some(timing) = &metadata.timing {
501 submit_timing_data(global, url, InitiatorType::Other, timing, can_gc);
502 }
503 return Ok((metadata, buf));
504 },
505 FetchResponseMsg::ProcessResponse(_, Err(e)) |
506 FetchResponseMsg::ProcessResponseEOF(_, Err(e)) => return Err(e),
507 FetchResponseMsg::ProcessCspViolations(_, violations) => {
508 csp_violations_processor.process_csp_violations(violations);
509 },
510 }
511 }
512}
513
514#[allow(clippy::too_many_arguments)]
516pub(crate) fn create_a_potential_cors_request(
517 webview_id: Option<WebViewId>,
518 url: ServoUrl,
519 destination: Destination,
520 cors_setting: Option<CorsSettings>,
521 same_origin_fallback: Option<bool>,
522 referrer: Referrer,
523 insecure_requests_policy: InsecureRequestsPolicy,
524 has_trustworthy_ancestor_origin: bool,
525 policy_container: PolicyContainer,
526) -> RequestBuilder {
527 RequestBuilder::new(webview_id, url, referrer)
528 .mode(match cors_setting {
531 Some(_) => RequestMode::CorsMode,
532 None if same_origin_fallback == Some(true) => RequestMode::SameOrigin,
533 None => RequestMode::NoCors,
534 })
535 .credentials_mode(match cors_setting {
538 Some(CorsSettings::Anonymous) => CredentialsMode::CredentialsSameOrigin,
539 _ => CredentialsMode::Include,
540 })
541 .destination(destination)
543 .use_url_credentials(true)
544 .insecure_requests_policy(insecure_requests_policy)
545 .has_trustworthy_ancestor_origin(has_trustworthy_ancestor_origin)
546 .policy_container(policy_container)
547}