1use std::sync::Arc;
6
7use base::generic_channel::GenericSharedMemory;
8use base::id::{PipelineId, WebViewId};
9use content_security_policy::{self as csp};
10use http::header::{AUTHORIZATION, HeaderName};
11use http::{HeaderMap, Method};
12use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
13use ipc_channel::router::ROUTER;
14use malloc_size_of_derive::MallocSizeOf;
15use mime::Mime;
16use parking_lot::Mutex;
17use rustc_hash::FxHashMap;
18use serde::{Deserialize, Serialize};
19use servo_url::{ImmutableOrigin, ServoUrl};
20use tokio::sync::oneshot::Sender as TokioSender;
21use url::Position;
22use uuid::Uuid;
23
24use crate::policy_container::{PolicyContainer, RequestPolicyContainer};
25use crate::pub_domains::is_same_site;
26use crate::response::{HttpsState, RedirectTaint, Response};
27use crate::{ReferrerPolicy, ResourceTimingType};
28
29#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
30pub struct RequestId(pub Uuid);
32
33impl Default for RequestId {
34 fn default() -> Self {
35 Self(Uuid::new_v4())
36 }
37}
38
39#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
41pub enum Initiator {
42 None,
43 Download,
44 ImageSet,
45 Manifest,
46 XSLT,
47 Prefetch,
48 Link,
49}
50
51pub use csp::Destination;
53
54#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
56pub enum Origin {
57 Client,
58 Origin(ImmutableOrigin),
59}
60
61impl Origin {
62 pub fn is_opaque(&self) -> bool {
63 matches!(self, Origin::Origin(ImmutableOrigin::Opaque(_)))
64 }
65}
66
67#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
69pub enum Referrer {
70 NoReferrer,
71 Client(ServoUrl),
77 ReferrerUrl(ServoUrl),
78}
79
80#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
82pub enum RequestMode {
83 Navigate,
84 SameOrigin,
85 NoCors,
86 CorsMode,
87 WebSocket {
88 protocols: Vec<String>,
89 original_url: ServoUrl,
90 },
91}
92
93#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
95pub enum CredentialsMode {
96 Omit,
97 CredentialsSameOrigin,
98 Include,
99}
100
101#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
103pub enum CacheMode {
104 Default,
105 NoStore,
106 Reload,
107 NoCache,
108 ForceCache,
109 OnlyIfCached,
110}
111
112#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
114pub enum ServiceWorkersMode {
115 All,
116 None,
117}
118
119#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
121pub enum RedirectMode {
122 Follow,
123 Error,
124 Manual,
125}
126
127#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
129pub enum ResponseTainting {
130 Basic,
131 CorsTainting,
132 Opaque,
133}
134
135#[derive(Clone, Debug, Eq, Hash, Deserialize, MallocSizeOf, Serialize, PartialEq)]
137pub struct PreloadKey {
138 pub url: ServoUrl,
140 pub destination: Destination,
142 pub mode: RequestMode,
144 pub credentials_mode: CredentialsMode,
146}
147
148impl PreloadKey {
149 pub fn new(request: &RequestBuilder) -> Self {
150 Self {
151 url: request.url.clone(),
152 destination: request.destination,
153 mode: request.mode.clone(),
154 credentials_mode: request.credentials_mode,
155 }
156 }
157}
158
159#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize, Hash, MallocSizeOf)]
160pub struct PreloadId(pub Uuid);
161
162impl Default for PreloadId {
163 fn default() -> Self {
164 Self(Uuid::new_v4())
165 }
166}
167
168#[derive(Debug, MallocSizeOf)]
170pub struct PreloadEntry {
171 pub integrity_metadata: String,
173 pub response: Option<Response>,
175 #[ignore_malloc_size_of = "channels are hard"]
177 pub on_response_available: Option<TokioSender<Response>>,
178}
179
180impl PreloadEntry {
181 pub fn new(integrity_metadata: String) -> Self {
182 Self {
183 integrity_metadata,
184 response: None,
185 on_response_available: None,
186 }
187 }
188
189 pub fn with_response(&mut self, response: Response) {
191 if let Some(sender) = self.on_response_available.take() {
194 let _ = sender.send(response);
195 } else {
196 self.response = Some(response);
197 }
198 }
199}
200
201pub type PreloadedResources = FxHashMap<PreloadKey, PreloadId>;
202
203#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
205pub struct RequestClient {
206 pub preloaded_resources: PreloadedResources,
208 pub policy_container: RequestPolicyContainer,
210 pub origin: Origin,
212 pub is_nested_browsing_context: bool,
214 pub insecure_requests_policy: InsecureRequestsPolicy,
216}
217
218#[derive(Clone, Copy, Default, MallocSizeOf, PartialEq)]
220pub enum SystemVisibilityState {
221 #[default]
222 Hidden,
223 Visible,
224}
225
226#[derive(Clone, Copy, Default, MallocSizeOf, PartialEq)]
228pub struct TraversableNavigable {
229 current_session_history_step: u8,
231 running_nested_apply_history_step: bool,
235 system_visibility_state: SystemVisibilityState,
237 is_created_by_web_content: bool,
239}
240
241#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
243pub enum TraversableForUserPrompts {
244 NoTraversable,
245 Client,
246 TraversableNavigable(TraversableNavigable),
247}
248
249#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
251pub enum CorsSettings {
252 Anonymous,
253 UseCredentials,
254}
255
256impl CorsSettings {
257 pub fn from_enumerated_attribute(value: &str) -> CorsSettings {
259 match value.to_ascii_lowercase().as_str() {
260 "anonymous" => CorsSettings::Anonymous,
261 "use-credentials" => CorsSettings::UseCredentials,
262 _ => CorsSettings::Anonymous,
263 }
264 }
265}
266
267#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
269pub enum ParserMetadata {
270 Default,
271 ParserInserted,
272 NotParserInserted,
273}
274
275#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
277pub enum BodySource {
278 Null,
279 Object,
280}
281
282#[derive(Debug, Deserialize, Serialize)]
285pub enum BodyChunkResponse {
286 Chunk(GenericSharedMemory),
288 Done,
290 Error,
293}
294
295#[derive(Debug, Deserialize, Serialize)]
299pub enum BodyChunkRequest {
300 Connect(IpcSender<BodyChunkResponse>),
302 Extract(IpcReceiver<BodyChunkRequest>),
304 Chunk,
306 Done,
308 Error,
310}
311
312#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
314pub struct RequestBody {
315 #[ignore_malloc_size_of = "Channels are hard"]
317 chan: Arc<Mutex<IpcSender<BodyChunkRequest>>>,
318 source: BodySource,
320 total_bytes: Option<usize>,
322}
323
324impl RequestBody {
325 pub fn new(
326 chan: IpcSender<BodyChunkRequest>,
327 source: BodySource,
328 total_bytes: Option<usize>,
329 ) -> Self {
330 RequestBody {
331 chan: Arc::new(Mutex::new(chan)),
332 source,
333 total_bytes,
334 }
335 }
336
337 pub fn extract_source(&mut self) {
339 match self.source {
340 BodySource::Null => panic!("Null sources should never be re-directed."),
341 BodySource::Object => {
342 let (chan, port) = ipc::channel().unwrap();
343 let mut selfchan = self.chan.lock();
344 let _ = selfchan.send(BodyChunkRequest::Extract(port));
345 *selfchan = chan;
346 },
347 }
348 }
349
350 pub fn take_stream(&self) -> Arc<Mutex<IpcSender<BodyChunkRequest>>> {
351 self.chan.clone()
352 }
353
354 pub fn source_is_null(&self) -> bool {
355 self.source == BodySource::Null
356 }
357
358 #[expect(clippy::len_without_is_empty)]
359 pub fn len(&self) -> Option<usize> {
360 self.total_bytes
361 }
362}
363
364trait RequestBodySize {
365 fn body_length(&self) -> usize;
366}
367
368impl RequestBodySize for Option<RequestBody> {
369 fn body_length(&self) -> usize {
370 self.as_ref()
371 .and_then(|body| body.len())
372 .unwrap_or_default()
373 }
374}
375
376#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
377pub enum InsecureRequestsPolicy {
378 DoNotUpgrade,
379 Upgrade,
380}
381
382pub trait RequestHeadersSize {
383 fn total_size(&self) -> usize;
384}
385
386impl RequestHeadersSize for HeaderMap {
387 fn total_size(&self) -> usize {
388 self.iter()
389 .map(|(name, value)| name.as_str().len() + value.len())
390 .sum()
391 }
392}
393
394#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
395pub struct RequestBuilder {
396 pub id: RequestId,
397
398 pub preload_id: Option<PreloadId>,
399
400 #[serde(
402 deserialize_with = "::hyper_serde::deserialize",
403 serialize_with = "::hyper_serde::serialize"
404 )]
405 #[ignore_malloc_size_of = "Defined in hyper"]
406 pub method: Method,
407
408 pub url: ServoUrl,
410
411 #[serde(
413 deserialize_with = "::hyper_serde::deserialize",
414 serialize_with = "::hyper_serde::serialize"
415 )]
416 #[ignore_malloc_size_of = "Defined in hyper"]
417 pub headers: HeaderMap,
418
419 pub unsafe_request: bool,
421
422 pub body: Option<RequestBody>,
424
425 pub service_workers_mode: ServiceWorkersMode,
427 pub client: Option<RequestClient>,
428 pub destination: Destination,
430 pub synchronous: bool,
431 pub mode: RequestMode,
432
433 pub cache_mode: CacheMode,
435
436 pub use_cors_preflight: bool,
438
439 pub keep_alive: bool,
441
442 pub credentials_mode: CredentialsMode,
444 pub use_url_credentials: bool,
445
446 pub origin: Origin,
448
449 pub policy_container: RequestPolicyContainer,
451 pub insecure_requests_policy: InsecureRequestsPolicy,
452 pub has_trustworthy_ancestor_origin: bool,
453
454 pub referrer: Referrer,
456
457 pub referrer_policy: ReferrerPolicy,
459 pub pipeline_id: Option<PipelineId>,
460 pub target_webview_id: Option<WebViewId>,
461
462 pub redirect_mode: RedirectMode,
464
465 pub integrity_metadata: String,
467
468 pub cryptographic_nonce_metadata: String,
470
471 pub url_list: Vec<ServoUrl>,
473
474 pub parser_metadata: ParserMetadata,
476
477 pub initiator: Initiator,
479 pub https_state: HttpsState,
480 pub response_tainting: ResponseTainting,
481 pub crash: Option<String>,
483}
484
485impl RequestBuilder {
486 pub fn new(webview_id: Option<WebViewId>, url: ServoUrl, referrer: Referrer) -> RequestBuilder {
487 RequestBuilder {
488 id: RequestId::default(),
489 preload_id: None,
490 method: Method::GET,
491 url,
492 headers: HeaderMap::new(),
493 unsafe_request: false,
494 body: None,
495 service_workers_mode: ServiceWorkersMode::All,
496 destination: Destination::None,
497 synchronous: false,
498 mode: RequestMode::NoCors,
499 cache_mode: CacheMode::Default,
500 use_cors_preflight: false,
501 keep_alive: false,
502 credentials_mode: CredentialsMode::CredentialsSameOrigin,
503 use_url_credentials: false,
504 origin: Origin::Client,
505 client: None,
506 policy_container: RequestPolicyContainer::default(),
507 insecure_requests_policy: InsecureRequestsPolicy::DoNotUpgrade,
508 has_trustworthy_ancestor_origin: false,
509 referrer,
510 referrer_policy: ReferrerPolicy::EmptyString,
511 pipeline_id: None,
512 target_webview_id: webview_id,
513 redirect_mode: RedirectMode::Follow,
514 integrity_metadata: "".to_owned(),
515 cryptographic_nonce_metadata: "".to_owned(),
516 url_list: vec![],
517 parser_metadata: ParserMetadata::Default,
518 initiator: Initiator::None,
519 https_state: HttpsState::None,
520 response_tainting: ResponseTainting::Basic,
521 crash: None,
522 }
523 }
524
525 pub fn preload_id(mut self, preload_id: PreloadId) -> RequestBuilder {
526 self.preload_id = Some(preload_id);
527 self
528 }
529
530 pub fn initiator(mut self, initiator: Initiator) -> RequestBuilder {
532 self.initiator = initiator;
533 self
534 }
535
536 pub fn method(mut self, method: Method) -> RequestBuilder {
538 self.method = method;
539 self
540 }
541
542 pub fn headers(mut self, headers: HeaderMap) -> RequestBuilder {
544 self.headers = headers;
545 self
546 }
547
548 pub fn unsafe_request(mut self, unsafe_request: bool) -> RequestBuilder {
550 self.unsafe_request = unsafe_request;
551 self
552 }
553
554 pub fn body(mut self, body: Option<RequestBody>) -> RequestBuilder {
556 self.body = body;
557 self
558 }
559
560 pub fn destination(mut self, destination: Destination) -> RequestBuilder {
562 self.destination = destination;
563 self
564 }
565
566 pub fn synchronous(mut self, synchronous: bool) -> RequestBuilder {
567 self.synchronous = synchronous;
568 self
569 }
570
571 pub fn mode(mut self, mode: RequestMode) -> RequestBuilder {
572 self.mode = mode;
573 self
574 }
575
576 pub fn use_cors_preflight(mut self, use_cors_preflight: bool) -> RequestBuilder {
578 self.use_cors_preflight = use_cors_preflight;
579 self
580 }
581
582 pub fn keep_alive(mut self, keep_alive: bool) -> RequestBuilder {
584 self.keep_alive = keep_alive;
585 self
586 }
587
588 pub fn credentials_mode(mut self, credentials_mode: CredentialsMode) -> RequestBuilder {
590 self.credentials_mode = credentials_mode;
591 self
592 }
593
594 pub fn use_url_credentials(mut self, use_url_credentials: bool) -> RequestBuilder {
595 self.use_url_credentials = use_url_credentials;
596 self
597 }
598
599 pub fn origin(mut self, origin: ImmutableOrigin) -> RequestBuilder {
601 self.origin = Origin::Origin(origin);
602 self
603 }
604
605 pub fn referrer_policy(mut self, referrer_policy: ReferrerPolicy) -> RequestBuilder {
607 self.referrer_policy = referrer_policy;
608 self
609 }
610
611 pub fn pipeline_id(mut self, pipeline_id: Option<PipelineId>) -> RequestBuilder {
612 self.pipeline_id = pipeline_id;
613 self
614 }
615
616 pub fn redirect_mode(mut self, redirect_mode: RedirectMode) -> RequestBuilder {
618 self.redirect_mode = redirect_mode;
619 self
620 }
621
622 pub fn integrity_metadata(mut self, integrity_metadata: String) -> RequestBuilder {
624 self.integrity_metadata = integrity_metadata;
625 self
626 }
627
628 pub fn cryptographic_nonce_metadata(mut self, nonce_metadata: String) -> RequestBuilder {
630 self.cryptographic_nonce_metadata = nonce_metadata;
631 self
632 }
633
634 pub fn parser_metadata(mut self, parser_metadata: ParserMetadata) -> RequestBuilder {
636 self.parser_metadata = parser_metadata;
637 self
638 }
639
640 pub fn https_state(mut self, https_state: HttpsState) -> RequestBuilder {
641 self.https_state = https_state;
642 self
643 }
644
645 pub fn response_tainting(mut self, response_tainting: ResponseTainting) -> RequestBuilder {
646 self.response_tainting = response_tainting;
647 self
648 }
649
650 pub fn crash(mut self, crash: Option<String>) -> Self {
651 self.crash = crash;
652 self
653 }
654
655 pub fn policy_container(mut self, policy_container: PolicyContainer) -> RequestBuilder {
657 self.policy_container = RequestPolicyContainer::PolicyContainer(policy_container);
658 self
659 }
660
661 pub fn client(mut self, client: RequestClient) -> RequestBuilder {
663 self.client = Some(client);
664 self
665 }
666
667 pub fn insecure_requests_policy(
668 mut self,
669 insecure_requests_policy: InsecureRequestsPolicy,
670 ) -> RequestBuilder {
671 self.insecure_requests_policy = insecure_requests_policy;
672 self
673 }
674
675 pub fn has_trustworthy_ancestor_origin(
676 mut self,
677 has_trustworthy_ancestor_origin: bool,
678 ) -> RequestBuilder {
679 self.has_trustworthy_ancestor_origin = has_trustworthy_ancestor_origin;
680 self
681 }
682
683 pub fn service_workers_mode(
685 mut self,
686 service_workers_mode: ServiceWorkersMode,
687 ) -> RequestBuilder {
688 self.service_workers_mode = service_workers_mode;
689 self
690 }
691
692 pub fn cache_mode(mut self, cache_mode: CacheMode) -> RequestBuilder {
694 self.cache_mode = cache_mode;
695 self
696 }
697
698 pub fn build(self) -> Request {
699 let mut request = Request::new(
700 self.id,
701 self.url.clone(),
702 Some(self.origin),
703 self.referrer,
704 self.pipeline_id,
705 self.target_webview_id,
706 self.https_state,
707 );
708 request.preload_id = self.preload_id;
709 request.initiator = self.initiator;
710 request.method = self.method;
711 request.headers = self.headers;
712 request.unsafe_request = self.unsafe_request;
713 request.body = self.body;
714 request.service_workers_mode = self.service_workers_mode;
715 request.destination = self.destination;
716 request.synchronous = self.synchronous;
717 request.mode = self.mode;
718 request.use_cors_preflight = self.use_cors_preflight;
719 request.keep_alive = self.keep_alive;
720 request.credentials_mode = self.credentials_mode;
721 request.use_url_credentials = self.use_url_credentials;
722 request.cache_mode = self.cache_mode;
723 request.referrer_policy = self.referrer_policy;
724 request.redirect_mode = self.redirect_mode;
725 let mut url_list = self.url_list;
726 if url_list.is_empty() {
727 url_list.push(self.url);
728 }
729 request.redirect_count = url_list.len() as u32 - 1;
730 request.url_list = url_list;
731 request.integrity_metadata = self.integrity_metadata;
732 request.cryptographic_nonce_metadata = self.cryptographic_nonce_metadata;
733 request.parser_metadata = self.parser_metadata;
734 request.response_tainting = self.response_tainting;
735 request.crash = self.crash;
736 request.client = self.client;
737 request.policy_container = self.policy_container;
738 request.insecure_requests_policy = self.insecure_requests_policy;
739 request.has_trustworthy_ancestor_origin = self.has_trustworthy_ancestor_origin;
740 request
741 }
742
743 pub fn keep_alive_body_length(&self) -> u64 {
745 assert!(self.keep_alive);
746 self.body.body_length() as u64
747 }
748}
749
750#[derive(Clone, MallocSizeOf)]
753pub struct Request {
754 pub id: RequestId,
758 pub preload_id: Option<PreloadId>,
759 #[ignore_malloc_size_of = "Defined in hyper"]
761 pub method: Method,
762 pub local_urls_only: bool,
764 #[ignore_malloc_size_of = "Defined in hyper"]
766 pub headers: HeaderMap,
767 pub unsafe_request: bool,
769 pub body: Option<RequestBody>,
771 pub client: Option<RequestClient>,
773 pub traversable_for_user_prompts: TraversableForUserPrompts,
775 pub target_webview_id: Option<WebViewId>,
776 pub keep_alive: bool,
778 pub service_workers_mode: ServiceWorkersMode,
780 pub initiator: Initiator,
782 pub destination: Destination,
784 pub origin: Origin,
787 pub referrer: Referrer,
789 pub referrer_policy: ReferrerPolicy,
791 pub pipeline_id: Option<PipelineId>,
792 pub synchronous: bool,
794 pub mode: RequestMode,
796 pub use_cors_preflight: bool,
798 pub credentials_mode: CredentialsMode,
800 pub use_url_credentials: bool,
802 pub cache_mode: CacheMode,
804 pub redirect_mode: RedirectMode,
806 pub integrity_metadata: String,
808 pub cryptographic_nonce_metadata: String,
810 pub url_list: Vec<ServoUrl>,
814 pub redirect_count: u32,
816 pub response_tainting: ResponseTainting,
818 pub parser_metadata: ParserMetadata,
820 pub policy_container: RequestPolicyContainer,
822 pub insecure_requests_policy: InsecureRequestsPolicy,
824 pub has_trustworthy_ancestor_origin: bool,
825 pub https_state: HttpsState,
826 pub crash: Option<String>,
828}
829
830impl Request {
831 pub fn new(
832 id: RequestId,
833 url: ServoUrl,
834 origin: Option<Origin>,
835 referrer: Referrer,
836 pipeline_id: Option<PipelineId>,
837 webview_id: Option<WebViewId>,
838 https_state: HttpsState,
839 ) -> Request {
840 Request {
841 id,
842 preload_id: None,
843 method: Method::GET,
844 local_urls_only: false,
845 headers: HeaderMap::new(),
846 unsafe_request: false,
847 body: None,
848 client: None,
849 traversable_for_user_prompts: TraversableForUserPrompts::Client,
850 keep_alive: false,
851 service_workers_mode: ServiceWorkersMode::All,
852 initiator: Initiator::None,
853 destination: Destination::None,
854 origin: origin.unwrap_or(Origin::Client),
855 referrer,
856 referrer_policy: ReferrerPolicy::EmptyString,
857 pipeline_id,
858 target_webview_id: webview_id,
859 synchronous: false,
860 mode: RequestMode::NoCors,
861 use_cors_preflight: false,
862 credentials_mode: CredentialsMode::CredentialsSameOrigin,
863 use_url_credentials: false,
864 cache_mode: CacheMode::Default,
865 redirect_mode: RedirectMode::Follow,
866 integrity_metadata: String::new(),
867 cryptographic_nonce_metadata: String::new(),
868 url_list: vec![url],
869 parser_metadata: ParserMetadata::Default,
870 redirect_count: 0,
871 response_tainting: ResponseTainting::Basic,
872 policy_container: RequestPolicyContainer::Client,
873 insecure_requests_policy: InsecureRequestsPolicy::DoNotUpgrade,
874 has_trustworthy_ancestor_origin: false,
875 https_state,
876 crash: None,
877 }
878 }
879
880 pub fn url(&self) -> ServoUrl {
882 self.url_list.first().unwrap().clone()
883 }
884
885 pub fn original_url(&self) -> ServoUrl {
886 match self.mode {
887 RequestMode::WebSocket {
888 protocols: _,
889 ref original_url,
890 } => original_url.clone(),
891 _ => self.url(),
892 }
893 }
894
895 pub fn current_url(&self) -> ServoUrl {
897 self.url_list.last().unwrap().clone()
898 }
899
900 pub fn current_url_mut(&mut self) -> &mut ServoUrl {
902 self.url_list.last_mut().unwrap()
903 }
904
905 pub fn is_navigation_request(&self) -> bool {
907 matches!(
908 self.destination,
909 Destination::Document |
910 Destination::Embed |
911 Destination::Frame |
912 Destination::IFrame |
913 Destination::Object
914 )
915 }
916
917 pub fn is_subresource_request(&self) -> bool {
919 matches!(
920 self.destination,
921 Destination::Audio |
922 Destination::Font |
923 Destination::Image |
924 Destination::Manifest |
925 Destination::Script |
926 Destination::Style |
927 Destination::Track |
928 Destination::Video |
929 Destination::Xslt |
930 Destination::None
931 )
932 }
933
934 pub fn timing_type(&self) -> ResourceTimingType {
935 if self.is_navigation_request() {
936 ResourceTimingType::Navigation
937 } else {
938 ResourceTimingType::Resource
939 }
940 }
941
942 pub fn populate_request_from_client(&mut self) {
944 if self.traversable_for_user_prompts == TraversableForUserPrompts::Client {
946 self.traversable_for_user_prompts = TraversableForUserPrompts::NoTraversable;
948 if self.client.is_some() {
950 self.traversable_for_user_prompts =
955 TraversableForUserPrompts::TraversableNavigable(Default::default());
956 }
957 }
958 if self.origin == Origin::Client {
960 let Some(client) = self.client.as_ref() else {
961 unreachable!();
963 };
964 self.origin = client.origin.clone();
966 }
967 if matches!(self.policy_container, RequestPolicyContainer::Client) {
969 if let Some(client) = self.client.as_ref() {
972 self.policy_container = client.policy_container.clone();
973 } else {
974 self.policy_container =
976 RequestPolicyContainer::PolicyContainer(PolicyContainer::default());
977 }
978 }
979 }
980
981 pub fn keep_alive_body_length(&self) -> u64 {
983 assert!(self.keep_alive);
984 self.body.body_length() as u64
985 }
986
987 pub fn total_request_length(&self) -> usize {
989 let mut total_request_length = self.url()[..Position::AfterQuery].len();
991 total_request_length += self
993 .referrer
994 .to_url()
995 .map(|url| url.as_str().len())
996 .unwrap_or_default();
997 total_request_length += self.headers.total_size();
1000 total_request_length += self.body.body_length();
1002 total_request_length
1004 }
1005
1006 pub fn redirect_taint_for_request(&self) -> RedirectTaint {
1008 let Origin::Origin(request_origin) = &self.origin else {
1010 unreachable!("origin cannot be \"client\" at this point in time");
1011 };
1012
1013 let mut last_url = None;
1015
1016 let mut taint = RedirectTaint::SameOrigin;
1018
1019 for url in &self.url_list {
1021 let Some(last_url) = &mut last_url else {
1023 last_url = Some(url);
1024 continue;
1025 };
1026
1027 if !is_same_site(&url.origin(), &last_url.origin()) &&
1030 !is_same_site(request_origin, &last_url.origin())
1031 {
1032 return RedirectTaint::CrossSite;
1033 }
1034
1035 if url.origin() != last_url.origin() && *request_origin != last_url.origin() {
1038 taint = RedirectTaint::SameSite;
1039 }
1040
1041 *last_url = url;
1043 }
1044
1045 taint
1047 }
1048}
1049
1050impl Referrer {
1051 pub fn to_url(&self) -> Option<&ServoUrl> {
1052 match *self {
1053 Referrer::NoReferrer => None,
1054 Referrer::Client(ref url) => Some(url),
1055 Referrer::ReferrerUrl(ref url) => Some(url),
1056 }
1057 }
1058}
1059
1060fn is_cors_unsafe_request_header_byte(value: &u8) -> bool {
1064 matches!(value,
1065 0x00..=0x08 |
1066 0x10..=0x19 |
1067 0x22 |
1068 0x28 |
1069 0x29 |
1070 0x3A |
1071 0x3C |
1072 0x3E |
1073 0x3F |
1074 0x40 |
1075 0x5B |
1076 0x5C |
1077 0x5D |
1078 0x7B |
1079 0x7D |
1080 0x7F
1081 )
1082}
1083
1084fn is_cors_safelisted_request_accept(value: &[u8]) -> bool {
1087 !(value.iter().any(is_cors_unsafe_request_header_byte))
1088}
1089
1090fn is_cors_safelisted_language(value: &[u8]) -> bool {
1093 value.iter().all(|&x| {
1094 matches!(x,
1095 0x30..=0x39 |
1096 0x41..=0x5A |
1097 0x61..=0x7A |
1098 0x20 |
1099 0x2A |
1100 0x2C |
1101 0x2D |
1102 0x2E |
1103 0x3B |
1104 0x3D
1105 )
1106 })
1107}
1108
1109pub fn is_cors_safelisted_request_content_type(value: &[u8]) -> bool {
1112 if value.iter().any(is_cors_unsafe_request_header_byte) {
1114 return false;
1115 }
1116 let value_string = if let Ok(s) = std::str::from_utf8(value) {
1118 s
1119 } else {
1120 return false;
1121 };
1122 let value_mime_result: Result<Mime, _> = value_string.parse();
1123 match value_mime_result {
1124 Err(_) => false, Ok(value_mime) => match (value_mime.type_(), value_mime.subtype()) {
1126 (mime::APPLICATION, mime::WWW_FORM_URLENCODED) |
1127 (mime::MULTIPART, mime::FORM_DATA) |
1128 (mime::TEXT, mime::PLAIN) => true,
1129 _ => false, },
1131 }
1132}
1133
1134pub fn is_cors_safelisted_request_header<N: AsRef<str>, V: AsRef<[u8]>>(
1138 name: &N,
1139 value: &V,
1140) -> bool {
1141 let name: &str = name.as_ref();
1142 let value: &[u8] = value.as_ref();
1143 if value.len() > 128 {
1144 return false;
1145 }
1146 match name {
1147 "accept" => is_cors_safelisted_request_accept(value),
1148 "accept-language" | "content-language" => is_cors_safelisted_language(value),
1149 "content-type" => is_cors_safelisted_request_content_type(value),
1150 "range" => is_cors_safelisted_request_range(value),
1151 _ => false,
1152 }
1153}
1154
1155pub fn is_cors_safelisted_request_range(value: &[u8]) -> bool {
1156 if let Ok(value_str) = std::str::from_utf8(value) {
1157 return validate_range_header(value_str);
1158 }
1159 false
1160}
1161
1162fn validate_range_header(value: &str) -> bool {
1163 let trimmed = value.trim();
1164 if !trimmed.starts_with("bytes=") {
1165 return false;
1166 }
1167
1168 if let Some(range) = trimmed.strip_prefix("bytes=") {
1169 let mut parts = range.split('-');
1170 let start = parts.next();
1171 let end = parts.next();
1172
1173 if let Some(start) = start {
1174 if let Ok(start_num) = start.parse::<u64>() {
1175 return match end {
1176 Some(e) if !e.is_empty() => {
1177 e.parse::<u64>().is_ok_and(|end_num| start_num <= end_num)
1178 },
1179 _ => true,
1180 };
1181 }
1182 }
1183 }
1184 false
1185}
1186
1187pub fn is_cors_safelisted_method(method: &Method) -> bool {
1189 matches!(*method, Method::GET | Method::HEAD | Method::POST)
1190}
1191
1192pub fn is_cors_non_wildcard_request_header_name(name: &HeaderName) -> bool {
1194 name == AUTHORIZATION
1195}
1196
1197pub fn get_cors_unsafe_header_names(headers: &HeaderMap) -> Vec<HeaderName> {
1199 let mut unsafe_names: Vec<&HeaderName> = vec![];
1201 let mut potentillay_unsafe_names: Vec<&HeaderName> = vec![];
1203 let mut safelist_value_size = 0;
1205
1206 for (name, value) in headers.iter() {
1208 if !is_cors_safelisted_request_header(&name, &value) {
1209 unsafe_names.push(name);
1210 } else {
1211 potentillay_unsafe_names.push(name);
1212 safelist_value_size += value.as_ref().len();
1213 }
1214 }
1215
1216 if safelist_value_size > 1024 {
1218 unsafe_names.extend_from_slice(&potentillay_unsafe_names);
1219 }
1220
1221 convert_header_names_to_sorted_lowercase_set(unsafe_names)
1223}
1224
1225pub fn convert_header_names_to_sorted_lowercase_set(
1227 header_names: Vec<&HeaderName>,
1228) -> Vec<HeaderName> {
1229 let mut ordered_set = header_names.to_vec();
1232 ordered_set.sort_by(|a, b| a.as_str().partial_cmp(b.as_str()).unwrap());
1233 ordered_set.dedup();
1234 ordered_set.into_iter().cloned().collect()
1235}
1236
1237pub fn create_request_body_with_content(content: &str) -> RequestBody {
1238 let content_bytes = GenericSharedMemory::from_bytes(content.as_bytes());
1239 let content_len = content_bytes.len();
1240
1241 let (chunk_request_sender, chunk_request_receiver) = ipc::channel().unwrap();
1242 ROUTER.add_typed_route(
1243 chunk_request_receiver,
1244 Box::new(move |message| {
1245 let request = message.unwrap();
1246 if let BodyChunkRequest::Connect(sender) = request {
1247 let _ = sender.send(BodyChunkResponse::Chunk(content_bytes.clone()));
1248 let _ = sender.send(BodyChunkResponse::Done);
1249 }
1250 }),
1251 );
1252
1253 RequestBody::new(chunk_request_sender, BodySource::Object, Some(content_len))
1254}