Skip to content

Instantly share code, notes, and snippets.

@max1220
Last active September 10, 2020 03:41
Show Gist options
  • Save max1220/41304c22ffee0a5bfe6368c036b24a80 to your computer and use it in GitHub Desktop.
Save max1220/41304c22ffee0a5bfe6368c036b24a80 to your computer and use it in GitHub Desktop.
simple openresty dynamic router
return function new_router(get_user)
-- This function returns a router table with some functions for dynamic routing.
local function pack(...)
return {...}
end
local get_user = get_user or function(request_uri, request_method, pattern_args, uri_args)
-- This function is called when a restricted route is accessed.
-- It should return something truethy when the user is authorized to access the resource.
-- The return value is passed to the handler of the route, so you could return a table representing the user.
return nil
end
local Router = {
routes = {}
}
function Router:add_dynamic(patterns, methods, handler, restricted_methods)
-- Adds a route with a "dynamic" handler which is called when the route is accessed.
local _restricted_methods = {}
for k,v in pairs(restricted_methods or {}) do
_restricted_methods[v] = true
end
table.insert(self.routes, {
handler = handler,
patterns = (type(patterns) == "table") and patterns or {patterns},
methods = (type(methods) == "table") and methods or {methods},
restricted_methods = _restricted_methods or {}
})
end
function Router:add_exec(patterns, path, restricted_methods, uri_args)
-- Add an internal redirect route. Only GET is supported ATM(Does anything else make sense?)
-- TODO: Remove dependency on the ngx module.
self:add_dynamic(patterns, "GET", function(request_uri, request_method, pattern_args, _uri_args, get_post_args)
return ngx.exec(path, uri_args)
end, restricted_methods)
end
function Router:add_redirect(patterns, path, restricted_methods, status_code)
-- Add a HTTP redirect route. Only GET is supported ATM(Does anything else make sense?)
-- TODO: Remove dependency on the ngx module.
self:add_dynamic(patterns, "GET", function(request_uri, request_method, pattern_args, uri_args, get_post_args)
return ngx.redirect(path, status_code)
end, restricted_methods)
end
function Router:route(request_method, request_uri, uri_args, get_post_args)
-- This function is called to route a single request.
-- It should never write anything to the response itself, or set headers;
-- This would prevent the useage of exec or redirect
-- Make sure URI does not contain any arguments anymore
if request_uri:find("?") then
request_uri = request_uri:sub(1, request_uri:find("?")-1)
end
local function match_found(route,pattern_args)
-- This function is called when a handler for an URI has been found.
local user = get_user(request_uri, request_method, pattern_args, uri_args)
if (#route.restricted_methods > 0) and (route.restricted_methods[request_method]) and not user then
-- The requested method is restricted, but we have no user -> Authentication not sufficient
return false, 403
end
-- The actual callback handler
route.handler(request_uri, request_method, pattern_args, uri_args, get_post_args, user)
return true
end
local function try_route(route)
-- This function is called with a new route to try until it finds one that matches
for _,method in pairs(route.methods) do
if method == request_method then
for _, pattern in pairs(route.patterns) do
local pattern_args = pack(request_uri:match(pattern))
if #pattern_args > 0 then
-- The pattern match succeeded, the method matches
match_found(route, pattern_args)
return true
end
end
end
end
end
-- Try to match a route to a method + pattern combination
for _,route in pairs(self.routes) do
if try_route(route) then
return true
end
end
-- If this gets executed, no matching route was found
return false, 404
end
return Router
end
--[[
Simple Example:
local utils = require("utils")
local new_router = require("route")
-- This function is passed to new_route and should return something truethy if a user is currently logged in.
-- If you don't require authentication, don't pass a function to new_route. Restricted routes then will always return a 403.
local function get_user()
local db = utils.connect_db()
local user = utils.get_session_user(db, cookie:new():get("sessionid"))
if user then
return user
end
end
local router = new_router( get_user )
-- These are "normal" dynamic routes
router:add_dynamic("^/$", "GET", require("lua.index"))
router:add_dynamic("^/login$", {"GET", "POST"}, require("lua.login"))
-- These routes require authentication
router:add_dynamic("^/logout$", {"GET", "POST"}, require("lua.logout"), {"GET"})
router:add_dynamic("^/secret$", {"GET", "POST"}, require("lua.secret"), {"GET"})
-- This route only requires authentication for POST
router:add_dynamic("^/debug$", {"GET", "POST"}, require("lua.debug"), {"POST"})
-- These routes do an internal redirect
router:add_exec("^/about$", "/static/about.html")
router:add_exec("^/api$", "/static/api.html")
router:add_exec("^/help$", "/static/help.html")
-- Actaully route the request
lcoal ok,status = router:route(ngx.var.request_method, ngx.var.request_uri, ngx.req.get_uri_args(), ngx.req.get_post_args)
if not ok then
ngx.exit(status)
end
]]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment