diff options
-rw-r--r-- | .luacheckrc | 20 | ||||
-rw-r--r-- | net/dns.lua | 4 | ||||
-rw-r--r-- | net/http/server.lua | 42 | ||||
-rw-r--r-- | plugins/mod_http_errors.lua | 3 | ||||
-rw-r--r-- | plugins/mod_http_files.lua | 26 | ||||
-rwxr-xr-x | prosodyctl | 2 | ||||
-rw-r--r-- | tests/test.lua | 1 | ||||
-rw-r--r-- | tests/test_net_http_parser.lua | 47 | ||||
-rw-r--r-- | tests/test_util_cache.lua | 6 | ||||
-rw-r--r-- | util/ip.lua | 8 | ||||
-rw-r--r-- | util/openssl.lua | 2 |
11 files changed, 130 insertions, 31 deletions
diff --git a/.luacheckrc b/.luacheckrc index 2fc05e75..90d2e273 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -8,16 +8,18 @@ codes = true ignore = { "411/err", "421/err", "411/ok", "421/ok", "211/_ENV" } files["plugins/"] = { - ignore = { "122/module" }; + globals = { "module" }; } files["tests/"] = { - ignore = { - "113/assert_equal", - "113/assert_table", - "113/assert_function", - "113/assert_string", - "113/assert_boolean", - "113/assert_is", - "113/assert_is_not", + read_globals = { + "testlib_new_env", + "assert_equal", + "assert_table", + "assert_function", + "assert_string", + "assert_boolean", + "assert_is", + "assert_is_not", + "runtest", }; } diff --git a/net/dns.lua b/net/dns.lua index 4a35fc1b..726b2b80 100644 --- a/net/dns.lua +++ b/net/dns.lua @@ -1011,7 +1011,7 @@ end function resolver.print(response) -- - - - - - - - - - - - - resolver.print - for s,s in pairs { 'id', 'qr', 'opcode', 'aa', 'tc', 'rd', 'ra', 'z', + for _, s in pairs { 'id', 'qr', 'opcode', 'aa', 'tc', 'rd', 'ra', 'z', 'rcode', 'qdcount', 'ancount', 'nscount', 'arcount' } do print( string.format('%-30s', 'header.'..s), response.header[s], hint(response.header, s) ); end @@ -1024,7 +1024,7 @@ function resolver.print(response) -- - - - - - - - - - - - - resolver.print local common = { name=1, type=1, class=1, ttl=1, rdlength=1, rdata=1 }; local tmp; - for s,s in pairs({'answer', 'authority', 'additional'}) do + for _, s in pairs({'answer', 'authority', 'additional'}) do for i,rr in pairs(response[s]) do for j,t in pairs({ 'name', 'type', 'class', 'ttl', 'rdlength' }) do tmp = string.format('%s[%i].%s', s, i, t); diff --git a/net/http/server.lua b/net/http/server.lua index aeaa7416..bc39767f 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -13,10 +13,12 @@ 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; @@ -140,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)..": "; @@ -190,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; }; @@ -272,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; diff --git a/plugins/mod_http_errors.lua b/plugins/mod_http_errors.lua index 0c37e104..17d357e5 100644 --- a/plugins/mod_http_errors.lua +++ b/plugins/mod_http_errors.lua @@ -43,7 +43,8 @@ local html = [[ <p>$message</p> <p>$extra</p> </body> -</html>]]; +</html> +]]; html = html:gsub("%s%s+", ""); local entities = { diff --git a/plugins/mod_http_files.lua b/plugins/mod_http_files.lua index 3b602495..ab2f3966 100644 --- a/plugins/mod_http_files.lua +++ b/plugins/mod_http_files.lua @@ -17,6 +17,8 @@ local build_path = require"socket.url".build_path; local path_sep = package.config:sub(1,1); local base_path = module:get_option_string("http_files_dir", module:get_option_string("http_path")); +local cache_size = module:get_option_number("http_files_cache_size", 128); +local cache_max_file_size = module:get_option_number("http_files_cache_max_file_size", 4096); local dir_indices = module:get_option("http_index_files", { "index.html", "index.htm" }); local directory_index = module:get_option_boolean("http_dir_listing"); @@ -81,7 +83,7 @@ function sanitize_path(path) return "/"..table.concat(out, "/"); end -local cache = setmetatable({}, { __mode = "kv" }); -- Let the garbage collector have it if it wants to. +local cache = require "util.cache".new(cache_size); function serve(opts) if type(opts) ~= "table" then -- assume path string @@ -109,7 +111,7 @@ function serve(opts) local last_modified = os_date('!%a, %d %b %Y %H:%M:%S GMT', attr.modification); response_headers.last_modified = last_modified; - local etag = ("%02x-%x-%x-%x"):format(attr.dev or 0, attr.ino or 0, attr.size or 0, attr.modification or 0); + local etag = ('"%02x-%x-%x-%x"'):format(attr.dev or 0, attr.ino or 0, attr.size or 0, attr.modification or 0); response_headers.etag = etag; local if_none_match = request_headers.if_none_match @@ -119,7 +121,7 @@ function serve(opts) return 304; end - local data = cache[orig_path]; + local data = cache:get(orig_path); if data and data.etag == etag then response_headers.content_type = data.content_type; data = data.data; @@ -147,18 +149,22 @@ function serve(opts) else local f, err = open(full_path, "rb"); - if f then - data, err = f:read("*a"); - f:close(); - end - if not data then - module:log("debug", "Could not open or read %s. Error was %s", full_path, err); + if not f then + module:log("debug", "Could not open %s. Error was %s", full_path, err); return 403; end local ext = full_path:match("%.([^./]+)$"); local content_type = ext and mime_map[ext]; - cache[orig_path] = { data = data; content_type = content_type; etag = etag }; response_headers.content_type = content_type; + if attr.size > cache_max_file_size then + response_headers.content_length = attr.size; + module:log("debug", "%d > cache_max_file_size", attr.size); + return response:send_file(f); + else + data = f:read("*a"); + f:close(); + end + cache:set(orig_path, { data = data; content_type = content_type; etag = etag }); end return response:send(data); @@ -717,7 +717,7 @@ function cert_commands.config(arg) else show_message("Please provide details to include in the certificate config file."); show_message("Leave the field empty to use the default value or '.' to exclude the field.") - for i, k in ipairs(openssl._DN_order) do + for _, k in ipairs(openssl._DN_order) do local v = conf.distinguished_name[k]; if v then local nv; diff --git a/tests/test.lua b/tests/test.lua index bb15250d..d6aa3b31 100644 --- a/tests/test.lua +++ b/tests/test.lua @@ -28,6 +28,7 @@ function run_all_tests() dotest "util.random" dotest "util.xml" dotest "util.xmppstream" + dotest "net.http.parser" dosingletest("test_sasl.lua", "latin1toutf8"); dosingletest("test_utf8.lua", "valid"); diff --git a/tests/test_net_http_parser.lua b/tests/test_net_http_parser.lua new file mode 100644 index 00000000..1157b5ac --- /dev/null +++ b/tests/test_net_http_parser.lua @@ -0,0 +1,47 @@ +local httpstreams = { [[ +GET / HTTP/1.1 +Host: example.com + +]], [[ +HTTP/1.1 200 OK +Content-Length: 0 + +]], [[ +HTTP/1.1 200 OK +Content-Length: 7 + +Hello +HTTP/1.1 200 OK +Transfer-Encoding: chunked + +1 +H +1 +e +2 +ll +1 +o +0 + + +]] +} + +function new(new) + + for _, stream in ipairs(httpstreams) do + local success; + local function success_cb(packet) + success = true; + end + stream = stream:gsub("\n", "\r\n"); + local parser = new(success_cb, error, stream:sub(1,4) == "HTTP" and "client" or "server") + for chunk in stream:gmatch("..?.?") do + parser:feed(chunk); + end + + assert_is(success); + end + +end diff --git a/tests/test_util_cache.lua b/tests/test_util_cache.lua index 78666edc..cbc60bc7 100644 --- a/tests/test_util_cache.lua +++ b/tests/test_util_cache.lua @@ -196,12 +196,12 @@ function new(new) assert_equal(i, 4); local evicted_key, evicted_value; - local c = new(3, function (_key, _value) + local c2 = new(3, function (_key, _value) evicted_key, evicted_value = _key, _value; end); local function set(k, v, should_evict_key, should_evict_value) evicted_key, evicted_value = nil, nil; - c:set(k, v); + c2:set(k, v); assert_equal(evicted_key, should_evict_key); assert_equal(evicted_value, should_evict_value); end @@ -219,7 +219,7 @@ function new(new) local evicted_key, evicted_value; - local c3 = new(1, function (_key, _value, c3) + local c3 = new(1, function (_key, _value) evicted_key, evicted_value = _key, _value; if _key == "a" then -- Sanity check for what we're evicting diff --git a/util/ip.lua b/util/ip.lua index ec3b4d7e..81a98ef7 100644 --- a/util/ip.lua +++ b/util/ip.lua @@ -51,15 +51,15 @@ local function toBits(ip) if not ip:match(":$") then fields[#fields] = nil; end for i, field in ipairs(fields) do if field:len() == 0 and i ~= 1 and i ~= #fields then - for i = 1, 16 * (9 - #fields) do + for _ = 1, 16 * (9 - #fields) do result = result .. "0"; end else - for i = 1, 4 - field:len() do + for _ = 1, 4 - field:len() do result = result .. "0000"; end - for i = 1, field:len() do - result = result .. hex2bits[field:sub(i,i)]; + for j = 1, field:len() do + result = result .. hex2bits[field:sub(j, j)]; end end end diff --git a/util/openssl.lua b/util/openssl.lua index 33234a7d..abd90a84 100644 --- a/util/openssl.lua +++ b/util/openssl.lua @@ -70,7 +70,7 @@ function ssl_config:serialize() end end elseif k == "distinguished_name" then - for i, k in ipairs(t[1] and t or DN_order) do + for _, k in ipairs(t[1] and t or DN_order) do local v = t[k]; if v then s = s .. ("%s = %s\n"):format(k, v); |