net.http.server: Add response method for reading response body from a file handle
[prosody.git] / net / http / server.lua
index ffcceabe68ee7e3b575ad1379f93c0ca4e99a107..bc39767fe49562e1d95c9c873902a27efb67f03a 100644 (file)
@@ -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 = require "socket".BLOCKSIZE or 2048;
 
 local _M = {};
 
 local sessions = {};
+local incomplete = {};
 local listener = {};
 local hosts = {};
 local default_host;
@@ -27,7 +30,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)
+       rawset(_handlers, key, nil);
+end);
 
 local event_map = events._event_map;
 setmetatable(events._handlers, {
@@ -62,10 +68,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;
@@ -139,17 +142,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)..": ";
@@ -189,6 +201,7 @@ 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;
        };
@@ -271,6 +284,36 @@ function _M.send_response(response, 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;
@@ -293,7 +336,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;