changeset 654:753f505670e0 NGINX_1_1_11

nginx 1.1.11 *) Feature: the "so_keepalive" parameter of the "listen" directive. Thanks to Vsevolod Stakhov. *) Feature: the "if_not_empty" parameter of the "fastcgi/scgi/uwsgi_param" directives. *) Feature: the $https variable. *) Feature: the "proxy_redirect" directive supports variables in the first parameter. *) Feature: the "proxy_redirect" directive supports regular expressions. *) Bugfix: the $sent_http_cache_control variable might contain a wrong value if the "expires" directive was used. Thanks to Yichun Zhang. *) Bugfix: the "read_ahead" directive might not work combined with "try_files" and "open_file_cache". *) Bugfix: a segmentation fault might occur in a worker process if small time was used in the "inactive" parameter of the "proxy_cache_path" directive. *) Bugfix: responses from cache might hang.
author Igor Sysoev <http://sysoev.ru>
date Mon, 12 Dec 2011 00:00:00 +0400
parents 8c96af2112c1
children 189afff6503f
files CHANGES CHANGES.ru auto/unix conf/fastcgi_params conf/scgi_params conf/uwsgi_params src/core/nginx.h src/core/ngx_connection.c src/core/ngx_connection.h src/core/ngx_regex.c src/http/modules/ngx_http_fastcgi_module.c src/http/modules/ngx_http_log_module.c src/http/modules/ngx_http_proxy_module.c src/http/modules/ngx_http_scgi_module.c src/http/modules/ngx_http_uwsgi_module.c src/http/modules/perl/nginx.pm src/http/ngx_http.c src/http/ngx_http_core_module.c src/http/ngx_http_core_module.h src/http/ngx_http_file_cache.c src/http/ngx_http_header_filter_module.c src/http/ngx_http_upstream.c src/http/ngx_http_upstream.h src/http/ngx_http_variables.c src/mail/ngx_mail.c src/mail/ngx_mail.h src/mail/ngx_mail_core_module.c src/os/unix/ngx_freebsd_sendfile_chain.c
diffstat 28 files changed, 789 insertions(+), 260 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES	Wed Nov 30 00:00:00 2011 +0400
+++ b/CHANGES	Mon Dec 12 00:00:00 2011 +0400
@@ -1,4 +1,33 @@
 
+Changes with nginx 1.1.11                                        12 Dec 2011
+
+    *) Feature: the "so_keepalive" parameter of the "listen" directive.
+       Thanks to Vsevolod Stakhov.
+
+    *) Feature: the "if_not_empty" parameter of the
+       "fastcgi/scgi/uwsgi_param" directives.
+
+    *) Feature: the $https variable.
+
+    *) Feature: the "proxy_redirect" directive supports variables in the
+       first parameter.
+
+    *) Feature: the "proxy_redirect" directive supports regular expressions.
+
+    *) Bugfix: the $sent_http_cache_control variable might contain a wrong
+       value if the "expires" directive was used.
+       Thanks to Yichun Zhang.
+
+    *) Bugfix: the "read_ahead" directive might not work combined with
+       "try_files" and "open_file_cache".
+
+    *) Bugfix: a segmentation fault might occur in a worker process if small
+       time was used in the "inactive" parameter of the "proxy_cache_path"
+       directive.
+
+    *) Bugfix: responses from cache might hang.
+
+
 Changes with nginx 1.1.10                                        30 Nov 2011
 
     *) Bugfix: a segmentation fault occured in a worker process if AIO was
--- a/CHANGES.ru	Wed Nov 30 00:00:00 2011 +0400
+++ b/CHANGES.ru	Mon Dec 12 00:00:00 2011 +0400
@@ -1,4 +1,34 @@
 
+Изменения в nginx 1.1.11                                          12.12.2011
+
+    *) Добавление: параметр so_keepalive в директиве listen.
+       Спасибо Всеволоду Стахову.
+
+    *) Добавление: параметр if_not_empty в директивах
+       fastcgi/scgi/uwsgi_param.
+
+    *) Добавление: переменная $https.
+
+    *) Добавление: директива proxy_redirect поддерживает переменные в первом
+       параметре.
+
+    *) Добавление: директива proxy_redirect поддерживает регулярные
+       выражения.
+
+    *) Исправление: переменная $sent_http_cache_control могла содержать
+       неверное значение при использовании директивы expires.
+       Спасибо Yichun Zhang.
+
+    *) Исправление: директива read_ahead могла не работать при использовании
+       совместно с try_files и open_file_cache.
+
+    *) Исправление: если в параметре inactive директивы proxy_cache_path
+       было указано малое время, в рабочем процессе мог произойти
+       segmentation fault.
+
+    *) Исправление: ответы из кэша могли зависать.
+
+
 Изменения в nginx 1.1.10                                          30.11.2011
 
     *) Исправление: при использовании AIO на Linux в рабочем процессе
--- a/auto/unix	Wed Nov 30 00:00:00 2011 +0400
+++ b/auto/unix	Mon Dec 12 00:00:00 2011 +0400
@@ -328,6 +328,20 @@
 . auto/feature
 
 
+ngx_feature="TCP_KEEPIDLE, TCP_KEEPINTVL, TCP_KEEPCNT"
+ngx_feature_name="NGX_HAVE_KEEPALIVE_TUNABLE"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/socket.h>
+                  #include <netinet/in.h>
+                  #include <netinet/tcp.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="setsockopt(0, IPPROTO_TCP, TCP_KEEPIDLE, NULL, 0);
+                  setsockopt(0, IPPROTO_TCP, TCP_KEEPINTVL, NULL, 0);
+                  setsockopt(0, IPPROTO_TCP, TCP_KEEPCNT, NULL, 0)"
+. auto/feature
+
+
 ngx_feature="accept4()"
 ngx_feature_name="NGX_HAVE_ACCEPT4"
 ngx_feature_run=no
--- a/conf/fastcgi_params	Wed Nov 30 00:00:00 2011 +0400
+++ b/conf/fastcgi_params	Mon Dec 12 00:00:00 2011 +0400
@@ -9,6 +9,7 @@
 fastcgi_param  DOCUMENT_URI       $document_uri;
 fastcgi_param  DOCUMENT_ROOT      $document_root;
 fastcgi_param  SERVER_PROTOCOL    $server_protocol;
+fastcgi_param  HTTPS              $https if_not_empty;
 
 fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
 fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;
--- a/conf/scgi_params	Wed Nov 30 00:00:00 2011 +0400
+++ b/conf/scgi_params	Mon Dec 12 00:00:00 2011 +0400
@@ -8,6 +8,7 @@
 scgi_param  DOCUMENT_ROOT      $document_root;
 scgi_param  SCGI               1;
 scgi_param  SERVER_PROTOCOL    $server_protocol;
+scgi_param  HTTPS              $https if_not_empty;
 
 scgi_param  REMOTE_ADDR        $remote_addr;
 scgi_param  REMOTE_PORT        $remote_port;
--- a/conf/uwsgi_params	Wed Nov 30 00:00:00 2011 +0400
+++ b/conf/uwsgi_params	Mon Dec 12 00:00:00 2011 +0400
@@ -8,6 +8,7 @@
 uwsgi_param  PATH_INFO          $document_uri;
 uwsgi_param  DOCUMENT_ROOT      $document_root;
 uwsgi_param  SERVER_PROTOCOL    $server_protocol;
+uwsgi_param  HTTPS              $https if_not_empty;
 
 uwsgi_param  REMOTE_ADDR        $remote_addr;
 uwsgi_param  REMOTE_PORT        $remote_port;
--- a/src/core/nginx.h	Wed Nov 30 00:00:00 2011 +0400
+++ b/src/core/nginx.h	Mon Dec 12 00:00:00 2011 +0400
@@ -8,8 +8,8 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define nginx_version      1001010
-#define NGINX_VERSION      "1.1.10"
+#define nginx_version      1001011
+#define NGINX_VERSION      "1.1.11"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_connection.c	Wed Nov 30 00:00:00 2011 +0400
+++ b/src/core/ngx_connection.c	Mon Dec 12 00:00:00 2011 +0400
@@ -462,6 +462,7 @@
 void
 ngx_configure_listening_sockets(ngx_cycle_t *cycle)
 {
+    int                        keepalive;
     ngx_uint_t                 i;
     ngx_listening_t           *ls;
 
@@ -499,6 +500,56 @@
             }
         }
 
+        if (ls[i].keepalive) {
+            keepalive = (ls[i].keepalive == 1) ? 1 : 0;
+
+            if (setsockopt(ls[i].fd, SOL_SOCKET, SO_KEEPALIVE,
+                           (const void *) &keepalive, sizeof(int))
+                == -1)
+            {
+                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
+                              "setsockopt(SO_KEEPALIVE, %d) %V failed, ignored",
+                              keepalive, &ls[i].addr_text);
+            }
+        }
+
+#if (NGX_HAVE_KEEPALIVE_TUNABLE) 
+
+        if (ls[i].keepidle) {
+            if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_KEEPIDLE,
+                           (const void *) &ls[i].keepidle, sizeof(int))
+                == -1)
+            {
+                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
+                              "setsockopt(TCP_KEEPIDLE, %d) %V failed, ignored",
+                              ls[i].keepidle, &ls[i].addr_text);
+            }
+        }
+
+        if (ls[i].keepintvl) {
+            if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_KEEPINTVL,
+                           (const void *) &ls[i].keepintvl, sizeof(int))
+                == -1)
+            {
+                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
+                             "setsockopt(TCP_KEEPINTVL, %d) %V failed, ignored",
+                             ls[i].keepintvl, &ls[i].addr_text);
+            }
+        }
+
+        if (ls[i].keepcnt) {
+            if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_KEEPCNT,
+                           (const void *) &ls[i].keepcnt, sizeof(int))
+                == -1)
+            {
+                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
+                              "setsockopt(TCP_KEEPCNT, %d) %V failed, ignored",
+                              ls[i].keepcnt, &ls[i].addr_text);
+            }
+        }
+
+#endif
+
 #if (NGX_HAVE_SETFIB)
         if (ls[i].setfib != -1) {
             if (setsockopt(ls[i].fd, SOL_SOCKET, SO_SETFIB,
--- a/src/core/ngx_connection.h	Wed Nov 30 00:00:00 2011 +0400
+++ b/src/core/ngx_connection.h	Mon Dec 12 00:00:00 2011 +0400
@@ -27,6 +27,11 @@
     int                 backlog;
     int                 rcvbuf;
     int                 sndbuf;
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+    int                 keepidle;
+    int                 keepintvl;
+    int                 keepcnt;
+#endif
 
     /* handler of accepted connection */
     ngx_connection_handler_pt   handler;
@@ -60,6 +65,7 @@
 #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
     unsigned            ipv6only:2;
 #endif
+    unsigned            keepalive:2;
 
 #if (NGX_HAVE_DEFERRED_ACCEPT)
     unsigned            deferred_accept:1;
--- a/src/core/ngx_regex.c	Wed Nov 30 00:00:00 2011 +0400
+++ b/src/core/ngx_regex.c	Mon Dec 12 00:00:00 2011 +0400
@@ -137,23 +137,6 @@
 
 
 ngx_int_t
-ngx_regex_capture_count(ngx_regex_t *re)
-{
-    int  rc, n;
-
-    n = 0;
-
-    rc = pcre_fullinfo(re, NULL, PCRE_INFO_CAPTURECOUNT, &n);
-
-    if (rc < 0) {
-        return (ngx_int_t) rc;
-    }
-
-    return (ngx_int_t) n;
-}
-
-
-ngx_int_t
 ngx_regex_exec_array(ngx_array_t *a, ngx_str_t *s, ngx_log_t *log)
 {
     ngx_int_t         n;
--- a/src/http/modules/ngx_http_fastcgi_module.c	Wed Nov 30 00:00:00 2011 +0400
+++ b/src/http/modules/ngx_http_fastcgi_module.c	Mon Dec 12 00:00:00 2011 +0400
@@ -411,8 +411,8 @@
       &ngx_http_fastcgi_next_upstream_masks },
 
     { ngx_string("fastcgi_param"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
-      ngx_conf_set_keyval_slot,
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE23,
+      ngx_http_upstream_param_set_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
       offsetof(ngx_http_fastcgi_loc_conf_t, params_source),
       NULL },
@@ -708,7 +708,7 @@
     u_char                        ch, *pos, *lowcase_key;
     size_t                        size, len, key_len, val_len, padding,
                                   allocated;
-    ngx_uint_t                    i, n, next, hash, header_params;
+    ngx_uint_t                    i, n, next, hash, skip_empty, header_params;
     ngx_buf_t                    *b;
     ngx_chain_t                  *cl, *body;
     ngx_list_part_t              *part;
@@ -739,11 +739,18 @@
             lcode = *(ngx_http_script_len_code_pt *) le.ip;
             key_len = lcode(&le);
 
+            lcode = *(ngx_http_script_len_code_pt *) le.ip;
+            skip_empty = lcode(&le);
+
             for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
                 lcode = *(ngx_http_script_len_code_pt *) le.ip;
             }
             le.ip += sizeof(uintptr_t);
 
+            if (skip_empty && val_len == 0) {
+                continue;
+            }
+
             len += 1 + key_len + ((val_len > 127) ? 4 : 1) + val_len;
         }
     }
@@ -893,11 +900,28 @@
             lcode = *(ngx_http_script_len_code_pt *) le.ip;
             key_len = (u_char) lcode(&le);
 
+            lcode = *(ngx_http_script_len_code_pt *) le.ip;
+            skip_empty = lcode(&le);
+
             for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
                 lcode = *(ngx_http_script_len_code_pt *) le.ip;
             }
             le.ip += sizeof(uintptr_t);
 
+            if (skip_empty && val_len == 0) {
+                e.skip = 1;
+
+                while (*(uintptr_t *) e.ip) {
+                    code = *(ngx_http_script_code_pt *) e.ip;
+                    code((ngx_http_script_engine_t *) &e);
+                }
+                e.ip += sizeof(uintptr_t);
+
+                e.skip = 0;
+
+                continue;
+            }
+
             *e.pos++ = (u_char) key_len;
 
             if (val_len > 127) {
@@ -2370,9 +2394,9 @@
 #if (NGX_HTTP_CACHE)
     ngx_array_t                   params_merged;
 #endif
-    ngx_keyval_t                 *src;
     ngx_hash_key_t               *hk;
     ngx_hash_init_t               hash;
+    ngx_http_upstream_param_t    *src;
     ngx_http_script_compile_t     sc;
     ngx_http_script_copy_code_t  *copy;
 
@@ -2433,9 +2457,11 @@
 #if (NGX_HTTP_CACHE)
 
     if (conf->upstream.cache) {
-        ngx_keyval_t  *h, *s;
-
-        if (ngx_array_init(&params_merged, cf->temp_pool, 4, sizeof(ngx_keyval_t))
+        ngx_keyval_t               *h;
+        ngx_http_upstream_param_t  *s;
+
+        if (ngx_array_init(&params_merged, cf->temp_pool, 4,
+                           sizeof(ngx_http_upstream_param_t))
             != NGX_OK)
         {
             return NGX_ERROR;
@@ -2469,7 +2495,9 @@
                 return NGX_ERROR;
             }
 
-            *s = *h;
+            s->key = h->key;
+            s->value = h->value;
+            s->skip_empty = 0;
 
         next:
 
@@ -2511,6 +2539,15 @@
         copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
         copy->len = src[i].key.len;
 
+        copy = ngx_array_push_n(conf->params_len,
+                                sizeof(ngx_http_script_copy_code_t));
+        if (copy == NULL) {
+            return NGX_ERROR;
+        }
+
+        copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
+        copy->len = src[i].skip_empty;
+
 
         size = (sizeof(ngx_http_script_copy_code_t)
                 + src[i].key.len + sizeof(uintptr_t) - 1)
--- a/src/http/modules/ngx_http_log_module.c	Wed Nov 30 00:00:00 2011 +0400
+++ b/src/http/modules/ngx_http_log_module.c	Mon Dec 12 00:00:00 2011 +0400
@@ -971,7 +971,7 @@
 
         if (buf == NGX_ERROR) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                               "invalid parameter \"%V\"", &value[3]);
+                               "invalid buffer value \"%V\"", &name);
             return NGX_CONF_ERROR;
         }
 
