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{ ... }`:
127151--
128152-- @type Router
129153--- @class Router
154+
130155local Router = {}
131156Router .__index = Router
132157
@@ -200,10 +225,29 @@ local function parseRoutes(self, routes, prefix)
200225 return rts
201226end
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
224268end
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
273308end
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+
275342return Router
0 commit comments