Changeset

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 7495:caee8a32983a
children 7576:d3646443a02e
files net/http/parser.lua
diffstat 1 files changed, 22 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/net/http/parser.lua	Mon Sep 21 23:19:48 2015 +0200
+++ b/net/http/parser.lua	Sat Aug 13 20:19:08 2016 +0200
@@ -1,5 +1,6 @@
 local tonumber = tonumber;
 local assert = assert;
+local t_insert, t_concat = table.insert, table.concat;
 local url_parse = require "socket.url".parse;
 local urldecode = require "util.http".urldecode;
 
@@ -27,7 +28,7 @@
 function httpstream.new(success_cb, error_cb, parser_type, options_cb)
 	local client = true;
 	if not parser_type or parser_type == "server" then client = false; else assert(parser_type == "client", "Invalid parser type"); end
-	local buf = "";
+	local buf, buflen, buftable = {}, 0, true;
 	local chunked, chunk_size, chunk_start;
 	local state = nil;
 	local packet;
@@ -38,6 +39,7 @@
 		feed = function(self, data)
 			if error then return nil, "parse has failed"; end
 			if not data then -- EOF
+				if buftable then buf, buftable = t_concat(buf), false; end
 				if state and client and not len then -- reading client body until EOF
 					packet.body = buf;
 					success_cb(packet);
@@ -46,9 +48,16 @@
 				end
 				return;
 			end
-			buf = buf..data;
-			while #buf > 0 do
+			if buftable then
+				t_insert(buf, data);
+			else
+				buf = { buf, data };
+				buftable = true;
+			end
+			buflen = buflen + #data;
+			while buflen > 0 do
 				if state == nil then -- read request
+					if buftable then buf, buftable = t_concat(buf), false; end
 					local index = buf:find("\r\n\r\n", nil, true);
 					if not index then return; end -- not enough data
 					local method, path, httpversion, status_code, reason_phrase;
@@ -115,11 +124,13 @@
 						};
 					end
 					buf = buf:sub(index + 4);
+					buflen = #buf;
 					state = true;
 				end
 				if state then -- read body
 					if client then
 						if chunked then
+							if buftable then buf, buftable = t_concat(buf), false; end
 							if not buf:find("\r\n", nil, true) then
 								return;
 							end -- not enough data
@@ -132,25 +143,29 @@
 								state, chunk_size = nil, nil;
 								buf = buf:gsub("^.-\r\n\r\n", ""); -- This ensure extensions and trailers are stripped
 								success_cb(packet);
-							elseif #buf - chunk_start - 2 >= chunk_size then -- we have a chunk
+							elseif buflen - chunk_start - 2 >= chunk_size then -- we have a chunk
 								packet.body = packet.body..buf:sub(chunk_start, chunk_start + (chunk_size-1));
 								buf = buf:sub(chunk_start + chunk_size + 2);
 								chunk_size, chunk_start = nil, nil;
 							else -- Partial chunk remaining
 								break;
 							end
-						elseif len and #buf >= len then
+						elseif len and buflen >= len then
+							if buftable then buf, buftable = t_concat(buf), false; end
 							if packet.code == 101 then
-								packet.body, buf = buf, "";
+								packet.body, buf, buflen, buftable = buf, {}, 0, true;
 							else
 								packet.body, buf = buf:sub(1, len), buf:sub(len + 1);
+								buflen = #buf;
 							end
 							state = nil; success_cb(packet);
 						else
 							break;
 						end
-					elseif #buf >= len then
+					elseif buflen >= len then
+						if buftable then buf, buftable = t_concat(buf), false; end
 						packet.body, buf = buf:sub(1, len), buf:sub(len + 1);
+						buflen = #buf;
 						state = nil; success_cb(packet);
 					else
 						break;