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