Software /
code /
prosody
Comparison
net/http/parser.lua @ 7581:01d0d466d7be
Merge 0.9->0.10
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Thu, 18 Aug 2016 15:16:02 +0200 |
parent | 7569:a15ce0014ac9 |
parent | 7578:65bf55fdf971 |
child | 7634:b1132d74f54c |
comparison
equal
deleted
inserted
replaced
7572:f549587b8c06 | 7581:01d0d466d7be |
---|---|
1 local tonumber = tonumber; | 1 local tonumber = tonumber; |
2 local assert = assert; | 2 local assert = assert; |
3 local t_insert, t_concat = table.insert, table.concat; | |
3 local url_parse = require "socket.url".parse; | 4 local url_parse = require "socket.url".parse; |
4 local urldecode = require "util.http".urldecode; | 5 local urldecode = require "util.http".urldecode; |
5 | 6 |
6 local function preprocess_path(path) | 7 local function preprocess_path(path) |
7 path = urldecode((path:gsub("//+", "/"))); | 8 path = urldecode((path:gsub("//+", "/"))); |
25 local httpstream = {}; | 26 local httpstream = {}; |
26 | 27 |
27 function httpstream.new(success_cb, error_cb, parser_type, options_cb) | 28 function httpstream.new(success_cb, error_cb, parser_type, options_cb) |
28 local client = true; | 29 local client = true; |
29 if not parser_type or parser_type == "server" then client = false; else assert(parser_type == "client", "Invalid parser type"); end | 30 if not parser_type or parser_type == "server" then client = false; else assert(parser_type == "client", "Invalid parser type"); end |
30 local buf = ""; | 31 local buf, buflen, buftable = {}, 0, true; |
32 local bodylimit = tonumber(options_cb and options_cb().body_size_limit) or 10*1024*1024; | |
33 local buflimit = tonumber(options_cb and options_cb().buffer_size_limit) or bodylimit * 2; | |
31 local chunked, chunk_size, chunk_start; | 34 local chunked, chunk_size, chunk_start; |
32 local state = nil; | 35 local state = nil; |
33 local packet; | 36 local packet; |
34 local len; | 37 local len; |
35 local have_body; | 38 local have_body; |
36 local error; | 39 local error; |
37 return { | 40 return { |
38 feed = function(_, data) | 41 feed = function(_, data) |
39 if error then return nil, "parse has failed"; end | 42 if error then return nil, "parse has failed"; end |
40 if not data then -- EOF | 43 if not data then -- EOF |
44 if buftable then buf, buftable = t_concat(buf), false; end | |
41 if state and client and not len then -- reading client body until EOF | 45 if state and client and not len then -- reading client body until EOF |
42 packet.body = buf; | 46 packet.body = buf; |
43 success_cb(packet); | 47 success_cb(packet); |
44 elseif buf ~= "" then -- unexpected EOF | 48 elseif buf ~= "" then -- unexpected EOF |
45 error = true; return error_cb(); | 49 error = true; return error_cb(); |
46 end | 50 end |
47 return; | 51 return; |
48 end | 52 end |
49 buf = buf..data; | 53 if buftable then |
50 while #buf > 0 do | 54 t_insert(buf, data); |
55 else | |
56 buf = { buf, data }; | |
57 buftable = true; | |
58 end | |
59 buflen = buflen + #data; | |
60 if buflen > buflimit then error = true; return error_cb("max-buffer-size-exceeded"); end | |
61 while buflen > 0 do | |
51 if state == nil then -- read request | 62 if state == nil then -- read request |
63 if buftable then buf, buftable = t_concat(buf), false; end | |
52 local index = buf:find("\r\n\r\n", nil, true); | 64 local index = buf:find("\r\n\r\n", nil, true); |
53 if not index then return; end -- not enough data | 65 if not index then return; end -- not enough data |
54 local method, path, httpversion, status_code, reason_phrase; | 66 local method, path, httpversion, status_code, reason_phrase; |
55 local first_line; | 67 local first_line; |
56 local headers = {}; | 68 local headers = {}; |
77 end | 89 end |
78 end | 90 end |
79 if not first_line then error = true; return error_cb("invalid-status-line"); end | 91 if not first_line then error = true; return error_cb("invalid-status-line"); end |
80 chunked = have_body and headers["transfer-encoding"] == "chunked"; | 92 chunked = have_body and headers["transfer-encoding"] == "chunked"; |
81 len = tonumber(headers["content-length"]); -- TODO check for invalid len | 93 len = tonumber(headers["content-length"]); -- TODO check for invalid len |
94 if len and len > bodylimit then error = true; return error_cb("content-length-limit-exceeded"); end | |
82 if client then | 95 if client then |
83 -- FIXME handle '100 Continue' response (by skipping it) | 96 -- FIXME handle '100 Continue' response (by skipping it) |
84 if not have_body then len = 0; end | 97 if not have_body then len = 0; end |
85 packet = { | 98 packet = { |
86 code = status_code; | 99 code = status_code; |
113 headers = headers; | 126 headers = headers; |
114 body = nil; | 127 body = nil; |
115 }; | 128 }; |
116 end | 129 end |
117 buf = buf:sub(index + 4); | 130 buf = buf:sub(index + 4); |
131 buflen = #buf; | |
118 state = true; | 132 state = true; |
119 end | 133 end |
120 if state then -- read body | 134 if state then -- read body |
121 if client then | 135 if client then |
122 if chunked then | 136 if chunked then |
137 if buftable then buf, buftable = t_concat(buf), false; end | |
123 if not buf:find("\r\n", nil, true) then | 138 if not buf:find("\r\n", nil, true) then |
124 return; | 139 return; |
125 end -- not enough data | 140 end -- not enough data |
126 if not chunk_size then | 141 if not chunk_size then |
127 chunk_size, chunk_start = buf:match("^(%x+)[^\r\n]*\r\n()"); | 142 chunk_size, chunk_start = buf:match("^(%x+)[^\r\n]*\r\n()"); |
130 end | 145 end |
131 if chunk_size == 0 and buf:find("\r\n\r\n", chunk_start-2, true) then | 146 if chunk_size == 0 and buf:find("\r\n\r\n", chunk_start-2, true) then |
132 state, chunk_size = nil, nil; | 147 state, chunk_size = nil, nil; |
133 buf = buf:gsub("^.-\r\n\r\n", ""); -- This ensure extensions and trailers are stripped | 148 buf = buf:gsub("^.-\r\n\r\n", ""); -- This ensure extensions and trailers are stripped |
134 success_cb(packet); | 149 success_cb(packet); |
135 elseif #buf - chunk_start - 2 >= chunk_size then -- we have a chunk | 150 elseif buflen - chunk_start - 2 >= chunk_size then -- we have a chunk |
136 packet.body = packet.body..buf:sub(chunk_start, chunk_start + (chunk_size-1)); | 151 packet.body = packet.body..buf:sub(chunk_start, chunk_start + (chunk_size-1)); |
137 buf = buf:sub(chunk_start + chunk_size + 2); | 152 buf = buf:sub(chunk_start + chunk_size + 2); |
138 chunk_size, chunk_start = nil, nil; | 153 chunk_size, chunk_start = nil, nil; |
139 else -- Partial chunk remaining | 154 else -- Partial chunk remaining |
140 break; | 155 break; |
141 end | 156 end |
142 elseif len and #buf >= len then | 157 elseif len and buflen >= len then |
158 if buftable then buf, buftable = t_concat(buf), false; end | |
143 if packet.code == 101 then | 159 if packet.code == 101 then |
144 packet.body, buf = buf, ""; | 160 packet.body, buf, buflen, buftable = buf, {}, 0, true; |
145 else | 161 else |
146 packet.body, buf = buf:sub(1, len), buf:sub(len + 1); | 162 packet.body, buf = buf:sub(1, len), buf:sub(len + 1); |
163 buflen = #buf; | |
147 end | 164 end |
148 state = nil; success_cb(packet); | 165 state = nil; success_cb(packet); |
149 else | 166 else |
150 break; | 167 break; |
151 end | 168 end |
152 elseif #buf >= len then | 169 elseif buflen >= len then |
170 if buftable then buf, buftable = t_concat(buf), false; end | |
153 packet.body, buf = buf:sub(1, len), buf:sub(len + 1); | 171 packet.body, buf = buf:sub(1, len), buf:sub(len + 1); |
172 buflen = #buf; | |
154 state = nil; success_cb(packet); | 173 state = nil; success_cb(packet); |
155 else | 174 else |
156 break; | 175 break; |
157 end | 176 end |
158 end | 177 end |