294 lines
9.4 KiB
Lua
294 lines
9.4 KiB
Lua
|
|
local socket = require("socket.core")
|
|
local copas = require("copas")
|
|
local utils = require("lua.utils")
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
|
|
local tinsert = table.insert
|
|
local tconcat = table.concat
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
|
|
local http_server = {
|
|
--{{Options
|
|
---The port number for the HTTP server. Default is 80
|
|
port=80,
|
|
---The parameter backlog specifies the number of client connections
|
|
-- that can be queued waiting for service. If the queue is full and
|
|
-- another client attempts connection, the connection is refused.
|
|
backlog=5,
|
|
--}}Options
|
|
|
|
NOT_FOUND = "endpoint not found.",
|
|
|
|
-- Handles gets and posts mapping for endpoints
|
|
router = {
|
|
unhandled_func = function() print("Endpoint invalid. 404.") end,
|
|
get_funcs = {},
|
|
post_funcs = {},
|
|
},
|
|
}
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
|
|
http_server.router.unhandled = function(func)
|
|
|
|
http_server.router.unhandled_func = func
|
|
end
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
|
|
http_server.router.get = function(pattern, func)
|
|
|
|
tinsert(http_server.router.get_funcs, { pattern = pattern, func = func })
|
|
end
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
|
|
http_server.router.post = function(pattern, func)
|
|
|
|
tinsert(http_server.router.post_funcs, { pattern = pattern, func = func })
|
|
end
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
|
|
http_server.create = function(port)
|
|
-- create a TCP socket and bind it to the local host, at any port
|
|
http_server.server = assert(socket.tcp())
|
|
http_server.server:setoption('reuseaddr', true)
|
|
assert(http_server.server:bind("*", port))
|
|
http_server.server:listen(http_server.backlog)
|
|
|
|
copas.addserver(http_server.server, http_server.handler)
|
|
copas.running = true
|
|
|
|
-- Print IP and port
|
|
local ip, newport = http_server.server:getsockname()
|
|
print("[http_server.create] IP:"..ip.." Port:"..newport.."...")
|
|
return http_server.server
|
|
end
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
|
|
http_server.start = function()
|
|
end
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
|
|
http_server.update = function()
|
|
copas.step(0.01666666)
|
|
end
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
|
|
http_server.html = function( html )
|
|
|
|
local resp = "HTTP/1.0 200 OK\nContent-Type: text/html\n\n"
|
|
resp = resp..tostring(html)
|
|
return resp
|
|
end
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
|
|
http_server.json = function(json)
|
|
local resp = "HTTP/1.0 200 OK\nContent-Type: application/json\n\n"
|
|
resp = resp..tostring(json)
|
|
return resp
|
|
end
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
|
|
http_server.file = function(data)
|
|
local resp = "HTTP/1.0 200 OK\nContent-Type: application/octet-stream\n\n"
|
|
resp = resp..tostring(data)
|
|
return resp
|
|
end
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
|
|
http_server.ttf = function(data)
|
|
local resp = "HTTP/1.0 200 OK\nContent-Type: font/truetype\n\n"
|
|
resp = resp..tostring(data)
|
|
return resp
|
|
end
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
|
|
http_server.woff = function(data)
|
|
local resp = "HTTP/1.0 200 OK\nContent-Type: font/woff2\n\n"
|
|
resp = resp..tostring(data)
|
|
return resp
|
|
end
|
|
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
|
|
http_server.script = function(data)
|
|
local resp = "HTTP/1.0 200 OK\nContent-Type: text/javascript\n\n"
|
|
resp = resp..tostring(data)
|
|
return resp
|
|
end
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
|
|
http_server.tscript = function(data)
|
|
local resp = "HTTP/1.0 200 OK\nContent-Type: text/x.typescript\n\n"
|
|
resp = resp..tostring(data)
|
|
return resp
|
|
end
|
|
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
|
|
http_server.css = function(data)
|
|
local resp = "HTTP/1.0 200 OK\nContent-Type: text/css\n\n"
|
|
resp = resp..tostring(data)
|
|
return resp
|
|
end
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
|
|
http_server.html_error = function( errno )
|
|
local resp = "HTTP/1.0 "..errno.." ERROR\n\n"
|
|
return resp
|
|
end
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
-- Because the whole req has already been put into lines, search for the main section header and body
|
|
local function get_header_body( lines )
|
|
|
|
-- Look for the first line ending with CRLF, then a CRLF on the following line. Then body follows.
|
|
local header = ""
|
|
local body = ""
|
|
local possible_header = false
|
|
local header_done = false
|
|
for i, line in ipairs(lines) do
|
|
-- Dont process the first line (thats the GET part)
|
|
if(i > 1) then
|
|
if(header_done == false) then
|
|
header = header..tostring(line).."\n"
|
|
else
|
|
body = body..tostring(line).."\n"
|
|
end
|
|
|
|
if string.match(line, "^\r\n$") and possible_header == true and header_done == false then
|
|
header_done = true
|
|
end
|
|
if string.match(line, ".-\r\n$") and possible_header == false and header_done == false then
|
|
possible_header = true
|
|
end
|
|
end
|
|
end
|
|
-- print("------------------- HEADER >>")
|
|
-- print(header)
|
|
-- print("------------------- BODY >>")
|
|
-- print(body)
|
|
return header, body
|
|
end
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
|
|
http_server.process_get = function( req, lines )
|
|
local pfunc = http_server.router.get_funcs
|
|
local header, body = get_header_body(lines)
|
|
for k,v in ipairs(pfunc) do
|
|
local matches = {}
|
|
for capture in string.gmatch(req[2], v.pattern) do
|
|
tinsert(matches, capture)
|
|
end
|
|
if(utils.tcount(matches) > 0) then
|
|
return v.func(matches, req, header, body)
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
|
|
http_server.process_post = function( req, lines )
|
|
local pfunc = http_server.router.post_funcs
|
|
local header, body = get_header_body(lines)
|
|
for k,v in ipairs(pfunc) do
|
|
local matches = {}
|
|
for capture in string.gmatch(req[2], v.pattern) do
|
|
tinsert(matches, capture)
|
|
end
|
|
if(utils.tcount(matches) > 0) then
|
|
return v.func(matches, req, header, body)
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
|
|
local function process_requests( req )
|
|
|
|
-- Break into lines first
|
|
local header = nil
|
|
local lines = utils.csplit( req, "\n")
|
|
if(utils.tcount(lines) == 0) then
|
|
lines = req
|
|
header = req
|
|
else
|
|
header = lines[1]
|
|
end
|
|
|
|
-- Get first line to determine what sort of req it is. Only process get and post.
|
|
local main_req = utils.csplit( header, " ")
|
|
if(main_req[1] == "GET") then
|
|
return http_server.process_get(main_req, lines)
|
|
elseif(main_req[1] == "POST") then
|
|
return http_server.process_post(main_req, lines)
|
|
else
|
|
return http_server.html_error(404)
|
|
end
|
|
end
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
-- loop forever waiting for clients
|
|
http_server.handler = function(skt)
|
|
|
|
-- Collate recieve if there is data to read
|
|
local finished = false
|
|
local data = {}
|
|
|
|
while true do
|
|
local indata, err = copas.receive(skt)
|
|
if(string.len(indata) == 0 or err ~= nil) then
|
|
break
|
|
end
|
|
tinsert(data, indata)
|
|
end
|
|
|
|
if(utils.tcount(data) > 0) then
|
|
local reqdata = tconcat(data, "\n")
|
|
-- print(reqdata)
|
|
-- Process request from client
|
|
local html = process_requests(reqdata)
|
|
if(html) then
|
|
copas.send(skt, html, 1, string.len(html))
|
|
end
|
|
-- copas.send(skt, "HTTP/1.0 200 OK\n\n\ncopas are you there?")
|
|
end
|
|
end
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
|
|
http_server.stop = function()
|
|
|
|
-- done with client, close the object
|
|
if(http_server.client) then
|
|
http_server.client:close()
|
|
end
|
|
http_server.server:close()
|
|
print("http server Terminated")
|
|
end
|
|
|
|
------------------------------------------------------------------------------------------------------------
|
|
|
|
return http_server
|
|
|
|
------------------------------------------------------------------------------------------------------------
|