1use std::sync::{Arc, Mutex};
6
7use base::id::{PipelineId, WebViewId};
8use content_security_policy::{self as csp};
9use http::header::{AUTHORIZATION, HeaderName};
10use http::{HeaderMap, Method};
11use indexmap::IndexMap;
12use ipc_channel::ipc::{self, IpcReceiver, IpcSender, IpcSharedMemory};
13use ipc_channel::router::ROUTER;
14use malloc_size_of_derive::MallocSizeOf;
15use mime::Mime;
16use serde::{Deserialize, Serialize};
17use servo_url::{ImmutableOrigin, ServoUrl};
18use uuid::Uuid;
19
20use crate::policy_container::{PolicyContainer, RequestPolicyContainer};
21use crate::response::{HttpsState, Response};
22use crate::{ReferrerPolicy, ResourceTimingType};
23
24#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
25pub struct RequestId(pub Uuid);
27
28impl Default for RequestId {
29 fn default() -> Self {
30 Self(Uuid::new_v4())
31 }
32}
33
34#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
36pub enum Initiator {
37 None,
38 Download,
39 ImageSet,
40 Manifest,
41 XSLT,
42 Prefetch,
43 Link,
44}
45
46pub use csp::Destination;
48
49#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
51pub enum Origin {
52 Client,
53 Origin(ImmutableOrigin),
54}
55
56impl Origin {
57 pub fn is_opaque(&self) -> bool {
58 matches!(self, Origin::Origin(ImmutableOrigin::Opaque(_)))
59 }
60}
61
62#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
64pub enum Referrer {
65 NoReferrer,
66 Client(ServoUrl),
72 ReferrerUrl(ServoUrl),
73}
74
75#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
77pub enum RequestMode {
78 Navigate,
79 SameOrigin,
80 NoCors,
81 CorsMode,
82 WebSocket {
83 protocols: Vec<String>,
84 original_url: ServoUrl,
85 },
86}
87
88#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
90pub enum CredentialsMode {
91 Omit,
92 CredentialsSameOrigin,
93 Include,
94}
95
96#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
98pub enum CacheMode {
99 Default,
100 NoStore,
101 Reload,
102 NoCache,
103 ForceCache,
104 OnlyIfCached,
105}
106
107#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
109pub enum ServiceWorkersMode {
110 All,
111 None,
112}
113
114#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
116pub enum RedirectMode {
117 Follow,
118 Error,
119 Manual,
120}
121
122#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
124pub enum ResponseTainting {
125 Basic,
126 CorsTainting,
127 Opaque,
128}
129
130#[derive(Clone, Debug, Eq, Hash, Deserialize, MallocSizeOf, Serialize, PartialEq)]
132pub struct PreloadKey {
133 url: ServoUrl,
135 destination: Destination,
137 mode: RequestMode,
139 credentials_mode: CredentialsMode,
141}
142
143impl PreloadKey {
144 pub fn new(request: &RequestBuilder) -> Self {
145 Self {
146 url: request.url.clone(),
147 destination: request.destination,
148 mode: request.mode.clone(),
149 credentials_mode: request.credentials_mode,
150 }
151 }
152}
153
154#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
156pub struct PreloadEntry {
157 integrity_metadata: String,
159 #[serde(skip)]
161 response: Option<Response>,
162}
163
164impl PreloadEntry {
165 pub fn new(integrity_metadata: String) -> Self {
166 Self {
167 integrity_metadata,
168 response: None,
169 }
170 }
171
172 pub fn with_response(&self, response: Response) -> Self {
173 Self {
174 integrity_metadata: self.integrity_metadata.clone(),
175 response: Some(response),
176 }
177 }
178}
179
180pub type PreloadedResources = Arc<Mutex<IndexMap<PreloadKey, PreloadEntry>>>;
181
182#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
184pub struct RequestClient {
185 #[conditional_malloc_size_of]
187 #[serde(skip)] pub preloaded_resources: PreloadedResources,
189 pub policy_container: RequestPolicyContainer,
191 pub origin: Origin,
193}
194
195impl RequestClient {
196 pub fn consume_preloaded_resource(
198 &self,
199 request: &Request,
200 on_response_available: impl FnOnce(Response),
201 ) -> bool {
202 let key = PreloadKey {
205 url: request.url().clone(),
206 destination: request.destination,
207 mode: request.mode.clone(),
208 credentials_mode: request.credentials_mode,
209 };
210 let mut preloaded_resources_lock = self.preloaded_resources.lock();
212 let preloads = preloaded_resources_lock.as_mut().unwrap();
213 let Some(entry) = preloads.get_mut(&key) else {
215 return false;
217 };
218 let consumer_integrity_metadata =
220 csp::parse_subresource_integrity_metadata(&request.integrity_metadata);
221 let preload_integrity_metadata =
223 csp::parse_subresource_integrity_metadata(&entry.integrity_metadata);
224 if !(
226 consumer_integrity_metadata == csp::SubresourceIntegrityMetadata::NoMetadata
228 || consumer_integrity_metadata == preload_integrity_metadata
230 ) {
231 return false;
233 }
234 if let Some(response) = entry.response.as_ref() {
236 on_response_available(response.clone());
237 } else {
238 }
241 preloads.shift_remove(&key);
245 true
247 }
248}
249
250#[derive(Clone, Copy, Default, MallocSizeOf, PartialEq)]
252pub enum SystemVisibilityState {
253 #[default]
254 Hidden,
255 Visible,
256}
257
258#[derive(Clone, Copy, Default, MallocSizeOf, PartialEq)]
260pub struct TraversableNavigable {
261 current_session_history_step: u8,
263 running_nested_apply_history_step: bool,
267 system_visibility_state: SystemVisibilityState,
269 is_created_by_web_content: bool,
271}
272
273#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
275pub enum TraversableForUserPrompts {
276 NoTraversable,
277 Client,
278 TraversableNavigable(TraversableNavigable),
279}
280
281#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
283pub enum CorsSettings {
284 Anonymous,
285 UseCredentials,
286}
287
288impl CorsSettings {
289 pub fn from_enumerated_attribute(value: &str) -> CorsSettings {
291 match value.to_ascii_lowercase().as_str() {
292 "anonymous" => CorsSettings::Anonymous,
293 "use-credentials" => CorsSettings::UseCredentials,
294 _ => CorsSettings::Anonymous,
295 }
296 }
297}
298
299#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
301pub enum ParserMetadata {
302 Default,
303 ParserInserted,
304 NotParserInserted,
305}
306
307#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
309pub enum BodySource {
310 Null,
311 Object,
312}
313
314#[derive(Debug, Deserialize, Serialize)]
317pub enum BodyChunkResponse {
318 Chunk(IpcSharedMemory),
320 Done,
322 Error,
325}
326
327#[derive(Debug, Deserialize, Serialize)]
331pub enum BodyChunkRequest {
332 Connect(IpcSender<BodyChunkResponse>),
334 Extract(IpcReceiver<BodyChunkRequest>),
336 Chunk,
338 Done,
340 Error,
342}
343
344#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
346pub struct RequestBody {
347 #[ignore_malloc_size_of = "Channels are hard"]
349 chan: Arc<Mutex<IpcSender<BodyChunkRequest>>>,
350 source: BodySource,
352 total_bytes: Option<usize>,
354}
355
356impl RequestBody {
357 pub fn new(
358 chan: IpcSender<BodyChunkRequest>,
359 source: BodySource,
360 total_bytes: Option<usize>,
361 ) -> Self {
362 RequestBody {
363 chan: Arc::new(Mutex::new(chan)),
364 source,
365 total_bytes,
366 }
367 }
368
369 pub fn extract_source(&mut self) {
371 match self.source {
372 BodySource::Null => panic!("Null sources should never be re-directed."),
373 BodySource::Object => {
374 let (chan, port) = ipc::channel().unwrap();
375 let mut selfchan = self.chan.lock().unwrap();
376 let _ = selfchan.send(BodyChunkRequest::Extract(port));
377 *selfchan = chan;
378 },
379 }
380 }
381
382 pub fn take_stream(&self) -> Arc<Mutex<IpcSender<BodyChunkRequest>>> {
383 self.chan.clone()
384 }
385
386 pub fn source_is_null(&self) -> bool {
387 self.source == BodySource::Null
388 }
389
390 #[allow(clippy::len_without_is_empty)]
391 pub fn len(&self) -> Option<usize> {
392 self.total_bytes
393 }
394}
395
396#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
397pub enum InsecureRequestsPolicy {
398 DoNotUpgrade,
399 Upgrade,
400}
401
402#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
403pub struct RequestBuilder {
404 pub id: RequestId,
405
406 #[serde(
408 deserialize_with = "::hyper_serde::deserialize",
409 serialize_with = "::hyper_serde::serialize"
410 )]
411 #[ignore_malloc_size_of = "Defined in hyper"]
412 pub method: Method,
413
414 pub url: ServoUrl,
416
417 #[serde(
419 deserialize_with = "::hyper_serde::deserialize",
420 serialize_with = "::hyper_serde::serialize"
421 )]
422 #[ignore_malloc_size_of = "Defined in hyper"]
423 pub headers: HeaderMap,
424
425 pub unsafe_request: bool,
427
428 pub body: Option<RequestBody>,
430
431 pub service_workers_mode: ServiceWorkersMode,
433 pub client: Option<RequestClient>,
434 pub destination: Destination,
436 pub synchronous: bool,
437 pub mode: RequestMode,
438
439 pub cache_mode: CacheMode,
441
442 pub use_cors_preflight: bool,
444
445 pub credentials_mode: CredentialsMode,
447 pub use_url_credentials: bool,
448
449 pub origin: Origin,
451
452 pub policy_container: RequestPolicyContainer,
454 pub insecure_requests_policy: InsecureRequestsPolicy,
455 pub has_trustworthy_ancestor_origin: bool,
456
457 pub referrer: Referrer,
459
460 pub referrer_policy: ReferrerPolicy,
462 pub pipeline_id: Option<PipelineId>,
463 pub target_webview_id: Option<WebViewId>,
464
465 pub redirect_mode: RedirectMode,
467
468 pub integrity_metadata: String,
470
471 pub cryptographic_nonce_metadata: String,
473
474 pub url_list: Vec<ServoUrl>,
476
477 pub parser_metadata: ParserMetadata,
479
480 pub initiator: Initiator,
482 pub https_state: HttpsState,
483 pub response_tainting: ResponseTainting,
484 pub crash: Option<String>,
486}
487
488impl RequestBuilder {
489 pub fn new(webview_id: Option<WebViewId>, url: ServoUrl, referrer: Referrer) -> RequestBuilder {
490 RequestBuilder {
491 id: RequestId::default(),
492 method: Method::GET,
493 url,
494 headers: HeaderMap::new(),
495 unsafe_request: false,
496 body: None,
497 service_workers_mode: ServiceWorkersMode::All,
498 destination: Destination::None,
499 synchronous: false,
500 mode: RequestMode::NoCors,
501 cache_mode: CacheMode::Default,
502 use_cors_preflight: false,
503 credentials_mode: CredentialsMode::CredentialsSameOrigin,
504 use_url_credentials: false,
505 origin: Origin::Client,
506 client: None,
507 policy_container: RequestPolicyContainer::default(),
508 insecure_requests_policy: InsecureRequestsPolicy::DoNotUpgrade,
509 has_trustworthy_ancestor_origin: false,
510 referrer,
511 referrer_policy: ReferrerPolicy::EmptyString,
512 pipeline_id: None,
513 target_webview_id: webview_id,
514 redirect_mode: RedirectMode::Follow,
515 integrity_metadata: "".to_owned(),
516 cryptographic_nonce_metadata: "".to_owned(),
517 url_list: vec![],
518 parser_metadata: ParserMetadata::Default,
519 initiator: Initiator::None,
520 https_state: HttpsState::None,
521 response_tainting: ResponseTainting::Basic,
522 crash: None,
523 }
524 }
525
526 pub fn initiator(mut self, initiator: Initiator) -> RequestBuilder {
528 self.initiator = initiator;
529 self
530 }
531
532 pub fn method(mut self, method: Method) -> RequestBuilder {
534 self.method = method;
535 self
536 }
537
538 pub fn headers(mut self, headers: HeaderMap) -> RequestBuilder {
540 self.headers = headers;
541 self
542 }
543
544 pub fn unsafe_request(mut self, unsafe_request: bool) -> RequestBuilder {
546 self.unsafe_request = unsafe_request;
547 self
548 }
549
550 pub fn body(mut self, body: Option<RequestBody>) -> RequestBuilder {
552 self.body = body;
553 self
554 }
555
556 pub fn destination(mut self, destination: Destination) -> RequestBuilder {
558 self.destination = destination;
559 self
560 }
561
562 pub fn synchronous(mut self, synchronous: bool) -> RequestBuilder {
563 self.synchronous = synchronous;
564 self
565 }
566
567 pub fn mode(mut self, mode: RequestMode) -> RequestBuilder {
568 self.mode = mode;
569 self
570 }
571
572 pub fn use_cors_preflight(mut self, use_cors_preflight: bool) -> RequestBuilder {
574 self.use_cors_preflight = use_cors_preflight;
575 self
576 }
577
578 pub fn credentials_mode(mut self, credentials_mode: CredentialsMode) -> RequestBuilder {
580 self.credentials_mode = credentials_mode;
581 self
582 }
583
584 pub fn use_url_credentials(mut self, use_url_credentials: bool) -> RequestBuilder {
585 self.use_url_credentials = use_url_credentials;
586 self
587 }
588
589 pub fn origin(mut self, origin: ImmutableOrigin) -> RequestBuilder {
591 self.origin = Origin::Origin(origin);
592 self
593 }
594
595 pub fn referrer_policy(mut self, referrer_policy: ReferrerPolicy) -> RequestBuilder {
597 self.referrer_policy = referrer_policy;
598 self
599 }
600
601 pub fn pipeline_id(mut self, pipeline_id: Option<PipelineId>) -> RequestBuilder {
602 self.pipeline_id = pipeline_id;
603 self
604 }
605
606 pub fn redirect_mode(mut self, redirect_mode: RedirectMode) -> RequestBuilder {
608 self.redirect_mode = redirect_mode;
609 self
610 }
611
612 pub fn integrity_metadata(mut self, integrity_metadata: String) -> RequestBuilder {
614 self.integrity_metadata = integrity_metadata;
615 self
616 }
617
618 pub fn cryptographic_nonce_metadata(mut self, nonce_metadata: String) -> RequestBuilder {
620 self.cryptographic_nonce_metadata = nonce_metadata;
621 self
622 }
623
624 pub fn parser_metadata(mut self, parser_metadata: ParserMetadata) -> RequestBuilder {
626 self.parser_metadata = parser_metadata;
627 self
628 }
629
630 pub fn https_state(mut self, https_state: HttpsState) -> RequestBuilder {
631 self.https_state = https_state;
632 self
633 }
634
635 pub fn response_tainting(mut self, response_tainting: ResponseTainting) -> RequestBuilder {
636 self.response_tainting = response_tainting;
637 self
638 }
639
640 pub fn crash(mut self, crash: Option<String>) -> Self {
641 self.crash = crash;
642 self
643 }
644
645 pub fn policy_container(mut self, policy_container: PolicyContainer) -> RequestBuilder {
647 self.policy_container = RequestPolicyContainer::PolicyContainer(policy_container);
648 self
649 }
650
651 pub fn client(mut self, client: RequestClient) -> RequestBuilder {
653 self.client = Some(client);
654 self
655 }
656
657 pub fn insecure_requests_policy(
658 mut self,
659 insecure_requests_policy: InsecureRequestsPolicy,
660 ) -> RequestBuilder {
661 self.insecure_requests_policy = insecure_requests_policy;
662 self
663 }
664
665 pub fn has_trustworthy_ancestor_origin(
666 mut self,
667 has_trustworthy_ancestor_origin: bool,
668 ) -> RequestBuilder {
669 self.has_trustworthy_ancestor_origin = has_trustworthy_ancestor_origin;
670 self
671 }
672
673 pub fn service_workers_mode(
675 mut self,
676 service_workers_mode: ServiceWorkersMode,
677 ) -> RequestBuilder {
678 self.service_workers_mode = service_workers_mode;
679 self
680 }
681
682 pub fn cache_mode(mut self, cache_mode: CacheMode) -> RequestBuilder {
684 self.cache_mode = cache_mode;
685 self
686 }
687
688 pub fn build(self) -> Request {
689 let mut request = Request::new(
690 self.id,
691 self.url.clone(),
692 Some(self.origin),
693 self.referrer,
694 self.pipeline_id,
695 self.target_webview_id,
696 self.https_state,
697 );
698 request.initiator = self.initiator;
699 request.method = self.method;
700 request.headers = self.headers;
701 request.unsafe_request = self.unsafe_request;
702 request.body = self.body;
703 request.service_workers_mode = self.service_workers_mode;
704 request.destination = self.destination;
705 request.synchronous = self.synchronous;
706 request.mode = self.mode;
707 request.use_cors_preflight = self.use_cors_preflight;
708 request.credentials_mode = self.credentials_mode;
709 request.use_url_credentials = self.use_url_credentials;
710 request.cache_mode = self.cache_mode;
711 request.referrer_policy = self.referrer_policy;
712 request.redirect_mode = self.redirect_mode;
713 let mut url_list = self.url_list;
714 if url_list.is_empty() {
715 url_list.push(self.url);
716 }
717 request.redirect_count = url_list.len() as u32 - 1;
718 request.url_list = url_list;
719 request.integrity_metadata = self.integrity_metadata;
720 request.cryptographic_nonce_metadata = self.cryptographic_nonce_metadata;
721 request.parser_metadata = self.parser_metadata;
722 request.response_tainting = self.response_tainting;
723 request.crash = self.crash;
724 request.client = self.client;
725 request.policy_container = self.policy_container;
726 request.insecure_requests_policy = self.insecure_requests_policy;
727 request.has_trustworthy_ancestor_origin = self.has_trustworthy_ancestor_origin;
728 request
729 }
730}
731
732#[derive(Clone, MallocSizeOf)]
735pub struct Request {
736 pub id: RequestId,
740 #[ignore_malloc_size_of = "Defined in hyper"]
742 pub method: Method,
743 pub local_urls_only: bool,
745 #[ignore_malloc_size_of = "Defined in hyper"]
747 pub headers: HeaderMap,
748 pub unsafe_request: bool,
750 pub body: Option<RequestBody>,
752 pub client: Option<RequestClient>,
754 pub traversable_for_user_prompts: TraversableForUserPrompts,
756 pub target_webview_id: Option<WebViewId>,
757 pub keep_alive: bool,
759 pub service_workers_mode: ServiceWorkersMode,
761 pub initiator: Initiator,
763 pub destination: Destination,
765 pub origin: Origin,
768 pub referrer: Referrer,
770 pub referrer_policy: ReferrerPolicy,
772 pub pipeline_id: Option<PipelineId>,
773 pub synchronous: bool,
775 pub mode: RequestMode,
777 pub use_cors_preflight: bool,
779 pub credentials_mode: CredentialsMode,
781 pub use_url_credentials: bool,
783 pub cache_mode: CacheMode,
785 pub redirect_mode: RedirectMode,
787 pub integrity_metadata: String,
789 pub cryptographic_nonce_metadata: String,
791 pub url_list: Vec<ServoUrl>,
795 pub redirect_count: u32,
797 pub response_tainting: ResponseTainting,
799 pub parser_metadata: ParserMetadata,
801 pub policy_container: RequestPolicyContainer,
803 pub insecure_requests_policy: InsecureRequestsPolicy,
805 pub has_trustworthy_ancestor_origin: bool,
806 pub https_state: HttpsState,
807 pub crash: Option<String>,
809}
810
811impl Request {
812 pub fn new(
813 id: RequestId,
814 url: ServoUrl,
815 origin: Option<Origin>,
816 referrer: Referrer,
817 pipeline_id: Option<PipelineId>,
818 webview_id: Option<WebViewId>,
819 https_state: HttpsState,
820 ) -> Request {
821 Request {
822 id,
823 method: Method::GET,
824 local_urls_only: false,
825 headers: HeaderMap::new(),
826 unsafe_request: false,
827 body: None,
828 client: None,
829 traversable_for_user_prompts: TraversableForUserPrompts::Client,
830 keep_alive: false,
831 service_workers_mode: ServiceWorkersMode::All,
832 initiator: Initiator::None,
833 destination: Destination::None,
834 origin: origin.unwrap_or(Origin::Client),
835 referrer,
836 referrer_policy: ReferrerPolicy::EmptyString,
837 pipeline_id,
838 target_webview_id: webview_id,
839 synchronous: false,
840 mode: RequestMode::NoCors,
841 use_cors_preflight: false,
842 credentials_mode: CredentialsMode::CredentialsSameOrigin,
843 use_url_credentials: false,
844 cache_mode: CacheMode::Default,
845 redirect_mode: RedirectMode::Follow,
846 integrity_metadata: String::new(),
847 cryptographic_nonce_metadata: String::new(),
848 url_list: vec![url],
849 parser_metadata: ParserMetadata::Default,
850 redirect_count: 0,
851 response_tainting: ResponseTainting::Basic,
852 policy_container: RequestPolicyContainer::Client,
853 insecure_requests_policy: InsecureRequestsPolicy::DoNotUpgrade,
854 has_trustworthy_ancestor_origin: false,
855 https_state,
856 crash: None,
857 }
858 }
859
860 pub fn url(&self) -> ServoUrl {
862 self.url_list.first().unwrap().clone()
863 }
864
865 pub fn original_url(&self) -> ServoUrl {
866 match self.mode {
867 RequestMode::WebSocket {
868 protocols: _,
869 ref original_url,
870 } => original_url.clone(),
871 _ => self.url(),
872 }
873 }
874
875 pub fn current_url(&self) -> ServoUrl {
877 self.url_list.last().unwrap().clone()
878 }
879
880 pub fn current_url_mut(&mut self) -> &mut ServoUrl {
882 self.url_list.last_mut().unwrap()
883 }
884
885 pub fn is_navigation_request(&self) -> bool {
887 matches!(
888 self.destination,
889 Destination::Document |
890 Destination::Embed |
891 Destination::Frame |
892 Destination::IFrame |
893 Destination::Object
894 )
895 }
896
897 pub fn is_subresource_request(&self) -> bool {
899 matches!(
900 self.destination,
901 Destination::Audio |
902 Destination::Font |
903 Destination::Image |
904 Destination::Manifest |
905 Destination::Script |
906 Destination::Style |
907 Destination::Track |
908 Destination::Video |
909 Destination::Xslt |
910 Destination::None
911 )
912 }
913
914 pub fn timing_type(&self) -> ResourceTimingType {
915 if self.is_navigation_request() {
916 ResourceTimingType::Navigation
917 } else {
918 ResourceTimingType::Resource
919 }
920 }
921
922 pub fn populate_request_from_client(&mut self) {
924 if self.traversable_for_user_prompts == TraversableForUserPrompts::Client {
926 self.traversable_for_user_prompts = TraversableForUserPrompts::NoTraversable;
928 if self.client.is_some() {
930 self.traversable_for_user_prompts =
935 TraversableForUserPrompts::TraversableNavigable(Default::default());
936 }
937 }
938 if self.origin == Origin::Client {
940 let Some(client) = self.client.as_ref() else {
941 unreachable!();
943 };
944 self.origin = client.origin.clone();
946 }
947 if matches!(self.policy_container, RequestPolicyContainer::Client) {
949 if let Some(client) = self.client.as_ref() {
952 self.policy_container = client.policy_container.clone();
953 } else {
954 self.policy_container =
956 RequestPolicyContainer::PolicyContainer(PolicyContainer::default());
957 }
958 }
959 }
960}
961
962impl Referrer {
963 pub fn to_url(&self) -> Option<&ServoUrl> {
964 match *self {
965 Referrer::NoReferrer => None,
966 Referrer::Client(ref url) => Some(url),
967 Referrer::ReferrerUrl(ref url) => Some(url),
968 }
969 }
970}
971
972fn is_cors_unsafe_request_header_byte(value: &u8) -> bool {
976 matches!(value,
977 0x00..=0x08 |
978 0x10..=0x19 |
979 0x22 |
980 0x28 |
981 0x29 |
982 0x3A |
983 0x3C |
984 0x3E |
985 0x3F |
986 0x40 |
987 0x5B |
988 0x5C |
989 0x5D |
990 0x7B |
991 0x7D |
992 0x7F
993 )
994}
995
996fn is_cors_safelisted_request_accept(value: &[u8]) -> bool {
999 !(value.iter().any(is_cors_unsafe_request_header_byte))
1000}
1001
1002fn is_cors_safelisted_language(value: &[u8]) -> bool {
1005 value.iter().all(|&x| {
1006 matches!(x,
1007 0x30..=0x39 |
1008 0x41..=0x5A |
1009 0x61..=0x7A |
1010 0x20 |
1011 0x2A |
1012 0x2C |
1013 0x2D |
1014 0x2E |
1015 0x3B |
1016 0x3D
1017 )
1018 })
1019}
1020
1021pub fn is_cors_safelisted_request_content_type(value: &[u8]) -> bool {
1024 if value.iter().any(is_cors_unsafe_request_header_byte) {
1026 return false;
1027 }
1028 let value_string = if let Ok(s) = std::str::from_utf8(value) {
1030 s
1031 } else {
1032 return false;
1033 };
1034 let value_mime_result: Result<Mime, _> = value_string.parse();
1035 match value_mime_result {
1036 Err(_) => false, Ok(value_mime) => match (value_mime.type_(), value_mime.subtype()) {
1038 (mime::APPLICATION, mime::WWW_FORM_URLENCODED) |
1039 (mime::MULTIPART, mime::FORM_DATA) |
1040 (mime::TEXT, mime::PLAIN) => true,
1041 _ => false, },
1043 }
1044}
1045
1046pub fn is_cors_safelisted_request_header<N: AsRef<str>, V: AsRef<[u8]>>(
1050 name: &N,
1051 value: &V,
1052) -> bool {
1053 let name: &str = name.as_ref();
1054 let value: &[u8] = value.as_ref();
1055 if value.len() > 128 {
1056 return false;
1057 }
1058 match name {
1059 "accept" => is_cors_safelisted_request_accept(value),
1060 "accept-language" | "content-language" => is_cors_safelisted_language(value),
1061 "content-type" => is_cors_safelisted_request_content_type(value),
1062 "range" => is_cors_safelisted_request_range(value),
1063 _ => false,
1064 }
1065}
1066
1067pub fn is_cors_safelisted_request_range(value: &[u8]) -> bool {
1068 if let Ok(value_str) = std::str::from_utf8(value) {
1069 return validate_range_header(value_str);
1070 }
1071 false
1072}
1073
1074fn validate_range_header(value: &str) -> bool {
1075 let trimmed = value.trim();
1076 if !trimmed.starts_with("bytes=") {
1077 return false;
1078 }
1079
1080 if let Some(range) = trimmed.strip_prefix("bytes=") {
1081 let mut parts = range.split('-');
1082 let start = parts.next();
1083 let end = parts.next();
1084
1085 if let Some(start) = start {
1086 if let Ok(start_num) = start.parse::<u64>() {
1087 return match end {
1088 Some(e) if !e.is_empty() => {
1089 e.parse::<u64>().is_ok_and(|end_num| start_num <= end_num)
1090 },
1091 _ => true,
1092 };
1093 }
1094 }
1095 }
1096 false
1097}
1098
1099pub fn is_cors_safelisted_method(method: &Method) -> bool {
1101 matches!(*method, Method::GET | Method::HEAD | Method::POST)
1102}
1103
1104pub fn is_cors_non_wildcard_request_header_name(name: &HeaderName) -> bool {
1106 name == AUTHORIZATION
1107}
1108
1109pub fn get_cors_unsafe_header_names(headers: &HeaderMap) -> Vec<HeaderName> {
1111 let mut unsafe_names: Vec<&HeaderName> = vec![];
1113 let mut potentillay_unsafe_names: Vec<&HeaderName> = vec![];
1115 let mut safelist_value_size = 0;
1117
1118 for (name, value) in headers.iter() {
1120 if !is_cors_safelisted_request_header(&name, &value) {
1121 unsafe_names.push(name);
1122 } else {
1123 potentillay_unsafe_names.push(name);
1124 safelist_value_size += value.as_ref().len();
1125 }
1126 }
1127
1128 if safelist_value_size > 1024 {
1130 unsafe_names.extend_from_slice(&potentillay_unsafe_names);
1131 }
1132
1133 convert_header_names_to_sorted_lowercase_set(unsafe_names)
1135}
1136
1137pub fn convert_header_names_to_sorted_lowercase_set(
1139 header_names: Vec<&HeaderName>,
1140) -> Vec<HeaderName> {
1141 let mut ordered_set = header_names.to_vec();
1144 ordered_set.sort_by(|a, b| a.as_str().partial_cmp(b.as_str()).unwrap());
1145 ordered_set.dedup();
1146 ordered_set.into_iter().cloned().collect()
1147}
1148
1149pub fn create_request_body_with_content(content: &str) -> RequestBody {
1150 let content_bytes = IpcSharedMemory::from_bytes(content.as_bytes());
1151 let content_len = content_bytes.len();
1152
1153 let (chunk_request_sender, chunk_request_receiver) = ipc::channel().unwrap();
1154 ROUTER.add_typed_route(
1155 chunk_request_receiver,
1156 Box::new(move |message| {
1157 let request = message.unwrap();
1158 if let BodyChunkRequest::Connect(sender) = request {
1159 let _ = sender.send(BodyChunkResponse::Chunk(content_bytes.clone()));
1160 let _ = sender.send(BodyChunkResponse::Done);
1161 }
1162 }),
1163 );
1164
1165 RequestBody::new(chunk_request_sender, BodySource::Object, Some(content_len))
1166}