Mercurial > prosody-hg
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 |
| parents | 63d3126b75f1 |
| children | 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 |
