libs/http/src/server/detail/router_base.cpp

88.2% Lines (60/68) 90.9% Functions (10/11) 81.0% Branches (34/42)
libs/http/src/server/detail/router_base.cpp
Line Branch Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/cppalliance/http
8 //
9
10 #include "src/server/detail/router_base.hpp"
11 #include <boost/http/server/detail/router_base.hpp>
12 #include <boost/http/server/flat_router.hpp>
13 #include <boost/http/server/router.hpp>
14 #include <boost/http/detail/except.hpp>
15 #include <boost/url/grammar/ci_string.hpp>
16 #include <boost/url/grammar/hexdig_chars.hpp>
17 #include "src/server/detail/route_match.hpp"
18
19 /*
20
21 pattern target path(use) path(get)
22 -------------------------------------------------
23 / / /
24 / /api /api
25 /api /api / /api
26 /api /api/ / /api/
27 /api /api/ / no-match strict
28 /api /api/v0 /v0 no-match
29 /api/ /api / /api
30 /api/ /api / no-match strict
31 /api/ /api/ / /api/
32 /api/ /api/v0 /v0 no-match
33
34 */
35
36 namespace boost {
37 namespace http {
38 namespace detail {
39
40 258 router_base::
41 ~router_base()
42 {
43
2/2
✓ Branch 0 taken 158 times.
✓ Branch 1 taken 100 times.
258 delete impl_;
44 258 }
45
46 158 router_base::
47 router_base(
48 158 opt_flags opt)
49 158 : impl_(new impl(opt))
50 {
51 158 }
52
53 100 router_base::
54 router_base(
55 100 router_base&& other) noexcept
56 100 :impl_(other.impl_)
57 {
58 100 other.impl_ = nullptr;
59 100 }
60
61 router_base&
62 router_base::
63 operator=(
64 router_base&& other) noexcept
65 {
66 delete impl_;
67 impl_ = other.impl_;
68 other.impl_ = 0;
69 return *this;
70 }
71
72 auto
73 77 router_base::
74 new_layer(
75 std::string_view pattern) -> layer&
76 {
77 // the pattern must not be empty
78
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 77 times.
77 if(pattern.empty())
79 detail::throw_invalid_argument();
80 // delete the last route if it is empty,
81 // this happens if they call route() without
82 // adding anything
83
4/6
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 70 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 77 times.
84 if(! impl_->layers.empty() &&
84 7 impl_->layers.back().entries.empty())
85 impl_->layers.pop_back();
86 77 impl_->layers.emplace_back(pattern);
87 65 return impl_->layers.back();
88 };
89
90 std::size_t
91 77 router_base::
92 new_layer_idx(
93 std::string_view pattern)
94 {
95 77 new_layer(pattern);
96 65 return impl_->layers.size() - 1;
97 }
98
99 auto
100 68 router_base::
101 get_layer(
102 std::size_t idx) -> layer&
103 {
104 68 return impl_->layers[idx];
105 }
106
107 void
108 102 router_base::
109 add_impl(
110 std::string_view pattern,
111 handlers hn)
112 {
113
2/2
✓ Branch 1 taken 21 times.
✓ Branch 2 taken 81 times.
102 if( pattern.empty())
114 21 pattern = "/";
115 102 impl_->layers.emplace_back(
116 pattern, hn);
117
118 // Validate depth for any nested routers
119 102 auto& lay = impl_->layers.back();
120
2/2
✓ Branch 5 taken 111 times.
✓ Branch 6 taken 101 times.
212 for(auto& entry : lay.entries)
121
2/2
✓ Branch 1 taken 50 times.
✓ Branch 2 taken 61 times.
111 if(entry.h->kind == is_router)
122
1/2
✓ Branch 2 taken 50 times.
✗ Branch 3 not taken.
50 if(auto* r = entry.h->get_router())
123
1/1
✓ Branch 1 taken 49 times.
50 r->set_nested_depth(impl_->depth_);
124 101 }
125
126 void
127 58 router_base::
128 add_impl(
129 layer& l,
130 http::method verb,
131 handlers hn)
132 {
133 // cannot be unknown
134
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
58 if(verb == http::method::unknown)
135 detail::throw_invalid_argument();
136
137 58 l.entries.reserve(l.entries.size() + hn.n);
138
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 58 times.
116 for(std::size_t i = 0; i < hn.n; ++i)
139 58 l.entries.emplace_back(verb,
140 58 std::move(hn.p[i]));
141 58 }
142
143 void
144 10 router_base::
145 add_impl(
146 layer& l,
147 std::string_view verb_str,
148 handlers hn)
149 {
150 10 l.entries.reserve(l.entries.size() + hn.n);
151
152
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 2 times.
10 if(verb_str.empty())
153 {
154 // all
155
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
16 for(std::size_t i = 0; i < hn.n; ++i)
156 8 l.entries.emplace_back(std::move(hn.p[i]));
157 8 return;
158 }
159
160 // possibly custom string
161
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 for(std::size_t i = 0; i < hn.n; ++i)
162 2 l.entries.emplace_back(verb_str,
163 2 std::move(hn.p[i]));
164 }
165
166 void
167 282 router_base::
168 set_nested_depth(
169 std::size_t parent_depth)
170 {
171 282 std::size_t d = parent_depth + 1;
172
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 281 times.
282 if(d >= max_path_depth)
173 1 detail::throw_length_error(
174 "router nesting depth exceeds max_path_depth");
175 281 impl_->depth_ = d;
176
2/2
✓ Branch 5 taken 281 times.
✓ Branch 6 taken 266 times.
547 for(auto& layer : impl_->layers)
177
2/2
✓ Branch 5 taken 281 times.
✓ Branch 6 taken 266 times.
547 for(auto& entry : layer.entries)
178
2/2
✓ Branch 1 taken 232 times.
✓ Branch 2 taken 49 times.
281 if(entry.h->kind == is_router)
179
1/2
✓ Branch 2 taken 232 times.
✗ Branch 3 not taken.
232 if(auto* r = entry.h->get_router())
180
1/1
✓ Branch 1 taken 217 times.
232 r->set_nested_depth(d);
181 266 }
182
183 } // detail
184 } // http
185 } // boost
186
187