Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[THREESCALE-10278] upgrade lua-resty-http to 0.17.1 #1434

Merged
merged 4 commits into from
Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

- Fix integration of upstream connection policy with camel policy [PR #1443](https://github.com/3scale/APIcast/pull/1443) [THREESCALE-10582](https://issues.redhat.com/browse/THREESCALE-10582)

- Upgrade lua-resty-http to 0.17.1 to fix 100 response header are not handled when using `HTTPS_PROXY` [PR #1434](https://github.com/3scale/APIcast/pull/1434) [THREESCALE-10278](https://issues.redhat.com/browse/THREESCALE-10278)

### Added

- Detect number of CPU shares when running on Cgroups V2 [PR #1410](https://github.com/3scale/apicast/pull/1410) [THREESCALE-10167](https://issues.redhat.com/browse/THREESCALE-10167)
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ ENV PATH="./lua_modules/bin:/usr/local/openresty/luajit/bin/:${PATH}" \
LUA_CPATH="./lua_modules/lib/lua/5.1/?.so;;" \
LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/opt/app-root/lib"

RUN luarocks install --deps-mode=none --tree /usr/local https://luarocks.org/manifests/pintsized/lua-resty-http-0.15-0.src.rock
RUN luarocks install --deps-mode=none --tree /usr/local https://luarocks.org/manifests/pintsized/lua-resty-http-0.17.1-0.src.rock
RUN luarocks install --deps-mode=none --tree /usr/local https://luarocks.org/manifests/kikito/router-2.1-0.src.rock
RUN luarocks install --deps-mode=none --tree /usr/local https://luarocks.org/manifests/kikito/inspect-3.1.1-0.src.rock
RUN luarocks install --deps-mode=none --tree /usr/local https://luarocks.org/manifests/cdbattags/lua-resty-jwt-0.2.0-0.src.rock
Expand Down
2 changes: 1 addition & 1 deletion gateway/Roverfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ ldoc 1.4.6-2||development
liquid 0.2.0-2||production
lua-resty-env 0.4.0-1||production
lua-resty-execvp 0.1.1-1||production
lua-resty-http 0.15-0||production
lua-resty-http 0.17.1-0||production
lua-resty-iputils 0.3.0-2||production
lua-resty-jit-uuid 0.0.7-2||production
lua-resty-jwt 0.2.0-0||production
Expand Down
2 changes: 1 addition & 1 deletion gateway/apicast-scm-1.rockspec
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ description = {
license = "Apache License 2.0"
}
dependencies = {
'lua-resty-http == 0.15',
'lua-resty-http == 0.17.1',
'inspect',
'lyaml',
'router',
Expand Down
193 changes: 86 additions & 107 deletions gateway/src/resty/http/proxy.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,109 +14,6 @@
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)
-- 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
local skip_https_connect = request.skip_https_connect

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
local custom_uri = { scheme = uri.scheme, host = uri.host, port = uri.port, path = request.path }
request.path = url_helper.absolute_url(custom_uri)
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 Down Expand Up @@ -150,15 +47,97 @@
end

local proxy_uri = find_proxy_url(request)
local uri = request.uri
local scheme = uri.scheme
local host = uri.host
local port = default_port(uri)
local skip_https_connect = request.skip_https_connect

request.ssl_verify = request.options and request.options.ssl and request.options.ssl.verify
request.proxy = proxy_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

local options = {
scheme = scheme,
host = host,
port = port
}
if scheme == 'https' then
options.ssl_server_name = host
options.ssl_verify = ssl_verify
end

-- Connect via proxy
if proxy_uri then
eguzki marked this conversation as resolved.
Show resolved Hide resolved
return connect_proxy(httpc, request)
if proxy_uri.scheme ~= 'http' then
return nil, 'proxy connection supports only http'

Check warning on line 73 in gateway/src/resty/http/proxy.lua

View check run for this annotation

Codecov / codecov/patch

gateway/src/resty/http/proxy.lua#L73

Added line #L73 was not covered by tests
else
proxy_uri.port = default_port(proxy_uri)
end

local proxy_url = format("%s://%s:%s", proxy_uri.scheme, proxy_uri.host, proxy_uri.port)
local proxy_auth = request.proxy_auth

if scheme == 'http' then
eguzki marked this conversation as resolved.
Show resolved Hide resolved
-- Used by http_ng module to send request to 3scale backend through proxy.

-- http proxy needs absolute URL as the request path, lua-resty-http 1.17.1 will
-- construct a path_prefix based on host and port so we only set request path here
--
-- https://github.com/ledgetech/lua-resty-http/blob/master/lib/resty/http_connect.lua#L99
request.path = uri.path or '/'
options.proxy_opts = {
http_proxy = proxy_url,
http_proxy_authorization = proxy_auth
}
elseif scheme == 'https' and skip_https_connect then
options.scheme = proxy_uri.scheme
options.host = proxy_uri.host
options.port = proxy_uri.port
options.pool = format('%s:%s:%s:%s', proxy_uri.host, proxy_uri.port, host, port)
local custom_uri = { scheme = uri.scheme, host = uri.host, port = uri.port, path = request.path }
request.path = url_helper.absolute_url(custom_uri)

Check warning on line 99 in gateway/src/resty/http/proxy.lua

View check run for this annotation

Codecov / codecov/patch

gateway/src/resty/http/proxy.lua#L93-L99

Added lines #L93 - L99 were not covered by tests

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

Check warning on line 102 in gateway/src/resty/http/proxy.lua

View check run for this annotation

Codecov / codecov/patch

gateway/src/resty/http/proxy.lua#L101-L102

Added lines #L101 - L102 were not covered by tests

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

Check warning on line 105 in gateway/src/resty/http/proxy.lua

View check run for this annotation

Codecov / codecov/patch

gateway/src/resty/http/proxy.lua#L104-L105

Added lines #L104 - L105 were not covered by tests

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

Check warning on line 107 in gateway/src/resty/http/proxy.lua

View check run for this annotation

Codecov / codecov/patch

gateway/src/resty/http/proxy.lua#L107

Added line #L107 was not covered by tests

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

Check warning on line 110 in gateway/src/resty/http/proxy.lua

View check run for this annotation

Codecov / codecov/patch

gateway/src/resty/http/proxy.lua#L109-L110

Added lines #L109 - L110 were not covered by tests

return httpc
elseif scheme == 'https' then
options.proxy_opts = {

Check warning on line 114 in gateway/src/resty/http/proxy.lua

View check run for this annotation

Codecov / codecov/patch

gateway/src/resty/http/proxy.lua#L112-L114

Added lines #L112 - L114 were not covered by tests
https_proxy = proxy_url,
https_proxy_authorization = proxy_auth

Check warning on line 116 in gateway/src/resty/http/proxy.lua

View check run for this annotation

Codecov / codecov/patch

gateway/src/resty/http/proxy.lua#L116

Added line #L116 was not covered by tests
}
else
return nil, 'invalid scheme'

Check warning on line 119 in gateway/src/resty/http/proxy.lua

View check run for this annotation

Codecov / codecov/patch

gateway/src/resty/http/proxy.lua#L119

Added line #L119 was not covered by tests
end

-- TLS tunnel is verified only once, so we need to reuse connections only for the same Host header
local ok, err = httpc:connect(options)
if not ok then return nil, err end

ngx.log(ngx.DEBUG, 'connection to ', proxy_uri.host, ':', proxy_uri.port, ' established',
', pool: ', httpc.pool, ' reused times: ', httpc:get_reused_times())
ngx.log(ngx.DEBUG, 'targeting server ', host, ':', port)
else
return connect_direct(httpc, request)
-- Connect direct
eguzki marked this conversation as resolved.
Show resolved Hide resolved
-- Mostly used by http_ng module to connect 3scale backend module.
local ok, err = httpc:connect(options)
if not ok then return nil, err end

ngx.log(ngx.DEBUG, 'connection to ', httpc.host, ':', httpc.port, ' established',
', pool: ', httpc.pool, ' reused times: ', httpc:get_reused_times())
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
41 changes: 37 additions & 4 deletions gateway/src/resty/resolver/http.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
local resty_http = require 'resty.http'
local resty_resolver = require 'resty.resolver'
local round_robin = require 'resty.balancer.round_robin'
local url_helper = require('resty.url_helper')
local format = string.format

local setmetatable = setmetatable

Expand Down Expand Up @@ -38,13 +40,44 @@
return ip, port
end

function _M.connect(self, host, port, ...)
local ip, real_port = self:resolve(host, port)
local ok, err = resty_http.connect(self, ip, real_port, ...)
function _M.connect(self, options, ...)
-- cache the host because we need to resolve host to IP
local host = options.host
local proxy_opts = options.proxy_opts
local proxy = proxy_opts and (proxy_opts.http_proxy or proxy_opts.https_proxy)
local ip, real_port

-- 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
--
-- Therefore, only resolve host IP when not using with proxy
if not proxy then
ip, real_port = self:resolve(options.host, options.port)
options.host = ip
options.port = real_port
else
local proxy_uri, err = url_helper.parse_url(proxy)
if not proxy_uri then
return nil, 'invalid proxy: ' .. err

Check warning on line 61 in gateway/src/resty/resolver/http.lua

View check run for this annotation

Codecov / codecov/patch

gateway/src/resty/resolver/http.lua#L61

Added line #L61 was not covered by tests
end

-- Resolve the proxy IP/Port
local proxy_host, proxy_port = self:resolve(proxy_uri.host, proxy_uri.port)
local proxy_url = format("%s://%s:%s", proxy_uri.scheme, proxy_host, proxy_port)

if proxy_opts.http_proxy then
options.proxy_opts.http_proxy = proxy_url
elseif proxy_opts.https_proxy then
options.proxy_opts.https_proxy = proxy_url

Check warning on line 71 in gateway/src/resty/resolver/http.lua

View check run for this annotation

Codecov / codecov/patch

gateway/src/resty/resolver/http.lua#L70-L71

Added lines #L70 - L71 were not covered by tests
end
end

local ok, err = resty_http.connect(self, options, ...)

if ok then
-- use correct host header
self.host = host
self.port = real_port
self.port = options.port
end

ngx.log(ngx.DEBUG, 'connected to ip:', ip, ' host: ', host, ' port: ', real_port, ' ok: ', ok, ' err: ', err)
Expand Down
2 changes: 1 addition & 1 deletion spec/resty/resolver/http_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ describe('resty.resolver.http', function()
local client = _M.new()
client:set_timeout(1000)
client.resolver.cache:save({ { address = '127.0.0.1', name = 'unknown.', ttl = 1800 } })
assert(client:connect('unknown', 1984))
assert(client:connect({scheme="http", host='unknown', port=1984}))
assert.equal('unknown', client.host)
assert.equal(1984, client.port)
end)
Expand Down
Loading