From 7d0d5add849de4b4c63b851307aa06351b14c7f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fatih=20Kadir=20Ak=C4=B1n?= Date: Fri, 22 Jan 2016 22:14:22 +0200 Subject: [PATCH] Implement radix algorithm for routing (thanks to beryl) --- .gitignore | 8 +++++++- shard.yml | 4 ++++ src/kemal/handler.cr | 21 +++++++++++++++++---- src/kemal/route.cr | 6 +++--- 4 files changed, 31 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index f31288b..a32ed2c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,8 @@ -.crystal +/libs/ +/.crystal/ +/.shards/ *.log + +# Libraries don't need dependency lock +# Dependencies will be locked in application that uses them +/shard.lock diff --git a/shard.yml b/shard.yml index ca9685d..b58e222 100644 --- a/shard.yml +++ b/shard.yml @@ -1,5 +1,9 @@ name: kemal version: 0.7.0 +dependencies: + beryl: + github: luislavena/beryl + author: - Serdar Dogruyol diff --git a/src/kemal/handler.cr b/src/kemal/handler.cr index c5a8ac4..e003f68 100644 --- a/src/kemal/handler.cr +++ b/src/kemal/handler.cr @@ -1,4 +1,5 @@ require "http/server" +require "beryl/beryl/routing/tree" # Kemal::Handler is the main handler which handles all the HTTP requests. Routing, parsing, rendering e.g # are done in this handler. @@ -7,7 +8,7 @@ class Kemal::Handler < HTTP::Handler INSTANCE = new def initialize - @routes = [] of Route + @tree = Beryl::Routing::Tree.new end def call(request) @@ -16,15 +17,18 @@ class Kemal::Handler < HTTP::Handler end def add_route(method, path, &handler : Kemal::Context -> _) - @routes << Route.new(method, path, &handler) + add_to_radix_tree method, path, Route.new(method, path, &handler) # Registering HEAD route for defined GET routes. - @routes << Route.new("HEAD", path, &handler) if method == "GET" + add_to_radix_tree("HEAD", path, Route.new("HEAD", path, &handler)) if method == "GET" end def process_request(request) url = request.path.not_nil! - @routes.each do |route| + Kemal::Route.check_for_method_override!(request) + lookup = @tree.find radix_path(request.override_method, request.path) + if lookup.found? + route = lookup.payload as Route if route.match?(request) context = Context.new(request, route) begin @@ -39,4 +43,13 @@ class Kemal::Handler < HTTP::Handler # Render 404 unless a route matches return render_404 end + + private def radix_path(method, path) + "#{method} #{path}" + end + + private def add_to_radix_tree(method, path, route) + node = radix_path method, path + @tree.add node, route + end end diff --git a/src/kemal/route.cr b/src/kemal/route.cr index ff34373..61ae84e 100644 --- a/src/kemal/route.cr +++ b/src/kemal/route.cr @@ -10,7 +10,7 @@ class Kemal::Route end def match?(request) - check_for_method_override!(request) + self.class.check_for_method_override!(request) return nil unless request.override_method == @method return true if request.path.not_nil!.includes?(':') && request.path.not_nil! == @path request.path.not_nil!.match(@compiled_regex) do |url_params| @@ -20,7 +20,7 @@ class Kemal::Route end # Checks if request params contain _method param to override request incoming method - def check_for_method_override!(request) + def self.check_for_method_override!(request) request.override_method = request.method if request.method == "POST" params = Kemal::ParamParser.new(self, request).parse_request @@ -31,7 +31,7 @@ class Kemal::Route end # Checks if method contained in _method param is valid one - def override_method_valid?(override_method) + def self.override_method_valid?(override_method) return false unless override_method.is_a?(String) override_method = override_method.upcase return (override_method == "PUT" || override_method == "PATCH" || override_method == "DELETE")