@@ -1004,6 +1004,12 @@
     ngx_uint_t           i;
     ngx_http_log_fmt_t  *fmt;
 
+    if (cf->cmd_type != NGX_HTTP_MAIN_CONF) {
+        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                           "the \"log_format\" directive may be used "
+                           "only on \"http\" level");
+    }
+
     value = cf->args->elts;
 
     fmt = lmcf->formats.elts;
--- a/src/http/modules/ngx_http_proxy_module.c	Wed Nov 30 00:00:00 2011 +0400
+++ b/src/http/modules/ngx_http_proxy_module.c	Mon Dec 12 00:00:00 2011 +0400
@@ -16,18 +16,15 @@
 
 struct ngx_http_proxy_redirect_s {
     ngx_http_proxy_redirect_pt     handler;
-    ngx_str_t                      redirect;
 
     union {
-        ngx_str_t                  text;
-
-        struct {
-            void                  *lengths;
-            void                  *values;
-        } vars;
-
-        void                      *regex;
-    } replacement;
+        ngx_http_complex_value_t   complex;
+#if (NGX_PCRE)
+        ngx_http_regex_t          *regex;
+#endif
+    } redirect;
+
+    ngx_http_complex_value_t       replacement;
 };
 
 
@@ -2289,20 +2286,29 @@
 
 
 static ngx_int_t
