-
-
Save joshholt/886269 to your computer and use it in GitHub Desktop.
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
-- http://lua-users.org/lists/lua-l/2002-04/msg00180.html | |
require 'socket' | |
local header = | |
[[HTTP/1.1 200 OK | |
Date: Fri, 19 Apr 2002 20:37:57 GMT | |
Server: Apache/1.3.23 (Darwin) mod_ssl/2.8.7 OpenSSL/0.9.6b | |
Cache-Control: max-age=60 | |
Expires: Fri, 19 Apr 2002 20:38:57 GMT | |
Last-Modified: Tue, 16 Apr 2002 02:00:34 GMT | |
ETag: "3c57e-1e47-3cbb85c2" | |
Accept-Ranges: bytes | |
Connection: close | |
]] | |
WebServer = | |
{ | |
_port = 8080, | |
_header = header, | |
} | |
function WebServer:run() | |
self._server = socket.bind("localhost", self._port) | |
self._server:settimeout(.01) | |
self._clients = {} | |
self._sendClients = {} | |
print("WebServer running on port "..self._port) | |
self:mainLoop() | |
end | |
function WebServer:lookForNewClients() | |
local client = self._server:accept() | |
if client then | |
client:settimeout(0.3) | |
table.insert(self._clients, client) | |
end | |
end | |
function split(str, pat) | |
local t = {} -- NOTE: use {n = 0} in Lua-5.0 | |
local fpat = "(.-)" .. pat | |
local last_end = 1 | |
local s, e, cap = str:find(fpat, 1) | |
while s do | |
if s ~= 1 or cap ~= "" then | |
table.insert(t,cap) | |
end | |
last_end = e+1 | |
s, e, cap = str:find(fpat, last_end) | |
end | |
if last_end <= #str then | |
cap = str:sub(last_end) | |
table.insert(t, cap) | |
end | |
return t | |
end | |
-- http://lua-users.org/lists/lua-l/2009-06/msg00572.html | |
function read(client, pattern, prefix) | |
local data, emsg, partial = client:receive(pattern, prefix) | |
if data then | |
return data | |
end | |
if partial and #partial > 0 then | |
return partial | |
end | |
return nil, emsg | |
end | |
cache = {} | |
function runfile(fname, req, resp) | |
local newgt = {} -- create new environment | |
setmetatable(newgt, {__index = _G}) | |
local text = "" | |
local oldprint = print | |
local printfunc = function(...) | |
for i, v in ipairs(arg) do | |
resp.text = resp.text .. tostring(v) | |
end | |
end | |
local redirfunc = function(uri) | |
resp.text = "HTTP/1.1 302 Moved\r\nLocation: /" .. fname .. uri .. "\r\n\r\n" | |
end | |
local errtext = nil | |
local errhandler = function(...) | |
print("Error!") | |
-- page:write("<pre>\n") | |
-- e(debug.traceback()) | |
errtext = debug.traceback() | |
print(errtext) | |
-- page:write("</pre>\n") | |
end | |
-- print("runfile: about to switch environment...") | |
newgt.request = req | |
newgt.response = resp | |
newgt.global_params = {} | |
newgt.web_send = printfunc | |
newgt.web_redirect = redirfunc | |
setfenv(1, newgt) -- set it | |
-- print("Request:", request.url) | |
request.url = string.sub(request.url, #fname + 1 + 1) | |
local func, message | |
if cache[fname] then | |
func = cache[fname] | |
else | |
func, message = loadfile(fname) | |
end | |
-- print("Loadfunc results:", func, message) | |
if func then | |
setfenv(func, newgt) | |
-- TODO: proper caching | |
-- cache[fname] = func | |
local res, err = xpcall(func, errhandler) | |
if not res then | |
print("result of pcall: ", err) | |
end | |
else | |
if message then | |
print("Error in loadfile:", message) | |
end | |
end | |
_G.print = oldprint | |
_G.page = page | |
setfenv(1, _G._G) | |
if resp.text == "" then | |
if errtext then | |
resp.text = "HTTP/1.0 500 Error\r\n\r\n" .. errtext | |
elseif page then | |
resp.text = page:full_response() | |
end | |
end | |
return resp | |
end | |
function WebServer:handleRequest(req) | |
local text = "" | |
local response = self._header.. "Content-length: " .. string.len(text) .. "\n\n" .. text | |
return response | |
end | |
function WebServer:mainLoop() | |
local clients = self._clients | |
while 1 do | |
self:lookForNewClients() | |
local receivingClients, _, error = socket.select(clients, nil, .01) | |
if error and error ~= "timeout" then | |
print("error = "..tostring(error)) | |
end | |
for i, client in ipairs(receivingClients) do | |
local clientdata = {} | |
repeat | |
local data, error = client:receive() | |
if data then | |
table.insert(clientdata, data) | |
-- print("Data:", data) | |
end | |
until data == "" or not data | |
local req = {} | |
if clientdata[1] then | |
local data = clientdata[1] | |
-- print("Data: " .. data) | |
req.firstline = split(data, "[ ]") | |
req.method = req.firstline[1] | |
req.full_url = req.firstline[2] | |
-- print("Full URL", req.full_url) | |
if req.full_url then | |
req.url, req.query_string = string.match(req.full_url, "^([^?]+)[?]*(.*)$") | |
req.query_string = req.query_string or "" | |
else | |
error = "no url in request" | |
end | |
end | |
if error then | |
print("error: "..tostring(error).." on client "..tostring(client)) | |
table.remove(clients, i) | |
else | |
bytesSent = client:send(WebServer:handleRequest(req)) | |
if bytesSent then | |
-- print("bytesSent = ".. bytesSent.." bytes = ".. #response) | |
end | |
client:close() | |
table.remove(clients, i) | |
end | |
end | |
end | |
end | |
WebServer:run() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment