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 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 ProcessContentLength(RequestId, usize),
261}
262
263#[derive(Deserialize, PartialEq, Serialize, MallocSizeOf)]
264pub struct DebugVec(pub Vec<u8>);
265
266impl From<Vec<u8>> for DebugVec {
267 fn from(v: Vec<u8>) -> Self {
268 Self(v)
269 }
270}
271
272impl std::ops::Deref for DebugVec {
273 type Target = Vec<u8>;
274 fn deref(&self) -> &Self::Target {
275 &self.0
276 }
277}
278
279impl std::fmt::Debug for DebugVec {
280 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
281 f.write_fmt(format_args!("[...; {}]", self.0.len()))
282 }
283}
284
285impl FetchResponseMsg {
286 pub fn request_id(&self) -> RequestId {
287 match self {
288 FetchResponseMsg::ProcessRequestBody(id) |
289 FetchResponseMsg::ProcessResponse(id, ..) |
290 FetchResponseMsg::ProcessResponseChunk(id, ..) |
291 FetchResponseMsg::ProcessResponseEOF(id, ..) |
292 FetchResponseMsg::ProcessCspViolations(id, ..) |
293 FetchResponseMsg::ProcessContentLength(id, _) => *id,
294 }
295 }
296}
297
298pub trait FetchTaskTarget {
299 fn process_request_body(&mut self, request: &Request);
303
304 fn process_response(&mut self, request: &Request, response: &Response);
308
309 fn process_response_chunk(&mut self, request: &Request, chunk: Vec<u8>);
311
312 fn process_response_eof(&mut self, request: &Request, response: &Response);
316
317 fn process_csp_violations(&mut self, request: &Request, violations: Vec<csp::Violation>);
318
319 fn process_response_length_hint(&mut self, request_id: &Request, length: usize);
321}
322
323#[derive(Clone, Debug, Deserialize, Serialize)]
324pub enum FilteredMetadata {
325 Basic(Metadata),
326 Cors(Metadata),
327 Opaque,
328 OpaqueRedirect(ServoUrl),
329}
330
331#[expect(clippy::large_enum_variant)]
333#[derive(Clone, Debug, Deserialize, Serialize)]
334pub enum FetchMetadata {
335 Unfiltered(Metadata),
336 Filtered {
337 filtered: FilteredMetadata,
338 unsafe_: Metadata,
339 },
340}
341
342impl FetchMetadata {
343 pub fn metadata(&self) -> &Metadata {
344 match self {
345 Self::Unfiltered(metadata) => metadata,
346 Self::Filtered { unsafe_, .. } => unsafe_,
347 }
348 }
349
350 pub fn is_cors_cross_origin(&self) -> bool {
352 if let Self::Filtered { filtered, .. } = self {
353 match filtered {
354 FilteredMetadata::Basic(_) | FilteredMetadata::Cors(_) => false,
355 FilteredMetadata::Opaque | FilteredMetadata::OpaqueRedirect(_) => true,
356 }
357 } else {
358 false
359 }
360 }
361}
362
363impl FetchTaskTarget for GenericCallback<FetchResponseMsg> {
364 fn process_request_body(&mut self, request: &Request) {
365 let _ = self.send(FetchResponseMsg::ProcessRequestBody(request.id));
366 }
367
368 fn process_response(&mut self, request: &Request, response: &Response) {
369 let _ = self.send(FetchResponseMsg::ProcessResponse(
370 request.id,
371 response.metadata(),
372 ));
373 }
374
375 fn process_response_chunk(&mut self, request: &Request, chunk: Vec<u8>) {
376 let _ = self.send(FetchResponseMsg::ProcessResponseChunk(
377 request.id,
378 chunk.into(),
379 ));
380 }
381
382 fn process_response_eof(&mut self, request: &Request, response: &Response) {
383 let result = response
384 .get_network_error()
385 .map_or_else(|| Ok(()), |network_error| Err(network_error.clone()));
386 let timing = response.get_resource_timing().inner().clone();
387
388 let _ = self.send(FetchResponseMsg::ProcessResponseEOF(
389 request.id, result, timing,
390 ));
391 }
392
393 fn process_csp_violations(&mut self, request: &Request, violations: Vec<csp::Violation>) {
394 let _ = self.send(FetchResponseMsg::ProcessCspViolations(
395 request.id, violations,
396 ));
397 }
398
399 fn process_response_length_hint(&mut self, request: &Request, length: usize) {
400 let _ = self.send(FetchResponseMsg::ProcessContentLength(request.id, length));
401 }
402}
403
404#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize)]
405#[serde(rename_all = "lowercase")]
406pub enum TlsSecurityState {
407 #[default]
409 Insecure,
410 Weak,
412 Broken,
414 Secure,
416}
417
418impl Display for TlsSecurityState {
419 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
420 let text = match self {
421 TlsSecurityState::Insecure => "insecure",
422 TlsSecurityState::Weak => "weak",
423 TlsSecurityState::Broken => "broken",
424 TlsSecurityState::Secure => "secure",
425 };
426 f.write_str(text)
427 }
428}
429
430#[derive(Clone, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize)]
431pub struct TlsSecurityInfo {
432 #[serde(default)]
434 pub state: TlsSecurityState,
435 pub weakness_reasons: Vec<String>,
437 pub protocol_version: Option<String>,
439 pub cipher_suite: Option<String>,
441 pub kea_group_name: Option<String>,
443 pub signature_scheme_name: Option<String>,
445 pub alpn_protocol: Option<String>,
447 pub certificate_chain_der: Vec<Vec<u8>>,
449 pub certificate_transparency: Option<String>,
451 pub hsts: bool,
453 pub hpkp: bool,
455 pub used_ech: bool,
457 pub used_delegated_credentials: bool,
459 pub used_ocsp: bool,
461 pub used_private_dns: bool,
463}
464
465impl FetchTaskTarget for IpcSender<WebSocketNetworkEvent> {
466 fn process_request_body(&mut self, _: &Request) {}
467 fn process_response(&mut self, _: &Request, response: &Response) {
468 if response.is_network_error() {
469 let _ = self.send(WebSocketNetworkEvent::Fail);
470 }
471 }
472 fn process_response_chunk(&mut self, _: &Request, _: Vec<u8>) {}
473 fn process_response_eof(&mut self, _: &Request, _: &Response) {}
474 fn process_csp_violations(&mut self, _: &Request, violations: Vec<csp::Violation>) {
475 let _ = self.send(WebSocketNetworkEvent::ReportCSPViolations(violations));
476 }
477 fn process_response_length_hint(&mut self, _: &Request, _: usize) {}
478}
479
480pub struct DiscardFetch;
484
485impl FetchTaskTarget for DiscardFetch {
486 fn process_request_body(&mut self, _: &Request) {}
487 fn process_response(&mut self, _: &Request, _: &Response) {}
488 fn process_response_chunk(&mut self, _: &Request, _: Vec<u8>) {}
489 fn process_response_eof(&mut self, _: &Request, _: &Response) {}
490 fn process_csp_violations(&mut self, _: &Request, _: Vec<csp::Violation>) {}
491 fn process_response_length_hint(&mut self, _: &Request, _: usize) {}
492}
493
494pub trait AsyncRuntime: Send {
497 fn shutdown(&mut self);
498}
499
500pub type CoreResourceThread = GenericSender<CoreResourceMsg>;
502
503#[derive(Clone, Debug, Deserialize, Serialize)]
509pub struct ResourceThreads {
510 pub core_thread: CoreResourceThread,
511}
512
513impl ResourceThreads {
514 pub fn new(core_thread: CoreResourceThread) -> ResourceThreads {
515 ResourceThreads { core_thread }
516 }
517
518 pub fn cache_entries(&self) -> Vec<CacheEntryDescriptor> {
519 let (sender, receiver) = generic_channel::channel().unwrap();
520 let _ = self
521 .core_thread
522 .send(CoreResourceMsg::GetCacheEntries(sender));
523 receiver.recv().unwrap()
524 }
525
526 pub fn clear_cache(&self) {
527 let (sender, receiver) = generic_channel::channel().unwrap();
534 let _ = self
535 .core_thread
536 .send(CoreResourceMsg::ClearCache(Some(sender)));
537 let _ = receiver.recv();
538 }
539
540 pub fn cookies(&self) -> Vec<SiteDescriptor> {
541 let (sender, receiver) = generic_channel::channel().unwrap();
542 let _ = self.core_thread.send(CoreResourceMsg::ListCookies(sender));
543 receiver.recv().unwrap()
544 }
545
546 pub fn clear_cookies_for_sites(&self, sites: &[&str]) {
547 let sites = sites.iter().map(|site| site.to_string()).collect();
548 let (sender, receiver) = generic_channel::channel().unwrap();
549 let _ = self
550 .core_thread
551 .send(CoreResourceMsg::DeleteCookiesForSites(sites, sender));
552 let _ = receiver.recv();
553 }
554
555 pub fn clear_cookies(&self) {
556 let (sender, receiver) = ipc::channel().unwrap();
557 let _ = self
558 .core_thread
559 .send(CoreResourceMsg::DeleteCookies(None, Some(sender)));
560 let _ = receiver.recv();
561 }
562
563 pub fn cookies_for_url(&self, url: ServoUrl, source: CookieSource) -> Vec<Cookie<'static>> {
564 let (sender, receiver) = generic_channel::channel().unwrap();
565 let _ = self
566 .core_thread
567 .send(CoreResourceMsg::GetCookiesForUrl(url, sender, source));
568 receiver
569 .recv()
570 .unwrap()
571 .into_iter()
572 .map(|cookie| cookie.into_inner())
573 .collect()
574 }
575
576 pub fn clear_session_cookies(&self) {
577 let (sender, receiver) = generic_channel::channel().unwrap();
578 let _ = self
579 .core_thread
580 .send(CoreResourceMsg::DeleteSessionCookies(sender));
581 let _ = receiver.recv();
582 }
583
584 pub fn set_cookie_for_url(&self, url: ServoUrl, cookie: Cookie<'static>, source: CookieSource) {
585 let _ = self.core_thread.send(CoreResourceMsg::SetCookieForUrl(
586 url,
587 Serde(cookie),
588 source,
589 None,
590 ));
591 }
592
593 pub fn set_cookie_for_url_sync(
594 &self,
595 url: ServoUrl,
596 cookie: Cookie<'static>,
597 source: CookieSource,
598 ) {
599 let (sender, receiver) = generic_channel::channel().unwrap();
600 let _ = self.core_thread.send(CoreResourceMsg::SetCookieForUrl(
601 url,
602 Serde(cookie),
603 source,
604 Some(sender),
605 ));
606 let _ = receiver.recv();
607 }
608
609 pub fn cookies_for_url_async(
610 &self,
611 id: CookieOperationId,
612 url: ServoUrl,
613 source: CookieSource,
614 ) {
615 let _ = self
616 .core_thread
617 .send(CoreResourceMsg::EmbedderGetCookiesForUrl(id, url, source));
618 }
619
620 pub fn set_cookie_for_url_async(
621 &self,
622 id: CookieOperationId,
623 url: ServoUrl,
624 cookie: Cookie<'static>,
625 source: CookieSource,
626 ) {
627 let _ = self
628 .core_thread
629 .send(CoreResourceMsg::EmbedderSetCookieForUrl(
630 id,
631 url,
632 Serde(cookie),
633 source,
634 ));
635 }
636
637 pub fn clear_cookies_async(&self, id: CookieOperationId) {
638 let _ = self
639 .core_thread
640 .send(CoreResourceMsg::EmbedderClearCookies(id));
641 }
642
643 pub fn clear_session_cookies_async(&self, id: CookieOperationId) {
644 let _ = self
645 .core_thread
646 .send(CoreResourceMsg::EmbedderClearSessionCookies(id));
647 }
648}
649
650impl GenericSend<CoreResourceMsg> for ResourceThreads {
651 fn send(&self, msg: CoreResourceMsg) -> SendResult {
652 self.core_thread.send(msg)
653 }
654
655 fn sender(&self) -> GenericSender<CoreResourceMsg> {
656 self.core_thread.clone()
657 }
658}
659
660malloc_size_of_is_0!(ResourceThreads);
662
663#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
664pub enum IncludeSubdomains {
665 Included,
666 NotIncluded,
667}
668
669#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
670pub enum MessageData {
671 Text(String),
672 Binary(Vec<u8>),
673}
674
675#[derive(Debug, Deserialize, Serialize, MallocSizeOf)]
676pub enum WebSocketDomAction {
677 SendMessage(MessageData),
678 Close(Option<u16>, Option<String>),
679}
680
681#[derive(Debug, Deserialize, Serialize)]
682pub enum WebSocketNetworkEvent {
683 ReportCSPViolations(Vec<csp::Violation>),
684 ConnectionEstablished { protocol_in_use: Option<String> },
685 MessageReceived(MessageData),
686 Close(Option<u16>, String),
687 Fail,
688}
689
690#[derive(Debug, Deserialize, Serialize)]
691pub enum FetchChannels {
693 ResponseMsg(GenericCallback<FetchResponseMsg>),
694 WebSocket {
695 event_sender: IpcSender<WebSocketNetworkEvent>,
696 action_receiver: CallbackSetter<WebSocketDomAction>,
697 },
698 Prefetch,
701}
702
703#[derive(Debug, Deserialize, Serialize)]
704pub enum CoreResourceMsg {
705 Fetch(RequestBuilder, FetchChannels),
706 Cancel(Vec<RequestId>),
707 FetchRedirect(
709 RequestBuilder,
710 ResponseInit,
711 GenericCallback<FetchResponseMsg>,
712 ),
713 SetCookieForUrl(
716 ServoUrl,
717 Serde<Cookie<'static>>,
718 CookieSource,
719 Option<GenericSender<()>>,
720 ),
721 SetCookiesForUrl(ServoUrl, Vec<Serde<Cookie<'static>>>, CookieSource),
723 SetCookieForUrlAsync(
724 CookieStoreId,
725 ServoUrl,
726 Serde<Cookie<'static>>,
727 CookieSource,
728 ),
729 GetCookieStringForUrl(ServoUrl, GenericSender<Option<String>>, CookieSource),
731 GetCookiesForUrl(
734 ServoUrl,
735 GenericSender<Vec<Serde<Cookie<'static>>>>,
736 CookieSource,
737 ),
738 EmbedderGetCookiesForUrl(CookieOperationId, ServoUrl, CookieSource),
741 EmbedderSetCookieForUrl(
744 CookieOperationId,
745 ServoUrl,
746 Serde<Cookie<'static>>,
747 CookieSource,
748 ),
749 EmbedderClearCookies(CookieOperationId),
751 EmbedderClearSessionCookies(CookieOperationId),
753 GetCookieDataForUrlAsync(CookieStoreId, ServoUrl, Option<String>),
754 GetAllCookieDataForUrlAsync(CookieStoreId, ServoUrl, Option<String>),
755 DeleteCookiesForSites(Vec<String>, GenericSender<()>),
756 DeleteCookies(Option<ServoUrl>, Option<IpcSender<()>>),
759 DeleteSessionCookies(GenericSender<()>),
761 DeleteCookie(ServoUrl, String),
762 DeleteCookieAsync(CookieStoreId, ServoUrl, String),
763 NewCookieListener(
764 CookieStoreId,
765 GenericCallback<CookieAsyncResponse>,
766 ServoUrl,
767 ),
768 RemoveCookieListener(CookieStoreId),
769 ListCookies(GenericSender<Vec<SiteDescriptor>>),
770 GetHistoryState(HistoryStateId, GenericSender<Option<Vec<u8>>>),
772 SetHistoryState(HistoryStateId, Vec<u8>),
774 RemoveHistoryStates(Vec<HistoryStateId>),
776 GetCacheEntries(GenericSender<Vec<CacheEntryDescriptor>>),
778 ClearCache(Option<GenericSender<()>>),
780 NetworkMediator(IpcSender<CustomResponseMediator>, ImmutableOrigin),
782 ToFileManager(FileManagerThreadMsg),
784 TotalSizeOfInFlightKeepAliveRecords(PipelineId, GenericSender<u64>),
785 Exit(GenericOneshotSender<()>),
788 CollectMemoryReport(ReportsChan),
789 RevokeTokenForFile(BlobTokenRevocationRequest),
790 RefreshTokenForFile(BlobTokenRefreshRequest),
791}
792
793#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
794pub struct BlobTokenRevocationRequest {
795 pub blob_id: Uuid,
796 pub token: Uuid,
797}
798
799#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
800pub struct BlobTokenRefreshRequest {
801 pub blob_id: Uuid,
802 pub new_token_sender: GenericSender<Uuid>,
803}
804
805#[derive(Clone, Debug, Deserialize, Serialize)]
806pub struct SiteDescriptor {
807 pub name: String,
808}
809
810impl SiteDescriptor {
811 pub fn new(name: String) -> Self {
812 SiteDescriptor { name }
813 }
814}
815
816#[derive(Clone, Debug, Deserialize, Serialize)]
817pub struct CacheEntryDescriptor {
818 pub key: String,
819}
820
821impl CacheEntryDescriptor {
822 pub fn new(key: String) -> Self {
823 Self { key }
824 }
825}
826
827#[expect(clippy::large_enum_variant)]
829enum ToFetchThreadMessage {
830 Cancel(Vec<RequestId>, CoreResourceThread),
831 StartFetch(
832 RequestBuilder,
833 Option<ResponseInit>,
834 BoxedFetchCallback,
835 CoreResourceThread,
836 ),
837 FetchResponse(FetchResponseMsg),
838 Exit,
840}
841
842pub type BoxedFetchCallback = Box<dyn FnMut(FetchResponseMsg) + Send + 'static>;
843
844pub struct FetchThread {
848 active_fetches: FxHashMap<RequestId, BoxedFetchCallback>,
851 receiver: Receiver<ToFetchThreadMessage>,
855 to_fetch_sender: GenericCallback<FetchResponseMsg>,
858}
859
860impl FetchThread {
861 fn spawn() -> FetchThreadHandle {
862 let (sender, receiver) = unbounded();
863
864 let sender_clone = sender.clone();
865 let to_fetch_sender = GenericCallback::new(move |message| {
866 let message: FetchResponseMsg = message.unwrap();
867 let _ = sender_clone.send(ToFetchThreadMessage::FetchResponse(message));
868 })
869 .expect("Couldn't create fetch callback");
870 let join_handle = thread::Builder::new()
871 .name("FetchThread".to_owned())
872 .spawn(move || {
873 let mut fetch_thread = FetchThread {
874 active_fetches: FxHashMap::default(),
875 receiver,
876 to_fetch_sender,
877 };
878 fetch_thread.run();
879 })
880 .expect("Thread spawning failed");
881 FetchThreadHandle {
882 sender,
883 join_handle: RwLock::new(Some(join_handle)),
884 }
885 }
886
887 fn run(&mut self) {
888 loop {
889 match self.receiver.recv() {
890 Ok(ToFetchThreadMessage::StartFetch(
891 request_builder,
892 response_init,
893 callback,
894 core_resource_thread,
895 )) => {
896 let request_builder_id = request_builder.id;
897
898 let message = match response_init {
900 Some(response_init) => CoreResourceMsg::FetchRedirect(
901 request_builder,
902 response_init,
903 self.to_fetch_sender.clone(),
904 ),
905 None => CoreResourceMsg::Fetch(
906 request_builder,
907 FetchChannels::ResponseMsg(self.to_fetch_sender.clone()),
908 ),
909 };
910
911 if core_resource_thread.send(message).is_err() {
912 break;
916 }
917
918 let preexisting_fetch =
919 self.active_fetches.insert(request_builder_id, callback);
920 assert!(preexisting_fetch.is_none());
924 },
925 Ok(ToFetchThreadMessage::FetchResponse(fetch_response_msg)) => {
926 let request_id = fetch_response_msg.request_id();
927 let fetch_finished =
928 matches!(fetch_response_msg, FetchResponseMsg::ProcessResponseEOF(..));
929
930 self.active_fetches
931 .get_mut(&request_id)
932 .expect("Got fetch response for unknown fetch")(
933 fetch_response_msg
934 );
935
936 if fetch_finished {
937 self.active_fetches.remove(&request_id);
938 }
939 },
940 Ok(ToFetchThreadMessage::Cancel(request_ids, core_resource_thread)) => {
941 let _ = core_resource_thread.send(CoreResourceMsg::Cancel(request_ids));
945 },
946 Ok(ToFetchThreadMessage::Exit) | Err(_) => break,
947 }
948 }
949 }
950
951 fn fetch_async(
952 core_resource_thread: &CoreResourceThread,
953 request: RequestBuilder,
954 response_init: Option<ResponseInit>,
955 callback: BoxedFetchCallback,
956 ) {
957 let _ = FETCH_THREAD.get_or_init(FetchThread::spawn).sender.send(
958 ToFetchThreadMessage::StartFetch(
959 request,
960 response_init,
961 callback,
962 core_resource_thread.clone(),
963 ),
964 );
965 }
966
967 fn cancel_async_fetch(request_ids: Vec<RequestId>, core_resource_thread: &CoreResourceThread) {
968 if let Some(fetch_thread) = FETCH_THREAD.get() {
969 let _ = fetch_thread.sender.send(ToFetchThreadMessage::Cancel(
970 request_ids,
971 core_resource_thread.clone(),
972 ));
973 }
974 }
975
976 pub fn exit() {
978 let Some(fetch_thread) = FETCH_THREAD.get() else {
979 return;
980 };
981 let _ = fetch_thread.sender.send(ToFetchThreadMessage::Exit);
982 if let Some(join_handle) = fetch_thread.join_handle.write().take() {
983 join_handle
984 .join()
985 .expect("Failed to join on the FetchThread join handle.");
986 }
987 }
988}
989
990struct FetchThreadHandle {
991 sender: Sender<ToFetchThreadMessage>,
992 join_handle: RwLock<Option<JoinHandle<()>>>,
993}
994
995static FETCH_THREAD: OnceLock<FetchThreadHandle> = OnceLock::new();
996
997pub fn fetch_async(
999 core_resource_thread: &CoreResourceThread,
1000 request: RequestBuilder,
1001 response_init: Option<ResponseInit>,
1002 callback: BoxedFetchCallback,
1003) {
1004 FetchThread::fetch_async(core_resource_thread, request, response_init, callback);
1005}
1006
1007pub fn cancel_async_fetch(request_ids: Vec<RequestId>, core_resource_thread: &CoreResourceThread) {
1010 FetchThread::cancel_async_fetch(request_ids, core_resource_thread);
1011}
1012
1013#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
1014pub struct ResourceCorsData {
1015 pub preflight: bool,
1017 pub origin: ServoUrl,
1019}
1020
1021#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
1023pub struct Metadata {
1024 pub final_url: ServoUrl,
1026
1027 pub location_url: Option<Result<ServoUrl, String>>,
1029
1030 #[ignore_malloc_size_of = "Defined in hyper"]
1031 pub content_type: Option<Serde<ContentType>>,
1033
1034 pub charset: Option<String>,
1036
1037 #[ignore_malloc_size_of = "Defined in hyper"]
1038 pub headers: Option<Serde<HeaderMap>>,
1040
1041 pub status: HttpStatus,
1043
1044 pub referrer: Option<ServoUrl>,
1046
1047 pub referrer_policy: ReferrerPolicy,
1049 pub timing: Option<ResourceFetchTiming>,
1051 pub redirected: bool,
1053 pub tls_security_info: Option<TlsSecurityInfo>,
1055}
1056
1057impl Metadata {
1058 pub fn default(url: ServoUrl) -> Self {
1060 Metadata {
1061 final_url: url,
1062 location_url: None,
1063 content_type: None,
1064 charset: None,
1065 headers: None,
1066 status: HttpStatus::default(),
1067 referrer: None,
1068 referrer_policy: ReferrerPolicy::EmptyString,
1069 timing: None,
1070 redirected: false,
1071 tls_security_info: None,
1072 }
1073 }
1074
1075 pub fn set_content_type(&mut self, content_type: Option<&Mime>) {
1077 if self.headers.is_none() {
1078 self.headers = Some(Serde(HeaderMap::new()));
1079 }
1080
1081 if let Some(mime) = content_type {
1082 self.headers
1083 .as_mut()
1084 .unwrap()
1085 .typed_insert(ContentType::from(mime.clone()));
1086 if let Some(charset) = mime.get_param(mime::CHARSET) {
1087 self.charset = Some(charset.to_string());
1088 }
1089 self.content_type = Some(Serde(ContentType::from(mime.clone())));
1090 }
1091 }
1092
1093 pub fn set_referrer_policy(&mut self, referrer_policy: ReferrerPolicy) {
1095 if referrer_policy == ReferrerPolicy::EmptyString {
1096 return;
1097 }
1098
1099 if self.headers.is_none() {
1100 self.headers = Some(Serde(HeaderMap::new()));
1101 }
1102
1103 self.referrer_policy = referrer_policy;
1104
1105 self.headers
1106 .as_mut()
1107 .unwrap()
1108 .typed_insert::<ReferrerPolicyHeader>(referrer_policy.into());
1109 }
1110
1111 pub fn resource_content_type_metadata(&self, load_context: LoadContext, data: &[u8]) -> Mime {
1113 let no_sniff = self
1115 .headers
1116 .as_deref()
1117 .is_some_and(determine_nosniff)
1118 .into();
1119 let mime = self
1120 .content_type
1121 .clone()
1122 .map(|content_type| content_type.into_inner().into());
1123 MimeClassifier::default().classify(
1124 load_context,
1125 no_sniff,
1126 ApacheBugFlag::from_content_type(mime.as_ref()),
1127 &mime,
1128 data,
1129 )
1130 }
1131}
1132
1133#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
1135pub enum CookieSource {
1136 HTTP,
1138 NonHTTP,
1140}
1141
1142#[derive(Clone, Debug, Deserialize, Serialize)]
1143pub struct CookieChange {
1144 changed: Vec<Serde<Cookie<'static>>>,
1145 deleted: Vec<Serde<Cookie<'static>>>,
1146}
1147
1148#[derive(Clone, Debug, Deserialize, Serialize)]
1149pub enum CookieData {
1150 Change(CookieChange),
1151 Get(Option<Serde<Cookie<'static>>>),
1152 GetAll(Vec<Serde<Cookie<'static>>>),
1153 Set(Result<(), ()>),
1154 Delete(Result<(), ()>),
1155}
1156
1157#[derive(Clone, Debug, Deserialize, Serialize)]
1158pub struct CookieAsyncResponse {
1159 pub data: CookieData,
1160}
1161
1162#[derive(Clone, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)]
1164pub enum NetworkError {
1165 LoadCancelled,
1166 SslValidation(String, Vec<u8>),
1168 Crash(String),
1170 UnsupportedScheme,
1171 CorsGeneral,
1172 CrossOriginResponse,
1173 CorsCredentials,
1174 CorsAllowMethods,
1175 CorsAllowHeaders,
1176 CorsMethod,
1177 CorsAuthorization,
1178 CorsHeaders,
1179 ConnectionFailure,
1180 RedirectError,
1181 TooManyRedirects,
1182 TooManyInFlightKeepAliveRequests,
1183 InvalidMethod,
1184 ResourceLoadError(String),
1185 ContentSecurityPolicy,
1186 Nosniff,
1187 MimeType(String),
1188 SubresourceIntegrity,
1189 MixedContent,
1190 CacheError,
1191 InvalidPort,
1192 WebsocketConnectionFailure(String),
1193 LocalDirectoryError,
1194 PartialResponseToNonRangeRequestError,
1195 ProtocolHandlerSubstitutionError,
1196 BlobURLStoreError(String),
1197 HttpError(String),
1198 DecompressionError,
1199}
1200
1201impl fmt::Debug for NetworkError {
1202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1203 match self {
1204 NetworkError::UnsupportedScheme => write!(f, "Unsupported scheme"),
1205 NetworkError::CorsGeneral => write!(f, "CORS check failed"),
1206 NetworkError::CrossOriginResponse => write!(f, "Cross-origin response"),
1207 NetworkError::CorsCredentials => write!(f, "Cross-origin credentials check failed"),
1208 NetworkError::CorsAllowMethods => write!(f, "CORS ACAM check failed"),
1209 NetworkError::CorsAllowHeaders => write!(f, "CORS ACAH check failed"),
1210 NetworkError::CorsMethod => write!(f, "CORS method check failed"),
1211 NetworkError::CorsAuthorization => write!(f, "CORS authorization check failed"),
1212 NetworkError::CorsHeaders => write!(f, "CORS headers check failed"),
1213 NetworkError::ConnectionFailure => write!(f, "Request failed"),
1214 NetworkError::RedirectError => write!(f, "Redirect failed"),
1215 NetworkError::TooManyRedirects => write!(f, "Too many redirects"),
1216 NetworkError::TooManyInFlightKeepAliveRequests => {
1217 write!(f, "Too many in flight keep-alive requests")
1218 },
1219 NetworkError::InvalidMethod => write!(f, "Unexpected method"),
1220 NetworkError::ResourceLoadError(s) => write!(f, "{}", s),
1221 NetworkError::ContentSecurityPolicy => write!(f, "Blocked by Content-Security-Policy"),
1222 NetworkError::Nosniff => write!(f, "Blocked by nosniff"),
1223 NetworkError::MimeType(s) => write!(f, "{}", s),
1224 NetworkError::SubresourceIntegrity => {
1225 write!(f, "Subresource integrity validation failed")
1226 },
1227 NetworkError::MixedContent => write!(f, "Blocked as mixed content"),
1228 NetworkError::CacheError => write!(f, "Couldn't find response in cache"),
1229 NetworkError::InvalidPort => write!(f, "Request attempted on bad port"),
1230 NetworkError::LocalDirectoryError => write!(f, "Local directory access failed"),
1231 NetworkError::LoadCancelled => write!(f, "Load cancelled"),
1232 NetworkError::SslValidation(s, _) => write!(f, "SSL validation error: {}", s),
1233 NetworkError::Crash(s) => write!(f, "Crash: {}", s),
1234 NetworkError::PartialResponseToNonRangeRequestError => write!(
1235 f,
1236 "Refusing to provide partial response from earlier ranged request to API that did not make a range request"
1237 ),
1238 NetworkError::ProtocolHandlerSubstitutionError => {
1239 write!(f, "Failed to parse substituted protocol handler url")
1240 },
1241 NetworkError::BlobURLStoreError(s) => write!(f, "Blob URL store error: {}", s),
1242 NetworkError::WebsocketConnectionFailure(s) => {
1243 write!(f, "Websocket connection failure: {}", s)
1244 },
1245 NetworkError::HttpError(s) => write!(f, "HTTP failure: {}", s),
1246 NetworkError::DecompressionError => write!(f, "Decompression error"),
1247 }
1248 }
1249}
1250
1251impl NetworkError {
1252 pub fn is_permanent_failure(&self) -> bool {
1253 matches!(
1254 self,
1255 NetworkError::ContentSecurityPolicy |
1256 NetworkError::MixedContent |
1257 NetworkError::SubresourceIntegrity |
1258 NetworkError::Nosniff |
1259 NetworkError::InvalidPort |
1260 NetworkError::CorsGeneral |
1261 NetworkError::CrossOriginResponse |
1262 NetworkError::CorsCredentials |
1263 NetworkError::CorsAllowMethods |
1264 NetworkError::CorsAllowHeaders |
1265 NetworkError::CorsMethod |
1266 NetworkError::CorsAuthorization |
1267 NetworkError::CorsHeaders |
1268 NetworkError::UnsupportedScheme
1269 )
1270 }
1271
1272 pub fn from_hyper_error(error: &HyperError, certificate: Option<CertificateDer>) -> Self {
1273 let error_string = error.to_string();
1274 match certificate {
1275 Some(certificate) => NetworkError::SslValidation(error_string, certificate.to_vec()),
1276 _ => NetworkError::HttpError(error_string),
1277 }
1278 }
1279}
1280
1281pub fn trim_http_whitespace(mut slice: &[u8]) -> &[u8] {
1284 const HTTP_WS_BYTES: &[u8] = b"\x09\x0A\x0D\x20";
1285
1286 loop {
1287 match slice.split_first() {
1288 Some((first, remainder)) if HTTP_WS_BYTES.contains(first) => slice = remainder,
1289 _ => break,
1290 }
1291 }
1292
1293 loop {
1294 match slice.split_last() {
1295 Some((last, remainder)) if HTTP_WS_BYTES.contains(last) => slice = remainder,
1296 _ => break,
1297 }
1298 }
1299
1300 slice
1301}
1302
1303pub fn get_current_locale() -> &'static (String, HeaderValue) {
1305 static CURRENT_LOCALE: OnceLock<(String, HeaderValue)> = OnceLock::new();
1306
1307 CURRENT_LOCALE.get_or_init(|| {
1308 let locale_override = servo_config::pref!(intl_locale_override);
1309 let locale = if locale_override.is_empty() {
1310 sys_locale::get_locale().unwrap_or_else(|| "en-US".into())
1311 } else {
1312 locale_override
1313 };
1314 let header_value = HeaderValue::from_str(&locale)
1315 .ok()
1316 .unwrap_or_else(|| HeaderValue::from_static("en-US"));
1317 (locale, header_value)
1318 })
1319}
1320
1321pub fn set_default_accept_language(headers: &mut HeaderMap) {
1323 if headers.contains_key(header::ACCEPT_LANGUAGE) {
1326 return;
1327 }
1328
1329 headers.insert(header::ACCEPT_LANGUAGE, get_current_locale().1.clone());
1331}
1332
1333pub static PRIVILEGED_SECRET: LazyLock<u32> = LazyLock::new(|| rng().next_u32());