-ngx_http_proxy_rewrite_redirect_text(ngx_http_request_t *r, ngx_table_elt_t *h,
-    size_t prefix, ngx_http_proxy_redirect_t *pr)
+ngx_http_proxy_rewrite_redirect_complex(ngx_http_request_t *r,
+    ngx_table_elt_t *h, size_t prefix, ngx_http_proxy_redirect_t *pr)
 {
-    size_t   len;
-    u_char  *data, *p;
-
-    if (pr->redirect.len > h->value.len - prefix
-        || ngx_rstrncmp(h->value.data + prefix, pr->redirect.data,
-                        pr->redirect.len) != 0)
+    size_t      len;
+    u_char     *data, *p;
+    ngx_str_t   redirect, replacement;
+
+    if (ngx_http_complex_value(r, &pr->redirect.complex, &redirect) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    if (redirect.len > h->value.len - prefix
+        || ngx_rstrncmp(h->value.data + prefix, redirect.data,
+                        redirect.len) != 0)
     {
         return NGX_DECLINED;
     }
 
-    len = pr->replacement.text.len + h->value.len - pr->redirect.len;
+    if (ngx_http_complex_value(r, &pr->replacement, &replacement) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    len = replacement.len + h->value.len - redirect.len;
 
     data = ngx_pnalloc(r->pool, len);
     if (data == NULL) {
@@ -2311,12 +2317,12 @@
 
     p = ngx_copy(data, h->value.data, prefix);
 
-    if (pr->replacement.text.len) {
-        p = ngx_copy(p, pr->replacement.text.data, pr->replacement.text.len);
+    if (replacement.len) {
+        p = ngx_copy(p, replacement.data, replacement.len);
     }
 
-    ngx_memcpy(p, h->value.data + prefix + pr->redirect.len,
-               h->value.len - pr->redirect.len - prefix);
+    ngx_memcpy(p, h->value.data + prefix + redirect.len,
+               h->value.len - redirect.len - prefix);
 
     h->value.len = len;
     h->value.data = data;
@@ -2325,59 +2331,32 @@
 }
 
 
+#if (NGX_PCRE)
+
 static ngx_int_t
-ngx_http_proxy_rewrite_redirect_vars(ngx_http_request_t *r, ngx_table_elt_t *h,
+ngx_http_proxy_rewrite_redirect_regex(ngx_http_request_t *r, ngx_table_elt_t *h,
     size_t prefix, ngx_http_proxy_redirect_t *pr)
 {
-    size_t                        len;
-    u_char                       *data, *p;
-    ngx_http_script_code_pt       code;
-    ngx_http_script_engine_t      e;
-    ngx_http_script_len_code_pt   lcode;
-
-    if (pr->redirect.len > h->value.len - prefix
-        || ngx_rstrncmp(h->value.data + prefix, pr->redirect.data,
-                        pr->redirect.len) != 0)
-    {
+    ngx_str_t   redirect, replacement;
+
+    redirect.len = h->value.len - prefix;
+    redirect.data = h->value.data + prefix;
+
+    if (ngx_http_regex_exec(r, pr->redirect.regex, &redirect) != NGX_OK) {
         return NGX_DECLINED;
     }
 
-    ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
-
-    e.ip = pr->replacement.vars.lengths;
-    e.request = r;
-
-    len = h->value.len - pr->redirect.len;
-
-    while (*(uintptr_t *) e.ip) {
-        lcode = *(ngx_http_script_len_code_pt *) e.ip;
-        len += lcode(&e);
-    }
-
-    data = ngx_pnalloc(r->pool, len);
-    if (data == NULL) {
+    if (ngx_http_complex_value(r, &pr->replacement, &replacement) != NGX_OK) {
         return NGX_ERROR;
     }
 
-    p = ngx_copy(data, h->value.data, prefix);
-
-    e.ip = pr->replacement.vars.values;
-    e.pos = p;
-
-    while (*(uintptr_t *) e.ip) {
-        code = *(ngx_http_script_code_pt *) e.ip;
-        code(&e);
-    }
-
-    ngx_memcpy(e.pos, h->value.data + prefix + pr->redirect.len,
-               h->value.len - pr->redirect.len - prefix);
-
-    h->value.len = len;
-    h->value.data = data;
+    h->value = replacement;
 
     return NGX_OK;
 }
 
+#endif
+
 
 static ngx_int_t
 ngx_http_proxy_add_variables(ngx_conf_t *cf)
@@ -2749,26 +2728,32 @@
                 return NGX_CONF_ERROR;
             }
 
-            pr->handler = ngx_http_proxy_rewrite_redirect_text;
+            ngx_memzero(&pr->redirect.complex,
+                        sizeof(ngx_http_complex_value_t));
+
+            ngx_memzero(&pr->replacement, sizeof(ngx_http_complex_value_t));
+
+            pr->handler = ngx_http_proxy_rewrite_redirect_complex;
 
             if (conf->vars.uri.len) {
-                pr->redirect = conf->url;
-                pr->replacement.text = conf->location;
+                pr->redirect.complex.value = conf->url;
+                pr->replacement.value = conf->location;
 
             } else {
-                pr->redirect.len = conf->url.len + sizeof("/") - 1;
-
-                p = ngx_pnalloc(cf->pool, pr->redirect.len);
+                pr->redirect.complex.value.len = conf->url.len
+                                                 + sizeof("/") - 1;
+
+                p = ngx_pnalloc(cf->pool, pr->redirect.complex.value.len);
                 if (p == NULL) {
                     return NGX_CONF_ERROR;
                 }
 
-                pr->redirect.data = p;
+                pr->redirect.complex.value.data = p;
 
                 p = ngx_cpymem(p, conf->url.data, conf->url.len);
                 *p = '/';
 
-                ngx_str_set(&pr->replacement.text, "/");
+                ngx_str_set(&pr->replacement.value, "/");
             }
         }
     }
@@ -3256,11 +3241,10 @@
 {
     ngx_http_proxy_loc_conf_t *plcf = conf;
 
-    u_char                     *p;
-    ngx_str_t                  *value;
-    ngx_array_t                *vars_lengths, *vars_values;
-    ngx_http_script_compile_t   sc;
-    ngx_http_proxy_redirect_t  *pr;
+    u_char                            *p;
+    ngx_str_t                         *value;
+    ngx_http_proxy_redirect_t         *pr;
+    ngx_http_compile_complex_value_t   ccv;
 
     if (plcf->redirect == 0) {
         return NGX_CONF_OK;
@@ -3318,60 +3302,96 @@
             return NGX_CONF_ERROR;
         }
 
-        pr->handler = ngx_http_proxy_rewrite_redirect_text;
+        pr->handler = ngx_http_proxy_rewrite_redirect_complex;
+
+        ngx_memzero(&pr->redirect.complex, sizeof(ngx_http_complex_value_t));
+
+        ngx_memzero(&pr->replacement, sizeof(ngx_http_complex_value_t));
 
         if (plcf->vars.uri.len) {
-            pr->redirect = plcf->url;
-            pr->replacement.text = plcf->location;
+            pr->redirect.complex.value = plcf->url;
+            pr->replacement.value = plcf->location;
 
         } else {
-            pr->redirect.len = plcf->url.len + sizeof("/") - 1;
-
-            p = ngx_pnalloc(cf->pool, pr->redirect.len);
+            pr->redirect.complex.value.len = plcf->url.len + sizeof("/") - 1;
+
+            p = ngx_pnalloc(cf->pool, pr->redirect.complex.value.len);
             if (p == NULL) {
                 return NGX_CONF_ERROR;
             }
 
-            pr->redirect.data = p;
+            pr->redirect.complex.value.data = p;
 
             p = ngx_cpymem(p, plcf->url.data, plcf->url.len);
             *p = '/';
 
-            ngx_str_set(&pr->replacement.text, "/");
+            ngx_str_set(&pr->replacement.value, "/");
         }
 
         return NGX_CONF_OK;
     }
 
-    if (ngx_http_script_variables_count(&value[2]) == 0) {
-        pr->handler = ngx_http_proxy_rewrite_redirect_text;
-        pr->redirect = value[1];
-        pr->replacement.text = value[2];
-
-        return NGX_CONF_OK;
+
+    if (value[1].data[0] == '~') {
+#if (NGX_PCRE)
+        u_char               errstr[NGX_MAX_CONF_ERRSTR];
+        ngx_regex_compile_t  rc;
+
+        value[1].len--;
+        value[1].data++;
+
+        ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
+
+        if (value[1].data[0] == '*') {
+            value[1].len--;
+            value[1].data++;
+            rc.options = NGX_REGEX_CASELESS;
+        }
+
+        rc.pattern = value[1];
+        rc.err.len = NGX_MAX_CONF_ERRSTR;
+        rc.err.data = errstr;
+
+        pr->redirect.regex = ngx_http_regex_compile(cf, &rc);
+        if (pr->redirect.regex == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        pr->handler = ngx_http_proxy_rewrite_redirect_regex;
+
+#else
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "using regex \"%V\" requires PCRE library",
+                           &value[1]);
+
+        return NGX_CONF_ERROR;
+#endif
+    } else {
+
+        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+        ccv.cf = cf;
+        ccv.value = &value[1];
+        ccv.complex_value = &pr->redirect.complex;
+
+        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+            return NGX_CONF_ERROR;
+        }
+
+        pr->handler = ngx_http_proxy_rewrite_redirect_complex;
     }
 
-    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
-
-    vars_lengths = NULL;
-    vars_values = NULL;
-
-    sc.cf = cf;
-    sc.source = &value[2];
-    sc.lengths = &vars_lengths;
-    sc.values = &vars_values;
-    sc.complete_lengths = 1;
-    sc.complete_values = 1;
-
-    if (ngx_http_script_compile(&sc) != NGX_OK) {
+
+    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+    ccv.cf = cf;
+    ccv.value = &value[2];
+    ccv.complex_value = &pr->replacement;
+
+    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
 
-    pr->handler = ngx_http_proxy_rewrite_redirect_vars;
-    pr->redirect = value[1];
-    pr->replacement.vars.lengths = vars_lengths->elts;
-    pr->replacement.vars.values = vars_values->elts;
-
     return NGX_CONF_OK;
 }
 
--- a/src/http/modules/ngx_http_scgi_module.c	Wed Nov 30 00:00:00 2011 +0400
+++ b/src/http/modules/ngx_http_scgi_module.c	Mon Dec 12 00:00:00 2011 +0400
@@ -278,8 +278,8 @@
       &ngx_http_scgi_next_upstream_masks },
 
     { ngx_string("scgi_param"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
-      ngx_conf_set_keyval_slot,
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE23,
+      ngx_http_upstream_param_set_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
       offsetof(ngx_http_scgi_loc_conf_t, params_source),
       NULL },
@@ -519,10 +519,10 @@
 ngx_http_scgi_create_request(ngx_http_request_t *r)
 {
     u_char                        ch, *key, *val, *lowcase_key;
-    size_t                        len, allocated;
+    size_t                        len, key_len, val_len, allocated;
     ngx_buf_t                    *b;
     ngx_str_t                    *content_length;
-    ngx_uint_t                    i, n, hash, header_params;
+    ngx_uint_t                    i, n, hash, skip_empty, header_params;
     ngx_chain_t                  *cl, *body;
     ngx_list_part_t              *part;
     ngx_table_elt_t              *header, **ignored;
@@ -554,15 +554,21 @@
         while (*(uintptr_t *) le.ip) {
 
             lcode = *(ngx_http_script_len_code_pt *) le.ip;
-            len += lcode(&le);
+            key_len = lcode(&le);
 
-            while (*(uintptr_t *) le.ip) {
+            lcode = *(ngx_http_script_len_code_pt *) le.ip;
+            skip_empty = lcode(&le);
+
+            for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
                 lcode = *(ngx_http_script_len_code_pt *) le.ip;
-                len += lcode(&le);
             }
-            len++;
+            le.ip += sizeof(uintptr_t);
 
-            le.ip += sizeof(uintptr_t);
+            if (skip_empty && val_len == 0) {
+                continue;
+            }
+
+            len += key_len + val_len + 1;
         }
     }
 
@@ -665,7 +671,34 @@
         e.request = r;
         e.flushed = 1;
 
-        while (*(uintptr_t *) e.ip) {
+        le.ip = scf->params_len->elts;
+
+        while (*(uintptr_t *) le.ip) {
+
+            lcode = *(ngx_http_script_len_code_pt *) le.ip;
+            lcode(&le); /* key length */
+
+            lcode = *(ngx_http_script_len_code_pt *) le.ip;
+            skip_empty = lcode(&le);
+
+            for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
+                lcode = *(ngx_http_script_len_code_pt *) le.ip;
+            }
+            le.ip += sizeof(uintptr_t);
+
+            if (skip_empty && val_len == 0) {
+                e.skip = 1;
+
+                while (*(uintptr_t *) e.ip) {
+                    code = *(ngx_http_script_code_pt *) e.ip;
+                    code((ngx_http_script_engine_t *) &e);
+                }
+                e.ip += sizeof(uintptr_t);
+
+                e.skip = 0;
+
+                continue;
+            }
 
 #if (NGX_DEBUG)
             key = e.pos;
@@ -1323,9 +1356,9 @@
 #if (NGX_HTTP_CACHE)
     ngx_array_t                   params_merged;
 #endif
-    ngx_keyval_t                 *src;
     ngx_hash_key_t               *hk;
     ngx_hash_init_t               hash;
+    ngx_http_upstream_param_t    *src;
     ngx_http_script_compile_t     sc;
     ngx_http_script_copy_code_t  *copy;
 
@@ -1386,9 +1419,11 @@
 #if (NGX_HTTP_CACHE)
 
     if (conf->upstream.cache) {
-        ngx_keyval_t  *h, *s;
+        ngx_keyval_t               *h;
+        ngx_http_upstream_param_t  *s;
 
-        if (ngx_array_init(&params_merged, cf->temp_pool, 4, sizeof(ngx_keyval_t))
+        if (ngx_array_init(&params_merged, cf->temp_pool, 4,
+                           sizeof(ngx_http_upstream_param_t))
             != NGX_OK)
         {
             return NGX_ERROR;
@@ -1422,7 +1457,9 @@
                 return NGX_ERROR;
             }
 
-            *s = *h;
+            s->key = h->key;
+            s->value = h->value;
+            s->skip_empty = 0;
 
         next:
 
@@ -1464,6 +1501,15 @@
         copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
         copy->len = src[i].key.len + 1;
 
+        copy = ngx_array_push_n(conf->params_len,
+                                sizeof(ngx_http_script_copy_code_t));
+        if (copy == NULL) {
+            return NGX_ERROR;
+        }
+
+        copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
+        copy->len = src[i].skip_empty;
+
 
         size = (sizeof(ngx_http_script_copy_code_t)
                 + src[i].key.len + 1 + sizeof(uintptr_t) - 1)
--- a/src/http/modules/ngx_http_uwsgi_module.c	Wed Nov 30 00:00:00 2011 +0400
+++ b/src/http/modules/ngx_http_uwsgi_module.c	Mon Dec 12 00:00:00 2011 +0400
@@ -305,8 +305,8 @@
       &ngx_http_uwsgi_next_upstream_masks },
 
     { ngx_string("uwsgi_param"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
-      ngx_conf_set_keyval_slot,
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE23,
+      ngx_http_upstream_param_set_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
       offsetof(ngx_http_uwsgi_loc_conf_t, params_source),
       NULL },
@@ -553,7 +553,7 @@
 {
     u_char                        ch, *lowcase_key;
     size_t                        key_len, val_len, len, allocated;
-    ngx_uint_t                    i, n, hash, header_params;
+    ngx_uint_t                    i, n, hash, skip_empty, header_params;
     ngx_buf_t                    *b;
     ngx_chain_t                  *cl, *body;
     ngx_list_part_t              *part;
@@ -583,11 +583,18 @@
             lcode = *(ngx_http_script_len_code_pt *) le.ip;
             key_len = lcode(&le);
 
+            lcode = *(ngx_http_script_len_code_pt *) le.ip;
+            skip_empty = lcode(&le);
+
             for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode (&le)) {
                 lcode = *(ngx_http_script_len_code_pt *) le.ip;
             }
             le.ip += sizeof(uintptr_t);
 
+            if (skip_empty && val_len == 0) {
+                continue;
+            }
+
             len += 2 + key_len + 2 + val_len;
         }
     }
@@ -706,11 +713,28 @@
             lcode = *(ngx_http_script_len_code_pt *) le.ip;
             key_len = (u_char) lcode (&le);
 
+            lcode = *(ngx_http_script_len_code_pt *) le.ip;
+            skip_empty = lcode(&le);
+
             for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
                 lcode = *(ngx_http_script_len_code_pt *) le.ip;
             }
             le.ip += sizeof(uintptr_t);
 
+            if (skip_empty && val_len == 0) {
+                e.skip = 1;
+
+                while (*(uintptr_t *) e.ip) {
+                    code = *(ngx_http_script_code_pt *) e.ip;
+                    code((ngx_http_script_engine_t *) &e);
+                }
+                e.ip += sizeof(uintptr_t);
+
+                e.skip = 0;
+
+                continue;
+            }
+
             *e.pos++ = (u_char) (key_len & 0xff);
             *e.pos++ = (u_char) ((key_len >> 8) & 0xff);
 
@@ -1379,9 +1403,9 @@
 #if (NGX_HTTP_CACHE)
     ngx_array_t                   params_merged;
 #endif
-    ngx_keyval_t                 *src;
     ngx_hash_key_t               *hk;
     ngx_hash_init_t               hash;
+    ngx_http_upstream_param_t    *src;
     ngx_http_script_compile_t     sc;
     ngx_http_script_copy_code_t  *copy;
 
@@ -1442,9 +1466,11 @@
 #if (NGX_HTTP_CACHE)
 
     if (conf->upstream.cache) {
-        ngx_keyval_t  *h, *s;
+        ngx_keyval_t               *h;
+        ngx_http_upstream_param_t  *s;
 
-        if (ngx_array_init(&params_merged, cf->temp_pool, 4, sizeof(ngx_keyval_t))
+        if (ngx_array_init(&params_merged, cf->temp_pool, 4,
+                           sizeof(ngx_http_upstream_param_t))
             != NGX_OK)
         {
             return NGX_ERROR;
@@ -1478,7 +1504,9 @@
                 return NGX_ERROR;
             }
 
-            *s = *h;
+            s->key = h->key;
+            s->value = h->value;
+            s->skip_empty = 0;
 
         next:
 
@@ -1520,6 +1548,15 @@
         copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
         copy->len = src[i].key.len;
 
+        copy = ngx_array_push_n(conf->params_len,
+                                sizeof(ngx_http_script_copy_code_t));
+        if (copy == NULL) {
+            return NGX_ERROR;
+        }
+
+        copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
+        copy->len = src[i].skip_empty;
+
 
         size = (sizeof(ngx_http_script_copy_code_t)
                 + src[i].key.len + sizeof(uintptr_t) - 1)
--- a/src/http/modules/perl/nginx.pm	Wed Nov 30 00:00:00 2011 +0400
+++ b/src/http/modules/perl/nginx.pm	Mon Dec 12 00:00:00 2011 +0400
@@ -48,7 +48,7 @@
     HTTP_INSUFFICIENT_STORAGE
 );
 
-our $VERSION = '1.1.10';
+our $VERSION = '1.1.11';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/ngx_http.c	Wed Nov 30 00:00:00 2011 +0400
+++ b/src/http/ngx_http.c	Mon Dec 12 00:00:00 2011 +0400
@@ -1762,6 +1762,13 @@
     ls->rcvbuf = addr->opt.rcvbuf;
     ls->sndbuf = addr->opt.sndbuf;
 
+    ls->keepalive = addr->opt.so_keepalive;
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+    ls->keepidle = addr->opt.tcp_keepidle;
+    ls->keepintvl = addr->opt.tcp_keepintvl;
+    ls->keepcnt = addr->opt.tcp_keepcnt;
+#endif
+
 #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
     ls->accept_filter = addr->opt.accept_filter;
 #endif
--- a/src/http/ngx_http_core_module.c	Wed Nov 30 00:00:00 2011 +0400
+++ b/src/http/ngx_http_core_module.c	Mon Dec 12 00:00:00 2011 +0400
@@ -1289,6 +1289,7 @@
 
         ngx_memzero(&of, sizeof(ngx_open_file_info_t));
 
+        of.read_ahead = clcf->read_ahead;
         of.directio = clcf->directio;
         of.valid = clcf->open_file_cache_valid;
         of.min_uses = clcf->open_file_cache_min_uses;
@@ -3815,6 +3816,97 @@
 #endif
         }
 
+        if (ngx_strncmp(value[n].data, "so_keepalive=", 13) == 0) {
+
+            if (ngx_strcmp(&value[n].data[13], "on") == 0) {
+                lsopt.so_keepalive = 1;
+
+            } else if (ngx_strcmp(&value[n].data[13], "off") == 0) {
+                lsopt.so_keepalive = 2;
+
+            } else {
+
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+                u_char     *p, *end;
+                ngx_str_t   s;
+
+                end = value[n].data + value[n].len;
+                s.data = value[n].data + 13;
+
+                p = ngx_strlchr(s.data, end, ':');
+                if (p == NULL) {
+                    p = end;
+                }
+
+                if (p > s.data) {
+                    s.len = p - s.data;
+
+                    lsopt.tcp_keepidle = ngx_parse_time(&s, 1);
+                    if (lsopt.tcp_keepidle == NGX_ERROR) {
+                        goto invalid_so_keepalive;
+                    }
+                }
+
+                s.data = (p < end) ? (p + 1) : end;
+
+                p = ngx_strlchr(s.data, end, ':');
+                if (p == NULL) {
+                    p = end;
+                }
+
+                if (p > s.data) {
+                    s.len = p - s.data;
+
+                    lsopt.tcp_keepintvl = ngx_parse_time(&s, 1);
+                    if (lsopt.tcp_keepintvl == NGX_ERROR) {
+                        goto invalid_so_keepalive;
+                    }
+                }
+
+                s.data = (p < end) ? (p + 1) : end;
+
+                if (s.data < end) {
+                    s.len = end - s.data;
+
+                    lsopt.tcp_keepcnt = ngx_atoi(s.data, s.len);
+                    if (lsopt.tcp_keepcnt == NGX_ERROR) {
+                        goto invalid_so_keepalive;
+                    }
+                }
+
+                if (lsopt.tcp_keepidle == 0 && lsopt.tcp_keepintvl == 0 
+                    && lsopt.tcp_keepcnt == 0)
+                {
+                    goto invalid_so_keepalive;
+                }
+
+                lsopt.so_keepalive = 1;
+
+#else
+
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "the \"so_keepalive\" parameter accepts "
+                                   "only \"on\" or \"off\" on this platform");
+                return NGX_CONF_ERROR;
+
+#endif
+            }
+
+            lsopt.set = 1;
+            lsopt.bind = 1;
+
+            continue;
+
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+        invalid_so_keepalive:
+
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "invalid so_keepalive value: \"%s\"",
+                               &value[n].data[13]);
+            return NGX_CONF_ERROR;
+#endif
+        }
+
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "invalid parameter \"%V\"", &value[n]);
         return NGX_CONF_ERROR;
--- a/src/http/ngx_http_core_module.h	Wed Nov 30 00:00:00 2011 +0400
+++ b/src/http/ngx_http_core_module.h	Mon Dec 12 00:00:00 2011 +0400
@@ -77,6 +77,7 @@
 #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
     unsigned                   ipv6only:2;
 #endif
+    unsigned                   so_keepalive:2;
 
     int                        backlog;
     int                        rcvbuf;
@@ -84,6 +85,11 @@
 #if (NGX_HAVE_SETFIB)
     int                        setfib;
 #endif
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+    int                        tcp_keepidle;
+    int                        tcp_keepintvl;
+    int                        tcp_keepcnt;
+#endif
 
 #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
     char                      *accept_filter;
--- a/src/http/ngx_http_file_cache.c	Wed Nov 30 00:00:00 2011 +0400
+++ b/src/http/ngx_http_file_cache.c	Mon Dec 12 00:00:00 2011 +0400
@@ -386,6 +386,13 @@
         return NGX_DECLINED;
     }
 
