1#![deny(unsafe_code)]
6
7use std::collections::hash_map::Entry;
8use std::fmt::{self, Debug, Display};
9use std::sync::{LazyLock, OnceLock};
10use std::thread::{self, JoinHandle};
11
12use bytes::Bytes;
13use content_security_policy::{self as csp};
14use cookie::Cookie;
15use crossbeam_channel::{Receiver, Sender, unbounded};
16use headers::{ContentType, HeaderMapExt, ReferrerPolicy as ReferrerPolicyHeader};
17use http::{HeaderMap, HeaderValue, StatusCode, header};
18use hyper_serde::Serde;
19use hyper_util::client::legacy::Error as HyperError;
20use malloc_size_of::malloc_size_of_is_0;
21use malloc_size_of_derive::MallocSizeOf;
22use mime::Mime;
23use parking_lot::RwLock;
24use profile_traits::generic_callback::GenericCallback as ProfileGenericCallback;
25use profile_traits::mem::ReportsChan;
26use rand::{Rng, rng};
27use request::RequestId;
28use rustc_hash::FxHashMap;
29use rustls::{CipherSuite, NamedGroup, ProtocolVersion};
30use rustls_pki_types::CertificateDer;
31use serde::{Deserialize, Serialize};
32use serde_with::{FromInto, serde_as};
33use servo_base::generic_channel::{
34 self, CallbackSetter, GenericCallback, GenericOneshotSender, GenericSend, GenericSender,
35 SendResult,
36};
37use servo_base::id::{CookieStoreId, HistoryStateId, PipelineId};
38use servo_url::{ImmutableOrigin, ServoUrl};
39use uuid::Uuid;
40
41#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
43pub struct CookieOperationId(pub u64);
44
45use crate::fetch::headers::determine_nosniff;
46use crate::filemanager_thread::FileManagerThreadMsg;
47use crate::http_status::HttpStatus;
48use crate::mime_classifier::{ApacheBugFlag, MimeClassifier};
49use crate::request::{Request, RequestBuilder};
50use crate::response::{Response, ResponseInit};
51
52pub mod blob_url_store;
53pub mod fetch;
54pub mod filemanager_thread;
55pub mod http_status;
56pub mod image_cache;
57pub mod mime_classifier;
58pub mod policy_container;
59pub mod pub_domains;
60pub mod quality;
61pub mod request;
62pub(crate) mod resource_fetch_timing;
63pub mod response;
64pub use resource_fetch_timing::{
65 RedirectEndValue, RedirectStartValue, ResourceAttribute, ResourceFetchTiming,
66 ResourceFetchTimingContainer, ResourceTimeValue, ResourceTimingType,
67};
68
69pub const DOCUMENT_ACCEPT_HEADER_VALUE: HeaderValue =
71 HeaderValue::from_static("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
72
73#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
76pub enum LoadContext {
77 Browsing,
78 Image,
79 AudioVideo,
80 Plugin,
81 Style,
82 Script,
83 Font,
84 TextTrack,
85 CacheManifest,
86}
87
88#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
89pub struct CustomResponse {
90 #[serde(
91 deserialize_with = "::hyper_serde::deserialize",
92 serialize_with = "::hyper_serde::serialize"
93 )]
94 pub headers: HeaderMap,
95 #[serde(
96 deserialize_with = "::hyper_serde::deserialize",
97 serialize_with = "::hyper_serde::serialize"
98 )]
99 pub raw_status: (StatusCode, String),
100 pub body: Vec<u8>,
101}
102
103impl CustomResponse {
104 pub fn new(
105 headers: HeaderMap,
106 raw_status: (StatusCode, String),
107 body: Vec<u8>,
108 ) -> CustomResponse {
109 CustomResponse {
110 headers,
111 raw_status,
112 body,
113 }
114 }
115}
116
117#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
118pub struct CustomResponseMediator {
119 pub response_chan: GenericCallback<Option<CustomResponse>>,
120 pub load_url: ServoUrl,
121}
122
123#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize)]
126pub enum ReferrerPolicy {
127 EmptyString,
129 NoReferrer,
131 NoReferrerWhenDowngrade,
133 Origin,
135 SameOrigin,
137 OriginWhenCrossOrigin,
139 UnsafeUrl,
141 StrictOrigin,
143 #[default]
145 StrictOriginWhenCrossOrigin,
146}
147
148impl ReferrerPolicy {
149 pub fn from_with_legacy(value: &str) -> Self {
151 if value.eq_ignore_ascii_case("never") {
154 ReferrerPolicy::NoReferrer
155 } else if value.eq_ignore_ascii_case("default") {
156 ReferrerPolicy::StrictOriginWhenCrossOrigin
157 } else if value.eq_ignore_ascii_case("always") {
158 ReferrerPolicy::UnsafeUrl
159 } else if value.eq_ignore_ascii_case("origin-when-crossorigin") {
160 ReferrerPolicy::OriginWhenCrossOrigin
161 } else {
162 ReferrerPolicy::from(value)
163 }
164 }
165
166 pub fn parse_header_for_response(headers: &Option<Serde<HeaderMap>>) -> Self {
168 headers
170 .as_ref()
171 .and_then(|headers| headers.typed_get::<ReferrerPolicyHeader>())
173 .into()
175 }
176}
177
178impl From<&str> for ReferrerPolicy {
179 fn from(value: &str) -> Self {
181 if value.eq_ignore_ascii_case("no-referrer") {
182 ReferrerPolicy::NoReferrer
183 } else if value.eq_ignore_ascii_case("no-referrer-when-downgrade") {
184 ReferrerPolicy::NoReferrerWhenDowngrade
185 } else if value.eq_ignore_ascii_case("origin") {
186 ReferrerPolicy::Origin
187 } else if value.eq_ignore_ascii_case("same-origin") {
188 ReferrerPolicy::SameOrigin
189 } else if value.eq_ignore_ascii_case("strict-origin") {
190 ReferrerPolicy::StrictOrigin
191 } else if value.eq_ignore_ascii_case("strict-origin-when-cross-origin") {
192 ReferrerPolicy::StrictOriginWhenCrossOrigin
193 } else if value.eq_ignore_ascii_case("origin-when-cross-origin") {
194 ReferrerPolicy::OriginWhenCrossOrigin
195 } else if value.eq_ignore_ascii_case("unsafe-url") {
196 ReferrerPolicy::UnsafeUrl
197 } else {
198 ReferrerPolicy::EmptyString
199 }
200 }
201}
202
203impl Display for ReferrerPolicy {
204 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
205 let string = match self {
206 ReferrerPolicy::EmptyString => "",
207 ReferrerPolicy::NoReferrer => "no-referrer",
208 ReferrerPolicy::NoReferrerWhenDowngrade => "no-referrer-when-downgrade",
209 ReferrerPolicy::Origin => "origin",
210 ReferrerPolicy::SameOrigin => "same-origin",
211 ReferrerPolicy::OriginWhenCrossOrigin => "origin-when-cross-origin",
212 ReferrerPolicy::UnsafeUrl => "unsafe-url",
213 ReferrerPolicy::StrictOrigin => "strict-origin",
214 ReferrerPolicy::StrictOriginWhenCrossOrigin => "strict-origin-when-cross-origin",
215 };
216 write!(formatter, "{string}")
217 }
218}
219
220impl From<Option<ReferrerPolicyHeader>> for ReferrerPolicy {
222 fn from(header: Option<ReferrerPolicyHeader>) -> Self {
223 header.map_or(ReferrerPolicy::EmptyString, |policy| match policy {
226 ReferrerPolicyHeader::NO_REFERRER => ReferrerPolicy::NoReferrer,
227 ReferrerPolicyHeader::NO_REFERRER_WHEN_DOWNGRADE => {
228 ReferrerPolicy::NoReferrerWhenDowngrade
229 },
230 ReferrerPolicyHeader::SAME_ORIGIN => ReferrerPolicy::SameOrigin,
231 ReferrerPolicyHeader::ORIGIN => ReferrerPolicy::Origin,
232 ReferrerPolicyHeader::ORIGIN_WHEN_CROSS_ORIGIN => ReferrerPolicy::OriginWhenCrossOrigin,
233 ReferrerPolicyHeader::UNSAFE_URL => ReferrerPolicy::UnsafeUrl,
234 ReferrerPolicyHeader::STRICT_ORIGIN => ReferrerPolicy::StrictOrigin,
235 ReferrerPolicyHeader::STRICT_ORIGIN_WHEN_CROSS_ORIGIN => {
236 ReferrerPolicy::StrictOriginWhenCrossOrigin
237 },
238 })
239 }
240}
241
242impl From<ReferrerPolicy> for ReferrerPolicyHeader {
243 fn from(referrer_policy: ReferrerPolicy) -> Self {
244 match referrer_policy {
245 ReferrerPolicy::NoReferrer => ReferrerPolicyHeader::NO_REFERRER,
246 ReferrerPolicy::NoReferrerWhenDowngrade => {
247 ReferrerPolicyHeader::NO_REFERRER_WHEN_DOWNGRADE
248 },
249 ReferrerPolicy::SameOrigin => ReferrerPolicyHeader::SAME_ORIGIN,
250 ReferrerPolicy::Origin => ReferrerPolicyHeader::ORIGIN,
251 ReferrerPolicy::OriginWhenCrossOrigin => ReferrerPolicyHeader::ORIGIN_WHEN_CROSS_ORIGIN,
252 ReferrerPolicy::UnsafeUrl => ReferrerPolicyHeader::UNSAFE_URL,
253 ReferrerPolicy::StrictOrigin => ReferrerPolicyHeader::STRICT_ORIGIN,
254 ReferrerPolicy::EmptyString | ReferrerPolicy::StrictOriginWhenCrossOrigin => {
255 ReferrerPolicyHeader::STRICT_ORIGIN_WHEN_CROSS_ORIGIN
256 },
257 }
258 }
259}
260
261#[expect(clippy::large_enum_variant)]
263#[derive(Debug, Deserialize, Serialize)]
264pub enum FetchResponseMsg {
265 ProcessRequestBody(RequestId),
267 ProcessResponse(RequestId, Result<FetchMetadata, NetworkError>),
269 ProcessResponseChunk(RequestId, Bytes),
270 ProcessResponseEOF(RequestId, Result<(), NetworkError>, ResourceFetchTiming),
271 ProcessCspViolations(RequestId, Vec<csp::Violation>),
272 ProcessContentLength(RequestId, usize),
273}
274
275impl FetchResponseMsg {
276 pub fn request_id(&self) -> RequestId {
277 match self {
278 FetchResponseMsg::ProcessRequestBody(id) |
279 FetchResponseMsg::ProcessResponse(id, ..) |
280 FetchResponseMsg::ProcessResponseChunk(id, ..) |
281 FetchResponseMsg::ProcessResponseEOF(id, ..) |
282 FetchResponseMsg::ProcessCspViolations(id, ..) |
283 FetchResponseMsg::ProcessContentLength(id, _) => *id,
284 }
285 }
286}
287
288pub trait FetchTaskTarget {
289 fn process_request_body(&mut self, request: &Request);
293
294 fn process_response(&mut self, request: &Request, response: &Response);
298
299 fn process_response_chunk(&mut self, request: &Request, chunk: bytes::Bytes);
301
302 fn process_response_eof(&mut self, request: &Request, response: &Response);
306
307 fn process_csp_violations(&mut self, request: &Request, violations: Vec<csp::Violation>);
308
309 fn process_response_length_hint(&mut self, request_id: &Request, length: usize);
311}
312
313#[derive(Clone, Debug, Deserialize, Serialize)]
314pub enum FilteredMetadata {
315 Basic(Metadata),
316 Cors(Metadata),
317 Opaque,
318 OpaqueRedirect(ServoUrl),
319}
320
321impl From<FetchMetadata> for Metadata {
322 fn from(value: FetchMetadata) -> Self {
323 match value {
324 FetchMetadata::Unfiltered(metadata) => metadata,
325 FetchMetadata::Filtered {
326 filtered: _,
327 unsafe_,
328 } => unsafe_,
329 }
330 }
331}
332
333#[expect(clippy::large_enum_variant)]
335#[derive(Clone, Debug, Deserialize, Serialize)]
336pub enum FetchMetadata {
337 Unfiltered(Metadata),
338 Filtered {
339 filtered: FilteredMetadata,
340 unsafe_: Metadata,
341 },
342}
343
344impl FetchMetadata {
345 pub fn metadata(&self) -> &Metadata {
346 match self {
347 Self::Unfiltered(metadata) => metadata,
348 Self::Filtered { unsafe_, .. } => unsafe_,
349 }
350 }
351
352 pub fn is_cors_cross_origin(&self) -> bool {
354 if let Self::Filtered { filtered, .. } = self {
355 match filtered {
356 FilteredMetadata::Basic(_) | FilteredMetadata::Cors(_) => false,
357 FilteredMetadata::Opaque | FilteredMetadata::OpaqueRedirect(_) => true,
358 }
359 } else {
360 false
361 }
362 }
363}
364
365impl FetchTaskTarget for GenericCallback<FetchResponseMsg> {
366 fn process_request_body(&mut self, request: &Request) {
367 let _ = self.send(FetchResponseMsg::ProcessRequestBody(request.id));
368 }
369
370 fn process_response(&mut self, request: &Request, response: &Response) {
371 let _ = self.send(FetchResponseMsg::ProcessResponse(
372 request.id,
373 response.metadata(),
374 ));
375 }
376
377 fn process_response_chunk(&mut self, request: &Request, chunk: bytes::Bytes) {
378 let _ = self.send(FetchResponseMsg::ProcessResponseChunk(request.id, chunk));
379 }
380
381 fn process_response_eof(&mut self, request: &Request, response: &Response) {
382 let result = response
383 .get_network_error()
384 .map_or_else(|| Ok(()), |network_error| Err(network_error.clone()));
385 let timing = response.get_resource_timing().inner().clone();
386
387 let _ = self.send(FetchResponseMsg::ProcessResponseEOF(
388 request.id, result, timing,
389 ));
390 }
391
392 fn process_csp_violations(&mut self, request: &Request, violations: Vec<csp::Violation>) {
393 let _ = self.send(FetchResponseMsg::ProcessCspViolations(
394 request.id, violations,
395 ));
396 }
397
398 fn process_response_length_hint(&mut self, request: &Request, length: usize) {
399 let _ = self.send(FetchResponseMsg::ProcessContentLength(request.id, length));
400 }
401}
402
403#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize)]
404#[serde(rename_all = "lowercase")]
405pub enum TlsSecurityState {
406 #[default]
408 Insecure,
409 Weak,
411 Broken,
413 Secure,
415}
416
417impl Display for TlsSecurityState {
418 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
419 let text = match self {
420 TlsSecurityState::Insecure => "insecure",
421 TlsSecurityState::Weak => "weak",
422 TlsSecurityState::Broken => "broken",
423 TlsSecurityState::Secure => "secure",
424 };
425 f.write_str(text)
426 }
427}
428
429#[serde_as]
430#[derive(Clone, Serialize, Deserialize, PartialEq)]
431pub struct ServoProtocolVersion(#[serde_as(as = "FromInto<u16>")] pub ProtocolVersion);
432
433impl malloc_size_of::MallocSizeOf for ServoProtocolVersion {
434 fn size_of(&self, _: &mut malloc_size_of::MallocSizeOfOps) -> usize {
435 0
436 }
437}
438
439impl std::fmt::Debug for ServoProtocolVersion {
440 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
441 match self.0 {
442 ProtocolVersion::SSLv2 => write!(f, "SSL 2.0"),
443 ProtocolVersion::SSLv3 => write!(f, "SSL 3.0"),
444 ProtocolVersion::TLSv1_0 => write!(f, "TLS 1.0"),
445 ProtocolVersion::TLSv1_1 => write!(f, "TLS 1.1"),
446 ProtocolVersion::TLSv1_2 => write!(f, "TLS 1.2"),
447 ProtocolVersion::TLSv1_3 => write!(f, "TLS 1.3"),
448 ProtocolVersion::DTLSv1_0 => write!(f, "DTLS 1.0"),
449 ProtocolVersion::DTLSv1_2 => write!(f, "DTLS 1.2"),
450 ProtocolVersion::DTLSv1_3 => write!(f, "DTLS 1.3"),
451 ProtocolVersion::Unknown(value) => write!(f, "Unknown ({value})"),
452 _ => write!(f, "Not yet implemented"),
453 }
454 }
455}
456
457#[serde_as]
458#[derive(Clone, Serialize, Deserialize, PartialEq)]
459pub struct ServoCipherSuite(#[serde_as(as = "FromInto<u16>")] pub CipherSuite);
460
461impl malloc_size_of::MallocSizeOf for ServoCipherSuite {
462 fn size_of(&self, _: &mut malloc_size_of::MallocSizeOfOps) -> usize {
463 0
464 }
465}
466
467impl std::fmt::Debug for ServoCipherSuite {
468 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
469 write!(f, "{:?}", self.0)
470 }
471}
472
473#[serde_as]
474#[derive(Clone, Serialize, Deserialize, PartialEq)]
475pub struct ServoNamedGroup(#[serde_as(as = "FromInto<u16>")] pub NamedGroup);
476
477impl malloc_size_of::MallocSizeOf for ServoNamedGroup {
478 fn size_of(&self, _: &mut malloc_size_of::MallocSizeOfOps) -> usize {
479 0
480 }
481}
482
483impl std::fmt::Debug for ServoNamedGroup {
484 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
485 write!(f, "{:?}", self.0)
486 }
487}
488
489#[derive(Clone, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize)]
490pub struct TlsSecurityInfo {
491 #[serde(default)]
493 pub state: TlsSecurityState,
494 pub weakness_reasons: Vec<String>,
496 pub protocol_version: Option<ServoProtocolVersion>,
498 pub cipher_suite: Option<ServoCipherSuite>,
500 pub kea_group_name: Option<ServoNamedGroup>,
502 pub signature_scheme_name: Option<String>,
504 pub alpn_protocol: Option<String>,
506 pub certificate_chain_der: Vec<Vec<u8>>,
508 pub certificate_transparency: Option<String>,
510 pub hsts: bool,
512 pub hpkp: bool,
514 pub used_ech: bool,
516 pub used_delegated_credentials: bool,
518 pub used_ocsp: bool,
520 pub used_private_dns: bool,
522}
523
524impl FetchTaskTarget for ProfileGenericCallback<WebSocketNetworkEvent> {
525 fn process_request_body(&mut self, _: &Request) {}
526 fn process_response(&mut self, _: &Request, response: &Response) {
527 if response.is_network_error() {
528 let _ = self.send(WebSocketNetworkEvent::Fail);
529 }
530 }
531 fn process_response_chunk(&mut self, _: &Request, _: bytes::Bytes) {}
532 fn process_response_eof(&mut self, _: &Request, _: &Response) {}
533 fn process_csp_violations(&mut self, _: &Request, violations: Vec<csp::Violation>) {
534 let _ = self.send(WebSocketNetworkEvent::ReportCSPViolations(violations));
535 }
536 fn process_response_length_hint(&mut self, _: &Request, _: usize) {}
537}
538
539pub struct DiscardFetch;
543
544impl FetchTaskTarget for DiscardFetch {
545 fn process_request_body(&mut self, _: &Request) {}
546 fn process_response(&mut self, _: &Request, _: &Response) {}
547 fn process_response_chunk(&mut self, _: &Request, _: bytes::Bytes) {}
548 fn process_response_eof(&mut self, _: &Request, _: &Response) {}
549 fn process_csp_violations(&mut self, _: &Request, _: Vec<csp::Violation>) {}
550 fn process_response_length_hint(&mut self, _: &Request, _: usize) {}
551}
552
553pub trait AsyncRuntime: Send {
556 fn shutdown(&mut self);
557}
558
559pub type CoreResourceThread = GenericSender<CoreResourceMsg>;
561
562#[derive(Clone, Debug, Deserialize, Serialize)]
568pub struct ResourceThreads {
569 pub core_thread: CoreResourceThread,
570}
571
572impl ResourceThreads {
573 pub fn new(core_thread: CoreResourceThread) -> ResourceThreads {
574 ResourceThreads { core_thread }
575 }
576
577 pub fn cache_entries(&self) -> Vec<CacheEntryDescriptor> {
578 let (sender, receiver) = generic_channel::channel().unwrap();
579 let _ = self
580 .core_thread
581 .send(CoreResourceMsg::GetCacheEntries(sender));
582 receiver.recv().unwrap()
583 }
584
585 pub fn clear_cache(&self) {
586 let (sender, receiver) = generic_channel::channel().unwrap();
593 let _ = self
594 .core_thread
595 .send(CoreResourceMsg::ClearCache(Some(sender)));
596 let _ = receiver.recv();
597 }
598
599 pub fn cookies(&self) -> Vec<SiteDescriptor> {
600 let (sender, receiver) = generic_channel::channel().unwrap();
601 let _ = self.core_thread.send(CoreResourceMsg::ListCookies(sender));
602 receiver.recv().unwrap()
603 }
604
605 pub fn clear_cookies_for_sites(&self, sites: &[&str]) {
606 let sites = sites.iter().map(|site| site.to_string()).collect();
607 let (sender, receiver) = generic_channel::channel().unwrap();
608 let _ = self
609 .core_thread
610 .send(CoreResourceMsg::DeleteCookiesForSites(sites, sender));
611 let _ = receiver.recv();
612 }
613
614 pub fn clear_cookies(&self) {
615 let (sender, receiver) = generic_channel::channel().unwrap();
616 let _ = self
617 .core_thread
618 .send(CoreResourceMsg::DeleteCookies(None, Some(sender)));
619 let _ = receiver.recv();
620 }
621
622 pub fn cookies_for_url(&self, url: ServoUrl, source: CookieSource) -> Vec<Cookie<'static>> {
623 let (sender, receiver) = generic_channel::channel().unwrap();
624 let _ = self
625 .core_thread
626 .send(CoreResourceMsg::GetCookiesForUrl(url, sender, source));
627 receiver
628 .recv()
629 .unwrap()
630 .into_iter()
631 .map(|cookie| cookie.into_inner())
632 .collect()
633 }
634
635 pub fn clear_session_cookies(&self) {
636 let (sender, receiver) = generic_channel::channel().unwrap();
637 let _ = self
638 .core_thread
639 .send(CoreResourceMsg::DeleteSessionCookies(sender));
640 let _ = receiver.recv();
641 }
642
643 pub fn set_cookie_for_url(&self, url: ServoUrl, cookie: Cookie<'static>, source: CookieSource) {
644 let _ = self.core_thread.send(CoreResourceMsg::SetCookieForUrl(
645 url,
646 Serde(cookie),
647 source,
648 None,
649 ));
650 }
651
652 pub fn set_cookie_for_url_sync(
653 &self,
654 url: ServoUrl,
655 cookie: Cookie<'static>,
656 source: CookieSource,
657 ) {
658 let (sender, receiver) = generic_channel::channel().unwrap();
659 let _ = self.core_thread.send(CoreResourceMsg::SetCookieForUrl(
660 url,
661 Serde(cookie),
662 source,
663 Some(sender),
664 ));
665 let _ = receiver.recv();
666 }
667
668 pub fn cookies_for_url_async(
669 &self,
670 id: CookieOperationId,
671 url: ServoUrl,
672 source: CookieSource,
673 ) {
674 let _ = self
675 .core_thread
676 .send(CoreResourceMsg::EmbedderGetCookiesForUrl(id, url, source));
677 }
678
679 pub fn set_cookie_for_url_async(
680 &self,
681 id: CookieOperationId,
682 url: ServoUrl,
683 cookie: Cookie<'static>,
684 source: CookieSource,
685 ) {
686 let _ = self
687 .core_thread
688 .send(CoreResourceMsg::EmbedderSetCookieForUrl(
689 id,
690 url,
691 Serde(cookie),
692 source,
693 ));
694 }
695
696 pub fn clear_cookies_async(&self, id: CookieOperationId) {
697 let _ = self
698 .core_thread
699 .send(CoreResourceMsg::EmbedderClearCookies(id));
700 }
701
702 pub fn clear_session_cookies_async(&self, id: CookieOperationId) {
703 let _ = self
704 .core_thread
705 .send(CoreResourceMsg::EmbedderClearSessionCookies(id));
706 }
707}
708
709impl GenericSend<CoreResourceMsg> for ResourceThreads {
710 fn send(&self, msg: CoreResourceMsg) -> SendResult {
711 self.core_thread.send(msg)
712 }
713
714 fn sender(&self) -> GenericSender<CoreResourceMsg> {
715 self.core_thread.clone()
716 }
717}
718
719malloc_size_of_is_0!(ResourceThreads);
721
722#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
723pub enum IncludeSubdomains {
724 Included,
725 NotIncluded,
726}
727
728#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
729pub enum MessageData {
730 Text(String),
731 Binary(Vec<u8>),
732}
733
734#[derive(Debug, Deserialize, Serialize, MallocSizeOf)]
735pub enum WebSocketDomAction {
736 SendMessage(MessageData),
737 Close(Option<u16>, Option<String>),
738}
739
740#[derive(Debug, Deserialize, Serialize)]
741pub enum WebSocketNetworkEvent {
742 ReportCSPViolations(Vec<csp::Violation>),
743 ConnectionEstablished { protocol_in_use: Option<String> },
744 MessageReceived(MessageData),
745 Close(Option<u16>, String),
746 Fail,
747}
748
749#[derive(Debug, Deserialize, Serialize)]
750pub enum FetchChannels {
752 ResponseMsg(GenericCallback<FetchResponseMsg>),
753 WebSocket {
754 event_sender: ProfileGenericCallback<WebSocketNetworkEvent>,
755 action_receiver: CallbackSetter<WebSocketDomAction>,
756 },
757 Prefetch,
760}
761
762#[derive(Debug, Deserialize, Serialize)]
763pub enum CoreResourceMsg {
764 Fetch(RequestBuilder, FetchChannels),
765 Cancel(Vec<RequestId>),
766 FetchRedirect(
768 RequestBuilder,
769 ResponseInit,
770 GenericCallback<FetchResponseMsg>,
771 ),
772 SetCookieForUrl(
775 ServoUrl,
776 Serde<Cookie<'static>>,
777 CookieSource,
778 Option<GenericSender<()>>,
779 ),
780 SetCookiesForUrl(ServoUrl, Vec<Serde<Cookie<'static>>>, CookieSource),
782 SetCookieForUrlAsync(
783 CookieStoreId,
784 ServoUrl,
785 Serde<Cookie<'static>>,
786 CookieSource,
787 ),
788 GetCookieStringForUrl(ServoUrl, GenericSender<Option<String>>, CookieSource),
790 GetCookiesForUrl(
793 ServoUrl,
794 GenericSender<Vec<Serde<Cookie<'static>>>>,
795 CookieSource,
796 ),
797 EmbedderGetCookiesForUrl(CookieOperationId, ServoUrl, CookieSource),
800 EmbedderSetCookieForUrl(
803 CookieOperationId,
804 ServoUrl,
805 Serde<Cookie<'static>>,
806 CookieSource,
807 ),
808 EmbedderClearCookies(CookieOperationId),
810 EmbedderClearSessionCookies(CookieOperationId),
812 GetCookieDataForUrlAsync(CookieStoreId, ServoUrl, Option<String>),
813 GetAllCookieDataForUrlAsync(CookieStoreId, ServoUrl, Option<String>),
814 DeleteCookiesForSites(Vec<String>, GenericSender<()>),
815 DeleteCookies(Option<ServoUrl>, Option<GenericSender<()>>),
818 DeleteSessionCookies(GenericSender<()>),
820 DeleteCookie(ServoUrl, String),
821 DeleteCookieAsync(CookieStoreId, ServoUrl, String),
822 NewCookieListener(
823 CookieStoreId,
824 GenericCallback<CookieAsyncResponse>,
825 ServoUrl,
826 ),
827 RemoveCookieListener(CookieStoreId),
828 ListCookies(GenericSender<Vec<SiteDescriptor>>),
829 GetHistoryState(HistoryStateId, GenericSender<Option<Vec<u8>>>),
831 SetHistoryState(HistoryStateId, Vec<u8>),
833 RemoveHistoryStates(Vec<HistoryStateId>),
835 GetCacheEntries(GenericSender<Vec<CacheEntryDescriptor>>),
837 ClearCache(Option<GenericSender<()>>),
839 NetworkMediator(GenericSender<CustomResponseMediator>, ImmutableOrigin),
841 ToFileManager(FileManagerThreadMsg),
843 TotalSizeOfInFlightKeepAliveRecords(PipelineId, GenericSender<u64>),
844 Exit(GenericOneshotSender<()>),
847 CollectMemoryReport(ReportsChan),
848 RevokeTokenForFile(BlobTokenRevocationRequest),
849 RefreshTokenForFile(BlobTokenRefreshRequest),
850}
851
852#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
853pub struct BlobTokenRevocationRequest {
854 pub blob_id: Uuid,
855 pub token: Uuid,
856}
857
858#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
859pub struct BlobTokenRefreshRequest {
860 pub blob_id: Uuid,
861 pub new_token_sender: GenericSender<Uuid>,
862}
863
864#[derive(Clone, Debug, Deserialize, Serialize)]
865pub struct SiteDescriptor {
866 pub name: String,
867}
868
869impl SiteDescriptor {
870 pub fn new(name: String) -> Self {
871 SiteDescriptor { name }
872 }
873}
874
875#[derive(Clone, Debug, Deserialize, Serialize)]
876pub struct CacheEntryDescriptor {
877 pub key: String,
878}
879
880impl CacheEntryDescriptor {
881 pub fn new(key: String) -> Self {
882 Self { key }
883 }
884}
885
886#[expect(clippy::large_enum_variant)]
888enum ToFetchThreadMessage {
889 Cancel {
890 request_ids: Vec<RequestId>,
891 forget: bool,
892 core_resource_thread: CoreResourceThread,
893 },
894 StartFetch(
895 RequestBuilder,
896 Option<ResponseInit>,
897 BoxedFetchCallback,
898 CoreResourceThread,
899 ),
900 FetchResponse(FetchResponseMsg),
901 Exit,
903}
904
905pub type BoxedFetchCallback = Box<dyn FnMut(FetchResponseMsg) + Send + 'static>;
906
907pub struct FetchThread {
911 active_fetches: FxHashMap<RequestId, BoxedFetchCallback>,
914 receiver: Receiver<ToFetchThreadMessage>,
918 to_fetch_sender: GenericCallback<FetchResponseMsg>,
921}
922
923impl FetchThread {
924 fn spawn() -> FetchThreadHandle {
925 let (sender, receiver) = unbounded();
926
927 let sender_clone = sender.clone();
928 let to_fetch_sender = GenericCallback::new(move |message| {
929 let message: FetchResponseMsg = message.unwrap();
930 let _ = sender_clone.send(ToFetchThreadMessage::FetchResponse(message));
931 })
932 .expect("Couldn't create fetch callback");
933 let join_handle = thread::Builder::new()
934 .name("FetchThread".to_owned())
935 .spawn(move || {
936 let mut fetch_thread = FetchThread {
937 active_fetches: FxHashMap::default(),
938 receiver,
939 to_fetch_sender,
940 };
941 fetch_thread.run();
942 })
943 .expect("Thread spawning failed");
944 FetchThreadHandle {
945 sender,
946 join_handle: RwLock::new(Some(join_handle)),
947 }
948 }
949
950 fn run(&mut self) {
951 loop {
952 match self.receiver.recv() {
953 Ok(ToFetchThreadMessage::StartFetch(
954 request_builder,
955 response_init,
956 callback,
957 core_resource_thread,
958 )) => {
959 let request_builder_id = request_builder.id;
960
961 let message = match response_init {
963 Some(response_init) => CoreResourceMsg::FetchRedirect(
964 request_builder,
965 response_init,
966 self.to_fetch_sender.clone(),
967 ),
968 None => CoreResourceMsg::Fetch(
969 request_builder,
970 FetchChannels::ResponseMsg(self.to_fetch_sender.clone()),
971 ),
972 };
973
974 if core_resource_thread.send(message).is_err() {
975 break;
979 }
980
981 let preexisting_fetch =
982 self.active_fetches.insert(request_builder_id, callback);
983 assert!(preexisting_fetch.is_none());
987 },
988 Ok(ToFetchThreadMessage::FetchResponse(fetch_response_msg)) => {
989 let request_id = fetch_response_msg.request_id();
990 let fetch_finished =
991 matches!(fetch_response_msg, FetchResponseMsg::ProcessResponseEOF(..));
992 let mut active_fetch = match self.active_fetches.entry(request_id) {
993 Entry::Occupied(occupied_entry) => occupied_entry,
994 Entry::Vacant(..) => {
995 continue;
998 },
999 };
1000
1001 active_fetch.get_mut()(fetch_response_msg);
1002 if fetch_finished {
1003 let _ = active_fetch.remove();
1004 }
1005 },
1006 Ok(ToFetchThreadMessage::Cancel {
1007 request_ids,
1008 forget,
1009 core_resource_thread,
1010 }) => {
1011 if forget {
1012 for request_id in &request_ids {
1013 self.active_fetches.remove(request_id);
1014 }
1015 }
1016
1017 let _ = core_resource_thread.send(CoreResourceMsg::Cancel(request_ids));
1021 },
1022 Ok(ToFetchThreadMessage::Exit) | Err(_) => break,
1023 }
1024 }
1025 }
1026
1027 fn fetch_async(
1028 core_resource_thread: &CoreResourceThread,
1029 request: RequestBuilder,
1030 response_init: Option<ResponseInit>,
1031 callback: BoxedFetchCallback,
1032 ) {
1033 let _ = FETCH_THREAD.get_or_init(FetchThread::spawn).sender.send(
1034 ToFetchThreadMessage::StartFetch(
1035 request,
1036 response_init,
1037 callback,
1038 core_resource_thread.clone(),
1039 ),
1040 );
1041 }
1042
1043 fn cancel_async_fetch(
1044 request_ids: Vec<RequestId>,
1045 core_resource_thread: &CoreResourceThread,
1046 forget: bool,
1047 ) {
1048 if let Some(fetch_thread) = FETCH_THREAD.get() {
1049 let _ = fetch_thread.sender.send(ToFetchThreadMessage::Cancel {
1050 request_ids,
1051 forget,
1052 core_resource_thread: core_resource_thread.clone(),
1053 });
1054 }
1055 }
1056
1057 pub fn exit() {
1059 let Some(fetch_thread) = FETCH_THREAD.get() else {
1060 return;
1061 };
1062 let _ = fetch_thread.sender.send(ToFetchThreadMessage::Exit);
1063 if let Some(join_handle) = fetch_thread.join_handle.write().take() {
1064 join_handle
1065 .join()
1066 .expect("Failed to join on the FetchThread join handle.");
1067 }
1068 }
1069}
1070
1071struct FetchThreadHandle {
1072 sender: Sender<ToFetchThreadMessage>,
1073 join_handle: RwLock<Option<JoinHandle<()>>>,
1074}
1075
1076static FETCH_THREAD: OnceLock<FetchThreadHandle> = OnceLock::new();
1077
1078pub fn fetch_async(
1080 core_resource_thread: &CoreResourceThread,
1081 request: RequestBuilder,
1082 response_init: Option<ResponseInit>,
1083 callback: BoxedFetchCallback,
1084) {
1085 FetchThread::fetch_async(core_resource_thread, request, response_init, callback);
1086}
1087
1088pub fn cancel_async_fetch(request_ids: Vec<RequestId>, core_resource_thread: &CoreResourceThread) {
1091 FetchThread::cancel_async_fetch(request_ids, core_resource_thread, false );
1092}
1093
1094pub fn cancel_and_forget_async_fetch(
1098 request_ids: Vec<RequestId>,
1099 core_resource_thread: &CoreResourceThread,
1100) {
1101 FetchThread::cancel_async_fetch(request_ids, core_resource_thread, true );
1102}
1103
1104#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
1105pub struct ResourceCorsData {
1106 pub preflight: bool,
1108 pub origin: ServoUrl,
1110}
1111
1112#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
1114pub struct Metadata {
1115 pub final_url: ServoUrl,
1117
1118 pub location_url: Option<Result<ServoUrl, String>>,
1120
1121 pub content_type: Option<Serde<ContentType>>,
1123
1124 pub charset: Option<String>,
1126
1127 pub headers: Option<Serde<HeaderMap>>,
1129
1130 pub status: HttpStatus,
1132
1133 pub referrer: Option<ServoUrl>,
1135
1136 pub referrer_policy: ReferrerPolicy,
1138 pub timing: Option<ResourceFetchTiming>,
1140 pub redirected: bool,
1142 pub tls_security_info: Option<TlsSecurityInfo>,
1144}
1145
1146impl Metadata {
1147 pub fn default(url: ServoUrl) -> Self {
1149 Metadata {
1150 final_url: url,
1151 location_url: None,
1152 content_type: None,
1153 charset: None,
1154 headers: None,
1155 status: HttpStatus::default(),
1156 referrer: None,
1157 referrer_policy: ReferrerPolicy::EmptyString,
1158 timing: None,
1159 redirected: false,
1160 tls_security_info: None,
1161 }
1162 }
1163
1164 pub fn set_content_type(&mut self, content_type: Option<&Mime>) {
1166 if self.headers.is_none() {
1167 self.headers = Some(Serde(HeaderMap::new()));
1168 }
1169
1170 if let Some(mime) = content_type {
1171 self.headers
1172 .as_mut()
1173 .unwrap()
1174 .typed_insert(ContentType::from(mime.clone()));
1175 if let Some(charset) = mime.get_param(mime::CHARSET) {
1176 self.charset = Some(charset.to_string());
1177 }
1178 self.content_type = Some(Serde(ContentType::from(mime.clone())));
1179 }
1180 }
1181
1182 pub fn set_referrer_policy(&mut self, referrer_policy: ReferrerPolicy) {
1184 if referrer_policy == ReferrerPolicy::EmptyString {
1185 return;
1186 }
1187
1188 if self.headers.is_none() {
1189 self.headers = Some(Serde(HeaderMap::new()));
1190 }
1191
1192 self.referrer_policy = referrer_policy;
1193
1194 self.headers
1195 .as_mut()
1196 .unwrap()
1197 .typed_insert::<ReferrerPolicyHeader>(referrer_policy.into());
1198 }
1199
1200 pub fn resource_content_type_metadata(&self, load_context: LoadContext, data: &[u8]) -> Mime {
1202 let no_sniff = self
1204 .headers
1205 .as_deref()
1206 .is_some_and(determine_nosniff)
1207 .into();
1208 let mime = self
1209 .content_type
1210 .clone()
1211 .map(|content_type| content_type.into_inner().into());
1212 MimeClassifier::default().classify(
1213 load_context,
1214 no_sniff,
1215 ApacheBugFlag::from_content_type(mime.as_ref()),
1216 &mime,
1217 data,
1218 )
1219 }
1220}
1221
1222#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
1224pub enum CookieSource {
1225 HTTP,
1227 NonHTTP,
1229}
1230
1231#[derive(Clone, Debug, Deserialize, Serialize)]
1232pub struct CookieChange {
1233 changed: Vec<Serde<Cookie<'static>>>,
1234 deleted: Vec<Serde<Cookie<'static>>>,
1235}
1236
1237#[derive(Clone, Debug, Deserialize, Serialize)]
1238pub enum CookieData {
1239 Change(CookieChange),
1240 Get(Option<Serde<Cookie<'static>>>),
1241 GetAll(Vec<Serde<Cookie<'static>>>),
1242 Set(Result<(), ()>),
1243 Delete(Result<(), ()>),
1244}
1245
1246#[derive(Clone, Debug, Deserialize, Serialize)]
1247pub struct CookieAsyncResponse {
1248 pub data: CookieData,
1249}
1250
1251#[derive(Clone, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)]
1253pub enum NetworkError {
1254 LoadCancelled,
1255 SslValidation(String, Vec<u8>),
1257 Crash(String),
1259 UnsupportedScheme,
1260 CorsGeneral,
1261 CrossOriginResponse,
1262 CorsCredentials,
1263 CorsAllowMethods,
1264 CorsAllowHeaders,
1265 CorsMethod,
1266 CorsAuthorization,
1267 CorsHeaders,
1268 ConnectionFailure,
1269 RedirectError,
1270 TooManyRedirects,
1271 TooManyInFlightKeepAliveRequests,
1272 InvalidMethod,
1273 ResourceLoadError(String),
1274 ContentSecurityPolicy,
1275 Nosniff,
1276 MimeType(String),
1277 SubresourceIntegrity,
1278 MixedContent,
1279 CacheError,
1280 InvalidPort,
1281 WebsocketConnectionFailure(String),
1282 LocalDirectoryError,
1283 PartialResponseToNonRangeRequestError,
1284 ProtocolHandlerSubstitutionError,
1285 BlobURLStoreError(String),
1286 HttpError(String),
1287 DecompressionError,
1288}
1289
1290impl fmt::Debug for NetworkError {
1291 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1292 match self {
1293 NetworkError::UnsupportedScheme => write!(f, "Unsupported scheme"),
1294 NetworkError::CorsGeneral => write!(f, "CORS check failed"),
1295 NetworkError::CrossOriginResponse => write!(f, "Cross-origin response"),
1296 NetworkError::CorsCredentials => write!(f, "Cross-origin credentials check failed"),
1297 NetworkError::CorsAllowMethods => write!(f, "CORS ACAM check failed"),
1298 NetworkError::CorsAllowHeaders => write!(f, "CORS ACAH check failed"),
1299 NetworkError::CorsMethod => write!(f, "CORS method check failed"),
1300 NetworkError::CorsAuthorization => write!(f, "CORS authorization check failed"),
1301 NetworkError::CorsHeaders => write!(f, "CORS headers check failed"),
1302 NetworkError::ConnectionFailure => write!(f, "Request failed"),
1303 NetworkError::RedirectError => write!(f, "Redirect failed"),
1304 NetworkError::TooManyRedirects => write!(f, "Too many redirects"),
1305 NetworkError::TooManyInFlightKeepAliveRequests => {
1306 write!(f, "Too many in flight keep-alive requests")
1307 },
1308 NetworkError::InvalidMethod => write!(f, "Unexpected method"),
1309 NetworkError::ResourceLoadError(s) => write!(f, "{}", s),
1310 NetworkError::ContentSecurityPolicy => write!(f, "Blocked by Content-Security-Policy"),
1311 NetworkError::Nosniff => write!(f, "Blocked by nosniff"),
1312 NetworkError::MimeType(s) => write!(f, "{}", s),
1313 NetworkError::SubresourceIntegrity => {
1314 write!(f, "Subresource integrity validation failed")
1315 },
1316 NetworkError::MixedContent => write!(f, "Blocked as mixed content"),
1317 NetworkError::CacheError => write!(f, "Couldn't find response in cache"),
1318 NetworkError::InvalidPort => write!(f, "Request attempted on bad port"),
1319 NetworkError::LocalDirectoryError => write!(f, "Local directory access failed"),
1320 NetworkError::LoadCancelled => write!(f, "Load cancelled"),
1321 NetworkError::SslValidation(s, _) => write!(f, "SSL validation error: {}", s),
1322 NetworkError::Crash(s) => write!(f, "Crash: {}", s),
1323 NetworkError::PartialResponseToNonRangeRequestError => write!(
1324 f,
1325 "Refusing to provide partial response from earlier ranged request to API that did not make a range request"
1326 ),
1327 NetworkError::ProtocolHandlerSubstitutionError => {
1328 write!(f, "Failed to parse substituted protocol handler url")
1329 },
1330 NetworkError::BlobURLStoreError(s) => write!(f, "Blob URL store error: {}", s),
1331 NetworkError::WebsocketConnectionFailure(s) => {
1332 write!(f, "Websocket connection failure: {}", s)
1333 },
1334 NetworkError::HttpError(s) => write!(f, "HTTP failure: {}", s),
1335 NetworkError::DecompressionError => write!(f, "Decompression error"),
1336 }
1337 }
1338}
1339
1340impl NetworkError {
1341 pub fn is_permanent_failure(&self) -> bool {
1342 matches!(
1343 self,
1344 NetworkError::ContentSecurityPolicy |
1345 NetworkError::MixedContent |
1346 NetworkError::SubresourceIntegrity |
1347 NetworkError::Nosniff |
1348 NetworkError::InvalidPort |
1349 NetworkError::CorsGeneral |
1350 NetworkError::CrossOriginResponse |
1351 NetworkError::CorsCredentials |
1352 NetworkError::CorsAllowMethods |
1353 NetworkError::CorsAllowHeaders |
1354 NetworkError::CorsMethod |
1355 NetworkError::CorsAuthorization |
1356 NetworkError::CorsHeaders |
1357 NetworkError::UnsupportedScheme
1358 )
1359 }
1360
1361 pub fn from_hyper_error(error: &HyperError, certificate: Option<CertificateDer>) -> Self {
1362 let error_string = error.to_string();
1363 match certificate {
1364 Some(certificate) => NetworkError::SslValidation(error_string, certificate.to_vec()),
1365 _ => NetworkError::HttpError(error_string),
1366 }
1367 }
1368}
1369
1370pub fn trim_http_whitespace(mut slice: &[u8]) -> &[u8] {
1373 const HTTP_WS_BYTES: &[u8] = b"\x09\x0A\x0D\x20";
1374
1375 loop {
1376 match slice.split_first() {
1377 Some((first, remainder)) if HTTP_WS_BYTES.contains(first) => slice = remainder,
1378 _ => break,
1379 }
1380 }
1381
1382 loop {
1383 match slice.split_last() {
1384 Some((last, remainder)) if HTTP_WS_BYTES.contains(last) => slice = remainder,
1385 _ => break,
1386 }
1387 }
1388
1389 slice
1390}
1391
1392pub fn ends_with_ignore_ascii_case(string: &str, suffix: &str) -> bool {
1394 string.len() >= suffix.len() &&
1395 string.as_bytes()[string.len() - suffix.len()..].eq_ignore_ascii_case(suffix.as_bytes())
1396}
1397
1398pub fn get_current_locale() -> &'static (String, HeaderValue) {
1400 static CURRENT_LOCALE: OnceLock<(String, HeaderValue)> = OnceLock::new();
1401
1402 CURRENT_LOCALE.get_or_init(|| {
1403 let locale_override = servo_config::pref!(intl_locale_override);
1404 let locale = if locale_override.is_empty() {
1405 sys_locale::get_locale().unwrap_or_else(|| "en-US".into())
1406 } else {
1407 locale_override
1408 };
1409 let header_value = HeaderValue::from_str(&locale)
1410 .ok()
1411 .unwrap_or_else(|| HeaderValue::from_static("en-US"));
1412 (locale, header_value)
1413 })
1414}
1415
1416pub fn set_default_accept_language(headers: &mut HeaderMap) {
1418 if headers.contains_key(header::ACCEPT_LANGUAGE) {
1421 return;
1422 }
1423
1424 headers.insert(header::ACCEPT_LANGUAGE, get_current_locale().1.clone());
1426}
1427
1428pub static PRIVILEGED_SECRET: LazyLock<u32> = LazyLock::new(|| rng().next_u32());