Skip to content
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ Description

This module provides array typed nginx variables to `nginx.conf`.

Under the hood, this module just "abuses" the nginx string values to hold binary pointers
to C data structures (NGINX core's `ngx_array_t` struct on the C land).
Under the hood, this module uses nginx string values to hold tagged references
to NGINX core's `ngx_array_t` structures.

The array type gives `nginx.onf` wonderful capabilities of handling value lists. Nowadays, however,
you are highly recommended to use the [ngx_lua](https://github.com/openresty/lua-nginx-module) module
Expand Down
101 changes: 73 additions & 28 deletions src/ngx_http_array_var_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@
static ngx_str_t ngx_http_array_it_key = ngx_string("array_it");


typedef struct {
ngx_module_t *tag;
ngx_array_t *array;
} ngx_http_array_var_ref_t;


typedef struct {
ngx_uint_t nargs;
} ngx_http_array_split_data_t;
Expand Down Expand Up @@ -128,6 +134,55 @@ ngx_module_t ngx_http_array_var_module = {
};


static ngx_array_t *
ngx_http_array_var_create(ngx_http_request_t *r, ngx_uint_t n, ngx_str_t *res)
{
ngx_array_t *array;
ngx_http_array_var_ref_t *a_ref;

if (n > NGX_MAX_SIZE_T_VALUE / sizeof(ngx_str_t)) {
return NULL;
}

array = ngx_array_create(r->pool, n, sizeof(ngx_str_t));
if (array == NULL) {
return NULL;
}

a_ref = ngx_palloc(r->pool, sizeof(ngx_http_array_var_ref_t));
if (a_ref == NULL) {
return NULL;
}

a_ref->tag = &ngx_http_array_var_module;
a_ref->array = array;
res->data = (u_char *) a_ref;
res->len = sizeof(ngx_http_array_var_ref_t);

return array;
}


static ngx_array_t *
ngx_http_array_var_get(ngx_http_variable_value_t *v)
{
ngx_http_array_var_ref_t a_ref;

if (v->len != sizeof(ngx_http_array_var_ref_t)) {
return NULL;
}

/* NDK copies values into buffers that need not be aligned. */
ngx_memcpy(&a_ref, v->data, sizeof(ngx_http_array_var_ref_t));

if (a_ref.tag != &ngx_http_array_var_module) {
return NULL;
}

return a_ref.array;
}


static char *
ngx_http_array_split(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
Expand Down Expand Up @@ -432,7 +487,7 @@ ngx_http_array_var_split(ngx_http_request_t *r, ngx_str_t *res,
max = ngx_atosz(v[2].data, v[2].len);
if (max == NGX_ERROR) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"array_split: invalid max items: \"%V\"",
"array_split: invalid max items: \"%v\"",
&v[2]);

return NGX_ERROR;
Expand All @@ -445,7 +500,7 @@ ngx_http_array_var_split(ngx_http_request_t *r, ngx_str_t *res,
len = max;
}

array = ngx_array_create(r->pool, len, sizeof(ngx_str_t));
array = ngx_http_array_var_create(r, len, res);
if (array == NULL) {
return NGX_ERROR;
}
Expand Down Expand Up @@ -515,9 +570,6 @@ ngx_http_array_var_split(ngx_http_request_t *r, ngx_str_t *res,
dd("split: array size: %d", (int) array->nelts);
dd("split array ptr: %p", array);

res->data = (u_char *) array;
res->len = sizeof(ngx_array_t);

return NGX_OK;
}

Expand All @@ -540,16 +592,15 @@ ngx_http_array_var_map(ngx_http_request_t *r, ngx_str_t *res,
return NGX_OK;
}

if (v[0].len != sizeof(ngx_array_t)) {
array = ngx_http_array_var_get(&v[0]);
if (array == NULL) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"array_join: invalid array variable value in the 2nd "
"argument: \"%.*s\"", &v[0]);
"array_map: invalid array variable value in the 2nd "
"argument: \"%v\"", &v[0]);

return NGX_ERROR;
}

array = (ngx_array_t *) v[0].data;

value = array->elts;

array_it = ngx_http_get_indexed_variable(r, conf->array_it_index);
Expand All @@ -559,10 +610,11 @@ ngx_http_array_var_map(ngx_http_request_t *r, ngx_str_t *res,

if (conf->in_place) {
new_array = array;
res->data = v[0].data;
res->len = v[0].len;

} else {
new_array = ngx_array_create(r->pool, array->nelts,
sizeof(ngx_str_t));
new_array = ngx_http_array_var_create(r, array->nelts, res);
if (new_array == NULL) {
return NGX_ERROR;
}
Expand Down Expand Up @@ -600,9 +652,6 @@ ngx_http_array_var_map(ngx_http_request_t *r, ngx_str_t *res,
array_it->not_found = 1;
array_it->valid = 0;

res->data = (u_char *) new_array;
res->len = sizeof(ngx_array_t);

return NGX_OK;
}

Expand Down Expand Up @@ -632,24 +681,24 @@ ngx_http_array_var_map_op(ngx_http_request_t *r,
return NGX_ERROR;
}

if (v[1].len != sizeof(ngx_array_t)) {
array = ngx_http_array_var_get(&v[1]);
if (array == NULL) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"array_map_op: invalid array variable value in the 2nd "
"argument: \"%.*s\"", &v[0]);
"argument: \"%v\"", &v[1]);

return NGX_ERROR;
}

array = (ngx_array_t *) v[1].data;

value = array->elts;

if (conf->in_place) {
new_array = array;
res->data = v[1].data;
res->len = v[1].len;

} else {
new_array = ngx_array_create(r->pool, array->nelts,
sizeof(ngx_str_t));
new_array = ngx_http_array_var_create(r, array->nelts, res);
if (new_array == NULL) {
return NGX_ERROR;
}
Expand Down Expand Up @@ -677,9 +726,6 @@ ngx_http_array_var_map_op(ngx_http_request_t *r,
}
}

res->data = (u_char *) new_array;
res->len = sizeof(ngx_array_t);

return NGX_OK;
}

