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:
commit
1d1739b4ea
|
@ -0,0 +1 @@
|
|||
*.t linguist-language=Text
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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 (章亦春) *<agentzh@gmail.com>*, 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)
|
||||
|
|
@ -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
|
|
@ -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 */
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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]
|
|
@ -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
|
|
@ -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]
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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:
|
|
@ -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?!)
|
||||
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue