local cqueues = require 'cqueues' local cq_condition = require'cqueues.condition' local http_server = require 'http.server' local http_headers = require 'http.headers' local http_util = require 'http.util' local http_cookie = require 'http.cookie' local pprint=require'pprint' local function make_req(app, server, stream) local req = {server=server, stream=stream, app=app} req.headers = assert(req.stream:get_headers()) req.method = req.headers:get":method" local path = req.headers:get":path" local sel, qs = path:match"^(.-)%?(.*)$" if sel then path = sel end req.path, req.qs = path, qs req.args = {} if req.qs then for k,v in http_util.query_args(req.qs) do req.args[k] = v end end req.cookies = assert(http_cookie.parse_cookies(req.headers)) if req.method == 'POST' then req.form = {} local content_type = req.headers:get'content-type' local is_form = content_type:match'^application/x%-www%-form%-urlencoded' if is_form then local body = stream:get_body_as_string() -- i'm like 96% sure this is correct body = body:gsub('+',' ') for k,v in http_util.query_args(body) do req.form[k] = v end end end req.app:run_hooks('pre_req',req) return req end -- take return values of view function, construct and send http response local function make_resp(req, body, header_dict, status_code) if body == false then return end -- assume view sent its own response local rheaders if rawequal(getmetatable(header_dict), http_headers.mt) then rheaders = header_dict else rheaders = http_headers.new() for k,v in pairs(header_dict or {}) do rheaders:append(k:gsub("_","-"), v) end if not rheaders:has':status' then rheaders:append(':status',tostring(status_code or 200)) end if not rheaders:has'content-type' then rheaders:append('content-type','text/html') end end req.app:run_hooks('post_req',req,body,rheaders,status_code) assert(req.stream:write_headers(rheaders, false)) if type(body) == 'string' then assert(req.stream:write_body_from_string(body)) elseif io.type(body) == 'file' then assert(req.stream:write_body_from_file(body)) body:close() elseif type(body) == 'table' and getmetatable(body).__tostring then assert(req.stream:write_body_from_string(tostring(body))) else error('unsupported response type: '..type(body)) end -- todo maybe: chunk iterators, if i need them end local function handle_request(app, server, stream) local req = make_req(app, server, stream) print('handling',app.version,req.method,req.path) local ok, body, headers, status = xpcall(app.view, debug.traceback, req) if not ok then if type(body)=='table' and body.fail then status,headers = body.status, nil body = '
'..body.tb..'' else status, headers = 500, nil body = '
'..body..'' end end return make_resp(req, body, headers, status) end local function onerror(server, ctx, op, err, errno) local msg = op .. " on " .. tostring(ctx) .. " failed" if err then msg = msg.. ": " .. tostring(err) end io.stderr:write(msg, "\n") end local function run(cq, app) local server = assert(http_server.listen { cq = cq, host = '0.0.0.0', port = 8082, onerror = onerror, onstream = function(server, stream) local ok, err = xpcall(handle_request, debug.traceback, app, server, stream) if not ok then error(err, 0) end end }) assert(server:listen()) local _,addr,port = server:localname() print(("listening on http://%s:%s"):format(addr,port)) end return { run = run, }