diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/http.lua | 53 | ||||
-rw-r--r-- | net/http/parser.lua | 40 |
2 files changed, 33 insertions, 60 deletions
diff --git a/net/http.lua b/net/http.lua index ec55af92..4eb4a2ac 100644 --- a/net/http.lua +++ b/net/http.lua @@ -9,7 +9,8 @@ local socket = require "socket" local b64 = require "util.encodings".base64.encode; local url = require "socket.url" -local httpstream_new = require "util.httpstream".new; +local httpstream_new = require "net.http.parser".new; +local util_http = require "util.http"; local ssl_available = pcall(require, "ssl"); @@ -70,52 +71,12 @@ function listener.ondisconnect(conn, err) requests[conn] = nil; end -function urlencode(s) return s and (s:gsub("[^a-zA-Z0-9.~_-]", function (c) return format("%%%02x", c:byte()); end)); end -function urldecode(s) return s and (s:gsub("%%(%x%x)", function (c) return char(tonumber(c,16)); end)); end - -local function _formencodepart(s) - return s and (s:gsub("%W", function (c) - if c ~= " " then - return format("%%%02x", c:byte()); - else - return "+"; - end - end)); -end - -function formencode(form) - local result = {}; - if form[1] then -- Array of ordered { name, value } - for _, field in ipairs(form) do - t_insert(result, _formencodepart(field.name).."=".._formencodepart(field.value)); - end - else -- Unordered map of name -> value - for name, value in pairs(form) do - t_insert(result, _formencodepart(name).."=".._formencodepart(value)); - end - end - return t_concat(result, "&"); -end - -function formdecode(s) - if not s:match("=") then return urldecode(s); end - local r = {}; - for k, v in s:gmatch("([^=&]*)=([^&]*)") do - k, v = k:gsub("%+", "%%20"), v:gsub("%+", "%%20"); - k, v = urldecode(k), urldecode(v); - t_insert(r, { name = k, value = v }); - r[k] = v; - end - return r; -end - -local function request_reader(request, data, startpos) +local function request_reader(request, data) if not request.parser then if not data then return; end local function success_cb(r) if request.callback then - for k,v in pairs(r) do request[k] = v; end - request.callback(r.body, r.code, request, r); + request.callback(r.body, r.code, r, request); request.callback = nil; end destroy_request(request); @@ -216,6 +177,10 @@ function destroy_request(request) end end -_M.urlencode = urlencode; +local urlencode, urldecode = util_http.urlencode, util_http.urldecode; +local formencode, formdecode = util_http.formencode, util_http.formdecode; + +_M.urlencode, _M.urldecode = urlencode, urldecode; +_M.formencode, _M.formdecode = formencode, formdecode; return _M; diff --git a/net/http/parser.lua b/net/http/parser.lua index 2545b5ac..34742d2b 100644 --- a/net/http/parser.lua +++ b/net/http/parser.lua @@ -1,8 +1,7 @@ - local tonumber = tonumber; local assert = assert; local url_parse = require "socket.url".parse; -local urldecode = require "net.http".urldecode; +local urldecode = require "util.http".urldecode; local function preprocess_path(path) path = urldecode((path:gsub("//+", "/"))); @@ -29,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 chunked; + local chunked, chunk_size, chunk_start; local state = nil; local packet; local len; @@ -65,12 +64,12 @@ function httpstream.new(success_cb, error_cb, parser_type, options_cb) first_line = line; if client then httpversion, status_code, reason_phrase = line:match("^HTTP/(1%.[01]) (%d%d%d) (.*)$"); + status_code = tonumber(status_code); if not status_code then error = true; return error_cb("invalid-status-line"); end have_body = not ( (options_cb and options_cb().method == "HEAD") or (status_code == 204 or status_code == 304 or status_code == 301) or (status_code >= 100 and status_code < 200) ); - chunked = have_body and headers["transfer-encoding"] == "chunked"; else method, path, httpversion = line:match("^(%w+) (%S+) HTTP/(1%.[01])$"); if not method then error = true; return error_cb("invalid-status-line"); end @@ -78,6 +77,7 @@ function httpstream.new(success_cb, error_cb, parser_type, options_cb) end end if not first_line then error = true; return error_cb("invalid-status-line"); end + chunked = have_body and headers["transfer-encoding"] == "chunked"; len = tonumber(headers["content-length"]); -- TODO check for invalid len if client then -- FIXME handle '100 Continue' response (by skipping it) @@ -120,22 +120,30 @@ function httpstream.new(success_cb, error_cb, parser_type, options_cb) if state then -- read body if client then if chunked then - local index = buf:find("\r\n", nil, true); - if not index then return; end -- not enough data - local chunk_size = buf:match("^%x+"); - if not chunk_size then error = true; return error_cb("invalid-chunk-size"); end - chunk_size = tonumber(chunk_size, 16); - index = index + 2; - if chunk_size == 0 then - state = nil; success_cb(packet); - elseif #buf - index + 1 >= chunk_size then -- we have a chunk - packet.body = packet.body..buf:sub(index, index + chunk_size - 1); - buf = buf:sub(index + chunk_size); + if not buf:find("\r\n", nil, true) then + return; + end -- not enough data + if not chunk_size then + chunk_size, chunk_start = buf:match("^(%x+)[^\r\n]*\r\n()"); + chunk_size = chunk_size and tonumber(chunk_size, 16); + if not chunk_size then error = true; return error_cb("invalid-chunk-size"); end + end + if chunk_size == 0 and buf:find("\r\n\r\n", chunk_start-2, true) then + 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 + packet.body = packet.body..buf:sub(chunk_start, chunk_start + chunk_size); + buf = buf:sub(chunk_start + chunk_size + 2); + chunk_size, chunk_start = nil, nil; + else -- Partial chunk remaining + break; end - error("trailers"); -- FIXME MUST read trailers elseif len and #buf >= len then packet.body, buf = buf:sub(1, len), buf:sub(len + 1); state = nil; success_cb(packet); + else + break; end elseif #buf >= len then packet.body, buf = buf:sub(1, len), buf:sub(len + 1); |