Skip to main content

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