Software /
code /
prosody
Comparison
net/http/parser.lua @ 5463:111953bfe767
net.http.parser: Fix chunked encoding response parsing, and make it more robust
author | Matthew Wild <mwild1@gmail.com> |
---|---|
date | Thu, 11 Apr 2013 20:01:03 +0100 |
parent | 5462:3ecae471d9dd |
child | 5475:c2c9f07c5d6a |
comparison
equal
deleted
inserted
replaced
5462:3ecae471d9dd | 5463:111953bfe767 |
---|---|
1 | |
2 local tonumber = tonumber; | 1 local tonumber = tonumber; |
3 local assert = assert; | 2 local assert = assert; |
4 local url_parse = require "socket.url".parse; | 3 local url_parse = require "socket.url".parse; |
5 local urldecode = require "util.http".urldecode; | 4 local urldecode = require "util.http".urldecode; |
6 | 5 |
27 | 26 |
28 function httpstream.new(success_cb, error_cb, parser_type, options_cb) | 27 function httpstream.new(success_cb, error_cb, parser_type, options_cb) |
29 local client = true; | 28 local client = true; |
30 if not parser_type or parser_type == "server" then client = false; else assert(parser_type == "client", "Invalid parser type"); end | 29 if not parser_type or parser_type == "server" then client = false; else assert(parser_type == "client", "Invalid parser type"); end |
31 local buf = ""; | 30 local buf = ""; |
32 local chunked; | 31 local chunked, chunk_size, chunk_start; |
33 local state = nil; | 32 local state = nil; |
34 local packet; | 33 local packet; |
35 local len; | 34 local len; |
36 local have_body; | 35 local have_body; |
37 local error; | 36 local error; |
69 if not status_code then error = true; return error_cb("invalid-status-line"); end | 68 if not status_code then error = true; return error_cb("invalid-status-line"); end |
70 have_body = not | 69 have_body = not |
71 ( (options_cb and options_cb().method == "HEAD") | 70 ( (options_cb and options_cb().method == "HEAD") |
72 or (status_code == 204 or status_code == 304 or status_code == 301) | 71 or (status_code == 204 or status_code == 304 or status_code == 301) |
73 or (status_code >= 100 and status_code < 200) ); | 72 or (status_code >= 100 and status_code < 200) ); |
74 chunked = have_body and headers["transfer-encoding"] == "chunked"; | |
75 else | 73 else |
76 method, path, httpversion = line:match("^(%w+) (%S+) HTTP/(1%.[01])$"); | 74 method, path, httpversion = line:match("^(%w+) (%S+) HTTP/(1%.[01])$"); |
77 if not method then error = true; return error_cb("invalid-status-line"); end | 75 if not method then error = true; return error_cb("invalid-status-line"); end |
78 end | 76 end |
79 end | 77 end |
80 end | 78 end |
81 if not first_line then error = true; return error_cb("invalid-status-line"); end | 79 if not first_line then error = true; return error_cb("invalid-status-line"); end |
80 chunked = have_body and headers["transfer-encoding"] == "chunked"; | |
82 len = tonumber(headers["content-length"]); -- TODO check for invalid len | 81 len = tonumber(headers["content-length"]); -- TODO check for invalid len |
83 if client then | 82 if client then |
84 -- FIXME handle '100 Continue' response (by skipping it) | 83 -- FIXME handle '100 Continue' response (by skipping it) |
85 if not have_body then len = 0; end | 84 if not have_body then len = 0; end |
86 packet = { | 85 packet = { |
119 state = true; | 118 state = true; |
120 end | 119 end |
121 if state then -- read body | 120 if state then -- read body |
122 if client then | 121 if client then |
123 if chunked then | 122 if chunked then |
124 local index = buf:find("\r\n", nil, true); | 123 if not buf:find("\r\n", nil, true) then |
125 if not index then return; end -- not enough data | 124 return; |
126 local chunk_size = buf:match("^%x+"); | 125 end -- not enough data |
127 if not chunk_size then error = true; return error_cb("invalid-chunk-size"); end | 126 if not chunk_size then |
128 chunk_size = tonumber(chunk_size, 16); | 127 chunk_size, chunk_start = buf:match("^(%x+)[^\r\n]*\r\n()"); |
129 index = index + 2; | 128 chunk_size = chunk_size and tonumber(chunk_size, 16); |
130 if chunk_size == 0 then | 129 if not chunk_size then error = true; return error_cb("invalid-chunk-size"); end |
131 state = nil; success_cb(packet); | |
132 elseif #buf - index + 1 >= chunk_size then -- we have a chunk | |
133 packet.body = packet.body..buf:sub(index, index + chunk_size - 1); | |
134 buf = buf:sub(index + chunk_size); | |
135 end | 130 end |
136 error("trailers"); -- FIXME MUST read trailers | 131 if chunk_size == 0 and buf:find("\r\n\r\n", chunk_start-2, true) then |
132 state, chunk_size = nil, nil; | |
133 buf = buf:gsub("^.-\r\n\r\n", ""); -- This ensure extensions and trailers are stripped | |
134 success_cb(packet); | |
135 elseif #buf - chunk_start + 2 >= chunk_size then -- we have a chunk | |
136 packet.body = packet.body..buf:sub(chunk_start, chunk_start + chunk_size); | |
137 buf = buf:sub(chunk_start + chunk_size + 2); | |
138 chunk_size, chunk_start = nil, nil; | |
139 else -- Partial chunk remaining | |
140 break; | |
141 end | |
137 elseif len and #buf >= len then | 142 elseif len and #buf >= len then |
138 packet.body, buf = buf:sub(1, len), buf:sub(len + 1); | 143 packet.body, buf = buf:sub(1, len), buf:sub(len + 1); |
139 state = nil; success_cb(packet); | 144 state = nil; success_cb(packet); |
140 else | 145 else |
141 break; | 146 break; |