Reworked error handling and pipeline functionality
This commit is contained in:
parent
dd745ca123
commit
7b7d3c928a
|
@ -8,19 +8,58 @@ app = Landline::Server.new do
|
||||||
header "content-type", "text/plain"
|
header "content-type", "text/plain"
|
||||||
"Hello World!"
|
"Hello World!"
|
||||||
end
|
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)"
|
raise StandardError, "I raised an error! (and that's very sad)"
|
||||||
end
|
end
|
||||||
handle do |status, backtrace: nil|
|
|
||||||
page = ([Landline::Util::HTTP_STATUS[status]] +
|
path "/handled" do
|
||||||
(backtrace || [""])).join("\n")
|
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
|
||||||
"content-length": page.bytesize,
|
|
||||||
"content-type": "text/plain"
|
handle do |status, backtrace: nil, error: nil|
|
||||||
},
|
([Landline::Util::HTTP_STATUS[status]] +
|
||||||
page
|
(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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ class HelloServer < Landline::App
|
||||||
page = ([Landline::Util::HTTP_STATUS[status]] +
|
page = ([Landline::Util::HTTP_STATUS[status]] +
|
||||||
(backtrace || [""])).join("\n")
|
(backtrace || [""])).join("\n")
|
||||||
[
|
[
|
||||||
|
status,
|
||||||
{
|
{
|
||||||
"content-length": page.bytesize,
|
"content-length": page.bytesize,
|
||||||
"content-type": "text/plain",
|
"content-type": "text/plain",
|
||||||
|
|
|
@ -9,14 +9,17 @@ module Landline
|
||||||
# @param errorcode [Integer]
|
# @param errorcode [Integer]
|
||||||
# @param backtrace [Array(String), nil]
|
# @param backtrace [Array(String), nil]
|
||||||
# @raise [UncaughtThrowError] throws :finish to return back to Server
|
# @raise [UncaughtThrowError] throws :finish to return back to Server
|
||||||
def die(errorcode, backtrace: nil)
|
def die(errorcode, backtrace: nil, error: nil)
|
||||||
throw :finish, [errorcode].append(
|
response = Landline::Response.convert(
|
||||||
*(@origin.properties["handle.#{errorcode}"] or
|
(@origin.properties["handle.#{errorcode}"] or
|
||||||
@origin.properties["handle.default"]).call(
|
@origin.properties["handle.default"]).call(
|
||||||
errorcode,
|
errorcode,
|
||||||
backtrace: backtrace
|
backtrace: backtrace,
|
||||||
)
|
error: error
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
response.status = errorcode if response.status == 200
|
||||||
|
throw :finish, response
|
||||||
end
|
end
|
||||||
|
|
||||||
# (in Landline::Probe context)
|
# (in Landline::Probe context)
|
||||||
|
|
|
@ -37,6 +37,24 @@ module Landline
|
||||||
context.instance_exec(&setup)
|
context.instance_exec(&setup)
|
||||||
end
|
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.
|
# Method callback on successful request navigation.
|
||||||
# Finds the next appropriate path to go to.
|
# Finds the next appropriate path to go to.
|
||||||
# @return [Boolean] true if further navigation will be done
|
# @return [Boolean] true if further navigation will be done
|
||||||
|
@ -138,8 +156,6 @@ module Landline
|
||||||
return exit_stack(request, value) if value
|
return exit_stack(request, value) if value
|
||||||
|
|
||||||
notfound(request)
|
notfound(request)
|
||||||
rescue StandardError => e
|
|
||||||
_die(request, 500, backtrace: [e.to_s] + e.backtrace)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Run enqueued postprocessors on navigation failure
|
# Run enqueued postprocessors on navigation failure
|
||||||
|
@ -175,16 +191,19 @@ module Landline
|
||||||
# @param errorcode [Integer]
|
# @param errorcode [Integer]
|
||||||
# @param backtrace [Array(String), nil]
|
# @param backtrace [Array(String), nil]
|
||||||
# @raise [UncaughtThrowError] throws :finish to stop processing
|
# @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)
|
proccontext = get_context(request)
|
||||||
throw :finish, [errorcode].append(
|
response = Landline::Response.convert(
|
||||||
*proccontext.instance_exec(
|
proccontext.instance_exec(
|
||||||
errorcode,
|
errorcode,
|
||||||
backtrace: backtrace,
|
backtrace: backtrace,
|
||||||
|
error: error,
|
||||||
&(@properties["handle.#{errorcode}"] or
|
&(@properties["handle.#{errorcode}"] or
|
||||||
@properties["handle.default"])
|
@properties["handle.default"])
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
response.status = errorcode if response.status == 200
|
||||||
|
throw :finish, response
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -35,7 +35,7 @@ module Landline
|
||||||
def match(input)
|
def match(input)
|
||||||
if @pattern.is_a? String
|
if @pattern.is_a? String
|
||||||
input = Landline::PatternMatching.canonicalize(input)
|
input = Landline::PatternMatching.canonicalize(input)
|
||||||
if input.start_with?(@pattern)
|
if _match?(input)
|
||||||
[input.delete_prefix(@pattern), [], {}]
|
[input.delete_prefix(@pattern), [], {}]
|
||||||
else
|
else
|
||||||
false
|
false
|
||||||
|
@ -51,7 +51,8 @@ module Landline
|
||||||
# @return [Boolean]
|
# @return [Boolean]
|
||||||
def match?(input)
|
def match?(input)
|
||||||
if @pattern.is_a? String
|
if @pattern.is_a? String
|
||||||
Landline::PatternMatching.canonicalize(input).start_with? @pattern
|
input = Landline::PatternMatching.canonicalize(input)
|
||||||
|
_match?(input)
|
||||||
else
|
else
|
||||||
@pattern.match?(input)
|
@pattern.match?(input)
|
||||||
end
|
end
|
||||||
|
@ -59,6 +60,13 @@ module Landline
|
||||||
|
|
||||||
private
|
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)
|
def patternify(pattern)
|
||||||
classdomain = Landline::PatternMatching
|
classdomain = Landline::PatternMatching
|
||||||
classdomain.constants
|
classdomain.constants
|
||||||
|
|
|
@ -58,14 +58,14 @@ module Landline
|
||||||
def setup_properties(*_args, **_opts)
|
def setup_properties(*_args, **_opts)
|
||||||
{
|
{
|
||||||
"index" => [],
|
"index" => [],
|
||||||
"handle.default" => proc do |code, backtrace: nil|
|
"handle.default" => proc do |code, backtrace: nil, **_extra|
|
||||||
page = Landline::Util.default_error_page(code, backtrace)
|
page = Landline::Util.default_error_page(code, backtrace)
|
||||||
headers = {
|
headers = {
|
||||||
"content-length": page.bytesize,
|
"content-length": page.bytesize,
|
||||||
"content-type": "text/html",
|
"content-type": "text/html",
|
||||||
"x-cascade": true
|
"x-cascade": true
|
||||||
}
|
}
|
||||||
[headers, page]
|
[code, headers, page]
|
||||||
end,
|
end,
|
||||||
"path" => "/"
|
"path" => "/"
|
||||||
}.each { |k, v| @properties[k] = v unless @properties[k] }
|
}.each { |k, v| @properties[k] = v unless @properties[k] }
|
||||||
|
|
|
@ -31,7 +31,7 @@ module Landline
|
||||||
@storage[key] = value
|
@storage[key] = value
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_accessor :parent
|
attr_accessor :parent, :storage
|
||||||
end
|
end
|
||||||
|
|
||||||
# Read-only lookup proxy
|
# Read-only lookup proxy
|
||||||
|
|
Loading…
Reference in New Issue