Skip to content

Commit daf26ee

Browse files
committed
feature: add the balancer.recreate_request function, which allows user
to recreate request buffer in balancer phase. This allows certain request parameters, such as headers (including `Host` header) to be modified between balancer retries.
1 parent 0085a9b commit daf26ee

File tree

3 files changed

+97
-1
lines changed

3 files changed

+97
-1
lines changed

lib/ngx/balancer.lua

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ if subsystem == 'http' then
3838
int ngx_http_lua_ffi_balancer_set_timeouts(ngx_http_request_t *r,
3939
long connect_timeout, long send_timeout,
4040
long read_timeout, char **err);
41+
42+
int
43+
ngx_http_lua_ffi_balancer_recreate_request(ngx_http_request_t *r,
44+
char **err);
4145
]]
4246

4347
ngx_lua_ffi_balancer_set_current_peer =
@@ -207,4 +211,25 @@ function _M.set_timeouts(connect_timeout, send_timeout, read_timeout)
207211
end
208212

209213

214+
if subsystem == 'http' then
215+
function _M.recreate_request()
216+
local r = get_request()
217+
if not r then
218+
error("no request found")
219+
end
220+
221+
local rc = C.ngx_http_lua_ffi_balancer_recreate_request(r, errmsg)
222+
if rc == FFI_OK then
223+
return true
224+
end
225+
226+
if errmsg[0] ~= nil then
227+
return nil, ffi_str(errmsg[0])
228+
end
229+
230+
return nil, "failed to recreate the upstream request"
231+
end
232+
end
233+
234+
210235
return _M

lib/ngx/balancer.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,29 @@ This function was first added in the `0.1.7` version of this library.
237237

238238
[Back to TOC](#table-of-contents)
239239

240+
recreate_request
241+
------------
242+
**syntax:** `ok, err = balancer.recreate_request()`
243+
244+
**context:** *balancer_by_lua**
245+
246+
Regenerates the request buffer for sending to the upstream server. This is useful, for example
247+
if you want to change a request header field to the new upstream server on balancer retries.
248+
249+
Normally this does not work because the request buffer is created once during upstream module
250+
initialization and won't be regenerated for subsequent retries. However you can use
251+
`proxy_set_header My-Header $my_header` and sets the `ngx.var.my_header` variable inside the
252+
balancer phase. Calling this function after that will cause the request buffer to be re-generated
253+
and the `My-Header` header will thus contain the new value.
254+
255+
**Warning:** because the request buffer has to be recreated and such allocation occurs on the
256+
request memory pool, the old buffer has to be thrown away and only be freed after the request
257+
finishes. Do not call this function too often or memory leaks may be noticeable. Even so, a call
258+
to this function should be made **only** if you know the request buffer must be regenerated,
259+
instead of unconditionally in each balancer retries.
260+
261+
This function was first added in the `0.1.19` version of this library.
262+
240263
Community
241264
=========
242265

t/balancer.t

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use t::TestCore;
99

1010
repeat_each(2);
1111

12-
plan tests => repeat_each() * (blocks() * 4 + 5);
12+
plan tests => repeat_each() * (blocks() * 4 + 6);
1313

1414
$ENV{TEST_NGINX_LUA_PACKAGE_PATH} = "$t::TestCore::lua_package_path";
1515

@@ -834,3 +834,51 @@ GET /t
834834
[lua] log_by_lua(nginx.conf:59):2: ngx.var.upstream_addr is 127.0.0.3:12345, 127.0.0.3:12346
835835
--- no_error_log
836836
[alert]
837+
838+
839+
840+
=== TEST 19: recreate upstream module requests with header change
841+
--- http_config
842+
lua_package_path "$TEST_NGINX_LUA_PACKAGE_PATH";
843+
844+
upstream backend {
845+
server 0.0.0.1;
846+
847+
balancer_by_lua_block {
848+
print("here")
849+
local b = require "ngx.balancer"
850+
851+
if ngx.ctx.balancer_ran then
852+
assert(b.set_current_peer("127.0.0.1", tonumber(ngx.var.server_port)))
853+
ngx.var.test = "second"
854+
assert(b.recreate_request())
855+
856+
else
857+
ngx.ctx.balancer_ran = true
858+
assert(b.set_current_peer("127.0.0.3", 12345))
859+
assert(b.set_more_tries(1))
860+
end
861+
}
862+
}
863+
--- config
864+
location = /t {
865+
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504 http_403 http_404;
866+
proxy_next_upstream_tries 2;
867+
868+
set $test "first";
869+
870+
proxy_set_header X-Test $test;
871+
proxy_pass http://backend/upstream;
872+
}
873+
874+
location = /upstream {
875+
return 200 "value is: $http_x_test";
876+
}
877+
--- request
878+
GET /t
879+
--- response_body: value is: second
880+
--- error_log
881+
connect() failed (111: Connection refused) while connecting to upstream, client: 127.0.0.1
882+
--- no_error_log
883+
[warn]
884+
[crit]

0 commit comments

Comments
 (0)