+    if (h->body_start > c->body_start) {
+        ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
+                      "cache file \"%s\" has too long header",
+                      c->file.name.data);
+        return NGX_DECLINED;
+    }
+
     c->buf->last += n;
 
     c->valid_sec = h->valid_sec;
@@ -1106,12 +1113,12 @@
         /*
          * abnormally exited workers may leave locked cache entries,
          * and although it may be safe to remove them completely,
-         * we prefer to remove them from inactive queue and rbtree
-         * only, and to allow other leaks
+         * we prefer to just move them to the top of the inactive queue
          */
 
         ngx_queue_remove(q);
-        ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node);
+        fcn->expire = ngx_time() + cache->inactive;
+        ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);
 
         ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
                       "ignore long locked inactive cache entry %*s, count:%d",
@@ -1752,69 +1759,3 @@
 
     return NGX_CONF_OK;
 }
-
-
-ngx_int_t
-ngx_http_cache(ngx_http_request_t *r, ngx_array_t *no_cache)
-{
-    ngx_str_t                  val;
-    ngx_uint_t                 i;
-    ngx_http_complex_value_t  *cv;
-
-    cv = no_cache->elts;
-
-    for (i = 0; i < no_cache->nelts; i++) {
-        if (ngx_http_complex_value(r, &cv[i], &val) != NGX_OK) {
-            return NGX_ERROR;
-        }
-
-        if (val.len && val.data[0] != '0') {
-            return NGX_DECLINED;
-        }
-    }
-
-    return NGX_OK;
-}
-
-
-char *
-ngx_http_no_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
-{
-    char  *p = conf;
-
-    ngx_str_t                          *value;
-    ngx_uint_t                          i;
-    ngx_array_t                       **a;
-    ngx_http_complex_value_t           *cv;
-    ngx_http_compile_complex_value_t    ccv;
-
-    a = (ngx_array_t **) (p + cmd->offset);
-
-    if (*a == NGX_CONF_UNSET_PTR) {
-        *a = ngx_array_create(cf->pool, 1, sizeof(ngx_http_complex_value_t));
-        if (*a == NULL) {
-            return NGX_CONF_ERROR;
-        }
-    }
-
-    value = cf->args->elts;
-
-    for (i = 1; i < cf->args->nelts; i++) {
-        cv = ngx_array_push(*a);
-        if (cv == NULL) {
-            return NGX_CONF_ERROR;
-        }
-
-        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
-
-        ccv.cf = cf;
-        ccv.value = &value[i];
-        ccv.complex_value = cv;
-
-        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
-            return NGX_CONF_ERROR;
-        }
-    }
-
-    return NGX_CONF_OK;
-}
--- a/src/http/ngx_http_header_filter_module.c	Wed Nov 30 00:00:00 2011 +0400
+++ b/src/http/ngx_http_header_filter_module.c	Mon Dec 12 00:00:00 2011 +0400
@@ -61,8 +61,8 @@
 
     /* ngx_null_string, */  /* "207 Multi-Status" */
 
