Skip to content

Commit

Permalink
Merge pull request #130 from Tieske/files
Browse files Browse the repository at this point in the history
refactor(files) move serving files into a plugin
  • Loading branch information
EvandroLG committed Apr 7, 2024
2 parents 77bbbb5 + 2eab8f2 commit 37cb2db
Show file tree
Hide file tree
Showing 11 changed files with 318 additions and 70 deletions.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ server:start(function (request, response)
end)
```

Or try the [included examples](example/README.md).

## Features

- Compatible with Linux, Mac and Windows systems
Expand Down Expand Up @@ -121,6 +123,25 @@ local server = Pegasus:new({
server:start()
```


* pegasus.plugins.files

```lua
local Pegasus = require 'pegasus'
local Files = require 'pegasus.plugins.files'

local server = Pegasus:new({
plugins = {
Files:new {
location = "./",
default = "index.html",
},
}
})

server:start()
```

* pegasus.plugins.tls

```lua
Expand Down
25 changes: 19 additions & 6 deletions example/app.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ package.path = './src/?.lua;./src/?/init.lua;' .. package.path
local Pegasus = require 'pegasus'
local Compress = require 'pegasus.plugins.compress'
local Downloads = require 'pegasus.plugins.downloads'
local Files = require 'pegasus.plugins.files'
-- local TLS = require 'pegasus.plugins.tls'

local server = Pegasus:new({
port = '9090',
location = '/example/root/',
plugins = {
-- TLS:new { -- the tls specific configuration
-- wrap = {
Expand All @@ -30,19 +30,32 @@ local server = Pegasus:new({
-- },

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

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

Compress:new(),
}
})

server:start(function(req)
local data = req:post()
server:start(function(req, resp)
local stop = false

local path = req:path()
if req:method() ~= "POST" or path ~= "/index.html" then
return stop
end

local data = req:post()
if data then
print(data['name'])
print(data['age'])
print("Name: ", data.name)
print("Age: ", data.age)
end
stop = not not resp:writeFile("./example/root" .. path)
return stop
end)
2 changes: 1 addition & 1 deletion example/copas.lua
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ local Downloads = require 'pegasus.plugins.downloads'
-- @tparam[opt] table opts.sslparams the tls based parameters, see the Copas documentation.
-- If not provided, then the connection will be accepted as a plain one.
-- @tparam[opt] table opts.plugins the plugins to use
-- @tparam[opt] function opts.handler the callback function to handle requests
-- @tparam[opt] function opts.callback the callback function to handle requests
-- @tparam[opt] string opts.location the file-path from where to server files
-- @return the server-socket on success, or nil+err on failure
local function newPegasusServer(opts)
Expand Down
1 change: 1 addition & 0 deletions rockspecs/pegasus-dev-1.rockspec
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ build = {
['pegasus.compress'] = 'src/pegasus/compress.lua',
['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.tls'] = 'src/pegasus/plugins/tls.lua',
}
}
2 changes: 1 addition & 1 deletion spec/integration/integration_spec.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
describe('integration', function()
local port = '7070'
local url = 'http://localhost:' .. port
local url = 'http://localhost:' .. port .. "/index.html"

local executeCommand = function(command)
local handle = assert(io.popen(command .. ' -s ' .. url))
Expand Down
127 changes: 127 additions & 0 deletions spec/unit/files_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
describe("Files plugin", function()

local Files = require "pegasus.plugins.files"



describe("instantiation", function()

local options = {}
local plugin = Files:new(options)

it("should return a table", function()
assert.is.table(plugin)
end)


it("should have a default location; '.'", function()
assert.is.equal(".", plugin.location)
end)


it("should have a default; '/index.html'", function()
assert.is.equal("/index.html", plugin.default)
end)

end)



describe("invocation", function()

local redirect_called, writeFile_called
local request = {}
local response = {
redirect = function(self, ...) redirect_called = {...} end,
writeFile = function(self, ...) writeFile_called = {...} return self end,
-- finish = function(self, ...) end,
-- setHeader = function(self, ...) end,
-- setStatusCode = function(self, ...) end,
}

before_each(function()
redirect_called = nil
writeFile_called = nil
end)


it("handles GET", function()
stub(request, "path", function() return "/some/file.html" end)
stub(request, "method", function() return "GET" end)
local stop = Files:new():newRequestResponse(request, response)
assert.is.True(stop)
assert.is.Nil(redirect_called)
assert.are.same({
"./some/file.html",
"text/html"
}, writeFile_called)
end)

it("handles HEAD", function()
stub(request, "path", function() return "/some/file.html" end)
stub(request, "method", function() return "HEAD" end)
local stop = Files:new():newRequestResponse(request, response)
assert.is.True(stop)
assert.is.Nil(redirect_called)
assert.are.same({
"./some/file.html",
"text/html"
}, writeFile_called)
end)

it("doesn't handle POST", function()
stub(request, "path", function() return "/some/file.html" end)
stub(request, "method", function() return "POST" end)
local stop = Files:new():newRequestResponse(request, response)
assert.is.False(stop)
assert.is.Nil(redirect_called)
assert.is.Nil(writeFile_called)
end)

it("doesn't handle PUT", function()
stub(request, "path", function() return "/some/file.html" end)
stub(request, "method", function() return "PUT" end)
local stop = Files:new():newRequestResponse(request, response)
assert.is.False(stop)
assert.is.Nil(redirect_called)
assert.is.Nil(writeFile_called)
end)

it("redirects GET /", function()
stub(request, "path", function() return "/" end)
stub(request, "method", function() return "GET" end)
local stop = Files:new():newRequestResponse(request, response)
assert.is.True(stop)
assert.are.same({
"/index.html"
}, redirect_called)
assert.is.Nil(writeFile_called)
end)

it("serves from specified location", function()
stub(request, "path", function() return "/some/file.html" end)
stub(request, "method", function() return "GET" end)
local stop = Files:new({ location = "./location" }):newRequestResponse(request, response)
assert.is.True(stop)
assert.is.Nil(redirect_called)
assert.are.same({
"./location/some/file.html",
"text/html"
}, writeFile_called)
end)

it("forces location to be relative", function()
stub(request, "path", function() return "/some/file.html" end)
stub(request, "method", function() return "GET" end)
local stop = Files:new({ location = "/location" }):newRequestResponse(request, response)
assert.is.True(stop)
assert.is.Nil(redirect_called)
assert.are.same({
"./location/some/file.html",
"text/html"
}, writeFile_called)
end)

end)

end)
59 changes: 19 additions & 40 deletions src/pegasus/handler.lua
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
local Request = require 'pegasus.request'
local Response = require 'pegasus.response'
local mimetypes = require 'mimetypes'
local lfs = require 'lfs'
local Files = require 'pegasus.plugins.files'

local Handler = {}
Handler.__index = Handler

function Handler:new(callback, location, plugins)
local handler = {}
handler.callback = callback
handler.location = location or ''
handler.plugins = plugins or {}

if location then
handler.plugins[#handler.plugins+1] = Files:new {
location = location,
default = "/index.html",
}
end

local result = setmetatable(handler, self)
result:pluginsAlterRequestResponseMetatable()

Expand Down Expand Up @@ -109,58 +114,32 @@ function Handler:processRequest(port, client, server)
return false
end

local request = Request:new(port, client, server)
if not request:method() then
local request = Request:new(port, client, server, self)
local response = request.response

local method = request:method()
if not method then
client:close()
return
end

local response = Response:new(client, self)
response.request = request
local stop = self:pluginsNewRequestResponse(request, response)

if stop then
return
end

if request:path() and self.location ~= '' then
local path = request:path()
if path == '/' or path == '' then
path = 'index.html'
end
local filename = '.' .. self.location .. path

if not lfs.attributes(filename) then
response:statusCode(404)
end

stop = self:pluginsProcessFile(request, response, filename)

if stop then
return
end

local file = io.open(filename, 'rb')

if file then
response:writeFile(file, mimetypes.guess(filename or '') or 'text/html')
stop = true
else
response:statusCode(404)
end
end

if self.callback and not stop then
if self.callback then
response:statusCode(200)
response.headers = {}
response:addHeader('Content-Type', 'text/html')

self.callback(request, response)
stop = self.callback(request, response)
if stop then
return
end
end

if response.status == 404 then
response:writeDefaultErrorMessage(404)
end
response:writeDefaultErrorMessage(404)
end


Expand Down

0 comments on commit 37cb2db

Please sign in to comment.