Software /
code /
prosody
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 |