Comparison

net/http/parser.lua @ 11021:9673c95895fb

net.http.parser: Allow specifying sink for large request bodies This enables uses such as saving uploaded files directly to a file on disk or streaming parsing of payloads. See #726
author Kim Alvefur <zash@zash.se>
date Sat, 01 Aug 2020 18:41:23 +0200
parent 11020:7076ed654ac9
child 11029:5550fc5e83f3
comparison
equal deleted inserted replaced
11020:7076ed654ac9 11021:9673c95895fb
86 end 86 end
87 end 87 end
88 if not first_line then error = true; return error_cb("invalid-status-line"); end 88 if not first_line then error = true; return error_cb("invalid-status-line"); end
89 chunked = have_body and headers["transfer-encoding"] == "chunked"; 89 chunked = have_body and headers["transfer-encoding"] == "chunked";
90 len = tonumber(headers["content-length"]); -- TODO check for invalid len 90 len = tonumber(headers["content-length"]); -- TODO check for invalid len
91 if len and len > bodylimit then error = true; return error_cb("content-length-limit-exceeded"); end
92 -- TODO ask a callback whether to proceed in case of large requests or Expect: 100-continue
93 if client then 91 if client then
94 -- FIXME handle '100 Continue' response (by skipping it) 92 -- FIXME handle '100 Continue' response (by skipping it)
95 if not have_body then len = 0; end 93 if not have_body then len = 0; end
96 packet = { 94 packet = {
97 code = status_code; 95 code = status_code;
124 headers = headers; 122 headers = headers;
125 body = false; 123 body = false;
126 body_sink = nil; 124 body_sink = nil;
127 }; 125 };
128 end 126 end
129 if chunked then 127 if len and len > bodylimit then
128 -- Early notification, for redirection
129 success_cb(packet);
130 if not packet.body_sink then error = true; return error_cb("content-length-limit-exceeded"); end
131 end
132 if chunked and not packet.body_sink then
133 success_cb(packet);
134 if not packet.body_sink then
130 packet.body_buffer = dbuffer.new(buflimit); 135 packet.body_buffer = dbuffer.new(buflimit);
136 end
131 end 137 end
132 state = true; 138 state = true;
133 end 139 end
134 if state then -- read body 140 if state then -- read body
135 if chunked then 141 if chunked then
152 buffer:write(buf); 158 buffer:write(buf);
153 state, chunked = nil, nil; 159 state, chunked = nil, nil;
154 success_cb(packet); 160 success_cb(packet);
155 elseif buffer:length() - chunk_start - 2 >= chunk_size then -- we have a chunk 161 elseif buffer:length() - chunk_start - 2 >= chunk_size then -- we have a chunk
156 buffer:discard(chunk_start - 1); -- TODO verify that it's not off-by-one 162 buffer:discard(chunk_start - 1); -- TODO verify that it's not off-by-one
157 packet.body_buffer:write(buffer:read(chunk_size)); 163 (packet.body_sink or packet.body_buffer):write(buffer:read(chunk_size));
158 buffer:discard(2); -- CRLF 164 buffer:discard(2); -- CRLF
159 else -- Partial chunk remaining 165 else -- Partial chunk remaining
160 break; 166 break;
161 end 167 end
168 elseif packet.body_sink then
169 local chunk = buffer:read_chunk(len);
170 while chunk and len > 0 do
171 if packet.body_sink:write(chunk) then
172 len = len - #chunk;
173 chunk = buffer:read_chunk(len);
174 else
175 error = true;
176 return error_cb("body-sink-write-failure");
177 end
178 end
179 if len == 0 then state = nil; success_cb(packet); end
162 elseif buffer:length() >= len then 180 elseif buffer:length() >= len then
163 assert(not chunked) 181 assert(not chunked)
164 packet.body = buffer:read(len) or ""; 182 packet.body = buffer:read(len) or "";
165 state = nil; success_cb(packet); 183 state = nil; success_cb(packet);
166 else 184 else