1#![deny(unsafe_code)]
6
7use std::fmt::Display;
8use std::sync::{LazyLock, OnceLock};
9use std::thread::{self, JoinHandle};
10
11use base::cross_process_instant::CrossProcessInstant;
12use base::id::{CookieStoreId, HistoryStateId};
13use base::{IpcSend, IpcSendResult};
14use content_security_policy::{self as csp};
15use cookie::Cookie;
16use crossbeam_channel::{Receiver, Sender, unbounded};
17use headers::{ContentType, HeaderMapExt, ReferrerPolicy as ReferrerPolicyHeader};
18use http::{Error as HttpError, HeaderMap, HeaderValue, StatusCode, header};
19use hyper_serde::Serde;
20use hyper_util::client::legacy::Error as HyperError;
21use ipc_channel::ipc::{self, IpcError, IpcReceiver, IpcSender};
22use ipc_channel::router::ROUTER;
23use malloc_size_of::malloc_size_of_is_0;
24use malloc_size_of_derive::MallocSizeOf;
25use mime::Mime;
26use rand::{RngCore, rng};
27use request::RequestId;
28use rustc_hash::FxHashMap;
29use rustls_pki_types::CertificateDer;
30use serde::{Deserialize, Serialize};
31use servo_url::{ImmutableOrigin, ServoUrl};
32
33use crate::filemanager_thread::FileManagerThreadMsg;
34use crate::http_status::HttpStatus;
35use crate::request::{Request, RequestBuilder};
36use crate::response::{HttpsState, Response, ResponseInit};
37
38pub mod blob_url_store;
39pub mod filemanager_thread;
40pub mod http_status;
41pub mod image_cache;
42pub mod mime_classifier;
43pub mod policy_container;
44pub mod pub_domains;
45pub mod quality;
46pub mod request;
47pub mod response;
48
49pub const DOCUMENT_ACCEPT_HEADER_VALUE: HeaderValue =
51 HeaderValue::from_static("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
52
53pub mod fetch {
55 pub mod headers;
56}
57
58#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
61pub enum LoadContext {
62 Browsing,
63 Image,
64 AudioVideo,
65 Plugin,
66 Style,
67 Script,
68 Font,
69 TextTrack,
70 CacheManifest,
71}
72
73#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
74pub struct CustomResponse {
75 #[ignore_malloc_size_of = "Defined in hyper"]
76 #[serde(
77 deserialize_with = "::hyper_serde::deserialize",
78 serialize_with = "::hyper_serde::serialize"
79 )]
80 pub headers: HeaderMap,
81 #[ignore_malloc_size_of = "Defined in hyper"]
82 #[serde(
83 deserialize_with = "::hyper_serde::deserialize",
84 serialize_with = "::hyper_serde::serialize"
85 )]
86 pub raw_status: (StatusCode, String),
87 pub body: Vec<u8>,
88}
89
90impl CustomResponse {
91 pub fn new(
92 headers: HeaderMap,
93 raw_status: (StatusCode, String),
94 body: Vec<u8>,
95 ) -> CustomResponse {
96 CustomResponse {
97 headers,
98 raw_status,
99 body,
100 }
101 }
102}
103
104#[derive(Clone, Debug, Deserialize, Serialize)]
105pub struct CustomResponseMediator {
106 pub response_chan: IpcSender<Option<CustomResponse>>,
107 pub load_url: ServoUrl,
108}
109
110#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize)]
113pub enum ReferrerPolicy {
114 EmptyString,
116 NoReferrer,
118 NoReferrerWhenDowngrade,
120 Origin,
122 SameOrigin,
124 OriginWhenCrossOrigin,
126 UnsafeUrl,
128 StrictOrigin,
130 #[default]
132 StrictOriginWhenCrossOrigin,
133}
134
135impl ReferrerPolicy {
136 pub fn from_with_legacy(value: &str) -> Self {
138 match value.to_ascii_lowercase().as_str() {
141 "never" => ReferrerPolicy::NoReferrer,
142 "default" => ReferrerPolicy::StrictOriginWhenCrossOrigin,
143 "always" => ReferrerPolicy::UnsafeUrl,
144 "origin-when-crossorigin" => ReferrerPolicy::OriginWhenCrossOrigin,
145 _ => ReferrerPolicy::from(value),
146 }
147 }
148
149 pub fn parse_header_for_response(headers: &Option<Serde<HeaderMap>>) -> Self {
151 headers
153 .as_ref()
154 .and_then(|headers| headers.typed_get::<ReferrerPolicyHeader>())
156 .into()
158 }
159}
160
161impl From<&str> for ReferrerPolicy {
162 fn from(value: &str) -> Self {
164 match value.to_ascii_lowercase().as_str() {
165 "no-referrer" => ReferrerPolicy::NoReferrer,
166 "no-referrer-when-downgrade" => ReferrerPolicy::NoReferrerWhenDowngrade,
167 "origin" => ReferrerPolicy::Origin,
168 "same-origin" => ReferrerPolicy::SameOrigin,
169 "strict-origin" => ReferrerPolicy::StrictOrigin,
170 "strict-origin-when-cross-origin" => ReferrerPolicy::StrictOriginWhenCrossOrigin,
171 "origin-when-cross-origin" => ReferrerPolicy::OriginWhenCrossOrigin,
172 "unsafe-url" => ReferrerPolicy::UnsafeUrl,
173 _ => ReferrerPolicy::EmptyString,
174 }
175 }
176}
177
178impl Display for ReferrerPolicy {
179 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
180 let string = match self {
181 ReferrerPolicy::EmptyString => "",
182 ReferrerPolicy::NoReferrer => "no-referrer",
183 ReferrerPolicy::NoReferrerWhenDowngrade => "no-referrer-when-downgrade",
184 ReferrerPolicy::Origin => "origin",
185 ReferrerPolicy::SameOrigin => "same-origin",
186 ReferrerPolicy::OriginWhenCrossOrigin => "origin-when-cross-origin",
187 ReferrerPolicy::UnsafeUrl => "unsafe-url",
188 ReferrerPolicy::StrictOrigin => "strict-origin",
189 ReferrerPolicy::StrictOriginWhenCrossOrigin => "strict-origin-when-cross-origin",
190 };
191 write!(formatter, "{string}")
192 }
193}
194
195impl From<Option<ReferrerPolicyHeader>> for ReferrerPolicy {
197 fn from(header: Option<ReferrerPolicyHeader>) -> Self {
198 header.map_or(ReferrerPolicy::EmptyString, |policy| match policy {
201 ReferrerPolicyHeader::NO_REFERRER => ReferrerPolicy::NoReferrer,
202 ReferrerPolicyHeader::NO_REFERRER_WHEN_DOWNGRADE => {
203 ReferrerPolicy::NoReferrerWhenDowngrade
204 },
205 ReferrerPolicyHeader::SAME_ORIGIN => ReferrerPolicy::SameOrigin,
206 ReferrerPolicyHeader::ORIGIN => ReferrerPolicy::Origin,
207 ReferrerPolicyHeader::ORIGIN_WHEN_CROSS_ORIGIN => ReferrerPolicy::OriginWhenCrossOrigin,
208 ReferrerPolicyHeader::UNSAFE_URL => ReferrerPolicy::UnsafeUrl,
209 ReferrerPolicyHeader::STRICT_ORIGIN => ReferrerPolicy::StrictOrigin,
210 ReferrerPolicyHeader::STRICT_ORIGIN_WHEN_CROSS_ORIGIN => {
211 ReferrerPolicy::StrictOriginWhenCrossOrigin
212 },
213 })
214 }
215}
216
217impl From<ReferrerPolicy> for ReferrerPolicyHeader {
218 fn from(referrer_policy: ReferrerPolicy) -> Self {
219 match referrer_policy {
220 ReferrerPolicy::NoReferrer => ReferrerPolicyHeader::NO_REFERRER,
221 ReferrerPolicy::NoReferrerWhenDowngrade => {
222 ReferrerPolicyHeader::NO_REFERRER_WHEN_DOWNGRADE
223 },
224 ReferrerPolicy::SameOrigin => ReferrerPolicyHeader::SAME_ORIGIN,
225 ReferrerPolicy::Origin => ReferrerPolicyHeader::ORIGIN,
226 ReferrerPolicy::OriginWhenCrossOrigin => ReferrerPolicyHeader::ORIGIN_WHEN_CROSS_ORIGIN,
227 ReferrerPolicy::UnsafeUrl => ReferrerPolicyHeader::UNSAFE_URL,
228 ReferrerPolicy::StrictOrigin => ReferrerPolicyHeader::STRICT_ORIGIN,
229 ReferrerPolicy::EmptyString | ReferrerPolicy::StrictOriginWhenCrossOrigin => {
230 ReferrerPolicyHeader::STRICT_ORIGIN_WHEN_CROSS_ORIGIN
231 },
232 }
233 }
234}
235
236#[expect(clippy::large_enum_variant)]
238#[derive(Debug, Deserialize, Serialize)]
239pub enum FetchResponseMsg {
240 ProcessRequestBody(RequestId),
242 ProcessRequestEOF(RequestId),
243 ProcessResponse(RequestId, Result<FetchMetadata, NetworkError>),
245 ProcessResponseChunk(RequestId, Vec<u8>),
246 ProcessResponseEOF(RequestId, Result<ResourceFetchTiming, NetworkError>),
247 ProcessCspViolations(RequestId, Vec<csp::Violation>),
248}
249
250impl FetchResponseMsg {
251 pub fn request_id(&self) -> RequestId {
252 match self {
253 FetchResponseMsg::ProcessRequestBody(id) |
254 FetchResponseMsg::ProcessRequestEOF(id) |
255 FetchResponseMsg::ProcessResponse(id, ..) |
256 FetchResponseMsg::ProcessResponseChunk(id, ..) |
257 FetchResponseMsg::ProcessResponseEOF(id, ..) |
258 FetchResponseMsg::ProcessCspViolations(id, ..) => *id,
259 }
260 }
261}
262
263pub trait FetchTaskTarget {
264 fn process_request_body(&mut self, request: &Request);
268
269 fn process_request_eof(&mut self, request: &Request);
273
274 fn process_response(&mut self, request: &Request, response: &Response);
278
279 fn process_response_chunk(&mut self, request: &Request, chunk: Vec<u8>);
281
282 fn process_response_eof(&mut self, request: &Request, response: &Response);
286
287 fn process_csp_violations(&mut self, request: &Request, violations: Vec<csp::Violation>);
288}
289
290#[derive(Clone, Debug, Deserialize, Serialize)]
291pub enum FilteredMetadata {
292 Basic(Metadata),
293 Cors(Metadata),
294 Opaque,
295 OpaqueRedirect(ServoUrl),
296}
297
298#[expect(clippy::large_enum_variant)]
300#[derive(Clone, Debug, Deserialize, Serialize)]
301pub enum FetchMetadata {
302 Unfiltered(Metadata),
303 Filtered {
304 filtered: FilteredMetadata,
305 unsafe_: Metadata,
306 },
307}
308
309impl FetchMetadata {
310 pub fn metadata(&self) -> &Metadata {
311 match self {
312 Self::Unfiltered(metadata) => metadata,
313 Self::Filtered { unsafe_, .. } => unsafe_,
314 }
315 }
316}
317
318pub trait FetchResponseListener {
319 fn process_request_body(&mut self, request_id: RequestId);
320 fn process_request_eof(&mut self, request_id: RequestId);
321 fn process_response(
322 &mut self,
323 request_id: RequestId,
324 metadata: Result<FetchMetadata, NetworkError>,
325 );
326 fn process_response_chunk(&mut self, request_id: RequestId, chunk: Vec<u8>);
327 fn process_response_eof(
328 &mut self,
329 request_id: RequestId,
330 response: Result<ResourceFetchTiming, NetworkError>,
331 );
332 fn resource_timing(&self) -> &ResourceFetchTiming;
333 fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming;
334 fn submit_resource_timing(&mut self);
335 fn process_csp_violations(&mut self, request_id: RequestId, violations: Vec<csp::Violation>);
336}
337
338impl FetchTaskTarget for IpcSender<FetchResponseMsg> {
339 fn process_request_body(&mut self, request: &Request) {
340 let _ = self.send(FetchResponseMsg::ProcessRequestBody(request.id));
341 }
342
343 fn process_request_eof(&mut self, request: &Request) {
344 let _ = self.send(FetchResponseMsg::ProcessRequestEOF(request.id));
345 }
346
347 fn process_response(&mut self, request: &Request, response: &Response) {
348 let _ = self.send(FetchResponseMsg::ProcessResponse(
349 request.id,
350 response.metadata(),
351 ));
352 }
353
354 fn process_response_chunk(&mut self, request: &Request, chunk: Vec<u8>) {
355 let _ = self.send(FetchResponseMsg::ProcessResponseChunk(request.id, chunk));
356 }
357
358 fn process_response_eof(&mut self, request: &Request, response: &Response) {
359 let payload = if let Some(network_error) = response.get_network_error() {
360 Err(network_error.clone())
361 } else {
362 Ok(response.get_resource_timing().lock().unwrap().clone())
363 };
364
365 let _ = self.send(FetchResponseMsg::ProcessResponseEOF(request.id, payload));
366 }
367
368 fn process_csp_violations(&mut self, request: &Request, violations: Vec<csp::Violation>) {
369 let _ = self.send(FetchResponseMsg::ProcessCspViolations(
370 request.id, violations,
371 ));
372 }
373}
374
375impl FetchTaskTarget for IpcSender<WebSocketNetworkEvent> {
376 fn process_request_body(&mut self, _: &Request) {}
377 fn process_request_eof(&mut self, _: &Request) {}
378 fn process_response(&mut self, _: &Request, response: &Response) {
379 if response.is_network_error() {
380 let _ = self.send(WebSocketNetworkEvent::Fail);
381 }
382 }
383 fn process_response_chunk(&mut self, _: &Request, _: Vec<u8>) {}
384 fn process_response_eof(&mut self, _: &Request, _: &Response) {}
385 fn process_csp_violations(&mut self, _: &Request, violations: Vec<csp::Violation>) {
386 let _ = self.send(WebSocketNetworkEvent::ReportCSPViolations(violations));
387 }
388}
389
390pub struct DiscardFetch;
394
395impl FetchTaskTarget for DiscardFetch {
396 fn process_request_body(&mut self, _: &Request) {}
397 fn process_request_eof(&mut self, _: &Request) {}
398 fn process_response(&mut self, _: &Request, _: &Response) {}
399 fn process_response_chunk(&mut self, _: &Request, _: Vec<u8>) {}
400 fn process_response_eof(&mut self, _: &Request, _: &Response) {}
401 fn process_csp_violations(&mut self, _: &Request, _: Vec<csp::Violation>) {}
402}
403
404pub trait Action<Listener> {
405 fn process(self, listener: &mut Listener);
406}
407
408impl<T: FetchResponseListener> Action<T> for FetchResponseMsg {
409 fn process(self, listener: &mut T) {
411 match self {
412 FetchResponseMsg::ProcessRequestBody(request_id) => {
413 listener.process_request_body(request_id)
414 },
415 FetchResponseMsg::ProcessRequestEOF(request_id) => {
416 listener.process_request_eof(request_id)
417 },
418 FetchResponseMsg::ProcessResponse(request_id, meta) => {
419 listener.process_response(request_id, meta)
420 },
421 FetchResponseMsg::ProcessResponseChunk(request_id, data) => {
422 listener.process_response_chunk(request_id, data)
423 },
424 FetchResponseMsg::ProcessResponseEOF(request_id, data) => {
425 match data {
426 Ok(ref response_resource_timing) => {
427 *listener.resource_timing_mut() = response_resource_timing.clone();
429 listener
430 .process_response_eof(request_id, Ok(response_resource_timing.clone()));
431 listener.submit_resource_timing();
434 },
435 Err(e) => listener.process_response_eof(request_id, Err(e)),
440 }
441 },
442 FetchResponseMsg::ProcessCspViolations(request_id, violations) => {
443 listener.process_csp_violations(request_id, violations)
444 },
445 }
446 }
447}
448
449pub trait AsyncRuntime: Send {
452 fn shutdown(&mut self);
453}
454
455pub type CoreResourceThread = IpcSender<CoreResourceMsg>;
457
458#[derive(Clone, Debug, Deserialize, Serialize)]
464pub struct ResourceThreads {
465 pub core_thread: CoreResourceThread,
466}
467
468impl ResourceThreads {
469 pub fn new(core_thread: CoreResourceThread) -> ResourceThreads {
470 ResourceThreads { core_thread }
471 }
472
473 pub fn clear_cache(&self) {
474 let _ = self.core_thread.send(CoreResourceMsg::ClearCache);
475 }
476}
477
478impl IpcSend<CoreResourceMsg> for ResourceThreads {
479 fn send(&self, msg: CoreResourceMsg) -> IpcSendResult {
480 self.core_thread.send(msg).map_err(IpcError::Bincode)
481 }
482
483 fn sender(&self) -> IpcSender<CoreResourceMsg> {
484 self.core_thread.clone()
485 }
486}
487
488malloc_size_of_is_0!(ResourceThreads);
490
491#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
492pub enum IncludeSubdomains {
493 Included,
494 NotIncluded,
495}
496
497#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
498pub enum MessageData {
499 Text(String),
500 Binary(Vec<u8>),
501}
502
503#[derive(Debug, Deserialize, Serialize)]
504pub enum WebSocketDomAction {
505 SendMessage(MessageData),
506 Close(Option<u16>, Option<String>),
507}
508
509#[derive(Debug, Deserialize, Serialize)]
510pub enum WebSocketNetworkEvent {
511 ReportCSPViolations(Vec<csp::Violation>),
512 ConnectionEstablished { protocol_in_use: Option<String> },
513 MessageReceived(MessageData),
514 Close(Option<u16>, String),
515 Fail,
516}
517
518#[derive(Debug, Deserialize, Serialize)]
519pub enum FetchChannels {
521 ResponseMsg(IpcSender<FetchResponseMsg>),
522 WebSocket {
523 event_sender: IpcSender<WebSocketNetworkEvent>,
524 action_receiver: IpcReceiver<WebSocketDomAction>,
525 },
526 Prefetch,
529}
530
531#[derive(Debug, Deserialize, Serialize)]
532pub enum CoreResourceMsg {
533 Fetch(RequestBuilder, FetchChannels),
534 Cancel(Vec<RequestId>),
535 FetchRedirect(RequestBuilder, ResponseInit, IpcSender<FetchResponseMsg>),
537 SetCookieForUrl(ServoUrl, Serde<Cookie<'static>>, CookieSource),
539 SetCookiesForUrl(ServoUrl, Vec<Serde<Cookie<'static>>>, CookieSource),
541 SetCookieForUrlAsync(
542 CookieStoreId,
543 ServoUrl,
544 Serde<Cookie<'static>>,
545 CookieSource,
546 ),
547 GetCookiesForUrl(ServoUrl, IpcSender<Option<String>>, CookieSource),
549 GetCookiesDataForUrl(
551 ServoUrl,
552 IpcSender<Vec<Serde<Cookie<'static>>>>,
553 CookieSource,
554 ),
555 GetCookieDataForUrlAsync(CookieStoreId, ServoUrl, Option<String>),
556 GetAllCookieDataForUrlAsync(CookieStoreId, ServoUrl, Option<String>),
557 DeleteCookies(ServoUrl),
558 DeleteCookie(ServoUrl, String),
559 DeleteCookieAsync(CookieStoreId, ServoUrl, String),
560 NewCookieListener(CookieStoreId, IpcSender<CookieAsyncResponse>, ServoUrl),
561 RemoveCookieListener(CookieStoreId),
562 GetHistoryState(HistoryStateId, IpcSender<Option<Vec<u8>>>),
564 SetHistoryState(HistoryStateId, Vec<u8>),
566 RemoveHistoryStates(Vec<HistoryStateId>),
568 ClearCache,
570 NetworkMediator(IpcSender<CustomResponseMediator>, ImmutableOrigin),
572 ToFileManager(FileManagerThreadMsg),
574 Exit(IpcSender<()>),
577}
578
579#[expect(clippy::large_enum_variant)]
581enum ToFetchThreadMessage {
582 Cancel(Vec<RequestId>, CoreResourceThread),
583 StartFetch(
584 RequestBuilder,
585 Option<ResponseInit>,
586 BoxedFetchCallback,
587 CoreResourceThread,
588 ),
589 FetchResponse(FetchResponseMsg),
590 Exit,
592}
593
594pub type BoxedFetchCallback = Box<dyn FnMut(FetchResponseMsg) + Send + 'static>;
595
596struct FetchThread {
600 active_fetches: FxHashMap<RequestId, BoxedFetchCallback>,
603 receiver: Receiver<ToFetchThreadMessage>,
607 to_fetch_sender: IpcSender<FetchResponseMsg>,
610}
611
612impl FetchThread {
613 fn spawn() -> (Sender<ToFetchThreadMessage>, JoinHandle<()>) {
614 let (sender, receiver) = unbounded();
615 let (to_fetch_sender, from_fetch_sender) = ipc::channel().unwrap();
616
617 let sender_clone = sender.clone();
618 ROUTER.add_typed_route(
619 from_fetch_sender,
620 Box::new(move |message| {
621 let message: FetchResponseMsg = message.unwrap();
622 let _ = sender_clone.send(ToFetchThreadMessage::FetchResponse(message));
623 }),
624 );
625 let join_handle = thread::Builder::new()
626 .name("FetchThread".to_owned())
627 .spawn(move || {
628 let mut fetch_thread = FetchThread {
629 active_fetches: FxHashMap::default(),
630 receiver,
631 to_fetch_sender,
632 };
633 fetch_thread.run();
634 })
635 .expect("Thread spawning failed");
636 (sender, join_handle)
637 }
638
639 fn run(&mut self) {
640 loop {
641 match self.receiver.recv().unwrap() {
642 ToFetchThreadMessage::StartFetch(
643 request_builder,
644 response_init,
645 callback,
646 core_resource_thread,
647 ) => {
648 let request_builder_id = request_builder.id;
649
650 let message = match response_init {
652 Some(response_init) => CoreResourceMsg::FetchRedirect(
653 request_builder,
654 response_init,
655 self.to_fetch_sender.clone(),
656 ),
657 None => CoreResourceMsg::Fetch(
658 request_builder,
659 FetchChannels::ResponseMsg(self.to_fetch_sender.clone()),
660 ),
661 };
662
663 core_resource_thread.send(message).unwrap();
664
665 self.active_fetches.insert(request_builder_id, callback);
666 },
667 ToFetchThreadMessage::FetchResponse(fetch_response_msg) => {
668 let request_id = fetch_response_msg.request_id();
669 let fetch_finished =
670 matches!(fetch_response_msg, FetchResponseMsg::ProcessResponseEOF(..));
671
672 self.active_fetches
673 .get_mut(&request_id)
674 .expect("Got fetch response for unknown fetch")(
675 fetch_response_msg
676 );
677
678 if fetch_finished {
679 self.active_fetches.remove(&request_id);
680 }
681 },
682 ToFetchThreadMessage::Cancel(request_ids, core_resource_thread) => {
683 let _ = core_resource_thread.send(CoreResourceMsg::Cancel(request_ids));
687 },
688 ToFetchThreadMessage::Exit => break,
689 }
690 }
691 }
692}
693
694static FETCH_THREAD: OnceLock<Sender<ToFetchThreadMessage>> = OnceLock::new();
695
696pub fn start_fetch_thread() -> JoinHandle<()> {
699 let (sender, join_handle) = FetchThread::spawn();
700 FETCH_THREAD
701 .set(sender)
702 .expect("Fetch thread should be set only once on start-up");
703 join_handle
704}
705
706pub fn exit_fetch_thread() {
711 let _ = FETCH_THREAD
712 .get()
713 .expect("Fetch thread should always be initialized on start-up")
714 .send(ToFetchThreadMessage::Exit);
715}
716
717pub fn fetch_async(
719 core_resource_thread: &CoreResourceThread,
720 request: RequestBuilder,
721 response_init: Option<ResponseInit>,
722 callback: BoxedFetchCallback,
723) {
724 let _ = FETCH_THREAD
725 .get()
726 .expect("Fetch thread should always be initialized on start-up")
727 .send(ToFetchThreadMessage::StartFetch(
728 request,
729 response_init,
730 callback,
731 core_resource_thread.clone(),
732 ));
733}
734
735pub fn cancel_async_fetch(request_ids: Vec<RequestId>, core_resource_thread: &CoreResourceThread) {
738 let _ = FETCH_THREAD
739 .get()
740 .expect("Fetch thread should always be initialized on start-up")
741 .send(ToFetchThreadMessage::Cancel(
742 request_ids,
743 core_resource_thread.clone(),
744 ));
745}
746
747#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
748pub struct ResourceCorsData {
749 pub preflight: bool,
751 pub origin: ServoUrl,
753}
754
755#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
756pub struct ResourceFetchTiming {
757 pub domain_lookup_start: Option<CrossProcessInstant>,
758 pub timing_check_passed: bool,
759 pub timing_type: ResourceTimingType,
760 pub redirect_count: u16,
762 pub request_start: Option<CrossProcessInstant>,
763 pub secure_connection_start: Option<CrossProcessInstant>,
764 pub response_start: Option<CrossProcessInstant>,
765 pub fetch_start: Option<CrossProcessInstant>,
766 pub response_end: Option<CrossProcessInstant>,
767 pub redirect_start: Option<CrossProcessInstant>,
768 pub redirect_end: Option<CrossProcessInstant>,
769 pub connect_start: Option<CrossProcessInstant>,
770 pub connect_end: Option<CrossProcessInstant>,
771 pub start_time: Option<CrossProcessInstant>,
772}
773
774pub enum RedirectStartValue {
775 #[allow(dead_code)]
776 Zero,
777 FetchStart,
778}
779
780pub enum RedirectEndValue {
781 Zero,
782 ResponseEnd,
783}
784
785pub enum ResourceTimeValue {
788 Zero,
789 Now,
790 FetchStart,
791 RedirectStart,
792}
793
794pub enum ResourceAttribute {
795 RedirectCount(u16),
796 DomainLookupStart,
797 RequestStart,
798 ResponseStart,
799 RedirectStart(RedirectStartValue),
800 RedirectEnd(RedirectEndValue),
801 FetchStart,
802 ConnectStart(CrossProcessInstant),
803 ConnectEnd(CrossProcessInstant),
804 SecureConnectionStart,
805 ResponseEnd,
806 StartTime(ResourceTimeValue),
807}
808
809#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
810pub enum ResourceTimingType {
811 Resource,
812 Navigation,
813 Error,
814 None,
815}
816
817impl ResourceFetchTiming {
818 pub fn new(timing_type: ResourceTimingType) -> ResourceFetchTiming {
819 ResourceFetchTiming {
820 timing_type,
821 timing_check_passed: true,
822 domain_lookup_start: None,
823 redirect_count: 0,
824 secure_connection_start: None,
825 request_start: None,
826 response_start: None,
827 fetch_start: None,
828 redirect_start: None,
829 redirect_end: None,
830 connect_start: None,
831 connect_end: None,
832 response_end: None,
833 start_time: None,
834 }
835 }
836
837 pub fn set_attribute(&mut self, attribute: ResourceAttribute) {
840 let should_attribute_always_be_updated = matches!(
841 attribute,
842 ResourceAttribute::FetchStart |
843 ResourceAttribute::ResponseEnd |
844 ResourceAttribute::StartTime(_)
845 );
846 if !self.timing_check_passed && !should_attribute_always_be_updated {
847 return;
848 }
849 let now = Some(CrossProcessInstant::now());
850 match attribute {
851 ResourceAttribute::DomainLookupStart => self.domain_lookup_start = now,
852 ResourceAttribute::RedirectCount(count) => self.redirect_count = count,
853 ResourceAttribute::RequestStart => self.request_start = now,
854 ResourceAttribute::ResponseStart => self.response_start = now,
855 ResourceAttribute::RedirectStart(val) => match val {
856 RedirectStartValue::Zero => self.redirect_start = None,
857 RedirectStartValue::FetchStart => {
858 if self.redirect_start.is_none() {
859 self.redirect_start = self.fetch_start
860 }
861 },
862 },
863 ResourceAttribute::RedirectEnd(val) => match val {
864 RedirectEndValue::Zero => self.redirect_end = None,
865 RedirectEndValue::ResponseEnd => self.redirect_end = self.response_end,
866 },
867 ResourceAttribute::FetchStart => self.fetch_start = now,
868 ResourceAttribute::ConnectStart(instant) => self.connect_start = Some(instant),
869 ResourceAttribute::ConnectEnd(instant) => self.connect_end = Some(instant),
870 ResourceAttribute::SecureConnectionStart => self.secure_connection_start = now,
871 ResourceAttribute::ResponseEnd => self.response_end = now,
872 ResourceAttribute::StartTime(val) => match val {
873 ResourceTimeValue::RedirectStart
874 if self.redirect_start.is_none() || !self.timing_check_passed => {},
875 _ => self.start_time = self.get_time_value(val),
876 },
877 }
878 }
879
880 fn get_time_value(&self, time: ResourceTimeValue) -> Option<CrossProcessInstant> {
881 match time {
882 ResourceTimeValue::Zero => None,
883 ResourceTimeValue::Now => Some(CrossProcessInstant::now()),
884 ResourceTimeValue::FetchStart => self.fetch_start,
885 ResourceTimeValue::RedirectStart => self.redirect_start,
886 }
887 }
888
889 pub fn mark_timing_check_failed(&mut self) {
890 self.timing_check_passed = false;
891 self.domain_lookup_start = None;
892 self.redirect_count = 0;
893 self.request_start = None;
894 self.response_start = None;
895 self.redirect_start = None;
896 self.connect_start = None;
897 self.connect_end = None;
898 }
899}
900
901#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
903pub struct Metadata {
904 pub final_url: ServoUrl,
906
907 pub location_url: Option<Result<ServoUrl, String>>,
909
910 #[ignore_malloc_size_of = "Defined in hyper"]
911 pub content_type: Option<Serde<ContentType>>,
913
914 pub charset: Option<String>,
916
917 #[ignore_malloc_size_of = "Defined in hyper"]
918 pub headers: Option<Serde<HeaderMap>>,
920
921 pub status: HttpStatus,
923
924 pub https_state: HttpsState,
926
927 pub referrer: Option<ServoUrl>,
929
930 pub referrer_policy: ReferrerPolicy,
932 pub timing: Option<ResourceFetchTiming>,
934 pub redirected: bool,
936}
937
938impl Metadata {
939 pub fn default(url: ServoUrl) -> Self {
941 Metadata {
942 final_url: url,
943 location_url: None,
944 content_type: None,
945 charset: None,
946 headers: None,
947 status: HttpStatus::default(),
948 https_state: HttpsState::None,
949 referrer: None,
950 referrer_policy: ReferrerPolicy::EmptyString,
951 timing: None,
952 redirected: false,
953 }
954 }
955
956 pub fn set_content_type(&mut self, content_type: Option<&Mime>) {
958 if self.headers.is_none() {
959 self.headers = Some(Serde(HeaderMap::new()));
960 }
961
962 if let Some(mime) = content_type {
963 self.headers
964 .as_mut()
965 .unwrap()
966 .typed_insert(ContentType::from(mime.clone()));
967 if let Some(charset) = mime.get_param(mime::CHARSET) {
968 self.charset = Some(charset.to_string());
969 }
970 self.content_type = Some(Serde(ContentType::from(mime.clone())));
971 }
972 }
973
974 pub fn set_referrer_policy(&mut self, referrer_policy: ReferrerPolicy) {
976 if referrer_policy == ReferrerPolicy::EmptyString {
977 return;
978 }
979
980 if self.headers.is_none() {
981 self.headers = Some(Serde(HeaderMap::new()));
982 }
983
984 self.referrer_policy = referrer_policy;
985
986 self.headers
987 .as_mut()
988 .unwrap()
989 .typed_insert::<ReferrerPolicyHeader>(referrer_policy.into());
990 }
991}
992
993#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
995pub enum CookieSource {
996 HTTP,
998 NonHTTP,
1000}
1001
1002#[derive(Clone, Debug, Deserialize, Serialize)]
1003pub struct CookieChange {
1004 changed: Vec<Serde<Cookie<'static>>>,
1005 deleted: Vec<Serde<Cookie<'static>>>,
1006}
1007
1008#[derive(Clone, Debug, Deserialize, Serialize)]
1009pub enum CookieData {
1010 Change(CookieChange),
1011 Get(Option<Serde<Cookie<'static>>>),
1012 GetAll(Vec<Serde<Cookie<'static>>>),
1013 Set(Result<(), ()>),
1014 Delete(Result<(), ()>),
1015}
1016
1017#[derive(Clone, Debug, Deserialize, Serialize)]
1018pub struct CookieAsyncResponse {
1019 pub data: CookieData,
1020}
1021
1022#[derive(Clone, Debug, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)]
1024pub enum NetworkError {
1025 Internal(String),
1027 LoadCancelled,
1028 SslValidation(String, Vec<u8>),
1030 Crash(String),
1032}
1033
1034impl NetworkError {
1035 pub fn from_hyper_error(error: &HyperError, certificate: Option<CertificateDer>) -> Self {
1036 let error_string = error.to_string();
1037 match certificate {
1038 Some(certificate) => NetworkError::SslValidation(error_string, certificate.to_vec()),
1039 _ => NetworkError::Internal(error_string),
1040 }
1041 }
1042
1043 pub fn from_http_error(error: &HttpError) -> Self {
1044 NetworkError::Internal(error.to_string())
1045 }
1046}
1047
1048pub fn trim_http_whitespace(mut slice: &[u8]) -> &[u8] {
1051 const HTTP_WS_BYTES: &[u8] = b"\x09\x0A\x0D\x20";
1052
1053 loop {
1054 match slice.split_first() {
1055 Some((first, remainder)) if HTTP_WS_BYTES.contains(first) => slice = remainder,
1056 _ => break,
1057 }
1058 }
1059
1060 loop {
1061 match slice.split_last() {
1062 Some((last, remainder)) if HTTP_WS_BYTES.contains(last) => slice = remainder,
1063 _ => break,
1064 }
1065 }
1066
1067 slice
1068}
1069
1070pub fn http_percent_encode(bytes: &[u8]) -> String {
1071 const HTTP_VALUE: &percent_encoding::AsciiSet = &percent_encoding::CONTROLS
1074 .add(b' ')
1075 .add(b'"')
1076 .add(b'%')
1077 .add(b'\'')
1078 .add(b'(')
1079 .add(b')')
1080 .add(b'*')
1081 .add(b',')
1082 .add(b'/')
1083 .add(b':')
1084 .add(b';')
1085 .add(b'<')
1086 .add(b'-')
1087 .add(b'>')
1088 .add(b'?')
1089 .add(b'[')
1090 .add(b'\\')
1091 .add(b']')
1092 .add(b'{')
1093 .add(b'}');
1094
1095 percent_encoding::percent_encode(bytes, HTTP_VALUE).to_string()
1096}
1097
1098pub fn set_default_accept_language(headers: &mut HeaderMap) {
1100 if headers.contains_key(header::ACCEPT_LANGUAGE) {
1103 return;
1104 }
1105
1106 headers.insert(
1108 header::ACCEPT_LANGUAGE,
1109 HeaderValue::from_static("en-US,en;q=0.5"),
1110 );
1111}
1112
1113pub static PRIVILEGED_SECRET: LazyLock<u32> = LazyLock::new(|| rng().next_u32());