From 0a533a276c35df9805f5a0ac9df4147fcdcc455c Mon Sep 17 00:00:00 2001
From: Metztli Information Technology <jose@metztli.it>
Date: Sat, 25 May 2019 21:56:37 -0700
Subject: [PATCH] Integrating ModSecurity Nginx Connector and adding
 b2evolution sample config file

---
 changelog                                     |   8 +
 conf/sites-available/b2evolution.conf.sample  |  34 +
 conf/sites-available/default                  |   4 +-
 control                                       |  14 +
 libnginx-mod-http-modsecurity.nginx           |  13 +
 libnginx-mod.conf/mod-http-modsecurity.conf   |   1 +
 modules/control                               |   5 +
 modules/http-modsecurity/AUTHORS              |   2 +
 modules/http-modsecurity/CHANGES              |  42 +
 modules/http-modsecurity/LICENSE              | 201 +++++
 modules/http-modsecurity/README.md            | 257 ++++++
 modules/http-modsecurity/config               | 192 +++++
 modules/http-modsecurity/ngx-modsec.stp       |  37 +
 modules/http-modsecurity/release.sh           |  20 +
 modules/http-modsecurity/src/ddebug.h         | 100 +++
 .../src/ngx_http_modsecurity_body_filter.c    | 180 ++++
 .../src/ngx_http_modsecurity_common.h         | 151 ++++
 .../src/ngx_http_modsecurity_header_filter.c  | 551 +++++++++++++
 .../src/ngx_http_modsecurity_log.c            |  76 ++
 .../src/ngx_http_modsecurity_module.c         | 770 ++++++++++++++++++
 .../src/ngx_http_modsecurity_pre_access.c     | 215 +++++
 .../src/ngx_http_modsecurity_rewrite.c        | 196 +++++
 modules/http-modsecurity/tests/README.md      |  10 +
 .../tests/modsecurity-config-auditlog.t       | 231 ++++++
 .../tests/modsecurity-config-debuglog.t       | 142 ++++
 .../tests/modsecurity-config-merge.t          | 231 ++++++
 .../tests/modsecurity-config.t                | 112 +++
 .../http-modsecurity/tests/modsecurity-h2.t   | 206 +++++
 .../tests/modsecurity-proxy-h2.t              | 288 +++++++
 .../tests/modsecurity-proxy.t                 | 208 +++++
 .../tests/modsecurity-request-body-h2.t       | 222 +++++
 .../tests/modsecurity-request-body.t          | 251 ++++++
 .../tests/modsecurity-response-body.t         |  69 ++
 .../tests/modsecurity-scoring.t               |  79 ++
 .../tests/modsecurity-transaction-id.t        | 167 ++++
 modules/http-modsecurity/tests/modsecurity.t  | 152 ++++
 .../http-modsecurity/tests/nginx-tests-cvt.pl |  39 +
 rules                                         |   2 +
 38 files changed, 5476 insertions(+), 2 deletions(-)
 create mode 100644 conf/sites-available/b2evolution.conf.sample
 create mode 100755 libnginx-mod-http-modsecurity.nginx
 create mode 100644 libnginx-mod.conf/mod-http-modsecurity.conf
 create mode 100644 modules/http-modsecurity/AUTHORS
 create mode 100644 modules/http-modsecurity/CHANGES
 create mode 100644 modules/http-modsecurity/LICENSE
 create mode 100644 modules/http-modsecurity/README.md
 create mode 100644 modules/http-modsecurity/config
 create mode 100644 modules/http-modsecurity/ngx-modsec.stp
 create mode 100644 modules/http-modsecurity/release.sh
 create mode 100644 modules/http-modsecurity/src/ddebug.h
 create mode 100644 modules/http-modsecurity/src/ngx_http_modsecurity_body_filter.c
 create mode 100644 modules/http-modsecurity/src/ngx_http_modsecurity_common.h
 create mode 100644 modules/http-modsecurity/src/ngx_http_modsecurity_header_filter.c
 create mode 100644 modules/http-modsecurity/src/ngx_http_modsecurity_log.c
 create mode 100644 modules/http-modsecurity/src/ngx_http_modsecurity_module.c
 create mode 100644 modules/http-modsecurity/src/ngx_http_modsecurity_pre_access.c
 create mode 100644 modules/http-modsecurity/src/ngx_http_modsecurity_rewrite.c
 create mode 100644 modules/http-modsecurity/tests/README.md
 create mode 100644 modules/http-modsecurity/tests/modsecurity-config-auditlog.t
 create mode 100644 modules/http-modsecurity/tests/modsecurity-config-debuglog.t
 create mode 100644 modules/http-modsecurity/tests/modsecurity-config-merge.t
 create mode 100644 modules/http-modsecurity/tests/modsecurity-config.t
 create mode 100644 modules/http-modsecurity/tests/modsecurity-h2.t
 create mode 100644 modules/http-modsecurity/tests/modsecurity-proxy-h2.t
 create mode 100644 modules/http-modsecurity/tests/modsecurity-proxy.t
 create mode 100644 modules/http-modsecurity/tests/modsecurity-request-body-h2.t
 create mode 100644 modules/http-modsecurity/tests/modsecurity-request-body.t
 create mode 100644 modules/http-modsecurity/tests/modsecurity-response-body.t
 create mode 100644 modules/http-modsecurity/tests/modsecurity-scoring.t
 create mode 100644 modules/http-modsecurity/tests/modsecurity-transaction-id.t
 create mode 100644 modules/http-modsecurity/tests/modsecurity.t
 create mode 100644 modules/http-modsecurity/tests/nginx-tests-cvt.pl

diff --git a/changelog b/changelog
index be5df64..a8baf12 100644
--- a/changelog
+++ b/changelog
@@ -1,3 +1,11 @@
+nginx (1.17.0-1) metztli; urgency=medium
+
+  * Upstream new nginx release
+  * Integrated ModSecurity Nginx Connector https://github.com/SpiderLabs/ModSecurity-nginx 
+  * Added b2evolution https://b2evolution.net sample configuration
+
+ -- Metztli Information Technology <jose@metztli.it>  Sat, 25 May 2019 21:47:11 -0700
+
 nginx (1.14.2-2) unstable; urgency=medium
 
   [ Kartik Mistry ]
