diff --git a/README.markdown b/README.markdown index 615ce82..3f95547 100644 --- a/README.markdown +++ b/README.markdown @@ -74,6 +74,9 @@ Synopsis # replace input header X-Foo *only* if it already exists more_set_input_headers -r 'X-Foo: howdy'; + + # add input header X-Foo *only* if it does not exist + more_set_input_headers -a 'X-Foo: howdy'; ``` Description @@ -134,7 +137,7 @@ Directives more_set_headers ---------------- -**syntax:** *more_set_headers [-t <content-type list>]... [-s <status-code list>]... <new-header>...* +**syntax:** *more_set_headers [-a] [-t <content-type list>]... [-s <status-code list>]... <new-header>...* **default:** *no* @@ -153,6 +156,8 @@ If either `-s` or `-t` is not specified or has an empty list value, then no matc Existing response headers with the same name are always overridden. If you want to add headers incrementally, use the standard [add_header](http://nginx.org/en/docs/http/ngx_http_headers_module.html#add_header) directive instead. +If the `-a` option is specified, then the headers will be added *only if* they do not exist. + A single directive can set/add multiple output headers. For example ```nginx @@ -261,7 +266,7 @@ The `*` wildcard support was first introduced in [v0.09](#v009). more_set_input_headers ---------------------- -**syntax:** *more_set_input_headers [-r] [-t <content-type list>]... <new-header>...* +**syntax:** *more_set_input_headers [-r] [-a] [-t <content-type list>]... <new-header>...* **default:** *no* @@ -280,6 +285,8 @@ and works in subrequests as well. If the `-r` option is specified, then the headers will be replaced to the new values *only if* they already exist. +If the `-a` options is specified, then the headers will be added *only if* they do not exist. + [Back to TOC](#table-of-contents) more_clear_input_headers diff --git a/src/ngx_http_headers_more_filter_module.h b/src/ngx_http_headers_more_filter_module.h index 72a5317..abbdc9e 100644 --- a/src/ngx_http_headers_more_filter_module.h +++ b/src/ngx_http_headers_more_filter_module.h @@ -61,6 +61,7 @@ struct ngx_http_headers_more_header_val_s { ngx_http_headers_more_set_header_pt handler; ngx_uint_t offset; ngx_flag_t replace; + ngx_flag_t add_only; ngx_flag_t wildcard; }; diff --git a/src/ngx_http_headers_more_headers_in.c b/src/ngx_http_headers_more_headers_in.c index f903908..0b15422 100644 --- a/src/ngx_http_headers_more_headers_in.c +++ b/src/ngx_http_headers_more_headers_in.c @@ -247,33 +247,34 @@ ngx_http_set_header_helper(ngx_http_request_t *r, && ngx_strncasecmp(h[i].key.data, hv->key.data, h[i].key.len) == 0) { - if (value->len == 0 || (matched && matched != &h[i])) { - h[i].hash = 0; + if (!(hv->add_only && !hv->replace)) { + if (value->len == 0 || (matched && matched != &h[i])) { + h[i].hash = 0; - rc = ngx_http_headers_more_rm_header_helper( + rc = ngx_http_headers_more_rm_header_helper( &r->headers_in.headers, part, i); - ngx_http_headers_more_assert( - !(r->headers_in.headers.part.next == NULL - && r->headers_in.headers.last - != &r->headers_in.headers.part)); + ngx_http_headers_more_assert( + !(r->headers_in.headers.part.next == NULL + && r->headers_in.headers.last + != &r->headers_in.headers.part)); - if (rc == NGX_OK) { - if (output_header) { - *output_header = NULL; + if (rc == NGX_OK) { + if (output_header) { + *output_header = NULL; + } + + goto retry; } - goto retry; + return NGX_ERROR; } + h[i].value = *value; - return NGX_ERROR; - } - - h[i].value = *value; - - if (output_header) { - *output_header = &h[i]; - dd("setting existing builtin input header"); + if (output_header) { + *output_header = &h[i]; + dd("setting existing builtin input header"); + } } if (matched == NULL) { @@ -286,7 +287,7 @@ ngx_http_set_header_helper(ngx_http_request_t *r, return NGX_OK; } - if (value->len == 0 || hv->replace) { + if (value->len == 0 || (hv->replace && !hv->add_only)) { return NGX_OK; } @@ -358,6 +359,11 @@ ngx_http_set_builtin_header(ngx_http_request_t *r, return ngx_http_set_header_helper(r, hv, value, old); } + if (hv->add_only) { + dd("skip because %s does set", hv->key.data); + return NGX_OK; + } + h = *old; if (value->len == 0) { @@ -496,17 +502,17 @@ static char * ngx_http_headers_more_parse_directive(ngx_conf_t *cf, ngx_command_t *ngx_cmd, void *conf, ngx_http_headers_more_opcode_t opcode) { + ngx_flag_t replace = 0; + ngx_flag_t add_only = 0; ngx_http_headers_more_loc_conf_t *hlcf = conf; - ngx_uint_t i; - ngx_http_headers_more_cmd_t *cmd; ngx_str_t *arg; - ngx_flag_t ignore_next_arg; ngx_str_t *cmd_name; ngx_int_t rc; - ngx_flag_t replace = 0; + ngx_uint_t i; + ngx_flag_t ignore_next_arg; + ngx_http_headers_more_cmd_t *cmd; ngx_http_headers_more_header_val_t *h; - ngx_http_headers_more_main_conf_t *hmcf; if (hlcf->cmds == NULL) { @@ -595,6 +601,12 @@ ngx_http_headers_more_parse_directive(ngx_conf_t *cf, ngx_command_t *ngx_cmd, replace = 1; continue; } + + if (arg[i].data[1] == 'a') { + dd("Found add only flag"); + add_only = 1; + continue; + } } ngx_log_error(NGX_LOG_ERR, cf->log, 0, @@ -615,6 +627,7 @@ ngx_http_headers_more_parse_directive(ngx_conf_t *cf, ngx_command_t *ngx_cmd, h = cmd->headers->elts; for (i = 0; i < cmd->headers->nelts; i++) { h[i].replace = replace; + h[i].add_only = add_only; } } @@ -741,6 +754,11 @@ ngx_http_set_builtin_multi_header(ngx_http_request_t *r, headers = (ngx_array_t *) ((char *) &r->headers_in + hv->offset); if (headers->nelts > 0) { + if (hv->add_only) { + dd("skip multi-value headers because %s does set", hv->key.data); + return NGX_OK; + } + ngx_array_destroy(headers); if (ngx_array_init(headers, r->pool, 2, diff --git a/src/ngx_http_headers_more_headers_out.c b/src/ngx_http_headers_more_headers_out.c index 0f9bc87..36bc699 100644 --- a/src/ngx_http_headers_more_headers_out.c +++ b/src/ngx_http_headers_more_headers_out.c @@ -224,6 +224,12 @@ ngx_http_set_header_helper(ngx_http_request_t *r, matched: + if (hv->add_only) { + dd("skip because %s does set", hv->key.data); + matched = 1; + continue; + } + if (value->len == 0 || matched) { dd("clearing normal header for %.*s", (int) hv->key.len, hv->key.data); @@ -304,6 +310,11 @@ ngx_http_set_builtin_header(ngx_http_request_t *r, return ngx_http_set_header_helper(r, hv, value, old, 0); } + if (hv->add_only) { + dd("skip because %s does set", hv->key.data); + return NGX_OK; + } + h = *old; if (value->len == 0) { @@ -344,6 +355,11 @@ ngx_http_set_builtin_multi_header(ngx_http_request_t *r, /* override old values (if any) */ if (pa->nelts > 0) { + if (hv->add_only) { + dd("skip because %s does set", hv->key.data); + return NGX_OK; + } + ph = pa->elts; for (i = 1; i < pa->nelts; i++) { ph[i]->hash = 0; @@ -565,16 +581,17 @@ static char * ngx_http_headers_more_parse_directive(ngx_conf_t *cf, ngx_command_t *ngx_cmd, void *conf, ngx_http_headers_more_opcode_t opcode) { + ngx_flag_t add_only = 0; ngx_http_headers_more_loc_conf_t *hlcf = conf; - ngx_uint_t i; - ngx_http_headers_more_cmd_t *cmd; - ngx_str_t *arg; - ngx_flag_t ignore_next_arg; - ngx_str_t *cmd_name; - ngx_int_t rc; - - ngx_http_headers_more_main_conf_t *hmcf; + ngx_str_t *arg; + ngx_str_t *cmd_name; + ngx_int_t rc; + ngx_flag_t ignore_next_arg; + ngx_uint_t i; + ngx_http_headers_more_cmd_t *cmd; + ngx_http_headers_more_main_conf_t *hmcf; + ngx_http_headers_more_header_val_t *h; if (hlcf->cmds == NULL) { hlcf->cmds = ngx_array_create(cf->pool, 1, @@ -680,6 +697,10 @@ ngx_http_headers_more_parse_directive(ngx_conf_t *cf, ngx_command_t *ngx_cmd, ignore_next_arg = 1; continue; + + } else if (arg[i].data[1] == 'a') { + add_only = 1; + continue; } } @@ -695,6 +716,13 @@ ngx_http_headers_more_parse_directive(ngx_conf_t *cf, ngx_command_t *ngx_cmd, if (cmd->headers->nelts == 0) { cmd->headers = NULL; + + } else { + h = cmd->headers->elts; + + for (i = 0; i < cmd->headers->nelts; i++) { + h[i].add_only = add_only; + } } if (cmd->types->nelts == 0) { diff --git a/t/input.t b/t/input.t index 0b8989f..3ac4d26 100644 --- a/t/input.t +++ b/t/input.t @@ -5,7 +5,7 @@ use Test::Nginx::Socket; # 'no_plan'; repeat_each(2); -plan tests => repeat_each() * 124; +plan tests => repeat_each() * 142; no_long_string(); #no_diff; @@ -1289,3 +1289,113 @@ X-Forwarded-For: 8.8.8.8 Foo: 127.0.0.1 --- no_error_log [error] + + + +=== TEST 50: set request header if not set the header with -a option +--- config + location /foo { + more_set_input_headers -a 'X-Foo: howdy'; + echo "input_header: $http_x_foo"; + } +--- request + GET /foo +--- response_body +input_header: howdy + + + +=== TEST 51: do not set request header if set the header with -a option +--- config + location /foo { + more_set_input_headers -a 'X-Foo: howdy'; + content_by_lua ' + local headers = ngx.req.get_headers() + ngx.say(headers["X-Foo"]) + '; + } +--- request + GET /foo +--- more_headers +X-Foo: blah + +--- response_body +blah + + + +=== TEST 52: do not remove multi request header if set the header with -a option +--- config + location /foo { + more_set_input_headers -a 'X-Foo: howdy'; + content_by_lua ' + local headers = ngx.req.get_headers() + ngx.say(headers["AAA"]) + '; + } +--- request + GET /foo +--- more_headers +AAA: blah +AAA: baz + +--- response_body +blahbaz + + + +=== TEST 53: test -a -t work together +--- config + location /foo { + more_set_input_headers -a -t 'text/html' 'X-Foo: howdy'; + content_by_lua ' + local headers = ngx.req.get_headers() + ngx.say(headers["X-Foo"]) + '; + } +--- request + GET /foo +--- more_headers +Content-Type: text/html + +--- response_body +howdy + + + +=== TEST 54: test -a -r work together +--- config + location /foo { + more_set_input_headers -a -r 'X-Foo: howdy'; + content_by_lua ' + local headers = ngx.req.get_headers() + ngx.say(headers["X-Foo"]) + '; + } +--- request eval +["GET /foo", "GET /foo"] +--- more_headers eval +["X-Foo: hi", ""] + +--- response_body eval +["howdy\n", "howdy\n"] + + + +=== TEST 55: test -a -r -t work together +--- config + location /foo { + more_set_input_headers -a -r -t 'text/html' 'X-Foo: howdy'; + content_by_lua ' + local headers = ngx.req.get_headers() + ngx.say(headers["X-Foo"]) + '; + } +--- request eval +["GET /foo", "GET /foo", "GET /foo"] +--- more_headers eval +["Content-Type: text/html", "", "Content-Type: text/html\nX-Foo: hi\n"] + +--- response_body eval +["howdy\n", "nil\n", "howdy\n"] + diff --git a/t/sanity.t b/t/sanity.t index e316cac..ab54b64 100644 --- a/t/sanity.t +++ b/t/sanity.t @@ -5,7 +5,7 @@ use Test::Nginx::Socket; repeat_each(2); -plan tests => repeat_each() * 113; +plan tests => repeat_each() * 124; #master_on(); #workers(2); @@ -566,3 +566,63 @@ hi ok --- http09 + + +=== TEST 34: do not set response header if set the header with -a option +--- config + location /foo { + more_set_headers -a 'X-Foo: bar'; + echo hi; + } +--- request + GET /foo +--- response_headers +X-Foo: bar +--- response_body +hi + + + +=== TEST 35: set the response header if set the header with -a option +--- config + location = /backend { + add_header X-Foo baz; + echo hi; + } + + location /foo { + more_set_headers -a 'X-Foo: bar'; + proxy_pass http://127.0.0.1:$server_port/backend; + } +--- request + GET /foo +--- response_headers +X-Foo: baz +--- response_body +hi + + + +=== TEST 36: test -t -a work together +--- config + location = /backend { + add_header Content-Type text/html; + echo hi; + } + + location /foo { + more_set_headers -t 'text/plain' -a 'X-Foo: bar'; + proxy_pass http://127.0.0.1:$server_port/backend; + } + + location /bar { + more_set_headers -t 'text/html' -a 'X-Foo: bar'; + proxy_pass http://127.0.0.1:$server_port/backend; + } + +--- request eval +["GET /foo", "GET /bar"] +--- response_headers eval +["", "X-Foo: bar"] +--- response_body eval +["hi\n", "hi\n"]