diff --git a/examples/errorpages.ru b/examples/errorpages.ru index 06d6fd9..185f87a 100644 --- a/examples/errorpages.ru +++ b/examples/errorpages.ru @@ -8,19 +8,58 @@ app = Landline::Server.new do header "content-type", "text/plain" "Hello World!" end - get "/error" do + + get "/error" do # This error will be caught by the default server handler raise StandardError, "I raised an error! (and that's very sad)" end - handle do |status, backtrace: nil| - page = ([Landline::Util::HTTP_STATUS[status]] + - (backtrace || [""])).join("\n") - [ - { - "content-length": page.bytesize, - "content-type": "text/plain" - }, - page - ] + + path "/handled" do + get "/error" do # This error will be caught by the handler defined for this path + raise StandardError, "I raised an error! (and that's very sad)" + end + + handle do |status, backtrace: nil, error: nil| + ([Landline::Util::HTTP_STATUS[status]] + + (backtrace || [""])).join("\n") + end + end + + path "/pipelined" do + # The pipeline executes inside the function which catches errors, + # so it can catch errors before the handler for the current path. + # This may be useful in order to override the way things are output. + pipeline do |request, &output| + output.call(request) + rescue StandardError + throw :finish, "Internal error caught in pipeline!" + end + + path "/inner" do + get "/error" do # This error will be caught by the pipeline + raise StandardError, "I raised an error! (and that's very sad)" + end + end + + path "/inner_override" do + get "/error" do # This error will be caught by the innermost handler + raise StandardError, "I raised an error! (and that's very sad)" + end + + handle do |status, backtrace: nil, error: nil| + ([Landline::Util::HTTP_STATUS[status]] + + (backtrace || [""])).join("\n") + end + end + + get "/error" do # This error will be caught by the pipeline + raise StandardError, "I raised an error! (and that's very sad)" + end + + # This handler is preceded by the pipeline, but it may handle 404 codes. + handle do |status, backtrace: nil, error: nil| + ([Landline::Util::HTTP_STATUS[status]] + + (backtrace || [""])).join("\n") + end end end diff --git a/examples/rack_app.ru b/examples/rack_app.ru index cc8aaa3..ac710cf 100644 --- a/examples/rack_app.ru +++ b/examples/rack_app.ru @@ -27,6 +27,7 @@ class HelloServer < Landline::App page = ([Landline::Util::HTTP_STATUS[status]] + (backtrace || [""])).join("\n") [ + status, { "content-length": page.bytesize, "content-type": "text/plain", diff --git a/lib/landline/dsl/methods_common.rb b/lib/landline/dsl/methods_common.rb index 192a94c..f62e03b 100644 --- a/lib/landline/dsl/methods_common.rb +++ b/lib/landline/dsl/methods_common.rb @@ -9,14 +9,17 @@ module Landline # @param errorcode [Integer] # @param backtrace [Array(String), nil] # @raise [UncaughtThrowError] throws :finish to return back to Server - def die(errorcode, backtrace: nil) - throw :finish, [errorcode].append( - *(@origin.properties["handle.#{errorcode}"] or - @origin.properties["handle.default"]).call( - errorcode, - backtrace: backtrace - ) + def die(errorcode, backtrace: nil, error: nil) + response = Landline::Response.convert( + (@origin.properties["handle.#{errorcode}"] or + @origin.properties["handle.default"]).call( + errorcode, + backtrace: backtrace, + error: error + ) ) + response.status = errorcode if response.status == 200 + throw :finish, response end # (in Landline::Probe context) diff --git a/lib/landline/path.rb b/lib/landline/path.rb index 620ac2f..37b59e7 100644 --- a/lib/landline/path.rb +++ b/lib/landline/path.rb @@ -37,6 +37,24 @@ module Landline context.instance_exec(&setup) end + # (see ::Landline::Node#go) + def go(request) + # This is done to allow pipeline to interject handlers + # I'm more than willing to admit that this is stupid, + # but it is well worth the logical flexibility. + if ['handle.default', 'handle.505'].any? do |x| + @properties.storage.include? x + end + begin + super(request) + rescue StandardError => e + _die(request, 500, backtrace: [e.to_s] + e.backtrace, error: e) + end + else + super(request) + end + end + # Method callback on successful request navigation. # Finds the next appropriate path to go to. # @return [Boolean] true if further navigation will be done @@ -138,8 +156,6 @@ module Landline return exit_stack(request, value) if value notfound(request) - rescue StandardError => e - _die(request, 500, backtrace: [e.to_s] + e.backtrace) end # Run enqueued postprocessors on navigation failure @@ -175,16 +191,19 @@ module Landline # @param errorcode [Integer] # @param backtrace [Array(String), nil] # @raise [UncaughtThrowError] throws :finish to stop processing - def _die(request, errorcode, backtrace: nil) + def _die(request, errorcode, backtrace: nil, error: nil) proccontext = get_context(request) - throw :finish, [errorcode].append( - *proccontext.instance_exec( + response = Landline::Response.convert( + proccontext.instance_exec( errorcode, backtrace: backtrace, + error: error, &(@properties["handle.#{errorcode}"] or @properties["handle.default"]) ) ) + response.status = errorcode if response.status == 200 + throw :finish, response end end end diff --git a/lib/landline/pattern_matching.rb b/lib/landline/pattern_matching.rb index e68f6e0..d5eee0e 100644 --- a/lib/landline/pattern_matching.rb +++ b/lib/landline/pattern_matching.rb @@ -35,7 +35,7 @@ module Landline def match(input) if @pattern.is_a? String input = Landline::PatternMatching.canonicalize(input) - if input.start_with?(@pattern) + if _match?(input) [input.delete_prefix(@pattern), [], {}] else false @@ -51,7 +51,8 @@ module Landline # @return [Boolean] def match?(input) if @pattern.is_a? String - Landline::PatternMatching.canonicalize(input).start_with? @pattern + input = Landline::PatternMatching.canonicalize(input) + _match?(input) else @pattern.match?(input) end @@ -59,6 +60,13 @@ module Landline private + def _match?(input) + parts = input.split("/") + @pattern.split("/").map.with_index do |part, index| + parts[index] == part + end.all?(true) + end + def patternify(pattern) classdomain = Landline::PatternMatching classdomain.constants diff --git a/lib/landline/server.rb b/lib/landline/server.rb index 52b686e..532b1aa 100644 --- a/lib/landline/server.rb +++ b/lib/landline/server.rb @@ -58,14 +58,14 @@ module Landline def setup_properties(*_args, **_opts) { "index" => [], - "handle.default" => proc do |code, backtrace: nil| + "handle.default" => proc do |code, backtrace: nil, **_extra| page = Landline::Util.default_error_page(code, backtrace) headers = { "content-length": page.bytesize, "content-type": "text/html", "x-cascade": true } - [headers, page] + [code, headers, page] end, "path" => "/" }.each { |k, v| @properties[k] = v unless @properties[k] } diff --git a/lib/landline/util/lookup.rb b/lib/landline/util/lookup.rb index 30b585a..428eb77 100644 --- a/lib/landline/util/lookup.rb +++ b/lib/landline/util/lookup.rb @@ -31,7 +31,7 @@ module Landline @storage[key] = value end - attr_accessor :parent + attr_accessor :parent, :storage end # Read-only lookup proxy