aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Wild <mwild1@gmail.com>2013-04-11 20:01:03 +0100
committerMatthew Wild <mwild1@gmail.com>2013-04-11 20:01:03 +0100
commitfec86f72cfdd76162667397ac7c82e950c1e5be7 (patch)
tree420b66684f7bf1047206ee015388a392590abcaa
parent1ebc90c25bfb3e02c4b2f0bbee59c84bcc330299 (diff)
downloadprosody-fec86f72cfdd76162667397ac7c82e950c1e5be7.tar.gz
prosody-fec86f72cfdd76162667397ac7c82e950c1e5be7.zip
net.http.parser: Fix chunked encoding response parsing, and make it more robust
-rw-r--r--net/http/parser.lua35
1 files changed, 20 insertions, 15 deletions
diff --git a/net/http/parser.lua b/net/http/parser.lua
index 684d62fe..34742d2b 100644
--- a/net/http/parser.lua
+++ b/net/http/parser.lua
@@ -1,4 +1,3 @@
-
local tonumber = tonumber;
local assert = assert;
local url_parse = require "socket.url".parse;
@@ -29,7 +28,7 @@ function httpstream.new(success_cb, error_cb, parser_type, options_cb)
local client = true;
if not parser_type or parser_type == "server" then client = false; else assert(parser_type == "client", "Invalid parser type"); end
local buf = "";
- local chunked;
+ local chunked, chunk_size, chunk_start;
local state = nil;
local packet;
local len;
@@ -71,7 +70,6 @@ function httpstream.new(success_cb, error_cb, parser_type, options_cb)
( (options_cb and options_cb().method == "HEAD")
or (status_code == 204 or status_code == 304 or status_code == 301)
or (status_code >= 100 and status_code < 200) );
- chunked = have_body and headers["transfer-encoding"] == "chunked";
else
method, path, httpversion = line:match("^(%w+) (%S+) HTTP/(1%.[01])$");
if not method then error = true; return error_cb("invalid-status-line"); end
@@ -79,6 +77,7 @@ function httpstream.new(success_cb, error_cb, parser_type, options_cb)
end
end
if not first_line then error = true; return error_cb("invalid-status-line"); end
+ chunked = have_body and headers["transfer-encoding"] == "chunked";
len = tonumber(headers["content-length"]); -- TODO check for invalid len
if client then
-- FIXME handle '100 Continue' response (by skipping it)
@@ -121,19 +120,25 @@ function httpstream.new(success_cb, error_cb, parser_type, options_cb)
if state then -- read body
if client then
if chunked then
- local index = buf:find("\r\n", nil, true);
- if not index then return; end -- not enough data
- local chunk_size = buf:match("^%x+");
- if not chunk_size then error = true; return error_cb("invalid-chunk-size"); end
- chunk_size = tonumber(chunk_size, 16);
- index = index + 2;
- if chunk_size == 0 then
- state = nil; success_cb(packet);
- elseif #buf - index + 1 >= chunk_size then -- we have a chunk
- packet.body = packet.body..buf:sub(index, index + chunk_size - 1);
- buf = buf:sub(index + chunk_size);
+ if not buf:find("\r\n", nil, true) then
+ return;
+ end -- not enough data
+ if not chunk_size then
+ chunk_size, chunk_start = buf:match("^(%x+)[^\r\n]*\r\n()");
+ chunk_size = chunk_size and tonumber(chunk_size, 16);
+ if not chunk_size then error = true; return error_cb("invalid-chunk-size"); end
+ end
+ if chunk_size == 0 and buf:find("\r\n\r\n", chunk_start-2, true) then
+ state, chunk_size = nil, nil;
+ buf = buf:gsub("^.-\r\n\r\n", ""); -- This ensure extensions and trailers are stripped
+ success_cb(packet);
+ elseif #buf - chunk_start + 2 >= chunk_size then -- we have a chunk
+ packet.body = packet.body..buf:sub(chunk_start, chunk_start + chunk_size);
+ buf = buf:sub(chunk_start + chunk_size + 2);
+ chunk_size, chunk_start = nil, nil;
+ else -- Partial chunk remaining
+ break;
end
- error("trailers"); -- FIXME MUST read trailers
elseif len and #buf >= len then
packet.body, buf = buf:sub(1, len), buf:sub(len + 1);
state = nil; success_cb(packet);