Skip to content

Commit 52b480a

Browse files
TieskeEvandroLG
authored andcommitted
feat(router): add errorhandling functionality
1 parent a551ae0 commit 52b480a

File tree

1 file changed

+78
-11
lines changed

1 file changed

+78
-11
lines changed

src/pegasus/plugins/router.lua

Lines changed: 78 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,30 @@
116116
-- routes = routes,
117117
-- }
118118

119+
120+
121+
local xpcall = xpcall
122+
123+
-- Test if xpcall supports extra arguments (Lua 5.2+, LuaJIT), fix if not (Lua 5.1)
124+
do
125+
local _, result = xpcall(function(arg) return arg == "test" end, function() end, "test")
126+
127+
if not result then
128+
-- Lua 5.1: wrap xpcall to support extra arguments
129+
local original_xpcall = xpcall
130+
xpcall = function(f, err, ...)
131+
local args = {...}
132+
local n = select('#', ...)
133+
return original_xpcall(
134+
function() return f(unpack(args, 1, n)) end,
135+
err
136+
)
137+
end
138+
end
139+
end
140+
141+
142+
119143
--- Router plugin instance.
120144
--
121145
-- Options passed to `Router:new{ ... }`:
@@ -127,6 +151,7 @@
127151
--
128152
-- @type Router
129153
---@class Router
154+
130155
local Router = {}
131156
Router.__index = Router
132157

@@ -200,10 +225,29 @@ local function parseRoutes(self, routes, prefix)
200225
return rts
201226
end
202227

228+
229+
230+
-- the errorhandler signature is: message_to_log = function(request, response, errobj)
231+
local function error_handler(request, response, errobj)
232+
-- an error occurred, return a 500 if still possible
233+
if not response.closed then
234+
pcall(response.writeDefaultErrorMessage, response, 500)
235+
end
236+
237+
-- return a stacktrace for logging
238+
local err = debug.traceback(tostring(errobj), 3)
239+
return err -- no tailcall since LuaJIT will eat a stack-level
240+
end
241+
242+
243+
203244
--- Creates a new Router plugin instance.
204245
-- @tparam options table the options table with the following fields;
205246
-- @tparam[opt] options.prefix string the base path for all underlying routes.
206247
-- @tparam options.routes table route definitions to be handled by this router plugin instance.
248+
-- @tparam[opt] options.errorHandler function an optional error handler function with signature
249+
-- `message_to_log = function(request, response, errobj)`. The default handler will send a 500 error
250+
-- response, and return a stack trace for logging.
207251
-- @return the new plugin
208252
---@param options table|nil
209253
---@return Router
@@ -218,23 +262,14 @@ function Router:new(options)
218262
plugin.prefix = prefix:sub(1, -2) -- drop trailing slash
219263

220264
plugin.routes = parseRoutes(plugin, options.routes)
221-
265+
plugin.errorHandler = options.errorHandler or error_handler
222266
setmetatable(plugin, Router)
223267
return plugin
224268
end
225269

226270

227271

228-
--- Route the request to the matching path/method callback.
229-
-- Populates `request.pathParameters` and `request.routerPath` upon match.
230-
-- Executes callbacks in order: router pre, path pre, method, path post, router post.
231-
-- @tparam table request
232-
-- @tparam table response
233-
-- @treturn boolean stop whether request handling should stop
234-
---@param request table
235-
---@param response table
236-
---@return boolean
237-
function Router:newRequestResponse(request, response)
272+
local function newRequestResponse(self, request, response)
238273
local stop = false
239274

240275
local path = request:path()
@@ -272,4 +307,36 @@ function Router:newRequestResponse(request, response)
272307
return stop
273308
end
274309

310+
311+
312+
--- Route the request to the matching path/method callback.
313+
-- Populates `request.pathParameters` and `request.routerPath` upon match.
314+
-- Executes callbacks in order: router pre, path pre, method, path post, router post.
315+
-- @tparam table request
316+
-- @tparam table response
317+
-- @treturn boolean stop whether request handling should stop
318+
---@param request table
319+
---@param response table
320+
---@return boolean
321+
function Router:newRequestResponse(request, response)
322+
323+
local errh = function(...) -- error function that injects request/response objects
324+
local errobj = self.errorHandler(request, response, ...)
325+
return errobj -- no tailcall since LuaJIT will eat a stack-level
326+
end
327+
328+
local ok, stop = xpcall(newRequestResponse, errh, self, request, response)
329+
if not ok then
330+
if stop ~= nil then
331+
-- 'stop' is now the error object returned from the error handler, log an error message
332+
request.log:error('Request for: %s %s, failed: %s', request:method(), request:path(), tostring(stop))
333+
end
334+
stop = true
335+
end
336+
337+
return stop
338+
end
339+
340+
341+
275342
return Router

0 commit comments

Comments
 (0)