class

Kemal::RouteHandler

Inherits HTTP::Handler < Reference < Object

Constants

INSTANCE = new

Constructors

Instance methods

add_route(method : String, path : String, &handler : HTTP::Server::Context -> _)

Adds a given route to routing tree.

Source
allowed_methods(path : String) : Array(String)

Returns every HTTP method registered for path, in Allow header order, or an empty array when nothing is routed there.

The radix tree is keyed by /METHOD/path, so there is no way to ask it which methods a path carries - each routable verb has to be looked up in turn. HEAD is reported wherever GET is, matching the HEAD -> GET fallback in lookup_route.

Deliberately bypasses the route cache: this only runs for a request that already failed to match, and priming the LRU with one entry per verb would let mismatched requests evict the routes actually being served.

Only verbs the application actually registered are probed, so an app serving GET and POST pays two tree lookups here rather than one per routable verb - this is the path scanner traffic takes. The index is maintained by add_route and rebuilt from the tree by routes=, which are the two supported ways routes get in. A route pushed straight into the tree through the routes getter bypasses both; that path degrades to the pre-405 answer of 404 rather than reporting a wrong Allow.

A ws route is reported as GET: the opening handshake is a GET (RFC 6455 ยง4.1), so a path carrying one does support the method. It gets no HEAD - the upgrade is the only thing served there, and Kemal::WebSocketHandler answers nothing else. The probe sits outside the @registered_methods shortcut above because that index only tracks HTTP routes, so a WebSocket-only app would otherwise report nothing at all. It is skipped once an HTTP route has already put GET in the list.

It adds one lookup in a separate, WebSocket-only tree to every miss: measured at ~40ns on top of the ~490ns a two-verb probe already costs (Crystal 1.21, --release, Apple M-series), and unchanged whether or not the app registers any ws routes.

Source
cached_routes
Source
cached_routes=(cache : LRUCache(String, Radix::Result(Route)))

Setter is synchronized for thread-safety when specs reset the cache.

Source
lookup_route(verb : String, path : String)

Looks up the route from the Radix::Tree for the first time and caches to improve performance. Cache access is synchronized so multiple fibers can call this concurrently.

Source
routes
Source
routes=(routes : Radix::Tree(Route))

Rebuilds the verb index allowed_methods probes with, so a tree handed over wholesale advertises the routes it already carries instead of none.

Source