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