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 ipc_channel::ipc::{self, IpcReceiver, IpcSender, IpcSharedMemory};
12use ipc_channel::router::ROUTER;
13use malloc_size_of_derive::MallocSizeOf;
14use mime::Mime;
15use serde::{Deserialize, Serialize};
16use servo_url::{ImmutableOrigin, ServoUrl};
17use uuid::Uuid;
18
19use crate::policy_container::{PolicyContainer, RequestPolicyContainer};
20use crate::response::HttpsState;
21use crate::{ReferrerPolicy, ResourceTimingType};
22
23#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
24pub struct RequestId(pub Uuid);
26
27impl Default for RequestId {
28 fn default() -> Self {
29 Self(servo_rand::random_uuid())
30 }
31}
32
33#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
35pub enum Initiator {
36 None,
37 Download,
38 ImageSet,
39 Manifest,
40 XSLT,
41 Prefetch,
42 Link,
43}
44
45pub use csp::Destination;
47
48#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
50pub enum Origin {
51 Client,
52 Origin(ImmutableOrigin),
53}
54
55impl Origin {
56 pub fn is_opaque(&self) -> bool {
57 matches!(self, Origin::Origin(ImmutableOrigin::Opaque(_)))
58 }
59}
60
61#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
63pub enum Referrer {
64 NoReferrer,
65 Client(ServoUrl),
71 ReferrerUrl(ServoUrl),
72}
73
74#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
76pub enum RequestMode {
77 Navigate,
78 SameOrigin,
79 NoCors,
80 CorsMode,
81 WebSocket { protocols: Vec<String> },
82}
83
84#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
86pub enum CredentialsMode {
87 Omit,
88 CredentialsSameOrigin,
89 Include,
90}
91
92#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
94pub enum CacheMode {
95 Default,
96 NoStore,
97 Reload,
98 NoCache,
99 ForceCache,
100 OnlyIfCached,
101}
102
103#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
105pub enum ServiceWorkersMode {
106 All,
107 None,
108}
109
110#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
112pub enum RedirectMode {
113 Follow,
114 Error,
115 Manual,
116}
117
118#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
120pub enum ResponseTainting {
121 Basic,
122 CorsTainting,
123 Opaque,
124}
125
126#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
128pub enum Window {
129 NoWindow,
130 Client, }
132
133#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
135pub enum CorsSettings {
136 Anonymous,
137 UseCredentials,
138}
139
140impl CorsSettings {
141 pub fn from_enumerated_attribute(value: &str) -> CorsSettings {
143 match value.to_ascii_lowercase().as_str() {
144 "anonymous" => CorsSettings::Anonymous,
145 "use-credentials" => CorsSettings::UseCredentials,
146 _ => CorsSettings::Anonymous,
147 }
148 }
149}
150
151#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
153pub enum ParserMetadata {
154 Default,
155 ParserInserted,
156 NotParserInserted,
157}
158
159#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
161pub enum BodySource {
162 Null,
163 Object,
164}
165
166#[derive(Debug, Deserialize, Serialize)]
169pub enum BodyChunkResponse {
170 Chunk(IpcSharedMemory),
172 Done,
174 Error,
177}
178
179#[derive(Debug, Deserialize, Serialize)]
183pub enum BodyChunkRequest {
184 Connect(IpcSender<BodyChunkResponse>),
186 Extract(IpcReceiver<BodyChunkRequest>),
188 Chunk,
190 Done,
192 Error,
194}
195
196#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
198pub struct RequestBody {
199 #[ignore_malloc_size_of = "Channels are hard"]
201 chan: Arc<Mutex<IpcSender<BodyChunkRequest>>>,
202 source: BodySource,
204 total_bytes: Option<usize>,
206}
207
208impl RequestBody {
209 pub fn new(
210 chan: IpcSender<BodyChunkRequest>,
211 source: BodySource,
212 total_bytes: Option<usize>,
213 ) -> Self {
214 RequestBody {
215 chan: Arc::new(Mutex::new(chan)),
216 source,
217 total_bytes,
218 }
219 }
220
221 pub fn extract_source(&mut self) {
223 match self.source {
224 BodySource::Null => panic!("Null sources should never be re-directed."),
225 BodySource::Object => {
226 let (chan, port) = ipc::channel().unwrap();
227 let mut selfchan = self.chan.lock().unwrap();
228 let _ = selfchan.send(BodyChunkRequest::Extract(port));
229 *selfchan = chan;
230 },
231 }
232 }
233
234 pub fn take_stream(&self) -> Arc<Mutex<IpcSender<BodyChunkRequest>>> {
235 self.chan.clone()
236 }
237
238 pub fn source_is_null(&self) -> bool {
239 self.source == BodySource::Null
240 }
241
242 #[allow(clippy::len_without_is_empty)]
243 pub fn len(&self) -> Option<usize> {
244 self.total_bytes
245 }
246}
247
248#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
249pub enum InsecureRequestsPolicy {
250 DoNotUpgrade,
251 Upgrade,
252}
253
254#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
255pub struct RequestBuilder {
256 pub id: RequestId,
257
258 #[serde(
260 deserialize_with = "::hyper_serde::deserialize",
261 serialize_with = "::hyper_serde::serialize"
262 )]
263 #[ignore_malloc_size_of = "Defined in hyper"]
264 pub method: Method,
265
266 pub url: ServoUrl,
268
269 #[serde(
271 deserialize_with = "::hyper_serde::deserialize",
272 serialize_with = "::hyper_serde::serialize"
273 )]
274 #[ignore_malloc_size_of = "Defined in hyper"]
275 pub headers: HeaderMap,
276
277 pub unsafe_request: bool,
279
280 pub body: Option<RequestBody>,
282
283 pub service_workers_mode: ServiceWorkersMode,
285 pub destination: Destination,
288 pub synchronous: bool,
289 pub mode: RequestMode,
290
291 pub cache_mode: CacheMode,
293
294 pub use_cors_preflight: bool,
296
297 pub credentials_mode: CredentialsMode,
299 pub use_url_credentials: bool,
300
301 pub origin: ImmutableOrigin,
303
304 pub policy_container: RequestPolicyContainer,
306 pub insecure_requests_policy: InsecureRequestsPolicy,
307 pub has_trustworthy_ancestor_origin: bool,
308
309 pub referrer: Referrer,
311
312 pub referrer_policy: ReferrerPolicy,
314 pub pipeline_id: Option<PipelineId>,
315 pub target_webview_id: Option<WebViewId>,
316
317 pub redirect_mode: RedirectMode,
319
320 pub integrity_metadata: String,
322
323 pub cryptographic_nonce_metadata: String,
325
326 pub url_list: Vec<ServoUrl>,
328
329 pub parser_metadata: ParserMetadata,
331
332 pub initiator: Initiator,
334 pub https_state: HttpsState,
335 pub response_tainting: ResponseTainting,
336 pub crash: Option<String>,
338}
339
340impl RequestBuilder {
341 pub fn new(webview_id: Option<WebViewId>, url: ServoUrl, referrer: Referrer) -> RequestBuilder {
342 RequestBuilder {
343 id: RequestId::default(),
344 method: Method::GET,
345 url,
346 headers: HeaderMap::new(),
347 unsafe_request: false,
348 body: None,
349 service_workers_mode: ServiceWorkersMode::All,
350 destination: Destination::None,
351 synchronous: false,
352 mode: RequestMode::NoCors,
353 cache_mode: CacheMode::Default,
354 use_cors_preflight: false,
355 credentials_mode: CredentialsMode::CredentialsSameOrigin,
356 use_url_credentials: false,
357 origin: ImmutableOrigin::new_opaque(),
358 policy_container: RequestPolicyContainer::default(),
359 insecure_requests_policy: InsecureRequestsPolicy::DoNotUpgrade,
360 has_trustworthy_ancestor_origin: false,
361 referrer,
362 referrer_policy: ReferrerPolicy::EmptyString,
363 pipeline_id: None,
364 target_webview_id: webview_id,
365 redirect_mode: RedirectMode::Follow,
366 integrity_metadata: "".to_owned(),
367 cryptographic_nonce_metadata: "".to_owned(),
368 url_list: vec![],
369 parser_metadata: ParserMetadata::Default,
370 initiator: Initiator::None,
371 https_state: HttpsState::None,
372 response_tainting: ResponseTainting::Basic,
373 crash: None,
374 }
375 }
376
377 pub fn initiator(mut self, initiator: Initiator) -> RequestBuilder {
379 self.initiator = initiator;
380 self
381 }
382
383 pub fn method(mut self, method: Method) -> RequestBuilder {
385 self.method = method;
386 self
387 }
388
389 pub fn headers(mut self, headers: HeaderMap) -> RequestBuilder {
391 self.headers = headers;
392 self
393 }
394
395 pub fn unsafe_request(mut self, unsafe_request: bool) -> RequestBuilder {
397 self.unsafe_request = unsafe_request;
398 self
399 }
400
401 pub fn body(mut self, body: Option<RequestBody>) -> RequestBuilder {
403 self.body = body;
404 self
405 }
406
407 pub fn destination(mut self, destination: Destination) -> RequestBuilder {
409 self.destination = destination;
410 self
411 }
412
413 pub fn synchronous(mut self, synchronous: bool) -> RequestBuilder {
414 self.synchronous = synchronous;
415 self
416 }
417
418 pub fn mode(mut self, mode: RequestMode) -> RequestBuilder {
419 self.mode = mode;
420 self
421 }
422
423 pub fn use_cors_preflight(mut self, use_cors_preflight: bool) -> RequestBuilder {
425 self.use_cors_preflight = use_cors_preflight;
426 self
427 }
428
429 pub fn credentials_mode(mut self, credentials_mode: CredentialsMode) -> RequestBuilder {
431 self.credentials_mode = credentials_mode;
432 self
433 }
434
435 pub fn use_url_credentials(mut self, use_url_credentials: bool) -> RequestBuilder {
436 self.use_url_credentials = use_url_credentials;
437 self
438 }
439
440 pub fn origin(mut self, origin: ImmutableOrigin) -> RequestBuilder {
442 self.origin = origin;
443 self
444 }
445
446 pub fn referrer_policy(mut self, referrer_policy: ReferrerPolicy) -> RequestBuilder {
448 self.referrer_policy = referrer_policy;
449 self
450 }
451
452 pub fn pipeline_id(mut self, pipeline_id: Option<PipelineId>) -> RequestBuilder {
453 self.pipeline_id = pipeline_id;
454 self
455 }
456
457 pub fn redirect_mode(mut self, redirect_mode: RedirectMode) -> RequestBuilder {
459 self.redirect_mode = redirect_mode;
460 self
461 }
462
463 pub fn integrity_metadata(mut self, integrity_metadata: String) -> RequestBuilder {
465 self.integrity_metadata = integrity_metadata;
466 self
467 }
468
469 pub fn cryptographic_nonce_metadata(mut self, nonce_metadata: String) -> RequestBuilder {
471 self.cryptographic_nonce_metadata = nonce_metadata;
472 self
473 }
474
475 pub fn parser_metadata(mut self, parser_metadata: ParserMetadata) -> RequestBuilder {
477 self.parser_metadata = parser_metadata;
478 self
479 }
480
481 pub fn https_state(mut self, https_state: HttpsState) -> RequestBuilder {
482 self.https_state = https_state;
483 self
484 }
485
486 pub fn response_tainting(mut self, response_tainting: ResponseTainting) -> RequestBuilder {
487 self.response_tainting = response_tainting;
488 self
489 }
490
491 pub fn crash(mut self, crash: Option<String>) -> Self {
492 self.crash = crash;
493 self
494 }
495
496 pub fn policy_container(mut self, policy_container: PolicyContainer) -> RequestBuilder {
498 self.policy_container = RequestPolicyContainer::PolicyContainer(policy_container);
499 self
500 }
501
502 pub fn insecure_requests_policy(
503 mut self,
504 insecure_requests_policy: InsecureRequestsPolicy,
505 ) -> RequestBuilder {
506 self.insecure_requests_policy = insecure_requests_policy;
507 self
508 }
509
510 pub fn has_trustworthy_ancestor_origin(
511 mut self,
512 has_trustworthy_ancestor_origin: bool,
513 ) -> RequestBuilder {
514 self.has_trustworthy_ancestor_origin = has_trustworthy_ancestor_origin;
515 self
516 }
517
518 pub fn service_workers_mode(
520 mut self,
521 service_workers_mode: ServiceWorkersMode,
522 ) -> RequestBuilder {
523 self.service_workers_mode = service_workers_mode;
524 self
525 }
526
527 pub fn cache_mode(mut self, cache_mode: CacheMode) -> RequestBuilder {
529 self.cache_mode = cache_mode;
530 self
531 }
532
533 pub fn build(self) -> Request {
534 let mut request = Request::new(
535 self.id,
536 self.url.clone(),
537 Some(Origin::Origin(self.origin)),
538 self.referrer,
539 self.pipeline_id,
540 self.target_webview_id,
541 self.https_state,
542 );
543 request.initiator = self.initiator;
544 request.method = self.method;
545 request.headers = self.headers;
546 request.unsafe_request = self.unsafe_request;
547 request.body = self.body;
548 request.service_workers_mode = self.service_workers_mode;
549 request.destination = self.destination;
550 request.synchronous = self.synchronous;
551 request.mode = self.mode;
552 request.use_cors_preflight = self.use_cors_preflight;
553 request.credentials_mode = self.credentials_mode;
554 request.use_url_credentials = self.use_url_credentials;
555 request.cache_mode = self.cache_mode;
556 request.referrer_policy = self.referrer_policy;
557 request.redirect_mode = self.redirect_mode;
558 let mut url_list = self.url_list;
559 if url_list.is_empty() {
560 url_list.push(self.url);
561 }
562 request.redirect_count = url_list.len() as u32 - 1;
563 request.url_list = url_list;
564 request.integrity_metadata = self.integrity_metadata;
565 request.cryptographic_nonce_metadata = self.cryptographic_nonce_metadata;
566 request.parser_metadata = self.parser_metadata;
567 request.response_tainting = self.response_tainting;
568 request.crash = self.crash;
569 request.policy_container = self.policy_container;
570 request.insecure_requests_policy = self.insecure_requests_policy;
571 request.has_trustworthy_ancestor_origin = self.has_trustworthy_ancestor_origin;
572 request
573 }
574}
575
576#[derive(Clone, MallocSizeOf)]
579pub struct Request {
580 pub id: RequestId,
584 #[ignore_malloc_size_of = "Defined in hyper"]
586 pub method: Method,
587 pub local_urls_only: bool,
589 #[ignore_malloc_size_of = "Defined in hyper"]
591 pub headers: HeaderMap,
592 pub unsafe_request: bool,
594 pub body: Option<RequestBody>,
596 pub window: Window,
598 pub target_webview_id: Option<WebViewId>,
599 pub keep_alive: bool,
601 pub service_workers_mode: ServiceWorkersMode,
603 pub initiator: Initiator,
605 pub destination: Destination,
607 pub origin: Origin,
610 pub referrer: Referrer,
612 pub referrer_policy: ReferrerPolicy,
614 pub pipeline_id: Option<PipelineId>,
615 pub synchronous: bool,
617 pub mode: RequestMode,
619 pub use_cors_preflight: bool,
621 pub credentials_mode: CredentialsMode,
623 pub use_url_credentials: bool,
625 pub cache_mode: CacheMode,
627 pub redirect_mode: RedirectMode,
629 pub integrity_metadata: String,
631 pub cryptographic_nonce_metadata: String,
633 pub url_list: Vec<ServoUrl>,
637 pub redirect_count: u32,
639 pub response_tainting: ResponseTainting,
641 pub parser_metadata: ParserMetadata,
643 pub policy_container: RequestPolicyContainer,
645 pub insecure_requests_policy: InsecureRequestsPolicy,
647 pub has_trustworthy_ancestor_origin: bool,
648 pub https_state: HttpsState,
649 pub crash: Option<String>,
651}
652
653impl Request {
654 pub fn new(
655 id: RequestId,
656 url: ServoUrl,
657 origin: Option<Origin>,
658 referrer: Referrer,
659 pipeline_id: Option<PipelineId>,
660 webview_id: Option<WebViewId>,
661 https_state: HttpsState,
662 ) -> Request {
663 Request {
664 id,
665 method: Method::GET,
666 local_urls_only: false,
667 headers: HeaderMap::new(),
668 unsafe_request: false,
669 body: None,
670 window: Window::Client,
671 keep_alive: false,
672 service_workers_mode: ServiceWorkersMode::All,
673 initiator: Initiator::None,
674 destination: Destination::None,
675 origin: origin.unwrap_or(Origin::Client),
676 referrer,
677 referrer_policy: ReferrerPolicy::EmptyString,
678 pipeline_id,
679 target_webview_id: webview_id,
680 synchronous: false,
681 mode: RequestMode::NoCors,
682 use_cors_preflight: false,
683 credentials_mode: CredentialsMode::CredentialsSameOrigin,
684 use_url_credentials: false,
685 cache_mode: CacheMode::Default,
686 redirect_mode: RedirectMode::Follow,
687 integrity_metadata: String::new(),
688 cryptographic_nonce_metadata: String::new(),
689 url_list: vec![url],
690 parser_metadata: ParserMetadata::Default,
691 redirect_count: 0,
692 response_tainting: ResponseTainting::Basic,
693 policy_container: RequestPolicyContainer::Client,
694 insecure_requests_policy: InsecureRequestsPolicy::DoNotUpgrade,
695 has_trustworthy_ancestor_origin: false,
696 https_state,
697 crash: None,
698 }
699 }
700
701 pub fn url(&self) -> ServoUrl {
703 self.url_list.first().unwrap().clone()
704 }
705
706 pub fn current_url(&self) -> ServoUrl {
708 self.url_list.last().unwrap().clone()
709 }
710
711 pub fn current_url_mut(&mut self) -> &mut ServoUrl {
713 self.url_list.last_mut().unwrap()
714 }
715
716 pub fn is_navigation_request(&self) -> bool {
718 matches!(
719 self.destination,
720 Destination::Document |
721 Destination::Embed |
722 Destination::Frame |
723 Destination::IFrame |
724 Destination::Object
725 )
726 }
727
728 pub fn is_subresource_request(&self) -> bool {
730 matches!(
731 self.destination,
732 Destination::Audio |
733 Destination::Font |
734 Destination::Image |
735 Destination::Manifest |
736 Destination::Script |
737 Destination::Style |
738 Destination::Track |
739 Destination::Video |
740 Destination::Xslt |
741 Destination::None
742 )
743 }
744
745 pub fn timing_type(&self) -> ResourceTimingType {
746 if self.is_navigation_request() {
747 ResourceTimingType::Navigation
748 } else {
749 ResourceTimingType::Resource
750 }
751 }
752}
753
754impl Referrer {
755 pub fn to_url(&self) -> Option<&ServoUrl> {
756 match *self {
757 Referrer::NoReferrer => None,
758 Referrer::Client(ref url) => Some(url),
759 Referrer::ReferrerUrl(ref url) => Some(url),
760 }
761 }
762}
763
764fn is_cors_unsafe_request_header_byte(value: &u8) -> bool {
768 matches!(value,
769 0x00..=0x08 |
770 0x10..=0x19 |
771 0x22 |
772 0x28 |
773 0x29 |
774 0x3A |
775 0x3C |
776 0x3E |
777 0x3F |
778 0x40 |
779 0x5B |
780 0x5C |
781 0x5D |
782 0x7B |
783 0x7D |
784 0x7F
785 )
786}
787
788fn is_cors_safelisted_request_accept(value: &[u8]) -> bool {
791 !(value.iter().any(is_cors_unsafe_request_header_byte))
792}
793
794fn is_cors_safelisted_language(value: &[u8]) -> bool {
797 value.iter().all(|&x| {
798 matches!(x,
799 0x30..=0x39 |
800 0x41..=0x5A |
801 0x61..=0x7A |
802 0x20 |
803 0x2A |
804 0x2C |
805 0x2D |
806 0x2E |
807 0x3B |
808 0x3D
809 )
810 })
811}
812
813pub fn is_cors_safelisted_request_content_type(value: &[u8]) -> bool {
816 if value.iter().any(is_cors_unsafe_request_header_byte) {
818 return false;
819 }
820 let value_string = if let Ok(s) = std::str::from_utf8(value) {
822 s
823 } else {
824 return false;
825 };
826 let value_mime_result: Result<Mime, _> = value_string.parse();
827 match value_mime_result {
828 Err(_) => false, Ok(value_mime) => match (value_mime.type_(), value_mime.subtype()) {
830 (mime::APPLICATION, mime::WWW_FORM_URLENCODED) |
831 (mime::MULTIPART, mime::FORM_DATA) |
832 (mime::TEXT, mime::PLAIN) => true,
833 _ => false, },
835 }
836}
837
838pub fn is_cors_safelisted_request_header<N: AsRef<str>, V: AsRef<[u8]>>(
842 name: &N,
843 value: &V,
844) -> bool {
845 let name: &str = name.as_ref();
846 let value: &[u8] = value.as_ref();
847 if value.len() > 128 {
848 return false;
849 }
850 match name {
851 "accept" => is_cors_safelisted_request_accept(value),
852 "accept-language" | "content-language" => is_cors_safelisted_language(value),
853 "content-type" => is_cors_safelisted_request_content_type(value),
854 "range" => is_cors_safelisted_request_range(value),
855 _ => false,
856 }
857}
858
859pub fn is_cors_safelisted_request_range(value: &[u8]) -> bool {
860 if let Ok(value_str) = std::str::from_utf8(value) {
861 return validate_range_header(value_str);
862 }
863 false
864}
865
866fn validate_range_header(value: &str) -> bool {
867 let trimmed = value.trim();
868 if !trimmed.starts_with("bytes=") {
869 return false;
870 }
871
872 if let Some(range) = trimmed.strip_prefix("bytes=") {
873 let mut parts = range.split('-');
874 let start = parts.next();
875 let end = parts.next();
876
877 if let Some(start) = start {
878 if let Ok(start_num) = start.parse::<u64>() {
879 return match end {
880 Some(e) if !e.is_empty() => {
881 e.parse::<u64>().is_ok_and(|end_num| start_num <= end_num)
882 },
883 _ => true,
884 };
885 }
886 }
887 }
888 false
889}
890
891pub fn is_cors_safelisted_method(method: &Method) -> bool {
893 matches!(*method, Method::GET | Method::HEAD | Method::POST)
894}
895
896pub fn is_cors_non_wildcard_request_header_name(name: &HeaderName) -> bool {
898 name == AUTHORIZATION
899}
900
901pub fn get_cors_unsafe_header_names(headers: &HeaderMap) -> Vec<HeaderName> {
903 let mut unsafe_names: Vec<&HeaderName> = vec![];
905 let mut potentillay_unsafe_names: Vec<&HeaderName> = vec![];
907 let mut safelist_value_size = 0;
909
910 for (name, value) in headers.iter() {
912 if !is_cors_safelisted_request_header(&name, &value) {
913 unsafe_names.push(name);
914 } else {
915 potentillay_unsafe_names.push(name);
916 safelist_value_size += value.as_ref().len();
917 }
918 }
919
920 if safelist_value_size > 1024 {
922 unsafe_names.extend_from_slice(&potentillay_unsafe_names);
923 }
924
925 convert_header_names_to_sorted_lowercase_set(unsafe_names)
927}
928
929pub fn convert_header_names_to_sorted_lowercase_set(
931 header_names: Vec<&HeaderName>,
932) -> Vec<HeaderName> {
933 let mut ordered_set = header_names.to_vec();
936 ordered_set.sort_by(|a, b| a.as_str().partial_cmp(b.as_str()).unwrap());
937 ordered_set.dedup();
938 ordered_set.into_iter().cloned().collect()
939}
940
941pub fn create_request_body_with_content(content: &str) -> RequestBody {
942 let content_bytes = IpcSharedMemory::from_bytes(content.as_bytes());
943 let content_len = content_bytes.len();
944
945 let (chunk_request_sender, chunk_request_receiver) = ipc::channel().unwrap();
946 ROUTER.add_typed_route(
947 chunk_request_receiver,
948 Box::new(move |message| {
949 let request = message.unwrap();
950 if let BodyChunkRequest::Connect(sender) = request {
951 let _ = sender.send(BodyChunkResponse::Chunk(content_bytes.clone()));
952 let _ = sender.send(BodyChunkResponse::Done);
953 }
954 }),
955 );
956
957 RequestBody::new(chunk_request_sender, BodySource::Object, Some(content_len))
958}