diff --git a/conf/sites-available/b2evolution.conf.sample b/conf/sites-available/b2evolution.conf.sample
new file mode 100644
index 0000000..67260fa
--- /dev/null
+++ b/conf/sites-available/b2evolution.conf.sample
@@ -0,0 +1,34 @@
+server {
+  listen 80;
+  server_name b2evolution.blog www.b2evolution.blog;
+  root /var/www/b2evolution;
+  index index.php index.htm index.html;
+
+location / {
+    index index.php;
+    try_files $uri $uri/ @b2evo;
+  }
+
+location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
+        try_files $uri /htsrv/getfile.php?$args;
+                expires max;
+                log_not_found off;
+        }
+
+location @b2evo {
+    rewrite ^(.*) /index.php?$1 last;
+  }
+
+location /api/ {
+auth_basic          off;
+rewrite "^/api/v(?<version>[^/]+)/(?<parameters>(.+))$" /htsrv/rest.php?api_version=$version&api_request=$parameters last;
+  }
+
+  location ~ \.php$ {
+    include /etc/nginx/fastcgi_params;
+    fastcgi_pass           unix:/run/php/php7.0-fpm.sock;
+    fastcgi_index index.php;
+    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+  }
+
+}
diff --git a/conf/sites-available/default b/conf/sites-available/default
index f5c5e1b..822000e 100644
--- a/conf/sites-available/default
+++ b/conf/sites-available/default
@@ -56,8 +56,8 @@ server {
 	#location ~ \.php$ {
 	#	include snippets/fastcgi-php.conf;
 	#
-	#	# With php-fpm (or other unix sockets):
-	#	fastcgi_pass unix:/run/php/php7.3-fpm.sock;
+	#	# With PHP7.0 php-fpm (or other unix sockets):
+	#	fastcgi_pass unix:/run/php/php7.0-fpm.sock;
 	#	# With php-cgi (or other tcp sockets):
 	#	fastcgi_pass 127.0.0.1:9000;
 	#}
diff --git a/control b/control
index 409dc22..d8d890d 100644
--- a/control
+++ b/control
@@ -151,6 +151,7 @@ Depends: libnginx-mod-http-auth-pam (= ${binary:Version}),
          libnginx-mod-mail (= ${binary:Version}),
          libnginx-mod-nchan (= ${binary:Version}),
          libnginx-mod-stream (= ${binary:Version}),
+         libnginx-mod-http-modsecurity (= ${binary:Version}),
          nginx-common (= ${source:Version}),
          ${misc:Depends},
          ${shlibs:Depends}
@@ -390,3 +391,16 @@ Description: RTMP support for Nginx
   - Stream relay support via a push or pull model;
   - Integrated stream recording;
   - and more.
+
+Package: libnginx-mod-http-modsecurity
+Architecture: any
+Depends: ${misc:Depends}, ${shlibs:Depends}
+Description: The ModSecurity-nginx connector
+ is the connection point between nginx and libmodsecurity (ModSecurity v3).
+ Said another way, this project provides a communication channel between nginx and libmodsecurity.
+ This connector is required to use LibModSecurity with nginx.
+ .
+ The ModSecurity-nginx connector takes the form of an nginx module.
+ The module simply serves as a layer of communication between nginx and ModSecurity.
+ .
+ Notice that this project depends on libmodsecurity rather than ModSecurity (version 2.9 or less).
diff --git a/libnginx-mod-http-modsecurity.nginx b/libnginx-mod-http-modsecurity.nginx
new file mode 100755
index 0000000..02c5edc
--- /dev/null
+++ b/libnginx-mod-http-modsecurity.nginx
@@ -0,0 +1,13 @@
+#!/usr/bin/perl -w
+
+use File::Basename;
+
+# Guess module name
+$module = basename($0, '.nginx');
+$module =~ s/^libnginx-mod-//;
+
+$modulepath = $module;
+$modulepath =~ s/-/_/g;
+
+print "mod debian/build-full/objs/ngx_${modulepath}_module.so\n";
+print "mod debian/libnginx-mod.conf/mod-${module}.conf\n";
diff --git a/libnginx-mod.conf/mod-http-modsecurity.conf b/libnginx-mod.conf/mod-http-modsecurity.conf
new file mode 100644
index 0000000..00547ce
--- /dev/null
+++ b/libnginx-mod.conf/mod-http-modsecurity.conf
@@ -0,0 +1 @@
+load_module modules/ngx_http_modsecurity_module.so;
diff --git a/modules/control b/modules/control
index a5d65a8..5b192d8 100644
--- a/modules/control
+++ b/modules/control
@@ -68,3 +68,8 @@ Homepage: https://github.com/arut/nginx-rtmp-module
 Files-Excluded: test
 Version: 1.2.1
 
+Module: http-modsecurity
+Homepage: https://github.com/SpiderLabs/ModSecurity-nginx
+Version: 1.0.0
+Files-Excluded: .git .travis.yml
+
diff --git a/modules/http-modsecurity/AUTHORS b/modules/http-modsecurity/AUTHORS
new file mode 100644
index 0000000..490612f
--- /dev/null
+++ b/modules/http-modsecurity/AUTHORS
@@ -0,0 +1,2 @@
+zimmerle = Felipe Zimmerle <felipe@zimmerle.org>
+defanator = Andrei Belov <defanator@gmail.com>
diff --git a/modules/http-modsecurity/CHANGES b/modules/http-modsecurity/CHANGES
new file mode 100644
index 0000000..da83e41
--- /dev/null
+++ b/modules/http-modsecurity/CHANGES
@@ -0,0 +1,42 @@
+v1.0.x - YYYY-MMM-DD (To be released)
+-------------------------------------
+
+ - Tests: extend request body tests
+   [Issue #142,#143 - @defanator]
+ - Added basic tests over HTTP/2
+   [Issue #145 - @defanator]
+ - Module configuration refactoring
+   [Issue #139 - @defanator]
+ - Restore r->write_event_handler after reading request body
+   [Issue #131 - @defanator]
+ - Increase log level for disruptive actions to "error"
+   [Issue #112 - @victorhora]
+ - Support for generating transaction ID in nginx
+   [Issue #126 - @defanator]
+ - Extend request body tests with ARGS_POST case
+   [Issue #124 - @defanator]
+ - Fix tests after 42a472a change in library
+   [Issue #122 - @defanator]
+ - Fix processing of response body when gzip compression is enabled
+   [Issue #107 - @turchanov]
+ - Fixed processing of response body chunks in
+   ngx_http_modsecurity_body_filter.
+   [Issue #105 - @turchanov, @defanator]
+ - Fix incorrect handling of request/response body data chain of ngx_buf_t
+   buffers
+   [Issue #104 - @turchanov, @defanator]
+ - Pool pointer is now handled in ngx_http_modsecurity_config_cleanup
+   [Issue #87 - @AirisX, @defanator, @zimmerle]
+ - Fix memory leak in intervention processing
+   [Issue #100 - @defanator]
+ - Emit connector version in error log
+   [Issue #88 - @defanator]
+ - Fixed memory leak on config cleanup.
+   [Issue #80 - @AirisX, @defanator]
+
+
+v1.0.0 - 2017-Dec-20
+--------------------
+
+ - First version of ModSecurity-nginx connector
+
diff --git a/modules/http-modsecurity/LICENSE b/modules/http-modsecurity/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/modules/http-modsecurity/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/modules/http-modsecurity/README.md b/modules/http-modsecurity/README.md
new file mode 100644
index 0000000..1ae2c58
--- /dev/null
+++ b/modules/http-modsecurity/README.md
@@ -0,0 +1,257 @@
+
+<img src="https://github.com/SpiderLabs/ModSecurity/raw/v3/master/others/modsec.png" width="50%">
+
+[![Build Status](https://travis-ci.org/SpiderLabs/ModSecurity-nginx.svg?branch=master)](https://travis-ci.org/SpiderLabs/ModSecurity-nginx)
+[![](https://raw.githubusercontent.com/ZenHubIO/support/master/zenhub-badge.png)](https://zenhub.com)
+
+
+The ModSecurity-nginx connector is the connection point between nginx and libmodsecurity (ModSecurity v3). Said another way, this project provides a communication channel between nginx and libmodsecurity. This connector is required to use LibModSecurity with nginx. 
+
+The ModSecurity-nginx connector takes the form of an nginx module. The module simply serves as a layer of communication between nginx and ModSecurity.
+
+Notice that this project depends on libmodsecurity rather than ModSecurity (version 2.9 or less).
+
+### What is the difference between this project and the old ModSecurity add-on for nginx?
+
+The old version uses ModSecurity standalone, which is a wrapper for
+Apache internals to link ModSecurity to nginx. This current version is closer
+to nginx, consuming the new libmodsecurity which is no longer dependent on
+Apache. As a result, this current version has less dependencies, fewer bugs, and is faster. In addition, some new functionality is also provided - such as the possibility of use of global rules configuration with per directory/location customizations (e.g. SecRuleRemoveById).
+
+
+# Compilation
+
+Before compile this software make sure that you have libmodsecurity installed.
+You can download it from the [ModSecurity git repository](https://github.com/SpiderLabs/ModSecurity). For information pertaining to the compilation and installation of libmodsecurity please consult the documentation provided along with it.
+
+With libmodsecurity installed, you can proceed with the installation of the ModSecurity-nginx connector, which follow the nginx 3rd party module installation procedure:
+
+```
+./configure --add-module=/path/to/ModSecurity-nginx
+```
+
+Further information about nginx 3rd party add-ons support are available here:
+http://wiki.nginx.org/3rdPartyModules
+
+
+# Usage
+
+ModSecurity for nginx extends your nginx configuration directives.
+It adds four new directives and they are:
+
+modsecurity
+-----------
+**syntax:** *modsecurity on | off*
+
+**context:** *http, server, location*
+
+**default:** *off*
+
+Turns on or off ModSecurity functionality.
+Note that this configuration directive is no longer related to the SecRule state.
+Instead, it now serves solely as an nginx flag to enable or disable the module.
+
+modsecurity_rules_file
+----------------------
+**syntax:** *modsecurity_rules_file &lt;path to rules file&gt;*
+
+**context:** *http, server, location*
+
+**default:** *no*
+
+Specifies the location of the modsecurity configuration file, e.g.:
+
+```nginx
+server {
+    modsecurity on;
+    location / {
+        root /var/www/html;
+        modsecurity_rules_file /etc/my_modsecurity_rules.conf;
+    }
+}
+```
+
+modsecurity_rules_remote
+------------------------
+**syntax:** *modsecurity_rules_remote &lt;key&gt; &lt;URL to rules&gt;*
+
+**context:** *http, server, location*
+
+**default:** *no*
+
+Specifies from where (on the internet) a modsecurity configuration file will be downloaded.
+It also specifies the key that will be used to authenticate to that server:
+
+```nginx
+server {
+    modsecurity on;
+    location / {
+        root /var/www/html;
+        modsecurity_rules_remote my-server-key https://my-own-server/rules/download;
+    }
+}
+```
+
+modsecurity_rules
+-----------------
+**syntax:** *modsecurity_rules &lt;modsecurity rule&gt;*
+
+**context:** *http, server, location*
+
+**default:** *no*
+
+Allows for the direct inclusion of a ModSecurity rule into the nginx configuration.
+The following example is loading rules from a file and injecting specific configurations per directory/alias:
+
+```nginx
+server {
+    modsecurity on;
+    location / {
+        root /var/www/html;
+        modsecurity_rules_file /etc/my_modsecurity_rules.conf;
+    }
+    location /ops {
+        root /var/www/html/opts;
+        modsecurity_rules '
+          SecRuleEngine On
+          SecDebugLog /tmp/modsec_debug.log
+          SecDebugLogLevel 9
+          SecRuleRemoveById 10
+        ';
+    }
+}
+```
+
+modsecurity_transaction_id
+--------------------------
+**syntax:** *modsecurity_transaction_id string*
+
+**context:** *http, server, location*
+
+**default:** *no*
+
+Allows to pass transaction ID from nginx instead of generating it in the library.
+This can be useful for tracing purposes, e.g. consider this configuration:
+
+```nginx
+log_format extended '$remote_addr - $remote_user [$time_local] '
+                    '"$request" $status $body_bytes_sent '
+                    '"$http_referer" "$http_user_agent" $request_id';
+
+server {
+    server_name host1;
+    modsecurity on;
+    modsecurity_transaction_id "host1-$request_id";
+    access_log logs/host1-access.log extended;
+    error_log logs/host1-error.log;
+    location / {
+        ...
+    }
+}
+
+server {
+    server_name host2;
+    modsecurity on;
+    modsecurity_transaction_id "host2-$request_id";
+    access_log logs/host2-access.log extended;
+    error_log logs/host2-error.log;
+    location / {
+        ...
+    }
+}
+```
+
+Using a combination of log_format and modsecurity_transaction_id you will
+be able to find correlations between access log and error log entries
+using the same unique identificator.
+
+String can contain variables.
+
+
+# Contributing
+
+As an open source project we invite (and encourage) anyone from the community to contribute to our project. This may take the form of: new
+functionality, bug fixes, bug reports, beginners user support, and anything else that you
+are willing to help with. Thank you.
+
+
+## Providing Patches
+
+We prefer to have your patch within the GitHub infrastructure to facilitate our
+review work, and our QA integration. GitHub provides an excellent
+documentation on how to perform “Pull Requests”. More information available
+here: https://help.github.com/articles/using-pull-requests/
+
+Please respect the coding style in use. Pull requests can include various commits, so
+provide one fix or one functionality per commit. Do not change anything outside
+the scope of your target work (e.g. coding style in a function that you have
+passed by). 
+
+### Don’t know where to start?
+
+Within our code there are various items marked as TODO or FIXME that may need
+your attention. Check the list of items by performing a grep:
+
+```
+$ cd /path/to/modsecurity-nginx
+$ egrep -Rin "TODO|FIXME" -R *
+```
+
+You may also take a look at recent bug reports and open issues to get an idea of what kind of help we are looking for.
+
+### Testing your patch
+
+Along with the manual testing, we strongly recommend that you to use the nginx test
+utility to make sure that you patch does not adversely affect the behavior or performance of nginx. 
+
+The nginx tests are available on: http://hg.nginx.org/nginx-tests/ 
+
+To use those tests, make sure you have the Perl utility prove (part of Perl 5)
+and proceed with the following commands:
+
+```
+$ cp /path/to/ModSecurity-nginx/tests/* /path/to/nginx/test/repository
+$ cd /path/to/nginx/test/repository
+$ TEST_NGINX_BINARY=/path/to/your/nginx prove .
+```
+
+If you are facing problems getting your added functionality to pass all the nginx tests, feel free to contact us or the nginx mailing list at: http://nginx.org/en/support.html
+
+### Debugging 
+
+We respect the nginx debugging schema. By using the configuration option
+"--with-debug" during the nginx configuration you will also be enabling the
+connector's debug messages. Core dumps and crashes are expected to be debugged
+in the same fashion that is used to debug nginx. For further information,
+please check the nginx debugging information: http://wiki.nginx.org/Debugging
+
+
+## Reporting Issues
+
+If you are facing a configuration issue or if something is not working as you
+expect it to be, please use ModSecurity user’s mailing list. Issues on GitHub
+are also welcome, but we prefer to have users question on the mailing list first,
+where you can reach an entire community. Also don’t forget to look for an
+existing issue before opening a new one.
+
+Lastly, If you are planning to open an issue on GitHub, please don’t forget to tell us the
+version of your libmodsecurity and the version of the nginx connector you are running.
+
+### Security issue
+
+Please do not publicly report any security issue. Instead, contact us at:
+security@modsecurity.org to report the issue. Once the problem is fixed we will provide you with credit for the discovery.
+
+
+## Feature Request
+
+We would love to discuss any ideas that you may have for a new feature. Please keep in mind this is a community driven project so be sure to contact the community via the mailing list to get feedback first. Alternatively,
+feel free to open GitHub issues requesting for new features. Before opening a new issue, please check if there is an existing feature request for the desired functionality.
+
+
+## Packing
+
+Having our packages in distros on time is something we highly desire. Let us know if
+there is anything we can do to facilitate your work as a packager.
+
+
diff --git a/modules/http-modsecurity/config b/modules/http-modsecurity/config
new file mode 100644
index 0000000..c6e7467
--- /dev/null
+++ b/modules/http-modsecurity/config
@@ -0,0 +1,192 @@
+# vim: filetype=sh
+
+# If $NGX_IGNORE_RPATH is set to "YES", we will ignore explicit
+# library path specification on resulting binary, allowing libmodsecurity.so
+# to be relocated across configured library pathes (adjust /etc/ld.so.conf
+# or set $LD_LIBRARY_PATH environment variable to manage them)
+#
+# $YAJL_LIB variable may need to be populated in case of non-standard
+# path of libyajl.so's installation
+
+ngx_feature_name=
+ngx_feature_run=no
+ngx_feature_incs="#include <modsecurity/modsecurity.h>"
+ngx_feature_libs="-lmodsecurity"
+ngx_feature_test='printf("hello");'
+ngx_modsecurity_opt_I=
+ngx_modsecurity_opt_L=
+
+YAJL_EXTRA=
+if test -n "$YAJL_LIB"; then
+    YAJL_EXTRA="-L$YAJL_LIB -lyajl"
+fi
+
+# If $MODSECURITY_INC is specified, lets use it. Otherwise lets try
+# the default paths
+#
+if [ -n "$MODSECURITY_INC" -o -n "$MODSECURITY_LIB" ]; then
+    # explicitly set ModSecurity lib path
+    ngx_feature="ModSecurity library in \"$MODSECURITY_LIB\" and \"$MODSECURITY_INC\" (specified by the MODSECURITY_LIB and MODSECURITY_INC env)"
+    ngx_feature_path="$MODSECURITY_INC"
+    ngx_modsecurity_opt_I="-I$MODSECURITY_INC"
+    ngx_modsecurity_opt_L="-L$MODSECURITY_LIB $YAJL_EXTRA"
+
+    if [ $NGX_RPATH = YES ]; then
+        ngx_feature_libs="-R$MODSECURITY_LIB -L$MODSECURITY_LIB -lmodsecurity $YAJL_EXTRA"
+    elif [ "$NGX_IGNORE_RPATH" != "YES" -a $NGX_SYSTEM = "Linux" ]; then
+        ngx_feature_libs="-Wl,-rpath,$MODSECURITY_LIB -L$MODSECURITY_LIB -lmodsecurity $YAJL_EXTRA"
+    else
+        ngx_feature_libs="-L$MODSECURITY_LIB -lmodsecurity $YAJL_EXTRA"
+    fi
+
+    . auto/feature
+
+    if [ $ngx_found = no ]; then
+        cat << END
+        $0: error: ngx_http_modsecurity_module requires the ModSecurity library and MODSECURITY_LIB is defined as "$MODSECURITY_LIB" and MODSECURITY_INC (path for modsecurity.h) "$MODSECURITY_INC", but we cannot find ModSecurity there.
+END
+        exit 1
+    fi
+else
+    # auto-discovery
+    ngx_feature="ModSecurity library"
+    ngx_feature_libs="-lmodsecurity"
+
+    . auto/feature
+
+    if [ $ngx_found = no ]; then
+        ngx_feature="ModSecurity library in /usr/local/modsecurity"
+        ngx_feature_path="/usr/local/modsecurity/include"
+        if [ $NGX_RPATH = YES ]; then
+            ngx_feature_libs="-R/usr/local/modsecurity/lib -L/usr/local/modsecurity/lib -lmodsecurity"
+        elif [ "$NGX_IGNORE_RPATH" != "YES" -a $NGX_SYSTEM = "Linux" ]; then
+            ngx_feature_libs="-Wl,-rpath,/usr/local/modsecurity/lib -L/usr/local/modsecurity/lib -lmodsecurity"
+        else
+            ngx_feature_libs="-L/usr/local/modsecurity/lib -lmodsecurity"
+        fi
+
+        . auto/feature
+
+    fi
+fi
+
+
+
+if [ $ngx_found = no ]; then
+ cat << END
+ $0: error: ngx_http_modsecurity_module requires the ModSecurity library.
+END
+ exit 1
+fi
+
+
+ngx_addon_name=ngx_http_modsecurity_module
+
+# We must place ngx_http_modsecurity_module after ngx_http_gzip_filter_module
+# in load order list to be able to read response body before it gets compressed
+# (for filter modules later initialization means earlier execution).
+#
+# Nginx implements load ordering only for dynamic modules and only a BEFORE part
+# of "ngx_module_order". So we list all of the modules that come after
+# ngx_http_gzip_filter_module as a BEFORE dependency for
+# ngx_http_modsecurity_module.
+#
+# For static compilation HTTP_FILTER_MODULES will be patched later.
+
+modsecurity_dependency="ngx_http_postpone_filter_module \
+                        ngx_http_ssi_filter_module \
+                        ngx_http_charset_filter_module \
+                        ngx_http_xslt_filter_module \
+                        ngx_http_image_filter_module \
+                        ngx_http_sub_filter_module \
+                        ngx_http_addition_filter_module \
+                        ngx_http_gunzip_filter_module \
+                        ngx_http_userid_filter_module \
+                        ngx_http_headers_filter_module \
+                        ngx_http_copy_filter_module"
+
+
+if test -n "$ngx_module_link"; then
+	ngx_module_type=HTTP_FILTER
+	ngx_module_name="$ngx_addon_name"
+	ngx_module_srcs="$ngx_addon_dir/src/ngx_http_modsecurity_module.c \
+            $ngx_addon_dir/src/ngx_http_modsecurity_pre_access.c \
+            $ngx_addon_dir/src/ngx_http_modsecurity_header_filter.c \
+            $ngx_addon_dir/src/ngx_http_modsecurity_body_filter.c \
+            $ngx_addon_dir/src/ngx_http_modsecurity_log.c \
+            $ngx_addon_dir/src/ngx_http_modsecurity_rewrite.c \
+            "
+	ngx_module_deps="$ngx_addon_dir/src/ddebug.h \
+            $ngx_addon_dir/src/ngx_http_modsecurity_common.h \
+            "
+        ngx_module_libs="$ngx_feature_libs"
+        ngx_module_incs="$ngx_feature_path"
+
+        ngx_module_order="ngx_http_chunked_filter_module \
+                          ngx_http_v2_filter_module \
+                          ngx_http_range_header_filter_module \
+                          ngx_http_gzip_filter_module \
+                          $ngx_module_name \
+                          $modsecurity_dependency";
+
+	. auto/module
+else
+	CFLAGS="$ngx_modsecurity_opt_I $CFLAGS"
+	NGX_LD_OPT="$ngx_modsecurity_opt_L $NGX_LD_OPT"
+
+	CORE_INCS="$CORE_INCS $ngx_feature_path"
+	CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
+
+	HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES ngx_http_modsecurity_module"
+	NGX_ADDON_SRCS="\
+	    $NGX_ADDON_SRCS \
+	    $ngx_addon_dir/src/ngx_http_modsecurity_module.c \
+	    $ngx_addon_dir/src/ngx_http_modsecurity_pre_access.c \
+	    $ngx_addon_dir/src/ngx_http_modsecurity_header_filter.c \
+	    $ngx_addon_dir/src/ngx_http_modsecurity_body_filter.c \
+	    $ngx_addon_dir/src/ngx_http_modsecurity_log.c \
+	    $ngx_addon_dir/src/ngx_http_modsecurity_rewrite.c \
+	    "
+
+	NGX_ADDON_DEPS="\
+	    $NGX_ADDON_DEPS \
+	    $ngx_addon_dir/src/ddebug.h \
+	    $ngx_addon_dir/src/ngx_http_modsecurity_common.h \
+	    "
+fi
+
+#
+# Nginx does not provide reliable way to introduce our module into required
+# place in static ($ngx_module_link=ADDON) compilation mode, so we must
+# explicitly update module "ordering rules".
+#
+if [ "$ngx_module_link" != DYNAMIC ] ; then
+    # Reposition modsecurity module to satisfy $modsecurity_dependency
+    # (this mimics dependency resolution made by ngx_add_module() function
+    # though less optimal in terms of computational complexity).
+    modules=
+    found=
+    for module in $HTTP_FILTER_MODULES; do
+        # skip our module name from the original list
+        if [ "$module" = "$ngx_addon_name" ]; then
+            continue
+        fi
+        if [ -z "${found}" ]; then
+            for item in $modsecurity_dependency; do
+                if [ "$module" = "$item" ]; then
+                    modules="${modules} $ngx_addon_name"
+                    found=1
+                    break
+                fi
+            done
+        fi
+        modules="${modules} $module"
+    done
+    if [ -z "${found}" ]; then
+        # This must never happen since ngx_http_copy_filter_module must be in HTTP_FILTER_MODULES
+        # and we stated dependency on it in $modsecurity_dependency
+        echo "$0: error: cannot reposition modsecurity module in HTTP_FILTER_MODULES list"
+        exit 1
+    fi
+    HTTP_FILTER_MODULES="${modules}"
+fi
diff --git a/modules/http-modsecurity/ngx-modsec.stp b/modules/http-modsecurity/ngx-modsec.stp
new file mode 100644
index 0000000..9c3e5fa
--- /dev/null
+++ b/modules/http-modsecurity/ngx-modsec.stp
@@ -0,0 +1,37 @@
+#!/usr/bin/env stap
+
+global begin_rule
+global rules
+global rules_phase
+
+
+# Rules
+probe process("/usr/local/lib/libmodsecurity.so.3").function("evaluate@rule.cc*")
+{
+    begin_rule = gettimeofday_us();
+}
+
+probe process("/usr/local/lib/libmodsecurity.so.3").function("evaluate@rule.cc*").return
+{
+    elapsed_rule = gettimeofday_us() - begin_rule
+    rules[$this->m_ruleId] <<< elapsed_rule
+    rules_phase[$this->m_ruleId] = $this->m_phase
+}
+
+
+# Resume
+probe end
+{
+    foreach ([rule] in rules)
+    {
+        if (@count(rules[rule])) {
+            p = rules_phase[rule] - 1;
+            if (p <= 0) {
+              p = 1
+            }
+            printf("Phase %d;Rule ID: %d %u\n", p, rule, @avg(rules[rule]));
+        }
+    }   
+}
+
+
diff --git a/modules/http-modsecurity/release.sh b/modules/http-modsecurity/release.sh
new file mode 100644
index 0000000..6b6e0a3
--- /dev/null
+++ b/modules/http-modsecurity/release.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+git clean -xfdi
+git submodule foreach --recursive git clean -xfdi
+
+VERSION=`git describe --tags`
+DIR_NAME="modsecurity-nginx-$VERSION"
+TAR_NAME="modsecurity-nginx-$VERSION.tar.gz"
+
+MY_DIR=${PWD##*/}
+
+cd ..
+tar --transform "s/^$MY_DIR/$DIR_NAME/" -cvzf $TAR_NAME --exclude .git $MY_DIR
+
+sha256sum $TAR_NAME > $TAR_NAME.sha256
+gpg --detach-sign -a $TAR_NAME
+
+cd -
+echo $TAR_NAME ": done."
+
diff --git a/modules/http-modsecurity/src/ddebug.h b/modules/http-modsecurity/src/ddebug.h
new file mode 100644
index 0000000..9785421
--- /dev/null
+++ b/modules/http-modsecurity/src/ddebug.h
@@ -0,0 +1,100 @@
+
+
+// From: https://raw.githubusercontent.com/openresty/lua-nginx-module/master/src/ddebug.h
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _DDEBUG_H_INCLUDED_
+#define _DDEBUG_H_INCLUDED_
+
+#include <ngx_core.h>
+
+/*
+ * #undef MODSECURITY_DDEBUG
+ * #define MODSECURITY_DDEBUG 1
+ */
+
+/*
+ * Setting MODSECURITY_SANITY_CHECKS will help you in the debug process. By
+ * defining MODSECURITY_SANITY_CHECKS a set of functions will be executed in
+ * order to make sure the well behavior of ModSecurity, letting you know (via
+ * debug_logs) if something unexpected happens.
+ *
+ * If performance is not a concern, it is safe to keep it set.
+ *
+ */
+#ifndef MODSECURITY_SANITY_CHECKS
+#define MODSECURITY_SANITY_CHECKS 0
+#endif
+
+#if defined(MODSECURITY_DDEBUG) && (MODSECURITY_DDEBUG)
+
+#   if (NGX_HAVE_VARIADIC_MACROS)
+
+#       define dd(...) fprintf(stderr, "modsec *** %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 void dd(const char *fmt, ...) {
+}
+
+#    endif
+
+#else
+
+#   if (NGX_HAVE_VARIADIC_MACROS)
+
+#       define dd(...)
+
+#   else
+
+#include <stdarg.h>
+
+static void dd(const char *fmt, ...) {
+}
+
+#   endif
+
+#endif
+
+#if defined(MODSECURITY_DDEBUG) && (MODSECURITY_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_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/modules/http-modsecurity/src/ngx_http_modsecurity_body_filter.c b/modules/http-modsecurity/src/ngx_http_modsecurity_body_filter.c
new file mode 100644
index 0000000..6118a94
--- /dev/null
+++ b/modules/http-modsecurity/src/ngx_http_modsecurity_body_filter.c
@@ -0,0 +1,180 @@
+/*
+ * ModSecurity connector for nginx, http://www.modsecurity.org/
+ * Copyright (c) 2015 Trustwave Holdings, Inc. (http://www.trustwave.com/)
+ *
+ * You may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * If any of the files related to licensing are missing or if you have any
+ * other questions related to licensing please contact Trustwave Holdings, Inc.
+ * directly using the email address security@modsecurity.org.
+ *
+ */
+
+#ifndef MODSECURITY_DDEBUG
+#define MODSECURITY_DDEBUG 0
+#endif
+#include "ddebug.h"
+
+#include "ngx_http_modsecurity_common.h"
+
+static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
+
+/* XXX: check behaviour on few body filters installed */
+ngx_int_t
+ngx_http_modsecurity_body_filter_init(void)
+{
+    ngx_http_next_body_filter = ngx_http_top_body_filter;
+    ngx_http_top_body_filter = ngx_http_modsecurity_body_filter;
+
+    return NGX_OK;
+}
+
+ngx_int_t
+ngx_http_modsecurity_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+    ngx_chain_t *chain = in;
+    ngx_http_modsecurity_ctx_t *ctx = NULL;
+#if defined(MODSECURITY_SANITY_CHECKS) && (MODSECURITY_SANITY_CHECKS)
+    ngx_http_modsecurity_conf_t *mcf;
+    ngx_list_part_t *part = &r->headers_out.headers.part;
+    ngx_table_elt_t *data = part->elts;
+    ngx_uint_t i = 0;
+#endif
+
+    if (in == NULL) {
+        return ngx_http_next_body_filter(r, in);
+    }
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+
+    dd("body filter, recovering ctx: %p", ctx);
+
+    if (ctx == NULL) {
+        return ngx_http_next_body_filter(r, in);
+    }
+
+#if defined(MODSECURITY_SANITY_CHECKS) && (MODSECURITY_SANITY_CHECKS)
+    mcf = ngx_http_get_module_loc_conf(r, ngx_http_modsecurity_module);
+    if (mcf != NULL && mcf->sanity_checks_enabled != NGX_CONF_UNSET)
+    {
+#if 0
+        dd("dumping stored ctx headers");
+        for (i = 0; i < ctx->sanity_headers_out->nelts; i++)
+        {
+            ngx_http_modsecurity_header_t *vals = ctx->sanity_headers_out->elts;
+            ngx_str_t *s2 = &vals[i].name, *s3 = &vals[i].value;
+            dd(" dump[%d]: name = '%.*s', value = '%.*s'", (int)i,
+                (int)s2->len, (char*)s2->data,
+                (int)s3->len, (char*)s3->data);
+        }
+#endif
+        /*
+         * Identify if there is a header that was not inspected by ModSecurity.
+         */
+        int worth_to_fail = 0;
+
+        for (i = 0; ; i++)
+        {
+            int found = 0;
+            ngx_uint_t j = 0;
+            ngx_table_elt_t *s1;
+            ngx_http_modsecurity_header_t *vals;
+
+            if (i >= part->nelts)
+            {
+                if (part->next == NULL) {
+                    break;
+                }
+
+                part = part->next;
+                data = part->elts;
+                i = 0;
+            }
+
+            vals = ctx->sanity_headers_out->elts;
+            s1 = &data[i];
+
+            /*
+             * Headers that were inspected by ModSecurity.
+             */
+            while (j < ctx->sanity_headers_out->nelts)
+            {
+                ngx_str_t *s2 = &vals[j].name;
+                ngx_str_t *s3 = &vals[j].value;
+
+                if (s1->key.len == s2->len && ngx_strncmp(s1->key.data, s2->data, s1->key.len) == 0)
+                {
+                    if (s1->value.len == s3->len && ngx_strncmp(s1->value.data, s3->data, s1->value.len) == 0)
+                    {
+                        found = 1;
+                        break;
+                    }
+                }
+                j++;
+            }
+            if (!found) {
+                dd("header: `%.*s' with value: `%.*s' was not inspected by ModSecurity",
+                    (int) s1->key.len,
+                    (const char *) s1->key.data,
+                    (int) s1->value.len,
+                    (const char *) s1->value.data);
+                worth_to_fail++;
+            }
+        }
+
+        if (worth_to_fail)
+        {
+            dd("%d header(s) were not inspected by ModSecurity, so exiting", worth_to_fail);
+            return ngx_http_filter_finalize_request(r,
+                &ngx_http_modsecurity_module, NGX_HTTP_INTERNAL_SERVER_ERROR);
+        }
+    }
+#endif
+
+    int is_request_processed = 0;
+    for (; chain != NULL; chain = chain->next)
+    {
+        u_char *data = chain->buf->pos;
+        int ret;
+
+        msc_append_response_body(ctx->modsec_transaction, data, chain->buf->last - data);
+        ret = ngx_http_modsecurity_process_intervention(ctx->modsec_transaction, r);
+        if (ret > 0) {
+            return ngx_http_filter_finalize_request(r,
+                &ngx_http_modsecurity_module, ret);
+        }
+
+/* XXX: chain->buf->last_buf || chain->buf->last_in_chain */
+        is_request_processed = chain->buf->last_buf;
+
+        if (is_request_processed) {
+            ngx_pool_t *old_pool;
+
+            old_pool = ngx_http_modsecurity_pcre_malloc_init(r->pool);
+            msc_process_response_body(ctx->modsec_transaction);
+            ngx_http_modsecurity_pcre_malloc_done(old_pool);
+
+/* XXX: I don't get how body from modsec being transferred to nginx's buffer.  If so - after adjusting of nginx's
+   XXX: body we can proceed to adjust body size (content-length).  see xslt_body_filter() for example */
+            ret = ngx_http_modsecurity_process_intervention(ctx->modsec_transaction, r);
+            if (ret > 0) {
+                return ret;
+            }
+            else if (ret < 0) {
+                return ngx_http_filter_finalize_request(r,
+                    &ngx_http_modsecurity_module, NGX_HTTP_INTERNAL_SERVER_ERROR);
+
+            }
+        }
+    }
+    if (!is_request_processed)
+    {
+        dd("buffer was not fully loaded! ctx: %p", ctx);
+    }
+
+/* XXX: xflt_filter() -- return NGX_OK here */
+    return ngx_http_next_body_filter(r, in);
+}
diff --git a/modules/http-modsecurity/src/ngx_http_modsecurity_common.h b/modules/http-modsecurity/src/ngx_http_modsecurity_common.h
new file mode 100644
index 0000000..52443fb
--- /dev/null
+++ b/modules/http-modsecurity/src/ngx_http_modsecurity_common.h
@@ -0,0 +1,151 @@
+/*
+ * ModSecurity connector for nginx, http://www.modsecurity.org/
+ * Copyright (c) 2015 Trustwave Holdings, Inc. (http://www.trustwave.com/)
+ *
+ * You may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * If any of the files related to licensing are missing or if you have any
+ * other questions related to licensing please contact Trustwave Holdings, Inc.
+ * directly using the email address security@modsecurity.org.
+ *
+ */
+
+
+#ifndef _NGX_HTTP_MODSECURITY_COMMON_H_INCLUDED_
+#define _NGX_HTTP_MODSECURITY_COMMON_H_INCLUDED_
+
+#include <nginx.h>
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+#include <modsecurity/modsecurity.h>
+#include <modsecurity/transaction.h>
+#include <modsecurity/rules.h>
+
+
+/**
+ * TAG_NUM:
+ *
+ * Alpha  - 001
+ * Beta   - 002
+ * Dev    - 010
+ * Rc1    - 051
+ * Rc2    - 052
+ * ...    - ...
+ * Release- 100
+ *
+ */
+
+#define MODSECURITY_NGINX_MAJOR "1"
+#define MODSECURITY_NGINX_MINOR "0"
+#define MODSECURITY_NGINX_PATCHLEVEL "0"
+#define MODSECURITY_NGINX_TAG ""
+#define MODSECURITY_NGINX_TAG_NUM "100"
+
+#define MODSECURITY_NGINX_VERSION MODSECURITY_NGINX_MAJOR "." \
+    MODSECURITY_NGINX_MINOR "." MODSECURITY_NGINX_PATCHLEVEL \
+    MODSECURITY_NGINX_TAG
+
+#define MODSECURITY_NGINX_VERSION_NUM MODSECURITY_NGINX_MAJOR \
+    MODSECURITY_NGINX_MINOR MODSECURITY_NGINX_PATCHLEVEL \
+    MODSECURITY_NGINX_TAG_NUM
+
+#define MODSECURITY_NGINX_WHOAMI "ModSecurity-nginx v" \
+    MODSECURITY_NGINX_VERSION
+
+typedef struct {
+    ngx_str_t name;
+    ngx_str_t value;
+} ngx_http_modsecurity_header_t;
+
+
+typedef struct {
+    ngx_http_request_t *r;
+    Transaction *modsec_transaction;
+    ModSecurityIntervention *delayed_intervention;
+
+#if defined(MODSECURITY_SANITY_CHECKS) && (MODSECURITY_SANITY_CHECKS)
+    /*
+     * Should be filled with the headers that were sent to ModSecurity.
+     *
+     * The idea is to compare this set of headers with the headers that were
+     * sent to the client. This check was placed because we don't have control
+     * over other modules, thus, we may partially inspect the headers.
+     *
+     */
+    ngx_array_t *sanity_headers_out;
+#endif
+
+    unsigned waiting_more_body:1;
+    unsigned body_requested:1;
+    unsigned processed:1;
+} ngx_http_modsecurity_ctx_t;
+
+
+typedef struct {
+    void                      *pool;
+    ModSecurity               *modsec;
+    ngx_uint_t                 rules_inline;
+    ngx_uint_t                 rules_file;
+    ngx_uint_t                 rules_remote;
+} ngx_http_modsecurity_main_conf_t;
+
+
+typedef struct {
+    void                      *pool;
+    Rules                     *rules_set;
+
+    ngx_flag_t                 enable;
+#if defined(MODSECURITY_SANITY_CHECKS) && (MODSECURITY_SANITY_CHECKS)
+    ngx_flag_t                 sanity_checks_enabled;
+#endif
+
+    ngx_http_complex_value_t  *transaction_id;
+} ngx_http_modsecurity_conf_t;
+
+
+typedef ngx_int_t (*ngx_http_modsecurity_resolv_header_pt)(ngx_http_request_t *r, ngx_str_t name, off_t offset);
+
+typedef struct {
+    ngx_str_t name;
+    ngx_uint_t offset;
+    ngx_http_modsecurity_resolv_header_pt resolver;
+} ngx_http_modsecurity_header_out_t;
+
+
+extern ngx_module_t ngx_http_modsecurity_module;
+
+/* ngx_http_modsecurity_module.c */
+int ngx_http_modsecurity_process_intervention (Transaction *transaction, ngx_http_request_t *r);
+ngx_http_modsecurity_ctx_t *ngx_http_modsecurity_create_ctx(ngx_http_request_t *r);
+char *ngx_str_to_char(ngx_str_t a, ngx_pool_t *p);
+ngx_pool_t *ngx_http_modsecurity_pcre_malloc_init(ngx_pool_t *pool);
+void ngx_http_modsecurity_pcre_malloc_done(ngx_pool_t *old_pool);
+
+/* ngx_http_modsecurity_body_filter.c */
+ngx_int_t ngx_http_modsecurity_body_filter_init(void);
+ngx_int_t ngx_http_modsecurity_body_filter(ngx_http_request_t *r, ngx_chain_t *in);
+
+/* ngx_http_modsecurity_header_filter.c */
+ngx_int_t ngx_http_modsecurity_header_filter_init(void);
+ngx_int_t ngx_http_modsecurity_header_filter(ngx_http_request_t *r);
+#if defined(MODSECURITY_SANITY_CHECKS) && (MODSECURITY_SANITY_CHECKS)
+int ngx_http_modescurity_store_ctx_header(ngx_http_request_t *r, ngx_str_t *name, ngx_str_t *value);
+#endif
+
+/* ngx_http_modsecurity_log.c */
+void ngx_http_modsecurity_log(void *log, const void* data);
+ngx_int_t ngx_http_modsecurity_log_handler(ngx_http_request_t *r);
+
+/* ngx_http_modsecurity_pre_access.c */
+ngx_int_t ngx_http_modsecurity_pre_access_handler(ngx_http_request_t *r);
+
+/* ngx_http_modsecurity_rewrite.c */
+ngx_int_t ngx_http_modsecurity_rewrite_handler(ngx_http_request_t *r);
+
+
+#endif /* _NGX_HTTP_MODSECURITY_COMMON_H_INCLUDED_ */
diff --git a/modules/http-modsecurity/src/ngx_http_modsecurity_header_filter.c b/modules/http-modsecurity/src/ngx_http_modsecurity_header_filter.c
new file mode 100644
index 0000000..3f9f748
--- /dev/null
+++ b/modules/http-modsecurity/src/ngx_http_modsecurity_header_filter.c
@@ -0,0 +1,551 @@
+/*
+ * ModSecurity connector for nginx, http://www.modsecurity.org/
+ * Copyright (c) 2015 Trustwave Holdings, Inc. (http://www.trustwave.com/)
+ *
+ * You may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * If any of the files related to licensing are missing or if you have any
+ * other questions related to licensing please contact Trustwave Holdings, Inc.
+ * directly using the email address security@modsecurity.org.
+ *
+ */
+
+#ifndef MODSECURITY_DDEBUG
+#define MODSECURITY_DDEBUG 0
+#endif
+#include "ddebug.h"
+
+#include "ngx_http_modsecurity_common.h"
+
+static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
+
+static ngx_int_t ngx_http_modsecurity_resolv_header_server(ngx_http_request_t *r, ngx_str_t name, off_t offset);
+static ngx_int_t ngx_http_modsecurity_resolv_header_date(ngx_http_request_t *r, ngx_str_t name, off_t offset);
+static ngx_int_t ngx_http_modsecurity_resolv_header_content_length(ngx_http_request_t *r, ngx_str_t name, off_t offset);
+static ngx_int_t ngx_http_modsecurity_resolv_header_content_type(ngx_http_request_t *r, ngx_str_t name, off_t offset);
+static ngx_int_t ngx_http_modsecurity_resolv_header_last_modified(ngx_http_request_t *r, ngx_str_t name, off_t offset);
+static ngx_int_t ngx_http_modsecurity_resolv_header_connection(ngx_http_request_t *r, ngx_str_t name, off_t offset);
+static ngx_int_t ngx_http_modsecurity_resolv_header_transfer_encoding(ngx_http_request_t *r, ngx_str_t name, off_t offset);
+static ngx_int_t ngx_http_modsecurity_resolv_header_vary(ngx_http_request_t *r, ngx_str_t name, off_t offset);
+
+ngx_http_modsecurity_header_out_t ngx_http_modsecurity_headers_out[] = {
+
+    { ngx_string("Server"),
+            offsetof(ngx_http_headers_out_t, server),
+            ngx_http_modsecurity_resolv_header_server },
+
+    { ngx_string("Date"),
+            offsetof(ngx_http_headers_out_t, date),
+            ngx_http_modsecurity_resolv_header_date },
+
+    { ngx_string("Content-Length"),
+            offsetof(ngx_http_headers_out_t, content_length_n),
+            ngx_http_modsecurity_resolv_header_content_length },
+
+    { ngx_string("Content-Type"),
+            offsetof(ngx_http_headers_out_t, content_type),
+            ngx_http_modsecurity_resolv_header_content_type },
+
+    { ngx_string("Last-Modified"),
+            offsetof(ngx_http_headers_out_t, last_modified),
+            ngx_http_modsecurity_resolv_header_last_modified },
+
+    { ngx_string("Connection"),
+            0,
+            ngx_http_modsecurity_resolv_header_connection },
+
+    { ngx_string("Transfer-Encoding"),
+            0,
+            ngx_http_modsecurity_resolv_header_transfer_encoding },
+
+    { ngx_string("Vary"),
+            0,
+            ngx_http_modsecurity_resolv_header_vary },
+
+#if 0
+    { ngx_string("Content-Encoding"),
+            offsetof(ngx_http_headers_out_t, content_encoding),
+            NGX_TABLE },
+
+    { ngx_string("Cache-Control"),
+            offsetof(ngx_http_headers_out_t, cache_control),
+            NGX_ARRAY },
+
+    { ngx_string("Location"),
+            offsetof(ngx_http_headers_out_t, location),
+            NGX_TABLE },
+
+    { ngx_string("Content-Range"),
+            offsetof(ngx_http_headers_out_t, content_range),
+            NGX_TABLE },
+
+    { ngx_string("Accept-Ranges"),
+            offsetof(ngx_http_headers_out_t, accept_ranges),
+            NGX_TABLE },
+
+    returiders_out[i].name 1;
+    { ngx_string("WWW-Authenticate"),
+            offsetof(ngx_http_headers_out_t, www_authenticate),
+            NGX_TABLE },
+
+    { ngx_string("Expires"),
+            offsetof(ngx_http_headers_out_t, expires),
+            NGX_TABLE },
+#endif
+    { ngx_null_string, 0, 0 }
+};
+
+
+#if defined(MODSECURITY_SANITY_CHECKS) && (MODSECURITY_SANITY_CHECKS)
+int
+ngx_http_modescurity_store_ctx_header(ngx_http_request_t *r, ngx_str_t *name, ngx_str_t *value)
+{
+    ngx_http_modsecurity_ctx_t     *ctx;
+    ngx_http_modsecurity_conf_t    *mcf;
+    ngx_http_modsecurity_header_t  *hdr;
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+    if (ctx == NULL || ctx->sanity_headers_out == NULL) {
+        return NGX_ERROR;
+    }
+
+    mcf = ngx_http_get_module_loc_conf(r, ngx_http_modsecurity_module);
+    if (mcf == NULL || mcf->sanity_checks_enabled == NGX_CONF_UNSET)
+    {
+        return NGX_OK;
+    }
+
+    hdr = ngx_array_push(ctx->sanity_headers_out);
+    if (hdr == NULL) {
+        return NGX_ERROR;
+    }
+
+    hdr->name.data = ngx_pnalloc(r->pool, name->len);
+    hdr->value.data = ngx_pnalloc(r->pool, value->len);
+    if (hdr->name.data == NULL || hdr->value.data == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_memcpy(hdr->name.data, name->data, name->len);
+    hdr->name.len = name->len;
+    ngx_memcpy(hdr->value.data, value->data, value->len);
+    hdr->value.len = value->len;
+
+    return NGX_OK;
+}
+#endif
+
+
+static ngx_int_t
+ngx_http_modsecurity_resolv_header_server(ngx_http_request_t *r, ngx_str_t name, off_t offset)
+{
+    static char ngx_http_server_full_string[] = NGINX_VER;
+    static char ngx_http_server_string[] = "nginx";
+
+    ngx_http_core_loc_conf_t *clcf = NULL;
+    ngx_http_modsecurity_ctx_t *ctx = NULL;
+    ngx_str_t value;
+
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+    ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+
+    if (r->headers_out.server == NULL) {
+        if (clcf->server_tokens) {
+            value.data = (u_char *)ngx_http_server_full_string;
+            value.len = sizeof(ngx_http_server_full_string);
+        } else {
+            value.data = (u_char *)ngx_http_server_string;
+            value.len = sizeof(ngx_http_server_string);
+        }
+    } else {
+        ngx_table_elt_t *h = r->headers_out.server;
+        value.data = h->value.data;
+        value.len =  h->value.len;
+    }
+
+#if defined(MODSECURITY_SANITY_CHECKS) && (MODSECURITY_SANITY_CHECKS)
+    ngx_http_modescurity_store_ctx_header(r, &name, &value);
+#endif
+
+    return msc_add_n_response_header(ctx->modsec_transaction,
+        (const unsigned char *) name.data,
+        name.len,
+        (const unsigned char *) value.data,
+        value.len);
+}
+
+
+static ngx_int_t
+ngx_http_modsecurity_resolv_header_date(ngx_http_request_t *r, ngx_str_t name, off_t offset)
+{
+    ngx_http_modsecurity_ctx_t *ctx = NULL;
+    ngx_str_t date;
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+
+    if (r->headers_out.date == NULL) {
+        date.data = ngx_cached_http_time.data;
+        date.len = ngx_cached_http_time.len;
+    } else {
+        ngx_table_elt_t *h = r->headers_out.date;
+        date.data = h->value.data;
+        date.len = h->value.len;
+    }
+
+#if defined(MODSECURITY_SANITY_CHECKS) && (MODSECURITY_SANITY_CHECKS)
+    ngx_http_modescurity_store_ctx_header(r, &name, &date);
+#endif
+
+    return msc_add_n_response_header(ctx->modsec_transaction,
+        (const unsigned char *) name.data,
+        name.len,
+        (const unsigned char *) date.data,
+        date.len);
+}
+
+
+static ngx_int_t
+ngx_http_modsecurity_resolv_header_content_length(ngx_http_request_t *r, ngx_str_t name, off_t offset)
+{
+    ngx_http_modsecurity_ctx_t *ctx = NULL;
+    ngx_str_t value;
+    char buf[NGX_INT64_LEN+2];
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+
+    if (r->headers_out.content_length_n > 0)
+    {
+        ngx_sprintf((u_char *)buf, "%O%Z", r->headers_out.content_length_n);
+        value.data = (unsigned char *)buf;
+        value.len = strlen(buf);
+
+#if defined(MODSECURITY_SANITY_CHECKS) && (MODSECURITY_SANITY_CHECKS)
+        ngx_http_modescurity_store_ctx_header(r, &name, &value);
+#endif
+        return msc_add_n_response_header(ctx->modsec_transaction,
+            (const unsigned char *) name.data,
+            name.len,
+            (const unsigned char *) value.data,
+            value.len);
+    }
+
+    return 1;
+}
+
+
+static ngx_int_t
+ngx_http_modsecurity_resolv_header_content_type(ngx_http_request_t *r, ngx_str_t name, off_t offset)
+{
+    ngx_http_modsecurity_ctx_t *ctx = NULL;
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+
+    if (r->headers_out.content_type.len > 0)
+    {
+
+#if defined(MODSECURITY_SANITY_CHECKS) && (MODSECURITY_SANITY_CHECKS)
+        ngx_http_modescurity_store_ctx_header(r, &name, &r->headers_out.content_type);
+#endif
+
+        return msc_add_n_response_header(ctx->modsec_transaction,
+            (const unsigned char *) name.data,
+            name.len,
+            (const unsigned char *) r->headers_out.content_type.data,
+            r->headers_out.content_type.len);
+    }
+
+    return 1;
+}
+
+
+static ngx_int_t
+ngx_http_modsecurity_resolv_header_last_modified(ngx_http_request_t *r, ngx_str_t name, off_t offset)
+{
+    ngx_http_modsecurity_ctx_t *ctx = NULL;
+    u_char buf[1024], *p;
+    ngx_str_t value;
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+
+    if (r->headers_out.last_modified_time == -1) {
+        return 1;
+    }
+
+    p = ngx_http_time(buf, r->headers_out.last_modified_time);
+
+    value.data = buf;
+    value.len = (int)(p-buf);
+
+#if defined(MODSECURITY_SANITY_CHECKS) && (MODSECURITY_SANITY_CHECKS)
+    ngx_http_modescurity_store_ctx_header(r, &name, &value);
+#endif
+
+    return msc_add_n_response_header(ctx->modsec_transaction,
+        (const unsigned char *) name.data,
+        name.len,
+        (const unsigned char *) value.data,
+        value.len);
+}
+
+
+static ngx_int_t
+ngx_http_modsecurity_resolv_header_connection(ngx_http_request_t *r, ngx_str_t name, off_t offset)
+{
+    ngx_http_modsecurity_ctx_t *ctx = NULL;
+    ngx_http_core_loc_conf_t *clcf = NULL;
+    char *connection = NULL;
+    ngx_str_t value;
+
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+    ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+
+    if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) {
+        connection = "upgrade";
+    } else if (r->keepalive) {
+        connection = "keep-alive";
+        if (clcf->keepalive_header)
+        {
+            u_char buf[1024];
+            ngx_sprintf(buf, "timeout=%T%Z", clcf->keepalive_header);
+            ngx_str_t name2 = ngx_string("Keep-Alive");
+
+            value.data = buf;
+            value.len = strlen((char *)buf);
+
+#if defined(MODSECURITY_SANITY_CHECKS) && (MODSECURITY_SANITY_CHECKS)
+            ngx_http_modescurity_store_ctx_header(r, &name2, &value);
+#endif
+
+            msc_add_n_response_header(ctx->modsec_transaction,
+                (const unsigned char *) name2.data,
+                name2.len,
+                (const unsigned char *) value.data,
+                value.len);
+        }
+    } else {
+        connection = "close";
+    }
+
+    value.data = (u_char *) connection;
+    value.len = strlen(connection);
+
+#if defined(MODSECURITY_SANITY_CHECKS) && (MODSECURITY_SANITY_CHECKS)
+    ngx_http_modescurity_store_ctx_header(r, &name, &value);
+#endif
+
+    return msc_add_n_response_header(ctx->modsec_transaction,
+        (const unsigned char *) name.data,
+        name.len,
+        (const unsigned char *) value.data,
+        value.len);
+}
+
+static ngx_int_t
+ngx_http_modsecurity_resolv_header_transfer_encoding(ngx_http_request_t *r, ngx_str_t name, off_t offset)
+{
+    ngx_http_modsecurity_ctx_t *ctx = NULL;
+
+    if (r->chunked) {
+        ngx_str_t value = ngx_string("chunked");
+
+        ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+
+#if defined(MODSECURITY_SANITY_CHECKS) && (MODSECURITY_SANITY_CHECKS)
+        ngx_http_modescurity_store_ctx_header(r, &name, &value);
+#endif
+
+        return msc_add_n_response_header(ctx->modsec_transaction,
+            (const unsigned char *) name.data,
+            name.len,
+            (const unsigned char *) value.data,
+            value.len);
+    }
+
+    return 1;
+}
+
+static ngx_int_t
+ngx_http_modsecurity_resolv_header_vary(ngx_http_request_t *r, ngx_str_t name, off_t offset)
+{
+#if (NGX_HTTP_GZIP)
+    ngx_http_modsecurity_ctx_t *ctx = NULL;
+    ngx_http_core_loc_conf_t *clcf = NULL;
+
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+    if (r->gzip_vary && clcf->gzip_vary) {
+        ngx_str_t value = ngx_string("Accept-Encoding");
+
+        ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+
+#if defined(MODSECURITY_SANITY_CHECKS) && (MODSECURITY_SANITY_CHECKS)
+        ngx_http_modescurity_store_ctx_header(r, &name, &value);
+#endif
+
+        return msc_add_n_response_header(ctx->modsec_transaction,
+            (const unsigned char *) name.data,
+            name.len,
+            (const unsigned char *) value.data,
+            value.len);
+    }
+#endif
+
+    return 1;
+}
+
+ngx_int_t
+ngx_http_modsecurity_header_filter_init(void)
+{
+    ngx_http_next_header_filter = ngx_http_top_header_filter;
+    ngx_http_top_header_filter = ngx_http_modsecurity_header_filter;
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_modsecurity_header_filter(ngx_http_request_t *r)
+{
+    ngx_http_modsecurity_ctx_t *ctx;
+    ngx_list_part_t *part = &r->headers_out.headers.part;
+    ngx_table_elt_t *data = part->elts;
+    ngx_uint_t i = 0;
+    int ret = 0;
+    ngx_uint_t status;
+    char *http_response_ver;
+    ngx_pool_t *old_pool;
+
+
+/* XXX: if NOT_MODIFIED, do we need to process it at all?  see xslt_header_filter() */
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+
+    dd("header filter, recovering ctx: %p", ctx);
+
+    if (ctx == NULL)
+    {
+        dd("something really bad happened or ModSecurity is disabled. going to the next filter.");
+        return ngx_http_next_header_filter(r);
+    }
+
+/* XXX: can it happen ?  already processed i mean */
+/* XXX: check behaviour on 'ModSecurity off' */
+
+    if (ctx && ctx->processed)
+    {
+        /*
+         * FIXME: verify if this request is already processed.
+         */
+        dd("Already processed... going to the next header...");
+        return ngx_http_next_header_filter(r);
+    }
+
+    /*
+     * Lets ask nginx to keep the response body in memory
+     *
+     * FIXME: I don't see a reason to keep it `1' when SecResponseBody is disabled.
+     */
+    r->filter_need_in_memory = 1;
+
+    ctx->processed = 1;
+    /*
+     *
+     * Assuming ModSecurity module is running immediately before the
+     * ngx_http_header_filter, we will be able to populate ModSecurity with
+     * headers from the headers_out structure.
+     *
+     * As ngx_http_header_filter place a direct call to the
+     * ngx_http_write_filter_module, we cannot hook between those two. In order
+     * to enumerate all headers, we first look at the headers_out structure,
+     * and later we look into the ngx_list_part_t. The ngx_list_part_t must be
+     * checked. Other module(s) in the chain may added some content to it.
+     *
+     */
+    for (i = 0; ngx_http_modsecurity_headers_out[i].name.len; i++)
+    {
+        dd(" Sending header to ModSecurity - header: `%.*s'.",
+            (int) ngx_http_modsecurity_headers_out[i].name.len,
+            ngx_http_modsecurity_headers_out[i].name.data);
+
+                ngx_http_modsecurity_headers_out[i].resolver(r,
+                    ngx_http_modsecurity_headers_out[i].name,
+                    ngx_http_modsecurity_headers_out[i].offset);
+    }
+
+    for (i = 0 ;; i++)
+    {
+        if (i >= part->nelts)
+        {
+            if (part->next == NULL) {
+                break;
+            }
+
+            part = part->next;
+            data = part->elts;
+            i = 0;
+        }
+
+#if defined(MODSECURITY_SANITY_CHECKS) && (MODSECURITY_SANITY_CHECKS)
+        ngx_http_modescurity_store_ctx_header(r, &data[i].key, &data[i].value);
+#endif
+
+        /*
+         * Doing this ugly cast here, explanation on the request_header
+         */
+        msc_add_n_response_header(ctx->modsec_transaction,
+            (const unsigned char *) data[i].key.data,
+            data[i].key.len,
+            (const unsigned char *) data[i].value.data,
+            data[i].value.len);
+    }
+
+    /* prepare extra paramters for msc_process_response_headers() */
+    if (r->err_status) {
+        status = r->err_status;
+    } else {
+        status = r->headers_out.status;
+    }
+
+    /*
+     * NGINX always sends HTTP response with HTTP/1.1, except cases when
+     * HTTP V2 module is enabled, and request has been posted with HTTP/2.0.
+     */
+    http_response_ver = "HTTP 1.1";
+#if (NGX_HTTP_V2)
+    if (r->stream) {
+        http_response_ver = "HTTP 2.0";
+    }
+#endif
+
+    old_pool = ngx_http_modsecurity_pcre_malloc_init(r->pool);
+    msc_process_response_headers(ctx->modsec_transaction, status, http_response_ver);
+    ngx_http_modsecurity_pcre_malloc_done(old_pool);
+    ret = ngx_http_modsecurity_process_intervention(ctx->modsec_transaction, r);
+    if (ret > 0) {
+        return ret;
+    }
+
+    /*
+     * Proxies will not like this... but it is necessary to unset
+     * the content length in order to manipulate the content of
+     * response body in ModSecurity.
+     *
+     * This header may arrive at the client before ModSecurity had
+     * a change to make any modification. That is why it is necessary
+     * to set this to -1 here.
+     *
+     * We need to have some kind of flag the decide if ModSecurity
+     * will make a modification or not. If not, keep the content and
+     * make the proxy servers happy.
+     *
+     */
+
+    /*
+     * The line below is commented to make the spdy test to work
+     */
+     //r->headers_out.content_length_n = -1;
+
+    return ngx_http_next_header_filter(r);
+}
diff --git a/modules/http-modsecurity/src/ngx_http_modsecurity_log.c b/modules/http-modsecurity/src/ngx_http_modsecurity_log.c
new file mode 100644
index 0000000..5546596
--- /dev/null
+++ b/modules/http-modsecurity/src/ngx_http_modsecurity_log.c
@@ -0,0 +1,76 @@
+/*
+ * ModSecurity connector for nginx, http://www.modsecurity.org/
+ * Copyright (c) 2015 Trustwave Holdings, Inc. (http://www.trustwave.com/)
+ *
+ * You may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * If any of the files related to licensing are missing or if you have any
+ * other questions related to licensing please contact Trustwave Holdings, Inc.
+ * directly using the email address security@modsecurity.org.
+ *
+ */
+
+#ifndef MODSECURITY_DDEBUG
+#define MODSECURITY_DDEBUG 0
+#endif
+#include "ddebug.h"
+
+#include "ngx_http_modsecurity_common.h"
+
+
+void
+ngx_http_modsecurity_log(void *log, const void* data)
+{
+    const char *msg;
+    if (log == NULL) {
+        return;
+    }
+    msg = (const char *) data;
+
+    ngx_log_error(NGX_LOG_INFO, (ngx_log_t *)log, 0, "%s", msg);
+}
+
+
+ngx_int_t
+ngx_http_modsecurity_log_handler(ngx_http_request_t *r)
+{
+    ngx_pool_t                   *old_pool;
+    ngx_http_modsecurity_ctx_t   *ctx;
+    ngx_http_modsecurity_conf_t  *mcf;
+
+    dd("catching a new _log_ phase handler");
+
+    mcf = ngx_http_get_module_loc_conf(r, ngx_http_modsecurity_module);
+    if (mcf == NULL || mcf->enable != 1)
+    {
+        dd("ModSecurity not enabled... returning");
+        return NGX_OK;
+    }
+
+    /*
+    if (r->method != NGX_HTTP_GET &&
+        r->method != NGX_HTTP_POST && r->method != NGX_HTTP_HEAD) {
+        dd("ModSecurity is not ready to deal with anything different from " \
+            "POST, GET or HEAD");
+        return NGX_OK;
+    }
+    */
+    ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+
+    dd("recovering ctx: %p", ctx);
+
+    if (ctx == NULL) {
+        dd("something really bad happened here. returning NGX_ERROR");
+        return NGX_ERROR;
+    }
+
+    dd("calling msc_process_logging for %p", ctx);
+    old_pool = ngx_http_modsecurity_pcre_malloc_init(r->pool);
+    msc_process_logging(ctx->modsec_transaction);
+    ngx_http_modsecurity_pcre_malloc_done(old_pool);
+
+    return NGX_OK;
+}
diff --git a/modules/http-modsecurity/src/ngx_http_modsecurity_module.c b/modules/http-modsecurity/src/ngx_http_modsecurity_module.c
new file mode 100644
index 0000000..420b917
--- /dev/null
+++ b/modules/http-modsecurity/src/ngx_http_modsecurity_module.c
@@ -0,0 +1,770 @@
+/*
+ * ModSecurity connector for nginx, http://www.modsecurity.org/
+ * Copyright (c) 2015 Trustwave Holdings, Inc. (http://www.trustwave.com/)
+ *
+ * You may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * If any of the files related to licensing are missing or if you have any
+ * other questions related to licensing please contact Trustwave Holdings, Inc.
+ * directly using the email address security@modsecurity.org.
+ *
+ */
+
+#ifndef MODSECURITY_DDEBUG
+#define MODSECURITY_DDEBUG 0
+#endif
+#include "ddebug.h"
+
+#include "ngx_http_modsecurity_common.h"
+#include "stdio.h"
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+static ngx_int_t ngx_http_modsecurity_init(ngx_conf_t *cf);
+static void *ngx_http_modsecurity_create_main_conf(ngx_conf_t *cf);
+static char *ngx_http_modsecurity_init_main_conf(ngx_conf_t *cf, void *conf);
+static void *ngx_http_modsecurity_create_conf(ngx_conf_t *cf);
+static char *ngx_http_modsecurity_merge_conf(ngx_conf_t *cf, void *parent, void *child);
+static void ngx_http_modsecurity_cleanup_instance(void *data);
+static void ngx_http_modsecurity_cleanup_rules(void *data);
+
+
+/*
+ * PCRE malloc/free workaround, based on
+ * https://github.com/openresty/lua-nginx-module/blob/master/src/ngx_http_lua_pcrefix.c
+ */
+
+static void *(*old_pcre_malloc)(size_t);
+static void (*old_pcre_free)(void *ptr);
+static ngx_pool_t *ngx_http_modsec_pcre_pool = NULL;
+
+static void *
+ngx_http_modsec_pcre_malloc(size_t size)
+{
+    if (ngx_http_modsec_pcre_pool) {
+        return ngx_palloc(ngx_http_modsec_pcre_pool, size);
+    }
+
+    fprintf(stderr, "error: modsec pcre malloc failed due to empty pcre pool");
+
+    return NULL;
+}
+
+static void
+ngx_http_modsec_pcre_free(void *ptr)
+{
+    if (ngx_http_modsec_pcre_pool) {
+        ngx_pfree(ngx_http_modsec_pcre_pool, ptr);
+        return;
+    }
+
+#if 0
+    /* this may happen when called from cleanup handlers */
+    fprintf(stderr, "error: modsec pcre free failed due to empty pcre pool");
+#endif
+
+    return;
+}
+
+ngx_pool_t *
+ngx_http_modsecurity_pcre_malloc_init(ngx_pool_t *pool)
+{
+    ngx_pool_t  *old_pool;
+
+    if (pcre_malloc != ngx_http_modsec_pcre_malloc) {
+        ngx_http_modsec_pcre_pool = pool;
+
+        old_pcre_malloc = pcre_malloc;
+        old_pcre_free = pcre_free;
+
+        pcre_malloc = ngx_http_modsec_pcre_malloc;
+        pcre_free = ngx_http_modsec_pcre_free;
+
+        return NULL;
+    }
+
+    old_pool = ngx_http_modsec_pcre_pool;
+    ngx_http_modsec_pcre_pool = pool;
+
+    return old_pool;
+}
+
+void
+ngx_http_modsecurity_pcre_malloc_done(ngx_pool_t *old_pool)
+{
+    ngx_http_modsec_pcre_pool = old_pool;
+
+    if (old_pool == NULL) {
+        pcre_malloc = old_pcre_malloc;
+        pcre_free = old_pcre_free;
+    }
+}
+
+/*
+ * ngx_string's are not null-terminated in common case, so we need to convert
+ * them into null-terminated ones before passing to ModSecurity
+ */
+ngx_inline char *ngx_str_to_char(ngx_str_t a, ngx_pool_t *p)
+{
+    char *str = NULL;
+
+    if (a.len == 0) {
+        return NULL;
+    }
+
+    str = ngx_pnalloc(p, a.len+1);
+    if (str == NULL) {
+        dd("failed to allocate memory to convert space ngx_string to C string");
+        /* We already returned NULL for an empty string, so return -1 here to indicate allocation error */
+        return (char *)-1;
+    }
+    ngx_memcpy(str, a.data, a.len);
+    str[a.len] = '\0';
+
+    return str;
+}
+
+
+ngx_inline int
+ngx_http_modsecurity_process_intervention (Transaction *transaction, ngx_http_request_t *r)
+{
+    char *log = NULL;
+    ModSecurityIntervention intervention;
+    intervention.status = 200;
+    intervention.url = NULL;
+    intervention.log = NULL;
+    intervention.disruptive = 0;
+
+    dd("processing intervention");
+
+    if (msc_intervention(transaction, &intervention) == 0) {
+        dd("nothing to do");
+        return 0;
+    }
+
+    log = intervention.log;
+    if (intervention.log == NULL) {
+        log = "(no log message was specified)";
+    }
+
+    ngx_log_error(NGX_LOG_ERR, (ngx_log_t *)r->connection->log, 0, "%s", log);
+
+    if (intervention.log != NULL) {
+        free(intervention.log);
+    }
+
+    if (intervention.url != NULL)
+    {
+        dd("intervention -- redirecting to: %s with status code: %d", intervention.url, intervention.status);
+
+        if (r->header_sent)
+        {
+            dd("Headers are already sent. Cannot perform the redirection at this point.");
+            return -1;
+        }
+
+        /**
+         * Not sure if it sane to do this indepent of the phase
+         * but, here we go...
+         *
+         * This code cames from: http/ngx_http_special_response.c
+         * function: ngx_http_send_error_page
+         * src/http/ngx_http_core_module.c
+         * From src/http/ngx_http_core_module.c (line 1910) i learnt
+         * that location->hash should be set to 1.
+         *
+         */
+        ngx_http_clear_location(r);
+        ngx_str_t a = ngx_string("");
+
+        a.data = (unsigned char *)intervention.url;
+        a.len = strlen(intervention.url);
+
+        ngx_table_elt_t *location = NULL;
+        location = ngx_list_push(&r->headers_out.headers);
+        ngx_str_set(&location->key, "Location");
+        location->value = a;
+        r->headers_out.location = location;
+        r->headers_out.location->hash = 1;
+
+#if defined(MODSECURITY_SANITY_CHECKS) && (MODSECURITY_SANITY_CHECKS)
+        ngx_http_modescurity_store_ctx_header(r, &location->key, &location->value);
+#endif
+
+        return intervention.status;
+    }
+
+    if (intervention.status != 200)
+    {
+        if (r->header_sent)
+        {
+            dd("Headers are already sent. Cannot perform the redirection at this point.");
+            return -1;
+        }
+        dd("intervention -- returning code: %d", intervention.status);
+        return intervention.status;
+    }
+    return 0;
+}
+
+
+void
+ngx_http_modsecurity_cleanup(void *data)
+{
+    ngx_http_modsecurity_ctx_t *ctx;
+
+    ctx = (ngx_http_modsecurity_ctx_t *) data;
+
+    msc_transaction_cleanup(ctx->modsec_transaction);
+
+#if defined(MODSECURITY_SANITY_CHECKS) && (MODSECURITY_SANITY_CHECKS)
+    /*
+     * Purge stored context headers.  Memory allocated for individual stored header
+     * name/value pair will be freed automatically when r->pool is destroyed.
+     */
+    ngx_array_destroy(ctx->sanity_headers_out);
+#endif
+}
+
+
+ngx_inline ngx_http_modsecurity_ctx_t *
+ngx_http_modsecurity_create_ctx(ngx_http_request_t *r)
+{
+    ngx_str_t                          s;
+    ngx_pool_cleanup_t                *cln;
+    ngx_http_modsecurity_ctx_t        *ctx;
+    ngx_http_modsecurity_conf_t       *mcf;
+    ngx_http_modsecurity_main_conf_t  *mmcf;
+
+    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_modsecurity_ctx_t));
+    if (ctx == NULL)
+    {
+        dd("failed to allocate memory for the context.");
+        return NULL;
+    }
+
+    mmcf = ngx_http_get_module_main_conf(r, ngx_http_modsecurity_module);
+    mcf = ngx_http_get_module_loc_conf(r, ngx_http_modsecurity_module);
+
+    dd("creating transaction with the following rules: '%p' -- ms: '%p'", mcf->rules_set, mmcf->modsec);
+
+    if (mcf->transaction_id) {
+        if (ngx_http_complex_value(r, mcf->transaction_id, &s) != NGX_OK) {
+            return NGX_CONF_ERROR;
+        }
+        ctx->modsec_transaction = msc_new_transaction_with_id(mmcf->modsec, mcf->rules_set, (char *) s.data, r->connection->log);
+
+    } else {
+        ctx->modsec_transaction = msc_new_transaction(mmcf->modsec, mcf->rules_set, r->connection->log);
+    }
+
+    dd("transaction created");
+
+    ngx_http_set_ctx(r, ctx, ngx_http_modsecurity_module);
+
+    cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_http_modsecurity_ctx_t));
+    if (cln == NULL)
+    {
+        dd("failed to create the ModSecurity context cleanup");
+        return NGX_CONF_ERROR;
+    }
+    cln->handler = ngx_http_modsecurity_cleanup;
+    cln->data = ctx;
+
+#if defined(MODSECURITY_SANITY_CHECKS) && (MODSECURITY_SANITY_CHECKS)
+    ctx->sanity_headers_out = ngx_array_create(r->pool, 12, sizeof(ngx_http_modsecurity_header_t));
+    if (ctx->sanity_headers_out == NULL) {
+        return NGX_CONF_ERROR;
+    }
+#endif
+
+    return ctx;
+}
+
+
+char *
+ngx_conf_set_rules(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    int                                res;
+    char                              *rules;
+    ngx_str_t                         *value;
+    const char                        *error;
+    ngx_pool_t                        *old_pool;
+    ngx_http_modsecurity_conf_t       *mcf = conf;
+    ngx_http_modsecurity_main_conf_t  *mmcf;
+
+    value = cf->args->elts;
+    rules = ngx_str_to_char(value[1], cf->pool);
+
+    if (rules == (char *)-1) {
+        return NGX_CONF_ERROR;
+    }
+
+    old_pool = ngx_http_modsecurity_pcre_malloc_init(cf->pool);
+    res = msc_rules_add(mcf->rules_set, rules, &error);
+    ngx_http_modsecurity_pcre_malloc_done(old_pool);
+
+    if (res < 0) {
+        dd("Failed to load the rules: '%s' - reason: '%s'", rules, error);
+        return strdup(error);
+    }
+
+    mmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_modsecurity_module);
+    mmcf->rules_inline += res;
+
+    return NGX_CONF_OK;
+}
+
+
+char *
+ngx_conf_set_rules_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    int                                res;
+    char                              *rules_set;
+    ngx_str_t                         *value;
+    const char                        *error;
+    ngx_pool_t                        *old_pool;
+    ngx_http_modsecurity_conf_t       *mcf = conf;
+    ngx_http_modsecurity_main_conf_t  *mmcf;
+
+    value = cf->args->elts;
+    rules_set = ngx_str_to_char(value[1], cf->pool);
+
+    if (rules_set == (char *)-1) {
+        return NGX_CONF_ERROR;
+    }
+
+    old_pool = ngx_http_modsecurity_pcre_malloc_init(cf->pool);
+    res = msc_rules_add_file(mcf->rules_set, rules_set, &error);
+    ngx_http_modsecurity_pcre_malloc_done(old_pool);
+
+    if (res < 0) {
+        dd("Failed to load the rules from: '%s' - reason: '%s'", rules_set, error);
+        return strdup(error);
+    }
+
+    mmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_modsecurity_module);
+    mmcf->rules_file += res;
+
+    return NGX_CONF_OK;
+}
+
+
+char *
+ngx_conf_set_rules_remote(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    int                                res;
+    ngx_str_t                         *value;
+    const char                        *error;
+    const char                        *rules_remote_key, *rules_remote_server;
+    ngx_pool_t                        *old_pool;
+    ngx_http_modsecurity_conf_t       *mcf = conf;
+    ngx_http_modsecurity_main_conf_t  *mmcf;
+
+    value = cf->args->elts;
+    rules_remote_key = ngx_str_to_char(value[1], cf->pool);
+    rules_remote_server = ngx_str_to_char(value[2], cf->pool);
+
+    if (rules_remote_server == (char *)-1) {
+        return NGX_CONF_ERROR;
+    }
+
+    if (rules_remote_key == (char *)-1) {
+        return NGX_CONF_ERROR;
+    }
+
+    old_pool = ngx_http_modsecurity_pcre_malloc_init(cf->pool);
+    res = msc_rules_add_remote(mcf->rules_set, rules_remote_key, rules_remote_server, &error);
+    ngx_http_modsecurity_pcre_malloc_done(old_pool);
+
+    if (res < 0) {
+        dd("Failed to load the rules from: '%s'  - reason: '%s'", rules_remote_server, error);
+        return strdup(error);
+    }
+
+    mmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_modsecurity_module);
+    mmcf->rules_remote += res;
+
+    return NGX_CONF_OK;
+}
+
+
+char *ngx_conf_set_transaction_id(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
+    ngx_str_t                         *value;
+    ngx_http_complex_value_t           cv;
+    ngx_http_compile_complex_value_t   ccv;
+    ngx_http_modsecurity_conf_t *mcf = conf;
+
+    value = cf->args->elts;
+
+    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+    ccv.cf = cf;
+    ccv.value = &value[1];
+    ccv.complex_value = &cv;
+    ccv.zero = 1;
+
+    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    mcf->transaction_id = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
+    if (mcf->transaction_id == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    *mcf->transaction_id = cv;
+
+    return NGX_CONF_OK;
+}
+
+
+static ngx_command_t ngx_http_modsecurity_commands[] =  {
+  {
+    ngx_string("modsecurity"),
+    NGX_HTTP_LOC_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_MAIN_CONF|NGX_CONF_FLAG,
+    ngx_conf_set_flag_slot,
+    NGX_HTTP_LOC_CONF_OFFSET,
+    offsetof(ngx_http_modsecurity_conf_t, enable),
+    NULL
+  },
+  {
+    ngx_string("modsecurity_rules"),
+    NGX_HTTP_LOC_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
+    ngx_conf_set_rules,
+    NGX_HTTP_LOC_CONF_OFFSET,
+    offsetof(ngx_http_modsecurity_conf_t, enable),
+    NULL
+  },
+  {
+    ngx_string("modsecurity_rules_file"),
+    NGX_HTTP_LOC_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
+    ngx_conf_set_rules_file,
+    NGX_HTTP_LOC_CONF_OFFSET,
+    offsetof(ngx_http_modsecurity_conf_t, enable),
+    NULL
+  },
+  {
+    ngx_string("modsecurity_rules_remote"),
+    NGX_HTTP_LOC_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE2,
+    ngx_conf_set_rules_remote,
+    NGX_HTTP_LOC_CONF_OFFSET,
+    offsetof(ngx_http_modsecurity_conf_t, enable),
+    NULL
+  },
+  {
+    ngx_string("modsecurity_transaction_id"),
+    NGX_HTTP_LOC_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_MAIN_CONF|NGX_CONF_1MORE,
+    ngx_conf_set_transaction_id,
+    NGX_HTTP_LOC_CONF_OFFSET,
+    0,
+    NULL
+  },
+  ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_modsecurity_ctx = {
+    NULL,                                  /* preconfiguration */
+    ngx_http_modsecurity_init,             /* postconfiguration */
+
+    ngx_http_modsecurity_create_main_conf, /* create main configuration */
+    ngx_http_modsecurity_init_main_conf,   /* init main configuration */
+
+    NULL,                                  /* create server configuration */
+    NULL,                                  /* merge server configuration */
+
+    ngx_http_modsecurity_create_conf,      /* create location configuration */
+    ngx_http_modsecurity_merge_conf        /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_modsecurity_module = {
+    NGX_MODULE_V1,
+    &ngx_http_modsecurity_ctx,             /* module context */
+    ngx_http_modsecurity_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_int_t
+ngx_http_modsecurity_init(ngx_conf_t *cf)
+{
+    ngx_http_handler_pt *h_rewrite;
+    ngx_http_handler_pt *h_preaccess;
+    ngx_http_handler_pt *h_log;
+    ngx_http_core_main_conf_t *cmcf;
+    int rc = 0;
+
+    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+    if (cmcf == NULL)
+    {
+        dd("We are not sure how this returns, NGINX doesn't seem to think it will ever be null");
+        return NGX_ERROR;
+    }
+    /**
+     *
+     * Seems like we cannot do this very same thing with
+     * NGX_HTTP_FIND_CONFIG_PHASE. it does not seems to
+     * be an array. Our next option is the REWRITE.
+     *
+     * TODO: check if we can hook prior to NGX_HTTP_REWRITE_PHASE phase.
+     *
+     */
+    h_rewrite = ngx_array_push(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers);
+    if (h_rewrite == NULL)
+    {
+        dd("Not able to create a new NGX_HTTP_REWRITE_PHASE handle");
+        return NGX_ERROR;
+    }
+    *h_rewrite = ngx_http_modsecurity_rewrite_handler;
+
+    /**
+     *
+     * Processing the request body on the preaccess phase.
+     *
+     * TODO: check if hook into separated phases is the best thing to do.
+     *
+     */
+    h_preaccess = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
+    if (h_preaccess == NULL)
+    {
+        dd("Not able to create a new NGX_HTTP_PREACCESS_PHASE handle");
+        return NGX_ERROR;
+    }
+    *h_preaccess = ngx_http_modsecurity_pre_access_handler;
+
+    /**
+     * Process the log phase.
+     *
+     * TODO: check if the log phase happens like it happens on Apache.
+     *       check if last phase will not hold the request.
+     *
+     */
+    h_log = ngx_array_push(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers);
+    if (h_log == NULL)
+    {
+        dd("Not able to create a new NGX_HTTP_LOG_PHASE handle");
+        return NGX_ERROR;
+    }
+    *h_log = ngx_http_modsecurity_log_handler;
+
+
+    rc = ngx_http_modsecurity_header_filter_init();
+    if (rc != NGX_OK) {
+        return rc;
+    }
+
+    rc = ngx_http_modsecurity_body_filter_init();
+    if (rc != NGX_OK) {
+        return rc;
+    }
+
+    return NGX_OK;
+}
+
+
+static void *
+ngx_http_modsecurity_create_main_conf(ngx_conf_t *cf)
+{
+    ngx_pool_cleanup_t                *cln;
+    ngx_http_modsecurity_main_conf_t  *conf;
+
+    conf = (ngx_http_modsecurity_main_conf_t *) ngx_pcalloc(cf->pool,
+                                    sizeof(ngx_http_modsecurity_main_conf_t));
+
+    if (conf == NULL)
+    {
+        return NGX_CONF_ERROR;
+    }
+
+    /*
+     * set by ngx_pcalloc():
+     *
+     *     conf->modsec = NULL;
+     *     conf->pool = NULL;
+     *     conf->rules_inline = 0;
+     *     conf->rules_file = 0;
+     *     conf->rules_remote = 0;
+     */
+
+    cln = ngx_pool_cleanup_add(cf->pool, 0);
+    if (cln == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    cln->handler = ngx_http_modsecurity_cleanup_instance;
+    cln->data = conf;
+
+    conf->pool = cf->pool;
+
+    /* Create our ModSecurity instance */
+    conf->modsec = msc_init();
+    if (conf->modsec == NULL)
+    {
+        dd("failed to create the ModSecurity instance");
+        return NGX_CONF_ERROR;
+    }
+
+    /* Provide our connector information to LibModSecurity */
+    msc_set_connector_info(conf->modsec, MODSECURITY_NGINX_WHOAMI);
+    msc_set_log_cb(conf->modsec, ngx_http_modsecurity_log);
+
+    dd ("main conf created at: '%p', instance is: '%p'", conf, conf->modsec);
+
+    return conf;
+}
+
+
+static char *
+ngx_http_modsecurity_init_main_conf(ngx_conf_t *cf, void *conf)
+{
+    ngx_http_modsecurity_main_conf_t  *mmcf;
+    mmcf = (ngx_http_modsecurity_main_conf_t *) conf;
+
+    ngx_log_error(NGX_LOG_NOTICE, cf->log, 0,
+                  "%s (rules loaded inline/local/remote: %ui/%ui/%ui)",
+                  MODSECURITY_NGINX_WHOAMI, mmcf->rules_inline,
+                  mmcf->rules_file, mmcf->rules_remote);
+
+    return NGX_CONF_OK;
+}
+
+
+static void *
+ngx_http_modsecurity_create_conf(ngx_conf_t *cf)
+{
+    ngx_pool_cleanup_t           *cln;
+    ngx_http_modsecurity_conf_t  *conf;
+
+    conf = (ngx_http_modsecurity_conf_t *) ngx_pcalloc(cf->pool,
+                                         sizeof(ngx_http_modsecurity_conf_t));
+
+    if (conf == NULL)
+    {
+        dd("Failed to allocate space for ModSecurity configuration");
+        return NGX_CONF_ERROR;
+    }
+
+    /*
+     * set by ngx_pcalloc():
+     *
+     *     conf->enable = 0;
+     *     conf->sanity_checks_enabled = 0;
+     *     conf->rules_set = NULL;
+     *     conf->pool = NULL;
+     *     conf->transaction_id = NULL;
+     */
+
+    conf->enable = NGX_CONF_UNSET;
+    conf->rules_set = msc_create_rules_set();
+    conf->pool = cf->pool;
+    conf->transaction_id = NGX_CONF_UNSET_PTR;
+#if defined(MODSECURITY_SANITY_CHECKS) && (MODSECURITY_SANITY_CHECKS)
+    conf->sanity_checks_enabled = NGX_CONF_UNSET;
+#endif
+
+    cln = ngx_pool_cleanup_add(cf->pool, 0);
+    if (cln == NULL) {
+        dd("failed to create the ModSecurity configuration cleanup");
+        return NGX_CONF_ERROR;
+    }
+
+    cln->handler = ngx_http_modsecurity_cleanup_rules;
+    cln->data = conf;
+
+    dd ("conf created at: '%p'", conf);
+
+    return conf;
+}
+
+
+static char *
+ngx_http_modsecurity_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+    ngx_http_modsecurity_conf_t *p = parent;
+    ngx_http_modsecurity_conf_t *c = child;
+#if defined(MODSECURITY_DDEBUG) && (MODSECURITY_DDEBUG)
+    ngx_http_core_loc_conf_t *clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
+#endif
+    int rules;
+    const char *error = NULL;
+
+    dd("merging loc config [%s] - parent: '%p' child: '%p'",
+        ngx_str_to_char(clcf->name, cf->pool), parent,
+        child);
+
+    dd("                  state - parent: '%d' child: '%d'",
+        (int) c->enable, (int) p->enable);
+
+    ngx_conf_merge_value(c->enable, p->enable, 0);
+    ngx_conf_merge_ptr_value(c->transaction_id, p->transaction_id, NULL);
+#if defined(MODSECURITY_SANITY_CHECKS) && (MODSECURITY_SANITY_CHECKS)
+    ngx_conf_merge_value(c->sanity_checks_enabled, p->sanity_checks_enabled, 0);
+#endif
+
+#if defined(MODSECURITY_DDEBUG) && (MODSECURITY_DDEBUG)
+    dd("PARENT RULES");
+    msc_rules_dump(p->rules_set);
+    dd("CHILD RULES");
+    msc_rules_dump(c->rules_set);
+#endif
+    rules = msc_rules_merge(c->rules_set, p->rules_set, &error);
+
+    if (rules < 0) {
+        return strdup(error);
+    }
+
+#if defined(MODSECURITY_DDEBUG) && (MODSECURITY_DDEBUG)
+    dd("NEW CHILD RULES");
+    msc_rules_dump(c->rules_set);
+#endif
+    return NGX_CONF_OK;
+}
+
+
+static void
+ngx_http_modsecurity_cleanup_instance(void *data)
+{
+    ngx_pool_t                        *old_pool;
+    ngx_http_modsecurity_main_conf_t  *mmcf;
+
+    mmcf = (ngx_http_modsecurity_main_conf_t *) data;
+
+    dd("deleting a main conf -- instance is: \"%p\"", mmcf->modsec);
+
+    old_pool = ngx_http_modsecurity_pcre_malloc_init(mmcf->pool);
+    msc_cleanup(mmcf->modsec);
+    ngx_http_modsecurity_pcre_malloc_done(old_pool);
+}
+
+
+static void
+ngx_http_modsecurity_cleanup_rules(void *data)
+{
+    ngx_pool_t                   *old_pool;
+    ngx_http_modsecurity_conf_t  *mcf;
+
+    mcf = (ngx_http_modsecurity_conf_t *) data;
+
+    dd("deleting a loc conf -- RuleSet is: \"%p\"", mcf->rules_set);
+
+    old_pool = ngx_http_modsecurity_pcre_malloc_init(mcf->pool);
+    msc_rules_cleanup(mcf->rules_set);
+    ngx_http_modsecurity_pcre_malloc_done(old_pool);
+}
+
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/modules/http-modsecurity/src/ngx_http_modsecurity_pre_access.c b/modules/http-modsecurity/src/ngx_http_modsecurity_pre_access.c
new file mode 100644
index 0000000..05d7140
--- /dev/null
+++ b/modules/http-modsecurity/src/ngx_http_modsecurity_pre_access.c
@@ -0,0 +1,215 @@
+/*
+ * ModSecurity connector for nginx, http://www.modsecurity.org/
+ * Copyright (c) 2015 Trustwave Holdings, Inc. (http://www.trustwave.com/)
+ *
+ * You may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * If any of the files related to licensing are missing or if you have any
+ * other questions related to licensing please contact Trustwave Holdings, Inc.
+ * directly using the email address security@modsecurity.org.
+ *
+ */
+
+#ifndef MODSECURITY_DDEBUG
+#define MODSECURITY_DDEBUG 0
+#endif
+#include "ddebug.h"
+
+#include "ngx_http_modsecurity_common.h"
+
+void
+ngx_http_modsecurity_request_read(ngx_http_request_t *r)
+{
+    ngx_http_modsecurity_ctx_t *ctx;
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+
+#if defined(nginx_version) && nginx_version >= 8011
+    r->main->count--;
+#endif
+
+    if (ctx->waiting_more_body)
+    {
+        ctx->waiting_more_body = 0;
+        r->write_event_handler = ngx_http_core_run_phases;
+        ngx_http_core_run_phases(r);
+    }
+}
+
+
+ngx_int_t
+ngx_http_modsecurity_pre_access_handler(ngx_http_request_t *r)
+{
+#if 1
+    ngx_pool_t                   *old_pool;
+    ngx_http_modsecurity_ctx_t   *ctx;
+    ngx_http_modsecurity_conf_t  *mcf;
+
+    dd("catching a new _preaccess_ phase handler");
+
+    mcf = ngx_http_get_module_loc_conf(r, ngx_http_modsecurity_module);
+    if (mcf == NULL || mcf->enable != 1)
+    {
+        dd("ModSecurity not enabled... returning");
+        return NGX_DECLINED;
+    }
+    /*
+     * FIXME:
+     * In order to perform some tests, let's accept everything.
+     *
+    if (r->method != NGX_HTTP_GET &&
+        r->method != NGX_HTTP_POST && r->method != NGX_HTTP_HEAD) {
+        dd("ModSecurity is not ready to deal with anything different from " \
+            "POST, GET or HEAD");
+        return NGX_DECLINED;
+    }
+    */
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+
+    dd("recovering ctx: %p", ctx);
+
+    if (ctx == NULL)
+    {
+        dd("ctx is null; Nothing we can do, returning an error.");
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    if (ctx->waiting_more_body == 1)
+    {
+        dd("waiting for more data before proceed. / count: %d",
+            r->main->count);
+
+        return NGX_DONE;
+    }
+
+    if (ctx->body_requested == 0)
+    {
+        ngx_int_t rc = NGX_OK;
+
+        ctx->body_requested = 1;
+
+        dd("asking for the request body, if any. Count: %d",
+            r->main->count);
+        /**
+         * TODO: Check if there is any benefit to use request_body_in_single_buf set to 1.
+         *
+         *       saw some module using this request_body_in_single_buf
+         *       but not sure what exactly it does, same for the others options below.
+         *
+         * r->request_body_in_single_buf = 1;
+         */
+        r->request_body_in_single_buf = 1;
+        r->request_body_in_persistent_file = 1;
+        r->request_body_in_clean_file = 1;
+
+        rc = ngx_http_read_client_request_body(r,
+            ngx_http_modsecurity_request_read);
+        if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
+#if (nginx_version < 1002006) ||                                             \
+    (nginx_version >= 1003000 && nginx_version < 1003009)
+            r->main->count--;
+#endif
+
+            return rc;
+        }
+        if (rc == NGX_AGAIN)
+        {
+            dd("nginx is asking us to wait for more data.");
+
+            ctx->waiting_more_body = 1;
+            return NGX_DONE;
+        }
+    }
+
+    if (ctx->waiting_more_body == 0)
+    {
+        int ret = 0;
+        int already_inspected = 0;
+
+        dd("request body is ready to be processed");
+
+        r->write_event_handler = ngx_http_core_run_phases;
+
+        ngx_chain_t *chain = r->request_body->bufs;
+
+        /**
+         * TODO: Speed up the analysis by sending chunk while they arrive.
+         *
+         * Notice that we are waiting for the full request body to
+         * start to process it, it may not be necessary. We may send
+         * the chunks to ModSecurity while nginx keep calling this
+         * function.
+         */
+
+        if (r->request_body->temp_file != NULL) {
+            ngx_str_t file_path = r->request_body->temp_file->file.name;
+            const char *file_name = ngx_str_to_char(file_path, r->pool);
+            if (file_name == (char*)-1) {
+                return NGX_HTTP_INTERNAL_SERVER_ERROR;
+            }
+            /*
+             * Request body was saved to a file, probably we don't have a
+             * copy of it in memory.
+             */
+            dd("request body inspection: file -- %s", file_name);
+
+            msc_request_body_from_file(ctx->modsec_transaction, file_name);
+
+            already_inspected = 1;
+        } else {
+            dd("inspection request body in memory.");
+        }
+
+        while (chain && !already_inspected)
+        {
+            u_char *data = chain->buf->pos;
+
+            msc_append_request_body(ctx->modsec_transaction, data,
+                chain->buf->last - data);
+
+            if (chain->buf->last_buf) {
+                break;
+            }
+            chain = chain->next;
+
+/* XXX: chains are processed one-by-one, maybe worth to pass all chains and then call intervention() ? */
+
+            /**
+             * ModSecurity may perform stream inspection on this buffer,
+             * it may ask for a intervention in consequence of that.
+             *
+             */
+            ret = ngx_http_modsecurity_process_intervention(ctx->modsec_transaction, r);
+            if (ret > 0) {
+                return ret;
+            }
+        }
+
+        /**
+         * At this point, all the request body was sent to ModSecurity
+         * and we want to make sure that all the request body inspection
+         * happened; consequently we have to check if ModSecurity have
+         * returned any kind of intervention.
+         */
+
+/* XXX: once more -- is body can be modified ?  content-length need to be adjusted ? */
+
+        old_pool = ngx_http_modsecurity_pcre_malloc_init(r->pool);
+        msc_process_request_body(ctx->modsec_transaction);
+        ngx_http_modsecurity_pcre_malloc_done(old_pool);
+
+        ret = ngx_http_modsecurity_process_intervention(ctx->modsec_transaction, r);
+        if (ret > 0) {
+            return ret;
+        }
+    }
+
+    dd("Nothing to add on the body inspection, reclaiming a NGX_DECLINED");
+#endif
+    return NGX_DECLINED;
+}
+
diff --git a/modules/http-modsecurity/src/ngx_http_modsecurity_rewrite.c b/modules/http-modsecurity/src/ngx_http_modsecurity_rewrite.c
new file mode 100644
index 0000000..cd02438
--- /dev/null
+++ b/modules/http-modsecurity/src/ngx_http_modsecurity_rewrite.c
@@ -0,0 +1,196 @@
+/*
+ * ModSecurity connector for nginx, http://www.modsecurity.org/
+ * Copyright (c) 2015 Trustwave Holdings, Inc. (http://www.trustwave.com/)
+ *
+ * You may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * If any of the files related to licensing are missing or if you have any
+ * other questions related to licensing please contact Trustwave Holdings, Inc.
+ * directly using the email address security@modsecurity.org.
+ *
+ */
+
+#ifndef MODSECURITY_DDEBUG
+#define MODSECURITY_DDEBUG 0
+#endif
+#include "ddebug.h"
+
+#include "ngx_http_modsecurity_common.h"
+
+ngx_int_t
+ngx_http_modsecurity_rewrite_handler(ngx_http_request_t *r)
+{
+    ngx_pool_t                   *old_pool;
+    ngx_http_modsecurity_ctx_t   *ctx;
+    ngx_http_modsecurity_conf_t  *mcf;
+
+    mcf = ngx_http_get_module_loc_conf(r, ngx_http_modsecurity_module);
+    if (mcf == NULL || mcf->enable != 1) {
+        dd("ModSecurity not enabled... returning");
+        return NGX_DECLINED;
+    }
+
+    /*
+    if (r->method != NGX_HTTP_GET &&
+        r->method != NGX_HTTP_POST && r->method != NGX_HTTP_HEAD) {
+        dd("ModSecurity is not ready to deal with anything different from " \
+            "POST, GET or HEAD");
+        return NGX_DECLINED;
+    }
+    */
+
+    dd("catching a new _rewrite_ phase handler");
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
+
+    dd("recovering ctx: %p", ctx);
+
+    if (ctx == NULL)
+    {
+        int ret = 0;
+
+        ngx_connection_t *connection = r->connection;
+        /**
+         * FIXME: We may want to use struct sockaddr instead of addr_text.
+         *
+         */
+        ngx_str_t addr_text = connection->addr_text;
+
+        ctx = ngx_http_modsecurity_create_ctx(r);
+
+        dd("ctx was NULL, creating new context: %p", ctx);
+
+        if (ctx == NULL) {
+            dd("ctx still null; Nothing we can do, returning an error.");
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        /**
+         * FIXME: Check if it is possible to hook on nginx on a earlier phase.
+         *
+         * At this point we are doing an late connection process. Maybe
+         * we have to hook into NGX_HTTP_FIND_CONFIG_PHASE, it seems to be the
+         * erliest phase that nginx allow us to attach those kind of hooks.
+         *
+         */
+        int client_port = htons(((struct sockaddr_in *) connection->sockaddr)->sin_port);
+        int server_port = htons(((struct sockaddr_in *) connection->listening->sockaddr)->sin_port);
+        const char *client_addr = ngx_str_to_char(addr_text, r->pool);
+        if (client_addr == (char*)-1) {
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
+        const char *server_addr = inet_ntoa(((struct sockaddr_in *) connection->sockaddr)->sin_addr);
+        old_pool = ngx_http_modsecurity_pcre_malloc_init(r->pool);
+        ret = msc_process_connection(ctx->modsec_transaction,
+            client_addr, client_port,
+            server_addr, server_port);
+        ngx_http_modsecurity_pcre_malloc_done(old_pool);
+        if (ret != 1){
+            dd("Was not able to extract connection information.");
+        }
+        /**
+         *
+         * FIXME: Check how we can finalize a request without crash nginx.
+         *
+         * I don't think nginx is expecting to finalize a request at that
+         * point as it seems that it clean the ngx_http_request_t information
+         * and try to use it later.
+         *
+         */
+        dd("Processing intervention with the connection information filled in");
+        ret = ngx_http_modsecurity_process_intervention(ctx->modsec_transaction, r);
+        if (ret > 0) {
+            return ret;
+        }
+
+        const char *http_version;
+        switch (r->http_version) {
+            case NGX_HTTP_VERSION_9 :
+                http_version = "0.9";
+                break;
+            case NGX_HTTP_VERSION_10 :
+                http_version = "1.0";
+                break;
+            case NGX_HTTP_VERSION_11 :
+                http_version = "1.1";
+                break;
+#if defined(nginx_version) && nginx_version >= 1009005
+            case NGX_HTTP_VERSION_20 :
+                http_version = "2.0";
+                break;
+#endif
+            default :
+                http_version = "1.0";
+                break;
+        }
+
+        const char *n_uri = ngx_str_to_char(r->unparsed_uri, r->pool);
+        const char *n_method = ngx_str_to_char(r->method_name, r->pool);
+        if (n_uri == (char*)-1 || n_method == (char*)-1) {
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
+        old_pool = ngx_http_modsecurity_pcre_malloc_init(r->pool);
+        msc_process_uri(ctx->modsec_transaction, n_uri, n_method, http_version);
+        ngx_http_modsecurity_pcre_malloc_done(old_pool);
+
+        dd("Processing intervention with the transaction information filled in (uri, method and version)");
+        ret = ngx_http_modsecurity_process_intervention(ctx->modsec_transaction, r);
+        if (ret > 0) {
+            return ret;
+        }
+
+        /**
+         * Since incoming request headers are already in place, lets send it to ModSecurity
+         *
+         */
+        ngx_list_part_t *part = &r->headers_in.headers.part;
+        ngx_table_elt_t *data = part->elts;
+        ngx_uint_t i = 0;
+        for (i = 0 ; /* void */ ; i++) {
+            if (i >= part->nelts) {
+                if (part->next == NULL) {
+                    break;
+                }
+
+                part = part->next;
+                data = part->elts;
+                i = 0;
+            }
+
+            /**
+             * By using u_char (utf8_t) I believe nginx is hoping to deal
+             * with utf8 strings.
+             * Casting those into to unsigned char * in order to pass
+             * it to ModSecurity, it will handle with those later.
+             *
+             */
+
+            dd("Adding request header: %.*s with value %.*s", (int)data[i].key.len, data[i].key.data, (int) data[i].value.len, data[i].value.data);
+            msc_add_n_request_header(ctx->modsec_transaction,
+                (const unsigned char *) data[i].key.data,
+                data[i].key.len,
+                (const unsigned char *) data[i].value.data,
+                data[i].value.len);
+        }
+
+        /**
+         * Since ModSecurity already knew about all headers, i guess it is safe
+         * to process this information.
+         */
+
+        old_pool = ngx_http_modsecurity_pcre_malloc_init(r->pool);
+        msc_process_request_headers(ctx->modsec_transaction);
+        ngx_http_modsecurity_pcre_malloc_done(old_pool);
+        dd("Processing intervention with the request headers information filled in");
+        ret = ngx_http_modsecurity_process_intervention(ctx->modsec_transaction, r);
+        if (ret > 0) {
+            return ret;
+        }
+    }
+
+
+    return NGX_DECLINED;
+}
diff --git a/modules/http-modsecurity/tests/README.md b/modules/http-modsecurity/tests/README.md
new file mode 100644
index 0000000..3c9bdb1
--- /dev/null
+++ b/modules/http-modsecurity/tests/README.md
@@ -0,0 +1,10 @@
+# Tests
+
+Those are nginx test files. You can copy those files into yours nginx test
+tree, to perform the tests using the "prove" utility.
+
+For more information about those tests, read the subsection "Testing your
+patch" on the project's README file.
+
+For more about nginx tests, check their repository:
+http://hg.nginx.org/nginx-tests/
\ No newline at end of file
diff --git a/modules/http-modsecurity/tests/modsecurity-config-auditlog.t b/modules/http-modsecurity/tests/modsecurity-config-auditlog.t
new file mode 100644
index 0000000..92a8291
--- /dev/null
+++ b/modules/http-modsecurity/tests/modsecurity-config-auditlog.t
@@ -0,0 +1,231 @@
+#!/usr/bin/perl
+
+#
+# ModSecurity, http://www.modsecurity.org/
+# Copyright (c) 2015 Trustwave Holdings, Inc. (http://www.trustwave.com/)
+#
+# You may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# If any of the files related to licensing are missing or if you have any
+# other questions related to licensing please contact Trustwave Holdings, Inc.
+# directly using the email address security@modsecurity.org.
+#
+
+
+# Tests for ModSecurity module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http/);
+
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+daemon off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        modsecurity on;
+        modsecurity_rules '
+            SecRuleEngine On
+            SecRule ARGS "@streq whee" "id:10,phase:2"
+            SecRule ARGS "@streq whee" "id:11,phase:2"
+        ';
+
+        location / {
+            modsecurity_rules '
+                SecRule ARGS "@streq root" "id:21,phase:1,auditlog,status:302,redirect:http://www.modsecurity.org"
+                SecDebugLog %%TESTDIR%%/auditlog-debug-root.txt
+                SecDebugLogLevel 9
+                SecAuditEngine On
+                SecAuditLogParts AB
+                SecAuditLog %%TESTDIR%%/auditlog-root.txt
+                SecAuditLogType Serial
+                SecAuditLogStorageDir %%TESTDIR%%/
+            ';
+        }
+
+        location /subfolder1/subfolder2 {
+            modsecurity_rules '
+                SecRule ARGS "@streq subfolder2" "id:41,phase:1,status:302,auditlog,redirect:http://www.modsecurity.org"
+                SecDebugLog %%TESTDIR%%/auditlog-debug-subfolder2.txt
+                SecDebugLogLevel 9
+                SecAuditEngine On
+                SecAuditLogParts AB
+                SecResponseBodyAccess On
+                SecAuditLog %%TESTDIR%%/auditlog-subfolder2.txt
+                SecAuditLogType Serial
+                SecAuditLogStorageDir %%TESTDIR%%/
+            ';
+        }
+
+        location /subfolder1 {
+            modsecurity_rules '
+                SecRule ARGS "@streq subfolder1" "id:31,phase:1,status:302,auditlog,redirect:http://www.modsecurity.org"
+                SecDebugLog %%TESTDIR%%/auditlog-debug-subfolder1.txt
+                SecDebugLogLevel 9
+                SecAuditLogParts AB
+                SecAuditEngine On
+                SecAuditLog %%TESTDIR%%/auditlog-subfolder1.txt
+                SecAuditLogType Serial
+                SecAuditLogStorageDir %%TESTDIR%%/
+            ';
+        }
+
+        location /subfolder3/subfolder4 {
+            modsecurity_rules '
+                SecResponseBodyAccess On
+                SecRule ARGS "@streq subfolder4" "id:61,phase:1,status:302,auditlog,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq subfolder4withE" "id:2,phase:1,ctl:auditLogParts=+E,auditlog"
+                SecDebugLog %%TESTDIR%%/auditlog-debug-subfolder4.txt
+                SecDebugLogLevel 9
+                SecAuditEngine On
+                SecAuditLogParts AB
+                SecAuditLog %%TESTDIR%%/auditlog-subfolder4.txt
+                SecAuditLogType Serial
+                SecAuditLogStorageDir %%TESTDIR%%/
+            ';
+        }
+
+        location /subfolder3 {
+            modsecurity_rules '
+                SecRule ARGS "@streq subfolder3" "id:51,phase:1,status:302,auditlog,redirect:http://www.modsecurity.org"
+                SecDebugLog %%TESTDIR%%/auditlog-debug-subfolder3.txt
+                SecDebugLogLevel 9
+                SecAuditLogParts AB
+                SecAuditEngine On
+                SecAuditLog %%TESTDIR%%/auditlog-subfolder3.txt
+                SecAuditLogType Serial
+                SecAuditLogStorageDir %%TESTDIR%%/
+            ';
+        }
+
+    }
+}
+EOF
+
+$t->write_file("/index.html", "should be moved/blocked before this.");
+mkdir($t->testdir() . '/subfolder1');
+$t->write_file("/subfolder1/index.html", "should be moved/blocked before this.");
+mkdir($t->testdir() . '/subfolder1/subfolder2');
+$t->write_file("/subfolder1/subfolder2/index.html", "should be moved/blocked before this.");
+mkdir($t->testdir() . '/subfolder3');
+$t->write_file("/subfolder3/index.html", "should be moved/blocked before this.");
+mkdir($t->testdir() . '/subfolder3/subfolder4');
+$t->write_file("/subfolder3/subfolder4/index.html", "should be moved/blocked before this.");
+
+$t->run();
+$t->plan(9);
+
+###############################################################################
+
+my $d = $t->testdir();
+
+my $r;
+# Performing requests at root
+$r = http_get('/index.html?what=root');
+$r = http_get('/index.html?what=subfolder1');
+$r = http_get('/index.html?what=subfolder2');
+$r = http_get('/index.html?what=subfolder3');
+$r = http_get('/index.html?what=subfolder4');
+
+# Performing requests at subfolder1
+$r = http_get('/subfolder1/index.html?what=root');
+$r = http_get('/subfolder1/index.html?what=subfolder1');
+$r = http_get('/subfolder1/index.html?what=subfolder2');
+$r = http_get('/subfolder1/index.html?what=subfolder3');
+$r = http_get('/subfolder1/index.html?what=subfolder4');
+
+# Performing requests at subfolder2
+$r = http_get('/subfolder1/subfolder2/index.html?what=root');
+$r = http_get('/subfolder1/subfolder2/index.html?what=subfolder1');
+$r = http_get('/subfolder1/subfolder2/index.html?what=subfolder2');
+$r = http_get('/subfolder1/subfolder2/index.html?what=subfolder3');
+$r = http_get('/subfolder1/subfolder2/index.html?what=subfolder4');
+
+# Performing requests at subfolder3
+$r = http_get('/subfolder3/index.html?what=root');
+$r = http_get('/subfolder3/index.html?what=subfolder1');
+$r = http_get('/subfolder3/index.html?what=subfolder2');
+$r = http_get('/subfolder3/index.html?what=subfolder3');
+$r = http_get('/subfolder3/index.html?what=subfolder4');
+
+# Performing requests at subfolder4
+$r = http_get('/subfolder3/subfolder4/index.html?what=root');
+$r = http_get('/subfolder3/subfolder4/index.html?what=subfolder1');
+$r = http_get('/subfolder3/subfolder4/index.html?what=subfolder2');
+$r = http_get('/subfolder3/subfolder4/index.html?what=subfolder3');
+$r = http_get('/subfolder3/subfolder4/index.html?what=subfolder4');
+$r = http_get('/subfolder3/subfolder4/index.html?what=subfolder4withE');
+
+my $root = do {
+    local $/ = undef;
+    open my $fh, "<", "$d/auditlog-root.txt"
+        or die "could not open: $!";
+    <$fh>;
+};
+my $subfolder1 = do {
+    local $/ = undef;
+    open my $fh, "<", "$d/auditlog-subfolder1.txt"
+        or die "could not open: $!";
+    <$fh>;
+};
+my $subfolder2 = do {
+    local $/ = undef;
+    open my $fh, "<", "$d/auditlog-subfolder2.txt"
+        or die "could not open: $!";
+    <$fh>;
+};
+my $subfolder3 = do {
+    local $/ = undef;
+    open my $fh, "<", "$d/auditlog-subfolder3.txt"
+        or die "could not open: $!";
+    <$fh>;
+};
+my $subfolder4 = do {
+    local $/ = undef;
+    open my $fh, "<", "$d/auditlog-subfolder4.txt"
+        or die "could not open: $!";
+    <$fh>;
+};
+
+
+like($root, qr/what=root/, 'root');
+like($subfolder1, qr/what=subfolder1/, 'subfolder1');
+like($subfolder2, qr/what=subfolder2/, 'subfolder2');
+like($subfolder2, qr/what=subfolder1/, 'subfolder2 / subfolder1');
+
+like($subfolder3, qr/what=subfolder3/, 'subfolder3');
+like($subfolder4, qr/what=subfolder4/, 'subfolder4');
+like($subfolder4, qr/what=subfolder3/, 'subfolder4 / subfolder3');
+
+like($subfolder4, qr/what=subfolder4withE/, 'subfolder4');
+like($subfolder4, qr/---E--/, 'subfolder4');
diff --git a/modules/http-modsecurity/tests/modsecurity-config-debuglog.t b/modules/http-modsecurity/tests/modsecurity-config-debuglog.t
new file mode 100644
index 0000000..5c65aca
--- /dev/null
+++ b/modules/http-modsecurity/tests/modsecurity-config-debuglog.t
@@ -0,0 +1,142 @@
+#!/usr/bin/perl
+
+#
+# ModSecurity, http://www.modsecurity.org/
+# Copyright (c) 2015 Trustwave Holdings, Inc. (http://www.trustwave.com/)
+#
+# You may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# If any of the files related to licensing are missing or if you have any
+# other questions related to licensing please contact Trustwave Holdings, Inc.
+# directly using the email address security@modsecurity.org.
+#
+
+
+# Tests for ModSecurity module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http/);
+
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+daemon off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        modsecurity on;
+        modsecurity_rules '
+            SecRuleEngine On
+            SecRule ARGS "@streq whee" "id:10,phase:2"
+            SecRule ARGS "@streq whee" "id:11,phase:2"
+        ';
+
+        location / {
+            modsecurity_rules '
+                SecRule ARGS "@streq root" "id:21,phase:1,status:302,redirect:http://www.modsecurity.org"
+                SecDebugLog %%TESTDIR%%/debuglog-root.txt
+                SecDebugLogLevel 9
+            ';
+        }
+
+        location /subfolder1 {
+            modsecurity_rules '
+                SecRule ARGS "@streq subfolder1" "id:31,phase:1,status:302,redirect:http://www.modsecurity.org"
+                SecDebugLog %%TESTDIR%%/debuglog-subfolder1.txt
+                SecDebugLogLevel 9
+            ';
+            location /subfolder1/subfolder2 {
+                modsecurity_rules '
+                    SecRule ARGS "@streq subfolder2" "id:41,phase:1,status:302,redirect:http://www.modsecurity.org"
+                    SecDebugLog %%TESTDIR%%/debuglog-subfolder2.txt
+                    SecDebugLogLevel 9
+                ';
+            }
+        }
+    }
+}
+EOF
+
+$t->write_file("/index.html", "should be moved/blocked before this.");
+mkdir($t->testdir() . '/subfolder1');
+$t->write_file("/subfolder1/index.html", "should be moved/blocked before this.");
+mkdir($t->testdir() . '/subfolder1/subfolder2');
+$t->write_file("/subfolder1/subfolder2/index.html", "should be moved/blocked before this.");
+
+$t->run();
+$t->plan(3);
+
+###############################################################################
+
+my $d = $t->testdir();
+
+my $r;
+# Performing requests at root
+$r = http_get('/index.html?what=root');
+$r = http_get('/index.html?what=subfolder1');
+$r = http_get('/index.html?what=subfolder2');
+
+# Performing requests at subfolder1
+$r = http_get('/subfolder1/index.html?what=root');
+$r = http_get('/subfolder1/index.html?what=subfolder1');
+$r = http_get('/subfolder1/index.html?what=subfolder2');
+
+# Performing requests at subfolder2
+$r = http_get('/subfolder1/subfolder2/index.html?what=root');
+$r = http_get('/subfolder1/subfolder2/index.html?what=subfolder1');
+$r = http_get('/subfolder1/subfolder2/index.html?what=subfolder2');
+
+my $root = do {
+    local $/ = undef;
+    open my $fh, "<", "$d/debuglog-root.txt"
+        or die "could not open: $!";
+    <$fh>;
+};
+my $subfolder1 = do {
+    local $/ = undef;
+    open my $fh, "<", "$d/debuglog-subfolder1.txt"
+        or die "could not open: $!";
+    <$fh>;
+};
+my $subfolder2 = do {
+    local $/ = undef;
+    open my $fh, "<", "$d/debuglog-subfolder2.txt"
+        or die "could not open: $!";
+    <$fh>;
+};
+
+like($root, qr/"what", value "root"/, 'root');
+like($subfolder1, qr/"what", value "subfolder1"/, 'subfolder1');
+like($subfolder2, qr/"what", value "subfolder2"/, 'subfolder2');
+
+
+
diff --git a/modules/http-modsecurity/tests/modsecurity-config-merge.t b/modules/http-modsecurity/tests/modsecurity-config-merge.t
new file mode 100644
index 0000000..5f22aa6
--- /dev/null
+++ b/modules/http-modsecurity/tests/modsecurity-config-merge.t
@@ -0,0 +1,231 @@
+#!/usr/bin/perl
+
+# (C) Andrei Belov
+
+# Tests for ModSecurity-nginx connector (configuration merge).
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+use Socket qw/ CRLF /;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http proxy/);
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+daemon off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    modsecurity on;
+    modsecurity_rules '
+        SecRuleEngine On
+        SecRequestBodyAccess On
+        SecRequestBodyLimit 128
+        SecRequestBodyLimitAction Reject
+        SecRule REQUEST_BODY "@rx BAD BODY" "id:11,phase:request,deny,log,status:403"
+    ';
+
+    server {
+        listen       127.0.0.1:%%PORT_8080%%;
+        server_name  localhost;
+
+        location / {
+            proxy_pass http://127.0.0.1:%%PORT_8081%%;
+        }
+
+        location /modsec-disabled {
+            modsecurity_rules '
+                SecRuleEngine Off
+            ';
+            proxy_pass http://127.0.0.1:%%PORT_8081%%;
+        }
+
+        location /nobodyaccess {
+            modsecurity_rules '
+                SecRequestBodyAccess Off
+            ';
+            proxy_pass http://127.0.0.1:%%PORT_8081%%;
+        }
+
+        location /bodylimitprocesspartial {
+            modsecurity_rules '
+                SecRequestBodyLimitAction ProcessPartial
+            ';
+            proxy_pass http://127.0.0.1:%%PORT_8081%%;
+        }
+
+        location /bodylimitincreased {
+            modsecurity_rules '
+                SecRequestBodyLimit 512
+            ';
+            proxy_pass http://127.0.0.1:%%PORT_8081%%;
+        }
+
+        location /server {
+            modsecurity off;
+
+            location /server/modsec-disabled {
+                proxy_pass http://127.0.0.1:%%PORT_8082%%;
+            }
+
+            location /server/nobodyaccess {
+                proxy_pass http://127.0.0.1:%%PORT_8083%%;
+            }
+
+            location /server/bodylimitprocesspartial {
+                proxy_pass http://127.0.0.1:%%PORT_8084%%;
+            }
+
+            location /server/bodylimitincreased {
+                proxy_pass http://127.0.0.1:%%PORT_8085%%;
+            }
+        }
+    }
+
+    server {
+        listen 127.0.0.1:%%PORT_8082%%;
+
+        modsecurity_rules '
+                SecRuleEngine Off
+        ';
+
+        location / {
+            proxy_pass http://127.0.0.1:%%PORT_8081%%;
+        }
+    }
+
+    server {
+        listen 127.0.0.1:%%PORT_8083%%;
+
+        modsecurity_rules '
+                SecRequestBodyAccess Off
+        ';
+
+        location / {
+            proxy_pass http://127.0.0.1:%%PORT_8081%%;
+        }
+    }
+
+    server {
+        listen 127.0.0.1:%%PORT_8084%%;
+
+        modsecurity_rules '
+                SecRequestBodyLimitAction ProcessPartial
+        ';
+
+        location / {
+            proxy_pass http://127.0.0.1:%%PORT_8081%%;
+        }
+    }
+
+    server {
+        listen 127.0.0.1:%%PORT_8085%%;
+
+        modsecurity_rules '
+                SecRequestBodyLimit 512
+        ';
+
+        location / {
+            proxy_pass http://127.0.0.1:%%PORT_8081%%;
+        }
+    }
+}
+EOF
+
+$t->run_daemon(\&http_daemon);
+$t->run()->waitforsocket('127.0.0.1:' . port(8081));
+
+$t->plan(10);
+
+###############################################################################
+
+like(http_get_body('/', 'GOOD BODY'), qr/TEST-OK-IF-YOU-SEE-THIS/, "http level defaults, pass");
+like(http_get_body('/', 'VERY BAD BODY'), qr/403 Forbidden/, "http level defaults, block");
+
+like(http_get_body('/modsec-disabled', 'VERY BAD BODY'), qr/TEST-OK-IF-YOU-SEE-THIS/, "location override for SecRuleEngine, pass");
+like(http_get_body('/nobodyaccess', 'VERY BAD BODY'), qr/TEST-OK-IF-YOU-SEE-THIS/, "location override for SecRequestBodyAccess, pass");
+like(http_get_body('/bodylimitprocesspartial', 'BODY' x 33), qr/TEST-OK-IF-YOU-SEE-THIS/, "location override for SecRequestBodyLimitAction, pass");
+like(http_get_body('/bodylimitincreased', 'BODY' x 64), qr/TEST-OK-IF-YOU-SEE-THIS/, "location override for SecRequestBodyLimit, pass");
+
+like(http_get_body('/server/modsec-disabled', 'VERY BAD BODY'), qr/TEST-OK-IF-YOU-SEE-THIS/, "server override for SecRuleEngine, pass");
+like(http_get_body('/server/nobodyaccess', 'VERY BAD BODY'), qr/TEST-OK-IF-YOU-SEE-THIS/, "server override for SecRequestBodyAccess, pass");
+like(http_get_body('/server/bodylimitprocesspartial', 'BODY' x 33), qr/TEST-OK-IF-YOU-SEE-THIS/, "server override for SecRequestBodyLimitAction, pass");
+like(http_get_body('/server/bodylimitincreased', 'BODY' x 64), qr/TEST-OK-IF-YOU-SEE-THIS/, "server override for SecRequestBodyLimit, pass");
+
+###############################################################################
+
+sub http_daemon {
+	my $server = IO::Socket::INET->new(
+		Proto => 'tcp',
+		LocalHost => '127.0.0.1:' . port(8081),
+		Listen => 5,
+		Reuse => 1
+	)
+		or die "Can't create listening socket: $!\n";
+
+	local $SIG{PIPE} = 'IGNORE';
+
+	while (my $client = $server->accept()) {
+		$client->autoflush(1);
+
+		my $headers = '';
+		my $uri = '';
+
+		while (<$client>) {
+			$headers .= $_;
+			last if (/^\x0d?\x0a?$/);
+		}
+
+		$uri = $1 if $headers =~ /^\S+\s+([^ ]+)\s+HTTP/i;
+
+		print $client <<'EOF';
+HTTP/1.1 200 OK
+Connection: close
+
+EOF
+		print $client "TEST-OK-IF-YOU-SEE-THIS"
+			unless $headers =~ /^HEAD/i;
+
+		close $client;
+	}
+}
+
+sub http_get_body {
+	my $uri = shift;
+	my $last = pop;
+	return http( join '', (map {
+		my $body = $_;
+		"GET $uri HTTP/1.1" . CRLF
+		. "Host: localhost" . CRLF
+		. "Content-Length: " . (length $body) . CRLF . CRLF
+		. $body
+	} @_),
+		"GET $uri HTTP/1.1" . CRLF
+		. "Host: localhost" . CRLF
+		. "Connection: close" . CRLF
+		. "Content-Length: " . (length $last) . CRLF . CRLF
+		. $last
+	);
+}
+
+###############################################################################
diff --git a/modules/http-modsecurity/tests/modsecurity-config.t b/modules/http-modsecurity/tests/modsecurity-config.t
new file mode 100644
index 0000000..26258dd
--- /dev/null
+++ b/modules/http-modsecurity/tests/modsecurity-config.t
@@ -0,0 +1,112 @@
+#!/usr/bin/perl
+
+#
+# ModSecurity, http://www.modsecurity.org/
+# Copyright (c) 2015 Trustwave Holdings, Inc. (http://www.trustwave.com/)
+#
+# You may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# If any of the files related to licensing are missing or if you have any
+# other questions related to licensing please contact Trustwave Holdings, Inc.
+# directly using the email address security@modsecurity.org.
+#
+
+
+# Tests for ModSecurity module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http/);
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+daemon off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        modsecurity on;
+        modsecurity_rules '
+            SecRuleEngine On
+            SecRule ARGS "@streq whee" "id:10,phase:2"
+            SecRule ARGS "@streq whee" "id:11,phase:2"
+        ';
+
+        location / {
+            modsecurity_rules '
+                SecRuleEngine On
+                SecRule ARGS "@streq root" "id:21,phase:1,status:302,redirect:http://www.modsecurity.org"
+            ';
+        }
+
+        location /subfolder1 {
+            modsecurity_rules '
+                SecRuleEngine On
+                SecRule ARGS "@streq subfolder1" "id:31,phase:1,status:302,redirect:http://www.modsecurity.org"
+            ';
+            location /subfolder1/subfolder2 {
+                modsecurity_rules '
+                    SecRuleEngine On
+                    SecRule ARGS "@streq subfolder2" "id:41,phase:1,status:302,redirect:http://www.modsecurity.org"
+                ';
+            }
+        }
+    }
+}
+EOF
+
+$t->write_file("/index.html", "should be moved/blocked before this.");
+mkdir($t->testdir() . '/subfolder1');
+$t->write_file("/subfolder1/index.html", "should be moved/blocked before this.");
+mkdir($t->testdir() . '/subfolder1/subfolder2');
+$t->write_file("/subfolder1/subfolder2/index.html", "should be moved/blocked before this.");
+
+$t->run();
+$t->plan(9);
+
+###############################################################################
+
+
+# Performing requests at root
+like(http_get('/index.html?what=root'), qr/302 Moved Temporarily/, 'redirect 302 - root');
+like(http_get('/index.html?what=subfolder1'), qr/should be moved\/blocked before this./, 'nothing - requested subfolder1 at root');
+like(http_get('/index.html?what=subfolder2'), qr/should be moved\/blocked before this./, 'nothing - requested subfolder2 at root');
+
+# Performing requests at subfolder1
+like(http_get('/subfolder1/index.html?what=root'), qr/should be moved\/blocked before this./, 'nothing - requested root at subfolder 1');
+like(http_get('/subfolder1/index.html?what=subfolder1'), qr/302 Moved Temporarily/, 'redirect 302 - subfolder 1');
+like(http_get('/subfolder1/index.html?what=subfolder2'), qr/should be moved\/blocked before this./, 'nothing - requested subfolder2 at subfolder1');
+
+# Performing requests at subfolder2
+like(http_get('/subfolder1/subfolder2/index.html?what=root'), qr/should be moved\/blocked before this./, 'nothing - requested root at subfolder 2');
+like(http_get('/subfolder1/subfolder2/index.html?what=subfolder1'), qr/302 Moved Temporarily/, 'redirect 302 - subfolder 2');
+like(http_get('/subfolder1/subfolder2/index.html?what=subfolder2'), qr/302 Moved Temporarily/, 'redirect 302 - subfolder 2');
+
+
diff --git a/modules/http-modsecurity/tests/modsecurity-h2.t b/modules/http-modsecurity/tests/modsecurity-h2.t
new file mode 100644
index 0000000..fcab5f2
--- /dev/null
+++ b/modules/http-modsecurity/tests/modsecurity-h2.t
@@ -0,0 +1,206 @@
+#!/usr/bin/perl
+
+# (C) Andrei Belov
+
+# Tests for ModSecurity module (HTTP/2).
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+use Test::Nginx::HTTP2;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http http_v2/);
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+daemon off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080 http2;
+        server_name  localhost;
+
+        location / {
+            modsecurity on;
+            modsecurity_rules '
+                SecRuleEngine On
+                SecRule ARGS "@streq whee" "id:10,phase:2"
+                SecRule ARGS "@streq whee" "id:11,phase:2"
+            ';
+        }
+        location /phase1 {
+            modsecurity on;
+            modsecurity_rules '
+                SecRuleEngine On
+                SecDefaultAction "phase:1,log,deny,status:403"
+                SecRule ARGS "@streq redirect301" "id:1,phase:1,status:301,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq redirect302" "id:2,phase:1,status:302,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq block401" "id:3,phase:1,status:401,block"
+                SecRule ARGS "@streq block403" "id:4,phase:1,status:403,block"
+            ';
+        }
+        location /phase2 {
+            modsecurity on;
+            modsecurity_rules '
+                SecRuleEngine On
+                SecDefaultAction "phase:2,log,deny,status:403"
+                SecRule ARGS "@streq redirect301" "id:1,phase:2,status:301,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq redirect302" "id:2,phase:2,status:302,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq block401" "id:3,phase:2,status:401,block"
+                SecRule ARGS "@streq block403" "id:4,phase:2,status:403,block"
+            ';
+        }
+        location /phase3 {
+            modsecurity on;
+            modsecurity_rules '
+                SecRuleEngine On
+                SecDefaultAction "phase:3,log,deny,status:403"
+                SecRule ARGS "@streq redirect301" "id:1,phase:3,status:301,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq redirect302" "id:2,phase:3,status:302,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq block401" "id:3,phase:3,status:401,block"
+                SecRule ARGS "@streq block403" "id:4,phase:3,status:403,block"
+            ';
+        }
+        location /phase4 {
+            modsecurity on;
+            modsecurity_rules '
+                SecRuleEngine On
+                SecResponseBodyAccess On
+                SecDefaultAction "phase:4,log,deny,status:403"
+                SecRule ARGS "@streq redirect301" "id:1,phase:4,status:301,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq redirect302" "id:2,phase:4,status:302,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq block401" "id:3,phase:4,status:401,block"
+                SecRule ARGS "@streq block403" "id:4,phase:4,status:403,block"
+            ';
+        }
+    }
+}
+EOF
+
+$t->write_file("/phase1", "should be moved/blocked before this.");
+$t->write_file("/phase2", "should be moved/blocked before this.");
+$t->write_file("/phase3", "should be moved/blocked before this.");
+$t->write_file("/phase4", "should not be moved/blocked, headers delivered before phase 4.");
+$t->run();
+$t->todo_alerts();
+$t->plan(20);
+
+###############################################################################
+
+my ($phase, $s, $sid, $frames, $frame);
+
+# Redirect (302)
+
+for $phase (1 .. 3) {
+	$s = Test::Nginx::HTTP2->new();
+	$sid = $s->new_stream({ path => "/phase${phase}?what=redirect302" });
+	$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+	($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+	is($frame->{headers}->{':status'}, 302, "redirect 302 - phase ${phase}");
+}
+
+SKIP: {
+skip 'long test', 1 unless $ENV{TEST_NGINX_UNSAFE};
+
+$s = Test::Nginx::HTTP2->new();
+$sid = $s->new_stream({ path => '/phase4?what=redirect302' });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "DATA" } @$frames;
+is($frame, undef, 'redirect 302 - phase 4');
+}
+
+# Redirect (301)
+
+for $phase (1 .. 3) {
+	$s = Test::Nginx::HTTP2->new();
+	$sid = $s->new_stream({ path => "/phase${phase}?what=redirect301" });
+	$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+	($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+	is($frame->{headers}->{':status'}, 301, "redirect 301 - phase ${phase}");
+}
+
+SKIP: {
+skip 'long test', 1 unless $ENV{TEST_NGINX_UNSAFE};
+
+$s = Test::Nginx::HTTP2->new();
+$sid = $s->new_stream({ path => '/phase4?what=redirect301' });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "DATA" } @$frames;
+is($frame, undef, 'redirect 301 - phase 4');
+}
+
+# Block (401)
+
+for $phase (1 .. 3) {
+	$s = Test::Nginx::HTTP2->new();
+	$sid = $s->new_stream({ path => "/phase${phase}?what=block401" });
+	$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+	($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+	is($frame->{headers}->{':status'}, 401, "block 401 - phase ${phase}");
+}
+
+SKIP: {
+skip 'long test', 1 unless $ENV{TEST_NGINX_UNSAFE};
+
+$s = Test::Nginx::HTTP2->new();
+$sid = $s->new_stream({ path => '/phase4?what=block401' });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "DATA" } @$frames;
+is($frame, undef, 'block 401 - phase 4');
+}
+
+# Block (403)
+
+for $phase (1 .. 3) {
+	$s = Test::Nginx::HTTP2->new();
+	$sid = $s->new_stream({ path => "/phase${phase}?what=block403" });
+	$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+	($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+	is($frame->{headers}->{':status'}, 403, "block 403 - phase ${phase}");
+}
+
+SKIP: {
+skip 'long test', 1 unless $ENV{TEST_NGINX_UNSAFE};
+
+$s = Test::Nginx::HTTP2->new();
+$sid = $s->new_stream({ path => '/phase4?what=block403' });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "DATA" } @$frames;
+is($frame, undef, 'block 403 - phase 4');
+}
+
+# Nothing to detect
+
+for $phase (1 .. 3) {
+	$s = Test::Nginx::HTTP2->new();
+	$sid = $s->new_stream({ path => "/phase${phase}?what=nothing" });
+	$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+	($frame) = grep { $_->{type} eq "DATA" } @$frames;
+	is($frame->{data}, "should be moved\/blocked before this.", "nothing phase ${phase}");
+}
+
+$s = Test::Nginx::HTTP2->new();
+$sid = $s->new_stream({ path => "/phase4?what=nothing" });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "DATA" } @$frames;
+is($frame->{data}, "should not be moved\/blocked, headers delivered before phase 4.", 'nothing phase 4');
diff --git a/modules/http-modsecurity/tests/modsecurity-proxy-h2.t b/modules/http-modsecurity/tests/modsecurity-proxy-h2.t
new file mode 100644
index 0000000..5d0f326
--- /dev/null
+++ b/modules/http-modsecurity/tests/modsecurity-proxy-h2.t
@@ -0,0 +1,288 @@
+#!/usr/bin/perl
+
+# (C) Andrei Belov
+
+# Tests for ModSecurity over the http proxy module (HTTP/2).
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+use Test::Nginx::HTTP2;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http http_v2 proxy/)->plan(23);
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+daemon off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080 http2;
+        server_name  localhost;
+
+        location / {
+            proxy_pass http://127.0.0.1:8081;
+            proxy_read_timeout 1s;
+        }
+
+        location /phase1 {
+            modsecurity on;
+            modsecurity_rules '
+                SecRuleEngine On
+                SecDefaultAction "phase:1,log,deny,status:403"
+                SecRule ARGS "@streq redirect301" "id:1,phase:1,status:301,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq redirect302" "id:2,phase:1,status:302,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq block401" "id:3,phase:1,status:401,block"
+                SecRule ARGS "@streq block403" "id:4,phase:1,status:403,block"
+            ';
+            proxy_pass http://127.0.0.1:8081;
+            proxy_read_timeout 1s;
+        }
+        location /phase2 {
+            modsecurity on;
+            modsecurity_rules '
+                SecRuleEngine On
+                SecDefaultAction "phase:2,log,deny,status:403"
+                SecRule ARGS "@streq redirect301" "id:1,phase:2,status:301,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq redirect302" "id:2,phase:2,status:302,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq block401" "id:3,phase:2,status:401,block"
+                SecRule ARGS "@streq block403" "id:4,phase:2,status:403,block"
+            ';
+            proxy_pass http://127.0.0.1:8081;
+            proxy_read_timeout 1s;
+        }
+        location /phase3 {
+            modsecurity on;
+            modsecurity_rules '
+                SecRuleEngine On
+                SecDefaultAction "phase:3,log,deny,status:403"
+                SecRule ARGS "@streq redirect301" "id:1,phase:3,status:301,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq redirect302" "id:2,phase:3,status:302,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq block401" "id:3,phase:3,status:401,block"
+                SecRule ARGS "@streq block403" "id:4,phase:3,status:403,block"
+            ';
+            proxy_pass http://127.0.0.1:8081;
+            proxy_read_timeout 1s;
+        }
+        location /phase4 {
+            modsecurity on;
+            modsecurity_rules '
+                SecRuleEngine On
+                SecResponseBodyAccess On
+                SecDefaultAction "phase:4,log,deny,status:403"
+                SecRule ARGS "@streq redirect301" "id:1,phase:4,status:301,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq redirect302" "id:2,phase:4,status:302,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq block401" "id:3,phase:4,status:401,block"
+                SecRule ARGS "@streq block403" "id:4,phase:4,status:403,block"
+            ';
+            proxy_pass http://127.0.0.1:8081;
+            proxy_read_timeout 1s;
+        }
+    }
+}
+
+EOF
+
+$t->todo_alerts();
+$t->run_daemon(\&http_daemon);
+$t->run()->waitforsocket('127.0.0.1:' . port(8081));
+
+###############################################################################
+
+my ($phase, $s, $sid, $frames, $frame);
+
+$s = Test::Nginx::HTTP2->new();
+$sid = $s->new_stream({ path => '/' });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "DATA" } @$frames;
+like($frame->{data}, qr/SEE-THIS/, "proxy request");
+
+$s = Test::Nginx::HTTP2->new();
+$sid = $s->new_stream({ path => '/multi' });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "DATA" } @$frames;
+like($frame->{data}, qr/AND-THIS/, "proxy request with multiple packets");
+
+$s = Test::Nginx::HTTP2->new();
+$sid = $s->new_stream({ path => '/', method => 'HEAD' });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "DATA" } @$frames;
+unlike($frame->{data}, qr/SEE-THIS/, "proxy head request");
+
+# Redirect (302)
+
+for $phase (1 .. 3) {
+	$s = Test::Nginx::HTTP2->new();
+	$sid = $s->new_stream({ path => "/phase${phase}?what=redirect302" });
+	$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+	($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+	is($frame->{headers}->{':status'}, 302, "redirect 302 - phase ${phase}");
+}
+
+SKIP: {
+skip 'long test', 1 unless $ENV{TEST_NGINX_UNSAFE};
+
+$s = Test::Nginx::HTTP2->new();
+$sid = $s->new_stream({ path => '/phase4?what=redirect302' });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "DATA" } @$frames;
+is($frame, undef, 'redirect 302 - phase 4');
+}
+
+# Redirect (301)
+
+for $phase (1 .. 3) {
+	$s = Test::Nginx::HTTP2->new();
+	$sid = $s->new_stream({ path => "/phase${phase}?what=redirect301" });
+	$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+	($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+	is($frame->{headers}->{':status'}, 301, "redirect 301 - phase ${phase}");
+}
+
+SKIP: {
+skip 'long test', 1 unless $ENV{TEST_NGINX_UNSAFE};
+
+$s = Test::Nginx::HTTP2->new();
+$sid = $s->new_stream({ path => '/phase4?what=redirect301' });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "DATA" } @$frames;
+is($frame, undef, 'redirect 301 - phase 4');
+}
+
+# Block (401)
+
+for $phase (1 .. 3) {
+	$s = Test::Nginx::HTTP2->new();
+	$sid = $s->new_stream({ path => "/phase${phase}?what=block401" });
+	$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+	($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+	is($frame->{headers}->{':status'}, 401, "block 401 - phase ${phase}");
+}
+
+SKIP: {
+skip 'long test', 1 unless $ENV{TEST_NGINX_UNSAFE};
+
+$s = Test::Nginx::HTTP2->new();
+$sid = $s->new_stream({ path => '/phase4?what=block401' });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "DATA" } @$frames;
+is($frame, undef, 'block 401 - phase 4');
+}
+
+
+# Block (403)
+
+for $phase (1 .. 3) {
+	$s = Test::Nginx::HTTP2->new();
+	$sid = $s->new_stream({ path => "/phase${phase}?what=block403" });
+	$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+	($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+	is($frame->{headers}->{':status'}, 403, "block 403 - phase ${phase}");
+}
+
+SKIP: {
+skip 'long test', 1 unless $ENV{TEST_NGINX_UNSAFE};
+
+$s = Test::Nginx::HTTP2->new();
+$sid = $s->new_stream({ path => '/phase4?what=block403' });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "DATA" } @$frames;
+is($frame, undef, 'block 403 - phase 4');
+}
+
+# Nothing to detect
+#like(http_get('/phase1?what=nothing'), qr/phase1\?what=nothing\' not found/, 'nothing phase 1');
+#like(http_get('/phase2?what=nothing'), qr/phase2\?what=nothing\' not found/, 'nothing phase 2');
+#like(http_get('/phase3?what=nothing'), qr/phase3\?what=nothing\' not found/, 'nothing phase 3');
+#like(http_get('/phase4?what=nothing'), qr/phase4\?what=nothing\' not found/, 'nothing phase 4');
+
+for $phase (1 .. 4) {
+	$s = Test::Nginx::HTTP2->new();
+	$sid = $s->new_stream({ path => "/phase${phase}?what=nothing" });
+	$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+	($frame) = grep { $_->{type} eq "DATA" } @$frames;
+	like($frame->{data}, qr/phase${phase}\?what=nothing\' not found/, "nothing phase ${phase}");
+}
+
+###############################################################################
+
+sub http_daemon {
+	my $server = IO::Socket::INET->new(
+		Proto => 'tcp',
+		LocalHost => '127.0.0.1:' . port(8081),
+		Listen => 5,
+		Reuse => 1
+	)
+		or die "Can't create listening socket: $!\n";
+
+	local $SIG{PIPE} = 'IGNORE';
+
+	while (my $client = $server->accept()) {
+		$client->autoflush(1);
+
+		my $headers = '';
+		my $uri = '';
+
+		while (<$client>) {
+			$headers .= $_;
+			last if (/^\x0d?\x0a?$/);
+		}
+
+		$uri = $1 if $headers =~ /^\S+\s+([^ ]+)\s+HTTP/i;
+
+		if ($uri eq '/') {
+			print $client <<'EOF';
+HTTP/1.1 200 OK
+Connection: close
+
+EOF
+			print $client "TEST-OK-IF-YOU-SEE-THIS"
+				unless $headers =~ /^HEAD/i;
+
+		} elsif ($uri eq '/multi') {
+
+			print $client <<"EOF";
+HTTP/1.1 200 OK
+Connection: close
+
+TEST-OK-IF-YOU-SEE-THIS
+EOF
+
+			select undef, undef, undef, 0.1;
+			print $client 'AND-THIS';
+
+		} else {
+
+			print $client <<"EOF";
+HTTP/1.1 404 Not Found
+Connection: close
+
+Oops, '$uri' not found
+EOF
+		}
+
+		close $client;
+	}
+}
+
+###############################################################################
diff --git a/modules/http-modsecurity/tests/modsecurity-proxy.t b/modules/http-modsecurity/tests/modsecurity-proxy.t
new file mode 100644
index 0000000..675499c
--- /dev/null
+++ b/modules/http-modsecurity/tests/modsecurity-proxy.t
@@ -0,0 +1,208 @@
+#!/usr/bin/perl
+
+
+# Tests for ModSecurity over the http proxy module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http proxy/)->plan(23);
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+daemon off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location / {
+            proxy_pass http://127.0.0.1:8081;
+            proxy_read_timeout 1s;
+        }
+
+        location /phase1 {
+            modsecurity on;
+            modsecurity_rules '
+                SecRuleEngine On
+                SecDefaultAction "phase:1,log,deny,status:403"
+                SecRule ARGS "@streq redirect301" "id:1,phase:1,status:301,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq redirect302" "id:2,phase:1,status:302,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq block401" "id:3,phase:1,status:401,block"
+                SecRule ARGS "@streq block403" "id:4,phase:1,status:403,block"
+            ';
+            proxy_pass http://127.0.0.1:8081;
+            proxy_read_timeout 1s;
+        }
+        location /phase2 {
+            modsecurity on;
+            modsecurity_rules '
+                SecRuleEngine On
+                SecDefaultAction "phase:2,log,deny,status:403"
+                SecRule ARGS "@streq redirect301" "id:1,phase:2,status:301,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq redirect302" "id:2,phase:2,status:302,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq block401" "id:3,phase:2,status:401,block"
+                SecRule ARGS "@streq block403" "id:4,phase:2,status:403,block"
+            ';
+            proxy_pass http://127.0.0.1:8081;
+            proxy_read_timeout 1s;
+        }
+        location /phase3 {
+            modsecurity on;
+            modsecurity_rules '
+                SecRuleEngine On
+                SecDefaultAction "phase:3,log,deny,status:403"
+                SecRule ARGS "@streq redirect301" "id:1,phase:3,status:301,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq redirect302" "id:2,phase:3,status:302,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq block401" "id:3,phase:3,status:401,block"
+                SecRule ARGS "@streq block403" "id:4,phase:3,status:403,block"
+            ';
+            proxy_pass http://127.0.0.1:8081;
+            proxy_read_timeout 1s;
+        }
+        location /phase4 {
+            modsecurity on;
+            modsecurity_rules '
+                SecRuleEngine On
+                SecResponseBodyAccess On
+                SecDefaultAction "phase:4,log,deny,status:403"
+                SecRule ARGS "@streq redirect301" "id:1,phase:4,status:301,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq redirect302" "id:2,phase:4,status:302,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq block401" "id:3,phase:4,status:401,block"
+                SecRule ARGS "@streq block403" "id:4,phase:4,status:403,block"
+            ';
+            proxy_pass http://127.0.0.1:8081;
+            proxy_read_timeout 1s;
+        }
+    }
+}
+
+EOF
+
+$t->todo_alerts();
+$t->run_daemon(\&http_daemon);
+$t->run()->waitforsocket('127.0.0.1:' . port(8081));
+
+###############################################################################
+
+like(http_get('/'), qr/SEE-THIS/, 'proxy request');
+like(http_get('/multi'), qr/AND-THIS/, 'proxy request with multiple packets');
+
+unlike(http_head('/'), qr/SEE-THIS/, 'proxy head request');
+
+
+# Redirect (302)
+like(http_get('/phase1?what=redirect302'), qr/302 Moved Temporarily/, 'redirect 302 - phase 1');
+like(http_get('/phase2?what=redirect302'), qr/302 Moved Temporarily/, 'redirect 302 - phase 2');
+like(http_get('/phase3?what=redirect302'), qr/302 Moved Temporarily/, 'redirect 302 - phase 3');
+is(http_get('/phase4?what=redirect302'), '', 'redirect 302 - phase 4');
+
+# Redirect (301)
+like(http_get('/phase1?what=redirect301'), qr/301 Moved Permanently/, 'redirect 301 - phase 1');
+like(http_get('/phase2?what=redirect301'), qr/301 Moved Permanently/, 'redirect 301 - phase 2');
+like(http_get('/phase3?what=redirect301'), qr/301 Moved Permanently/, 'redirect 301 - phase 3');
+is(http_get('/phase4?what=redirect301'), '', 'redirect 301 - phase 4');
+
+# Block (401)
+like(http_get('/phase1?what=block401'), qr/401 Unauthorized/, 'block 401 - phase 1');
+like(http_get('/phase2?what=block401'), qr/401 Unauthorized/, 'block 401 - phase 2');
+like(http_get('/phase3?what=block401'), qr/401 Unauthorized/, 'block 401 - phase 3');
+is(http_get('/phase4?what=block401'), '', 'block 401 - phase 4');
+
+# Block (403)
+like(http_get('/phase1?what=block403'), qr/403 Forbidden/, 'block 403 - phase 1');
+like(http_get('/phase2?what=block403'), qr/403 Forbidden/, 'block 403 - phase 2');
+like(http_get('/phase3?what=block403'), qr/403 Forbidden/, 'block 403 - phase 3');
+is(http_get('/phase4?what=block403'), '', 'block 403 - phase 4');
+
+# Nothing to detect
+like(http_get('/phase1?what=nothing'), qr/phase1\?what=nothing\' not found/, 'nothing phase 1');
+like(http_get('/phase2?what=nothing'), qr/phase2\?what=nothing\' not found/, 'nothing phase 2');
+like(http_get('/phase3?what=nothing'), qr/phase3\?what=nothing\' not found/, 'nothing phase 3');
+like(http_get('/phase4?what=nothing'), qr/phase4\?what=nothing\' not found/, 'nothing phase 4');
+
+
+###############################################################################
+
+sub http_daemon {
+	my $server = IO::Socket::INET->new(
+		Proto => 'tcp',
+		LocalHost => '127.0.0.1:' . port(8081),
+		Listen => 5,
+		Reuse => 1
+	)
+		or die "Can't create listening socket: $!\n";
+
+	local $SIG{PIPE} = 'IGNORE';
+
+	while (my $client = $server->accept()) {
+		$client->autoflush(1);
+
+		my $headers = '';
+		my $uri = '';
+
+		while (<$client>) {
+			$headers .= $_;
+			last if (/^\x0d?\x0a?$/);
+		}
+
+		$uri = $1 if $headers =~ /^\S+\s+([^ ]+)\s+HTTP/i;
+
+		if ($uri eq '/') {
+			print $client <<'EOF';
+HTTP/1.1 200 OK
+Connection: close
+
+EOF
+			print $client "TEST-OK-IF-YOU-SEE-THIS"
+				unless $headers =~ /^HEAD/i;
+
+		} elsif ($uri eq '/multi') {
+
+			print $client <<"EOF";
+HTTP/1.1 200 OK
+Connection: close
+
+TEST-OK-IF-YOU-SEE-THIS
+EOF
+
+			select undef, undef, undef, 0.1;
+			print $client 'AND-THIS';
+
+		} else {
+
+			print $client <<"EOF";
+HTTP/1.1 404 Not Found
+Connection: close
+
+Oops, '$uri' not found
+EOF
+		}
+
+		close $client;
+	}
+}
+
+###############################################################################
diff --git a/modules/http-modsecurity/tests/modsecurity-request-body-h2.t b/modules/http-modsecurity/tests/modsecurity-request-body-h2.t
new file mode 100644
index 0000000..7c947c5
--- /dev/null
+++ b/modules/http-modsecurity/tests/modsecurity-request-body-h2.t
@@ -0,0 +1,222 @@
+#!/usr/bin/perl
+
+# (C) Andrei Belov
+
+# Tests for ModSecurity-nginx connector (request body operations, HTTP/2).
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+use Socket qw/ CRLF /;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+use Test::Nginx::HTTP2;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http http_v2 proxy auth_request/);
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+daemon off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen 127.0.0.1:8081;
+        location / {
+            return 200 "TEST-OK-IF-YOU-SEE-THIS";
+        }
+    }
+
+    server {
+        listen       127.0.0.1:8080 http2;
+        server_name  localhost;
+
+        modsecurity on;
+        client_header_buffer_size 1024;
+
+        location /bodyaccess {
+            modsecurity_rules '
+                SecRuleEngine On
+                SecRequestBodyAccess On
+                SecRule REQUEST_BODY "@rx BAD BODY" "id:11,phase:request,deny,log,status:403"
+            ';
+            proxy_pass http://127.0.0.1:8081;
+        }
+
+        location /nobodyaccess {
+            modsecurity_rules '
+                SecRuleEngine On
+                SecRequestBodyAccess Off
+                SecRule REQUEST_BODY "@rx BAD BODY" "id:21,phase:request,deny,log,status:403"
+                SecRule ARGS_POST|ARGS_POST_NAMES "@rx BAD ARG" "id:22,phase:request,deny,log,status:403"
+            ';
+            proxy_pass http://127.0.0.1:8081;
+        }
+
+        location /bodylimitreject {
+            modsecurity_rules '
+                SecRuleEngine On
+                SecRequestBodyAccess On
+                SecRequestBodyLimit 128
+                SecRequestBodyLimitAction Reject
+                SecRule REQUEST_BODY "@rx BAD BODY" "id:31,phase:request,deny,log,status:403"
+            ';
+            proxy_pass http://127.0.0.1:8081;
+        }
+
+        location /bodylimitprocesspartial {
+            modsecurity_rules '
+                SecRuleEngine On
+                SecRequestBodyAccess On
+                SecRequestBodyLimit 128
+                SecRequestBodyLimitAction ProcessPartial
+                SecRule REQUEST_BODY "@rx BAD BODY" "id:41,phase:request,deny,log,status:403"
+            ';
+            proxy_pass http://127.0.0.1:8081;
+        }
+
+        location = /auth {
+            return 200;
+        }
+
+        location = /useauth {
+            modsecurity on;
+            modsecurity_rules '
+                SecRuleEngine On
+                SecRequestBodyAccess On
+            ';
+            auth_request /auth;
+            proxy_pass http://127.0.0.1:8081;
+        }
+    }
+}
+EOF
+
+$t->run();
+
+$t->plan(36);
+
+###############################################################################
+
+my ($s, $sid, $frames, $frame);
+
+foreach my $method (('GET', 'POST', 'PUT', 'DELETE')) {
+
+$s = Test::Nginx::HTTP2->new();
+$sid = $s->new_stream({ method => $method, path => '/bodyaccess', 'body_more' => 1 });
+$s->h2_body('GOOD BODY');
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "DATA" } @$frames;
+like($frame->{data}, qr/TEST-OK-IF-YOU-SEE-THIS/, "${method} request body access on, pass");
+
+$s = Test::Nginx::HTTP2->new();
+$sid = $s->new_stream({ method => $method, path => '/bodyaccess', 'body_more' => 1 });
+$s->h2_body('VERY BAD BODY');
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{':status'}, 403, "${method} request body access on, block");
+
+$s = Test::Nginx::HTTP2->new();
+$sid = $s->new_stream({ method => $method, path => '/nobodyaccess', 'body_more' => 1 });
+$s->h2_body('VERY BAD BODY');
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "DATA" } @$frames;
+like($frame->{data}, qr/TEST-OK-IF-YOU-SEE-THIS/, "${method} request body access off, pass");
+
+$s = Test::Nginx::HTTP2->new();
+$sid = $s->new_stream({ 'body_more' => 1,
+	headers => [
+		{name => ':method', value => "${method}" },
+		{name => ':scheme', value => 'http' },
+		{name => ':path', value => '/nobodyaccess' },
+		{name => 'host', value => 'localhost' },
+		{name => 'content-type', value => 'application/x-www-form-urlencoded' }
+	] });
+$s->h2_body('test=VERY BAD BODY');
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "DATA" } @$frames;
+like($frame->{data}, qr/TEST-OK-IF-YOU-SEE-THIS/, "${method} request body access off (ARGS_POST), pass");
+
+$s = Test::Nginx::HTTP2->new();
+$sid = $s->new_stream({ method => $method, path => '/bodylimitreject', 'body_more' => 1 });
+$s->h2_body('BODY' x 32);
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "DATA" } @$frames;
+like($frame->{data}, qr/TEST-OK-IF-YOU-SEE-THIS/, "${method} request body limit reject, pass");
+
+$s = Test::Nginx::HTTP2->new();
+$sid = $s->new_stream({ method => $method, path => '/bodylimitreject', 'body_more' => 1 });
+$s->h2_body('BODY' x 33);
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{':status'}, 403, "${method} request body limit reject, block");
+
+$s = Test::Nginx::HTTP2->new();
+$sid = $s->new_stream({ method => $method, path => '/bodylimitprocesspartial', 'body_more' => 1 });
+$s->h2_body('BODY' x 32 . 'BAD BODY');
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "DATA" } @$frames;
+like($frame->{data}, qr/TEST-OK-IF-YOU-SEE-THIS/, "${method} request body limit process partial, pass");
+
+$s = Test::Nginx::HTTP2->new();
+$sid = $s->new_stream({ method => $method, path => '/bodylimitprocesspartial', 'body_more' => 1 });
+$s->h2_body('BODY' x 30 . 'BAD BODY' x 32);
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
+is($frame->{headers}->{':status'}, 403, "${method} request body limit process partial, block");
+
+}
+
+TODO: {
+local $TODO = 'not yet';
+
+$s = Test::Nginx::HTTP2->new();
+$sid = $s->new_stream({ method => 'POST', path => '/useauth', 'body' => 'BODY' x 16 });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "DATA" } @$frames;
+like($frame->{data}, qr/TEST-OK-IF-YOU-SEE-THIS/, "POST with auth_request (request size < client_header_buffer_size)");
+
+$s = Test::Nginx::HTTP2->new();
+$sid = $s->new_stream({ method => 'POST', path => '/useauth', 'body' => 'BODY' x 257 });
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "DATA" } @$frames;
+like($frame->{data}, qr/TEST-OK-IF-YOU-SEE-THIS/, "POST with auth_request (request size > client_header_buffer_size)");
+
+$s = Test::Nginx::HTTP2->new();
+$sid = $s->new_stream({ method => 'POST', path => '/useauth', 'body_more' => 1 });
+$s->h2_body('BODY' x 15, { 'body_more' => 1 });
+select undef, undef, undef, 0.1;
+$s->h2_body('BODY');
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "DATA" } @$frames;
+like($frame->{data}, qr/TEST-OK-IF-YOU-SEE-THIS/, "POST with auth_request (request size < client_header_buffer_size), no preread");
+
+$s = Test::Nginx::HTTP2->new();
+$sid = $s->new_stream({ method => 'POST', path => '/useauth', 'body_more' => 1 });
+$s->h2_body('BODY' x 256, { 'body_more' => 1 });
+select undef, undef, undef, 0.1;
+$s->h2_body('BODY');
+$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
+($frame) = grep { $_->{type} eq "DATA" } @$frames;
+like($frame->{data}, qr/TEST-OK-IF-YOU-SEE-THIS/, "POST with auth_request (request size > client_header_buffer_size), no preread");
+
+}
+
+###############################################################################
diff --git a/modules/http-modsecurity/tests/modsecurity-request-body.t b/modules/http-modsecurity/tests/modsecurity-request-body.t
new file mode 100644
index 0000000..2d71c81
--- /dev/null
+++ b/modules/http-modsecurity/tests/modsecurity-request-body.t
@@ -0,0 +1,251 @@
+#!/usr/bin/perl
+
+# (C) Andrei Belov
+
+# Tests for ModSecurity-nginx connector (request body operations).
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+use Socket qw/ CRLF /;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http proxy auth_request/);
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+daemon off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        modsecurity on;
+        client_header_buffer_size 1024;
+
+        location /bodyaccess {
+            modsecurity_rules '
+                SecRuleEngine On
+                SecRequestBodyAccess On
+                SecRule REQUEST_BODY "@rx BAD BODY" "id:11,phase:request,deny,log,status:403"
+            ';
+            proxy_pass http://127.0.0.1:%%PORT_8081%%;
+        }
+
+        location /nobodyaccess {
+            modsecurity_rules '
+                SecRuleEngine On
+                SecRequestBodyAccess Off
+                SecRule REQUEST_BODY "@rx BAD BODY" "id:21,phase:request,deny,log,status:403"
+                SecRule ARGS_POST|ARGS_POST_NAMES "@rx BAD ARG" "id:22,phase:request,deny,log,status:403"
+            ';
+            proxy_pass http://127.0.0.1:%%PORT_8081%%;
+        }
+
+        location /bodylimitreject {
+            modsecurity_rules '
+                SecRuleEngine On
+                SecRequestBodyAccess On
+                SecRequestBodyLimit 128
+                SecRequestBodyLimitAction Reject
+                SecRule REQUEST_BODY "@rx BAD BODY" "id:31,phase:request,deny,log,status:403"
+            ';
+            proxy_pass http://127.0.0.1:%%PORT_8081%%;
+        }
+
+        location /bodylimitrejectserver {
+            modsecurity off;
+            proxy_pass http://127.0.0.1:%%PORT_8082%%;
+        }
+
+        location /bodylimitprocesspartial {
+            modsecurity_rules '
+                SecRuleEngine On
+                SecRequestBodyAccess On
+                SecRequestBodyLimit 128
+                SecRequestBodyLimitAction ProcessPartial
+                SecRule REQUEST_BODY "@rx BAD BODY" "id:41,phase:request,deny,log,status:403"
+            ';
+            proxy_pass http://127.0.0.1:%%PORT_8081%%;
+        }
+
+        location = /auth {
+            return 200;
+        }
+
+        location = /useauth {
+            modsecurity on;
+            modsecurity_rules '
+                SecRuleEngine On
+                SecRequestBodyAccess On
+            ';
+            auth_request /auth;
+            proxy_pass http://127.0.0.1:%%PORT_8081%%;
+        }
+    }
+
+    server {
+        listen 127.0.0.1:%%PORT_8082%%;
+        modsecurity on;
+        modsecurity_rules '
+            SecRuleEngine On
+            SecRequestBodyAccess On
+            SecRequestBodyLimit 128
+            SecRequestBodyLimitAction Reject
+            SecRule REQUEST_BODY "@rx BAD BODY" "id:31,phase:request,deny,log,status:403"
+        ';
+        location / {
+            proxy_pass http://127.0.0.1:%%PORT_8081%%;
+        }
+    }
+}
+EOF
+
+$t->run_daemon(\&http_daemon);
+$t->run()->waitforsocket('127.0.0.1:' . port(8081));
+
+$t->plan(40);
+
+###############################################################################
+
+foreach my $method (('GET', 'POST', 'PUT', 'DELETE')) {
+like(http_req_body($method, '/bodyaccess', 'GOOD BODY'), qr/TEST-OK-IF-YOU-SEE-THIS/, "$method request body access on, pass");
+like(http_req_body($method, '/bodyaccess', 'VERY BAD BODY'), qr/403 Forbidden/, "$method request body access on, block");
+like(http_req_body($method, '/nobodyaccess', 'VERY BAD BODY'), qr/TEST-OK-IF-YOU-SEE-THIS/, "$method request body access off, pass");
+like(http_req_body_postargs($method, '/nobodyaccess', 'BAD ARG'), qr/TEST-OK-IF-YOU-SEE-THIS/, "$method request body access off (ARGS_POST), pass");
+like(http_req_body($method, '/bodylimitreject', 'BODY' x 32), qr/TEST-OK-IF-YOU-SEE-THIS/, "$method request body limit reject, pass");
+like(http_req_body($method, '/bodylimitreject', 'BODY' x 33), qr/403 Forbidden/, "$method request body limit reject, block");
+like(http_req_body($method, '/bodylimitprocesspartial', 'BODY' x 32 . 'BAD BODY'), qr/TEST-OK-IF-YOU-SEE-THIS/, "$method request body limit process partial, pass");
+like(http_req_body($method, '/bodylimitprocesspartial', 'BODY' x 30 . 'BAD BODY' x 32), qr/403 Forbidden/, "$method request body limit process partial, block");
+}
+
+like(http_req_body('POST', '/useauth', 'BODY' x 16), qr/TEST-OK-IF-YOU-SEE-THIS/, "POST with auth_request (request size < client_header_buffer_size)");
+like(http_req_body('POST', '/useauth', 'BODY' x 257), qr/TEST-OK-IF-YOU-SEE-THIS/, "POST with auth_request (request size > client_header_buffer_size)");
+
+like(
+        http(
+                'POST /useauth HTTP/1.0' . CRLF
+                . 'Content-Length: 1028' . CRLF . CRLF
+                . 'BODY' x 256,
+                sleep => 0.1,
+                body => 'BODY'
+        ),
+        qr/TEST-OK-IF-YOU-SEE-THIS/,
+        'POST with auth_request (request size > client_header_buffer_size), no preread'
+);
+
+like(
+        http(
+                'POST /useauth HTTP/1.0' . CRLF
+                . 'Content-Length: 64' . CRLF . CRLF
+                . 'BODY' x 15,
+                sleep => 0.1,
+                body => 'BODY'
+        ),
+        qr/TEST-OK-IF-YOU-SEE-THIS/,
+        'POST with auth_request (request size < client_header_buffer_size), no preread'
+);
+
+foreach my $method (('GET', 'POST', 'PUT', 'DELETE')) {
+like(http_req_body($method, '/bodylimitrejectserver', 'BODY' x 33), qr/403 Forbidden/, "$method request body limit reject, block (inherited SecRequestBodyLimit)");
+}
+
+###############################################################################
+
+sub http_daemon {
+	my $server = IO::Socket::INET->new(
+		Proto => 'tcp',
+		LocalHost => '127.0.0.1:' . port(8081),
+		Listen => 5,
+		Reuse => 1
+	)
+		or die "Can't create listening socket: $!\n";
+
+	local $SIG{PIPE} = 'IGNORE';
+
+	while (my $client = $server->accept()) {
+		$client->autoflush(1);
+
+		my $headers = '';
+		my $uri = '';
+
+		while (<$client>) {
+			$headers .= $_;
+			last if (/^\x0d?\x0a?$/);
+		}
+
+		$uri = $1 if $headers =~ /^\S+\s+([^ ]+)\s+HTTP/i;
+
+		print $client <<'EOF';
+HTTP/1.1 200 OK
+Connection: close
+
+EOF
+		print $client "TEST-OK-IF-YOU-SEE-THIS"
+			unless $headers =~ /^HEAD/i;
+
+		close $client;
+	}
+}
+
+sub http_req_body {
+	my $method = shift;
+	my $uri = shift;
+	my $last = pop;
+	return http( join '', (map {
+		my $body = $_;
+		"$method $uri HTTP/1.1" . CRLF
+		. "Host: localhost" . CRLF
+		. "Content-Length: " . (length $body) . CRLF . CRLF
+		. $body
+	} @_),
+		"$method $uri HTTP/1.1" . CRLF
+		. "Host: localhost" . CRLF
+		. "Connection: close" . CRLF
+		. "Content-Length: " . (length $last) . CRLF . CRLF
+		. $last
+	);
+}
+
+sub http_req_body_postargs {
+	my $method = shift;
+	my $uri = shift;
+	my $last = pop;
+	return http( join '', (map {
+		my $body = $_;
+		"$method $uri HTTP/1.1" . CRLF
+		. "Host: localhost" . CRLF
+		. "Content-Type: application/x-www-form-urlencoded" . CRLF
+		. "Content-Length: " . (length "test=" . $body) . CRLF . CRLF
+		. "test=" . $body
+	} @_),
+		"$method $uri HTTP/1.1" . CRLF
+		. "Host: localhost" . CRLF
+		. "Connection: close" . CRLF
+		. "Content-Type: application/x-www-form-urlencoded" . CRLF
+		. "Content-Length: " . (length "test=" . $last) . CRLF . CRLF
+		. "test=" . $last
+	);
+}
+
+###############################################################################
diff --git a/modules/http-modsecurity/tests/modsecurity-response-body.t b/modules/http-modsecurity/tests/modsecurity-response-body.t
new file mode 100644
index 0000000..0c518d2
--- /dev/null
+++ b/modules/http-modsecurity/tests/modsecurity-response-body.t
@@ -0,0 +1,69 @@
+#!/usr/bin/perl
+
+# (C) Andrei Belov
+
+# Tests for ModSecurity-nginx connector (response body operations).
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http/);
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+daemon off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        modsecurity on;
+
+        location /body1 {
+            default_type text/plain;
+            modsecurity_rules '
+                SecRuleEngine On
+                SecResponseBodyAccess On
+                SecResponseBodyLimit 128
+                SecRule RESPONSE_BODY "@rx BAD BODY" "id:11,phase:response,deny,log,status:403"
+            ';
+        }
+    }
+}
+EOF
+
+$t->write_file("/body1", "BAD BODY");
+$t->run();
+$t->todo_alerts();
+$t->plan(1);
+
+###############################################################################
+
+TODO: {
+local $TODO = 'not yet';
+
+like(http_get('/body1'), qr/403 Forbidden/, 'response body (block)');
+}
+
diff --git a/modules/http-modsecurity/tests/modsecurity-scoring.t b/modules/http-modsecurity/tests/modsecurity-scoring.t
new file mode 100644
index 0000000..ddd3b61
--- /dev/null
+++ b/modules/http-modsecurity/tests/modsecurity-scoring.t
@@ -0,0 +1,79 @@
+#!/usr/bin/perl
+
+# (C) Andrei Belov
+
+# Tests for ModSecurity-nginx connector (scoring).
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http/);
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+daemon off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        modsecurity on;
+
+        location /absolute {
+            modsecurity_rules '
+                SecRuleEngine On
+                SecRule ARGS "@streq badarg1" "id:11,phase:2,setvar:tx.score=1"
+                SecRule ARGS "@streq badarg2" "id:12,phase:2,setvar:tx.score=2"
+                SecRule TX:SCORE "@ge 2" "id:199,phase:request,deny,log,status:403"
+            ';
+        }
+
+        location /iterative {
+            modsecurity_rules '
+                SecRuleEngine On
+                SecRule ARGS "@streq badarg1" "id:21,phase:2,setvar:tx.score=+1"
+                SecRule ARGS "@streq badarg2" "id:22,phase:2,setvar:tx.score=+1"
+                SecRule ARGS "@streq badarg3" "id:23,phase:2,setvar:tx.score=+1"
+                SecRule TX:SCORE "@ge 3" "id:299,phase:request,deny,log,status:403"
+            ';
+        }
+    }
+}
+EOF
+
+$t->write_file("/absolute", "should be moved/blocked before this.");
+$t->write_file("/iterative", "should be moved/blocked before this.");
+$t->run();
+$t->plan(5);
+
+###############################################################################
+
+like(http_get('/absolute?what=badarg1'), qr/should be moved\/blocked before this./, 'absolute scoring 1 (pass)');
+like(http_get('/absolute?what=badarg2'), qr/403 Forbidden/, 'absolute scoring 2 (block)');
+
+like(http_get('/iterative?arg1=badarg1'), qr/should be moved\/blocked before this./, 'iterative scoring 1 (pass)');
+like(http_get('/iterative?arg1=badarg1&arg2=badarg2'), qr/should be moved\/blocked before this./, 'iterative scoring 2 (pass)');
+like(http_get('/iterative?arg1=badarg1&arg2=badarg2&arg3=badarg3'), qr/403 Forbidden/, 'iterative scoring 3 (block)');
+
diff --git a/modules/http-modsecurity/tests/modsecurity-transaction-id.t b/modules/http-modsecurity/tests/modsecurity-transaction-id.t
new file mode 100644
index 0000000..f431c10
--- /dev/null
+++ b/modules/http-modsecurity/tests/modsecurity-transaction-id.t
@@ -0,0 +1,167 @@
+#!/usr/bin/perl
+
+# (C) Andrei Belov
+
+# Tests for ModSecurity-nginx connector (modsecurity_transaction_id).
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->plan(5)->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+daemon off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    modsecurity_transaction_id "tid-HTTP-DEFAULT-$request_id";
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  server1;
+
+        location / {
+            error_log %%TESTDIR%%/e_s1l1.log info;
+            modsecurity on;
+            modsecurity_rules '
+                SecRuleEngine On
+                SecDefaultAction "phase:1,log,deny,status:403"
+                SecRule ARGS "@streq block403" "id:4,phase:1,status:403,block"
+            ';
+        }
+    }
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  server2;
+
+        modsecurity_transaction_id "tid-SERVER-DEFAULT-$request_id";
+
+        location / {
+            error_log %%TESTDIR%%/e_s2l1.log info;
+            modsecurity on;
+            modsecurity_rules '
+                SecRuleEngine On
+                SecDefaultAction "phase:1,log,deny,status:403"
+                SecRule ARGS "@streq block403" "id:4,phase:1,status:403,block"
+            ';
+        }
+
+        location /specific {
+            error_log %%TESTDIR%%/e_s2l2.log info;
+            modsecurity on;
+            modsecurity_transaction_id "tid-LOCATION-SPECIFIC-$request_id";
+            modsecurity_rules '
+                SecRuleEngine On
+                SecDefaultAction "phase:1,log,deny,status:403"
+                SecRule ARGS "@streq block403" "id:4,phase:1,status:403,block"
+            ';
+        }
+
+        location /debuglog {
+            modsecurity on;
+            modsecurity_transaction_id "tid-DEBUG-$request_id";
+            modsecurity_rules '
+                SecRuleEngine On
+                SecDebugLog %%TESTDIR%%/modsec_debug.log
+                SecDebugLogLevel 4
+                SecDefaultAction "phase:1,log,deny,status:403"
+                SecRule ARGS "@streq block403" "id:4,phase:1,status:403,block"
+            ';
+        }
+
+        location /auditlog {
+            modsecurity on;
+            modsecurity_transaction_id "tid-AUDIT-$request_id";
+            modsecurity_rules '
+                SecRuleEngine On
+                SecDefaultAction "phase:1,log,deny,status:403"
+                SecAuditEngine On
+                SecAuditLogParts A
+                SecAuditLog %%TESTDIR%%/modsec_audit.log
+                SecAuditLogType Serial
+                SecAuditLogStorageDir %%TESTDIR%%/
+                SecRule ARGS "@streq block403" "id:4,phase:1,status:403,block"
+            ';
+        }
+    }
+}
+EOF
+
+$t->run();
+
+###############################################################################
+
+# charge limit_req
+
+http(<<EOF);
+GET /?what=block403 HTTP/1.0
+Host: server1
+
+EOF
+
+isnt(lines($t, 'e_s1l1.log', 'unique_id "tid-HTTP-DEFAULT-'), 0, 'http default');
+
+http(<<EOF);
+GET /?what=block403 HTTP/1.0
+Host: server2
+
+EOF
+
+isnt(lines($t, 'e_s2l1.log', 'unique_id "tid-SERVER-DEFAULT-'), 0, 'server default');
+
+http(<<EOF);
+GET /specific/?what=block403 HTTP/1.0
+Host: server2
+
+EOF
+
+isnt(lines($t, 'e_s2l2.log', 'unique_id "tid-LOCATION-SPECIFIC-'), 0, 'location specific');
+
+http(<<EOF);
+GET /debuglog/?what=block403 HTTP/1.0
+Host: server2
+
+EOF
+
+isnt(lines($t, 'modsec_debug.log', 'tid-DEBUG-'), 0, 'libmodsecurity debug log');
+
+http(<<EOF);
+GET /auditlog/?what=block403 HTTP/1.0
+Host: server2
+
+EOF
+
+isnt(lines($t, 'modsec_audit.log', 'tid-AUDIT-'), 0, 'libmodsecurity audit log');
+
+###############################################################################
+
+sub lines {
+	my ($t, $file, $pattern) = @_;
+	my $path = $t->testdir() . '/' . $file;
+	open my $fh, '<', $path or return "$!";
+	my $value = map { $_ =~ /\Q$pattern\E/ } (<$fh>);
+	close $fh;
+	return $value;
+}
+
+###############################################################################
diff --git a/modules/http-modsecurity/tests/modsecurity.t b/modules/http-modsecurity/tests/modsecurity.t
new file mode 100644
index 0000000..b39e54c
--- /dev/null
+++ b/modules/http-modsecurity/tests/modsecurity.t
@@ -0,0 +1,152 @@
+#!/usr/bin/perl
+
+#
+# ModSecurity, http://www.modsecurity.org/
+# Copyright (c) 2015 Trustwave Holdings, Inc. (http://www.trustwave.com/)
+#
+# You may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# If any of the files related to licensing are missing or if you have any
+# other questions related to licensing please contact Trustwave Holdings, Inc.
+# directly using the email address security@modsecurity.org.
+#
+
+
+# Tests for ModSecurity module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http/);
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+daemon off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location / {
+            modsecurity on;
+            modsecurity_rules '
+                SecRuleEngine On
+                SecRule ARGS "@streq whee" "id:10,phase:2"
+                SecRule ARGS "@streq whee" "id:11,phase:2"
+            ';
+        }
+        location /phase1 {
+            modsecurity on;
+            modsecurity_rules '
+                SecRuleEngine On
+                SecDefaultAction "phase:1,log,deny,status:403"
+                SecRule ARGS "@streq redirect301" "id:1,phase:1,status:301,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq redirect302" "id:2,phase:1,status:302,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq block401" "id:3,phase:1,status:401,block"
+                SecRule ARGS "@streq block403" "id:4,phase:1,status:403,block"
+            ';
+        }
+        location /phase2 {
+            modsecurity on;
+            modsecurity_rules '
+                SecRuleEngine On
+                SecDefaultAction "phase:2,log,deny,status:403"
+                SecRule ARGS "@streq redirect301" "id:1,phase:2,status:301,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq redirect302" "id:2,phase:2,status:302,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq block401" "id:3,phase:2,status:401,block"
+                SecRule ARGS "@streq block403" "id:4,phase:2,status:403,block"
+            ';
+        }
+        location /phase3 {
+            modsecurity on;
+            modsecurity_rules '
+                SecRuleEngine On
+                SecDefaultAction "phase:3,log,deny,status:403"
+                SecRule ARGS "@streq redirect301" "id:1,phase:3,status:301,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq redirect302" "id:2,phase:3,status:302,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq block401" "id:3,phase:3,status:401,block"
+                SecRule ARGS "@streq block403" "id:4,phase:3,status:403,block"
+            ';
+        }
+        location /phase4 {
+            modsecurity on;
+            modsecurity_rules '
+                SecRuleEngine On
+                SecResponseBodyAccess On
+                SecDefaultAction "phase:4,log,deny,status:403"
+                SecRule ARGS "@streq redirect301" "id:1,phase:4,status:301,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq redirect302" "id:2,phase:4,status:302,redirect:http://www.modsecurity.org"
+                SecRule ARGS "@streq block401" "id:3,phase:4,status:401,block"
+                SecRule ARGS "@streq block403" "id:4,phase:4,status:403,block"
+            ';
+        }
+    }
+}
+EOF
+
+$t->write_file("/phase1", "should be moved/blocked before this.");
+$t->write_file("/phase2", "should be moved/blocked before this.");
+$t->write_file("/phase3", "should be moved/blocked before this.");
+$t->write_file("/phase4", "should not be moved/blocked, headers delivered before phase 4.");
+$t->run();
+$t->todo_alerts();
+$t->plan(20);
+
+###############################################################################
+
+
+# Redirect (302)
+like(http_get('/phase1?what=redirect302'), qr/302 Moved Temporarily/, 'redirect 302 - phase 1');
+like(http_get('/phase2?what=redirect302'), qr/302 Moved Temporarily/, 'redirect 302 - phase 2');
+like(http_get('/phase3?what=redirect302'), qr/302 Moved Temporarily/, 'redirect 302 - phase 3');
+is(http_get('/phase4?what=redirect302'), '', 'redirect 302 - phase 4');
+
+# Redirect (301)
+like(http_get('/phase1?what=redirect301'), qr/301 Moved Permanently/, 'redirect 301 - phase 1');
+like(http_get('/phase2?what=redirect301'), qr/301 Moved Permanently/, 'redirect 301 - phase 2');
+like(http_get('/phase3?what=redirect301'), qr/301 Moved Permanently/, 'redirect 301 - phase 3');
+is(http_get('/phase4?what=redirect301'), '', 'redirect 301 - phase 4');
+
+# Block (401)
+like(http_get('/phase1?what=block401'), qr/401 Unauthorized/, 'block 401 - phase 1');
+like(http_get('/phase2?what=block401'), qr/401 Unauthorized/, 'block 401 - phase 2');
+like(http_get('/phase3?what=block401'), qr/401 Unauthorized/, 'block 401 - phase 3');
+is(http_get('/phase4?what=block401'), '', 'block 401 - phase 4');
+
+# Block (403)
+like(http_get('/phase1?what=block403'), qr/403 Forbidden/, 'block 403 - phase 1');
+like(http_get('/phase2?what=block403'), qr/403 Forbidden/, 'block 403 - phase 2');
+like(http_get('/phase3?what=block403'), qr/403 Forbidden/, 'block 403 - phase 3');
+is(http_get('/phase4?what=block403'), '', 'block 403 - phase 4');
+
+# Nothing to detect
+like(http_get('/phase1?what=nothing'), qr/should be moved\/blocked before this./, 'nothing phase 1');
+like(http_get('/phase2?what=nothing'), qr/should be moved\/blocked before this./, 'nothing phase 2');
+like(http_get('/phase3?what=nothing'), qr/should be moved\/blocked before this./, 'nothing phase 3');
+like(http_get('/phase4?what=nothing'), qr/should not be moved\/blocked, headers delivered before phase 4./, 'nothing phase 4');
+
diff --git a/modules/http-modsecurity/tests/nginx-tests-cvt.pl b/modules/http-modsecurity/tests/nginx-tests-cvt.pl
new file mode 100644
index 0000000..c778e46
--- /dev/null
+++ b/modules/http-modsecurity/tests/nginx-tests-cvt.pl
@@ -0,0 +1,39 @@
+#!/usr/bin/perl
+
+#
+# Script to adjust nginx tests to include ModSecurity directives.  It enables
+# us to passively test nginx functionality with ModSecurity module enabled.
+#
+
+# sh command line variations:
+#
+# for i in *.t; do cp -n $i $i.orig; perl nginx-tests-cvt.pl < $i.orig > $i; done
+# for i in *.t; do perl nginx-tests-cvt.pl < $i.orig > $i; done
+# for i in *.t; do cp $i.orig $i; done
+
+my $ignore = 0;
+while (<STDIN>) {
+	print $_;
+
+	$ignore = 1	if (/^mail {/);					# skip mail_*.t mail blocks
+	$ignore = 0	if (/^http {/);
+
+	next		if ($ignore);
+
+	if (/^ *server_name .*;$/) {
+
+		next if (/^ *server_name *below;/);			# skip duplication on refresh.t
+		next if (/^ *server_name *many4.example.com;/);		# skip duplication on http_server_name.t
+
+		print "
+        modsecurity on;
+        modsecurity_rules '
+        SecRuleEngine On
+        SecDebugLogLevel 9
+        SecRule ARGS \"\@streq whee\" \"id:10,phase:2\"
+        SecRule ARGS \"\@streq whee\" \"id:11,phase:2\"
+        ';
+";
+
+	}
+}
diff --git a/rules b/rules
index 981a57e..0760de8 100755
--- a/rules
+++ b/rules
@@ -20,6 +20,7 @@ DYN_MODS := \
 	http-perl \
 	http-subs-filter \
 	http-uploadprogress \
+	http-modsecurity \
 	http-upstream-fair \
 	http-xslt-filter \
 	mail \
@@ -101,6 +102,7 @@ full_configure_flags := \
 			--add-dynamic-module=$(MODULESDIR)/http-auth-pam \
 			--add-dynamic-module=$(MODULESDIR)/http-dav-ext \
 			--add-dynamic-module=$(MODULESDIR)/http-echo \
+			--add-dynamic-module=$(MODULESDIR)/http-modsecurity \
 			--add-dynamic-module=$(MODULESDIR)/http-upstream-fair \
 			--add-dynamic-module=$(MODULESDIR)/http-subs-filter
 
-- 
2.20.1

