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
140#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
142pub enum ParserMetadata {
143 Default,
144 ParserInserted,
145 NotParserInserted,
146}
147
148#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
150pub enum BodySource {
151 Null,
152 Object,
153}
154
155#[derive(Debug, Deserialize, Serialize)]
158pub enum BodyChunkResponse {
159 Chunk(IpcSharedMemory),
161 Done,
163 Error,
166}
167
168#[derive(Debug, Deserialize, Serialize)]
172pub enum BodyChunkRequest {
173 Connect(IpcSender<BodyChunkResponse>),
175 Extract(IpcReceiver<BodyChunkRequest>),
177 Chunk,
179 Done,
181 Error,
183}
184
185#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
187pub struct RequestBody {
188 #[ignore_malloc_size_of = "Channels are hard"]
190 chan: Arc<Mutex<IpcSender<BodyChunkRequest>>>,
191 source: BodySource,
193 total_bytes: Option<usize>,
195}
196
197impl RequestBody {
198 pub fn new(
199 chan: IpcSender<BodyChunkRequest>,
200 source: BodySource,
201 total_bytes: Option<usize>,
202 ) -> Self {
203 RequestBody {
204 chan: Arc::new(Mutex::new(chan)),
205 source,
206 total_bytes,
207 }
208 }
209
210 pub fn extract_source(&mut self) {
212 match self.source {
213 BodySource::Null => panic!("Null sources should never be re-directed."),
214 BodySource::Object => {
215 let (chan, port) = ipc::channel().unwrap();
216 let mut selfchan = self.chan.lock().unwrap();
217 let _ = selfchan.send(BodyChunkRequest::Extract(port));
218 *selfchan = chan;
219 },
220 }
221 }
222
223 pub fn take_stream(&self) -> Arc<Mutex<IpcSender<BodyChunkRequest>>> {
224 self.chan.clone()
225 }
226
227 pub fn source_is_null(&self) -> bool {
228 self.source == BodySource::Null
229 }
230
231 #[allow(clippy::len_without_is_empty)]
232 pub fn len(&self) -> Option<usize> {
233 self.total_bytes
234 }
235}
236
237#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
238pub enum InsecureRequestsPolicy {
239 DoNotUpgrade,
240 Upgrade,
241}
242
243#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
244pub struct RequestBuilder {
245 pub id: RequestId,
246
247 #[serde(
249 deserialize_with = "::hyper_serde::deserialize",
250 serialize_with = "::hyper_serde::serialize"
251 )]
252 #[ignore_malloc_size_of = "Defined in hyper"]
253 pub method: Method,
254
255 pub url: ServoUrl,
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 headers: HeaderMap,
265
266 pub unsafe_request: bool,
268
269 pub body: Option<RequestBody>,
271
272 pub service_workers_mode: ServiceWorkersMode,
274 pub destination: Destination,
277 pub synchronous: bool,
278 pub mode: RequestMode,
279
280 pub cache_mode: CacheMode,
282
283 pub use_cors_preflight: bool,
285
286 pub credentials_mode: CredentialsMode,
288 pub use_url_credentials: bool,
289
290 pub origin: ImmutableOrigin,
292
293 pub policy_container: RequestPolicyContainer,
295 pub insecure_requests_policy: InsecureRequestsPolicy,
296 pub has_trustworthy_ancestor_origin: bool,
297
298 pub referrer: Referrer,
300
301 pub referrer_policy: ReferrerPolicy,
303 pub pipeline_id: Option<PipelineId>,
304 pub target_webview_id: Option<WebViewId>,
305
306 pub redirect_mode: RedirectMode,
308
309 pub integrity_metadata: String,
311
312 pub cryptographic_nonce_metadata: String,
314
315 pub url_list: Vec<ServoUrl>,
317
318 pub parser_metadata: ParserMetadata,
320
321 pub initiator: Initiator,
323 pub https_state: HttpsState,
324 pub response_tainting: ResponseTainting,
325 pub crash: Option<String>,
327}
328
329impl RequestBuilder {
330 pub fn new(webview_id: Option<WebViewId>, url: ServoUrl, referrer: Referrer) -> RequestBuilder {
331 RequestBuilder {
332 id: RequestId::default(),
333 method: Method::GET,
334 url,
335 headers: HeaderMap::new(),
336 unsafe_request: false,
337 body: None,
338 service_workers_mode: ServiceWorkersMode::All,
339 destination: Destination::None,
340 synchronous: false,
341 mode: RequestMode::NoCors,
342 cache_mode: CacheMode::Default,
343 use_cors_preflight: false,
344 credentials_mode: CredentialsMode::CredentialsSameOrigin,
345 use_url_credentials: false,
346 origin: ImmutableOrigin::new_opaque(),
347 policy_container: RequestPolicyContainer::default(),
348 insecure_requests_policy: InsecureRequestsPolicy::DoNotUpgrade,
349 has_trustworthy_ancestor_origin: false,
350 referrer,
351 referrer_policy: ReferrerPolicy::EmptyString,
352 pipeline_id: None,
353 target_webview_id: webview_id,
354 redirect_mode: RedirectMode::Follow,
355 integrity_metadata: "".to_owned(),
356 cryptographic_nonce_metadata: "".to_owned(),
357 url_list: vec![],
358 parser_metadata: ParserMetadata::Default,
359 initiator: Initiator::None,
360 https_state: HttpsState::None,
361 response_tainting: ResponseTainting::Basic,
362 crash: None,
363 }
364 }
365
366 pub fn initiator(mut self, initiator: Initiator) -> RequestBuilder {
368 self.initiator = initiator;
369 self
370 }
371
372 pub fn method(mut self, method: Method) -> RequestBuilder {
374 self.method = method;
375 self
376 }
377
378 pub fn headers(mut self, headers: HeaderMap) -> RequestBuilder {
380 self.headers = headers;
381 self
382 }
383
384 pub fn unsafe_request(mut self, unsafe_request: bool) -> RequestBuilder {
386 self.unsafe_request = unsafe_request;
387 self
388 }
389
390 pub fn body(mut self, body: Option<RequestBody>) -> RequestBuilder {
392 self.body = body;
393 self
394 }
395
396 pub fn destination(mut self, destination: Destination) -> RequestBuilder {
398 self.destination = destination;
399 self
400 }
401
402 pub fn synchronous(mut self, synchronous: bool) -> RequestBuilder {
403 self.synchronous = synchronous;
404 self
405 }
406
407 pub fn mode(mut self, mode: RequestMode) -> RequestBuilder {
408 self.mode = mode;
409 self
410 }
411
412 pub fn use_cors_preflight(mut self, use_cors_preflight: bool) -> RequestBuilder {
414 self.use_cors_preflight = use_cors_preflight;
415 self
416 }
417
418 pub fn credentials_mode(mut self, credentials_mode: CredentialsMode) -> RequestBuilder {
420 self.credentials_mode = credentials_mode;
421 self
422 }
423
424 pub fn use_url_credentials(mut self, use_url_credentials: bool) -> RequestBuilder {
425 self.use_url_credentials = use_url_credentials;
426 self
427 }
428
429 pub fn origin(mut self, origin: ImmutableOrigin) -> RequestBuilder {
431 self.origin = origin;
432 self
433 }
434
435 pub fn referrer_policy(mut self, referrer_policy: ReferrerPolicy) -> RequestBuilder {
437 self.referrer_policy = referrer_policy;
438 self
439 }
440
441 pub fn pipeline_id(mut self, pipeline_id: Option<PipelineId>) -> RequestBuilder {
442 self.pipeline_id = pipeline_id;
443 self
444 }
445
446 pub fn redirect_mode(mut self, redirect_mode: RedirectMode) -> RequestBuilder {
448 self.redirect_mode = redirect_mode;
449 self
450 }
451
452 pub fn integrity_metadata(mut self, integrity_metadata: String) -> RequestBuilder {
454 self.integrity_metadata = integrity_metadata;
455 self
456 }
457
458 pub fn cryptographic_nonce_metadata(mut self, nonce_metadata: String) -> RequestBuilder {
460 self.cryptographic_nonce_metadata = nonce_metadata;
461 self
462 }
463
464 pub fn parser_metadata(mut self, parser_metadata: ParserMetadata) -> RequestBuilder {
466 self.parser_metadata = parser_metadata;
467 self
468 }
469
470 pub fn https_state(mut self, https_state: HttpsState) -> RequestBuilder {
471 self.https_state = https_state;
472 self
473 }
474
475 pub fn response_tainting(mut self, response_tainting: ResponseTainting) -> RequestBuilder {
476 self.response_tainting = response_tainting;
477 self
478 }
479
480 pub fn crash(mut self, crash: Option<String>) -> Self {
481 self.crash = crash;
482 self
483 }
484
485 pub fn policy_container(mut self, policy_container: PolicyContainer) -> RequestBuilder {
487 self.policy_container = RequestPolicyContainer::PolicyContainer(policy_container);
488 self
489 }
490
491 pub fn insecure_requests_policy(
492 mut self,
493 insecure_requests_policy: InsecureRequestsPolicy,
494 ) -> RequestBuilder {
495 self.insecure_requests_policy = insecure_requests_policy;
496 self
497 }
498
499 pub fn has_trustworthy_ancestor_origin(
500 mut self,
501 has_trustworthy_ancestor_origin: bool,
502 ) -> RequestBuilder {
503 self.has_trustworthy_ancestor_origin = has_trustworthy_ancestor_origin;
504 self
505 }
506
507 pub fn service_workers_mode(
509 mut self,
510 service_workers_mode: ServiceWorkersMode,
511 ) -> RequestBuilder {
512 self.service_workers_mode = service_workers_mode;
513 self
514 }
515
516 pub fn cache_mode(mut self, cache_mode: CacheMode) -> RequestBuilder {
518 self.cache_mode = cache_mode;
519 self
520 }
521
522 pub fn build(self) -> Request {
523 let mut request = Request::new(
524 self.id,
525 self.url.clone(),
526 Some(Origin::Origin(self.origin)),
527 self.referrer,
528 self.pipeline_id,
529 self.target_webview_id,
530 self.https_state,
531 );
532 request.initiator = self.initiator;
533 request.method = self.method;
534 request.headers = self.headers;
535 request.unsafe_request = self.unsafe_request;
536 request.body = self.body;
537 request.service_workers_mode = self.service_workers_mode;
538 request.destination = self.destination;
539 request.synchronous = self.synchronous;
540 request.mode = self.mode;
541 request.use_cors_preflight = self.use_cors_preflight;
542 request.credentials_mode = self.credentials_mode;
543 request.use_url_credentials = self.use_url_credentials;
544 request.cache_mode = self.cache_mode;
545 request.referrer_policy = self.referrer_policy;
546 request.redirect_mode = self.redirect_mode;
547 let mut url_list = self.url_list;
548 if url_list.is_empty() {
549 url_list.push(self.url);
550 }
551 request.redirect_count = url_list.len() as u32 - 1;
552 request.url_list = url_list;
553 request.integrity_metadata = self.integrity_metadata;
554 request.cryptographic_nonce_metadata = self.cryptographic_nonce_metadata;
555 request.parser_metadata = self.parser_metadata;
556 request.response_tainting = self.response_tainting;
557 request.crash = self.crash;
558 request.policy_container = self.policy_container;
559 request.insecure_requests_policy = self.insecure_requests_policy;
560 request.has_trustworthy_ancestor_origin = self.has_trustworthy_ancestor_origin;
561 request
562 }
563}
564
565#[derive(Clone, MallocSizeOf)]
568pub struct Request {
569 pub id: RequestId,
573 #[ignore_malloc_size_of = "Defined in hyper"]
575 pub method: Method,
576 pub local_urls_only: bool,
578 #[ignore_malloc_size_of = "Defined in hyper"]
580 pub headers: HeaderMap,
581 pub unsafe_request: bool,
583 pub body: Option<RequestBody>,
585 pub window: Window,
587 pub target_webview_id: Option<WebViewId>,
588 pub keep_alive: bool,
590 pub service_workers_mode: ServiceWorkersMode,
592 pub initiator: Initiator,
594 pub destination: Destination,
596 pub origin: Origin,
599 pub referrer: Referrer,
601 pub referrer_policy: ReferrerPolicy,
603 pub pipeline_id: Option<PipelineId>,
604 pub synchronous: bool,
606 pub mode: RequestMode,
608 pub use_cors_preflight: bool,
610 pub credentials_mode: CredentialsMode,
612 pub use_url_credentials: bool,
614 pub cache_mode: CacheMode,
616 pub redirect_mode: RedirectMode,
618 pub integrity_metadata: String,
620 pub cryptographic_nonce_metadata: String,
622 pub url_list: Vec<ServoUrl>,
626 pub redirect_count: u32,
628 pub response_tainting: ResponseTainting,
630 pub parser_metadata: ParserMetadata,
632 pub policy_container: RequestPolicyContainer,
634 pub insecure_requests_policy: InsecureRequestsPolicy,
636 pub has_trustworthy_ancestor_origin: bool,
637 pub https_state: HttpsState,
638 pub crash: Option<String>,
640}
641
642impl Request {
643 pub fn new(
644 id: RequestId,
645 url: ServoUrl,
646 origin: Option<Origin>,
647 referrer: Referrer,
648 pipeline_id: Option<PipelineId>,
649 webview_id: Option<WebViewId>,
650 https_state: HttpsState,
651 ) -> Request {
652 Request {
653 id,
654 method: Method::GET,
655 local_urls_only: false,
656 headers: HeaderMap::new(),
657 unsafe_request: false,
658 body: None,
659 window: Window::Client,
660 keep_alive: false,
661 service_workers_mode: ServiceWorkersMode::All,
662 initiator: Initiator::None,
663 destination: Destination::None,
664 origin: origin.unwrap_or(Origin::Client),
665 referrer,
666 referrer_policy: ReferrerPolicy::EmptyString,
667 pipeline_id,
668 target_webview_id: webview_id,
669 synchronous: false,
670 mode: RequestMode::NoCors,
671 use_cors_preflight: false,
672 credentials_mode: CredentialsMode::CredentialsSameOrigin,
673 use_url_credentials: false,
674 cache_mode: CacheMode::Default,
675 redirect_mode: RedirectMode::Follow,
676 integrity_metadata: String::new(),
677 cryptographic_nonce_metadata: String::new(),
678 url_list: vec![url],
679 parser_metadata: ParserMetadata::Default,
680 redirect_count: 0,
681 response_tainting: ResponseTainting::Basic,
682 policy_container: RequestPolicyContainer::Client,
683 insecure_requests_policy: InsecureRequestsPolicy::DoNotUpgrade,
684 has_trustworthy_ancestor_origin: false,
685 https_state,
686 crash: None,
687 }
688 }
689
690 pub fn url(&self) -> ServoUrl {
692 self.url_list.first().unwrap().clone()
693 }
694
695 pub fn current_url(&self) -> ServoUrl {
697 self.url_list.last().unwrap().clone()
698 }
699
700 pub fn current_url_mut(&mut self) -> &mut ServoUrl {
702 self.url_list.last_mut().unwrap()
703 }
704
705 pub fn is_navigation_request(&self) -> bool {
707 matches!(
708 self.destination,
709 Destination::Document |
710 Destination::Embed |
711 Destination::Frame |
712 Destination::IFrame |
713 Destination::Object
714 )
715 }
716
717 pub fn is_subresource_request(&self) -> bool {
719 matches!(
720 self.destination,
721 Destination::Audio |
722 Destination::Font |
723 Destination::Image |
724 Destination::Manifest |
725 Destination::Script |
726 Destination::Style |
727 Destination::Track |
728 Destination::Video |
729 Destination::Xslt |
730 Destination::None
731 )
732 }
733
734 pub fn timing_type(&self) -> ResourceTimingType {
735 if self.is_navigation_request() {
736 ResourceTimingType::Navigation
737 } else {
738 ResourceTimingType::Resource
739 }
740 }
741}
742
743impl Referrer {
744 pub fn to_url(&self) -> Option<&ServoUrl> {
745 match *self {
746 Referrer::NoReferrer => None,
747 Referrer::Client(ref url) => Some(url),
748 Referrer::ReferrerUrl(ref url) => Some(url),
749 }
750 }
751}
752
753fn is_cors_unsafe_request_header_byte(value: &u8) -> bool {
757 matches!(value,
758 0x00..=0x08 |
759 0x10..=0x19 |
760 0x22 |
761 0x28 |
762 0x29 |
763 0x3A |
764 0x3C |
765 0x3E |
766 0x3F |
767 0x40 |
768 0x5B |
769 0x5C |
770 0x5D |
771 0x7B |
772 0x7D |
773 0x7F
774 )
775}
776
777fn is_cors_safelisted_request_accept(value: &[u8]) -> bool {
780 !(value.iter().any(is_cors_unsafe_request_header_byte))
781}
782
783fn is_cors_safelisted_language(value: &[u8]) -> bool {
786 value.iter().all(|&x| {
787 matches!(x,
788 0x30..=0x39 |
789 0x41..=0x5A |
790 0x61..=0x7A |
791 0x20 |
792 0x2A |
793 0x2C |
794 0x2D |
795 0x2E |
796 0x3B |
797 0x3D
798 )
799 })
800}
801
802pub fn is_cors_safelisted_request_content_type(value: &[u8]) -> bool {
805 if value.iter().any(is_cors_unsafe_request_header_byte) {
807 return false;
808 }
809 let value_string = if let Ok(s) = std::str::from_utf8(value) {
811 s
812 } else {
813 return false;
814 };
815 let value_mime_result: Result<Mime, _> = value_string.parse();
816 match value_mime_result {
817 Err(_) => false, Ok(value_mime) => match (value_mime.type_(), value_mime.subtype()) {
819 (mime::APPLICATION, mime::WWW_FORM_URLENCODED) |
820 (mime::MULTIPART, mime::FORM_DATA) |
821 (mime::TEXT, mime::PLAIN) => true,
822 _ => false, },
824 }
825}
826
827pub fn is_cors_safelisted_request_header<N: AsRef<str>, V: AsRef<[u8]>>(
831 name: &N,
832 value: &V,
833) -> bool {
834 let name: &str = name.as_ref();
835 let value: &[u8] = value.as_ref();
836 if value.len() > 128 {
837 return false;
838 }
839 match name {
840 "accept" => is_cors_safelisted_request_accept(value),
841 "accept-language" | "content-language" => is_cors_safelisted_language(value),
842 "content-type" => is_cors_safelisted_request_content_type(value),
843 "range" => is_cors_safelisted_request_range(value),
844 _ => false,
845 }
846}
847
848pub fn is_cors_safelisted_request_range(value: &[u8]) -> bool {
849 if let Ok(value_str) = std::str::from_utf8(value) {
850 return validate_range_header(value_str);
851 }
852 false
853}
854
855fn validate_range_header(value: &str) -> bool {
856 let trimmed = value.trim();
857 if !trimmed.starts_with("bytes=") {
858 return false;
859 }
860
861 if let Some(range) = trimmed.strip_prefix("bytes=") {
862 let mut parts = range.split('-');
863 let start = parts.next();
864 let end = parts.next();
865
866 if let Some(start) = start {
867 if let Ok(start_num) = start.parse::<u64>() {
868 return match end {
869 Some(e) if !e.is_empty() => {
870 e.parse::<u64>().is_ok_and(|end_num| start_num <= end_num)
871 },
872 _ => true,
873 };
874 }
875 }
876 }
877 false
878}
879
880pub fn is_cors_safelisted_method(m: &Method) -> bool {
882 matches!(*m, Method::GET | Method::HEAD | Method::POST)
883}
884
885pub fn is_cors_non_wildcard_request_header_name(name: &HeaderName) -> bool {
887 name == AUTHORIZATION
888}
889
890pub fn get_cors_unsafe_header_names(headers: &HeaderMap) -> Vec<HeaderName> {
892 let mut unsafe_names: Vec<&HeaderName> = vec![];
894 let mut potentillay_unsafe_names: Vec<&HeaderName> = vec![];
896 let mut safelist_value_size = 0;
898
899 for (name, value) in headers.iter() {
901 if !is_cors_safelisted_request_header(&name, &value) {
902 unsafe_names.push(name);
903 } else {
904 potentillay_unsafe_names.push(name);
905 safelist_value_size += value.as_ref().len();
906 }
907 }
908
909 if safelist_value_size > 1024 {
911 unsafe_names.extend_from_slice(&potentillay_unsafe_names);
912 }
913
914 convert_header_names_to_sorted_lowercase_set(unsafe_names)
916}
917
918pub fn convert_header_names_to_sorted_lowercase_set(
920 header_names: Vec<&HeaderName>,
921) -> Vec<HeaderName> {
922 let mut ordered_set = header_names.to_vec();
925 ordered_set.sort_by(|a, b| a.as_str().partial_cmp(b.as_str()).unwrap());
926 ordered_set.dedup();
927 ordered_set.into_iter().cloned().collect()
928}
929
930pub fn create_request_body_with_content(content: &str) -> RequestBody {
931 let content_bytes = IpcSharedMemory::from_bytes(content.as_bytes());
932 let content_len = content_bytes.len();
933
934 let (chunk_request_sender, chunk_request_receiver) = ipc::channel().unwrap();
935 ROUTER.add_typed_route(
936 chunk_request_receiver,
937 Box::new(move |message| {
938 let request = message.unwrap();
939 if let BodyChunkRequest::Connect(sender) = request {
940 let _ = sender.send(BodyChunkResponse::Chunk(content_bytes.clone()));
941 let _ = sender.send(BodyChunkResponse::Done);
942 }
943 }),
944 );
945
946 RequestBody::new(chunk_request_sender, BodySource::Object, Some(content_len))
947}