net/
resource_thread.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! A thread that takes a URL and streams back the binary data.
6
7use std::borrow::ToOwned;
8use std::collections::HashMap;
9use std::fs::File;
10use std::io::{self, BufReader};
11use std::path::{Path, PathBuf};
12use std::sync::{Arc, Weak};
13use std::thread;
14
15use base::generic_channel::{self, GenericReceiver, GenericReceiverSet, GenericSelectionResult};
16use base::id::CookieStoreId;
17use cookie::Cookie;
18use crossbeam_channel::Sender;
19use devtools_traits::DevtoolsControlMsg;
20use embedder_traits::GenericEmbedderProxy;
21use hyper_serde::Serde;
22use ipc_channel::ipc::{IpcReceiver, IpcSender};
23use log::{debug, trace, warn};
24use net_traits::blob_url_store::parse_blob_url;
25use net_traits::filemanager_thread::FileTokenCheck;
26use net_traits::pub_domains::public_suffix_list_size_of;
27use net_traits::request::{Destination, PreloadEntry, PreloadId, RequestBuilder, RequestId};
28use net_traits::response::{Response, ResponseInit};
29use net_traits::{
30    AsyncRuntime, CookieAsyncResponse, CookieData, CookieSource, CoreResourceMsg,
31    CoreResourceThread, CustomResponseMediator, DiscardFetch, FetchChannels, FetchTaskTarget,
32    ResourceFetchTiming, ResourceThreads, ResourceTimingType, WebSocketDomAction,
33    WebSocketNetworkEvent,
34};
35use parking_lot::{Mutex, RwLock};
36use profile_traits::mem::{
37    ProcessReports, ProfilerChan as MemProfilerChan, Report, ReportKind, ReportsChan,
38    perform_memory_report,
39};
40use profile_traits::path;
41use profile_traits::time::ProfilerChan;
42use rustc_hash::FxHashMap;
43use rustls_pki_types::CertificateDer;
44use rustls_pki_types::pem::PemObject;
45use serde::{Deserialize, Serialize};
46use servo_arc::Arc as ServoArc;
47use servo_url::{ImmutableOrigin, ServoUrl};
48use tokio::sync::Mutex as TokioMutex;
49
50use crate::async_runtime::{init_async_runtime, spawn_task};
51use crate::connector::{
52    CACertificates, CertificateErrorOverrideManager, create_http_client, create_tls_config,
53};
54use crate::cookie::ServoCookie;
55use crate::cookie_storage::CookieStorage;
56use crate::embedder::NetToEmbedderMsg;
57use crate::fetch::cors_cache::CorsCache;
58use crate::fetch::fetch_params::{FetchParams, SharedPreloadedResources};
59use crate::fetch::methods::{
60    CancellationListener, FetchContext, SharedInflightKeepAliveRecords, WebSocketChannel, fetch,
61};
62use crate::filemanager_thread::FileManager;
63use crate::hsts::{self, HstsList};
64use crate::http_cache::HttpCache;
65use crate::http_loader::{HttpState, http_redirect_fetch};
66use crate::protocols::ProtocolRegistry;
67use crate::request_interceptor::RequestInterceptor;
68use crate::websocket_loader::create_handshake_request;
69
70/// Load a file with CA certificate and produce a RootCertStore with the results.
71fn load_root_cert_store_from_file(file_path: String) -> io::Result<Vec<CertificateDer<'static>>> {
72    let mut pem = BufReader::new(File::open(file_path)?);
73
74    let certs = CertificateDer::pem_reader_iter(&mut pem)
75        .filter_map(|cert| {
76            cert.inspect_err(|e| log::error!("Could not load certificate ({e}). Ignoring it."))
77                .ok()
78        })
79        .collect();
80    Ok(certs)
81}
82
83/// Returns a tuple of (public, private) senders to the new threads.
84#[expect(clippy::too_many_arguments)]
85pub fn new_resource_threads(
86    devtools_sender: Option<Sender<DevtoolsControlMsg>>,
87    time_profiler_chan: ProfilerChan,
88    mem_profiler_chan: MemProfilerChan,
89    embedder_proxy: GenericEmbedderProxy<NetToEmbedderMsg>,
90    config_dir: Option<PathBuf>,
91    certificate_path: Option<String>,
92    ignore_certificate_errors: bool,
93    protocols: Arc<ProtocolRegistry>,
94) -> (ResourceThreads, ResourceThreads, Box<dyn AsyncRuntime>) {
95    // Initialize the async runtime, and get a handle to it for use in clean shutdown.
96    let async_runtime = init_async_runtime();
97
98    let ca_certificates = certificate_path
99        .and_then(|path| {
100            Some(CACertificates::Override(
101                load_root_cert_store_from_file(path).ok()?,
102            ))
103        })
104        .unwrap_or_default();
105
106    let (public_core, private_core) = new_core_resource_thread(
107        devtools_sender,
108        time_profiler_chan,
109        mem_profiler_chan.clone(),
110        embedder_proxy,
111        config_dir.clone(),
112        ca_certificates,
113        ignore_certificate_errors,
114        protocols,
115    );
116    (
117        ResourceThreads::new(public_core),
118        ResourceThreads::new(private_core),
119        async_runtime,
120    )
121}
122
123/// Create a CoreResourceThread
124#[expect(clippy::too_many_arguments)]
125pub fn new_core_resource_thread(
126    devtools_sender: Option<Sender<DevtoolsControlMsg>>,
127    time_profiler_chan: ProfilerChan,
128    mem_profiler_chan: MemProfilerChan,
129    embedder_proxy: GenericEmbedderProxy<NetToEmbedderMsg>,
130    config_dir: Option<PathBuf>,
131    ca_certificates: CACertificates<'static>,
132    ignore_certificate_errors: bool,
133    protocols: Arc<ProtocolRegistry>,
134) -> (CoreResourceThread, CoreResourceThread) {
135    let (public_setup_chan, public_setup_port) = generic_channel::channel().unwrap();
136    let (private_setup_chan, private_setup_port) = generic_channel::channel().unwrap();
137    let (report_chan, report_port) = generic_channel::channel().unwrap();
138
139    thread::Builder::new()
140        .name("ResourceManager".to_owned())
141        .spawn(move || {
142            let resource_manager = CoreResourceManager::new(
143                devtools_sender,
144                time_profiler_chan,
145                embedder_proxy.clone(),
146                ca_certificates.clone(),
147                ignore_certificate_errors,
148            );
149
150            let mut channel_manager = ResourceChannelManager {
151                resource_manager,
152                config_dir,
153                ca_certificates,
154                ignore_certificate_errors,
155                cancellation_listeners: Default::default(),
156                cookie_listeners: Default::default(),
157            };
158
159            mem_profiler_chan.run_with_memory_reporting(
160                || {
161                    channel_manager.start(
162                        public_setup_port,
163                        private_setup_port,
164                        report_port,
165                        protocols,
166                        embedder_proxy,
167                    )
168                },
169                String::from("network-cache-reporter"),
170                report_chan,
171                CoreResourceMsg::CollectMemoryReport,
172            );
173        })
174        .expect("Thread spawning failed");
175    (public_setup_chan, private_setup_chan)
176}
177
178struct ResourceChannelManager {
179    resource_manager: CoreResourceManager,
180    config_dir: Option<PathBuf>,
181    ca_certificates: CACertificates<'static>,
182    ignore_certificate_errors: bool,
183    cancellation_listeners: FxHashMap<RequestId, Weak<CancellationListener>>,
184    cookie_listeners: FxHashMap<CookieStoreId, IpcSender<CookieAsyncResponse>>,
185}
186
187/// This returns a tuple HttpState and a private HttpState.
188fn create_http_states(
189    config_dir: Option<&Path>,
190    ca_certificates: CACertificates<'static>,
191    ignore_certificate_errors: bool,
192    embedder_proxy: GenericEmbedderProxy<NetToEmbedderMsg>,
193) -> (Arc<HttpState>, Arc<HttpState>) {
194    let mut hsts_list = HstsList::default();
195    let mut auth_cache = AuthCache::default();
196    let mut cookie_jar = CookieStorage::new(150);
197    if let Some(config_dir) = config_dir {
198        base::read_json_from_file(&mut auth_cache, config_dir, "auth_cache.json");
199        base::read_json_from_file(&mut hsts_list, config_dir, "hsts_list.json");
200        base::read_json_from_file(&mut cookie_jar, config_dir, "cookie_jar.json");
201    }
202
203    let override_manager = CertificateErrorOverrideManager::new();
204    let http_state = HttpState {
205        hsts_list: RwLock::new(hsts_list),
206        cookie_jar: RwLock::new(cookie_jar),
207        auth_cache: RwLock::new(auth_cache),
208        history_states: RwLock::new(FxHashMap::default()),
209        http_cache: HttpCache::default(),
210        client: create_http_client(create_tls_config(
211            ca_certificates.clone(),
212            ignore_certificate_errors,
213            override_manager.clone(),
214        )),
215        override_manager,
216        embedder_proxy: embedder_proxy.clone(),
217    };
218
219    let override_manager = CertificateErrorOverrideManager::new();
220    let private_http_state = HttpState {
221        hsts_list: RwLock::new(HstsList::default()),
222        cookie_jar: RwLock::new(CookieStorage::new(150)),
223        auth_cache: RwLock::new(AuthCache::default()),
224        history_states: RwLock::new(FxHashMap::default()),
225        http_cache: HttpCache::default(),
226        client: create_http_client(create_tls_config(
227            ca_certificates,
228            ignore_certificate_errors,
229            override_manager.clone(),
230        )),
231        override_manager,
232        embedder_proxy,
233    };
234
235    (Arc::new(http_state), Arc::new(private_http_state))
236}
237
238impl ResourceChannelManager {
239    fn start(
240        &mut self,
241        public_receiver: GenericReceiver<CoreResourceMsg>,
242        private_receiver: GenericReceiver<CoreResourceMsg>,
243        memory_reporter: GenericReceiver<CoreResourceMsg>,
244        protocols: Arc<ProtocolRegistry>,
245        embedder_proxy: GenericEmbedderProxy<NetToEmbedderMsg>,
246    ) {
247        let (public_http_state, private_http_state) = create_http_states(
248            self.config_dir.as_deref(),
249            self.ca_certificates.clone(),
250            self.ignore_certificate_errors,
251            embedder_proxy,
252        );
253
254        let mut rx_set = GenericReceiverSet::new();
255        let private_id = rx_set.add(private_receiver);
256        let public_id = rx_set.add(public_receiver);
257        let reporter_id = rx_set.add(memory_reporter);
258
259        loop {
260            for received in rx_set.select().into_iter() {
261                // Handles case where profiler thread shuts down before resource thread.
262                match received {
263                    GenericSelectionResult::ChannelClosed(_) => continue,
264                    GenericSelectionResult::Error(error) => {
265                        log::error!("Found selection error: {error}")
266                    },
267                    GenericSelectionResult::MessageReceived(id, msg) => {
268                        if id == reporter_id {
269                            if let CoreResourceMsg::CollectMemoryReport(report_chan) = msg {
270                                self.process_report(
271                                    report_chan,
272                                    &public_http_state,
273                                    &private_http_state,
274                                );
275                                continue;
276                            } else {
277                                log::error!("memory reporter should only send CollectMemoryReport");
278                            }
279                        } else {
280                            let group = if id == private_id {
281                                &private_http_state
282                            } else {
283                                assert_eq!(id, public_id);
284                                &public_http_state
285                            };
286                            if !self.process_msg(msg, group, Arc::clone(&protocols)) {
287                                return;
288                            }
289                        }
290                    },
291                }
292            }
293        }
294    }
295
296    fn process_report(
297        &mut self,
298        msg: ReportsChan,
299        public_http_state: &Arc<HttpState>,
300        private_http_state: &Arc<HttpState>,
301    ) {
302        perform_memory_report(|ops| {
303            let mut reports = public_http_state.memory_reports("public", ops);
304            reports.extend(private_http_state.memory_reports("private", ops));
305            reports.extend(vec![
306                Report {
307                    path: path!["hsts-preload-list"],
308                    kind: ReportKind::ExplicitJemallocHeapSize,
309                    size: hsts::hsts_preload_size_of(ops),
310                },
311                Report {
312                    path: path!["public-suffix-list"],
313                    kind: ReportKind::ExplicitJemallocHeapSize,
314                    size: public_suffix_list_size_of(ops),
315                },
316            ]);
317            msg.send(ProcessReports::new(reports));
318        })
319    }
320
321    fn cancellation_listener(&self, request_id: RequestId) -> Option<Arc<CancellationListener>> {
322        self.cancellation_listeners
323            .get(&request_id)
324            .and_then(Weak::upgrade)
325    }
326
327    fn get_or_create_cancellation_listener(
328        &mut self,
329        request_id: RequestId,
330    ) -> Arc<CancellationListener> {
331        if let Some(listener) = self.cancellation_listener(request_id) {
332            return listener;
333        }
334
335        // Clear away any cancellation listeners that are no longer valid.
336        self.cancellation_listeners
337            .retain(|_, listener| listener.strong_count() > 0);
338
339        let cancellation_listener = Arc::new(Default::default());
340        self.cancellation_listeners
341            .insert(request_id, Arc::downgrade(&cancellation_listener));
342        cancellation_listener
343    }
344
345    fn send_cookie_response(&self, store_id: CookieStoreId, data: CookieData) {
346        let Some(sender) = self.cookie_listeners.get(&store_id) else {
347            warn!(
348                "Async cookie request made for store id that is non-existent {:?}",
349                store_id
350            );
351            return;
352        };
353        let res = sender.send(CookieAsyncResponse { data });
354        if res.is_err() {
355            warn!("Unable to send cookie response to script thread");
356        }
357    }
358
359    /// Returns false if the thread should exit.
360    fn process_msg(
361        &mut self,
362        msg: CoreResourceMsg,
363        http_state: &Arc<HttpState>,
364        protocols: Arc<ProtocolRegistry>,
365    ) -> bool {
366        match msg {
367            CoreResourceMsg::Fetch(request_builder, channels) => match channels {
368                FetchChannels::ResponseMsg(sender) => {
369                    let cancellation_listener =
370                        self.get_or_create_cancellation_listener(request_builder.id);
371                    self.resource_manager.fetch(
372                        request_builder,
373                        None,
374                        sender,
375                        http_state,
376                        cancellation_listener,
377                        protocols,
378                    );
379                },
380                FetchChannels::WebSocket {
381                    event_sender,
382                    action_receiver,
383                } => {
384                    let cancellation_listener =
385                        self.get_or_create_cancellation_listener(request_builder.id);
386
387                    self.resource_manager.websocket_connect(
388                        request_builder,
389                        event_sender,
390                        action_receiver,
391                        http_state,
392                        cancellation_listener,
393                        protocols,
394                    )
395                },
396                FetchChannels::Prefetch => self.resource_manager.fetch(
397                    request_builder,
398                    None,
399                    DiscardFetch,
400                    http_state,
401                    Arc::new(Default::default()),
402                    protocols,
403                ),
404            },
405            CoreResourceMsg::Cancel(request_ids) => {
406                for cancellation_listener in request_ids
407                    .into_iter()
408                    .filter_map(|request_id| self.cancellation_listener(request_id))
409                {
410                    cancellation_listener.cancel();
411                }
412            },
413            CoreResourceMsg::DeleteCookiesForSites(sites, sender) => {
414                http_state
415                    .cookie_jar
416                    .write()
417                    .delete_cookies_for_sites(&sites);
418                let _ = sender.send(());
419            },
420            CoreResourceMsg::DeleteCookies(request, sender) => {
421                http_state
422                    .cookie_jar
423                    .write()
424                    .clear_storage(request.as_ref());
425                if let Some(sender) = sender {
426                    let _ = sender.send(());
427                }
428                return true;
429            },
430            CoreResourceMsg::DeleteCookie(request, name) => {
431                http_state
432                    .cookie_jar
433                    .write()
434                    .delete_cookie_with_name(&request, name);
435                return true;
436            },
437            CoreResourceMsg::DeleteCookieAsync(cookie_store_id, url, name) => {
438                http_state
439                    .cookie_jar
440                    .write()
441                    .delete_cookie_with_name(&url, name);
442                self.send_cookie_response(cookie_store_id, CookieData::Delete(Ok(())));
443            },
444            CoreResourceMsg::FetchRedirect(request_builder, res_init, sender) => {
445                let cancellation_listener =
446                    self.get_or_create_cancellation_listener(request_builder.id);
447                self.resource_manager.fetch(
448                    request_builder,
449                    Some(res_init),
450                    sender,
451                    http_state,
452                    cancellation_listener,
453                    protocols,
454                )
455            },
456            CoreResourceMsg::SetCookieForUrl(request, cookie, source) => self
457                .resource_manager
458                .set_cookie_for_url(&request, cookie.into_inner().to_owned(), source, http_state),
459            CoreResourceMsg::SetCookiesForUrl(request, cookies, source) => {
460                for cookie in cookies {
461                    self.resource_manager.set_cookie_for_url(
462                        &request,
463                        cookie.into_inner(),
464                        source,
465                        http_state,
466                    );
467                }
468            },
469            CoreResourceMsg::SetCookieForUrlAsync(cookie_store_id, url, cookie, source) => {
470                self.resource_manager.set_cookie_for_url(
471                    &url,
472                    cookie.into_inner().to_owned(),
473                    source,
474                    http_state,
475                );
476                self.send_cookie_response(cookie_store_id, CookieData::Set(Ok(())));
477            },
478            CoreResourceMsg::GetCookiesForUrl(url, consumer, source) => {
479                let mut cookie_jar = http_state.cookie_jar.write();
480                cookie_jar.remove_expired_cookies_for_url(&url);
481                consumer
482                    .send(cookie_jar.cookies_for_url(&url, source))
483                    .unwrap();
484            },
485            CoreResourceMsg::GetCookieDataForUrlAsync(cookie_store_id, url, name) => {
486                let mut cookie_jar = http_state.cookie_jar.write();
487                cookie_jar.remove_expired_cookies_for_url(&url);
488                let cookie = cookie_jar
489                    .query_cookies(&url, name)
490                    .into_iter()
491                    .map(Serde)
492                    .next();
493                self.send_cookie_response(cookie_store_id, CookieData::Get(cookie));
494            },
495            CoreResourceMsg::GetAllCookieDataForUrlAsync(cookie_store_id, url, name) => {
496                let mut cookie_jar = http_state.cookie_jar.write();
497                cookie_jar.remove_expired_cookies_for_url(&url);
498                let cookies = cookie_jar
499                    .query_cookies(&url, name)
500                    .into_iter()
501                    .map(Serde)
502                    .collect();
503                self.send_cookie_response(cookie_store_id, CookieData::GetAll(cookies));
504            },
505            CoreResourceMsg::NewCookieListener(cookie_store_id, sender, _url) => {
506                // TODO: Use the URL for setting up the actual monitoring
507                self.cookie_listeners.insert(cookie_store_id, sender);
508            },
509            CoreResourceMsg::RemoveCookieListener(cookie_store_id) => {
510                self.cookie_listeners.remove(&cookie_store_id);
511            },
512            CoreResourceMsg::NetworkMediator(mediator_chan, origin) => {
513                self.resource_manager
514                    .sw_managers
515                    .insert(origin, mediator_chan);
516            },
517            CoreResourceMsg::GetCookiesDataForUrl(url, consumer, source) => {
518                let mut cookie_jar = http_state.cookie_jar.write();
519                cookie_jar.remove_expired_cookies_for_url(&url);
520                let cookies = cookie_jar
521                    .cookies_data_for_url(&url, source)
522                    .map(Serde)
523                    .collect();
524                consumer.send(cookies).unwrap();
525            },
526            CoreResourceMsg::ListCookies(sender) => {
527                let mut cookie_jar = http_state.cookie_jar.write();
528                cookie_jar.remove_all_expired_cookies();
529                let _ = sender.send(cookie_jar.cookie_site_descriptors());
530            },
531            CoreResourceMsg::GetHistoryState(history_state_id, consumer) => {
532                let history_states = http_state.history_states.read();
533                consumer
534                    .send(history_states.get(&history_state_id).cloned())
535                    .unwrap();
536            },
537            CoreResourceMsg::SetHistoryState(history_state_id, structured_data) => {
538                let mut history_states = http_state.history_states.write();
539                history_states.insert(history_state_id, structured_data);
540            },
541            CoreResourceMsg::RemoveHistoryStates(states_to_remove) => {
542                let mut history_states = http_state.history_states.write();
543                for history_state in states_to_remove {
544                    history_states.remove(&history_state);
545                }
546            },
547            CoreResourceMsg::GetCacheEntries(sender) => {
548                let _ = sender.send(http_state.http_cache.cache_entry_descriptors());
549            },
550            CoreResourceMsg::ClearCache(sender) => {
551                http_state.http_cache.clear();
552                if let Some(sender) = sender {
553                    let _ = sender.send(());
554                }
555            },
556            CoreResourceMsg::ToFileManager(msg) => self.resource_manager.filemanager.handle(msg),
557            CoreResourceMsg::StorePreloadedResponse(preload_id, response) => self
558                .resource_manager
559                .handle_preloaded_response(preload_id, response),
560            CoreResourceMsg::TotalSizeOfInFlightKeepAliveRecords(pipeline_id, sender) => {
561                let total = self
562                    .resource_manager
563                    .in_flight_keep_alive_records
564                    .lock()
565                    .get(&pipeline_id)
566                    .map(|records| {
567                        records
568                            .iter()
569                            .map(|record| record.keep_alive_body_length)
570                            .sum()
571                    })
572                    .unwrap_or_default();
573                let _ = sender.send(total);
574            },
575            CoreResourceMsg::Exit(sender) => {
576                if let Some(ref config_dir) = self.config_dir {
577                    let auth_cache = http_state.auth_cache.read();
578                    base::write_json_to_file(&*auth_cache, config_dir, "auth_cache.json");
579                    let jar = http_state.cookie_jar.read();
580                    base::write_json_to_file(&*jar, config_dir, "cookie_jar.json");
581                    let hsts = http_state.hsts_list.read();
582                    base::write_json_to_file(&*hsts, config_dir, "hsts_list.json");
583                }
584                self.resource_manager.exit();
585                let _ = sender.send(());
586                return false;
587            },
588            // Ignore this message as we handle it only in the reporter chan
589            CoreResourceMsg::CollectMemoryReport(_) => {},
590        }
591        true
592    }
593}
594
595#[derive(Clone, Debug, Deserialize, Serialize)]
596pub struct AuthCacheEntry {
597    pub user_name: String,
598    pub password: String,
599}
600
601impl Default for AuthCache {
602    fn default() -> Self {
603        Self {
604            version: 1,
605            entries: HashMap::new(),
606        }
607    }
608}
609
610#[derive(Clone, Debug, Deserialize, Serialize)]
611pub struct AuthCache {
612    pub version: u32,
613    pub entries: HashMap<String, AuthCacheEntry>,
614}
615
616pub struct CoreResourceManager {
617    devtools_sender: Option<Sender<DevtoolsControlMsg>>,
618    sw_managers: HashMap<ImmutableOrigin, IpcSender<CustomResponseMediator>>,
619    filemanager: FileManager,
620    request_interceptor: RequestInterceptor,
621    ca_certificates: CACertificates<'static>,
622    ignore_certificate_errors: bool,
623    preloaded_resources: SharedPreloadedResources,
624    /// <https://fetch.spec.whatwg.org/#concept-fetch-record>
625    in_flight_keep_alive_records: SharedInflightKeepAliveRecords,
626}
627
628impl CoreResourceManager {
629    pub fn new(
630        devtools_sender: Option<Sender<DevtoolsControlMsg>>,
631        _profiler_chan: ProfilerChan,
632        embedder_proxy: GenericEmbedderProxy<NetToEmbedderMsg>,
633        ca_certificates: CACertificates<'static>,
634        ignore_certificate_errors: bool,
635    ) -> CoreResourceManager {
636        CoreResourceManager {
637            devtools_sender,
638            sw_managers: Default::default(),
639            filemanager: FileManager::new(embedder_proxy.clone()),
640            request_interceptor: RequestInterceptor::new(embedder_proxy),
641            ca_certificates,
642            ignore_certificate_errors,
643            preloaded_resources: Default::default(),
644            in_flight_keep_alive_records: Default::default(),
645        }
646    }
647
648    fn handle_preloaded_response(&self, preload_id: PreloadId, response: Response) {
649        let mut preloaded_resources = self.preloaded_resources.lock().unwrap();
650        if let Some(entry) = preloaded_resources.get_mut(&preload_id) {
651            entry.with_response(response);
652        }
653    }
654
655    /// Exit the core resource manager.
656    pub fn exit(&mut self) {
657        debug!("Exited CoreResourceManager");
658    }
659
660    fn set_cookie_for_url(
661        &mut self,
662        request: &ServoUrl,
663        cookie: Cookie<'static>,
664        source: CookieSource,
665        http_state: &Arc<HttpState>,
666    ) {
667        if let Some(cookie) = ServoCookie::new_wrapped(cookie, request, source) {
668            let mut cookie_jar = http_state.cookie_jar.write();
669            cookie_jar.push(cookie, request, source)
670        }
671    }
672
673    fn fetch<Target: 'static + FetchTaskTarget + Send>(
674        &self,
675        request_builder: RequestBuilder,
676        res_init_: Option<ResponseInit>,
677        mut sender: Target,
678        http_state: &Arc<HttpState>,
679        cancellation_listener: Arc<CancellationListener>,
680        protocols: Arc<ProtocolRegistry>,
681    ) {
682        let http_state = http_state.clone();
683        let devtools_chan = self.devtools_sender.clone();
684        let filemanager = self.filemanager.clone();
685        let request_interceptor = self.request_interceptor.clone();
686
687        let timing_type = match request_builder.destination {
688            Destination::Document => ResourceTimingType::Navigation,
689            _ => ResourceTimingType::Resource,
690        };
691
692        let request = request_builder.build();
693        let url = request.current_url();
694
695        // In the case of a valid blob URL, acquiring a token granting access to a file,
696        // regardless if the URL is revoked after token acquisition.
697        //
698        // TODO: to make more tests pass, acquire this token earlier,
699        // probably in a separate message flow.
700        //
701        // In such a setup, the token would not be acquired here,
702        // but could instead be contained in the actual CoreResourceMsg::Fetch message.
703        //
704        // See https://github.com/servo/servo/issues/25226
705        let (file_token, blob_url_file_id) = match url.scheme() {
706            "blob" => {
707                if let Ok((id, _)) = parse_blob_url(&url) {
708                    (self.filemanager.get_token_for_file(&id), Some(id))
709                } else {
710                    (FileTokenCheck::ShouldFail, None)
711                }
712            },
713            _ => (FileTokenCheck::NotRequired, None),
714        };
715
716        let ca_certificates = self.ca_certificates.clone();
717        let ignore_certificate_errors = self.ignore_certificate_errors;
718        let in_flight_keep_alive_records = self.in_flight_keep_alive_records.clone();
719        let preloaded_resources = self.preloaded_resources.clone();
720        if let Some(ref preload_id) = request.preload_id {
721            let mut preloaded_resources = self.preloaded_resources.lock().unwrap();
722            let entry = PreloadEntry::new(request.integrity_metadata.clone());
723            preloaded_resources.insert(preload_id.clone(), entry);
724        }
725
726        spawn_task(async move {
727            // XXXManishearth: Check origin against pipeline id (also ensure that the mode is allowed)
728            // todo load context / mimesniff in fetch
729            // todo referrer policy?
730            // todo service worker stuff
731            let context = FetchContext {
732                state: http_state,
733                user_agent: servo_config::pref!(user_agent),
734                devtools_chan,
735                filemanager,
736                file_token,
737                request_interceptor: Arc::new(TokioMutex::new(request_interceptor)),
738                cancellation_listener,
739                timing: ServoArc::new(Mutex::new(ResourceFetchTiming::new(request.timing_type()))),
740                protocols,
741                websocket_chan: None,
742                ca_certificates,
743                ignore_certificate_errors,
744                preloaded_resources,
745                in_flight_keep_alive_records,
746            };
747
748            match res_init_ {
749                Some(res_init) => {
750                    let response = Response::from_init(res_init, timing_type);
751
752                    let mut fetch_params = FetchParams::new(request);
753                    http_redirect_fetch(
754                        &mut fetch_params,
755                        &mut CorsCache::default(),
756                        response,
757                        true,
758                        &mut sender,
759                        &mut None,
760                        &context,
761                    )
762                    .await;
763                },
764                None => {
765                    fetch(request, &mut sender, &context).await;
766                },
767            };
768
769            // Remove token after fetch.
770            if let Some(id) = blob_url_file_id.as_ref() {
771                context
772                    .filemanager
773                    .invalidate_token(&context.file_token, id);
774            }
775        });
776    }
777
778    /// <https://websockets.spec.whatwg.org/#concept-websocket-establish>
779    fn websocket_connect(
780        &self,
781        mut request: RequestBuilder,
782        event_sender: IpcSender<WebSocketNetworkEvent>,
783        action_receiver: IpcReceiver<WebSocketDomAction>,
784        http_state: &Arc<HttpState>,
785        cancellation_listener: Arc<CancellationListener>,
786        protocols: Arc<ProtocolRegistry>,
787    ) {
788        let http_state = http_state.clone();
789        let devtools_chan = self.devtools_sender.clone();
790        let filemanager = self.filemanager.clone();
791        let request_interceptor = self.request_interceptor.clone();
792
793        let ca_certificates = self.ca_certificates.clone();
794        let ignore_certificate_errors = self.ignore_certificate_errors;
795        let in_flight_keep_alive_records = self.in_flight_keep_alive_records.clone();
796        let preloaded_resources = self.preloaded_resources.clone();
797
798        spawn_task(async move {
799            let mut event_sender = event_sender;
800
801            // Let requestURL be a copy of url, with its scheme set to "http", if url’s scheme is
802            // "ws"; otherwise to "https"
803            let scheme = match request.url.scheme() {
804                "ws" => "http",
805                _ => "https",
806            };
807            request
808                .url
809                .as_mut_url()
810                .set_scheme(scheme)
811                .unwrap_or_else(|_| panic!("Can't set scheme to {scheme}"));
812
813            match create_handshake_request(request, http_state.clone()) {
814                Ok(request) => {
815                    let context = FetchContext {
816                        state: http_state,
817                        user_agent: servo_config::pref!(user_agent),
818                        devtools_chan,
819                        filemanager,
820                        file_token: FileTokenCheck::NotRequired,
821                        request_interceptor: Arc::new(TokioMutex::new(request_interceptor)),
822                        cancellation_listener,
823                        timing: ServoArc::new(Mutex::new(ResourceFetchTiming::new(
824                            request.timing_type(),
825                        ))),
826                        protocols: protocols.clone(),
827                        websocket_chan: Some(Arc::new(Mutex::new(WebSocketChannel::new(
828                            event_sender.clone(),
829                            Some(action_receiver),
830                        )))),
831                        ca_certificates,
832                        ignore_certificate_errors,
833                        preloaded_resources,
834                        in_flight_keep_alive_records,
835                    };
836                    fetch(request, &mut event_sender, &context).await;
837                },
838                Err(e) => {
839                    trace!("unable to create websocket handshake request {:?}", e);
840                    let _ = event_sender.send(WebSocketNetworkEvent::Fail);
841                },
842            }
843        });
844    }
845}