From ec11698ab1fe7d114f717d34a444f19390c4c707 Mon Sep 17 00:00:00 2001 From: Serdar Dogruyol Date: Wed, 9 Nov 2016 19:18:39 +0400 Subject: [PATCH] Only / Exclude Paths in Middleware (#245) Add only / exclude paths in Middlewares --- spec/handler_spec.cr | 95 +++++++++++++++++++++++++++++++++++++++- src/kemal.cr | 1 + src/kemal/ext/handler.cr | 64 +++++++++++++++++++++++++++ 3 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 src/kemal/ext/handler.cr diff --git a/spec/handler_spec.cr b/spec/handler_spec.cr index 087d442..b2a65db 100644 --- a/spec/handler_spec.cr +++ b/spec/handler_spec.cr @@ -1,12 +1,52 @@ require "./spec_helper" -class CustomTestHandler < HTTP::Handler +class CustomTestHandler < Kemal::Handler def call(env) env.response << "Kemal" call_next env end end +class OnlyHandler < Kemal::Handler + only ["/only"] + + def call(env) + return call_next(env) unless only_match?(env) + env.response.print "Only" + call_next env + end +end + +class ExcludeHandler < Kemal::Handler + exclude ["/exclude"] + + def call(env) + return call_next(env) if exclude_match?(env) + env.response.print "Exclude" + call_next env + end +end + +class PostOnlyHandler < Kemal::Handler + only ["/only", "/route1", "/route2"], "POST" + + def call(env) + return call_next(env) unless only_match?(env) + env.response.print "Only" + call_next env + end +end + +class PostExcludeHandler < Kemal::Handler + exclude ["/exclude"], "POST" + + def call(env) + return call_next(env) if exclude_match?(env) + env.response.print "Exclude" + call_next env + end +end + describe "Handler" do it "adds custom handler before before_*" do filter_middleware = Kemal::Middleware::Filter.new @@ -27,4 +67,57 @@ describe "Handler" do client_response.status_code.should eq(200) client_response.body.should eq("Kemal is so Great") end + + it "runs specified only_routes in middleware" do + get "/only" do |env| + "Get" + end + add_handler OnlyHandler.new + request = HTTP::Request.new("GET", "/only") + client_response = call_request_on_app(request) + client_response.body.should eq "OnlyGet" + end + + it "doesn't run specified exclude_routes in middleware" do + get "/" do |env| + "Get" + end + get "/exclude" do + "Exclude" + end + add_handler ExcludeHandler.new + request = HTTP::Request.new("GET", "/") + client_response = call_request_on_app(request) + client_response.body.should eq "ExcludeGet" + end + + it "runs specified only_routes with method in middleware" do + post "/only" do + "Post" + end + get "/only" do + "Get" + end + add_handler PostOnlyHandler.new + request = HTTP::Request.new("POST", "/only") + client_response = call_request_on_app(request) + client_response.body.should eq "OnlyPost" + end + + it "doesn't run specified exclude_routes with method in middleware" do + post "/exclude" do + "Post" + end + post "/only" do + "Post" + end + add_handler PostOnlyHandler.new + request = HTTP::Request.new("POST", "/only") + client_response = call_request_on_app(request) + client_response.body.should eq "OnlyPost" + add_handler PostExcludeHandler.new + request = HTTP::Request.new("POST", "/only") + client_response = call_request_on_app(request) + client_response.body.should eq "OnlyExcludePost" + end end diff --git a/src/kemal.cr b/src/kemal.cr index 2241768..06637fb 100644 --- a/src/kemal.cr +++ b/src/kemal.cr @@ -1,6 +1,7 @@ require "http" require "multipart" require "./kemal/*" +require "./kemal/ext/*" require "./kemal/helpers/*" require "./kemal/middleware/*" diff --git a/src/kemal/ext/handler.cr b/src/kemal/ext/handler.cr new file mode 100644 index 0000000..c4d525a --- /dev/null +++ b/src/kemal/ext/handler.cr @@ -0,0 +1,64 @@ +class Kemal::Handler < HTTP::Handler + @@only_routes_tree = Radix::Tree(String).new + @@exclude_routes_tree = Radix::Tree(String).new + + macro only(paths, method = "GET") + class_name = {{@type.name}} + {{paths}}.each do |path| + @@only_routes_tree.add "#{class_name}/#{{{method}}.downcase}#{path}", "/#{{{method}}.downcase}#{path}" + end + end + + macro exclude(paths, method = "GET") + class_name = {{@type.name}} + {{paths}}.each do |path| + @@exclude_routes_tree.add "#{class_name}/#{{{method}}.downcase}#{path}", "/#{{{method}}.downcase}#{path}" + end + end + + def call(env) + call_next(env) + end + + # Processes the path based on `only` paths which is a `Array(String)`. + # If the path is not found on `only` conditions the handler will continue processing. + # If the path is found in `only` conditions it'll stop processing and will pass the request + # to next handler. + # + # However this is not done automatically. All handlers must inherit from `Kemal::Handler`. + # + # OnlyHandler < Kemal::Handler + # only ["/"] + # + # def call(env) + # return call_next(env) unless only_match?(env) + # puts "If the path is / i will be doing some processing here." + # end + # end + def only_match?(env) + @@only_routes_tree.find(radix_path(env.request.method, env.request.path)).found? + end + + # Processes the path based on `exclude` paths which is a `Array(String)`. + # If the path is not found on `exclude` conditions the handler will continue processing. + # If the path is found in `exclude` conditions it'll stop processing and will pass the request + # to next handler. + # + # However this is not done automatically. All handlers must inherit from `Kemal::Handler`. + # + # ExcludeHandler < Kemal::Handler + # exclude ["/"] + # + # def call(env) + # return call_next(env) if exclude_match?(env) + # puts "If the path is not / i will be doing some processing here." + # end + # end + def exclude_match?(env) + @@exclude_routes_tree.find(radix_path(env.request.method, env.request.path)).found? + end + + private def radix_path(method : String, path) + "#{self.class}/#{method.downcase}#{path}" + end +end