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 malloc_size_of::malloc_size_of_is_0;
20use malloc_size_of_derive::MallocSizeOf;
21use mime::Mime;
22use parking_lot::RwLock;
23use profile_traits::mem::ReportsChan;
24use rand::{Rng, 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::{Request, RequestBuilder};
46use crate::response::{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 if value.eq_ignore_ascii_case("never") {
154 ReferrerPolicy::NoReferrer
155 } else if value.eq_ignore_ascii_case("default") {
156 ReferrerPolicy::StrictOriginWhenCrossOrigin
157 } else if value.eq_ignore_ascii_case("always") {
158 ReferrerPolicy::UnsafeUrl
159 } else if value.eq_ignore_ascii_case("origin-when-crossorigin") {
160 ReferrerPolicy::OriginWhenCrossOrigin
161 } else {
162 ReferrerPolicy::from(value)
163 }
164 }
165
166 pub fn parse_header_for_response(headers: &Option<Serde<HeaderMap>>) -> Self {
168 headers
170 .as_ref()
171 .and_then(|headers| headers.typed_get::<ReferrerPolicyHeader>())
173 .into()
175 }
176}
177
178impl From<&str> for ReferrerPolicy {
179 fn from(value: &str) -> Self {
181 if value.eq_ignore_ascii_case("no-referrer") {
182 ReferrerPolicy::NoReferrer
183 } else if value.eq_ignore_ascii_case("no-referrer-when-downgrade") {
184 ReferrerPolicy::NoReferrerWhenDowngrade
185 } else if value.eq_ignore_ascii_case("origin") {
186 ReferrerPolicy::Origin
187 } else if value.eq_ignore_ascii_case("same-origin") {
188 ReferrerPolicy::SameOrigin
189 } else if value.eq_ignore_ascii_case("strict-origin") {
190 ReferrerPolicy::StrictOrigin
191 } else if value.eq_ignore_ascii_case("strict-origin-when-cross-origin") {
192 ReferrerPolicy::StrictOriginWhenCrossOrigin
193 } else if value.eq_ignore_ascii_case("origin-when-cross-origin") {
194 ReferrerPolicy::OriginWhenCrossOrigin
195 } else if value.eq_ignore_ascii_case("unsafe-url") {
196 ReferrerPolicy::UnsafeUrl
197 } else {
198 ReferrerPolicy::EmptyString
199 }
200 }
201}
202
203impl Display for ReferrerPolicy {
204 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
205 let string = match self {
206 ReferrerPolicy::EmptyString => "",
207 ReferrerPolicy::NoReferrer => "no-referrer",
208 ReferrerPolicy::NoReferrerWhenDowngrade => "no-referrer-when-downgrade",
209 ReferrerPolicy::Origin => "origin",
210 ReferrerPolicy::SameOrigin => "same-origin",
211 ReferrerPolicy::OriginWhenCrossOrigin => "origin-when-cross-origin",
212 ReferrerPolicy::UnsafeUrl => "unsafe-url",
213 ReferrerPolicy::StrictOrigin => "strict-origin",
214 ReferrerPolicy::StrictOriginWhenCrossOrigin => "strict-origin-when-cross-origin",
215 };
216 write!(formatter, "{string}")
217 }
218}
219
220impl From<Option<ReferrerPolicyHeader>> for ReferrerPolicy {
222 fn from(header: Option<ReferrerPolicyHeader>) -> Self {
223 header.map_or(ReferrerPolicy::EmptyString, |policy| match policy {
226 ReferrerPolicyHeader::NO_REFERRER => ReferrerPolicy::NoReferrer,
227 ReferrerPolicyHeader::NO_REFERRER_WHEN_DOWNGRADE => {
228 ReferrerPolicy::NoReferrerWhenDowngrade
229 },
230 ReferrerPolicyHeader::SAME_ORIGIN => ReferrerPolicy::SameOrigin,
231 ReferrerPolicyHeader::ORIGIN => ReferrerPolicy::Origin,
232 ReferrerPolicyHeader::ORIGIN_WHEN_CROSS_ORIGIN => ReferrerPolicy::OriginWhenCrossOrigin,
233 ReferrerPolicyHeader::UNSAFE_URL => ReferrerPolicy::UnsafeUrl,
234 ReferrerPolicyHeader::STRICT_ORIGIN => ReferrerPolicy::StrictOrigin,
235 ReferrerPolicyHeader::STRICT_ORIGIN_WHEN_CROSS_ORIGIN => {
236 ReferrerPolicy::StrictOriginWhenCrossOrigin
237 },
238 })
239 }
240}
241
242impl From<ReferrerPolicy> for ReferrerPolicyHeader {
243 fn from(referrer_policy: ReferrerPolicy) -> Self {
244 match referrer_policy {
245 ReferrerPolicy::NoReferrer => ReferrerPolicyHeader::NO_REFERRER,
246 ReferrerPolicy::NoReferrerWhenDowngrade => {
247 ReferrerPolicyHeader::NO_REFERRER_WHEN_DOWNGRADE
248 },
249 ReferrerPolicy::SameOrigin => ReferrerPolicyHeader::SAME_ORIGIN,
250 ReferrerPolicy::Origin => ReferrerPolicyHeader::ORIGIN,
251 ReferrerPolicy::OriginWhenCrossOrigin => ReferrerPolicyHeader::ORIGIN_WHEN_CROSS_ORIGIN,
252 ReferrerPolicy::UnsafeUrl => ReferrerPolicyHeader::UNSAFE_URL,
253 ReferrerPolicy::StrictOrigin => ReferrerPolicyHeader::STRICT_ORIGIN,
254 ReferrerPolicy::EmptyString | ReferrerPolicy::StrictOriginWhenCrossOrigin => {
255 ReferrerPolicyHeader::STRICT_ORIGIN_WHEN_CROSS_ORIGIN
256 },
257 }
258 }
259}
260
261#[expect(clippy::large_enum_variant)]
263#[derive(Debug, Deserialize, Serialize)]
264pub enum FetchResponseMsg {
265 ProcessRequestBody(RequestId),
267 ProcessResponse(RequestId, Result<FetchMetadata, NetworkError>),
269 ProcessResponseChunk(RequestId, DebugVec),
270 ProcessResponseEOF(RequestId, Result<(), NetworkError>, ResourceFetchTiming),
271 ProcessCspViolations(RequestId, Vec<csp::Violation>),
272 ProcessContentLength(RequestId, usize),
273}
274
275#[derive(Deserialize, PartialEq, Serialize, MallocSizeOf)]
276pub struct DebugVec(pub Vec<u8>);
277
278impl From<Vec<u8>> for DebugVec {
279 fn from(v: Vec<u8>) -> Self {
280 Self(v)
281 }
282}
283
284impl std::ops::Deref for DebugVec {
285 type Target = Vec<u8>;
286 fn deref(&self) -> &Self::Target {
287 &self.0
288 }
289}
290
291impl std::fmt::Debug for DebugVec {
292 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
293 f.write_fmt(format_args!("[...; {}]", self.0.len()))
294 }
295}
296
297impl FetchResponseMsg {
298 pub fn request_id(&self) -> RequestId {
299 match self {
300 FetchResponseMsg::ProcessRequestBody(id) |
301 FetchResponseMsg::ProcessResponse(id, ..) |
302 FetchResponseMsg::ProcessResponseChunk(id, ..) |
303 FetchResponseMsg::ProcessResponseEOF(id, ..) |
304 FetchResponseMsg::ProcessCspViolations(id, ..) |
305 FetchResponseMsg::ProcessContentLength(id, _) => *id,
306 }
307 }
308}
309
310pub trait FetchTaskTarget {
311 fn process_request_body(&mut self, request: &Request);
315
316 fn process_response(&mut self, request: &Request, response: &Response);
320
321 fn process_response_chunk(&mut self, request: &Request, chunk: Vec<u8>);
323
324 fn process_response_eof(&mut self, request: &Request, response: &Response);
328
329 fn process_csp_violations(&mut self, request: &Request, violations: Vec<csp::Violation>);
330
331 fn process_response_length_hint(&mut self, request_id: &Request, length: usize);
333}
334
335#[derive(Clone, Debug, Deserialize, Serialize)]
336pub enum FilteredMetadata {
337 Basic(Metadata),
338 Cors(Metadata),
339 Opaque,
340 OpaqueRedirect(ServoUrl),
341}
342
343#[expect(clippy::large_enum_variant)]
345#[derive(Clone, Debug, Deserialize, Serialize)]
346pub enum FetchMetadata {
347 Unfiltered(Metadata),
348 Filtered {
349 filtered: FilteredMetadata,
350 unsafe_: Metadata,
351 },
352}
353
354impl FetchMetadata {
355 pub fn metadata(&self) -> &Metadata {
356 match self {
357 Self::Unfiltered(metadata) => metadata,
358 Self::Filtered { unsafe_, .. } => unsafe_,
359 }
360 }
361
362 pub fn is_cors_cross_origin(&self) -> bool {
364 if let Self::Filtered { filtered, .. } = self {
365 match filtered {
366 FilteredMetadata::Basic(_) | FilteredMetadata::Cors(_) => false,
367 FilteredMetadata::Opaque | FilteredMetadata::OpaqueRedirect(_) => true,
368 }
369 } else {
370 false
371 }
372 }
373}
374
375impl FetchTaskTarget for GenericCallback<FetchResponseMsg> {
376 fn process_request_body(&mut self, request: &Request) {
377 let _ = self.send(FetchResponseMsg::ProcessRequestBody(request.id));
378 }
379
380 fn process_response(&mut self, request: &Request, response: &Response) {
381 let _ = self.send(FetchResponseMsg::ProcessResponse(
382 request.id,
383 response.metadata(),
384 ));
385 }
386
387 fn process_response_chunk(&mut self, request: &Request, chunk: Vec<u8>) {
388 let _ = self.send(FetchResponseMsg::ProcessResponseChunk(
389 request.id,
390 chunk.into(),
391 ));
392 }
393
394 fn process_response_eof(&mut self, request: &Request, response: &Response) {
395 let result = response
396 .get_network_error()
397 .map_or_else(|| Ok(()), |network_error| Err(network_error.clone()));
398 let timing = response.get_resource_timing().inner().clone();
399
400 let _ = self.send(FetchResponseMsg::ProcessResponseEOF(
401 request.id, result, timing,
402 ));
403 }
404
405 fn process_csp_violations(&mut self, request: &Request, violations: Vec<csp::Violation>) {
406 let _ = self.send(FetchResponseMsg::ProcessCspViolations(
407 request.id, violations,
408 ));
409 }
410
411 fn process_response_length_hint(&mut self, request: &Request, length: usize) {
412 let _ = self.send(FetchResponseMsg::ProcessContentLength(request.id, length));
413 }
414}
415
416#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize)]
417#[serde(rename_all = "lowercase")]
418pub enum TlsSecurityState {
419 #[default]
421 Insecure,
422 Weak,
424 Broken,
426 Secure,
428}
429
430impl Display for TlsSecurityState {
431 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
432 let text = match self {
433 TlsSecurityState::Insecure => "insecure",
434 TlsSecurityState::Weak => "weak",
435 TlsSecurityState::Broken => "broken",
436 TlsSecurityState::Secure => "secure",
437 };
438 f.write_str(text)
439 }
440}
441
442#[derive(Clone, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize)]
443pub struct TlsSecurityInfo {
444 #[serde(default)]
446 pub state: TlsSecurityState,
447 pub weakness_reasons: Vec<String>,
449 pub protocol_version: Option<String>,
451 pub cipher_suite: Option<String>,
453 pub kea_group_name: Option<String>,
455 pub signature_scheme_name: Option<String>,
457 pub alpn_protocol: Option<String>,
459 pub certificate_chain_der: Vec<Vec<u8>>,
461 pub certificate_transparency: Option<String>,
463 pub hsts: bool,
465 pub hpkp: bool,
467 pub used_ech: bool,
469 pub used_delegated_credentials: bool,
471 pub used_ocsp: bool,
473 pub used_private_dns: bool,
475}
476
477impl FetchTaskTarget for IpcSender<WebSocketNetworkEvent> {
478 fn process_request_body(&mut self, _: &Request) {}
479 fn process_response(&mut self, _: &Request, response: &Response) {
480 if response.is_network_error() {
481 let _ = self.send(WebSocketNetworkEvent::Fail);
482 }
483 }
484 fn process_response_chunk(&mut self, _: &Request, _: Vec<u8>) {}
485 fn process_response_eof(&mut self, _: &Request, _: &Response) {}
486 fn process_csp_violations(&mut self, _: &Request, violations: Vec<csp::Violation>) {
487 let _ = self.send(WebSocketNetworkEvent::ReportCSPViolations(violations));
488 }
489 fn process_response_length_hint(&mut self, _: &Request, _: usize) {}
490}
491
492pub struct DiscardFetch;
496
497impl FetchTaskTarget for DiscardFetch {
498 fn process_request_body(&mut self, _: &Request) {}
499 fn process_response(&mut self, _: &Request, _: &Response) {}
500 fn process_response_chunk(&mut self, _: &Request, _: Vec<u8>) {}
501 fn process_response_eof(&mut self, _: &Request, _: &Response) {}
502 fn process_csp_violations(&mut self, _: &Request, _: Vec<csp::Violation>) {}
503 fn process_response_length_hint(&mut self, _: &Request, _: usize) {}
504}
505
506pub trait AsyncRuntime: Send {
509 fn shutdown(&mut self);
510}
511
512pub type CoreResourceThread = GenericSender<CoreResourceMsg>;
514
515#[derive(Clone, Debug, Deserialize, Serialize)]
521pub struct ResourceThreads {
522 pub core_thread: CoreResourceThread,
523}
524
525impl ResourceThreads {
526 pub fn new(core_thread: CoreResourceThread) -> ResourceThreads {
527 ResourceThreads { core_thread }
528 }
529
530 pub fn cache_entries(&self) -> Vec<CacheEntryDescriptor> {
531 let (sender, receiver) = generic_channel::channel().unwrap();
532 let _ = self
533 .core_thread
534 .send(CoreResourceMsg::GetCacheEntries(sender));
535 receiver.recv().unwrap()
536 }
537
538 pub fn clear_cache(&self) {
539 let (sender, receiver) = generic_channel::channel().unwrap();
546 let _ = self
547 .core_thread
548 .send(CoreResourceMsg::ClearCache(Some(sender)));
549 let _ = receiver.recv();
550 }
551
552 pub fn cookies(&self) -> Vec<SiteDescriptor> {
553 let (sender, receiver) = generic_channel::channel().unwrap();
554 let _ = self.core_thread.send(CoreResourceMsg::ListCookies(sender));
555 receiver.recv().unwrap()
556 }
557
558 pub fn clear_cookies_for_sites(&self, sites: &[&str]) {
559 let sites = sites.iter().map(|site| site.to_string()).collect();
560 let (sender, receiver) = generic_channel::channel().unwrap();
561 let _ = self
562 .core_thread
563 .send(CoreResourceMsg::DeleteCookiesForSites(sites, sender));
564 let _ = receiver.recv();
565 }
566
567 pub fn clear_cookies(&self) {
568 let (sender, receiver) = ipc::channel().unwrap();
569 let _ = self
570 .core_thread
571 .send(CoreResourceMsg::DeleteCookies(None, Some(sender)));
572 let _ = receiver.recv();
573 }
574
575 pub fn cookies_for_url(&self, url: ServoUrl, source: CookieSource) -> Vec<Cookie<'static>> {
576 let (sender, receiver) = generic_channel::channel().unwrap();
577 let _ = self
578 .core_thread
579 .send(CoreResourceMsg::GetCookiesForUrl(url, sender, source));
580 receiver
581 .recv()
582 .unwrap()
583 .into_iter()
584 .map(|cookie| cookie.into_inner())
585 .collect()
586 }
587
588 pub fn clear_session_cookies(&self) {
589 let (sender, receiver) = generic_channel::channel().unwrap();
590 let _ = self
591 .core_thread
592 .send(CoreResourceMsg::DeleteSessionCookies(sender));
593 let _ = receiver.recv();
594 }
595
596 pub fn set_cookie_for_url(&self, url: ServoUrl, cookie: Cookie<'static>, source: CookieSource) {
597 let _ = self.core_thread.send(CoreResourceMsg::SetCookieForUrl(
598 url,
599 Serde(cookie),
600 source,
601 None,
602 ));
603 }
604
605 pub fn set_cookie_for_url_sync(
606 &self,
607 url: ServoUrl,
608 cookie: Cookie<'static>,
609 source: CookieSource,
610 ) {
611 let (sender, receiver) = generic_channel::channel().unwrap();
612 let _ = self.core_thread.send(CoreResourceMsg::SetCookieForUrl(
613 url,
614 Serde(cookie),
615 source,
616 Some(sender),
617 ));
618 let _ = receiver.recv();
619 }
620
621 pub fn cookies_for_url_async(
622 &self,
623 id: CookieOperationId,
624 url: ServoUrl,
625 source: CookieSource,
626 ) {
627 let _ = self
628 .core_thread
629 .send(CoreResourceMsg::EmbedderGetCookiesForUrl(id, url, source));
630 }
631
632 pub fn set_cookie_for_url_async(
633 &self,
634 id: CookieOperationId,
635 url: ServoUrl,
636 cookie: Cookie<'static>,
637 source: CookieSource,
638 ) {
639 let _ = self
640 .core_thread
641 .send(CoreResourceMsg::EmbedderSetCookieForUrl(
642 id,
643 url,
644 Serde(cookie),
645 source,
646 ));
647 }
648
649 pub fn clear_cookies_async(&self, id: CookieOperationId) {
650 let _ = self
651 .core_thread
652 .send(CoreResourceMsg::EmbedderClearCookies(id));
653 }
654
655 pub fn clear_session_cookies_async(&self, id: CookieOperationId) {
656 let _ = self
657 .core_thread
658 .send(CoreResourceMsg::EmbedderClearSessionCookies(id));
659 }
660}
661
662impl GenericSend<CoreResourceMsg> for ResourceThreads {
663 fn send(&self, msg: CoreResourceMsg) -> SendResult {
664 self.core_thread.send(msg)
665 }
666
667 fn sender(&self) -> GenericSender<CoreResourceMsg> {
668 self.core_thread.clone()
669 }
670}
671
672malloc_size_of_is_0!(ResourceThreads);
674
675#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
676pub enum IncludeSubdomains {
677 Included,
678 NotIncluded,
679}
680
681#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
682pub enum MessageData {
683 Text(String),
684 Binary(Vec<u8>),
685}
686
687#[derive(Debug, Deserialize, Serialize, MallocSizeOf)]
688pub enum WebSocketDomAction {
689 SendMessage(MessageData),
690 Close(Option<u16>, Option<String>),
691}
692
693#[derive(Debug, Deserialize, Serialize)]
694pub enum WebSocketNetworkEvent {
695 ReportCSPViolations(Vec<csp::Violation>),
696 ConnectionEstablished { protocol_in_use: Option<String> },
697 MessageReceived(MessageData),
698 Close(Option<u16>, String),
699 Fail,
700}
701
702#[derive(Debug, Deserialize, Serialize)]
703pub enum FetchChannels {
705 ResponseMsg(GenericCallback<FetchResponseMsg>),
706 WebSocket {
707 event_sender: IpcSender<WebSocketNetworkEvent>,
708 action_receiver: CallbackSetter<WebSocketDomAction>,
709 },
710 Prefetch,
713}
714
715#[derive(Debug, Deserialize, Serialize)]
716pub enum CoreResourceMsg {
717 Fetch(RequestBuilder, FetchChannels),
718 Cancel(Vec<RequestId>),
719 FetchRedirect(
721 RequestBuilder,
722 ResponseInit,
723 GenericCallback<FetchResponseMsg>,
724 ),
725 SetCookieForUrl(
728 ServoUrl,
729 Serde<Cookie<'static>>,
730 CookieSource,
731 Option<GenericSender<()>>,
732 ),
733 SetCookiesForUrl(ServoUrl, Vec<Serde<Cookie<'static>>>, CookieSource),
735 SetCookieForUrlAsync(
736 CookieStoreId,
737 ServoUrl,
738 Serde<Cookie<'static>>,
739 CookieSource,
740 ),
741 GetCookieStringForUrl(ServoUrl, GenericSender<Option<String>>, CookieSource),
743 GetCookiesForUrl(
746 ServoUrl,
747 GenericSender<Vec<Serde<Cookie<'static>>>>,
748 CookieSource,
749 ),
750 EmbedderGetCookiesForUrl(CookieOperationId, ServoUrl, CookieSource),
753 EmbedderSetCookieForUrl(
756 CookieOperationId,
757 ServoUrl,
758 Serde<Cookie<'static>>,
759 CookieSource,
760 ),
761 EmbedderClearCookies(CookieOperationId),
763 EmbedderClearSessionCookies(CookieOperationId),
765 GetCookieDataForUrlAsync(CookieStoreId, ServoUrl, Option<String>),
766 GetAllCookieDataForUrlAsync(CookieStoreId, ServoUrl, Option<String>),
767 DeleteCookiesForSites(Vec<String>, GenericSender<()>),
768 DeleteCookies(Option<ServoUrl>, Option<IpcSender<()>>),
771 DeleteSessionCookies(GenericSender<()>),
773 DeleteCookie(ServoUrl, String),
774 DeleteCookieAsync(CookieStoreId, ServoUrl, String),
775 NewCookieListener(
776 CookieStoreId,
777 GenericCallback<CookieAsyncResponse>,
778 ServoUrl,
779 ),
780 RemoveCookieListener(CookieStoreId),
781 ListCookies(GenericSender<Vec<SiteDescriptor>>),
782 GetHistoryState(HistoryStateId, GenericSender<Option<Vec<u8>>>),
784 SetHistoryState(HistoryStateId, Vec<u8>),
786 RemoveHistoryStates(Vec<HistoryStateId>),
788 GetCacheEntries(GenericSender<Vec<CacheEntryDescriptor>>),
790 ClearCache(Option<GenericSender<()>>),
792 NetworkMediator(IpcSender<CustomResponseMediator>, ImmutableOrigin),
794 ToFileManager(FileManagerThreadMsg),
796 TotalSizeOfInFlightKeepAliveRecords(PipelineId, GenericSender<u64>),
797 Exit(GenericOneshotSender<()>),
800 CollectMemoryReport(ReportsChan),
801 RevokeTokenForFile(BlobTokenRevocationRequest),
802 RefreshTokenForFile(BlobTokenRefreshRequest),
803}
804
805#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
806pub struct BlobTokenRevocationRequest {
807 pub blob_id: Uuid,
808 pub token: Uuid,
809}
810
811#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
812pub struct BlobTokenRefreshRequest {
813 pub blob_id: Uuid,
814 pub new_token_sender: GenericSender<Uuid>,
815}
816
817#[derive(Clone, Debug, Deserialize, Serialize)]
818pub struct SiteDescriptor {
819 pub name: String,
820}
821
822impl SiteDescriptor {
823 pub fn new(name: String) -> Self {
824 SiteDescriptor { name }
825 }
826}
827
828#[derive(Clone, Debug, Deserialize, Serialize)]
829pub struct CacheEntryDescriptor {
830 pub key: String,
831}
832
833impl CacheEntryDescriptor {
834 pub fn new(key: String) -> Self {
835 Self { key }
836 }
837}
838
839#[expect(clippy::large_enum_variant)]
841enum ToFetchThreadMessage {
842 Cancel(Vec<RequestId>, CoreResourceThread),
843 StartFetch(
844 RequestBuilder,
845 Option<ResponseInit>,
846 BoxedFetchCallback,
847 CoreResourceThread,
848 ),
849 FetchResponse(FetchResponseMsg),
850 Exit,
852}
853
854pub type BoxedFetchCallback = Box<dyn FnMut(FetchResponseMsg) + Send + 'static>;
855
856pub struct FetchThread {
860 active_fetches: FxHashMap<RequestId, BoxedFetchCallback>,
863 receiver: Receiver<ToFetchThreadMessage>,
867 to_fetch_sender: GenericCallback<FetchResponseMsg>,
870}
871
872impl FetchThread {
873 fn spawn() -> FetchThreadHandle {
874 let (sender, receiver) = unbounded();
875
876 let sender_clone = sender.clone();
877 let to_fetch_sender = GenericCallback::new(move |message| {
878 let message: FetchResponseMsg = message.unwrap();
879 let _ = sender_clone.send(ToFetchThreadMessage::FetchResponse(message));
880 })
881 .expect("Couldn't create fetch callback");
882 let join_handle = thread::Builder::new()
883 .name("FetchThread".to_owned())
884 .spawn(move || {
885 let mut fetch_thread = FetchThread {
886 active_fetches: FxHashMap::default(),
887 receiver,
888 to_fetch_sender,
889 };
890 fetch_thread.run();
891 })
892 .expect("Thread spawning failed");
893 FetchThreadHandle {
894 sender,
895 join_handle: RwLock::new(Some(join_handle)),
896 }
897 }
898
899 fn run(&mut self) {
900 loop {
901 match self.receiver.recv() {
902 Ok(ToFetchThreadMessage::StartFetch(
903 request_builder,
904 response_init,
905 callback,
906 core_resource_thread,
907 )) => {
908 let request_builder_id = request_builder.id;
909
910 let message = match response_init {
912 Some(response_init) => CoreResourceMsg::FetchRedirect(
913 request_builder,
914 response_init,
915 self.to_fetch_sender.clone(),
916 ),
917 None => CoreResourceMsg::Fetch(
918 request_builder,
919 FetchChannels::ResponseMsg(self.to_fetch_sender.clone()),
920 ),
921 };
922
923 if core_resource_thread.send(message).is_err() {
924 break;
928 }
929
930 let preexisting_fetch =
931 self.active_fetches.insert(request_builder_id, callback);
932 assert!(preexisting_fetch.is_none());
936 },
937 Ok(ToFetchThreadMessage::FetchResponse(fetch_response_msg)) => {
938 let request_id = fetch_response_msg.request_id();
939 let fetch_finished =
940 matches!(fetch_response_msg, FetchResponseMsg::ProcessResponseEOF(..));
941
942 self.active_fetches
943 .get_mut(&request_id)
944 .expect("Got fetch response for unknown fetch")(
945 fetch_response_msg
946 );
947
948 if fetch_finished {
949 self.active_fetches.remove(&request_id);
950 }
951 },
952 Ok(ToFetchThreadMessage::Cancel(request_ids, core_resource_thread)) => {
953 let _ = core_resource_thread.send(CoreResourceMsg::Cancel(request_ids));
957 },
958 Ok(ToFetchThreadMessage::Exit) | Err(_) => break,
959 }
960 }
961 }
962
963 fn fetch_async(
964 core_resource_thread: &CoreResourceThread,
965 request: RequestBuilder,
966 response_init: Option<ResponseInit>,
967 callback: BoxedFetchCallback,
968 ) {
969 let _ = FETCH_THREAD.get_or_init(FetchThread::spawn).sender.send(
970 ToFetchThreadMessage::StartFetch(
971 request,
972 response_init,
973 callback,
974 core_resource_thread.clone(),
975 ),
976 );
977 }
978
979 fn cancel_async_fetch(request_ids: Vec<RequestId>, core_resource_thread: &CoreResourceThread) {
980 if let Some(fetch_thread) = FETCH_THREAD.get() {
981 let _ = fetch_thread.sender.send(ToFetchThreadMessage::Cancel(
982 request_ids,
983 core_resource_thread.clone(),
984 ));
985 }
986 }
987
988 pub fn exit() {
990 let Some(fetch_thread) = FETCH_THREAD.get() else {
991 return;
992 };
993 let _ = fetch_thread.sender.send(ToFetchThreadMessage::Exit);
994 if let Some(join_handle) = fetch_thread.join_handle.write().take() {
995 join_handle
996 .join()
997 .expect("Failed to join on the FetchThread join handle.");
998 }
999 }
1000}
1001
1002struct FetchThreadHandle {
1003 sender: Sender<ToFetchThreadMessage>,
1004 join_handle: RwLock<Option<JoinHandle<()>>>,
1005}
1006
1007static FETCH_THREAD: OnceLock<FetchThreadHandle> = OnceLock::new();
1008
1009pub fn fetch_async(
1011 core_resource_thread: &CoreResourceThread,
1012 request: RequestBuilder,
1013 response_init: Option<ResponseInit>,
1014 callback: BoxedFetchCallback,
1015) {
1016 FetchThread::fetch_async(core_resource_thread, request, response_init, callback);
1017}
1018
1019pub fn cancel_async_fetch(request_ids: Vec<RequestId>, core_resource_thread: &CoreResourceThread) {
1022 FetchThread::cancel_async_fetch(request_ids, core_resource_thread);
1023}
1024
1025#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
1026pub struct ResourceCorsData {
1027 pub preflight: bool,
1029 pub origin: ServoUrl,
1031}
1032
1033#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
1035pub struct Metadata {
1036 pub final_url: ServoUrl,
1038
1039 pub location_url: Option<Result<ServoUrl, String>>,
1041
1042 #[ignore_malloc_size_of = "Defined in hyper"]
1043 pub content_type: Option<Serde<ContentType>>,
1045
1046 pub charset: Option<String>,
1048
1049 #[ignore_malloc_size_of = "Defined in hyper"]
1050 pub headers: Option<Serde<HeaderMap>>,
1052
1053 pub status: HttpStatus,
1055
1056 pub referrer: Option<ServoUrl>,
1058
1059 pub referrer_policy: ReferrerPolicy,
1061 pub timing: Option<ResourceFetchTiming>,
1063 pub redirected: bool,
1065 pub tls_security_info: Option<TlsSecurityInfo>,
1067}
1068
1069impl Metadata {
1070 pub fn default(url: ServoUrl) -> Self {
1072 Metadata {
1073 final_url: url,
1074 location_url: None,
1075 content_type: None,
1076 charset: None,
1077 headers: None,
1078 status: HttpStatus::default(),
1079 referrer: None,
1080 referrer_policy: ReferrerPolicy::EmptyString,
1081 timing: None,
1082 redirected: false,
1083 tls_security_info: None,
1084 }
1085 }
1086
1087 pub fn set_content_type(&mut self, content_type: Option<&Mime>) {
1089 if self.headers.is_none() {
1090 self.headers = Some(Serde(HeaderMap::new()));
1091 }
1092
1093 if let Some(mime) = content_type {
1094 self.headers
1095 .as_mut()
1096 .unwrap()
1097 .typed_insert(ContentType::from(mime.clone()));
1098 if let Some(charset) = mime.get_param(mime::CHARSET) {
1099 self.charset = Some(charset.to_string());
1100 }
1101 self.content_type = Some(Serde(ContentType::from(mime.clone())));
1102 }
1103 }
1104
1105 pub fn set_referrer_policy(&mut self, referrer_policy: ReferrerPolicy) {
1107 if referrer_policy == ReferrerPolicy::EmptyString {
1108 return;
1109 }
1110
1111 if self.headers.is_none() {
1112 self.headers = Some(Serde(HeaderMap::new()));
1113 }
1114
1115 self.referrer_policy = referrer_policy;
1116
1117 self.headers
1118 .as_mut()
1119 .unwrap()
1120 .typed_insert::<ReferrerPolicyHeader>(referrer_policy.into());
1121 }
1122
1123 pub fn resource_content_type_metadata(&self, load_context: LoadContext, data: &[u8]) -> Mime {
1125 let no_sniff = self
1127 .headers
1128 .as_deref()
1129 .is_some_and(determine_nosniff)
1130 .into();
1131 let mime = self
1132 .content_type
1133 .clone()
1134 .map(|content_type| content_type.into_inner().into());
1135 MimeClassifier::default().classify(
1136 load_context,
1137 no_sniff,
1138 ApacheBugFlag::from_content_type(mime.as_ref()),
1139 &mime,
1140 data,
1141 )
1142 }
1143}
1144
1145#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
1147pub enum CookieSource {
1148 HTTP,
1150 NonHTTP,
1152}
1153
1154#[derive(Clone, Debug, Deserialize, Serialize)]
1155pub struct CookieChange {
1156 changed: Vec<Serde<Cookie<'static>>>,
1157 deleted: Vec<Serde<Cookie<'static>>>,
1158}
1159
1160#[derive(Clone, Debug, Deserialize, Serialize)]
1161pub enum CookieData {
1162 Change(CookieChange),
1163 Get(Option<Serde<Cookie<'static>>>),
1164 GetAll(Vec<Serde<Cookie<'static>>>),
1165 Set(Result<(), ()>),
1166 Delete(Result<(), ()>),
1167}
1168
1169#[derive(Clone, Debug, Deserialize, Serialize)]
1170pub struct CookieAsyncResponse {
1171 pub data: CookieData,
1172}
1173
1174#[derive(Clone, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)]
1176pub enum NetworkError {
1177 LoadCancelled,
1178 SslValidation(String, Vec<u8>),
1180 Crash(String),
1182 UnsupportedScheme,
1183 CorsGeneral,
1184 CrossOriginResponse,
1185 CorsCredentials,
1186 CorsAllowMethods,
1187 CorsAllowHeaders,
1188 CorsMethod,
1189 CorsAuthorization,
1190 CorsHeaders,
1191 ConnectionFailure,
1192 RedirectError,
1193 TooManyRedirects,
1194 TooManyInFlightKeepAliveRequests,
1195 InvalidMethod,
1196 ResourceLoadError(String),
1197 ContentSecurityPolicy,
1198 Nosniff,
1199 MimeType(String),
1200 SubresourceIntegrity,
1201 MixedContent,
1202 CacheError,
1203 InvalidPort,
1204 WebsocketConnectionFailure(String),
1205 LocalDirectoryError,
1206 PartialResponseToNonRangeRequestError,
1207 ProtocolHandlerSubstitutionError,
1208 BlobURLStoreError(String),
1209 HttpError(String),
1210 DecompressionError,
1211}
1212
1213impl fmt::Debug for NetworkError {
1214 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1215 match self {
1216 NetworkError::UnsupportedScheme => write!(f, "Unsupported scheme"),
1217 NetworkError::CorsGeneral => write!(f, "CORS check failed"),
1218 NetworkError::CrossOriginResponse => write!(f, "Cross-origin response"),
1219 NetworkError::CorsCredentials => write!(f, "Cross-origin credentials check failed"),
1220 NetworkError::CorsAllowMethods => write!(f, "CORS ACAM check failed"),
1221 NetworkError::CorsAllowHeaders => write!(f, "CORS ACAH check failed"),
1222 NetworkError::CorsMethod => write!(f, "CORS method check failed"),
1223 NetworkError::CorsAuthorization => write!(f, "CORS authorization check failed"),
1224 NetworkError::CorsHeaders => write!(f, "CORS headers check failed"),
1225 NetworkError::ConnectionFailure => write!(f, "Request failed"),
1226 NetworkError::RedirectError => write!(f, "Redirect failed"),
1227 NetworkError::TooManyRedirects => write!(f, "Too many redirects"),
1228 NetworkError::TooManyInFlightKeepAliveRequests => {
1229 write!(f, "Too many in flight keep-alive requests")
1230 },
1231 NetworkError::InvalidMethod => write!(f, "Unexpected method"),
1232 NetworkError::ResourceLoadError(s) => write!(f, "{}", s),
1233 NetworkError::ContentSecurityPolicy => write!(f, "Blocked by Content-Security-Policy"),
1234 NetworkError::Nosniff => write!(f, "Blocked by nosniff"),
1235 NetworkError::MimeType(s) => write!(f, "{}", s),
1236 NetworkError::SubresourceIntegrity => {
1237 write!(f, "Subresource integrity validation failed")
1238 },
1239 NetworkError::MixedContent => write!(f, "Blocked as mixed content"),
1240 NetworkError::CacheError => write!(f, "Couldn't find response in cache"),
1241 NetworkError::InvalidPort => write!(f, "Request attempted on bad port"),
1242 NetworkError::LocalDirectoryError => write!(f, "Local directory access failed"),
1243 NetworkError::LoadCancelled => write!(f, "Load cancelled"),
1244 NetworkError::SslValidation(s, _) => write!(f, "SSL validation error: {}", s),
1245 NetworkError::Crash(s) => write!(f, "Crash: {}", s),
1246 NetworkError::PartialResponseToNonRangeRequestError => write!(
1247 f,
1248 "Refusing to provide partial response from earlier ranged request to API that did not make a range request"
1249 ),
1250 NetworkError::ProtocolHandlerSubstitutionError => {
1251 write!(f, "Failed to parse substituted protocol handler url")
1252 },
1253 NetworkError::BlobURLStoreError(s) => write!(f, "Blob URL store error: {}", s),
1254 NetworkError::WebsocketConnectionFailure(s) => {
1255 write!(f, "Websocket connection failure: {}", s)
1256 },
1257 NetworkError::HttpError(s) => write!(f, "HTTP failure: {}", s),
1258 NetworkError::DecompressionError => write!(f, "Decompression error"),
1259 }
1260 }
1261}
1262
1263impl NetworkError {
1264 pub fn is_permanent_failure(&self) -> bool {
1265 matches!(
1266 self,
1267 NetworkError::ContentSecurityPolicy |
1268 NetworkError::MixedContent |
1269 NetworkError::SubresourceIntegrity |
1270 NetworkError::Nosniff |
1271 NetworkError::InvalidPort |
1272 NetworkError::CorsGeneral |
1273 NetworkError::CrossOriginResponse |
1274 NetworkError::CorsCredentials |
1275 NetworkError::CorsAllowMethods |
1276 NetworkError::CorsAllowHeaders |
1277 NetworkError::CorsMethod |
1278 NetworkError::CorsAuthorization |
1279 NetworkError::CorsHeaders |
1280 NetworkError::UnsupportedScheme
1281 )
1282 }
1283
1284 pub fn from_hyper_error(error: &HyperError, certificate: Option<CertificateDer>) -> Self {
1285 let error_string = error.to_string();
1286 match certificate {
1287 Some(certificate) => NetworkError::SslValidation(error_string, certificate.to_vec()),
1288 _ => NetworkError::HttpError(error_string),
1289 }
1290 }
1291}
1292
1293pub fn trim_http_whitespace(mut slice: &[u8]) -> &[u8] {
1296 const HTTP_WS_BYTES: &[u8] = b"\x09\x0A\x0D\x20";
1297
1298 loop {
1299 match slice.split_first() {
1300 Some((first, remainder)) if HTTP_WS_BYTES.contains(first) => slice = remainder,
1301 _ => break,
1302 }
1303 }
1304
1305 loop {
1306 match slice.split_last() {
1307 Some((last, remainder)) if HTTP_WS_BYTES.contains(last) => slice = remainder,
1308 _ => break,
1309 }
1310 }
1311
1312 slice
1313}
1314
1315pub fn get_current_locale() -> &'static (String, HeaderValue) {
1317 static CURRENT_LOCALE: OnceLock<(String, HeaderValue)> = OnceLock::new();
1318
1319 CURRENT_LOCALE.get_or_init(|| {
1320 let locale_override = servo_config::pref!(intl_locale_override);
1321 let locale = if locale_override.is_empty() {
1322 sys_locale::get_locale().unwrap_or_else(|| "en-US".into())
1323 } else {
1324 locale_override
1325 };
1326 let header_value = HeaderValue::from_str(&locale)
1327 .ok()
1328 .unwrap_or_else(|| HeaderValue::from_static("en-US"));
1329 (locale, header_value)
1330 })
1331}
1332
1333pub fn set_default_accept_language(headers: &mut HeaderMap) {
1335 if headers.contains_key(header::ACCEPT_LANGUAGE) {
1338 return;
1339 }
1340
1341 headers.insert(header::ACCEPT_LANGUAGE, get_current_locale().1.clone());
1343}
1344
1345pub static PRIVILEGED_SECRET: LazyLock<u32> = LazyLock::new(|| rng().next_u32());