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