Comparison

net/http/parser.lua @ 7575:3ae247af68f4

net.http.parser: Buffer into a table to reduce GC pressure, collapse to string when needed (fixes #603)
author Kim Alvefur <zash@zash.se>
date Sat, 13 Aug 2016 20:19:08 +0200
parent 6523:63d3126b75f1
child 7576:d3646443a02e
comparison
equal deleted inserted replaced
7495:caee8a32983a 7575:3ae247af68f4
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;
31 local chunked, chunk_size, chunk_start; 32 local chunked, chunk_size, chunk_start;
32 local state = nil; 33 local state = nil;
33 local packet; 34 local packet;
34 local len; 35 local len;
35 local have_body; 36 local have_body;
36 local error; 37 local error;
37 return { 38 return {
38 feed = function(self, data) 39 feed = function(self, data)
39 if error then return nil, "parse has failed"; end 40 if error then return nil, "parse has failed"; end
40 if not data then -- EOF 41 if not data then -- EOF
42 if buftable then buf, buftable = t_concat(buf), false; end
41 if state and client and not len then -- reading client body until EOF 43 if state and client and not len then -- reading client body until EOF
42 packet.body = buf; 44 packet.body = buf;
43 success_cb(packet); 45 success_cb(packet);
44 elseif buf ~= "" then -- unexpected EOF 46 elseif buf ~= "" then -- unexpected EOF
45 error = true; return error_cb(); 47 error = true; return error_cb();
46 end 48 end
47 return; 49 return;
48 end 50 end
49 buf = buf..data; 51 if buftable then
50 while #buf > 0 do 52 t_insert(buf, data);
53 else
54 buf = { buf, data };
55 buftable = true;
56 end
57 buflen = buflen + #data;
58 while buflen > 0 do
51 if state == nil then -- read request 59 if state == nil then -- read request
60 if buftable then buf, buftable = t_concat(buf), false; end
52 local index = buf:find("\r\n\r\n", nil, true); 61 local index = buf:find("\r\n\r\n", nil, true);
53 if not index then return; end -- not enough data 62 if not index then return; end -- not enough data
54 local method, path, httpversion, status_code, reason_phrase; 63 local method, path, httpversion, status_code, reason_phrase;
55 local first_line; 64 local first_line;
56 local headers = {}; 65 local headers = {};
113 headers = headers; 122 headers = headers;
114 body = nil; 123 body = nil;
115 }; 124 };
116 end 125 end
117 buf = buf:sub(index + 4); 126 buf = buf:sub(index + 4);
127 buflen = #buf;
118 state = true; 128 state = true;
119 end 129 end
120 if state then -- read body 130 if state then -- read body
121 if client then 131 if client then
122 if chunked then 132 if chunked then
133 if buftable then buf, buftable = t_concat(buf), false; end
123 if not buf:find("\r\n", nil, true) then 134 if not buf:find("\r\n", nil, true) then
124 return; 135 return;
125 end -- not enough data 136 end -- not enough data
126 if not chunk_size then 137 if not chunk_size then
127 chunk_size, chunk_start = buf:match("^(%x+)[^\r\n]*\r\n()"); 138 chunk_size, chunk_start = buf:match("^(%x+)[^\r\n]*\r\n()");
130 end 141 end
131 if chunk_size == 0 and buf:find("\r\n\r\n", chunk_start-2, true) then 142 if chunk_size == 0 and buf:find("\r\n\r\n", chunk_start-2, true) then
132 state, chunk_size = nil, nil; 143 state, chunk_size = nil, nil;
133 buf = buf:gsub("^.-\r\n\r\n", ""); -- This ensure extensions and trailers are stripped 144 buf = buf:gsub("^.-\r\n\r\n", ""); -- This ensure extensions and trailers are stripped
134 success_cb(packet); 145 success_cb(packet);
135 elseif #buf - chunk_start - 2 >= chunk_size then -- we have a chunk 146 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)); 147 packet.body = packet.body..buf:sub(chunk_start, chunk_start + (chunk_size-1));
137 buf = buf:sub(chunk_start + chunk_size + 2); 148 buf = buf:sub(chunk_start + chunk_size + 2);
138 chunk_size, chunk_start = nil, nil; 149 chunk_size, chunk_start = nil, nil;
139 else -- Partial chunk remaining 150 else -- Partial chunk remaining
140 break; 151 break;
141 end 152 end
142 elseif len and #buf >= len then 153 elseif len and buflen >= len then
154 if buftable then buf, buftable = t_concat(buf), false; end
143 if packet.code == 101 then 155 if packet.code == 101 then
144 packet.body, buf = buf, ""; 156 packet.body, buf, buflen, buftable = buf, {}, 0, true;
145 else 157 else
146 packet.body, buf = buf:sub(1, len), buf:sub(len + 1); 158 packet.body, buf = buf:sub(1, len), buf:sub(len + 1);
159 buflen = #buf;
147 end 160 end
148 state = nil; success_cb(packet); 161 state = nil; success_cb(packet);
149 else 162 else
150 break; 163 break;
151 end 164 end
152 elseif #buf >= len then 165 elseif buflen >= len then
166 if buftable then buf, buftable = t_concat(buf), false; end
153 packet.body, buf = buf:sub(1, len), buf:sub(len + 1); 167 packet.body, buf = buf:sub(1, len), buf:sub(len + 1);
168 buflen = #buf;
154 state = nil; success_cb(packet); 169 state = nil; success_cb(packet);
155 else 170 else
156 break; 171 break;
157 end 172 end
158 end 173 end