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