kemal/src/kemal/filter_handler.cr

91 lines
3.7 KiB
Crystal
Raw Normal View History

2016-12-03 22:43:30 +00:00
module Kemal
2017-03-03 20:42:08 +00:00
# :nodoc:
2016-12-24 11:22:44 +00:00
class FilterHandler
include HTTP::Handler
INSTANCE = new
# This middleware is lazily instantiated and added to the handlers as soon as a call to `after_X` or `before_X` is made.
def initialize
@tree = Radix::Tree(Array(FilterBlock)).new
Kemal.config.add_filter_handler(self)
end
2017-10-06 11:53:53 +00:00
# The call order of the filters is `before_all -> before_x -> X -> after_x -> after_all`.
2017-08-25 13:41:02 +00:00
def call(context : HTTP::Server::Context)
2018-08-13 16:21:18 +00:00
return call_next(context) unless context.route_found?
call_block_for_path_type("ALL", context.request.path, :before, context)
2018-06-30 12:12:13 +00:00
call_block_for_path_type(context.request.method, context.request.path, :before, context)
if Kemal.config.error_handlers.has_key?(context.response.status_code)
raise Kemal::Exceptions::CustomException.new(context)
end
call_next(context)
2018-06-30 12:12:13 +00:00
call_block_for_path_type(context.request.method, context.request.path, :after, context)
call_block_for_path_type("ALL", context.request.path, :after, context)
context
end
2017-10-06 11:53:53 +00:00
# :nodoc: This shouldn't be called directly, it's not private because
# I need to call it for testing purpose since I can't call the macros in the spec.
# It adds the block for the corresponding verb/path/type combination to the tree.
2017-08-25 13:41:02 +00:00
def _add_route_filter(verb : String, path, type, &block : HTTP::Server::Context -> _)
2016-02-17 19:35:55 +00:00
lookup = lookup_filters_for_path_type(verb, path, type)
2016-11-10 20:54:25 +00:00
if lookup.found? && lookup.payload.is_a?(Array(FilterBlock))
2018-11-01 11:29:05 +00:00
lookup.payload << FilterBlock.new(&block)
2016-02-17 19:35:55 +00:00
else
2016-11-10 20:54:25 +00:00
@tree.add radix_path(verb, path, type), [FilterBlock.new(&block)]
2016-02-17 19:35:55 +00:00
end
end
2017-10-06 11:53:53 +00:00
# This can be called directly but it's simpler to just use the macros,
# it will check if another filter is not already defined for this
# verb/path/type and proceed to call `add_route_filter`
2017-08-25 13:41:02 +00:00
def before(verb : String, path : String = "*", &block : HTTP::Server::Context -> _)
_add_route_filter verb, path, :before, &block
end
2017-10-06 11:53:53 +00:00
# This can be called directly but it's simpler to just use the macros,
# it will check if another filter is not already defined for this
# verb/path/type and proceed to call `add_route_filter`
2017-08-25 13:41:02 +00:00
def after(verb : String, path : String = "*", &block : HTTP::Server::Context -> _)
_add_route_filter verb, path, :after, &block
end
# This will fetch the block for the verb/path/type from the tree and call it.
2017-08-25 13:41:02 +00:00
private def call_block_for_path_type(verb : String?, path : String, type, context : HTTP::Server::Context)
2016-02-17 19:35:55 +00:00
lookup = lookup_filters_for_path_type(verb, path, type)
2016-11-10 20:54:25 +00:00
if lookup.found? && lookup.payload.is_a? Array(FilterBlock)
2018-11-01 11:29:05 +00:00
blocks = lookup.payload
blocks.each &.call(context)
end
end
2016-02-17 19:35:55 +00:00
# This checks is filter is already defined for the verb/path/type combination
2017-08-25 13:41:02 +00:00
private def filter_for_path_type_defined?(verb : String, path : String, type)
2016-02-17 19:35:55 +00:00
lookup = @tree.find radix_path(verb, path, type)
2016-11-10 20:54:25 +00:00
lookup.found? && lookup.payload.is_a? FilterBlock
end
2016-02-17 19:35:55 +00:00
# This returns a lookup for verb/path/type
2017-08-25 13:41:02 +00:00
private def lookup_filters_for_path_type(verb : String?, path : String, type)
2016-02-17 19:35:55 +00:00
@tree.find radix_path(verb, path, type)
end
2017-08-25 13:41:02 +00:00
private def radix_path(verb : String?, path : String, type : Symbol)
"/#{type}/#{verb}/#{path}"
end
# :nodoc:
class FilterBlock
property block : HTTP::Server::Context -> String
2016-02-14 13:15:28 +00:00
def initialize(&block : HTTP::Server::Context -> _)
@block = ->(context : HTTP::Server::Context) { block.call(context).to_s }
end
2016-02-17 19:35:55 +00:00
2017-08-25 13:41:02 +00:00
def call(context : HTTP::Server::Context)
@block.call(context)
end
2016-02-17 19:35:55 +00:00
end
end
end