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