changeset 178:87699398f955 NGINX_0_3_36

nginx 0.3.36 *) Feature: the ngx_http_addition_filter_module. *) Feature: the "proxy_pass" and "fastcgi_pass" directives may be used inside the "if" block. *) Feature: the "proxy_ignore_client_abort" and "fastcgi_ignore_client_abort" directives. *) Feature: the "$request_completion" variable. *) Feature: the ngx_http_perl_module supports the $r->request_method and $r->remote_addr. *) Feature: the ngx_http_ssi_module supports the "elif" command. *) Bugfix: the "\/" string in the expression of the "if" command of the ngx_http_ssi_module was treated incorrectly. *) Bugfix: in the regular expressions in the "if" command of the ngx_http_ssi_module. *) Bugfix: if the relative path was specified in the "client_body_temp_path", "proxy_temp_path", "fastcgi_temp_path", and "perl_modules" directives, then the directory was used relatively to a current path but not to a server prefix.
author Igor Sysoev <http://sysoev.ru>
date Wed, 05 Apr 2006 00:00:00 +0400
parents 4a3ddd758222
children 654cbdc0401d
files CHANGES CHANGES.ru auto/modules auto/options auto/os/linux auto/sources src/core/nginx.h src/core/ngx_conf_file.c src/core/ngx_file.c src/core/ngx_palloc.c src/http/modules/ngx_http_addition_filter_module.c src/http/modules/ngx_http_chunked_filter_module.c src/http/modules/ngx_http_fastcgi_module.c src/http/modules/ngx_http_memcached_module.c src/http/modules/ngx_http_proxy_module.c src/http/modules/ngx_http_ssi_filter_module.c src/http/modules/ngx_http_ssi_filter_module.h src/http/modules/perl/nginx.xs src/http/modules/perl/ngx_http_perl_module.c src/http/ngx_http_request.c src/http/ngx_http_request.h src/http/ngx_http_upstream.c src/http/ngx_http_upstream.h src/http/ngx_http_variables.c
diffstat 24 files changed, 538 insertions(+), 81 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,32 @@
+
+Changes with nginx 0.3.36                                        05 Apr 2006
+
+    *) Feature: the ngx_http_addition_filter_module.
+
+    *) Feature: the "proxy_pass" and "fastcgi_pass" directives may be used 
+       inside the "if" block.
+
+    *) Feature: the "proxy_ignore_client_abort" and 
+       "fastcgi_ignore_client_abort" directives.
+
+    *) Feature: the "$request_completion" variable.
+
+    *) Feature: the ngx_http_perl_module supports the $r->request_method 
+       and $r->remote_addr.
+
+    *) Feature: the ngx_http_ssi_module supports the "elif" command.
+
+    *) Bugfix: the "\/" string in the expression of the "if" command of the 
+       ngx_http_ssi_module was treated incorrectly.
+
+    *) Bugfix: in the regular expressions in the "if" command of the 
+       ngx_http_ssi_module.
+
+    *) Bugfix: if the relative path was specified in the 
+       "client_body_temp_path", "proxy_temp_path", "fastcgi_temp_path", and 
+       "perl_modules" directives, then the directory was used relatively to 
+       a current path but not to a server prefix.
+
 
 Changes with nginx 0.3.35                                        22 Mar 2006
 
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,3 +1,32 @@
+
+Изменения в nginx 0.3.36                                          05.04.2006
+
+    *) Добавление: модуль ngx_http_addition_filter_module.
+
+    *) Добавление: директивы proxy_pass и fastcgi_pass можно использовать 
+       внутри блока if.
+
+    *) Добавление: директивы proxy_ignore_client_abort и 
+       fastcgi_ignore_client_abort.
+
+    *) Добавление: переменная $request_completion.
+
+    *) Добавление: модуль ngx_http_perl_module поддерживает методы 
+       $r->request_method и $r->remote_addr.
+
+    *) Добавление: модуль ngx_http_ssi_module поддерживает команду elif.
+
+    *) Исправление: строка "\/" в начале выражения команды if модуля 
+       ngx_http_ssi_module воспринималась неверно.
+
+    *) Исправление: в использовании регулярных выражениях в команде if 
+       модуля ngx_http_ssi_module.
+
+    *) Исправление: при задании относительного пути в директивах 
+       client_body_temp_path, proxy_temp_path, fastcgi_temp_path и 
+       perl_modules использовался каталог относительно текущего каталога, а 
+       не относительно префикса сервера.
+
 
 Изменения в nginx 0.3.35                                          22.03.2006
 
@@ -19,7 +48,7 @@
        fastcgi_next_upstream.
 
     *) Исправление: ngx_http_perl_module не работал со встроенным в 
-       конфигурацинный файл кодом, если он не начинался сразу же с "sub".
+       конфигурационный файл кодом, если он не начинался сразу же с "sub".
 
     *) Исправление: в директиве post_action.
 
--- a/auto/modules
+++ b/auto/modules
@@ -78,6 +78,7 @@ fi
 #     ngx_http_postpone_filter
 #     ngx_http_charset_filter
 #     ngx_http_ssi_filter
