diff options
author | 2017-07-25 15:18:33 +0800 | |
---|---|---|
committer | 2017-07-25 18:11:59 +0800 | |
commit | 672f3d40be83d9e380fd7be4b674d5e8d5fa36de (patch) | |
tree | 43105e1d5e2ba8e8accea8648e57e1cf87db3f00 /msb-core/openresty-ext/src/assembly/resources/openresty/lualib/resty/http.lua | |
parent | 41d3db15a8e1a0496f9c2a5e15db2998a32bb9bf (diff) |
Divide the MSB source codes into two repos
Change-Id: Ie76d545b214a8ce5191f215350a623e1529983d9
Issue-id: MSB-5
Signed-off-by: HuabingZhao <zhao.huabing@zte.com.cn>
Diffstat (limited to 'msb-core/openresty-ext/src/assembly/resources/openresty/lualib/resty/http.lua')
-rw-r--r-- | msb-core/openresty-ext/src/assembly/resources/openresty/lualib/resty/http.lua | 850 |
1 files changed, 0 insertions, 850 deletions
diff --git a/msb-core/openresty-ext/src/assembly/resources/openresty/lualib/resty/http.lua b/msb-core/openresty-ext/src/assembly/resources/openresty/lualib/resty/http.lua deleted file mode 100644 index 94f9813..0000000 --- a/msb-core/openresty-ext/src/assembly/resources/openresty/lualib/resty/http.lua +++ /dev/null @@ -1,850 +0,0 @@ -local http_headers = require "resty.http_headers" - -local ngx_socket_tcp = ngx.socket.tcp -local ngx_req = ngx.req -local ngx_req_socket = ngx_req.socket -local ngx_req_get_headers = ngx_req.get_headers -local ngx_req_get_method = ngx_req.get_method -local str_gmatch = string.gmatch -local str_lower = string.lower -local str_upper = string.upper -local str_find = string.find -local str_sub = string.sub -local str_gsub = string.gsub -local tbl_concat = table.concat -local tbl_insert = table.insert -local ngx_encode_args = ngx.encode_args -local ngx_re_match = ngx.re.match -local ngx_re_gsub = ngx.re.gsub -local ngx_log = ngx.log -local ngx_DEBUG = ngx.DEBUG -local ngx_ERR = ngx.ERR -local ngx_NOTICE = ngx.NOTICE -local ngx_var = ngx.var -local co_yield = coroutine.yield -local co_create = coroutine.create -local co_status = coroutine.status -local co_resume = coroutine.resume - - --- http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.1 -local HOP_BY_HOP_HEADERS = { - ["connection"] = true, - ["keep-alive"] = true, - ["proxy-authenticate"] = true, - ["proxy-authorization"] = true, - ["te"] = true, - ["trailers"] = true, - ["transfer-encoding"] = true, - ["upgrade"] = true, - ["content-length"] = true, -- Not strictly hop-by-hop, but Nginx will deal - -- with this (may send chunked for example). -} - - --- Reimplemented coroutine.wrap, returning "nil, err" if the coroutine cannot --- be resumed. This protects user code from inifite loops when doing things like --- repeat --- local chunk, err = res.body_reader() --- if chunk then -- <-- This could be a string msg in the core wrap function. --- ... --- end --- until not chunk -local co_wrap = function(func) - local co = co_create(func) - if not co then - return nil, "could not create coroutine" - else - return function(...) - if co_status(co) == "suspended" then - return select(2, co_resume(co, ...)) - else - return nil, "can't resume a " .. co_status(co) .. " coroutine" - end - end - end -end - - -local _M = { - _VERSION = '0.09', -} -_M._USER_AGENT = "lua-resty-http/" .. _M._VERSION .. " (Lua) ngx_lua/" .. ngx.config.ngx_lua_version - -local mt = { __index = _M } - - -local HTTP = { - [1.0] = " HTTP/1.0\r\n", - [1.1] = " HTTP/1.1\r\n", -} - -local DEFAULT_PARAMS = { - method = "GET", - path = "/", - version = 1.1, -} - - -function _M.new(self) - local sock, err = ngx_socket_tcp() - if not sock then - return nil, err - end - return setmetatable({ sock = sock, keepalive = true }, mt) -end - - -function _M.set_timeout(self, timeout) - local sock = self.sock - if not sock then - return nil, "not initialized" - end - - return sock:settimeout(timeout) -end - - -function _M.ssl_handshake(self, ...) - local sock = self.sock - if not sock then - return nil, "not initialized" - end - - self.ssl = true - - return sock:sslhandshake(...) -end - - -function _M.connect(self, ...) - local sock = self.sock - if not sock then - return nil, "not initialized" - end - - self.host = select(1, ...) - self.port = select(2, ...) - - -- If port is not a number, this is likely a unix domain socket connection. - if type(self.port) ~= "number" then - self.port = nil - end - - self.keepalive = true - - return sock:connect(...) -end - - -function _M.set_keepalive(self, ...) - local sock = self.sock - if not sock then - return nil, "not initialized" - end - - if self.keepalive == true then - return sock:setkeepalive(...) - else - -- The server said we must close the connection, so we cannot setkeepalive. - -- If close() succeeds we return 2 instead of 1, to differentiate between - -- a normal setkeepalive() failure and an intentional close(). - local res, err = sock:close() - if res then - return 2, "connection must be closed" - else - return res, err - end - end -end - - -function _M.get_reused_times(self) - local sock = self.sock - if not sock then - return nil, "not initialized" - end - - return sock:getreusedtimes() -end - - -function _M.close(self) - local sock = self.sock - if not sock then - return nil, "not initialized" - end - - return sock:close() -end - - -local function _should_receive_body(method, code) - if method == "HEAD" then return nil end - if code == 204 or code == 304 then return nil end - if code >= 100 and code < 200 then return nil end - return true -end - - -function _M.parse_uri(self, uri) - local m, err = ngx_re_match(uri, [[^(http[s]?)://([^:/]+)(?::(\d+))?(.*)]], - "jo") - - if not m then - if err then - return nil, "failed to match the uri: " .. uri .. ", " .. err - end - - return nil, "bad uri: " .. uri - else - if m[3] then - m[3] = tonumber(m[3]) - else - if m[1] == "https" then - m[3] = 443 - else - m[3] = 80 - end - end - if not m[4] or "" == m[4] then m[4] = "/" end - return m, nil - end -end - - -local function _format_request(params) - local version = params.version - local headers = params.headers or {} - - local query = params.query or "" - if query then - if type(query) == "table" then - query = "?" .. ngx_encode_args(query) - end - end - - -- Initialize request - local req = { - str_upper(params.method), - " ", - params.path, - query, - HTTP[version], - -- Pre-allocate slots for minimum headers and carriage return. - true, - true, - true, - } - local c = 6 -- req table index it's faster to do this inline vs table.insert - - -- Append headers - for key, values in pairs(headers) do - if type(values) ~= "table" then - values = {values} - end - - key = tostring(key) - for _, value in pairs(values) do - req[c] = key .. ": " .. tostring(value) .. "\r\n" - c = c + 1 - end - end - - -- Close headers - req[c] = "\r\n" - - return tbl_concat(req) -end - - -local function _receive_status(sock) - local line, err = sock:receive("*l") - if not line then - return nil, nil, nil, err - end - - return tonumber(str_sub(line, 10, 12)), tonumber(str_sub(line, 6, 8)), str_sub(line, 14) -end - - - -local function _receive_headers(sock) - local headers = http_headers.new() - - repeat - local line, err = sock:receive("*l") - if not line then - return nil, err - end - - for key, val in str_gmatch(line, "([^:%s]+):%s*(.+)") do - if headers[key] then - if type(headers[key]) ~= "table" then - headers[key] = { headers[key] } - end - tbl_insert(headers[key], tostring(val)) - else - headers[key] = tostring(val) - end - end - until str_find(line, "^%s*$") - - return headers, nil -end - - -local function _chunked_body_reader(sock, default_chunk_size) - return co_wrap(function(max_chunk_size) - local max_chunk_size = max_chunk_size or default_chunk_size - local remaining = 0 - local length - - repeat - -- If we still have data on this chunk - if max_chunk_size and remaining > 0 then - - if remaining > max_chunk_size then - -- Consume up to max_chunk_size - length = max_chunk_size - remaining = remaining - max_chunk_size - else - -- Consume all remaining - length = remaining - remaining = 0 - end - else -- This is a fresh chunk - - -- Receive the chunk size - local str, err = sock:receive("*l") - if not str then - co_yield(nil, err) - end - - length = tonumber(str, 16) - - if not length then - co_yield(nil, "unable to read chunksize") - end - - if max_chunk_size and length > max_chunk_size then - -- Consume up to max_chunk_size - remaining = length - max_chunk_size - length = max_chunk_size - end - end - - if length > 0 then - local str, err = sock:receive(length) - if not str then - co_yield(nil, err) - end - - max_chunk_size = co_yield(str) or default_chunk_size - - -- If we're finished with this chunk, read the carriage return. - if remaining == 0 then - sock:receive(2) -- read \r\n - end - else - -- Read the last (zero length) chunk's carriage return - sock:receive(2) -- read \r\n - end - - until length == 0 - end) -end - - -local function _body_reader(sock, content_length, default_chunk_size) - return co_wrap(function(max_chunk_size) - local max_chunk_size = max_chunk_size or default_chunk_size - - if not content_length and max_chunk_size then - -- We have no length, but wish to stream. - -- HTTP 1.0 with no length will close connection, so read chunks to the end. - repeat - local str, err, partial = sock:receive(max_chunk_size) - if not str and err == "closed" then - max_chunk_size = tonumber(co_yield(partial, err) or default_chunk_size) - end - - max_chunk_size = tonumber(co_yield(str) or default_chunk_size) - if max_chunk_size and max_chunk_size < 0 then max_chunk_size = nil end - - if not max_chunk_size then - ngx_log(ngx_ERR, "Buffer size not specified, bailing") - break - end - until not str - - elseif not content_length then - -- We have no length but don't wish to stream. - -- HTTP 1.0 with no length will close connection, so read to the end. - co_yield(sock:receive("*a")) - - elseif not max_chunk_size then - -- We have a length and potentially keep-alive, but want everything. - co_yield(sock:receive(content_length)) - - else - -- We have a length and potentially a keep-alive, and wish to stream - -- the response. - local received = 0 - repeat - local length = max_chunk_size - if received + length > content_length then - length = content_length - received - end - - if length > 0 then - local str, err = sock:receive(length) - if not str then - max_chunk_size = tonumber(co_yield(nil, err) or default_chunk_size) - end - received = received + length - - max_chunk_size = tonumber(co_yield(str) or default_chunk_size) - if max_chunk_size and max_chunk_size < 0 then max_chunk_size = nil end - - if not max_chunk_size then - ngx_log(ngx_ERR, "Buffer size not specified, bailing") - break - end - end - - until length == 0 - end - end) -end - - -local function _no_body_reader() - return nil -end - - -local function _read_body(res) - local reader = res.body_reader - - if not reader then - -- Most likely HEAD or 304 etc. - return nil, "no body to be read" - end - - local chunks = {} - local c = 1 - - local chunk, err - repeat - chunk, err = reader() - - if err then - return nil, err, tbl_concat(chunks) -- Return any data so far. - end - if chunk then - chunks[c] = chunk - c = c + 1 - end - until not chunk - - return tbl_concat(chunks) -end - - -local function _trailer_reader(sock) - return co_wrap(function() - co_yield(_receive_headers(sock)) - end) -end - - -local function _read_trailers(res) - local reader = res.trailer_reader - if not reader then - return nil, "no trailers" - end - - local trailers = reader() - setmetatable(res.headers, { __index = trailers }) -end - - -local function _send_body(sock, body) - if type(body) == 'function' then - repeat - local chunk, err, partial = body() - - if chunk then - local ok,err = sock:send(chunk) - - if not ok then - return nil, err - end - elseif err ~= nil then - return nil, err, partial - end - - until chunk == nil - elseif body ~= nil then - local bytes, err = sock:send(body) - - if not bytes then - return nil, err - end - end - return true, nil -end - - -local function _handle_continue(sock, body) - local status, version, reason, err = _receive_status(sock) - if not status then - return nil, nil, err - end - - -- Only send body if we receive a 100 Continue - if status == 100 then - local ok, err = sock:receive("*l") -- Read carriage return - if not ok then - return nil, nil, err - end - _send_body(sock, body) - end - return status, version, err -end - - -function _M.send_request(self, params) - -- Apply defaults - setmetatable(params, { __index = DEFAULT_PARAMS }) - - local sock = self.sock - local body = params.body - local headers = http_headers.new() - - local params_headers = params.headers - if params_headers then - -- We assign one by one so that the metatable can handle case insensitivity - -- for us. You can blame the spec for this inefficiency. - for k,v in pairs(params_headers) do - headers[k] = v - end - end - - -- Ensure minimal headers are set - if type(body) == 'string' and not headers["Content-Length"] then - headers["Content-Length"] = #body - end - if not headers["Host"] then - if (str_sub(self.host, 1, 5) == "unix:") then - return nil, "Unable to generate a useful Host header for a unix domain socket. Please provide one." - end - -- If we have a port (i.e. not connected to a unix domain socket), and this - -- port is non-standard, append it to the Host heaer. - if self.port then - if self.ssl and self.port ~= 443 then - headers["Host"] = self.host .. ":" .. self.port - elseif not self.ssl and self.port ~= 80 then - headers["Host"] = self.host .. ":" .. self.port - else - headers["Host"] = self.host - end - else - headers["Host"] = self.host - end - end - if not headers["User-Agent"] then - headers["User-Agent"] = _M._USER_AGENT - end - if params.version == 1.0 and not headers["Connection"] then - headers["Connection"] = "Keep-Alive" - end - - params.headers = headers - - -- Format and send request - local req = _format_request(params) - ngx_log(ngx_DEBUG, "\n", req) - local bytes, err = sock:send(req) - - if not bytes then - return nil, err - end - - -- Send the request body, unless we expect: continue, in which case - -- we handle this as part of reading the response. - if headers["Expect"] ~= "100-continue" then - local ok, err, partial = _send_body(sock, body) - if not ok then - return nil, err, partial - end - end - - return true -end - - -function _M.read_response(self, params) - local sock = self.sock - - local status, version, reason, err - - -- If we expect: continue, we need to handle this, sending the body if allowed. - -- If we don't get 100 back, then status is the actual status. - if params.headers["Expect"] == "100-continue" then - local _status, _version, _err = _handle_continue(sock, params.body) - if not _status then - return nil, _err - elseif _status ~= 100 then - status, version, err = _status, _version, _err - end - end - - -- Just read the status as normal. - if not status then - status, version, reason, err = _receive_status(sock) - if not status then - return nil, err - end - end - - - local res_headers, err = _receive_headers(sock) - if not res_headers then - return nil, err - end - - -- keepalive is true by default. Determine if this is correct or not. - local ok, connection = pcall(str_lower, res_headers["Connection"]) - if ok then - if (version == 1.1 and connection == "close") or - (version == 1.0 and connection ~= "keep-alive") then - self.keepalive = false - end - else - -- no connection header - if version == 1.0 then - self.keepalive = false - end - end - - local body_reader = _no_body_reader - local trailer_reader, err = nil, nil - local has_body = false - - -- Receive the body_reader - if _should_receive_body(params.method, status) then - local ok, encoding = pcall(str_lower, res_headers["Transfer-Encoding"]) - if ok and version == 1.1 and encoding == "chunked" then - body_reader, err = _chunked_body_reader(sock) - has_body = true - else - - local ok, length = pcall(tonumber, res_headers["Content-Length"]) - if ok then - body_reader, err = _body_reader(sock, length) - has_body = true - end - end - end - - if res_headers["Trailer"] then - trailer_reader, err = _trailer_reader(sock) - end - - if err then - return nil, err - else - return { - status = status, - reason = reason, - headers = res_headers, - has_body = has_body, - body_reader = body_reader, - read_body = _read_body, - trailer_reader = trailer_reader, - read_trailers = _read_trailers, - } - end -end - - -function _M.request(self, params) - local res, err = self:send_request(params) - if not res then - return res, err - else - return self:read_response(params) - end -end - - -function _M.request_pipeline(self, requests) - for i, params in ipairs(requests) do - if params.headers and params.headers["Expect"] == "100-continue" then - return nil, "Cannot pipeline request specifying Expect: 100-continue" - end - - local res, err = self:send_request(params) - if not res then - return res, err - end - end - - local responses = {} - for i, params in ipairs(requests) do - responses[i] = setmetatable({ - params = params, - response_read = false, - }, { - -- Read each actual response lazily, at the point the user tries - -- to access any of the fields. - __index = function(t, k) - local res, err - if t.response_read == false then - res, err = _M.read_response(self, t.params) - t.response_read = true - - if not res then - ngx_log(ngx_ERR, err) - else - for rk, rv in pairs(res) do - t[rk] = rv - end - end - end - return rawget(t, k) - end, - }) - end - return responses -end - - -function _M.request_uri(self, uri, params) - if not params then params = {} end - - local parsed_uri, err = self:parse_uri(uri) - if not parsed_uri then - return nil, err - end - - local scheme, host, port, path = unpack(parsed_uri) - if not params.path then params.path = path end - - local c, err = self:connect(host, port) - if not c then - return nil, err - end - - if scheme == "https" then - local verify = true - if params.ssl_verify == false then - verify = false - end - local ok, err = self:ssl_handshake(nil, host, verify) - if not ok then - return nil, err - end - end - - local res, err = self:request(params) - if not res then - return nil, err - end - - local body, err = res:read_body() - if not body then - return nil, err - end - - res.body = body - - local ok, err = self:set_keepalive() - if not ok then - ngx_log(ngx_ERR, err) - end - - return res, nil -end - - -function _M.get_client_body_reader(self, chunksize, sock) - local chunksize = chunksize or 65536 - if not sock then - local ok, err - ok, sock, err = pcall(ngx_req_socket) - - if not ok then - return nil, sock -- pcall err - end - - if not sock then - if err == "no body" then - return nil - else - return nil, err - end - end - end - - local headers = ngx_req_get_headers() - local length = headers.content_length - local encoding = headers.transfer_encoding - if length then - return _body_reader(sock, tonumber(length), chunksize) - elseif encoding and str_lower(encoding) == 'chunked' then - -- Not yet supported by ngx_lua but should just work... - return _chunked_body_reader(sock, chunksize) - else - return nil - end -end - - -function _M.proxy_request(self, chunksize) - return self:request{ - method = ngx_req_get_method(), - path = ngx_re_gsub(ngx_var.uri, "\\s", "%20", "jo") .. ngx_var.is_args .. (ngx_var.query_string or ""), - body = self:get_client_body_reader(chunksize), - headers = ngx_req_get_headers(), - } -end - - -function _M.proxy_response(self, response, chunksize) - if not response then - ngx_log(ngx_ERR, "no response provided") - return - end - - ngx.status = response.status - - -- Filter out hop-by-hop headeres - for k,v in pairs(response.headers) do - if not HOP_BY_HOP_HEADERS[str_lower(k)] then - ngx.header[k] = v - end - end - - local reader = response.body_reader - repeat - local chunk, err = reader(chunksize) - if err then - ngx_log(ngx_ERR, err) - break - end - - if chunk then - local res, err = ngx.print(chunk) - if not res then - ngx_log(ngx_ERR, err) - break - end - end - until not chunk -end - - -return _M |