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