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::policy_container::{PolicyContainer, RequestPolicyContainer};
26use crate::pub_domains::is_same_site;
27use crate::response::{HttpsState, RedirectTaint, Response};
28use crate::{ReferrerPolicy, ResourceTimingType};
29
30#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
31pub struct RequestId(pub Uuid);
33
34impl Default for RequestId {
35 fn default() -> Self {
36 Self(Uuid::new_v4())
37 }
38}
39
40#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
42pub enum Initiator {
43 None,
44 Download,
45 ImageSet,
46 Manifest,
47 XSLT,
48 Prefetch,
49 Link,
50}
51
52pub use csp::Destination;
54
55#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
57pub enum Origin {
58 Client,
59 Origin(ImmutableOrigin),
60}
61
62impl Origin {
63 pub fn is_opaque(&self) -> bool {
64 matches!(self, Origin::Origin(ImmutableOrigin::Opaque(_)))
65 }
66}
67
68#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
70pub enum Referrer {
71 NoReferrer,
72 Client(ServoUrl),
78 ReferrerUrl(ServoUrl),
79}
80
81#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
83pub enum RequestMode {
84 Navigate,
85 SameOrigin,
86 NoCors,
87 CorsMode,
88 WebSocket {
89 protocols: Vec<String>,
90 original_url: ServoUrl,
91 },
92}
93
94#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
96pub enum CredentialsMode {
97 Omit,
98 CredentialsSameOrigin,
99 Include,
100}
101
102#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
104pub enum CacheMode {
105 Default,
106 NoStore,
107 Reload,
108 NoCache,
109 ForceCache,
110 OnlyIfCached,
111}
112
113#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
115pub enum ServiceWorkersMode {
116 All,
117 None,
118}
119
120#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
122pub enum RedirectMode {
123 Follow,
124 Error,
125 Manual,
126}
127
128#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
130pub enum ResponseTainting {
131 Basic,
132 CorsTainting,
133 Opaque,
134}
135
136#[derive(Clone, Debug, Eq, Hash, Deserialize, MallocSizeOf, Serialize, PartialEq)]
138pub struct PreloadKey {
139 pub url: ServoUrl,
141 pub destination: Destination,
143 pub mode: RequestMode,
145 pub credentials_mode: CredentialsMode,
147}
148
149impl PreloadKey {
150 pub fn new(request: &RequestBuilder) -> Self {
151 Self {
152 url: request.url.clone(),
153 destination: request.destination,
154 mode: request.mode.clone(),
155 credentials_mode: request.credentials_mode,
156 }
157 }
158}
159
160#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize, Hash, MallocSizeOf)]
161pub struct PreloadId(pub Uuid);
162
163impl Default for PreloadId {
164 fn default() -> Self {
165 Self(Uuid::new_v4())
166 }
167}
168
169#[derive(Debug, MallocSizeOf)]
171pub struct PreloadEntry {
172 pub integrity_metadata: String,
174 pub response: Option<Response>,
176 #[ignore_malloc_size_of = "Channels are hard"]
178 pub on_response_available: Option<TokioSender<Response>>,
179}
180
181impl PreloadEntry {
182 pub fn new(integrity_metadata: String) -> Self {
183 Self {
184 integrity_metadata,
185 response: None,
186 on_response_available: None,
187 }
188 }
189
190 pub fn with_response(&mut self, response: Response) {
192 if let Some(sender) = self.on_response_available.take() {
195 let _ = sender.send(response);
196 } else {
197 self.response = Some(response);
198 }
199 }
200}
201
202pub type PreloadedResources = FxHashMap<PreloadKey, PreloadId>;
203
204#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
206pub struct RequestClient {
207 pub preloaded_resources: PreloadedResources,
209 pub policy_container: RequestPolicyContainer,
211 pub origin: Origin,
213 pub is_nested_browsing_context: bool,
215 pub insecure_requests_policy: InsecureRequestsPolicy,
217}
218
219#[derive(Clone, Copy, Default, MallocSizeOf, PartialEq)]
221pub enum SystemVisibilityState {
222 #[default]
223 Hidden,
224 Visible,
225}
226
227#[derive(Clone, Copy, Default, MallocSizeOf, PartialEq)]
229pub struct TraversableNavigable {
230 current_session_history_step: u8,
232 running_nested_apply_history_step: bool,
236 system_visibility_state: SystemVisibilityState,
238 is_created_by_web_content: bool,
240}
241
242#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
244pub enum TraversableForUserPrompts {
245 NoTraversable,
246 Client,
247 TraversableNavigable(TraversableNavigable),
248}
249
250#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
252pub enum CorsSettings {
253 Anonymous,
254 UseCredentials,
255}
256
257impl CorsSettings {
258 pub fn from_enumerated_attribute(value: &str) -> CorsSettings {
260 match value.to_ascii_lowercase().as_str() {
261 "anonymous" => CorsSettings::Anonymous,
262 "use-credentials" => CorsSettings::UseCredentials,
263 _ => CorsSettings::Anonymous,
264 }
265 }
266}
267
268#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
270pub enum ParserMetadata {
271 Default,
272 ParserInserted,
273 NotParserInserted,
274}
275
276#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
278pub enum BodySource {
279 Null,
280 Object,
281}
282
283#[derive(Debug, Deserialize, Serialize)]
286pub enum BodyChunkResponse {
287 Chunk(GenericSharedMemory),
289 Done,
291 Error,
294}
295
296#[derive(Debug, Deserialize, Serialize)]
300pub enum BodyChunkRequest {
301 Connect(IpcSender<BodyChunkResponse>),
303 Extract(IpcReceiver<BodyChunkRequest>),
305 Chunk,
307 Done,
309 Error,
311}
312
313#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
319pub struct RequestBody {
320 #[ignore_malloc_size_of = "Channels are hard"]
322 body_chunk_request_channel: Arc<Mutex<Option<IpcSender<BodyChunkRequest>>>>,
323 source: BodySource,
325 total_bytes: Option<usize>,
327}
328
329impl RequestBody {
330 pub fn new(
331 body_chunk_request_channel: IpcSender<BodyChunkRequest>,
332 source: BodySource,
333 total_bytes: Option<usize>,
334 ) -> Self {
335 RequestBody {
336 body_chunk_request_channel: Arc::new(Mutex::new(Some(body_chunk_request_channel))),
337 source,
338 total_bytes,
339 }
340 }
341
342 pub fn extract_source(&mut self) {
344 match self.source {
345 BodySource::Null => panic!("Null sources should never be re-directed."),
346 BodySource::Object => {
347 let (chan, port) = ipc::channel().unwrap();
348 let mut lock = self.body_chunk_request_channel.lock();
349 let Some(selfchan) = lock.as_mut() else {
350 error!(
351 "Could not re-extract the request body source because the body stream has already been closed."
352 );
353 return;
354 };
355 if let Err(error) = selfchan.send(BodyChunkRequest::Extract(port)) {
356 error!(
357 "Could not re-extract the request body source because the body stream has already been closed: {error}"
358 );
359 return;
360 }
361 *selfchan = chan;
362 },
363 }
364 }
365
366 pub fn clone_stream(&self) -> Arc<Mutex<Option<IpcSender<BodyChunkRequest>>>> {
368 self.body_chunk_request_channel.clone()
369 }
370
371 pub fn close_stream(&self) {
376 self.body_chunk_request_channel.lock().take();
377 }
378
379 pub fn source_is_null(&self) -> bool {
380 self.source == BodySource::Null
381 }
382
383 #[expect(clippy::len_without_is_empty)]
384 pub fn len(&self) -> Option<usize> {
385 self.total_bytes
386 }
387}
388
389trait RequestBodySize {
390 fn body_length(&self) -> usize;
391}
392
393impl RequestBodySize for Option<RequestBody> {
394 fn body_length(&self) -> usize {
395 self.as_ref()
396 .and_then(|body| body.len())
397 .unwrap_or_default()
398 }
399}
400
401#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
402pub enum InsecureRequestsPolicy {
403 DoNotUpgrade,
404 Upgrade,
405}
406
407pub trait RequestHeadersSize {
408 fn total_size(&self) -> usize;
409}
410
411impl RequestHeadersSize for HeaderMap {
412 fn total_size(&self) -> usize {
413 self.iter()
414 .map(|(name, value)| name.as_str().len() + value.len())
415 .sum()
416 }
417}
418
419#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
420pub struct RequestBuilder {
421 pub id: RequestId,
422
423 pub preload_id: Option<PreloadId>,
424
425 #[serde(
427 deserialize_with = "::hyper_serde::deserialize",
428 serialize_with = "::hyper_serde::serialize"
429 )]
430 #[ignore_malloc_size_of = "Defined in hyper"]
431 pub method: Method,
432
433 pub url: ServoUrl,
435
436 #[serde(
438 deserialize_with = "::hyper_serde::deserialize",
439 serialize_with = "::hyper_serde::serialize"
440 )]
441 #[ignore_malloc_size_of = "Defined in hyper"]
442 pub headers: HeaderMap,
443
444 pub unsafe_request: bool,
446
447 pub body: Option<RequestBody>,
449
450 pub service_workers_mode: ServiceWorkersMode,
452 pub client: Option<RequestClient>,
453 pub destination: Destination,
455 pub synchronous: bool,
456 pub mode: RequestMode,
457
458 pub cache_mode: CacheMode,
460
461 pub use_cors_preflight: bool,
463
464 pub keep_alive: bool,
466
467 pub credentials_mode: CredentialsMode,
469 pub use_url_credentials: bool,
470
471 pub origin: Origin,
473
474 pub policy_container: RequestPolicyContainer,
476 pub insecure_requests_policy: InsecureRequestsPolicy,
477 pub has_trustworthy_ancestor_origin: bool,
478
479 pub referrer: Referrer,
481
482 pub referrer_policy: ReferrerPolicy,
484 pub pipeline_id: Option<PipelineId>,
485 pub target_webview_id: Option<WebViewId>,
486
487 pub redirect_mode: RedirectMode,
489
490 pub integrity_metadata: String,
492
493 pub cryptographic_nonce_metadata: String,
495
496 pub url_list: Vec<ServoUrl>,
498
499 pub parser_metadata: ParserMetadata,
501
502 pub initiator: Initiator,
504 pub https_state: HttpsState,
505 pub response_tainting: ResponseTainting,
506 pub crash: Option<String>,
508}
509
510impl RequestBuilder {
511 pub fn new(webview_id: Option<WebViewId>, url: ServoUrl, referrer: Referrer) -> RequestBuilder {
512 RequestBuilder {
513 id: RequestId::default(),
514 preload_id: None,
515 method: Method::GET,
516 url,
517 headers: HeaderMap::new(),
518 unsafe_request: false,
519 body: None,
520 service_workers_mode: ServiceWorkersMode::All,
521 destination: Destination::None,
522 synchronous: false,
523 mode: RequestMode::NoCors,
524 cache_mode: CacheMode::Default,
525 use_cors_preflight: false,
526 keep_alive: false,
527 credentials_mode: CredentialsMode::CredentialsSameOrigin,
528 use_url_credentials: false,
529 origin: Origin::Client,
530 client: None,
531 policy_container: RequestPolicyContainer::default(),
532 insecure_requests_policy: InsecureRequestsPolicy::DoNotUpgrade,
533 has_trustworthy_ancestor_origin: false,
534 referrer,
535 referrer_policy: ReferrerPolicy::EmptyString,
536 pipeline_id: None,
537 target_webview_id: webview_id,
538 redirect_mode: RedirectMode::Follow,
539 integrity_metadata: "".to_owned(),
540 cryptographic_nonce_metadata: "".to_owned(),
541 url_list: vec![],
542 parser_metadata: ParserMetadata::Default,
543 initiator: Initiator::None,
544 https_state: HttpsState::None,
545 response_tainting: ResponseTainting::Basic,
546 crash: None,
547 }
548 }
549
550 pub fn preload_id(mut self, preload_id: PreloadId) -> RequestBuilder {
551 self.preload_id = Some(preload_id);
552 self
553 }
554
555 pub fn initiator(mut self, initiator: Initiator) -> RequestBuilder {
557 self.initiator = initiator;
558 self
559 }
560
561 pub fn method(mut self, method: Method) -> RequestBuilder {
563 self.method = method;
564 self
565 }
566
567 pub fn headers(mut self, headers: HeaderMap) -> RequestBuilder {
569 self.headers = headers;
570 self
571 }
572
573 pub fn unsafe_request(mut self, unsafe_request: bool) -> RequestBuilder {
575 self.unsafe_request = unsafe_request;
576 self
577 }
578
579 pub fn body(mut self, body: Option<RequestBody>) -> RequestBuilder {
581 self.body = body;
582 self
583 }
584
585 pub fn destination(mut self, destination: Destination) -> RequestBuilder {
587 self.destination = destination;
588 self
589 }
590
591 pub fn synchronous(mut self, synchronous: bool) -> RequestBuilder {
592 self.synchronous = synchronous;
593 self
594 }
595
596 pub fn mode(mut self, mode: RequestMode) -> RequestBuilder {
597 self.mode = mode;
598 self
599 }
600
601 pub fn use_cors_preflight(mut self, use_cors_preflight: bool) -> RequestBuilder {
603 self.use_cors_preflight = use_cors_preflight;
604 self
605 }
606
607 pub fn keep_alive(mut self, keep_alive: bool) -> RequestBuilder {
609 self.keep_alive = keep_alive;
610 self
611 }
612
613 pub fn credentials_mode(mut self, credentials_mode: CredentialsMode) -> RequestBuilder {
615 self.credentials_mode = credentials_mode;
616 self
617 }
618
619 pub fn use_url_credentials(mut self, use_url_credentials: bool) -> RequestBuilder {
620 self.use_url_credentials = use_url_credentials;
621 self
622 }
623
624 pub fn origin(mut self, origin: ImmutableOrigin) -> RequestBuilder {
626 self.origin = Origin::Origin(origin);
627 self
628 }
629
630 pub fn referrer_policy(mut self, referrer_policy: ReferrerPolicy) -> RequestBuilder {
632 self.referrer_policy = referrer_policy;
633 self
634 }
635
636 pub fn pipeline_id(mut self, pipeline_id: Option<PipelineId>) -> RequestBuilder {
637 self.pipeline_id = pipeline_id;
638 self
639 }
640
641 pub fn redirect_mode(mut self, redirect_mode: RedirectMode) -> RequestBuilder {
643 self.redirect_mode = redirect_mode;
644 self
645 }
646
647 pub fn integrity_metadata(mut self, integrity_metadata: String) -> RequestBuilder {
649 self.integrity_metadata = integrity_metadata;
650 self
651 }
652
653 pub fn cryptographic_nonce_metadata(mut self, nonce_metadata: String) -> RequestBuilder {
655 self.cryptographic_nonce_metadata = nonce_metadata;
656 self
657 }
658
659 pub fn parser_metadata(mut self, parser_metadata: ParserMetadata) -> RequestBuilder {
661 self.parser_metadata = parser_metadata;
662 self
663 }
664
665 pub fn https_state(mut self, https_state: HttpsState) -> RequestBuilder {
666 self.https_state = https_state;
667 self
668 }
669
670 pub fn response_tainting(mut self, response_tainting: ResponseTainting) -> RequestBuilder {
671 self.response_tainting = response_tainting;
672 self
673 }
674
675 pub fn crash(mut self, crash: Option<String>) -> Self {
676 self.crash = crash;
677 self
678 }
679
680 pub fn policy_container(mut self, policy_container: PolicyContainer) -> RequestBuilder {
682 self.policy_container = RequestPolicyContainer::PolicyContainer(policy_container);
683 self
684 }
685
686 pub fn client(mut self, client: RequestClient) -> RequestBuilder {
688 self.client = Some(client);
689 self
690 }
691
692 pub fn insecure_requests_policy(
693 mut self,
694 insecure_requests_policy: InsecureRequestsPolicy,
695 ) -> RequestBuilder {
696 self.insecure_requests_policy = insecure_requests_policy;
697 self
698 }
699
700 pub fn has_trustworthy_ancestor_origin(
701 mut self,
702 has_trustworthy_ancestor_origin: bool,
703 ) -> RequestBuilder {
704 self.has_trustworthy_ancestor_origin = has_trustworthy_ancestor_origin;
705 self
706 }
707
708 pub fn service_workers_mode(
710 mut self,
711 service_workers_mode: ServiceWorkersMode,
712 ) -> RequestBuilder {
713 self.service_workers_mode = service_workers_mode;
714 self
715 }
716
717 pub fn cache_mode(mut self, cache_mode: CacheMode) -> RequestBuilder {
719 self.cache_mode = cache_mode;
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 self.https_state,
732 );
733 request.preload_id = self.preload_id;
734 request.initiator = self.initiator;
735 request.method = self.method;
736 request.headers = self.headers;
737 request.unsafe_request = self.unsafe_request;
738 request.body = self.body;
739 request.service_workers_mode = self.service_workers_mode;
740 request.destination = self.destination;
741 request.synchronous = self.synchronous;
742 request.mode = self.mode;
743 request.use_cors_preflight = self.use_cors_preflight;
744 request.keep_alive = self.keep_alive;
745 request.credentials_mode = self.credentials_mode;
746 request.use_url_credentials = self.use_url_credentials;
747 request.cache_mode = self.cache_mode;
748 request.referrer_policy = self.referrer_policy;
749 request.redirect_mode = self.redirect_mode;
750 let mut url_list = self.url_list;
751 if url_list.is_empty() {
752 url_list.push(self.url);
753 }
754 request.redirect_count = url_list.len() as u32 - 1;
755 request.url_list = url_list;
756 request.integrity_metadata = self.integrity_metadata;
757 request.cryptographic_nonce_metadata = self.cryptographic_nonce_metadata;
758 request.parser_metadata = self.parser_metadata;
759 request.response_tainting = self.response_tainting;
760 request.crash = self.crash;
761 request.client = self.client;
762 request.policy_container = self.policy_container;
763 request.insecure_requests_policy = self.insecure_requests_policy;
764 request.has_trustworthy_ancestor_origin = self.has_trustworthy_ancestor_origin;
765 request
766 }
767
768 pub fn keep_alive_body_length(&self) -> u64 {
770 assert!(self.keep_alive);
771 self.body.body_length() as u64
772 }
773}
774
775#[derive(Clone, MallocSizeOf)]
778pub struct Request {
779 pub id: RequestId,
783 pub preload_id: Option<PreloadId>,
784 #[ignore_malloc_size_of = "Defined in hyper"]
786 pub method: Method,
787 pub local_urls_only: bool,
789 #[ignore_malloc_size_of = "Defined in hyper"]
791 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<ServoUrl>,
839 pub redirect_count: u32,
841 pub response_tainting: ResponseTainting,
843 pub parser_metadata: ParserMetadata,
845 pub policy_container: RequestPolicyContainer,
847 pub insecure_requests_policy: InsecureRequestsPolicy,
849 pub has_trustworthy_ancestor_origin: bool,
850 pub https_state: HttpsState,
851 pub crash: Option<String>,
853}
854
855impl Request {
856 pub fn new(
857 id: RequestId,
858 url: ServoUrl,
859 origin: Option<Origin>,
860 referrer: Referrer,
861 pipeline_id: Option<PipelineId>,
862 webview_id: Option<WebViewId>,
863 https_state: HttpsState,
864 ) -> Request {
865 Request {
866 id,
867 preload_id: None,
868 method: Method::GET,
869 local_urls_only: false,
870 headers: HeaderMap::new(),
871 unsafe_request: false,
872 body: None,
873 client: None,
874 traversable_for_user_prompts: TraversableForUserPrompts::Client,
875 keep_alive: false,
876 service_workers_mode: ServiceWorkersMode::All,
877 initiator: Initiator::None,
878 destination: Destination::None,
879 origin: origin.unwrap_or(Origin::Client),
880 referrer,
881 referrer_policy: ReferrerPolicy::EmptyString,
882 pipeline_id,
883 target_webview_id: webview_id,
884 synchronous: false,
885 mode: RequestMode::NoCors,
886 use_cors_preflight: false,
887 credentials_mode: CredentialsMode::CredentialsSameOrigin,
888 use_url_credentials: false,
889 cache_mode: CacheMode::Default,
890 redirect_mode: RedirectMode::Follow,
891 integrity_metadata: String::new(),
892 cryptographic_nonce_metadata: String::new(),
893 url_list: vec![url],
894 parser_metadata: ParserMetadata::Default,
895 redirect_count: 0,
896 response_tainting: ResponseTainting::Basic,
897 policy_container: RequestPolicyContainer::Client,
898 insecure_requests_policy: InsecureRequestsPolicy::DoNotUpgrade,
899 has_trustworthy_ancestor_origin: false,
900 https_state,
901 crash: None,
902 }
903 }
904
905 pub fn url(&self) -> ServoUrl {
907 self.url_list.first().unwrap().clone()
908 }
909
910 pub fn original_url(&self) -> ServoUrl {
911 match self.mode {
912 RequestMode::WebSocket {
913 protocols: _,
914 ref original_url,
915 } => original_url.clone(),
916 _ => self.url(),
917 }
918 }
919
920 pub fn current_url(&self) -> ServoUrl {
922 self.url_list.last().unwrap().clone()
923 }
924
925 pub fn current_url_mut(&mut self) -> &mut ServoUrl {
927 self.url_list.last_mut().unwrap()
928 }
929
930 pub fn is_navigation_request(&self) -> bool {
932 matches!(
933 self.destination,
934 Destination::Document |
935 Destination::Embed |
936 Destination::Frame |
937 Destination::IFrame |
938 Destination::Object
939 )
940 }
941
942 pub fn is_subresource_request(&self) -> bool {
944 matches!(
945 self.destination,
946 Destination::Audio |
947 Destination::Font |
948 Destination::Image |
949 Destination::Manifest |
950 Destination::Script |
951 Destination::Style |
952 Destination::Track |
953 Destination::Video |
954 Destination::Xslt |
955 Destination::None
956 )
957 }
958
959 pub fn timing_type(&self) -> ResourceTimingType {
960 if self.is_navigation_request() {
961 ResourceTimingType::Navigation
962 } else {
963 ResourceTimingType::Resource
964 }
965 }
966
967 pub fn populate_request_from_client(&mut self) {
969 if self.traversable_for_user_prompts == TraversableForUserPrompts::Client {
971 self.traversable_for_user_prompts = TraversableForUserPrompts::NoTraversable;
973 if self.client.is_some() {
975 self.traversable_for_user_prompts =
980 TraversableForUserPrompts::TraversableNavigable(Default::default());
981 }
982 }
983 if self.origin == Origin::Client {
985 let Some(client) = self.client.as_ref() else {
986 unreachable!();
988 };
989 self.origin = client.origin.clone();
991 }
992 if matches!(self.policy_container, RequestPolicyContainer::Client) {
994 if let Some(client) = self.client.as_ref() {
997 self.policy_container = client.policy_container.clone();
998 } else {
999 self.policy_container =
1001 RequestPolicyContainer::PolicyContainer(PolicyContainer::default());
1002 }
1003 }
1004 }
1005
1006 pub fn keep_alive_body_length(&self) -> u64 {
1008 assert!(self.keep_alive);
1009 self.body.body_length() as u64
1010 }
1011
1012 pub fn total_request_length(&self) -> usize {
1014 let mut total_request_length = self.url()[..Position::AfterQuery].len();
1016 total_request_length += self
1018 .referrer
1019 .to_url()
1020 .map(|url| url.as_str().len())
1021 .unwrap_or_default();
1022 total_request_length += self.headers.total_size();
1025 total_request_length += self.body.body_length();
1027 total_request_length
1029 }
1030
1031 pub fn redirect_taint_for_request(&self) -> RedirectTaint {
1033 let Origin::Origin(request_origin) = &self.origin else {
1035 unreachable!("origin cannot be \"client\" at this point in time");
1036 };
1037
1038 let mut last_url = None;
1040
1041 let mut taint = RedirectTaint::SameOrigin;
1043
1044 for url in &self.url_list {
1046 let Some(last_url) = &mut last_url else {
1048 last_url = Some(url);
1049 continue;
1050 };
1051
1052 if !is_same_site(&url.origin(), &last_url.origin()) &&
1055 !is_same_site(request_origin, &last_url.origin())
1056 {
1057 return RedirectTaint::CrossSite;
1058 }
1059
1060 if url.origin() != last_url.origin() && *request_origin != last_url.origin() {
1063 taint = RedirectTaint::SameSite;
1064 }
1065
1066 *last_url = url;
1068 }
1069
1070 taint
1072 }
1073}
1074
1075impl Referrer {
1076 pub fn to_url(&self) -> Option<&ServoUrl> {
1077 match *self {
1078 Referrer::NoReferrer => None,
1079 Referrer::Client(ref url) => Some(url),
1080 Referrer::ReferrerUrl(ref url) => Some(url),
1081 }
1082 }
1083}
1084
1085fn is_cors_unsafe_request_header_byte(value: &u8) -> bool {
1089 matches!(value,
1090 0x00..=0x08 |
1091 0x10..=0x19 |
1092 0x22 |
1093 0x28 |
1094 0x29 |
1095 0x3A |
1096 0x3C |
1097 0x3E |
1098 0x3F |
1099 0x40 |
1100 0x5B |
1101 0x5C |
1102 0x5D |
1103 0x7B |
1104 0x7D |
1105 0x7F
1106 )
1107}
1108
1109fn is_cors_safelisted_request_accept(value: &[u8]) -> bool {
1112 !(value.iter().any(is_cors_unsafe_request_header_byte))
1113}
1114
1115fn is_cors_safelisted_language(value: &[u8]) -> bool {
1118 value.iter().all(|&x| {
1119 matches!(x,
1120 0x30..=0x39 |
1121 0x41..=0x5A |
1122 0x61..=0x7A |
1123 0x20 |
1124 0x2A |
1125 0x2C |
1126 0x2D |
1127 0x2E |
1128 0x3B |
1129 0x3D
1130 )
1131 })
1132}
1133
1134pub fn is_cors_safelisted_request_content_type(value: &[u8]) -> bool {
1137 if value.iter().any(is_cors_unsafe_request_header_byte) {
1139 return false;
1140 }
1141 let value_string = if let Ok(s) = std::str::from_utf8(value) {
1143 s
1144 } else {
1145 return false;
1146 };
1147 let value_mime_result: Result<Mime, _> = value_string.parse();
1148 match value_mime_result {
1149 Err(_) => false, Ok(value_mime) => match (value_mime.type_(), value_mime.subtype()) {
1151 (mime::APPLICATION, mime::WWW_FORM_URLENCODED) |
1152 (mime::MULTIPART, mime::FORM_DATA) |
1153 (mime::TEXT, mime::PLAIN) => true,
1154 _ => false, },
1156 }
1157}
1158
1159pub fn is_cors_safelisted_request_header<N: AsRef<str>, V: AsRef<[u8]>>(
1163 name: &N,
1164 value: &V,
1165) -> bool {
1166 let name: &str = name.as_ref();
1167 let value: &[u8] = value.as_ref();
1168 if value.len() > 128 {
1169 return false;
1170 }
1171 match name {
1172 "accept" => is_cors_safelisted_request_accept(value),
1173 "accept-language" | "content-language" => is_cors_safelisted_language(value),
1174 "content-type" => is_cors_safelisted_request_content_type(value),
1175 "range" => is_cors_safelisted_request_range(value),
1176 _ => false,
1177 }
1178}
1179
1180pub fn is_cors_safelisted_request_range(value: &[u8]) -> bool {
1181 if let Ok(value_str) = std::str::from_utf8(value) {
1182 return validate_range_header(value_str);
1183 }
1184 false
1185}
1186
1187fn validate_range_header(value: &str) -> bool {
1188 let trimmed = value.trim();
1189 if !trimmed.starts_with("bytes=") {
1190 return false;
1191 }
1192
1193 if let Some(range) = trimmed.strip_prefix("bytes=") {
1194 let mut parts = range.split('-');
1195 let start = parts.next();
1196 let end = parts.next();
1197
1198 if let Some(start) = start {
1199 if let Ok(start_num) = start.parse::<u64>() {
1200 return match end {
1201 Some(e) if !e.is_empty() => {
1202 e.parse::<u64>().is_ok_and(|end_num| start_num <= end_num)
1203 },
1204 _ => true,
1205 };
1206 }
1207 }
1208 }
1209 false
1210}
1211
1212pub fn is_cors_safelisted_method(method: &Method) -> bool {
1214 matches!(*method, Method::GET | Method::HEAD | Method::POST)
1215}
1216
1217pub fn is_cors_non_wildcard_request_header_name(name: &HeaderName) -> bool {
1219 name == AUTHORIZATION
1220}
1221
1222pub fn get_cors_unsafe_header_names(headers: &HeaderMap) -> Vec<HeaderName> {
1224 let mut unsafe_names: Vec<&HeaderName> = vec![];
1226 let mut potentillay_unsafe_names: Vec<&HeaderName> = vec![];
1228 let mut safelist_value_size = 0;
1230
1231 for (name, value) in headers.iter() {
1233 if !is_cors_safelisted_request_header(&name, &value) {
1234 unsafe_names.push(name);
1235 } else {
1236 potentillay_unsafe_names.push(name);
1237 safelist_value_size += value.as_ref().len();
1238 }
1239 }
1240
1241 if safelist_value_size > 1024 {
1243 unsafe_names.extend_from_slice(&potentillay_unsafe_names);
1244 }
1245
1246 convert_header_names_to_sorted_lowercase_set(unsafe_names)
1248}
1249
1250pub fn convert_header_names_to_sorted_lowercase_set(
1252 header_names: Vec<&HeaderName>,
1253) -> Vec<HeaderName> {
1254 let mut ordered_set = header_names.to_vec();
1257 ordered_set.sort_by(|a, b| a.as_str().partial_cmp(b.as_str()).unwrap());
1258 ordered_set.dedup();
1259 ordered_set.into_iter().cloned().collect()
1260}
1261
1262pub fn create_request_body_with_content(content: &str) -> RequestBody {
1263 let content_bytes = GenericSharedMemory::from_bytes(content.as_bytes());
1264 let content_len = content_bytes.len();
1265
1266 let (chunk_request_sender, chunk_request_receiver) = ipc::channel().unwrap();
1267 ROUTER.add_typed_route(
1268 chunk_request_receiver,
1269 Box::new(move |message| {
1270 let request = message.unwrap();
1271 if let BodyChunkRequest::Connect(sender) = request {
1272 let _ = sender.send(BodyChunkResponse::Chunk(content_bytes.clone()));
1273 let _ = sender.send(BodyChunkResponse::Done);
1274 }
1275 }),
1276 );
1277
1278 RequestBody::new(chunk_request_sender, BodySource::Object, Some(content_len))
1279}