12c593315Sopenharmony_ci/*
22c593315Sopenharmony_ci * nghttp2 - HTTP/2 C Library
32c593315Sopenharmony_ci *
42c593315Sopenharmony_ci * Copyright (c) 2015 Tatsuhiro Tsujikawa
52c593315Sopenharmony_ci *
62c593315Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining
72c593315Sopenharmony_ci * a copy of this software and associated documentation files (the
82c593315Sopenharmony_ci * "Software"), to deal in the Software without restriction, including
92c593315Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish,
102c593315Sopenharmony_ci * distribute, sublicense, and/or sell copies of the Software, and to
112c593315Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to
122c593315Sopenharmony_ci * the following conditions:
132c593315Sopenharmony_ci *
142c593315Sopenharmony_ci * The above copyright notice and this permission notice shall be
152c593315Sopenharmony_ci * included in all copies or substantial portions of the Software.
162c593315Sopenharmony_ci *
172c593315Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
182c593315Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
192c593315Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
202c593315Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
212c593315Sopenharmony_ci * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
222c593315Sopenharmony_ci * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
232c593315Sopenharmony_ci * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
242c593315Sopenharmony_ci */
252c593315Sopenharmony_ci#include "shrpx_router.h"
262c593315Sopenharmony_ci
272c593315Sopenharmony_ci#include <algorithm>
282c593315Sopenharmony_ci
292c593315Sopenharmony_ci#include "shrpx_config.h"
302c593315Sopenharmony_ci#include "shrpx_log.h"
312c593315Sopenharmony_ci
322c593315Sopenharmony_cinamespace shrpx {
332c593315Sopenharmony_ci
342c593315Sopenharmony_ciRNode::RNode() : s(nullptr), len(0), index(-1), wildcard_index(-1) {}
352c593315Sopenharmony_ci
362c593315Sopenharmony_ciRNode::RNode(const char *s, size_t len, ssize_t index, ssize_t wildcard_index)
372c593315Sopenharmony_ci    : s(s), len(len), index(index), wildcard_index(wildcard_index) {}
382c593315Sopenharmony_ci
392c593315Sopenharmony_ciRouter::Router() : balloc_(1024, 1024), root_{} {}
402c593315Sopenharmony_ci
412c593315Sopenharmony_ciRouter::~Router() {}
422c593315Sopenharmony_ci
432c593315Sopenharmony_cinamespace {
442c593315Sopenharmony_ciRNode *find_next_node(const RNode *node, char c) {
452c593315Sopenharmony_ci  auto itr = std::lower_bound(std::begin(node->next), std::end(node->next), c,
462c593315Sopenharmony_ci                              [](const std::unique_ptr<RNode> &lhs,
472c593315Sopenharmony_ci                                 const char c) { return lhs->s[0] < c; });
482c593315Sopenharmony_ci  if (itr == std::end(node->next) || (*itr)->s[0] != c) {
492c593315Sopenharmony_ci    return nullptr;
502c593315Sopenharmony_ci  }
512c593315Sopenharmony_ci
522c593315Sopenharmony_ci  return (*itr).get();
532c593315Sopenharmony_ci}
542c593315Sopenharmony_ci} // namespace
552c593315Sopenharmony_ci
562c593315Sopenharmony_cinamespace {
572c593315Sopenharmony_civoid add_next_node(RNode *node, std::unique_ptr<RNode> new_node) {
582c593315Sopenharmony_ci  auto itr = std::lower_bound(std::begin(node->next), std::end(node->next),
592c593315Sopenharmony_ci                              new_node->s[0],
602c593315Sopenharmony_ci                              [](const std::unique_ptr<RNode> &lhs,
612c593315Sopenharmony_ci                                 const char c) { return lhs->s[0] < c; });
622c593315Sopenharmony_ci  node->next.insert(itr, std::move(new_node));
632c593315Sopenharmony_ci}
642c593315Sopenharmony_ci} // namespace
652c593315Sopenharmony_ci
662c593315Sopenharmony_civoid Router::add_node(RNode *node, const char *pattern, size_t patlen,
672c593315Sopenharmony_ci                      ssize_t index, ssize_t wildcard_index) {
682c593315Sopenharmony_ci  auto pat = make_string_ref(balloc_, StringRef{pattern, patlen});
692c593315Sopenharmony_ci  auto new_node =
702c593315Sopenharmony_ci      std::make_unique<RNode>(pat.c_str(), pat.size(), index, wildcard_index);
712c593315Sopenharmony_ci  add_next_node(node, std::move(new_node));
722c593315Sopenharmony_ci}
732c593315Sopenharmony_ci
742c593315Sopenharmony_cisize_t Router::add_route(const StringRef &pattern, size_t idx, bool wildcard) {
752c593315Sopenharmony_ci  ssize_t index = -1, wildcard_index = -1;
762c593315Sopenharmony_ci  if (wildcard) {
772c593315Sopenharmony_ci    wildcard_index = idx;
782c593315Sopenharmony_ci  } else {
792c593315Sopenharmony_ci    index = idx;
802c593315Sopenharmony_ci  }
812c593315Sopenharmony_ci
822c593315Sopenharmony_ci  auto node = &root_;
832c593315Sopenharmony_ci  size_t i = 0;
842c593315Sopenharmony_ci
852c593315Sopenharmony_ci  for (;;) {
862c593315Sopenharmony_ci    auto next_node = find_next_node(node, pattern[i]);
872c593315Sopenharmony_ci    if (next_node == nullptr) {
882c593315Sopenharmony_ci      add_node(node, pattern.c_str() + i, pattern.size() - i, index,
892c593315Sopenharmony_ci               wildcard_index);
902c593315Sopenharmony_ci      return idx;
912c593315Sopenharmony_ci    }
922c593315Sopenharmony_ci
932c593315Sopenharmony_ci    node = next_node;
942c593315Sopenharmony_ci
952c593315Sopenharmony_ci    auto slen = pattern.size() - i;
962c593315Sopenharmony_ci    auto s = pattern.c_str() + i;
972c593315Sopenharmony_ci    auto n = std::min(node->len, slen);
982c593315Sopenharmony_ci    size_t j;
992c593315Sopenharmony_ci    for (j = 0; j < n && node->s[j] == s[j]; ++j)
1002c593315Sopenharmony_ci      ;
1012c593315Sopenharmony_ci    if (j == n) {
1022c593315Sopenharmony_ci      // The common prefix was matched
1032c593315Sopenharmony_ci      if (slen == node->len) {
1042c593315Sopenharmony_ci        // Complete match
1052c593315Sopenharmony_ci        if (index != -1) {
1062c593315Sopenharmony_ci          if (node->index != -1) {
1072c593315Sopenharmony_ci            // Return the existing index for duplicates.
1082c593315Sopenharmony_ci            return node->index;
1092c593315Sopenharmony_ci          }
1102c593315Sopenharmony_ci          node->index = index;
1112c593315Sopenharmony_ci          return idx;
1122c593315Sopenharmony_ci        }
1132c593315Sopenharmony_ci
1142c593315Sopenharmony_ci        assert(wildcard_index != -1);
1152c593315Sopenharmony_ci
1162c593315Sopenharmony_ci        if (node->wildcard_index != -1) {
1172c593315Sopenharmony_ci          return node->wildcard_index;
1182c593315Sopenharmony_ci        }
1192c593315Sopenharmony_ci        node->wildcard_index = wildcard_index;
1202c593315Sopenharmony_ci        return idx;
1212c593315Sopenharmony_ci      }
1222c593315Sopenharmony_ci
1232c593315Sopenharmony_ci      if (slen > node->len) {
1242c593315Sopenharmony_ci        // We still have pattern to add
1252c593315Sopenharmony_ci        i += j;
1262c593315Sopenharmony_ci
1272c593315Sopenharmony_ci        continue;
1282c593315Sopenharmony_ci      }
1292c593315Sopenharmony_ci    }
1302c593315Sopenharmony_ci
1312c593315Sopenharmony_ci    if (node->len > j) {
1322c593315Sopenharmony_ci      // node must be split into 2 nodes.  new_node is now the child
1332c593315Sopenharmony_ci      // of node.
1342c593315Sopenharmony_ci      auto new_node = std::make_unique<RNode>(
1352c593315Sopenharmony_ci          &node->s[j], node->len - j, node->index, node->wildcard_index);
1362c593315Sopenharmony_ci      std::swap(node->next, new_node->next);
1372c593315Sopenharmony_ci
1382c593315Sopenharmony_ci      node->len = j;
1392c593315Sopenharmony_ci      node->index = -1;
1402c593315Sopenharmony_ci      node->wildcard_index = -1;
1412c593315Sopenharmony_ci
1422c593315Sopenharmony_ci      add_next_node(node, std::move(new_node));
1432c593315Sopenharmony_ci
1442c593315Sopenharmony_ci      if (slen == j) {
1452c593315Sopenharmony_ci        node->index = index;
1462c593315Sopenharmony_ci        node->wildcard_index = wildcard_index;
1472c593315Sopenharmony_ci        return idx;
1482c593315Sopenharmony_ci      }
1492c593315Sopenharmony_ci    }
1502c593315Sopenharmony_ci
1512c593315Sopenharmony_ci    i += j;
1522c593315Sopenharmony_ci
1532c593315Sopenharmony_ci    assert(pattern.size() > i);
1542c593315Sopenharmony_ci    add_node(node, pattern.c_str() + i, pattern.size() - i, index,
1552c593315Sopenharmony_ci             wildcard_index);
1562c593315Sopenharmony_ci
1572c593315Sopenharmony_ci    return idx;
1582c593315Sopenharmony_ci  }
1592c593315Sopenharmony_ci}
1602c593315Sopenharmony_ci
1612c593315Sopenharmony_cinamespace {
1622c593315Sopenharmony_ciconst RNode *match_complete(size_t *offset, const RNode *node,
1632c593315Sopenharmony_ci                            const char *first, const char *last) {
1642c593315Sopenharmony_ci  *offset = 0;
1652c593315Sopenharmony_ci
1662c593315Sopenharmony_ci  if (first == last) {
1672c593315Sopenharmony_ci    return node;
1682c593315Sopenharmony_ci  }
1692c593315Sopenharmony_ci
1702c593315Sopenharmony_ci  auto p = first;
1712c593315Sopenharmony_ci
1722c593315Sopenharmony_ci  for (;;) {
1732c593315Sopenharmony_ci    auto next_node = find_next_node(node, *p);
1742c593315Sopenharmony_ci    if (next_node == nullptr) {
1752c593315Sopenharmony_ci      return nullptr;
1762c593315Sopenharmony_ci    }
1772c593315Sopenharmony_ci
1782c593315Sopenharmony_ci    node = next_node;
1792c593315Sopenharmony_ci
1802c593315Sopenharmony_ci    auto n = std::min(node->len, static_cast<size_t>(last - p));
1812c593315Sopenharmony_ci    if (memcmp(node->s, p, n) != 0) {
1822c593315Sopenharmony_ci      return nullptr;
1832c593315Sopenharmony_ci    }
1842c593315Sopenharmony_ci    p += n;
1852c593315Sopenharmony_ci    if (p == last) {
1862c593315Sopenharmony_ci      *offset = n;
1872c593315Sopenharmony_ci      return node;
1882c593315Sopenharmony_ci    }
1892c593315Sopenharmony_ci  }
1902c593315Sopenharmony_ci}
1912c593315Sopenharmony_ci} // namespace
1922c593315Sopenharmony_ci
1932c593315Sopenharmony_cinamespace {
1942c593315Sopenharmony_ciconst RNode *match_partial(bool *pattern_is_wildcard, const RNode *node,
1952c593315Sopenharmony_ci                           size_t offset, const char *first, const char *last) {
1962c593315Sopenharmony_ci  *pattern_is_wildcard = false;
1972c593315Sopenharmony_ci
1982c593315Sopenharmony_ci  if (first == last) {
1992c593315Sopenharmony_ci    if (node->len == offset) {
2002c593315Sopenharmony_ci      return node;
2012c593315Sopenharmony_ci    }
2022c593315Sopenharmony_ci    return nullptr;
2032c593315Sopenharmony_ci  }
2042c593315Sopenharmony_ci
2052c593315Sopenharmony_ci  auto p = first;
2062c593315Sopenharmony_ci
2072c593315Sopenharmony_ci  const RNode *found_node = nullptr;
2082c593315Sopenharmony_ci
2092c593315Sopenharmony_ci  if (offset > 0) {
2102c593315Sopenharmony_ci    auto n = std::min(node->len - offset, static_cast<size_t>(last - first));
2112c593315Sopenharmony_ci    if (memcmp(node->s + offset, first, n) != 0) {
2122c593315Sopenharmony_ci      return nullptr;
2132c593315Sopenharmony_ci    }
2142c593315Sopenharmony_ci
2152c593315Sopenharmony_ci    p += n;
2162c593315Sopenharmony_ci
2172c593315Sopenharmony_ci    if (p == last) {
2182c593315Sopenharmony_ci      if (node->len == offset + n) {
2192c593315Sopenharmony_ci        if (node->index != -1) {
2202c593315Sopenharmony_ci          return node;
2212c593315Sopenharmony_ci        }
2222c593315Sopenharmony_ci
2232c593315Sopenharmony_ci        // The last '/' handling, see below.
2242c593315Sopenharmony_ci        node = find_next_node(node, '/');
2252c593315Sopenharmony_ci        if (node != nullptr && node->index != -1 && node->len == 1) {
2262c593315Sopenharmony_ci          return node;
2272c593315Sopenharmony_ci        }
2282c593315Sopenharmony_ci
2292c593315Sopenharmony_ci        return nullptr;
2302c593315Sopenharmony_ci      }
2312c593315Sopenharmony_ci
2322c593315Sopenharmony_ci      // The last '/' handling, see below.
2332c593315Sopenharmony_ci      if (node->index != -1 && offset + n + 1 == node->len &&
2342c593315Sopenharmony_ci          node->s[node->len - 1] == '/') {
2352c593315Sopenharmony_ci        return node;
2362c593315Sopenharmony_ci      }
2372c593315Sopenharmony_ci
2382c593315Sopenharmony_ci      return nullptr;
2392c593315Sopenharmony_ci    }
2402c593315Sopenharmony_ci
2412c593315Sopenharmony_ci    if (node->wildcard_index != -1) {
2422c593315Sopenharmony_ci      found_node = node;
2432c593315Sopenharmony_ci      *pattern_is_wildcard = true;
2442c593315Sopenharmony_ci    } else if (node->index != -1 && node->s[node->len - 1] == '/') {
2452c593315Sopenharmony_ci      found_node = node;
2462c593315Sopenharmony_ci      *pattern_is_wildcard = false;
2472c593315Sopenharmony_ci    }
2482c593315Sopenharmony_ci
2492c593315Sopenharmony_ci    assert(node->len == offset + n);
2502c593315Sopenharmony_ci  }
2512c593315Sopenharmony_ci
2522c593315Sopenharmony_ci  for (;;) {
2532c593315Sopenharmony_ci    auto next_node = find_next_node(node, *p);
2542c593315Sopenharmony_ci    if (next_node == nullptr) {
2552c593315Sopenharmony_ci      return found_node;
2562c593315Sopenharmony_ci    }
2572c593315Sopenharmony_ci
2582c593315Sopenharmony_ci    node = next_node;
2592c593315Sopenharmony_ci
2602c593315Sopenharmony_ci    auto n = std::min(node->len, static_cast<size_t>(last - p));
2612c593315Sopenharmony_ci    if (memcmp(node->s, p, n) != 0) {
2622c593315Sopenharmony_ci      return found_node;
2632c593315Sopenharmony_ci    }
2642c593315Sopenharmony_ci
2652c593315Sopenharmony_ci    p += n;
2662c593315Sopenharmony_ci
2672c593315Sopenharmony_ci    if (p == last) {
2682c593315Sopenharmony_ci      if (node->len == n) {
2692c593315Sopenharmony_ci        // Complete match with this node
2702c593315Sopenharmony_ci        if (node->index != -1) {
2712c593315Sopenharmony_ci          *pattern_is_wildcard = false;
2722c593315Sopenharmony_ci          return node;
2732c593315Sopenharmony_ci        }
2742c593315Sopenharmony_ci
2752c593315Sopenharmony_ci        // The last '/' handling, see below.
2762c593315Sopenharmony_ci        node = find_next_node(node, '/');
2772c593315Sopenharmony_ci        if (node != nullptr && node->index != -1 && node->len == 1) {
2782c593315Sopenharmony_ci          *pattern_is_wildcard = false;
2792c593315Sopenharmony_ci          return node;
2802c593315Sopenharmony_ci        }
2812c593315Sopenharmony_ci
2822c593315Sopenharmony_ci        return found_node;
2832c593315Sopenharmony_ci      }
2842c593315Sopenharmony_ci
2852c593315Sopenharmony_ci      // We allow match without trailing "/" at the end of pattern.
2862c593315Sopenharmony_ci      // So, if pattern ends with '/', and pattern and path matches
2872c593315Sopenharmony_ci      // without that slash, we consider they match to deal with
2882c593315Sopenharmony_ci      // request to the directory without trailing slash.  That is if
2892c593315Sopenharmony_ci      // pattern is "/foo/" and path is "/foo", we consider they
2902c593315Sopenharmony_ci      // match.
2912c593315Sopenharmony_ci      if (node->index != -1 && n + 1 == node->len && node->s[n] == '/') {
2922c593315Sopenharmony_ci        *pattern_is_wildcard = false;
2932c593315Sopenharmony_ci        return node;
2942c593315Sopenharmony_ci      }
2952c593315Sopenharmony_ci
2962c593315Sopenharmony_ci      return found_node;
2972c593315Sopenharmony_ci    }
2982c593315Sopenharmony_ci
2992c593315Sopenharmony_ci    if (node->wildcard_index != -1) {
3002c593315Sopenharmony_ci      found_node = node;
3012c593315Sopenharmony_ci      *pattern_is_wildcard = true;
3022c593315Sopenharmony_ci    } else if (node->index != -1 && node->s[node->len - 1] == '/') {
3032c593315Sopenharmony_ci      // This is the case when pattern which ends with "/" is included
3042c593315Sopenharmony_ci      // in query.
3052c593315Sopenharmony_ci      found_node = node;
3062c593315Sopenharmony_ci      *pattern_is_wildcard = false;
3072c593315Sopenharmony_ci    }
3082c593315Sopenharmony_ci
3092c593315Sopenharmony_ci    assert(node->len == n);
3102c593315Sopenharmony_ci  }
3112c593315Sopenharmony_ci}
3122c593315Sopenharmony_ci} // namespace
3132c593315Sopenharmony_ci
3142c593315Sopenharmony_cissize_t Router::match(const StringRef &host, const StringRef &path) const {
3152c593315Sopenharmony_ci  const RNode *node;
3162c593315Sopenharmony_ci  size_t offset;
3172c593315Sopenharmony_ci
3182c593315Sopenharmony_ci  node = match_complete(&offset, &root_, std::begin(host), std::end(host));
3192c593315Sopenharmony_ci  if (node == nullptr) {
3202c593315Sopenharmony_ci    return -1;
3212c593315Sopenharmony_ci  }
3222c593315Sopenharmony_ci
3232c593315Sopenharmony_ci  bool pattern_is_wildcard;
3242c593315Sopenharmony_ci  node = match_partial(&pattern_is_wildcard, node, offset, std::begin(path),
3252c593315Sopenharmony_ci                       std::end(path));
3262c593315Sopenharmony_ci  if (node == nullptr || node == &root_) {
3272c593315Sopenharmony_ci    return -1;
3282c593315Sopenharmony_ci  }
3292c593315Sopenharmony_ci
3302c593315Sopenharmony_ci  return pattern_is_wildcard ? node->wildcard_index : node->index;
3312c593315Sopenharmony_ci}
3322c593315Sopenharmony_ci
3332c593315Sopenharmony_cissize_t Router::match(const StringRef &s) const {
3342c593315Sopenharmony_ci  const RNode *node;
3352c593315Sopenharmony_ci  size_t offset;
3362c593315Sopenharmony_ci
3372c593315Sopenharmony_ci  node = match_complete(&offset, &root_, std::begin(s), std::end(s));
3382c593315Sopenharmony_ci  if (node == nullptr) {
3392c593315Sopenharmony_ci    return -1;
3402c593315Sopenharmony_ci  }
3412c593315Sopenharmony_ci
3422c593315Sopenharmony_ci  if (node->len != offset) {
3432c593315Sopenharmony_ci    return -1;
3442c593315Sopenharmony_ci  }
3452c593315Sopenharmony_ci
3462c593315Sopenharmony_ci  return node->index;
3472c593315Sopenharmony_ci}
3482c593315Sopenharmony_ci
3492c593315Sopenharmony_cinamespace {
3502c593315Sopenharmony_ciconst RNode *match_prefix(size_t *nread, const RNode *node, const char *first,
3512c593315Sopenharmony_ci                          const char *last) {
3522c593315Sopenharmony_ci  if (first == last) {
3532c593315Sopenharmony_ci    return nullptr;
3542c593315Sopenharmony_ci  }
3552c593315Sopenharmony_ci
3562c593315Sopenharmony_ci  auto p = first;
3572c593315Sopenharmony_ci
3582c593315Sopenharmony_ci  for (;;) {
3592c593315Sopenharmony_ci    auto next_node = find_next_node(node, *p);
3602c593315Sopenharmony_ci    if (next_node == nullptr) {
3612c593315Sopenharmony_ci      return nullptr;
3622c593315Sopenharmony_ci    }
3632c593315Sopenharmony_ci
3642c593315Sopenharmony_ci    node = next_node;
3652c593315Sopenharmony_ci
3662c593315Sopenharmony_ci    auto n = std::min(node->len, static_cast<size_t>(last - p));
3672c593315Sopenharmony_ci    if (memcmp(node->s, p, n) != 0) {
3682c593315Sopenharmony_ci      return nullptr;
3692c593315Sopenharmony_ci    }
3702c593315Sopenharmony_ci
3712c593315Sopenharmony_ci    p += n;
3722c593315Sopenharmony_ci
3732c593315Sopenharmony_ci    if (p != last) {
3742c593315Sopenharmony_ci      if (node->index != -1) {
3752c593315Sopenharmony_ci        *nread = p - first;
3762c593315Sopenharmony_ci        return node;
3772c593315Sopenharmony_ci      }
3782c593315Sopenharmony_ci      continue;
3792c593315Sopenharmony_ci    }
3802c593315Sopenharmony_ci
3812c593315Sopenharmony_ci    if (node->len == n) {
3822c593315Sopenharmony_ci      *nread = p - first;
3832c593315Sopenharmony_ci      return node;
3842c593315Sopenharmony_ci    }
3852c593315Sopenharmony_ci
3862c593315Sopenharmony_ci    return nullptr;
3872c593315Sopenharmony_ci  }
3882c593315Sopenharmony_ci}
3892c593315Sopenharmony_ci} // namespace
3902c593315Sopenharmony_ci
3912c593315Sopenharmony_cissize_t Router::match_prefix(size_t *nread, const RNode **last_node,
3922c593315Sopenharmony_ci                             const StringRef &s) const {
3932c593315Sopenharmony_ci  if (*last_node == nullptr) {
3942c593315Sopenharmony_ci    *last_node = &root_;
3952c593315Sopenharmony_ci  }
3962c593315Sopenharmony_ci
3972c593315Sopenharmony_ci  auto node =
3982c593315Sopenharmony_ci      ::shrpx::match_prefix(nread, *last_node, std::begin(s), std::end(s));
3992c593315Sopenharmony_ci  if (node == nullptr) {
4002c593315Sopenharmony_ci    return -1;
4012c593315Sopenharmony_ci  }
4022c593315Sopenharmony_ci
4032c593315Sopenharmony_ci  *last_node = node;
4042c593315Sopenharmony_ci
4052c593315Sopenharmony_ci  return node->index;
4062c593315Sopenharmony_ci}
4072c593315Sopenharmony_ci
4082c593315Sopenharmony_cinamespace {
4092c593315Sopenharmony_civoid dump_node(const RNode *node, int depth) {
4102c593315Sopenharmony_ci  fprintf(stderr, "%*ss='%.*s', len=%zu, index=%zd\n", depth, "",
4112c593315Sopenharmony_ci          (int)node->len, node->s, node->len, node->index);
4122c593315Sopenharmony_ci  for (auto &nd : node->next) {
4132c593315Sopenharmony_ci    dump_node(nd.get(), depth + 4);
4142c593315Sopenharmony_ci  }
4152c593315Sopenharmony_ci}
4162c593315Sopenharmony_ci} // namespace
4172c593315Sopenharmony_ci
4182c593315Sopenharmony_civoid Router::dump() const { dump_node(&root_, 0); }
4192c593315Sopenharmony_ci
4202c593315Sopenharmony_ci} // namespace shrpx
421