1use std::rc::Rc;
6use std::sync::{Arc, Mutex};
7
8use base::id::WebViewId;
9use ipc_channel::ipc;
10use net_traits::policy_container::{PolicyContainer, RequestPolicyContainer};
11use net_traits::request::{
12 CorsSettings, CredentialsMode, Destination, InsecureRequestsPolicy, Referrer,
13 Request as NetTraitsRequest, RequestBuilder, RequestId, RequestMode, ServiceWorkersMode,
14};
15use net_traits::{
16 CoreResourceMsg, CoreResourceThread, FetchChannels, FetchMetadata, FetchResponseListener,
17 FetchResponseMsg, FilteredMetadata, Metadata, NetworkError, ResourceFetchTiming,
18 ResourceTimingType, cancel_async_fetch,
19};
20use servo_url::ServoUrl;
21
22use crate::dom::bindings::codegen::Bindings::RequestBinding::{
23 RequestInfo, RequestInit, RequestMethods,
24};
25use crate::dom::bindings::codegen::Bindings::ResponseBinding::Response_Binding::ResponseMethods;
26use crate::dom::bindings::codegen::Bindings::ResponseBinding::ResponseType as DOMResponseType;
27use crate::dom::bindings::error::Error;
28use crate::dom::bindings::inheritance::Castable;
29use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
30use crate::dom::bindings::reflector::DomGlobal;
31use crate::dom::bindings::root::DomRoot;
32use crate::dom::bindings::trace::RootedTraceableBox;
33use crate::dom::csp::{GlobalCspReporting, Violation};
34use crate::dom::globalscope::GlobalScope;
35use crate::dom::headers::Guard;
36use crate::dom::performanceresourcetiming::InitiatorType;
37use crate::dom::promise::Promise;
38use crate::dom::request::Request;
39use crate::dom::response::Response;
40use crate::dom::serviceworkerglobalscope::ServiceWorkerGlobalScope;
41use crate::network_listener::{self, PreInvoke, ResourceTimingListener, submit_timing_data};
42use crate::realms::{InRealm, enter_realm};
43use crate::script_runtime::CanGc;
44
45struct FetchContext {
46 fetch_promise: Option<TrustedPromise>,
47 response_object: Trusted<Response>,
48 resource_timing: ResourceFetchTiming,
49}
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
142#[allow(non_snake_case)]
144#[cfg_attr(crown, allow(crown::unrooted_must_root))]
145pub(crate) fn Fetch(
146 global: &GlobalScope,
147 input: RequestInfo,
148 init: RootedTraceableBox<RequestInit>,
149 comp: InRealm,
150 can_gc: CanGc,
151) -> Rc<Promise> {
152 let promise = Promise::new_in_current_realm(comp, can_gc);
154
155 let response = Response::new(global, can_gc);
158 response.Headers(can_gc).set_guard(Guard::Immutable);
159
160 let request = match Request::Constructor(global, None, can_gc, input, init) {
163 Err(e) => {
164 response.error_stream(e.clone(), can_gc);
165 promise.reject_error(e, can_gc);
166 return promise;
167 },
168 Ok(r) => {
169 r.get_request()
171 },
172 };
173 let timing_type = request.timing_type();
174
175 let mut request_init = request_init_from_request(request);
176 request_init.policy_container =
177 RequestPolicyContainer::PolicyContainer(global.policy_container());
178
179 if global.is::<ServiceWorkerGlobalScope>() {
187 request_init.service_workers_mode = ServiceWorkersMode::None;
188 }
189
190 let fetch_context = Arc::new(Mutex::new(FetchContext {
195 fetch_promise: Some(TrustedPromise::new(promise.clone())),
196 response_object: Trusted::new(&*response),
197 resource_timing: ResourceFetchTiming::new(timing_type),
198 }));
199
200 global.fetch(
201 request_init,
202 fetch_context,
203 global.task_manager().networking_task_source().to_sendable(),
204 );
205
206 promise
208}
209
210impl PreInvoke for FetchContext {}
211
212impl FetchResponseListener for FetchContext {
213 fn process_request_body(&mut self, _: RequestId) {
214 }
216
217 fn process_request_eof(&mut self, _: RequestId) {
218 }
220
221 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
222 fn process_response(
223 &mut self,
224 _: RequestId,
225 fetch_metadata: Result<FetchMetadata, NetworkError>,
226 ) {
227 let promise = self
228 .fetch_promise
229 .take()
230 .expect("fetch promise is missing")
231 .root();
232
233 let _ac = enter_realm(&*promise);
234 match fetch_metadata {
235 Err(_) => {
237 promise.reject_error(
238 Error::Type("Network error occurred".to_string()),
239 CanGc::note(),
240 );
241 self.fetch_promise = Some(TrustedPromise::new(promise));
242 let response = self.response_object.root();
243 response.set_type(DOMResponseType::Error, CanGc::note());
244 response.error_stream(
245 Error::Type("Network error occurred".to_string()),
246 CanGc::note(),
247 );
248 return;
249 },
250 Ok(metadata) => match metadata {
252 FetchMetadata::Unfiltered(m) => {
253 fill_headers_with_metadata(self.response_object.root(), m, CanGc::note());
254 self.response_object
255 .root()
256 .set_type(DOMResponseType::Default, CanGc::note());
257 },
258 FetchMetadata::Filtered { filtered, .. } => match filtered {
259 FilteredMetadata::Basic(m) => {
260 fill_headers_with_metadata(self.response_object.root(), m, CanGc::note());
261 self.response_object
262 .root()
263 .set_type(DOMResponseType::Basic, CanGc::note());
264 },
265 FilteredMetadata::Cors(m) => {
266 fill_headers_with_metadata(self.response_object.root(), m, CanGc::note());
267 self.response_object
268 .root()
269 .set_type(DOMResponseType::Cors, CanGc::note());
270 },
271 FilteredMetadata::Opaque => {
272 self.response_object
273 .root()
274 .set_type(DOMResponseType::Opaque, CanGc::note());
275 },
276 FilteredMetadata::OpaqueRedirect(url) => {
277 let r = self.response_object.root();
278 r.set_type(DOMResponseType::Opaqueredirect, CanGc::note());
279 r.set_final_url(url);
280 },
281 },
282 },
283 }
284
285 promise.resolve_native(&self.response_object.root(), CanGc::note());
287 self.fetch_promise = Some(TrustedPromise::new(promise));
288 }
289
290 fn process_response_chunk(&mut self, _: RequestId, chunk: Vec<u8>) {
291 let response = self.response_object.root();
292 response.stream_chunk(chunk, CanGc::note());
293 }
294
295 fn process_response_eof(
296 &mut self,
297 _: RequestId,
298 _response: Result<ResourceFetchTiming, NetworkError>,
299 ) {
300 let response = self.response_object.root();
301 let _ac = enter_realm(&*response);
302 response.finish(CanGc::note());
303 }
306
307 fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming {
308 &mut self.resource_timing
309 }
310
311 fn resource_timing(&self) -> &ResourceFetchTiming {
312 &self.resource_timing
313 }
314
315 fn submit_resource_timing(&mut self) {
316 if self.resource_timing.timing_type == ResourceTimingType::Resource {
318 network_listener::submit_timing(self, CanGc::note())
319 }
320 }
321
322 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
323 let global = &self.resource_timing_global();
324 global.report_csp_violations(violations, None, None);
325 }
326}
327
328impl ResourceTimingListener for FetchContext {
329 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
330 (
331 InitiatorType::Fetch,
332 self.resource_timing_global().get_url().clone(),
333 )
334 }
335
336 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
337 self.response_object.root().global()
338 }
339}
340
341fn fill_headers_with_metadata(r: DomRoot<Response>, m: Metadata, can_gc: CanGc) {
342 r.set_headers(m.headers, can_gc);
343 r.set_status(&m.status);
344 r.set_final_url(m.final_url);
345 r.set_redirected(m.redirected);
346}
347
348pub(crate) trait CspViolationsProcessor {
349 fn process_csp_violations(&self, violations: Vec<Violation>);
350}
351
352pub(crate) fn load_whole_resource(
354 request: RequestBuilder,
355 core_resource_thread: &CoreResourceThread,
356 global: &GlobalScope,
357 csp_violations_processor: &dyn CspViolationsProcessor,
358 can_gc: CanGc,
359) -> Result<(Metadata, Vec<u8>), NetworkError> {
360 let request = request.https_state(global.get_https_state());
361 let (action_sender, action_receiver) = ipc::channel().unwrap();
362 let url = request.url.clone();
363 core_resource_thread
364 .send(CoreResourceMsg::Fetch(
365 request,
366 FetchChannels::ResponseMsg(action_sender),
367 ))
368 .unwrap();
369
370 let mut buf = vec![];
371 let mut metadata = None;
372 loop {
373 match action_receiver.recv().unwrap() {
374 FetchResponseMsg::ProcessRequestBody(..) | FetchResponseMsg::ProcessRequestEOF(..) => {
375 },
376 FetchResponseMsg::ProcessResponse(_, Ok(m)) => {
377 metadata = Some(match m {
378 FetchMetadata::Unfiltered(m) => m,
379 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
380 })
381 },
382 FetchResponseMsg::ProcessResponseChunk(_, data) => buf.extend_from_slice(&data),
383 FetchResponseMsg::ProcessResponseEOF(_, Ok(_)) => {
384 let metadata = metadata.unwrap();
385 if let Some(timing) = &metadata.timing {
386 submit_timing_data(global, url, InitiatorType::Other, timing, can_gc);
387 }
388 return Ok((metadata, buf));
389 },
390 FetchResponseMsg::ProcessResponse(_, Err(e)) |
391 FetchResponseMsg::ProcessResponseEOF(_, Err(e)) => return Err(e),
392 FetchResponseMsg::ProcessCspViolations(_, violations) => {
393 csp_violations_processor.process_csp_violations(violations);
394 },
395 }
396 }
397}
398
399#[allow(clippy::too_many_arguments)]
401pub(crate) fn create_a_potential_cors_request(
402 webview_id: Option<WebViewId>,
403 url: ServoUrl,
404 destination: Destination,
405 cors_setting: Option<CorsSettings>,
406 same_origin_fallback: Option<bool>,
407 referrer: Referrer,
408 insecure_requests_policy: InsecureRequestsPolicy,
409 has_trustworthy_ancestor_origin: bool,
410 policy_container: PolicyContainer,
411) -> RequestBuilder {
412 RequestBuilder::new(webview_id, url, referrer)
413 .mode(match cors_setting {
416 Some(_) => RequestMode::CorsMode,
417 None if same_origin_fallback == Some(true) => RequestMode::SameOrigin,
418 None => RequestMode::NoCors,
419 })
420 .credentials_mode(match cors_setting {
423 Some(CorsSettings::Anonymous) => CredentialsMode::CredentialsSameOrigin,
424 _ => CredentialsMode::Include,
425 })
426 .destination(destination)
428 .use_url_credentials(true)
429 .insecure_requests_policy(insecure_requests_policy)
430 .has_trustworthy_ancestor_origin(has_trustworthy_ancestor_origin)
431 .policy_container(policy_container)
432}