Last active
September 10, 2020 03:41
-
-
Save max1220/41304c22ffee0a5bfe6368c036b24a80 to your computer and use it in GitHub Desktop.
simple openresty dynamic router
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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