Squashed 'src/deps/src/headers-more-nginx-module/' content from commit bea1be3bb

git-subtree-dir: src/deps/src/headers-more-nginx-module
git-subtree-split: bea1be3bbf6af28f6aa8cf0c01c07ee1637e2bd0
This commit is contained in:
Théophile Diot 2023-06-30 15:37:43 -04:00
commit 1d1739b4ea
28 changed files with 7580 additions and 0 deletions

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
*.t linguist-language=Text

54
.gitignore vendored Normal file
View File

@ -0,0 +1,54 @@
reindex
.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

55
.travis.yml Normal file
View File

@ -0,0 +1,55 @@
sudo: required
dist: bionic
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.19.3
- NGINX_VERSION=1.19.9
- NGINX_VERSION=1.23.0 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

555
README.markdown Normal file
View File

@ -0,0 +1,555 @@
Name
====
**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.33](https://github.com/openresty/headers-more-nginx-module/tags) released on 3 November 2017.
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>]... <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 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:
<http://openresty.org/#Changes>
[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 (章亦春) *&lt;agentzh@gmail.com&gt;*, OpenResty Inc.
* Bernd Dorn ( <http://www.lovelysystems.com/> )
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 (章亦春) <agentzh@gmail.com>, 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)

32
config Normal file
View File

@ -0,0 +1,32 @@
ngx_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

124
src/ddebug.h Normal file
View File

@ -0,0 +1,124 @@
#ifndef DDEBUG_H
#define DDEBUG_H
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include <nginx.h>
#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 <stdarg.h>
#include <stdio.h>
#include <stdarg.h>
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 <stdarg.h>
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 */

View File

@ -0,0 +1,348 @@
/*
* 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 <ngx_config.h>
/* 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;
}

View File

@ -0,0 +1,80 @@
/*
* Copyright (c) Yichun Zhang (agentzh)
*/
#ifndef NGX_HTTP_HEADERS_MORE_FILTER_MODULE_H
#define NGX_HTTP_HEADERS_MORE_FILTER_MODULE_H
#include <ngx_core.h>
#include <ngx_http.h>
#include <assert.h>
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;
ngx_flag_t replace;
ngx_flag_t wildcard;
};
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 */

View File

@ -0,0 +1,954 @@
/*
* 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 <ctype.h>
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;
} 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;
int nelts;
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) {
nelts = 0;
for (h = *headers; h; h = h->next) {
nelts++;
}
*headers = NULL;
dd("clear multi-value headers: %d", nelts);
}
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;
}

View File

@ -0,0 +1,26 @@
/*
* 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 */

View File

@ -0,0 +1,757 @@
/*
* 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 <ctype.h>
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
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 */
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;
ngx_http_headers_more_cmd_t *cmd;
ngx_str_t *arg;
ngx_flag_t ignore_next_arg;
ngx_str_t *cmd_name;
ngx_int_t rc;
ngx_http_headers_more_main_conf_t *hmcf;
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;
}
}
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;
}
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;
}

View File

@ -0,0 +1,26 @@
/*
* 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 */

View File

@ -0,0 +1,382 @@
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_headers_more_util.h"
#include <ctype.h>
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;
}

View File

@ -0,0 +1,52 @@
/*
* 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 */

416
t/bug.t Normal file
View File

@ -0,0 +1,416 @@
# 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

338
t/builtin.t Normal file
View File

@ -0,0 +1,338 @@
# 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

36
t/eval.t Normal file
View File

@ -0,0 +1,36 @@
# 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

137
t/input-conn.t Normal file
View File

@ -0,0 +1,137 @@
# 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]

183
t/input-cookie.t Normal file
View File

@ -0,0 +1,183 @@
# 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

628
t/input-ua.t Normal file
View File

@ -0,0 +1,628 @@
# 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]

1331
t/input.t Normal file

File diff suppressed because it is too large Load Diff

25
t/phase.t Normal file
View File

@ -0,0 +1,25 @@
# 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

567
t/sanity.t Normal file
View File

@ -0,0 +1,567 @@
# vi:filetype=
use lib 'lib';
use Test::Nginx::Socket;
repeat_each(2);
plan tests => repeat_each() * 113;
#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

68
t/subrequest.t Normal file
View File

@ -0,0 +1,68 @@
# 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

174
t/unused.t Normal file
View File

@ -0,0 +1,174 @@
# 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

58
t/vars.t Normal file
View File

@ -0,0 +1,58 @@
# 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:

38
util/build.sh Executable file
View File

@ -0,0 +1,38 @@
#!/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?!)

135
valgrind.suppress Normal file
View File

@ -0,0 +1,135 @@
{
<insert_a_suppression_name_here>
Memcheck:Leak
fun:malloc
fun:ngx_alloc
obj:*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
fun:malloc
fun:ngx_alloc
fun:ngx_calloc
fun:ngx_event_process_init
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
fun:malloc
fun:ngx_alloc
fun:ngx_event_process_init
}
{
<insert_a_suppression_name_here>
Memcheck:Param
epoll_ctl(event)
fun:epoll_ctl
}
{
<insert_a_suppression_name_here>
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
}
{
<insert_a_suppression_name_here>
Memcheck:Addr8
fun:getenv
fun:gcov_exit
fun:exit
fun:ngx_master_process_exit
}
{
<insert_a_suppression_name_here>
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
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
match-leak-kinds: definite
fun:malloc
fun:ngx_alloc
fun:ngx_set_environment
fun:ngx_single_process_cycle
}
{
<insert_a_suppression_name_here>
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
}