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::blob_url_store::UrlWithBlobClaim;
26use crate::policy_container::{PolicyContainer, RequestPolicyContainer};
27use crate::pub_domains::is_same_site;
28use crate::response::{HttpsState, RedirectTaint, Response};
29use crate::{ReferrerPolicy, ResourceTimingType};
30
31#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
32pub struct RequestId(pub Uuid);
34
35impl Default for RequestId {
36 fn default() -> Self {
37 Self(Uuid::new_v4())
38 }
39}
40
41#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
43pub enum Initiator {
44 None,
45 Download,
46 ImageSet,
47 Manifest,
48 XSLT,
49 Prefetch,
50 Link,
51}
52
53pub use csp::Destination;
55
56#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
58pub enum Origin {
59 Client,
60 Origin(ImmutableOrigin),
61}
62
63impl Origin {
64 pub fn is_opaque(&self) -> bool {
65 matches!(self, Origin::Origin(ImmutableOrigin::Opaque(_)))
66 }
67}
68
69#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
71pub enum Referrer {
72 NoReferrer,
73 Client(ServoUrl),
79 ReferrerUrl(ServoUrl),
80}
81
82#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
84pub enum RequestMode {
85 Navigate,
86 SameOrigin,
87 NoCors,
88 CorsMode,
89 WebSocket {
90 protocols: Vec<String>,
91 original_url: ServoUrl,
92 },
93}
94
95#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
97pub enum CredentialsMode {
98 Omit,
99 CredentialsSameOrigin,
100 Include,
101}
102
103#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
105pub enum CacheMode {
106 Default,
107 NoStore,
108 Reload,
109 NoCache,
110 ForceCache,
111 OnlyIfCached,
112}
113
114#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
116pub enum ServiceWorkersMode {
117 All,
118 None,
119}
120
121#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
123pub enum RedirectMode {
124 Follow,
125 Error,
126 Manual,
127}
128
129#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
131pub enum ResponseTainting {
132 Basic,
133 CorsTainting,
134 Opaque,
135}
136
137#[derive(Clone, Debug, Eq, Hash, Deserialize, MallocSizeOf, Serialize, PartialEq)]
139pub struct PreloadKey {
140 pub url: ServoUrl,
142 pub destination: Destination,
144 pub mode: RequestMode,
146 pub credentials_mode: CredentialsMode,
148}
149
150impl PreloadKey {
151 pub fn new(request: &RequestBuilder) -> Self {
152 Self {
153 url: request.url.url(),
154 destination: request.destination,
155 mode: request.mode.clone(),
156 credentials_mode: request.credentials_mode,
157 }
158 }
159}
160
161#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize, Hash, MallocSizeOf)]
162pub struct PreloadId(pub Uuid);
163
164impl Default for PreloadId {
165 fn default() -> Self {
166 Self(Uuid::new_v4())
167 }
168}
169
170#[derive(Debug, MallocSizeOf)]
172pub struct PreloadEntry {
173 pub integrity_metadata: String,
175 pub response: Option<Response>,
177 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 #[conditional_malloc_size_of]
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 pub method: Method,
431
432 pub url: UrlWithBlobClaim,
434
435 #[serde(
437 deserialize_with = "::hyper_serde::deserialize",
438 serialize_with = "::hyper_serde::serialize"
439 )]
440 pub headers: HeaderMap,
441
442 pub unsafe_request: bool,
444
445 pub body: Option<RequestBody>,
447
448 pub service_workers_mode: ServiceWorkersMode,
450 pub client: Option<RequestClient>,
451 pub destination: Destination,
453 pub synchronous: bool,
454 pub mode: RequestMode,
455
456 pub cache_mode: CacheMode,
458
459 pub use_cors_preflight: bool,
461
462 pub keep_alive: bool,
464
465 pub credentials_mode: CredentialsMode,
467 pub use_url_credentials: bool,
468
469 pub origin: Origin,
471
472 pub policy_container: RequestPolicyContainer,
474 pub insecure_requests_policy: InsecureRequestsPolicy,
475 pub has_trustworthy_ancestor_origin: bool,
476
477 pub referrer: Referrer,
479
480 pub referrer_policy: ReferrerPolicy,
482 pub pipeline_id: Option<PipelineId>,
483 pub target_webview_id: Option<WebViewId>,
484
485 pub redirect_mode: RedirectMode,
487
488 pub integrity_metadata: String,
490
491 pub cryptographic_nonce_metadata: String,
493
494 pub url_list: Vec<ServoUrl>,
496
497 pub parser_metadata: ParserMetadata,
499
500 pub initiator: Initiator,
502 pub https_state: HttpsState,
503 pub response_tainting: ResponseTainting,
504 pub crash: Option<String>,
506}
507
508impl RequestBuilder {
509 pub fn new(
510 webview_id: Option<WebViewId>,
511 url: UrlWithBlobClaim,
512 referrer: Referrer,
513 ) -> RequestBuilder {
514 RequestBuilder {
515 id: RequestId::default(),
516 preload_id: None,
517 method: Method::GET,
518 url,
519 headers: HeaderMap::new(),
520 unsafe_request: false,
521 body: None,
522 service_workers_mode: ServiceWorkersMode::All,
523 destination: Destination::None,
524 synchronous: false,
525 mode: RequestMode::NoCors,
526 cache_mode: CacheMode::Default,
527 use_cors_preflight: false,
528 keep_alive: false,
529 credentials_mode: CredentialsMode::CredentialsSameOrigin,
530 use_url_credentials: false,
531 origin: Origin::Client,
532 client: None,
533 policy_container: RequestPolicyContainer::default(),
534 insecure_requests_policy: InsecureRequestsPolicy::DoNotUpgrade,
535 has_trustworthy_ancestor_origin: false,
536 referrer,
537 referrer_policy: ReferrerPolicy::EmptyString,
538 pipeline_id: None,
539 target_webview_id: webview_id,
540 redirect_mode: RedirectMode::Follow,
541 integrity_metadata: "".to_owned(),
542 cryptographic_nonce_metadata: "".to_owned(),
543 url_list: vec![],
544 parser_metadata: ParserMetadata::Default,
545 initiator: Initiator::None,
546 https_state: HttpsState::None,
547 response_tainting: ResponseTainting::Basic,
548 crash: None,
549 }
550 }
551
552 pub fn preload_id(mut self, preload_id: PreloadId) -> RequestBuilder {
553 self.preload_id = Some(preload_id);
554 self
555 }
556
557 pub fn initiator(mut self, initiator: Initiator) -> RequestBuilder {
559 self.initiator = initiator;
560 self
561 }
562
563 pub fn method(mut self, method: Method) -> RequestBuilder {
565 self.method = method;
566 self
567 }
568
569 pub fn headers(mut self, headers: HeaderMap) -> RequestBuilder {
571 self.headers = headers;
572 self
573 }
574
575 pub fn unsafe_request(mut self, unsafe_request: bool) -> RequestBuilder {
577 self.unsafe_request = unsafe_request;
578 self
579 }
580
581 pub fn body(mut self, body: Option<RequestBody>) -> RequestBuilder {
583 self.body = body;
584 self
585 }
586
587 pub fn destination(mut self, destination: Destination) -> RequestBuilder {
589 self.destination = destination;
590 self
591 }
592
593 pub fn synchronous(mut self, synchronous: bool) -> RequestBuilder {
594 self.synchronous = synchronous;
595 self
596 }
597
598 pub fn mode(mut self, mode: RequestMode) -> RequestBuilder {
599 self.mode = mode;
600 self
601 }
602
603 pub fn use_cors_preflight(mut self, use_cors_preflight: bool) -> RequestBuilder {
605 self.use_cors_preflight = use_cors_preflight;
606 self
607 }
608
609 pub fn keep_alive(mut self, keep_alive: bool) -> RequestBuilder {
611 self.keep_alive = keep_alive;
612 self
613 }
614
615 pub fn credentials_mode(mut self, credentials_mode: CredentialsMode) -> RequestBuilder {
617 self.credentials_mode = credentials_mode;
618 self
619 }
620
621 pub fn use_url_credentials(mut self, use_url_credentials: bool) -> RequestBuilder {
622 self.use_url_credentials = use_url_credentials;
623 self
624 }
625
626 pub fn origin(mut self, origin: ImmutableOrigin) -> RequestBuilder {
628 self.origin = Origin::Origin(origin);
629 self
630 }
631
632 pub fn referrer_policy(mut self, referrer_policy: ReferrerPolicy) -> RequestBuilder {
634 self.referrer_policy = referrer_policy;
635 self
636 }
637
638 pub fn url_list(mut self, url_list: Vec<ServoUrl>) -> RequestBuilder {
640 self.url_list = url_list;
641 self
642 }
643
644 pub fn pipeline_id(mut self, pipeline_id: Option<PipelineId>) -> RequestBuilder {
645 self.pipeline_id = pipeline_id;
646 self
647 }
648
649 pub fn redirect_mode(mut self, redirect_mode: RedirectMode) -> RequestBuilder {
651 self.redirect_mode = redirect_mode;
652 self
653 }
654
655 pub fn integrity_metadata(mut self, integrity_metadata: String) -> RequestBuilder {
657 self.integrity_metadata = integrity_metadata;
658 self
659 }
660
661 pub fn cryptographic_nonce_metadata(mut self, nonce_metadata: String) -> RequestBuilder {
663 self.cryptographic_nonce_metadata = nonce_metadata;
664 self
665 }
666
667 pub fn parser_metadata(mut self, parser_metadata: ParserMetadata) -> RequestBuilder {
669 self.parser_metadata = parser_metadata;
670 self
671 }
672
673 pub fn https_state(mut self, https_state: HttpsState) -> RequestBuilder {
674 self.https_state = https_state;
675 self
676 }
677
678 pub fn response_tainting(mut self, response_tainting: ResponseTainting) -> RequestBuilder {
679 self.response_tainting = response_tainting;
680 self
681 }
682
683 pub fn crash(mut self, crash: Option<String>) -> Self {
684 self.crash = crash;
685 self
686 }
687
688 pub fn policy_container(mut self, policy_container: PolicyContainer) -> RequestBuilder {
690 self.policy_container = RequestPolicyContainer::PolicyContainer(policy_container);
691 self
692 }
693
694 pub fn client(mut self, client: RequestClient) -> RequestBuilder {
696 self.client = Some(client);
697 self
698 }
699
700 pub fn insecure_requests_policy(
701 mut self,
702 insecure_requests_policy: InsecureRequestsPolicy,
703 ) -> RequestBuilder {
704 self.insecure_requests_policy = insecure_requests_policy;
705 self
706 }
707
708 pub fn has_trustworthy_ancestor_origin(
709 mut self,
710 has_trustworthy_ancestor_origin: bool,
711 ) -> RequestBuilder {
712 self.has_trustworthy_ancestor_origin = has_trustworthy_ancestor_origin;
713 self
714 }
715
716 pub fn service_workers_mode(
718 mut self,
719 service_workers_mode: ServiceWorkersMode,
720 ) -> RequestBuilder {
721 self.service_workers_mode = service_workers_mode;
722 self
723 }
724
725 pub fn cache_mode(mut self, cache_mode: CacheMode) -> RequestBuilder {
727 self.cache_mode = cache_mode;
728 self
729 }
730
731 pub fn build(self) -> Request {
732 let mut request = Request::new(
733 self.id,
734 self.url.clone(),
735 Some(self.origin),
736 self.referrer,
737 self.pipeline_id,
738 self.target_webview_id,
739 self.https_state,
740 );
741 request.preload_id = self.preload_id;
742 request.initiator = self.initiator;
743 request.method = self.method;
744 request.headers = self.headers;
745 request.unsafe_request = self.unsafe_request;
746 request.body = self.body;
747 request.service_workers_mode = self.service_workers_mode;
748 request.destination = self.destination;
749 request.synchronous = self.synchronous;
750 request.mode = self.mode;
751 request.use_cors_preflight = self.use_cors_preflight;
752 request.keep_alive = self.keep_alive;
753 request.credentials_mode = self.credentials_mode;
754 request.use_url_credentials = self.use_url_credentials;
755 request.cache_mode = self.cache_mode;
756 request.referrer_policy = self.referrer_policy;
757 request.redirect_mode = self.redirect_mode;
758 let mut url_list: Vec<_> = self
759 .url_list
760 .into_iter()
761 .map(UrlWithBlobClaim::from_url_without_having_claimed_blob)
762 .collect();
763 if url_list.is_empty() {
764 url_list.push(self.url);
765 }
766 request.redirect_count = url_list.len() as u32 - 1;
767 request.url_list = url_list;
768 request.integrity_metadata = self.integrity_metadata;
769 request.cryptographic_nonce_metadata = self.cryptographic_nonce_metadata;
770 request.parser_metadata = self.parser_metadata;
771 request.response_tainting = self.response_tainting;
772 request.crash = self.crash;
773 request.client = self.client;
774 request.policy_container = self.policy_container;
775 request.insecure_requests_policy = self.insecure_requests_policy;
776 request.has_trustworthy_ancestor_origin = self.has_trustworthy_ancestor_origin;
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 client: Option<RequestClient>,
808 pub traversable_for_user_prompts: TraversableForUserPrompts,
810 pub target_webview_id: Option<WebViewId>,
811 pub keep_alive: bool,
813 pub service_workers_mode: ServiceWorkersMode,
815 pub initiator: Initiator,
817 pub destination: Destination,
819 pub origin: Origin,
822 pub referrer: Referrer,
824 pub referrer_policy: ReferrerPolicy,
826 pub pipeline_id: Option<PipelineId>,
827 pub synchronous: bool,
829 pub mode: RequestMode,
831 pub use_cors_preflight: bool,
833 pub credentials_mode: CredentialsMode,
835 pub use_url_credentials: bool,
837 pub cache_mode: CacheMode,
839 pub redirect_mode: RedirectMode,
841 pub integrity_metadata: String,
843 pub cryptographic_nonce_metadata: String,
845 pub url_list: Vec<UrlWithBlobClaim>,
847 pub redirect_count: u32,
849 pub response_tainting: ResponseTainting,
851 pub parser_metadata: ParserMetadata,
853 pub policy_container: RequestPolicyContainer,
855 pub insecure_requests_policy: InsecureRequestsPolicy,
857 pub has_trustworthy_ancestor_origin: bool,
858 pub https_state: HttpsState,
859 pub crash: Option<String>,
861}
862
863impl Request {
864 pub fn new(
865 id: RequestId,
866 url: UrlWithBlobClaim,
867 origin: Option<Origin>,
868 referrer: Referrer,
869 pipeline_id: Option<PipelineId>,
870 webview_id: Option<WebViewId>,
871 https_state: HttpsState,
872 ) -> Request {
873 Request {
874 id,
875 preload_id: None,
876 method: Method::GET,
877 local_urls_only: false,
878 headers: HeaderMap::new(),
879 unsafe_request: false,
880 body: None,
881 client: None,
882 traversable_for_user_prompts: TraversableForUserPrompts::Client,
883 keep_alive: false,
884 service_workers_mode: ServiceWorkersMode::All,
885 initiator: Initiator::None,
886 destination: Destination::None,
887 origin: origin.unwrap_or(Origin::Client),
888 referrer,
889 referrer_policy: ReferrerPolicy::EmptyString,
890 pipeline_id,
891 target_webview_id: webview_id,
892 synchronous: false,
893 mode: RequestMode::NoCors,
894 use_cors_preflight: false,
895 credentials_mode: CredentialsMode::CredentialsSameOrigin,
896 use_url_credentials: false,
897 cache_mode: CacheMode::Default,
898 redirect_mode: RedirectMode::Follow,
899 integrity_metadata: String::new(),
900 cryptographic_nonce_metadata: String::new(),
901 url_list: vec![url],
902 parser_metadata: ParserMetadata::Default,
903 redirect_count: 0,
904 response_tainting: ResponseTainting::Basic,
905 policy_container: RequestPolicyContainer::Client,
906 insecure_requests_policy: InsecureRequestsPolicy::DoNotUpgrade,
907 has_trustworthy_ancestor_origin: false,
908 https_state,
909 crash: None,
910 }
911 }
912
913 pub fn url(&self) -> ServoUrl {
915 self.url_list.first().unwrap().url()
916 }
917
918 pub fn url_with_blob_claim(&self) -> UrlWithBlobClaim {
919 self.url_list.first().unwrap().clone()
920 }
921
922 pub fn original_url(&self) -> ServoUrl {
923 match self.mode {
924 RequestMode::WebSocket {
925 protocols: _,
926 ref original_url,
927 } => original_url.clone(),
928 _ => self.url(),
929 }
930 }
931
932 pub fn current_url(&self) -> ServoUrl {
934 self.current_url_with_blob_claim().url()
935 }
936
937 pub fn current_url_with_blob_claim(&self) -> UrlWithBlobClaim {
939 self.url_list.last().unwrap().clone()
940 }
941
942 pub fn current_url_mut(&mut self) -> &mut ServoUrl {
944 self.url_list.last_mut().unwrap()
945 }
946
947 pub fn is_navigation_request(&self) -> bool {
949 matches!(
950 self.destination,
951 Destination::Document |
952 Destination::Embed |
953 Destination::Frame |
954 Destination::IFrame |
955 Destination::Object
956 )
957 }
958
959 pub fn is_subresource_request(&self) -> bool {
961 matches!(
962 self.destination,
963 Destination::Audio |
964 Destination::Font |
965 Destination::Image |
966 Destination::Manifest |
967 Destination::Script |
968 Destination::Style |
969 Destination::Track |
970 Destination::Video |
971 Destination::Xslt |
972 Destination::None
973 )
974 }
975
976 pub fn timing_type(&self) -> ResourceTimingType {
977 if self.is_navigation_request() {
978 ResourceTimingType::Navigation
979 } else {
980 ResourceTimingType::Resource
981 }
982 }
983
984 pub fn populate_request_from_client(&mut self) {
986 if self.traversable_for_user_prompts == TraversableForUserPrompts::Client {
988 self.traversable_for_user_prompts = TraversableForUserPrompts::NoTraversable;
990 if self.client.is_some() {
992 self.traversable_for_user_prompts =
997 TraversableForUserPrompts::TraversableNavigable(Default::default());
998 }
999 }
1000 if self.origin == Origin::Client {
1002 let Some(client) = self.client.as_ref() else {
1003 unreachable!();
1005 };
1006 self.origin = client.origin.clone();
1008 }
1009 if matches!(self.policy_container, RequestPolicyContainer::Client) {
1011 if let Some(client) = self.client.as_ref() {
1014 self.policy_container = client.policy_container.clone();
1015 } else {
1016 self.policy_container =
1018 RequestPolicyContainer::PolicyContainer(PolicyContainer::default());
1019 }
1020 }
1021 }
1022
1023 pub fn keep_alive_body_length(&self) -> u64 {
1025 assert!(self.keep_alive);
1026 self.body.body_length() as u64
1027 }
1028
1029 pub fn total_request_length(&self) -> usize {
1031 let mut total_request_length = self.url()[..Position::AfterQuery].len();
1033 total_request_length += self
1035 .referrer
1036 .to_url()
1037 .map(|url| url.as_str().len())
1038 .unwrap_or_default();
1039 total_request_length += self.headers.total_size();
1042 total_request_length += self.body.body_length();
1044 total_request_length
1046 }
1047
1048 pub fn redirect_taint_for_request(&self) -> RedirectTaint {
1050 let Origin::Origin(request_origin) = &self.origin else {
1052 unreachable!("origin cannot be \"client\" at this point in time");
1053 };
1054
1055 let mut last_url = None;
1057
1058 let mut taint = RedirectTaint::SameOrigin;
1060
1061 for url in &self.url_list {
1063 let Some(last_url) = &mut last_url else {
1065 last_url = Some(url);
1066 continue;
1067 };
1068
1069 if !is_same_site(&url.origin(), &last_url.origin()) &&
1072 !is_same_site(request_origin, &last_url.origin())
1073 {
1074 return RedirectTaint::CrossSite;
1075 }
1076
1077 if url.origin() != last_url.origin() && *request_origin != last_url.origin() {
1080 taint = RedirectTaint::SameSite;
1081 }
1082
1083 *last_url = url;
1085 }
1086
1087 taint
1089 }
1090}
1091
1092impl Referrer {
1093 pub fn to_url(&self) -> Option<&ServoUrl> {
1094 match *self {
1095 Referrer::NoReferrer => None,
1096 Referrer::Client(ref url) => Some(url),
1097 Referrer::ReferrerUrl(ref url) => Some(url),
1098 }
1099 }
1100}
1101
1102fn is_cors_unsafe_request_header_byte(value: &u8) -> bool {
1106 matches!(value,
1107 0x00..=0x08 |
1108 0x10..=0x19 |
1109 0x22 |
1110 0x28 |
1111 0x29 |
1112 0x3A |
1113 0x3C |
1114 0x3E |
1115 0x3F |
1116 0x40 |
1117 0x5B |
1118 0x5C |
1119 0x5D |
1120 0x7B |
1121 0x7D |
1122 0x7F
1123 )
1124}
1125
1126fn is_cors_safelisted_request_accept(value: &[u8]) -> bool {
1129 !(value.iter().any(is_cors_unsafe_request_header_byte))
1130}
1131
1132fn is_cors_safelisted_language(value: &[u8]) -> bool {
1135 value.iter().all(|&x| {
1136 matches!(x,
1137 0x30..=0x39 |
1138 0x41..=0x5A |
1139 0x61..=0x7A |
1140 0x20 |
1141 0x2A |
1142 0x2C |
1143 0x2D |
1144 0x2E |
1145 0x3B |
1146 0x3D
1147 )
1148 })
1149}
1150
1151pub fn is_cors_safelisted_request_content_type(value: &[u8]) -> bool {
1154 if value.iter().any(is_cors_unsafe_request_header_byte) {
1156 return false;
1157 }
1158 let value_string = if let Ok(s) = std::str::from_utf8(value) {
1160 s
1161 } else {
1162 return false;
1163 };
1164 let value_mime_result: Result<Mime, _> = value_string.parse();
1165 match value_mime_result {
1166 Err(_) => false, Ok(value_mime) => match (value_mime.type_(), value_mime.subtype()) {
1168 (mime::APPLICATION, mime::WWW_FORM_URLENCODED) |
1169 (mime::MULTIPART, mime::FORM_DATA) |
1170 (mime::TEXT, mime::PLAIN) => true,
1171 _ => false, },
1173 }
1174}
1175
1176pub fn is_cors_safelisted_request_header<N: AsRef<str>, V: AsRef<[u8]>>(
1180 name: &N,
1181 value: &V,
1182) -> bool {
1183 let name: &str = name.as_ref();
1184 let value: &[u8] = value.as_ref();
1185 if value.len() > 128 {
1186 return false;
1187 }
1188 match name {
1189 "accept" => is_cors_safelisted_request_accept(value),
1190 "accept-language" | "content-language" => is_cors_safelisted_language(value),
1191 "content-type" => is_cors_safelisted_request_content_type(value),
1192 "range" => is_cors_safelisted_request_range(value),
1193 _ => false,
1194 }
1195}
1196
1197pub fn is_cors_safelisted_request_range(value: &[u8]) -> bool {
1198 if let Ok(value_str) = std::str::from_utf8(value) {
1199 return validate_range_header(value_str);
1200 }
1201 false
1202}
1203
1204fn validate_range_header(value: &str) -> bool {
1205 let trimmed = value.trim();
1206 if !trimmed.starts_with("bytes=") {
1207 return false;
1208 }
1209
1210 if let Some(range) = trimmed.strip_prefix("bytes=") {
1211 let mut parts = range.split('-');
1212 let start = parts.next();
1213 let end = parts.next();
1214
1215 if let Some(start) = start {
1216 if let Ok(start_num) = start.parse::<u64>() {
1217 return match end {
1218 Some(e) if !e.is_empty() => {
1219 e.parse::<u64>().is_ok_and(|end_num| start_num <= end_num)
1220 },
1221 _ => true,
1222 };
1223 }
1224 }
1225 }
1226 false
1227}
1228
1229pub fn is_cors_safelisted_method(method: &Method) -> bool {
1231 matches!(*method, Method::GET | Method::HEAD | Method::POST)
1232}
1233
1234pub fn is_cors_non_wildcard_request_header_name(name: &HeaderName) -> bool {
1236 name == AUTHORIZATION
1237}
1238
1239pub fn get_cors_unsafe_header_names(headers: &HeaderMap) -> Vec<HeaderName> {
1241 let mut unsafe_names: Vec<&HeaderName> = vec![];
1243 let mut potentillay_unsafe_names: Vec<&HeaderName> = vec![];
1245 let mut safelist_value_size = 0;
1247
1248 for (name, value) in headers.iter() {
1250 if !is_cors_safelisted_request_header(&name, &value) {
1251 unsafe_names.push(name);
1252 } else {
1253 potentillay_unsafe_names.push(name);
1254 safelist_value_size += value.as_ref().len();
1255 }
1256 }
1257
1258 if safelist_value_size > 1024 {
1260 unsafe_names.extend_from_slice(&potentillay_unsafe_names);
1261 }
1262
1263 convert_header_names_to_sorted_lowercase_set(unsafe_names)
1265}
1266
1267pub fn convert_header_names_to_sorted_lowercase_set(
1269 header_names: Vec<&HeaderName>,
1270) -> Vec<HeaderName> {
1271 let mut ordered_set = header_names.to_vec();
1274 ordered_set.sort_by(|a, b| a.as_str().partial_cmp(b.as_str()).unwrap());
1275 ordered_set.dedup();
1276 ordered_set.into_iter().cloned().collect()
1277}
1278
1279pub fn create_request_body_with_content(content: &str) -> RequestBody {
1280 let content_bytes = GenericSharedMemory::from_bytes(content.as_bytes());
1281 let content_len = content_bytes.len();
1282
1283 let (chunk_request_sender, chunk_request_receiver) = ipc::channel().unwrap();
1284 ROUTER.add_typed_route(
1285 chunk_request_receiver,
1286 Box::new(move |message| {
1287 let request = message.unwrap();
1288 if let BodyChunkRequest::Connect(sender) = request {
1289 let _ = sender.send(BodyChunkResponse::Chunk(content_bytes.clone()));
1290 let _ = sender.send(BodyChunkResponse::Done);
1291 }
1292 }),
1293 );
1294
1295 RequestBody::new(chunk_request_sender, BodySource::Object, Some(content_len))
1296}