From d95a61cf4e19f386bd06d988905ef7a8de96be5f Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Sun, 8 Apr 2012 04:09:33 +0500 Subject: net.http.{server|codes|parser}: Initial commit. --- net/http/server.lua | 223 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 net/http/server.lua (limited to 'net/http/server.lua') diff --git a/net/http/server.lua b/net/http/server.lua new file mode 100644 index 00000000..788f046b --- /dev/null +++ b/net/http/server.lua @@ -0,0 +1,223 @@ + +local t_insert, t_remove, t_concat = table.insert, table.remove, table.concat; +local parser_new = require "net.http.parser".new; +local events = require "util.events".new(); +local addserver = require "net.server".addserver; +local log = require "util.logger".init("http.server"); +local os_date = os.date; +local pairs = pairs; +local s_upper = string.upper; +local setmetatable = setmetatable; +local xpcall = xpcall; +local debug = debug; +local tostring = tostring; +local codes = require "net.http.codes"; +local _G = _G; + +local _M = {}; + +local sessions = {}; +local handlers = {}; + +local listener = {}; + +local handle_request; +local _1, _2, _3; +local function _handle_request() return handle_request(_1, _2, _3); end +local function _traceback_handler(err) log("error", "Traceback[http]: %s: %s", tostring(err), debug.traceback()); end + +function listener.onconnect(conn) + local secure = conn:ssl() and true or nil; + local pending = {}; + local waiting = false; + local function process_next(last_response) + --if waiting then log("debug", "can't process_next, waiting"); return; end + if sessions[conn] and #pending > 0 then + local request = t_remove(pending); + --log("debug", "process_next: %s", request.path); + waiting = true; + --handle_request(conn, request, process_next); + _1, _2, _3 = conn, request, process_next; + if not xpcall(_handle_request, _traceback_handler) then + conn:write("HTTP/1.0 503 Internal Server Error\r\n\r\nAn error occured during the processing of this request."); + conn:close(); + end + else + --log("debug", "ready for more"); + waiting = false; + end + end + local function success_cb(request) + --log("debug", "success_cb: %s", request.path); + request.secure = secure; + t_insert(pending, request); + if not waiting then + process_next(); + end + end + local function error_cb(err) + log("debug", "error_cb: %s", err or ""); + -- FIXME don't close immediately, wait until we process current stuff + -- FIXME if err, send off a bad-request response + sessions[conn] = nil; + conn:close(); + end + sessions[conn] = parser_new(success_cb, error_cb); +end + +function listener.ondisconnect(conn) + sessions[conn] = nil; +end + +function listener.onincoming(conn, data) + sessions[conn]:feed(data); +end + +local headerfix = setmetatable({}, { + __index = function(t, k) + local v = "\r\n"..k:gsub("_", "-"):gsub("%f[%w].", s_upper)..": "; + t[k] = v; + return v; + end +}); + +function _M.hijack_response(response, listener) + error("TODO"); +end +function handle_request(conn, request, finish_cb) + --log("debug", "handler: %s", request.path); + local headers = {}; + for k,v in pairs(request.headers) do headers[k:gsub("-", "_")] = v; end + request.headers = headers; + request.conn = conn; + + local date_header = os_date('!%a, %d %b %Y %H:%M:%S GMT'); -- FIXME use + local conn_header = request.headers.connection; + local keep_alive = conn_header == "Keep-Alive" or (request.httpversion == "1.1" and conn_header ~= "close"); + + local response = { + request = request; + status_code = 200; + headers = { date = date_header, connection = (keep_alive and "Keep-Alive" or "close") }; + conn = conn; + send = _M.send_response; + finish_cb = finish_cb; + }; + + if not request.headers.host then + response.status_code = 400; + response.headers.content_type = "text/html"; + response:send("400 Bad Request400 Bad Request: No Host header."); + else + -- TODO call handler + --response.headers.content_type = "text/plain"; + --response:send("host="..(request.headers.host or "").."\npath="..request.path.."\n"..(request.body or "")); + local host = request.headers.host; + if host then + host = host:match("[^:]*"):lower(); + local event = request.method.." "..host..request.path:match("[^?]*"); + local payload = { request = request, response = response }; + --[[repeat + if events.fire_event(event, payload) ~= nil then return; end + event = (event:sub(-1) == "/") and event:sub(1, -1) or event:gsub("[^/]*$", ""); + if event:sub(-1) == "/" then + event = event:sub(1, -1); + else + event = event:gsub("[^/]*$", ""); + end + until not event:find("/", 1, true);]] + --log("debug", "Event: %s", event); + if events.fire_event(event, payload) ~= nil then return; end + -- TODO try adding/stripping / at the end, but this needs to work via an HTTP redirect + end + + -- if handler not called, fallback to legacy httpserver handlers + _M.legacy_handler(request, response); + end +end +function _M.send_response(response, body) + local status_line = "HTTP/"..response.request.httpversion.." "..(response.status or codes[response.status_code]); + local headers = response.headers; + body = 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"); + t_insert(output, body); + + response.conn:write(t_concat(output)); + if headers.connection == "Keep-Alive" then + response:finish_cb(); + else + response.conn:close(); + end +end +function _M.legacy_handler(request, response) + log("debug", "Invoking legacy handler"); + local base = request.path:match("^/([^/?]+)"); + local legacy_server = _G.httpserver and _G.httpserver.new.http_servers[5280]; + local handler = legacy_server and legacy_server.handlers[base]; + if not handler then handler = _G.httpserver and _G.httpserver.set_default_handler.default_handler; end + if handler then + -- add legacy properties to request object + request.url = { path = request.path }; + request.handler = response.conn; + request.id = tostring{}:match("%x+$"); + local headers = {}; + for k,v in pairs(request.headers) do + headers[k:gsub("_", "-")] = v; + end + request.headers = headers; + function request:send(resp) + if self.destroyed then return; end + if resp.body or resp.headers then + if resp.headers then + for k,v in pairs(resp.headers) do response.headers[k] = v; end + end + response:send(resp.body) + else + response:send(resp) + end + self.sent = true; + self:destroy(); + end + function request:destroy() + if self.destroyed then return; end + if not self.sent then return self:send(""); end + self.destroyed = true; + if self.on_destroy then + log("debug", "Request has destroy callback"); + self:on_destroy(); + else + log("debug", "Request has no destroy callback"); + end + end + local r = handler(request.method, request.body, request); + if r ~= true then + request:send(r); + end + else + log("debug", "No handler found"); + response.status_code = 404; + response.headers.content_type = "text/html"; + response:send("404 Not Found404 Not Found: No such page."); + end +end + +function _M.add_handler(event, handler, priority) + events.add_handler(event, handler, priority); +end +function _M.remove_handler(event, handler) + events.remove_handler(event, handler); +end + +function _M.listen_on(port, interface, ssl) + addserver(interface or "*", port, listener, "*a", ssl); +end + +_M.listener = listener; +_M.codes = codes; +return _M; -- cgit v1.2.3 From 4c43038f7494c0800c27ff198a931b92e63c02d1 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Sat, 21 Apr 2012 17:37:00 +0500 Subject: net.http.server: Missing in 404 Not Found response. --- net/http/server.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/http/server.lua') diff --git a/net/http/server.lua b/net/http/server.lua index 788f046b..feb8f766 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -203,7 +203,7 @@ function _M.legacy_handler(request, response) log("debug", "No handler found"); response.status_code = 404; response.headers.content_type = "text/html"; - response:send("<html><head>404 Not Found</head><body>404 Not Found: No such page.</body></html>"); + response:send("<html><head><title>404 Not Found404 Not Found: No such page."); end end -- cgit v1.2.3 From 7f2c53a45dd7583d4cf560988c81e0ff59d007e1 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Sat, 21 Apr 2012 17:38:01 +0500 Subject: net.http.server: Fire global HTTP event when no specific handlers available. --- net/http/server.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'net/http/server.lua') diff --git a/net/http/server.lua b/net/http/server.lua index feb8f766..1f61c7b9 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -129,6 +129,7 @@ function handle_request(conn, request, finish_cb) --log("debug", "Event: %s", event); if events.fire_event(event, payload) ~= nil then return; end -- TODO try adding/stripping / at the end, but this needs to work via an HTTP redirect + if events.fire_event("*", payload) ~= nil then return; end end -- if handler not called, fallback to legacy httpserver handlers -- cgit v1.2.3 From 608d074ba071a82cb409eb5a8b7aa5b5c4bd4eb4 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Sun, 22 Apr 2012 23:44:21 +0500 Subject: net.http.server: Fix legacy net.httpserver fallback (httpserver is no longer a global). --- net/http/server.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'net/http/server.lua') diff --git a/net/http/server.lua b/net/http/server.lua index 1f61c7b9..3a0cb53a 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -13,6 +13,7 @@ local debug = debug; local tostring = tostring; local codes = require "net.http.codes"; local _G = _G; +local legacy_httpserver = require "net.httpserver"; local _M = {}; @@ -159,9 +160,9 @@ end function _M.legacy_handler(request, response) log("debug", "Invoking legacy handler"); local base = request.path:match("^/([^/?]+)"); - local legacy_server = _G.httpserver and _G.httpserver.new.http_servers[5280]; + local legacy_server = legacy_httpserver and legacy_httpserver.new.http_servers[5280]; local handler = legacy_server and legacy_server.handlers[base]; - if not handler then handler = _G.httpserver and _G.httpserver.set_default_handler.default_handler; end + if not handler then handler = legacy_httpserver and legacy_httpserver.set_default_handler.default_handler; end if handler then -- add legacy properties to request object request.url = { path = request.path }; -- cgit v1.2.3 From b19b3bb07d6d7da9ed4562945db3038214aa9baf Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Apr 2012 21:29:18 +0100 Subject: net.http.server: Support for wildcard events (events that end with '/*') --- net/http/server.lua | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) (limited to 'net/http/server.lua') diff --git a/net/http/server.lua b/net/http/server.lua index 3a0cb53a..94487e5e 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -22,6 +22,57 @@ local handlers = {}; local listener = {}; +local function is_wildcard_event(event) + return event:sub(-2, -1) == "/*"; +end +local function is_wildcard_match(wildcard_event, event) + log("debug", "comparing %q with %q", wildcard_event:sub(1, -2), event:sub(1, #wildcard_event-1)); + return wildcard_event:sub(1, -2) == event:sub(1, #wildcard_event-1); +end + +local event_map = events._event_map; +setmetatable(events._handlers, { + __index = function (handlers, curr_event) + if is_wildcard_event(curr_event) then return; end -- Wildcard events cannot be fired + -- Find all handlers that could match this event, sort them + -- and then put the array into handlers[event] + local matching_handlers_set = {}; + local handlers_array = {}; + for event, handlers_set in pairs(event_map) do + if event == curr_event or + is_wildcard_event(event) and is_wildcard_match(event, curr_event) then + for handler, priority in pairs(handlers_set) do + matching_handlers_set[handler] = { (select(2, event:gsub("/", "%1"))), priority }; + table.insert(handlers_array, handler); + end + end + end + if #handlers_array == 0 then return; end + table.sort(handlers_array, function(b, a) + local a_score, b_score = matching_handlers_set[a], matching_handlers_set[b]; + for i = 1, #a_score do + if a ~= b then -- If equal, compare next score value + return a_score[i] < b_score[i]; + end + end + return false; + end); + handlers[curr_event] = handlers_array; + return handlers_array; + end; + __newindex = function (handlers, curr_event, handlers_array) + if handlers_array == nil + and is_wildcard_event(curr_event) then + -- Invalidate all matching + for event in pairs(handlers) do + if is_wildcard_match(curr_event, event) then + handlers[event] = nil; + end + end + end + end; +}); + local handle_request; local _1, _2, _3; local function _handle_request() return handle_request(_1, _2, _3); end -- cgit v1.2.3 From 58896e197237213df9061f5e141530387887f814 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 24 Apr 2012 19:05:45 +0100 Subject: net.http.server: Remove debug message --- net/http/server.lua | 1 - 1 file changed, 1 deletion(-) (limited to 'net/http/server.lua') diff --git a/net/http/server.lua b/net/http/server.lua index 94487e5e..50a5c5a1 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -26,7 +26,6 @@ local function is_wildcard_event(event) return event:sub(-2, -1) == "/*"; end local function is_wildcard_match(wildcard_event, event) - log("debug", "comparing %q with %q", wildcard_event:sub(1, -2), event:sub(1, #wildcard_event-1)); return wildcard_event:sub(1, -2) == event:sub(1, #wildcard_event-1); end -- cgit v1.2.3 From b23e6a2ef012c2d2568766ef0f41aaadf3cac826 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 24 Apr 2012 19:07:12 +0100 Subject: net.http.server: Handle results returned by handlers, and send as a response. Also removes explicit firing of '*', which can now be done via wildcard events. --- net/http/server.lua | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) (limited to 'net/http/server.lua') diff --git a/net/http/server.lua b/net/http/server.lua index 50a5c5a1..185ac9a0 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -168,19 +168,27 @@ function handle_request(conn, request, finish_cb) host = host:match("[^:]*"):lower(); local event = request.method.." "..host..request.path:match("[^?]*"); local payload = { request = request, response = response }; - --[[repeat - if events.fire_event(event, payload) ~= nil then return; end - event = (event:sub(-1) == "/") and event:sub(1, -1) or event:gsub("[^/]*$", ""); - if event:sub(-1) == "/" then - event = event:sub(1, -1); - else - event = event:gsub("[^/]*$", ""); + --log("debug", "Firing event: %s", event); + local result = events.fire_event(event, payload); + if result ~= nil then + if result ~= true then + local code, body = 200, ""; + local result_type = type(result); + if result_type == "number" then + response.status_code = result; + elseif result_type == "string" then + body = result; + elseif result_type == "table" then + body = result.body; + result.body = nil; + for k, v in pairs(result) do + response[k] = v; + end + end + response:send(body); end - until not event:find("/", 1, true);]] - --log("debug", "Event: %s", event); - if events.fire_event(event, payload) ~= nil then return; end - -- TODO try adding/stripping / at the end, but this needs to work via an HTTP redirect - if events.fire_event("*", payload) ~= nil then return; end + return; + end end -- if handler not called, fallback to legacy httpserver handlers -- cgit v1.2.3 From e6f8d445c0ee25e310dd64258fe43a472c4308bd Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 25 Apr 2012 14:58:10 +0100 Subject: net.http.server: Remove legacy compatibility --- net/http/server.lua | 61 ++++------------------------------------------------- 1 file changed, 4 insertions(+), 57 deletions(-) (limited to 'net/http/server.lua') diff --git a/net/http/server.lua b/net/http/server.lua index 185ac9a0..00571bce 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -160,9 +160,6 @@ function handle_request(conn, request, finish_cb) response.headers.content_type = "text/html"; response:send("400 Bad Request400 Bad Request: No Host header."); else - -- TODO call handler - --response.headers.content_type = "text/plain"; - --response:send("host="..(request.headers.host or "").."\npath="..request.path.."\n"..(request.body or "")); local host = request.headers.host; if host then host = host:match("[^:]*"):lower(); @@ -191,8 +188,10 @@ function handle_request(conn, request, finish_cb) end end - -- if handler not called, fallback to legacy httpserver handlers - _M.legacy_handler(request, response); + -- if handler not called, return 404 + response.status_code = 404; + response.headers.content_type = "text/html"; + response:send("404 Not Found404 Not Found: No such page."); end end function _M.send_response(response, body) @@ -215,58 +214,6 @@ function _M.send_response(response, body) response.conn:close(); end end -function _M.legacy_handler(request, response) - log("debug", "Invoking legacy handler"); - local base = request.path:match("^/([^/?]+)"); - local legacy_server = legacy_httpserver and legacy_httpserver.new.http_servers[5280]; - local handler = legacy_server and legacy_server.handlers[base]; - if not handler then handler = legacy_httpserver and legacy_httpserver.set_default_handler.default_handler; end - if handler then - -- add legacy properties to request object - request.url = { path = request.path }; - request.handler = response.conn; - request.id = tostring{}:match("%x+$"); - local headers = {}; - for k,v in pairs(request.headers) do - headers[k:gsub("_", "-")] = v; - end - request.headers = headers; - function request:send(resp) - if self.destroyed then return; end - if resp.body or resp.headers then - if resp.headers then - for k,v in pairs(resp.headers) do response.headers[k] = v; end - end - response:send(resp.body) - else - response:send(resp) - end - self.sent = true; - self:destroy(); - end - function request:destroy() - if self.destroyed then return; end - if not self.sent then return self:send(""); end - self.destroyed = true; - if self.on_destroy then - log("debug", "Request has destroy callback"); - self:on_destroy(); - else - log("debug", "Request has no destroy callback"); - end - end - local r = handler(request.method, request.body, request); - if r ~= true then - request:send(r); - end - else - log("debug", "No handler found"); - response.status_code = 404; - response.headers.content_type = "text/html"; - response:send("404 Not Found404 Not Found: No such page."); - end -end - function _M.add_handler(event, handler, priority) events.add_handler(event, handler, priority); end -- cgit v1.2.3 From 0558bb4bb6fb21dbeb1e1e209286b88f38bee2c8 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 25 Apr 2012 15:02:27 +0100 Subject: net.http.server: Small fix to comment --- net/http/server.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/http/server.lua') diff --git a/net/http/server.lua b/net/http/server.lua index 00571bce..dc7c5a06 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -34,7 +34,7 @@ setmetatable(events._handlers, { __index = function (handlers, curr_event) if is_wildcard_event(curr_event) then return; end -- Wildcard events cannot be fired -- Find all handlers that could match this event, sort them - -- and then put the array into handlers[event] + -- and then put the array into handlers[curr_event] (and return it) local matching_handlers_set = {}; local handlers_array = {}; for event, handlers_set in pairs(event_map) do -- cgit v1.2.3 From 7e60956c9e072083e1aa33fce1cbd7f1d80a4b2e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 25 Apr 2012 23:08:33 +0100 Subject: net.http.server: Support for on_destroy callback on response objects, and a 'finished' flag to say when they are destroyed (responded to or connection closed) --- net/http/server.lua | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'net/http/server.lua') diff --git a/net/http/server.lua b/net/http/server.lua index dc7c5a06..c108a398 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -117,6 +117,11 @@ function listener.onconnect(conn) end function listener.ondisconnect(conn) + local open_response = conn._http_open_response; + if open_response and open_response.on_destroy then + open_response.finished = true; + open_response:on_destroy(); + end sessions[conn] = nil; end @@ -154,6 +159,7 @@ function handle_request(conn, request, finish_cb) send = _M.send_response; finish_cb = finish_cb; }; + conn._http_open_response = response; if not request.headers.host then response.status_code = 400; @@ -195,6 +201,10 @@ function handle_request(conn, request, finish_cb) end end function _M.send_response(response, body) + if response.finished then return; end + response.finished = true; + response.conn._http_open_response = nil; + local status_line = "HTTP/"..response.request.httpversion.." "..(response.status or codes[response.status_code]); local headers = response.headers; body = body or ""; @@ -208,6 +218,10 @@ function _M.send_response(response, body) t_insert(output, body); response.conn:write(t_concat(output)); + if response.on_destroy then + response:on_destroy(); + response.on_destroy = nil; + end if headers.connection == "Keep-Alive" then response:finish_cb(); else -- cgit v1.2.3 From 2416b4e47b1dc2e04b394138fb38ace0f7e5cc61 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Apr 2012 04:24:43 +0100 Subject: net.http.server: Fix to compare priority if path lengths are the same (logic fail) --- net/http/server.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/http/server.lua') diff --git a/net/http/server.lua b/net/http/server.lua index c108a398..58554f39 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -50,7 +50,7 @@ setmetatable(events._handlers, { table.sort(handlers_array, function(b, a) local a_score, b_score = matching_handlers_set[a], matching_handlers_set[b]; for i = 1, #a_score do - if a ~= b then -- If equal, compare next score value + if a_score[i] ~= b_score[i] then -- If equal, compare next score value return a_score[i] < b_score[i]; end end -- cgit v1.2.3 From e3d135d3f1ee7a956228e642024e00e3da5cd807 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Apr 2012 06:47:17 +0100 Subject: net.http.server: Lower score of wildcard handlers to ensure specific handlers beat them --- net/http/server.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/http/server.lua') diff --git a/net/http/server.lua b/net/http/server.lua index 58554f39..906b3eaf 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -41,7 +41,7 @@ setmetatable(events._handlers, { if event == curr_event or is_wildcard_event(event) and is_wildcard_match(event, curr_event) then for handler, priority in pairs(handlers_set) do - matching_handlers_set[handler] = { (select(2, event:gsub("/", "%1"))), priority }; + matching_handlers_set[handler] = { (select(2, event:gsub("/", "%1"))), is_wildcard_event(event) and 0 or 1, priority }; table.insert(handlers_array, handler); end end -- cgit v1.2.3 From 0e5b2786b420aeaea1372c532b464f9e48d40fa1 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Apr 2012 06:58:57 +0100 Subject: net.http.server: Expose events object (for debug purposes) --- net/http/server.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'net/http/server.lua') diff --git a/net/http/server.lua b/net/http/server.lua index 906b3eaf..d6c3a03a 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -241,4 +241,5 @@ end _M.listener = listener; _M.codes = codes; +_M._events = events; return _M; -- cgit v1.2.3 From 99e8f3cff54ce9238756023e682e96d59bdde4ae Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Apr 2012 15:05:05 +0100 Subject: net.server.http: Parse absolute URIs in requests (thanks Maranda) --- net/http/server.lua | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'net/http/server.lua') diff --git a/net/http/server.lua b/net/http/server.lua index d6c3a03a..4f9e1463 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -1,6 +1,7 @@ local t_insert, t_remove, t_concat = table.insert, table.remove, table.concat; local parser_new = require "net.http.parser".new; +local url_parse = require "socket.url".parse; local events = require "util.events".new(); local addserver = require "net.server".addserver; local log = require "util.logger".init("http.server"); @@ -101,6 +102,9 @@ function listener.onconnect(conn) local function success_cb(request) --log("debug", "success_cb: %s", request.path); request.secure = secure; + local parsed_dest = url_parse(request.path); + request.url = parsed_dest; + request.path = parsed_dest.path; t_insert(pending, request); if not waiting then process_next(); -- cgit v1.2.3 From de13771c8b72ee9137ca8e661beb10b80e58d1e3 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Apr 2012 15:14:19 +0100 Subject: net.http.server: Make error handling overrideable via 'http-error' event --- net/http/server.lua | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'net/http/server.lua') diff --git a/net/http/server.lua b/net/http/server.lua index 4f9e1463..deaa44cd 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -76,7 +76,12 @@ setmetatable(events._handlers, { local handle_request; local _1, _2, _3; local function _handle_request() return handle_request(_1, _2, _3); end -local function _traceback_handler(err) log("error", "Traceback[http]: %s: %s", tostring(err), debug.traceback()); end + +local last_err; +local function _traceback_handler(err) last_err = err; log("error", "Traceback[http]: %s: %s", tostring(err), debug.traceback()); end +events.add_handler("http-error", function (error) + return "Error processing request: "..codes[error.code]..". Check your error log for more information."; +end, -1); function listener.onconnect(conn) local secure = conn:ssl() and true or nil; @@ -91,7 +96,7 @@ function listener.onconnect(conn) --handle_request(conn, request, process_next); _1, _2, _3 = conn, request, process_next; if not xpcall(_handle_request, _traceback_handler) then - conn:write("HTTP/1.0 503 Internal Server Error\r\n\r\nAn error occured during the processing of this request."); + conn:write("HTTP/1.0 500 Internal Server Error\r\n\r\n"..events.fire_event("http-error", { code = 500, private_message = last_err })); conn:close(); end else @@ -168,7 +173,7 @@ function handle_request(conn, request, finish_cb) if not request.headers.host then response.status_code = 400; response.headers.content_type = "text/html"; - response:send("400 Bad Request400 Bad Request: No Host header."); + response:send(events.fire_event("http-error", { code = 400, message = "No 'Host' header" })); else local host = request.headers.host; if host then @@ -183,6 +188,9 @@ function handle_request(conn, request, finish_cb) local result_type = type(result); if result_type == "number" then response.status_code = result; + if result >= 400 then + body = events.fire_event("http-error", { code = result }); + end elseif result_type == "string" then body = result; elseif result_type == "table" then @@ -201,7 +209,7 @@ function handle_request(conn, request, finish_cb) -- if handler not called, return 404 response.status_code = 404; response.headers.content_type = "text/html"; - response:send("404 Not Found404 Not Found: No such page."); + response:send(events.fire_event("http-error", { code = 404 })); end end function _M.send_response(response, body) -- cgit v1.2.3 From 9db4ba52f9cbab52721ed819e4e81f3cbd62d106 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Apr 2012 16:11:08 +0100 Subject: net.http.server: Fire http-error 400 if request fails sanity checks --- net/http/server.lua | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'net/http/server.lua') diff --git a/net/http/server.lua b/net/http/server.lua index deaa44cd..938e676d 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -170,10 +170,17 @@ function handle_request(conn, request, finish_cb) }; conn._http_open_response = response; + local err; if not request.headers.host then + err = "No 'Host' header"; + elseif not request.path then + err = "Invalid path"; + end + + if err then response.status_code = 400; response.headers.content_type = "text/html"; - response:send(events.fire_event("http-error", { code = 400, message = "No 'Host' header" })); + response:send(events.fire_event("http-error", { code = 400, message = err })); else local host = request.headers.host; if host then -- cgit v1.2.3 From ec56b1a798e4a91ce5beaeeeee5d45847cd38a31 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Apr 2012 16:25:11 +0100 Subject: Revert 98bfebb38705, moved to net.http.parser in 4fc99f1b7570 --- net/http/server.lua | 4 ---- 1 file changed, 4 deletions(-) (limited to 'net/http/server.lua') diff --git a/net/http/server.lua b/net/http/server.lua index 4f9e1463..d6c3a03a 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -1,7 +1,6 @@ local t_insert, t_remove, t_concat = table.insert, table.remove, table.concat; local parser_new = require "net.http.parser".new; -local url_parse = require "socket.url".parse; local events = require "util.events".new(); local addserver = require "net.server".addserver; local log = require "util.logger".init("http.server"); @@ -102,9 +101,6 @@ function listener.onconnect(conn) local function success_cb(request) --log("debug", "success_cb: %s", request.path); request.secure = secure; - local parsed_dest = url_parse(request.path); - request.url = parsed_dest; - request.path = parsed_dest.path; t_insert(pending, request); if not waiting then process_next(); -- cgit v1.2.3 From 53a4656587ab75f31bcf2437f3b36b3c41c01e5c Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 27 Apr 2012 20:00:06 +0100 Subject: net.http.server: Correctly cache results of handler indexing, and also cache failures --- net/http/server.lua | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'net/http/server.lua') diff --git a/net/http/server.lua b/net/http/server.lua index d693fb52..00d98fcb 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -46,17 +46,20 @@ setmetatable(events._handlers, { end end end - if #handlers_array == 0 then return; end - table.sort(handlers_array, function(b, a) - local a_score, b_score = matching_handlers_set[a], matching_handlers_set[b]; - for i = 1, #a_score do - if a_score[i] ~= b_score[i] then -- If equal, compare next score value - return a_score[i] < b_score[i]; + if #handlers_array > 0 then + table.sort(handlers_array, function(b, a) + local a_score, b_score = matching_handlers_set[a], matching_handlers_set[b]; + for i = 1, #a_score do + if a_score[i] ~= b_score[i] then -- If equal, compare next score value + return a_score[i] < b_score[i]; + end end - end - return false; - end); - handlers[curr_event] = handlers_array; + return false; + end); + else + handlers_array = false; + end + rawset(handlers, curr_event, handlers_array); return handlers_array; end; __newindex = function (handlers, curr_event, handlers_array) @@ -69,6 +72,7 @@ setmetatable(events._handlers, { end end end + rawset(handlers, curr_event, handlers_array); end; }); -- cgit v1.2.3 From 6d82c54cfd5f73de76082d80ebbc1614811e9911 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 27 Apr 2012 20:01:21 +0100 Subject: net.http.server: Remove unused variable --- net/http/server.lua | 1 - 1 file changed, 1 deletion(-) (limited to 'net/http/server.lua') diff --git a/net/http/server.lua b/net/http/server.lua index 00d98fcb..86d729f0 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -18,7 +18,6 @@ local legacy_httpserver = require "net.httpserver"; local _M = {}; local sessions = {}; -local handlers = {}; local listener = {}; -- cgit v1.2.3 From 987c79e4d0cbf7c35a6201603813a3a0ff491db2 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 27 Apr 2012 22:37:56 +0100 Subject: net.http.server: Improve comment --- net/http/server.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/http/server.lua') diff --git a/net/http/server.lua b/net/http/server.lua index 86d729f0..978a5a82 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -64,7 +64,7 @@ setmetatable(events._handlers, { __newindex = function (handlers, curr_event, handlers_array) if handlers_array == nil and is_wildcard_event(curr_event) then - -- Invalidate all matching + -- Invalidate the indexes of all matching events for event in pairs(handlers) do if is_wildcard_match(curr_event, event) then handlers[event] = nil; -- cgit v1.2.3 From fb9c619707409485b4b1afbd5585785759c6922d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 27 Apr 2012 23:11:23 +0100 Subject: net.http.server, mod_http: Support http_default_host config option to specify where to direct requests for unknown HTTP vhosts --- net/http/server.lua | 97 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 56 insertions(+), 41 deletions(-) (limited to 'net/http/server.lua') diff --git a/net/http/server.lua b/net/http/server.lua index 978a5a82..325eb42f 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -18,8 +18,9 @@ local legacy_httpserver = require "net.httpserver"; local _M = {}; local sessions = {}; - local listener = {}; +local hosts = {}; +local default_host; local function is_wildcard_event(event) return event:sub(-2, -1) == "/*"; @@ -169,54 +170,59 @@ function handle_request(conn, request, finish_cb) }; conn._http_open_response = response; - local err; - if not request.headers.host then - err = "No 'Host' header"; + local host = (request.headers.host or ""):match("[^:]+"); + + -- Some sanity checking + local err_code, err; + if not host then + err_code, err = 400, "Missing or invalid 'Host' header"; elseif not request.path then - err = "Invalid path"; + err_code, err = 400, "Invalid path"; + end + if not hosts[host] then + if hosts[default_host] then + host = default_host; + else + err_code, err = 404, "Unknown host: "..host; + end end if err then - response.status_code = 400; - response.headers.content_type = "text/html"; - response:send(events.fire_event("http-error", { code = 400, message = err })); - else - local host = request.headers.host; - if host then - host = host:match("[^:]*"):lower(); - local event = request.method.." "..host..request.path:match("[^?]*"); - local payload = { request = request, response = response }; - --log("debug", "Firing event: %s", event); - local result = events.fire_event(event, payload); - if result ~= nil then - if result ~= true then - local code, body = 200, ""; - local result_type = type(result); - if result_type == "number" then - response.status_code = result; - if result >= 400 then - body = events.fire_event("http-error", { code = result }); - end - elseif result_type == "string" then - body = result; - elseif result_type == "table" then - body = result.body; - result.body = nil; - for k, v in pairs(result) do - response[k] = v; - end - end - response:send(body); + response.status_code = err_code; + response:send(events.fire_event("http-error", { code = err_code, message = err })); + return; + end + + local event = request.method.." "..host..request.path:match("[^?]*"); + local payload = { request = request, response = response }; + --log("debug", "Firing event: %s", event); + local result = events.fire_event(event, payload); + if result ~= nil then + if result ~= true then + local code, body = 200, ""; + local result_type = type(result); + if result_type == "number" then + response.status_code = result; + if result >= 400 then + body = events.fire_event("http-error", { code = result }); + end + elseif result_type == "string" then + body = result; + elseif result_type == "table" then + body = result.body; + result.body = nil; + for k, v in pairs(result) do + response[k] = v; end - return; end + response:send(body); end - - -- if handler not called, return 404 - response.status_code = 404; - response.headers.content_type = "text/html"; - response:send(events.fire_event("http-error", { code = 404 })); + return; end + + -- if handler not called, return 404 + response.status_code = 404; + response:send(events.fire_event("http-error", { code = 404 })); end function _M.send_response(response, body) if response.finished then return; end @@ -256,6 +262,15 @@ end function _M.listen_on(port, interface, ssl) addserver(interface or "*", port, listener, "*a", ssl); end +function _M.add_host(host) + hosts[host] = true; +end +function _M.remove_host(host) + hosts[host] = nil; +end +function _M.set_default_host(host) + default_host = host; +end _M.listener = listener; _M.codes = codes; -- cgit v1.2.3 From a5affc4e5d4639700b8a4ea34b2bf8d350e5ab74 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 00:51:36 +0100 Subject: net.http.server: Fix traceback on missing host header (thanks darkrain) --- net/http/server.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'net/http/server.lua') diff --git a/net/http/server.lua b/net/http/server.lua index 325eb42f..eb30da78 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -178,8 +178,7 @@ function handle_request(conn, request, finish_cb) err_code, err = 400, "Missing or invalid 'Host' header"; elseif not request.path then err_code, err = 400, "Invalid path"; - end - if not hosts[host] then + elseif not hosts[host] then if hosts[default_host] then host = default_host; else -- cgit v1.2.3 From bdbf9df051b000de88e6e2d962d23606d44cfb65 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 01:13:49 +0100 Subject: net.http.server: Try default_host if client sent no host anywhere, otherwise... fail. It's 2012. --- net/http/server.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'net/http/server.lua') diff --git a/net/http/server.lua b/net/http/server.lua index eb30da78..8b91b4d3 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -174,15 +174,15 @@ function handle_request(conn, request, finish_cb) -- Some sanity checking local err_code, err; - if not host then - err_code, err = 400, "Missing or invalid 'Host' header"; - elseif not request.path then + if not request.path then err_code, err = 400, "Invalid path"; elseif not hosts[host] then if hosts[default_host] then host = default_host; - else + elseif host then err_code, err = 404, "Unknown host: "..host; + else + err_code, err = 400, "Missing or invalid 'Host' header"; end end -- cgit v1.2.3 From 423b3a5296a8b163ff05251892645a1b8ae27f5d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 14:27:48 +0100 Subject: net.http.server: Remove unused imports and variables (fixes traceback due to removed net.httpserver) --- net/http/server.lua | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'net/http/server.lua') diff --git a/net/http/server.lua b/net/http/server.lua index 8b91b4d3..295592d3 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -12,8 +12,6 @@ local xpcall = xpcall; local debug = debug; local tostring = tostring; local codes = require "net.http.codes"; -local _G = _G; -local legacy_httpserver = require "net.httpserver"; local _M = {}; @@ -90,7 +88,7 @@ function listener.onconnect(conn) local secure = conn:ssl() and true or nil; local pending = {}; local waiting = false; - local function process_next(last_response) + local function process_next() --if waiting then log("debug", "can't process_next, waiting"); return; end if sessions[conn] and #pending > 0 then local request = t_remove(pending); @@ -198,7 +196,7 @@ function handle_request(conn, request, finish_cb) local result = events.fire_event(event, payload); if result ~= nil then if result ~= true then - local code, body = 200, ""; + local body = ""; local result_type = type(result); if result_type == "number" then response.status_code = result; -- cgit v1.2.3 From cd8b026b999a7b7abc77f557302ac751982f6d6d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 19:36:09 +0100 Subject: net.http.server: Use response.body if it exists and body is not specified to send_response --- net/http/server.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/http/server.lua') diff --git a/net/http/server.lua b/net/http/server.lua index 295592d3..e15e6e14 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -228,7 +228,7 @@ function _M.send_response(response, body) local status_line = "HTTP/"..response.request.httpversion.." "..(response.status or codes[response.status_code]); local headers = response.headers; - body = body or ""; + body = body or response.body or ""; headers.content_length = #body; local output = { status_line }; -- cgit v1.2.3 From 2f5ffa76dcb1a7a5968b8cb4d5a20041b7d81d56 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Apr 2012 19:36:57 +0100 Subject: net.http.server: Code cleanup/adjustment now that send_response() accepts response.body --- net/http/server.lua | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'net/http/server.lua') diff --git a/net/http/server.lua b/net/http/server.lua index e15e6e14..69908e4e 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -196,7 +196,7 @@ function handle_request(conn, request, finish_cb) local result = events.fire_event(event, payload); if result ~= nil then if result ~= true then - local body = ""; + local body; local result_type = type(result); if result_type == "number" then response.status_code = result; @@ -206,8 +206,6 @@ function handle_request(conn, request, finish_cb) elseif result_type == "string" then body = result; elseif result_type == "table" then - body = result.body; - result.body = nil; for k, v in pairs(result) do response[k] = v; end -- cgit v1.2.3