Skip to content

Commit

Permalink
Refactor code to use lua-resty-http 0.17.1
Browse files Browse the repository at this point in the history
  • Loading branch information
tkan145 committed Dec 1, 2023
1 parent e6f1cf0 commit 666b5e9
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 120 deletions.
174 changes: 68 additions & 106 deletions gateway/src/resty/http/proxy.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,108 +13,6 @@ local function default_port(uri)
return uri.port or resty_url.default_port(uri.scheme)
end

local function connect_direct(httpc, request)
local uri = request.uri
local host = uri.host
local ip, port = httpc:resolve(host, nil, uri)
-- #TODO: This logic may no longer be needed as of PR#1323 and should be reviewed as part of a refactor
local options = { pool = format('%s:%s', host, port) }
local ok, err = httpc:connect(ip, port or default_port(uri), options)

if not ok then return nil, err end

ngx.log(ngx.DEBUG, 'connection to ', host, ':', httpc.port, ' established',
', reused times: ', httpc:get_reused_times())

if uri.scheme == 'https' then
ok, err = httpc:ssl_handshake(nil, host, request.ssl_verify)
if not ok then return nil, err end
end

-- use correct host header
httpc.host = host

return httpc
end

local function _connect_tls_direct(httpc, request, host, port)

local uri = request.uri

local ok, err = httpc:ssl_handshake(nil, uri.host, request.ssl_verify)
if not ok then return nil, err end

return httpc
end

local function _connect_proxy_https(httpc, request, host, port)
-- When the connection is reused the tunnel is already established, so
-- the second CONNECT request would reach the upstream instead of the proxy.
if httpc:get_reused_times() > 0 then
return httpc, 'already connected'
end

local uri = request.uri

local res, err = httpc:request({
method = 'CONNECT',
path = format('%s:%s', host, port or default_port(uri)),
headers = {
['Host'] = request.headers.host or format('%s:%s', uri.host, default_port(uri)),
['Proxy-Authorization'] = request.proxy_auth or ''
}
})
if not res then return nil, err end

if res.status < 200 or res.status > 299 then
return nil, "failed to establish a tunnel through a proxy: " .. res.status
end

res, err = httpc:ssl_handshake(nil, uri.host, request.ssl_verify)
if not res then return nil, err end

return httpc
end

local function connect_proxy(httpc, request, skip_https_connect)
-- target server requires hostname not IP and DNS resolution is left to the proxy itself as specified in the RFC #7231
-- https://httpwg.org/specs/rfc7231.html#CONNECT
local uri = request.uri
local proxy_uri = request.proxy

if proxy_uri.scheme ~= 'http' then
return nil, 'proxy connection supports only http'
else
proxy_uri.port = default_port(proxy_uri)
end

local port = default_port(uri)

-- TLS tunnel is verified only once, so we need to reuse connections only for the same Host header
local options = { pool = format('%s:%s:%s:%s', proxy_uri.host, proxy_uri.port, uri.host, port) }
local ok, err = httpc:connect(proxy_uri.host, proxy_uri.port, options)
if not ok then return nil, err end

ngx.log(ngx.DEBUG, 'connection to ', proxy_uri.host, ':', proxy_uri.port, ' established',
', pool: ', options.pool, ' reused times: ', httpc:get_reused_times())

ngx.log(ngx.DEBUG, 'targeting server ', uri.host, ':', uri.port)

if uri.scheme == 'http' then
-- http proxy needs absolute URL as the request path
request.path = format('%s://%s:%s%s', uri.scheme, uri.host, uri.port, uri.path or '/')
return httpc
elseif uri.scheme == 'https' and skip_https_connect then
request.path = format('%s://%s:%s%s', uri.scheme, uri.host, uri.port, request.path or '/')
return _connect_tls_direct(httpc, request, uri.host, uri.port)
elseif uri.scheme == 'https' then
return _connect_proxy_https(httpc, request, uri.host, uri.port)

else
return nil, 'invalid scheme'
end
end

local function parse_request_uri(request)
local uri = request.uri or resty_url.parse(request.url)
request.uri = uri
Expand All @@ -131,17 +29,81 @@ local function find_proxy_url(request)
end

local function connect(request, skip_https_connect)
local httpc = http.new()
local httpc = http.new()
local proxy_uri = find_proxy_url(request)
local uri = request.uri
local host = uri.host
local port = default_port(uri)
-- set ssl_verify: lua-resty-http set ssl_verify to true by default if scheme is https, whereas
-- openresty treat nil as false, so we need to explicitly set ssl_verify to false if nil
local ssl_verify = request.options and request.options.ssl and request.options.ssl.verify or false

