1use 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
64fn 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#[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 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#[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 if let ipc::IpcSelectionResult::ChannelClosed(..) = receiver {
259 continue;
260 }
261 let (id, data) = receiver.unwrap();
262 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 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 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 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 pub fn exit(&mut self) {
612 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 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 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 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 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 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}