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::{HttpsState, 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, Debug, Eq, Hash, Deserialize, MallocSizeOf, Serialize, PartialEq)]
140pub struct PreloadKey {
141 pub url: ServoUrl,
143 pub destination: Destination,
145 pub mode: RequestMode,
147 pub credentials_mode: CredentialsMode,
149}
150
151impl PreloadKey {
152 pub fn new(request: &RequestBuilder) -> Self {
153 Self {
154 url: request.url.url(),
155 destination: request.destination,
156 mode: request.mode.clone(),
157 credentials_mode: request.credentials_mode,
158 }
159 }
160}
161
162#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize, Hash, MallocSizeOf)]
163pub struct PreloadId(pub Uuid);
164
165impl Default for PreloadId {
166 fn default() -> Self {
167 Self(Uuid::new_v4())
168 }
169}
170
171#[derive(Debug, MallocSizeOf)]
173pub struct PreloadEntry {
174 pub integrity_metadata: String,
176 pub response: Option<Response>,
178 pub on_response_available: Option<TokioSender<Response>>,
180}
181
182impl PreloadEntry {
183 pub fn new(integrity_metadata: String) -> Self {
184 Self {
185 integrity_metadata,
186 response: None,
187 on_response_available: None,
188 }
189 }
190
191 pub fn with_response(&mut self, response: Response) {
193 if let Some(sender) = self.on_response_available.take() {
196 let _ = sender.send(response);
197 } else {
198 self.response = Some(response);
199 }
200 }
201}
202
203pub type PreloadedResources = FxHashMap<PreloadKey, PreloadId>;
204
205#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
207pub struct RequestClient {
208 pub preloaded_resources: PreloadedResources,
210 pub policy_container: RequestPolicyContainer,
212 pub origin: Origin,
214 pub is_nested_browsing_context: bool,
216 pub insecure_requests_policy: InsecureRequestsPolicy,
218}
219
220#[derive(Clone, Copy, Default, MallocSizeOf, PartialEq)]
222pub enum SystemVisibilityState {
223 #[default]
224 Hidden,
225 Visible,
226}
227
228#[derive(Clone, Copy, Default, MallocSizeOf, PartialEq)]
230pub struct TraversableNavigable {
231 current_session_history_step: u8,
233 running_nested_apply_history_step: bool,
237 system_visibility_state: SystemVisibilityState,
239 is_created_by_web_content: bool,
241}
242
243#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
245pub enum TraversableForUserPrompts {
246 NoTraversable,
247 Client,
248 TraversableNavigable(TraversableNavigable),
249}
250
251#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
253pub enum CorsSettings {
254 Anonymous,
255 UseCredentials,
256}
257
258impl CorsSettings {
259 pub fn from_enumerated_attribute(value: &str) -> CorsSettings {
261 match value.to_ascii_lowercase().as_str() {
262 "anonymous" => CorsSettings::Anonymous,
263 "use-credentials" => CorsSettings::UseCredentials,
264 _ => CorsSettings::Anonymous,
265 }
266 }
267}
268
269#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
271pub enum ParserMetadata {
272 Default,
273 ParserInserted,
274 NotParserInserted,
275}
276
277#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
279pub enum BodySource {
280 Null,
281 Object,
282}
283
284#[derive(Debug, Deserialize, Serialize)]
287pub enum BodyChunkResponse {
288 Chunk(GenericSharedMemory),
290 Done,
292 Error,
295}
296
297#[derive(Debug, Deserialize, Serialize)]
301pub enum BodyChunkRequest {
302 Connect(IpcSender<BodyChunkResponse>),
304 Extract(IpcReceiver<BodyChunkRequest>),
306 Chunk,
308 Done,
310 Error,
312}
313
314#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
320pub struct RequestBody {
321 #[conditional_malloc_size_of]
323 body_chunk_request_channel: Arc<Mutex<Option<IpcSender<BodyChunkRequest>>>>,
324 source: BodySource,
326 total_bytes: Option<usize>,
328}
329
330impl RequestBody {
331 pub fn new(
332 body_chunk_request_channel: IpcSender<BodyChunkRequest>,
333 source: BodySource,
334 total_bytes: Option<usize>,
335 ) -> Self {
336 RequestBody {
337 body_chunk_request_channel: Arc::new(Mutex::new(Some(body_chunk_request_channel))),
338 source,
339 total_bytes,
340 }
341 }
342
343 pub fn extract_source(&mut self) {
345 match self.source {
346 BodySource::Null => panic!("Null sources should never be re-directed."),
347 BodySource::Object => {
348 let (chan, port) = ipc::channel().unwrap();
349 let mut lock = self.body_chunk_request_channel.lock();
350 let Some(selfchan) = lock.as_mut() else {
351 error!(
352 "Could not re-extract the request body source because the body stream has already been closed."
353 );
354 return;
355 };
356 if let Err(error) = selfchan.send(BodyChunkRequest::Extract(port)) {
357 error!(
358 "Could not re-extract the request body source because the body stream has already been closed: {error}"
359 );
360 return;
361 }
362 *selfchan = chan;
363 },
364 }
365 }
366
367 pub fn clone_stream(&self) -> Arc<Mutex<Option<IpcSender<BodyChunkRequest>>>> {
369 self.body_chunk_request_channel.clone()
370 }
371
372 pub fn close_stream(&self) {
377 self.body_chunk_request_channel.lock().take();
378 }
379
380 pub fn source_is_null(&self) -> bool {
381 self.source == BodySource::Null
382 }
383
384 #[expect(clippy::len_without_is_empty)]
385 pub fn len(&self) -> Option<usize> {
386 self.total_bytes
387 }
388}
389
390trait RequestBodySize {
391 fn body_length(&self) -> usize;
392}
393
394impl RequestBodySize for Option<RequestBody> {
395 fn body_length(&self) -> usize {
396 self.as_ref()
397 .and_then(|body| body.len())
398 .unwrap_or_default()
399 }
400}
401
402#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
403pub enum InsecureRequestsPolicy {
404 DoNotUpgrade,
405 Upgrade,
406}
407
408pub trait RequestHeadersSize {
409 fn total_size(&self) -> usize;
410}
411
412impl RequestHeadersSize for HeaderMap {
413 fn total_size(&self) -> usize {
414 self.iter()
415 .map(|(name, value)| name.as_str().len() + value.len())
416 .sum()
417 }
418}
419
420#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
421pub struct RequestBuilder {
422 pub id: RequestId,
423
424 pub preload_id: Option<PreloadId>,
425
426 #[serde(
428 deserialize_with = "::hyper_serde::deserialize",
429 serialize_with = "::hyper_serde::serialize"
430 )]
431 pub method: Method,
432
433 pub url: UrlWithBlobClaim,
435
436 #[serde(
438 deserialize_with = "::hyper_serde::deserialize",
439 serialize_with = "::hyper_serde::serialize"
440 )]
441 pub headers: HeaderMap,
442
443 pub unsafe_request: bool,
445
446 pub body: Option<RequestBody>,
448
449 pub service_workers_mode: ServiceWorkersMode,
451 pub client: Option<RequestClient>,
452 pub destination: Destination,
454 pub synchronous: bool,
455 pub mode: RequestMode,
456
457 pub cache_mode: CacheMode,
459
460 pub use_cors_preflight: bool,
462
463 pub keep_alive: bool,
465
466 pub credentials_mode: CredentialsMode,
468 pub use_url_credentials: bool,
469
470 pub origin: Origin,
472
473 pub policy_container: RequestPolicyContainer,
475 pub insecure_requests_policy: InsecureRequestsPolicy,
476 pub has_trustworthy_ancestor_origin: bool,
477
478 pub referrer: Referrer,
480
481 pub referrer_policy: ReferrerPolicy,
483 pub pipeline_id: Option<PipelineId>,
484 pub target_webview_id: Option<WebViewId>,
485
486 pub redirect_mode: RedirectMode,
488
489 pub integrity_metadata: String,
491
492 pub cryptographic_nonce_metadata: String,
494
495 pub url_list: Vec<ServoUrl>,
497
498 pub parser_metadata: ParserMetadata,
500
501 pub initiator: Initiator,
503 pub https_state: HttpsState,
504 pub response_tainting: ResponseTainting,
505 pub crash: Option<String>,
507}
508
509impl RequestBuilder {
510 pub fn new(
511 webview_id: Option<WebViewId>,
512 url: UrlWithBlobClaim,
513 referrer: Referrer,
514 ) -> RequestBuilder {
515 RequestBuilder {
516 id: RequestId::default(),
517 preload_id: None,
518 method: Method::GET,
519 url,
520 headers: HeaderMap::new(),
521 unsafe_request: false,
522 body: None,
523 service_workers_mode: ServiceWorkersMode::All,
524 destination: Destination::None,
525 synchronous: false,
526 mode: RequestMode::NoCors,
527 cache_mode: CacheMode::Default,
528 use_cors_preflight: false,
529 keep_alive: false,
530 credentials_mode: CredentialsMode::CredentialsSameOrigin,
531 use_url_credentials: false,
532 origin: Origin::Client,
533 client: None,
534 policy_container: RequestPolicyContainer::default(),
535 insecure_requests_policy: InsecureRequestsPolicy::DoNotUpgrade,
536 has_trustworthy_ancestor_origin: false,
537 referrer,
538 referrer_policy: ReferrerPolicy::EmptyString,
539 pipeline_id: None,
540 target_webview_id: webview_id,
541 redirect_mode: RedirectMode::Follow,
542 integrity_metadata: "".to_owned(),
543 cryptographic_nonce_metadata: "".to_owned(),
544 url_list: vec![],
545 parser_metadata: ParserMetadata::Default,
546 initiator: Initiator::None,
547 https_state: HttpsState::None,
548 response_tainting: ResponseTainting::Basic,
549 crash: None,
550 }
551 }
552
553 pub fn preload_id(mut self, preload_id: PreloadId) -> RequestBuilder {
554 self.preload_id = Some(preload_id);
555 self
556 }
557
558 pub fn initiator(mut self, initiator: Initiator) -> RequestBuilder {
560 self.initiator = initiator;
561 self
562 }
563
564 pub fn method(mut self, method: Method) -> RequestBuilder {
566 self.method = method;
567 self
568 }
569
570 pub fn headers(mut self, headers: HeaderMap) -> RequestBuilder {
572 self.headers = headers;
573 self
574 }
575
576 pub fn unsafe_request(mut self, unsafe_request: bool) -> RequestBuilder {
578 self.unsafe_request = unsafe_request;
579 self
580 }
581
582 pub fn body(mut self, body: Option<RequestBody>) -> RequestBuilder {
584 self.body = body;
585 self
586 }
587
588 pub fn destination(mut self, destination: Destination) -> RequestBuilder {
590 self.destination = destination;
591 self
592 }
593
594 pub fn synchronous(mut self, synchronous: bool) -> RequestBuilder {
595 self.synchronous = synchronous;
596 self
597 }
598
599 pub fn mode(mut self, mode: RequestMode) -> RequestBuilder {
600 self.mode = mode;
601 self
602 }
603
604 pub fn use_cors_preflight(mut self, use_cors_preflight: bool) -> RequestBuilder {
606 self.use_cors_preflight = use_cors_preflight;
607 self
608 }
609
610 pub fn keep_alive(mut self, keep_alive: bool) -> RequestBuilder {
612 self.keep_alive = keep_alive;
613 self
614 }
615
616 pub fn credentials_mode(mut self, credentials_mode: CredentialsMode) -> RequestBuilder {
618 self.credentials_mode = credentials_mode;
619 self
620 }
621
622 pub fn use_url_credentials(mut self, use_url_credentials: bool) -> RequestBuilder {
623 self.use_url_credentials = use_url_credentials;
624 self
625 }
626
627 pub fn origin(mut self, origin: ImmutableOrigin) -> RequestBuilder {
629 self.origin = Origin::Origin(origin);
630 self
631 }
632
633 pub fn referrer_policy(mut self, referrer_policy: ReferrerPolicy) -> RequestBuilder {
635 self.referrer_policy = referrer_policy;
636 self
637 }
638
639 pub fn url_list(mut self, url_list: Vec<ServoUrl>) -> RequestBuilder {
641 self.url_list = url_list;
642 self
643 }
644
645 pub fn pipeline_id(mut self, pipeline_id: Option<PipelineId>) -> RequestBuilder {
646 self.pipeline_id = pipeline_id;
647 self
648 }
649
650 pub fn redirect_mode(mut self, redirect_mode: RedirectMode) -> RequestBuilder {
652 self.redirect_mode = redirect_mode;
653 self
654 }
655
656 pub fn integrity_metadata(mut self, integrity_metadata: String) -> RequestBuilder {
658 self.integrity_metadata = integrity_metadata;
659 self
660 }
661
662 pub fn cryptographic_nonce_metadata(mut self, nonce_metadata: String) -> RequestBuilder {
664 self.cryptographic_nonce_metadata = nonce_metadata;
665 self
666 }
667
668 pub fn parser_metadata(mut self, parser_metadata: ParserMetadata) -> RequestBuilder {
670 self.parser_metadata = parser_metadata;
671 self
672 }
673
674 pub fn https_state(mut self, https_state: HttpsState) -> RequestBuilder {
675 self.https_state = https_state;
676 self
677 }
678
679 pub fn response_tainting(mut self, response_tainting: ResponseTainting) -> RequestBuilder {
680 self.response_tainting = response_tainting;
681 self
682 }
683
684 pub fn crash(mut self, crash: Option<String>) -> Self {
685 self.crash = crash;
686 self
687 }
688
689 pub fn policy_container(mut self, policy_container: PolicyContainer) -> RequestBuilder {
691 self.policy_container = RequestPolicyContainer::PolicyContainer(policy_container);
692 self
693 }
694
695 pub fn client(mut self, client: RequestClient) -> RequestBuilder {
697 self.client = Some(client);
698 self
699 }
700
701 pub fn insecure_requests_policy(
702 mut self,
703 insecure_requests_policy: InsecureRequestsPolicy,
704 ) -> RequestBuilder {
705 self.insecure_requests_policy = insecure_requests_policy;
706 self
707 }
708
709 pub fn has_trustworthy_ancestor_origin(
710 mut self,
711 has_trustworthy_ancestor_origin: bool,
712 ) -> RequestBuilder {
713 self.has_trustworthy_ancestor_origin = has_trustworthy_ancestor_origin;
714 self
715 }
716
717 pub fn service_workers_mode(
719 mut self,
720 service_workers_mode: ServiceWorkersMode,
721 ) -> RequestBuilder {
722 self.service_workers_mode = service_workers_mode;
723 self
724 }
725
726 pub fn cache_mode(mut self, cache_mode: CacheMode) -> RequestBuilder {
728 self.cache_mode = cache_mode;
729 self
730 }
731
732 pub fn build(self) -> Request {
733 let mut request = Request::new(
734 self.id,
735 self.url.clone(),
736 Some(self.origin),
737 self.referrer,
738 self.pipeline_id,
739 self.target_webview_id,
740 self.https_state,
741 );
742 request.preload_id = self.preload_id;
743 request.initiator = self.initiator;
744 request.method = self.method;
745 request.headers = self.headers;
746 request.unsafe_request = self.unsafe_request;
747 request.body = self.body;
748 request.service_workers_mode = self.service_workers_mode;
749 request.destination = self.destination;
750 request.synchronous = self.synchronous;
751 request.mode = self.mode;
752 request.use_cors_preflight = self.use_cors_preflight;
753 request.keep_alive = self.keep_alive;
754 request.credentials_mode = self.credentials_mode;
755 request.use_url_credentials = self.use_url_credentials;
756 request.cache_mode = self.cache_mode;
757 request.referrer_policy = self.referrer_policy;
758 request.redirect_mode = self.redirect_mode;
759 let mut url_list: Vec<_> = self
760 .url_list
761 .into_iter()
762 .map(UrlWithBlobClaim::from_url_without_having_claimed_blob)
763 .collect();
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.insecure_requests_policy = self.insecure_requests_policy;
777 request.has_trustworthy_ancestor_origin = self.has_trustworthy_ancestor_origin;
778 request
779 }
780
781 pub fn keep_alive_body_length(&self) -> u64 {
783 assert!(self.keep_alive);
784 self.body.body_length() as u64
785 }
786}
787
788#[derive(Clone, MallocSizeOf)]
791pub struct Request {
792 pub id: RequestId,
796 pub preload_id: Option<PreloadId>,
797 pub method: Method,
799 pub local_urls_only: bool,
801 pub headers: HeaderMap,
803 pub unsafe_request: bool,
805 pub body: Option<RequestBody>,
807 pub client: Option<RequestClient>,
809 pub traversable_for_user_prompts: TraversableForUserPrompts,
811 pub target_webview_id: Option<WebViewId>,
812 pub keep_alive: bool,
814 pub service_workers_mode: ServiceWorkersMode,
816 pub initiator: Initiator,
818 pub destination: Destination,
820 pub origin: Origin,
823 pub referrer: Referrer,
825 pub referrer_policy: ReferrerPolicy,
827 pub pipeline_id: Option<PipelineId>,
828 pub synchronous: bool,
830 pub mode: RequestMode,
832 pub use_cors_preflight: bool,
834 pub credentials_mode: CredentialsMode,
836 pub use_url_credentials: bool,
838 pub cache_mode: CacheMode,
840 pub redirect_mode: RedirectMode,
842 pub integrity_metadata: String,
844 pub cryptographic_nonce_metadata: String,
846 pub url_list: Vec<UrlWithBlobClaim>,
848 pub redirect_count: u32,
850 pub response_tainting: ResponseTainting,
852 pub parser_metadata: ParserMetadata,
854 pub policy_container: RequestPolicyContainer,
856 pub insecure_requests_policy: InsecureRequestsPolicy,
858 pub has_trustworthy_ancestor_origin: bool,
859 pub https_state: HttpsState,
860 pub crash: Option<String>,
862}
863
864impl Request {
865 pub fn new(
866 id: RequestId,
867 url: UrlWithBlobClaim,
868 origin: Option<Origin>,
869 referrer: Referrer,
870 pipeline_id: Option<PipelineId>,
871 webview_id: Option<WebViewId>,
872 https_state: HttpsState,
873 ) -> Request {
874 Request {
875 id,
876 preload_id: None,
877 method: Method::GET,
878 local_urls_only: false,
879 headers: HeaderMap::new(),
880 unsafe_request: false,
881 body: None,
882 client: None,
883 traversable_for_user_prompts: TraversableForUserPrompts::Client,
884 keep_alive: false,
885 service_workers_mode: ServiceWorkersMode::All,
886 initiator: Initiator::None,
887 destination: Destination::None,
888 origin: origin.unwrap_or(Origin::Client),
889 referrer,
890 referrer_policy: ReferrerPolicy::EmptyString,
891 pipeline_id,
892 target_webview_id: webview_id,
893 synchronous: false,
894 mode: RequestMode::NoCors,
895 use_cors_preflight: false,
896 credentials_mode: CredentialsMode::CredentialsSameOrigin,
897 use_url_credentials: false,
898 cache_mode: CacheMode::Default,
899 redirect_mode: RedirectMode::Follow,
900 integrity_metadata: String::new(),
901 cryptographic_nonce_metadata: String::new(),
902 url_list: vec![url],
903 parser_metadata: ParserMetadata::Default,
904 redirect_count: 0,
905 response_tainting: ResponseTainting::Basic,
906 policy_container: RequestPolicyContainer::Client,
907 insecure_requests_policy: InsecureRequestsPolicy::DoNotUpgrade,
908 has_trustworthy_ancestor_origin: false,
909 https_state,
910 crash: None,
911 }
912 }
913
914 pub fn url(&self) -> ServoUrl {
916 self.url_list.first().unwrap().url()
917 }
918
919 pub fn url_with_blob_claim(&self) -> UrlWithBlobClaim {
920 self.url_list.first().unwrap().clone()
921 }
922
923 pub fn original_url(&self) -> ServoUrl {
924 match self.mode {
925 RequestMode::WebSocket {
926 protocols: _,
927 ref original_url,
928 } => original_url.clone(),
929 _ => self.url(),
930 }
931 }
932
933 pub fn current_url(&self) -> ServoUrl {
935 self.current_url_with_blob_claim().url()
936 }
937
938 pub fn current_url_with_blob_claim(&self) -> UrlWithBlobClaim {
940 self.url_list.last().unwrap().clone()
941 }
942
943 pub fn current_url_mut(&mut self) -> &mut ServoUrl {
945 self.url_list.last_mut().unwrap()
946 }
947
948 pub fn is_navigation_request(&self) -> bool {
950 matches!(
951 self.destination,
952 Destination::Document |
953 Destination::Embed |
954 Destination::Frame |
955 Destination::IFrame |
956 Destination::Object
957 )
958 }
959
960 pub fn is_subresource_request(&self) -> bool {
962 matches!(
963 self.destination,
964 Destination::Audio |
965 Destination::Font |
966 Destination::Image |
967 Destination::Manifest |
968 Destination::Script |
969 Destination::Style |
970 Destination::Track |
971 Destination::Video |
972 Destination::Xslt |
973 Destination::None
974 )
975 }
976
977 pub fn timing_type(&self) -> ResourceTimingType {
978 if self.is_navigation_request() {
979 ResourceTimingType::Navigation
980 } else {
981 ResourceTimingType::Resource
982 }
983 }
984
985 pub fn populate_request_from_client(&mut self) {
987 if self.traversable_for_user_prompts == TraversableForUserPrompts::Client {
989 self.traversable_for_user_prompts = TraversableForUserPrompts::NoTraversable;
991 if self.client.is_some() {
993 self.traversable_for_user_prompts =
998 TraversableForUserPrompts::TraversableNavigable(Default::default());
999 }
1000 }
1001 if self.origin == Origin::Client {
1003 let Some(client) = self.client.as_ref() else {
1004 unreachable!();
1006 };
1007 self.origin = client.origin.clone();
1009 }
1010 if matches!(self.policy_container, RequestPolicyContainer::Client) {
1012 if let Some(client) = self.client.as_ref() {
1015 self.policy_container = client.policy_container.clone();
1016 } else {
1017 self.policy_container =
1019 RequestPolicyContainer::PolicyContainer(PolicyContainer::default());
1020 }
1021 }
1022 }
1023
1024 pub fn keep_alive_body_length(&self) -> u64 {
1026 assert!(self.keep_alive);
1027 self.body.body_length() as u64
1028 }
1029
1030 pub fn total_request_length(&self) -> usize {
1032 let mut total_request_length = self.url()[..Position::AfterQuery].len();
1034 total_request_length += self
1036 .referrer
1037 .to_url()
1038 .map(|url| url.as_str().len())
1039 .unwrap_or_default();
1040 total_request_length += self.headers.total_size();
1043 total_request_length += self.body.body_length();
1045 total_request_length
1047 }
1048
1049 pub fn redirect_taint_for_request(&self) -> RedirectTaint {
1051 let Origin::Origin(request_origin) = &self.origin else {
1053 unreachable!("origin cannot be \"client\" at this point in time");
1054 };
1055
1056 let mut last_url = None;
1058
1059 let mut taint = RedirectTaint::SameOrigin;
1061
1062 for url in &self.url_list {
1064 let Some(last_url) = &mut last_url else {
1066 last_url = Some(url);
1067 continue;
1068 };
1069
1070 if !is_same_site(&url.origin(), &last_url.origin()) &&
1073 !is_same_site(request_origin, &last_url.origin())
1074 {
1075 return RedirectTaint::CrossSite;
1076 }
1077
1078 if url.origin() != last_url.origin() && *request_origin != last_url.origin() {
1081 taint = RedirectTaint::SameSite;
1082 }
1083
1084 *last_url = url;
1086 }
1087
1088 taint
1090 }
1091}
1092
1093impl Referrer {
1094 pub fn to_url(&self) -> Option<&ServoUrl> {
1095 match *self {
1096 Referrer::NoReferrer => None,
1097 Referrer::Client(ref url) => Some(url),
1098 Referrer::ReferrerUrl(ref url) => Some(url),
1099 }
1100 }
1101}
1102
1103fn is_cors_unsafe_request_header_byte(value: &u8) -> bool {
1107 matches!(value,
1108 0x00..=0x08 |
1109 0x10..=0x19 |
1110 0x22 |
1111 0x28 |
1112 0x29 |
1113 0x3A |
1114 0x3C |
1115 0x3E |
1116 0x3F |
1117 0x40 |
1118 0x5B |
1119 0x5C |
1120 0x5D |
1121 0x7B |
1122 0x7D |
1123 0x7F
1124 )
1125}
1126
1127fn is_cors_safelisted_request_accept(value: &[u8]) -> bool {
1130 !(value.iter().any(is_cors_unsafe_request_header_byte))
1131}
1132
1133fn is_cors_safelisted_language(value: &[u8]) -> bool {
1136 value.iter().all(|&x| {
1137 matches!(x,
1138 0x30..=0x39 |
1139 0x41..=0x5A |
1140 0x61..=0x7A |
1141 0x20 |
1142 0x2A |
1143 0x2C |
1144 0x2D |
1145 0x2E |
1146 0x3B |
1147 0x3D
1148 )
1149 })
1150}
1151
1152pub fn is_cors_safelisted_request_content_type(value: &[u8]) -> bool {
1155 if value.iter().any(is_cors_unsafe_request_header_byte) {
1157 return false;
1158 }
1159 let value_string = if let Ok(s) = std::str::from_utf8(value) {
1161 s
1162 } else {
1163 return false;
1164 };
1165 let value_mime_result: Result<Mime, _> = value_string.parse();
1166 match value_mime_result {
1167 Err(_) => false, Ok(value_mime) => match (value_mime.type_(), value_mime.subtype()) {
1169 (mime::APPLICATION, mime::WWW_FORM_URLENCODED) |
1170 (mime::MULTIPART, mime::FORM_DATA) |
1171 (mime::TEXT, mime::PLAIN) => true,
1172 _ => false, },
1174 }
1175}
1176
1177pub fn is_cors_safelisted_request_header<N: AsRef<str>, V: AsRef<[u8]>>(
1181 name: &N,
1182 value: &V,
1183) -> bool {
1184 let name: &str = name.as_ref();
1185 let value: &[u8] = value.as_ref();
1186 if value.len() > 128 {
1187 return false;
1188 }
1189 match name {
1190 "accept" => is_cors_safelisted_request_accept(value),
1191 "accept-language" | "content-language" => is_cors_safelisted_language(value),
1192 "content-type" => is_cors_safelisted_request_content_type(value),
1193 "range" => is_cors_safelisted_request_range(value),
1194 _ => false,
1195 }
1196}
1197
1198pub fn is_cors_safelisted_request_range(value: &[u8]) -> bool {
1199 if let Ok(value_str) = std::str::from_utf8(value) {
1200 return validate_range_header(value_str);
1201 }
1202 false
1203}
1204
1205fn validate_range_header(value: &str) -> bool {
1206 let trimmed = value.trim();
1207 if !trimmed.starts_with("bytes=") {
1208 return false;
1209 }
1210
1211 if let Some(range) = trimmed.strip_prefix("bytes=") {
1212 let mut parts = range.split('-');
1213 let start = parts.next();
1214 let end = parts.next();
1215
1216 if let Some(start) = start {
1217 if let Ok(start_num) = start.parse::<u64>() {
1218 return match end {
1219 Some(e) if !e.is_empty() => {
1220 e.parse::<u64>().is_ok_and(|end_num| start_num <= end_num)
1221 },
1222 _ => true,
1223 };
1224 }
1225 }
1226 }
1227 false
1228}
1229
1230pub fn is_cors_safelisted_method(method: &Method) -> bool {
1232 matches!(*method, Method::GET | Method::HEAD | Method::POST)
1233}
1234
1235pub fn is_cors_non_wildcard_request_header_name(name: &HeaderName) -> bool {
1237 name == AUTHORIZATION
1238}
1239
1240pub fn get_cors_unsafe_header_names(headers: &HeaderMap) -> Vec<HeaderName> {
1242 let mut unsafe_names: Vec<&HeaderName> = vec![];
1244 let mut potentillay_unsafe_names: Vec<&HeaderName> = vec![];
1246 let mut safelist_value_size = 0;
1248
1249 for (name, value) in headers.iter() {
1251 if !is_cors_safelisted_request_header(&name, &value) {
1252 unsafe_names.push(name);
1253 } else {
1254 potentillay_unsafe_names.push(name);
1255 safelist_value_size += value.as_ref().len();
1256 }
1257 }
1258
1259 if safelist_value_size > 1024 {
1261 unsafe_names.extend_from_slice(&potentillay_unsafe_names);
1262 }
1263
1264 convert_header_names_to_sorted_lowercase_set(unsafe_names)
1266}
1267
1268pub fn convert_header_names_to_sorted_lowercase_set(
1270 header_names: Vec<&HeaderName>,
1271) -> Vec<HeaderName> {
1272 let mut ordered_set = header_names.to_vec();
1275 ordered_set.sort_by(|a, b| a.as_str().partial_cmp(b.as_str()).unwrap());
1276 ordered_set.dedup();
1277 ordered_set.into_iter().cloned().collect()
1278}
1279
1280pub fn create_request_body_with_content(content: &str) -> RequestBody {
1281 let content_bytes = GenericSharedMemory::from_bytes(content.as_bytes());
1282 let content_len = content_bytes.len();
1283
1284 let (chunk_request_sender, chunk_request_receiver) = ipc::channel().unwrap();
1285 ROUTER.add_typed_route(
1286 chunk_request_receiver,
1287 Box::new(move |message| {
1288 let request = message.unwrap();
1289 if let BodyChunkRequest::Connect(sender) = request {
1290 let _ = sender.send(BodyChunkResponse::Chunk(content_bytes.clone()));
1291 let _ = sender.send(BodyChunkResponse::Done);
1292 }
1293 }),
1294 );
1295
1296 RequestBody::new(chunk_request_sender, BodySource::Object, Some(content_len))
1297}