Software / code / prosody
Comparison
net/http/parser.lua @ 7581:01d0d466d7be
Merge 0.9->0.10
| author | Kim Alvefur <zash@zash.se> |
|---|---|
| date | Thu, 18 Aug 2016 15:16:02 +0200 |
| parent | 7569:a15ce0014ac9 |
| parent | 7578:65bf55fdf971 |
| child | 7634:b1132d74f54c |
comparison
equal
deleted
inserted
replaced
| 7572:f549587b8c06 | 7581:01d0d466d7be |
|---|---|
| 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; |
| 32 local bodylimit = tonumber(options_cb and options_cb().body_size_limit) or 10*1024*1024; | |
| 33 local buflimit = tonumber(options_cb and options_cb().buffer_size_limit) or bodylimit * 2; | |
| 31 local chunked, chunk_size, chunk_start; | 34 local chunked, chunk_size, chunk_start; |
| 32 local state = nil; | 35 local state = nil; |
| 33 local packet; | 36 local packet; |
| 34 local len; | 37 local len; |
| 35 local have_body; | 38 local have_body; |
| 36 local error; | 39 local error; |
| 37 return { | 40 return { |
| 38 feed = function(_, data) | 41 feed = function(_, data) |
| 39 if error then return nil, "parse has failed"; end | 42 if error then return nil, "parse has failed"; end |
| 40 if not data then -- EOF | 43 if not data then -- EOF |
| 44 if buftable then buf, buftable = t_concat(buf), false; end | |
| 41 if state and client and not len then -- reading client body until EOF | 45 if state and client and not len then -- reading client body until EOF |
| 42 packet.body = buf; | 46 packet.body = buf; |
| 43 success_cb(packet); | 47 success_cb(packet); |
| 44 elseif buf ~= "" then -- unexpected EOF | 48 elseif buf ~= "" then -- unexpected EOF |
| 45 error = true; return error_cb(); | 49 error = true; return error_cb(); |
| 46 end | 50 end |
| 47 return; | 51 return; |
| 48 end | 52 end |
| 49 buf = buf..data; | 53 if buftable then |
| 50 while #buf > 0 do | 54 t_insert(buf, data); |
| 55 else | |
| 56 buf = { buf, data }; | |
| 57 buftable = true; | |
| 58 end | |
| 59 buflen = buflen + #data; | |
| 60 if buflen > buflimit then error = true; return error_cb("max-buffer-size-exceeded"); end | |
| 61 while buflen > 0 do | |
| 51 if state == nil then -- read request | 62 if state == nil then -- read request |
| 63 if buftable then buf, buftable = t_concat(buf), false; end | |
| 52 local index = buf:find("\r\n\r\n", nil, true); | 64 local index = buf:find("\r\n\r\n", nil, true); |
| 53 if not index then return; end -- not enough data | 65 if not index then return; end -- not enough data |
| 54 local method, path, httpversion, status_code, reason_phrase; | 66 local method, path, httpversion, status_code, reason_phrase; |
| 55 local first_line; | 67 local first_line; |
| 56 local headers = {}; | 68 local headers = {}; |
| 77 end | 89 end |
| 78 end | 90 end |
| 79 if not first_line then error = true; return error_cb("invalid-status-line"); end | 91 if not first_line then error = true; return error_cb("invalid-status-line"); end |
| 80 chunked = have_body and headers["transfer-encoding"] == "chunked"; | 92 chunked = have_body and headers["transfer-encoding"] == "chunked"; |
| 81 len = tonumber(headers["content-length"]); -- TODO check for invalid len | 93 len = tonumber(headers["content-length"]); -- TODO check for invalid len |
| 94 if len and len > bodylimit then error = true; return error_cb("content-length-limit-exceeded"); end | |
| 82 if client then | 95 if client then |
| 83 -- FIXME handle '100 Continue' response (by skipping it) | 96 -- FIXME handle '100 Continue' response (by skipping it) |
| 84 if not have_body then len = 0; end | 97 if not have_body then len = 0; end |
| 85 packet = { | 98 packet = { |
| 86 code = status_code; | 99 code = status_code; |
| 113 headers = headers; | 126 headers = headers; |
| 114 body = nil; | 127 body = nil; |
| 115 }; | 128 }; |
| 116 end | 129 end |
| 117 buf = buf:sub(index + 4); | 130 buf = buf:sub(index + 4); |
| 131 buflen = #buf; | |
| 118 state = true; | 132 state = true; |
| 119 end | 133 end |
| 120 if state then -- read body | 134 if state then -- read body |
| 121 if client then | 135 if client then |
| 122 if chunked then | 136 if chunked then |
| 137 if buftable then buf, buftable = t_concat(buf), false; end | |
| 123 if not buf:find("\r\n", nil, true) then | 138 if not buf:find("\r\n", nil, true) then |
| 124 return; | 139 return; |
| 125 end -- not enough data | 140 end -- not enough data |
| 126 if not chunk_size then | 141 if not chunk_size then |
| 127 chunk_size, chunk_start = buf:match("^(%x+)[^\r\n]*\r\n()"); | 142 chunk_size, chunk_start = buf:match("^(%x+)[^\r\n]*\r\n()"); |
| 130 end | 145 end |
| 131 if chunk_size == 0 and buf:find("\r\n\r\n", chunk_start-2, true) then | 146 if chunk_size == 0 and buf:find("\r\n\r\n", chunk_start-2, true) then |
| 132 state, chunk_size = nil, nil; | 147 state, chunk_size = nil, nil; |
| 133 buf = buf:gsub("^.-\r\n\r\n", ""); -- This ensure extensions and trailers are stripped | 148 buf = buf:gsub("^.-\r\n\r\n", ""); -- This ensure extensions and trailers are stripped |
| 134 success_cb(packet); | 149 success_cb(packet); |
| 135 elseif #buf - chunk_start - 2 >= chunk_size then -- we have a chunk | 150 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)); | 151 packet.body = packet.body..buf:sub(chunk_start, chunk_start + (chunk_size-1)); |
| 137 buf = buf:sub(chunk_start + chunk_size + 2); | 152 buf = buf:sub(chunk_start + chunk_size + 2); |
| 138 chunk_size, chunk_start = nil, nil; | 153 chunk_size, chunk_start = nil, nil; |
| 139 else -- Partial chunk remaining | 154 else -- Partial chunk remaining |
| 140 break; | 155 break; |
| 141 end | 156 end |
| 142 elseif len and #buf >= len then | 157 elseif len and buflen >= len then |
| 158 if buftable then buf, buftable = t_concat(buf), false; end | |
| 143 if packet.code == 101 then | 159 if packet.code == 101 then |
| 144 packet.body, buf = buf, ""; | 160 packet.body, buf, buflen, buftable = buf, {}, 0, true; |
| 145 else | 161 else |
| 146 packet.body, buf = buf:sub(1, len), buf:sub(len + 1); | 162 packet.body, buf = buf:sub(1, len), buf:sub(len + 1); |
| 163 buflen = #buf; | |
| 147 end | 164 end |
| 148 state = nil; success_cb(packet); | 165 state = nil; success_cb(packet); |
| 149 else | 166 else |
| 150 break; | 167 break; |
| 151 end | 168 end |
| 152 elseif #buf >= len then | 169 elseif buflen >= len then |
| 170 if buftable then buf, buftable = t_concat(buf), false; end | |
| 153 packet.body, buf = buf:sub(1, len), buf:sub(len + 1); | 171 packet.body, buf = buf:sub(1, len), buf:sub(len + 1); |
| 172 buflen = #buf; | |
| 154 state = nil; success_cb(packet); | 173 state = nil; success_cb(packet); |
| 155 else | 174 else |
| 156 break; | 175 break; |
| 157 end | 176 end |
| 158 end | 177 end |