+#         ngx_http_addition_filter
 #         ngx_http_userid_filter
 #         ngx_http_headers_filter
 #     ngx_http_copy_filter
@@ -114,6 +115,11 @@ if [ $HTTP_SSI = YES ]; then
     HTTP_SRCS="$HTTP_SRCS $HTTP_SSI_SRCS"
 fi
 
+if [ $HTTP_ADDITION = YES ]; then
+    HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_ADDITION_FILTER_MODULE"
+    HTTP_SRCS="$HTTP_SRCS $HTTP_ADDITION_SRCS"
+fi
+
 if [ $HTTP_USERID = YES ]; then
     HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_USERID_FILTER_MODULE"
     HTTP_SRCS="$HTTP_SRCS $HTTP_USERID_SRCS"
--- a/auto/options
+++ b/auto/options
@@ -52,6 +52,7 @@ HTTP_SSL=NO
 HTTP_SSI=YES
 HTTP_POSTPONE=NO
 HTTP_REALIP=NO
+HTTP_ADDITION=NO
 HTTP_ACCESS=YES
 HTTP_AUTH_BASIC=YES
 HTTP_USERID=YES
@@ -139,6 +140,7 @@ do
 
         --with-http_ssl_module)          HTTP_SSL=YES               ;;
         --with-http_realip_module)       HTTP_REALIP=YES            ;;
+        --with-http_addition_module)     HTTP_ADDITION=YES          ;;
         --without-http_charset_module)   HTTP_CHARSET=NO            ;;
         --without-http_gzip_module)      HTTP_GZIP=NO               ;;
         --without-http_ssi_module)       HTTP_SSI=NO                ;;
--- a/auto/os/linux
+++ b/auto/os/linux
@@ -59,7 +59,7 @@ fi
 
 # sendfile()
 
-CC_AUX_FLAGS="-D_GNU_SOURCE"
+CC_AUX_FLAGS="$CC_AUX_FLAGS -D_GNU_SOURCE"
 ngx_feature="sendfile()"
 ngx_feature_name="NGX_HAVE_SENDFILE"
 ngx_feature_run=yes
@@ -79,7 +79,7 @@ fi
 
 # sendfile64()
 
-CC_AUX_FLAGS="-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64"
+CC_AUX_FLAGS="$CC_AUX_FLAGS -D_FILE_OFFSET_BITS=64"
 ngx_feature="sendfile64()"
 ngx_feature_name="NGX_HAVE_SENDFILE64"
 ngx_feature_run=yes
--- a/auto/sources
+++ b/auto/sources
@@ -319,6 +319,10 @@ HTTP_REALIP_MODULE=ngx_http_realip_modul
 HTTP_REALIP_SRCS=src/http/modules/ngx_http_realip_module.c
 
 
+HTTP_ADDITION_FILTER_MODULE=ngx_http_addition_filter_module
+HTTP_ADDITION_SRCS=src/http/modules/ngx_http_addition_filter_module.c
+
+
 HTTP_ACCESS_MODULE=ngx_http_access_module
 HTTP_ACCESS_SRCS=src/http/modules/ngx_http_access_module.c
 
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,7 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define NGINX_VER          "nginx/0.3.35"
+#define NGINX_VER          "nginx/0.3.36"
 
 #define NGINX_VAR          "NGINX"
 #define NGX_OLDPID_EXT     ".oldbin"
--- a/src/core/ngx_conf_file.c
+++ b/src/core/ngx_conf_file.c
@@ -63,6 +63,7 @@ ngx_conf_parse(ngx_conf_t *cf, ngx_str_t
     char             *rv;
     ngx_fd_t          fd;
     ngx_int_t         rc;
+    ngx_buf_t        *b;
     ngx_uint_t        block;
     ngx_conf_file_t  *prev;
 
@@ -95,11 +96,23 @@ ngx_conf_parse(ngx_conf_t *cf, ngx_str_t
                           ngx_fd_info_n " \"%s\" failed", filename->data);
         }
 
