diff options
Diffstat (limited to 'net/http')
-rw-r--r-- | net/http/codes.lua | 15 | ||||
-rw-r--r-- | net/http/parser.lua | 8 | ||||
-rw-r--r-- | net/http/server.lua | 92 |
3 files changed, 89 insertions, 26 deletions
diff --git a/net/http/codes.lua b/net/http/codes.lua index 0cadd079..1090e545 100644 --- a/net/http/codes.lua +++ b/net/http/codes.lua @@ -25,6 +25,7 @@ local response_codes = { [305] = "Use Proxy"; -- The 306 status code was used in a previous version of [RFC2616], is no longer used, and the code is reserved. [307] = "Temporary Redirect"; + [308] = "Permanent Redirect"; [400] = "Bad Request"; [401] = "Unauthorized"; @@ -39,17 +40,22 @@ local response_codes = { [410] = "Gone"; [411] = "Length Required"; [412] = "Precondition Failed"; - [413] = "Request Entity Too Large"; - [414] = "Request-URI Too Long"; + [413] = "Payload Too Large"; + [414] = "URI Too Long"; [415] = "Unsupported Media Type"; - [416] = "Requested Range Not Satisfiable"; + [416] = "Range Not Satisfiable"; [417] = "Expectation Failed"; [418] = "I'm a teapot"; + [421] = "Misdirected Request"; [422] = "Unprocessable Entity"; [423] = "Locked"; [424] = "Failed Dependency"; -- The 425 status code is reserved for the WebDAV advanced collections expired proposal [RFC2817] [426] = "Upgrade Required"; + [428] = "Precondition Required"; + [429] = "Too Many Requests"; + [431] = "Request Header Fields Too Large"; + [451] = "Unavailable For Legal Reasons"; [500] = "Internal Server Error"; [501] = "Not Implemented"; @@ -61,7 +67,8 @@ local response_codes = { [507] = "Insufficient Storage"; [508] = "Loop Detected"; [510] = "Not Extended"; + [511] = "Network Authentication Required"; }; for k,v in pairs(response_codes) do response_codes[k] = k.." "..v; end -return setmetatable(response_codes, { __index = function(t, k) return k.." Unassigned"; end }) +return setmetatable(response_codes, { __index = function(_, k) return k.." Unassigned"; end }) diff --git a/net/http/parser.lua b/net/http/parser.lua index 1e698728..4e4ae9fb 100644 --- a/net/http/parser.lua +++ b/net/http/parser.lua @@ -38,7 +38,7 @@ function httpstream.new(success_cb, error_cb, parser_type, options_cb) local have_body; local error; return { - feed = function(self, data) + feed = function(_, data) if error then return nil, "parse has failed"; end if not data then -- EOF if buftable then buf, buftable = t_concat(buf), false; end @@ -46,7 +46,7 @@ function httpstream.new(success_cb, error_cb, parser_type, options_cb) packet.body = buf; success_cb(packet); elseif buf ~= "" then -- unexpected EOF - error = true; return error_cb(); + error = true; return error_cb("unexpected-eof"); end return; end @@ -134,6 +134,9 @@ function httpstream.new(success_cb, error_cb, parser_type, options_cb) if state then -- read body if client then if chunked then + if chunk_start and buflen - chunk_start - 2 < chunk_size then + return; + end -- not enough data if buftable then buf, buftable = t_concat(buf), false; end if not buf:find("\r\n", nil, true) then return; @@ -150,6 +153,7 @@ function httpstream.new(success_cb, error_cb, parser_type, options_cb) elseif buflen - chunk_start - 2 >= chunk_size then -- we have a chunk packet.body = packet.body..buf:sub(chunk_start, chunk_start + (chunk_size-1)); buf = buf:sub(chunk_start + chunk_size + 2); + buflen = buflen - (chunk_start + chunk_size + 2 - 1); chunk_size, chunk_start = nil, nil; else -- Partial chunk remaining break; diff --git a/net/http/server.lua b/net/http/server.lua index 32cda8aa..877c7f17 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -11,11 +11,14 @@ local setmetatable = setmetatable; local xpcall = xpcall; local traceback = debug.traceback; local tostring = tostring; +local cache = require "util.cache"; local codes = require "net.http.codes"; +local blocksize = 2^16; local _M = {}; local sessions = {}; +local incomplete = {}; local listener = {}; local hosts = {}; local default_host; @@ -28,7 +31,10 @@ local function is_wildcard_match(wildcard_event, event) return wildcard_event:sub(1, -2) == event:sub(1, #wildcard_event-1); end -local recent_wildcard_events, max_cached_wildcard_events = {}, 10000; +local _handlers = events._handlers; +local recent_wildcard_events = cache.new(10000, function (key, value) -- luacheck: ignore 212/value + rawset(_handlers, key, nil); +end); local event_map = events._event_map; setmetatable(events._handlers, { @@ -63,10 +69,7 @@ setmetatable(events._handlers, { end rawset(handlers, curr_event, handlers_array); if not event_map[curr_event] then -- Only wildcard handlers match, if any - table.insert(recent_wildcard_events, curr_event); - if #recent_wildcard_events > max_cached_wildcard_events then - rawset(handlers, table.remove(recent_wildcard_events, 1), nil); - end + recent_wildcard_events:set(curr_event, true); end return handlers_array; end; @@ -143,17 +146,26 @@ function listener.ondisconnect(conn) open_response.finished = true; open_response:on_destroy(); end + incomplete[conn] = nil; sessions[conn] = nil; end function listener.ondetach(conn) sessions[conn] = nil; + incomplete[conn] = nil; end function listener.onincoming(conn, data) sessions[conn]:feed(data); end +function listener.ondrain(conn) + local response = incomplete[conn]; + if response and response._send_more then + response._send_more(); + end +end + local headerfix = setmetatable({}, { __index = function(t, k) local v = "\r\n"..k:gsub("_", "-"):gsub("%f[%w].", s_upper)..": "; @@ -162,7 +174,7 @@ local headerfix = setmetatable({}, { end }); -function _M.hijack_response(response, listener) +function _M.hijack_response(response, listener) -- luacheck: ignore error("TODO"); end function handle_request(conn, request, finish_cb) @@ -193,6 +205,8 @@ function handle_request(conn, request, finish_cb) persistent = persistent; conn = conn; send = _M.send_response; + send_file = _M.send_file; + done = _M.finish_response; finish_cb = finish_cb; }; conn._http_open_response = response; @@ -212,10 +226,10 @@ function handle_request(conn, request, finish_cb) err_code, err = 400, "Missing or invalid 'Host' header"; end end - + if err then response.status_code = err_code; - response:send(events.fire_event("http-error", { code = err_code, message = err })); + response:send(events.fire_event("http-error", { code = err_code, message = err, response = response })); return; end @@ -230,7 +244,8 @@ function handle_request(conn, request, finish_cb) if result_type == "number" then response.status_code = result; if result >= 400 then - body = events.fire_event("http-error", { code = result }); + payload.code = result; + body = events.fire_event("http-error", payload); end elseif result_type == "string" then body = result; @@ -252,26 +267,63 @@ function handle_request(conn, request, finish_cb) -- if handler not called, return 404 response.status_code = 404; - response:send(events.fire_event("http-error", { code = 404 })); + payload.code = 404; + response:send(events.fire_event("http-error", payload)); end -function _M.send_response(response, body) - if response.finished then return; end - response.finished = true; - response.conn._http_open_response = nil; - +local function prepare_header(response) local status_line = "HTTP/"..response.request.httpversion.." "..(response.status or codes[response.status_code]); local headers = response.headers; - body = body or response.body or ""; - headers.content_length = #body; - local output = { status_line }; for k,v in pairs(headers) do t_insert(output, headerfix[k]..v); end t_insert(output, "\r\n\r\n"); + return output; +end +_M.prepare_header = prepare_header; +function _M.send_response(response, body) + if response.finished then return; end + body = body or response.body or ""; + response.headers.content_length = #body; + local output = prepare_header(response); t_insert(output, body); - response.conn:write(t_concat(output)); + response:done(); +end +function _M.send_file(response, f) + if response.finished then return; end + local chunked = not response.headers.content_length; + if chunked then response.headers.transfer_encoding = "chunked"; end + incomplete[response.conn] = response; + response._send_more = function () + if response.finished then + incomplete[response.conn] = nil; + return; + end + local chunk = f:read(blocksize); + if chunk then + if chunked then + chunk = ("%x\r\n%s\r\n"):format(#chunk, chunk); + end + -- io.write("."); io.flush(); + response.conn:write(chunk); + else + if chunked then + response.conn:write("0\r\n\r\n"); + end + -- io.write("\n"); + if f.close then f:close(); end + incomplete[response.conn] = nil; + return response:done(); + end + end + response.conn:write(t_concat(prepare_header(response))); + return true; +end +function _M.finish_response(response) + if response.finished then return; end + response.finished = true; + response.conn._http_open_response = nil; if response.on_destroy then response:on_destroy(); response.on_destroy = nil; @@ -290,7 +342,7 @@ function _M.remove_handler(event, handler) end function _M.listen_on(port, interface, ssl) - addserver(interface or "*", port, listener, "*a", ssl); + return addserver(interface or "*", port, listener, "*a", ssl); end function _M.add_host(host) hosts[host] = true; |