rama_http/matcher/
header.rs1use crate::{HeaderName, HeaderValue, Request};
2use rama_core::{extensions::Extensions, matcher::Matcher};
3
4#[derive(Debug, Clone)]
5pub struct HeaderMatcher {
9 name: HeaderName,
10 kind: HeaderMatcherKind,
11}
12
13#[derive(Debug, Clone)]
14enum HeaderMatcherKind {
15 Exists,
16 Is(HeaderValue),
17 Contains(HeaderValue),
18}
19
20impl HeaderMatcher {
21 pub fn exists(name: HeaderName) -> Self {
23 Self {
24 name,
25 kind: HeaderMatcherKind::Exists,
26 }
27 }
28
29 pub fn is(name: HeaderName, value: HeaderValue) -> Self {
31 Self {
32 name,
33 kind: HeaderMatcherKind::Is(value),
34 }
35 }
36
37 pub fn contains(name: HeaderName, value: HeaderValue) -> Self {
39 Self {
40 name,
41 kind: HeaderMatcherKind::Contains(value),
42 }
43 }
44}
45
46impl<Body> Matcher<Request<Body>> for HeaderMatcher {
47 fn matches(&self, _ext: Option<&Extensions>, req: &Request<Body>) -> bool {
48 let headers = req.headers();
49 match self.kind {
50 HeaderMatcherKind::Exists => headers.contains_key(&self.name),
51 HeaderMatcherKind::Is(ref value) => headers.get(&self.name) == Some(value),
52 HeaderMatcherKind::Contains(ref value) => {
53 headers.get_all(&self.name).iter().any(|v| v == value)
54 }
55 }
56 }
57}
58
59#[cfg(test)]
60mod test {
61 use super::*;
62
63 #[test]
64 fn test_header_matcher_exists() {
65 let matcher = HeaderMatcher::exists("content-type".parse().unwrap());
66 let req = Request::builder()
67 .header("content-type", "text/plain")
68 .body(())
69 .unwrap();
70 assert!(matcher.matches(None, &req));
71 }
72
73 #[test]
74 fn test_header_matcher_exists_no_match() {
75 let matcher = HeaderMatcher::exists("content-type".parse().unwrap());
76 let req = Request::builder().body(()).unwrap();
77 assert!(!matcher.matches(None, &req));
78 }
79
80 #[test]
81 fn test_header_matcher_is() {
82 let matcher = HeaderMatcher::is(
83 "content-type".parse().unwrap(),
84 "text/plain".parse().unwrap(),
85 );
86 let req = Request::builder()
87 .header("content-type", "text/plain")
88 .body(())
89 .unwrap();
90 assert!(matcher.matches(None, &req));
91 }
92
93 #[test]
94 fn test_header_matcher_is_no_match() {
95 let matcher = HeaderMatcher::is(
96 "content-type".parse().unwrap(),
97 "text/plain".parse().unwrap(),
98 );
99 let req = Request::builder()
100 .header("content-type", "text/html")
101 .body(())
102 .unwrap();
103 assert!(!matcher.matches(None, &req));
104 }
105
106 #[test]
107 fn test_header_matcher_contains() {
108 let matcher = HeaderMatcher::contains(
109 "content-type".parse().unwrap(),
110 "text/plain".parse().unwrap(),
111 );
112 let req = Request::builder()
113 .header("content-type", "text/plain")
114 .body(())
115 .unwrap();
116 assert!(matcher.matches(None, &req));
117 }
118
119 #[test]
120 fn test_header_matcher_contains_no_match() {
121 let matcher = HeaderMatcher::contains(
122 "content-type".parse().unwrap(),
123 "text/plain".parse().unwrap(),
124 );
125 let req = Request::builder()
126 .header("content-type", "text/html")
127 .body(())
128 .unwrap();
129 assert!(!matcher.matches(None, &req));
130 }
131
132 #[test]
133 fn test_header_matcher_contains_multiple() {
134 let matcher = HeaderMatcher::contains(
135 "content-type".parse().unwrap(),
136 "text/plain".parse().unwrap(),
137 );
138 let req = Request::builder()
139 .header("content-type", "text/html")
140 .header("content-type", "text/plain")
141 .body(())
142 .unwrap();
143 assert!(matcher.matches(None, &req));
144 }
145
146 #[test]
147 fn test_header_matcher_contains_multiple_no_match() {
148 let matcher = HeaderMatcher::contains(
149 "content-type".parse().unwrap(),
150 "text/plain".parse().unwrap(),
151 );
152 let req = Request::builder()
153 .header("content-type", "text/html")
154 .header("content-type", "text/xml")
155 .body(())
156 .unwrap();
157 assert!(!matcher.matches(None, &req));
158 }
159}