1#![deny(unsafe_code)]
6
7use std::fmt::{self, Debug, Display};
8use std::sync::{LazyLock, OnceLock};
9use std::thread::{self, JoinHandle};
10
11use content_security_policy::{self as csp};
12use cookie::Cookie;
13use crossbeam_channel::{Receiver, Sender, unbounded};
14use headers::{ContentType, HeaderMapExt, ReferrerPolicy as ReferrerPolicyHeader};
15use http::{HeaderMap, HeaderValue, StatusCode, header};
16use hyper_serde::Serde;
17use hyper_util::client::legacy::Error as HyperError;
18use ipc_channel::ipc::{self, IpcSender};
19use ipc_channel::router::ROUTER;
20use malloc_size_of::malloc_size_of_is_0;
21use malloc_size_of_derive::MallocSizeOf;
22use mime::Mime;
23use profile_traits::mem::ReportsChan;
24use rand::{RngCore, rng};
25use request::RequestId;
26use rustc_hash::FxHashMap;
27use rustls_pki_types::CertificateDer;
28use serde::{Deserialize, Serialize};
29use servo_base::generic_channel::{
30 self, CallbackSetter, GenericCallback, GenericOneshotSender, GenericSend, GenericSender,
31 SendResult,
32};
33use servo_base::id::{CookieStoreId, HistoryStateId, PipelineId};
34use servo_url::{ImmutableOrigin, ServoUrl};
35use uuid::Uuid;
36
37#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
39pub struct CookieOperationId(pub u64);
40
41use crate::fetch::headers::determine_nosniff;
42use crate::filemanager_thread::FileManagerThreadMsg;
43use crate::http_status::HttpStatus;
44use crate::mime_classifier::{ApacheBugFlag, MimeClassifier};
45use crate::request::{PreloadId, Request, RequestBuilder};
46use crate::response::{HttpsState, Response, ResponseInit};
47
48pub mod blob_url_store;
49pub mod filemanager_thread;
50pub mod http_status;
51pub mod image_cache;
52pub mod mime_classifier;
53pub mod policy_container;
54pub mod pub_domains;
55pub mod quality;
56pub mod request;
57pub(crate) mod resource_fetch_timing;
58pub mod response;
59pub use resource_fetch_timing::{
60 RedirectEndValue, RedirectStartValue, ResourceAttribute, ResourceFetchTiming,
61 ResourceFetchTimingContainer, ResourceTimeValue, ResourceTimingType,
62};
63
64pub const DOCUMENT_ACCEPT_HEADER_VALUE: HeaderValue =
66 HeaderValue::from_static("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
67
68pub mod fetch {
70 pub mod headers;
71}
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, Serialize)]
118pub struct CustomResponseMediator {
119 pub response_chan: IpcSender<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 match value.to_ascii_lowercase().as_str() {
154 "never" => ReferrerPolicy::NoReferrer,
155 "default" => ReferrerPolicy::StrictOriginWhenCrossOrigin,
156 "always" => ReferrerPolicy::UnsafeUrl,
157 "origin-when-crossorigin" => ReferrerPolicy::OriginWhenCrossOrigin,
158 _ => ReferrerPolicy::from(value),
159 }
160 }
161
162 pub fn parse_header_for_response(headers: &Option<Serde<HeaderMap>>) -> Self {
164 headers
166 .as_ref()
167 .and_then(|headers| headers.typed_get::<ReferrerPolicyHeader>())
169 .into()
171 }
172}
173
174impl From<&str> for ReferrerPolicy {
175 fn from(value: &str) -> Self {
177 match value.to_ascii_lowercase().as_str() {
178 "no-referrer" => ReferrerPolicy::NoReferrer,
179 "no-referrer-when-downgrade" => ReferrerPolicy::NoReferrerWhenDowngrade,
180 "origin" => ReferrerPolicy::Origin,
181 "same-origin" => ReferrerPolicy::SameOrigin,
182 "strict-origin" => ReferrerPolicy::StrictOrigin,
183 "strict-origin-when-cross-origin" => ReferrerPolicy::StrictOriginWhenCrossOrigin,
184 "origin-when-cross-origin" => ReferrerPolicy::OriginWhenCrossOrigin,
185 "unsafe-url" => ReferrerPolicy::UnsafeUrl,
186 _ => ReferrerPolicy::EmptyString,
187 }
188 }
189}
190
191impl Display for ReferrerPolicy {
192 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193 let string = match self {
194 ReferrerPolicy::EmptyString => "",
195 ReferrerPolicy::NoReferrer => "no-referrer",
196 ReferrerPolicy::NoReferrerWhenDowngrade => "no-referrer-when-downgrade",
197 ReferrerPolicy::Origin => "origin",
198 ReferrerPolicy::SameOrigin => "same-origin",
199 ReferrerPolicy::OriginWhenCrossOrigin => "origin-when-cross-origin",
200 ReferrerPolicy::UnsafeUrl => "unsafe-url",
201 ReferrerPolicy::StrictOrigin => "strict-origin",
202 ReferrerPolicy::StrictOriginWhenCrossOrigin => "strict-origin-when-cross-origin",
203 };
204 write!(formatter, "{string}")
205 }
206}
207
208impl From<Option<ReferrerPolicyHeader>> for ReferrerPolicy {
210 fn from(header: Option<ReferrerPolicyHeader>) -> Self {
211 header.map_or(ReferrerPolicy::EmptyString, |policy| match policy {
214 ReferrerPolicyHeader::NO_REFERRER => ReferrerPolicy::NoReferrer,
215 ReferrerPolicyHeader::NO_REFERRER_WHEN_DOWNGRADE => {
216 ReferrerPolicy::NoReferrerWhenDowngrade
217 },
218 ReferrerPolicyHeader::SAME_ORIGIN => ReferrerPolicy::SameOrigin,
219 ReferrerPolicyHeader::ORIGIN => ReferrerPolicy::Origin,
220 ReferrerPolicyHeader::ORIGIN_WHEN_CROSS_ORIGIN => ReferrerPolicy::OriginWhenCrossOrigin,
221 ReferrerPolicyHeader::UNSAFE_URL => ReferrerPolicy::UnsafeUrl,
222 ReferrerPolicyHeader::STRICT_ORIGIN => ReferrerPolicy::StrictOrigin,
223 ReferrerPolicyHeader::STRICT_ORIGIN_WHEN_CROSS_ORIGIN => {
224 ReferrerPolicy::StrictOriginWhenCrossOrigin
225 },
226 })
227 }
228}
229
230impl From<ReferrerPolicy> for ReferrerPolicyHeader {
231 fn from(referrer_policy: ReferrerPolicy) -> Self {
232 match referrer_policy {
233 ReferrerPolicy::NoReferrer => ReferrerPolicyHeader::NO_REFERRER,
234 ReferrerPolicy::NoReferrerWhenDowngrade => {
235 ReferrerPolicyHeader::NO_REFERRER_WHEN_DOWNGRADE
236 },
237 ReferrerPolicy::SameOrigin => ReferrerPolicyHeader::SAME_ORIGIN,
238 ReferrerPolicy::Origin => ReferrerPolicyHeader::ORIGIN,
239 ReferrerPolicy::OriginWhenCrossOrigin => ReferrerPolicyHeader::ORIGIN_WHEN_CROSS_ORIGIN,
240 ReferrerPolicy::UnsafeUrl => ReferrerPolicyHeader::UNSAFE_URL,
241 ReferrerPolicy::StrictOrigin => ReferrerPolicyHeader::STRICT_ORIGIN,
242 ReferrerPolicy::EmptyString | ReferrerPolicy::StrictOriginWhenCrossOrigin => {
243 ReferrerPolicyHeader::STRICT_ORIGIN_WHEN_CROSS_ORIGIN
244 },
245 }
246 }
247}
248
249#[expect(clippy::large_enum_variant)]
251#[derive(Debug, Deserialize, Serialize)]
252pub enum FetchResponseMsg {
253 ProcessRequestBody(RequestId),
255 ProcessResponse(RequestId, Result<FetchMetadata, NetworkError>),
257 ProcessResponseChunk(RequestId, DebugVec),
258 ProcessResponseEOF(RequestId, Result<(), NetworkError>, ResourceFetchTiming),
259 ProcessCspViolations(RequestId, Vec<csp::Violation>),
260}
261
262#[derive(Deserialize, PartialEq, Serialize, MallocSizeOf)]
263pub struct DebugVec(pub Vec<u8>);
264
265impl From<Vec<u8>> for DebugVec {
266 fn from(v: Vec<u8>) -> Self {
267 Self(v)
268 }
269}
270
271impl std::ops::Deref for DebugVec {
272 type Target = Vec<u8>;
273 fn deref(&self) -> &Self::Target {
274 &self.0
275 }
276}
277
278impl std::fmt::Debug for DebugVec {
279 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
280 f.write_fmt(format_args!("[...; {}]", self.0.len()))
281 }
282}
283
284impl FetchResponseMsg {
285 pub fn request_id(&self) -> RequestId {
286 match self {
287 FetchResponseMsg::ProcessRequestBody(id) |
288 FetchResponseMsg::ProcessResponse(id, ..) |
289 FetchResponseMsg::ProcessResponseChunk(id, ..) |
290 FetchResponseMsg::ProcessResponseEOF(id, ..) |
291 FetchResponseMsg::ProcessCspViolations(id, ..) => *id,
292 }
293 }
294}
295
296pub trait FetchTaskTarget {
297 fn process_request_body(&mut self, request: &Request);
301
302 fn process_response(&mut self, request: &Request, response: &Response);
306
307 fn process_response_chunk(&mut self, request: &Request, chunk: Vec<u8>);
309
310 fn process_response_eof(&mut self, request: &Request, response: &Response);
314
315 fn process_csp_violations(&mut self, request: &Request, violations: Vec<csp::Violation>);
316}
317
318#[derive(Clone, Debug, Deserialize, Serialize)]
319pub enum FilteredMetadata {
320 Basic(Metadata),
321 Cors(Metadata),
322 Opaque,
323 OpaqueRedirect(ServoUrl),
324}
325
326#[expect(clippy::large_enum_variant)]
328#[derive(Clone, Debug, Deserialize, Serialize)]
329pub enum FetchMetadata {
330 Unfiltered(Metadata),
331 Filtered {
332 filtered: FilteredMetadata,
333 unsafe_: Metadata,
334 },
335}
336
337impl FetchMetadata {
338 pub fn metadata(&self) -> &Metadata {
339 match self {
340 Self::Unfiltered(metadata) => metadata,
341 Self::Filtered { unsafe_, .. } => unsafe_,
342 }
343 }
344
345 pub fn is_cors_cross_origin(&self) -> bool {
347 if let Self::Filtered { filtered, .. } = self {
348 match filtered {
349 FilteredMetadata::Basic(_) | FilteredMetadata::Cors(_) => false,
350 FilteredMetadata::Opaque | FilteredMetadata::OpaqueRedirect(_) => true,
351 }
352 } else {
353 false
354 }
355 }
356}
357
358impl FetchTaskTarget for IpcSender<FetchResponseMsg> {
359 fn process_request_body(&mut self, request: &Request) {
360 let _ = self.send(FetchResponseMsg::ProcessRequestBody(request.id));
361 }
362
363 fn process_response(&mut self, request: &Request, response: &Response) {
364 let _ = self.send(FetchResponseMsg::ProcessResponse(
365 request.id,
366 response.metadata(),
367 ));
368 }
369
370 fn process_response_chunk(&mut self, request: &Request, chunk: Vec<u8>) {
371 let _ = self.send(FetchResponseMsg::ProcessResponseChunk(
372 request.id,
373 chunk.into(),
374 ));
375 }
376
377 fn process_response_eof(&mut self, request: &Request, response: &Response) {
378 let result = response
379 .get_network_error()
380 .map_or_else(|| Ok(()), |network_error| Err(network_error.clone()));
381 let timing = response.get_resource_timing().inner().clone();
382
383 let _ = self.send(FetchResponseMsg::ProcessResponseEOF(
384 request.id, result, timing,
385 ));
386 }
387
388 fn process_csp_violations(&mut self, request: &Request, violations: Vec<csp::Violation>) {
389 let _ = self.send(FetchResponseMsg::ProcessCspViolations(
390 request.id, violations,
391 ));
392 }
393}
394
395#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize)]
396#[serde(rename_all = "lowercase")]
397pub enum TlsSecurityState {
398 #[default]
400 Insecure,
401 Weak,
403 Broken,
405 Secure,
407}
408
409impl Display for TlsSecurityState {
410 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
411 let text = match self {
412 TlsSecurityState::Insecure => "insecure",
413 TlsSecurityState::Weak => "weak",
414 TlsSecurityState::Broken => "broken",
415 TlsSecurityState::Secure => "secure",
416 };
417 f.write_str(text)
418 }
419}
420
421#[derive(Clone, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize)]
422pub struct TlsSecurityInfo {
423 #[serde(default)]
425 pub state: TlsSecurityState,
426 pub weakness_reasons: Vec<String>,
428 pub protocol_version: Option<String>,
430 pub cipher_suite: Option<String>,
432 pub kea_group_name: Option<String>,
434 pub signature_scheme_name: Option<String>,
436 pub alpn_protocol: Option<String>,
438 pub certificate_chain_der: Vec<Vec<u8>>,
440 pub certificate_transparency: Option<String>,
442 pub hsts: bool,
444 pub hpkp: bool,
446 pub used_ech: bool,
448 pub used_delegated_credentials: bool,
450 pub used_ocsp: bool,
452 pub used_private_dns: bool,
454}
455
456impl FetchTaskTarget for IpcSender<WebSocketNetworkEvent> {
457 fn process_request_body(&mut self, _: &Request) {}
458 fn process_response(&mut self, _: &Request, response: &Response) {
459 if response.is_network_error() {
460 let _ = self.send(WebSocketNetworkEvent::Fail);
461 }
462 }
463 fn process_response_chunk(&mut self, _: &Request, _: Vec<u8>) {}
464 fn process_response_eof(&mut self, _: &Request, _: &Response) {}
465 fn process_csp_violations(&mut self, _: &Request, violations: Vec<csp::Violation>) {
466 let _ = self.send(WebSocketNetworkEvent::ReportCSPViolations(violations));
467 }
468}
469
470pub struct DiscardFetch;
474
475impl FetchTaskTarget for DiscardFetch {
476 fn process_request_body(&mut self, _: &Request) {}
477 fn process_response(&mut self, _: &Request, _: &Response) {}
478 fn process_response_chunk(&mut self, _: &Request, _: Vec<u8>) {}
479 fn process_response_eof(&mut self, _: &Request, _: &Response) {}
480 fn process_csp_violations(&mut self, _: &Request, _: Vec<csp::Violation>) {}
481}
482
483pub trait AsyncRuntime: Send {
486 fn shutdown(&mut self);
487}
488
489pub type CoreResourceThread = GenericSender<CoreResourceMsg>;
491
492#[derive(Clone, Debug, Deserialize, Serialize)]
498pub struct ResourceThreads {
499 pub core_thread: CoreResourceThread,
500}
501
502impl ResourceThreads {
503 pub fn new(core_thread: CoreResourceThread) -> ResourceThreads {
504 ResourceThreads { core_thread }
505 }
506
507 pub fn cache_entries(&self) -> Vec<CacheEntryDescriptor> {
508 let (sender, receiver) = generic_channel::channel().unwrap();
509 let _ = self
510 .core_thread
511 .send(CoreResourceMsg::GetCacheEntries(sender));
512 receiver.recv().unwrap()
513 }
514
515 pub fn clear_cache(&self) {
516 let (sender, receiver) = generic_channel::channel().unwrap();
523 let _ = self
524 .core_thread
525 .send(CoreResourceMsg::ClearCache(Some(sender)));
526 let _ = receiver.recv();
527 }
528
529 pub fn cookies(&self) -> Vec<SiteDescriptor> {
530 let (sender, receiver) = generic_channel::channel().unwrap();
531 let _ = self.core_thread.send(CoreResourceMsg::ListCookies(sender));
532 receiver.recv().unwrap()
533 }
534
535 pub fn clear_cookies_for_sites(&self, sites: &[&str]) {
536 let sites = sites.iter().map(|site| site.to_string()).collect();
537 let (sender, receiver) = generic_channel::channel().unwrap();
538 let _ = self
539 .core_thread
540 .send(CoreResourceMsg::DeleteCookiesForSites(sites, sender));
541 let _ = receiver.recv();
542 }
543
544 pub fn clear_cookies(&self) {
545 let (sender, receiver) = ipc::channel().unwrap();
546 let _ = self
547 .core_thread
548 .send(CoreResourceMsg::DeleteCookies(None, Some(sender)));
549 let _ = receiver.recv();
550 }
551
552 pub fn cookies_for_url(&self, url: ServoUrl, source: CookieSource) -> Vec<Cookie<'static>> {
553 let (sender, receiver) = generic_channel::channel().unwrap();
554 let _ = self
555 .core_thread
556 .send(CoreResourceMsg::GetCookiesForUrl(url, sender, source));
557 receiver
558 .recv()
559 .unwrap()
560 .into_iter()
561 .map(|cookie| cookie.into_inner())
562 .collect()
563 }
564
565 pub fn set_cookie_for_url(&self, url: ServoUrl, cookie: Cookie<'static>, source: CookieSource) {
566 let _ = self.core_thread.send(CoreResourceMsg::SetCookieForUrl(
567 url,
568 Serde(cookie),
569 source,
570 None,
571 ));
572 }
573
574 pub fn set_cookie_for_url_sync(
575 &self,
576 url: ServoUrl,
577 cookie: Cookie<'static>,
578 source: CookieSource,
579 ) {
580 let (sender, receiver) = generic_channel::channel().unwrap();
581 let _ = self.core_thread.send(CoreResourceMsg::SetCookieForUrl(
582 url,
583 Serde(cookie),
584 source,
585 Some(sender),
586 ));
587 let _ = receiver.recv();
588 }
589
590 pub fn cookies_for_url_async(
591 &self,
592 id: CookieOperationId,
593 url: ServoUrl,
594 source: CookieSource,
595 ) {
596 let _ = self
597 .core_thread
598 .send(CoreResourceMsg::EmbedderGetCookiesForUrl(id, url, source));
599 }
600
601 pub fn set_cookie_for_url_async(
602 &self,
603 id: CookieOperationId,
604 url: ServoUrl,
605 cookie: Cookie<'static>,
606 source: CookieSource,
607 ) {
608 let _ = self
609 .core_thread
610 .send(CoreResourceMsg::EmbedderSetCookieForUrl(
611 id,
612 url,
613 Serde(cookie),
614 source,
615 ));
616 }
617}
618
619impl GenericSend<CoreResourceMsg> for ResourceThreads {
620 fn send(&self, msg: CoreResourceMsg) -> SendResult {
621 self.core_thread.send(msg)
622 }
623
624 fn sender(&self) -> GenericSender<CoreResourceMsg> {
625 self.core_thread.clone()
626 }
627}
628
629malloc_size_of_is_0!(ResourceThreads);
631
632#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
633pub enum IncludeSubdomains {
634 Included,
635 NotIncluded,
636}
637
638#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
639pub enum MessageData {
640 Text(String),
641 Binary(Vec<u8>),
642}
643
644#[derive(Debug, Deserialize, Serialize, MallocSizeOf)]
645pub enum WebSocketDomAction {
646 SendMessage(MessageData),
647 Close(Option<u16>, Option<String>),
648}
649
650#[derive(Debug, Deserialize, Serialize)]
651pub enum WebSocketNetworkEvent {
652 ReportCSPViolations(Vec<csp::Violation>),
653 ConnectionEstablished { protocol_in_use: Option<String> },
654 MessageReceived(MessageData),
655 Close(Option<u16>, String),
656 Fail,
657}
658
659#[derive(Debug, Deserialize, Serialize)]
660pub enum FetchChannels {
662 ResponseMsg(IpcSender<FetchResponseMsg>),
663 WebSocket {
664 event_sender: IpcSender<WebSocketNetworkEvent>,
665 action_receiver: CallbackSetter<WebSocketDomAction>,
666 },
667 Prefetch,
670}
671
672#[derive(Debug, Deserialize, Serialize)]
673pub enum CoreResourceMsg {
674 Fetch(RequestBuilder, FetchChannels),
675 Cancel(Vec<RequestId>),
676 FetchRedirect(RequestBuilder, ResponseInit, IpcSender<FetchResponseMsg>),
678 SetCookieForUrl(
681 ServoUrl,
682 Serde<Cookie<'static>>,
683 CookieSource,
684 Option<GenericSender<()>>,
685 ),
686 SetCookiesForUrl(ServoUrl, Vec<Serde<Cookie<'static>>>, CookieSource),
688 SetCookieForUrlAsync(
689 CookieStoreId,
690 ServoUrl,
691 Serde<Cookie<'static>>,
692 CookieSource,
693 ),
694 GetCookieStringForUrl(ServoUrl, GenericSender<Option<String>>, CookieSource),
696 GetCookiesForUrl(
699 ServoUrl,
700 GenericSender<Vec<Serde<Cookie<'static>>>>,
701 CookieSource,
702 ),
703 EmbedderGetCookiesForUrl(CookieOperationId, ServoUrl, CookieSource),
706 EmbedderSetCookieForUrl(
709 CookieOperationId,
710 ServoUrl,
711 Serde<Cookie<'static>>,
712 CookieSource,
713 ),
714 GetCookieDataForUrlAsync(CookieStoreId, ServoUrl, Option<String>),
715 GetAllCookieDataForUrlAsync(CookieStoreId, ServoUrl, Option<String>),
716 DeleteCookiesForSites(Vec<String>, GenericSender<()>),
717 DeleteCookies(Option<ServoUrl>, Option<IpcSender<()>>),
720 DeleteCookie(ServoUrl, String),
721 DeleteCookieAsync(CookieStoreId, ServoUrl, String),
722 NewCookieListener(
723 CookieStoreId,
724 GenericCallback<CookieAsyncResponse>,
725 ServoUrl,
726 ),
727 RemoveCookieListener(CookieStoreId),
728 ListCookies(GenericSender<Vec<SiteDescriptor>>),
729 GetHistoryState(HistoryStateId, GenericSender<Option<Vec<u8>>>),
731 SetHistoryState(HistoryStateId, Vec<u8>),
733 RemoveHistoryStates(Vec<HistoryStateId>),
735 GetCacheEntries(GenericSender<Vec<CacheEntryDescriptor>>),
737 ClearCache(Option<GenericSender<()>>),
739 NetworkMediator(IpcSender<CustomResponseMediator>, ImmutableOrigin),
741 ToFileManager(FileManagerThreadMsg),
743 StorePreloadedResponse(PreloadId, Response),
744 TotalSizeOfInFlightKeepAliveRecords(PipelineId, GenericSender<u64>),
745 Exit(GenericOneshotSender<()>),
748 CollectMemoryReport(ReportsChan),
749 RevokeTokenForFile(BlobTokenRevocationRequest),
750 RefreshTokenForFile(BlobTokenRefreshRequest),
751}
752
753#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
754pub struct BlobTokenRevocationRequest {
755 pub blob_id: Uuid,
756 pub token: Uuid,
757}
758
759#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
760pub struct BlobTokenRefreshRequest {
761 pub blob_id: Uuid,
762 pub new_token_sender: GenericSender<Uuid>,
763}
764
765#[derive(Clone, Debug, Deserialize, Serialize)]
766pub struct SiteDescriptor {
767 pub name: String,
768}
769
770impl SiteDescriptor {
771 pub fn new(name: String) -> Self {
772 SiteDescriptor { name }
773 }
774}
775
776#[derive(Clone, Debug, Deserialize, Serialize)]
777pub struct CacheEntryDescriptor {
778 pub key: String,
779}
780
781impl CacheEntryDescriptor {
782 pub fn new(key: String) -> Self {
783 Self { key }
784 }
785}
786
787#[expect(clippy::large_enum_variant)]
789enum ToFetchThreadMessage {
790 Cancel(Vec<RequestId>, CoreResourceThread),
791 StartFetch(
792 RequestBuilder,
793 Option<ResponseInit>,
794 BoxedFetchCallback,
795 CoreResourceThread,
796 ),
797 FetchResponse(FetchResponseMsg),
798 Exit,
800}
801
802pub type BoxedFetchCallback = Box<dyn FnMut(FetchResponseMsg) + Send + 'static>;
803
804struct FetchThread {
808 active_fetches: FxHashMap<RequestId, BoxedFetchCallback>,
811 receiver: Receiver<ToFetchThreadMessage>,
815 to_fetch_sender: IpcSender<FetchResponseMsg>,
818}
819
820impl FetchThread {
821 fn spawn() -> (Sender<ToFetchThreadMessage>, JoinHandle<()>) {
822 let (sender, receiver) = unbounded();
823 let (to_fetch_sender, from_fetch_sender) = ipc::channel().unwrap();
824
825 let sender_clone = sender.clone();
826 ROUTER.add_typed_route(
827 from_fetch_sender,
828 Box::new(move |message| {
829 let message: FetchResponseMsg = message.unwrap();
830 let _ = sender_clone.send(ToFetchThreadMessage::FetchResponse(message));
831 }),
832 );
833 let join_handle = thread::Builder::new()
834 .name("FetchThread".to_owned())
835 .spawn(move || {
836 let mut fetch_thread = FetchThread {
837 active_fetches: FxHashMap::default(),
838 receiver,
839 to_fetch_sender,
840 };
841 fetch_thread.run();
842 })
843 .expect("Thread spawning failed");
844 (sender, join_handle)
845 }
846
847 fn run(&mut self) {
848 loop {
849 match self.receiver.recv().unwrap() {
850 ToFetchThreadMessage::StartFetch(
851 request_builder,
852 response_init,
853 callback,
854 core_resource_thread,
855 ) => {
856 let request_builder_id = request_builder.id;
857
858 let message = match response_init {
860 Some(response_init) => CoreResourceMsg::FetchRedirect(
861 request_builder,
862 response_init,
863 self.to_fetch_sender.clone(),
864 ),
865 None => CoreResourceMsg::Fetch(
866 request_builder,
867 FetchChannels::ResponseMsg(self.to_fetch_sender.clone()),
868 ),
869 };
870
871 core_resource_thread.send(message).unwrap();
872
873 let preexisting_fetch =
874 self.active_fetches.insert(request_builder_id, callback);
875 assert!(preexisting_fetch.is_none());
879 },
880 ToFetchThreadMessage::FetchResponse(fetch_response_msg) => {
881 let request_id = fetch_response_msg.request_id();
882 let fetch_finished =
883 matches!(fetch_response_msg, FetchResponseMsg::ProcessResponseEOF(..));
884
885 self.active_fetches
886 .get_mut(&request_id)
887 .expect("Got fetch response for unknown fetch")(
888 fetch_response_msg
889 );
890
891 if fetch_finished {
892 self.active_fetches.remove(&request_id);
893 }
894 },
895 ToFetchThreadMessage::Cancel(request_ids, core_resource_thread) => {
896 let _ = core_resource_thread.send(CoreResourceMsg::Cancel(request_ids));
900 },
901 ToFetchThreadMessage::Exit => break,
902 }
903 }
904 }
905}
906
907static FETCH_THREAD: OnceLock<Sender<ToFetchThreadMessage>> = OnceLock::new();
908
909pub fn start_fetch_thread() -> JoinHandle<()> {
912 let (sender, join_handle) = FetchThread::spawn();
913 FETCH_THREAD
914 .set(sender)
915 .expect("Fetch thread should be set only once on start-up");
916 join_handle
917}
918
919pub fn exit_fetch_thread() {
924 let _ = FETCH_THREAD
925 .get()
926 .expect("Fetch thread should always be initialized on start-up")
927 .send(ToFetchThreadMessage::Exit);
928}
929
930pub fn fetch_async(
932 core_resource_thread: &CoreResourceThread,
933 request: RequestBuilder,
934 response_init: Option<ResponseInit>,
935 callback: BoxedFetchCallback,
936) {
937 let _ = FETCH_THREAD
938 .get()
939 .expect("Fetch thread should always be initialized on start-up")
940 .send(ToFetchThreadMessage::StartFetch(
941 request,
942 response_init,
943 callback,
944 core_resource_thread.clone(),
945 ));
946}
947
948pub fn cancel_async_fetch(request_ids: Vec<RequestId>, core_resource_thread: &CoreResourceThread) {
951 let _ = FETCH_THREAD
952 .get()
953 .expect("Fetch thread should always be initialized on start-up")
954 .send(ToFetchThreadMessage::Cancel(
955 request_ids,
956 core_resource_thread.clone(),
957 ));
958}
959
960#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
961pub struct ResourceCorsData {
962 pub preflight: bool,
964 pub origin: ServoUrl,
966}
967
968#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
970pub struct Metadata {
971 pub final_url: ServoUrl,
973
974 pub location_url: Option<Result<ServoUrl, String>>,
976
977 #[ignore_malloc_size_of = "Defined in hyper"]
978 pub content_type: Option<Serde<ContentType>>,
980
981 pub charset: Option<String>,
983
984 #[ignore_malloc_size_of = "Defined in hyper"]
985 pub headers: Option<Serde<HeaderMap>>,
987
988 pub status: HttpStatus,
990
991 pub https_state: HttpsState,
993
994 pub referrer: Option<ServoUrl>,
996
997 pub referrer_policy: ReferrerPolicy,
999 pub timing: Option<ResourceFetchTiming>,
1001 pub redirected: bool,
1003 pub tls_security_info: Option<TlsSecurityInfo>,
1005}
1006
1007impl Metadata {
1008 pub fn default(url: ServoUrl) -> Self {
1010 Metadata {
1011 final_url: url,
1012 location_url: None,
1013 content_type: None,
1014 charset: None,
1015 headers: None,
1016 status: HttpStatus::default(),
1017 https_state: HttpsState::None,
1018 referrer: None,
1019 referrer_policy: ReferrerPolicy::EmptyString,
1020 timing: None,
1021 redirected: false,
1022 tls_security_info: None,
1023 }
1024 }
1025
1026 pub fn set_content_type(&mut self, content_type: Option<&Mime>) {
1028 if self.headers.is_none() {
1029 self.headers = Some(Serde(HeaderMap::new()));
1030 }
1031
1032 if let Some(mime) = content_type {
1033 self.headers
1034 .as_mut()
1035 .unwrap()
1036 .typed_insert(ContentType::from(mime.clone()));
1037 if let Some(charset) = mime.get_param(mime::CHARSET) {
1038 self.charset = Some(charset.to_string());
1039 }
1040 self.content_type = Some(Serde(ContentType::from(mime.clone())));
1041 }
1042 }
1043
1044 pub fn set_referrer_policy(&mut self, referrer_policy: ReferrerPolicy) {
1046 if referrer_policy == ReferrerPolicy::EmptyString {
1047 return;
1048 }
1049
1050 if self.headers.is_none() {
1051 self.headers = Some(Serde(HeaderMap::new()));
1052 }
1053
1054 self.referrer_policy = referrer_policy;
1055
1056 self.headers
1057 .as_mut()
1058 .unwrap()
1059 .typed_insert::<ReferrerPolicyHeader>(referrer_policy.into());
1060 }
1061
1062 pub fn resource_content_type_metadata(&self, load_context: LoadContext, data: &[u8]) -> Mime {
1064 let no_sniff = self
1066 .headers
1067 .as_deref()
1068 .is_some_and(determine_nosniff)
1069 .into();
1070 let mime = self
1071 .content_type
1072 .clone()
1073 .map(|content_type| content_type.into_inner().into());
1074 MimeClassifier::default().classify(
1075 load_context,
1076 no_sniff,
1077 ApacheBugFlag::from_content_type(mime.as_ref()),
1078 &mime,
1079 data,
1080 )
1081 }
1082}
1083
1084#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
1086pub enum CookieSource {
1087 HTTP,
1089 NonHTTP,
1091}
1092
1093#[derive(Clone, Debug, Deserialize, Serialize)]
1094pub struct CookieChange {
1095 changed: Vec<Serde<Cookie<'static>>>,
1096 deleted: Vec<Serde<Cookie<'static>>>,
1097}
1098
1099#[derive(Clone, Debug, Deserialize, Serialize)]
1100pub enum CookieData {
1101 Change(CookieChange),
1102 Get(Option<Serde<Cookie<'static>>>),
1103 GetAll(Vec<Serde<Cookie<'static>>>),
1104 Set(Result<(), ()>),
1105 Delete(Result<(), ()>),
1106}
1107
1108#[derive(Clone, Debug, Deserialize, Serialize)]
1109pub struct CookieAsyncResponse {
1110 pub data: CookieData,
1111}
1112
1113#[derive(Clone, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)]
1115pub enum NetworkError {
1116 LoadCancelled,
1117 SslValidation(String, Vec<u8>),
1119 Crash(String),
1121 UnsupportedScheme,
1122 CorsGeneral,
1123 CrossOriginResponse,
1124 CorsCredentials,
1125 CorsAllowMethods,
1126 CorsAllowHeaders,
1127 CorsMethod,
1128 CorsAuthorization,
1129 CorsHeaders,
1130 ConnectionFailure,
1131 RedirectError,
1132 TooManyRedirects,
1133 TooManyInFlightKeepAliveRequests,
1134 InvalidMethod,
1135 ResourceLoadError(String),
1136 ContentSecurityPolicy,
1137 Nosniff,
1138 MimeType(String),
1139 SubresourceIntegrity,
1140 MixedContent,
1141 CacheError,
1142 InvalidPort,
1143 WebsocketConnectionFailure(String),
1144 LocalDirectoryError,
1145 PartialResponseToNonRangeRequestError,
1146 ProtocolHandlerSubstitutionError,
1147 BlobURLStoreError(String),
1148 HttpError(String),
1149 DecompressionError,
1150}
1151
1152impl fmt::Debug for NetworkError {
1153 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1154 match self {
1155 NetworkError::UnsupportedScheme => write!(f, "Unsupported scheme"),
1156 NetworkError::CorsGeneral => write!(f, "CORS check failed"),
1157 NetworkError::CrossOriginResponse => write!(f, "Cross-origin response"),
1158 NetworkError::CorsCredentials => write!(f, "Cross-origin credentials check failed"),
1159 NetworkError::CorsAllowMethods => write!(f, "CORS ACAM check failed"),
1160 NetworkError::CorsAllowHeaders => write!(f, "CORS ACAH check failed"),
1161 NetworkError::CorsMethod => write!(f, "CORS method check failed"),
1162 NetworkError::CorsAuthorization => write!(f, "CORS authorization check failed"),
1163 NetworkError::CorsHeaders => write!(f, "CORS headers check failed"),
1164 NetworkError::ConnectionFailure => write!(f, "Request failed"),
1165 NetworkError::RedirectError => write!(f, "Redirect failed"),
1166 NetworkError::TooManyRedirects => write!(f, "Too many redirects"),
1167 NetworkError::TooManyInFlightKeepAliveRequests => {
1168 write!(f, "Too many in flight keep-alive requests")
1169 },
1170 NetworkError::InvalidMethod => write!(f, "Unexpected method"),
1171 NetworkError::ResourceLoadError(s) => write!(f, "{}", s),
1172 NetworkError::ContentSecurityPolicy => write!(f, "Blocked by Content-Security-Policy"),
1173 NetworkError::Nosniff => write!(f, "Blocked by nosniff"),
1174 NetworkError::MimeType(s) => write!(f, "{}", s),
1175 NetworkError::SubresourceIntegrity => {
1176 write!(f, "Subresource integrity validation failed")
1177 },
1178 NetworkError::MixedContent => write!(f, "Blocked as mixed content"),
1179 NetworkError::CacheError => write!(f, "Couldn't find response in cache"),
1180 NetworkError::InvalidPort => write!(f, "Request attempted on bad port"),
1181 NetworkError::LocalDirectoryError => write!(f, "Local directory access failed"),
1182 NetworkError::LoadCancelled => write!(f, "Load cancelled"),
1183 NetworkError::SslValidation(s, _) => write!(f, "SSL validation error: {}", s),
1184 NetworkError::Crash(s) => write!(f, "Crash: {}", s),
1185 NetworkError::PartialResponseToNonRangeRequestError => write!(
1186 f,
1187 "Refusing to provide partial response from earlier ranged request to API that did not make a range request"
1188 ),
1189 NetworkError::ProtocolHandlerSubstitutionError => {
1190 write!(f, "Failed to parse substituted protocol handler url")
1191 },
1192 NetworkError::BlobURLStoreError(s) => write!(f, "Blob URL store error: {}", s),
1193 NetworkError::WebsocketConnectionFailure(s) => {
1194 write!(f, "Websocket connection failure: {}", s)
1195 },
1196 NetworkError::HttpError(s) => write!(f, "HTTP failure: {}", s),
1197 NetworkError::DecompressionError => write!(f, "Decompression error"),
1198 }
1199 }
1200}
1201
1202impl NetworkError {
1203 pub fn is_permanent_failure(&self) -> bool {
1204 matches!(
1205 self,
1206 NetworkError::ContentSecurityPolicy |
1207 NetworkError::MixedContent |
1208 NetworkError::SubresourceIntegrity |
1209 NetworkError::Nosniff |
1210 NetworkError::InvalidPort |
1211 NetworkError::CorsGeneral |
1212 NetworkError::CrossOriginResponse |
1213 NetworkError::CorsCredentials |
1214 NetworkError::CorsAllowMethods |
1215 NetworkError::CorsAllowHeaders |
1216 NetworkError::CorsMethod |
1217 NetworkError::CorsAuthorization |
1218 NetworkError::CorsHeaders |
1219 NetworkError::UnsupportedScheme
1220 )
1221 }
1222
1223 pub fn from_hyper_error(error: &HyperError, certificate: Option<CertificateDer>) -> Self {
1224 let error_string = error.to_string();
1225 match certificate {
1226 Some(certificate) => NetworkError::SslValidation(error_string, certificate.to_vec()),
1227 _ => NetworkError::HttpError(error_string),
1228 }
1229 }
1230}
1231
1232pub fn trim_http_whitespace(mut slice: &[u8]) -> &[u8] {
1235 const HTTP_WS_BYTES: &[u8] = b"\x09\x0A\x0D\x20";
1236
1237 loop {
1238 match slice.split_first() {
1239 Some((first, remainder)) if HTTP_WS_BYTES.contains(first) => slice = remainder,
1240 _ => break,
1241 }
1242 }
1243
1244 loop {
1245 match slice.split_last() {
1246 Some((last, remainder)) if HTTP_WS_BYTES.contains(last) => slice = remainder,
1247 _ => break,
1248 }
1249 }
1250
1251 slice
1252}
1253
1254pub fn http_percent_encode(bytes: &[u8]) -> String {
1255 const HTTP_VALUE: &percent_encoding::AsciiSet = &percent_encoding::CONTROLS
1258 .add(b' ')
1259 .add(b'"')
1260 .add(b'%')
1261 .add(b'\'')
1262 .add(b'(')
1263 .add(b')')
1264 .add(b'*')
1265 .add(b',')
1266 .add(b'/')
1267 .add(b':')
1268 .add(b';')
1269 .add(b'<')
1270 .add(b'-')
1271 .add(b'>')
1272 .add(b'?')
1273 .add(b'[')
1274 .add(b'\\')
1275 .add(b']')
1276 .add(b'{')
1277 .add(b'}');
1278
1279 percent_encoding::percent_encode(bytes, HTTP_VALUE).to_string()
1280}
1281
1282pub fn get_current_locale() -> &'static (String, HeaderValue) {
1284 static CURRENT_LOCALE: OnceLock<(String, HeaderValue)> = OnceLock::new();
1285
1286 CURRENT_LOCALE.get_or_init(|| {
1287 let locale_override = servo_config::pref!(intl_locale_override);
1288 let locale = if locale_override.is_empty() {
1289 sys_locale::get_locale().unwrap_or_else(|| "en-US".into())
1290 } else {
1291 locale_override
1292 };
1293 let header_value = HeaderValue::from_str(&locale)
1294 .ok()
1295 .unwrap_or_else(|| HeaderValue::from_static("en-US"));
1296 (locale, header_value)
1297 })
1298}
1299
1300pub fn set_default_accept_language(headers: &mut HeaderMap) {
1302 if headers.contains_key(header::ACCEPT_LANGUAGE) {
1305 return;
1306 }
1307
1308 headers.insert(header::ACCEPT_LANGUAGE, get_current_locale().1.clone());
1310}
1311
1312pub static PRIVILEGED_SECRET: LazyLock<u32> = LazyLock::new(|| rng().next_u32());