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