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, 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
72fn 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#[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 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#[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
189fn 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 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 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 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 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 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 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 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 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 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 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 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 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}