Expand All @@ -699,16 +745,15 @@ ngx_http_array_var_join(ngx_http_request_t *r,

dd("sep %.*s", sep->len, sep->data);

if (v[1].len != sizeof(ngx_array_t)) {
array = ngx_http_array_var_get(&v[1]);
if (array == NULL) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"array_join: invalid array variable value in the "
"2nd argument: \"%V\"", &v[1]);
"2nd argument: \"%v\"", &v[1]);

return NGX_ERROR;
}

array = (ngx_array_t *) v[1].data;

dd("join array ptr %p", array);
dd("array->nelts: %d", (int) array->nelts);

Expand Down
169 changes: 169 additions & 0 deletions t/invalid-array.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
# vi:filetype=

use lib 'lib';
use Config ();
use Test::Nginx::Socket;

repeat_each(1);

plan tests => repeat_each() * 3 * blocks();

no_long_string();

run_tests();

__DATA__

=== TEST 1: array map reports the scalar value after a successful array operation
--- config
location /foo {
error_page 500 = @invalid_array;
array_split ',' 'a,b' to=$array;
set $scalar 'invalid';
array_map '[$array_it]' $scalar;
echo 'unexpected success';
}

location @invalid_array {
return 500 "invalid array\n";
}
--- request
GET /foo
--- error_code: 500
--- response_body
invalid array
--- grep_error_log eval
qr/array_map: invalid array variable value in the 2nd argument: "[^"]*"/
--- grep_error_log_out
array_map: invalid array variable value in the 2nd argument: "invalid"



=== TEST 2: array map op reports the scalar value rather than the directive name
--- config
location /foo {
error_page 500 = @invalid_array;
array_split ',' 'a,b' to=$array;
set $scalar 'invalid';
array_map_op set_quote_sql_str $scalar;
echo 'unexpected success';
}

location @invalid_array {
return 500 "invalid array\n";
}
--- request
GET /foo
--- error_code: 500
--- response_body
invalid array
--- grep_error_log eval
qr/array_map_op: invalid array variable value in the 2nd argument: "[^"]*"/
--- grep_error_log_out
array_map_op: invalid array variable value in the 2nd argument: "invalid"



=== TEST 3: array join reports the scalar value
--- config
location /foo {
error_page 500 = @invalid_array;
array_split ',' 'a,b' to=$array;
set $scalar 'invalid';
array_join ',' $scalar;
echo 'unexpected success';
}

location @invalid_array {
return 500 "invalid array\n";
}
--- request
GET /foo
--- error_code: 500
--- response_body
invalid array
--- grep_error_log eval
qr/array_join: invalid array variable value in the 2nd argument: "[^"]*"/
--- grep_error_log_out
array_join: invalid array variable value in the 2nd argument: "invalid"



=== TEST 4: array join rejects a scalar the same size as an array reference
--- config eval
my $scalar = 'a' x (2 * $Config::Config{ptrsize});
sprintf q{
location /foo {
error_page 500 = @invalid_array;
set $scalar '%s';
array_join ',' $scalar;
echo 'unexpected success';
}

location @invalid_array {
return 500 "invalid array\n";
}
}, $scalar;
--- request
GET /foo
--- error_code: 500
--- response_body
invalid array
--- error_log
array_join: invalid array variable value in the 2nd argument:



=== TEST 5: array map op rejects an array reference with a null tag
--- config eval
my $scalar = '0' x (4 * $Config::Config{ptrsize});
sprintf q{
location /foo {
error_page 500 = @invalid_array;
array_split ',' 'a,b' to=$array;
set_decode_hex $scalar '%s';
array_map_op set_quote_sql_str $scalar;
echo 'unexpected success';
}

location @invalid_array {
return 500 "invalid array\n";
}
}, $scalar;
--- request
GET /foo
--- error_code: 500
--- response_body
invalid array
--- error_log
array_map_op: invalid array variable value in the 2nd argument:



=== TEST 6: array map rejects an invalid tag with a valid array pointer
--- config eval
my $digits = 2 * $Config::Config{ptrsize};
sprintf q{
location /foo {
error_page 500 = @invalid_array;
array_split ',' 'a,b' to=$array;
set_encode_hex $encoded $array;
if ($encoded ~ "^.{%d}(.{%d})$") {
set $bad_hex "%s$1";
}
set_decode_hex $scalar $bad_hex;
array_map '[$array_it]' $scalar;
echo 'unexpected success';
}

location @invalid_array {
return 500 "invalid array\n";
}
}, $digits, $digits, '0' x $digits;
--- request
GET /foo
--- error_code: 500
--- response_body
invalid array
--- error_log
array_map: invalid array variable value in the 2nd argument:
Loading
Loading