aboutsummaryrefslogtreecommitdiff
path: root/httpd.lua
diff options
context:
space:
mode:
Diffstat (limited to 'httpd.lua')
-rw-r--r--httpd.lua136
1 files changed, 136 insertions, 0 deletions
diff --git a/httpd.lua b/httpd.lua
new file mode 100644
index 0000000..de578c5
--- /dev/null
+++ b/httpd.lua
@@ -0,0 +1,136 @@
+local httpd = { }
+
+local socket
+local process
+
+local function send_and_close(sock, data)
+ sock:send(data, function(sock) sock:close() end)
+end
+
+local function default(method, path, headers)
+ if method == 'GET' and path == '/' then
+ return 200, 'OK', { }, 'OK, good!\n'
+ else
+ return 404, 'Not Found', { }, 'Not found!\n'
+ end
+end
+
+local function response(sock, code, status, headers, content)
+ local resp = { string.format('HTTP/1.1 %d %s', code, status) }
+ headers['Connection'] = 'close'
+ for k, v in pairs(headers) do
+ table.insert(resp, string.format('%s: %s', k, v))
+ end
+ table.insert(resp, '')
+ table.insert(resp, content)
+ resp = table.concat(resp, '\r\n')
+ send_and_close(sock, resp)
+ return true
+end
+
+local function listenFunc(sock)
+ local buf = ''
+ local method
+ local path
+ local headers = { }
+ local header_done
+ local content_length = 0
+ local function recv(sock, c)
+ local function bad_request()
+ send_and_close(sock, 'HTTP/1.1 400 Bad request\r\n')
+ end
+ buf = buf .. c
+ while true do
+ if not header_done then
+ local s = buf:find('\r\n', 1, true)
+ if s then
+ local line = buf:sub(1, s - 1)
+ buf = buf:sub(s + 2)
+ if not method then
+ method, path = line:match('^(%u+) (%S+) HTTP/1%.1$')
+ if not method then
+ bad_request()
+ break
+ end
+ elseif not header_done then
+ if line == '' then
+ header_done = true
+ else
+ local h, v = line:match('^([-a-zA-Z]+): (.*)$')
+ if h then
+ h = h:lower()
+ headers[h] = v
+ if h == 'content-length' then
+ content_length = tonumber(v)
+ end
+ else
+ bad_request()
+ break
+ end
+ end
+ end
+ else
+ break
+ end
+ elseif content_length == #buf then
+ local code, status, headers, content
+ = process(method, path, headers, buf)
+ if not code then
+ code, status, headers, content
+ = default(method, path, headers, buf)
+ end
+ response(sock, code, status, headers, content)
+ break
+ else
+ break
+ end
+ end
+ end
+ sock:on('receive', recv)
+end
+
+function httpd.init(processFunc)
+ socket = net.createServer()
+ socket:listen(80, listenFunc)
+ process = processFunc
+end
+
+function httpd.dump(method, path, headers, content)
+ local c = { method, path }
+ for k, v in pairs(headers) do
+ table.insert(c, string.format('%s: %s', k, v))
+ end
+ table.insert(c, content)
+ return 200, 'OK', { }, table.concat(c, '\n')
+end
+
+function httpd.upload(method, path, headers, content)
+ if method == 'POST' and path:sub(1, 8) == '/upload/' then
+ local fname = path:sub(9)
+ file.open(fname, "w")
+ file.write(content)
+ file.close()
+ return 200, 'OK', { }, 'OK, file uploaded.\n'
+ end
+end
+
+function httpd.parse_form(form)
+ local r = { }
+ local function a(p)
+ local k, v = p:match('(.-)=(.*)')
+ if k and v then
+ v = v:gsub('%%3A', ':')
+ r[k] = v
+ end
+ end
+ local s = form:find('&')
+ while s do
+ a(form:sub(1, s - 1))
+ form = form:sub(s + 1)
+ s = form:find('&')
+ end
+ a(form)
+ return r
+end
+
+return httpd