-#define NGX_HTTP_LAST_LEVEL_200  207
-#define NGX_HTTP_LEVEL_200       (NGX_HTTP_LAST_LEVEL_200 - 200)
+#define NGX_HTTP_LAST_2XX  207
+#define NGX_HTTP_OFF_3XX   (NGX_HTTP_LAST_2XX - 200)
 
     /* ngx_null_string, */  /* "300 Multiple Choices" */
 
@@ -75,8 +75,8 @@
     /* ngx_null_string, */  /* "306 unused" */
     /* ngx_null_string, */  /* "307 Temporary Redirect" */
 
-#define NGX_HTTP_LAST_LEVEL_300  305
-#define NGX_HTTP_LEVEL_300       (NGX_HTTP_LAST_LEVEL_300 - 301)
+#define NGX_HTTP_LAST_3XX  305
+#define NGX_HTTP_OFF_4XX   (NGX_HTTP_LAST_3XX - 301 + NGX_HTTP_OFF_3XX)
 
     ngx_string("400 Bad Request"),
     ngx_string("401 Unauthorized"),
@@ -108,8 +108,8 @@
     /* ngx_null_string, */  /* "423 Locked" */
     /* ngx_null_string, */  /* "424 Failed Dependency" */
 
-#define NGX_HTTP_LAST_LEVEL_400  417
-#define NGX_HTTP_LEVEL_400       (NGX_HTTP_LAST_LEVEL_400 - 400)
+#define NGX_HTTP_LAST_4XX  417
+#define NGX_HTTP_OFF_5XX   (NGX_HTTP_LAST_4XX - 400 + NGX_HTTP_OFF_4XX)
 
     ngx_string("500 Internal Server Error"),
     ngx_string("501 Method Not Implemented"),
