1use router::{Param, Params};
2use std::mem;
4use std::str;
5
6fn min(a: usize, b: usize) -> usize {
7 if a <= b {
8 return a;
9 }
10 b
11}
12
13fn count_params(path: &[u8]) -> u8 {
14 let mut n = 0;
15 for &c in path {
16 if c != b':' && c != b'*' {
17 continue;
18 }
19 n += 1;
20 }
21 if n > 255 {
22 return 255;
23 }
24 n as u8
25}
26
27#[derive(PartialEq, Clone, Debug, PartialOrd)]
28pub enum NodeType {
29 Static,
30 Root,
31 Param,
32 CatchAll,
33}
34
35#[derive(Debug, Clone)]
36pub struct Node<T> {
37 path: Vec<u8>,
38 wild_child: bool,
39 n_type: NodeType,
40 max_params: u8,
41 indices: Vec<u8>,
42 children: Vec<Box<Node<T>>>,
43 handle: Option<T>,
44 priority: u32,
45}
46
47impl<T> Node<T> {
48 pub fn new() -> Node<T> {
49 Node {
50 path: Vec::new(),
51 wild_child: false,
52 n_type: NodeType::Static,
53 max_params: 0,
54 indices: Vec::new(),
55 children: Vec::new(),
56 handle: None,
57 priority: 0,
58 }
59 }
60
61 fn increment_child_prio(&mut self, pos: usize) -> usize {
63 self.children[pos].priority += 1;
64 let prio = self.children[pos].priority;
65 let mut new_pos = pos;
67
68 while new_pos > 0 && self.children[new_pos - 1].priority < prio {
69 self.children.swap(new_pos - 1, new_pos);
71 new_pos -= 1;
72 }
73
74 if new_pos != pos {
76 self.indices = [
77 &self.indices[..new_pos], &self.indices[pos..pos + 1], &self.indices[new_pos..pos], &self.indices[pos + 1..], ].concat();
82 }
83
84 new_pos
85 }
86
87 pub fn add_route(&mut self, path: &str, handle: T) {
90 let full_path = path.clone();
91 let path = path.as_ref();
92 self.priority += 1;
93 let num_params = count_params(path);
94
95 if self.path.len() > 0 || self.children.len() > 0 {
97 self.add_route_loop(num_params, path, full_path, handle);
98 } else {
99 self.insert_child(num_params, path, full_path, handle);
101 self.n_type = NodeType::Root;
102 }
103 }
104
105 fn add_route_loop(&mut self, num_params: u8, mut path: &[u8], full_path: &str, handle: T) {
106 if num_params > self.max_params {
108 self.max_params = num_params;
109 }
110
111 let mut i = 0;
115 let max = min(path.len(), self.path.len());
116
117 while i < max && path[i] == self.path[i] {
118 i += 1;
119 }
120
121 if i < self.path.len() {
123 let mut child = Node {
124 path: self.path[i..].to_vec(),
125 wild_child: self.wild_child,
126 n_type: NodeType::Static,
127 indices: self.indices.clone(),
128 children: Vec::new(),
129 handle: self.handle.take(),
130 priority: self.priority - 1,
131
132 max_params: 0,
133 };
134
135 mem::swap(&mut self.children, &mut child.children);
136
137 for c in &child.children {
139 if c.max_params > child.max_params {
140 child.max_params = c.max_params;
141 }
142 }
143
144 self.children = vec![Box::new(child)];
145 self.indices = vec![self.path[i]];
146 self.path = path[..i].to_vec();
147 self.wild_child = false;
148 }
149
150 if i < path.len() {
152 path = &path[i..];
153
154 if self.wild_child {
155 return self.children[0].is_wild_child(num_params, path, full_path, handle);
157 }
158
159 let c = path[0];
160
161 if self.n_type == NodeType::Param && c == b'/' && self.children.len() == 1 {
163 self.children[0].priority += 1;
164 return self.children[0].add_route_loop(num_params, path, full_path, handle);
165 }
166
167 for mut i in 0..self.indices.len() {
169 if c == self.indices[i] {
170 i = self.increment_child_prio(i);
171 return self.children[i].add_route_loop(num_params, path, full_path, handle);
172 }
173 }
174
175 if c != b':' && c != b'*' {
177 self.indices.push(c);
178
179 let len = self.indices.len();
180
181 let child: Box<Node<T>> = Box::new(Node {
182 path: Vec::new(),
183
184 wild_child: false,
185
186 n_type: NodeType::Static,
187
188 max_params: num_params,
189
190 indices: Vec::new(),
191
192 children: Vec::new(),
193
194 handle: None,
195
196 priority: 0,
197 });
198
199 self.children.push(child);
200
201 let i = self.increment_child_prio(len - 1);
202
203 return self.children[i].insert_child(num_params, path, full_path, handle);
204 }
205
206 return self.insert_child(num_params, path, full_path, handle);
207 } else if i == path.len() {
208 if self.handle.is_some() {
210 panic!("a handle is already registered for path '{}'", full_path);
211 }
212
213 self.handle = Some(handle);
214 }
215
216 return;
217 }
218
219 fn is_wild_child(&mut self, mut num_params: u8, path: &[u8], full_path: &str, handle: T) {
220 self.priority += 1;
221
222 if num_params > self.max_params {
225 self.max_params = num_params;
226 }
227
228 num_params -= 1;
229
230 if path.len() >= self.path.len()
233 && self.path == &path[..self.path.len()]
234 && (self.path.len() >= path.len() || path[self.path.len()] == b'/')
236 {
237 self.add_route_loop(num_params, path, full_path, handle);
238 } else {
239 let path_seg = if self.n_type == NodeType::CatchAll {
241 str::from_utf8(path).unwrap()
242 } else {
243 str::from_utf8(path)
244 .unwrap()
245 .splitn(2, '/')
246 .into_iter()
247 .next()
248 .unwrap()
249 };
250
251 let prefix = [
252 &full_path[..full_path.find(path_seg).unwrap()],
253 str::from_utf8(&self.path).unwrap(),
254 ].concat();
255
256 panic!("'{}' in new path '{}' conflicts with existing wildcard '{}' in existing prefix '{}'", path_seg, full_path, str::from_utf8(&self.path).unwrap(), prefix);
257 }
258 }
259
260 fn insert_child(&mut self, num_params: u8, path: &[u8], full_path: &str, handle: T) {
261 self.insert_child_loop(0, 0, num_params, path, full_path, handle);
262 }
263
264 fn insert_child_loop(
265 &mut self,
266 mut offset: usize,
267 mut i: usize,
268 mut num_params: u8,
269 path: &[u8],
270 full_path: &str,
271 handle: T,
272 ) {
273 if num_params > 0 {
274 let max = path.len();
275 let c = path[i];
276
277 if c != b':' && c != b'*' {
279 return self.insert_child_loop(offset, i + 1, num_params, path, full_path, handle);
280 }
281
282 let mut end = i + 1;
284 while end < max && path[end] != b'/' {
285 match path[end] {
286 b':' | b'*' => panic!(
288 "only one wildcard per path segment is allowed, has: '{}' in path '{}'",
289 str::from_utf8(&path[i..]).unwrap(),
290 full_path
291 ),
292 _ => end += 1,
293 }
294 }
295
296 if self.children.len() > 0 {
304 panic!(
305 "wildcard route '{}' conflicts with existing children in path '{}'",
306 str::from_utf8(&path[i..end]).unwrap(),
307 full_path
308 )
309 }
310
311 if end - i < 2 {
313 panic!(
314 "wildcards must be named with a non-empty name in path '{}'",
315 full_path
316 );
317 }
318
319 if c == b':' {
320 if i > 0 {
323 self.path = path[offset..i].to_vec();
324 offset = i;
325 }
326
327 let child = Box::new(Node {
328 path: Vec::new(),
329 wild_child: false,
330 n_type: NodeType::Param,
331 max_params: num_params,
332 indices: Vec::new(),
333 children: Vec::new(),
334 handle: None,
335 priority: 0,
336 });
337
338 self.children = vec![child];
339 self.wild_child = true;
340
341 self.children[0].priority += 1;
342 num_params -= 1;
343
344 if end < max {
345 self.children[0].path = path[offset..end].to_vec();
346 offset = end;
347
348 let child: Box<Node<T>> = Box::new(Node {
349 path: Vec::new(),
350 wild_child: false,
351 n_type: NodeType::Static,
352 max_params: num_params,
353 indices: Vec::new(),
354 children: Vec::new(),
355 handle: None,
356 priority: 1,
357 });
358
359 self.children[0].children.push(child);
360 self.children[0].children[0].insert_child_loop(
361 offset,
362 i + 1,
363 num_params,
364 path,
365 full_path,
366 handle,
367 );
368 } else {
369 self.children[0].insert_child_loop(
370 offset,
371 i + 1,
372 num_params,
373 path,
374 full_path,
375 handle,
376 );
377 }
378 } else {
379 if end != max || num_params > 1 {
381 panic!(
382 "catch-all routes are only allowed at the end of the path in path '{}'",
383 full_path
384 );
385 }
386
387 if self.path.len() > 0 && self.path[self.path.len() - 1] == b'/' {
388 panic!(
389 "catch-all conflicts with existing handle for the path segment root in path '{}'",
390 full_path
391 );
392 }
393
394 i -= 1;
396 if path[i] != b'/' {
397 panic!("no / before catch-all in path '{}'", full_path);
398 }
399
400 self.path = path[offset..i].to_vec();
401
402 let child = Box::new(Node {
404 path: Vec::new(),
405 wild_child: true,
406 n_type: NodeType::CatchAll,
407 max_params: 1,
408 indices: Vec::new(),
409 children: Vec::new(),
410 handle: None,
411 priority: 0,
412 });
413
414 self.children = vec![child];
415
416 self.indices = vec![path[i]];
417
418 self.children[0].priority += 1;
419
420 let child: Box<Node<T>> = Box::new(Node {
422 path: path[i..].to_vec(),
423 wild_child: false,
424 n_type: NodeType::CatchAll,
425 max_params: 1,
426 indices: Vec::new(),
427 children: Vec::new(),
428 handle: Some(handle),
429 priority: 1,
430 });
431
432 self.children[0].children.push(child);
433
434 return;
435 }
436 } else {
437 self.path = path[offset..].to_vec();
439 self.handle = Some(handle);
440 }
441 }
442
443 pub fn get_value(&self, path: &str) -> (Option<&T>, Params, bool) {
449 self.get_value_loop(path.as_ref(), Params::new())
451 }
452
453 fn get_value_loop(&self, mut path: &[u8], p: Params) -> (Option<&T>, Params, bool) {
455 if path.len() > self.path.len() {
456 if self.path == &path[..self.path.len()] {
457 path = &path[self.path.len()..];
458 if !self.wild_child {
462 let c = path[0];
463 for i in 0..self.indices.len() {
464 if c == self.indices[i] {
465 return self.children[i].get_value_loop(path, p);
466 }
467 }
468 let tsr = path == [b'/'] && self.handle.is_some();
472 return (None, p, tsr);
473 }
474
475 return self.children[0].handle_wildcard_child(path, p);
477 }
478 } else if self.path == path {
479 if self.handle.is_some() {
482 return (self.handle.as_ref(), p, false);
483 }
484
485 if path == [b'/'] && self.wild_child && self.n_type != NodeType::Root {
486 return (self.handle.as_ref(), p, true);
488 }
489
490 for i in 0..self.indices.len() {
493 if self.indices[i] == b'/' {
494 let tsr = (self.path.len() == 1 && self.children[i].handle.is_some())
495 || (self.children[i].n_type == NodeType::CatchAll
496 && self.children[i].children[0].handle.is_some());
497 return (self.handle.as_ref(), p, tsr);
498 }
499 }
500
501 return (self.handle.as_ref(), p, false);
502 }
503
504 let tsr = (path == [b'/'])
507 || (self.path.len() == path.len() + 1
508 && self.path[path.len()] == b'/'
509 && path == &self.path[..self.path.len() - 1]
510 && self.handle.is_some());
511
512 return (None, p, tsr);
513 }
514
515 fn handle_wildcard_child(&self, mut path: &[u8], mut p: Params) -> (Option<&T>, Params, bool) {
516 match self.n_type {
517 NodeType::Param => {
518 let mut end = 0;
520 while end < path.len() && path[end] != b'/' {
521 end += 1;
522 }
523
524 if p.is_empty() {
526 p = Params(Vec::with_capacity(self.max_params as usize));
528 }
529
530 p.push(Param {
531 key: String::from_utf8(self.path[1..].to_vec()).unwrap(),
532 value: String::from_utf8(path[..end].to_vec()).unwrap(),
533 });
534
535 if end < path.len() {
537 if self.children.len() > 0 {
538 path = &path[end..];
539
540 return self.children[0].get_value_loop(path, p);
541 }
542
543 let tsr = path.len() == end + 1;
545 return (None, p, tsr);
546 }
547
548 if self.handle.is_some() {
549 return (self.handle.as_ref(), p, false);
550 } else if self.children.len() == 1 {
551 let tsr = self.children[0].path == &[b'/'] && self.children[0].handle.is_some();
554 return (None, p, tsr);
555 }
556
557 return (None, p, false);
558 }
559 NodeType::CatchAll => {
560 if p.is_empty() {
562 p = Params(Vec::with_capacity(self.max_params as usize));
564 }
565
566 p.push(Param {
567 key: String::from_utf8(self.path[2..].to_vec()).unwrap(),
568 value: String::from_utf8(path.to_vec()).unwrap(),
569 });
570
571 return (self.handle.as_ref(), p, false);
572 }
573 _ => panic!("invalid node type"),
574 }
575 }
576
577 pub fn find_case_insensitive_path(
582 &self,
583 path: &str,
584 fix_trailing_slash: bool,
585 ) -> (String, bool) {
586 let mut ci_path = Vec::with_capacity(path.len() + 1);
587 let found = self.find_case_insensitive_path_rec(
588 path.as_bytes(),
589 path.to_ascii_lowercase().as_bytes(),
590 &mut ci_path,
591 [0; 4],
592 fix_trailing_slash,
593 );
594 (String::from_utf8(ci_path).unwrap(), found)
595 }
596
597 fn find_case_insensitive_path_rec(
599 &self,
600 mut path: &[u8],
601 mut lo_path: &[u8],
602 ci_path: &mut Vec<u8>,
603 mut rb: [u8; 4],
604 fix_trailing_slash: bool,
605 ) -> bool {
606 let lo_n_path: Vec<u8> = self.path.iter().map(|u| u.to_ascii_lowercase()).collect();
610
611 if lo_path.len() >= lo_n_path.len()
612 && (lo_n_path.len() == 0 || lo_path[1..lo_n_path.len()] == lo_n_path[1..])
613 {
614 ci_path.append(&mut self.path.clone());
616
617 path = &path[self.path.len()..];
618
619 if path.len() > 0 {
620 let lo_old = lo_path.clone();
621 lo_path = &lo_path[lo_n_path.len()..];
622
623 if !self.wild_child {
627 rb = shift_n_rune_bytes(rb, lo_n_path.len());
629
630 if rb[0] != 0 {
631 for i in 0..self.indices.len() {
633 if self.indices[i] == rb[0] {
634 return self.children[i].find_case_insensitive_path_rec(
636 path,
637 lo_path,
638 ci_path,
639 rb,
640 fix_trailing_slash,
641 );
642 }
643 }
644 } else {
645 let mut rv = 0 as char;
647
648 let mut off = 0;
652 for j in 0..min(lo_n_path.len(), 3) {
654 let i = lo_n_path.len() - j;
655 if rune_start(lo_old[i]) {
656 rv = str::from_utf8(&lo_old[i..])
658 .unwrap()
659 .chars()
660 .next()
661 .unwrap();
662 off = j;
663 break;
664 }
665 }
666 rv.encode_utf8(&mut rb);
669
670 rb = shift_n_rune_bytes(rb, off);
672 for i in 0..self.indices.len() {
674 if self.indices[i] == rb[0] {
676 let found = self.children[i].find_case_insensitive_path_rec(
680 path,
681 lo_path,
682 ci_path,
683 rb,
684 fix_trailing_slash,
685 );
686
687 if found {
688 return true;
690 }
691 if ci_path.len() > self.children[i].path.len() {
692 let prev_len = ci_path.len() - self.children[i].path.len();
693 ci_path.truncate(prev_len);
694 }
695
696 break;
697 }
698 }
699
700 let up = rv.to_ascii_uppercase();
702 if up != rv {
703 up.encode_utf8(&mut rb);
704 rb = shift_n_rune_bytes(rb, off);
705
706 for i in 0..self.indices.len() {
707 if self.indices[i] == rb[0] {
708 return self.children[i].find_case_insensitive_path_rec(
709 path,
710 lo_path,
711 ci_path,
712 rb,
713 fix_trailing_slash,
714 );
715 }
716 }
717 }
718 }
719
720 return fix_trailing_slash && path == [b'/'] && self.handle.is_some();
723 }
724
725 return self.children[0].find_case_insensitive_path_rec_match(
726 path,
727 lo_path,
728 ci_path,
729 rb,
730 fix_trailing_slash,
731 );
732 } else {
733 if self.handle.is_some() {
734 return true;
735 }
736
737 if fix_trailing_slash {
738 for i in 0..self.indices.len() {
739 if self.indices[i] == b'/' {
740 if (self.children[i].path.len() == 1
741 && self.children[i].handle.is_some())
742 || (self.children[i].n_type == NodeType::CatchAll
743 && self.children[i].children[0].handle.is_some())
744 {
745 ci_path.push(b'/');
746 return true;
747 }
748 return false;
749 }
750 }
751 }
752 return false;
753 }
754 }
755
756 if fix_trailing_slash {
757 if path == [b'/'] {
758 return true;
759 }
760 if lo_path.len() + 1 == lo_n_path.len()
761 && lo_n_path[lo_path.len()] == b'/'
762 && lo_path[1..] == lo_n_path[1..lo_path.len()]
763 && self.handle.is_some()
764 {
765 ci_path.append(&mut self.path.clone());
766 return true;
767 }
768 }
769
770 false
771 }
772
773 fn find_case_insensitive_path_rec_match(
775 &self,
776 mut path: &[u8],
777 mut lo_path: &[u8],
778 ci_path: &mut Vec<u8>,
779 rb: [u8; 4],
780 fix_trailing_slash: bool,
781 ) -> bool {
782 match self.n_type {
783 NodeType::Param => {
784 let mut k = 0;
785 while k < path.len() && path[k] != b'/' {
786 k += 1;
787 }
788 let mut path_k = path[..k].to_vec();
789 ci_path.append(&mut path_k);
790
791 if k < path.len() {
792 if self.children.len() > 0 {
793 lo_path = &lo_path[k..];
794 path = &path[k..];
795
796 return self.children[0].find_case_insensitive_path_rec(
797 path,
798 lo_path,
799 ci_path,
800 rb,
801 fix_trailing_slash,
802 );
803 }
804
805 if fix_trailing_slash && path.len() == k + 1 {
806 return true;
807 }
808 return false;
809 }
810
811 if self.handle.is_some() {
812 return true;
813 } else if fix_trailing_slash && self.children.len() == 1 {
814 if self.children[0].path == [b'/'] && self.children[0].handle.is_some() {
815 ci_path.push(b'/');
816 return true;
817 }
818 }
819
820 return false;
821 }
822 NodeType::CatchAll => {
823 ci_path.append(&mut path.to_vec());
824 return true;
825 }
826 _ => panic!("invalid node type"),
827 }
828 }
829}
830
831fn shift_n_rune_bytes(rb: [u8; 4], n: usize) -> [u8; 4] {
832 match n {
833 0 => rb,
834 1 => [rb[1], rb[2], rb[3], 0],
835 2 => [rb[2], rb[3], 0, 0],
836 3 => [rb[3], 0, 0, 0],
837 _ => [0; 4],
838 }
839}
840
841fn rune_start(b: u8) -> bool {
843 b & 0xC0 != 0x80
844}
845
846#[cfg(test)]
847mod tests {
848 use super::*;
849 use router::Params;
851 use std::panic;
852 use std::sync::Mutex;
853
854 struct TestRequest<'a> {
857 path: &'a str,
858 nil_handler: bool,
859 route: &'a str,
860 ps: Params,
861 }
862
863 impl<'a> TestRequest<'a> {
864 pub fn new(
865 path: &'a str,
866 nil_handler: bool,
867 route: &'a str,
868 ps: Params,
869 ) -> TestRequest<'a> {
870 TestRequest {
871 path,
872 nil_handler,
873 route,
874 ps,
875 }
876 }
877 }
878
879 type TestRequests<'a> = Vec<TestRequest<'a>>;
880
881 fn check_requests<T: Fn() -> String>(tree: &mut Node<T>, requests: TestRequests) {
882 for request in requests {
883 let (handler, ps, _) = tree.get_value(request.path);
884
885 if handler.is_none() {
886 if !request.nil_handler {
887 panic!(
888 "handle mismatch for route '{}': Expected non-nil handle",
889 request.path
890 );
891 }
892 } else if request.nil_handler {
893 panic!(
894 "handle m ismatch for route '{}': Expected nil handle",
895 request.path
896 );
897 } else {
898 match handler {
899 Some(h) => {
900 let res = h();
901 if res != request.route {
902 panic!(
903 "handle mismatch for route '{}': Wrong handle ({} != {})",
904 request.path, res, request.route
905 );
906 }
907 }
908 None => {
909 panic!("handle not found");
910 }
911 }
912 }
913
914 if ps != request.ps {
915 panic!("Params mismatch for route '{}'", request.path);
916 }
917 }
918 }
919
920 fn check_priorities<T: Fn() -> String>(n: &mut Node<T>) -> u32 {
921 let mut prio: u32 = 0;
923 for i in 0..n.children.len() {
924 prio += check_priorities(&mut *n.children[i]);
925 }
926
927 if n.handle.is_some() {
928 prio += 1;
929 }
930
931 if n.priority != prio {
932 panic!(
933 "priority mismatch for node '{}': is {}, should be {}",
934 str::from_utf8(&n.path).unwrap(),
935 n.priority,
936 prio
937 )
938 }
939
940 prio
941 }
942
943 fn check_max_params<T: Fn() -> String>(n: &mut Node<T>) -> u8 {
944 let mut max_params: u8 = 0;
945 for i in 0..n.children.len() {
946 let params = check_max_params(&mut *n.children[i]);
947
948 if params > max_params {
949 max_params = params;
950 }
951 }
952
953 if n.n_type > NodeType::Root && !n.wild_child {
954 max_params += 1;
955 }
956
957 if n.max_params != max_params {
958 panic!(
959 "maxParams mismatch for node '{}': is {}, should be {}",
960 str::from_utf8(&n.path).unwrap(),
961 n.max_params,
962 max_params,
963 )
964 }
965
966 max_params
967 }
968
969 fn fake_handler(val: &'static str) -> impl Fn() -> String {
970 move || val.to_string()
971 }
972
973 #[test]
974 fn test_count_params() {
975 assert_eq!(
976 2,
977 count_params("/path/:param1/static/*catch-all".as_bytes())
978 );
979 assert_eq!(255, count_params("/:param".repeat(256).as_bytes()));
980 }
981
982 #[test]
983 fn test_tree_add_and_get() {
984 let mut tree = Node::new();
985
986 let routes = vec![
987 "/hi",
988 "/contact",
989 "/co",
990 "/c",
991 "/a",
992 "/ab",
993 "/doc/",
994 "/doc/go_faq.html",
995 "/doc/go1.html",
996 "/α",
997 "/β",
998 ];
999
1000 for route in routes {
1001 tree.add_route(route, fake_handler(route));
1002 }
1003
1004 check_requests(
1005 &mut tree,
1006 vec![
1007 TestRequest::new("/a", false, "/a", Params::new()),
1008 TestRequest::new("/", true, "", Params::new()),
1009 TestRequest::new("/hi", false, "/hi", Params::new()),
1010 TestRequest::new("/contact", false, "/contact", Params::new()),
1011 TestRequest::new("/co", false, "/co", Params::new()),
1012 TestRequest::new("/con", true, "", Params::new()), TestRequest::new("/cona", true, "", Params::new()), TestRequest::new("/no", true, "", Params::new()), TestRequest::new("/ab", false, "/ab", Params::new()),
1016 TestRequest::new("/α", false, "/α", Params::new()),
1017 TestRequest::new("/β", false, "/β", Params::new()),
1018 ],
1019 );
1020
1021 check_priorities(&mut tree);
1022 check_max_params(&mut tree);
1023 }
1024
1025 #[test]
1026 fn test_tree_wildcard() {
1027 let mut tree = Node::new();
1028
1029 let routes = vec![
1030 "/",
1031 "/cmd/:tool/:sub",
1032 "/cmd/:tool/",
1033 "/src/*filepath",
1034 "/search/",
1035 "/search/:query",
1036 "/user_:name",
1037 "/user_:name/about",
1038 "/files/:dir/*filepath",
1039 "/doc/",
1040 "/doc/go_faq.html",
1041 "/doc/go1.html",
1042 "/info/:user/public",
1043 "/info/:user/project/:project",
1044 ];
1045
1046 for route in routes {
1047 tree.add_route(route, fake_handler(route));
1048 }
1049
1050 check_requests(
1051 &mut tree,
1052 vec![
1053 TestRequest::new("/", false, "/", Params::new()),
1054 TestRequest::new(
1055 "/cmd/test/",
1056 false,
1057 "/cmd/:tool/",
1058 Params(vec![Param::new("tool", "test")]),
1059 ),
1060 TestRequest::new(
1061 "/cmd/test",
1062 true,
1063 "",
1064 Params(vec![Param::new("tool", "test")]),
1065 ),
1066 TestRequest::new(
1067 "/cmd/test/3",
1068 false,
1069 "/cmd/:tool/:sub",
1070 Params(vec![Param::new("tool", "test"), Param::new("sub", "3")]),
1071 ),
1072 TestRequest::new(
1073 "/src/",
1074 false,
1075 "/src/*filepath",
1076 Params(vec![Param::new("filepath", "/")]),
1077 ),
1078 TestRequest::new(
1079 "/src/some/file.png",
1080 false,
1081 "/src/*filepath",
1082 Params(vec![Param::new("filepath", "/some/file.png")]),
1083 ),
1084 TestRequest::new("/search/", false, "/search/", Params::new()),
1085 TestRequest::new(
1086 "/search/someth!ng+in+ünìcodé",
1087 false,
1088 "/search/:query",
1089 Params(vec![Param::new("query", "someth!ng+in+ünìcodé")]),
1090 ),
1091 TestRequest::new(
1092 "/search/someth!ng+in+ünìcodé/",
1093 true,
1094 "",
1095 Params(vec![Param::new("query", "someth!ng+in+ünìcodé")]),
1096 ),
1097 TestRequest::new(
1098 "/user_gopher",
1099 false,
1100 "/user_:name",
1101 Params(vec![Param::new("name", "gopher")]),
1102 ),
1103 TestRequest::new(
1104 "/user_gopher/about",
1105 false,
1106 "/user_:name/about",
1107 Params(vec![Param::new("name", "gopher")]),
1108 ),
1109 TestRequest::new(
1110 "/files/js/inc/framework.js",
1111 false,
1112 "/files/:dir/*filepath",
1113 Params(vec![
1114 Param::new("dir", "js"),
1115 Param::new("filepath", "/inc/framework.js"),
1116 ]),
1117 ),
1118 TestRequest::new(
1119 "/info/gordon/public",
1120 false,
1121 "/info/:user/public",
1122 Params(vec![Param::new("user", "gordon")]),
1123 ),
1124 TestRequest::new(
1125 "/info/gordon/project/go",
1126 false,
1127 "/info/:user/project/:project",
1128 Params(vec![
1129 Param::new("user", "gordon"),
1130 Param::new("project", "go"),
1131 ]),
1132 ),
1133 ],
1134 );
1135
1136 check_priorities(&mut tree);
1137 check_max_params(&mut tree);
1138 }
1139
1140 type TestRoute = (&'static str, bool);
1142
1143 fn test_routes(routes: Vec<TestRoute>) {
1144 let tree = Mutex::new(Node::new());
1145 for route in routes {
1148 let recv = panic::catch_unwind(|| {
1149 let mut guard = match tree.lock() {
1150 Ok(guard) => guard,
1151 Err(poisoned) => poisoned.into_inner(),
1152 };
1153 guard.add_route(route.0, ());
1154 });
1155
1156 if route.1 {
1157 if recv.is_ok() {
1158 panic!("no panic for conflicting route '{}'", route.0);
1159 }
1160 } else if recv.is_err() {
1161 panic!("unexpected panic for route '{}': {:?}", route.0, recv);
1162 }
1163 }
1164 }
1165
1166 #[test]
1167 fn test_tree_wildcard_conflict() {
1168 let routes = vec![
1169 ("/cmd/:tool/:sub", false),
1170 ("/cmd/vet", true),
1171 ("/src/*filepath", false),
1172 ("/src/*filepathx", true),
1173 ("/src/", true),
1174 ("/src1/", false),
1175 ("/src1/*filepath", true),
1176 ("/src2*filepath", true),
1177 ("/search/:query", false),
1178 ("/search/invalid", true),
1179 ("/user_:name", false),
1180 ("/user_x", true),
1181 ("/user_:name", true), ("/id:id", false),
1184 ("/id/:id", true),
1185 ];
1186 test_routes(routes);
1187 }
1188
1189 #[test]
1190 fn test_tree_child_conflict() {
1191 let routes = vec![
1192 ("/cmd/vet", false),
1193 ("/cmd/:tool/:sub", true),
1194 ("/src/AUTHORS", false),
1195 ("/src/*filepath", true),
1196 ("/user_x", false),
1197 ("/user_:name", true),
1198 ("/id/:id", false),
1199 ("/id:id", true),
1200 ("/:id", true),
1201 ("/*filepath", true),
1202 ];
1203
1204 test_routes(routes);
1205 }
1206
1207 #[test]
1208 fn test_tree_duplicate_path() {
1209 let tree = Mutex::new(Node::new());
1210
1211 let routes = vec![
1212 "/",
1213 "/doc/",
1214 "/src/*filepath",
1215 "/search/:query",
1216 "/user_:name",
1217 ];
1218
1219 for route in routes {
1220 let mut recv = panic::catch_unwind(|| {
1221 let mut guard = match tree.lock() {
1222 Ok(guard) => guard,
1223 Err(poisoned) => poisoned.into_inner(),
1224 };
1225 guard.add_route(route, fake_handler(route));
1226 });
1227
1228 if recv.is_err() {
1229 panic!("panic inserting route '{}': {:?}", route, recv);
1230 }
1231
1232 recv = panic::catch_unwind(|| {
1233 let mut guard = match tree.lock() {
1234 Ok(guard) => guard,
1235 Err(poisoned) => poisoned.into_inner(),
1236 };
1237 guard.add_route(route, fake_handler(route));
1238 });
1239
1240 if recv.is_ok() {
1241 panic!("no panic while inserting duplicate route '{}'", route);
1242 }
1243 }
1244
1245 check_requests(
1246 &mut tree.lock().unwrap_or_else(|poisoned| poisoned.into_inner()),
1247 vec![
1248 TestRequest::new("/", false, "/", Params::new()),
1249 TestRequest::new("/doc/", false, "/doc/", Params::new()),
1250 TestRequest::new(
1251 "/src/some/file.png",
1252 false,
1253 "/src/*filepath",
1254 Params(vec![Param::new("filepath", "/some/file.png")]),
1255 ),
1256 TestRequest::new(
1257 "/search/someth!ng+in+ünìcodé",
1258 false,
1259 "/search/:query",
1260 Params(vec![Param::new("query", "someth!ng+in+ünìcodé")]),
1261 ),
1262 TestRequest::new(
1263 "/user_gopher",
1264 false,
1265 "/user_:name",
1266 Params(vec![Param::new("name", "gopher")]),
1267 ),
1268 ],
1269 );
1270 }
1271
1272 #[test]
1273 fn test_empty_wildcard_name() {
1274 let tree = Mutex::new(Node::new());
1275 let routes = vec!["/user:", "/user:/", "/cmd/:/", "/src/*"];
1276
1277 for route in routes {
1278 let mut recv = panic::catch_unwind(|| {
1279 let mut guard = match tree.lock() {
1280 Ok(guard) => guard,
1281 Err(poisoned) => poisoned.into_inner(),
1282 };
1283 guard.add_route(route, fake_handler(route));
1284 });
1285
1286 if recv.is_ok() {
1287 panic!(
1288 "no panic while inserting route with empty wildcard name '{}",
1289 route
1290 );
1291 }
1292 }
1293 }
1294
1295 #[test]
1296 fn test_tree_catch_all_conflict() {
1297 let routes = vec![
1298 ("/src/*filepath/x", true),
1299 ("/src2/", false),
1300 ("/src2/*filepath/x", true),
1301 ];
1302
1303 test_routes(routes);
1304 }
1305
1306 #[test]
1307 fn test_tree_catch_all_conflict_root() {
1308 let routes = vec![("/", false), ("/*filepath", true)];
1309
1310 test_routes(routes);
1311 }
1312
1313 #[test]
1314 fn test_tree_double_wildcard() {
1315 let panic_msg = "only one wildcard per path segment is allowed";
1316 let routes = vec!["/:foo:bar", "/:foo:bar/", "/:foo*bar"];
1317
1318 for route in routes {
1319 let tree = Mutex::new(Node::new());
1320 let mut recv = panic::catch_unwind(|| {
1321 let mut guard = match tree.lock() {
1322 Ok(guard) => guard,
1323 Err(poisoned) => poisoned.into_inner(),
1324 };
1325 guard.add_route(route, fake_handler(route));
1326 });
1327
1328 if recv.is_ok() {
1330 panic!(panic_msg);
1331 }
1332 }
1333 }
1334
1335 #[test]
1336 fn test_tree_trailing_slash_redirect() {
1337 let tree = Mutex::new(Node::new());
1338 let routes = vec![
1339 "/hi",
1340 "/b/",
1341 "/search/:query",
1342 "/cmd/:tool/",
1343 "/src/*filepath",
1344 "/x",
1345 "/x/y",
1346 "/y/",
1347 "/y/z",
1348 "/0/:id",
1349 "/0/:id/1",
1350 "/1/:id/",
1351 "/1/:id/2",
1352 "/aa",
1353 "/a/",
1354 "/admin",
1355 "/admin/:category",
1356 "/admin/:category/:page",
1357 "/doc",
1358 "/doc/go_faq.html",
1359 "/doc/go1.html",
1360 "/no/a",
1361 "/no/b",
1362 "/api/hello/:name",
1363 ];
1364
1365 for route in routes {
1366 let mut recv = panic::catch_unwind(|| {
1367 let mut guard = match tree.lock() {
1368 Ok(guard) => guard,
1369 Err(poisoned) => poisoned.into_inner(),
1370 };
1371 guard.add_route(route, fake_handler(route));
1372 });
1373
1374 if recv.is_err() {
1375 panic!("panic inserting route '{}': {:?}", route, recv);
1376 }
1377 }
1378
1379 let tsr_routes = vec![
1380 "/hi/",
1381 "/b",
1382 "/search/gopher/",
1383 "/cmd/vet",
1384 "/src",
1385 "/x/",
1386 "/y",
1387 "/0/go/",
1388 "/1/go",
1389 "/a",
1390 "/admin/",
1391 "/admin/config/",
1392 "/admin/config/permissions/",
1393 "/doc/",
1394 ];
1395
1396 for route in tsr_routes {
1397 let mut guard = match tree.lock() {
1398 Ok(guard) => guard,
1399 Err(poisoned) => poisoned.into_inner(),
1400 };
1401 let (handler, _, tsr) = guard.get_value(route);
1402
1403 if handler.is_some() {
1404 panic!("non-nil handler for TSR route '{}'", route);
1405 } else if !tsr {
1406 panic!("expected TSR recommendation for route '{}'", route);
1407 }
1408 }
1409
1410 let no_tsr_routes = vec!["/", "/no", "/no/", "/_", "/_/", "/api/world/abc"];
1411
1412 for route in no_tsr_routes {
1413 let mut guard = match tree.lock() {
1414 Ok(guard) => guard,
1415 Err(poisoned) => poisoned.into_inner(),
1416 };
1417 let (handler, _, tsr) = guard.get_value(route);
1418
1419 if handler.is_some() {
1420 panic!("non-nil handler for TSR route '{}'", route);
1421 } else if tsr {
1422 panic!("expected TSR recommendation for route '{}'", route);
1423 }
1424 }
1425 }
1426
1427 #[test]
1428 fn test_tree_root_trailing_slash_redirect() {
1429 let tree = Mutex::new(Node::new());
1430
1431 let recv = panic::catch_unwind(|| {
1432 let mut guard = match tree.lock() {
1433 Ok(guard) => guard,
1434 Err(poisoned) => poisoned.into_inner(),
1435 };
1436 guard.add_route("/:test", fake_handler("/:test"));
1437 });
1438
1439 if recv.is_err() {
1440 panic!("panic inserting test route: {:?}", recv);
1441 }
1442
1443 let guard = match tree.lock() {
1444 Ok(guard) => guard,
1445 Err(poisoned) => poisoned.into_inner(),
1446 };
1447 let (handler, _, tsr) = guard.get_value("/");
1448
1449 if handler.is_some() {
1450 panic!("non-nil handler");
1451 } else if tsr {
1452 panic!("expected no TSR recommendation");
1453 }
1454 }
1455
1456 #[test]
1457 fn test_tree_find_case_insensitive_path() {
1458 let mut tree = Node::new();
1460
1461 let routes = vec![
1462 "/hi",
1463 "/b/",
1464 "/ABC/",
1465 "/search/:query",
1466 "/cmd/:tool/",
1467 "/src/*filepath",
1468 "/x",
1469 "/x/y",
1470 "/y/",
1471 "/y/z",
1472 "/0/:id",
1473 "/0/:id/1",
1474 "/1/:id/",
1475 "/1/:id/2",
1476 "/aa",
1477 "/a/",
1478 "/doc",
1479 "/doc/go_faq.html",
1480 "/doc/go1.html",
1481 "/doc/go/away",
1482 "/no/a",
1483 "/no/b",
1484 "/Π",
1485 "/u/apfêl/",
1486 "/u/äpfêl/",
1487 "/u/öpfêl",
1488 "/v/Äpfêl/",
1489 "/v/Öpfêl",
1490 "/w/♬", "/w/♭/", "/w/𠜎", "/w/𠜏/", ];
1495
1496 for route in &routes {
1497 tree.add_route(route, fake_handler(route));
1509 }
1510
1511 for route in &routes {
1512 let (out, found) = tree.find_case_insensitive_path(route, false);
1518 if !found {
1520 panic!("Route '{}' not found!", route);
1521 } else if out != *route {
1523 panic!("Wrong result for route '{}': {}", route, out);
1524 }
1525 }
1526 }
1527}