Opened 4 years ago
Last modified 4 years ago
#2213 new defect
The get_handler of ngx_http_variable_t is overwritten by ngx_http_regex_compile if existing
| Reported by: | Owned by: | ||
|---|---|---|---|
| Priority: | major | Milestone: | |
| Component: | nginx-module | Version: | 1.19.x |
| Keywords: | get_handler, ngx_http_variable_t | Cc: | |
| uname -a: | Linux emsp 3.10.0-1127.19.1.el7.x86_64 #1 SMP Tue Aug 25 17:23:54 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux | ||
| nginx -V: |
nginx version: nginx/1.19.3 (emsp-1.0.1)
built by gcc 8.3.1 20191121 (Red Hat 8.3.1-5) (GCC) built with OpenSSL 1.1.1g FIPS 21 Apr 2020 TLS SNI support enabled configure arguments: --builddir=../objs --prefix=/usr/local/nginx --user=nginx --group=nginx --with-pcre --with-http_ssl_module --with-http_stub_status_module --with-http_gzip_static_module --with-compat --add-dynamic-module=../nginx-emsp-module --with-threads --with-debug --with-cc-opt='-O0 -DNGX_BUILD="emsp-1.0.1" -DNGX_API= -Wno-error=unused-function -Wno-missing-field-initializers' |
||
Description
I'm developing a dynamical NGINX module. I added a variable at preconfiguration stage and print its get_handler at postconfiguration stage. Because I added a named capture group in a regex location directive of nginx.conf, i.e. the variable would be set/defined (set access) at configuration stage, I used the NGX_HTTP_VAR_CHANGEABLE flag. However, I found this variable was empty when it's used in a subrequest location block. Then I checked its get_handler at postconfiguration stage and I found it was overrided.
So I think we should check its value of get_handler before setting it.
The key C++ code snippet (some omitted for brevity),
#define NGX_HTTP_VAR_sp_resid "sp_resid"
static const ngx_str_t sp_resid_name = ngx_string(NGX_HTTP_VAR_sp_resid)
#define NGX_HTTP_EM_VAR_NAME(name) const_cast<ngx_str_t*>(&::name##_name)
/* The module context. */
static ngx_http_module_t ngx_http_em_module_ctx = {
ngx_http_em_preconfiguration, /* preconfiguration */
ngx_http_em_postconfiguration, /* postconfiguration */
ngx_http_em_create_main_conf, /* create main configuration */
ngx_http_em_init_main_conf, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_em_create_loc_conf, /* create location-specific configuration */
ngx_http_em_merge_loc_conf, /* merge location configuration */
};
static ngx_int_t ngx_http_sp_resid_variable(ngx_http_request_t *req, ngx_http_variable_value_t *vv, uintptr_t data) {
const auto ctx = ngx_http_em_get_module_ctx(req->main);
logdf("ctx@%p, req=%.*s?%.*s, spResId=%.*s", ctx, ARGS_NGX_STR(req->uri), ARGS_NGX_STR(req->args), ARGS_NGX_STR(ctx->spResId));
if (ctx == NULL) {
vv->not_found = 1;
return NGX_OK;
}
vv->valid = 1;
vv->no_cacheable = 0;
vv->not_found = 0;
vv->data = ctx->spResId.data;
vv->len = ctx->spResId.len;
return NGX_OK;
}
ngx_int_t ngx_http_em_preconfiguration(ngx_conf_t *cf) {
ngx_http_variable_t *var;
var = ngx_http_add_variable(cf, NGX_HTTP_EM_VAR_NAME(sp_resid), NGX_HTTP_VAR_CHANGEABLE | NGX_HTTP_VAR_NOCACHEABLE);
if (var == NULL) {
return NGX_ERROR;
}
var->get_handler = ngx_http_sp_resid_variable;
return NGX_OK;
}
ngx_int_t ngx_http_em_postconfiguration(ngx_conf_t *cf) {
// Restore v->get_handler = ngx_http_variable_not_found set
// by ngx_http_regex_t *ngx_http_regex_compile(ngx_conf_t *cf, ngx_regex_compile_t *rc)
// in nginx\nginx\src\http\ngx_http_variables.c
auto vars_keys = cmcf->variables_keys->keys;
auto keys = static_cast<ngx_hash_key_t*>(vars_keys.elts);
for (auto idx = vars_keys.nelts; idx > 0;) {
const auto& var_entry = keys[--idx]; // ngx_strncasecmp
if (sizeof(NGX_HTTP_VAR_sp_resid) - 1 == var_entry.key.len &&
0 == ngx_strncasecmp(PUChar(NGX_HTTP_VAR_sp_resid), var_entry.key.data, sizeof(NGX_HTTP_VAR_sp_resid) - 1)) {
const auto var = static_cast<ngx_http_variable_t*>(var_entry.value);
logdf("var->get_handler=%p, ngx_http_sp_resid_variable=%p", var->get_handler, ngx_http_sp_resid_variable);
var->get_handler = ngx_http_sp_resid_variable; // workaround
}
}
}
nginx.conf
location / {
proxy_pass $scheme://$host;
}
location ~ "^/_api/(?<sp_resid>[[:xdigit:]]{8}(?:-[[:xdigit:]]{4}){3}-[[:xdigit:]]{12})/driveItem$" {
proxy_pass $scheme://$host;
}
location = /GetList {
internal;
subrequest_output_buffer_size 128k;
proxy_set_header Content-Length "";
proxy_set_header Accept-Encoding "";
proxy_set_header Accept "application/json;odata=nometadata";
proxy_pass $scheme://$host/_api/web/GetList(@a1)?@a1='$sp_resid'&%24expand=RootFolder;
}