@@ -124,7 +124,7 @@
     /* ngx_null_string, */  /* "509 unused" */
     /* ngx_null_string, */  /* "510 Not Extended" */
 
-#define NGX_HTTP_LAST_LEVEL_500  508
+#define NGX_HTTP_LAST_5XX  508
 
 };
 
@@ -216,7 +216,7 @@
         status = r->headers_out.status;
 
         if (status >= NGX_HTTP_OK
-            && status < NGX_HTTP_LAST_LEVEL_200)
+            && status < NGX_HTTP_LAST_2XX)
         {
             /* 2XX */
 
@@ -234,7 +234,7 @@
             len += ngx_http_status_lines[status].len;
 
         } else if (status >= NGX_HTTP_MOVED_PERMANENTLY
-                   && status < NGX_HTTP_LAST_LEVEL_300)
+                   && status < NGX_HTTP_LAST_3XX)
         {
             /* 3XX */
 
@@ -242,29 +242,26 @@
                 r->header_only = 1;
             }
 
-            status = status - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_LEVEL_200;
+            status = status - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_OFF_3XX;
             status_line = &ngx_http_status_lines[status];
             len += ngx_http_status_lines[status].len;
 
         } else if (status >= NGX_HTTP_BAD_REQUEST
-                   && status < NGX_HTTP_LAST_LEVEL_400)
+                   && status < NGX_HTTP_LAST_4XX)
         {
             /* 4XX */
             status = status - NGX_HTTP_BAD_REQUEST
-                            + NGX_HTTP_LEVEL_200
-                            + NGX_HTTP_LEVEL_300;
+                            + NGX_HTTP_OFF_4XX;
 
             status_line = &ngx_http_status_lines[status];
             len += ngx_http_status_lines[status].len;
 
         } else if (status >= NGX_HTTP_INTERNAL_SERVER_ERROR
-                   && status < NGX_HTTP_LAST_LEVEL_500)
+                   && status < NGX_HTTP_LAST_5XX)
         {
             /* 5XX */
             status = status - NGX_HTTP_INTERNAL_SERVER_ERROR
-                            + NGX_HTTP_LEVEL_200
-                            + NGX_HTTP_LEVEL_300
-                            + NGX_HTTP_LEVEL_400;
+                            + NGX_HTTP_OFF_5XX;
 
             status_line = &ngx_http_status_lines[status];
             len += ngx_http_status_lines[status].len;
--- a/src/http/ngx_http_upstream.c	Wed Nov 30 00:00:00 2011 +0400
+++ b/src/http/ngx_http_upstream.c	Mon Dec 12 00:00:00 2011 +0400
@@ -4444,6 +4444,50 @@
 }
 
 
