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: RequestPolicyContainer,
220 pub origin: Origin,
222 pub is_nested_browsing_context: bool,
224 pub insecure_requests_policy: InsecureRequestsPolicy,
226}
227
228#[derive(Clone, Copy, Default, MallocSizeOf, PartialEq)]
230pub enum SystemVisibilityState {
231 #[default]
232 Hidden,
233 Visible,
234}
235
236#[derive(Clone, Copy, Default, MallocSizeOf, PartialEq)]
238pub struct TraversableNavigable {
239 current_session_history_step: u8,
241 running_nested_apply_history_step: bool,
245 system_visibility_state: SystemVisibilityState,
247 is_created_by_web_content: bool,
249}
250
251#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
253pub enum TraversableForUserPrompts {
254 NoTraversable,
255 Client,
256 TraversableNavigable(TraversableNavigable),
257}
258
259#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
261pub enum CorsSettings {
262 Anonymous,
263 UseCredentials,
264}
265
266impl CorsSettings {
267 pub fn from_enumerated_attribute(value: &str) -> CorsSettings {
269 match value.to_ascii_lowercase().as_str() {
270 "anonymous" => CorsSettings::Anonymous,
271 "use-credentials" => CorsSettings::UseCredentials,
272 _ => CorsSettings::Anonymous,
273 }
274 }
275}
276
277#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
279pub enum ParserMetadata {
280 Default,
281 ParserInserted,
282 NotParserInserted,
283}
284
285#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
287pub enum BodySource {
288 Null,
289 Object,
290}
291
292#[derive(Debug, Deserialize, Serialize)]
295pub enum BodyChunkResponse {
296 Chunk(GenericSharedMemory),
298 Done,
300 Error,
303}
304
305#[derive(Debug, Deserialize, Serialize)]
309pub enum BodyChunkRequest {
310 Connect(IpcSender<BodyChunkResponse>),
312 Extract(IpcReceiver<BodyChunkRequest>),
314 Chunk,
316 Done,
318 Error,
320}
321
322#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
328pub struct RequestBody {
329 #[conditional_malloc_size_of]
331 body_chunk_request_channel: Arc<Mutex<Option<IpcSender<BodyChunkRequest>>>>,
332 source: BodySource,
334 total_bytes: Option<usize>,
336}
337
338impl RequestBody {
339 pub fn new(
340 body_chunk_request_channel: IpcSender<BodyChunkRequest>,
341 source: BodySource,
342 total_bytes: Option<usize>,
343 ) -> Self {
344 RequestBody {
345 body_chunk_request_channel: Arc::new(Mutex::new(Some(body_chunk_request_channel))),
346 source,
347 total_bytes,
348 }
349 }
350
351 pub fn extract_source(&mut self) {
353 match self.source {
354 BodySource::Null => panic!("Null sources should never be re-directed."),
355 BodySource::Object => {
356 let (chan, port) = ipc::channel().unwrap();
357 let mut lock = self.body_chunk_request_channel.lock();
358 let Some(selfchan) = lock.as_mut() else {
359 error!(
360 "Could not re-extract the request body source because the body stream has already been closed."
361 );
362 return;
363 };
364 if let Err(error) = selfchan.send(BodyChunkRequest::Extract(port)) {
365 error!(
366 "Could not re-extract the request body source because the body stream has already been closed: {error}"
367 );
368 return;
369 }
370 *selfchan = chan;
371 },
372 }
373 }
374
375 pub fn clone_stream(&self) -> Arc<Mutex<Option<IpcSender<BodyChunkRequest>>>> {
377 self.body_chunk_request_channel.clone()
378 }
379
380 pub fn close_stream(&self) {
385 self.body_chunk_request_channel.lock().take();
386 }
387
388 pub fn source_is_null(&self) -> bool {
389 self.source == BodySource::Null
390 }
391
392 #[expect(clippy::len_without_is_empty)]
393 pub fn len(&self) -> Option<usize> {
394 self.total_bytes
395 }
396}
397
398trait RequestBodySize {
399 fn body_length(&self) -> usize;
400}
401
402impl RequestBodySize for Option<RequestBody> {
403 fn body_length(&self) -> usize {
404 self.as_ref()
405 .and_then(|body| body.len())
406 .unwrap_or_default()
407 }
408}
409
410#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
411pub enum InsecureRequestsPolicy {
412 DoNotUpgrade,
413 Upgrade,
414}
415
416pub trait RequestHeadersSize {
417 fn total_size(&self) -> usize;
418}
419
420impl RequestHeadersSize for HeaderMap {
421 fn total_size(&self) -> usize {
422 self.iter()
423 .map(|(name, value)| name.as_str().len() + value.len())
424 .sum()
425 }
426}
427
428#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
429pub struct RequestBuilder {
430 pub id: RequestId,
431
432 pub preload_id: Option<PreloadId>,
433
434 #[serde(
436 deserialize_with = "::hyper_serde::deserialize",
437 serialize_with = "::hyper_serde::serialize"
438 )]
439 pub method: Method,
440
441 pub url: UrlWithBlobClaim,
443
444 #[serde(
446 deserialize_with = "::hyper_serde::deserialize",
447 serialize_with = "::hyper_serde::serialize"
448 )]
449 pub headers: HeaderMap,
450
451 pub unsafe_request: bool,
453
454 pub body: Option<RequestBody>,
456
457 pub service_workers_mode: ServiceWorkersMode,
459 pub client: Option<RequestClient>,
460 pub destination: Destination,
462 pub synchronous: bool,
463 pub mode: RequestMode,
464
465 pub cache_mode: CacheMode,
467
468 pub use_cors_preflight: bool,
470
471 pub keep_alive: bool,
473
474 pub credentials_mode: CredentialsMode,
476 pub use_url_credentials: bool,
477
478 pub origin: Origin,
480
481 pub policy_container: RequestPolicyContainer,
483 pub insecure_requests_policy: InsecureRequestsPolicy,
484 pub has_trustworthy_ancestor_origin: bool,
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 insecure_requests_policy: InsecureRequestsPolicy::DoNotUpgrade,
545 has_trustworthy_ancestor_origin: false,
546 referrer,
547 referrer_policy: ReferrerPolicy::EmptyString,
548 pipeline_id: None,
549 target_webview_id: webview_id,
550 redirect_mode: RedirectMode::Follow,
551 integrity_metadata: "".to_owned(),
552 cryptographic_nonce_metadata: "".to_owned(),
553 url_list: vec![],
554 parser_metadata: ParserMetadata::Default,
555 initiator: Initiator::None,
556 response_tainting: ResponseTainting::Basic,
557 is_internal_request: Default::default(),
558 crash: None,
559 }
560 }
561
562 pub fn preload_id(mut self, preload_id: PreloadId) -> RequestBuilder {
563 self.preload_id = Some(preload_id);
564 self
565 }
566
567 pub fn initiator(mut self, initiator: Initiator) -> RequestBuilder {
569 self.initiator = initiator;
570 self
571 }
572
573 pub fn method(mut self, method: Method) -> RequestBuilder {
575 self.method = method;
576 self
577 }
578
579 pub fn headers(mut self, headers: HeaderMap) -> RequestBuilder {
581 self.headers = headers;
582 self
583 }
584
585 pub fn unsafe_request(mut self, unsafe_request: bool) -> RequestBuilder {
587 self.unsafe_request = unsafe_request;
588 self
589 }
590
591 pub fn body(mut self, body: Option<RequestBody>) -> RequestBuilder {
593 self.body = body;
594 self
595 }
596
597 pub fn destination(mut self, destination: Destination) -> RequestBuilder {
599 self.destination = destination;
600 self
601 }
602
603 pub fn synchronous(mut self, synchronous: bool) -> RequestBuilder {
604 self.synchronous = synchronous;
605 self
606 }
607
608 pub fn mode(mut self, mode: RequestMode) -> RequestBuilder {
609 self.mode = mode;
610 self
611 }
612
613 pub fn use_cors_preflight(mut self, use_cors_preflight: bool) -> RequestBuilder {
615 self.use_cors_preflight = use_cors_preflight;
616 self
617 }
618
619 pub fn keep_alive(mut self, keep_alive: bool) -> RequestBuilder {
621 self.keep_alive = keep_alive;
622 self
623 }
624
625 pub fn credentials_mode(mut self, credentials_mode: CredentialsMode) -> RequestBuilder {
627 self.credentials_mode = credentials_mode;
628 self
629 }
630
631 pub fn use_url_credentials(mut self, use_url_credentials: bool) -> RequestBuilder {
632 self.use_url_credentials = use_url_credentials;
633 self
634 }
635
636 pub fn origin(mut self, origin: ImmutableOrigin) -> RequestBuilder {
638 self.origin = Origin::Origin(origin);
639 self
640 }
641
642 pub fn referrer_policy(mut self, referrer_policy: ReferrerPolicy) -> RequestBuilder {
644 self.referrer_policy = referrer_policy;
645 self
646 }
647
648 pub fn url_list(mut self, url_list: Vec<ServoUrl>) -> RequestBuilder {
650 self.url_list = url_list;
651 self
652 }
653
654 pub fn pipeline_id(mut self, pipeline_id: Option<PipelineId>) -> RequestBuilder {
655 self.pipeline_id = pipeline_id;
656 self
657 }
658
659 pub fn redirect_mode(mut self, redirect_mode: RedirectMode) -> RequestBuilder {
661 self.redirect_mode = redirect_mode;
662 self
663 }
664
665 pub fn integrity_metadata(mut self, integrity_metadata: String) -> RequestBuilder {
667 self.integrity_metadata = integrity_metadata;
668 self
669 }
670
671 pub fn cryptographic_nonce_metadata(mut self, nonce_metadata: String) -> RequestBuilder {
673 self.cryptographic_nonce_metadata = nonce_metadata;
674 self
675 }
676
677 pub fn parser_metadata(mut self, parser_metadata: ParserMetadata) -> RequestBuilder {
679 self.parser_metadata = parser_metadata;
680 self
681 }
682
683 pub fn response_tainting(mut self, response_tainting: ResponseTainting) -> RequestBuilder {
684 self.response_tainting = response_tainting;
685 self
686 }
687
688 pub fn crash(mut self, crash: Option<String>) -> Self {
689 self.crash = crash;
690 self
691 }
692
693 pub fn policy_container(mut self, policy_container: PolicyContainer) -> RequestBuilder {
695 self.policy_container = RequestPolicyContainer::PolicyContainer(policy_container);
696 self
697 }
698
699 pub fn client(mut self, client: RequestClient) -> RequestBuilder {
701 self.client = Some(client);
702 self
703 }
704
705 pub fn insecure_requests_policy(
706 mut self,
707 insecure_requests_policy: InsecureRequestsPolicy,
708 ) -> RequestBuilder {
709 self.insecure_requests_policy = insecure_requests_policy;
710 self
711 }
712
713 pub fn has_trustworthy_ancestor_origin(
714 mut self,
715 has_trustworthy_ancestor_origin: bool,
716 ) -> RequestBuilder {
717 self.has_trustworthy_ancestor_origin = has_trustworthy_ancestor_origin;
718 self
719 }
720
721 pub fn service_workers_mode(
723 mut self,
724 service_workers_mode: ServiceWorkersMode,
725 ) -> RequestBuilder {
726 self.service_workers_mode = service_workers_mode;
727 self
728 }
729
730 pub fn cache_mode(mut self, cache_mode: CacheMode) -> RequestBuilder {
732 self.cache_mode = cache_mode;
733 self
734 }
735
736 pub fn is_internal_request(mut self, is_internal_request: InternalRequest) -> RequestBuilder {
737 self.is_internal_request = is_internal_request;
738 self
739 }
740
741 pub fn build(self) -> Request {
742 let mut request = Request::new(
743 self.id,
744 self.url.clone(),
745 Some(self.origin),
746 self.referrer,
747 self.pipeline_id,
748 self.target_webview_id,
749 );
750 request.preload_id = self.preload_id;
751 request.initiator = self.initiator;
752 request.method = self.method;
753 request.headers = self.headers;
754 request.unsafe_request = self.unsafe_request;
755 request.body = self.body;
756 request.service_workers_mode = self.service_workers_mode;
757 request.destination = self.destination;
758 request.synchronous = self.synchronous;
759 request.mode = self.mode;
760 request.use_cors_preflight = self.use_cors_preflight;
761 request.keep_alive = self.keep_alive;
762 request.credentials_mode = self.credentials_mode;
763 request.use_url_credentials = self.use_url_credentials;
764 request.cache_mode = self.cache_mode;
765 request.referrer_policy = self.referrer_policy;
766 request.redirect_mode = self.redirect_mode;
767 let mut url_list: Vec<_> = self
768 .url_list
769 .into_iter()
770 .map(UrlWithBlobClaim::from_url_without_having_claimed_blob)
771 .collect();
772 if url_list.is_empty() {
773 url_list.push(self.url);
774 }
775 request.redirect_count = url_list.len() as u32 - 1;
776 request.url_list = url_list;
777 request.integrity_metadata = self.integrity_metadata;
778 request.cryptographic_nonce_metadata = self.cryptographic_nonce_metadata;
779 request.parser_metadata = self.parser_metadata;
780 request.response_tainting = self.response_tainting;
781 request.crash = self.crash;
782 request.client = self.client;
783 request.policy_container = self.policy_container;
784 request.insecure_requests_policy = self.insecure_requests_policy;
785 request.has_trustworthy_ancestor_origin = self.has_trustworthy_ancestor_origin;
786 request.is_internal_request = self.is_internal_request;
787 request
788 }
789
790 pub fn keep_alive_body_length(&self) -> u64 {
792 assert!(self.keep_alive);
793 self.body.body_length() as u64
794 }
795}
796
797#[derive(Clone, MallocSizeOf)]
800pub struct Request {
801 pub id: RequestId,
805 pub preload_id: Option<PreloadId>,
806 pub method: Method,
808 pub local_urls_only: bool,
810 pub headers: HeaderMap,
812 pub unsafe_request: bool,
814 pub body: Option<RequestBody>,
816 pub client: Option<RequestClient>,
818 pub traversable_for_user_prompts: TraversableForUserPrompts,
820 pub target_webview_id: Option<WebViewId>,
821 pub keep_alive: bool,
823 pub service_workers_mode: ServiceWorkersMode,
825 pub initiator: Initiator,
827 pub destination: Destination,
829 pub origin: Origin,
832 pub referrer: Referrer,
834 pub referrer_policy: ReferrerPolicy,
836 pub pipeline_id: Option<PipelineId>,
837 pub synchronous: bool,
839 pub mode: RequestMode,
841 pub use_cors_preflight: bool,
843 pub credentials_mode: CredentialsMode,
845 pub use_url_credentials: bool,
847 pub cache_mode: CacheMode,
849 pub redirect_mode: RedirectMode,
851 pub integrity_metadata: String,
853 pub cryptographic_nonce_metadata: String,
855 pub url_list: Vec<UrlWithBlobClaim>,
857 pub redirect_count: u32,
859 pub response_tainting: ResponseTainting,
861 pub parser_metadata: ParserMetadata,
863 pub policy_container: RequestPolicyContainer,
865 pub insecure_requests_policy: InsecureRequestsPolicy,
867 pub has_trustworthy_ancestor_origin: bool,
868 pub crash: Option<String>,
870 pub is_internal_request: InternalRequest,
872}
873
874impl Request {
875 pub fn new(
876 id: RequestId,
877 url: UrlWithBlobClaim,
878 origin: Option<Origin>,
879 referrer: Referrer,
880 pipeline_id: Option<PipelineId>,
881 webview_id: Option<WebViewId>,
882 ) -> Request {
883 Request {
884 id,
885 preload_id: None,
886 method: Method::GET,
887 local_urls_only: false,
888 headers: HeaderMap::new(),
889 unsafe_request: false,
890 body: None,
891 client: None,
892 traversable_for_user_prompts: TraversableForUserPrompts::Client,
893 keep_alive: false,
894 service_workers_mode: ServiceWorkersMode::All,
895 initiator: Initiator::None,
896 destination: Destination::None,
897 origin: origin.unwrap_or(Origin::Client),
898 referrer,
899 referrer_policy: ReferrerPolicy::EmptyString,
900 pipeline_id,
901 target_webview_id: webview_id,
902 synchronous: false,
903 mode: RequestMode::NoCors,
904 use_cors_preflight: false,
905 credentials_mode: CredentialsMode::CredentialsSameOrigin,
906 use_url_credentials: false,
907 cache_mode: CacheMode::Default,
908 redirect_mode: RedirectMode::Follow,
909 integrity_metadata: String::new(),
910 cryptographic_nonce_metadata: String::new(),
911 url_list: vec![url],
912 parser_metadata: ParserMetadata::Default,
913 redirect_count: 0,
914 response_tainting: ResponseTainting::Basic,
915 policy_container: RequestPolicyContainer::Client,
916 insecure_requests_policy: InsecureRequestsPolicy::DoNotUpgrade,
917 has_trustworthy_ancestor_origin: false,
918 is_internal_request: Default::default(),
919 crash: None,
920 }
921 }
922
923 pub fn url(&self) -> ServoUrl {
925 self.url_list.first().unwrap().url()
926 }
927
928 pub fn url_with_blob_claim(&self) -> UrlWithBlobClaim {
929 self.url_list.first().unwrap().clone()
930 }
931
932 pub fn original_url(&self) -> ServoUrl {
933 match self.mode {
934 RequestMode::WebSocket {
935 protocols: _,
936 ref original_url,
937 } => original_url.clone(),
938 _ => self.url(),
939 }
940 }
941
942 pub fn current_url(&self) -> ServoUrl {
944 self.current_url_with_blob_claim().url()
945 }
946
947 pub fn current_url_with_blob_claim(&self) -> UrlWithBlobClaim {
949 self.url_list.last().unwrap().clone()
950 }
951
952 pub fn current_url_mut(&mut self) -> &mut ServoUrl {
954 self.url_list.last_mut().unwrap()
955 }
956
957 pub fn is_navigation_request(&self) -> bool {
959 matches!(
960 self.destination,
961 Destination::Document |
962 Destination::Embed |
963 Destination::Frame |
964 Destination::IFrame |
965 Destination::Object
966 )
967 }
968
969 pub fn is_subresource_request(&self) -> bool {
971 matches!(
972 self.destination,
973 Destination::Audio |
974 Destination::Font |
975 Destination::Image |
976 Destination::Manifest |
977 Destination::Script |
978 Destination::Style |
979 Destination::Track |
980 Destination::Video |
981 Destination::Xslt |
982 Destination::None
983 )
984 }
985
986 pub fn timing_type(&self) -> ResourceTimingType {
987 if self.is_navigation_request() {
988 ResourceTimingType::Navigation
989 } else {
990 ResourceTimingType::Resource
991 }
992 }
993
994 pub fn populate_request_from_client(&mut self) {
996 if self.traversable_for_user_prompts == TraversableForUserPrompts::Client {
998 self.traversable_for_user_prompts = TraversableForUserPrompts::NoTraversable;
1000 if self.client.is_some() {
1002 self.traversable_for_user_prompts =
1007 TraversableForUserPrompts::TraversableNavigable(Default::default());
1008 }
1009 }
1010 if self.origin == Origin::Client {
1012 let Some(client) = self.client.as_ref() else {
1013 unreachable!();
1015 };
1016 self.origin = client.origin.clone();
1018 }
1019 if matches!(self.policy_container, RequestPolicyContainer::Client) {
1021 if let Some(client) = self.client.as_ref() {
1024 self.policy_container = client.policy_container.clone();
1025 } else {
1026 self.policy_container =
1028 RequestPolicyContainer::PolicyContainer(PolicyContainer::default());
1029 }
1030 }
1031 }
1032
1033 pub fn keep_alive_body_length(&self) -> u64 {
1035 assert!(self.keep_alive);
1036 self.body.body_length() as u64
1037 }
1038
1039 pub fn total_request_length(&self) -> usize {
1041 let mut total_request_length = self.url()[..Position::AfterQuery].len();
1043 total_request_length += self
1045 .referrer
1046 .to_url()
1047 .map(|url| url.as_str().len())
1048 .unwrap_or_default();
1049 total_request_length += self.headers.total_size();
1052 total_request_length += self.body.body_length();
1054 total_request_length
1056 }
1057
1058 pub fn redirect_taint_for_request(&self) -> RedirectTaint {
1060 let Origin::Origin(request_origin) = &self.origin else {
1062 unreachable!("origin cannot be \"client\" at this point in time");
1063 };
1064
1065 let mut last_url = None;
1067
1068 let mut taint = RedirectTaint::SameOrigin;
1070
1071 for url in &self.url_list {
1073 let Some(last_url) = &mut last_url else {
1075 last_url = Some(url);
1076 continue;
1077 };
1078
1079 if !is_same_site(&url.origin(), &last_url.origin()) &&
1082 !is_same_site(request_origin, &last_url.origin())
1083 {
1084 return RedirectTaint::CrossSite;
1085 }
1086
1087 if url.origin() != last_url.origin() && *request_origin != last_url.origin() {
1090 taint = RedirectTaint::SameSite;
1091 }
1092
1093 *last_url = url;
1095 }
1096
1097 taint
1099 }
1100}
1101
1102impl Referrer {
1103 pub fn to_url(&self) -> Option<&ServoUrl> {
1104 match *self {
1105 Referrer::NoReferrer => None,
1106 Referrer::Client(ref url) => Some(url),
1107 Referrer::ReferrerUrl(ref url) => Some(url),
1108 }
1109 }
1110}
1111
1112fn is_cors_unsafe_request_header_byte(value: &u8) -> bool {
1116 matches!(value,
1117 0x00..=0x08 |
1118 0x10..=0x19 |
1119 0x22 |
1120 0x28 |
1121 0x29 |
1122 0x3A |
1123 0x3C |
1124 0x3E |
1125 0x3F |
1126 0x40 |
1127 0x5B |
1128 0x5C |
1129 0x5D |
1130 0x7B |
1131 0x7D |
1132 0x7F
1133 )
1134}
1135
1136fn is_cors_safelisted_request_accept(value: &[u8]) -> bool {
1139 !(value.iter().any(is_cors_unsafe_request_header_byte))
1140}
1141
1142fn is_cors_safelisted_language(value: &[u8]) -> bool {
1145 value.iter().all(|&x| {
1146 matches!(x,
1147 0x30..=0x39 |
1148 0x41..=0x5A |
1149 0x61..=0x7A |
1150 0x20 |
1151 0x2A |
1152 0x2C |
1153 0x2D |
1154 0x2E |
1155 0x3B |
1156 0x3D
1157 )
1158 })
1159}
1160
1161pub fn is_cors_safelisted_request_content_type(value: &[u8]) -> bool {
1164 if value.iter().any(is_cors_unsafe_request_header_byte) {
1166 return false;
1167 }
1168 let value_string = if let Ok(s) = std::str::from_utf8(value) {
1170 s
1171 } else {
1172 return false;
1173 };
1174 let value_mime_result: Result<Mime, _> = value_string.parse();
1175 match value_mime_result {
1176 Err(_) => false, Ok(value_mime) => match (value_mime.type_(), value_mime.subtype()) {
1178 (mime::APPLICATION, mime::WWW_FORM_URLENCODED) |
1179 (mime::MULTIPART, mime::FORM_DATA) |
1180 (mime::TEXT, mime::PLAIN) => true,
1181 _ => false, },
1183 }
1184}
1185
1186pub fn is_cors_safelisted_request_header<N: AsRef<str>, V: AsRef<[u8]>>(
1190 name: &N,
1191 value: &V,
1192) -> bool {
1193 let name: &str = name.as_ref();
1194 let value: &[u8] = value.as_ref();
1195 if value.len() > 128 {
1196 return false;
1197 }
1198 match name {
1199 "accept" => is_cors_safelisted_request_accept(value),
1200 "accept-language" | "content-language" => is_cors_safelisted_language(value),
1201 "content-type" => is_cors_safelisted_request_content_type(value),
1202 "range" => is_cors_safelisted_request_range(value),
1203 _ => false,
1204 }
1205}
1206
1207pub fn is_cors_safelisted_request_range(value: &[u8]) -> bool {
1208 if let Ok(value_str) = std::str::from_utf8(value) {
1209 return validate_range_header(value_str);
1210 }
1211 false
1212}
1213
1214fn validate_range_header(value: &str) -> bool {
1215 let trimmed = value.trim();
1216 if !trimmed.starts_with("bytes=") {
1217 return false;
1218 }
1219
1220 if let Some(range) = trimmed.strip_prefix("bytes=") {
1221 let mut parts = range.split('-');
1222 let start = parts.next();
1223 let end = parts.next();
1224
1225 if let Some(start) = start &&
1226 let Ok(start_num) = start.parse::<u64>()
1227 {
1228 return match end {
1229 Some(e) if !e.is_empty() => {
1230 e.parse::<u64>().is_ok_and(|end_num| start_num <= end_num)
1231 },
1232 _ => true,
1233 };
1234 }
1235 }
1236 false
1237}
1238
1239pub fn is_cors_safelisted_method(method: &Method) -> bool {
1241 matches!(*method, Method::GET | Method::HEAD | Method::POST)
1242}
1243
1244pub fn is_cors_non_wildcard_request_header_name(name: &HeaderName) -> bool {
1246 name == AUTHORIZATION
1247}
1248
1249pub fn get_cors_unsafe_header_names(headers: &HeaderMap) -> Vec<HeaderName> {
1251 let mut unsafe_names: Vec<&HeaderName> = vec![];
1253 let mut potentillay_unsafe_names: Vec<&HeaderName> = vec![];
1255 let mut safelist_value_size = 0;
1257
1258 for (name, value) in headers.iter() {
1260 if !is_cors_safelisted_request_header(&name, &value) {
1261 unsafe_names.push(name);
1262 } else {
1263 potentillay_unsafe_names.push(name);
1264 safelist_value_size += value.as_ref().len();
1265 }
1266 }
1267
1268 if safelist_value_size > 1024 {
1270 unsafe_names.extend_from_slice(&potentillay_unsafe_names);
1271 }
1272
1273 convert_header_names_to_sorted_lowercase_set(unsafe_names)
1275}
1276
1277pub fn convert_header_names_to_sorted_lowercase_set(
1279 header_names: Vec<&HeaderName>,
1280) -> Vec<HeaderName> {
1281 let mut ordered_set = header_names.to_vec();
1284 ordered_set.sort_by(|a, b| a.as_str().partial_cmp(b.as_str()).unwrap());
1285 ordered_set.dedup();
1286 ordered_set.into_iter().cloned().collect()
1287}
1288
1289pub fn create_request_body_with_content(content: String) -> RequestBody {
1290 let content_bytes = GenericSharedMemory::from_vec(content.into_bytes());
1291 let content_len = content_bytes.len();
1292
1293 let (chunk_request_sender, chunk_request_receiver) = ipc::channel().unwrap();
1294 ROUTER.add_typed_route(
1295 chunk_request_receiver,
1296 Box::new(move |message| {
1297 let request = message.unwrap();
1298 if let BodyChunkRequest::Connect(sender) = request {
1299 let _ = sender.send(BodyChunkResponse::Chunk(content_bytes.clone()));
1300 let _ = sender.send(BodyChunkResponse::Done);
1301 }
1302 }),
1303 );
1304
1305 RequestBody::new(chunk_request_sender, BodySource::Object, Some(content_len))
1306}