Skip to content

Commit

Permalink
Moved HTTP router to separate module
Browse files Browse the repository at this point in the history
  • Loading branch information
britzl committed Sep 23, 2022
1 parent a30f4c4 commit 82e9b89
Show file tree
Hide file tree
Showing 2 changed files with 163 additions and 81 deletions.
128 changes: 128 additions & 0 deletions defnet/http_router.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
local M = {}

--- Create a router instance
-- @return instance
function M.create()

local routes = {}

local unhandled_route_fn = nil

local instance = {}

function instance.get(pattern, fn)
assert(pattern, "You must provide a route pattern")
assert(fn, "You must provide a route handler function")
table.insert(routes, { method = "GET", pattern = pattern, fn = fn })
end

function instance.post(pattern, fn)
assert(pattern, "You must provide a route pattern")
assert(fn, "You must provide a route handler function")
table.insert(routes, { method = "POST", pattern = pattern, fn = fn })
end

function instance.all(pattern, fn)
assert(pattern, "You must provide a route pattern")
assert(fn, "You must provide a route handler function")
table.insert(routes, { method = nil, pattern = pattern, fn = fn })
end

function instance.unhandled(fn)
assert(fn, "You must provide an unhandled route function")
unhandled_route_fn = fn
end

function instance.match(method, uri)
local response
if uri then
for _,route in ipairs(routes) do
if not route.method or route.method == method then
local matches = { uri:match(route.pattern) }
if next(matches) then
response = route.fn(matches, stream_fn, headers, message_body)
break
end
end
end
end

-- unhandled response
if not response and unhandled_route_fn then
response = unhandled_route_fn(method, uri, stream_fn, headers, message_body)
end

return response
end

return instance
end

--- Route HTTP GET requests matching a specific pattern to a
-- provided function.
-- The function will receive a list of matches from the pattern as
-- it's first arguments. The second argument is a stream function in case
-- the response should be streamed.
-- The function must either return the full response or a function that
-- can be called multiple times to get more data to return.
-- @param router
-- @param pattern Standard Lua pattern
-- @param fn Function to call
function M.get(router, pattern, fn)
assert(router)
return router.get(pattern, fn)
end

--- Route HTTP POST requests matching a specific pattern to a
-- provided function.
-- The function will receive a list of matches from the pattern as
-- it's first arguments. The second argument is a stream function in case
-- the response should be streamed.
-- The function must either return the full response or a function that
-- can be called multiple times to get more data to return.
-- @param router
-- @param pattern Standard Lua pattern
-- @param fn Function to call
function M.post(router, pattern, fn)
assert(router)
return router.post(pattern, fn)
end

--- Route all HTTP requests matching a specific pattern to a
-- provided function.
-- The function will receive a list of matches from the pattern as
-- it's first arguments. The second argument is a stream function in case
-- the response should be streamed.
-- The function must either return the full response or a function that
-- can be called multiple times to get more data to return.
-- @param router
-- @param pattern Standard Lua pattern
-- @param fn Function to call
function M.all(pattern, fn)
assert(router)
return router.all(pattern, fn)
end

--- Add a handler for unhandled routes. This is typically where
-- you would return a 404 page
-- @param router
-- @param fn The function to call when an unhandled route is encountered. The
-- function will receive the method and uri of the unhandled route as
-- arguments.
function M.unhandled(router, fn)
assert(router)
return router.unhandled(fn)
end

--- Match a method and uri with a route. If no match exists the unhandled route function is used
-- @param router
-- @param method
-- @param uri
-- @return response
function M.match(router, method, uri)
assert(router)
return router.match(method, uri)
end


return M
116 changes: 35 additions & 81 deletions defnet/http_server.lua
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
--

local tcp_server = require "defnet.tcp_server"
local http_router = require "defnet.http_router"

local M = {}

Expand Down Expand Up @@ -124,12 +125,10 @@ function M.create(port)
server_header = "Server: Simple Lua Server v1",
}

local routes = {}
instance.router = http_router.create()

local request_handlers = {}

local unhandled_route_fn = nil

local ss = tcp_server.create(port, function() end)

-- Replace the underlying socket server's receive function
Expand Down Expand Up @@ -160,23 +159,7 @@ function M.create(port)
end

-- handle request and get a response
local response
if uri then
for _,route in ipairs(routes) do
if not route.method or route.method == method then
local matches = { uri:match(route.pattern) }
if next(matches) then
response = route.fn(matches, stream_fn, headers, message_body)
break
end
end
end
end

-- unhandled response
if not response and unhandled_route_fn then
response = unhandled_route_fn(method, uri, stream_fn, headers, message_body)
end
local response = http_router.match(instance.router, method, uri)

-- send response
if response then
Expand All @@ -193,76 +176,15 @@ function M.create(port)
return nil, err
end

instance.router = {}

--- Route HTTP GET requests matching a specific pattern to a
-- provided function.
-- The function will receive a list of matches from the pattern as
-- it's first arguments. The second argument is a stream function in case
-- the response should be streamed.
-- The function must either return the full response or a function that
-- can be called multiple times to get more data to return.
-- @param pattern Standard Lua pattern
-- @param fn Function to call
function instance.router.get(pattern, fn)
assert(pattern, "You must provide a route pattern")
assert(fn, "You must provide a route handler function")
table.insert(routes, { method = "GET", pattern = pattern, fn = fn })
end

--- Route HTTP POST requests matching a specific pattern to a
-- provided function.
-- The function will receive a list of matches from the pattern as
-- it's first arguments. The second argument is a stream function in case
-- the response should be streamed.
-- The function must either return the full response or a function that
-- can be called multiple times to get more data to return.
-- @param pattern Standard Lua pattern
-- @param fn Function to call
function instance.router.post(pattern, fn)
assert(pattern, "You must provide a route pattern")
assert(fn, "You must provide a route handler function")
table.insert(routes, { method = "POST", pattern = pattern, fn = fn })
end

--- Route all HTTP requests matching a specific pattern to a
-- provided function.
-- The function will receive a list of matches from the pattern as
-- it's first arguments. The second argument is a stream function in case
-- the response should be streamed.
-- The function must either return the full response or a function that
-- can be called multiple times to get more data to return.
-- @param pattern Standard Lua pattern
-- @param fn Function to call
function instance.router.all(pattern, fn)
assert(pattern, "You must provide a route pattern")
assert(fn, "You must provide a route handler function")
table.insert(routes, { method = nil, pattern = pattern, fn = fn })
end

--- Add a handler for unhandled routes. This is typically where
-- you would return a 404 page
-- @param fn The function to call when an unhandled route is encountered. The
-- function will receive the method and uri of the unhandled route as
-- arguments.
function instance.router.unhandled(fn)
assert(fn, "You must provide an unhandled route function")
unhandled_route_fn = fn
end

--- Start the server
-- @return success
-- @return error_message
function instance.start()
return ss.start()
end

--- Stop the server
function instance.stop()
ss.stop()
end

--- Stop the server
function instance.update()
ss.update()
for k,handler in pairs(request_handlers) do
Expand Down Expand Up @@ -359,4 +281,36 @@ function M.create(port)
return instance
end

--- Start a server
-- @param server
-- @return success
-- @return error_message
function M.start(server)
assert(server)
return server.start()
end

--- Stop a server
-- @param server
function M.stop(server)
assert(server)
return server.stop()
end

--- Update a server
-- @param server
function M.update(server)
assert(server)
return server.update()
end

--- Set a router for a server
-- @param server
-- @param router
function M.set_router(server, router)
assert(server)
assert(router)
server.router = router
end

return M

0 comments on commit 82e9b89

Please sign in to comment.