Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(router): add a router plugin #131

Merged
merged 1 commit into from
Apr 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions example/app.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,73 @@ local Pegasus = require 'pegasus'
local Compress = require 'pegasus.plugins.compress'
local Downloads = require 'pegasus.plugins.downloads'
local Files = require 'pegasus.plugins.files'
local Router = require 'pegasus.plugins.router'
local json = require 'pegasus.json'
-- local TLS = require 'pegasus.plugins.tls'


-- example data for the "router" plugin
local routes do
local testData = {
Jane = { firstName = "Jane", lastName = "Doe", age = 25 },
John = { firstName = "John", lastName = "Doe", age = 30 },
}

routes = {
-- router-level preFunction runs before the method prefunction and callback
preFunction = function(req, resp)
local stop = false
local headers = req:headers()
local accept = (headers.accept or "*/*"):lower()
if not accept:find("application/json", 1, true) and
not accept:find("application/*", 1, true) and
not accept:find("*/*", 1, true) then

resp:writeDefaultErrorMessage(406, "This API only produces 'application/json'")
stop = true
end
return stop
end,

["/people"] = {
GET = function(req, resp)
resp:statusCode(200)
resp:addHeader("Content-Type", "application/json")
resp:write(json.encode(testData))
end,
},

["/people/{name}"] = {
-- path-level preFunction runs before the actual method callback
preFunction = function(req, resp)
local stop = false
local name = req.pathParameters.name
if not testData[name] then
local err = ("'%s' is an unknown person"):format(name)
resp:writeDefaultErrorMessage(404, err)
stop = true
end
return stop
end,

-- callback per method
GET = function(req, resp)
resp:statusCode(200)
resp:addHeader("Content-Type", "application/json")
resp:write(json.encode(testData[req.pathParameters.name]))
end,

-- postFunction runs after the actual method callback
postFunction = function(req, resp)
local stop = false
print("served " .. req.pathParameters.name .. "'s data")
return stop
end,
}
}
end


local server = Pegasus:new({
port = '9090',
plugins = {
Expand All @@ -39,6 +104,11 @@ local server = Pegasus:new({
location = '/example/root/',
},

Router:new {
prefix = "/api/1v0/",
routes = routes,
},

Compress:new(),
}
})
Expand Down
91 changes: 86 additions & 5 deletions example/copas.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ local Handler = require 'pegasus.handler'
local copas = require('copas')
local socket = require('socket')
local Downloads = require 'pegasus.plugins.downloads'
local Files = require 'pegasus.plugins.files'
local Router = require 'pegasus.plugins.router'
local Compress = require 'pegasus.plugins.compress'
local json = require 'pegasus.json'


--- Creates a new server within the Copas scheduler.
-- @tparam table opts options table.
Expand All @@ -25,7 +30,6 @@ local Downloads = require 'pegasus.plugins.downloads'
-- @return the server-socket on success, or nil+err on failure
local function newPegasusServer(opts)
opts = opts or {}
assert(opts.location or opts.callback, "either 'location' or 'callback' must be provided")
assert(opts.port, "option 'port' must be provided")

local server_sock, err = socket.bind(opts.interface or '*', opts.port)
Expand All @@ -36,6 +40,10 @@ local function newPegasusServer(opts)
local server_ip, server_port = server_sock:getsockname()
if not server_ip then
return nil, "failed to get server socket name; "..tostring(server_port)
else
if server_ip == "0.0.0.0" then
server_ip = "localhost"
end
end

local hdlr = Handler:new(opts.callback, opts.location, opts.plugins)
Expand All @@ -49,6 +57,68 @@ local function newPegasusServer(opts)
end


-- example data for the "router" plugin
local routes do
local testData = {
Jane = { firstName = "Jane", lastName = "Doe", age = 25 },
John = { firstName = "John", lastName = "Doe", age = 30 },
}

routes = {
-- router-level preFunction runs before the method prefunction and callback
preFunction = function(req, resp)
local stop = false
local headers = req:headers()
local accept = (headers.accept or "*/*"):lower()
if not accept:find("application/json", 1, true) and
not accept:find("application/*", 1, true) and
not accept:find("*/*", 1, true) then

resp:writeDefaultErrorMessage(406, "This API only produces 'application/json'")
stop = true
end
return stop
end,

["/people"] = {
GET = function(req, resp)
resp:statusCode(200)
resp:addHeader("Content-Type", "application/json")
resp:write(json.encode(testData))
end,
},

["/people/{name}"] = {
-- path-level preFunction runs before the actual method callback
preFunction = function(req, resp)
local stop = false
local name = req.pathParameters.name
if not testData[name] then
local err = ("'%s' is an unknown person"):format(name)
resp:writeDefaultErrorMessage(404, err)
stop = true
end
return stop
end,

-- callback per method
GET = function(req, resp)
resp:statusCode(200)
resp:addHeader("Content-Type", "application/json")
resp:write(json.encode(testData[req.pathParameters.name]))
end,

-- postFunction runs after the actual method callback
postFunction = function(req, resp)
local stop = false
print("served " .. req.pathParameters.name .. "'s data")
return stop
end,
}
}
end



-- Create http server
assert(newPegasusServer{
Expand Down Expand Up @@ -80,14 +150,25 @@ assert(newPegasusServer{
},
sni = nil,
},
location = '/example/root/',
callback = nil,

plugins = {
Downloads:new {
prefix = "downloads",
location = '/example/root/',
prefix = 'downloads',
stripPrefix = true,
},
},

Files:new {
location = '/example/root/',
},

Router:new {
prefix = "/api/1v0/",
routes = routes,
},

Compress:new(),
}
})

-- Start
Expand Down
12 changes: 11 additions & 1 deletion example/root/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,15 @@
Age: <input type="text" name="age">
<input type="submit" value="send">
</form>
<p>Try the following API calls for the Router plugin:
<ul>
<li><a href="/api/1v0/people"><code>/api/1v0/people</code></a></li>
<li><a href="/api/1v0/people/John"><code>/api/1v0/people/John</code></a></li>
<li><a href="/api/1v0/people/Mary"><code>/api/1v0/people/Mary</code></a> (fails with 404, change to "Jane" to make it work)</li>
<li><code>curl -v -X POST http://localhost:9090/api/1v0/people/John</code> (a POST request, failing with 405)</li>
<li><code>curl -v -X GET -H "Accept: application/xml" http://localhost:9090/api/1v0/people/John</code> (failing with 406)</li>
</ul>
To see the router features in effect
</p>
</body>
</html>
</html>
1 change: 1 addition & 0 deletions rockspecs/pegasus-dev-1.rockspec
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ build = {
['pegasus.plugins.compress'] = 'src/pegasus/plugins/compress.lua',
['pegasus.plugins.downloads'] = 'src/pegasus/plugins/downloads.lua',
['pegasus.plugins.files'] = 'src/pegasus/plugins/files.lua',
['pegasus.plugins.router'] = 'src/pegasus/plugins/router.lua',
['pegasus.plugins.tls'] = 'src/pegasus/plugins/tls.lua',
EvandroLG marked this conversation as resolved.
Show resolved Hide resolved
}
}
Loading
Loading