pax_global_header00006660000000000000000000000064145431603120014511gustar00rootroot0000000000000052 comment=06dc0be56e5ec9f7fd814e881b066b5540a85bec headers-more-nginx-module-0.37/000077500000000000000000000000001454316031200164215ustar00rootroot00000000000000headers-more-nginx-module-0.37/.gitattributes000066400000000000000000000000331454316031200213100ustar00rootroot00000000000000*.t linguist-language=Text headers-more-nginx-module-0.37/.gitignore000066400000000000000000000007031454316031200204110ustar00rootroot00000000000000reindex .libs *.swp *.slo *.la *.swo *.lo *~ *.o print.txt .rsync *.tar.gz dist build[78] build tags update-readme *.tmp test/Makefile test/blib test.sh t.sh t/t.sh test/t/servroot/ releng reset *.t_ genmobi.sh *.mobi misc/chunked src/headers.c src/headers.h src/module.c src/module.h src/util.c src/util.h go ctags src/in.c src/in.h src/out.c src/out.h build[89] build1[0-9] buildroot/ work/ all t/servroot analyze cov nginx *.plist a.patch Makefile headers-more-nginx-module-0.37/.travis.yml000066400000000000000000000034421454316031200205350ustar00rootroot00000000000000sudo: required dist: focal branches: only: - "master" os: linux language: c compiler: - gcc addons: apt: packages: - axel - cpanminus env: global: - LUAJIT_PREFIX=/opt/luajit21 - LUAJIT_LIB=$LUAJIT_PREFIX/lib - LUAJIT_INC=$LUAJIT_PREFIX/include/luajit-2.1 - LD_LIBRARY_PATH=$LUAJIT_LIB:$LD_LIBRARY_PATH matrix: - NGINX_VERSION=1.21.4 - NGINX_VERSION=1.25.3 WITHOUT_PCRE2=1 before_install: - sudo apt-get update -y - sudo apt-get install -y ca-certificates - sudo cpanm -v --notest Test::Nginx > build.log 2>&1 || (cat build.log && exit 1) install: - git clone https://github.com/openresty/echo-nginx-module.git ../echo-nginx-module - git clone https://github.com/openresty/lua-nginx-module.git ../lua-nginx-module - git clone https://github.com/openresty/lua-resty-core.git ../lua-resty-core - git clone https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache - git clone https://github.com/openresty/nginx-eval-module.git ../eval-nginx-module - git clone https://github.com/openresty/openresty.git ../openresty - git clone https://github.com/openresty/no-pool-nginx.git ../no-pool-nginx - git clone https://github.com/openresty/nginx-devel-utils.git - git clone -b v2.1-agentzh https://github.com/openresty/luajit2.git luajit2 before_script: - cd luajit2/ - make -j$JOBS CCDEBUG=-g Q= PREFIX=$LUAJIT_PREFIX CC=$CC XCFLAGS='-DLUA_USE_APICHECK -DLUA_USE_ASSERT -msse4.2' > build.log 2>&1 || (cat build.log && exit 1) - sudo make install PREFIX=$LUAJIT_PREFIX > build.log 2>&1 || (cat build.log && exit 1) - cd .. script: - export PATH=$PWD/work/nginx/sbin:$PWD/nginx-devel-utils:$PATH - export NGX_BUILD_CC=$CC - sh util/build.sh $NGINX_VERSION > build.log 2>&1 || (cat build.log && exit 1) - prove -I. -r t headers-more-nginx-module-0.37/README.markdown000066400000000000000000000447761454316031200211440ustar00rootroot00000000000000Name ==== **ngx_headers_more** - Set and clear input and output headers...more than "add"! *This module is not distributed with the Nginx source.* See [the installation instructions](#installation). Table of Contents ================= * [Name](#name) * [Version](#version) * [Synopsis](#synopsis) * [Description](#description) * [Directives](#directives) * [more_set_headers](#more_set_headers) * [more_clear_headers](#more_clear_headers) * [more_set_input_headers](#more_set_input_headers) * [more_clear_input_headers](#more_clear_input_headers) * [Limitations](#limitations) * [Installation](#installation) * [Compatibility](#compatibility) * [Community](#community) * [English Mailing List](#english-mailing-list) * [Chinese Mailing List](#chinese-mailing-list) * [Bugs and Patches](#bugs-and-patches) * [Source Repository](#source-repository) * [Changes](#changes) * [Test Suite](#test-suite) * [TODO](#todo) * [Getting involved](#getting-involved) * [Authors](#authors) * [Copyright & License](#copyright--license) * [See Also](#see-also) Version ======= This document describes headers-more-nginx-module [v0.34](https://github.com/openresty/headers-more-nginx-module/tags) released on 17 July 2022. Synopsis ======== ```nginx # set the Server output header more_set_headers 'Server: my-server'; # set and clear output headers location /bar { more_set_headers 'X-MyHeader: blah' 'X-MyHeader2: foo'; more_set_headers -t 'text/plain text/css' 'Content-Type: text/foo'; more_set_headers -s '400 404 500 503' -s 413 'Foo: Bar'; more_clear_headers 'Content-Type'; # your proxy_pass/memcached_pass/or any other config goes here... } # set output headers location /type { more_set_headers 'Content-Type: text/plain'; # ... } # set input headers location /foo { set $my_host 'my dog'; more_set_input_headers 'Host: $my_host'; more_set_input_headers -t 'text/plain' 'X-Foo: bah'; # now $host and $http_host have their new values... # ... } # replace input header X-Foo *only* if it already exists more_set_input_headers -r 'X-Foo: howdy'; ``` Description =========== This module allows you to add, set, or clear any output or input header that you specify. This is an enhanced version of the standard [headers](http://nginx.org/en/docs/http/ngx_http_headers_module.html) module because it provides more utilities like resetting or clearing "builtin headers" like `Content-Type`, `Content-Length`, and `Server`. It also allows you to specify an optional HTTP status code criteria using the `-s` option and an optional content type criteria using the `-t` option while modifying the output headers with the [more_set_headers](#more_set_headers) and [more_clear_headers](#more_clear_headers) directives. For example, ```nginx more_set_headers -s 404 -t 'text/html' 'X-Foo: Bar'; ``` You can also specify multiple MIME types to filter out in a single `-t` option. For example, ```nginx more_set_headers -t 'text/html text/plain' 'X-Foo: Bar'; ``` Never use other parameters like `charset=utf-8` in the `-t` option values; they will not work as you would expect. Input headers can be modified as well. For example ```nginx location /foo { more_set_input_headers 'Host: foo' 'User-Agent: faked'; # now $host, $http_host, $user_agent, and # $http_user_agent all have their new values. } ``` The option `-t` is also available in the [more_set_input_headers](#more_set_input_headers) and [more_clear_input_headers](#more_clear_input_headers) directives (for request header filtering) while the `-s` option is not allowed. Unlike the standard [headers](http://nginx.org/en/docs/http/ngx_http_headers_module.html) module, this module's directives will by default apply to all the status codes, including `4xx` and `5xx`. [Back to TOC](#table-of-contents) Directives ========== [Back to TOC](#table-of-contents) more_set_headers ---------------- **syntax:** *more_set_headers [-t <content-type list>]... [-s <status-code list>]... [-a] <new-header>...* **default:** *no* **context:** *http, server, location, location if* **phase:** *output-header-filter* Replaces (if any) or adds (if not any) the specified output headers when the response status code matches the codes specified by the `-s` option *AND* the response content type matches the types specified by the `-t` option. If the "-a" option is specified, the specified output headers can be appended directly without clearing the old fields. The behavior of builtin headers such as "Content-Type", "Content-Length", "Server", etc. cannot be changed. If either `-s` or `-t` is not specified or has an empty list value, then no match is required. Therefore, the following directive set the `Server` output header to the custom value for *any* status code and *any* content type: ```nginx more_set_headers "Server: my_server"; ``` 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. A single directive can set/add multiple output headers. For example ```nginx more_set_headers 'Foo: bar' 'Baz: bah'; ``` Multiple occurrences of the options are allowed in a single directive. Their values will be merged together. For instance ```nginx more_set_headers -s 404 -s '500 503' 'Foo: bar'; ``` is equivalent to ```nginx more_set_headers -s '404 500 503' 'Foo: bar'; ``` The new header should be the one of the forms: 1. `Name: Value` 1. `Name: ` 1. `Name` The last two effectively clear the value of the header `Name`. Nginx variables are allowed in header values. For example: ```nginx set $my_var "dog"; more_set_headers "Server: $my_var"; ``` But variables won't work in header keys due to performance considerations. Multiple set/clear header directives are allowed in a single location, and they're executed sequentially. Directives inherited from an upper level scope (say, http block or server blocks) are executed before the directives in the location block. Note that although `more_set_headers` is allowed in *location* if blocks, it is *not* allowed in the *server* if blocks, as in ```nginx ? # This is NOT allowed! ? server { ? if ($args ~ 'download') { ? more_set_headers 'Foo: Bar'; ? } ? ... ? } ``` Behind the scene, use of this directive and its friend [more_clear_headers](#more_clear_headers) will (lazily) register an ouput header filter that modifies `r->headers_out` the way you specify. [Back to TOC](#table-of-contents) more_clear_headers ------------------ **syntax:** *more_clear_headers [-t <content-type list>]... [-s <status-code list>]... <new-header>...* **default:** *no* **context:** *http, server, location, location if* **phase:** *output-header-filter* Clears the specified output headers. In fact, ```nginx more_clear_headers -s 404 -t 'text/plain' Foo Baz; ``` is exactly equivalent to ```nginx more_set_headers -s 404 -t 'text/plain' "Foo: " "Baz: "; ``` or ```nginx more_set_headers -s 404 -t 'text/plain' Foo Baz ``` See [more_set_headers](#more_set_headers) for more details. The wildcard character, `*`, can also be used at the end of the header name to specify a pattern. For example, the following directive effectively clears *any* output headers starting by "`X-Hidden-`": ```nginx more_clear_headers 'X-Hidden-*'; ``` The `*` wildcard support was first introduced in [v0.09](#v009). [Back to TOC](#table-of-contents) more_set_input_headers ---------------------- **syntax:** *more_set_input_headers [-r] [-t <content-type list>]... <new-header>...* **default:** *no* **context:** *http, server, location, location if* **phase:** *rewrite tail* Very much like [more_set_headers](#more_set_headers) except that it operates on input headers (or request headers) and it only supports the `-t` option. Note that using the `-t` option in this directive means filtering by the `Content-Type` *request* header, rather than the response header. Behind the scene, use of this directive and its friend [more_clear_input_headers](#more_clear_input_headers) will (lazily) register a `rewrite phase` handler that modifies `r->headers_in` the way you specify. Note that it always run at the *end* of the `rewrite` phase so that it runs *after* the standard [rewrite module](http://nginx.org/en/docs/http/ngx_http_rewrite_module.html) 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. [Back to TOC](#table-of-contents) more_clear_input_headers ------------------------ **syntax:** *more_clear_input_headers [-t <content-type list>]... <new-header>...* **default:** *no* **context:** *http, server, location, location if* **phase:** *rewrite tail* Clears the specified input headers. In fact, ```nginx more_clear_input_headers -t 'text/plain' Foo Baz; ``` is exactly equivalent to ```nginx more_set_input_headers -t 'text/plain' "Foo: " "Baz: "; ``` or ```nginx more_set_input_headers -t 'text/plain' Foo Baz ``` To remove request headers "Foo" and "Baz" for all incoming requests regardless of the content type, we can write ```nginx more_clear_input_headers "Foo" "Baz"; ``` See [more_set_input_headers](#more_set_input_headers) for more details. The wildcard character, `*`, can also be used at the end of the header name to specify a pattern. For example, the following directive effectively clears *any* input headers starting by "`X-Hidden-`": ```nginx more_clear_input_headers 'X-Hidden-*'; ``` [Back to TOC](#table-of-contents) Limitations =========== * Unlike the standard [headers](http://nginx.org/en/docs/http/ngx_http_headers_module.html) module, this module does not automatically take care of the constraint among the `Expires`, `Cache-Control`, and `Last-Modified` headers. You have to get them right yourself or use the [headers](http://nginx.org/en/docs/http/ngx_http_headers_module.html) module together with this module. * You cannot remove the `Connection` response header using this module because the `Connection` response header is generated by the standard `ngx_http_header_filter_module` in the Nginx core, whose output header filter runs always *after* the filter of this module. The only way to actually remove the `Connection` header is to patch the Nginx core, that is, editing the C function `ngx_http_header_filter` in the `src/http/ngx_http_header_filter_module.c` file. [Back to TOC](#table-of-contents) Installation ============ Grab the nginx source code from [nginx.org](http://nginx.org/), for example, the version 1.17.8 (see [nginx compatibility](#compatibility)), and then build the source with this module: ```bash wget 'http://nginx.org/download/nginx-1.17.8.tar.gz' tar -xzvf nginx-1.17.8.tar.gz cd nginx-1.17.8/ # Here we assume you would install you nginx under /opt/nginx/. ./configure --prefix=/opt/nginx \ --add-module=/path/to/headers-more-nginx-module make make install ``` Download the latest version of the release tarball of this module from [headers-more-nginx-module file list](https://github.com/openresty/headers-more-nginx-module/tags). Starting from NGINX 1.9.11, you can also compile this module as a dynamic module, by using the `--add-dynamic-module=PATH` option instead of `--add-module=PATH` on the `./configure` command line above. And then you can explicitly load the module in your `nginx.conf` via the [load_module](http://nginx.org/en/docs/ngx_core_module.html#load_module) directive, for example, ```nginx load_module /path/to/modules/ngx_http_headers_more_filter_module.so; ``` Also, this module is included and enabled by default in the [OpenResty bundle](http://openresty.org). [Back to TOC](#table-of-contents) Compatibility ============= The following versions of Nginx should work with this module: * **1.21.x** (last tested: 1.21.4) * **1.19.x** (last tested: 1.19.9) * **1.17.x** (last tested: 1.17.8) * **1.16.x** * **1.15.x** (last tested: 1.15.8) * **1.14.x** * **1.13.x** (last tested: 1.13.6) * **1.12.x** * **1.11.x** (last tested: 1.11.2) * **1.10.x** * **1.9.x** (last tested: 1.9.15) * **1.8.x** * **1.7.x** (last tested: 1.7.10) * **1.6.x** (last tested: 1.6.2) * **1.5.x** (last tested: 1.5.8) * **1.4.x** (last tested: 1.4.4) * **1.3.x** (last tested: 1.3.7) * **1.2.x** (last tested: 1.2.9) * **1.1.x** (last tested: 1.1.5) * **1.0.x** (last tested: 1.0.11) * **0.9.x** (last tested: 0.9.4) * **0.8.x** (last tested: 0.8.54) * **0.7.x >= 0.7.44** (last tested: 0.7.68) Earlier versions of Nginx like 0.6.x and 0.5.x will *not* work. If you find that any particular version of Nginx above 0.7.44 does not work with this module, please consider [reporting a bug](#report-bugs). [Back to TOC](#table-of-contents) Community ========= [Back to TOC](#table-of-contents) English Mailing List -------------------- The [openresty-en](https://groups.google.com/group/openresty-en) mailing list is for English speakers. [Back to TOC](#table-of-contents) Chinese Mailing List -------------------- The [openresty](https://groups.google.com/group/openresty) mailing list is for Chinese speakers. [Back to TOC](#table-of-contents) Bugs and Patches ================ Please submit bug reports, wishlists, or patches by 1. creating a ticket on the [GitHub Issue Tracker](https://github.com/chaoslawful/lua-nginx-module/issues), 1. or posting to the [OpenResty community](#community). [Back to TOC](#table-of-contents) Source Repository ================= Available on github at [openresty/headers-more-nginx-module](https://github.com/openresty/headers-more-nginx-module). [Back to TOC](#table-of-contents) Changes ======= The changes of every release of this module can be obtained from the OpenResty bundle's change logs: [Back to TOC](#table-of-contents) Test Suite ========== This module comes with a Perl-driven test suite. The [test cases](https://github.com/openresty/headers-more-nginx-module/tree/master/t/) are [declarative](https://github.com/openresty/headers-more-nginx-module/blob/master/t/sanity.t) too. Thanks to the [Test::Nginx](http://search.cpan.org/perldoc?Test::Nginx) module in the Perl world. To run it on your side: ```bash $ PATH=/path/to/your/nginx-with-headers-more-module:$PATH prove -r t ``` To run the test suite with valgrind's memcheck, use the following commands: ```bash $ export PATH=/path/to/your/nginx-with-headers-more-module:$PATH $ TEST_NGINX_USE_VALGRIND=1 prove -r t ``` You need to terminate any Nginx processes before running the test suite if you have changed the Nginx server binary. Because a single nginx server (by default, `localhost:1984`) is used across all the test scripts (`.t` files), it's meaningless to run the test suite in parallel by specifying `-jN` when invoking the `prove` utility. Some parts of the test suite requires modules [proxy](http://nginx.org/en/docs/http/ngx_http_proxy_module.html), [rewrite](http://nginx.org/en/docs/http/ngx_http_rewrite_module.html), and [echo](https://github.com/openresty/echo-nginx-module) to be enabled as well when building Nginx. [Back to TOC](#table-of-contents) TODO ==== * Support variables in new headers' keys. [Back to TOC](#table-of-contents) Getting involved ================ You'll be very welcomed to submit patches to the [author](#author) or just ask for a commit bit to the [source repository](#source-repository) on GitHub. [Back to TOC](#table-of-contents) Authors ======= * Yichun "agentzh" Zhang (章亦春) *<agentzh@gmail.com>*, OpenResty Inc. * Bernd Dorn ( ) This wiki page is also maintained by the author himself, and everybody is encouraged to improve this page as well. [Back to TOC](#table-of-contents) Copyright & License =================== The code base is borrowed directly from the standard [headers](http://nginx.org/en/docs/http/ngx_http_headers_module.html) module in Nginx 0.8.24. This part of code is copyrighted by Igor Sysoev. Copyright (c) 2009-2017, Yichun "agentzh" Zhang (章亦春) , OpenResty Inc. Copyright (c) 2010-2013, Bernd Dorn. This module is licensed under the terms of the BSD license. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [Back to TOC](#table-of-contents) See Also ======== * The original thread on the Nginx mailing list that inspires this module's development: ["A question about add_header replication"](http://forum.nginx.org/read.php?2,11206,11738). * The orginal announcement thread on the Nginx mailing list: ["The "headers_more" module: Set and clear output headers...more than 'add'!"](http://forum.nginx.org/read.php?2,23460). * The original [blog post](http://agentzh.blogspot.com/2009/11/headers-more-module-scripting-input-and.html) about this module's initial development. * The [echo module](https://github.com/openresty/echo-nginx-module) for Nginx module's automated testing. * The standard [headers](http://nginx.org/en/docs/http/ngx_http_headers_module.html) module. [Back to TOC](#table-of-contents) headers-more-nginx-module-0.37/config000066400000000000000000000026741454316031200176220ustar00rootroot00000000000000ngx_addon_name=ngx_http_headers_more_filter_module HEADERS_MORE_SRCS=" \ $ngx_addon_dir/src/ngx_http_headers_more_filter_module.c \ $ngx_addon_dir/src/ngx_http_headers_more_headers_out.c \ $ngx_addon_dir/src/ngx_http_headers_more_headers_in.c \ $ngx_addon_dir/src/ngx_http_headers_more_util.c \ " HEADERS_MORE_DEPS=" \ $ngx_addon_dir/src/ddebug.h \ $ngx_addon_dir/src/ngx_http_headers_more_filter_module.h \ $ngx_addon_dir/src/ngx_http_headers_more_headers_in.h \ $ngx_addon_dir/src/ngx_http_headers_more_headers_out.h \ $ngx_addon_dir/src/ngx_http_headers_more_headers_in.h \ $ngx_addon_dir/src/ngx_http_headers_more_util.h \ " if test -n "$ngx_module_link"; then ngx_module_type=HTTP_AUX_FILTER ngx_module_name=$ngx_addon_name ngx_module_incs= ngx_module_deps="$HEADERS_MORE_DEPS" ngx_module_srcs="$HEADERS_MORE_SRCS" ngx_module_libs= . auto/module else HTTP_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_MODULES $ngx_addon_name" NGX_ADDON_SRCS="$NGX_ADDON_SRCS $HEADERS_MORE_SRCS" NGX_ADDON_DEPS="$NGX_ADDON_DEPS $HEADERS_MORE_DEPS" fi headers-more-nginx-module-0.37/src/000077500000000000000000000000001454316031200172105ustar00rootroot00000000000000headers-more-nginx-module-0.37/src/ddebug.h000066400000000000000000000052331454316031200206160ustar00rootroot00000000000000#ifndef DDEBUG_H #define DDEBUG_H #include #include #include #include #if defined(DDEBUG) && (DDEBUG) # if (NGX_HAVE_VARIADIC_MACROS) # define dd(...) fprintf(stderr, "headers-more *** %s: ", __func__); \ fprintf(stderr, __VA_ARGS__); \ fprintf(stderr, " at %s line %d.\n", __FILE__, __LINE__) # else #include #include #include static ngx_inline void dd(const char * fmt, ...) { } # endif # if DDEBUG > 1 # define dd_enter() dd_enter_helper(r, __func__) # if defined(nginx_version) && nginx_version >= 8011 # define dd_main_req_count r->main->count # else # define dd_main_req_count 0 # endif static ngx_inline void dd_enter_helper(ngx_http_request_t *r, const char *func) { ngx_http_posted_request_t *pr; fprintf(stderr, "headers-more *** enter %s %.*s %.*s?%.*s c:%d m:%p r:%p ar:%p pr:%p", func, (int) r->method_name.len, r->method_name.data, (int) r->uri.len, r->uri.data, (int) r->args.len, r->args.data, (int) dd_main_req_count, r->main, r, r->connection->data, r->parent); if (r->posted_requests) { fprintf(stderr, " posted:"); for (pr = r->posted_requests; pr; pr = pr->next) { fprintf(stderr, "%p,", pr); } } fprintf(stderr, "\n"); } # else # define dd_enter() # endif #else # if (NGX_HAVE_VARIADIC_MACROS) # define dd(...) # define dd_enter() # else #include static ngx_inline void dd(const char * fmt, ...) { } static ngx_inline void dd_enter() { } # endif #endif #if defined(DDEBUG) && (DDEBUG) #define dd_check_read_event_handler(r) \ dd("r->read_event_handler = %s", \ r->read_event_handler == ngx_http_block_reading ? \ "ngx_http_block_reading" : \ r->read_event_handler == ngx_http_test_reading ? \ "ngx_http_test_reading" : \ r->read_event_handler == ngx_http_request_empty_handler ? \ "ngx_http_request_empty_handler" : "UNKNOWN") #define dd_check_write_event_handler(r) \ dd("r->write_event_handler = %s", \ r->write_event_handler == ngx_http_handler ? \ "ngx_http_handler" : \ r->write_event_handler == ngx_http_core_run_phases ? \ "ngx_http_core_run_phases" : \ r->write_event_handler == ngx_http_request_empty_handler ? \ "ngx_http_request_empty_handler" : "UNKNOWN") #else #define dd_check_read_event_handler(r) #define dd_check_write_event_handler(r) #endif #endif /* DDEBUG_H */ headers-more-nginx-module-0.37/src/ngx_http_headers_more_filter_module.c000066400000000000000000000227131454316031200266430ustar00rootroot00000000000000 /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_headers_more_filter_module.h" #include "ngx_http_headers_more_headers_out.h" #include "ngx_http_headers_more_headers_in.h" #include "ngx_http_headers_more_util.h" #include /* config handlers */ static void *ngx_http_headers_more_create_loc_conf(ngx_conf_t *cf); static char *ngx_http_headers_more_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child); static void *ngx_http_headers_more_create_main_conf(ngx_conf_t *cf); static ngx_int_t ngx_http_headers_more_post_config(ngx_conf_t *cf); /* post-read-phase handler */ static ngx_int_t ngx_http_headers_more_handler(ngx_http_request_t *r); /* filter handlers */ static ngx_int_t ngx_http_headers_more_filter_init(ngx_conf_t *cf); ngx_uint_t ngx_http_headers_more_location_hash = 0; static ngx_command_t ngx_http_headers_more_filter_commands[] = { { ngx_string("more_set_headers"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_1MORE, ngx_http_headers_more_set_headers, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL}, { ngx_string("more_clear_headers"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_1MORE, ngx_http_headers_more_clear_headers, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL}, { ngx_string("more_set_input_headers"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_1MORE, ngx_http_headers_more_set_input_headers, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL}, { ngx_string("more_clear_input_headers"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_1MORE, ngx_http_headers_more_clear_input_headers, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL}, ngx_null_command }; static ngx_http_module_t ngx_http_headers_more_filter_module_ctx = { NULL, /* preconfiguration */ ngx_http_headers_more_post_config, /* postconfiguration */ ngx_http_headers_more_create_main_conf, /* create main configuration */ NULL, /* init main configuration */ NULL, /* create server configuration */ NULL, /* merge server configuration */ ngx_http_headers_more_create_loc_conf, /* create location configuration */ ngx_http_headers_more_merge_loc_conf /* merge location configuration */ }; ngx_module_t ngx_http_headers_more_filter_module = { NGX_MODULE_V1, &ngx_http_headers_more_filter_module_ctx, /* module context */ ngx_http_headers_more_filter_commands, /* module directives */ NGX_HTTP_MODULE, /* module type */ NULL, /* init master */ NULL, /* init module */ NULL, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ NULL, /* exit process */ NULL, /* exit master */ NGX_MODULE_V1_PADDING }; static ngx_http_output_header_filter_pt ngx_http_next_header_filter; static volatile ngx_cycle_t *ngx_http_headers_more_prev_cycle = NULL; static ngx_int_t ngx_http_headers_more_filter(ngx_http_request_t *r) { ngx_int_t rc; ngx_uint_t i; ngx_http_headers_more_loc_conf_t *conf; ngx_http_headers_more_cmd_t *cmd; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "headers more header filter, uri \"%V\"", &r->uri); conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_more_filter_module); if (conf->cmds) { cmd = conf->cmds->elts; for (i = 0; i < conf->cmds->nelts; i++) { if (cmd[i].is_input) { continue; } rc = ngx_http_headers_more_exec_cmd(r, &cmd[i]); if (rc != NGX_OK) { return rc; } } } return ngx_http_next_header_filter(r); } static ngx_int_t ngx_http_headers_more_filter_init(ngx_conf_t *cf) { ngx_http_next_header_filter = ngx_http_top_header_filter; ngx_http_top_header_filter = ngx_http_headers_more_filter; return NGX_OK; } static void * ngx_http_headers_more_create_loc_conf(ngx_conf_t *cf) { ngx_http_headers_more_loc_conf_t *conf; conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_headers_more_loc_conf_t)); if (conf == NULL) { return NULL; } /* * set by ngx_pcalloc(): * * conf->cmds = NULL; */ return conf; } static char * ngx_http_headers_more_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) { ngx_uint_t i; ngx_uint_t orig_len; ngx_http_headers_more_cmd_t *prev_cmd, *cmd; ngx_http_headers_more_loc_conf_t *prev = parent; ngx_http_headers_more_loc_conf_t *conf = child; if (conf->cmds == NULL || conf->cmds->nelts == 0) { conf->cmds = prev->cmds; } else if (prev->cmds && prev->cmds->nelts) { orig_len = conf->cmds->nelts; (void) ngx_array_push_n(conf->cmds, prev->cmds->nelts); cmd = conf->cmds->elts; for (i = 0; i < orig_len; i++) { cmd[conf->cmds->nelts - 1 - i] = cmd[orig_len - 1 - i]; } prev_cmd = prev->cmds->elts; for (i = 0; i < prev->cmds->nelts; i++) { cmd[i] = prev_cmd[i]; } } return NGX_CONF_OK; } static ngx_int_t ngx_http_headers_more_post_config(ngx_conf_t *cf) { int multi_http_blocks; ngx_int_t rc; ngx_http_handler_pt *h; ngx_http_core_main_conf_t *cmcf; ngx_http_headers_more_main_conf_t *hmcf; ngx_http_headers_more_location_hash = ngx_http_headers_more_hash_literal("location"); hmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_headers_more_filter_module); if (ngx_http_headers_more_prev_cycle != ngx_cycle) { ngx_http_headers_more_prev_cycle = ngx_cycle; multi_http_blocks = 0; } else { multi_http_blocks = 1; } if (multi_http_blocks || hmcf->requires_filter) { rc = ngx_http_headers_more_filter_init(cf); if (rc != NGX_OK) { return rc; } } if (!hmcf->requires_handler) { return NGX_OK; } cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); h = ngx_array_push(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers); if (h == NULL) { return NGX_ERROR; } *h = ngx_http_headers_more_handler; return NGX_OK; } static ngx_int_t ngx_http_headers_more_handler(ngx_http_request_t *r) { ngx_int_t rc; ngx_uint_t i; ngx_http_headers_more_loc_conf_t *conf; ngx_http_headers_more_main_conf_t *hmcf; ngx_http_headers_more_cmd_t *cmd; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "headers more rewrite handler, uri \"%V\"", &r->uri); hmcf = ngx_http_get_module_main_conf(r, ngx_http_headers_more_filter_module); if (!hmcf->postponed_to_phase_end) { ngx_http_core_main_conf_t *cmcf; ngx_http_phase_handler_t tmp; ngx_http_phase_handler_t *ph; ngx_http_phase_handler_t *cur_ph; ngx_http_phase_handler_t *last_ph; hmcf->postponed_to_phase_end = 1; cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); ph = cmcf->phase_engine.handlers; cur_ph = &ph[r->phase_handler]; last_ph = &ph[cur_ph->next - 1]; if (cur_ph < last_ph) { dd("swaping the contents of cur_ph and last_ph..."); tmp = *cur_ph; memmove(cur_ph, cur_ph + 1, (last_ph - cur_ph) * sizeof (ngx_http_phase_handler_t)); *last_ph = tmp; r->phase_handler--; /* redo the current ph */ return NGX_DECLINED; } } dd("running phase handler..."); conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_more_filter_module); if (conf->cmds) { if (r->http_version < NGX_HTTP_VERSION_10) { return NGX_DECLINED; } cmd = conf->cmds->elts; for (i = 0; i < conf->cmds->nelts; i++) { if (!cmd[i].is_input) { continue; } rc = ngx_http_headers_more_exec_input_cmd(r, &cmd[i]); if (rc != NGX_OK) { return rc; } } } return NGX_DECLINED; } static void * ngx_http_headers_more_create_main_conf(ngx_conf_t *cf) { ngx_http_headers_more_main_conf_t *hmcf; hmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_headers_more_main_conf_t)); if (hmcf == NULL) { return NULL; } /* set by ngx_pcalloc: * hmcf->postponed_to_phase_end = 0; * hmcf->requires_filter = 0; * hmcf->requires_handler = 0; */ return hmcf; } headers-more-nginx-module-0.37/src/ngx_http_headers_more_filter_module.h000066400000000000000000000042701454316031200266460ustar00rootroot00000000000000 /* * Copyright (c) Yichun Zhang (agentzh) */ #ifndef NGX_HTTP_HEADERS_MORE_FILTER_MODULE_H #define NGX_HTTP_HEADERS_MORE_FILTER_MODULE_H #include #include #include typedef enum { ngx_http_headers_more_opcode_set, ngx_http_headers_more_opcode_clear } ngx_http_headers_more_opcode_t; typedef struct { ngx_array_t *types; /* of ngx_str_t */ ngx_array_t *statuses; /* of ngx_uint_t */ ngx_array_t *headers; /* of ngx_http_header_val_t */ ngx_flag_t is_input; } ngx_http_headers_more_cmd_t; typedef struct { ngx_array_t *cmds; /* of ngx_http_headers_more_cmd_t */ } ngx_http_headers_more_loc_conf_t; typedef struct { ngx_int_t postponed_to_phase_end; ngx_int_t requires_filter; ngx_int_t requires_handler; } ngx_http_headers_more_main_conf_t; typedef struct ngx_http_headers_more_header_val_s ngx_http_headers_more_header_val_t; typedef ngx_int_t (*ngx_http_headers_more_set_header_pt)(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value); typedef struct { ngx_str_t name; ngx_uint_t offset; ngx_http_headers_more_set_header_pt handler; } ngx_http_headers_more_set_header_t; struct ngx_http_headers_more_header_val_s { ngx_http_complex_value_t value; ngx_uint_t hash; ngx_str_t key; ngx_http_headers_more_set_header_pt handler; ngx_uint_t offset; unsigned replace:1; unsigned wildcard:1; unsigned append:1; }; extern ngx_module_t ngx_http_headers_more_filter_module; #ifndef ngx_str_set #define ngx_str_set(str, text) \ (str)->len = sizeof(text) - 1; (str)->data = (u_char *) text #endif #define ngx_http_headers_more_assert(a) assert(a) #endif /* NGX_HTTP_HEADERS_MORE_FILTER_MODULE_H */ headers-more-nginx-module-0.37/src/ngx_http_headers_more_headers_in.c000066400000000000000000000575741454316031200261270ustar00rootroot00000000000000 /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_headers_more_headers_in.h" #include "ngx_http_headers_more_util.h" #include 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); static int ngx_http_headers_more_check_type(ngx_http_request_t *r, ngx_array_t *types); static ngx_int_t ngx_http_set_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_set_header_helper(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value, ngx_table_elt_t **output_header); static ngx_int_t ngx_http_set_builtin_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_set_user_agent_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_set_content_length_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_clear_builtin_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_clear_content_length_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_set_host_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_set_connection_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_set_builtin_multi_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_headers_more_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_uint_t alloc); static ngx_http_headers_more_set_header_t ngx_http_headers_more_set_handlers[] = { { ngx_string("Host"), offsetof(ngx_http_headers_in_t, host), ngx_http_set_host_header }, { ngx_string("Connection"), offsetof(ngx_http_headers_in_t, connection), ngx_http_set_connection_header }, { ngx_string("If-Modified-Since"), offsetof(ngx_http_headers_in_t, if_modified_since), ngx_http_set_builtin_header }, #if defined(nginx_version) && nginx_version >= 9002 { ngx_string("If-Unmodified-Since"), offsetof(ngx_http_headers_in_t, if_unmodified_since), ngx_http_set_builtin_header }, #endif #if defined(nginx_version) && nginx_version >= 1003003 { ngx_string("If-Match"), offsetof(ngx_http_headers_in_t, if_match), ngx_http_set_builtin_header }, { ngx_string("If-None-Match"), offsetof(ngx_http_headers_in_t, if_none_match), ngx_http_set_builtin_header }, #endif { ngx_string("User-Agent"), offsetof(ngx_http_headers_in_t, user_agent), ngx_http_set_user_agent_header }, { ngx_string("Referer"), offsetof(ngx_http_headers_in_t, referer), ngx_http_set_builtin_header }, { ngx_string("Content-Length"), offsetof(ngx_http_headers_in_t, content_length), ngx_http_set_content_length_header }, { ngx_string("Content-Type"), offsetof(ngx_http_headers_in_t, content_type), ngx_http_set_builtin_header }, { ngx_string("Range"), offsetof(ngx_http_headers_in_t, range), ngx_http_set_builtin_header }, { ngx_string("If-Range"), offsetof(ngx_http_headers_in_t, if_range), ngx_http_set_builtin_header }, { ngx_string("Transfer-Encoding"), offsetof(ngx_http_headers_in_t, transfer_encoding), ngx_http_set_builtin_header }, { ngx_string("Expect"), offsetof(ngx_http_headers_in_t, expect), ngx_http_set_builtin_header }, #if defined(nginx_version) && nginx_version >= 1003013 { ngx_string("Upgrade"), offsetof(ngx_http_headers_in_t, upgrade), ngx_http_set_builtin_header }, #endif #if (NGX_HTTP_GZIP) { ngx_string("Accept-Encoding"), offsetof(ngx_http_headers_in_t, accept_encoding), ngx_http_set_builtin_header }, { ngx_string("Via"), offsetof(ngx_http_headers_in_t, via), ngx_http_set_builtin_header }, #endif { ngx_string("Authorization"), offsetof(ngx_http_headers_in_t, authorization), ngx_http_set_builtin_header }, { ngx_string("Keep-Alive"), offsetof(ngx_http_headers_in_t, keep_alive), ngx_http_set_builtin_header }, #if (NGX_HTTP_X_FORWARDED_FOR) { ngx_string("X-Forwarded-For"), offsetof(ngx_http_headers_in_t, x_forwarded_for), ngx_http_set_builtin_multi_header }, #endif #if (NGX_HTTP_REALIP) { ngx_string("X-Real-IP"), offsetof(ngx_http_headers_in_t, x_real_ip), ngx_http_set_builtin_header }, #endif #if (NGX_HTTP_DAV) { ngx_string("Depth"), offsetof(ngx_http_headers_in_t, depth), ngx_http_set_builtin_header }, { ngx_string("Destination"), offsetof(ngx_http_headers_in_t, destination), ngx_http_set_builtin_header }, { ngx_string("Overwrite"), offsetof(ngx_http_headers_in_t, overwrite), ngx_http_set_builtin_header }, { ngx_string("Date"), offsetof(ngx_http_headers_in_t, date), ngx_http_set_builtin_header }, #endif #if defined(nginx_version) && nginx_version >= 1023000 { ngx_string("Cookie"), offsetof(ngx_http_headers_in_t, cookie), ngx_http_set_builtin_multi_header }, #else { ngx_string("Cookie"), offsetof(ngx_http_headers_in_t, cookies), ngx_http_set_builtin_multi_header }, #endif { ngx_null_string, 0, ngx_http_set_header } }; ngx_int_t ngx_http_headers_more_exec_input_cmd(ngx_http_request_t *r, ngx_http_headers_more_cmd_t *cmd) { ngx_str_t value; ngx_http_headers_more_header_val_t *h; ngx_uint_t i; if (!cmd->headers) { return NGX_OK; } if (cmd->types && !ngx_http_headers_more_check_type(r, cmd->types)) { return NGX_OK; } h = cmd->headers->elts; for (i = 0; i < cmd->headers->nelts; i++) { if (ngx_http_complex_value(r, &h[i].value, &value) != NGX_OK) { return NGX_ERROR; } if (value.len) { value.len--; /* remove the trailing '\0' added by ngx_http_headers_more_parse_header */ } if (h[i].handler(r, &h[i], &value) != NGX_OK) { return NGX_ERROR; } } return NGX_OK; } static ngx_int_t ngx_http_set_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value) { return ngx_http_set_header_helper(r, hv, value, NULL); } static ngx_int_t ngx_http_set_header_helper(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value, ngx_table_elt_t **output_header) { ngx_table_elt_t *h, *matched; ngx_list_part_t *part; ngx_uint_t i; ngx_uint_t rc; dd_enter(); matched = NULL; retry: part = &r->headers_in.headers.part; h = part->elts; for (i = 0; /* void */; i++) { dd("i: %d, part: %p", (int) i, part); if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; h = part->elts; i = 0; } if (!hv->wildcard && h[i].key.len == hv->key.len && ngx_strncasecmp(h[i].key.data, hv->key.data, h[i].key.len) == 0) { goto matched; } if (hv->wildcard && value->len == 0 && h[i].key.len >= hv->key.len - 1 && ngx_strncasecmp(h[i].key.data, hv->key.data, hv->key.len - 1) == 0) { goto matched; } /* not matched */ continue; matched: if (value->len == 0 || (matched && matched != &h[i])) { h[i].hash = 0; 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)); if (rc == NGX_OK) { if (output_header) { *output_header = NULL; } goto retry; } return NGX_ERROR; } h[i].value = *value; if (output_header) { *output_header = &h[i]; dd("setting existing builtin input header"); } if (matched == NULL) { matched = &h[i]; } } if (matched) { return NGX_OK; } if (value->len == 0 || hv->replace) { return NGX_OK; } if (r->headers_in.headers.last == NULL) { /* must be 400 bad request */ return NGX_OK; } h = ngx_list_push(&r->headers_in.headers); if (h == NULL) { return NGX_ERROR; } dd("created new header for %.*s", (int) hv->key.len, hv->key.data); if (value->len == 0) { h->hash = 0; } else { h->hash = hv->hash; } h->key = hv->key; h->value = *value; #if defined(nginx_version) && nginx_version >= 1023000 h->next = NULL; #endif h->lowcase_key = ngx_pnalloc(r->pool, h->key.len); if (h->lowcase_key == NULL) { return NGX_ERROR; } ngx_strlow(h->lowcase_key, h->key.data, h->key.len); if (output_header) { *output_header = h; while (r != r->main) { r->parent->headers_in = r->headers_in; r = r->parent; } } return NGX_OK; } static ngx_int_t ngx_http_set_builtin_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value) { ngx_table_elt_t *h, **old; dd("entered set_builtin_header (input)"); if (hv->offset) { old = (ngx_table_elt_t **) ((char *) &r->headers_in + hv->offset); } else { old = NULL; } dd("old builtin ptr ptr: %p", old); if (old) { dd("old builtin ptr: %p", *old); } if (old == NULL || *old == NULL) { dd("set normal header"); return ngx_http_set_header_helper(r, hv, value, old); } h = *old; if (value->len == 0) { h->hash = 0; h->value = *value; return ngx_http_set_header_helper(r, hv, value, old); } h->hash = hv->hash; h->value = *value; return NGX_OK; } static ngx_int_t ngx_http_set_host_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value) { ngx_str_t host; if (value->len) { host= *value; if (ngx_http_headers_more_validate_host(&host, r->pool, 0) != NGX_OK) { return NGX_ERROR; } r->headers_in.server = host; } else { r->headers_in.server = *value; } return ngx_http_set_builtin_header(r, hv, value); } static ngx_int_t ngx_http_set_content_length_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value) { off_t len; if (value->len == 0) { return ngx_http_clear_content_length_header(r, hv, value); } len = ngx_atosz(value->data, value->len); if (len == NGX_ERROR) { return NGX_ERROR; } dd("reset headers_in.content_length_n to %d", (int) len); r->headers_in.content_length_n = len; return ngx_http_set_builtin_header(r, hv, value); } static ngx_int_t ngx_http_clear_content_length_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value) { r->headers_in.content_length_n = -1; return ngx_http_clear_builtin_header(r, hv, value); } static ngx_int_t ngx_http_clear_builtin_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value) { value->len = 0; return ngx_http_set_builtin_header(r, hv, value); } char * ngx_http_headers_more_set_input_headers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { return ngx_http_headers_more_parse_directive(cf, cmd, conf, ngx_http_headers_more_opcode_set); } char * ngx_http_headers_more_clear_input_headers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { return ngx_http_headers_more_parse_directive(cf, cmd, conf, ngx_http_headers_more_opcode_clear); } static int ngx_http_headers_more_check_type(ngx_http_request_t *r, ngx_array_t *types) { ngx_uint_t i; ngx_str_t *t; ngx_str_t actual_type; if (r->headers_in.content_type == NULL) { return 0; } actual_type = r->headers_in.content_type->value; if (actual_type.len == 0) { return 0; } dd("headers_in->content_type: %.*s", (int) actual_type.len, actual_type.data); t = types->elts; for (i = 0; i < types->nelts; i++) { dd("...comparing with type [%.*s]", (int) t[i].len, t[i].data); if (actual_type.len == t[i].len && ngx_strncmp(actual_type.data, t[i].data, t[i].len) == 0) { return 1; } } return 0; } 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_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_http_headers_more_header_val_t *h; ngx_http_headers_more_main_conf_t *hmcf; if (hlcf->cmds == NULL) { hlcf->cmds = ngx_array_create(cf->pool, 1, sizeof(ngx_http_headers_more_cmd_t)); if (hlcf->cmds == NULL) { return NGX_CONF_ERROR; } } cmd = ngx_array_push(hlcf->cmds); if (cmd == NULL) { return NGX_CONF_ERROR; } cmd->headers = ngx_array_create(cf->pool, 1, sizeof(ngx_http_headers_more_header_val_t)); if (cmd->headers == NULL) { return NGX_CONF_ERROR; } cmd->types = ngx_array_create(cf->pool, 1, sizeof(ngx_str_t)); if (cmd->types == NULL) { return NGX_CONF_ERROR; } cmd->statuses = NULL; arg = cf->args->elts; cmd_name = &arg[0]; ignore_next_arg = 0; for (i = 1; i < cf->args->nelts; i++) { if (ignore_next_arg) { ignore_next_arg = 0; continue; } if (arg[i].len == 0) { continue; } if (arg[i].data[0] != '-') { rc = ngx_http_headers_more_parse_header(cf, cmd_name, &arg[i], cmd->headers, opcode, ngx_http_headers_more_set_handlers); if (rc != NGX_OK) { return NGX_CONF_ERROR; } continue; } if (arg[i].len == 2) { if (arg[i].data[1] == 't') { if (i == cf->args->nelts - 1) { ngx_log_error(NGX_LOG_ERR, cf->log, 0, "%V: option -t takes an argument.", cmd_name); return NGX_CONF_ERROR; } rc = ngx_http_headers_more_parse_types(cf->log, cmd_name, &arg[i + 1], cmd->types); if (rc != NGX_OK) { return NGX_CONF_ERROR; } ignore_next_arg = 1; continue; } if (arg[i].data[1] == 'r') { dd("Found replace flag"); replace = 1; continue; } } ngx_log_error(NGX_LOG_ERR, cf->log, 0, "%V: invalid option name: \"%V\"", cmd_name, &arg[i]); return NGX_CONF_ERROR; } dd("Found %d types, and %d headers", (int) cmd->types->nelts, (int) cmd->headers->nelts); if (cmd->headers->nelts == 0) { ngx_pfree(cf->pool, cmd->headers); cmd->headers = NULL; } else { h = cmd->headers->elts; for (i = 0; i < cmd->headers->nelts; i++) { h[i].replace = replace; } } if (cmd->types->nelts == 0) { ngx_pfree(cf->pool, cmd->types); cmd->types = NULL; } cmd->is_input = 1; hmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_headers_more_filter_module); hmcf->requires_handler = 1; return NGX_CONF_OK; } /* borrowed the code from ngx_http_request.c:ngx_http_process_user_agent */ static ngx_int_t ngx_http_set_user_agent_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value) { u_char *user_agent, *msie; /* clear existing settings */ r->headers_in.msie = 0; r->headers_in.msie6 = 0; r->headers_in.opera = 0; r->headers_in.gecko = 0; r->headers_in.chrome = 0; r->headers_in.safari = 0; r->headers_in.konqueror = 0; if (value->len == 0) { return ngx_http_set_builtin_header(r, hv, value); } /* check some widespread browsers */ user_agent = value->data; msie = ngx_strstrn(user_agent, "MSIE ", 5 - 1); if (msie && msie + 7 < user_agent + value->len) { r->headers_in.msie = 1; if (msie[6] == '.') { switch (msie[5]) { case '4': case '5': r->headers_in.msie6 = 1; break; case '6': if (ngx_strstrn(msie + 8, "SV1", 3 - 1) == NULL) { r->headers_in.msie6 = 1; } break; } } } if (ngx_strstrn(user_agent, "Opera", 5 - 1)) { r->headers_in.opera = 1; r->headers_in.msie = 0; r->headers_in.msie6 = 0; } if (!r->headers_in.msie && !r->headers_in.opera) { if (ngx_strstrn(user_agent, "Gecko/", 6 - 1)) { r->headers_in.gecko = 1; } else if (ngx_strstrn(user_agent, "Chrome/", 7 - 1)) { r->headers_in.chrome = 1; } else if (ngx_strstrn(user_agent, "Safari/", 7 - 1) && ngx_strstrn(user_agent, "Mac OS X", 8 - 1)) { r->headers_in.safari = 1; } else if (ngx_strstrn(user_agent, "Konqueror", 9 - 1)) { r->headers_in.konqueror = 1; } } return ngx_http_set_builtin_header(r, hv, value); } static ngx_int_t ngx_http_set_connection_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value) { r->headers_in.connection_type = 0; if (value->len == 0) { return ngx_http_set_builtin_header(r, hv, value); } if (ngx_strcasestrn(value->data, "close", 5 - 1)) { r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE; r->headers_in.keep_alive_n = -1; r->keepalive = 0; } else if (ngx_strcasestrn(value->data, "keep-alive", 10 - 1)) { r->headers_in.connection_type = NGX_HTTP_CONNECTION_KEEP_ALIVE; } return ngx_http_set_builtin_header(r, hv, value); } static ngx_int_t ngx_http_set_builtin_multi_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value) { #if defined(nginx_version) && nginx_version >= 1023000 ngx_table_elt_t **headers, **ph, *h; #if (DDEBUG) int nelts; #endif if (r->headers_out.status == 400 || r->headers_in.headers.last == NULL) { /* must be a 400 Bad Request */ return NGX_OK; } headers = (ngx_table_elt_t **) ((char *) &r->headers_in + hv->offset); if (*headers) { #if (DDEBUG) nelts = 0; for (h = *headers; h; h = h->next) { nelts++; } dd("clear multi-value headers: %d", nelts); #endif *headers = NULL; } if (ngx_http_set_header_helper(r, hv, value, &h) == NGX_ERROR) { return NGX_ERROR; } if (value->len == 0) { return NGX_OK; } dd("new multi-value header: %p", h); if (*headers) { for (ph = headers; *ph; ph = &(*ph)->next) { /* void */ } *ph = h; } else { *headers = h; } h->next = NULL; return NGX_OK; #else ngx_array_t *headers; ngx_table_elt_t **v, *h; if (r->headers_out.status == 400 || r->headers_in.headers.last == NULL) { /* must be a 400 Bad Request */ return NGX_OK; } headers = (ngx_array_t *) ((char *) &r->headers_in + hv->offset); if (headers->nelts > 0) { ngx_array_destroy(headers); if (ngx_array_init(headers, r->pool, 2, sizeof(ngx_table_elt_t *)) != NGX_OK) { return NGX_ERROR; } dd("clear multi-value headers: %d", (int) headers->nelts); } #if 1 if (headers->nalloc == 0) { if (ngx_array_init(headers, r->pool, 2, sizeof(ngx_table_elt_t *)) != NGX_OK) { return NGX_ERROR; } } #endif h = NULL; if (ngx_http_set_header_helper(r, hv, value, &h) == NGX_ERROR) { return NGX_ERROR; } if (value->len == 0) { return NGX_OK; } dd("new cookie header: %p", h); v = ngx_array_push(headers); if (v == NULL) { return NGX_ERROR; } *v = h; return NGX_OK; #endif } static ngx_int_t ngx_http_headers_more_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_uint_t alloc) { u_char *h, ch; size_t i, dot_pos, host_len; enum { sw_usual = 0, sw_literal, sw_rest } state; dot_pos = host->len; host_len = host->len; h = host->data; state = sw_usual; for (i = 0; i < host->len; i++) { ch = h[i]; switch (ch) { case '.': if (dot_pos == i - 1) { return NGX_DECLINED; } dot_pos = i; break; case ':': if (state == sw_usual) { host_len = i; state = sw_rest; } break; case '[': if (i == 0) { state = sw_literal; } break; case ']': if (state == sw_literal) { host_len = i + 1; state = sw_rest; } break; case '\0': return NGX_DECLINED; default: if (ngx_path_separator(ch)) { return NGX_DECLINED; } if (ch >= 'A' && ch <= 'Z') { alloc = 1; } break; } } if (dot_pos == host_len - 1) { host_len--; } if (host_len == 0) { return NGX_DECLINED; } if (alloc) { host->data = ngx_pnalloc(pool, host_len); if (host->data == NULL) { return NGX_ERROR; } ngx_strlow(host->data, h, host_len); } host->len = host_len; return NGX_OK; } headers-more-nginx-module-0.37/src/ngx_http_headers_more_headers_in.h000066400000000000000000000011311454316031200261060ustar00rootroot00000000000000 /* * Copyright (c) Yichun Zhang (agentzh) */ #ifndef NGX_HTTP_HEADERS_MORE_INPUT_HEADERS_H #define NGX_HTTP_HEADERS_MORE_INPUT_HEADERS_H #include "ngx_http_headers_more_filter_module.h" /* output header setters and clearers */ ngx_int_t ngx_http_headers_more_exec_input_cmd(ngx_http_request_t *r, ngx_http_headers_more_cmd_t *cmd); char *ngx_http_headers_more_set_input_headers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); char *ngx_http_headers_more_clear_input_headers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); #endif /* NGX_HTTP_HEADERS_MORE_INPUT_HEADERS_H */ headers-more-nginx-module-0.37/src/ngx_http_headers_more_headers_out.c000066400000000000000000000507241454316031200263160ustar00rootroot00000000000000 /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_headers_more_headers_out.h" #include "ngx_http_headers_more_util.h" #include 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); static ngx_flag_t ngx_http_headers_more_check_type(ngx_http_request_t *r, ngx_array_t *types); static ngx_flag_t ngx_http_headers_more_check_status(ngx_http_request_t *r, ngx_array_t *statuses); static ngx_int_t ngx_http_set_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_set_header_helper(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value, ngx_table_elt_t **output_header, ngx_flag_t no_create); static ngx_int_t ngx_http_set_builtin_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_set_accept_ranges_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_set_content_length_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_set_content_type_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_clear_builtin_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_clear_content_length_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_set_builtin_multi_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value); static ngx_http_headers_more_set_header_t ngx_http_headers_more_set_handlers[] = { { ngx_string("Server"), offsetof(ngx_http_headers_out_t, server), ngx_http_set_builtin_header }, { ngx_string("Date"), offsetof(ngx_http_headers_out_t, date), ngx_http_set_builtin_header }, { ngx_string("Content-Encoding"), offsetof(ngx_http_headers_out_t, content_encoding), ngx_http_set_builtin_header }, { ngx_string("Location"), offsetof(ngx_http_headers_out_t, location), ngx_http_set_builtin_header }, { ngx_string("Refresh"), offsetof(ngx_http_headers_out_t, refresh), ngx_http_set_builtin_header }, { ngx_string("Last-Modified"), offsetof(ngx_http_headers_out_t, last_modified), ngx_http_set_builtin_header }, { ngx_string("Content-Range"), offsetof(ngx_http_headers_out_t, content_range), ngx_http_set_builtin_header }, { ngx_string("Accept-Ranges"), offsetof(ngx_http_headers_out_t, accept_ranges), ngx_http_set_accept_ranges_header }, { ngx_string("WWW-Authenticate"), offsetof(ngx_http_headers_out_t, www_authenticate), ngx_http_set_builtin_header }, { ngx_string("Expires"), offsetof(ngx_http_headers_out_t, expires), ngx_http_set_builtin_header }, { ngx_string("E-Tag"), offsetof(ngx_http_headers_out_t, etag), ngx_http_set_builtin_header }, { ngx_string("Content-Length"), offsetof(ngx_http_headers_out_t, content_length), ngx_http_set_content_length_header }, { ngx_string("Content-Type"), 0, ngx_http_set_content_type_header }, { ngx_string("Cache-Control"), offsetof(ngx_http_headers_out_t, cache_control), ngx_http_set_builtin_multi_header }, { ngx_null_string, 0, ngx_http_set_header } }; ngx_int_t ngx_http_headers_more_exec_cmd(ngx_http_request_t *r, ngx_http_headers_more_cmd_t *cmd) { ngx_str_t value; ngx_http_headers_more_header_val_t *h; ngx_uint_t i; if (!cmd->headers) { return NGX_OK; } if (cmd->types && !ngx_http_headers_more_check_type(r, cmd->types)) { return NGX_OK; } if (cmd->statuses && !ngx_http_headers_more_check_status(r, cmd->statuses)) { return NGX_OK; } h = cmd->headers->elts; for (i = 0; i < cmd->headers->nelts; i++) { if (ngx_http_complex_value(r, &h[i].value, &value) != NGX_OK) { return NGX_ERROR; } if (value.len) { value.len--; /* remove the trailing '\0' added by ngx_http_headers_more_parse_header */ } if (h[i].handler(r, &h[i], &value) != NGX_OK) { return NGX_ERROR; } } return NGX_OK; } static ngx_int_t ngx_http_set_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value) { return ngx_http_set_header_helper(r, hv, value, NULL, 0); } static ngx_int_t ngx_http_set_header_helper(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value, ngx_table_elt_t **output_header, ngx_flag_t no_create) { ngx_table_elt_t *h; ngx_list_part_t *part; ngx_uint_t i; ngx_flag_t matched = 0; dd_enter(); #if 1 if (r->headers_out.location && r->headers_out.location->value.len && r->headers_out.location->value.data[0] == '/') { /* XXX ngx_http_core_find_config_phase, for example, * may not initialize the "key" and "hash" fields * for a nasty optimization purpose, and * we have to work-around it here */ r->headers_out.location->hash = ngx_http_headers_more_location_hash; ngx_str_set(&r->headers_out.location->key, "Location"); } #endif if (hv->append) { goto append; } part = &r->headers_out.headers.part; h = part->elts; for (i = 0; /* void */; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; h = part->elts; i = 0; } if (h[i].hash == 0) { continue; } if (!hv->wildcard && h[i].key.len == hv->key.len && ngx_strncasecmp(h[i].key.data, hv->key.data, h[i].key.len) == 0) { goto matched; } if (hv->wildcard && h[i].key.len >= hv->key.len - 1 && ngx_strncasecmp(h[i].key.data, hv->key.data, hv->key.len - 1) == 0) { goto matched; } /* not matched */ continue; matched: if (value->len == 0 || matched) { dd("clearing normal header for %.*s", (int) hv->key.len, hv->key.data); h[i].value.len = 0; h[i].hash = 0; } else { h[i].value = *value; h[i].hash = hv->hash; } if (output_header) { *output_header = &h[i]; } matched = 1; } if (matched){ return NGX_OK; } if ((hv->wildcard || no_create) && value->len == 0) { return NGX_OK; } /* XXX we still need to create header slot even if the value * is empty because some builtin headers like Last-Modified * relies on this to get cleared */ append: h = ngx_list_push(&r->headers_out.headers); if (h == NULL) { return NGX_ERROR; } if (value->len == 0) { h->hash = 0; } else { h->hash = hv->hash; } h->key = hv->key; h->value = *value; h->lowcase_key = ngx_pnalloc(r->pool, h->key.len); if (h->lowcase_key == NULL) { return NGX_ERROR; } ngx_strlow(h->lowcase_key, h->key.data, h->key.len); if (output_header) { *output_header = h; } return NGX_OK; } static ngx_int_t ngx_http_set_builtin_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value) { ngx_table_elt_t *h, **old; dd_enter(); if (hv->offset) { old = (ngx_table_elt_t **) ((char *) &r->headers_out + hv->offset); } else { old = NULL; } if (old == NULL || *old == NULL) { return ngx_http_set_header_helper(r, hv, value, old, 0); } h = *old; if (value->len == 0) { dd("clearing the builtin header"); h->hash = 0; h->value = *value; return NGX_OK; } h->hash = hv->hash; h->key = hv->key; h->value = *value; return NGX_OK; } static ngx_int_t ngx_http_set_builtin_multi_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value) { #if defined(nginx_version) && nginx_version >= 1023000 ngx_table_elt_t **headers, *h, *ho, **ph; headers = (ngx_table_elt_t **) ((char *) &r->headers_out + hv->offset); if (*headers) { for (h = (*headers)->next; h; h = h->next) { h->hash = 0; h->value.len = 0; } h = *headers; h->value = *value; if (value->len == 0) { h->hash = 0; } else { h->hash = hv->hash; } return NGX_OK; } for (ph = headers; *ph; ph = &(*ph)->next) { /* void */ } ho = ngx_list_push(&r->headers_out.headers); if (ho == NULL) { return NGX_ERROR; } ho->value = *value; ho->hash = hv->hash; ngx_str_set(&ho->key, "Cache-Control"); ho->next = NULL; *ph = ho; return NGX_OK; #else ngx_array_t *pa; ngx_table_elt_t *ho, **ph; ngx_uint_t i; pa = (ngx_array_t *) ((char *) &r->headers_out + hv->offset); if (pa->elts == NULL) { if (ngx_array_init(pa, r->pool, 2, sizeof(ngx_table_elt_t *)) != NGX_OK) { return NGX_ERROR; } } /* override old values (if any) */ if (pa->nelts > 0) { ph = pa->elts; for (i = 1; i < pa->nelts; i++) { ph[i]->hash = 0; ph[i]->value.len = 0; } ph[0]->value = *value; if (value->len == 0) { ph[0]->hash = 0; } else { ph[0]->hash = hv->hash; } return NGX_OK; } ph = ngx_array_push(pa); if (ph == NULL) { return NGX_ERROR; } ho = ngx_list_push(&r->headers_out.headers); if (ho == NULL) { return NGX_ERROR; } ho->value = *value; ho->hash = hv->hash; ngx_str_set(&ho->key, "Cache-Control"); *ph = ho; return NGX_OK; #endif } static ngx_int_t ngx_http_set_content_type_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value) { u_char *p, *last, *end; r->headers_out.content_type_len = value->len; r->headers_out.content_type = *value; r->headers_out.content_type_hash = hv->hash; r->headers_out.content_type_lowcase = NULL; p = value->data; end = p + value->len; for (; p != end; p++) { if (*p != ';') { continue; } last = p; while (*++p == ' ') { /* void */ } if (p == end) { break; } if (ngx_strncasecmp(p, (u_char *) "charset=", 8) != 0) { continue; } p += 8; r->headers_out.content_type_len = last - value->data; if (*p == '"') { p++; } last = end; if (*(last - 1) == '"') { last--; } r->headers_out.charset.len = last - p; r->headers_out.charset.data = p; break; } value->len = 0; return ngx_http_set_header_helper(r, hv, value, NULL, 1); } static ngx_int_t ngx_http_set_content_length_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value) { off_t len; if (value->len == 0) { return ngx_http_clear_content_length_header(r, hv, value); } len = ngx_atosz(value->data, value->len); if (len == NGX_ERROR) { return NGX_ERROR; } r->headers_out.content_length_n = len; return ngx_http_set_builtin_header(r, hv, value); } static ngx_int_t ngx_http_set_accept_ranges_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value) { if (value->len == 0) { r->allow_ranges = 0; } return ngx_http_set_builtin_header(r, hv, value); } static ngx_int_t ngx_http_clear_content_length_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value) { r->headers_out.content_length_n = -1; return ngx_http_clear_builtin_header(r, hv, value); } static ngx_int_t ngx_http_clear_builtin_header(ngx_http_request_t *r, ngx_http_headers_more_header_val_t *hv, ngx_str_t *value) { dd_enter(); value->len = 0; return ngx_http_set_builtin_header(r, hv, value); } char * ngx_http_headers_more_set_headers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { return ngx_http_headers_more_parse_directive(cf, cmd, conf, ngx_http_headers_more_opcode_set); } char * ngx_http_headers_more_clear_headers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { return ngx_http_headers_more_parse_directive(cf, cmd, conf, ngx_http_headers_more_opcode_clear); } static ngx_flag_t ngx_http_headers_more_check_type(ngx_http_request_t *r, ngx_array_t *types) { ngx_uint_t i; ngx_str_t *t; dd("headers_out->content_type: %.*s (len %d)", (int) r->headers_out.content_type.len, r->headers_out.content_type.data, (int) r->headers_out.content_type.len); t = types->elts; for (i = 0; i < types->nelts; i++) { dd("...comparing with type [%.*s]", (int) t[i].len, t[i].data); if (r->headers_out.content_type_len == t[i].len && ngx_strncmp(r->headers_out.content_type.data, t[i].data, t[i].len) == 0) { return 1; } } return 0; } static ngx_flag_t ngx_http_headers_more_check_status(ngx_http_request_t *r, ngx_array_t *statuses) { ngx_uint_t i; ngx_uint_t *status; dd("headers_out.status = %d", (int) r->headers_out.status); status = statuses->elts; for (i = 0; i < statuses->nelts; i++) { dd("...comparing with specified status %d", (int) status[i]); if (r->headers_out.status == status[i]) { return 1; } } return 0; } 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_http_headers_more_loc_conf_t *hlcf = conf; ngx_uint_t i, j; 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 append = 0; ngx_flag_t is_builtin_header = 0; ngx_http_headers_more_header_val_t *h; ngx_http_headers_more_set_header_t *handlers; ngx_http_headers_more_main_conf_t *hmcf; if (hlcf->cmds == NULL) { hlcf->cmds = ngx_array_create(cf->pool, 1, sizeof(ngx_http_headers_more_cmd_t)); if (hlcf->cmds == NULL) { return NGX_CONF_ERROR; } } cmd = ngx_array_push(hlcf->cmds); if (cmd == NULL) { return NGX_CONF_ERROR; } cmd->headers = ngx_array_create(cf->pool, 1, sizeof(ngx_http_headers_more_header_val_t)); if (cmd->headers == NULL) { return NGX_CONF_ERROR; } cmd->types = ngx_array_create(cf->pool, 1, sizeof(ngx_str_t)); if (cmd->types == NULL) { return NGX_CONF_ERROR; } cmd->statuses = ngx_array_create(cf->pool, 1, sizeof(ngx_uint_t)); if (cmd->statuses == NULL) { return NGX_CONF_ERROR; } arg = cf->args->elts; cmd_name = &arg[0]; ignore_next_arg = 0; for (i = 1; i < cf->args->nelts; i++) { if (ignore_next_arg) { ignore_next_arg = 0; continue; } if (arg[i].len == 0) { continue; } if (arg[i].data[0] != '-') { rc = ngx_http_headers_more_parse_header(cf, cmd_name, &arg[i], cmd->headers, opcode, ngx_http_headers_more_set_handlers); if (rc != NGX_OK) { return NGX_CONF_ERROR; } continue; } if (arg[i].len == 2) { if (arg[i].data[1] == 't') { if (i == cf->args->nelts - 1) { ngx_log_error(NGX_LOG_ERR, cf->log, 0, "%V: option -t takes an argument.", cmd_name); return NGX_CONF_ERROR; } rc = ngx_http_headers_more_parse_types(cf->log, cmd_name, &arg[i + 1], cmd->types); if (rc != NGX_OK) { return NGX_CONF_ERROR; } ignore_next_arg = 1; continue; } else if (arg[i].data[1] == 's') { if (i == cf->args->nelts - 1) { ngx_log_error(NGX_LOG_ERR, cf->log, 0, "%V: option -s takes an argument.", cmd_name); return NGX_CONF_ERROR; } rc = ngx_http_headers_more_parse_statuses(cf->log, cmd_name, &arg[i + 1], cmd->statuses); if (rc != NGX_OK) { return NGX_CONF_ERROR; } ignore_next_arg = 1; continue; } else if (arg[i].data[1] == 'a') { if (ngx_strncasecmp((u_char *) "more_set_headers", cmd_name->data, cmd_name->len) != 0) { ngx_log_error(NGX_LOG_ERR, cf->log, 0, "%V: invalid option name: \"%V\"", cmd_name, &arg[i]); return NGX_CONF_ERROR; } dd("Found append flag"); append = 1; continue; } } ngx_log_error(NGX_LOG_ERR, cf->log, 0, "%V: invalid option name: \"%V\"", cmd_name, &arg[i]); return NGX_CONF_ERROR; } dd("Found %d statuses, %d types, and %d headers", (int) cmd->statuses->nelts, (int) cmd->types->nelts, (int) cmd->headers->nelts); if (cmd->headers->nelts == 0) { cmd->headers = NULL; } else { h = cmd->headers->elts; for (i = 0; i < cmd->headers->nelts; i++) { h[i].append = 0; handlers = ngx_http_headers_more_set_handlers; for (j = 0; handlers[j].name.len; j++) { if (h[i].key.len == handlers[j].name.len && ngx_strncasecmp(h[i].key.data, handlers[j].name.data, h[i].key.len) == 0) { is_builtin_header = 1; break; } } if (is_builtin_header && append) { ngx_log_error(NGX_LOG_ERR, cf->log, 0, "%V: can not append builtin headers \"%V\"", cmd_name, &h[i].key); return NGX_CONF_ERROR; } if (!is_builtin_header) { h[i].append = append; } } } if (cmd->types->nelts == 0) { cmd->types = NULL; } if (cmd->statuses->nelts == 0) { cmd->statuses = NULL; } cmd->is_input = 0; hmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_headers_more_filter_module); hmcf->requires_filter = 1; return NGX_CONF_OK; } headers-more-nginx-module-0.37/src/ngx_http_headers_more_headers_out.h000066400000000000000000000011121454316031200263060ustar00rootroot00000000000000 /* * Copyright (c) Yichun Zhang (agentzh) */ #ifndef NGX_HTTP_HEADERS_MORE_OUTPUT_HEADERS_H #define NGX_HTTP_HEADERS_MORE_OUTPUT_HEADERS_H #include "ngx_http_headers_more_filter_module.h" /* output header setters and clearers */ ngx_int_t ngx_http_headers_more_exec_cmd(ngx_http_request_t *r, ngx_http_headers_more_cmd_t *cmd); char *ngx_http_headers_more_set_headers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); char *ngx_http_headers_more_clear_headers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); #endif /* NGX_HTTP_HEADERS_MORE_OUTPUT_HEADERS_H */ headers-more-nginx-module-0.37/src/ngx_http_headers_more_util.c000066400000000000000000000212361454316031200247650ustar00rootroot00000000000000 /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_headers_more_util.h" #include ngx_int_t ngx_http_headers_more_parse_header(ngx_conf_t *cf, ngx_str_t *cmd_name, ngx_str_t *raw_header, ngx_array_t *headers, ngx_http_headers_more_opcode_t opcode, ngx_http_headers_more_set_header_t *handlers) { ngx_http_headers_more_header_val_t *hv; ngx_uint_t i; ngx_str_t key = ngx_null_string; ngx_str_t value = ngx_null_string; ngx_flag_t seen_end_of_key; ngx_http_compile_complex_value_t ccv; u_char *p; hv = ngx_array_push(headers); if (hv == NULL) { return NGX_ERROR; } seen_end_of_key = 0; for (i = 0; i < raw_header->len; i++) { if (key.len == 0) { if (isspace(raw_header->data[i])) { continue; } key.data = raw_header->data; key.len = 1; continue; } if (!seen_end_of_key) { if (raw_header->data[i] == ':' || isspace(raw_header->data[i])) { seen_end_of_key = 1; continue; } key.len++; continue; } if (value.len == 0) { if (raw_header->data[i] == ':' || isspace(raw_header->data[i])) { continue; } value.data = &raw_header->data[i]; value.len = 1; continue; } value.len++; } if (key.len == 0) { ngx_log_error(NGX_LOG_ERR, cf->log, 0, "%V: no key found in the header argument: %V", cmd_name, raw_header); return NGX_ERROR; } hv->wildcard = (key.data[key.len - 1] == '*'); if (hv->wildcard && key.len<2){ ngx_log_error(NGX_LOG_ERR, cf->log, 0, "%V: wildcard key too short: %V", cmd_name, raw_header); return NGX_ERROR; } hv->hash = ngx_hash_key_lc(key.data, key.len); hv->key = key; hv->offset = 0; for (i = 0; handlers[i].name.len; i++) { if (hv->key.len != handlers[i].name.len || ngx_strncasecmp(hv->key.data, handlers[i].name.data, handlers[i].name.len) != 0) { dd("hv key comparison: %s <> %s", handlers[i].name.data, hv->key.data); continue; } hv->offset = handlers[i].offset; hv->handler = handlers[i].handler; break; } if (handlers[i].name.len == 0 && handlers[i].handler) { hv->offset = handlers[i].offset; hv->handler = handlers[i].handler; } if (opcode == ngx_http_headers_more_opcode_clear) { value.len = 0; } if (value.len == 0) { ngx_memzero(&hv->value, sizeof(ngx_http_complex_value_t)); return NGX_OK; } /* Nginx request header value requires to be a null-terminated * C string */ p = ngx_palloc(cf->pool, value.len + 1); if (p == NULL) { return NGX_ERROR; } ngx_memcpy(p, value.data, value.len); p[value.len] = '\0'; value.data = p; value.len++; /* we should also compile the trailing '\0' */ /* compile the header value as a complex value */ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); ccv.cf = cf; ccv.value = &value; ccv.complex_value = &hv->value; if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { return NGX_ERROR; } return NGX_OK; } ngx_int_t ngx_http_headers_more_parse_statuses(ngx_log_t *log, ngx_str_t *cmd_name, ngx_str_t *value, ngx_array_t *statuses) { u_char *p, *last; ngx_uint_t *s = NULL; p = value->data; last = p + value->len; for (; p != last; p++) { if (s == NULL) { if (isspace(*p)) { continue; } s = ngx_array_push(statuses); if (s == NULL) { return NGX_ERROR; } if (*p >= '0' && *p <= '9') { *s = *p - '0'; } else { ngx_log_error(NGX_LOG_ERR, log, 0, "%V: invalid digit \"%c\" found in " "the status code list \"%V\"", cmd_name, *p, value); return NGX_ERROR; } continue; } if (isspace(*p)) { dd("Parsed status %d", (int) *s); s = NULL; continue; } if (*p >= '0' && *p <= '9') { *s *= 10; *s += *p - '0'; } else { ngx_log_error(NGX_LOG_ERR, log, 0, "%V: invalid digit \"%c\" found in " "the status code list \"%V\"", cmd_name, *p, value); return NGX_ERROR; } } if (s) { dd("Parsed status %d", (int) *s); } return NGX_OK; } ngx_int_t ngx_http_headers_more_parse_types(ngx_log_t *log, ngx_str_t *cmd_name, ngx_str_t *value, ngx_array_t *types) { u_char *p, *last; ngx_str_t *t = NULL; p = value->data; last = p + value->len; for (; p != last; p++) { if (t == NULL) { if (isspace(*p) || *p == ';') { continue; } t = ngx_array_push(types); if (t == NULL) { return NGX_ERROR; } t->len = 1; t->data = p; continue; } if (isspace(*p) || *p == ';') { t = NULL; continue; } t->len++; } return NGX_OK; } ngx_int_t ngx_http_headers_more_rm_header_helper(ngx_list_t *l, ngx_list_part_t *cur, ngx_uint_t i) { ngx_table_elt_t *data; ngx_list_part_t *new, *part; dd("list rm item: part %p, i %d, nalloc %d", cur, (int) i, (int) l->nalloc); data = cur->elts; dd("cur: nelts %d, nalloc %d", (int) cur->nelts, (int) l->nalloc); if (i == 0) { cur->elts = (char *) cur->elts + l->size; cur->nelts--; if (cur == l->last) { if (cur->nelts == 0) { #if 1 part = &l->part; if (part == cur) { cur->elts = (char *) cur->elts - l->size; /* do nothing */ } else { while (part->next != cur) { if (part->next == NULL) { return NGX_ERROR; } part = part->next; } l->last = part; part->next = NULL; dd("part nelts: %d", (int) part->nelts); l->nalloc = part->nelts; } #endif } else { l->nalloc--; } return NGX_OK; } if (cur->nelts == 0) { part = &l->part; if (part == cur) { ngx_http_headers_more_assert(cur->next != NULL); dd("remove 'cur' from the list by rewriting 'cur': " "l->last: %p, cur: %p, cur->next: %p, part: %p", l->last, cur, cur->next, part); if (l->last == cur->next) { dd("last is cur->next"); l->part = *(cur->next); l->last = part; l->nalloc = part->nelts; } else { l->part = *(cur->next); } } else { dd("remove 'cur' from the list"); while (part->next != cur) { if (part->next == NULL) { return NGX_ERROR; } part = part->next; } part->next = cur->next; } return NGX_OK; } return NGX_OK; } if (i == cur->nelts - 1) { cur->nelts--; if (cur == l->last) { l->nalloc = cur->nelts; } return NGX_OK; } new = ngx_palloc(l->pool, sizeof(ngx_list_part_t)); if (new == NULL) { return NGX_ERROR; } new->elts = &data[i + 1]; new->nelts = cur->nelts - i - 1; new->next = cur->next; cur->nelts = i; cur->next = new; if (cur == l->last) { l->last = new; l->nalloc = new->nelts; } return NGX_OK; } headers-more-nginx-module-0.37/src/ngx_http_headers_more_util.h000066400000000000000000000023131454316031200247650ustar00rootroot00000000000000 /* * Copyright (c) Yichun Zhang (agentzh) */ #ifndef NGX_HTTP_HEADERS_MORE_UTIL_H #define NGX_HTTP_HEADERS_MORE_UTIL_H #include "ngx_http_headers_more_filter_module.h" #define ngx_http_headers_more_hash_literal(s) \ ngx_http_headers_more_hash_str((u_char *) s, sizeof(s) - 1) static ngx_inline ngx_uint_t ngx_http_headers_more_hash_str(u_char *src, size_t n) { ngx_uint_t key; key = 0; while (n--) { key = ngx_hash(key, *src); src++; } return key; } extern ngx_uint_t ngx_http_headers_more_location_hash; ngx_int_t ngx_http_headers_more_parse_header(ngx_conf_t *cf, ngx_str_t *cmd_name, ngx_str_t *raw_header, ngx_array_t *headers, ngx_http_headers_more_opcode_t opcode, ngx_http_headers_more_set_header_t *handlers); ngx_int_t ngx_http_headers_more_parse_statuses(ngx_log_t *log, ngx_str_t *cmd_name, ngx_str_t *value, ngx_array_t *statuses); ngx_int_t ngx_http_headers_more_parse_types(ngx_log_t *log, ngx_str_t *cmd_name, ngx_str_t *value, ngx_array_t *types); ngx_int_t ngx_http_headers_more_rm_header_helper(ngx_list_t *l, ngx_list_part_t *cur, ngx_uint_t i); #endif /* NGX_HTTP_HEADERS_MORE_UTIL_H */ headers-more-nginx-module-0.37/t/000077500000000000000000000000001454316031200166645ustar00rootroot00000000000000headers-more-nginx-module-0.37/t/bug.t000066400000000000000000000174111454316031200176320ustar00rootroot00000000000000# vi:filetype= use Test::Nginx::Socket; # 'no_plan'; repeat_each(2); plan tests => 56 * repeat_each(); no_diff; run_tests(); __DATA__ === TEST 1: set Server --- config #more_set_headers 'Last-Modified: x'; more_clear_headers 'Last-Modified'; --- request GET /index.html --- response_headers ! Last-Modified --- response_body_like: It works! === TEST 2: variables in the Ranges header --- config location /index.html { set $rfrom 1; set $rto 3; more_set_input_headers 'Range: bytes=$rfrom - $rto'; #more_set_input_headers 'Range: bytes=1 - 3'; #echo $http_range; } --- request GET /index.html --- error_code: 206 --- response_body chomp htm === TEST 3: mime type overriding (inlined types) --- config more_clear_headers 'X-Powered-By' 'X-Runtime' 'ETag'; types { text/html html htm shtml; text/css css; } --- user_files >>> a.css hello --- request GET /a.css --- error_code: 200 --- response_headers Content-Type: text/css --- response_body hello === TEST 4: mime type overriding (included types file) --- config more_clear_headers 'X-Powered-By' 'X-Runtime' 'ETag'; include mime.types; --- user_files >>> a.css hello >>> ../conf/mime.types types { text/html html htm shtml; text/css css; } --- request GET /a.css --- error_code: 200 --- response_headers Content-Type: text/css --- response_body hello === TEST 5: empty variable as the header value --- config location /foo { more_set_headers 'X-Foo: $arg_foo'; echo hi; } --- request GET /foo --- response_headers ! X-Foo --- response_body hi === TEST 6: range bug --- config location /index.html { more_clear_input_headers "Range*" ; more_clear_input_headers "Content-Range*" ; more_set_input_headers 'Range: bytes=1-5'; more_set_headers 'Content-Range: bytes 1-5/1000'; } --- request GET /index.html --- more_headers Range: bytes=1-3 --- raw_response_headers_like: Content-Range: bytes 1-5/1000$ --- response_body chop html> --- error_code: 206 --- SKIP === TEST 7: Allow-Ranges --- config location /index.html { more_clear_headers 'Accept-Ranges'; } --- request GET /index.html --- response_headers ! Accept-Ranges --- response_body_like: It works === TEST 8: clear hand-written Allow-Ranges headers --- config location /index.html { more_set_headers 'Accept-Ranges: bytes'; more_clear_headers 'Accept-Ranges'; } --- request GET /index.html --- response_headers ! Accept-Ranges --- response_body_like: It works === TEST 9: clear first, then add --- config location /bug { more_clear_headers 'Foo'; more_set_headers 'Foo: a'; echo hello; } --- request GET /bug --- raw_response_headers_like eval ".*Foo: a.*" --- response_body hello === TEST 10: first add, then clear, then add again --- config location /bug { more_set_headers 'Foo: a'; more_clear_headers 'Foo'; more_set_headers 'Foo: b'; echo hello; } --- request GET /bug --- raw_response_headers_like eval ".*Foo: b.*" --- response_body hello === TEST 11: override charset --- config location /foo { charset iso-8859-1; default_type "text/html"; echo hiya; } location /bug { more_set_headers "Content-Type: text/html; charset=UTF-8"; proxy_pass http://127.0.0.1:$server_port/foo; } --- request GET /bug --- response_body hiya --- response_headers Content-Type: text/html; charset=UTF-8 === TEST 12: set multi-value header to a single value --- config location /main { set $footer ''; proxy_pass http://127.0.0.1:$server_port/foo; more_set_headers 'Foo: b'; header_filter_by_lua ' ngx.var.footer = ngx.header.Foo '; echo_after_body $footer; } location /foo { echo foo; add_header Foo a; add_header Foo c; } --- request GET /main --- response_headers Foo: b --- response_body foo b === TEST 13: set multi values to cache-control and override it with multiple values (to reproduce a bug) --- config location /lua { content_by_lua ' ngx.header.cache_control = { "private", "no-store", "foo", "bar", "baz" } ngx.send_headers() ngx.say("Cache-Control: ", ngx.var.sent_http_cache_control) '; more_clear_headers Cache-Control; add_header Cache-Control "blah"; } --- request GET /lua --- response_headers Cache-Control: blah --- response_body Cache-Control: blah === TEST 14: set 20+ headers --- config location /test { more_clear_input_headers "Authorization"; echo $http_a1; echo $http_authorization; echo $http_a2; echo $http_a3; echo $http_a23; echo $http_a24; echo $http_a25; } --- request GET /test --- more_headers eval my $i = 1; my $s; while ($i <= 25) { $s .= "A$i: $i\n"; if ($i == 22) { $s .= "Authorization: blah\n"; } $i++; } #warn $s; $s --- response_body 1 2 3 23 24 25 === TEST 15: github #20: segfault caused by the nasty optimization in the nginx core (set) --- config location = /t/ { more_set_headers "Foo: 1"; proxy_pass http://127.0.0.1:$server_port; } --- request GET /t --- more_headers Foo: bar Bah: baz --- response_body_like: 301 Moved Permanently --- error_code: 301 --- no_error_log [error] === TEST 16: github #20: segfault caused by the nasty optimization in the nginx core (clear) --- config location = /t/ { more_clear_headers Foo; proxy_pass http://127.0.0.1:$server_port; } --- request GET /t --- more_headers Foo: bar Bah: baz --- response_body_like: 301 Moved Permanently --- error_code: 301 --- no_error_log [error] === TEST 17: Content-Type response headers with a charset param (correct -t values) --- config location = /t { more_set_headers -t 'text/html' 'X-Foo: Bar'; proxy_pass http://127.0.0.1:$server_port/fake; } location = /fake { default_type text/html; charset utf-8; echo ok; } --- request GET /t --- response_headers X-Foo: Bar --- response_body ok === TEST 18: Content-Type response headers with a charset param (WRONG -t values) --- config location = /t { more_set_headers -t 'text/html; charset=utf-8' 'X-Foo: Bar'; proxy_pass http://127.0.0.1:$server_port/fake; } location = /fake { default_type text/html; charset utf-8; echo ok; } --- request GET /t --- response_headers X-Foo: Bar --- response_body ok === TEST 19: for bad requests (bad request method letter case) --- config error_page 400 = /err; location = /err { more_set_input_headers "Foo: bar"; echo ok; } --- raw_request GeT / HTTP/1.1 --- response_body ok --- no_check_leak === TEST 20: for bad requests (bad request method names) --- config error_page 400 = /err; location = /err { more_set_input_headers "Foo: bar"; echo ok; } --- raw_request GET x HTTP/1.1 --- response_body ok --- no_check_leak === TEST 21: override Cache-Control header sent by proxy module --- config location = /back { content_by_lua_block { ngx.header['Cache-Control'] = 'max-age=0, no-cache' ngx.send_headers() ngx.say("Cache-Control: ", ngx.var.sent_http_cache_control) } } location = /t { more_set_headers "Cache-Control: max-age=1800"; proxy_pass http://127.0.0.1:$server_port/back; } --- request GET /t --- response_headers Cache-Control: max-age=1800 --- response_body Cache-Control: max-age=0, no-cache headers-more-nginx-module-0.37/t/builtin.t000066400000000000000000000134711454316031200205250ustar00rootroot00000000000000# vi:filetype= use lib 'lib'; use Test::Nginx::Socket; # 'no_plan'; plan tests => 60; no_diff; run_tests(); __DATA__ === TEST 1: set Server --- config location /foo { echo hi; more_set_headers 'Server: Foo'; } --- request GET /foo --- response_headers Server: Foo --- response_body hi === TEST 2: clear Server --- config location /foo { echo hi; more_clear_headers 'Server: '; } --- request GET /foo --- response_headers ! Server --- response_body hi === TEST 3: set Content-Type --- config location /foo { default_type 'text/plan'; more_set_headers 'Content-Type: text/css'; echo hi; } --- request GET /foo --- response_headers Content-Type: text/css --- response_body hi === TEST 4: set Content-Type --- config location /foo { default_type 'text/plan'; more_set_headers 'Content-Type: text/css'; return 404; } --- request GET /foo --- response_headers Content-Type: text/css --- response_body_like: 404 Not Found --- error_code: 404 === TEST 5: clear Content-Type --- config location /foo { default_type 'text/plain'; more_clear_headers 'Content-Type: '; return 404; } --- request GET /foo --- response_headers ! Content-Type --- response_body_like: 404 Not Found --- error_code: 404 === TEST 6: clear Content-Type (colon not required) --- config location /foo { default_type 'text/plain'; more_set_headers 'Content-Type: Hello'; more_clear_headers 'Content-Type'; return 404; } --- request GET /foo --- response_headers ! Content-Type --- response_body_like: 404 Not Found --- error_code: 404 === TEST 7: clear Content-Type (value ignored) --- config location /foo { default_type 'text/plain'; more_set_headers 'Content-Type: Hello'; more_clear_headers 'Content-Type: blah'; return 404; } --- request GET /foo --- response_headers ! Content-Type --- response_body_like: 404 Not Found --- error_code: 404 === TEST 8: clear Content-Type (case insensitive) --- config location /foo { default_type 'text/plain'; more_set_headers 'Content-Type: Hello'; more_clear_headers 'content-type: blah'; return 404; } --- request GET /foo --- response_headers ! Content-Type --- response_body_like: 404 Not Found --- error_code: 404 === TEST 9: clear Content-Type using set empty --- config location /foo { default_type 'text/plain'; more_set_headers 'Content-Type: Hello'; more_set_headers 'content-type:'; return 404; } --- request GET /foo --- response_headers ! Content-Type --- response_body_like: 404 Not Found --- error_code: 404 === TEST 10: clear Content-Type using setting key only --- config location /foo { default_type 'text/plain'; more_set_headers 'Content-Type: Hello'; more_set_headers 'content-type'; return 404; } --- request GET /foo --- response_headers ! Content-Type --- response_body_like: 404 Not Found --- error_code: 404 === TEST 11: set content-length --- config location /len { more_set_headers 'Content-Length: 2'; echo hello; } --- request GET /len --- response_headers Content-Length: 2 --- response_body chop he === TEST 12: set content-length multiple times --- config location /len { more_set_headers 'Content-Length: 2'; more_set_headers 'Content-Length: 4'; echo hello; } --- request GET /len --- response_headers Content-Length: 4 --- response_body chop hell === TEST 13: clear content-length --- config location /len { more_set_headers 'Content-Length: 4'; more_set_headers 'Content-Length:'; echo hello; } --- request GET /len --- response_headers ! Content-Length --- response_body hello === TEST 14: clear content-length (another way) --- config location /len { more_set_headers 'Content-Length: 4'; more_clear_headers 'Content-Length'; echo hello; } --- request GET /len --- response_headers ! Content-Length --- response_body hello === TEST 15: clear content-type --- config location /len { default_type 'text/plain'; more_set_headers 'Content-Type:'; echo hello; } --- request GET /len --- response_headers ! Content-Type --- response_body hello === TEST 16: clear content-type (the other way) --- config location /len { default_type 'text/plain'; more_clear_headers 'Content-Type:'; echo hello; } --- request GET /len --- response_headers ! Content-Type --- response_body hello === TEST 17: set Charset --- config location /len { default_type 'text/plain'; more_set_headers 'Charset: gbk'; echo hello; } --- request GET /len --- response_headers Charset: gbk --- response_body hello === TEST 18: clear Charset --- config location /len { default_type 'text/plain'; more_set_headers 'Charset: gbk'; more_clear_headers 'Charset'; echo hello; } --- request GET /len --- response_headers ! Charset --- response_body hello === TEST 19: clear Charset (the other way: using set) --- config location /len { default_type 'text/plain'; more_set_headers 'Charset: gbk'; more_set_headers 'Charset: '; echo hello; } --- request GET /len --- response_headers ! Charset --- response_body hello === TEST 20: set Vary --- config location /foo { more_set_headers 'Vary: gbk'; echo hello; } location /len { default_type 'text/plain'; more_set_headers 'Vary: hello'; proxy_pass http://127.0.0.1:$server_port/foo; } --- request GET /len --- response_headers Vary: hello --- response_body hello headers-more-nginx-module-0.37/t/eval.t000066400000000000000000000011621454316031200200000ustar00rootroot00000000000000# vi:filetype= use lib 'lib'; use Test::Nginx::Socket; # 'no_plan'; repeat_each(3); plan tests => repeat_each() * 2 * blocks(); #no_long_string(); #no_diff; run_tests(); __DATA__ === TEST 1: set request header at client side --- config location /foo { eval_subrequest_in_memory off; eval_override_content_type text/plain; eval $res { echo -n 1; } #echo "[$res]"; if ($res = '1') { more_set_input_headers 'Foo: Bar'; echo "OK"; break; } echo "NOT OK"; } --- request GET /foo --- response_body OK headers-more-nginx-module-0.37/t/input-conn.t000066400000000000000000000050251454316031200211450ustar00rootroot00000000000000# vim:set ft= ts=4 sw=4 et fdm=marker: use lib 'lib'; use Test::Nginx::Socket; #worker_connections(1014); #master_process_enabled(1); #log_level('warn'); repeat_each(2); plan tests => repeat_each() * (4 * blocks()); #no_diff(); no_long_string(); run_tests(); __DATA__ === TEST 1: clear the Connection req header --- config location /req-header { more_clear_input_headers Connection; echo "connection: $http_connection"; } --- request GET /req-header --- stap F(ngx_http_headers_more_exec_input_cmd) { printf("rewrite: conn type: %d\n", $r->headers_in->connection_type) } F(ngx_http_core_content_phase) { printf("content: conn type: %d\n", $r->headers_in->connection_type) } --- stap_out rewrite: conn type: 1 content: conn type: 0 --- response_body connection: --- no_error_log [error] === TEST 2: set custom Connection req header (close) --- config location /req-header { more_set_input_headers "Connection: CLOSE"; echo "connection: $http_connection"; } --- request GET /req-header --- stap F(ngx_http_headers_more_exec_input_cmd) { printf("rewrite: conn type: %d\n", $r->headers_in->connection_type) } F(ngx_http_core_content_phase) { printf("content: conn type: %d\n", $r->headers_in->connection_type) } --- stap_out rewrite: conn type: 1 content: conn type: 1 --- response_body connection: CLOSE --- no_error_log [error] === TEST 3: set custom Connection req header (keep-alive) --- config location /req-header { more_set_input_headers "Connection: keep-alive"; echo "connection: $http_connection"; } --- request GET /req-header --- stap F(ngx_http_headers_more_exec_input_cmd) { printf("rewrite: conn type: %d\n", $r->headers_in->connection_type) } F(ngx_http_core_content_phase) { printf("content: conn type: %d\n", $r->headers_in->connection_type) } --- stap_out rewrite: conn type: 1 content: conn type: 2 --- response_body connection: keep-alive --- no_error_log [error] === TEST 4: set custom Connection req header (bad) --- config location /req-header { more_set_input_headers "Connection: bad"; echo "connection: $http_connection"; } --- request GET /req-header --- stap F(ngx_http_headers_more_exec_input_cmd) { printf("rewrite: conn type: %d\n", $r->headers_in->connection_type) } F(ngx_http_core_content_phase) { printf("content: conn type: %d\n", $r->headers_in->connection_type) } --- stap_out rewrite: conn type: 1 content: conn type: 0 --- response_body connection: bad --- no_error_log [error] headers-more-nginx-module-0.37/t/input-cookie.t000066400000000000000000000065131454316031200214640ustar00rootroot00000000000000# vim:set ft= ts=4 sw=4 et fdm=marker: use lib 'lib'; use Test::Nginx::Socket; #worker_connections(1014); #master_process_enabled(1); #log_level('warn'); repeat_each(2); plan tests => repeat_each() * (4 * blocks()); #no_diff(); no_long_string(); run_tests(); __DATA__ === TEST 1: clear cookie (with existing cookies) --- config location /t { more_clear_input_headers Cookie; echo "Cookie foo: $cookie_foo"; echo "Cookie baz: $cookie_baz"; echo "Cookie: $http_cookie"; } --- request GET /t --- more_headers Cookie: foo=bar Cookie: baz=blah --- stap F(ngx_http_headers_more_exec_input_cmd) { printf("rewrite: cookies: %d\n", $r->headers_in->cookies->nelts) } F(ngx_http_core_content_phase) { printf("content: cookies: %d\n", $r->headers_in->cookies->nelts) } --- stap_out rewrite: cookies: 2 content: cookies: 0 --- response_body Cookie foo: Cookie baz: Cookie: --- no_error_log [error] === TEST 2: clear cookie (without existing cookies) --- config location /t { more_clear_input_headers Cookie; echo "Cookie foo: $cookie_foo"; echo "Cookie baz: $cookie_baz"; echo "Cookie: $http_cookie"; } --- request GET /t --- stap F(ngx_http_headers_more_exec_input_cmd) { printf("rewrite: cookies: %d\n", $r->headers_in->cookies->nelts) } F(ngx_http_core_content_phase) { printf("content: cookies: %d\n", $r->headers_in->cookies->nelts) } --- stap_out rewrite: cookies: 0 content: cookies: 0 --- response_body Cookie foo: Cookie baz: Cookie: --- no_error_log [error] === TEST 3: set one custom cookie (with existing cookies) --- config location /t { more_set_input_headers "Cookie: boo=123"; echo "Cookie foo: $cookie_foo"; echo "Cookie baz: $cookie_baz"; echo "Cookie boo: $cookie_boo"; echo "Cookie: $http_cookie"; } --- request GET /t --- more_headers Cookie: foo=bar Cookie: baz=blah --- stap F(ngx_http_headers_more_exec_input_cmd) { printf("rewrite: cookies: %d\n", $r->headers_in->cookies->nelts) } F(ngx_http_core_content_phase) { printf("content: cookies: %d\n", $r->headers_in->cookies->nelts) } --- stap_out rewrite: cookies: 2 content: cookies: 1 --- response_body Cookie foo: Cookie baz: Cookie boo: 123 Cookie: boo=123 --- no_error_log [error] === TEST 4: set one custom cookie (without existing cookies) --- config location /t { more_set_input_headers "Cookie: boo=123"; echo "Cookie foo: $cookie_foo"; echo "Cookie baz: $cookie_baz"; echo "Cookie boo: $cookie_boo"; echo "Cookie: $http_cookie"; } --- request GET /t --- stap F(ngx_http_headers_more_exec_input_cmd) { printf("rewrite: cookies: %d\n", $r->headers_in->cookies->nelts) } F(ngx_http_core_content_phase) { printf("content: cookies: %d\n", $r->headers_in->cookies->nelts) } --- stap_out rewrite: cookies: 0 content: cookies: 1 --- response_body Cookie foo: Cookie baz: Cookie boo: 123 Cookie: boo=123 --- no_error_log [error] === TEST 5: for bad requests causing segfaults when setting & getting multi-value headers --- config error_page 400 = /err; location = /err { more_set_input_headers "Cookie: foo=bar"; echo -n $cookie_foo; echo ok; } --- raw_request GeT / HTTP/1.1 --- response_body ok --- no_error_log [warn] [error] --- no_check_leak headers-more-nginx-module-0.37/t/input-ua.t000066400000000000000000000311221454316031200206120ustar00rootroot00000000000000# vim:set ft= ts=4 sw=4 et fdm=marker: use lib 'lib'; use Test::Nginx::Socket; #worker_connections(1014); #master_process_enabled(1); #log_level('warn'); repeat_each(2); plan tests => repeat_each() * (4 * blocks()); #no_diff(); no_long_string(); run_tests(); __DATA__ === TEST 1: clear Opera user-agent --- config location /t { more_clear_input_headers User-Agent; echo "User-Agent: $http_user_agent"; } --- request GET /t --- more_headers User-Agent: Opera/9.80 (Macintosh; Intel Mac OS X 10.7.4; U; en) Presto/2.10.229 Version/11.62 --- stap F(ngx_http_headers_more_exec_input_cmd) { printf("rewrite: opera: %d\n", $r->headers_in->opera) } F(ngx_http_core_content_phase) { printf("content: opera: %d\n", $r->headers_in->opera) } --- stap_out rewrite: opera: 1 content: opera: 0 --- response_body User-Agent: --- no_error_log [error] === TEST 2: clear MSIE 4 user-agent --- config location /t { more_clear_input_headers User-Agent; echo "User-Agent: $http_user_agent"; } --- request GET /t --- more_headers User-Agent: Mozilla/4.0 (compatible; MSIE 4.01; Windows NT 5.0) --- stap F(ngx_http_headers_more_exec_input_cmd) { printf("rewrite: msie=%d msie6=%d\n", $r->headers_in->msie, $r->headers_in->msie6) } F(ngx_http_core_content_phase) { printf("content: msie=%d msie6=%d\n", $r->headers_in->msie, $r->headers_in->msie6) } --- stap_out rewrite: msie=1 msie6=1 content: msie=0 msie6=0 --- response_body User-Agent: --- no_error_log [error] === TEST 3: set custom MSIE 4 user-agent --- config location /t { more_set_input_headers "User-Agent: Mozilla/4.0 (compatible; MSIE 4.01; Windows NT 5.0)"; echo "User-Agent: $http_user_agent"; } --- request GET /t --- stap F(ngx_http_headers_more_exec_input_cmd) { printf("rewrite: msie=%d msie6=%d\n", $r->headers_in->msie, $r->headers_in->msie6) } F(ngx_http_core_content_phase) { printf("content: msie=%d msie6=%d\n", $r->headers_in->msie, $r->headers_in->msie6) } --- stap_out rewrite: msie=0 msie6=0 content: msie=1 msie6=1 --- response_body User-Agent: Mozilla/4.0 (compatible; MSIE 4.01; Windows NT 5.0) --- no_error_log [error] === TEST 4: clear MSIE 5 user-agent --- config location /t { more_clear_input_headers User-Agent; echo "User-Agent: $http_user_agent"; } --- request GET /t --- more_headers User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows 95; MSIECrawler) --- stap F(ngx_http_headers_more_exec_input_cmd) { printf("rewrite: msie=%d msie6=%d\n", $r->headers_in->msie, $r->headers_in->msie6) } F(ngx_http_core_content_phase) { printf("content: msie=%d msie6=%d\n", $r->headers_in->msie, $r->headers_in->msie6) } --- stap_out rewrite: msie=1 msie6=1 content: msie=0 msie6=0 --- response_body User-Agent: --- no_error_log [error] === TEST 5: set custom MSIE 5 user-agent --- config location /t { more_set_input_headers "User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows 95; MSIECrawler)"; echo "User-Agent: $http_user_agent"; } --- request GET /t --- stap F(ngx_http_headers_more_exec_input_cmd) { printf("rewrite: msie=%d msie6=%d\n", $r->headers_in->msie, $r->headers_in->msie6) } F(ngx_http_core_content_phase) { printf("content: msie=%d msie6=%d\n", $r->headers_in->msie, $r->headers_in->msie6) } --- stap_out rewrite: msie=0 msie6=0 content: msie=1 msie6=1 --- response_body User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows 95; MSIECrawler) --- no_error_log [error] === TEST 6: clear MSIE 6 (without SV1) user-agent --- config location /t { more_clear_input_headers User-Agent; echo "User-Agent: $http_user_agent"; } --- request GET /t --- more_headers User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; Google Wireless Transcoder;) --- stap F(ngx_http_headers_more_exec_input_cmd) { printf("rewrite: msie=%d msie6=%d\n", $r->headers_in->msie, $r->headers_in->msie6) } F(ngx_http_core_content_phase) { printf("content: msie=%d msie6=%d\n", $r->headers_in->msie, $r->headers_in->msie6) } --- stap_out rewrite: msie=1 msie6=1 content: msie=0 msie6=0 --- response_body User-Agent: --- no_error_log [error] === TEST 7: set custom MSIE 6 (without SV1) user-agent --- config location /t { more_set_input_headers "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; Google Wireless Transcoder;)"; echo "User-Agent: $http_user_agent"; } --- request GET /t --- stap F(ngx_http_headers_more_exec_input_cmd) { printf("rewrite: msie=%d msie6=%d\n", $r->headers_in->msie, $r->headers_in->msie6) } F(ngx_http_core_content_phase) { printf("content: msie=%d msie6=%d\n", $r->headers_in->msie, $r->headers_in->msie6) } --- stap_out rewrite: msie=0 msie6=0 content: msie=1 msie6=1 --- response_body User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; Google Wireless Transcoder;) --- no_error_log [error] === TEST 8: clear MSIE 6 (with SV1) user-agent --- config location /t { more_clear_input_headers User-Agent; echo "User-Agent: $http_user_agent"; } --- request GET /t --- more_headers User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; InfoPath.1) --- stap F(ngx_http_headers_more_exec_input_cmd) { printf("rewrite: msie=%d msie6=%d\n", $r->headers_in->msie, $r->headers_in->msie6) } F(ngx_http_core_content_phase) { printf("content: msie=%d msie6=%d\n", $r->headers_in->msie, $r->headers_in->msie6) } --- stap_out rewrite: msie=1 msie6=0 content: msie=0 msie6=0 --- response_body User-Agent: --- no_error_log [error] === TEST 9: set custom MSIE 6 (with SV1) user-agent --- config location /t { more_set_input_headers "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; InfoPath.1)"; echo "User-Agent: $http_user_agent"; } --- request GET /t --- stap F(ngx_http_headers_more_exec_input_cmd) { printf("rewrite: msie=%d msie6=%d\n", $r->headers_in->msie, $r->headers_in->msie6) } F(ngx_http_core_content_phase) { printf("content: msie=%d msie6=%d\n", $r->headers_in->msie, $r->headers_in->msie6) } --- stap_out rewrite: msie=0 msie6=0 content: msie=1 msie6=0 --- response_body User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; InfoPath.1) --- no_error_log [error] === TEST 10: set custom MSIE 7 user-agent --- config location /t { more_set_input_headers "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; winfx; .NET CLR 1.1.4322; .NET CLR 2.0.50727; Zune 2.0)"; echo "User-Agent: $http_user_agent"; } --- request GET /t --- stap F(ngx_http_headers_more_exec_input_cmd) { printf("rewrite: msie=%d msie6=%d\n", $r->headers_in->msie, $r->headers_in->msie6) } F(ngx_http_core_content_phase) { printf("content: msie=%d msie6=%d\n", $r->headers_in->msie, $r->headers_in->msie6) } --- stap_out rewrite: msie=0 msie6=0 content: msie=1 msie6=0 --- response_body User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; winfx; .NET CLR 1.1.4322; .NET CLR 2.0.50727; Zune 2.0) --- no_error_log [error] === TEST 11: clear Gecko user-agent --- config location /t { more_clear_input_headers User-Agent; echo "User-Agent: $http_user_agent"; } --- request GET /t --- more_headers User-Agent: Mozilla/5.0 (Android; Mobile; rv:13.0) Gecko/13.0 Firefox/13.0 --- stap F(ngx_http_headers_more_exec_input_cmd) { printf("rewrite: gecko: %d\n", $r->headers_in->gecko) } F(ngx_http_core_content_phase) { printf("content: gecko: %d\n", $r->headers_in->gecko) } --- stap_out rewrite: gecko: 1 content: gecko: 0 --- response_body User-Agent: --- no_error_log [error] === TEST 12: set custom Gecko user-agent --- config location /t { more_set_input_headers "User-Agent: Mozilla/5.0 (Android; Mobile; rv:13.0) Gecko/13.0 Firefox/13.0"; echo "User-Agent: $http_user_agent"; } --- request GET /t --- stap F(ngx_http_headers_more_exec_input_cmd) { printf("rewrite: gecko: %d\n", $r->headers_in->gecko) } F(ngx_http_core_content_phase) { printf("content: gecko: %d\n", $r->headers_in->gecko) } --- stap_out rewrite: gecko: 0 content: gecko: 1 --- response_body User-Agent: Mozilla/5.0 (Android; Mobile; rv:13.0) Gecko/13.0 Firefox/13.0 --- no_error_log [error] === TEST 13: clear Chrome user-agent --- config location /t { more_clear_input_headers User-Agent; echo "User-Agent: $http_user_agent"; } --- request GET /t --- more_headers User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.151 Safari/535.19 --- stap F(ngx_http_headers_more_exec_input_cmd) { printf("rewrite: chrome: %d\n", $r->headers_in->chrome) } F(ngx_http_core_content_phase) { printf("content: chrome: %d\n", $r->headers_in->chrome) } --- stap_out rewrite: chrome: 1 content: chrome: 0 --- response_body User-Agent: --- no_error_log [error] === TEST 14: set custom Chrome user-agent --- config location /t { more_set_input_headers "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.151 Safari/535.19"; echo "User-Agent: $http_user_agent"; } --- request GET /t --- stap F(ngx_http_headers_more_exec_input_cmd) { printf("rewrite: chrome: %d\n", $r->headers_in->chrome) } F(ngx_http_core_content_phase) { printf("content: chrome: %d\n", $r->headers_in->chrome) } --- stap_out rewrite: chrome: 0 content: chrome: 1 --- response_body User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.151 Safari/535.19 --- no_error_log [error] === TEST 15: clear Safari (Mac OS X) user-agent --- config location /t { more_clear_input_headers User-Agent; echo "User-Agent: $http_user_agent"; } --- request GET /t --- more_headers User-Agent: Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/125.2 (KHTML, like Gecko) Safari/125.8 --- stap F(ngx_http_headers_more_exec_input_cmd) { printf("rewrite: safari: %d\n", $r->headers_in->safari) } F(ngx_http_core_content_phase) { printf("content: safari: %d\n", $r->headers_in->safari) } --- stap_out rewrite: safari: 1 content: safari: 0 --- response_body User-Agent: --- no_error_log [error] === TEST 16: set custom Safari user-agent --- config location /t { more_set_input_headers "User-Agent: Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/125.2 (KHTML, like Gecko) Safari/125.8"; echo "User-Agent: $http_user_agent"; } --- request GET /t --- stap F(ngx_http_headers_more_exec_input_cmd) { printf("rewrite: safari: %d\n", $r->headers_in->safari) } F(ngx_http_core_content_phase) { printf("content: safari: %d\n", $r->headers_in->safari) } --- stap_out rewrite: safari: 0 content: safari: 1 --- response_body User-Agent: Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/125.2 (KHTML, like Gecko) Safari/125.8 --- no_error_log [error] === TEST 17: clear Konqueror user-agent --- config location /t { more_clear_input_headers User-Agent; echo "User-Agent: $http_user_agent"; } --- request GET /t --- more_headers User-Agent: Mozilla/5.0 (compatible; Konqueror/3.5; Linux) KHTML/3.5.10 (like Gecko) (Kubuntu) --- stap F(ngx_http_headers_more_exec_input_cmd) { printf("rewrite: konqueror: %d\n", $r->headers_in->konqueror) } F(ngx_http_core_content_phase) { printf("content: konqueror: %d\n", $r->headers_in->konqueror) } --- stap_out rewrite: konqueror: 1 content: konqueror: 0 --- response_body User-Agent: --- no_error_log [error] === TEST 18: set custom Konqueror user-agent --- config location /t { more_set_input_headers "User-Agent: Mozilla/5.0 (compatible; Konqueror/3.5; Linux) KHTML/3.5.10 (like Gecko) (Kubuntu)"; echo "User-Agent: $http_user_agent"; } --- request GET /t --- stap F(ngx_http_headers_more_exec_input_cmd) { printf("rewrite: konqueror: %d\n", $r->headers_in->konqueror) } F(ngx_http_core_content_phase) { printf("content: konqueror: %d\n", $r->headers_in->konqueror) } --- stap_out rewrite: konqueror: 0 content: konqueror: 1 --- response_body User-Agent: Mozilla/5.0 (compatible; Konqueror/3.5; Linux) KHTML/3.5.10 (like Gecko) (Kubuntu) --- no_error_log [error] headers-more-nginx-module-0.37/t/input.t000066400000000000000000000613301454316031200202130ustar00rootroot00000000000000# vi:filetype= use lib 'lib'; use Test::Nginx::Socket; # 'no_plan'; repeat_each(2); plan tests => repeat_each() * 128; no_long_string(); #no_diff; run_tests(); __DATA__ === TEST 1: set request header at client side --- config location /foo { #more_set_input_headers 'X-Foo: howdy'; echo $http_x_foo; } --- request GET /foo --- more_headers X-Foo: blah --- response_headers ! X-Foo --- response_body blah === TEST 2: set request header at client side and rewrite it --- config location /foo { more_set_input_headers 'X-Foo: howdy'; echo $http_x_foo; } --- request GET /foo --- more_headers X-Foo: blah --- response_headers ! X-Foo --- response_body howdy === TEST 3: rewrite content length --- config location /bar { more_set_input_headers 'Content-Length: 2048'; echo_read_request_body; echo_request_body; } --- request eval "POST /bar\n" . "a" x 4096 --- response_body eval "a" x 2048 --- timeout: 15 === TEST 4: try to rewrite content length using the rewrite module Thisshould not take effect ;) --- config location /bar { set $http_content_length 2048; echo_read_request_body; echo_request_body; } --- request eval "POST /bar\n" . "a" x 4096 --- response_body eval "a" x 4096 === TEST 5: rewrite host and user-agent --- config location /bar { more_set_input_headers 'Host: foo' 'User-Agent: blah'; echo "Host: $host"; echo "User-Agent: $http_user_agent"; } --- request GET /bar --- response_body Host: foo User-Agent: blah === TEST 6: clear host and user-agent $host always has a default value and cannot be really cleared. --- config location /bar { more_clear_input_headers 'Host: foo' 'User-Agent: blah'; echo "Host: $host"; echo "Host (2): $http_host"; echo "User-Agent: $http_user_agent"; } --- request GET /bar --- response_body Host: localhost Host (2): User-Agent: === TEST 7: clear host and user-agent (the other way) --- config location /bar { more_set_input_headers 'Host:' 'User-Agent:' 'X-Foo:'; echo "Host: $host"; echo "User-Agent: $http_user_agent"; echo "X-Foo: $http_x_foo"; } --- request GET /bar --- more_headers X-Foo: bar --- response_body Host: localhost User-Agent: X-Foo: === TEST 8: clear content-length --- config location /bar { more_set_input_headers 'Content-Length: '; echo "Content-Length: $http_content_length"; } --- request POST /bar hello --- more_headers --- response_body Content-Length: === TEST 9: clear content-length (the other way) --- config location /bar { more_clear_input_headers 'Content-Length: '; echo "Content-Length: $http_content_length"; } --- request POST /bar hello --- more_headers --- response_body Content-Length: === TEST 10: rewrite type --- config location /bar { more_set_input_headers 'Content-Type: text/css'; echo "Content-Type: $content_type"; } --- request POST /bar hello --- more_headers Content-Type: text/plain --- response_body Content-Type: text/css === TEST 11: clear type --- config location /bar { more_set_input_headers 'Content-Type:'; echo "Content-Type: $content_type"; } --- request POST /bar hello --- more_headers Content-Type: text/plain --- response_body Content-Type: === TEST 12: clear type (the other way) --- config location /bar { more_clear_input_headers 'Content-Type:foo'; echo "Content-Type: $content_type"; } --- request POST /bar hello --- more_headers Content-Type: text/plain --- response_body Content-Type: === TEST 13: add type constraints --- config location /bar { more_set_input_headers -t 'text/plain' 'X-Blah:yay'; echo $http_x_blah; } --- request POST /bar hello --- more_headers Content-Type: text/plain --- response_body yay === TEST 14: add type constraints (not matched) --- config location /bar { more_set_input_headers -t 'text/plain' 'X-Blah:yay'; echo $http_x_blah; } --- request POST /bar hello --- more_headers Content-Type: text/css --- response_body eval: "\n" === TEST 15: add type constraints (OR'd) --- config location /bar { more_set_input_headers -t 'text/plain text/css' 'X-Blah:yay'; echo $http_x_blah; } --- request POST /bar hello --- more_headers Content-Type: text/css --- response_body yay === TEST 16: add type constraints (OR'd) --- config location /bar { more_set_input_headers -t 'text/plain text/css' 'X-Blah:yay'; echo $http_x_blah; } --- request POST /bar hello --- more_headers Content-Type: text/plain --- response_body yay === TEST 17: add type constraints (OR'd) (not matched) --- config location /bar { more_set_input_headers -t 'text/plain text/css' 'X-Blah:yay'; echo $http_x_blah; } --- request POST /bar hello --- more_headers Content-Type: text/html --- response_body eval: "\n" === TEST 18: mix input and output cmds --- config location /bar { more_set_input_headers 'X-Blah:yay'; more_set_headers 'X-Blah:hiya'; echo $http_x_blah; } --- request GET /bar --- response_headers X-Blah: hiya --- response yay === TEST 19: set request header at client side and replace --- config location /foo { more_set_input_headers -r 'X-Foo: howdy'; echo $http_x_foo; } --- request GET /foo --- more_headers X-Foo: blah --- response_headers ! X-Foo --- response_body howdy === TEST 20: do no set request header at client, so no replace with -r option --- config location /foo { more_set_input_headers -r 'X-Foo: howdy'; echo "empty_header:" $http_x_foo; } --- request GET /foo --- response_headers ! X-Foo --- response_body empty_header: === TEST 21: clear input headers --- config location /foo { set $val 'dog'; more_clear_input_headers 'User-Agent'; proxy_pass http://127.0.0.1:$server_port/proxy; } location /proxy { echo -n $echo_client_request_headers; } --- request GET /foo --- more_headers User-Agent: my-sock --- response_body eval "GET /proxy HTTP/1.0\r Host: 127.0.0.1:\$ServerPort\r Connection: close\r \r " --- skip_nginx: 3: < 0.7.46 === TEST 22: clear input headers --- config location /foo { more_clear_input_headers 'User-Agent'; proxy_pass http://127.0.0.1:$server_port/proxy; } location /proxy { echo -n $echo_client_request_headers; } --- request GET /foo --- response_body eval "GET /proxy HTTP/1.0\r Host: 127.0.0.1:\$ServerPort\r Connection: close\r \r " --- skip_nginx: 3: < 0.7.46 === TEST 23: clear input headers --- config location /foo { more_clear_input_headers 'X-Foo19'; more_clear_input_headers 'X-Foo20'; more_clear_input_headers 'X-Foo21'; proxy_pass http://127.0.0.1:$server_port/proxy; } location /proxy { echo -n $echo_client_request_headers; } --- request GET /foo --- more_headers eval my $s; for my $i (3..21) { $s .= "X-Foo$i: $i\n"; } $s; --- response_body eval "GET /proxy HTTP/1.0\r Host: 127.0.0.1:\$ServerPort\r Connection: close\r X-Foo3: 3\r X-Foo4: 4\r X-Foo5: 5\r X-Foo6: 6\r X-Foo7: 7\r X-Foo8: 8\r X-Foo9: 9\r X-Foo10: 10\r X-Foo11: 11\r X-Foo12: 12\r X-Foo13: 13\r X-Foo14: 14\r X-Foo15: 15\r X-Foo16: 16\r X-Foo17: 17\r X-Foo18: 18\r \r " --- skip_nginx: 3: < 0.7.46 === TEST 24: Accept-Encoding --- config location /bar { default_type 'text/plain'; more_set_input_headers 'Accept-Encoding: gzip'; gzip on; gzip_min_length 1; gzip_buffers 4 8k; gzip_types text/plain; } --- user_files ">>> bar " . ("hello" x 512) --- request GET /bar --- response_headers Content-Encoding: gzip --- response_body_like: . === TEST 25: rewrite + set request header --- config location /t { rewrite ^ /foo last; } location /foo { more_set_input_headers 'X-Foo: howdy'; proxy_pass http://127.0.0.1:$server_port/echo; } location /echo { echo "X-Foo: $http_x_foo"; } --- request GET /foo --- response_headers ! X-Foo --- response_body X-Foo: howdy === TEST 26: clear_header should clear all the instances of the user custom header --- config location = /t { more_clear_input_headers Foo; proxy_pass http://127.0.0.1:$server_port/echo; } location = /echo { echo "Foo: [$http_foo]"; echo "Test-Header: [$http_test_header]"; } --- request GET /t --- more_headers Foo: foo Foo: bah Test-Header: 1 --- response_body Foo: [] Test-Header: [1] === TEST 27: clear_header should clear all the instances of the builtin header --- config location = /t { more_clear_input_headers Content-Type; proxy_pass http://127.0.0.1:$server_port/echo; } location = /echo { echo "Content-Type: [$http_content_type]"; echo "Test-Header: [$http_test_header]"; #echo $echo_client_request_headers; } --- request GET /t --- more_headers Content-Type: foo Content-Type: bah Test-Header: 1 --- response_body Content-Type: [] Test-Header: [1] === TEST 28: Converting POST to GET - clearing headers (bug found by Matthieu Tourne, 411 error page) --- config location /t { more_clear_input_headers Content-Type; more_clear_input_headers Content-Length; #proxy_pass http://127.0.0.1:8888; proxy_pass http://127.0.0.1:$server_port/back; } location /back { echo $echo_client_request_headers; } --- request POST /t hello world --- more_headers Content-Type: application/ocsp-request Test-Header: 1 --- response_body_like eval qr/Connection: close\r Test-Header: 1\r \r $/ --- no_error_log [error] === TEST 29: clear_header() does not duplicate subsequent headers (old bug) --- config location = /t { more_clear_input_headers Foo; proxy_pass http://127.0.0.1:$server_port/echo; } location = /echo { echo $echo_client_request_headers; } --- request GET /t --- more_headers Bah: bah Foo: foo Test-Header: 1 Foo1: foo1 Foo2: foo2 Foo3: foo3 Foo4: foo4 Foo5: foo5 Foo6: foo6 Foo7: foo7 Foo8: foo8 Foo9: foo9 Foo10: foo10 Foo11: foo11 Foo12: foo12 Foo13: foo13 Foo14: foo14 Foo15: foo15 Foo16: foo16 Foo17: foo17 Foo18: foo18 Foo19: foo19 Foo20: foo20 Foo21: foo21 Foo22: foo22 --- response_body_like eval qr/Bah: bah\r Test-Header: 1\r Foo1: foo1\r Foo2: foo2\r Foo3: foo3\r Foo4: foo4\r Foo5: foo5\r Foo6: foo6\r Foo7: foo7\r Foo8: foo8\r Foo9: foo9\r Foo10: foo10\r Foo11: foo11\r Foo12: foo12\r Foo13: foo13\r Foo14: foo14\r Foo15: foo15\r Foo16: foo16\r Foo17: foo17\r Foo18: foo18\r Foo19: foo19\r Foo20: foo20\r Foo21: foo21\r Foo22: foo22\r / === TEST 30: clear input header (just more than 20 headers) --- config location = /t { more_clear_input_headers "R"; proxy_pass http://127.0.0.1:$server_port/back; proxy_set_header Host foo; #proxy_pass http://127.0.0.1:1234/back; } location = /back { echo -n $echo_client_request_headers; } --- request GET /t --- more_headers eval my $s = "User-Agent: curl\n"; for my $i ('a' .. 'r') { $s .= uc($i) . ": " . "$i\n" } $s --- response_body eval "GET /back HTTP/1.0\r Host: foo\r Connection: close\r User-Agent: curl\r A: a\r B: b\r C: c\r D: d\r E: e\r F: f\r G: g\r H: h\r I: i\r J: j\r K: k\r L: l\r M: m\r N: n\r O: o\r P: p\r Q: q\r \r " === TEST 31: clear input header (just more than 20 headers, and add more) --- config location = /t { more_clear_input_headers R; more_set_input_headers "foo-1: 1" "foo-2: 2" "foo-3: 3" "foo-4: 4" "foo-5: 5" "foo-6: 6" "foo-7: 7" "foo-8: 8" "foo-9: 9" "foo-10: 10" "foo-11: 11" "foo-12: 12" "foo-13: 13" "foo-14: 14" "foo-15: 15" "foo-16: 16" "foo-17: 17" "foo-18: 18" "foo-19: 19" "foo-20: 20" "foo-21: 21"; proxy_pass http://127.0.0.1:$server_port/back; proxy_set_header Host foo; #proxy_pass http://127.0.0.1:1234/back; } location = /back { echo -n $echo_client_request_headers; } --- request GET /t --- more_headers eval my $s = "User-Agent: curl\n"; for my $i ('a' .. 'r') { $s .= uc($i) . ": " . "$i\n" } $s --- response_body eval "GET /back HTTP/1.0\r Host: foo\r Connection: close\r User-Agent: curl\r A: a\r B: b\r C: c\r D: d\r E: e\r F: f\r G: g\r H: h\r I: i\r J: j\r K: k\r L: l\r M: m\r N: n\r O: o\r P: p\r Q: q\r foo-1: 1\r foo-2: 2\r foo-3: 3\r foo-4: 4\r foo-5: 5\r foo-6: 6\r foo-7: 7\r foo-8: 8\r foo-9: 9\r foo-10: 10\r foo-11: 11\r foo-12: 12\r foo-13: 13\r foo-14: 14\r foo-15: 15\r foo-16: 16\r foo-17: 17\r foo-18: 18\r foo-19: 19\r foo-20: 20\r foo-21: 21\r \r " === TEST 32: clear input header (just more than 21 headers) --- config location = /t { more_clear_input_headers R Q; proxy_pass http://127.0.0.1:$server_port/back; proxy_set_header Host foo; #proxy_pass http://127.0.0.1:1234/back; } location = /back { echo -n $echo_client_request_headers; } --- request GET /t --- more_headers eval my $s = "User-Agent: curl\nBah: bah\n"; for my $i ('a' .. 'r') { $s .= uc($i) . ": " . "$i\n" } $s --- response_body eval "GET /back HTTP/1.0\r Host: foo\r Connection: close\r User-Agent: curl\r Bah: bah\r A: a\r B: b\r C: c\r D: d\r E: e\r F: f\r G: g\r H: h\r I: i\r J: j\r K: k\r L: l\r M: m\r N: n\r O: o\r P: p\r \r " === TEST 33: clear input header (just more than 21 headers) --- config location = /t { more_clear_input_headers R Q; more_set_input_headers "foo-1: 1" "foo-2: 2" "foo-3: 3" "foo-4: 4" "foo-5: 5" "foo-6: 6" "foo-7: 7" "foo-8: 8" "foo-9: 9" "foo-10: 10" "foo-11: 11" "foo-12: 12" "foo-13: 13" "foo-14: 14" "foo-15: 15" "foo-16: 16" "foo-17: 17" "foo-18: 18" "foo-19: 19" "foo-20: 20" "foo-21: 21"; proxy_pass http://127.0.0.1:$server_port/back; proxy_set_header Host foo; #proxy_pass http://127.0.0.1:1234/back; } location = /back { echo -n $echo_client_request_headers; } --- request GET /t --- more_headers eval my $s = "User-Agent: curl\nBah: bah\n"; for my $i ('a' .. 'r') { $s .= uc($i) . ": " . "$i\n" } $s --- response_body eval "GET /back HTTP/1.0\r Host: foo\r Connection: close\r User-Agent: curl\r Bah: bah\r A: a\r B: b\r C: c\r D: d\r E: e\r F: f\r G: g\r H: h\r I: i\r J: j\r K: k\r L: l\r M: m\r N: n\r O: o\r P: p\r foo-1: 1\r foo-2: 2\r foo-3: 3\r foo-4: 4\r foo-5: 5\r foo-6: 6\r foo-7: 7\r foo-8: 8\r foo-9: 9\r foo-10: 10\r foo-11: 11\r foo-12: 12\r foo-13: 13\r foo-14: 14\r foo-15: 15\r foo-16: 16\r foo-17: 17\r foo-18: 18\r foo-19: 19\r foo-20: 20\r foo-21: 21\r \r " === TEST 34: clear X-Real-IP --- config location /t { more_clear_input_headers X-Real-IP; echo "X-Real-IP: $http_x_real_ip"; } --- request GET /t --- more_headers X-Real-IP: 8.8.8.8 --- stap F(ngx_http_headers_more_exec_input_cmd) { if (@defined($r->headers_in->x_real_ip) && $r->headers_in->x_real_ip) { printf("rewrite: x-real-ip: %s\n", user_string_n($r->headers_in->x_real_ip->value->data, $r->headers_in->x_real_ip->value->len)) } else { println("rewrite: no x-real-ip") } } F(ngx_http_core_content_phase) { if (@defined($r->headers_in->x_real_ip) && $r->headers_in->x_real_ip) { printf("content: x-real-ip: %s\n", user_string_n($r->headers_in->x_real_ip->value->data, $r->headers_in->x_real_ip->value->len)) } else { println("content: no x-real-ip") } } --- stap_out rewrite: x-real-ip: 8.8.8.8 content: no x-real-ip --- response_body X-Real-IP: --- no_error_log [error] === TEST 35: set custom X-Real-IP --- config location /t { more_set_input_headers "X-Real-IP: 8.8.4.4"; echo "X-Real-IP: $http_x_real_ip"; } --- request GET /t --- stap F(ngx_http_headers_more_exec_input_cmd) { if (@defined($r->headers_in->x_real_ip) && $r->headers_in->x_real_ip) { printf("rewrite: x-real-ip: %s\n", user_string_n($r->headers_in->x_real_ip->value->data, $r->headers_in->x_real_ip->value->len)) } else { println("rewrite: no x-real-ip") } } F(ngx_http_core_content_phase) { if (@defined($r->headers_in->x_real_ip) && $r->headers_in->x_real_ip) { printf("content: x-real-ip: %s\n", user_string_n($r->headers_in->x_real_ip->value->data, $r->headers_in->x_real_ip->value->len)) } else { println("content: no x-real-ip") } } --- stap_out rewrite: no x-real-ip content: x-real-ip: 8.8.4.4 --- response_body X-Real-IP: 8.8.4.4 --- no_error_log [error] === TEST 36: clear Via --- config location /t { more_clear_input_headers Via; echo "Via: $http_via"; } --- request GET /t --- more_headers Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1) --- stap F(ngx_http_headers_more_exec_input_cmd) { if (@defined($r->headers_in->via) && $r->headers_in->via) { printf("rewrite: via: %s\n", user_string_n($r->headers_in->via->value->data, $r->headers_in->via->value->len)) } else { println("rewrite: no via") } } F(ngx_http_core_content_phase) { if (@defined($r->headers_in->via) && $r->headers_in->via) { printf("content: via: %s\n", user_string_n($r->headers_in->via->value->data, $r->headers_in->via->value->len)) } else { println("content: no via") } } --- stap_out rewrite: via: 1.0 fred, 1.1 nowhere.com (Apache/1.1) content: no via --- response_body Via: --- no_error_log [error] === TEST 37: set custom Via --- config location /t { more_set_input_headers "Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1)"; echo "Via: $http_via"; } --- request GET /t --- stap F(ngx_http_headers_more_exec_input_cmd) { if (@defined($r->headers_in->via) && $r->headers_in->via) { printf("rewrite: via: %s\n", user_string_n($r->headers_in->via->value->data, $r->headers_in->via->value->len)) } else { println("rewrite: no via") } } F(ngx_http_core_content_phase) { if (@defined($r->headers_in->via) && $r->headers_in->via) { printf("content: via: %s\n", user_string_n($r->headers_in->via->value->data, $r->headers_in->via->value->len)) } else { println("content: no via") } } --- stap_out rewrite: no via content: via: 1.0 fred, 1.1 nowhere.com (Apache/1.1) --- response_body Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1) --- no_error_log [error] === TEST 38: HTTP 0.9 (set) --- config location /foo { more_set_input_headers 'X-Foo: howdy'; echo "x-foo: $http_x_foo"; } --- raw_request eval "GET /foo\r\n" --- response_headers ! X-Foo --- response_body x-foo: --- http09 === TEST 39: HTTP 0.9 (clear) --- config location /foo { more_clear_input_headers 'X-Foo'; echo "x-foo: $http_x_foo"; } --- raw_request eval "GET /foo\r\n" --- response_headers ! X-Foo --- response_body x-foo: --- http09 === TEST 40: Host header with port and $host --- config location /bar { more_set_input_headers 'Host: agentzh.org:1984'; echo "host var: $host"; echo "http_host var: $http_host"; } --- request GET /bar --- response_body host var: agentzh.org http_host var: agentzh.org:1984 === TEST 41: Host header with upper case letters and $host --- config location /bar { more_set_input_headers 'Host: agentZH.org:1984'; echo "host var: $host"; echo "http_host var: $http_host"; } --- request GET /bar --- response_body host var: agentzh.org http_host var: agentZH.org:1984 === TEST 42: clear all and re-insert --- config location = /t { more_clear_input_headers Host Connection Cache-Control Accept User-Agent Accept-Encoding Accept-Language Cookie; more_set_input_headers "Host: a" "Connection: b" "Cache-Control: c" "Accept: d" "User-Agent: e" "Accept-Encoding: f" "Accept-Language: g" "Cookie: h"; more_clear_input_headers Host Connection Cache-Control Accept User-Agent Accept-Encoding Accept-Language Cookie; more_set_input_headers "Host: a" "Connection: b" "Cache-Control: c" "Accept: d" "User-Agent: e" "Accept-Encoding: f" "Accept-Language: g" "Cookie: h"; echo ok; } --- raw_request eval "GET /t HTTP/1.1\r Host: localhost\r Connection: close\r Cache-Control: max-age=0\r Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36\r Accept-Encoding: gzip,deflate,sdch\r Accept-Language: en-US,en;q=0.8\r Cookie: test=cookie;\r \r " --- response_body ok --- no_error_log [error] === TEST 43: more_set_input_header does not override request headers with multiple values --- config #lua_code_cache off; location = /t { more_set_input_headers "AAA: 111"; content_by_lua ' local headers = ngx.req.get_headers() ngx.say(headers["AAA"]) '; } --- request GET /t --- more_headers AAA: 123 AAA: 456 AAA: 678 --- response_body 111 --- no_error_log [error] === TEST 44: clear If-Unmodified-Since req header --- config location = /t { more_clear_input_headers 'If-Unmodified-Since'; content_by_lua ' ngx.header["Last-Modified"] = "Tue, 30 Jun 2011 12:16:36 GMT" ngx.say("If-Unmodified-Since: ", ngx.var.http_if_unmodified_since) '; } --- request GET /t --- more_headers If-Unmodified-Since: Tue, 28 Jun 2011 12:16:36 GMT --- response_body If-Unmodified-Since: nil --- no_error_log [error] === TEST 45: clear If-Match req header --- config location = /t { more_clear_input_headers 'If-Match'; echo "If-Match: $http_if_match"; } --- request GET /t --- more_headers If-Match: abc --- response_body If-Match: --- no_error_log [error] === TEST 46: clear If-None-Match req header --- config location = /t { more_clear_input_headers 'If-None-Match'; echo "If-None-Match: $http_if_none_match"; } --- request GET /t --- more_headers If-None-Match: * --- response_body If-None-Match: --- no_error_log [error] === TEST 47: set the Destination request header for WebDav --- config location = /a.txt { more_set_input_headers "Destination: /b.txt"; dav_methods MOVE; dav_access all:rw; root html; } --- user_files >>> a.txt hello, world! --- request MOVE /a.txt --- response_body --- no_error_log client sent no "Destination" header [error] --- error_code: 204 === TEST 48: more_set_input_headers + X-Forwarded-For --- config location = /t { more_set_input_headers "X-Forwarded-For: 8.8.8.8"; proxy_pass http://127.0.0.1:$server_port/back; proxy_set_header Foo $proxy_add_x_forwarded_for; } location = /back { echo "Foo: $http_foo"; } --- request GET /t --- response_body Foo: 8.8.8.8, 127.0.0.1 --- no_error_log [error] === TEST 49: more_clear_input_headers + X-Forwarded-For --- config location = /t { more_clear_input_headers "X-Forwarded-For"; proxy_pass http://127.0.0.1:$server_port/back; proxy_set_header Foo $proxy_add_x_forwarded_for; } location = /back { echo "Foo: $http_foo"; } --- request GET /t --- more_headers X-Forwarded-For: 8.8.8.8 --- response_body Foo: 127.0.0.1 --- no_error_log [error] === TEST 50: clear input headers with wildcard --- config location /hello { more_clear_input_headers 'X-Hidden-*'; content_by_lua ' ngx.say("X-Hidden-One: ", ngx.var.http_x_hidden_one) ngx.say("X-Hidden-Two: ", ngx.var.http_x_hidden_two) '; } --- request GET /hello --- more_headers X-Hidden-One: i am hidden X-Hidden-Two: me 2 --- response_body X-Hidden-One: nil X-Hidden-Two: nil === TEST 51: make sure wildcard doesn't affect more_set_input_headers --- config location /hello { more_set_input_headers 'X-Hidden-*: lol'; content_by_lua ' ngx.say("X-Hidden-One: ", ngx.var.http_x_hidden_one) ngx.say("X-Hidden-Two: ", ngx.var.http_x_hidden_two) '; } --- request GET /hello --- more_headers X-Hidden-One: i am hidden X-Hidden-Two: me 2 --- response_body X-Hidden-One: i am hidden X-Hidden-Two: me 2 headers-more-nginx-module-0.37/t/phase.t000066400000000000000000000005431454316031200201530ustar00rootroot00000000000000# vi:filetype=perl use lib 'lib'; use Test::Nginx::Socket; plan tests => 3; no_diff; run_tests(); __DATA__ === TEST 1: simple set (1 arg) --- config location /foo { deny all; more_set_headers 'X-Foo: Blah'; } --- request GET /foo --- response_headers X-Foo: Blah --- response_body_like: 403 Forbidden --- error_code: 403 headers-more-nginx-module-0.37/t/sanity.t000066400000000000000000000266161454316031200203730ustar00rootroot00000000000000# vi:filetype= use lib 'lib'; use Test::Nginx::Socket; repeat_each(2); plan tests => repeat_each() * 123; #master_on(); #workers(2); log_level("warn"); no_diff; run_tests(); __DATA__ === TEST 1: simple set (1 arg) --- config location /foo { echo hi; more_set_headers 'X-Foo: Blah'; } --- request GET /foo --- response_headers X-Foo: Blah --- response_body hi === TEST 2: simple set (2 args) --- config location /foo { echo hi; more_set_headers 'X-Foo: Blah' 'X-Bar: hi'; } --- request GET /foo --- response_headers X-Foo: Blah X-Bar: hi --- response_body hi === TEST 3: two sets in a single location --- config location /two { echo hi; more_set_headers 'X-Foo: Blah' more_set_headers 'X-Bar: hi'; } --- request GET /two --- response_headers X-Foo: Blah X-Bar: hi --- response_body hi === TEST 4: two sets in a single location (for 404 too) --- config location /two { more_set_headers 'X-Foo: Blah' more_set_headers 'X-Bar: hi'; return 404; } --- request GET /two --- response_headers X-Foo: Blah X-Bar: hi --- response_body_like: 404 Not Found --- error_code: 404 === TEST 5: set a header then clears it (500) --- config location /two { more_set_headers 'X-Foo: Blah'; more_set_headers 'X-Foo:'; return 500; } --- request GET /two --- response_headers ! X-Foo ! X-Bar --- response_body_like: 500 Internal Server Error --- error_code: 500 === TEST 6: set a header only when 500 (matched) --- config location /bad { more_set_headers -s 500 'X-Mine: Hiya'; more_set_headers -s 404 'X-Yours: Blah'; return 500; } --- request GET /bad --- response_headers X-Mine: Hiya ! X-Yours --- response_body_like: 500 Internal Server Error --- error_code: 500 === TEST 7: set a header only when 500 (not matched with 200) --- config location /bad { more_set_headers -s 500 'X-Mine: Hiya'; more_set_headers -s 404 'X-Yours: Blah'; echo hello; } --- request GET /bad --- response_headers ! X-Mine ! X-Yours --- response_body hello --- error_code: 200 === TEST 8: set a header only when 500 (not matched with 404) --- config location /bad { more_set_headers -s 500 'X-Mine: Hiya'; more_set_headers -s 404 'X-Yours: Blah'; return 404; } --- request GET /bad --- response_headers ! X-Mine X-Yours: Blah --- response_body_like: 404 Not Found --- error_code: 404 === TEST 9: more conditions --- config location /bad { more_set_headers -s '503 404' 'X-Mine: Hiya'; more_set_headers -s ' 404 413 ' 'X-Yours: Blah'; return 503; } --- request GET /bad --- response_headers X-Mine: Hiya ! X-Yours --- response_body_like: 503 Service --- error_code: 503 === TEST 10: more conditions --- config location /bad { more_set_headers -s '503 404' 'X-Mine: Hiya'; more_set_headers -s ' 404 413 ' 'X-Yours: Blah'; return 404; } --- request GET /bad --- response_headers X-Mine: Hiya X-Yours: Blah --- response_body_like: 404 Not Found --- error_code: 404 === TEST 11: more conditions --- config location /bad { more_set_headers -s '503 404' 'X-Mine: Hiya'; more_set_headers -s ' 404 413 ' 'X-Yours: Blah'; return 413; } --- request GET /bad --- response_headers ! X-Mine X-Yours: Blah --- response_body_like: 413 Request Entity Too Large --- error_code: 413 === TEST 12: simple -t --- config location /bad { default_type 'text/css'; more_set_headers -t 'text/css' 'X-CSS: yes'; echo hi; } --- request GET /bad --- response_headers X-CSS: yes --- response_body hi === TEST 13: simple -t (not matched) --- config location /bad { default_type 'text/plain'; more_set_headers -t 'text/css' 'X-CSS: yes'; echo hi; } --- request GET /bad --- response_headers ! X-CSS --- response_body hi === TEST 14: multiple -t (not matched) --- config location /bad { default_type 'text/plain'; more_set_headers -t 'text/javascript' -t 'text/css' 'X-CSS: yes'; echo hi; } --- request GET /bad --- response_headers ! X-CSS --- response_body hi === TEST 15: multiple -t (matched) --- config location /bad { default_type 'text/plain'; more_set_headers -t 'text/javascript' -t 'text/plain' 'X-CSS: yes'; echo hi; } --- request GET /bad --- response_headers X-CSS: yes --- response_body hi === TEST 16: multiple -t (matched) --- config location /bad { default_type 'text/javascript'; more_set_headers -t 'text/javascript' -t 'text/plain' 'X-CSS: yes'; echo hi; } --- request GET /bad --- response_headers X-CSS: yes --- response_body hi === TEST 17: multiple -t (matched) with extra spaces --- config location /bad { default_type 'text/javascript'; more_set_headers -t ' text/javascript ' -t 'text/plain' 'X-CSS: yes'; echo hi; } --- request GET /bad --- response_headers X-CSS: yes --- response_body hi === TEST 18: multiple -t merged --- config location /bad { default_type 'text/javascript'; more_set_headers -t ' text/javascript text/plain' 'X-CSS: yes'; echo hi; } --- request GET /bad --- response_headers X-CSS: yes --- response_body hi === TEST 19: multiple -t merged (2) --- config location /bad { default_type 'text/plain'; more_set_headers -t ' text/javascript text/plain' 'X-CSS: yes'; echo hi; } --- request GET /bad --- response_headers X-CSS: yes --- response_body hi === TEST 20: multiple -s option in a directive (not matched) --- config location /bad { more_set_headers -s 404 -s 500 'X-status: howdy'; echo hi; } --- request GET /bad --- response_headers ! X-status --- response_body hi === TEST 21: multiple -s option in a directive (matched 404) --- config location /bad { more_set_headers -s 404 -s 500 'X-status: howdy'; return 404; } --- request GET /bad --- response_headers X-status: howdy --- response_body_like: 404 Not Found --- error_code: 404 === TEST 22: multiple -s option in a directive (matched 500) --- config location /bad { more_set_headers -s 404 -s 500 'X-status: howdy'; return 500; } --- request GET /bad --- response_headers X-status: howdy --- response_body_like: 500 Internal Server Error --- error_code: 500 === TEST 23: -s mixed with -t --- config location /bad { default_type 'text/html'; more_set_headers -s 404 -s 200 -t 'text/html' 'X-status: howdy2'; return 404; } --- request GET /bad --- response_headers X-status: howdy2 --- response_body_like: 404 Not Found --- error_code: 404 === TEST 24: -s mixed with -t --- config location /bad { default_type 'text/html'; more_set_headers -s 404 -s 200 -t 'text/plain' 'X-status: howdy2'; return 404; } --- request GET /bad --- response_headers ! X-status --- response_body_like: 404 Not Found --- error_code: 404 === TEST 25: -s mixed with -t --- config location /bad { default_type 'text/html'; more_set_headers -s 404 -s 200 -t 'text/html' 'X-status: howdy2'; echo hi; } --- request GET /bad --- response_headers X-status: howdy2 --- response_body hi --- error_code: 200 === TEST 26: -s mixed with -t --- config location /bad { default_type 'text/html'; more_set_headers -s 500 -s 200 -t 'text/html' 'X-status: howdy2'; return 404; } --- request GET /bad --- response_headers ! X-status --- response_body_like: 404 Not Found --- error_code: 404 === TEST 27: merge from the upper level --- config more_set_headers -s 404 -t 'text/html' 'X-status2: howdy3'; location /bad { default_type 'text/html'; more_set_headers -s 500 -s 200 -t 'text/html' 'X-status: howdy2'; return 404; } --- request GET /bad --- response_headers X-status2: howdy3 ! X-status --- response_body_like: 404 Not Found --- error_code: 404 === TEST 28: merge from the upper level --- config more_set_headers -s 404 -t 'text/html' 'X-status2: howdy3'; location /bad { default_type 'text/html'; more_set_headers -s 500 -s 200 -t 'text/html' 'X-status: howdy2'; echo yeah; } --- request GET /bad --- response_headers ! X-status2 X-status: howdy2 --- response_body yeah --- error_code: 200 === TEST 29: override settings by inheritance --- config more_set_headers -s 404 -t 'text/html' 'X-status: yeah'; location /bad { default_type 'text/html'; more_set_headers -s 404 -t 'text/html' 'X-status: nope'; return 404; } --- request GET /bad --- response_headers X-status: nope --- response_body_like: 404 Not Found --- error_code: 404 === TEST 30: append settings by inheritance --- config more_set_headers -s 404 -t 'text/html' 'X-status: yeah'; location /bad { default_type 'text/html'; more_set_headers -s 404 -t 'text/html' 'X-status2: nope'; return 404; } --- request GET /bad --- response_headers X-status: yeah X-status2: nope --- response_body_like: 404 Not Found --- error_code: 404 === TEST 31: clear headers with wildcard --- config location = /backend { add_header X-Hidden-One "i am hidden"; add_header X-Hidden-Two "me 2"; echo hi; } location /hello { more_clear_headers 'X-Hidden-*'; proxy_pass http://127.0.0.1:$server_port/backend; } --- request GET /hello --- response_headers ! X-Hidden-One ! X-Hidden-Two --- response_body hi === TEST 32: clear duplicate headers --- config location = /backend { add_header pragma no-cache; add_header pragma no-cache; echo hi; } location /hello { more_clear_headers 'pragma'; proxy_pass http://127.0.0.1:$server_port/backend; } --- request GET /hello --- response_headers !pragma --- response_body hi === TEST 33: HTTP 0.9 (set) --- config location /foo { more_set_headers 'X-Foo: howdy'; echo ok; } --- raw_request eval "GET /foo\r\n" --- response_headers ! X-Foo --- response_body ok --- http09 === TEST 34: use the -a option to append the cookie field --- config location /cookie { more_set_headers -a 'Set-Cookie: name=lynch'; echo ok; } --- request GET /cookie --- response_headers Set-Cookie: name=lynch --- response_body ok === TEST 35: the original Set-Cookie fields will not be overwritten, when using the -a option --- config location /cookie { more_set_headers 'Set-Cookie: name=lynch'; more_set_headers -a 'Set-Cookie: born=1981'; echo ok; } --- request GET /cookie --- raw_response_headers_like eval "Set-Cookie: name=lynch\r\nSet-Cookie: born=1981\r\n" --- response_body ok === TEST 36: The behavior of builtin headers can not be changed --- config location /foo { more_set_headers -a "Server: myServer"; echo ok; } --- request GET /foo --- must_die --- error_log chomp can not append builtin headers --- suppress_stderr === TEST 37: can not use -a option with more_clear_headers --- config location /foo { more_clear_headers -a 'Content-Type'; echo ok; } --- request GET /foo --- must_die --- error_log chomp invalid option name: "-a" --- suppress_stderr headers-more-nginx-module-0.37/t/subrequest.t000066400000000000000000000023321454316031200212530ustar00rootroot00000000000000# vi:filetype= use lib 'lib'; use Test::Nginx::Socket; # 'no_plan'; plan tests => blocks() * 3; no_diff; run_tests(); __DATA__ === TEST 1: vars in input header directives --- config location /main { echo_location /foo; echo "main: $http_user_agent"; } location /foo { set $val 'dog'; more_set_input_headers 'User-Agent: $val'; proxy_pass http://127.0.0.1:$server_port/proxy; } location /proxy { echo "sub: $http_user_agent"; } --- request GET /main --- more_headers User-Agent: my-sock --- response_body sub: dog main: dog --- response_headers ! Host --- skip_nginx: 3: < 0.7.46 === TEST 2: vars in input header directives --- config location /main { #more_set_input_headers 'User-Agent: cat'; echo_location /foo; echo "main: $http_user_agent"; } location /foo { set $val 'dog'; more_set_input_headers 'User-Agent: $val'; proxy_pass http://127.0.0.1:$server_port/proxy; #echo $http_user_agent; } location /proxy { echo "sub: $http_user_agent"; } --- request GET /main --- response_body sub: dog main: dog --- response_headers ! Host --- skip_nginx: 3: < 0.7.46 headers-more-nginx-module-0.37/t/unused.t000066400000000000000000000053141454316031200203570ustar00rootroot00000000000000# vi:filetype= use lib 'lib'; use Test::Nginx::Socket; repeat_each(2); plan tests => repeat_each() * (blocks() * 4 + 2); #master_on(); #workers(2); log_level("warn"); no_diff; run_tests(); __DATA__ === TEST 1: used output filter --- config location /foo { echo hi; more_set_headers "Foo: bar"; } --- request GET /foo --- response_headers Foo: bar --- response_body hi --- error_log headers more header filter --- no_error_log [error] --- log_level: debug === TEST 2: unused output filter (none) --- config location /foo { echo hi; } --- request GET /foo --- response_body hi --- no_error_log headers more header filter [error] --- log_level: debug === TEST 3: unused output filter (with more_set_input_headers only) --- config location /foo { more_set_input_headers "Foo: bar"; echo hi; } --- request GET /foo --- response_body hi --- no_error_log headers more header filter [error] --- log_level: debug === TEST 4: used rewrite handler --- config location /foo { more_set_input_headers "Foo: bar"; echo hi; } --- request GET /foo --- response_body hi --- error_log headers more rewrite handler --- no_error_log [error] --- log_level: debug === TEST 5: unused rewrite handler (none) --- config location /foo { #more_set_input_headers "Foo: bar"; echo hi; } --- request GET /foo --- response_body hi --- no_error_log headers more rewrite handler [error] --- log_level: debug === TEST 6: unused rewrite handler (with output header filters) --- config location /foo { #more_set_input_headers "Foo: bar"; echo hi; more_set_headers "Foo: bar"; } --- request GET /foo --- response_headers Foo: bar --- response_body hi --- no_error_log headers more rewrite handler [error] --- log_level: debug === TEST 7: multiple http {} blocks (filter) This test case won't run with nginx 1.9.3+ since duplicate http {} blocks have been prohibited since then. --- SKIP --- config location /foo { echo hi; more_set_headers 'Foo: bar'; } --- post_main_config http { } --- request GET /foo --- response_body hi --- response_headers Foo: bar --- no_error_log [error] --- error_log headers more header filter --- log_level: debug === TEST 8: multiple http {} blocks (handler) This test case won't run with nginx 1.9.3+ since duplicate http {} blocks have been prohibited since then. --- SKIP --- config location /foo { more_set_input_headers 'Foo: bar'; echo $http_foo; } --- post_main_config http { } --- request GET /foo --- response_body bar --- no_error_log headers more header handler [error] --- log_level: debug headers-more-nginx-module-0.37/t/vars.t000066400000000000000000000015161454316031200200270ustar00rootroot00000000000000# vi:ft= use lib 'lib'; use Test::Nginx::Socket; # 'no_plan'; plan tests => 9; no_diff; run_tests(); __DATA__ === TEST 1: vars --- config location /foo { echo hi; set $val 'hello, world'; more_set_headers 'X-Foo: $val'; } --- request GET /foo --- response_headers X-Foo: hello, world --- response_body hi === TEST 2: vars in both key and val --- config location /foo { echo hi; set $val 'hello, world'; more_set_headers '$val: $val'; } --- request GET /foo --- response_headers $val: hello, world --- response_body hi === TEST 3: vars in input header directives --- config location /foo { set $val 'dog'; more_set_input_headers 'Host: $val'; echo $host; } --- request GET /foo --- response_body dog --- response_headers Host: headers-more-nginx-module-0.37/util/000077500000000000000000000000001454316031200173765ustar00rootroot00000000000000headers-more-nginx-module-0.37/util/build.sh000077500000000000000000000022401454316031200210320ustar00rootroot00000000000000#!/bin/bash # this file is mostly meant to be used by the author himself. root=`pwd` version=$1 home=~ force=$2 pcre2_opt="" if [ "$WITHOUT_PCRE2" = "1" ]; then pcre2_opt="--without-pcre2" fi #--with-cc=gcc46 \ ngx-build $force $version \ --with-ld-opt="-L$PCRE_LIB -Wl,-rpath,$PCRE_LIB:$LIBDRIZZLE_LIB" \ --without-mail_pop3_module \ --without-mail_imap_module \ --without-mail_smtp_module \ --without-http_upstream_ip_hash_module \ --without-http_empty_gif_module \ --without-http_memcached_module \ --without-http_referer_module \ --without-http_autoindex_module \ --without-http_auth_basic_module \ --without-http_userid_module \ $pcre2_opt \ --with-http_realip_module \ --with-http_dav_module \ --add-module=$root/../eval-nginx-module \ --add-module=$root/../lua-nginx-module \ --add-module=$root/../echo-nginx-module \ --add-module=$root $opts \ --with-debug #--add-module=$root/../ndk-nginx-module \ #--without-http_ssi_module # we cannot disable ssi because echo_location_async depends on it (i dunno why?!) headers-more-nginx-module-0.37/valgrind.suppress000066400000000000000000000046701454316031200220440ustar00rootroot00000000000000{ Memcheck:Leak fun:malloc fun:ngx_alloc obj:* } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_calloc fun:ngx_event_process_init } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_event_process_init } { Memcheck:Param epoll_ctl(event) fun:epoll_ctl } { Memcheck:Cond fun:memcpy fun:ngx_vslprintf fun:ngx_log_error_core fun:ngx_http_charset_header_filter } { nginx-core-process-init Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_event_process_init } { nginx-core-crc32-init Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_crc32_table_init fun:main } { palloc_large_for_init_request Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_palloc_large fun:ngx_palloc fun:ngx_pcalloc fun:ngx_http_init_request fun:ngx_epoll_process_events fun:ngx_process_events_and_timers } { palloc_large_for_create_temp_buf Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_palloc_large fun:ngx_palloc fun:ngx_create_temp_buf fun:ngx_http_init_request fun:ngx_epoll_process_events fun:ngx_process_events_and_timers } { accept_create_pool Memcheck:Leak fun:memalign fun:posix_memalign fun:ngx_memalign fun:ngx_create_pool fun:ngx_event_accept fun:ngx_epoll_process_events fun:ngx_process_events_and_timers } { create_pool_for_init_req Memcheck:Leak fun:memalign fun:posix_memalign fun:ngx_memalign fun:ngx_create_pool fun:ngx_http_init_request fun:ngx_epoll_process_events fun:ngx_process_events_and_timers } { Memcheck:Addr8 fun:getenv fun:gcov_exit fun:exit fun:ngx_master_process_exit } { Memcheck:Cond fun:index fun:expand_dynamic_string_token fun:_dl_map_object fun:map_doit fun:_dl_catch_error fun:do_preload fun:dl_main } { Memcheck:Leak match-leak-kinds: definite fun:malloc fun:ngx_alloc fun:ngx_set_environment fun:ngx_single_process_cycle } { Memcheck:Leak match-leak-kinds: definite fun:malloc fun:ngx_alloc fun:ngx_set_environment fun:ngx_worker_process_init fun:ngx_worker_process_cycle }