From 8c4fca3ce54e85c812400bb10a720e4410b237bc Mon Sep 17 00:00:00 2001 From: syeopite Date: Wed, 21 Aug 2024 02:08:55 -0700 Subject: [PATCH 1/8] Add ability to add handlers for raised exceptions Closes #622 --- spec/exception_handler_spec.cr | 70 ++++++++++++++++++++++++++++++++++ spec/spec_helper.cr | 3 ++ src/kemal/config.cr | 6 ++- src/kemal/dsl.cr | 5 +++ src/kemal/exception_handler.cr | 14 +++++++ 5 files changed, 97 insertions(+), 1 deletion(-) diff --git a/spec/exception_handler_spec.cr b/spec/exception_handler_spec.cr index 7064e84c..a356594f 100644 --- a/spec/exception_handler_spec.cr +++ b/spec/exception_handler_spec.cr @@ -59,6 +59,76 @@ describe "Kemal::ExceptionHandler" do response.body.should eq "Something happened" end + it "renders custom error for a crystal exception" do + error RuntimeError do + "A RuntimeError has occured" + end + + get "/" do |env| + raise RuntimeError.new() + end + + request = HTTP::Request.new("GET", "/") + io = IO::Memory.new + response = HTTP::Server::Response.new(io) + context = HTTP::Server::Context.new(request, response) + Kemal::ExceptionHandler::INSTANCE.next = Kemal::RouteHandler::INSTANCE + Kemal::ExceptionHandler::INSTANCE.call(context) + response.close + io.rewind + response = HTTP::Client::Response.from_io(io, decompress: false) + response.status_code.should eq 500 + response.headers["Content-Type"].should eq "text/html" + response.body.should eq "A RuntimeError has occured" + end + + it "renders custom error for a custom exception" do + error CustomExceptionType do + "A custom exception of CustomExceptionType has occurred" + end + + get "/" do |env| + raise CustomExceptionType.new() + end + + request = HTTP::Request.new("GET", "/") + io = IO::Memory.new + response = HTTP::Server::Response.new(io) + context = HTTP::Server::Context.new(request, response) + Kemal::ExceptionHandler::INSTANCE.next = Kemal::RouteHandler::INSTANCE + Kemal::ExceptionHandler::INSTANCE.call(context) + response.close + io.rewind + response = HTTP::Client::Response.from_io(io, decompress: false) + response.status_code.should eq 500 + response.headers["Content-Type"].should eq "text/html" + response.body.should eq "A custom exception of CustomExceptionType has occurred" + end + + it "renders custom error for a custom exception with a specific HTTP status code" do + error CustomExceptionType do | env | + env.response.status_code = 503 + "A custom exception of CustomExceptionType has occurred" + end + + get "/" do |env| + raise CustomExceptionType.new() + end + + request = HTTP::Request.new("GET", "/") + io = IO::Memory.new + response = HTTP::Server::Response.new(io) + context = HTTP::Server::Context.new(request, response) + Kemal::ExceptionHandler::INSTANCE.next = Kemal::RouteHandler::INSTANCE + Kemal::ExceptionHandler::INSTANCE.call(context) + response.close + io.rewind + response = HTTP::Client::Response.from_io(io, decompress: false) + response.status_code.should eq 503 + response.headers["Content-Type"].should eq "text/html" + response.body.should eq "A custom exception of CustomExceptionType has occurred" + end + it "overrides the content type for filters" do before_get do |env| env.response.content_type = "application/json" diff --git a/spec/spec_helper.cr b/spec/spec_helper.cr index 6509e1e2..1fce0b94 100644 --- a/spec/spec_helper.cr +++ b/spec/spec_helper.cr @@ -26,6 +26,9 @@ class AnotherContextStorageType @name = "kemal-context" end +class CustomExceptionType < Exception +end + add_context_storage_type(TestContextStorageType) add_context_storage_type(AnotherContextStorageType) diff --git a/src/kemal/config.cr b/src/kemal/config.cr index f728f57b..27f5cfb9 100644 --- a/src/kemal/config.cr +++ b/src/kemal/config.cr @@ -12,7 +12,7 @@ module Kemal HANDLERS = [] of HTTP::Handler CUSTOM_HANDLERS = [] of Tuple(Nil | Int32, HTTP::Handler) FILTER_HANDLERS = [] of HTTP::Handler - ERROR_HANDLERS = {} of Int32 => HTTP::Server::Context, Exception -> String + ERROR_HANDLERS = {} of (Int32 | Exception.class) => HTTP::Server::Context, Exception -> String {% if flag?(:without_openssl) %} @ssl : Bool? @@ -96,6 +96,10 @@ module Kemal ERROR_HANDLERS[status_code] = ->(context : HTTP::Server::Context, error : Exception) { handler.call(context, error).to_s } end + def add_error_handler(exception : Exception.class, &handler : HTTP::Server::Context, Exception -> _) + ERROR_HANDLERS[exception] = ->(context : HTTP::Server::Context, error : Exception) { handler.call(context, error).to_s } + end + def extra_options(&@extra_options : OptionParser ->) end diff --git a/src/kemal/dsl.cr b/src/kemal/dsl.cr index 50fa51bf..e282e7fd 100644 --- a/src/kemal/dsl.cr +++ b/src/kemal/dsl.cr @@ -25,6 +25,11 @@ def error(status_code : Int32, &block : HTTP::Server::Context, Exception -> _) Kemal.config.add_error_handler status_code, &block end +# Defines an error handler to be called when the given exception is raised +def error(exception : Exception.class, &block : HTTP::Server::Context, Exception -> _) + Kemal.config.add_error_handler exception, &block +end + # All the helper methods available are: # - before_all, before_get, before_post, before_put, before_patch, before_delete, before_options # - after_all, after_get, after_post, after_put, after_patch, after_delete, after_options diff --git a/src/kemal/exception_handler.cr b/src/kemal/exception_handler.cr index eee6eecd..dbf8b88f 100644 --- a/src/kemal/exception_handler.cr +++ b/src/kemal/exception_handler.cr @@ -11,12 +11,26 @@ module Kemal rescue ex : Kemal::Exceptions::CustomException call_exception_with_status_code(context, ex, context.response.status_code) rescue ex : Exception + # Use error handler defined for the current exception if it exists + return call_exception_with_exception(context, ex, 500) if Kemal.config.error_handlers.has_key?(ex.class) log("Exception: #{ex.inspect_with_backtrace}") + # Else use generic 500 handler if defined return call_exception_with_status_code(context, ex, 500) if Kemal.config.error_handlers.has_key?(500) verbosity = Kemal.config.env == "production" ? false : true render_500(context, ex, verbosity) end + private def call_exception_with_exception(context : HTTP::Server::Context, exception : Exception, status_code : Int32 = 500) + return if context.response.closed? + + if !Kemal.config.error_handlers.empty? && Kemal.config.error_handlers.has_key?(exception.class) + context.response.content_type = "text/html" unless context.response.headers.has_key?("Content-Type") + context.response.status_code = status_code + context.response.print Kemal.config.error_handlers[exception.class].call(context, exception) + context + end + end + private def call_exception_with_status_code(context : HTTP::Server::Context, exception : Exception, status_code : Int32) return if context.response.closed? if !Kemal.config.error_handlers.empty? && Kemal.config.error_handlers.has_key?(status_code) From 9c64861797930b094be98cc83f36240123515d93 Mon Sep 17 00:00:00 2001 From: syeopite Date: Wed, 21 Aug 2024 02:22:47 -0700 Subject: [PATCH 2/8] Fix lint --- spec/exception_handler_spec.cr | 20 ++++++++++---------- src/kemal/config.cr | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/spec/exception_handler_spec.cr b/spec/exception_handler_spec.cr index a356594f..916eff5f 100644 --- a/spec/exception_handler_spec.cr +++ b/spec/exception_handler_spec.cr @@ -59,13 +59,13 @@ describe "Kemal::ExceptionHandler" do response.body.should eq "Something happened" end - it "renders custom error for a crystal exception" do + it "renders custom error for a crystal exception" do error RuntimeError do "A RuntimeError has occured" end - get "/" do |env| - raise RuntimeError.new() + get "/" do + raise RuntimeError.new end request = HTTP::Request.new("GET", "/") @@ -82,13 +82,13 @@ describe "Kemal::ExceptionHandler" do response.body.should eq "A RuntimeError has occured" end - it "renders custom error for a custom exception" do + it "renders custom error for a custom exception" do error CustomExceptionType do "A custom exception of CustomExceptionType has occurred" end - get "/" do |env| - raise CustomExceptionType.new() + get "/" do + raise CustomExceptionType.new end request = HTTP::Request.new("GET", "/") @@ -105,14 +105,14 @@ describe "Kemal::ExceptionHandler" do response.body.should eq "A custom exception of CustomExceptionType has occurred" end - it "renders custom error for a custom exception with a specific HTTP status code" do - error CustomExceptionType do | env | + it "renders custom error for a custom exception with a specific HTTP status code" do + error CustomExceptionType do |env| env.response.status_code = 503 "A custom exception of CustomExceptionType has occurred" end - get "/" do |env| - raise CustomExceptionType.new() + get "/" do + raise CustomExceptionType.new end request = HTTP::Request.new("GET", "/") diff --git a/src/kemal/config.cr b/src/kemal/config.cr index 27f5cfb9..44e8bba3 100644 --- a/src/kemal/config.cr +++ b/src/kemal/config.cr @@ -12,7 +12,7 @@ module Kemal HANDLERS = [] of HTTP::Handler CUSTOM_HANDLERS = [] of Tuple(Nil | Int32, HTTP::Handler) FILTER_HANDLERS = [] of HTTP::Handler - ERROR_HANDLERS = {} of (Int32 | Exception.class) => HTTP::Server::Context, Exception -> String + ERROR_HANDLERS = {} of (Int32 | Exception.class) => HTTP::Server::Context, Exception -> String {% if flag?(:without_openssl) %} @ssl : Bool? From bf19e27a0f96c1c35292a6abd7a5bc1bdeec023c Mon Sep 17 00:00:00 2001 From: syeopite Date: Thu, 19 Sep 2024 19:31:41 -0700 Subject: [PATCH 3/8] Route unknown error to parent exception's handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Johannes Müller --- spec/exception_handler_spec.cr | 23 +++++++++++++++++++++++ spec/spec_helper.cr | 3 +++ src/kemal/exception_handler.cr | 24 +++++++++++++++++++++--- 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/spec/exception_handler_spec.cr b/spec/exception_handler_spec.cr index 916eff5f..5aa22789 100644 --- a/spec/exception_handler_spec.cr +++ b/spec/exception_handler_spec.cr @@ -129,6 +129,29 @@ describe "Kemal::ExceptionHandler" do response.body.should eq "A custom exception of CustomExceptionType has occurred" end + it "renders custom error for a child of a custom exception" do + error CustomExceptionType do |env, error| + "A custom exception of #{error.class} has occurred" + end + + get "/" do + raise ChildCustomExceptionType.new + end + + request = HTTP::Request.new("GET", "/") + io = IO::Memory.new + response = HTTP::Server::Response.new(io) + context = HTTP::Server::Context.new(request, response) + Kemal::ExceptionHandler::INSTANCE.next = Kemal::RouteHandler::INSTANCE + Kemal::ExceptionHandler::INSTANCE.call(context) + response.close + io.rewind + response = HTTP::Client::Response.from_io(io, decompress: false) + response.status_code.should eq 500 + response.headers["Content-Type"].should eq "text/html" + response.body.should eq "A custom exception of ChildCustomExceptionType has occurred" + end + it "overrides the content type for filters" do before_get do |env| env.response.content_type = "application/json" diff --git a/spec/spec_helper.cr b/spec/spec_helper.cr index 1fce0b94..8ce02fae 100644 --- a/spec/spec_helper.cr +++ b/spec/spec_helper.cr @@ -29,6 +29,9 @@ end class CustomExceptionType < Exception end +class ChildCustomExceptionType < CustomExceptionType +end + add_context_storage_type(TestContextStorageType) add_context_storage_type(AnotherContextStorageType) diff --git a/src/kemal/exception_handler.cr b/src/kemal/exception_handler.cr index dbf8b88f..4b7e83a3 100644 --- a/src/kemal/exception_handler.cr +++ b/src/kemal/exception_handler.cr @@ -13,6 +13,14 @@ module Kemal rescue ex : Exception # Use error handler defined for the current exception if it exists return call_exception_with_exception(context, ex, 500) if Kemal.config.error_handlers.has_key?(ex.class) + + # Use error handler for an ancestor of the current exception if it exists + Kemal.config.error_handlers.each_key do |key| + if key.is_a? Exception.class && ex.class <= key + return call_exception_with_exception(context, ex, 500, override_handler_used: key) + end + end + log("Exception: #{ex.inspect_with_backtrace}") # Else use generic 500 handler if defined return call_exception_with_status_code(context, ex, 500) if Kemal.config.error_handlers.has_key?(500) @@ -20,13 +28,23 @@ module Kemal render_500(context, ex, verbosity) end - private def call_exception_with_exception(context : HTTP::Server::Context, exception : Exception, status_code : Int32 = 500) + # Calls the defined error handler for the given exception if it exists + # + # By default it tries to use the handler that is defined for the given exception. However, another + # handler can be used via the `override_handler_used` parameter. + private def call_exception_with_exception(context : HTTP::Server::Context, exception : Exception, status_code : Int32 = 500, override_handler_used : (Exception.class)? = nil) return if context.response.closed? - if !Kemal.config.error_handlers.empty? && Kemal.config.error_handlers.has_key?(exception.class) + if !override_handler_used + handler_to_use = exception.class + else + handler_to_use = override_handler_used + end + + if !Kemal.config.error_handlers.empty? && Kemal.config.error_handlers.has_key?(handler_to_use) context.response.content_type = "text/html" unless context.response.headers.has_key?("Content-Type") context.response.status_code = status_code - context.response.print Kemal.config.error_handlers[exception.class].call(context, exception) + context.response.print Kemal.config.error_handlers[handler_to_use].call(context, exception) context end end From f51fda851741e4bde644e234639a966bc40475e4 Mon Sep 17 00:00:00 2001 From: syeopite Date: Tue, 17 Dec 2024 19:06:28 -0800 Subject: [PATCH 4/8] Remove duplicate logic with calling error handler `ex.class <= key` below should already handle the exact match ```crystal Kemal.config.error_handlers.each_key do |key| if ex.class <= key end end ``` --- src/kemal/exception_handler.cr | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/kemal/exception_handler.cr b/src/kemal/exception_handler.cr index 4b7e83a3..f68db956 100644 --- a/src/kemal/exception_handler.cr +++ b/src/kemal/exception_handler.cr @@ -11,9 +11,6 @@ module Kemal rescue ex : Kemal::Exceptions::CustomException call_exception_with_status_code(context, ex, context.response.status_code) rescue ex : Exception - # Use error handler defined for the current exception if it exists - return call_exception_with_exception(context, ex, 500) if Kemal.config.error_handlers.has_key?(ex.class) - # Use error handler for an ancestor of the current exception if it exists Kemal.config.error_handlers.each_key do |key| if key.is_a? Exception.class && ex.class <= key From 122d77ef44a7cd83bb4ca7600f2fd942b5023525 Mon Sep 17 00:00:00 2001 From: syeopite Date: Tue, 17 Dec 2024 19:25:03 -0800 Subject: [PATCH 5/8] Remove duplicate hash look ups for error handler --- src/kemal/exception_handler.cr | 40 +++++++++++++++++----------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/kemal/exception_handler.cr b/src/kemal/exception_handler.cr index f68db956..83f16575 100644 --- a/src/kemal/exception_handler.cr +++ b/src/kemal/exception_handler.cr @@ -11,10 +11,13 @@ module Kemal rescue ex : Kemal::Exceptions::CustomException call_exception_with_status_code(context, ex, context.response.status_code) rescue ex : Exception - # Use error handler for an ancestor of the current exception if it exists - Kemal.config.error_handlers.each_key do |key| - if key.is_a? Exception.class && ex.class <= key - return call_exception_with_exception(context, ex, 500, override_handler_used: key) + # Matches an error handler for the given exception + # + # Matches based on order of declaration rather than inheritance relationship + # for child exceptions + Kemal.config.error_handlers.each do | expected_exception, handler | + if expected_exception.is_a? Exception.class && ex.class <= expected_exception + return call_exception_with_exception(context, ex, handler, 500) end end @@ -25,25 +28,22 @@ module Kemal render_500(context, ex, verbosity) end - # Calls the defined error handler for the given exception if it exists + # Calls the given error handler with the current exception # - # By default it tries to use the handler that is defined for the given exception. However, another - # handler can be used via the `override_handler_used` parameter. - private def call_exception_with_exception(context : HTTP::Server::Context, exception : Exception, status_code : Int32 = 500, override_handler_used : (Exception.class)? = nil) + # The logic for validating that the current exception should be handled + # by the given error handler should be done by the caller of this method. + private def call_exception_with_exception( + context : HTTP::Server::Context, + exception : Exception, + handler : Proc(HTTP::Server::Context, Exception, String), + status_code : Int32 = 500, + ) return if context.response.closed? - if !override_handler_used - handler_to_use = exception.class - else - handler_to_use = override_handler_used - end - - if !Kemal.config.error_handlers.empty? && Kemal.config.error_handlers.has_key?(handler_to_use) - context.response.content_type = "text/html" unless context.response.headers.has_key?("Content-Type") - context.response.status_code = status_code - context.response.print Kemal.config.error_handlers[handler_to_use].call(context, exception) - context - end + context.response.content_type = "text/html" unless context.response.headers.has_key?("Content-Type") + context.response.status_code = status_code + context.response.print handler.call(context, exception) + context end private def call_exception_with_status_code(context : HTTP::Server::Context, exception : Exception, status_code : Int32) From 6b12977599dfddbe11e9ae9c460c734bb646a3b0 Mon Sep 17 00:00:00 2001 From: syeopite Date: Tue, 17 Dec 2024 19:34:54 -0800 Subject: [PATCH 6/8] Split exceptions from HTTP status code handlers Prior to this commit, handlers for exceptions and handlers for HTTP status codes were stored in a single collection. However, the two are used completely independently from each other and should instead be stored in two separate collections. --- src/kemal/config.cr | 17 +++++++++++++---- src/kemal/dsl.cr | 3 ++- src/kemal/exception_handler.cr | 4 ++-- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/kemal/config.cr b/src/kemal/config.cr index 44e8bba3..ad7ba8b1 100644 --- a/src/kemal/config.cr +++ b/src/kemal/config.cr @@ -12,7 +12,8 @@ module Kemal HANDLERS = [] of HTTP::Handler CUSTOM_HANDLERS = [] of Tuple(Nil | Int32, HTTP::Handler) FILTER_HANDLERS = [] of HTTP::Handler - ERROR_HANDLERS = {} of (Int32 | Exception.class) => HTTP::Server::Context, Exception -> String + ERROR_HANDLERS = {} of Int32 => HTTP::Server::Context, Exception -> String + EXCEPTION_HANDLERS = {} of Exception.class => HTTP::Server::Context, Exception -> String {% if flag?(:without_openssl) %} @ssl : Bool? @@ -88,16 +89,24 @@ module Kemal FILTER_HANDLERS << handler end + # Returns the defined error handlers for HTTP status codes def error_handlers ERROR_HANDLERS end - + + # Adds an error handler for the given HTTP status code def add_error_handler(status_code : Int32, &handler : HTTP::Server::Context, Exception -> _) ERROR_HANDLERS[status_code] = ->(context : HTTP::Server::Context, error : Exception) { handler.call(context, error).to_s } end + + # Returns defined error handlers for exceptions + def exception_handlers + EXCEPTION_HANDLERS + end - def add_error_handler(exception : Exception.class, &handler : HTTP::Server::Context, Exception -> _) - ERROR_HANDLERS[exception] = ->(context : HTTP::Server::Context, error : Exception) { handler.call(context, error).to_s } + # Returns the defined error handlers for HTTP status codes + def add_exception_handler(exception : Exception.class, &handler : HTTP::Server::Context, Exception -> _) + EXCEPTION_HANDLERS[exception] = ->(context : HTTP::Server::Context, error : Exception) { handler.call(context, error).to_s } end def extra_options(&@extra_options : OptionParser ->) diff --git a/src/kemal/dsl.cr b/src/kemal/dsl.cr index e282e7fd..202dca8b 100644 --- a/src/kemal/dsl.cr +++ b/src/kemal/dsl.cr @@ -21,13 +21,14 @@ def ws(path : String, &block : HTTP::WebSocket, HTTP::Server::Context -> Void) Kemal::WebSocketHandler::INSTANCE.add_route path, &block end +# Defines an error handler to be called when route returns the given HTTP status code def error(status_code : Int32, &block : HTTP::Server::Context, Exception -> _) Kemal.config.add_error_handler status_code, &block end # Defines an error handler to be called when the given exception is raised def error(exception : Exception.class, &block : HTTP::Server::Context, Exception -> _) - Kemal.config.add_error_handler exception, &block + Kemal.config.add_exception_handler exception, &block end # All the helper methods available are: diff --git a/src/kemal/exception_handler.cr b/src/kemal/exception_handler.cr index 83f16575..804ac866 100644 --- a/src/kemal/exception_handler.cr +++ b/src/kemal/exception_handler.cr @@ -15,8 +15,8 @@ module Kemal # # Matches based on order of declaration rather than inheritance relationship # for child exceptions - Kemal.config.error_handlers.each do | expected_exception, handler | - if expected_exception.is_a? Exception.class && ex.class <= expected_exception + Kemal.config.exception_handlers.each do | expected_exception, handler | + if ex.class <= expected_exception return call_exception_with_exception(context, ex, handler, 500) end end From cd72a7ecefcbc95d827e079b9a6673e9f7a4bc28 Mon Sep 17 00:00:00 2001 From: syeopite Date: Tue, 17 Dec 2024 20:07:11 -0800 Subject: [PATCH 7/8] Fix formatter --- src/kemal/config.cr | 16 ++++++++-------- src/kemal/exception_handler.cr | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/kemal/config.cr b/src/kemal/config.cr index ad7ba8b1..d9c72728 100644 --- a/src/kemal/config.cr +++ b/src/kemal/config.cr @@ -8,12 +8,12 @@ module Kemal # Kemal.config # ``` class Config - INSTANCE = Config.new - HANDLERS = [] of HTTP::Handler - CUSTOM_HANDLERS = [] of Tuple(Nil | Int32, HTTP::Handler) - FILTER_HANDLERS = [] of HTTP::Handler - ERROR_HANDLERS = {} of Int32 => HTTP::Server::Context, Exception -> String - EXCEPTION_HANDLERS = {} of Exception.class => HTTP::Server::Context, Exception -> String + INSTANCE = Config.new + HANDLERS = [] of HTTP::Handler + CUSTOM_HANDLERS = [] of Tuple(Nil | Int32, HTTP::Handler) + FILTER_HANDLERS = [] of HTTP::Handler + ERROR_HANDLERS = {} of Int32 => HTTP::Server::Context, Exception -> String + EXCEPTION_HANDLERS = {} of Exception.class => HTTP::Server::Context, Exception -> String {% if flag?(:without_openssl) %} @ssl : Bool? @@ -93,12 +93,12 @@ module Kemal def error_handlers ERROR_HANDLERS end - + # Adds an error handler for the given HTTP status code def add_error_handler(status_code : Int32, &handler : HTTP::Server::Context, Exception -> _) ERROR_HANDLERS[status_code] = ->(context : HTTP::Server::Context, error : Exception) { handler.call(context, error).to_s } end - + # Returns defined error handlers for exceptions def exception_handlers EXCEPTION_HANDLERS diff --git a/src/kemal/exception_handler.cr b/src/kemal/exception_handler.cr index 804ac866..714651b6 100644 --- a/src/kemal/exception_handler.cr +++ b/src/kemal/exception_handler.cr @@ -15,7 +15,7 @@ module Kemal # # Matches based on order of declaration rather than inheritance relationship # for child exceptions - Kemal.config.exception_handlers.each do | expected_exception, handler | + Kemal.config.exception_handlers.each do |expected_exception, handler| if ex.class <= expected_exception return call_exception_with_exception(context, ex, handler, 500) end From ac63711a2eadc9cf1836ff111b63fbe85b2be73f Mon Sep 17 00:00:00 2001 From: syeopite Date: Tue, 17 Dec 2024 20:08:32 -0800 Subject: [PATCH 8/8] Typo --- src/kemal/config.cr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/kemal/config.cr b/src/kemal/config.cr index d9c72728..0893e9d0 100644 --- a/src/kemal/config.cr +++ b/src/kemal/config.cr @@ -99,12 +99,12 @@ module Kemal ERROR_HANDLERS[status_code] = ->(context : HTTP::Server::Context, error : Exception) { handler.call(context, error).to_s } end - # Returns defined error handlers for exceptions + # Returns tje defined error handlers for exceptions def exception_handlers EXCEPTION_HANDLERS end - # Returns the defined error handlers for HTTP status codes + # Adds an error handler for the given exception def add_exception_handler(exception : Exception.class, &handler : HTTP::Server::Context, Exception -> _) EXCEPTION_HANDLERS[exception] = ->(context : HTTP::Server::Context, error : Exception) { handler.call(context, error).to_s } end