1use std::sync::Arc;
3use std::sync::Mutex;
4use std::sync::mpsc;
5use std::sync::atomic::{AtomicBool, Ordering};
6use std::collections::HashMap;
7use std::time::Duration;
8
9use ::Method;
10use ::Status;
11
12use regex::Regex;
13
14pub struct Resource {
52 uri: String,
53 uri_regex: Regex,
54 params: Arc<Mutex<URIParameters>>,
55 status_code: Arc<Mutex<Status>>,
56 custom_status_code: Arc<Mutex<Option<String>>>,
57 headers: Arc<Mutex<HashMap<String, String>>>,
58 body: Arc<Mutex<Option<&'static str>>>,
59 body_builder: Arc<Mutex<Option<BodyBuilder>>>, method: Arc<Mutex<Method>>,
61 delay: Arc<Mutex<Option<Duration>>>,
62 request_count: Arc<Mutex<u32>>,
63 is_stream: Arc<AtomicBool>,
64 stream_listeners: Arc<Mutex<Vec<mpsc::Sender<String>>>>
65}
66
67struct URIParameters {
68 path: Vec<String>,
69 query: HashMap<String, String>
70}
71
72type BodyBuilder = Box<dyn Fn(RequestParameters) -> String + Send>;
73
74impl Resource {
75 pub(crate) fn new(uri: &str) -> Resource {
76 let (uri_regex, params) = create_uri_regex(uri);
77
78 Resource {
79 uri: String::from(uri),
80 uri_regex,
81 params: Arc::new(Mutex::new(params)),
82 status_code: Arc::new(Mutex::new(Status::OK)),
83 custom_status_code: Arc::new(Mutex::new(None)),
84 headers: Arc::new(Mutex::new(HashMap::new())),
85 body: Arc::new(Mutex::new(None)),
86 body_builder: Arc::new(Mutex::new(None)),
87 method: Arc::new(Mutex::new(Method::GET)),
88 delay: Arc::new(Mutex::new(None)),
89 request_count: Arc::new(Mutex::new(0)),
90 is_stream: Arc::new(AtomicBool::new(false)),
91 stream_listeners: Arc::new(Mutex::new(vec!()))
92 }
93 }
94
95 pub fn status(&self, status_code: Status) -> &Resource {
108 if let Ok(mut status) = self.status_code.lock() {
109 *status = status_code;
110 }
111
112 if let Ok(mut custom_status) = self.custom_status_code.lock() {
113 *custom_status = None;
114 }
115
116 self
117 }
118
119 fn get_status_description(&self) -> String {
120 match *(self.custom_status_code.lock().unwrap()) {
121 Some(ref custom_status) => custom_status.clone(),
122 None => self.status_code.lock().unwrap().description().to_string()
123 }
124 }
125
126 pub fn custom_status(&self, status_code: u16, description: &str) -> &Resource {
138 if let Ok(mut status) = self.custom_status_code.lock() {
139 *status = Some(format!("{} {}", status_code, description));
140 }
141 self
142 }
143
144 pub fn header(&self, header_name: &str, header_value: &str) -> &Resource {
158 let mut headers = self.headers.lock().unwrap();
159 headers.insert(String::from(header_name), String::from(header_value));
160 self
161 }
162
163 fn get_headers(&self) -> String {
164 let headers = self.headers.lock().unwrap();
165 headers.iter().fold(String::new(), | headers, (name, value) | {
166 headers + &format!("{}: {}\r\n", name, value)
167 })
168 }
169
170 pub fn query(&self, name: &str, value: &str) -> &Resource {
188 let mut params = self.params.lock().unwrap();
189 params.query.insert(String::from(name), String::from(value));
190 self
191 }
192
193 pub fn body(&self, content: &'static str) -> &Resource {
214 if self.body_builder.lock().unwrap().is_some() {
215 panic!("You can't define 'body' when 'body_fn' is already defined");
216 }
217
218 if let Ok(mut body) = self.body.lock() {
219 *body = Some(content);
220 }
221
222 self
223 }
224
225 pub fn body_fn(&self, builder: impl Fn(RequestParameters) -> String + Send + 'static) -> &Resource {
246 if self.body.lock().unwrap().is_some() {
247 panic!("You can't define 'body_fn' when 'body' is already defined");
248 }
249
250 if let Ok(mut body_builder) = self.body_builder.lock() {
251 *body_builder = Some(Box::new(builder));
252 }
253
254 self
255 }
256
257 pub fn method(&self, method: Method) -> &Resource {
272 if let Ok(mut m) = self.method.lock() {
273 *m = method;
274 }
275
276 self
277 }
278
279 pub(crate) fn get_method(&self) -> Method {
280 (*self.method.lock().unwrap()).clone()
281 }
282
283 pub fn delay(&self, delay: Duration) -> &Resource {
293 if let Ok(mut d) = self.delay.lock() {
294 *d = Some(delay);
295 }
296
297 self
298 }
299
300 pub(crate) fn get_delay(&self) -> Option<Duration> {
301 *self.delay.lock().unwrap()
302 }
303
304 pub fn stream(&self) -> &Resource {
325 self.is_stream.store(true, Ordering::Relaxed);
326
327 self
328 }
329
330 pub(crate) fn is_stream(&self) -> bool {
331 self.is_stream.load(Ordering::Relaxed)
332 }
333
334 fn create_body(&self, uri: &str) -> String {
335 let params = self.extract_params_from_uri(uri);
336
337 if let Some(body_builder) = &*self.body_builder.lock().unwrap() {
338 return body_builder(params);
339 }
340
341 match *self.body.lock().unwrap() {
342 Some(body) => {
343 let mut body = body.to_string();
344
345 for (name, value) in ¶ms.path {
346 let key = format!("{{path.{}}}", name);
347 body = body.replace(&key, value);
348 }
349
350 for (name, value) in ¶ms.query {
351 let key = format!("{{query.{}}}", name);
352 body = body.replace(&key, value);
353 }
354
355 body
356 },
357 None => {
358 String::from("")
359 }
360 }
361 }
362
363 fn extract_params_from_uri(&self, uri: &str) -> RequestParameters {
364 RequestParameters { path: self.extra_path_params(uri), query: extract_query_params(uri) }
365 }
366
367 fn extra_path_params(&self, uri: &str) -> HashMap<String, String> {
368 let mut params = HashMap::new();
369
370 if let Some(values) = self.uri_regex.captures(uri) {
371 for param in &self.params.lock().unwrap().path {
372 if let Some(value) = values.name(param) {
373 params.insert(String::from(param), String::from(value.as_str()));
374 }
375 }
376 }
377
378 params
379 }
380
381 pub(crate) fn build_response(&self, uri: &str) -> String {
382 format!("HTTP/1.1 {}\r\n{}\r\n{}",
383 self.get_status_description(),
384 self.get_headers(),
385 self.create_body(uri)
386 )
387 }
388
389 pub(crate) fn increment_request_count(&self) {
390 *(self.request_count.lock().unwrap()) += 1;
391 }
392
393 pub fn send(&self, data: &str) -> &Resource {
410 if let Ok(mut listeners) = self.stream_listeners.lock() {
411 let mut invalid_listeners = vec!();
412 for (i, listener) in listeners.iter().enumerate() {
413 if listener.send(String::from(data)).is_err() {
414 invalid_listeners.push(i);
415 }
416 }
417
418 for i in invalid_listeners.iter() {
419 listeners.remove(*i);
420 }
421 }
422
423 self
424 }
425
426 pub fn send_line(&self, data: &str) -> &Resource {
444 self.send(&format!("{}\n", data))
445 }
446
447 pub fn close_open_connections(&self) {
462 if let Ok(mut listeners) = self.stream_listeners.lock() {
463 listeners.clear();
464 }
465 }
466
467 pub fn open_connections_count(&self) -> usize {
483 let listeners = self.stream_listeners.lock().unwrap();
484 listeners.len()
485 }
486
487 pub fn stream_receiver(&self) -> mpsc::Receiver<String> {
504 let (tx, rx) = mpsc::channel();
505
506 if let Ok(mut listeners) = self.stream_listeners.lock() {
507 listeners.push(tx);
508 }
509 rx
510 }
511
512 pub fn request_count(&self) -> u32 {
520 *(self.request_count.lock().unwrap())
521 }
522
523 pub(crate) fn matches_uri(&self, uri: &str) -> bool {
524 self.uri_regex.is_match(uri) && self.matches_query_parameters(uri)
525 }
526
527 fn matches_query_parameters(&self, uri: &str) -> bool {
528 let query_params = extract_query_params(uri);
529
530 for (expected_key, expected_value) in &self.params.lock().unwrap().query {
531 if let Some(value) = query_params.get(expected_key) {
532 if expected_value != value && expected_value != "*" {
533 return false;
534 }
535 } else {
536 return false;
537 }
538 }
539
540 true
541 }
542}
543
544impl Clone for Resource {
545 fn clone(&self) -> Self {
549 Resource {
550 uri: self.uri.clone(),
551 uri_regex: self.uri_regex.clone(),
552 params: self.params.clone(),
553 status_code: self.status_code.clone(),
554 custom_status_code: self.custom_status_code.clone(),
555 headers: self.headers.clone(),
556 body: self.body.clone(),
557 body_builder: self.body_builder.clone(),
558 method: self.method.clone(),
559 delay: self.delay.clone(),
560 request_count: self.request_count.clone(),
561 is_stream: self.is_stream.clone(),
562 stream_listeners: self.stream_listeners.clone()
563 }
564 }
565}
566
567pub struct RequestParameters {
568 pub path: HashMap<String, String>,
569 pub query: HashMap<String, String>
570}
571
572
573fn create_uri_regex(uri: &str) -> (Regex, URIParameters) {
574 let re = Regex::new(r"\{(?P<p>([A-z|0-9|_])+)\}").unwrap();
575 let query_regex = Regex::new(r"\?.*").unwrap();
576
577 let params: Vec<String> = re.captures_iter(uri).filter_map(|cap| {
578 match cap.name("p") {
579 Some(p) => Some(String::from(p.as_str())),
580 None => None
581 }
582 }).collect();
583
584 let query_params = extract_query_params(uri);
585
586 let pattern = query_regex.replace(uri, "");
587 let pattern = re.replace_all(&pattern, r"(?P<$p>[^//|/?]+)");
588
589 (Regex::new(&pattern).unwrap(), URIParameters { path: params, query: query_params})
590}
591
592fn extract_query_params(uri: &str) -> HashMap<String, String> {
593 let query_regex = Regex::new(r"((?P<qk>[^&]+)=(?P<qv>[^&]+))*").unwrap();
594 let path_regex = Regex::new(r".*\?").unwrap();
595 let only_query_parameters = path_regex.replace(uri, "");
596
597 query_regex.captures_iter(&only_query_parameters).filter_map(|cap| {
598 if let Some(query_key) = cap.name("qk") {
599 let query_value = match cap.name("qv") {
600 Some(v) => String::from(v.as_str()),
601 None => String::from("")
602 };
603 return Some((String::from(query_key.as_str()), query_value));
604 }
605 None
606 }).collect()
607}
608
609#[cfg(test)]
610mod tests {
611 use super::*;
612 use std::thread;
613
614 #[test]
615 fn should_convert_to_response_string() {
616 let resource = Resource::new("/");
617 resource.status(Status::NotFound);
618
619 assert_eq!(resource.build_response("/"), "HTTP/1.1 404 Not Found\r\n\r\n");
620 }
621
622 #[test]
623 fn should_convert_to_response_with_body() {
624 let resource = Resource::new("/");
625 resource.status(Status::Accepted).body("hello!");
626
627 assert_eq!(resource.build_response("/"), "HTTP/1.1 202 Accepted\r\n\r\nhello!");
628 }
629
630 #[test]
631 fn should_allows_custom_status() {
632 let resource = Resource::new("/");
633 resource.custom_status(666, "The Number Of The Beast").body("hello!");
634
635 assert_eq!(resource.build_response("/"), "HTTP/1.1 666 The Number Of The Beast\r\n\r\nhello!");
636 }
637
638 #[test]
639 fn should_overwrite_custom_status_with_status() {
640 let resource = Resource::new("/");
641 resource.custom_status(666, "The Number Of The Beast").status(Status::Forbidden).body("hello!");
642
643 assert_eq!(resource.build_response("/"), "HTTP/1.1 403 Forbidden\r\n\r\nhello!");
644 }
645
646 #[test]
647 fn should_add_headers() {
648 let resource = Resource::new("/");
649 resource
650 .header("Content-Type", "application/json")
651 .body("hello!");
652
653 assert_eq!(resource.build_response("/"), "HTTP/1.1 200 Ok\r\nContent-Type: application/json\r\n\r\nhello!");
654 }
655
656 #[test]
657 fn should_append_headers() {
658 let resource = Resource::new("/");
659 resource
660 .header("Content-Type", "application/json")
661 .header("Connection", "Keep-Alive")
662 .body("hello!");
663
664 let response = resource.build_response("/");
665
666 assert!(response.contains("Content-Type: application/json\r\n"));
667 assert!(response.contains("Connection: Keep-Alive\r\n"));
668 }
669
670 #[test]
671 fn should_increment_request_count() {
672 let resource = Resource::new("/");
673 resource.body("hello!");
674
675 resource.increment_request_count();
676 resource.increment_request_count();
677 resource.increment_request_count();
678
679 assert_eq!(resource.request_count(), 3);
680 }
681
682 #[test]
683 fn clones_should_share_same_state() {
684 let resource = Resource::new("/");
685 let dolly = resource.clone();
686
687 resource.increment_request_count();
688 dolly.increment_request_count();
689
690 assert_eq!(resource.request_count(), dolly.request_count());
691 assert_eq!(resource.request_count(), 2);
692 }
693
694 #[test]
695 fn should_set_as_stream() {
696 let resource = Resource::new("/");
697
698 resource.stream().status(Status::Accepted);
699
700 assert!(resource.is_stream());
701 }
702
703
704 #[test]
705 fn should_notify_data() {
706 let resource = Resource::new("/");
707
708 let receiver = resource.stream_receiver();
709 resource.send("some data").send("some data");
710
711 assert_eq!(receiver.recv().unwrap(), "some data");
712 assert_eq!(receiver.recv().unwrap(), "some data");
713 }
714
715 #[test]
716 fn should_close_connections() {
717 let resource = Resource::new("/");
718 let res = resource.clone();
719 let receiver = resource.stream_receiver();
720
721 thread::spawn(move || {
722 res.send("some data");
723 res.send("some data");
724 res.close_open_connections();
725 });
726
727 let mut string = String::new();
728
729 for data in receiver.iter() {
730 string = string + &data;
731 }
732
733 assert_eq!(string, "some datasome data");
734 }
735
736 #[test]
737 fn should_return_number_of_connecteds_users() {
738 let resource = Resource::new("/");
739 let _receiver = resource.stream_receiver();
740 let _receiver_2 = resource.stream_receiver();
741
742 assert_eq!(resource.open_connections_count(), 2);
743 }
744
745
746 #[test]
747 fn should_decrease_count_when_receiver_dropped() {
748 let resource = Resource::new("/");
749 resource.stream_receiver();
750
751 resource.send("some data");
752
753 assert_eq!(resource.open_connections_count(), 0);
754 }
755
756 #[test]
757 fn should_send_data_with_line_break() {
758 let resource = Resource::new("/");
759
760 let receiver = resource.stream_receiver();
761 resource.send_line("some data").send_line("again");
762
763 assert_eq!(receiver.recv().unwrap(), "some data\n");
764 assert_eq!(receiver.recv().unwrap(), "again\n");
765 }
766
767 #[test]
768 fn should_set_delay() {
769 let resource = Resource::new("/");
770 resource.delay(Duration::from_millis(200));
771
772 assert_eq!(resource.get_delay(), Some(Duration::from_millis(200)));
773 }
774
775 #[test]
776 fn should_match_uri() {
777 let resource = Resource::new("/some-endpoint");
778 assert!(resource.matches_uri("/some-endpoint"));
779 }
780
781 #[test]
782 fn should_not_match_uri_when_uri_does_not_match() {
783 let resource = Resource::new("/some-endpoint");
784 assert!(!resource.matches_uri("/some-other-endpoint"));
785 }
786
787 #[test]
788 fn should_match_uri_with_path_params() {
789 let resource = Resource::new("/endpoint/{param1}/some/{param2}");
790 assert!(resource.matches_uri("/endpoint/123/some/abc"));
791 assert!(resource.matches_uri("/endpoint/123-345/some/abc"));
792 }
793
794 #[test]
795 fn should_not_match_uri_with_path_params_when_uri_does_not_match() {
796 let resource = Resource::new("/endpoint/{param1}/some/{param2}");
797 assert!(!resource.matches_uri("/endpoint/123/some/"));
798 }
799
800 #[test]
801 fn should_match_uri_with_query_params() {
802 let resource = Resource::new("/endpoint?userId=123");
803 assert!(resource.matches_uri("/endpoint?userId=123"));
804 }
805
806 #[test]
807 fn should_not_match_uri_with_wrong_query_parameter() {
808 let resource = Resource::new("/endpoint?userId=123");
809 assert!(!resource.matches_uri("/endpoint?userId=abc"));
810 }
811
812 #[test]
813 fn should_match_uri_with_multiple_query_params() {
814 let resource = Resource::new("/endpoint?userId=123&hello=abc");
815 assert!(resource.matches_uri("/endpoint?userId=123&hello=abc"));
816 }
817
818 #[test]
819 fn should_match_uri_with_wildcard_query_params() {
820 let resource = Resource::new("/endpoint?userId=123&collectionId=*");
821 assert!(resource.matches_uri("/endpoint?userId=123&collectionId=banana"));
822 }
823
824 #[test]
825 fn should_match_uri_with_query_params_in_different_order() {
826 let resource = Resource::new("/endpoint?hello=abc&userId=123");
827 assert!(resource.matches_uri("/endpoint?userId=123&hello=abc"));
828 }
829
830 #[test]
831 fn should_not_match_uri_when_one_query_param_is_wrong() {
832 let resource = Resource::new("/endpoint?userId=123&hello=abc");
833 assert!(!resource.matches_uri("/endpoint?userId=123&hello=bbc"));
834 }
835
836 #[test]
837 fn should_match_uri_with_query_params_defined_through_method() {
838 let resource = Resource::new("/endpoint");
839 resource.query("hello", "abc").query("userId", "123");
840 assert!(resource.matches_uri("/endpoint?userId=123&hello=abc"));
841 }
842
843 #[test]
844 fn should_match_uri_with_wildcard_query_params_defined_through_method() {
845 let resource = Resource::new("/endpoint");
846 resource.query("hello", "*");
847 assert!(resource.matches_uri("/endpoint?hello=1234"));
848 }
849
850 #[test]
851 fn should_build_response() {
852 let resource = Resource::new("/");
853 resource.status(Status::NotFound);
854
855 assert_eq!(resource.build_response("/"), "HTTP/1.1 404 Not Found\r\n\r\n");
856 }
857
858 #[test]
859 fn should_build_response_with_body() {
860 let resource = Resource::new("/");
861 resource.status(Status::Accepted).body("hello!");
862
863 assert_eq!(resource.build_response("/"), "HTTP/1.1 202 Accepted\r\n\r\nhello!");
864 }
865
866 #[test]
867 fn should_build_response_with_path_parameters() {
868 let resource = Resource::new("/endpoint/{param1}/{param2}");
869 resource.status(Status::Accepted).body("Hello: {path.param2} {path.param1}");
870
871 assert_eq!(resource.build_response("/endpoint/123/abc"), "HTTP/1.1 202 Accepted\r\n\r\nHello: abc 123");
872 }
873
874 #[test]
875 fn should_build_response_with_query_parameters() {
876 let resource = Resource::new("/endpoint/{param1}?param2=111");
877 resource.status(Status::Accepted).body("Hello: {query.param2} {path.param1}");
878
879 assert_eq!(resource.build_response("/endpoint/123?param2=111"), "HTTP/1.1 202 Accepted\r\n\r\nHello: 111 123");
880 }
881
882 #[test]
883 fn should_build_response_with_wildcard_query_parameters() {
884 let resource = Resource::new("/endpoint/{param1}?param2=111¶m3=*");
885 resource.status(Status::Accepted).body("Hello: {query.param3}");
886
887 assert_eq!(resource.build_response("/endpoint/123?param2=111¶m3=banana"), "HTTP/1.1 202 Accepted\r\n\r\nHello: banana");
888 }
889
890 #[test]
891 fn should_build_response_using_body_fn() {
892 let resource = Resource::new("/endpoint/{param1}/{param2}");
893 resource.status(Status::Accepted).body_fn(|params| {
894 format!("Hello: {} {}", params.path.get("param2").unwrap(), params.path.get("param1").unwrap())
895 });
896
897 assert_eq!(resource.build_response("/endpoint/123/abc"), "HTTP/1.1 202 Accepted\r\n\r\nHello: abc 123");
898 }
899
900 #[test]
901 #[should_panic(expected = "You can't define 'body_fn' when 'body' is already defined")]
902 fn should_fail_when_trying_to_define_body_fn_after_defining_body() {
903 let resource = Resource::new("/endpoint/{param1}/{param2}");
904 resource.body("some body");
905 resource.body_fn(|_params| String::from(""));
906 }
907
908 #[test]
909 #[should_panic(expected = "You can't define 'body' when 'body_fn' is already defined")]
910 fn should_fail_when_trying_to_define_body_after_defining_body_fn() {
911 let resource = Resource::new("/endpoint/{param1}/{param2}");
912 resource.body_fn(|_params| String::from(""));
913 resource.body("some body");
914 }
915}