Skip to content

Commit 107d58d

Browse files
committed
fix(http_proxy): flush response buffers to avoid memory bloat
1 parent d245271 commit 107d58d

3 files changed

Lines changed: 65 additions & 22 deletions

File tree

gateway/src/apicast/http_proxy.lua

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ local file_reader = require("resty.file").file_reader
1111
local file_size = require("resty.file").file_size
1212
local client_body_reader = require("resty.http.request_reader").get_client_body_reader
1313
local send_response = require("resty.http.response_writer").send_response
14+
local proxy_response = require("resty.http.response_writer").proxy_response
1415
local concat = table.concat
1516

1617
local _M = { }
@@ -165,7 +166,11 @@ local function forward_https_request(proxy_uri, uri, proxy_opts)
165166
return sock:send("HTTP/1.1 502 Bad Gateway")
166167
end
167168
else
168-
httpc:proxy_response(res)
169+
local ok, proxy_err = proxy_response(res, DEFAULT_CHUNKSIZE)
170+
if not ok then
171+
ngx.log(ngx.ERR, 'failed to proxy request to: ', proxy_uri, ' err : ', proxy_err)
172+
return ngx.exit(ngx.HTTP_BAD_GATEWAY)
173+
end
169174
httpc:set_keepalive()
170175
end
171176
else

gateway/src/resty/http/proxy.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ local http = require 'resty.resolver.http'
44
local resty_url = require 'resty.url'
55
local resty_env = require 'resty.env'
66
local url_helper = require('resty.url_helper')
7-
local format = string.format
7+
local format = string.format
88

99
local _M = {
1010

gateway/src/resty/http/response_writer.lua

Lines changed: 58 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ local str_lower = string.lower
33
local insert = table.insert
44
local concat = table.concat
55

6+
local ngx = ngx
7+
local ngx_print = ngx.print
8+
local ngx_flush = ngx.flush
9+
local ngx_header= ngx.header
10+
611
local _M = {
712
}
813

@@ -22,13 +27,31 @@ local HOP_BY_HOP_HEADERS = {
2227
-- with this (may send chunked for example).
2328
}
2429

25-
local function send(socket, data)
26-
if not data or data == '' then
27-
ngx.log(ngx.DEBUG, 'skipping sending nil')
28-
return
30+
-- forward_body reads chunks from a body_reader and passes them to the callback
31+
-- function cb.
32+
-- cb(chunk) should return a true on success, or nil/false, err on failure.
33+
local function forward_body(reader, cb, chunksize)
34+
if not reader then
35+
return nil, "no body reader"
36+
end
37+
38+
repeat
39+
local chunk, read_err = reader(chunksize)
40+
41+
if read_err then
42+
return nil, "failed to read response body: " .. read_err
2943
end
3044

31-
return socket:send(data)
45+
if chunk then
46+
local ok, send_err = cb(chunk)
47+
48+
if not ok then
49+
return nil, "failed to send response body: " .. (send_err or "unknown")
50+
end
51+
end
52+
until not chunk
53+
54+
return true
3255
end
3356

3457
-- write_response writes response body reader to sock in the HTTP/1.x server response format,
@@ -65,25 +88,40 @@ function _M.send_response(sock, response, chunksize)
6588
return nil, "failed to send headers, err: " .. (err or "unknown")
6689
end
6790

68-
-- Write body
69-
local reader = response.body_reader
70-
repeat
71-
local chunk, read_err
91+
return forward_body(response.body_reader, function(chunk)
92+
return sock:send(chunk)
93+
end, chunksize)
94+
end
7295

73-
chunk, read_err = reader(chunksize)
74-
if read_err then
75-
return nil, "failed to read response body, err: " .. (err or "unknown")
76-
end
96+
local function ngx_send_and_flush(chunk)
97+
local ok, err = ngx_print(chunk)
98+
if not ok then
99+
return nil, "output response failed: " .. (err or "")
100+
end
77101

78-
if chunk then
79-
bytes, err = send(sock, chunk)
80-
if not bytes then
81-
return nil, "failed to send response body, err: " .. (err or "unknown")
82-
end
102+
ok, err = ngx_flush(true)
103+
if not ok then
104+
ngx.log(ngx.WARN, "flush response failed: ", err)
105+
end
106+
107+
return true
108+
end
109+
110+
function _M.proxy_response(res, chunksize)
111+
if not res then
112+
ngx.log(ngx.ERR, "no response provided")
113+
return
114+
end
115+
116+
ngx.status = res.status
117+
118+
for k, v in pairs(res.headers) do
119+
if not HOP_BY_HOP_HEADERS[str_lower(k)] then
120+
ngx_header[k] = v
83121
end
84-
until not chunk
122+
end
85123

86-
return true, nil
124+
return forward_body(res.body_reader, ngx_send_and_flush, chunksize)
87125
end
88126

89127
return _M

0 commit comments

Comments
 (0)