• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * nghttp2 - HTTP/2 C Library
3  *
4  * Copyright (c) 2015 Tatsuhiro Tsujikawa
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25 #include "shrpx_router.h"
26 
27 #include <algorithm>
28 
29 #include "shrpx_config.h"
30 #include "shrpx_log.h"
31 
32 namespace shrpx {
33 
RNode()34 RNode::RNode() : s(nullptr), len(0), index(-1), wildcard_index(-1) {}
35 
RNode(const char * s,size_t len,ssize_t index,ssize_t wildcard_index)36 RNode::RNode(const char *s, size_t len, ssize_t index, ssize_t wildcard_index)
37     : s(s), len(len), index(index), wildcard_index(wildcard_index) {}
38 
Router()39 Router::Router() : balloc_(1024, 1024), root_{} {}
40 
~Router()41 Router::~Router() {}
42 
43 namespace {
find_next_node(const RNode * node,char c)44 RNode *find_next_node(const RNode *node, char c) {
45   auto itr = std::lower_bound(std::begin(node->next), std::end(node->next), c,
46                               [](const std::unique_ptr<RNode> &lhs,
47                                  const char c) { return lhs->s[0] < c; });
48   if (itr == std::end(node->next) || (*itr)->s[0] != c) {
49     return nullptr;
50   }
51 
52   return (*itr).get();
53 }
54 } // namespace
55 
56 namespace {
add_next_node(RNode * node,std::unique_ptr<RNode> new_node)57 void add_next_node(RNode *node, std::unique_ptr<RNode> new_node) {
58   auto itr = std::lower_bound(std::begin(node->next), std::end(node->next),
59                               new_node->s[0],
60                               [](const std::unique_ptr<RNode> &lhs,
61                                  const char c) { return lhs->s[0] < c; });
62   node->next.insert(itr, std::move(new_node));
63 }
64 } // namespace
65 
add_node(RNode * node,const char * pattern,size_t patlen,ssize_t index,ssize_t wildcard_index)66 void Router::add_node(RNode *node, const char *pattern, size_t patlen,
67                       ssize_t index, ssize_t wildcard_index) {
68   auto pat = make_string_ref(balloc_, StringRef{pattern, patlen});
69   auto new_node =
70       std::make_unique<RNode>(pat.c_str(), pat.size(), index, wildcard_index);
71   add_next_node(node, std::move(new_node));
72 }
73 
add_route(const StringRef & pattern,size_t idx,bool wildcard)74 size_t Router::add_route(const StringRef &pattern, size_t idx, bool wildcard) {
75   ssize_t index = -1, wildcard_index = -1;
76   if (wildcard) {
77     wildcard_index = idx;
78   } else {
79     index = idx;
80   }
81 
82   auto node = &root_;
83   size_t i = 0;
84 
85   for (;;) {
86     auto next_node = find_next_node(node, pattern[i]);
87     if (next_node == nullptr) {
88       add_node(node, pattern.c_str() + i, pattern.size() - i, index,
89                wildcard_index);
90       return idx;
91     }
92 
93     node = next_node;
94 
95     auto slen = pattern.size() - i;
96     auto s = pattern.c_str() + i;
97     auto n = std::min(node->len, slen);
98     size_t j;
99     for (j = 0; j < n && node->s[j] == s[j]; ++j)
100       ;
101     if (j == n) {
102       // The common prefix was matched
103       if (slen == node->len) {
104         // Complete match
105         if (index != -1) {
106           if (node->index != -1) {
107             // Return the existing index for duplicates.
108             return node->index;
109           }
110           node->index = index;
111           return idx;
112         }
113 
114         assert(wildcard_index != -1);
115 
116         if (node->wildcard_index != -1) {
117           return node->wildcard_index;
118         }
119         node->wildcard_index = wildcard_index;
120         return idx;
121       }
122 
123       if (slen > node->len) {
124         // We still have pattern to add
125         i += j;
126 
127         continue;
128       }
129     }
130 
131     if (node->len > j) {
132       // node must be split into 2 nodes.  new_node is now the child
133       // of node.
134       auto new_node = std::make_unique<RNode>(
135           &node->s[j], node->len - j, node->index, node->wildcard_index);
136       std::swap(node->next, new_node->next);
137 
138       node->len = j;
139       node->index = -1;
140       node->wildcard_index = -1;
141 
142       add_next_node(node, std::move(new_node));
143 
144       if (slen == j) {
145         node->index = index;
146         node->wildcard_index = wildcard_index;
147         return idx;
148       }
149     }
150 
151     i += j;
152 
153     assert(pattern.size() > i);
154     add_node(node, pattern.c_str() + i, pattern.size() - i, index,
155              wildcard_index);
156 
157     return idx;
158   }
159 }
160 
161 namespace {
match_complete(size_t * offset,const RNode * node,const char * first,const char * last)162 const RNode *match_complete(size_t *offset, const RNode *node,
163                             const char *first, const char *last) {
164   *offset = 0;
165 
166   if (first == last) {
167     return node;
168   }
169 
170   auto p = first;
171 
172   for (;;) {
173     auto next_node = find_next_node(node, *p);
174     if (next_node == nullptr) {
175       return nullptr;
176     }
177 
178     node = next_node;
179 
180     auto n = std::min(node->len, static_cast<size_t>(last - p));
181     if (memcmp(node->s, p, n) != 0) {
182       return nullptr;
183     }
184     p += n;
185     if (p == last) {
186       *offset = n;
187       return node;
188     }
189   }
190 }
191 } // namespace
192 
193 namespace {
match_partial(bool * pattern_is_wildcard,const RNode * node,size_t offset,const char * first,const char * last)194 const RNode *match_partial(bool *pattern_is_wildcard, const RNode *node,
195                            size_t offset, const char *first, const char *last) {
196   *pattern_is_wildcard = false;
197 
198   if (first == last) {
199     if (node->len == offset) {
200       return node;
201     }
202     return nullptr;
203   }
204 
205   auto p = first;
206 
207   const RNode *found_node = nullptr;
208 
209   if (offset > 0) {
210     auto n = std::min(node->len - offset, static_cast<size_t>(last - first));
211     if (memcmp(node->s + offset, first, n) != 0) {
212       return nullptr;
213     }
214 
215     p += n;
216 
217     if (p == last) {
218       if (node->len == offset + n) {
219         if (node->index != -1) {
220           return node;
221         }
222 
223         // The last '/' handling, see below.
224         node = find_next_node(node, '/');
225         if (node != nullptr && node->index != -1 && node->len == 1) {
226           return node;
227         }
228 
229         return nullptr;
230       }
231 
232       // The last '/' handling, see below.
233       if (node->index != -1 && offset + n + 1 == node->len &&
234           node->s[node->len - 1] == '/') {
235         return node;
236       }
237 
238       return nullptr;
239     }
240 
241     if (node->wildcard_index != -1) {
242       found_node = node;
243       *pattern_is_wildcard = true;
244     } else if (node->index != -1 && node->s[node->len - 1] == '/') {
245       found_node = node;
246       *pattern_is_wildcard = false;
247     }
248 
249     assert(node->len == offset + n);
250   }
251 
252   for (;;) {
253     auto next_node = find_next_node(node, *p);
254     if (next_node == nullptr) {
255       return found_node;
256     }
257 
258     node = next_node;
259 
260     auto n = std::min(node->len, static_cast<size_t>(last - p));
261     if (memcmp(node->s, p, n) != 0) {
262       return found_node;
263     }
264 
265     p += n;
266 
267     if (p == last) {
268       if (node->len == n) {
269         // Complete match with this node
270         if (node->index != -1) {
271           *pattern_is_wildcard = false;
272           return node;
273         }
274 
275         // The last '/' handling, see below.
276         node = find_next_node(node, '/');
277         if (node != nullptr && node->index != -1 && node->len == 1) {
278           *pattern_is_wildcard = false;
279           return node;
280         }
281 
282         return found_node;
283       }
284 
285       // We allow match without trailing "/" at the end of pattern.
286       // So, if pattern ends with '/', and pattern and path matches
287       // without that slash, we consider they match to deal with
288       // request to the directory without trailing slash.  That is if
289       // pattern is "/foo/" and path is "/foo", we consider they
290       // match.
291       if (node->index != -1 && n + 1 == node->len && node->s[n] == '/') {
292         *pattern_is_wildcard = false;
293         return node;
294       }
295 
296       return found_node;
297     }
298 
299     if (node->wildcard_index != -1) {
300       found_node = node;
301       *pattern_is_wildcard = true;
302     } else if (node->index != -1 && node->s[node->len - 1] == '/') {
303       // This is the case when pattern which ends with "/" is included
304       // in query.
305       found_node = node;
306       *pattern_is_wildcard = false;
307     }
308 
309     assert(node->len == n);
310   }
311 }
312 } // namespace
313 
match(const StringRef & host,const StringRef & path) const314 ssize_t Router::match(const StringRef &host, const StringRef &path) const {
315   const RNode *node;
316   size_t offset;
317 
318   node = match_complete(&offset, &root_, std::begin(host), std::end(host));
319   if (node == nullptr) {
320     return -1;
321   }
322 
323   bool pattern_is_wildcard;
324   node = match_partial(&pattern_is_wildcard, node, offset, std::begin(path),
325                        std::end(path));
326   if (node == nullptr || node == &root_) {
327     return -1;
328   }
329 
330   return pattern_is_wildcard ? node->wildcard_index : node->index;
331 }
332 
match(const StringRef & s) const333 ssize_t Router::match(const StringRef &s) const {
334   const RNode *node;
335   size_t offset;
336 
337   node = match_complete(&offset, &root_, std::begin(s), std::end(s));
338   if (node == nullptr) {
339     return -1;
340   }
341 
342   if (node->len != offset) {
343     return -1;
344   }
345 
346   return node->index;
347 }
348 
349 namespace {
match_prefix(size_t * nread,const RNode * node,const char * first,const char * last)350 const RNode *match_prefix(size_t *nread, const RNode *node, const char *first,
351                           const char *last) {
352   if (first == last) {
353     return nullptr;
354   }
355 
356   auto p = first;
357 
358   for (;;) {
359     auto next_node = find_next_node(node, *p);
360     if (next_node == nullptr) {
361       return nullptr;
362     }
363 
364     node = next_node;
365 
366     auto n = std::min(node->len, static_cast<size_t>(last - p));
367     if (memcmp(node->s, p, n) != 0) {
368       return nullptr;
369     }
370 
371     p += n;
372 
373     if (p != last) {
374       if (node->index != -1) {
375         *nread = p - first;
376         return node;
377       }
378       continue;
379     }
380 
381     if (node->len == n) {
382       *nread = p - first;
383       return node;
384     }
385 
386     return nullptr;
387   }
388 }
389 } // namespace
390 
match_prefix(size_t * nread,const RNode ** last_node,const StringRef & s) const391 ssize_t Router::match_prefix(size_t *nread, const RNode **last_node,
392                              const StringRef &s) const {
393   if (*last_node == nullptr) {
394     *last_node = &root_;
395   }
396 
397   auto node =
398       ::shrpx::match_prefix(nread, *last_node, std::begin(s), std::end(s));
399   if (node == nullptr) {
400     return -1;
401   }
402 
403   *last_node = node;
404 
405   return node->index;
406 }
407 
408 namespace {
dump_node(const RNode * node,int depth)409 void dump_node(const RNode *node, int depth) {
410   fprintf(stderr, "%*ss='%.*s', len=%zu, index=%zd\n", depth, "",
411           (int)node->len, node->s, node->len, node->index);
412   for (auto &nd : node->next) {
413     dump_node(nd.get(), depth + 4);
414   }
415 }
416 } // namespace
417 
dump() const418 void Router::dump() const { dump_node(&root_, 0); }
419 
420 } // namespace shrpx
421