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

feature: support ngx.location.capture and ngx.location.capture_multi with headers option #2328

Merged
merged 2 commits into from
Jul 7, 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
30 changes: 29 additions & 1 deletion README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -966,7 +966,6 @@ TODO
* cosocket: add support in the context of [init_by_lua*](#init_by_lua).
* cosocket: review and merge aviramc's [patch](https://github.com/openresty/lua-nginx-module/pull/290) for adding the `bsdrecv` method.
* cosocket: add configure options for different strategies of handling the cosocket connection exceeding in the pools.
* review and apply vadim-pavlov's patch for [ngx.location.capture](#ngxlocationcapture)'s `extra_headers` option
* use `ngx_hash_t` to optimize the built-in header look-up process for [ngx.req.set_header](#ngxreqset_header), and etc.
* add `ignore_resp_headers`, `ignore_resp_body`, and `ignore_resp` options to [ngx.location.capture](#ngxlocationcapture) and [ngx.location.capture_multi](#ngxlocationcapture_multi) methods, to allow micro performance tuning on the user side.
* add automatic Lua code time slicing support by yielding and resuming the Lua VM actively via Lua's debug hooks.
Expand Down Expand Up @@ -4305,6 +4304,8 @@ argument, which supports the options:
specify the subrequest's request body (string value only).
* `args`
specify the subrequest's URI query arguments (both string value and Lua tables are accepted)
* `headers`
specify the subrequest's request headers (Lua table only). this headers will override the original headers of the subrequest.
* `ctx`
specify a Lua table to be the [ngx.ctx](#ngxctx) table for the subrequest. It can be the current request's [ngx.ctx](#ngxctx) table, which effectively makes the parent and its subrequest to share exactly the same context table. This option was first introduced in the `v0.3.1rc25` release.
* `vars`
Expand Down Expand Up @@ -4456,6 +4457,33 @@ Accessing `/lua` will yield the output
dog = hello
cat = 32

The `headers` option can be used to specify the request headers for the subrequest. The value of this option should be a Lua table where the keys are the header names and the values are the header values. For example,

```lua

location /foo {
content_by_lua_block {
ngx.print(ngx.var.http_x_test)
}
}

location /lua {
content_by_lua_block {
local res = ngx.location.capture("/foo", {
headers = {
["X-Test"] = "aa",
}
})
ngx.print(res.body)
}
}
```

Accessing `/lua` will yield the output


aa


The `ctx` option can be used to specify a custom Lua table to serve as the [ngx.ctx](#ngxctx) table for the subrequest.

Expand Down
66 changes: 57 additions & 9 deletions src/ngx_http_lua_subrequest.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ static ngx_str_t ngx_http_lua_content_length_header_key =
static ngx_int_t ngx_http_lua_adjust_subrequest(ngx_http_request_t *sr,
ngx_uint_t method, int forward_body,
ngx_http_request_body_t *body, unsigned vars_action,
ngx_array_t *extra_vars);
ngx_array_t *extra_vars, ngx_array_t *extra_headers);
static int ngx_http_lua_ngx_location_capture(lua_State *L);
static int ngx_http_lua_ngx_location_capture_multi(lua_State *L);
static void ngx_http_lua_process_vars_option(ngx_http_request_t *r,
static void ngx_http_lua_process_keyval_option(ngx_http_request_t *r,
lua_State *L, int table, ngx_array_t **varsp);
static ngx_int_t ngx_http_lua_subrequest_add_extra_vars(ngx_http_request_t *r,
ngx_array_t *extra_vars);
Expand All @@ -79,7 +79,7 @@ static void ngx_http_lua_cancel_subreq(ngx_http_request_t *r);
static ngx_int_t ngx_http_post_request_to_head(ngx_http_request_t *r);
static ngx_int_t ngx_http_lua_copy_in_file_request_body(ngx_http_request_t *r);
static ngx_int_t ngx_http_lua_copy_request_headers(ngx_http_request_t *sr,
ngx_http_request_t *pr, int pr_not_chunked);
ngx_http_request_t *pr, int pr_not_chunked, ngx_array_t *extra_headers);


enum {
Expand Down Expand Up @@ -127,6 +127,7 @@ ngx_http_lua_ngx_location_capture_multi(lua_State *L)
ngx_http_lua_ctx_t *sr_ctx;
ngx_http_lua_ctx_t *ctx;
ngx_array_t *extra_vars;
ngx_array_t *extra_headers;
ngx_str_t uri;
ngx_str_t args;
ngx_str_t extra_args;
Expand Down Expand Up @@ -224,6 +225,7 @@ ngx_http_lua_ngx_location_capture_multi(lua_State *L)
coctx->pending_subreqs = 0;

extra_vars = NULL;
extra_headers = NULL;

for (index = 0; index < nsubreqs; index++) {
coctx->pending_subreqs++;
Expand Down Expand Up @@ -263,6 +265,11 @@ ngx_http_lua_ngx_location_capture_multi(lua_State *L)
extra_vars->nelts = 0;
}

if (extra_headers != NULL) {
/* flush out existing elements in the array */
extra_headers->nelts = 0;
}

vars_action = 0;

custom_ctx = 0;
Expand Down Expand Up @@ -318,7 +325,7 @@ ngx_http_lua_ngx_location_capture_multi(lua_State *L)

switch (lua_type(L, -1)) {
case LUA_TTABLE:
ngx_http_lua_process_vars_option(r, L, -1, &extra_vars);
ngx_http_lua_process_keyval_option(r, L, -1, &extra_vars);

dd("post process vars top: %d", lua_gettop(L));
break;
Expand All @@ -335,6 +342,29 @@ ngx_http_lua_ngx_location_capture_multi(lua_State *L)

dd("queries query uri opts: %d", lua_gettop(L));

/* check the headers option */

lua_getfield(L, 4, "headers");

switch (lua_type(L, -1)) {
case LUA_TTABLE:
ngx_http_lua_process_keyval_option(r, L, -1, &extra_headers);

dd("post process vars top: %d", lua_gettop(L));
break;

case LUA_TNIL:
/* do nothing */
break;

default:
return luaL_error(L, "Bad headers option value");
}

lua_pop(L, 1); /* pop the headers */

dd("queries query uri opts: %d", lua_gettop(L));

/* check the share_all_vars option */

lua_getfield(L, 4, "share_all_vars");
Expand Down Expand Up @@ -595,7 +625,8 @@ ngx_http_lua_ngx_location_capture_multi(lua_State *L)
ngx_http_set_ctx(sr, sr_ctx, ngx_http_lua_module);

rc = ngx_http_lua_adjust_subrequest(sr, method, always_forward_body,
body, vars_action, extra_vars);
body, vars_action, extra_vars,
extra_headers);

if (rc != NGX_OK) {
ngx_http_lua_cancel_subreq(sr);
Expand Down Expand Up @@ -631,7 +662,7 @@ ngx_http_lua_ngx_location_capture_multi(lua_State *L)
static ngx_int_t
ngx_http_lua_adjust_subrequest(ngx_http_request_t *sr, ngx_uint_t method,
int always_forward_body, ngx_http_request_body_t *body,
unsigned vars_action, ngx_array_t *extra_vars)
unsigned vars_action, ngx_array_t *extra_vars, ngx_array_t *extra_headers)
{
ngx_http_request_t *r;
ngx_http_core_main_conf_t *cmcf;
Expand Down Expand Up @@ -667,7 +698,9 @@ ngx_http_lua_adjust_subrequest(ngx_http_request_t *sr, ngx_uint_t method,
}
}

if (ngx_http_lua_copy_request_headers(sr, r, pr_not_chunked) != NGX_OK) {
if (ngx_http_lua_copy_request_headers(sr, r, pr_not_chunked, extra_headers)
!= NGX_OK)
{
return NGX_ERROR;
}

Expand Down Expand Up @@ -882,7 +915,7 @@ ngx_http_lua_subrequest_add_extra_vars(ngx_http_request_t *sr,


static void
ngx_http_lua_process_vars_option(ngx_http_request_t *r, lua_State *L,
ngx_http_lua_process_keyval_option(ngx_http_request_t *r, lua_State *L,
int table, ngx_array_t **varsp)
{
ngx_array_t *vars;
Expand Down Expand Up @@ -1635,10 +1668,11 @@ ngx_http_lua_copy_in_file_request_body(ngx_http_request_t *r)

static ngx_int_t
ngx_http_lua_copy_request_headers(ngx_http_request_t *sr,
ngx_http_request_t *pr, int pr_not_chunked)
ngx_http_request_t *pr, int pr_not_chunked, ngx_array_t *extra_headers)
{
ngx_table_elt_t *clh, *header;
ngx_list_part_t *part;
ngx_keyval_t *header_keyval;
ngx_chain_t *in;
ngx_uint_t i;
u_char *p;
Expand Down Expand Up @@ -1742,6 +1776,20 @@ ngx_http_lua_copy_request_headers(ngx_http_request_t *sr,
}
}

if (extra_headers && extra_headers->nelts > 0) {

header_keyval = extra_headers->elts;

for (i = 0; i < extra_headers->nelts; i++, header_keyval++) {

if (ngx_http_lua_set_input_header(sr, header_keyval->key,
header_keyval->value, 1) == NGX_ERROR)
{
return NGX_ERROR;
}
}
}

dd("after: parent req headers count: %d",
(int) pr->headers_in.headers.part.nelts);

Expand Down
72 changes: 72 additions & 0 deletions t/027-multi-capture.t
Original file line number Diff line number Diff line change
Expand Up @@ -752,3 +752,75 @@ proxy_cache_path conf/cache levels=1:2 keys_zone=STATIC:10m inactive=10m max_siz
GET /foo
--- response_body
ok



=== TEST 14: capture multi with headers
--- config
location /foo {
content_by_lua_block {
local res1, res2, res3 = ngx.location.capture_multi{
{"/test", { headers = { ["X-Test-Header"] = "aa"} } },
{"/test", { headers = { ["X-Test-Header"] = "bb"} } },
{"/test"},
}
ngx.say("res1.status = " .. res1.status)
ngx.say("res1.body = " .. res1.body)
ngx.say("res2.status = " .. res2.status)
ngx.say("res2.body = " .. res2.body)
ngx.say("res3.status = " .. res3.status)
ngx.say("res3.body = " .. res3.body)
}
}

location = /test {
content_by_lua_block {
ngx.print(ngx.var.http_x_test_header)
}
}
--- request
GET /foo
--- response_body
res1.status = 200
res1.body = aa
res2.status = 200
res2.body = bb
res3.status = 200
res3.body = nil



=== TEST 15: capture multi with headers override
--- config
location /foo {
content_by_lua_block {
local res1, res2, res3 = ngx.location.capture_multi{
{"/test", { headers = { ["X-Test-Header"] = "aa"} } },
{"/test", { headers = { ["X-Test-Header"] = "bb"} } },
{"/test"},
}
ngx.say("res1.status = " .. res1.status)
ngx.say("res1.body = " .. res1.body)
ngx.say("res2.status = " .. res2.status)
ngx.say("res2.body = " .. res2.body)
ngx.say("res3.status = " .. res3.status)
ngx.say("res3.body = " .. res3.body)
}
}

location = /test {
content_by_lua_block {
ngx.print(ngx.var.http_x_test_header)
}
}
--- request
GET /foo
--- more_headers
X-Test-Header: cc
--- response_body
res1.status = 200
res1.body = aa
res2.status = 200
res2.body = bb
res3.status = 200
res3.body = cc
Loading