+char *
+ngx_http_upstream_param_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf)
+{
+    char  *p = conf;
+
+    ngx_str_t                   *value;
+    ngx_array_t                **a;
+    ngx_http_upstream_param_t   *param;
+
+    a = (ngx_array_t **) (p + cmd->offset);
+
+    if (*a == NULL) {
+        *a = ngx_array_create(cf->pool, 4, sizeof(ngx_http_upstream_param_t));
+        if (*a == NULL) {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    param = ngx_array_push(*a);
+    if (param == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    value = cf->args->elts;
+
+    param->key = value[1];
+    param->value = value[2];
+    param->skip_empty = 0;
+
+    if (cf->args->nelts == 4) {
+        if (ngx_strcmp(value[3].data, "if_not_empty") != 0) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "invalid parameter \"%V\"", &value[3]);
+            return NGX_CONF_ERROR;
+        }
+
+        param->skip_empty = 1;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
 ngx_int_t
 ngx_http_upstream_hide_headers_hash(ngx_conf_t *cf,
     ngx_http_upstream_conf_t *conf, ngx_http_upstream_conf_t *prev,
--- a/src/http/ngx_http_upstream.h	Wed Nov 30 00:00:00 2011 +0400
+++ b/src/http/ngx_http_upstream.h	Mon Dec 12 00:00:00 2011 +0400
@@ -328,6 +328,13 @@
 } ngx_http_upstream_next_t;
 
 
+typedef struct {
+    ngx_str_t   key;
+    ngx_str_t   value;
+    ngx_uint_t  skip_empty;
+} ngx_http_upstream_param_t;
+
+
 ngx_int_t ngx_http_upstream_header_variable(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
 
@@ -337,6 +344,8 @@
     ngx_url_t *u, ngx_uint_t flags);
 char *ngx_http_upstream_bind_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
+char *ngx_http_upstream_param_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
 ngx_int_t ngx_http_upstream_hide_headers_hash(ngx_conf_t *cf,
     ngx_http_upstream_conf_t *conf, ngx_http_upstream_conf_t *prev,
     ngx_str_t *default_hide_headers, ngx_hash_init_t *hash);
--- a/src/http/ngx_http_variables.c	Wed Nov 30 00:00:00 2011 +0400
+++ b/src/http/ngx_http_variables.c	Mon Dec 12 00:00:00 2011 +0400
@@ -48,6 +48,8 @@
     ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_variable_scheme(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_https(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_variable_is_args(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_variable_document_root(ngx_http_request_t *r,
@@ -157,6 +159,8 @@
 
     { ngx_string("scheme"), NULL, ngx_http_variable_scheme, 0, 0, 0 },
 
+    { ngx_string("https"), NULL, ngx_http_variable_https, 0, 0, 0 },
+
     { ngx_string("request_uri"), NULL, ngx_http_variable_request,
       offsetof(ngx_http_request_t, unparsed_uri), 0, 0 },
 
@@ -640,8 +644,8 @@
 ngx_http_variable_headers(ngx_http_request_t *r, ngx_http_variable_value_t *v,
     uintptr_t data)
 {
-    ssize_t            len;
-    u_char            *p;
+    size_t             len;
+    u_char            *p, *end;
     ngx_uint_t         i, n;
     ngx_array_t       *a;
     ngx_table_elt_t  **h;
@@ -649,18 +653,30 @@
     a = (ngx_array_t *) ((char *) r + data);
 
     n = a->nelts;
-
-    if (n == 0) {
+    h = a->elts;
+
+    len = 0;
+
+    for (i = 0; i < n; i++) {
+
+        if (h[i]->hash == 0) {
+            continue;
+        }
+
+        len += h[i]->value.len + sizeof("; ") - 1;
+    }
+
+    if (len == 0) {
         v->not_found = 1;
         return NGX_OK;
     }
 
+    len -= sizeof("; ") - 1;
+
     v->valid = 1;
     v->no_cacheable = 0;
     v->not_found = 0;
 
-    h = a->elts;
-
     if (n == 1) {
         v->len = (*h)->value.len;
         v->data = (*h)->value.data;
@@ -668,12 +684,6 @@
         return NGX_OK;
     }
 
-    len = - (ssize_t) (sizeof("; ") - 1);
-
-    for (i = 0; i < n; i++) {
-        len += h[i]->value.len + sizeof("; ") - 1;
-    }
-
     p = ngx_pnalloc(r->pool, len);
     if (p == NULL) {
         return NGX_ERROR;
@@ -682,10 +692,17 @@
     v->len = len;
     v->data = p;
 
+    end = p + len;
+
     for (i = 0; /* void */ ; i++) {
+
+        if (h[i]->hash == 0) {
+            continue;
+        }
+
         p = ngx_copy(p, h[i]->value.data, h[i]->value.len);
 
-        if (i == n - 1) {
+        if (p == end) {
             break;
         }
 
@@ -738,6 +755,10 @@
             i = 0;
         }
 
+        if (header[i].hash == 0) {
+            continue;
+        }
+
         for (n = 0; n + prefix < var->len && n < header[i].key.len; n++) {
             ch = header[i].key.data[n];
 
@@ -1091,6 +1112,30 @@
 
 
 static ngx_int_t
+ngx_http_variable_https(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+#if (NGX_HTTP_SSL)
+
+    if (r->connection->ssl) {
+        v->len = sizeof("on") - 1;
+        v->valid = 1;
+        v->no_cacheable = 0;
+        v->not_found = 0;
+        v->data = (u_char *) "on";
+
+        return NGX_OK;
+    }
+
+#endif
+
+    *v = ngx_http_variable_null_value;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_http_variable_is_args(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data)
 {
--- a/src/mail/ngx_mail.c	Wed Nov 30 00:00:00 2011 +0400
+++ b/src/mail/ngx_mail.c	Mon Dec 12 00:00:00 2011 +0400
@@ -308,6 +308,12 @@
     addr->ctx = listen->ctx;
     addr->bind = listen->bind;
     addr->wildcard = listen->wildcard;
+    addr->so_keepalive = listen->so_keepalive;
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+    addr->tcp_keepidle = listen->tcp_keepidle;
+    addr->tcp_keepintvl = listen->tcp_keepintvl;
+    addr->tcp_keepcnt = listen->tcp_keepcnt;
+#endif
 #if (NGX_MAIL_SSL)
     addr->ssl = listen->ssl;
 #endif
@@ -373,6 +379,13 @@
             ls->log.data = &ls->addr_text;
             ls->log.handler = ngx_accept_log_error;
 
+            ls->keepalive = addr[i].so_keepalive;
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+            ls->keepidle = addr[i].tcp_keepidle;
+            ls->keepintvl = addr[i].tcp_keepintvl;
+            ls->keepcnt = addr[i].tcp_keepcnt;
+#endif
+
 #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
             ls->ipv6only = addr[i].ipv6only;
 #endif
--- a/src/mail/ngx_mail.h	Wed Nov 30 00:00:00 2011 +0400
+++ b/src/mail/ngx_mail.h	Mon Dec 12 00:00:00 2011 +0400
@@ -40,6 +40,12 @@
 #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
     unsigned                ipv6only:2;
 #endif
+    unsigned                so_keepalive:2;
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+    int                     tcp_keepidle;
+    int                     tcp_keepintvl;
+    int                     tcp_keepcnt;
+#endif
 } ngx_mail_listen_t;
 
 
@@ -95,6 +101,12 @@
 #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
     unsigned                ipv6only:2;
 #endif
+    unsigned                so_keepalive:2;
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+    int                     tcp_keepidle;
+    int                     tcp_keepintvl;
+    int                     tcp_keepcnt;
+#endif
 } ngx_mail_conf_addr_t;
 
 
--- a/src/mail/ngx_mail_core_module.c	Wed Nov 30 00:00:00 2011 +0400
+++ b/src/mail/ngx_mail_core_module.c	Mon Dec 12 00:00:00 2011 +0400
@@ -24,6 +24,12 @@
     void *conf);
 
 
+static ngx_conf_deprecated_t  ngx_conf_deprecated_so_keepalive = {
+    ngx_conf_deprecated, "so_keepalive", 
+    "so_keepalive\" parameter of the \"listen"
+};
+
+
 static ngx_command_t  ngx_mail_core_commands[] = {
 
     { ngx_string("server"),
@@ -52,7 +58,7 @@
       ngx_conf_set_flag_slot,
       NGX_MAIL_SRV_CONF_OFFSET,
       offsetof(ngx_mail_core_srv_conf_t, so_keepalive),
-      NULL },
+      &ngx_conf_deprecated_so_keepalive },
 
     { ngx_string("timeout"),
       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
@@ -446,6 +452,96 @@
 #endif
         }
 
+        if (ngx_strncmp(value[i].data, "so_keepalive=", 13) == 0) {
+
+            if (ngx_strcmp(&value[i].data[13], "on") == 0) {
+                ls->so_keepalive = 1;
+
+            } else if (ngx_strcmp(&value[i].data[13], "off") == 0) {
+                ls->so_keepalive = 2;
+
+            } else {
+
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+                u_char     *p, *end;
+                ngx_str_t   s;
+
+                end = value[i].data + value[i].len;
+                s.data = value[i].data + 13;
+
+                p = ngx_strlchr(s.data, end, ':');
+                if (p == NULL) {
+                    p = end;
+                }
+
+                if (p > s.data) {
+                    s.len = p - s.data;
+
+                    ls->tcp_keepidle = ngx_parse_time(&s, 1);
+                    if (ls->tcp_keepidle == NGX_ERROR) {
+                        goto invalid_so_keepalive;
+                    }
+                }
+
+                s.data = (p < end) ? (p + 1) : end;
+
+                p = ngx_strlchr(s.data, end, ':');
+                if (p == NULL) {
+                    p = end;
+                }
+
+                if (p > s.data) {
+                    s.len = p - s.data;
+
+                    ls->tcp_keepintvl = ngx_parse_time(&s, 1);
+                    if (ls->tcp_keepintvl == NGX_ERROR) {
+                        goto invalid_so_keepalive;
+                    }
+                }
+
+                s.data = (p < end) ? (p + 1) : end;
+
+                if (s.data < end) {
+                    s.len = end - s.data;
+
+                    ls->tcp_keepcnt = ngx_atoi(s.data, s.len);
+                    if (ls->tcp_keepcnt == NGX_ERROR) {
+                        goto invalid_so_keepalive;
+                    }
+                }
+
+                if (ls->tcp_keepidle == 0 && ls->tcp_keepintvl == 0
+                    && ls->tcp_keepcnt == 0)
+                {
+                    goto invalid_so_keepalive;
+                }
+
+                ls->so_keepalive = 1;
+
+#else
+
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "the \"so_keepalive\" parameter accepts "
+                                   "only \"on\" or \"off\" on this platform");
+                return NGX_CONF_ERROR;
+
+#endif
+            }
+
+            ls->bind = 1;
+
+            continue;
+
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+        invalid_so_keepalive:
+
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "invalid so_keepalive value: \"%s\"",
+                               &value[i].data[13]);
+            return NGX_CONF_ERROR;
+#endif
+        }
+
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "the invalid \"%V\" parameter", &value[i]);
         return NGX_CONF_ERROR;
--- a/src/os/unix/ngx_freebsd_sendfile_chain.c	Wed Nov 30 00:00:00 2011 +0400
+++ b/src/os/unix/ngx_freebsd_sendfile_chain.c	Mon Dec 12 00:00:00 2011 +0400
@@ -246,9 +246,14 @@
                 }
             }
 
-            hdtr.headers = (struct iovec *) header.elts;
+            /*
+             * sendfile() does unneeded work if sf_hdtr's count is 0,
+             * but corresponding pointer is not NULL
+             */
+
+            hdtr.headers = header.nelts ? (struct iovec *) header.elts: NULL;
             hdtr.hdr_cnt = header.nelts;
-            hdtr.trailers = (struct iovec *) trailer.elts;
+            hdtr.trailers = trailer.nelts ? (struct iovec *) trailer.elts: NULL;
             hdtr.trl_cnt = trailer.nelts;
 
             /*