@@ -282,13 +282,122 @@ static const lib_pattern_t config_libraries[] = {
282282 {NULL , CBM_SVC_NONE , NULL },
283283};
284284
285- /* ── HTTP method inference from function/method name suffix ───── */
285+ /* Route registration frameworks — callee resolves to one of these AND
286+ * has an HTTP method suffix → CBM_SVC_ROUTE_REG.
287+ * Distinguished from HTTP clients: "gin.GET" registers a handler,
288+ * "requests.get" makes an outbound HTTP call. */
289+ static const lib_pattern_t route_reg_libraries [] = {
290+ /* Go */
291+ {"gin-gonic/gin" , CBM_SVC_ROUTE_REG , NULL },
292+ {"gin." , CBM_SVC_ROUTE_REG , NULL },
293+ {"go-chi/chi" , CBM_SVC_ROUTE_REG , NULL },
294+ {"chi." , CBM_SVC_ROUTE_REG , NULL },
295+ {"gorilla/mux" , CBM_SVC_ROUTE_REG , NULL },
296+ {"labstack/echo" , CBM_SVC_ROUTE_REG , NULL },
297+ {"echo." , CBM_SVC_ROUTE_REG , NULL },
298+ {"gofiber/fiber" , CBM_SVC_ROUTE_REG , NULL },
299+ {"fiber." , CBM_SVC_ROUTE_REG , NULL },
300+ {"net/http.ServeMux" , CBM_SVC_ROUTE_REG , NULL },
301+ {"http.ServeMux" , CBM_SVC_ROUTE_REG , NULL },
302+ {"httprouter" , CBM_SVC_ROUTE_REG , NULL },
303+
304+ /* JavaScript / TypeScript */
305+ {"express" , CBM_SVC_ROUTE_REG , NULL },
306+ {"fastify" , CBM_SVC_ROUTE_REG , NULL },
307+ {"koa-router" , CBM_SVC_ROUTE_REG , NULL },
308+ {"hono" , CBM_SVC_ROUTE_REG , NULL },
309+ {"hapi" , CBM_SVC_ROUTE_REG , NULL },
310+
311+ /* Python (non-decorator, e.g., Flask add_url_rule) */
312+ {"flask" , CBM_SVC_ROUTE_REG , NULL },
313+ {"FastAPI" , CBM_SVC_ROUTE_REG , NULL },
314+ {"starlette" , CBM_SVC_ROUTE_REG , NULL },
315+
316+ /* PHP */
317+ {"Laravel" , CBM_SVC_ROUTE_REG , NULL },
318+ {"Illuminate.Routing" , CBM_SVC_ROUTE_REG , NULL },
319+ {"Symfony.Routing" , CBM_SVC_ROUTE_REG , NULL },
320+
321+ /* Kotlin */
322+ {"ktor.server" , CBM_SVC_ROUTE_REG , NULL },
323+ {"ktor.routing" , CBM_SVC_ROUTE_REG , NULL },
324+
325+ /* Rust */
326+ {"actix-web" , CBM_SVC_ROUTE_REG , NULL },
327+ {"actix_web" , CBM_SVC_ROUTE_REG , NULL },
328+ {"axum" , CBM_SVC_ROUTE_REG , NULL },
329+ {"rocket" , CBM_SVC_ROUTE_REG , NULL },
330+
331+ /* Java */
332+ {"Spring" , CBM_SVC_ROUTE_REG , NULL },
333+ {"jakarta.ws.rs" , CBM_SVC_ROUTE_REG , NULL },
334+
335+ /* C# */
336+ {"Microsoft.AspNetCore" , CBM_SVC_ROUTE_REG , NULL },
337+ {"MapGet" , CBM_SVC_ROUTE_REG , NULL },
338+ {"MapPost" , CBM_SVC_ROUTE_REG , NULL },
339+
340+ /* Ruby */
341+ {"ActionDispatch" , CBM_SVC_ROUTE_REG , NULL },
342+ {"Sinatra" , CBM_SVC_ROUTE_REG , NULL },
343+
344+ /* Elixir */
345+ {"Phoenix.Router" , CBM_SVC_ROUTE_REG , NULL },
346+
347+ /* Scala */
348+ {"akka.http.scaladsl.server" , CBM_SVC_ROUTE_REG , NULL },
349+ {"play.api.routing" , CBM_SVC_ROUTE_REG , NULL },
286350
351+ {NULL , CBM_SVC_NONE , NULL },
352+ };
353+
354+ /* Method suffix type (used by both route registration and HTTP client tables) */
287355typedef struct {
288356 const char * suffix ;
289357 const char * method ;
290358} method_suffix_t ;
291359
360+ /* Route registration method suffixes — matched on callee name.
361+ * These are methods on router objects that register handlers. */
362+ static const method_suffix_t route_reg_suffixes [] = {
363+ /* HTTP method registrations */
364+ {".GET" , "GET" },
365+ {".Get" , "GET" },
366+ {".get" , "GET" },
367+ {".POST" , "POST" },
368+ {".Post" , "POST" },
369+ {".post" , "POST" },
370+ {".PUT" , "PUT" },
371+ {".Put" , "PUT" },
372+ {".put" , "PUT" },
373+ {".DELETE" , "DELETE" },
374+ {".Delete" , "DELETE" },
375+ {".delete" , "DELETE" },
376+ {".PATCH" , "PATCH" },
377+ {".Patch" , "PATCH" },
378+ {".patch" , "PATCH" },
379+ /* Handle/HandleFunc (Go stdlib, gorilla) */
380+ {".Handle" , "ANY" },
381+ {".HandleFunc" , "ANY" },
382+ {".handle" , "ANY" },
383+ /* Framework-specific route registration */
384+ {".Route" , "ANY" },
385+ {".route" , "ANY" },
386+ {"::get" , "GET" },
387+ {"::post" , "POST" },
388+ {"::put" , "PUT" },
389+ {"::delete" , "DELETE" },
390+ {"::patch" , "PATCH" },
391+ /* Minimal API (C# ASP.NET) */
392+ {".MapGet" , "GET" },
393+ {".MapPost" , "POST" },
394+ {".MapPut" , "PUT" },
395+ {".MapDelete" , "DELETE" },
396+ {NULL , NULL },
397+ };
398+
399+ /* ── HTTP method inference from function/method name suffix ───── */
400+
292401static const method_suffix_t method_suffixes [] = {
293402 {".get" , "GET" }, {".Get" , "GET" }, {".GET" , "GET" },
294403 {".post" , "POST" }, {".Post" , "POST" }, {".POST" , "POST" },
@@ -331,7 +440,14 @@ cbm_svc_kind_t cbm_service_pattern_match(const char *resolved_qn) {
331440 return CBM_SVC_NONE ;
332441 }
333442
334- const lib_pattern_t * p = match_qn (resolved_qn , http_libraries );
443+ /* Route registration checked first — prevents gin/echo from matching
444+ * as HTTP clients (both have .get/.post suffixes). */
445+ const lib_pattern_t * p = match_qn (resolved_qn , route_reg_libraries );
446+ if (p ) {
447+ return p -> kind ;
448+ }
449+
450+ p = match_qn (resolved_qn , http_libraries );
335451 if (p ) {
336452 return p -> kind ;
337453 }
@@ -363,6 +479,20 @@ const char *cbm_service_pattern_http_method(const char *callee_name) {
363479 return NULL ;
364480}
365481
482+ const char * cbm_service_pattern_route_method (const char * callee_name ) {
483+ if (!callee_name ) {
484+ return NULL ;
485+ }
486+ size_t clen = strlen (callee_name );
487+ for (int i = 0 ; route_reg_suffixes [i ].suffix != NULL ; i ++ ) {
488+ size_t slen = strlen (route_reg_suffixes [i ].suffix );
489+ if (clen >= slen && strcmp (callee_name + clen - slen , route_reg_suffixes [i ].suffix ) == 0 ) {
490+ return route_reg_suffixes [i ].method ;
491+ }
492+ }
493+ return NULL ;
494+ }
495+
366496const char * cbm_service_pattern_broker (const char * resolved_qn ) {
367497 if (!resolved_qn ) {
368498 return NULL ;
0 commit comments