91.60% Lines (218/238)
94.12% Functions (16/17)
| TLA | Baseline | Branch | ||||||
|---|---|---|---|---|---|---|---|---|
| Line | Hits | Code | Line | Hits | Code | |||
| 1 | // | 1 | // | |||||
| 2 | // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) | 2 | // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) | |||||
| 3 | // | 3 | // | |||||
| 4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | 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) | 5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |||||
| 6 | // | 6 | // | |||||
| 7 | // Official repository: https://github.com/cppalliance/http | 7 | // Official repository: https://github.com/cppalliance/http | |||||
| 8 | // | 8 | // | |||||
| 9 | 9 | |||||||
| 10 | #include "src/server/detail/any_router.hpp" | 10 | #include "src/server/detail/any_router.hpp" | |||||
| 11 | #include <boost/http/server/detail/router_base.hpp> | 11 | #include <boost/http/server/detail/router_base.hpp> | |||||
| 12 | #include <boost/http/detail/except.hpp> | 12 | #include <boost/http/detail/except.hpp> | |||||
| 13 | #include <boost/http/error.hpp> | 13 | #include <boost/http/error.hpp> | |||||
| 14 | #include <boost/url/grammar/ci_string.hpp> | 14 | #include <boost/url/grammar/ci_string.hpp> | |||||
| 15 | #include <boost/url/grammar/hexdig_chars.hpp> | 15 | #include <boost/url/grammar/hexdig_chars.hpp> | |||||
| 16 | #include "src/server/detail/pct_decode.hpp" | 16 | #include "src/server/detail/pct_decode.hpp" | |||||
| 17 | 17 | |||||||
| 18 | #include <algorithm> | 18 | #include <algorithm> | |||||
| 19 | 19 | |||||||
| 20 | namespace boost { | 20 | namespace boost { | |||||
| 21 | namespace http { | 21 | namespace http { | |||||
| 22 | namespace detail { | 22 | namespace detail { | |||||
| 23 | 23 | |||||||
| 24 | //------------------------------------------------ | 24 | //------------------------------------------------ | |||||
| 25 | // | 25 | // | |||||
| 26 | // impl helpers | 26 | // impl helpers | |||||
| 27 | // | 27 | // | |||||
| 28 | //------------------------------------------------ | 28 | //------------------------------------------------ | |||||
| 29 | 29 | |||||||
| 30 | std::string | 30 | std::string | |||||
| HITCBC | 31 | 187 | router_base::impl:: | 31 | 187 | router_base::impl:: | ||
| 32 | build_allow_header( | 32 | build_allow_header( | |||||
| 33 | std::uint64_t methods, | 33 | std::uint64_t methods, | |||||
| 34 | std::vector<std::string> const& custom) | 34 | std::vector<std::string> const& custom) | |||||
| 35 | { | 35 | { | |||||
| HITCBC | 36 | 187 | if(methods == ~0ULL) | 36 | 187 | if(methods == ~0ULL) | ||
| HITCBC | 37 | 34 | return "DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT"; | 37 | 34 | return "DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT"; | ||
| 38 | 38 | |||||||
| HITCBC | 39 | 170 | std::string result; | 39 | 170 | std::string result; | ||
| 40 | static constexpr std::pair<http::method, char const*> known[] = { | 40 | static constexpr std::pair<http::method, char const*> known[] = { | |||||
| 41 | {http::method::acl, "ACL"}, | 41 | {http::method::acl, "ACL"}, | |||||
| 42 | {http::method::bind, "BIND"}, | 42 | {http::method::bind, "BIND"}, | |||||
| 43 | {http::method::checkout, "CHECKOUT"}, | 43 | {http::method::checkout, "CHECKOUT"}, | |||||
| 44 | {http::method::connect, "CONNECT"}, | 44 | {http::method::connect, "CONNECT"}, | |||||
| 45 | {http::method::copy, "COPY"}, | 45 | {http::method::copy, "COPY"}, | |||||
| 46 | {http::method::delete_, "DELETE"}, | 46 | {http::method::delete_, "DELETE"}, | |||||
| 47 | {http::method::get, "GET"}, | 47 | {http::method::get, "GET"}, | |||||
| 48 | {http::method::head, "HEAD"}, | 48 | {http::method::head, "HEAD"}, | |||||
| 49 | {http::method::link, "LINK"}, | 49 | {http::method::link, "LINK"}, | |||||
| 50 | {http::method::lock, "LOCK"}, | 50 | {http::method::lock, "LOCK"}, | |||||
| 51 | {http::method::merge, "MERGE"}, | 51 | {http::method::merge, "MERGE"}, | |||||
| 52 | {http::method::mkactivity, "MKACTIVITY"}, | 52 | {http::method::mkactivity, "MKACTIVITY"}, | |||||
| 53 | {http::method::mkcalendar, "MKCALENDAR"}, | 53 | {http::method::mkcalendar, "MKCALENDAR"}, | |||||
| 54 | {http::method::mkcol, "MKCOL"}, | 54 | {http::method::mkcol, "MKCOL"}, | |||||
| 55 | {http::method::move, "MOVE"}, | 55 | {http::method::move, "MOVE"}, | |||||
| 56 | {http::method::msearch, "M-SEARCH"}, | 56 | {http::method::msearch, "M-SEARCH"}, | |||||
| 57 | {http::method::notify, "NOTIFY"}, | 57 | {http::method::notify, "NOTIFY"}, | |||||
| 58 | {http::method::options, "OPTIONS"}, | 58 | {http::method::options, "OPTIONS"}, | |||||
| 59 | {http::method::patch, "PATCH"}, | 59 | {http::method::patch, "PATCH"}, | |||||
| 60 | {http::method::post, "POST"}, | 60 | {http::method::post, "POST"}, | |||||
| 61 | {http::method::propfind, "PROPFIND"}, | 61 | {http::method::propfind, "PROPFIND"}, | |||||
| 62 | {http::method::proppatch, "PROPPATCH"}, | 62 | {http::method::proppatch, "PROPPATCH"}, | |||||
| 63 | {http::method::purge, "PURGE"}, | 63 | {http::method::purge, "PURGE"}, | |||||
| 64 | {http::method::put, "PUT"}, | 64 | {http::method::put, "PUT"}, | |||||
| 65 | {http::method::rebind, "REBIND"}, | 65 | {http::method::rebind, "REBIND"}, | |||||
| 66 | {http::method::report, "REPORT"}, | 66 | {http::method::report, "REPORT"}, | |||||
| 67 | {http::method::search, "SEARCH"}, | 67 | {http::method::search, "SEARCH"}, | |||||
| 68 | {http::method::subscribe, "SUBSCRIBE"}, | 68 | {http::method::subscribe, "SUBSCRIBE"}, | |||||
| 69 | {http::method::trace, "TRACE"}, | 69 | {http::method::trace, "TRACE"}, | |||||
| 70 | {http::method::unbind, "UNBIND"}, | 70 | {http::method::unbind, "UNBIND"}, | |||||
| 71 | {http::method::unlink, "UNLINK"}, | 71 | {http::method::unlink, "UNLINK"}, | |||||
| 72 | {http::method::unlock, "UNLOCK"}, | 72 | {http::method::unlock, "UNLOCK"}, | |||||
| 73 | {http::method::unsubscribe, "UNSUBSCRIBE"}, | 73 | {http::method::unsubscribe, "UNSUBSCRIBE"}, | |||||
| 74 | }; | 74 | }; | |||||
| HITCBC | 75 | 5780 | for(auto const& [m, name] : known) | 75 | 5780 | for(auto const& [m, name] : known) | ||
| 76 | { | 76 | { | |||||
| HITCBC | 77 | 5610 | if(methods & (1ULL << static_cast<unsigned>(m))) | 77 | 5610 | if(methods & (1ULL << static_cast<unsigned>(m))) | ||
| 78 | { | 78 | { | |||||
| HITCBC | 79 | 128 | if(!result.empty()) | 79 | 128 | if(!result.empty()) | ||
| HITCBC | 80 | 8 | result += ", "; | 80 | 8 | result += ", "; | ||
| HITCBC | 81 | 128 | result += name; | 81 | 128 | result += name; | ||
| 82 | } | 82 | } | |||||
| 83 | } | 83 | } | |||||
| HITCBC | 84 | 174 | for(auto const& v : custom) | 84 | 174 | for(auto const& v : custom) | ||
| 85 | { | 85 | { | |||||
| HITCBC | 86 | 4 | if(!result.empty()) | 86 | 4 | if(!result.empty()) | ||
| MISUBC | 87 | ✗ | result += ", "; | 87 | ✗ | result += ", "; | ||
| HITCBC | 88 | 4 | result += v; | 88 | 4 | result += v; | ||
| 89 | } | 89 | } | |||||
| HITCBC | 90 | 170 | return result; | 90 | 170 | return result; | ||
| HITCBC | 91 | 170 | } | 91 | 170 | } | ||
| 92 | 92 | |||||||
| 93 | router_base::opt_flags | 93 | router_base::opt_flags | |||||
| HITCBC | 94 | 495 | router_base::impl:: | 94 | 495 | router_base::impl:: | ||
| 95 | compute_effective_opts( | 95 | compute_effective_opts( | |||||
| 96 | opt_flags parent, | 96 | opt_flags parent, | |||||
| 97 | opt_flags child) | 97 | opt_flags child) | |||||
| 98 | { | 98 | { | |||||
| HITCBC | 99 | 495 | opt_flags result = parent; | 99 | 495 | opt_flags result = parent; | ||
| 100 | 100 | |||||||
| 101 | // case_sensitive: bits 1-2 (2=true, 4=false) | 101 | // case_sensitive: bits 1-2 (2=true, 4=false) | |||||
| HITCBC | 102 | 495 | if(child & 2) | 102 | 495 | if(child & 2) | ||
| HITCBC | 103 | 4 | result = (result & ~6) | 2; | 103 | 4 | result = (result & ~6) | 2; | ||
| HITCBC | 104 | 491 | else if(child & 4) | 104 | 491 | else if(child & 4) | ||
| HITCBC | 105 | 4 | result = (result & ~6) | 4; | 105 | 4 | result = (result & ~6) | 4; | ||
| 106 | 106 | |||||||
| 107 | // strict: bits 3-4 (8=true, 16=false) | 107 | // strict: bits 3-4 (8=true, 16=false) | |||||
| HITCBC | 108 | 495 | if(child & 8) | 108 | 495 | if(child & 8) | ||
| HITCBC | 109 | 1 | result = (result & ~24) | 8; | 109 | 1 | result = (result & ~24) | 8; | ||
| HITCBC | 110 | 494 | else if(child & 16) | 110 | 494 | else if(child & 16) | ||
| HITCBC | 111 | 1 | result = (result & ~24) | 16; | 111 | 1 | result = (result & ~24) | 16; | ||
| 112 | 112 | |||||||
| HITCBC | 113 | 495 | return result; | 113 | 495 | return result; | ||
| 114 | } | 114 | } | |||||
| 115 | 115 | |||||||
| 116 | void | 116 | void | |||||
| HITCBC | 117 | 34 | router_base::impl:: | 117 | 34 | router_base::impl:: | ||
| 118 | restore_path( | 118 | restore_path( | |||||
| 119 | route_params& p, | 119 | route_params& p, | |||||
| 120 | std::size_t base_len) | 120 | std::size_t base_len) | |||||
| 121 | { | 121 | { | |||||
| HITCBC | 122 | 34 | auto& pv = *route_params_access{p}; | 122 | 34 | auto& pv = *route_params_access{p}; | ||
| HITCBC | 123 | 34 | p.base_path = { pv.decoded_path_.data(), base_len }; | 123 | 34 | p.base_path = { pv.decoded_path_.data(), base_len }; | ||
| HITCBC | 124 | 34 | auto const path_len = pv.decoded_path_.size() - (pv.addedSlash_ ? 1 : 0); | 124 | 34 | auto const path_len = pv.decoded_path_.size() - (pv.addedSlash_ ? 1 : 0); | ||
| HITCBC | 125 | 34 | if(base_len < path_len) | 125 | 34 | if(base_len < path_len) | ||
| HITCBC | 126 | 33 | p.path = { pv.decoded_path_.data() + base_len, | 126 | 33 | p.path = { pv.decoded_path_.data() + base_len, | ||
| 127 | path_len - base_len }; | 127 | path_len - base_len }; | |||||
| 128 | else | 128 | else | |||||
| HITCBC | 129 | 2 | p.path = { pv.decoded_path_.data() + | 129 | 2 | p.path = { pv.decoded_path_.data() + | ||
| HITCBC | 130 | 1 | pv.decoded_path_.size() - 1, 1 }; // soft slash | 130 | 1 | pv.decoded_path_.size() - 1, 1 }; // soft slash | ||
| HITCBC | 131 | 34 | } | 131 | 34 | } | ||
| 132 | 132 | |||||||
| 133 | void | 133 | void | |||||
| HITCBC | 134 | 68 | router_base::impl:: | 134 | 68 | router_base::impl:: | ||
| 135 | update_allow_for_entry( | 135 | update_allow_for_entry( | |||||
| 136 | matcher& m, | 136 | matcher& m, | |||||
| 137 | entry const& e) | 137 | entry const& e) | |||||
| 138 | { | 138 | { | |||||
| HITCBC | 139 | 68 | if(!m.end_) | 139 | 68 | if(!m.end_) | ||
| MISUBC | 140 | ✗ | return; | 140 | ✗ | return; | ||
| 141 | 141 | |||||||
| 142 | // Per-matcher collection | 142 | // Per-matcher collection | |||||
| HITCBC | 143 | 68 | if(e.all) | 143 | 68 | if(e.all) | ||
| HITCBC | 144 | 8 | m.allowed_methods_ = ~0ULL; | 144 | 8 | m.allowed_methods_ = ~0ULL; | ||
| HITCBC | 145 | 60 | else if(e.verb != http::method::unknown) | 145 | 60 | else if(e.verb != http::method::unknown) | ||
| HITCBC | 146 | 58 | m.allowed_methods_ |= (1ULL << static_cast<unsigned>(e.verb)); | 146 | 58 | m.allowed_methods_ |= (1ULL << static_cast<unsigned>(e.verb)); | ||
| HITCBC | 147 | 2 | else if(!e.verb_str.empty()) | 147 | 2 | else if(!e.verb_str.empty()) | ||
| HITCBC | 148 | 2 | m.custom_verbs_.push_back(e.verb_str); | 148 | 2 | m.custom_verbs_.push_back(e.verb_str); | ||
| 149 | 149 | |||||||
| 150 | // Rebuild per-matcher Allow header eagerly | 150 | // Rebuild per-matcher Allow header eagerly | |||||
| HITCBC | 151 | 68 | m.allow_header_ = build_allow_header( | 151 | 68 | m.allow_header_ = build_allow_header( | ||
| HITCBC | 152 | 68 | m.allowed_methods_, m.custom_verbs_); | 152 | 68 | m.allowed_methods_, m.custom_verbs_); | ||
| 153 | 153 | |||||||
| 154 | // Global collection (for OPTIONS *) | 154 | // Global collection (for OPTIONS *) | |||||
| HITCBC | 155 | 68 | if(e.all) | 155 | 68 | if(e.all) | ||
| HITCBC | 156 | 8 | global_methods_ = ~0ULL; | 156 | 8 | global_methods_ = ~0ULL; | ||
| HITCBC | 157 | 60 | else if(e.verb != http::method::unknown) | 157 | 60 | else if(e.verb != http::method::unknown) | ||
| HITCBC | 158 | 58 | global_methods_ |= (1ULL << static_cast<unsigned>(e.verb)); | 158 | 58 | global_methods_ |= (1ULL << static_cast<unsigned>(e.verb)); | ||
| HITCBC | 159 | 2 | else if(!e.verb_str.empty()) | 159 | 2 | else if(!e.verb_str.empty()) | ||
| HITCBC | 160 | 2 | global_custom_verbs_.push_back(e.verb_str); | 160 | 2 | global_custom_verbs_.push_back(e.verb_str); | ||
| 161 | } | 161 | } | |||||
| 162 | 162 | |||||||
| 163 | void | 163 | void | |||||
| HITCBC | 164 | 117 | router_base::impl:: | 164 | 117 | router_base::impl:: | ||
| 165 | rebuild_global_allow_header() | 165 | rebuild_global_allow_header() | |||||
| 166 | { | 166 | { | |||||
| HITCBC | 167 | 117 | std::sort(global_custom_verbs_.begin(), global_custom_verbs_.end()); | 167 | 117 | std::sort(global_custom_verbs_.begin(), global_custom_verbs_.end()); | ||
| HITCBC | 168 | 234 | global_custom_verbs_.erase( | 168 | 234 | global_custom_verbs_.erase( | ||
| HITCBC | 169 | 117 | std::unique(global_custom_verbs_.begin(), global_custom_verbs_.end()), | 169 | 117 | std::unique(global_custom_verbs_.begin(), global_custom_verbs_.end()), | ||
| HITCBC | 170 | 117 | global_custom_verbs_.end()); | 170 | 117 | global_custom_verbs_.end()); | ||
| HITCBC | 171 | 117 | global_allow_header_ = build_allow_header( | 171 | 117 | global_allow_header_ = build_allow_header( | ||
| HITCBC | 172 | 117 | global_methods_, global_custom_verbs_); | 172 | 117 | global_methods_, global_custom_verbs_); | ||
| HITCBC | 173 | 117 | } | 173 | 117 | } | ||
| 174 | 174 | |||||||
| 175 | void | 175 | void | |||||
| HITCBC | 176 | 341 | router_base::impl:: | 176 | 341 | router_base::impl:: | ||
| 177 | finalize_pending() | 177 | finalize_pending() | |||||
| 178 | { | 178 | { | |||||
| HITCBC | 179 | 341 | if(pending_route_ == SIZE_MAX) | 179 | 341 | if(pending_route_ == SIZE_MAX) | ||
| HITCBC | 180 | 277 | return; | 180 | 277 | return; | ||
| HITCBC | 181 | 64 | auto& m = matchers[pending_route_]; | 181 | 64 | auto& m = matchers[pending_route_]; | ||
| HITCBC | 182 | 64 | if(entries.size() == m.first_entry_) | 182 | 64 | if(entries.size() == m.first_entry_) | ||
| 183 | { | 183 | { | |||||
| 184 | // empty route, remove it | 184 | // empty route, remove it | |||||
| MISUBC | 185 | ✗ | matchers.pop_back(); | 185 | ✗ | matchers.pop_back(); | ||
| 186 | } | 186 | } | |||||
| 187 | else | 187 | else | |||||
| 188 | { | 188 | { | |||||
| HITCBC | 189 | 64 | m.skip_ = entries.size(); | 189 | 64 | m.skip_ = entries.size(); | ||
| 190 | } | 190 | } | |||||
| HITCBC | 191 | 64 | pending_route_ = SIZE_MAX; | 191 | 64 | pending_route_ = SIZE_MAX; | ||
| 192 | } | 192 | } | |||||
| 193 | 193 | |||||||
| 194 | //------------------------------------------------ | 194 | //------------------------------------------------ | |||||
| 195 | // | 195 | // | |||||
| 196 | // dispatch | 196 | // dispatch | |||||
| 197 | // | 197 | // | |||||
| 198 | //------------------------------------------------ | 198 | //------------------------------------------------ | |||||
| 199 | 199 | |||||||
| 200 | route_task | 200 | route_task | |||||
| HITCBC | 201 | 112 | router_base::impl:: | 201 | 112 | router_base::impl:: | ||
| 202 | dispatch_loop(route_params& p, bool is_options) const | 202 | dispatch_loop(route_params& p, bool is_options) const | |||||
| 203 | { | 203 | { | |||||
| 204 | auto& pv = *route_params_access{p}; | 204 | auto& pv = *route_params_access{p}; | |||||
| 205 | 205 | |||||||
| 206 | std::size_t last_matched = SIZE_MAX; | 206 | std::size_t last_matched = SIZE_MAX; | |||||
| 207 | std::uint32_t current_depth = 0; | 207 | std::uint32_t current_depth = 0; | |||||
| 208 | 208 | |||||||
| 209 | std::uint64_t options_methods = 0; | 209 | std::uint64_t options_methods = 0; | |||||
| 210 | std::vector<std::string> options_custom_verbs; | 210 | std::vector<std::string> options_custom_verbs; | |||||
| 211 | 211 | |||||||
| 212 | std::size_t path_stack[router_base::max_path_depth]; | 212 | std::size_t path_stack[router_base::max_path_depth]; | |||||
| 213 | path_stack[0] = 0; | 213 | path_stack[0] = 0; | |||||
| 214 | 214 | |||||||
| 215 | std::size_t matched_at_depth[router_base::max_path_depth]; | 215 | std::size_t matched_at_depth[router_base::max_path_depth]; | |||||
| 216 | for(std::size_t d = 0; d < router_base::max_path_depth; ++d) | 216 | for(std::size_t d = 0; d < router_base::max_path_depth; ++d) | |||||
| 217 | matched_at_depth[d] = SIZE_MAX; | 217 | matched_at_depth[d] = SIZE_MAX; | |||||
| 218 | 218 | |||||||
| 219 | for(std::size_t i = 0; i < entries.size(); ) | 219 | for(std::size_t i = 0; i < entries.size(); ) | |||||
| 220 | { | 220 | { | |||||
| 221 | auto const& e = entries[i]; | 221 | auto const& e = entries[i]; | |||||
| 222 | auto const& m = matchers[e.matcher_idx]; | 222 | auto const& m = matchers[e.matcher_idx]; | |||||
| 223 | auto const target_depth = m.depth_; | 223 | auto const target_depth = m.depth_; | |||||
| 224 | 224 | |||||||
| 225 | bool ancestors_ok = true; | 225 | bool ancestors_ok = true; | |||||
| 226 | 226 | |||||||
| 227 | std::size_t start_idx = (last_matched == SIZE_MAX) ? 0 : last_matched + 1; | 227 | std::size_t start_idx = (last_matched == SIZE_MAX) ? 0 : last_matched + 1; | |||||
| 228 | 228 | |||||||
| 229 | for(std::size_t check_idx = start_idx; | 229 | for(std::size_t check_idx = start_idx; | |||||
| 230 | check_idx <= e.matcher_idx && ancestors_ok; | 230 | check_idx <= e.matcher_idx && ancestors_ok; | |||||
| 231 | ++check_idx) | 231 | ++check_idx) | |||||
| 232 | { | 232 | { | |||||
| 233 | auto const& cm = matchers[check_idx]; | 233 | auto const& cm = matchers[check_idx]; | |||||
| 234 | 234 | |||||||
| 235 | bool is_needed_ancestor = (cm.depth_ < target_depth) && | 235 | bool is_needed_ancestor = (cm.depth_ < target_depth) && | |||||
| 236 | (matched_at_depth[cm.depth_] == SIZE_MAX); | 236 | (matched_at_depth[cm.depth_] == SIZE_MAX); | |||||
| 237 | bool is_self = (check_idx == e.matcher_idx); | 237 | bool is_self = (check_idx == e.matcher_idx); | |||||
| 238 | 238 | |||||||
| 239 | if(!is_needed_ancestor && !is_self) | 239 | if(!is_needed_ancestor && !is_self) | |||||
| 240 | continue; | 240 | continue; | |||||
| 241 | 241 | |||||||
| 242 | if(cm.depth_ <= current_depth && current_depth > 0) | 242 | if(cm.depth_ <= current_depth && current_depth > 0) | |||||
| 243 | { | 243 | { | |||||
| 244 | restore_path(p, path_stack[cm.depth_]); | 244 | restore_path(p, path_stack[cm.depth_]); | |||||
| 245 | } | 245 | } | |||||
| 246 | 246 | |||||||
| 247 | if(cm.end_ && pv.kind_ != router_base::is_plain) | 247 | if(cm.end_ && pv.kind_ != router_base::is_plain) | |||||
| 248 | { | 248 | { | |||||
| 249 | i = cm.skip_; | 249 | i = cm.skip_; | |||||
| 250 | ancestors_ok = false; | 250 | ancestors_ok = false; | |||||
| 251 | break; | 251 | break; | |||||
| 252 | } | 252 | } | |||||
| 253 | 253 | |||||||
| 254 | pv.case_sensitive = (cm.effective_opts_ & 2) != 0; | 254 | pv.case_sensitive = (cm.effective_opts_ & 2) != 0; | |||||
| 255 | pv.strict = (cm.effective_opts_ & 8) != 0; | 255 | pv.strict = (cm.effective_opts_ & 8) != 0; | |||||
| 256 | 256 | |||||||
| 257 | if(cm.depth_ < router_base::max_path_depth) | 257 | if(cm.depth_ < router_base::max_path_depth) | |||||
| 258 | path_stack[cm.depth_] = p.base_path.size(); | 258 | path_stack[cm.depth_] = p.base_path.size(); | |||||
| 259 | 259 | |||||||
| 260 | match_result mr; | 260 | match_result mr; | |||||
| 261 | if(!cm(p, mr)) | 261 | if(!cm(p, mr)) | |||||
| 262 | { | 262 | { | |||||
| 263 | for(std::size_t d = cm.depth_; d < router_base::max_path_depth; ++d) | 263 | for(std::size_t d = cm.depth_; d < router_base::max_path_depth; ++d) | |||||
| 264 | matched_at_depth[d] = SIZE_MAX; | 264 | matched_at_depth[d] = SIZE_MAX; | |||||
| 265 | i = cm.skip_; | 265 | i = cm.skip_; | |||||
| 266 | ancestors_ok = false; | 266 | ancestors_ok = false; | |||||
| 267 | break; | 267 | break; | |||||
| 268 | } | 268 | } | |||||
| 269 | 269 | |||||||
| 270 | if(!mr.params_.empty()) | 270 | if(!mr.params_.empty()) | |||||
| 271 | { | 271 | { | |||||
| 272 | for(auto& param : mr.params_) | 272 | for(auto& param : mr.params_) | |||||
| 273 | p.params.push_back(std::move(param)); | 273 | p.params.push_back(std::move(param)); | |||||
| 274 | } | 274 | } | |||||
| 275 | 275 | |||||||
| 276 | if(cm.depth_ < router_base::max_path_depth) | 276 | if(cm.depth_ < router_base::max_path_depth) | |||||
| 277 | matched_at_depth[cm.depth_] = check_idx; | 277 | matched_at_depth[cm.depth_] = check_idx; | |||||
| 278 | 278 | |||||||
| 279 | last_matched = check_idx; | 279 | last_matched = check_idx; | |||||
| 280 | current_depth = cm.depth_ + 1; | 280 | current_depth = cm.depth_ + 1; | |||||
| 281 | 281 | |||||||
| 282 | if(current_depth < router_base::max_path_depth) | 282 | if(current_depth < router_base::max_path_depth) | |||||
| 283 | path_stack[current_depth] = p.base_path.size(); | 283 | path_stack[current_depth] = p.base_path.size(); | |||||
| 284 | } | 284 | } | |||||
| 285 | 285 | |||||||
| 286 | if(!ancestors_ok) | 286 | if(!ancestors_ok) | |||||
| 287 | continue; | 287 | continue; | |||||
| 288 | 288 | |||||||
| 289 | // Collect methods from matching end-route matchers for OPTIONS | 289 | // Collect methods from matching end-route matchers for OPTIONS | |||||
| 290 | if(is_options && m.end_) | 290 | if(is_options && m.end_) | |||||
| 291 | { | 291 | { | |||||
| 292 | options_methods |= m.allowed_methods_; | 292 | options_methods |= m.allowed_methods_; | |||||
| 293 | for(auto const& v : m.custom_verbs_) | 293 | for(auto const& v : m.custom_verbs_) | |||||
| 294 | options_custom_verbs.push_back(v); | 294 | options_custom_verbs.push_back(v); | |||||
| 295 | } | 295 | } | |||||
| 296 | 296 | |||||||
| 297 | if(m.end_ && !e.match_method( | 297 | if(m.end_ && !e.match_method( | |||||
| 298 | const_cast<route_params&>(p))) | 298 | const_cast<route_params&>(p))) | |||||
| 299 | { | 299 | { | |||||
| 300 | ++i; | 300 | ++i; | |||||
| 301 | continue; | 301 | continue; | |||||
| 302 | } | 302 | } | |||||
| 303 | 303 | |||||||
| 304 | if(e.h->kind != pv.kind_) | 304 | if(e.h->kind != pv.kind_) | |||||
| 305 | { | 305 | { | |||||
| 306 | ++i; | 306 | ++i; | |||||
| 307 | continue; | 307 | continue; | |||||
| 308 | } | 308 | } | |||||
| 309 | 309 | |||||||
| 310 | //-------------------------------------------------- | 310 | //-------------------------------------------------- | |||||
| 311 | // Invoke handler | 311 | // Invoke handler | |||||
| 312 | //-------------------------------------------------- | 312 | //-------------------------------------------------- | |||||
| 313 | 313 | |||||||
| 314 | route_result rv; | 314 | route_result rv; | |||||
| 315 | try | 315 | try | |||||
| 316 | { | 316 | { | |||||
| 317 | rv = co_await e.h->invoke( | 317 | rv = co_await e.h->invoke( | |||||
| 318 | const_cast<route_params&>(p)); | 318 | const_cast<route_params&>(p)); | |||||
| 319 | } | 319 | } | |||||
| 320 | catch(...) | 320 | catch(...) | |||||
| 321 | { | 321 | { | |||||
| 322 | pv.ep_ = std::current_exception(); | 322 | pv.ep_ = std::current_exception(); | |||||
| 323 | pv.kind_ = router_base::is_exception; | 323 | pv.kind_ = router_base::is_exception; | |||||
| 324 | ++i; | 324 | ++i; | |||||
| 325 | continue; | 325 | continue; | |||||
| 326 | } | 326 | } | |||||
| 327 | 327 | |||||||
| 328 | if(rv.what() == route_what::next) | 328 | if(rv.what() == route_what::next) | |||||
| 329 | { | 329 | { | |||||
| 330 | ++i; | 330 | ++i; | |||||
| 331 | continue; | 331 | continue; | |||||
| 332 | } | 332 | } | |||||
| 333 | 333 | |||||||
| 334 | if(rv.what() == route_what::next_route) | 334 | if(rv.what() == route_what::next_route) | |||||
| 335 | { | 335 | { | |||||
| 336 | if(!m.end_) | 336 | if(!m.end_) | |||||
| 337 | co_return route_error(error::invalid_route_result); | 337 | co_return route_error(error::invalid_route_result); | |||||
| 338 | i = m.skip_; | 338 | i = m.skip_; | |||||
| 339 | continue; | 339 | continue; | |||||
| 340 | } | 340 | } | |||||
| 341 | 341 | |||||||
| 342 | if(rv.what() == route_what::done || | 342 | if(rv.what() == route_what::done || | |||||
| 343 | rv.what() == route_what::close) | 343 | rv.what() == route_what::close) | |||||
| 344 | { | 344 | { | |||||
| 345 | co_return rv; | 345 | co_return rv; | |||||
| 346 | } | 346 | } | |||||
| 347 | 347 | |||||||
| 348 | // Error - transition to error mode | 348 | // Error - transition to error mode | |||||
| 349 | pv.ec_ = rv.error(); | 349 | pv.ec_ = rv.error(); | |||||
| 350 | pv.kind_ = router_base::is_error; | 350 | pv.kind_ = router_base::is_error; | |||||
| 351 | 351 | |||||||
| 352 | if(m.end_) | 352 | if(m.end_) | |||||
| 353 | { | 353 | { | |||||
| 354 | i = m.skip_; | 354 | i = m.skip_; | |||||
| 355 | continue; | 355 | continue; | |||||
| 356 | } | 356 | } | |||||
| 357 | 357 | |||||||
| 358 | ++i; | 358 | ++i; | |||||
| 359 | } | 359 | } | |||||
| 360 | 360 | |||||||
| 361 | if(pv.kind_ == router_base::is_exception) | 361 | if(pv.kind_ == router_base::is_exception) | |||||
| 362 | co_return route_error(error::unhandled_exception); | 362 | co_return route_error(error::unhandled_exception); | |||||
| 363 | if(pv.kind_ == router_base::is_error) | 363 | if(pv.kind_ == router_base::is_error) | |||||
| 364 | co_return route_error(pv.ec_); | 364 | co_return route_error(pv.ec_); | |||||
| 365 | 365 | |||||||
| 366 | // OPTIONS fallback | 366 | // OPTIONS fallback | |||||
| 367 | if(is_options && options_methods != 0 && options_handler_) | 367 | if(is_options && options_methods != 0 && options_handler_) | |||||
| 368 | { | 368 | { | |||||
| 369 | std::string allow = build_allow_header(options_methods, options_custom_verbs); | 369 | std::string allow = build_allow_header(options_methods, options_custom_verbs); | |||||
| 370 | co_return co_await options_handler_->invoke(p, allow); | 370 | co_return co_await options_handler_->invoke(p, allow); | |||||
| 371 | } | 371 | } | |||||
| 372 | 372 | |||||||
| 373 | co_return route_next; | 373 | co_return route_next; | |||||
| HITCBC | 374 | 224 | } | 374 | 224 | } | ||
| 375 | 375 | |||||||
| 376 | //------------------------------------------------ | 376 | //------------------------------------------------ | |||||
| 377 | // | 377 | // | |||||
| 378 | // router_base | 378 | // router_base | |||||
| 379 | // | 379 | // | |||||
| 380 | //------------------------------------------------ | 380 | //------------------------------------------------ | |||||
| 381 | 381 | |||||||
| HITCBC | 382 | 168 | router_base:: | 382 | 168 | router_base:: | ||
| 383 | router_base( | 383 | router_base( | |||||
| HITCBC | 384 | 168 | opt_flags opt) | 384 | 168 | opt_flags opt) | ||
| HITCBC | 385 | 168 | : impl_(std::make_shared<impl>(opt)) | 385 | 168 | : impl_(std::make_shared<impl>(opt)) | ||
| 386 | { | 386 | { | |||||
| HITCBC | 387 | 168 | } | 387 | 168 | } | ||
| 388 | 388 | |||||||
| 389 | void | 389 | void | |||||
| HITCBC | 390 | 65 | router_base:: | 390 | 65 | router_base:: | ||
| 391 | add_middleware( | 391 | add_middleware( | |||||
| 392 | std::string_view pattern, | 392 | std::string_view pattern, | |||||
| 393 | handlers hn) | 393 | handlers hn) | |||||
| 394 | { | 394 | { | |||||
| HITCBC | 395 | 65 | impl_->finalize_pending(); | 395 | 65 | impl_->finalize_pending(); | ||
| 396 | 396 | |||||||
| HITCBC | 397 | 65 | if(pattern.empty()) | 397 | 65 | if(pattern.empty()) | ||
| HITCBC | 398 | 34 | pattern = "/"; | 398 | 34 | pattern = "/"; | ||
| 399 | 399 | |||||||
| HITCBC | 400 | 65 | auto const matcher_idx = impl_->matchers.size(); | 400 | 65 | auto const matcher_idx = impl_->matchers.size(); | ||
| HITCBC | 401 | 65 | impl_->matchers.emplace_back(pattern, false); | 401 | 65 | impl_->matchers.emplace_back(pattern, false); | ||
| HITCBC | 402 | 65 | auto& m = impl_->matchers.back(); | 402 | 65 | auto& m = impl_->matchers.back(); | ||
| HITCBC | 403 | 65 | if(m.error()) | 403 | 65 | if(m.error()) | ||
| MISUBC | 404 | ✗ | throw_invalid_argument(); | 404 | ✗ | throw_invalid_argument(); | ||
| HITCBC | 405 | 65 | m.first_entry_ = impl_->entries.size(); | 405 | 65 | m.first_entry_ = impl_->entries.size(); | ||
| HITCBC | 406 | 65 | m.effective_opts_ = impl::compute_effective_opts(0, impl_->opt_); | 406 | 65 | m.effective_opts_ = impl::compute_effective_opts(0, impl_->opt_); | ||
| HITCBC | 407 | 65 | m.own_opts_ = impl_->opt_; | 407 | 65 | m.own_opts_ = impl_->opt_; | ||
| HITCBC | 408 | 65 | m.depth_ = 0; | 408 | 65 | m.depth_ = 0; | ||
| 409 | 409 | |||||||
| HITCBC | 410 | 139 | for(std::size_t i = 0; i < hn.n; ++i) | 410 | 139 | for(std::size_t i = 0; i < hn.n; ++i) | ||
| 411 | { | 411 | { | |||||
| HITCBC | 412 | 74 | impl_->entries.emplace_back(std::move(hn.p[i])); | 412 | 74 | impl_->entries.emplace_back(std::move(hn.p[i])); | ||
| HITCBC | 413 | 74 | impl_->entries.back().matcher_idx = matcher_idx; | 413 | 74 | impl_->entries.back().matcher_idx = matcher_idx; | ||
| 414 | } | 414 | } | |||||
| 415 | 415 | |||||||
| HITCBC | 416 | 65 | m.skip_ = impl_->entries.size(); | 416 | 65 | m.skip_ = impl_->entries.size(); | ||
| HITCBC | 417 | 65 | } | 417 | 65 | } | ||
| 418 | 418 | |||||||
| 419 | void | 419 | void | |||||
| HITCBC | 420 | 50 | router_base:: | 420 | 50 | router_base:: | ||
| 421 | inline_router( | 421 | inline_router( | |||||
| 422 | std::string_view pattern, | 422 | std::string_view pattern, | |||||
| 423 | router_base&& sub) | 423 | router_base&& sub) | |||||
| 424 | { | 424 | { | |||||
| HITCBC | 425 | 50 | impl_->finalize_pending(); | 425 | 50 | impl_->finalize_pending(); | ||
| 426 | 426 | |||||||
| HITCBC | 427 | 50 | if(!sub.impl_) | 427 | 50 | if(!sub.impl_) | ||
| MISUBC | 428 | ✗ | return; | 428 | ✗ | return; | ||
| 429 | 429 | |||||||
| HITCBC | 430 | 50 | sub.impl_->finalize_pending(); | 430 | 50 | sub.impl_->finalize_pending(); | ||
| 431 | 431 | |||||||
| HITCBC | 432 | 50 | if(pattern.empty()) | 432 | 50 | if(pattern.empty()) | ||
| MISUBC | 433 | ✗ | pattern = "/"; | 433 | ✗ | pattern = "/"; | ||
| 434 | 434 | |||||||
| 435 | // Create parent matcher for the mount point | 435 | // Create parent matcher for the mount point | |||||
| HITCBC | 436 | 50 | auto const parent_matcher_idx = impl_->matchers.size(); | 436 | 50 | auto const parent_matcher_idx = impl_->matchers.size(); | ||
| HITCBC | 437 | 50 | impl_->matchers.emplace_back(pattern, false); | 437 | 50 | impl_->matchers.emplace_back(pattern, false); | ||
| HITCBC | 438 | 50 | auto& parent_m = impl_->matchers.back(); | 438 | 50 | auto& parent_m = impl_->matchers.back(); | ||
| HITCBC | 439 | 50 | if(parent_m.error()) | 439 | 50 | if(parent_m.error()) | ||
| MISUBC | 440 | ✗ | throw_invalid_argument(); | 440 | ✗ | throw_invalid_argument(); | ||
| HITCBC | 441 | 50 | parent_m.first_entry_ = impl_->entries.size(); | 441 | 50 | parent_m.first_entry_ = impl_->entries.size(); | ||
| 442 | 442 | |||||||
| HITCBC | 443 | 50 | auto parent_eff = impl::compute_effective_opts(0, impl_->opt_); | 443 | 50 | auto parent_eff = impl::compute_effective_opts(0, impl_->opt_); | ||
| HITCBC | 444 | 50 | parent_m.effective_opts_ = parent_eff; | 444 | 50 | parent_m.effective_opts_ = parent_eff; | ||
| HITCBC | 445 | 50 | parent_m.own_opts_ = impl_->opt_; | 445 | 50 | parent_m.own_opts_ = impl_->opt_; | ||
| HITCBC | 446 | 50 | parent_m.depth_ = 0; | 446 | 50 | parent_m.depth_ = 0; | ||
| 447 | 447 | |||||||
| 448 | // Check nesting depth | 448 | // Check nesting depth | |||||
| HITCBC | 449 | 50 | std::size_t max_sub_depth = 0; | 449 | 50 | std::size_t max_sub_depth = 0; | ||
| HITCBC | 450 | 332 | for(auto const& sm : sub.impl_->matchers) | 450 | 332 | for(auto const& sm : sub.impl_->matchers) | ||
| HITCBC | 451 | 564 | max_sub_depth = (std::max)(max_sub_depth, | 451 | 564 | max_sub_depth = (std::max)(max_sub_depth, | ||
| HITCBC | 452 | 282 | static_cast<std::size_t>(sm.depth_)); | 452 | 282 | static_cast<std::size_t>(sm.depth_)); | ||
| HITCBC | 453 | 50 | if(max_sub_depth + 1 >= max_path_depth) | 453 | 50 | if(max_sub_depth + 1 >= max_path_depth) | ||
| HITCBC | 454 | 1 | throw_length_error( | 454 | 1 | throw_length_error( | ||
| 455 | "router nesting depth exceeds max_path_depth"); | 455 | "router nesting depth exceeds max_path_depth"); | |||||
| 456 | 456 | |||||||
| 457 | // Compute offsets for re-indexing | 457 | // Compute offsets for re-indexing | |||||
| HITCBC | 458 | 49 | auto const matcher_offset = impl_->matchers.size(); | 458 | 49 | auto const matcher_offset = impl_->matchers.size(); | ||
| HITCBC | 459 | 49 | auto const entry_offset = impl_->entries.size(); | 459 | 49 | auto const entry_offset = impl_->entries.size(); | ||
| 460 | 460 | |||||||
| 461 | // Recompute effective_opts for inlined matchers using depth stack | 461 | // Recompute effective_opts for inlined matchers using depth stack | |||||
| HITCBC | 462 | 49 | auto sub_root_eff = impl::compute_effective_opts( | 462 | 49 | auto sub_root_eff = impl::compute_effective_opts( | ||
| HITCBC | 463 | 49 | parent_eff, sub.impl_->opt_); | 463 | 49 | parent_eff, sub.impl_->opt_); | ||
| 464 | opt_flags eff_stack[max_path_depth]; | 464 | opt_flags eff_stack[max_path_depth]; | |||||
| HITCBC | 465 | 49 | eff_stack[0] = sub_root_eff; | 465 | 49 | eff_stack[0] = sub_root_eff; | ||
| 466 | 466 | |||||||
| 467 | // Inline sub's matchers | 467 | // Inline sub's matchers | |||||
| HITCBC | 468 | 315 | for(auto& sm : sub.impl_->matchers) | 468 | 315 | for(auto& sm : sub.impl_->matchers) | ||
| 469 | { | 469 | { | |||||
| HITCBC | 470 | 266 | auto d = sm.depth_; | 470 | 266 | auto d = sm.depth_; | ||
| HITCBC | 471 | 266 | opt_flags parent = (d > 0) ? eff_stack[d - 1] : parent_eff; | 471 | 266 | opt_flags parent = (d > 0) ? eff_stack[d - 1] : parent_eff; | ||
| HITCBC | 472 | 266 | eff_stack[d] = impl::compute_effective_opts(parent, sm.own_opts_); | 472 | 266 | eff_stack[d] = impl::compute_effective_opts(parent, sm.own_opts_); | ||
| HITCBC | 473 | 266 | sm.effective_opts_ = eff_stack[d]; | 473 | 266 | sm.effective_opts_ = eff_stack[d]; | ||
| HITCBC | 474 | 266 | sm.depth_ += 1; // increase by 1 (parent is at depth 0) | 474 | 266 | sm.depth_ += 1; // increase by 1 (parent is at depth 0) | ||
| HITCBC | 475 | 266 | sm.first_entry_ += entry_offset; | 475 | 266 | sm.first_entry_ += entry_offset; | ||
| HITCBC | 476 | 266 | sm.skip_ += entry_offset; | 476 | 266 | sm.skip_ += entry_offset; | ||
| HITCBC | 477 | 266 | impl_->matchers.push_back(std::move(sm)); | 477 | 266 | impl_->matchers.push_back(std::move(sm)); | ||
| 478 | } | 478 | } | |||||
| 479 | 479 | |||||||
| 480 | // Inline sub's entries | 480 | // Inline sub's entries | |||||
| HITCBC | 481 | 98 | for(auto& se : sub.impl_->entries) | 481 | 98 | for(auto& se : sub.impl_->entries) | ||
| 482 | { | 482 | { | |||||
| HITCBC | 483 | 49 | se.matcher_idx += matcher_offset; | 483 | 49 | se.matcher_idx += matcher_offset; | ||
| HITCBC | 484 | 49 | impl_->entries.push_back(std::move(se)); | 484 | 49 | impl_->entries.push_back(std::move(se)); | ||
| 485 | } | 485 | } | |||||
| 486 | 486 | |||||||
| 487 | // Set parent matcher's skip | 487 | // Set parent matcher's skip | |||||
| 488 | // Need to re-fetch since vector may have reallocated | 488 | // Need to re-fetch since vector may have reallocated | |||||
| HITCBC | 489 | 49 | impl_->matchers[parent_matcher_idx].skip_ = impl_->entries.size(); | 489 | 49 | impl_->matchers[parent_matcher_idx].skip_ = impl_->entries.size(); | ||
| 490 | 490 | |||||||
| 491 | // Merge global methods | 491 | // Merge global methods | |||||
| HITCBC | 492 | 49 | impl_->global_methods_ |= sub.impl_->global_methods_; | 492 | 49 | impl_->global_methods_ |= sub.impl_->global_methods_; | ||
| HITCBC | 493 | 49 | for(auto& v : sub.impl_->global_custom_verbs_) | 493 | 49 | for(auto& v : sub.impl_->global_custom_verbs_) | ||
| MISUBC | 494 | ✗ | impl_->global_custom_verbs_.push_back(std::move(v)); | 494 | ✗ | impl_->global_custom_verbs_.push_back(std::move(v)); | ||
| HITCBC | 495 | 49 | impl_->rebuild_global_allow_header(); | 495 | 49 | impl_->rebuild_global_allow_header(); | ||
| 496 | 496 | |||||||
| 497 | // Move options handler if sub has one and parent doesn't | 497 | // Move options handler if sub has one and parent doesn't | |||||
| HITCBC | 498 | 49 | if(sub.impl_->options_handler_ && !impl_->options_handler_) | 498 | 49 | if(sub.impl_->options_handler_ && !impl_->options_handler_) | ||
| MISUBC | 499 | ✗ | impl_->options_handler_ = std::move(sub.impl_->options_handler_); | 499 | ✗ | impl_->options_handler_ = std::move(sub.impl_->options_handler_); | ||
| 500 | 500 | |||||||
| HITCBC | 501 | 49 | sub.impl_.reset(); | 501 | 49 | sub.impl_.reset(); | ||
| 502 | } | 502 | } | |||||
| 503 | 503 | |||||||
| 504 | std::size_t | 504 | std::size_t | |||||
| HITCBC | 505 | 77 | router_base:: | 505 | 77 | router_base:: | ||
| 506 | new_route( | 506 | new_route( | |||||
| 507 | std::string_view pattern) | 507 | std::string_view pattern) | |||||
| 508 | { | 508 | { | |||||
| HITCBC | 509 | 77 | impl_->finalize_pending(); | 509 | 77 | impl_->finalize_pending(); | ||
| 510 | 510 | |||||||
| HITCBC | 511 | 77 | if(pattern.empty()) | 511 | 77 | if(pattern.empty()) | ||
| MISUBC | 512 | ✗ | throw_invalid_argument(); | 512 | ✗ | throw_invalid_argument(); | ||
| 513 | 513 | |||||||
| HITCBC | 514 | 77 | auto const idx = impl_->matchers.size(); | 514 | 77 | auto const idx = impl_->matchers.size(); | ||
| HITCBC | 515 | 77 | impl_->matchers.emplace_back(pattern, true); | 515 | 77 | impl_->matchers.emplace_back(pattern, true); | ||
| HITCBC | 516 | 77 | auto& m = impl_->matchers.back(); | 516 | 77 | auto& m = impl_->matchers.back(); | ||
| HITCBC | 517 | 77 | if(m.error()) | 517 | 77 | if(m.error()) | ||
| HITCBC | 518 | 12 | throw_invalid_argument(); | 518 | 12 | throw_invalid_argument(); | ||
| HITCBC | 519 | 65 | m.first_entry_ = impl_->entries.size(); | 519 | 65 | m.first_entry_ = impl_->entries.size(); | ||
| HITCBC | 520 | 65 | m.effective_opts_ = impl::compute_effective_opts(0, impl_->opt_); | 520 | 65 | m.effective_opts_ = impl::compute_effective_opts(0, impl_->opt_); | ||
| HITCBC | 521 | 65 | m.own_opts_ = impl_->opt_; | 521 | 65 | m.own_opts_ = impl_->opt_; | ||
| HITCBC | 522 | 65 | m.depth_ = 0; | 522 | 65 | m.depth_ = 0; | ||
| 523 | 523 | |||||||
| HITCBC | 524 | 65 | impl_->pending_route_ = idx; | 524 | 65 | impl_->pending_route_ = idx; | ||
| HITCBC | 525 | 65 | return idx; | 525 | 65 | return idx; | ||
| 526 | } | 526 | } | |||||
| 527 | 527 | |||||||
| 528 | void | 528 | void | |||||
| HITCBC | 529 | 58 | router_base:: | 529 | 58 | router_base:: | ||
| 530 | add_to_route( | 530 | add_to_route( | |||||
| 531 | std::size_t idx, | 531 | std::size_t idx, | |||||
| 532 | http::method verb, | 532 | http::method verb, | |||||
| 533 | handlers hn) | 533 | handlers hn) | |||||
| 534 | { | 534 | { | |||||
| HITCBC | 535 | 58 | if(verb == http::method::unknown) | 535 | 58 | if(verb == http::method::unknown) | ||
| MISUBC | 536 | ✗ | throw_invalid_argument(); | 536 | ✗ | throw_invalid_argument(); | ||
| 537 | 537 | |||||||
| HITCBC | 538 | 58 | auto& m = impl_->matchers[idx]; | 538 | 58 | auto& m = impl_->matchers[idx]; | ||
| HITCBC | 539 | 116 | for(std::size_t i = 0; i < hn.n; ++i) | 539 | 116 | for(std::size_t i = 0; i < hn.n; ++i) | ||
| 540 | { | 540 | { | |||||
| HITCBC | 541 | 58 | impl_->entries.emplace_back(verb, std::move(hn.p[i])); | 541 | 58 | impl_->entries.emplace_back(verb, std::move(hn.p[i])); | ||
| HITCBC | 542 | 58 | impl_->entries.back().matcher_idx = idx; | 542 | 58 | impl_->entries.back().matcher_idx = idx; | ||
| HITCBC | 543 | 58 | impl_->update_allow_for_entry(m, impl_->entries.back()); | 543 | 58 | impl_->update_allow_for_entry(m, impl_->entries.back()); | ||
| 544 | } | 544 | } | |||||
| HITCBC | 545 | 58 | impl_->rebuild_global_allow_header(); | 545 | 58 | impl_->rebuild_global_allow_header(); | ||
| HITCBC | 546 | 58 | } | 546 | 58 | } | ||
| 547 | 547 | |||||||
| 548 | void | 548 | void | |||||
| HITCBC | 549 | 10 | router_base:: | 549 | 10 | router_base:: | ||
| 550 | add_to_route( | 550 | add_to_route( | |||||
| 551 | std::size_t idx, | 551 | std::size_t idx, | |||||
| 552 | std::string_view verb, | 552 | std::string_view verb, | |||||
| 553 | handlers hn) | 553 | handlers hn) | |||||
| 554 | { | 554 | { | |||||
| HITCBC | 555 | 10 | auto& m = impl_->matchers[idx]; | 555 | 10 | auto& m = impl_->matchers[idx]; | ||
| 556 | 556 | |||||||
| HITCBC | 557 | 10 | if(verb.empty()) | 557 | 10 | if(verb.empty()) | ||
| 558 | { | 558 | { | |||||
| 559 | // all methods | 559 | // all methods | |||||
| HITCBC | 560 | 16 | for(std::size_t i = 0; i < hn.n; ++i) | 560 | 16 | for(std::size_t i = 0; i < hn.n; ++i) | ||
| 561 | { | 561 | { | |||||
| HITCBC | 562 | 8 | impl_->entries.emplace_back(std::move(hn.p[i])); | 562 | 8 | impl_->entries.emplace_back(std::move(hn.p[i])); | ||
| HITCBC | 563 | 8 | impl_->entries.back().matcher_idx = idx; | 563 | 8 | impl_->entries.back().matcher_idx = idx; | ||
| HITCBC | 564 | 8 | impl_->update_allow_for_entry(m, impl_->entries.back()); | 564 | 8 | impl_->update_allow_for_entry(m, impl_->entries.back()); | ||
| 565 | } | 565 | } | |||||
| 566 | } | 566 | } | |||||
| 567 | else | 567 | else | |||||
| 568 | { | 568 | { | |||||
| 569 | // specific method string | 569 | // specific method string | |||||
| HITCBC | 570 | 4 | for(std::size_t i = 0; i < hn.n; ++i) | 570 | 4 | for(std::size_t i = 0; i < hn.n; ++i) | ||
| 571 | { | 571 | { | |||||
| HITCBC | 572 | 2 | impl_->entries.emplace_back(verb, std::move(hn.p[i])); | 572 | 2 | impl_->entries.emplace_back(verb, std::move(hn.p[i])); | ||
| HITCBC | 573 | 2 | impl_->entries.back().matcher_idx = idx; | 573 | 2 | impl_->entries.back().matcher_idx = idx; | ||
| HITCBC | 574 | 2 | impl_->update_allow_for_entry(m, impl_->entries.back()); | 574 | 2 | impl_->update_allow_for_entry(m, impl_->entries.back()); | ||
| 575 | } | 575 | } | |||||
| 576 | } | 576 | } | |||||
| HITCBC | 577 | 10 | impl_->rebuild_global_allow_header(); | 577 | 10 | impl_->rebuild_global_allow_header(); | ||
| HITCBC | 578 | 10 | } | 578 | 10 | } | ||
| 579 | 579 | |||||||
| 580 | void | 580 | void | |||||
| MISUBC | 581 | ✗ | router_base:: | 581 | ✗ | router_base:: | ||
| 582 | finalize_pending() | 582 | finalize_pending() | |||||
| 583 | { | 583 | { | |||||
| MISUBC | 584 | ✗ | if(impl_) | 584 | ✗ | if(impl_) | ||
| MISUBC | 585 | ✗ | impl_->finalize_pending(); | 585 | ✗ | impl_->finalize_pending(); | ||
| MISUBC | 586 | ✗ | } | 586 | ✗ | } | ||
| 587 | 587 | |||||||
| 588 | void | 588 | void | |||||
| HITCBC | 589 | 4 | router_base:: | 589 | 4 | router_base:: | ||
| 590 | set_options_handler_impl( | 590 | set_options_handler_impl( | |||||
| 591 | options_handler_ptr p) | 591 | options_handler_ptr p) | |||||
| 592 | { | 592 | { | |||||
| HITCBC | 593 | 4 | impl_->options_handler_ = std::move(p); | 593 | 4 | impl_->options_handler_ = std::move(p); | ||
| HITCBC | 594 | 4 | } | 594 | 4 | } | ||
| 595 | 595 | |||||||
| 596 | //------------------------------------------------ | 596 | //------------------------------------------------ | |||||
| 597 | // | 597 | // | |||||
| 598 | // dispatch | 598 | // dispatch | |||||
| 599 | // | 599 | // | |||||
| 600 | //------------------------------------------------ | 600 | //------------------------------------------------ | |||||
| 601 | 601 | |||||||
| 602 | route_task | 602 | route_task | |||||
| HITCBC | 603 | 109 | router_base:: | 603 | 109 | router_base:: | ||
| 604 | dispatch( | 604 | dispatch( | |||||
| 605 | http::method verb, | 605 | http::method verb, | |||||
| 606 | urls::url_view const& url, | 606 | urls::url_view const& url, | |||||
| 607 | route_params& p) const | 607 | route_params& p) const | |||||
| 608 | { | 608 | { | |||||
| HITCBC | 609 | 109 | if(verb == http::method::unknown) | 609 | 109 | if(verb == http::method::unknown) | ||
| HITCBC | 610 | 1 | throw_invalid_argument(); | 610 | 1 | throw_invalid_argument(); | ||
| 611 | 611 | |||||||
| HITCBC | 612 | 108 | impl_->ensure_finalized(); | 612 | 108 | impl_->ensure_finalized(); | ||
| 613 | 613 | |||||||
| 614 | // Handle OPTIONS * before normal dispatch | 614 | // Handle OPTIONS * before normal dispatch | |||||
| HITCBC | 615 | 113 | if(verb == http::method::options && | 615 | 113 | if(verb == http::method::options && | ||
| HITCBC | 616 | 113 | url.encoded_path() == "*") | 616 | 113 | url.encoded_path() == "*") | ||
| 617 | { | 617 | { | |||||
| HITCBC | 618 | 1 | if(impl_->options_handler_) | 618 | 1 | if(impl_->options_handler_) | ||
| 619 | { | 619 | { | |||||
| HITCBC | 620 | 1 | return impl_->options_handler_->invoke( | 620 | 1 | return impl_->options_handler_->invoke( | ||
| HITCBC | 621 | 1 | p, impl_->global_allow_header_); | 621 | 1 | p, impl_->global_allow_header_); | ||
| 622 | } | 622 | } | |||||
| 623 | } | 623 | } | |||||
| 624 | 624 | |||||||
| 625 | // Initialize params | 625 | // Initialize params | |||||
| HITCBC | 626 | 107 | auto& pv = *route_params_access{p}; | 626 | 107 | auto& pv = *route_params_access{p}; | ||
| HITCBC | 627 | 107 | pv.kind_ = is_plain; | 627 | 107 | pv.kind_ = is_plain; | ||
| HITCBC | 628 | 107 | pv.verb_ = verb; | 628 | 107 | pv.verb_ = verb; | ||
| HITCBC | 629 | 107 | pv.verb_str_.clear(); | 629 | 107 | pv.verb_str_.clear(); | ||
| HITCBC | 630 | 107 | pv.ec_.clear(); | 630 | 107 | pv.ec_.clear(); | ||
| HITCBC | 631 | 107 | pv.ep_ = nullptr; | 631 | 107 | pv.ep_ = nullptr; | ||
| HITCBC | 632 | 107 | p.params.clear(); | 632 | 107 | p.params.clear(); | ||
| HITCBC | 633 | 107 | pv.decoded_path_ = pct_decode_path(url.encoded_path()); | 633 | 107 | pv.decoded_path_ = pct_decode_path(url.encoded_path()); | ||
| HITCBC | 634 | 107 | if(pv.decoded_path_.empty() || pv.decoded_path_.back() != '/') | 634 | 107 | if(pv.decoded_path_.empty() || pv.decoded_path_.back() != '/') | ||
| 635 | { | 635 | { | |||||
| HITCBC | 636 | 70 | pv.decoded_path_.push_back('/'); | 636 | 70 | pv.decoded_path_.push_back('/'); | ||
| HITCBC | 637 | 70 | pv.addedSlash_ = true; | 637 | 70 | pv.addedSlash_ = true; | ||
| 638 | } | 638 | } | |||||
| 639 | else | 639 | else | |||||
| 640 | { | 640 | { | |||||
| HITCBC | 641 | 37 | pv.addedSlash_ = false; | 641 | 37 | pv.addedSlash_ = false; | ||
| 642 | } | 642 | } | |||||
| HITCBC | 643 | 107 | p.base_path = { pv.decoded_path_.data(), 0 }; | 643 | 107 | p.base_path = { pv.decoded_path_.data(), 0 }; | ||
| HITCBC | 644 | 107 | auto const subtract = (pv.addedSlash_ && pv.decoded_path_.size() > 1) ? 1 : 0; | 644 | 107 | auto const subtract = (pv.addedSlash_ && pv.decoded_path_.size() > 1) ? 1 : 0; | ||
| HITCBC | 645 | 107 | p.path = { pv.decoded_path_.data(), pv.decoded_path_.size() - subtract }; | 645 | 107 | p.path = { pv.decoded_path_.data(), pv.decoded_path_.size() - subtract }; | ||
| 646 | 646 | |||||||
| HITCBC | 647 | 107 | return impl_->dispatch_loop(p, verb == http::method::options); | 647 | 107 | return impl_->dispatch_loop(p, verb == http::method::options); | ||
| 648 | } | 648 | } | |||||
| 649 | 649 | |||||||
| 650 | route_task | 650 | route_task | |||||
| HITCBC | 651 | 6 | router_base:: | 651 | 6 | router_base:: | ||
| 652 | dispatch( | 652 | dispatch( | |||||
| 653 | std::string_view verb, | 653 | std::string_view verb, | |||||
| 654 | urls::url_view const& url, | 654 | urls::url_view const& url, | |||||
| 655 | route_params& p) const | 655 | route_params& p) const | |||||
| 656 | { | 656 | { | |||||
| HITCBC | 657 | 6 | if(verb.empty()) | 657 | 6 | if(verb.empty()) | ||
| HITCBC | 658 | 1 | throw_invalid_argument(); | 658 | 1 | throw_invalid_argument(); | ||
| 659 | 659 | |||||||
| HITCBC | 660 | 5 | impl_->ensure_finalized(); | 660 | 5 | impl_->ensure_finalized(); | ||
| 661 | 661 | |||||||
| HITCBC | 662 | 5 | auto const method = http::string_to_method(verb); | 662 | 5 | auto const method = http::string_to_method(verb); | ||
| HITCBC | 663 | 5 | bool const is_options = (method == http::method::options); | 663 | 5 | bool const is_options = (method == http::method::options); | ||
| 664 | 664 | |||||||
| 665 | // Handle OPTIONS * before normal dispatch | 665 | // Handle OPTIONS * before normal dispatch | |||||
| HITCBC | 666 | 5 | if(is_options && url.encoded_path() == "*") | 666 | 5 | if(is_options && url.encoded_path() == "*") | ||
| 667 | { | 667 | { | |||||
| MISUBC | 668 | ✗ | if(impl_->options_handler_) | 668 | ✗ | if(impl_->options_handler_) | ||
| 669 | { | 669 | { | |||||
| MISUBC | 670 | ✗ | return impl_->options_handler_->invoke( | 670 | ✗ | return impl_->options_handler_->invoke( | ||
| MISUBC | 671 | ✗ | p, impl_->global_allow_header_); | 671 | ✗ | p, impl_->global_allow_header_); | ||
| 672 | } | 672 | } | |||||
| 673 | } | 673 | } | |||||
| 674 | 674 | |||||||
| 675 | // Initialize params | 675 | // Initialize params | |||||
| HITCBC | 676 | 5 | auto& pv = *route_params_access{p}; | 676 | 5 | auto& pv = *route_params_access{p}; | ||
| HITCBC | 677 | 5 | pv.kind_ = is_plain; | 677 | 5 | pv.kind_ = is_plain; | ||
| HITCBC | 678 | 5 | pv.verb_ = method; | 678 | 5 | pv.verb_ = method; | ||
| HITCBC | 679 | 5 | if(pv.verb_ == http::method::unknown) | 679 | 5 | if(pv.verb_ == http::method::unknown) | ||
| HITCBC | 680 | 4 | pv.verb_str_ = verb; | 680 | 4 | pv.verb_str_ = verb; | ||
| 681 | else | 681 | else | |||||
| HITCBC | 682 | 1 | pv.verb_str_.clear(); | 682 | 1 | pv.verb_str_.clear(); | ||
| HITCBC | 683 | 5 | pv.ec_.clear(); | 683 | 5 | pv.ec_.clear(); | ||
| HITCBC | 684 | 5 | pv.ep_ = nullptr; | 684 | 5 | pv.ep_ = nullptr; | ||
| HITCBC | 685 | 5 | p.params.clear(); | 685 | 5 | p.params.clear(); | ||
| HITCBC | 686 | 5 | pv.decoded_path_ = pct_decode_path(url.encoded_path()); | 686 | 5 | pv.decoded_path_ = pct_decode_path(url.encoded_path()); | ||
| HITCBC | 687 | 5 | if(pv.decoded_path_.empty() || pv.decoded_path_.back() != '/') | 687 | 5 | if(pv.decoded_path_.empty() || pv.decoded_path_.back() != '/') | ||
| 688 | { | 688 | { | |||||
| MISUBC | 689 | ✗ | pv.decoded_path_.push_back('/'); | 689 | ✗ | pv.decoded_path_.push_back('/'); | ||
| MISUBC | 690 | ✗ | pv.addedSlash_ = true; | 690 | ✗ | pv.addedSlash_ = true; | ||
| 691 | } | 691 | } | |||||
| 692 | else | 692 | else | |||||
| 693 | { | 693 | { | |||||
| HITCBC | 694 | 5 | pv.addedSlash_ = false; | 694 | 5 | pv.addedSlash_ = false; | ||
| 695 | } | 695 | } | |||||
| HITCBC | 696 | 5 | p.base_path = { pv.decoded_path_.data(), 0 }; | 696 | 5 | p.base_path = { pv.decoded_path_.data(), 0 }; | ||
| HITCBC | 697 | 5 | auto const subtract = (pv.addedSlash_ && pv.decoded_path_.size() > 1) ? 1 : 0; | 697 | 5 | auto const subtract = (pv.addedSlash_ && pv.decoded_path_.size() > 1) ? 1 : 0; | ||
| HITCBC | 698 | 5 | p.path = { pv.decoded_path_.data(), pv.decoded_path_.size() - subtract }; | 698 | 5 | p.path = { pv.decoded_path_.data(), pv.decoded_path_.size() - subtract }; | ||
| 699 | 699 | |||||||
| HITCBC | 700 | 5 | return impl_->dispatch_loop(p, is_options); | 700 | 5 | return impl_->dispatch_loop(p, is_options); | ||
| 701 | } | 701 | } | |||||
| 702 | 702 | |||||||
| 703 | } // detail | 703 | } // detail | |||||
| 704 | } // http | 704 | } // http | |||||
| 705 | } // boost | 705 | } // boost | |||||