util.stanza: Iterate on childtags instead of all childs.
[prosody.git] / util / httpstream.lua
index bef3350c62b7ffe5b27752e8a0e7bc70575ba88b..4b5060a1a6835e1303a463af5d2a9d0d8474c636 100644 (file)
@@ -7,7 +7,8 @@ coroutine.resume(deadroutine);
 
 module("httpstream")
 
-local function parser(data, success_cb, parser_type)
+local function parser(success_cb, parser_type, options_cb)
+       local data = coroutine.yield();
        local function readline()
                local pos = data:find("\r\n", nil, true);
                while not pos do
@@ -66,20 +67,39 @@ local function parser(data, success_cb, parser_type)
                        -- read status line
                        local status_line = readline();
                        local httpversion, status_code, reason_phrase = status_line:match("^HTTP/(%S+)%s+(%d%d%d)%s+(.*)$");
-                       if not httpversion then coroutine.yield("invalid-status-line"); end
+                       status_code = tonumber(status_code);
+                       if not status_code then coroutine.yield("invalid-status-line"); end
                        local headers = readheaders();
                        
                        -- read body
+                       local have_body = not
+                                ( (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) );
+                       
                        local body;
-                       local len = tonumber(headers["content-length"]);
-                       if len then -- TODO check for invalid len
-                               body = readlength(len);
-                       else -- read to end
-                               repeat
-                                       local newdata = coroutine.yield();
-                                       data = data..newdata;
-                               until newdata == "";
-                               body, data = data, "";
+                       if have_body then
+                               local len = tonumber(headers["content-length"]);
+                               if headers["transfer-encoding"] == "chunked" then
+                                       body = "";
+                                       while true do
+                                               local chunk_size = readline():match("^%x+");
+                                               if not chunk_size then coroutine.yield("invalid-chunk-size"); end
+                                               chunk_size = tonumber(chunk_size, 16)
+                                               if chunk_size == 0 then break; end
+                                               body = body..readlength(chunk_size);
+                                               if readline() ~= "" then coroutine.yield("invalid-chunk-ending"); end
+                                       end
+                                       local trailers = readheaders();
+                               elseif len then -- TODO check for invalid len
+                                       body = readlength(len);
+                               else -- read to end
+                                       repeat
+                                               local newdata = coroutine.yield();
+                                               data = data..newdata;
+                                       until newdata == "";
+                                       body, data = data, "";
+                               end
                        end
                        
                        success_cb({
@@ -92,16 +112,17 @@ local function parser(data, success_cb, parser_type)
        else coroutine.yield("unknown-parser-type"); end
 end
 
-function new(success_cb, error_cb, parser_type)
+function new(success_cb, error_cb, parser_type, options_cb)
        local co = coroutine.create(parser);
+       coroutine.resume(co, success_cb, parser_type, options_cb)
        return {
                feed = function(self, data)
                        if not data then
-                               if parser_type == "client" then coroutine.resume(co, "", success_cb, parser_type); end
+                               if parser_type == "client" then coroutine.resume(co, ""); end
                                co = deadroutine;
                                return error_cb();
                        end
-                       local success, result = coroutine.resume(co, data, success_cb, parser_type);
+                       local success, result = coroutine.resume(co, data);
                        if result then
                                co = deadroutine;
                                return error_cb(result);