1use std::sync::Arc;
6
7use content_security_policy::{self as csp};
8use http::header::{AUTHORIZATION, HeaderName};
9use http::{HeaderMap, Method};
10use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
11use ipc_channel::router::ROUTER;
12use log::error;
13use malloc_size_of_derive::MallocSizeOf;
14use mime::Mime;
15use parking_lot::Mutex;
16use rustc_hash::FxHashMap;
17use serde::{Deserialize, Serialize};
18use servo_base::generic_channel::GenericSharedMemory;
19use servo_base::id::{PipelineId, WebViewId};
20use servo_url::{ImmutableOrigin, ServoUrl};
21use tokio::sync::oneshot::Sender as TokioSender;
22use url::Position;
23use uuid::Uuid;
24
25use crate::ReferrerPolicy;
26use crate::blob_url_store::UrlWithBlobClaim;
27use crate::policy_container::{PolicyContainer, RequestPolicyContainer};
28use crate::pub_domains::is_same_site;
29use crate::resource_fetch_timing::ResourceTimingType;
30use crate::response::{RedirectTaint, Response};
31
32#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
33pub struct RequestId(pub Uuid);
35
36impl Default for RequestId {
37 fn default() -> Self {
38 Self(Uuid::new_v4())
39 }
40}
41
42#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
44pub enum Initiator {
45 None,
46 Download,
47 ImageSet,
48 Manifest,
49 XSLT,
50 Prefetch,
51 Link,
52}
53
54pub use csp::Destination;
56
57#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
59pub enum Origin {
60 Client,
61 Origin(ImmutableOrigin),
62}
63
64impl Origin {
65 pub fn is_opaque(&self) -> bool {
66 matches!(self, Origin::Origin(ImmutableOrigin::Opaque(_)))
67 }
68}
69
70#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
72pub enum Referrer {
73 NoReferrer,
74 Client(ServoUrl),
80 ReferrerUrl(ServoUrl),
81}
82
83#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
85pub enum RequestMode {
86 Navigate,
87 SameOrigin,
88 NoCors,
89 CorsMode,
90 WebSocket {
91 protocols: Vec<String>,
92 original_url: ServoUrl,
93 },
94}
95
96#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
98pub enum CredentialsMode {
99 Omit,
100 CredentialsSameOrigin,
101 Include,
102}
103
104#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
106pub enum CacheMode {
107 Default,
108 NoStore,
109 Reload,
110 NoCache,
111 ForceCache,
112 OnlyIfCached,
113}
114
115#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
117pub enum ServiceWorkersMode {
118 All,
119 None,
120}
121
122#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
124pub enum RedirectMode {
125 Follow,
126 Error,
127 Manual,
128}
129
130#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
132pub enum ResponseTainting {
133 Basic,
134 CorsTainting,
135 Opaque,
136}
137
138#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize)]
140pub enum InternalRequest {
141 Yes,
142 #[default]
143 No,
144}
145
146#[derive(Clone, Debug, Eq, Hash, Deserialize, MallocSizeOf, Serialize, PartialEq)]
148pub struct PreloadKey {
149 pub url: ServoUrl,
151 pub destination: Destination,
153 pub mode: RequestMode,
155 pub credentials_mode: CredentialsMode,
157}
158
159impl PreloadKey {
160 pub fn new(request: &RequestBuilder) -> Self {
161 Self {
162 url: request.url.url(),
163 destination: request.destination,
164 mode: request.mode.clone(),
165 credentials_mode: request.credentials_mode,
166 }
167 }
168}
169
170#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize, Hash, MallocSizeOf)]
171pub struct PreloadId(pub Uuid);
172
173impl Default for PreloadId {
174 fn default() -> Self {
175 Self(Uuid::new_v4())
176 }
177}
178
179#[derive(Debug, MallocSizeOf)]
181pub struct PreloadEntry {
182 pub integrity_metadata: String,
184 pub response: Option<Response>,
186 pub on_response_available: Option<TokioSender<Response>>,
188}
189
190impl PreloadEntry {
191 pub fn new(integrity_metadata: String) -> Self {
192 Self {
193 integrity_metadata,
194 response: None,
195 on_response_available: None,
196 }
197 }
198
199 pub fn with_response(&mut self, response: Response) {
201 if let Some(sender) = self.on_response_available.take() {
204 let _ = sender.send(response);
205 } else {
206 self.response = Some(response);
207 }
208 }
209}
210
211pub type PreloadedResources = FxHashMap<PreloadKey, PreloadId>;
212
213#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
215pub struct RequestClient {
216 pub preloaded_resources: PreloadedResources,
218 pub policy_container: PolicyContainer,
220 pub origin: Origin,
222 pub is_nested_browsing_context: bool,
224 pub insecure_requests_policy: InsecureRequestsPolicy,
226 pub has_trustworthy_ancestor_origin: bool,
228}
229
230#[derive(Clone, Copy, Default, MallocSizeOf, PartialEq)]
232pub enum SystemVisibilityState {
233 #[default]
234 Hidden,
235 Visible,
236}
237
238#[derive(Clone, Copy, Default, MallocSizeOf, PartialEq)]
240pub struct TraversableNavigable {
241 current_session_history_step: u8,
243 running_nested_apply_history_step: bool,
247 system_visibility_state: SystemVisibilityState,
249 is_created_by_web_content: bool,
251}
252
253#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
255pub enum TraversableForUserPrompts {
256 NoTraversable,
257 Client,
258 TraversableNavigable(TraversableNavigable),
259}
260
261#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
263pub enum CorsSettings {
264 Anonymous,
265 UseCredentials,
266}
267
268impl CorsSettings {
269 pub fn from_enumerated_attribute(value: &str) -> CorsSettings {
271 match value.to_ascii_lowercase().as_str() {
272 "anonymous" => CorsSettings::Anonymous,
273 "use-credentials" => CorsSettings::UseCredentials,
274 _ => CorsSettings::Anonymous,
275 }
276 }
277}
278
279#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
281pub enum ParserMetadata {
282 Default,
283 ParserInserted,
284 NotParserInserted,
285}
286
287#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
289pub enum BodySource {
290 Null,
291 Object,
292}
293
294#[derive(Debug, Deserialize, Serialize)]
297pub enum BodyChunkResponse {
298 Chunk(GenericSharedMemory),
300 Done,
302 Error,
305}
306
307#[derive(Debug, Deserialize, Serialize)]
311pub enum BodyChunkRequest {
312 Connect(IpcSender<BodyChunkResponse>),
314 Extract(IpcReceiver<BodyChunkRequest>),
316 Chunk,
318 Done,
320 Error,
322}
323
324#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
330pub struct RequestBody {
331 #[conditional_malloc_size_of]
333 body_chunk_request_channel: Arc<Mutex<Option<IpcSender<BodyChunkRequest>>>>,
334 source: BodySource,
336 total_bytes: Option<usize>,
338}
339
340impl RequestBody {
341 pub fn new(
342 body_chunk_request_channel: IpcSender<BodyChunkRequest>,
343 source: BodySource,
344 total_bytes: Option<usize>,
345 ) -> Self {
346 RequestBody {
347 body_chunk_request_channel: Arc::new(Mutex::new(Some(body_chunk_request_channel))),
348 source,
349 total_bytes,
350 }
351 }
352
353 pub fn extract_source(&mut self) {
355 match self.source {
356 BodySource::Null => panic!("Null sources should never be re-directed."),
357 BodySource::Object => {
358 let (chan, port) = ipc::channel().unwrap();
359 let mut lock = self.body_chunk_request_channel.lock();
360 let Some(selfchan) = lock.as_mut() else {
361 error!(
362 "Could not re-extract the request body source because the body stream has already been closed."
363 );
364 return;
365 };
366 if let Err(error) = selfchan.send(BodyChunkRequest::Extract(port)) {
367 error!(
368 "Could not re-extract the request body source because the body stream has already been closed: {error}"
369 );
370 return;
371 }
372 *selfchan = chan;
373 },
374 }
375 }
376
377 pub fn clone_stream(&self) -> Arc<Mutex<Option<IpcSender<BodyChunkRequest>>>> {
379 self.body_chunk_request_channel.clone()
380 }
381
382 pub fn close_stream(&self) {
387 self.body_chunk_request_channel.lock().take();
388 }
389
390 pub fn source_is_null(&self) -> bool {
391 self.source == BodySource::Null
392 }
393
394 #[expect(clippy::len_without_is_empty)]
395 pub fn len(&self) -> Option<usize> {
396 self.total_bytes
397 }
398}
399
400trait RequestBodySize {
401 fn body_length(&self) -> usize;
402}
403
404impl RequestBodySize for Option<RequestBody> {
405 fn body_length(&self) -> usize {
406 self.as_ref()
407 .and_then(|body| body.len())
408 .unwrap_or_default()
409 }
410}
411
412#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
413pub enum InsecureRequestsPolicy {
414 DoNotUpgrade,
415 Upgrade,
416}
417
418pub trait RequestHeadersSize {
419 fn total_size(&self) -> usize;
420}
421
422impl RequestHeadersSize for HeaderMap {
423 fn total_size(&self) -> usize {
424 self.iter()
425 .map(|(name, value)| name.as_str().len() + value.len())
426 .sum()
427 }
428}
429
430#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
431pub struct RequestBuilder {
432 pub id: RequestId,
433
434 pub preload_id: Option<PreloadId>,
435
436 #[serde(
438 deserialize_with = "::hyper_serde::deserialize",
439 serialize_with = "::hyper_serde::serialize"
440 )]
441 pub method: Method,
442
443 pub url: UrlWithBlobClaim,
445
446 #[serde(
448 deserialize_with = "::hyper_serde::deserialize",
449 serialize_with = "::hyper_serde::serialize"
450 )]
451 pub headers: HeaderMap,
452
453 pub unsafe_request: bool,
455
456 pub body: Option<RequestBody>,
458
459 pub service_workers_mode: ServiceWorkersMode,
461 pub client: Option<RequestClient>,
462 pub destination: Destination,
464 pub synchronous: bool,
465 pub mode: RequestMode,
466
467 pub cache_mode: CacheMode,
469
470 pub use_cors_preflight: bool,
472
473 pub keep_alive: bool,
475
476 pub credentials_mode: CredentialsMode,
478 pub use_url_credentials: bool,
479
480 pub origin: Origin,
482
483 pub policy_container: RequestPolicyContainer,
485
486 pub referrer: Referrer,
488
489 pub referrer_policy: ReferrerPolicy,
491 pub pipeline_id: Option<PipelineId>,
492 pub target_webview_id: Option<WebViewId>,
493
494 pub redirect_mode: RedirectMode,
496
497 pub integrity_metadata: String,
499
500 pub cryptographic_nonce_metadata: String,
502
503 pub url_list: Vec<ServoUrl>,
505
506 pub parser_metadata: ParserMetadata,
508
509 pub initiator: Initiator,
511 pub response_tainting: ResponseTainting,
512 pub crash: Option<String>,
514 pub is_internal_request: InternalRequest,
516}
517
518impl RequestBuilder {
519 pub fn new(
520 webview_id: Option<WebViewId>,
521 url: UrlWithBlobClaim,
522 referrer: Referrer,
523 ) -> RequestBuilder {
524 RequestBuilder {
525 id: RequestId::default(),
526 preload_id: None,
527 method: Method::GET,
528 url,
529 headers: HeaderMap::new(),
530 unsafe_request: false,
531 body: None,
532 service_workers_mode: ServiceWorkersMode::All,
533 destination: Destination::None,
534 synchronous: false,
535 mode: RequestMode::NoCors,
536 cache_mode: CacheMode::Default,
537 use_cors_preflight: false,
538 keep_alive: false,
539 credentials_mode: CredentialsMode::CredentialsSameOrigin,
540 use_url_credentials: false,
541 origin: Origin::Client,
542 client: None,
543 policy_container: RequestPolicyContainer::default(),
544 referrer,
545 referrer_policy: ReferrerPolicy::EmptyString,
546 pipeline_id: None,
547 target_webview_id: webview_id,
548 redirect_mode: RedirectMode::Follow,
549 integrity_metadata: "".to_owned(),
550 cryptographic_nonce_metadata: "".to_owned(),
551 url_list: vec![],
552 parser_metadata: ParserMetadata::Default,
553 initiator: Initiator::None,
554 response_tainting: ResponseTainting::Basic,
555 is_internal_request: Default::default(),
556 crash: None,
557 }
558 }
559
560 pub fn preload_id(mut self, preload_id: PreloadId) -> RequestBuilder {
561 self.preload_id = Some(preload_id);
562 self
563 }
564
565 pub fn initiator(mut self, initiator: Initiator) -> RequestBuilder {
567 self.initiator = initiator;
568 self
569 }
570
571 pub fn method(mut self, method: Method) -> RequestBuilder {
573 self.method = method;
574 self
575 }
576
577 pub fn headers(mut self, headers: HeaderMap) -> RequestBuilder {
579 self.headers = headers;
580 self
581 }
582
583 pub fn unsafe_request(mut self, unsafe_request: bool) -> RequestBuilder {
585 self.unsafe_request = unsafe_request;
586 self
587 }
588
589 pub fn body(mut self, body: Option<RequestBody>) -> RequestBuilder {
591 self.body = body;
592 self
593 }
594
595 pub fn destination(mut self, destination: Destination) -> RequestBuilder {
597 self.destination = destination;
598 self
599 }
600
601 pub fn synchronous(mut self, synchronous: bool) -> RequestBuilder {
602 self.synchronous = synchronous;
603 self
604 }
605
606 pub fn mode(mut self, mode: RequestMode) -> RequestBuilder {
607 self.mode = mode;
608 self
609 }
610
611 pub fn use_cors_preflight(mut self, use_cors_preflight: bool) -> RequestBuilder {
613 self.use_cors_preflight = use_cors_preflight;
614 self
615 }
616
617 pub fn keep_alive(mut self, keep_alive: bool) -> RequestBuilder {
619 self.keep_alive = keep_alive;
620 self
621 }
622
623 pub fn credentials_mode(mut self, credentials_mode: CredentialsMode) -> RequestBuilder {
625 self.credentials_mode = credentials_mode;
626 self
627 }
628
629 pub fn use_url_credentials(mut self, use_url_credentials: bool) -> RequestBuilder {
630 self.use_url_credentials = use_url_credentials;
631 self
632 }
633
634 pub fn origin(mut self, origin: ImmutableOrigin) -> RequestBuilder {
636 self.origin = Origin::Origin(origin);
637 self
638 }
639
640 pub fn referrer_policy(mut self, referrer_policy: ReferrerPolicy) -> RequestBuilder {
642 self.referrer_policy = referrer_policy;
643 self
644 }
645
646 pub fn url_list(mut self, url_list: Vec<ServoUrl>) -> RequestBuilder {
648 self.url_list = url_list;
649 self
650 }
651
652 pub fn pipeline_id(mut self, pipeline_id: Option<PipelineId>) -> RequestBuilder {
653 self.pipeline_id = pipeline_id;
654 self
655 }
656
657 pub fn redirect_mode(mut self, redirect_mode: RedirectMode) -> RequestBuilder {
659 self.redirect_mode = redirect_mode;
660 self
661 }
662
663 pub fn integrity_metadata(mut self, integrity_metadata: String) -> RequestBuilder {
665 self.integrity_metadata = integrity_metadata;
666 self
667 }
668
669 pub fn cryptographic_nonce_metadata(mut self, nonce_metadata: String) -> RequestBuilder {
671 self.cryptographic_nonce_metadata = nonce_metadata;
672 self
673 }
674
675 pub fn parser_metadata(mut self, parser_metadata: ParserMetadata) -> RequestBuilder {
677 self.parser_metadata = parser_metadata;
678 self
679 }
680
681 pub fn response_tainting(mut self, response_tainting: ResponseTainting) -> RequestBuilder {
682 self.response_tainting = response_tainting;
683 self
684 }
685
686 pub fn crash(mut self, crash: Option<String>) -> Self {
687 self.crash = crash;
688 self
689 }
690
691 pub fn policy_container(mut self, policy_container: PolicyContainer) -> RequestBuilder {
693 self.policy_container = RequestPolicyContainer::PolicyContainer(policy_container);
694 self
695 }
696
697 pub fn client(mut self, client: RequestClient) -> RequestBuilder {
699 self.client = Some(client);
700 self
701 }
702
703 pub fn service_workers_mode(
705 mut self,
706 service_workers_mode: ServiceWorkersMode,
707 ) -> RequestBuilder {
708 self.service_workers_mode = service_workers_mode;
709 self
710 }
711
712 pub fn cache_mode(mut self, cache_mode: CacheMode) -> RequestBuilder {
714 self.cache_mode = cache_mode;
715 self
716 }
717
718 pub fn is_internal_request(mut self, is_internal_request: InternalRequest) -> RequestBuilder {
719 self.is_internal_request = is_internal_request;
720 self
721 }
722
723 pub fn build(self) -> Request {
724 let mut request = Request::new(
725 self.id,
726 self.url.clone(),
727 Some(self.origin),
728 self.referrer,
729 self.pipeline_id,
730 self.target_webview_id,
731 );
732 request.preload_id = self.preload_id;
733 request.initiator = self.initiator;
734 request.method = self.method;
735 request.headers = self.headers;
736 request.unsafe_request = self.unsafe_request;
737 request.body = self.body;
738 request.service_workers_mode = self.service_workers_mode;
739 request.destination = self.destination;
740 request.synchronous = self.synchronous;
741 request.mode = self.mode;
742 request.use_cors_preflight = self.use_cors_preflight;
743 request.keep_alive = self.keep_alive;
744 request.credentials_mode = self.credentials_mode;
745 request.use_url_credentials = self.use_url_credentials;
746 request.cache_mode = self.cache_mode;
747 request.referrer_policy = self.referrer_policy;
748 request.redirect_mode = self.redirect_mode;
749 let mut url_list: Vec<_> = self
750 .url_list
751 .into_iter()
752 .map(UrlWithBlobClaim::from_url_without_having_claimed_blob)
753 .collect();
754 if url_list.is_empty() {
755 url_list.push(self.url);
756 }
757 request.redirect_count = url_list.len() as u32 - 1;
758 request.url_list = url_list;
759 request.integrity_metadata = self.integrity_metadata;
760 request.cryptographic_nonce_metadata = self.cryptographic_nonce_metadata;
761 request.parser_metadata = self.parser_metadata;
762 request.response_tainting = self.response_tainting;
763 request.crash = self.crash;
764 request.client = self.client;
765 request.policy_container = self.policy_container;
766 request.is_internal_request = self.is_internal_request;
767 request
768 }
769
770 pub fn keep_alive_body_length(&self) -> u64 {
772 assert!(self.keep_alive);
773 self.body.body_length() as u64
774 }
775}
776
777#[derive(Clone, MallocSizeOf)]
780pub struct Request {
781 pub id: RequestId,
785 pub preload_id: Option<PreloadId>,
786 pub method: Method,
788 pub local_urls_only: bool,
790 pub headers: HeaderMap,
792 pub unsafe_request: bool,
794 pub body: Option<RequestBody>,
796 pub client: Option<RequestClient>,
798 pub traversable_for_user_prompts: TraversableForUserPrompts,
800 pub target_webview_id: Option<WebViewId>,
801 pub keep_alive: bool,
803 pub service_workers_mode: ServiceWorkersMode,
805 pub initiator: Initiator,
807 pub destination: Destination,
809 pub origin: Origin,
812 pub referrer: Referrer,
814 pub referrer_policy: ReferrerPolicy,
816 pub pipeline_id: Option<PipelineId>,
817 pub synchronous: bool,
819 pub mode: RequestMode,
821 pub use_cors_preflight: bool,
823 pub credentials_mode: CredentialsMode,
825 pub use_url_credentials: bool,
827 pub cache_mode: CacheMode,
829 pub redirect_mode: RedirectMode,
831 pub integrity_metadata: String,
833 pub cryptographic_nonce_metadata: String,
835 pub url_list: Vec<UrlWithBlobClaim>,
837 pub redirect_count: u32,
839 pub response_tainting: ResponseTainting,
841 pub parser_metadata: ParserMetadata,
843 pub policy_container: RequestPolicyContainer,
845 pub crash: Option<String>,
847 pub is_internal_request: InternalRequest,
849}
850
851impl Request {
852 pub fn new(
853 id: RequestId,
854 url: UrlWithBlobClaim,
855 origin: Option<Origin>,
856 referrer: Referrer,
857 pipeline_id: Option<PipelineId>,
858 webview_id: Option<WebViewId>,
859 ) -> Request {
860 Request {
861 id,
862 preload_id: None,
863 method: Method::GET,
864 local_urls_only: false,
865 headers: HeaderMap::new(),
866 unsafe_request: false,
867 body: None,
868 client: None,
869 traversable_for_user_prompts: TraversableForUserPrompts::Client,
870 keep_alive: false,
871 service_workers_mode: ServiceWorkersMode::All,
872 initiator: Initiator::None,
873 destination: Destination::None,
874 origin: origin.unwrap_or(Origin::Client),
875 referrer,
876 referrer_policy: ReferrerPolicy::EmptyString,
877 pipeline_id,
878 target_webview_id: webview_id,
879 synchronous: false,
880 mode: RequestMode::NoCors,
881 use_cors_preflight: false,
882 credentials_mode: CredentialsMode::CredentialsSameOrigin,
883 use_url_credentials: false,
884 cache_mode: CacheMode::Default,
885 redirect_mode: RedirectMode::Follow,
886 integrity_metadata: String::new(),
887 cryptographic_nonce_metadata: String::new(),
888 url_list: vec![url],
889 parser_metadata: ParserMetadata::Default,
890 redirect_count: 0,
891 response_tainting: ResponseTainting::Basic,
892 policy_container: RequestPolicyContainer::Client,
893 is_internal_request: Default::default(),
894 crash: None,
895 }
896 }
897
898 pub fn url(&self) -> ServoUrl {
900 self.url_list.first().unwrap().url()
901 }
902
903 pub fn url_with_blob_claim(&self) -> UrlWithBlobClaim {
904 self.url_list.first().unwrap().clone()
905 }
906
907 pub fn original_url(&self) -> ServoUrl {
908 match self.mode {
909 RequestMode::WebSocket {
910 protocols: _,
911 ref original_url,
912 } => original_url.clone(),
913 _ => self.url(),
914 }
915 }
916
917 pub fn current_url(&self) -> ServoUrl {
919 self.current_url_with_blob_claim().url()
920 }
921
922 pub fn current_url_with_blob_claim(&self) -> UrlWithBlobClaim {
924 self.url_list.last().unwrap().clone()
925 }
926
927 pub fn current_url_mut(&mut self) -> &mut ServoUrl {
929 self.url_list.last_mut().unwrap()
930 }
931
932 pub fn is_navigation_request(&self) -> bool {
934 matches!(
935 self.destination,
936 Destination::Document |
937 Destination::Embed |
938 Destination::Frame |
939 Destination::IFrame |
940 Destination::Object
941 )
942 }
943
944 pub fn is_subresource_request(&self) -> bool {
946 matches!(
947 self.destination,
948 Destination::Audio |
949 Destination::Font |
950 Destination::Image |
951 Destination::Manifest |
952 Destination::Script |
953 Destination::Style |
954 Destination::Track |
955 Destination::Video |
956 Destination::Xslt |
957 Destination::None
958 )
959 }
960
961 pub fn timing_type(&self) -> ResourceTimingType {
962 if self.is_navigation_request() {
963 ResourceTimingType::Navigation
964 } else {
965 ResourceTimingType::Resource
966 }
967 }
968
969 pub fn populate_request_from_client(&mut self) {
971 if self.traversable_for_user_prompts == TraversableForUserPrompts::Client {
973 self.traversable_for_user_prompts = TraversableForUserPrompts::NoTraversable;
975 if self.client.is_some() {
977 self.traversable_for_user_prompts =
982 TraversableForUserPrompts::TraversableNavigable(Default::default());
983 }
984 }
985 if self.origin == Origin::Client {
987 let Some(client) = self.client.as_ref() else {
988 unreachable!();
990 };
991 self.origin = client.origin.clone();
993 }
994 if matches!(self.policy_container, RequestPolicyContainer::Client) {
996 if let Some(client) = self.client.as_ref() {
999 self.policy_container =
1000 RequestPolicyContainer::PolicyContainer(client.policy_container.clone());
1001 } else {
1002 self.policy_container =
1004 RequestPolicyContainer::PolicyContainer(PolicyContainer::default());
1005 }
1006 }
1007 }
1008
1009 pub fn keep_alive_body_length(&self) -> u64 {
1011 assert!(self.keep_alive);
1012 self.body.body_length() as u64
1013 }
1014
1015 pub fn total_request_length(&self) -> usize {
1017 let mut total_request_length = self.url()[..Position::AfterQuery].len();
1019 total_request_length += self
1021 .referrer
1022 .to_url()
1023 .map(|url| url.as_str().len())
1024 .unwrap_or_default();
1025 total_request_length += self.headers.total_size();
1028 total_request_length += self.body.body_length();
1030 total_request_length
1032 }
1033
1034 pub fn redirect_taint_for_request(&self) -> RedirectTaint {
1036 let Origin::Origin(request_origin) = &self.origin else {
1038 unreachable!("origin cannot be \"client\" at this point in time");
1039 };
1040
1041 let mut last_url = None;
1043
1044 let mut taint = RedirectTaint::SameOrigin;
1046
1047 for url in &self.url_list {
1049 let Some(last_url) = &mut last_url else {
1051 last_url = Some(url);
1052 continue;
1053 };
1054
1055 if !is_same_site(&url.origin(), &last_url.origin()) &&
1058 !is_same_site(request_origin, &last_url.origin())
1059 {
1060 return RedirectTaint::CrossSite;
1061 }
1062
1063 if url.origin() != last_url.origin() && *request_origin != last_url.origin() {
1066 taint = RedirectTaint::SameSite;
1067 }
1068
1069 *last_url = url;
1071 }
1072
1073 taint
1075 }
1076}
1077
1078impl Referrer {
1079 pub fn to_url(&self) -> Option<&ServoUrl> {
1080 match *self {
1081 Referrer::NoReferrer => None,
1082 Referrer::Client(ref url) => Some(url),
1083 Referrer::ReferrerUrl(ref url) => Some(url),
1084 }
1085 }
1086}
1087
1088fn is_cors_unsafe_request_header_byte(value: &u8) -> bool {
1092 matches!(value,
1093 0x00..=0x08 |
1094 0x10..=0x19 |
1095 0x22 |
1096 0x28 |
1097 0x29 |
1098 0x3A |
1099 0x3C |
1100 0x3E |
1101 0x3F |
1102 0x40 |
1103 0x5B |
1104 0x5C |
1105 0x5D |
1106 0x7B |
1107 0x7D |
1108 0x7F
1109 )
1110}
1111
1112fn is_cors_safelisted_request_accept(value: &[u8]) -> bool {
1115 !(value.iter().any(is_cors_unsafe_request_header_byte))
1116}
1117
1118fn is_cors_safelisted_language(value: &[u8]) -> bool {
1121 value.iter().all(|&x| {
1122 matches!(x,
1123 0x30..=0x39 |
1124 0x41..=0x5A |
1125 0x61..=0x7A |
1126 0x20 |
1127 0x2A |
1128 0x2C |
1129 0x2D |
1130 0x2E |
1131 0x3B |
1132 0x3D
1133 )
1134 })
1135}
1136
1137pub fn is_cors_safelisted_request_content_type(value: &[u8]) -> bool {
1140 if value.iter().any(is_cors_unsafe_request_header_byte) {
1142 return false;
1143 }
1144 let value_string = if let Ok(s) = std::str::from_utf8(value) {
1146 s
1147 } else {
1148 return false;
1149 };
1150 let value_mime_result: Result<Mime, _> = value_string.parse();
1151 match value_mime_result {
1152 Err(_) => false, Ok(value_mime) => match (value_mime.type_(), value_mime.subtype()) {
1154 (mime::APPLICATION, mime::WWW_FORM_URLENCODED) |
1155 (mime::MULTIPART, mime::FORM_DATA) |
1156 (mime::TEXT, mime::PLAIN) => true,
1157 _ => false, },
1159 }
1160}
1161
1162pub fn is_cors_safelisted_request_header<N: AsRef<str>, V: AsRef<[u8]>>(
1166 name: &N,
1167 value: &V,
1168) -> bool {
1169 let name: &str = name.as_ref();
1170 let value: &[u8] = value.as_ref();
1171 if value.len() > 128 {
1172 return false;
1173 }
1174 match name {
1175 "accept" => is_cors_safelisted_request_accept(value),
1176 "accept-language" | "content-language" => is_cors_safelisted_language(value),
1177 "content-type" => is_cors_safelisted_request_content_type(value),
1178 "range" => is_cors_safelisted_request_range(value),
1179 _ => false,
1180 }
1181}
1182
1183pub fn is_cors_safelisted_request_range(value: &[u8]) -> bool {
1184 if let Ok(value_str) = std::str::from_utf8(value) {
1185 return validate_range_header(value_str);
1186 }
1187 false
1188}
1189
1190fn validate_range_header(value: &str) -> bool {
1191 let trimmed = value.trim();
1192 if !trimmed.starts_with("bytes=") {
1193 return false;
1194 }
1195
1196 if let Some(range) = trimmed.strip_prefix("bytes=") {
1197 let mut parts = range.split('-');
1198 let start = parts.next();
1199 let end = parts.next();
1200
1201 if let Some(start) = start &&
1202 let Ok(start_num) = start.parse::<u64>()
1203 {
1204 return match end {
1205 Some(e) if !e.is_empty() => {
1206 e.parse::<u64>().is_ok_and(|end_num| start_num <= end_num)
1207 },
1208 _ => true,
1209 };
1210 }
1211 }
1212 false
1213}
1214
1215pub fn is_cors_safelisted_method(method: &Method) -> bool {
1217 matches!(*method, Method::GET | Method::HEAD | Method::POST)
1218}
1219
1220pub fn is_cors_non_wildcard_request_header_name(name: &HeaderName) -> bool {
1222 name == AUTHORIZATION
1223}
1224
1225pub fn get_cors_unsafe_header_names(headers: &HeaderMap) -> Vec<HeaderName> {
1227 let mut unsafe_names: Vec<&HeaderName> = vec![];
1229 let mut potentillay_unsafe_names: Vec<&HeaderName> = vec![];
1231 let mut safelist_value_size = 0;
1233
1234 for (name, value) in headers.iter() {
1236 if !is_cors_safelisted_request_header(&name, &value) {
1237 unsafe_names.push(name);
1238 } else {
1239 potentillay_unsafe_names.push(name);
1240 safelist_value_size += value.as_ref().len();
1241 }
1242 }
1243
1244 if safelist_value_size > 1024 {
1246 unsafe_names.extend_from_slice(&potentillay_unsafe_names);
1247 }
1248
1249 convert_header_names_to_sorted_lowercase_set(unsafe_names)
1251}
1252
1253pub fn convert_header_names_to_sorted_lowercase_set(
1255 header_names: Vec<&HeaderName>,
1256) -> Vec<HeaderName> {
1257 let mut ordered_set = header_names.to_vec();
1260 ordered_set.sort_by(|a, b| a.as_str().partial_cmp(b.as_str()).unwrap());
1261 ordered_set.dedup();
1262 ordered_set.into_iter().cloned().collect()
1263}
1264
1265pub fn create_request_body_with_content(content: String) -> RequestBody {
1266 let content_bytes = GenericSharedMemory::from_vec(content.into_bytes());
1267 let content_len = content_bytes.len();
1268
1269 let (chunk_request_sender, chunk_request_receiver) = ipc::channel().unwrap();
1270 ROUTER.add_typed_route(
1271 chunk_request_receiver,
1272 Box::new(move |message| {
1273 let request = message.unwrap();
1274 if let BodyChunkRequest::Connect(sender) = request {
1275 let _ = sender.send(BodyChunkResponse::Chunk(content_bytes.clone()));
1276 let _ = sender.send(BodyChunkResponse::Done);
1277 }
1278 }),
1279 );
1280
1281 RequestBody::new(chunk_request_sender, BodySource::Object, Some(content_len))
1282}