@@ -95,15 +95,10 @@ namespace detail {
9595// route_resolved and before_handler firing sites landed.
9696
9797void webserver_impl::invalidate_route_cache () {
98- // Clear both the v1 and v2 caches. v1's cache is keyed on
99- // standardized_url only; v2's is keyed on (method, path). A
100- // registration may invalidate both, so clear both atomically.
101- {
102- std::lock_guard<std::mutex> lock (route_cache_mutex_);
103- route_cache_list.clear ();
104- route_cache_map.clear ();
105- }
106- route_cache_v2.clear ();
98+ // Clear the v2 LRU cache. (The v1 dispatch cache — route_cache_list
99+ // / route_cache_map / route_cache_mutex_ — was removed in TASK-053
100+ // step 3 along with the v1 resolver.)
101+ route_lru_cache.clear ();
107102}
108103
109104// TASK-027: 3-tier route lookup. Pipeline:
@@ -153,7 +148,7 @@ webserver_impl::lookup_v2(http_method method, const std::string& path) {
153148 // into a cache_key on every call, including warm-cache hits.
154149 // cache_key is only constructed when an insert is needed (miss path).
155150 cache_value cached;
156- if (route_cache_v2 .find_by_view (method, lookup_path, cached)) {
151+ if (route_lru_cache .find_by_view (method, lookup_path, cached)) {
157152 result.found = true ;
158153 result.tier = tier_hit::cache;
159154 result.entry = std::move (cached.entry );
@@ -214,7 +209,7 @@ webserver_impl::lookup_v2(http_method method, const std::string& path) {
214209 cache_value v;
215210 v.entry = result.entry ;
216211 v.captured_params = result.captured_params ;
217- route_cache_v2 .insert (key, std::move (v));
212+ route_lru_cache .insert (key, std::move (v));
218213 }
219214
220215 return result;
@@ -224,88 +219,15 @@ webserver_impl::lookup_v2(http_method method, const std::string& path) {
224219
225220namespace detail {
226221
227- std::optional<webserver_impl::regex_route_lookup>
228- webserver_impl::lookup_route_cache (const std::string& key) {
229- std::lock_guard<std::mutex> cache_lock (route_cache_mutex_);
230- auto cache_it = route_cache_map.find (key);
231- if (cache_it == route_cache_map.end ()) {
232- return std::nullopt ;
233- }
234- // Cache hit — promote to LRU front and copy the match data while
235- // still under the cache lock, so a concurrent invalidation can't
236- // free the cached endpoint out from under us.
237- // The shared_ptr copy (cached.resource) performs an atomic refcount bump
238- // while holding route_cache_mutex_ exclusively. This is the hottest
239- // point in the dispatch path; the atomic op is unavoidable as long as
240- // callers need the resource to survive a concurrent unregister_resource.
241- route_cache_list.splice (route_cache_list.begin (), route_cache_list, cache_it->second );
242- const route_cache_entry& cached = cache_it->second ->second ;
243- return regex_route_lookup{
244- cached.resource ,
245- cached.matched_endpoint .get_url_pars (),
246- cached.matched_endpoint .get_chunk_positions (),
247- };
248- }
249-
250- std::optional<webserver_impl::regex_route_scan_hit>
251- webserver_impl::scan_regex_routes (const detail::http_endpoint& target) {
252- // Longest-match-wins tie-breaking: prefer the endpoint with more
253- // url-pieces; among equals, prefer the longer url_complete string.
254- bool found = false ;
255- size_t len = 0 ;
256- size_t tot_len = 0 ;
257- map<detail::http_endpoint, std::shared_ptr<http_resource>>::iterator found_endpoint;
258- for (auto it = registered_resources_regex.begin ();
259- it != registered_resources_regex.end (); ++it) {
260- size_t endpoint_pieces_len = it->first .get_url_pieces ().size ();
261- size_t endpoint_tot_len = it->first .get_url_complete ().size ();
262- if (found && endpoint_pieces_len <= len
263- && !(endpoint_pieces_len == len && endpoint_tot_len > tot_len)) {
264- continue ;
265- }
266- if (!it->first .match (target)) continue ;
267- found = true ;
268- len = endpoint_pieces_len;
269- tot_len = endpoint_tot_len;
270- found_endpoint = it;
271- }
272- if (!found) return std::nullopt ;
273- return regex_route_scan_hit{found_endpoint->first , found_endpoint->second };
274- }
275-
276- void webserver_impl::store_route_cache (const std::string& key,
277- const detail::http_endpoint& matched,
278- std::shared_ptr<http_resource> hrm) {
279- std::lock_guard<std::mutex> cache_lock (route_cache_mutex_);
280- route_cache_list.emplace_front (key, route_cache_entry{matched, std::move (hrm)});
281- route_cache_map[key] = route_cache_list.begin ();
282- if (route_cache_map.size () > ROUTE_CACHE_MAX_SIZE ) {
283- route_cache_map.erase (route_cache_list.back ().first );
284- route_cache_list.pop_back ();
285- }
286- }
287-
288- void webserver_impl::apply_extracted_params (detail::modded_request* mr,
289- const detail::http_endpoint& target,
290- const std::vector<std::string>& url_pars,
291- const std::vector<int >& chunks) {
292- const auto & url_pieces = target.get_url_pieces ();
293- for (unsigned int i = 0 ; i < url_pars.size (); i++) {
294- if (chunks[i] < 0 || static_cast <size_t >(chunks[i]) >= url_pieces.size ()) continue ;
295- mr->dhr ->set_arg (url_pars[i], url_pieces[chunks[i]]);
296- }
297- }
298-
299- // TASK-053 step 2: v2 lookup-backed resolver. Routes finalize_answer
300- // through lookup_v2 (the 3-tier v2 table fronted by route_cache_v2)
301- // instead of the v1 registered_resources* maps. See header comment in
302- // webserver_impl_dispatch.hpp for the full contract.
222+ // TASK-053: v2 lookup-backed resolver. Routes finalize_answer through
223+ // lookup_v2 (the 3-tier v2 table fronted by route_lru_cache) — the v1
224+ // resolver and its helpers (lookup_route_cache, scan_regex_routes,
225+ // store_route_cache, apply_extracted_params) were deleted in TASK-053
226+ // step 3. See header comment in webserver_impl_dispatch.hpp for the
227+ // full contract.
303228//
304- // The dispatch path used to do:
305- // v1: hash registered_resources_str -> walk registered_resources_regex
306- // (with LRU front-end route_cache_list / route_cache_map).
307- // This path now does:
308- // v2: lookup_v2() walks route_cache_v2 -> exact_routes_ ->
229+ // The dispatch path now does:
230+ // lookup_v2() walks route_lru_cache -> exact_routes_ ->
309231// param_and_prefix_routes_ -> regex_routes_, returning a
310232// route_entry whose handler arm is always shared_ptr<http_resource>
311233// (the on_*/route entry points wrap user lambdas in lambda_resource
@@ -317,22 +239,22 @@ void webserver_impl::apply_extracted_params(detail::modded_request* mr,
317239// would also work because single_resource installs a radix prefix at
318240// "/"). Reading registered_resources avoids the captured-params /
319241// route_entry plumbing for a configuration that has no parameters.
320- bool webserver_impl::resolve_resource_for_request_v2 (detail::modded_request* mr,
242+ bool webserver_impl::resolve_resource_for_request (detail::modded_request* mr,
321243 std::shared_ptr<http_resource>& hrm) {
322244 // matched_path_template + matched_is_prefix feed the route_resolved
323245 // and before_handler hook ctxs. Skip the heap allocation when no
324- // hook in either phase is registered. Mirrors the v1 resolver.
246+ // hook in either phase is registered.
325247 const bool need_path_template =
326248 has_hooks_for (hook_phase::route_resolved) ||
327249 has_hooks_for (hook_phase::before_handler);
328250
329251 // single_resource fast path: the one registered endpoint serves
330- // every URL. Read it directly from registered_resources to mirror
331- // the v1 resolver's behaviour. registered_resources_mutex protects
332- // a registration-time data store; under dispatch we still need a
333- // shared lock to make the read-then-copy atomic with respect to a
334- // concurrent unregister_path. (NOTE: single_resource webservers do
335- // not support runtime register/unregister in practice, but the lock
252+ // every URL. Read it directly from registered_resources.
253+ // registered_resources_mutex protects a registration-time data
254+ // store; under dispatch we still need a shared lock to make the
255+ // read-then-copy atomic with respect to a concurrent
256+ // unregister_path. (NOTE: single_resource webservers do not
257+ // support runtime register/unregister in practice, but the lock
336258 // is cheap when uncontended and the safety guarantee is worth it.)
337259 if (parent->single_resource ) {
338260 std::shared_lock registered_resources_lock (registered_resources_mutex);
@@ -366,9 +288,9 @@ bool webserver_impl::resolve_resource_for_request_v2(detail::modded_request* mr,
366288 hrm = *hp;
367289
368290 // Replay captured URL parameters into the request. This is the v2
369- // equivalent of apply_extracted_params (which the v1 resolver calls
370- // on regex-tier hits ). Per-name set_arg matches the v1 behaviour
371- // exactly — duplicates with later wins, etc.
291+ // equivalent of the v1-era apply_extracted_params (removed in
292+ // TASK-053 step 3 ). Per-name set_arg matches v1 behaviour exactly —
293+ // duplicates with later wins, etc.
372294 if (mr->dhr != nullptr ) {
373295 for (const auto & [name, value] : result.captured_params ) {
374296 mr->dhr ->set_arg (name, value);
@@ -378,90 +300,14 @@ bool webserver_impl::resolve_resource_for_request_v2(detail::modded_request* mr,
378300 // Populate the hook ctx scratch slots when at least one hook is
379301 // registered for the phases that read them. v2 cache_value does
380302 // not store the matched URL template; fall back to standardized_url
381- // (matches the v1 cache-hit path; see resolve_resource_for_request
382- // ll. 348-353).
303+ // (matches the v1 cache-hit path the legacy resolver used).
383304 if (need_path_template) {
384305 mr->matched_path_template = mr->standardized_url ;
385306 mr->matched_is_prefix = result.entry .is_prefix ;
386307 }
387308 return true ;
388309}
389310
390- bool webserver_impl::resolve_resource_for_request (detail::modded_request* mr,
391- std::shared_ptr<http_resource>& hrm) {
392- // TASK-048 perf: matched_path_template is only consumed by
393- // fire_route_resolved_gated and the before_handler firing site, both
394- // of which are gated by their respective any_hooks_ entries. Skip the
395- // heap allocation on every matched request when no hooks are registered.
396- const bool need_path_template =
397- has_hooks_for (hook_phase::route_resolved) ||
398- has_hooks_for (hook_phase::before_handler);
399-
400- std::shared_lock registered_resources_lock (registered_resources_mutex);
401-
402- if (parent->single_resource ) {
403- hrm = registered_resources.begin ()->second ;
404- // single_resource: the one registered endpoint serves every URL.
405- // Capture its key for the route_resolved/before_handler hook ctx.
406- if (need_path_template) {
407- const auto & only = *registered_resources.begin ();
408- mr->matched_path_template = only.first .get_url_complete ();
409- mr->matched_is_prefix = only.first .is_family_url ();
410- }
411- return true ;
412- }
413-
414- auto exact_it = registered_resources_str.find (mr->standardized_url .c_str ());
415- if (exact_it != registered_resources_str.end ()) {
416- // Copy the shared_ptr (atomic refcount bump) while the shared_lock
417- // on registered_resources_mutex is still held. The copy extends the
418- // resource's lifetime past a concurrent unregister_path that might
419- // erase this slot — without the copy, dispatch_resource_handler
420- // could hold a dangling pointer. The single atomic op is the
421- // unavoidable cost of the use-after-unregister safety guarantee.
422- hrm = exact_it->second ;
423- // Exact-match: the registration key equals the standardized URL.
424- // Copy into modded_request so the hook context's string_view is
425- // safe across hook calls even if a concurrent unregister_path
426- // erases the slot.
427- if (need_path_template) {
428- mr->matched_path_template = exact_it->first ;
429- mr->matched_is_prefix = false ;
430- }
431- return true ;
432- }
433-
434- if (!parent->regex_checking ) return false ;
435-
436- detail::http_endpoint endpoint (mr->standardized_url .c_str (), false , false , false );
437-
438- if (auto cached = lookup_route_cache (mr->standardized_url )) {
439- hrm = cached->hrm ;
440- apply_extracted_params (mr, endpoint, cached->url_pars , cached->chunks );
441- if (need_path_template) {
442- // Cache layer dropped the matched endpoint at its API boundary;
443- // fall back to the requested URL as a stable approximation of
444- // the path_template (used by the route_resolved hook ctx only).
445- mr->matched_path_template = mr->standardized_url ;
446- mr->matched_is_prefix = false ;
447- }
448- return true ;
449- }
450-
451- auto scan_hit = scan_regex_routes (endpoint);
452- if (!scan_hit) return false ;
453-
454- hrm = scan_hit->hrm ;
455- store_route_cache (mr->standardized_url , scan_hit->endpoint , hrm);
456- apply_extracted_params (mr, endpoint, scan_hit->endpoint .get_url_pars (),
457- scan_hit->endpoint .get_chunk_positions ());
458- if (need_path_template) {
459- mr->matched_path_template = scan_hit->endpoint .get_url_complete ();
460- mr->matched_is_prefix = scan_hit->endpoint .is_family_url ();
461- }
462- return true ;
463- }
464-
465311std::string webserver_impl::serialize_allow_methods (method_set allowed) const {
466312 // TASK-048 review cleanup (findings 3 & 4): delegate to the shared free
467313 // function format_allow_header in detail/method_utils.hpp. The member
0 commit comments