-        cf->conf_file->buffer = ngx_create_temp_buf(cf->pool, ngx_pagesize);
-        if (cf->conf_file->buffer == NULL) {
+        b = ngx_calloc_buf(cf->pool);
+        if (b == NULL) {
             return NGX_CONF_ERROR;
         }
 
+        cf->conf_file->buffer = b;
+
+        b->start = ngx_alloc(ngx_pagesize, cf->log);
+        if (b->start == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        b->pos = b->start;
+        b->last = b->start;
+        b->end = b->last + ngx_pagesize;
+        b->temporary = 1;
+
         cf->conf_file->file.fd = fd;
         cf->conf_file->file.name.len = filename->len;
         cf->conf_file->file.name.data = filename->data;
@@ -183,7 +196,7 @@ ngx_conf_parse(ngx_conf_t *cf, ngx_str_t
 
 
     if (filename) {
-        ngx_pfree(cf->pool, cf->conf_file->buffer->start);
+        ngx_free(cf->conf_file->buffer->start);
 
         cf->conf_file = prev;
 
--- a/src/core/ngx_file.c
+++ b/src/core/ngx_file.c
@@ -211,8 +211,8 @@ ngx_conf_set_path_slot(ngx_conf_t *cf, n
     char  *p = conf;
 
     ssize_t      level;
+    ngx_str_t   *value;
     ngx_uint_t   i, n;
-    ngx_str_t   *value;
     ngx_path_t  *path, **slot;
 
     slot = (ngx_path_t **) (p + cmd->offset);
@@ -229,6 +229,11 @@ ngx_conf_set_path_slot(ngx_conf_t *cf, n
     value = cf->args->elts;
 
     path->name = value[1];
+
+    if (ngx_conf_full_name(cf->cycle, &path->name) == NGX_ERROR) {
+        return NULL;
+    }
+
     path->len = 0;
     path->cleaner = (ngx_gc_handler_pt) cmd->post;
     path->conf_file = cf->conf_file->file.name.data;
--- a/src/core/ngx_palloc.c
+++ b/src/core/ngx_palloc.c
@@ -86,7 +86,7 @@ ngx_palloc(ngx_pool_t *pool, size_t size
 {
     u_char            *m;
     ngx_pool_t        *p, *n;
-    ngx_pool_large_t  *large, *last;
+    ngx_pool_large_t  *large;
 
     if (size <= (size_t) NGX_MAX_ALLOC_FROM_POOL
         && size <= (size_t) (pool->end - (u_char *) pool)
@@ -134,34 +134,6 @@ ngx_palloc(ngx_pool_t *pool, size_t size
         return m;
     }
 
-    /* allocate a large block */
-
-    large = NULL;
-    last = NULL;
-
-    if (pool->large) {
-        for (last = pool->large; /* void */ ; last = last->next) {
-            if (last->alloc == NULL) {
-                large = last;
-                last = NULL;
-                break;
-            }
-
-            if (last->next == NULL) {
-                break;
-            }
-        }
-    }
-
-    if (large == NULL) {
-        large = ngx_palloc(pool, sizeof(ngx_pool_large_t));
-        if (large == NULL) {
-            return NULL;
-        }
-
-        large->next = NULL;
-    }
-
 #if 0
     p = ngx_memalign(ngx_pagesize, size, pool->log);
     if (p == NULL) {
@@ -174,14 +146,14 @@ ngx_palloc(ngx_pool_t *pool, size_t size
     }
 #endif
 
-    if (pool->large == NULL) {
-        pool->large = large;
-
-    } else if (last) {
-        last->next = large;
+    large = ngx_palloc(pool, sizeof(ngx_pool_large_t));
+    if (large == NULL) {
+        return NULL;
     }
 
     large->alloc = p;
+    large->next = pool->large;
+    pool->large = large;
 
     return p;
 }
new file mode 100644
--- /dev/null
+++ b/src/http/modules/ngx_http_addition_filter_module.c
@@ -0,0 +1,228 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+    ngx_str_t   before_body;
+    ngx_str_t   after_body;
+} ngx_http_addition_conf_t;
+
+
+typedef struct {
+    unsigned    before_body_sent:1;
+    unsigned    after_body_sent:1;
+} ngx_http_addition_ctx_t;
+
+
+static ngx_int_t ngx_http_addition_filter_init(ngx_cycle_t *cycle);
+static void *ngx_http_addition_create_conf(ngx_conf_t *cf);
+static char *ngx_http_addition_merge_conf(ngx_conf_t *cf, void *parent,
+    void *child);
+
+
+static ngx_command_t  ngx_http_addition_commands[] = {
+
+    { ngx_string("add_before_body"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_str_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_addition_conf_t, before_body),
+      NULL },
+
+    { ngx_string("add_after_body"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_str_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_addition_conf_t, after_body),
+      NULL },
+
+      ngx_null_command
+};
+
+
+static ngx_http_module_t  ngx_http_addition_filter_module_ctx = {
+    NULL,                                  /* preconfiguration */
+    NULL,                                  /* postconfiguration */
+
+    NULL,                                  /* create main configuration */
+    NULL,                                  /* init main configuration */
+
+    NULL,                                  /* create server configuration */
+    NULL,                                  /* merge server configuration */
+
+    ngx_http_addition_create_conf,         /* create location configuration */
+    ngx_http_addition_merge_conf           /* merge location configuration */
+};
+
+
+ngx_module_t  ngx_http_addition_filter_module = {
+    NGX_MODULE_V1,
+    &ngx_http_addition_filter_module_ctx,  /* module context */
+    ngx_http_addition_commands,            /* module directives */
+    NGX_HTTP_MODULE,                       /* module type */
+    NULL,                                  /* init master */
+    ngx_http_addition_filter_init,         /* init module */
+    NULL,                                  /* init process */
+    NULL,                                  /* init thread */
+    NULL,                                  /* exit thread */
+    NULL,                                  /* exit process */
+    NULL,                                  /* exit master */
+    NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
+static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;
+
+
+static ngx_int_t
+ngx_http_addition_header_filter(ngx_http_request_t *r)
+{
+    ngx_http_addition_ctx_t   *ctx;
+    ngx_http_addition_conf_t  *conf;
+
+    if (r->headers_out.status != NGX_HTTP_OK || r != r->main) {
+        return ngx_http_next_header_filter(r);
+    }
+
+    if (ngx_strncasecmp(r->headers_out.content_type.data, "text/html",
+                        sizeof("text/html") - 1)
+        != 0)
+    {
+        return ngx_http_next_header_filter(r);
+    }
+
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_addition_filter_module);
+
+    if (conf->before_body.len == 0 && conf->after_body.len == 0) {
+        return ngx_http_next_header_filter(r);
+    }
+
+    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_addition_ctx_t));
+    if (ctx == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_http_set_ctx(r, ctx, ngx_http_addition_filter_module);
+
+    ngx_http_clear_content_length(r);
+    ngx_http_clear_accept_ranges(r);
+
+    return ngx_http_next_header_filter(r);
+}
+
+
+static ngx_int_t
+ngx_http_addition_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+    ngx_int_t                  rc;
+    ngx_uint_t                 last;
+    ngx_chain_t               *cl;
+    ngx_http_addition_ctx_t   *ctx;
+    ngx_http_addition_conf_t  *conf;
+
+    if (in == NULL || r->header_only) {
+        return ngx_http_next_body_filter(r, in);
+    }
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_addition_filter_module);
+
+    if (ctx == NULL) {
+        return ngx_http_next_body_filter(r, in);
+    }
+
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_addition_filter_module);
+
+    if (!ctx->before_body_sent) {
+        ctx->before_body_sent = 1;
+
+        if (conf->before_body.len) {
+            if (ngx_http_subrequest(r, &conf->before_body, NULL, 0) != NGX_OK) {
+                return NGX_ERROR;
+            }
+        }
+    }
+
+    last = 0;
+
+    for (cl = in; cl; cl = cl->next) {
+        if (cl->buf->last_buf) {
+            cl->buf->last_buf = 0;
+            last = 1;
+        }
+    }
+
+    rc = ngx_http_next_body_filter(r, in);
+
+    if (rc == NGX_ERROR
+        || !last
+        || ctx->after_body_sent
+        || conf->after_body.len == 0)
+    {
+        return rc;
+    }
+
+    if (ngx_http_subrequest(r, &conf->after_body, NULL, 0) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    ctx->after_body_sent = 1;
+
+    return ngx_http_send_special(r, NGX_HTTP_LAST);
+}
+
+
+static ngx_int_t
+ngx_http_addition_filter_init(ngx_cycle_t *cycle)
+{
+    ngx_http_next_header_filter = ngx_http_top_header_filter;
+    ngx_http_top_header_filter = ngx_http_addition_header_filter;
+
+    ngx_http_next_body_filter = ngx_http_top_body_filter;
+    ngx_http_top_body_filter = ngx_http_addition_body_filter;
+
+    return NGX_OK;
+}
+
+
+static void *
+ngx_http_addition_create_conf(ngx_conf_t *cf)
+{
+    ngx_http_addition_conf_t  *conf;
+
+    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_addition_conf_t));
+    if (conf == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    /*
+     * set by ngx_pcalloc():
+     *
+     *     conf->before_body.len = 0;
+     *     conf->before_body.date = NULL;
+     *     conf->after_body.len = 0;
+     *     conf->after_body.date = NULL;
+     */
+
+    return conf;
+}
+
+
+static char *
+ngx_http_addition_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+    ngx_http_addition_conf_t *prev = parent;
+    ngx_http_addition_conf_t *conf = child;
+
+    ngx_conf_merge_str_value(conf->before_body, prev->before_body, "");
+    ngx_conf_merge_str_value(conf->after_body, prev->after_body, "");
+
+    return NGX_CONF_OK;
+}
--- a/src/http/modules/ngx_http_chunked_filter_module.c
+++ b/src/http/modules/ngx_http_chunked_filter_module.c
@@ -23,7 +23,7 @@ static ngx_http_module_t  ngx_http_chunk
     NULL,                                  /* merge server configuration */
 
     NULL,                                  /* create location configuration */
-    NULL,                                  /* merge location configuration */
+    NULL                                   /* merge location configuration */
 };
 
 
--- a/src/http/modules/ngx_http_fastcgi_module.c
+++ b/src/http/modules/ngx_http_fastcgi_module.c
@@ -170,7 +170,7 @@ static ngx_conf_bitmask_t  ngx_http_fast
 static ngx_command_t  ngx_http_fastcgi_commands[] = {
 
     { ngx_string("fastcgi_pass"),
-      NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
       ngx_http_fastcgi_pass,
       NGX_HTTP_LOC_CONF_OFFSET,
       0,
@@ -183,6 +183,13 @@ static ngx_command_t  ngx_http_fastcgi_c
       offsetof(ngx_http_fastcgi_loc_conf_t, index),
       NULL },
 
+    { ngx_string("fastcgi_ignore_client_abort"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_flag_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.ignore_client_abort),
+      NULL },
+
     { ngx_string("fastcgi_connect_timeout"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_msec_slot,
@@ -1470,6 +1477,7 @@ ngx_http_fastcgi_create_loc_conf(ngx_con
      */
 
     conf->upstream.buffering = NGX_CONF_UNSET;
+    conf->upstream.ignore_client_abort = NGX_CONF_UNSET;
 
     conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
     conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
@@ -1520,6 +1528,9 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf
     ngx_conf_merge_value(conf->upstream.buffering,
                               prev->upstream.buffering, 1);
 
+    ngx_conf_merge_value(conf->upstream.ignore_client_abort,
+                              prev->upstream.ignore_client_abort, 0);
+
     ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
                               prev->upstream.connect_timeout, 60000);
 
@@ -1670,6 +1681,7 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf
 
     if (conf->peers == NULL) {
         conf->peers = prev->peers;
+        conf->upstream.schema = prev->upstream.schema;
     }
 
     if (conf->params_source == NULL) {
--- a/src/http/modules/ngx_http_memcached_module.c
+++ b/src/http/modules/ngx_http_memcached_module.c
@@ -517,6 +517,8 @@ ngx_http_memcached_create_loc_conf(ngx_c
     conf->upstream.cyclic_temp_file = 0;
 
     /* the hardcoded values */
+    conf->upstream.buffering = 0;
+    conf->upstream.ignore_client_abort = 0;
     conf->upstream.send_lowat = 0;
     conf->upstream.bufs.num = 0;
     conf->upstream.busy_buffers_size = 0;
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -130,7 +130,7 @@ static ngx_conf_bitmask_t  ngx_http_prox
 static ngx_command_t  ngx_http_proxy_commands[] = {
 
     { ngx_string("proxy_pass"),
-      NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
       ngx_http_proxy_pass,
       NGX_HTTP_LOC_CONF_OFFSET,
       0,
@@ -150,6 +150,13 @@ static ngx_command_t  ngx_http_proxy_com
       offsetof(ngx_http_proxy_loc_conf_t, upstream.buffering),
       NULL },
 
+    { ngx_string("proxy_ignore_client_abort"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_flag_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_proxy_loc_conf_t, upstream.ignore_client_abort),
+      NULL },
+
     { ngx_string("proxy_connect_timeout"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_msec_slot,
@@ -1415,6 +1422,7 @@ ngx_http_proxy_create_loc_conf(ngx_conf_
      */
 
     conf->upstream.buffering = NGX_CONF_UNSET;
+    conf->upstream.ignore_client_abort = NGX_CONF_UNSET;
 
     conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
     conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
@@ -1468,6 +1476,9 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
     ngx_conf_merge_value(conf->upstream.buffering,
                               prev->upstream.buffering, 1);
 
+    ngx_conf_merge_value(conf->upstream.ignore_client_abort,
+                              prev->upstream.ignore_client_abort, 0);
+
     ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
                               prev->upstream.connect_timeout, 60000);
 
@@ -1654,6 +1665,10 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
 
     if (conf->peers == NULL) {
         conf->peers = prev->peers;
+
+        conf->host_header = prev->host_header;
+        conf->port_text = prev->port_text;
+        conf->upstream.schema = prev->upstream.schema;
     }
 
 
@@ -2057,11 +2072,12 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_
 
 #if (NGX_PCRE)
 
-    if (clcf->regex) {
+    if (clcf->regex || clcf->noname) {
         if (plcf->upstream.uri.len) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                "\"proxy_pass\" may not have URI part in "
-                               "location given by regular expression");
+                               "location given by regular expression or "
+                               "inside the \"if\" statement");
             return NGX_CONF_ERROR;
         }
 
--- a/src/http/modules/ngx_http_ssi_filter_module.c
+++ b/src/http/modules/ngx_http_ssi_filter_module.c
@@ -13,6 +13,7 @@
 #define NGX_HTTP_SSI_DATE_LEN       2048
 
 #define NGX_HTTP_SSI_ADD_PREFIX     1
+#define NGX_HTTP_SSI_ADD_ZERO       2
 
 
 typedef struct {
@@ -247,8 +248,12 @@ static ngx_http_ssi_command_t  ngx_http_
     { ngx_string("set"), ngx_http_ssi_set, ngx_http_ssi_set_params, 0, 0 },
 
     { ngx_string("if"), ngx_http_ssi_if, ngx_http_ssi_if_params, 0, 0 },
-    { ngx_string("else"), ngx_http_ssi_else, ngx_http_ssi_no_params, 1, 0 },
-    { ngx_string("endif"), ngx_http_ssi_endif, ngx_http_ssi_no_params, 1, 0 },
+    { ngx_string("elif"), ngx_http_ssi_if, ngx_http_ssi_if_params,
+                       NGX_HTTP_SSI_COND_IF, 0 },
+    { ngx_string("else"), ngx_http_ssi_else, ngx_http_ssi_no_params,
+                       NGX_HTTP_SSI_COND_IF, 0 },
+    { ngx_string("endif"), ngx_http_ssi_endif, ngx_http_ssi_no_params,
+                       NGX_HTTP_SSI_COND_ELSE, 0 },
 
     { ngx_null_string, NULL, NULL, 0, 0 }
 };
@@ -523,7 +528,17 @@ ngx_http_ssi_body_filter(ngx_http_reques
                     continue;
                 }
 
-                if (!ctx->output && !cmd->conditional) {
+                if (cmd->conditional
+                    && (ctx->conditional == 0
+                        || ctx->conditional > cmd->conditional))
+                {
+                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                                  "invalid context of SSI command: \"%V\"",
+                                  &ctx->command);
+                    goto ssi_error;
+                }
+
+                if (!ctx->output && cmd->conditional == 0) {
                     continue;
                 }
 
@@ -926,6 +941,7 @@ ngx_http_ssi_parse(ngx_http_request_t *r
                 ctx->key = ngx_hash(ctx->key, ch);
 
                 ctx->params.nelts = 0;
+
                 state = ssi_command_state;
                 break;
             }
@@ -1565,7 +1581,7 @@ ngx_http_ssi_evaluate_string(ngx_http_re
         }
     }
 
-    p = ngx_palloc(r->pool, len);
+    p = ngx_palloc(r->pool, len + ((flags & NGX_HTTP_SSI_ADD_ZERO) ? 1 : 0));
     if (p == NULL) {
         return NGX_ERROR;
     }
@@ -1809,13 +1825,26 @@ ngx_http_ssi_if(ngx_http_request_t *r, n
     u_char       *p, *last;
     ngx_str_t    *expr, left, right;
     ngx_int_t     rc;
-    ngx_uint_t    negative, noregex;
+    ngx_uint_t    negative, noregex, flags;
 #if (NGX_PCRE)
     ngx_str_t     err;
     ngx_regex_t  *regex;
     u_char        errstr[NGX_MAX_CONF_ERRSTR];
 #endif
 
+    if (ctx->command.len == 2) {
+        if (ctx->conditional) {
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                          "the \"if\" command inside the \"if\" command");
+            return NGX_HTTP_SSI_ERROR;
+        }
+    }
+
+    if (ctx->output_chosen) {
+        ctx->output = 0;
+        return NGX_OK;
+    }
+
     expr = params[NGX_HTTP_SSI_IF_EXPR];
 
     left.data = expr->data;
@@ -1857,11 +1886,14 @@ ngx_http_ssi_if(ngx_http_request_t *r, n
     if (p == last) {
         if (left.len) {
             ctx->output = 1;
+            ctx->output_chosen = 1;
 
         } else {
             ctx->output = 0;
         }
 
+        ctx->conditional = NGX_HTTP_SSI_COND_IF;
+
         return NGX_OK;
     }
 
@@ -1887,11 +1919,17 @@ ngx_http_ssi_if(ngx_http_request_t *r, n
         }
 
         noregex = 0;
+        flags = NGX_HTTP_SSI_ADD_ZERO;
         last--;
 	p++;
 
     } else {
         noregex = 1;
+        flags = 0;
+
+        if (p < last - 1 && p[0] == '\\' && p[1] == '/') {
+	    p++;
+        }
     }
 
     right.len = last - p;
@@ -1900,7 +1938,7 @@ ngx_http_ssi_if(ngx_http_request_t *r, n
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "right: \"%V\"", &right);
 
-    if (ngx_http_ssi_evaluate_string(r, ctx, &right, 0) != NGX_OK) {
+    if (ngx_http_ssi_evaluate_string(r, ctx, &right, flags) != NGX_OK) {
         return NGX_HTTP_SSI_ERROR;
     }
 
@@ -1948,11 +1986,14 @@ ngx_http_ssi_if(ngx_http_request_t *r, n
 
     if ((rc == 0 && !negative) || (rc != 0 && negative)) {
         ctx->output = 1;
+        ctx->output_chosen = 1;
 
     } else {
         ctx->output = 0;
     }
 
+    ctx->conditional = NGX_HTTP_SSI_COND_IF;
+
     return NGX_OK;
 
 invalid_expression:
@@ -1968,7 +2009,13 @@ static ngx_int_t
 ngx_http_ssi_else(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
     ngx_str_t **params)
 {
-    ctx->output = !ctx->output;
+    if (ctx->output_chosen) {
+        ctx->output = 0;
+    } else {
+        ctx->output = 1;
+    }
+
+    ctx->conditional = NGX_HTTP_SSI_COND_ELSE;
 
     return NGX_OK;
 }
@@ -1979,6 +2026,8 @@ ngx_http_ssi_endif(ngx_http_request_t *r
     ngx_str_t **params)
 {
     ctx->output = 1;
+    ctx->output_chosen = 0;
+    ctx->conditional = 0;
 
     return NGX_OK;
 }
--- a/src/http/modules/ngx_http_ssi_filter_module.h
+++ b/src/http/modules/ngx_http_ssi_filter_module.h
@@ -20,6 +20,10 @@
 #define NGX_HTTP_SSI_PARAMS_N       4
 
 
+#define NGX_HTTP_SSI_COND_IF        1
+#define NGX_HTTP_SSI_COND_ELSE      2
+
+
 typedef struct {
     ngx_hash_t                hash;
     ngx_hash_keys_arrays_t    commands;
@@ -54,7 +58,9 @@ typedef struct {
 
     ngx_array_t               variables;
 
-    ngx_uint_t                output;        /* unsigned  output:1; */
+    unsigned                  conditional:2;
+    unsigned                  output:1;
+    unsigned                  output_chosen:1;
 
     void                     *value_buf;
     ngx_str_t                 timefmt;
@@ -80,7 +86,7 @@ typedef struct {
     ngx_http_ssi_command_pt   handler;
     ngx_http_ssi_param_t     *params;
 
-    unsigned                  conditional:1;
+    unsigned                  conditional:2;
     unsigned                  flush:1;
 } ngx_http_ssi_command_t;
 
--- a/src/http/modules/perl/nginx.xs
+++ b/src/http/modules/perl/nginx.xs
@@ -178,6 +178,35 @@ args(r, ...)
 
 
 char *
+request_method(r)
+    nginx  r
+
+    CODE:
+
+    RETVAL = ngx_palloc(r->pool, r->method_name.len + 1);
+    if (RETVAL == NULL) {
+        XSRETURN_UNDEF;
+    }
+
+    ngx_cpystrn((u_char *) RETVAL, r->method_name.data, r->method_name.len + 1);
+
+    OUTPUT:
+    RETVAL
+
+
+char *
+remote_addr(r)
+    nginx  r
+
+    CODE:
+
+    RETVAL = (char *) r->connection->addr_text.data;
+
+    OUTPUT:
+    RETVAL
+
+
+char *
 header_in(r, key)
     nginx             r
     SV               *key
--- a/src/http/modules/perl/ngx_http_perl_module.c
+++ b/src/http/modules/perl/ngx_http_perl_module.c
@@ -39,6 +39,7 @@ static ngx_int_t ngx_http_perl_ssi(ngx_h
     ngx_http_ssi_ctx_t *ssi_ctx, ngx_str_t **params);
 #endif
 
+static void ngx_http_perl_handle_request(ngx_http_request_t *r);
 static ngx_int_t
     ngx_http_perl_get_interpreter(ngx_http_perl_main_conf_t *pmcf,
     PerlInterpreter **perl, ngx_log_t *log);
@@ -174,23 +175,39 @@ ngx_http_perl_xs_init(pTHX)
 static ngx_int_t
 ngx_http_perl_handler(ngx_http_request_t *r)
 {
+    ngx_int_t  rc;
+
+    /* TODO: Win32 */
+    if (r->zero_in_uri) {
+        return NGX_HTTP_NOT_FOUND;
+    }
+
+    rc = ngx_http_read_client_request_body(r, ngx_http_perl_handle_request);
+
+    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
+        return rc;
+    }
+
+    return NGX_DONE;
+}
+
+
+static void
+ngx_http_perl_handle_request(ngx_http_request_t *r)
+{
     ngx_int_t                   rc;
     ngx_str_t                   uri, args;
     ngx_http_perl_ctx_t        *ctx;
     ngx_http_perl_loc_conf_t   *plcf;
     ngx_http_perl_main_conf_t  *pmcf;
 
-    /* TODO: Win32 */
-    if (r->zero_in_uri) {
-        return NGX_HTTP_NOT_FOUND;
-    }
-
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "perl handler");
 
     /* mod_perl's content handler assumes that content type was already set */
 
     if (ngx_http_set_content_type(r) != NGX_OK) {
-        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+        return;
     }
 
     ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);
@@ -198,7 +215,8 @@ ngx_http_perl_handler(ngx_http_request_t
     if (ctx == NULL) {
         ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_perl_ctx_t));
         if (ctx == NULL) {
-            return NGX_ERROR;
+            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+	    return;
         }
 
         ngx_http_set_ctx(r, ctx, ngx_http_perl_module);
@@ -209,7 +227,8 @@ ngx_http_perl_handler(ngx_http_request_t
     rc = ngx_http_perl_get_interpreter(pmcf, &ctx->perl, r->connection->log);
 
     if (rc != NGX_OK) {
-        return rc;
+        ngx_http_finalize_request(r, rc);
+        return;
     }
 
     {
@@ -235,20 +254,24 @@ ngx_http_perl_handler(ngx_http_request_t
     if (ctx->redirect_uri.len) {
         uri = ctx->redirect_uri;
         args = ctx->redirect_args;
+
+    } else {
+        uri.len = 0;
     }
 
     ctx->filename = NULL;
     ctx->redirect_uri.len = 0;
 
     if (uri.len) {
-        return ngx_http_internal_redirect(r, &uri, &args);
+        ngx_http_internal_redirect(r, &uri, &args);
+        return;
     }
 
     if (rc == NGX_OK || rc == NGX_HTTP_OK) {
-        return ngx_http_send_special(r, NGX_HTTP_LAST);
+        ngx_http_send_special(r, NGX_HTTP_LAST);
     }
 
-    return rc;
+    ngx_http_finalize_request(r, rc);
 }
 
 
@@ -448,6 +471,10 @@ ngx_http_perl_init_interpreter(ngx_conf_
     }
 #endif
 
+    if (ngx_conf_full_name(cf->cycle, &pmcf->modules) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
     PERL_SYS_INIT(&ngx_argc, &ngx_argv);
 
     pmcf->perl = ngx_http_perl_create_interpreter(pmcf, cf->log);
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -1503,6 +1503,10 @@ ngx_http_finalize_request(ngx_http_reque
         return;
     }
 
+    if (!r->post_action) {
+        r->request_complete = 1;
+    }
+
     if (ngx_http_post_action(r) == NGX_OK) {
         return;
     }
@@ -2254,6 +2258,7 @@ ngx_http_post_action(ngx_http_request_t 
 
     r->http_version = NGX_HTTP_VERSION_9;
     r->header_only = 1;
+    r->post_action = 1;
 
     ngx_http_internal_redirect(r, &clcf->post_action, NULL);
 
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -405,6 +405,8 @@ struct ngx_http_request_s {
     unsigned                          lingering_close:1;
     unsigned                          discard_body:1;
     unsigned                          internal:1;
+    unsigned                          post_action:1;
+    unsigned                          request_complete:1;
     unsigned                          done:1;
     unsigned                          utf8:1;
 
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -277,9 +277,9 @@ ngx_http_upstream_init(ngx_http_request_
         ngx_del_timer(c->read);
     }
 
-    if (!(r->http_version == NGX_HTTP_VERSION_9 && r->header_only)) {
-        /* not a post_action */
-
+    u = r->upstream;
+
+    if (!r->post_action && !u->conf->ignore_client_abort) {
         r->read_event_handler = ngx_http_upstream_rd_check_broken_connection;
         r->write_event_handler = ngx_http_upstream_wr_check_broken_connection;
     }
@@ -296,8 +296,6 @@ ngx_http_upstream_init(ngx_http_request_
         }
     }
 
-    u = r->upstream;
-
     if (r->request_body) {
         u->request_bufs = r->request_body->bufs;
     }
@@ -1208,10 +1206,7 @@ ngx_http_upstream_send_response(ngx_http
 
     rc = ngx_http_send_header(r);
 
-    if (rc == NGX_ERROR
-        || rc > NGX_OK
-                          /* post_action */
-        || (r->http_version == NGX_HTTP_VERSION_9 && r->header_only)) {
+    if (rc == NGX_ERROR || rc > NGX_OK || r->post_action) {
         ngx_http_upstream_finalize_request(r, u, rc);
         return;
     }
@@ -1947,11 +1942,7 @@ ngx_http_upstream_finalize_request(ngx_h
 
     r->connection->log->action = "sending to client";
 
-    if (rc == 0
-        && r == r->main
-             /* not a post_action */
-        && !(r->http_version == NGX_HTTP_VERSION_9 && r->header_only))
-    {
+    if (rc == 0 && r == r->main && !r->post_action) {
         rc = ngx_http_send_special(r, NGX_HTTP_LAST);
     }
 
--- a/src/http/ngx_http_upstream.h
+++ b/src/http/ngx_http_upstream.h
@@ -73,6 +73,7 @@ typedef struct {
     ngx_flag_t                      pass_request_headers;
     ngx_flag_t                      pass_request_body;
 
+    ngx_flag_t                      ignore_client_abort;
     ngx_flag_t                      redirect_errors;
     ngx_flag_t                      cyclic_temp_file;
 
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -44,6 +44,8 @@ static ngx_int_t ngx_http_variable_remot
     ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_variable_body_bytes_sent(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_request_completion(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
 
 
 /*
@@ -135,6 +137,9 @@ static ngx_http_variable_t  ngx_http_cor
     { ngx_string("body_bytes_sent"), ngx_http_variable_body_bytes_sent,
       0, 0, 0 },
 
+    { ngx_string("request_completion"), ngx_http_variable_request_completion,
+      0, 0, 0 },
+
     { ngx_null_string, NULL, 0, 0, 0 }
 };
 
@@ -798,6 +803,30 @@ ngx_http_variable_body_bytes_sent(ngx_ht
 }
 
 
+static ngx_int_t
+ngx_http_variable_request_completion(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    if (r->request_complete) {
+        v->len = 2;
+        v->valid = 1;
+        v->no_cachable = 0;
+        v->not_found = 0;
+        v->data = (u_char *) "OK";
+
+        return NGX_OK;
+    }
+
+    v->len = 0;
+    v->valid = 1;
+    v->no_cachable = 0;
+    v->not_found = 0;
+    v->data = (u_char *) "";
+
+    return NGX_OK;
+}
+
+
 ngx_int_t
 ngx_http_variables_add_core_vars(ngx_conf_t *cf)
 {