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::{Site, is_same_site};
29use crate::resource_fetch_timing::ResourceTimingType;
30use crate::response::{RedirectTaint, Response};
31
32pub type NetworkPartitionKey = Option<Site>;
35
36#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
37pub struct RequestId(pub Uuid);
39
40impl Default for RequestId {
41 fn default() -> Self {
42 Self(Uuid::new_v4())
43 }
44}
45
46#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
48pub enum Initiator {
49 None,
50 Download,
51 ImageSet,
52 Manifest,
53 XSLT,
54 Prefetch,
55 Link,
56}
57
58pub use csp::Destination;
60
61#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
63pub enum Origin {
64 Client,
65 Origin(ImmutableOrigin),
66}
67
68impl Origin {
69 pub fn is_opaque(&self) -> bool {
70 matches!(self, Origin::Origin(ImmutableOrigin::Opaque(_)))
71 }
72}
73
74#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
76pub enum Referrer {
77 NoReferrer,
78 Client(ServoUrl),
84 ReferrerUrl(ServoUrl),
85}
86
87#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
89pub enum RequestMode {
90 Navigate,
91 SameOrigin,
92 NoCors,
93 CorsMode,
94 WebSocket {
95 protocols: Vec<String>,
96 original_url: ServoUrl,
97 },
98}
99
100#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
102pub enum CredentialsMode {
103 Omit,
104 CredentialsSameOrigin,
105 Include,
106}
107
108#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
110pub enum CacheMode {
111 Default,
112 NoStore,
113 Reload,
114 NoCache,
115 ForceCache,
116 OnlyIfCached,
117}
118
119#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
121pub enum ServiceWorkersMode {
122 All,
123 None,
124}
125
126#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
128pub enum RedirectMode {
129 Follow,
130 Error,
131 Manual,
132}
133
134#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
136pub enum ResponseTainting {
137 Basic,
138 CorsTainting,
139 Opaque,
140}
141
142#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize)]
144pub enum InternalRequest {
145 Yes,
146 #[default]
147 No,
148}
149
150#[derive(Clone, Debug, Eq, Hash, Deserialize, MallocSizeOf, Serialize, PartialEq)]
152pub struct PreloadKey {
153 pub url: ServoUrl,
155 pub destination: Destination,
157 pub mode: RequestMode,
159 pub credentials_mode: CredentialsMode,
161}
162
163impl PreloadKey {
164 pub fn new(request: &RequestBuilder) -> Self {
165 Self {
166 url: request.url.url(),
167 destination: request.destination,
168 mode: request.mode.clone(),
169 credentials_mode: request.credentials_mode,
170 }
171 }
172}
173
174#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize, Hash, MallocSizeOf)]
175pub struct PreloadId(pub Uuid);
176
177impl Default for PreloadId {
178 fn default() -> Self {
179 Self(Uuid::new_v4())
180 }
181}
182
183#[derive(Debug, MallocSizeOf)]
185pub struct PreloadEntry {
186 pub integrity_metadata: String,
188 pub response: Option<Response>,
190 pub on_response_available: Option<TokioSender<Response>>,
192}
193
194impl PreloadEntry {
195 pub fn new(integrity_metadata: String) -> Self {
196 Self {
197 integrity_metadata,
198 response: None,
199 on_response_available: None,
200 }
201 }
202
203 pub fn with_response(&mut self, response: Response) {
205 if let Some(sender) = self.on_response_available.take() {
208 let _ = sender.send(response);
209 } else {
210 self.response = Some(response);
211 }
212 }
213}
214
215pub type PreloadedResources = FxHashMap<PreloadKey, PreloadId>;
216
217#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
219pub struct RequestClient {
220 pub preloaded_resources: PreloadedResources,
222 #[conditional_malloc_size_of]
224 pub policy_container: Arc<PolicyContainer>,
225 pub origin: Origin,
227 pub is_nested_browsing_context: bool,
229 pub insecure_requests_policy: InsecureRequestsPolicy,
231 pub has_trustworthy_ancestor_origin: bool,
233 pub network_partition_key: NetworkPartitionKey,
235}
236
237#[derive(Clone, Copy, Default, MallocSizeOf, PartialEq)]
239pub enum SystemVisibilityState {
240 #[default]
241 Hidden,
242 Visible,
243}
244
245#[derive(Clone, Copy, Default, MallocSizeOf, PartialEq)]
247pub struct TraversableNavigable {
248 current_session_history_step: u8,
250 running_nested_apply_history_step: bool,
254 system_visibility_state: SystemVisibilityState,
256 is_created_by_web_content: bool,
258}
259
260#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
262pub enum TraversableForUserPrompts {
263 NoTraversable,
264 Client,
265 TraversableNavigable(TraversableNavigable),
266}
267
268#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
270pub enum CorsSettings {
271 Anonymous,
272 UseCredentials,
273}
274
275impl CorsSettings {
276 pub fn from_enumerated_attribute(value: &str) -> CorsSettings {
278 if value.eq_ignore_ascii_case("use-credentials") {
279 CorsSettings::UseCredentials
280 } else {
281 CorsSettings::Anonymous
282 }
283 }
284}
285
286#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
288pub enum ParserMetadata {
289 Default,
290 ParserInserted,
291 NotParserInserted,
292}
293
294#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
296pub enum BodySource {
297 Null,
298 Object,
299}
300
301#[derive(Debug, Deserialize, Serialize)]
304pub enum BodyChunkResponse {
305 Chunk(GenericSharedMemory),
307 Done,
309 Error,
312}
313
314#[derive(Debug, Deserialize, Serialize)]
319pub enum BodyChunkRequest {
320 Connect(IpcSender<BodyChunkResponse>),
322 Extract(IpcReceiver<BodyChunkRequest>),
324 Chunk,
326}
327
328#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
334pub struct RequestBody {
335 #[conditional_malloc_size_of]
337 body_chunk_request_channel: Arc<Mutex<Option<IpcSender<BodyChunkRequest>>>>,
338 source: BodySource,
340 total_bytes: Option<usize>,
342}
343
344impl RequestBody {
345 pub fn new(
346 body_chunk_request_channel: IpcSender<BodyChunkRequest>,
347 source: BodySource,
348 total_bytes: Option<usize>,
349 ) -> Self {
350 RequestBody {
351 body_chunk_request_channel: Arc::new(Mutex::new(Some(body_chunk_request_channel))),
352 source,
353 total_bytes,
354 }
355 }
356
357 pub fn extract_source(&mut self) {
359 match self.source {
360 BodySource::Null => panic!("Null sources should never be re-directed."),
361 BodySource::Object => {
362 let (chan, port) = ipc::channel().unwrap();
363 let mut lock = self.body_chunk_request_channel.lock();
364 let Some(selfchan) = lock.as_mut() else {
365 error!(
366 "Could not re-extract the request body source because the body stream has already been closed."
367 );
368 return;
369 };
370 if let Err(error) = selfchan.send(BodyChunkRequest::Extract(port)) {
371 error!(
372 "Could not re-extract the request body source because the body stream has already been closed: {error}"
373 );
374 return;
375 }
376 *selfchan = chan;
377 },
378 }
379 }
380
381 pub fn clone_stream(&self) -> Arc<Mutex<Option<IpcSender<BodyChunkRequest>>>> {
383 self.body_chunk_request_channel.clone()
384 }
385
386 pub fn close_stream(&self) {
391 self.body_chunk_request_channel.lock().take();
392 }
393
394 pub fn source_is_null(&self) -> bool {
395 self.source == BodySource::Null
396 }
397
398 #[expect(clippy::len_without_is_empty)]
399 pub fn len(&self) -> Option<usize> {
400 self.total_bytes
401 }
402}
403
404trait RequestBodySize {
405 fn body_length(&self) -> usize;
406}
407
408impl RequestBodySize for Option<RequestBody> {
409 fn body_length(&self) -> usize {
410 self.as_ref()
411 .and_then(|body| body.len())
412 .unwrap_or_default()
413 }
414}
415
416#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
417pub enum InsecureRequestsPolicy {
418 DoNotUpgrade,
419 Upgrade,
420}
421
422pub trait RequestHeadersSize {
423 fn total_size(&self) -> usize;
424}
425
426impl RequestHeadersSize for HeaderMap {
427 fn total_size(&self) -> usize {
428 self.iter()
429 .map(|(name, value)| name.as_str().len() + value.len())
430 .sum()
431 }
432}
433
434#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
435pub struct RequestBuilder {
436 pub id: RequestId,
437
438 pub preload_id: Option<PreloadId>,
439
440 #[serde(
442 deserialize_with = "::hyper_serde::deserialize",
443 serialize_with = "::hyper_serde::serialize"
444 )]
445 pub method: Method,
446
447 pub url: UrlWithBlobClaim,
449
450 #[serde(
452 deserialize_with = "::hyper_serde::deserialize",
453 serialize_with = "::hyper_serde::serialize"
454 )]
455 pub headers: HeaderMap,
456
457 pub unsafe_request: bool,
459
460 pub body: Option<RequestBody>,
462 pub reload_navigation: bool,
465 pub history_navigation: bool,
468
469 pub service_workers_mode: ServiceWorkersMode,
471 pub client: Option<RequestClient>,
472 pub destination: Destination,
474 pub synchronous: bool,
475 pub mode: RequestMode,
476
477 pub cache_mode: CacheMode,
479
480 pub use_cors_preflight: bool,
482
483 pub keep_alive: bool,
485
486 pub credentials_mode: CredentialsMode,
488 pub use_url_credentials: bool,
489
490 pub origin: Origin,
492
493 pub policy_container: RequestPolicyContainer,
495
496 pub referrer: Referrer,
498
499 pub referrer_policy: ReferrerPolicy,
501 pub pipeline_id: Option<PipelineId>,
502 pub target_webview_id: Option<WebViewId>,
503
504 pub redirect_mode: RedirectMode,
506
507 pub integrity_metadata: String,
509
510 pub cryptographic_nonce_metadata: String,
512
513 pub url_list: Vec<UrlWithBlobClaim>,
515
516 pub parser_metadata: ParserMetadata,
518
519 pub initiator: Initiator,
521 pub response_tainting: ResponseTainting,
522 pub crash: Option<String>,
524 pub is_internal_request: InternalRequest,
526}
527
528impl RequestBuilder {
529 pub fn new(
530 webview_id: Option<WebViewId>,
531 url: UrlWithBlobClaim,
532 referrer: Referrer,
533 ) -> RequestBuilder {
534 RequestBuilder {
535 id: RequestId::default(),
536 preload_id: None,
537 method: Method::GET,
538 url,
539 headers: HeaderMap::new(),
540 unsafe_request: false,
541 body: None,
542 reload_navigation: false,
543 history_navigation: false,
544 service_workers_mode: ServiceWorkersMode::All,
545 destination: Destination::None,
546 synchronous: false,
547 mode: RequestMode::NoCors,
548 cache_mode: CacheMode::Default,
549 use_cors_preflight: false,
550 keep_alive: false,
551 credentials_mode: CredentialsMode::CredentialsSameOrigin,
552 use_url_credentials: false,
553 origin: Origin::Client,
554 client: None,
555 policy_container: RequestPolicyContainer::default(),
556 referrer,
557 referrer_policy: ReferrerPolicy::EmptyString,
558 pipeline_id: None,
559 target_webview_id: webview_id,
560 redirect_mode: RedirectMode::Follow,
561 integrity_metadata: String::new(),
562 cryptographic_nonce_metadata: String::new(),
563 url_list: vec![],
564 parser_metadata: ParserMetadata::Default,
565 initiator: Initiator::None,
566 response_tainting: ResponseTainting::Basic,
567 is_internal_request: Default::default(),
568 crash: None,
569 }
570 }
571
572 pub fn preload_id(mut self, preload_id: PreloadId) -> RequestBuilder {
573 self.preload_id = Some(preload_id);
574 self
575 }
576
577 pub fn initiator(mut self, initiator: Initiator) -> RequestBuilder {
579 self.initiator = initiator;
580 self
581 }
582
583 pub fn method(mut self, method: Method) -> RequestBuilder {
585 self.method = method;
586 self
587 }
588
589 pub fn headers(mut self, headers: HeaderMap) -> RequestBuilder {
591 self.headers = headers;
592 self
593 }
594
595 pub fn unsafe_request(mut self, unsafe_request: bool) -> RequestBuilder {
597 self.unsafe_request = unsafe_request;
598 self
599 }
600
601 pub fn body(mut self, body: Option<RequestBody>) -> RequestBuilder {
603 self.body = body;
604 self
605 }
606
607 pub fn destination(mut self, destination: Destination) -> RequestBuilder {
609 self.destination = destination;
610 self
611 }
612
613 pub fn synchronous(mut self, synchronous: bool) -> RequestBuilder {
614 self.synchronous = synchronous;
615 self
616 }
617
618 pub fn mode(mut self, mode: RequestMode) -> RequestBuilder {
619 self.mode = mode;
620 self
621 }
622
623 pub fn use_cors_preflight(mut self, use_cors_preflight: bool) -> RequestBuilder {
625 self.use_cors_preflight = use_cors_preflight;
626 self
627 }
628
629 pub fn keep_alive(mut self, keep_alive: bool) -> RequestBuilder {
631 self.keep_alive = keep_alive;
632 self
633 }
634
635 pub fn credentials_mode(mut self, credentials_mode: CredentialsMode) -> RequestBuilder {
637 self.credentials_mode = credentials_mode;
638 self
639 }
640
641 pub fn use_url_credentials(mut self, use_url_credentials: bool) -> RequestBuilder {
642 self.use_url_credentials = use_url_credentials;
643 self
644 }
645
646 pub fn origin(mut self, origin: ImmutableOrigin) -> RequestBuilder {
648 self.origin = Origin::Origin(origin);
649 self
650 }
651
652 pub fn referrer_policy(mut self, referrer_policy: ReferrerPolicy) -> RequestBuilder {
654 self.referrer_policy = referrer_policy;
655 self
656 }
657
658 pub fn url_list(mut self, url_list: Vec<UrlWithBlobClaim>) -> RequestBuilder {
660 self.url_list = url_list;
661 self
662 }
663
664 pub fn pipeline_id(mut self, pipeline_id: Option<PipelineId>) -> RequestBuilder {
665 self.pipeline_id = pipeline_id;
666 self
667 }
668
669 pub fn redirect_mode(mut self, redirect_mode: RedirectMode) -> RequestBuilder {
671 self.redirect_mode = redirect_mode;
672 self
673 }
674
675 pub fn integrity_metadata(mut self, integrity_metadata: String) -> RequestBuilder {
677 self.integrity_metadata = integrity_metadata;
678 self
679 }
680
681 pub fn cryptographic_nonce_metadata(mut self, nonce_metadata: String) -> RequestBuilder {
683 self.cryptographic_nonce_metadata = nonce_metadata;
684 self
685 }
686
687 pub fn parser_metadata(mut self, parser_metadata: ParserMetadata) -> RequestBuilder {
689 self.parser_metadata = parser_metadata;
690 self
691 }
692
693 pub fn response_tainting(mut self, response_tainting: ResponseTainting) -> RequestBuilder {
694 self.response_tainting = response_tainting;
695 self
696 }
697
698 pub fn crash(mut self, crash: Option<String>) -> Self {
699 self.crash = crash;
700 self
701 }
702
703 pub fn policy_container(mut self, policy_container: Arc<PolicyContainer>) -> RequestBuilder {
705 self.policy_container = RequestPolicyContainer::PolicyContainer(policy_container);
706 self
707 }
708
709 pub fn client(mut self, client: RequestClient) -> RequestBuilder {
711 self.client = Some(client);
712 self
713 }
714
715 pub fn service_workers_mode(
717 mut self,
718 service_workers_mode: ServiceWorkersMode,
719 ) -> RequestBuilder {
720 self.service_workers_mode = service_workers_mode;
721 self
722 }
723
724 pub fn cache_mode(mut self, cache_mode: CacheMode) -> RequestBuilder {
726 self.cache_mode = cache_mode;
727 self
728 }
729
730 pub fn is_internal_request(mut self, is_internal_request: InternalRequest) -> RequestBuilder {
731 self.is_internal_request = is_internal_request;
732 self
733 }
734
735 pub fn build(self) -> Request {
736 let mut request = Request::new(
737 self.id,
738 self.url.clone(),
739 Some(self.origin),
740 self.referrer,
741 self.pipeline_id,
742 self.target_webview_id,
743 );
744 request.preload_id = self.preload_id;
745 request.initiator = self.initiator;
746 request.method = self.method;
747 request.headers = self.headers;
748 request.unsafe_request = self.unsafe_request;
749 request.body = self.body;
750 request.reload_navigation = self.reload_navigation;
751 request.history_navigation = self.history_navigation;
752 request.service_workers_mode = self.service_workers_mode;
753 request.destination = self.destination;
754 request.synchronous = self.synchronous;
755 request.mode = self.mode;
756 request.use_cors_preflight = self.use_cors_preflight;
757 request.keep_alive = self.keep_alive;
758 request.credentials_mode = self.credentials_mode;
759 request.use_url_credentials = self.use_url_credentials;
760 request.cache_mode = self.cache_mode;
761 request.referrer_policy = self.referrer_policy;
762 request.redirect_mode = self.redirect_mode;
763 let mut url_list = self.url_list;
764 if url_list.is_empty() {
765 url_list.push(self.url);
766 }
767 request.redirect_count = url_list.len() as u32 - 1;
768 request.url_list = url_list;
769 request.integrity_metadata = self.integrity_metadata;
770 request.cryptographic_nonce_metadata = self.cryptographic_nonce_metadata;
771 request.parser_metadata = self.parser_metadata;
772 request.response_tainting = self.response_tainting;
773 request.crash = self.crash;
774 request.client = self.client;
775 request.policy_container = self.policy_container;
776 request.is_internal_request = self.is_internal_request;
777 request
778 }
779
780 pub fn keep_alive_body_length(&self) -> u64 {
782 assert!(self.keep_alive);
783 self.body.body_length() as u64
784 }
785}
786
787#[derive(Clone, MallocSizeOf)]
790pub struct Request {
791 pub id: RequestId,
795 pub preload_id: Option<PreloadId>,
796 pub method: Method,
798 pub local_urls_only: bool,
800 pub headers: HeaderMap,
802 pub unsafe_request: bool,
804 pub body: Option<RequestBody>,
806 pub reload_navigation: bool,
809 pub history_navigation: bool,
812 pub client: Option<RequestClient>,
814 pub traversable_for_user_prompts: TraversableForUserPrompts,
816 pub target_webview_id: Option<WebViewId>,
817 pub keep_alive: bool,
819 pub service_workers_mode: ServiceWorkersMode,
821 pub initiator: Initiator,
823 pub destination: Destination,
825 pub origin: Origin,
828 pub referrer: Referrer,
830 pub referrer_policy: ReferrerPolicy,
832 pub pipeline_id: Option<PipelineId>,
833 pub synchronous: bool,
835 pub mode: RequestMode,
837 pub use_cors_preflight: bool,
839 pub credentials_mode: CredentialsMode,
841 pub use_url_credentials: bool,
843 pub cache_mode: CacheMode,
845 pub redirect_mode: RedirectMode,
847 pub integrity_metadata: String,
849 pub cryptographic_nonce_metadata: String,
851 pub url_list: Vec<UrlWithBlobClaim>,
853 pub redirect_count: u32,
855 pub response_tainting: ResponseTainting,
857 pub parser_metadata: ParserMetadata,
859 pub policy_container: RequestPolicyContainer,
861 pub crash: Option<String>,
863 pub is_internal_request: InternalRequest,
865}
866
867impl Request {
868 pub fn new(
869 id: RequestId,
870 url: UrlWithBlobClaim,
871 origin: Option<Origin>,
872 referrer: Referrer,
873 pipeline_id: Option<PipelineId>,
874 webview_id: Option<WebViewId>,
875 ) -> Request {
876 Request {
877 id,
878 preload_id: None,
879 method: Method::GET,
880 local_urls_only: false,
881 headers: HeaderMap::new(),
882 unsafe_request: false,
883 body: None,
884 reload_navigation: false,
885 history_navigation: false,
886 client: None,
887 traversable_for_user_prompts: TraversableForUserPrompts::Client,
888 keep_alive: false,
889 service_workers_mode: ServiceWorkersMode::All,
890 initiator: Initiator::None,
891 destination: Destination::None,
892 origin: origin.unwrap_or(Origin::Client),
893 referrer,
894 referrer_policy: ReferrerPolicy::EmptyString,
895 pipeline_id,
896 target_webview_id: webview_id,
897 synchronous: false,
898 mode: RequestMode::NoCors,
899 use_cors_preflight: false,
900 credentials_mode: CredentialsMode::CredentialsSameOrigin,
901 use_url_credentials: false,
902 cache_mode: CacheMode::Default,
903 redirect_mode: RedirectMode::Follow,
904 integrity_metadata: String::new(),
905 cryptographic_nonce_metadata: String::new(),
906 url_list: vec![url],
907 parser_metadata: ParserMetadata::Default,
908 redirect_count: 0,
909 response_tainting: ResponseTainting::Basic,
910 policy_container: RequestPolicyContainer::Client,
911 is_internal_request: Default::default(),
912 crash: None,
913 }
914 }
915
916 pub fn url(&self) -> ServoUrl {
918 self.url_list.first().unwrap().url()
919 }
920
921 pub fn url_with_blob_claim(&self) -> UrlWithBlobClaim {
922 self.url_list.first().unwrap().clone()
923 }
924
925 pub fn original_url(&self) -> ServoUrl {
926 match self.mode {
927 RequestMode::WebSocket {
928 protocols: _,
929 ref original_url,
930 } => original_url.clone(),
931 _ => self.url(),
932 }
933 }
934
935 pub fn current_url(&self) -> ServoUrl {
937 self.current_url_with_blob_claim().url()
938 }
939
940 pub fn current_url_with_blob_claim(&self) -> UrlWithBlobClaim {
942 self.url_list.last().unwrap().clone()
943 }
944
945 pub fn current_url_mut(&mut self) -> &mut ServoUrl {
947 self.url_list.last_mut().unwrap()
948 }
949
950 pub fn is_navigation_request(&self) -> bool {
952 matches!(
953 self.destination,
954 Destination::Document |
955 Destination::Embed |
956 Destination::Frame |
957 Destination::IFrame |
958 Destination::Object
959 )
960 }
961
962 pub fn is_subresource_request(&self) -> bool {
964 matches!(
965 self.destination,
966 Destination::Audio |
967 Destination::AudioWorklet |
968 Destination::Font |
969 Destination::Image |
970 Destination::Json |
971 Destination::Manifest |
972 Destination::PaintWorklet |
973 Destination::Script |
974 Destination::Style |
975 Destination::Text |
976 Destination::Track |
977 Destination::Video |
978 Destination::Xslt |
979 Destination::None
980 )
981 }
982
983 pub fn timing_type(&self) -> ResourceTimingType {
984 if self.is_navigation_request() {
985 ResourceTimingType::Navigation
986 } else {
987 ResourceTimingType::Resource
988 }
989 }
990
991 pub fn populate_request_from_client(&mut self) {
993 if self.traversable_for_user_prompts == TraversableForUserPrompts::Client {
995 self.traversable_for_user_prompts = TraversableForUserPrompts::NoTraversable;
997 if self.client.is_some() {
999 self.traversable_for_user_prompts =
1004 TraversableForUserPrompts::TraversableNavigable(Default::default());
1005 }
1006 }
1007 if self.origin == Origin::Client {
1009 let Some(client) = self.client.as_ref() else {
1010 unreachable!();
1012 };
1013 self.origin = client.origin.clone();
1015 }
1016 if matches!(self.policy_container, RequestPolicyContainer::Client) {
1018 if let Some(client) = self.client.as_ref() {
1021 self.policy_container =
1022 RequestPolicyContainer::PolicyContainer(client.policy_container.clone());
1023 } else {
1024 self.policy_container =
1026 RequestPolicyContainer::PolicyContainer(Arc::new(PolicyContainer::default()));
1027 }
1028 }
1029 }
1030
1031 pub fn keep_alive_body_length(&self) -> u64 {
1033 assert!(self.keep_alive);
1034 self.body.body_length() as u64
1035 }
1036
1037 pub fn total_request_length(&self) -> usize {
1039 let mut total_request_length = self.url()[..Position::AfterQuery].len();
1041 total_request_length += self
1043 .referrer
1044 .to_url()
1045 .map(|url| url.as_str().len())
1046 .unwrap_or_default();
1047 total_request_length += self.headers.total_size();
1050 total_request_length += self.body.body_length();
1052 total_request_length
1054 }
1055
1056 pub fn redirect_taint_for_request(&self) -> RedirectTaint {
1058 let Origin::Origin(request_origin) = &self.origin else {
1060 unreachable!("origin cannot be \"client\" at this point in time");
1061 };
1062
1063 let mut last_url = None;
1065
1066 let mut taint = RedirectTaint::SameOrigin;
1068
1069 for url in &self.url_list {
1071 let Some(last_url) = &mut last_url else {
1073 last_url = Some(url);
1074 continue;
1075 };
1076
1077 if !is_same_site(&url.origin(), &last_url.origin()) &&
1080 !is_same_site(request_origin, &last_url.origin())
1081 {
1082 return RedirectTaint::CrossSite;
1083 }
1084
1085 if url.origin() != last_url.origin() && *request_origin != last_url.origin() {
1088 taint = RedirectTaint::SameSite;
1089 }
1090
1091 *last_url = url;
1093 }
1094
1095 taint
1097 }
1098}
1099
1100impl Referrer {
1101 pub fn to_url(&self) -> Option<&ServoUrl> {
1102 match *self {
1103 Referrer::NoReferrer => None,
1104 Referrer::Client(ref url) => Some(url),
1105 Referrer::ReferrerUrl(ref url) => Some(url),
1106 }
1107 }
1108}
1109
1110fn is_cors_unsafe_request_header_byte(value: &u8) -> bool {
1114 matches!(value,
1115 0x00..=0x08 |
1116 0x10..=0x19 |
1117 0x22 |
1118 0x28 |
1119 0x29 |
1120 0x3A |
1121 0x3C |
1122 0x3E |
1123 0x3F |
1124 0x40 |
1125 0x5B |
1126 0x5C |
1127 0x5D |
1128 0x7B |
1129 0x7D |
1130 0x7F
1131 )
1132}
1133
1134fn is_cors_safelisted_request_accept(value: &[u8]) -> bool {
1137 !(value.iter().any(is_cors_unsafe_request_header_byte))
1138}
1139
1140fn is_cors_safelisted_language(value: &[u8]) -> bool {
1143 value.iter().all(|&x| {
1144 matches!(x,
1145 0x30..=0x39 |
1146 0x41..=0x5A |
1147 0x61..=0x7A |
1148 0x20 |
1149 0x2A |
1150 0x2C |
1151 0x2D |
1152 0x2E |
1153 0x3B |
1154 0x3D
1155 )
1156 })
1157}
1158
1159pub fn is_cors_safelisted_request_content_type(value: &[u8]) -> bool {
1162 if value.iter().any(is_cors_unsafe_request_header_byte) {
1164 return false;
1165 }
1166 let value_string = if let Ok(s) = std::str::from_utf8(value) {
1168 s
1169 } else {
1170 return false;
1171 };
1172 let value_mime_result: Result<Mime, _> = value_string.parse();
1173 match value_mime_result {
1174 Err(_) => false, Ok(value_mime) => match (value_mime.type_(), value_mime.subtype()) {
1176 (mime::APPLICATION, mime::WWW_FORM_URLENCODED) |
1177 (mime::MULTIPART, mime::FORM_DATA) |
1178 (mime::TEXT, mime::PLAIN) => true,
1179 _ => false, },
1181 }
1182}
1183
1184pub fn is_cors_safelisted_request_header<N: AsRef<str>, V: AsRef<[u8]>>(
1188 name: &N,
1189 value: &V,
1190) -> bool {
1191 let name: &str = name.as_ref();
1192 let value: &[u8] = value.as_ref();
1193 if value.len() > 128 {
1194 return false;
1195 }
1196 match name {
1197 "accept" => is_cors_safelisted_request_accept(value),
1198 "accept-language" | "content-language" => is_cors_safelisted_language(value),
1199 "content-type" => is_cors_safelisted_request_content_type(value),
1200 "range" => is_cors_safelisted_request_range(value),
1201 _ => false,
1202 }
1203}
1204
1205pub fn is_cors_safelisted_request_range(value: &[u8]) -> bool {
1206 if let Ok(value_str) = std::str::from_utf8(value) {
1207 return validate_range_header(value_str);
1208 }
1209 false
1210}
1211
1212fn validate_range_header(value: &str) -> bool {
1213 let trimmed = value.trim();
1214 if !trimmed.starts_with("bytes=") {
1215 return false;
1216 }
1217
1218 if let Some(range) = trimmed.strip_prefix("bytes=") {
1219 let mut parts = range.split('-');
1220 let start = parts.next();
1221 let end = parts.next();
1222
1223 if let Some(start) = start &&
1224 let Ok(start_num) = start.parse::<u64>()
1225 {
1226 return match end {
1227 Some(e) if !e.is_empty() => {
1228 e.parse::<u64>().is_ok_and(|end_num| start_num <= end_num)
1229 },
1230 _ => true,
1231 };
1232 }
1233 }
1234 false
1235}
1236
1237pub fn is_cors_safelisted_method(method: &Method) -> bool {
1239 matches!(*method, Method::GET | Method::HEAD | Method::POST)
1240}
1241
1242pub fn is_cors_non_wildcard_request_header_name(name: &HeaderName) -> bool {
1244 name == AUTHORIZATION
1245}
1246
1247pub fn get_cors_unsafe_header_names(headers: &HeaderMap) -> Vec<HeaderName> {
1249 let mut unsafe_names: Vec<&HeaderName> = vec![];
1251 let mut potentillay_unsafe_names: Vec<&HeaderName> = vec![];
1253 let mut safelist_value_size = 0;
1255
1256 for (name, value) in headers.iter() {
1258 if !is_cors_safelisted_request_header(&name, &value) {
1259 unsafe_names.push(name);
1260 } else {
1261 potentillay_unsafe_names.push(name);
1262 safelist_value_size += value.as_ref().len();
1263 }
1264 }
1265
1266 if safelist_value_size > 1024 {
1268 unsafe_names.extend_from_slice(&potentillay_unsafe_names);
1269 }
1270
1271 convert_header_names_to_sorted_lowercase_set(unsafe_names)
1273}
1274
1275pub fn convert_header_names_to_sorted_lowercase_set(
1277 mut header_names: Vec<&HeaderName>,
1278) -> Vec<HeaderName> {
1279 header_names.sort_by(|a, b| a.as_str().partial_cmp(b.as_str()).unwrap());
1282 header_names.dedup();
1283 header_names.into_iter().cloned().collect()
1284}
1285
1286pub fn create_request_body_with_content(content: String) -> RequestBody {
1287 let content_bytes = GenericSharedMemory::from_vec(content.into_bytes());
1288 let content_len = content_bytes.len();
1289
1290 let (chunk_request_sender, chunk_request_receiver) = ipc::channel().unwrap();
1291 ROUTER.add_typed_route(
1292 chunk_request_receiver,
1293 Box::new(move |message| {
1294 let request = message.unwrap();
1295 if let BodyChunkRequest::Connect(sender) = request {
1296 let _ = sender.send(BodyChunkResponse::Chunk(content_bytes.clone()));
1297 let _ = sender.send(BodyChunkResponse::Done);
1298 }
1299 }),
1300 );
1301
1302 RequestBody::new(chunk_request_sender, BodySource::Object, Some(content_len))
1303}