request.ssl_verify = request.options and request.options.ssl and request.options.ssl.verify
-- request.ssl_verify = request.options and request.options.ssl and request.options.ssl.verify
request.proxy = proxy_uri

local options = {
scheme = uri.scheme,
host = host,
port = uri.port,
ssl_server_name=host,
ssl_verify = ssl_verify,
}

if proxy_uri then
return connect_proxy(httpc, request, skip_https_connect)
-- target server requires hostname not IP and DNS resolution is left to the proxy itself as specified in the RFC #7231
-- https://httpwg.org/specs/rfc7231.html#CONNECT

if proxy_uri.scheme ~= 'http' then
return nil, 'proxy connection supports only http'
else
proxy_uri.port = default_port(proxy_uri)
end

if uri.scheme == 'http' then
request.path = uri.path or '/'
options.proxy_opts = {
http_proxy = format("%s://%s:%s", proxy_uri.scheme, proxy_uri.host, proxy_uri.port),
http_proxy_authorization = request.proxy_auth
}
elseif uri.scheme == 'https' and skip_https_connect then
request.path = request.path or '/'

-- The new lua-resty-http connect method does not allow skipping CONNECT when proxying https request
-- so keep the old code here
--
-- TLS tunnel is verified only once, so we need to reuse connections only for the same Host header
local options = { pool = format('%s:%s:%s:%s', proxy_uri.host, proxy_uri.port, uri.host, port) }
local ok, err = httpc:connect(proxy_uri.host, proxy_uri.port, options)
if not ok then return nil, err end

ngx.log(ngx.DEBUG, 'connection to ', proxy_uri.host, ':', proxy_uri.port, ' established',
', pool: ', options.pool, ' reused times: ', httpc:get_reused_times())

ngx.log(ngx.DEBUG, 'targeting server ', host, ':', port)

local ok, err = httpc:ssl_handshake(nil, host, ssl_verify)
if not ok then return nil, err end

return httpc
elseif uri.scheme == 'https' then
options.proxy_opts = {
https_proxy = format("%s://%s:%s", proxy_uri.scheme, proxy_uri.host, proxy_uri.port),
https_proxy_authorization = request.proxy_auth
}
else
return nil, 'invalid scheme'
end
else
return connect_direct(httpc, request)
local ip, resolved_port = httpc:resolve(host, nil, uri)
-- #TODO: This logic may no longer be needed as of PR#1323 and should be reviewed as part of a refactor
options.host = ip
options.port = resolved_port
end

local ok, err = httpc:connect(options)
if not ok then return nil, err end

return httpc
end

function _M.env()
Expand Down
25 changes: 12 additions & 13 deletions gateway/src/resty/http_ng/backend/async_resty.lua
Original file line number Diff line number Diff line change
Expand Up @@ -45,22 +45,21 @@ _M.async = function(request)
end
end

local ok, err = httpc:connect(host, port)
local verify = request.options and request.options.ssl and request.options.ssl.verify
if type(verify) == 'nil' then verify = true end

if not ok then
return response.error(request, err)
end

if scheme == 'https' then
local verify = request.options and request.options.ssl and request.options.ssl.verify
if type(verify) == 'nil' then verify = true end
local options = {
scheme = scheme,
host = host,
port = port,
ssl_server_name=host,
ssl_verify = verify
}

local session
session, err = httpc:ssl_handshake(false, host, verify)
local ok, err = httpc:connect(options)

if not session then
return response.error(request, err)
end
if not ok then
return response.error(request, err)
end

local res
Expand Down
12 changes: 11 additions & 1 deletion gateway/src/resty/resolver/http.lua
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ function _M:resolve(host, port, options)
return ip, port
end

function _M.connect(self, host, port, ...)
local function v0_15_connect(self, host, port, ...)
local ip, real_port = self:resolve(host, port)
local ok, err = resty_http.connect(self, ip, real_port, ...)

Expand All @@ -52,4 +52,14 @@ function _M.connect(self, host, port, ...)
return ok, err
end

function _M.connect(self, options, ...)
if type(options) == "table" then
return resty_http.connect(self, options)
else
-- backward compatible
return v0_15_connect(self, options, ...)
end
end


return _M

0 comments on commit 666b5e9

Please sign in to comment.