changeset 326:9fc4ab6673f9 NGINX_0_6_7

nginx 0.6.7 *) Change: now the paths specified in the "include", "auth_basic_user_file", "perl_modules", "ssl_certificate", "ssl_certificate_key", and "ssl_client_certificate" directives are relative to directory of nginx configuration file nginx.conf, but not to nginx prefix directory. *) Change: the --sysconfdir=PATH option in configure was canceled. *) Change: the special make target "upgrade1" was defined for online upgrade of 0.1.x versions. *) Feature: the "server_name" and "valid_referers" directives support regular expressions. *) Feature: the "server" directive in the "upstream" context supports the "backup" parameter. *) Feature: the ngx_http_perl_module supports the $r->discard_request_body. *) Feature: the "add_header Last-Modified ..." directive changes the "Last-Modified" response header line. *) Bugfix: if an response different than 200 was returned to an request with body and connection went to the keep-alive state after the request, then nginx returned 400 for the next request. *) Bugfix: a segmentation fault occurred in worker process if invalid address was set in the "auth_http" directive. *) Bugfix: now nginx uses default listen backlog value 511 on all platforms except FreeBSD. Thanks to Jiang Hong. *) Bugfix: a worker process may got caught in an endless loop, if an "server" inside "upstream" block was marked as "down"; bug appeared in 0.6.6. *) Bugfix: now Solaris sendfilev() is not used to transfer the client request body to FastCGI-server via the unix domain socket.
author Igor Sysoev <http://sysoev.ru>
date Wed, 15 Aug 2007 00:00:00 +0400
parents f395c7a4c8a8
children be18d26e067c
files CHANGES CHANGES.ru auto/cc/sunc auto/init auto/install auto/options conf/nginx.conf src/core/nginx.h src/core/ngx_connection.c src/core/ngx_inet.c src/event/ngx_event.c src/event/ngx_event_connect.c src/http/modules/ngx_http_access_module.c src/http/modules/ngx_http_dav_module.c src/http/modules/ngx_http_empty_gif_module.c src/http/modules/ngx_http_flv_module.c src/http/modules/ngx_http_geo_module.c src/http/modules/ngx_http_headers_filter_module.c src/http/modules/ngx_http_memcached_module.c src/http/modules/ngx_http_realip_module.c src/http/modules/ngx_http_referer_module.c src/http/modules/ngx_http_static_module.c src/http/modules/ngx_http_stub_status_module.c src/http/modules/perl/nginx.pm src/http/modules/perl/nginx.xs src/http/ngx_http.c src/http/ngx_http.h src/http/ngx_http_core_module.c src/http/ngx_http_core_module.h src/http/ngx_http_request.c src/http/ngx_http_request.h src/http/ngx_http_request_body.c src/http/ngx_http_special_response.c src/http/ngx_http_upstream.c src/http/ngx_http_upstream.h src/http/ngx_http_upstream_round_robin.c src/http/ngx_http_upstream_round_robin.h src/mail/ngx_mail.c src/mail/ngx_mail_auth_http_module.c src/os/unix/ngx_freebsd_config.h src/os/unix/ngx_linux_config.h src/os/unix/ngx_posix_config.h src/os/unix/ngx_solaris_config.h
diffstat 43 files changed, 1075 insertions(+), 303 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,48 @@
 
+Changes with nginx 0.6.7                                         15 Aug 2007
+
+    *) Change: now the paths specified in the "include", 
+       "auth_basic_user_file", "perl_modules", "ssl_certificate", 
+       "ssl_certificate_key", and "ssl_client_certificate" directives are 
+       relative to directory of nginx configuration file nginx.conf, but no 
+       to nginx prefix directory.
+
+    *) Change: the --sysconfdir=PATH option in configure was canceled.
+
+    *) Change: the special make target "upgrade1" was defined for online 
+       upgrade of 0.1.x versions.
+
+    *) Feature: the "server_name" and "valid_referers" directives support 
+       regular expressions.
+
+    *) Feature: the "server" directive in the "upstream" context supports 
+       the "backup" parameter.
+
+    *) Feature: the ngx_http_perl_module supports the 
+       $r->discard_request_body.
+
+    *) Feature: the "add_header Last-Modified ..." directive changes the 
+       "Last-Modified" response header line.
+
+    *) Bugfix: if an response different than 200 was returned to an request 
+       with body and connection went to the keep-alive state after the 
+       request, then nginx returned 400 for the next request.
+
+    *) Bugfix: a segmentation fault occurred in worker process if invalid 
+       address was set in the "auth_http" directive.
+
+    *) Bugfix: now nginx uses default listen backlog value 511 on all 
+       platforms except FreeBSD.
+       Thanks to Jiang Hong.
+
+    *) Bugfix: a worker process may got caught in an endless loop, if an 
+       "server" inside "upstream" block was marked as "down"; bug appeared 
+       in 0.6.6.
+
+    *) Bugfix: now Solaris sendfilev() is not used to transfer the client 
+       request body to FastCGI-server via the unix domain socket.
+
+
 Changes with nginx 0.6.6                                         30 Jul 2007
 
     *) Feature: the --sysconfdir=PATH option in configure.
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,48 @@
 
+Изменения в nginx 0.6.7                                           15.08.2007
+
+    *) Изменение: теперь пути, указанные в директивах include, 
+       auth_basic_user_file, perl_modules, ssl_certificate, 
+       ssl_certificate_key и ssl_client_certificate, определяются 
+       относительно каталогу конфигурационного файла nginx.conf, а не 
+       относительно префиксу.
+
+    *) Изменение: параметр --sysconfdir=PATH в configure упразднён.
+
+    *) Изменение: для обновления на лету версий 0.1.x создан специальный 
+       сценарий make upgrade1.
+
+    *) Добавление: директивы server_name и valid_referers поддерживают 
+       регулярные выражения.
+
+    *) Добавление: директива server в блоке upstream поддерживает параметр 
+       backup.
+
+    *) Добавление: модуль ngx_http_perl_module поддерживает метод 
+       $r->discard_request_body.
+
+    *) Добавление: директива "add_header Last-Modified ..." меняет строку 
+       "Last-Modified" в заголовке ответа.
+
+    *) Исправление: если на запрос с телом возвращался ответ с кодом HTTP 
+       отличным от 200, и после этого запроса соединение переходило в 
+       состояние keep-alive, то на следующий запрос nginx возвращал 400.
+
+    *) Исправление: если в директиве auth_http был задан неправильный 
+       адрес, то в рабочем процессе происходил segmentation fault.
+       <br>
+
+    *) Исправление: теперь по умолчанию nginx использует значение 511 для 
+       listen backlog на всех платформах, кроме FreeBSD.
+       Спасибо Jiang Hong.
+
+    *) Исправление: рабочий процесс мог зациклиться, если server в блоке 
+       upstream был помечен как down; ошибка появилась в 0.6.6.
+
+    *) Исправление: sendfilev() в Solaris теперь не используется при 
+       передаче тела запроса FastCGI-серверу через unix domain сокет.
+
+
 Изменения в nginx 0.6.6                                           30.07.2007
 
     *) Добавление: параметр --sysconfdir=PATH в configure.
--- a/auto/cc/sunc
+++ b/auto/cc/sunc
@@ -155,3 +155,6 @@ fi
 
 # stop on warning
 CFLAGS="$CFLAGS -errwarn=%all"
+
+# debug
+CFLAGS="$CFLAGS -g"
--- a/auto/init
+++ b/auto/init
@@ -55,15 +55,24 @@ clean:
 upgrade:
 	$NGX_SBIN_PATH -t
 
-	# upgrade compatibility from 0.1.x to 0.2.x
+	kill -USR2 \`cat $NGX_PID_PATH\`
+	sleep 1
+	test -f $NGX_PID_PATH.oldbin
+
+	kill -QUIT \`cat $NGX_PID_PATH.oldbin\`
+
+upgrade1:
+	# upgrade 0.1.x to 0.2+
+
+	$NGX_SBIN_PATH -t
+
 	cp $NGX_PID_PATH $NGX_PID_PATH.oldbin
 
 	kill -USR2 \`cat $NGX_PID_PATH\`
 	sleep 1
 	test -f $NGX_PID_PATH.oldbin
 
-	# upgrade compatibility from 0.1.x to 0.2.x
 	cp $NGX_PID_PATH $NGX_PID_PATH.newbin
 
-	kill -WINCH \`cat $NGX_PID_PATH.oldbin\`
+	kill -QUIT \`cat $NGX_PID_PATH.oldbin\`
 END
--- a/auto/install
+++ b/auto/install
@@ -34,11 +34,11 @@ install:	$NGX_OBJS${ngx_dirsep}nginx${ng
 
 	test -f '$NGX_CONF_PREFIX/mime.types' \
 		|| cp conf/mime.types '$NGX_CONF_PREFIX'
-	cp conf/mime.types '$NGX_CONF_PATH/mime.types.default'
+	cp conf/mime.types '$NGX_CONF_PREFIX/mime.types.default'
 
 	test -f '$NGX_CONF_PREFIX/fastcgi_params' \
 		|| cp conf/fastcgi_params '$NGX_CONF_PREFIX'
-	cp conf/fastcgi_params '$NGX_CONF_PATH/fastcgi_params.default'
+	cp conf/fastcgi_params '$NGX_CONF_PREFIX/fastcgi_params.default'
 
 	test -f '$NGX_CONF_PATH' || cp conf/nginx.conf '$NGX_CONF_PREFIX'
 	cp conf/nginx.conf '$NGX_CONF_PREFIX/nginx.conf.default'
--- a/auto/options
+++ b/auto/options
@@ -124,7 +124,6 @@ do
 
         --prefix=*)                      NGX_PREFIX="$value"        ;;
         --sbin-path=*)                   NGX_SBIN_PATH="$value"     ;;
-        --sysconfdir=*)                  NGX_CONF_PREFIX="$value"   ;;
         --conf-path=*)                   NGX_CONF_PATH="$value"     ;;
         --error-log-path=*)              NGX_ERROR_LOG_PATH="$value";;
         --pid-path=*)                    NGX_PID_PATH="$value"      ;;
@@ -242,7 +241,6 @@ cat << END
 
   --prefix=PATH                      set the installation prefix
   --sbin-path=PATH                   set path to the nginx binary file
-  --sysconfdir=PATH                  set the configuration prefix
   --conf-path=PATH                   set path to the nginx.conf file
   --error-log-path=PATH              set path to the error log
   --pid-path=PATH                    set path to nginx.pid file
@@ -363,7 +361,6 @@ fi
 
 
 NGX_PREFIX=${NGX_PREFIX:-/usr/local/nginx}
-NGX_CONF_PREFIX=${NGX_CONF_PREFIX:-$NGX_PREFIX}
 
 
 case ".$NGX_SBIN_PATH" in
@@ -385,15 +382,18 @@ case ".$NGX_CONF_PATH" in
     ;;
 
     .)
-        NGX_CONF_PATH=$NGX_CONF_PREFIX/conf/nginx.conf
+        NGX_CONF_PATH=$NGX_PREFIX/conf/nginx.conf
     ;;
 
     *)
-        NGX_CONF_PATH=$NGX_CONF_PREFIX/$NGX_CONF_PATH
+        NGX_CONF_PATH=$NGX_PREFIX/$NGX_CONF_PATH
     ;;
 esac
 
 
+NGX_CONF_PREFIX=`dirname $NGX_CONF_PATH`
+
+
 case ".$NGX_PID_PATH" in
     ./*)
     ;;
--- a/conf/nginx.conf
+++ b/conf/nginx.conf
@@ -15,7 +15,7 @@ events {
 
 
 http {
-    include       conf/mime.types;
+    include       mime.types;
     default_type  application/octet-stream;
 
     #log_format  main  '$remote_addr - $remote_user [$time_local] $request '
@@ -66,7 +66,7 @@ http {
         #    fastcgi_pass   127.0.0.1:9000;
         #    fastcgi_index  index.php;
         #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
-        #    include        conf/fastcgi_params;
+        #    include        fastcgi_params;
         #}
 
         # deny access to .htaccess files, if Apache's document root
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,7 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define NGINX_VERSION      "0.6.6"
+#define NGINX_VERSION      "0.6.7"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_connection.c
+++ b/src/core/ngx_connection.c
@@ -123,7 +123,7 @@ ngx_set_inherited_sockets(ngx_cycle_t *c
                                           ntohs(sin->sin_port))
                               - ls[i].addr_text.data;
 
-        ls[i].backlog = -1;
+        ls[i].backlog = NGX_LISTEN_BACKLOG;
 
         olen = sizeof(int);
 
--- a/src/core/ngx_inet.c
+++ b/src/core/ngx_inet.c
@@ -214,7 +214,13 @@ ngx_ptocidr(ngx_str_t *text, void *cidr)
 
     in_cidr->mask = htonl((ngx_uint_t) (0 - (1 << (32 - m))));
 
-    return NGX_OK;
+    if (in_cidr->addr == (in_cidr->addr & in_cidr->mask)) {
+        return NGX_OK;
+    }
+
+    in_cidr->addr &= in_cidr->mask;
+
+    return NGX_DONE;
 }
 
 
--- a/src/event/ngx_event.c
+++ b/src/event/ngx_event.c
@@ -1038,8 +1038,9 @@ ngx_event_debug_connection(ngx_conf_t *c
 #if (NGX_DEBUG)
     ngx_event_conf_t  *ecf = conf;
 
+    ngx_int_t           rc;
+    ngx_str_t          *value;
     ngx_event_debug_t  *dc;
-    ngx_str_t          *value;
     struct hostent     *h;
     ngx_inet_cidr_t     in_cidr;
 
@@ -1056,13 +1057,21 @@ ngx_event_debug_connection(ngx_conf_t *c
 
     if (dc->addr != INADDR_NONE) {
         dc->mask = 0xffffffff;
-        return NGX_OK;
+        return NGX_CONF_OK;
     }
 
-    if (ngx_ptocidr(&value[1], &in_cidr) == NGX_OK) {
+    rc = ngx_ptocidr(&value[1], &in_cidr);
+
+    if (rc == NGX_DONE) {
+        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                           "low address bits of %V are meaningless", &value[1]);
+        rc = NGX_OK;
+    }
+
+    if (rc == NGX_OK) {
         dc->mask = in_cidr.mask;
         dc->addr = in_cidr.addr;
-        return NGX_OK;
+        return NGX_CONF_OK;
     }
 
     h = gethostbyname((char *) value[1].data);
@@ -1084,7 +1093,7 @@ ngx_event_debug_connection(ngx_conf_t *c
 
 #endif
 
-    return NGX_OK;
+    return NGX_CONF_OK;
 }
 
 
--- a/src/event/ngx_event_connect.c
+++ b/src/event/ngx_event_connect.c
@@ -85,6 +85,8 @@ ngx_event_connect_peer(ngx_peer_connecti
     c->recv_chain = ngx_recv_chain;
     c->send_chain = ngx_send_chain;
 
+    c->sendfile = 1;
+
     c->log_error = pc->log_error;
 
     if (pc->sockaddr->sa_family != AF_INET) {
--- a/src/http/modules/ngx_http_access_module.c
+++ b/src/http/modules/ngx_http_access_module.c
@@ -137,6 +137,7 @@ ngx_http_access_rule(ngx_conf_t *cf, ngx
 {
     ngx_http_access_loc_conf_t *alcf = conf;
 
+    ngx_int_t                rc;
     ngx_str_t               *value;
     ngx_inet_cidr_t          in_cidr;
     ngx_http_access_rule_t  *rule;
@@ -173,12 +174,19 @@ ngx_http_access_rule(ngx_conf_t *cf, ngx
         return NGX_CONF_OK;
     }
 
-    if (ngx_ptocidr(&value[1], &in_cidr) == NGX_ERROR) {
+    rc = ngx_ptocidr(&value[1], &in_cidr);
+
+    if (rc == NGX_ERROR) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"",
                            &value[1]);
         return NGX_CONF_ERROR;
     }
 
+    if (rc == NGX_DONE) {
+        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                           "low address bits of %V are meaningless", &value[1]);
+    }
+
     rule->mask = in_cidr.mask;
     rule->addr = in_cidr.addr;
 
--- a/src/http/modules/ngx_http_dav_module.c
+++ b/src/http/modules/ngx_http_dav_module.c
@@ -353,9 +353,9 @@ ngx_http_dav_delete_handler(ngx_http_req
         return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
     }
 
-    rc = ngx_http_discard_body(r);
+    rc = ngx_http_discard_request_body(r);
 
-    if (rc != NGX_OK && rc != NGX_AGAIN) {
+    if (rc != NGX_OK) {
         return rc;
     }
 
@@ -469,9 +469,9 @@ ngx_http_dav_mkcol_handler(ngx_http_requ
         return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
     }
 
-    rc = ngx_http_discard_body(r);
+    rc = ngx_http_discard_request_body(r);
 
-    if (rc != NGX_OK && rc != NGX_AGAIN) {
+    if (rc != NGX_OK) {
         return rc;
     }
 
@@ -611,9 +611,9 @@ destination_done:
 
 overwrite_done:
 
-    rc = ngx_http_discard_body(r);
+    rc = ngx_http_discard_request_body(r);
 
-    if (rc != NGX_OK && rc != NGX_AGAIN) {
+    if (rc != NGX_OK) {
         return rc;
     }
 
--- a/src/http/modules/ngx_http_empty_gif_module.c
+++ b/src/http/modules/ngx_http_empty_gif_module.c
@@ -116,9 +116,9 @@ ngx_http_empty_gif_handler(ngx_http_requ
         return NGX_HTTP_NOT_ALLOWED;
     }
 
-    rc = ngx_http_discard_body(r);
+    rc = ngx_http_discard_request_body(r);
 
-    if (rc != NGX_OK && rc != NGX_AGAIN) {
+    if (rc != NGX_OK) {
         return rc;
     }
 
--- a/src/http/modules/ngx_http_flv_module.c
+++ b/src/http/modules/ngx_http_flv_module.c
@@ -89,9 +89,9 @@ ngx_http_flv_handler(ngx_http_request_t 
         return NGX_DECLINED;
     }
 
-    rc = ngx_http_discard_body(r);
+    rc = ngx_http_discard_request_body(r);
 
-    if (rc != NGX_OK && rc != NGX_AGAIN) {
+    if (rc != NGX_OK) {
         return rc;
     }
 
--- a/src/http/modules/ngx_http_geo_module.c
+++ b/src/http/modules/ngx_http_geo_module.c
@@ -212,12 +212,20 @@ ngx_http_geo(ngx_conf_t *cf, ngx_command
         cidrin.mask = 0;
 
     } else {
-        if (ngx_ptocidr(&value[0], &cidrin) == NGX_ERROR) {
+        rc = ngx_ptocidr(&value[0], &cidrin);
+
+        if (rc == NGX_ERROR) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                "invalid parameter \"%V\"", &value[0]);
             return NGX_CONF_ERROR;
         }
 
+        if (rc == NGX_DONE) {
+            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                               "low address bits of %V are meaningless",
+                               &value[0]);
+        }
+
         cidrin.addr = ntohl(cidrin.addr);
         cidrin.mask = ntohl(cidrin.mask);
     }
--- a/src/http/modules/ngx_http_headers_filter_module.c
+++ b/src/http/modules/ngx_http_headers_filter_module.c
@@ -9,17 +9,31 @@
 #include <ngx_http.h>
 
 
-typedef struct {
-    ngx_table_elt_t   value;
-    ngx_array_t      *lengths;
-    ngx_array_t      *values;
-} ngx_http_header_val_t;
+typedef struct ngx_http_header_val_s  ngx_http_header_val_t;
+
+typedef ngx_int_t (*ngx_http_set_header_pt)(ngx_http_request_t *r,
+    ngx_http_header_val_t *hv, ngx_str_t *value);
 
 
 typedef struct {
-    time_t            expires;
-    ngx_str_t         cache_control;
-    ngx_array_t      *headers;
+    ngx_str_t                name;
+    ngx_uint_t               offset;
+    ngx_http_set_header_pt   handler;
+} ngx_http_set_header_t;
+
+
+struct ngx_http_header_val_s {
+    ngx_table_elt_t          value;
+    ngx_uint_t               offset;
+    ngx_http_set_header_pt   handler;
+    ngx_array_t             *lengths;
+    ngx_array_t             *values;
+};
+
+
+typedef struct {
+    time_t                   expires;
+    ngx_array_t             *headers;
 } ngx_http_headers_conf_t;
 
 
@@ -29,6 +43,13 @@ typedef struct {
 #define NGX_HTTP_EXPIRES_MAX     -2147483644
 
 
+static ngx_int_t ngx_http_set_expires(ngx_http_request_t *r,
+    ngx_http_headers_conf_t *conf);
+static ngx_int_t ngx_http_add_cache_control(ngx_http_request_t *r,
+    ngx_http_header_val_t *hv, ngx_str_t *value);
+static ngx_int_t ngx_http_set_last_modified(ngx_http_request_t *r,
+    ngx_http_header_val_t *hv, ngx_str_t *value);
+
 static void *ngx_http_headers_create_conf(ngx_conf_t *cf);
 static char *ngx_http_headers_merge_conf(ngx_conf_t *cf,
     void *parent, void *child);
@@ -39,6 +60,18 @@ static char *ngx_http_headers_add(ngx_co
     void *conf);
 
 
+static ngx_http_set_header_t  ngx_http_set_headers[] = {
+
+    { ngx_string("Cache-Control"), 0, ngx_http_add_cache_control },
+
+    { ngx_string("Last-Modified"),
+                 offsetof(ngx_http_headers_out_t, last_modified),
+                 ngx_http_set_last_modified },
+
+    { ngx_null_string, 0, NULL }
+};
+
+
 static ngx_command_t  ngx_http_headers_filter_commands[] = {
 
     { ngx_string("expires"),
@@ -98,13 +131,15 @@ static ngx_http_output_header_filter_pt 
 static ngx_int_t
 ngx_http_headers_filter(ngx_http_request_t *r)
 {
-    size_t                    len;
+    ngx_str_t                 value;
     ngx_uint_t                i;
-    ngx_table_elt_t          *expires, *cc, **ccp, *out;
     ngx_http_header_val_t    *h;
     ngx_http_headers_conf_t  *conf;
 
-    if (r != r->main
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module);
+
+    if ((conf->expires == NGX_HTTP_EXPIRES_OFF && conf->headers == NULL)
+        || r != r->main
         || (r->headers_out.status != NGX_HTTP_OK
             && r->headers_out.status != NGX_HTTP_NO_CONTENT
             && r->headers_out.status != NGX_HTTP_MOVED_PERMANENTLY
@@ -114,124 +149,73 @@ ngx_http_headers_filter(ngx_http_request
         return ngx_http_next_header_filter(r);
     }
 
-    conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module);
-
     if (conf->expires != NGX_HTTP_EXPIRES_OFF) {
-
-        expires = r->headers_out.expires;
-
-        if (expires == NULL) {
+        if (ngx_http_set_expires(r, conf) != NGX_OK) {
+            return NGX_ERROR;
+        }
+    }
 
-            expires = ngx_list_push(&r->headers_out.headers);
-            if (expires == NULL) {
-                return NGX_ERROR;
-            }
-
-            r->headers_out.expires = expires;
-
-            expires->hash = 1;
-            expires->key.len = sizeof("Expires") - 1;
-            expires->key.data = (u_char *) "Expires";
-        }
+    if (conf->headers) {
+        h = conf->headers->elts;
+        for (i = 0; i < conf->headers->nelts; i++) {
 
-        len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT");
-        expires->value.len = len - 1;
-
-        ccp = r->headers_out.cache_control.elts;
-
-        if (ccp == NULL) {
+            if (h[i].lengths == NULL) {
+                value = h[i].value.value;
 
-            if (ngx_array_init(&r->headers_out.cache_control, r->pool,
-                               1, sizeof(ngx_table_elt_t *))
-                != NGX_OK)
-            {
-                return NGX_ERROR;
-            }
-
-            ccp = ngx_array_push(&r->headers_out.cache_control);
-            if (ccp == NULL) {
-                return NGX_ERROR;
-            }
-
-            cc = ngx_list_push(&r->headers_out.headers);
-            if (cc == NULL) {
-                return NGX_ERROR;
+            } else {
+                if (ngx_http_script_run(r, &value, h[i].lengths->elts, 0,
+                                        h[i].values->elts)
+                    == NULL)
+                {
+                    return NGX_ERROR;
+                }
             }
 
-            cc->hash = 1;
-            cc->key.len = sizeof("Cache-Control") - 1;
-            cc->key.data = (u_char *) "Cache-Control";
-
-            *ccp = cc;
-
-        } else {
-            for (i = 1; i < r->headers_out.cache_control.nelts; i++) {
-                ccp[i]->hash = 0;
-            }
-
-            cc = ccp[0];
-        }
-
-        if (conf->expires == NGX_HTTP_EXPIRES_EPOCH) {
-            expires->value.data = (u_char *) "Thu, 01 Jan 1970 00:00:01 GMT";
-
-            cc->value.len = sizeof("no-cache") - 1;
-            cc->value.data = (u_char *) "no-cache";
-
-        } else if (conf->expires == NGX_HTTP_EXPIRES_MAX) {
-            expires->value.data = (u_char *) "Thu, 31 Dec 2037 23:55:55 GMT";
-
-            /* 10 years */
-            cc->value.len = sizeof("max-age=315360000") - 1;
-            cc->value.data = (u_char *) "max-age=315360000";
-
-        } else {
-            expires->value.data = ngx_palloc(r->pool, len);
-            if (expires->value.data == NULL) {
+            if (h[i].handler(r, &h[i], &value) != NGX_OK) {
                 return NGX_ERROR;
             }
-
-            if (conf->expires == 0) {
-                ngx_memcpy(expires->value.data, ngx_cached_http_time.data,
-                           ngx_cached_http_time.len + 1);
-
-                cc->value.len = sizeof("max-age=0") - 1;
-                cc->value.data = (u_char *) "max-age=0";
-
-            } else {
-                ngx_http_time(expires->value.data, ngx_time() + conf->expires);
-
-                if (conf->expires < 0) {
-                    cc->value.len = sizeof("no-cache") - 1;
-                    cc->value.data = (u_char *) "no-cache";
-
-                } else {
-                    cc->value.data = ngx_palloc(r->pool, sizeof("max-age=")
-                                                         + NGX_TIME_T_LEN + 1);
-                    if (cc->value.data == NULL) {
-                        return NGX_ERROR;
-                    }
-
-                    cc->value.len = ngx_sprintf(cc->value.data, "max-age=%T",
-                                                conf->expires)
-                                    - cc->value.data;
-                }
-            }
         }
     }
 
-    if (conf->cache_control.len) {
+    return ngx_http_next_header_filter(r);
+}
+
 
-        ccp = r->headers_out.cache_control.elts;
+static ngx_int_t
+ngx_http_set_expires(ngx_http_request_t *r, ngx_http_headers_conf_t *conf)
+{
+    size_t            len;
+    ngx_uint_t        i;
+    ngx_table_elt_t  *expires, *cc, **ccp;
 
-        if (ccp == NULL) {
+    expires = r->headers_out.expires;
+
+    if (expires == NULL) {
+
+        expires = ngx_list_push(&r->headers_out.headers);
+        if (expires == NULL) {
+            return NGX_ERROR;
+        }
 
-            if (ngx_array_init(&r->headers_out.cache_control, r->pool,
-                               1, sizeof(ngx_table_elt_t *))
-                != NGX_OK)
-            {
-                return NGX_ERROR;
-            }
+        r->headers_out.expires = expires;
+
+        expires->hash = 1;
+        expires->key.len = sizeof("Expires") - 1;
+        expires->key.data = (u_char *) "Expires";
+    }
+
+    len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT");
+    expires->value.len = len - 1;
+
+    ccp = r->headers_out.cache_control.elts;
+
+    if (ccp == NULL) {
+
+        if (ngx_array_init(&r->headers_out.cache_control, r->pool,
+                           1, sizeof(ngx_table_elt_t *))
+            != NGX_OK)
+        {
+            return NGX_ERROR;
         }
 
         ccp = ngx_array_push(&r->headers_out.cache_control);
@@ -247,37 +231,161 @@ ngx_http_headers_filter(ngx_http_request
         cc->hash = 1;
         cc->key.len = sizeof("Cache-Control") - 1;
         cc->key.data = (u_char *) "Cache-Control";
-        cc->value = conf->cache_control;
 
         *ccp = cc;
+
+    } else {
+        for (i = 1; i < r->headers_out.cache_control.nelts; i++) {
+            ccp[i]->hash = 0;
+        }
+
+        cc = ccp[0];
+    }
+
+    if (conf->expires == NGX_HTTP_EXPIRES_EPOCH) {
+        expires->value.data = (u_char *) "Thu, 01 Jan 1970 00:00:01 GMT";
+
+        cc->value.len = sizeof("no-cache") - 1;
+        cc->value.data = (u_char *) "no-cache";
+
+        return NGX_OK;
+    }
+
+    if (conf->expires == NGX_HTTP_EXPIRES_MAX) {
+        expires->value.data = (u_char *) "Thu, 31 Dec 2037 23:55:55 GMT";
+
+        /* 10 years */
+        cc->value.len = sizeof("max-age=315360000") - 1;
+        cc->value.data = (u_char *) "max-age=315360000";
+
+        return NGX_OK;
+    }
+
+    expires->value.data = ngx_palloc(r->pool, len);
+    if (expires->value.data == NULL) {
+        return NGX_ERROR;
+    }
+
+    if (conf->expires == 0) {
+        ngx_memcpy(expires->value.data, ngx_cached_http_time.data,
+                   ngx_cached_http_time.len + 1);
+
+        cc->value.len = sizeof("max-age=0") - 1;
+        cc->value.data = (u_char *) "max-age=0";
+
+        return NGX_OK;
     }
 
-    if (conf->headers) {
-        h = conf->headers->elts;
-        for (i = 0; i < conf->headers->nelts; i++) {
-            out = ngx_list_push(&r->headers_out.headers);
-            if (out == NULL) {
-                return NGX_ERROR;
-            }
+    ngx_http_time(expires->value.data, ngx_time() + conf->expires);
+
+    if (conf->expires < 0) {
+        cc->value.len = sizeof("no-cache") - 1;
+        cc->value.data = (u_char *) "no-cache";
+
+        return NGX_OK;
+    }
 
-            out->hash = h[i].value.hash;
-            out->key = h[i].value.key;
+    cc->value.data = ngx_palloc(r->pool,
+                                sizeof("max-age=") + NGX_TIME_T_LEN + 1);
+    if (cc->value.data == NULL) {
+        return NGX_ERROR;
+    }
+
+    cc->value.len = ngx_sprintf(cc->value.data, "max-age=%T", conf->expires)
+                    - cc->value.data;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_add_header(ngx_http_request_t *r, ngx_http_header_val_t *hv,
+    ngx_str_t *value)
+{
+    ngx_table_elt_t  *h;
 
-            if (h[i].lengths == NULL) {
-                out->value = h[i].value.value;
-                continue;
-            }
+    h = ngx_list_push(&r->headers_out.headers);
+    if (h == NULL) {
+        return NGX_ERROR;
+    }
+
+    h->hash = hv->value.hash;
+    h->key = hv->value.key;
+    h->value = *value;
+
+    return NGX_OK;
+}
+
 
-            if (ngx_http_script_run(r, &out->value, h[i].lengths->elts, 0,
-                                    h[i].values->elts)
-                == NULL)
-            {
-                return NGX_ERROR;
-            }
+static ngx_int_t
+ngx_http_add_cache_control(ngx_http_request_t *r, ngx_http_header_val_t *hv,
+    ngx_str_t *value)
+{
+    ngx_table_elt_t  *cc, **ccp;
+
+    ccp = r->headers_out.cache_control.elts;
+
+    if (ccp == NULL) {
+
+        if (ngx_array_init(&r->headers_out.cache_control, r->pool,
+                           1, sizeof(ngx_table_elt_t *))
+            != NGX_OK)
+        {
+            return NGX_ERROR;
         }
     }
 
-    return ngx_http_next_header_filter(r);
+    ccp = ngx_array_push(&r->headers_out.cache_control);
+    if (ccp == NULL) {
+        return NGX_ERROR;
+    }
+
+    cc = ngx_list_push(&r->headers_out.headers);
+    if (cc == NULL) {
+        return NGX_ERROR;
+    }
+
+    cc->hash = 1;
+    cc->key.len = sizeof("Cache-Control") - 1;
+    cc->key.data = (u_char *) "Cache-Control";
+    cc->value = *value;
+
+    *ccp = cc;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_set_last_modified(ngx_http_request_t *r, ngx_http_header_val_t *hv,
+    ngx_str_t *value)
+{
+    ngx_table_elt_t  *h, **old;
+
+    if (hv->offset) {
+        old = (ngx_table_elt_t **) ((char *) &r->headers_out + hv->offset);
+
+    } else {
+        old = NULL;
+    }
+
+    if (old == NULL || *old == NULL) {
+        h = ngx_list_push(&r->headers_out.headers);
+        if (h == NULL) {
+            return NGX_ERROR;
+        }
+
+    } else {
+        h = *old;
+    }
+
+    h->hash = hv->value.hash;
+    h->key = hv->value.key;
+    h->value = *value;
+
+    r->headers_out.last_modified_time = -1;
+
+    return NGX_OK;
 }
 
 
@@ -294,8 +402,6 @@ ngx_http_headers_create_conf(ngx_conf_t 
     /*
      * set by ngx_pcalloc():
      *
-     *     conf->cache_control.len = 0;
-     *     conf->cache_control.data = NULL;
      *     conf->headers = NULL;
      */
 
@@ -313,11 +419,7 @@ ngx_http_headers_merge_conf(ngx_conf_t *
 
     if (conf->expires == NGX_HTTP_EXPIRES_UNSET) {
         conf->expires = (prev->expires == NGX_HTTP_EXPIRES_UNSET) ?
-                                          NGX_HTTP_EXPIRES_OFF : prev->expires;
-    }
-
-    if (conf->cache_control.data == NULL) {
-        conf->cache_control = prev->cache_control;
+                            NGX_HTTP_EXPIRES_OFF : prev->expires;
     }
 
     if (conf->headers == NULL) {
@@ -406,16 +508,13 @@ ngx_http_headers_add(ngx_conf_t *cf, ngx
 
     ngx_int_t                   n;
     ngx_str_t                  *value;
+    ngx_uint_t                  i;
     ngx_http_header_val_t      *h;
+    ngx_http_set_header_t      *sh;
     ngx_http_script_compile_t   sc;
 
     value = cf->args->elts;
 
-    if (ngx_strcasecmp(value[1].data, (u_char *) "cache-control") == 0) {
-        hcf->cache_control = value[2];
-        return NGX_CONF_OK;
-    }
-
     if (hcf->headers == NULL) {
         hcf->headers = ngx_array_create(cf->pool, 1,
                                         sizeof(ngx_http_header_val_t));
@@ -432,9 +531,22 @@ ngx_http_headers_add(ngx_conf_t *cf, ngx
     h->value.hash = 1;
     h->value.key = value[1];
     h->value.value = value[2];
+    h->offset = 0;
+    h->handler = ngx_http_add_header;
     h->lengths = NULL;
     h->values = NULL;
 
+    sh = ngx_http_set_headers;
+    for (i = 0; sh[i].name.len; i++) {
+        if (ngx_strcasecmp(value[1].data, sh[i].name.data) != 0) {
+            continue;
+        }
+
+        h->offset = sh[i].offset;
+        h->handler = sh[i].handler;
+        break;
+    }
+
     n = ngx_http_script_variables_count(&value[2]);
 
     if (n == 0) {
--- a/src/http/modules/ngx_http_memcached_module.c
+++ b/src/http/modules/ngx_http_memcached_module.c
@@ -167,9 +167,9 @@ ngx_http_memcached_handler(ngx_http_requ
         return NGX_HTTP_NOT_ALLOWED;
     }
 
-    rc = ngx_http_discard_body(r);
+    rc = ngx_http_discard_request_body(r);
 
-    if (rc != NGX_OK && rc != NGX_AGAIN) {
+    if (rc != NGX_OK) {
         return rc;
     }
 
--- a/src/http/modules/ngx_http_realip_module.c
+++ b/src/http/modules/ngx_http_realip_module.c
@@ -188,6 +188,7 @@ ngx_http_realip_from(ngx_conf_t *cf, ngx
 {
     ngx_http_realip_loc_conf_t *rlcf = conf;
 
+    ngx_int_t                 rc;
     ngx_str_t                *value;
     ngx_inet_cidr_t           in_cidr;
     ngx_http_realip_from_t   *from;
@@ -215,12 +216,19 @@ ngx_http_realip_from(ngx_conf_t *cf, ngx
         return NGX_CONF_OK;
     }
 
-    if (ngx_ptocidr(&value[1], &in_cidr) == NGX_ERROR) {
+    rc = ngx_ptocidr(&value[1], &in_cidr);
+
+    if (rc == NGX_ERROR) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"",
                            &value[1]);
         return NGX_CONF_ERROR;
     }
 
+    if (rc == NGX_DONE) {
+        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                           "low address bits of %V are meaningless", &value[1]);
+    }
+
     from->mask = in_cidr.mask;
     from->addr = in_cidr.addr;
 
--- a/src/http/modules/ngx_http_referer_module.c
+++ b/src/http/modules/ngx_http_referer_module.c
@@ -11,9 +11,27 @@
 
 #define NGX_HTTP_REFERER_NO_URI_PART  ((void *) 4)
 
+#if (NGX_PCRE)
+
+typedef struct {
+    ngx_regex_t             *regex;
+    ngx_str_t                name;
+} ngx_http_referer_regex_t;
+
+#else
+
+#define ngx_regex_t          void
+
+#endif
+
+
 typedef struct {
     ngx_hash_combined_t      hash;
 
+#if (NGX_PCRE)
+    ngx_array_t             *regex;
+#endif
+
     ngx_flag_t               no_referer;
     ngx_flag_t               blocked_referer;
 
@@ -28,6 +46,8 @@ static char *ngx_http_valid_referers(ngx
     void *conf);
 static char *ngx_http_add_referer(ngx_conf_t *cf, ngx_hash_keys_arrays_t *keys,
     ngx_str_t *value, ngx_str_t *uri);
+static char *ngx_http_add_regex_referer(ngx_conf_t *cf,
+    ngx_http_referer_conf_t *rlcf, ngx_str_t *name, ngx_regex_t *regex);
 static int ngx_libc_cdecl ngx_http_cmp_referer_wildcards(const void *one,
     const void *two);
 
@@ -80,18 +100,27 @@ static ngx_int_t
 ngx_http_referer_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
      uintptr_t data)
 {
-    u_char                   *p, *ref, *last;
-    size_t                    len;
-    ngx_str_t                *uri;
-    ngx_uint_t                i, key;
-    ngx_http_referer_conf_t  *rlcf;
-    u_char                    buf[256];
+    u_char                    *p, *ref, *last;
+    size_t                     len;
+    ngx_str_t                 *uri;
+    ngx_uint_t                 i, key;
+    ngx_http_referer_conf_t   *rlcf;
+    u_char                     buf[256];
+#if (NGX_PCRE)
+    ngx_int_t                  n;
+    ngx_str_t                  referer;
+    ngx_http_referer_regex_t  *regex;
+#endif
 
     rlcf = ngx_http_get_module_loc_conf(r, ngx_http_referer_module);
 
     if (rlcf->hash.hash.buckets == NULL
         && rlcf->hash.wc_head == NULL
-        && rlcf->hash.wc_tail == NULL)
+        && rlcf->hash.wc_tail == NULL
+#if (NGX_PCRE)
+        && rlcf->regex == NULL
+#endif
+       )
     {
         goto valid;
     }
@@ -135,14 +164,44 @@ ngx_http_referer_variable(ngx_http_reque
         }
     }
 
-    len = p - ref;
-
-    uri = ngx_hash_find_combined(&rlcf->hash, key, buf, len);
+    uri = ngx_hash_find_combined(&rlcf->hash, key, buf, p - ref);
 
     if (uri) {
         goto uri;
     }
 
+#if (NGX_PCRE)
+
+    if (rlcf->regex) {
+
+        referer.len = len - 7;
+        referer.data = ref;
+
+        regex = rlcf->regex->elts;
+
+        for (i = 0; i < rlcf->regex->nelts; i++) {
+            n = ngx_regex_exec(regex[i].regex, &referer, NULL, 0);
+
+            if (n == NGX_REGEX_NO_MATCHED) {
+                continue;
+            }
+
+            if (n < 0) {
+                ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                              ngx_regex_exec_n
+                              " failed: %d on \"%V\" using \"%V\"",
+                              n, &referer, &regex[i].name);
+                return NGX_ERROR;
+            }
+
+            /* match */
+
+            goto valid;
+        }
+    }
+
+#endif
+
 invalid:
 
     *v = ngx_http_variable_true_value;
@@ -357,6 +416,21 @@ ngx_http_valid_referers(ngx_conf_t *cf, 
 
             sn = cscf->server_names.elts;
             for (n = 0; n < cscf->server_names.nelts; n++) {
+
+#if (NGX_PCRE)
+                if (sn[n].regex) {
+
+                    if (ngx_http_add_regex_referer(cf, rlcf, &sn[n].name,
+                                                   sn[n].regex)
+                        != NGX_OK)
+                    {
+                        return NGX_CONF_ERROR;
+                    }
+
+                    continue;
+                }
+#endif
+
                 if (ngx_http_add_referer(cf, rlcf->keys, &sn[n].name, &uri)
                     != NGX_OK)
                 {
@@ -367,6 +441,15 @@ ngx_http_valid_referers(ngx_conf_t *cf, 
             continue;
         }
 
+        if (value[i].data[0] == '~') {
+            if (ngx_http_add_regex_referer(cf, rlcf, &value[i], NULL) != NGX_OK)
+            {
+                return NGX_CONF_ERROR;
+            }
+
+            continue;
+        }
+
         p = (u_char *) ngx_strchr(value[i].data, '/');
 
         if (p) {
@@ -423,6 +506,64 @@ ngx_http_add_referer(ngx_conf_t *cf, ngx
 }
 
 
+static char *
+ngx_http_add_regex_referer(ngx_conf_t *cf, ngx_http_referer_conf_t *rlcf,
+    ngx_str_t *name, ngx_regex_t *regex)
+{
+#if (NGX_PCRE)
+    ngx_str_t                  err;
+    ngx_http_referer_regex_t  *rr;
+    u_char                     errstr[NGX_MAX_CONF_ERRSTR];
+
+    if (rlcf->regex == NULL) {
+        rlcf->regex = ngx_array_create(cf->pool, 2,
+                                       sizeof(ngx_http_referer_regex_t));
+        if (rlcf->regex == NULL) {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    rr = ngx_array_push(rlcf->regex);
+    if (rr == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    if (regex) {
+        rr->regex = regex;
+        rr->name = *name;
+
+        return NGX_CONF_OK;
+    }
+
+    err.len = NGX_MAX_CONF_ERRSTR;
+    err.data = errstr;
+
+    name->len--;
+    name->data++;
+
+    rr->regex = ngx_regex_compile(name, NGX_REGEX_CASELESS, cf->pool, &err);
+
+    if (rr->regex == NULL) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data);
+        return NGX_CONF_ERROR;
+    }
+
+    rr->name = *name;
+
+    return NGX_CONF_OK;
+
+#else
+
+    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                       "the using of the regex \"%V\" requires PCRE library",
+                       name);
+
+    return NGX_CONF_ERROR;
+
+#endif
+}
+
+
 static int ngx_libc_cdecl
 ngx_http_cmp_referer_wildcards(const void *one, const void *two)
 {
--- a/src/http/modules/ngx_http_static_module.c
+++ b/src/http/modules/ngx_http_static_module.c
@@ -100,9 +100,9 @@ ngx_http_static_handler(ngx_http_request
         return NGX_DECLINED;
     }
 
-    rc = ngx_http_discard_body(r);
+    rc = ngx_http_discard_request_body(r);
 
-    if (rc != NGX_OK && rc != NGX_AGAIN) {
+    if (rc != NGX_OK) {
         return rc;
     }
 
--- a/src/http/modules/ngx_http_stub_status_module.c
+++ b/src/http/modules/ngx_http_stub_status_module.c
@@ -69,9 +69,9 @@ static ngx_int_t ngx_http_status_handler
         return NGX_HTTP_NOT_ALLOWED;
     }
 
-    rc = ngx_http_discard_body(r);
+    rc = ngx_http_discard_request_body(r);
 
-    if (rc != NGX_OK && rc != NGX_AGAIN) {
+    if (rc != NGX_OK) {
         return rc;
     }
 
--- a/src/http/modules/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -47,7 +47,7 @@ our @EXPORT = qw(
     HTTP_INSUFFICIENT_STORAGE
 );
 
-our $VERSION = '0.6.6';
+our $VERSION = '0.6.7';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/modules/perl/nginx.xs
+++ b/src/http/modules/perl/nginx.xs
@@ -425,6 +425,17 @@ request_body_file(r)
 
 
 void
+discard_request_body(r)
+    CODE:
+
+    ngx_http_request_t  *r;
+
+    ngx_http_perl_set_request(r);
+
+    ngx_http_discard_request_body(r);
+
+
+void
 header_out(r, key, value)
     CODE:
 
@@ -461,8 +472,6 @@ header_out(r, key, value)
         r->headers_out.content_length = header;
     }
 
-    XSRETURN_EMPTY;
-
 
 void
 filename(r)
@@ -589,8 +598,6 @@ print(r, ...)
 
     (void) ngx_http_perl_output(r, b);
 
-    XSRETURN_EMPTY;
-
 
 void
 sendfile(r, filename, offset = -1, bytes = 0)
@@ -677,8 +684,6 @@ sendfile(r, filename, offset = -1, bytes
 
     (void) ngx_http_perl_output(r, b);
 
-    XSRETURN_EMPTY;
-
 
 void
 flush(r)
@@ -744,8 +749,6 @@ allow_ranges(r)
 
     r->allow_ranges = 1;
 
-    XSRETURN_EMPTY;
-
 
 void
 unescape(r, text, type = 0)
@@ -942,8 +945,6 @@ sleep(r, sleep, next)
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "perl sleep: %d", ctx->sleep);
 
-    XSRETURN_EMPTY;
-
 
 void
 log_error(r, err, msg)
@@ -974,5 +975,3 @@ log_error(r, err, msg)
     p = (u_char *) SvPV(msg, len);
 
     ngx_log_error(NGX_LOG_ERR, r->connection->log, e, "perl: %s", p);
-
-    XSRETURN_EMPTY;
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -98,6 +98,9 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
     ngx_http_core_loc_conf_t    *clcf;
     ngx_http_phase_handler_pt    checker;
     ngx_http_core_main_conf_t   *cmcf;
+#if (NGX_PCRE)
+    ngx_uint_t                   regex;
+#endif
 #if (NGX_WIN32)
     ngx_iocp_conf_t             *iocpcf;
 #endif
@@ -655,10 +658,21 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
                 return NGX_CONF_ERROR;
             }
 
+#if (NGX_PCRE)
+            regex = 0;
+#endif
+
             name = in_addr[a].names.elts;
 
             for (s = 0; s < in_addr[a].names.nelts; s++) {
 
+#if (NGX_PCRE)
+                if (name[s].regex) {
+                    regex++;
+                    continue;
+                }
+#endif
+
                 rc = ngx_hash_add_key(&ha, &name[s].name, name[s].core_srv_conf,
                                       NGX_HASH_WILDCARD_KEY);
 
@@ -740,6 +754,27 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
             }
 
             ngx_destroy_pool(ha.temp_pool);
+
+#if (NGX_PCRE)
+
+            if (regex == 0) {
+                continue;
+            }
+
+            in_addr[a].nregex = regex;
+            in_addr[a].regex = ngx_palloc(cf->pool,
+                                       regex * sizeof(ngx_http_server_name_t));
+
+            if (in_addr[a].regex == NULL) {
+                return NGX_CONF_ERROR;
+            }
+
+            for (i = 0, s = 0; s < in_addr[a].names.nelts; s++) {
+                if (name[s].regex) {
+                    in_addr[a].regex[i++] = name[s];
+                }
+            }
+#endif
         }
 
         in_addr = in_port[p].addrs.elts;
@@ -871,9 +906,13 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
                 }
                 hip->addrs[i].virtual_names = vn;
 
-                vn->hash = in_addr[i].hash;
-                vn->wc_head = in_addr[i].wc_head;
-                vn->wc_tail = in_addr[i].wc_tail;
+                vn->names.hash = in_addr[i].hash;
+                vn->names.wc_head = in_addr[i].wc_head;
+                vn->names.wc_tail = in_addr[i].wc_tail;
+#if (NGX_PCRE)
+                vn->nregex = in_addr[i].nregex;
+                vn->regex = in_addr[i].regex;
+#endif
             }
 
             if (done) {
@@ -932,7 +971,8 @@ ngx_http_add_address(ngx_conf_t *cf, ngx
 
     if (in_port->addrs.elts == NULL) {
         if (ngx_array_init(&in_port->addrs, cf->temp_pool, 4,
-                           sizeof(ngx_http_conf_in_addr_t)) != NGX_OK)
+                           sizeof(ngx_http_conf_in_addr_t))
+            != NGX_OK)
         {
             return NGX_ERROR;
         }
@@ -949,6 +989,10 @@ ngx_http_add_address(ngx_conf_t *cf, ngx
     in_addr->wc_head = NULL;
     in_addr->wc_tail = NULL;
     in_addr->names.elts = NULL;
+#if (NGX_PCRE)
+    in_addr->nregex = 0;
+    in_addr->regex = NULL;
+#endif
     in_addr->core_srv_conf = cscf;
     in_addr->default_server = lscf->conf.default_server;
     in_addr->bind = lscf->conf.bind;
@@ -981,13 +1025,15 @@ ngx_http_add_names(ngx_conf_t *cf, ngx_h
 
     if (in_addr->names.elts == NULL) {
         if (ngx_array_init(&in_addr->names, cf->temp_pool, 4,
-                           sizeof(ngx_http_server_name_t)) != NGX_OK)
+                           sizeof(ngx_http_server_name_t))
+            != NGX_OK)
         {
             return NGX_ERROR;
         }
     }
 
     server_names = cscf->server_names.elts;
+
     for (i = 0; i < cscf->server_names.nelts; i++) {
 
         for (n = 0; n < server_names[i].name.len; n++) {
@@ -998,7 +1044,6 @@ ngx_http_add_names(ngx_conf_t *cf, ngx_h
         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, cf->log, 0,
                        "name: %V", &server_names[i].name);
 
-
         name = ngx_array_push(&in_addr->names);
         if (name == NULL) {
             return NGX_ERROR;
--- a/src/http/ngx_http.h
+++ b/src/http/ngx_http.h
@@ -98,7 +98,8 @@ size_t ngx_http_get_time(char *buf, time
 
 
 
-ngx_int_t ngx_http_discard_body(ngx_http_request_t *r);
+ngx_int_t ngx_http_discard_request_body(ngx_http_request_t *r);
+void ngx_http_block_reading(ngx_http_request_t *r);
 
 
 extern ngx_module_t  ngx_http_module;
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -933,14 +933,19 @@ ngx_http_core_find_location(ngx_http_req
     ngx_array_t *locations, ngx_uint_t regex_start, size_t len)
 {
     ngx_int_t                  n, rc;
-    ngx_uint_t                 i, found, noregex;
+    ngx_uint_t                 i, found;
     ngx_http_core_loc_conf_t  *clcf, **clcfp;
+#if (NGX_PCRE)
+    ngx_uint_t                 noregex;
+#endif
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "find location for \"%V\"", &r->uri);
 
     found = 0;
+#if (NGX_PCRE)
     noregex = 0;
+#endif
 
     clcfp = locations->elts;
     for (i = 0; i < locations->nelts; i++) {
@@ -998,9 +1003,12 @@ ngx_http_core_find_location(ngx_http_req
                 break;
             }
 
+            found = 1;
+
             r->loc_conf = clcfp[i]->loc_conf;
+#if (NGX_PCRE)
             noregex = clcfp[i]->noregex;
-            found = 1;
+#endif
         }
     }
 
@@ -2219,7 +2227,7 @@ ngx_http_core_merge_srv_conf(ngx_conf_t 
 #endif
         ls->family = AF_INET;
 
-        ls->conf.backlog = -1;
+        ls->conf.backlog = NGX_LISTEN_BACKLOG;
         ls->conf.rcvbuf = -1;
         ls->conf.sndbuf = -1;
     }
@@ -2570,7 +2578,7 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx
     ls->port = u.port;
     ls->file_name = cf->conf_file->file.name;
     ls->line = cf->conf_file->line;
-    ls->conf.backlog = -1;
+    ls->conf.backlog = NGX_LISTEN_BACKLOG;
     ls->conf.rcvbuf = -1;
     ls->conf.sndbuf = -1;
 
@@ -2692,6 +2700,10 @@ ngx_http_core_server_name(ngx_conf_t *cf
     ngx_str_t               *value, name;
     ngx_uint_t               i;
     ngx_http_server_name_t  *sn;
+#if (NGX_PCRE)
+    ngx_str_t                err;
+    u_char                   errstr[NGX_MAX_CONF_ERRSTR];
+#endif
 
     value = cf->args->elts;
 
@@ -2705,6 +2717,13 @@ ngx_http_core_server_name(ngx_conf_t *cf
             return NGX_CONF_ERROR;
         }
 
+        if (value[1].data[0] == '~') {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "first server name \"%V\" "
+                               "must not be regular expression", &value[1]);
+            return NGX_CONF_ERROR;
+        }
+
         name = value[1];
 
         if (ch == '.') {
@@ -2748,9 +2767,42 @@ ngx_http_core_server_name(ngx_conf_t *cf
             return NGX_CONF_ERROR;
         }
 
+#if (NGX_PCRE)
+        sn->regex = NULL;
+#endif
+        sn->core_srv_conf = cscf;
         sn->name.len = value[i].len;
         sn->name.data = value[i].data;
-        sn->core_srv_conf = cscf;
+
+        if (value[i].data[0] != '~') {
+            continue;
+        }
+
+#if (NGX_PCRE)
+        err.len = NGX_MAX_CONF_ERRSTR;
+        err.data = errstr;
+
+        value[i].len--;
+        value[i].data++;
+
+        sn->regex = ngx_regex_compile(&value[i], NGX_REGEX_CASELESS, cf->pool,
+                                      &err);
+
+        if (sn->regex == NULL) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data);
+            return NGX_CONF_ERROR;
+        }
+
+        sn->name.len = value[i].len;
+        sn->name.data = value[i].data;
+
+#else
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "the using of the regex \"%V\" "
+                           "requires PCRE library", &value[i]);
+
+        return NGX_CONF_ERROR;
+#endif
     }
 
     return NGX_CONF_OK;
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -151,8 +151,10 @@ typedef struct {
 
 typedef struct {
     in_addr_t                  addr;
+
     /* the default server configuration for this address:port */
     ngx_http_core_srv_conf_t  *core_srv_conf;
+
     ngx_http_virtual_names_t  *virtual_names;
 } ngx_http_in_addr_t;
 
@@ -180,6 +182,12 @@ typedef struct {
 
     ngx_array_t                names;      /* array of ngx_http_server_name_t */
 
+#if (NGX_PCRE)
+    ngx_uint_t                 nregex;
+    ngx_http_server_name_t    *regex;
+
+#endif
+
     /* the default server configuration for this address:port */
     ngx_http_core_srv_conf_t  *core_srv_conf;
 
@@ -190,10 +198,13 @@ typedef struct {
 } ngx_http_conf_in_addr_t;
 
 
-typedef struct {
+struct ngx_http_server_name_s {
+#if (NGX_PCRE)
+    ngx_regex_t               *regex;
+#endif
+    ngx_http_core_srv_conf_t  *core_srv_conf; /* virtual name server conf */
     ngx_str_t                  name;
-    ngx_http_core_srv_conf_t  *core_srv_conf; /* virtual name server conf */
-} ngx_http_server_name_t;
+};
 
 
 typedef struct {
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -33,8 +33,7 @@ static void ngx_http_request_handler(ngx
 static ngx_int_t ngx_http_set_write_handler(ngx_http_request_t *r);
 static void ngx_http_writer(ngx_http_request_t *r);
 
-static void ngx_http_block_read(ngx_http_request_t *r);
-static void ngx_http_test_read(ngx_http_request_t *r);
+static void ngx_http_test_reading(ngx_http_request_t *r);
 static void ngx_http_set_keepalive(ngx_http_request_t *r);
 static void ngx_http_keepalive_handler(ngx_event_t *ev);
 static void ngx_http_set_lingering_close(ngx_http_request_t *r);
@@ -1442,7 +1441,7 @@ ngx_http_process_request(ngx_http_reques
 
     c->read->handler = ngx_http_request_handler;
     c->write->handler = ngx_http_request_handler;
-    r->read_event_handler = ngx_http_block_read;
+    r->read_event_handler = ngx_http_block_reading;
 
     ngx_http_handler(r);
 
@@ -1454,18 +1453,56 @@ static void
 ngx_http_find_virtual_server(ngx_http_request_t *r, u_char *host, size_t len,
     ngx_uint_t hash)
 {
-    ngx_http_virtual_names_t  *vn;
     ngx_http_core_loc_conf_t  *clcf;
     ngx_http_core_srv_conf_t  *cscf;
-
-    vn = r->virtual_names;
-
-    cscf = ngx_hash_find_combined(vn, hash, host, len);
+#if (NGX_PCRE)
+    ngx_int_t                  n;
+    ngx_uint_t                 i;
+    ngx_str_t                  name;
+    ngx_http_server_name_t    *sn;
+#endif
+
+    cscf = ngx_hash_find_combined(&r->virtual_names->names, hash, host, len);
 
     if (cscf) {
         goto found;
     }
 
+#if (NGX_PCRE)
+
+    if (r->virtual_names->nregex) {
+
+        name.len = len;
+        name.data = host;
+
+        sn = r->virtual_names->regex;
+
+        for (i = 0; i < r->virtual_names->nregex; i++) {
+
+            n = ngx_regex_exec(sn[i].regex, &name, NULL, 0);
+
+            if (n == NGX_REGEX_NO_MATCHED) {
+                continue;
+            }
+
+            if (n < 0) {
+                ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                              ngx_regex_exec_n
+                              " failed: %d on \"%V\" using \"%V\"",
+                              n, &name, &sn[i].name);
+                return;
+            }
+
+            /* match */
+
+            cscf = sn[i].core_srv_conf;
+
+            goto found;
+        }
+    }
+
+#endif
+
     cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
 
     if (cscf->wildcard) {
@@ -1702,7 +1739,7 @@ ngx_http_set_write_handler(ngx_http_requ
 
     r->http_state = NGX_HTTP_WRITING_REQUEST_STATE;
 
-    r->read_event_handler = ngx_http_test_read;
+    r->read_event_handler = ngx_http_test_reading;
     r->write_event_handler = ngx_http_writer;
 
     wev = r->connection->write;
@@ -1812,11 +1849,11 @@ ngx_http_writer(ngx_http_request_t *r)
 }
 
 
-static void
-ngx_http_block_read(ngx_http_request_t *r)
+void
+ngx_http_block_reading(ngx_http_request_t *r)
 {
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "http read blocked");
+                   "http reading blocked");
 
     /* aio does not call this handler */
 
@@ -1833,7 +1870,7 @@ ngx_http_block_read(ngx_http_request_t *
 
 
 static void
-ngx_http_test_read(ngx_http_request_t *r)
+ngx_http_test_reading(ngx_http_request_t *r)
 {
     int                n;
     char               buf[1];
@@ -1844,7 +1881,7 @@ ngx_http_test_read(ngx_http_request_t *r
     c = r->connection;
     rev = c->read;
 
-    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http test read");
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http test reading");
 
 #if (NGX_HAVE_KQUEUE)
 
@@ -1922,8 +1959,16 @@ ngx_http_set_keepalive(ngx_http_request_
     c = r->connection;
     rev = c->read;
 
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "set http keepalive handler");
 
+    if (r->discard_body) {
+        r->lingering_time = ngx_time() + (time_t) (clcf->lingering_time / 1000);
+        ngx_add_timer(rev, clcf->lingering_timeout);
+        return;
+    }
+
     c->log->action = "closing request";
 
     hc = r->http_connection;
@@ -1967,8 +2012,6 @@ ngx_http_set_keepalive(ngx_http_request_
         }
     }
 
-    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
-
     ngx_http_request_done(r, 0);
 
     c->data = hc;
@@ -2381,7 +2424,7 @@ ngx_http_post_action(ngx_http_request_t 
     r->header_only = 1;
     r->post_action = 1;
 
-    r->read_event_handler = ngx_http_block_read;
+    r->read_event_handler = ngx_http_block_reading;
 
     ngx_http_internal_redirect(r, &clcf->post_action, NULL);
 
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -275,7 +275,15 @@ typedef struct {
 } ngx_http_connection_t;
 
 
-typedef ngx_hash_combined_t  ngx_http_virtual_names_t;
+typedef struct ngx_http_server_name_s  ngx_http_server_name_t;
+
+
+typedef struct {
+     ngx_hash_combined_t              names;
+
+     ngx_uint_t                       nregex;
+     ngx_http_server_name_t          *regex;
+} ngx_http_virtual_names_t;
 
 
 typedef void (*ngx_http_cleanup_pt)(void *data);
--- a/src/http/ngx_http_request_body.c
+++ b/src/http/ngx_http_request_body.c
@@ -14,8 +14,8 @@ static void ngx_http_read_client_request
 static ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r);
 static ngx_int_t ngx_http_write_request_body(ngx_http_request_t *r,
     ngx_chain_t *body);
-static void ngx_http_read_discarded_body_handler(ngx_http_request_t *r);
-static ngx_int_t ngx_http_read_discarded_body(ngx_http_request_t *r);
+static void ngx_http_read_discarded_request_body_handler(ngx_http_request_t *r);
+static ngx_int_t ngx_http_read_discarded_request_body(ngx_http_request_t *r);
 
 
 /*
@@ -425,7 +425,7 @@ ngx_http_write_request_body(ngx_http_req
 
 
 ngx_int_t
-ngx_http_discard_body(ngx_http_request_t *r)
+ngx_http_discard_request_body(ngx_http_request_t *r)
 {
     ssize_t       size;
     ngx_event_t  *rev;
@@ -442,12 +442,12 @@ ngx_http_discard_body(ngx_http_request_t
         ngx_del_timer(rev);
     }
 
-    r->discard_body = 1;
-
     if (r->headers_in.content_length_n <= 0) {
         return NGX_OK;
     }
 
+    r->discard_body = 1;
+
     size = r->header_in->last - r->header_in->pos;
 
     if (size) {
@@ -461,38 +461,87 @@ ngx_http_discard_body(ngx_http_request_t
         }
     }
 
-    r->read_event_handler = ngx_http_read_discarded_body_handler;
+    r->read_event_handler = ngx_http_read_discarded_request_body_handler;
 
     if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
-    return ngx_http_read_discarded_body(r);
+    (void) ngx_http_read_discarded_request_body(r);
+
+    return NGX_OK;
 }
 
 
 static void
-ngx_http_read_discarded_body_handler(ngx_http_request_t *r)
+ngx_http_read_discarded_request_body_handler(ngx_http_request_t *r)
 {
-    ngx_int_t  rc;
+    ngx_int_t                  rc;
+    ngx_msec_t                 timer;
+    ngx_event_t               *rev;
+    ngx_connection_t          *c;
+    ngx_http_core_loc_conf_t  *clcf;
+
+    c = r->connection;
+    rev = c->read;
 
-    rc = ngx_http_read_discarded_body(r);
+    if (rev->timedout) {
+        c->timedout = 1;
+        c->error = 1;
+        ngx_http_finalize_request(r, 0);
+        return;
+    }
 
-    if (rc == NGX_AGAIN) {
-        if (ngx_handle_read_event(r->connection->read, 0) == NGX_ERROR) {
-            ngx_http_finalize_request(r, rc);
+    if (r->lingering_time) {
+        timer = r->lingering_time - ngx_time();
+
+        if (timer <= 0) {
+            r->discard_body = 0;
+            ngx_http_finalize_request(r, 0);
             return;
         }
+
+    } else {
+        timer = 0;
+    }
+
+    rc = ngx_http_read_discarded_request_body(r);
+
+    if (rc == NGX_OK) {
+
+        r->discard_body = 0;
+
+        if (r->done) {
+            ngx_http_finalize_request(r, 0);
+        }
+
+        return;
     }
 
-    if (rc != NGX_OK) {
+    /* rc == NGX_AGAIN */
+
+    if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
         ngx_http_finalize_request(r, rc);
+        return;
+    }
+
+    if (timer) {
+
+        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+        timer *= 1000;
+
+        if (timer > clcf->lingering_timeout) {
+            timer = clcf->lingering_timeout;
+        }
+
+        ngx_add_timer(rev, timer);
     }
 }
 
 
 static ngx_int_t
-ngx_http_read_discarded_body(ngx_http_request_t *r)
+ngx_http_read_discarded_request_body(ngx_http_request_t *r)
 {
     size_t   size;
     ssize_t  n;
@@ -501,33 +550,30 @@ ngx_http_read_discarded_body(ngx_http_re
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http read discarded body");
 
-    if (r->headers_in.content_length_n == 0) {
-        return NGX_OK;
-    }
+    do {
+        if (r->headers_in.content_length_n == 0) {
+            r->read_event_handler = ngx_http_block_reading;
+            return NGX_OK;
+        }
 
-    size = (r->headers_in.content_length_n > NGX_HTTP_DISCARD_BUFFER_SIZE) ?
-                NGX_HTTP_DISCARD_BUFFER_SIZE:
-                (size_t) r->headers_in.content_length_n;
+        size = (r->headers_in.content_length_n > NGX_HTTP_DISCARD_BUFFER_SIZE) ?
+                   NGX_HTTP_DISCARD_BUFFER_SIZE:
+                   (size_t) r->headers_in.content_length_n;
 
-    n = r->connection->recv(r->connection, buffer, size);
-
-    if (n == NGX_ERROR) {
-
-        r->connection->error = 1;
+        n = r->connection->recv(r->connection, buffer, size);
 
-        /*
-         * if a client request body is discarded then we already set
-         * some HTTP response code for client and we can ignore the error
-         */
-
-        return NGX_OK;
-    }
+        if (n == NGX_ERROR) {
+            r->connection->error = 1;
+            return NGX_OK;
+        }
 
-    if (n == NGX_AGAIN) {
-        return NGX_AGAIN;
-    }
+        if (n == NGX_AGAIN) {
+            return NGX_AGAIN;
+        }
 
-    r->headers_in.content_length_n -= n;
+        r->headers_in.content_length_n -= n;
 
-    return NGX_OK;
+    } while (r->connection->read->ready);
+
+    return NGX_AGAIN;
 }
--- a/src/http/ngx_http_special_response.c
+++ b/src/http/ngx_http_special_response.c
@@ -327,7 +327,7 @@ ngx_http_special_response_handler(ngx_ht
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http special response: %d, \"%V\"", error, &r->uri);
 
-    rc = ngx_http_discard_body(r);
+    rc = ngx_http_discard_request_body(r);
 
     if (rc == NGX_HTTP_INTERNAL_SERVER_ERROR) {
         error = NGX_HTTP_INTERNAL_SERVER_ERROR;
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -565,9 +565,11 @@ ngx_http_upstream_connect(ngx_http_reque
 
     if (rc == NGX_BUSY) {
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no live upstreams");
+        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_NOLIVE);
+        return;
     }
 
-    if (rc == NGX_BUSY || rc == NGX_DECLINED) {
+    if (rc == NGX_DECLINED) {
         ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
         return;
     }
@@ -581,7 +583,7 @@ ngx_http_upstream_connect(ngx_http_reque
     c->write->handler = ngx_http_upstream_send_request_handler;
     c->read->handler = ngx_http_upstream_process_header;
 
-    c->sendfile = r->connection->sendfile;
+    c->sendfile &= r->connection->sendfile;
 
     c->pool = r->pool;
     c->read->log = c->write->log = c->log = r->connection->log;
@@ -2153,7 +2155,9 @@ ngx_http_upstream_next(ngx_http_request_
         state = NGX_PEER_FAILED;
     }
 
-    u->peer.free(&u->peer, u->peer.data, state);
+    if (ft_type != NGX_HTTP_UPSTREAM_FT_NOLIVE) {
+        u->peer.free(&u->peer, u->peer.data, state);
+    }
 
     if (ft_type == NGX_HTTP_UPSTREAM_FT_TIMEOUT) {
         ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_ETIMEDOUT,
@@ -3118,6 +3122,17 @@ ngx_http_upstream_server(ngx_conf_t *cf,
             continue;
         }
 
+        if (ngx_strncmp(value[i].data, "backup", 6) == 0) {
+
+            if (!(uscf->flags & NGX_HTTP_UPSTREAM_BACKUP)) {
+                goto invalid;
+            }
+
+            us->backup = 1;
+
+            continue;
+        }
+
         if (ngx_strncmp(value[i].data, "down", 4) == 0) {
 
             if (!(uscf->flags & NGX_HTTP_UPSTREAM_DOWN)) {
--- a/src/http/ngx_http_upstream.h
+++ b/src/http/ngx_http_upstream.h
@@ -24,6 +24,7 @@
 #define NGX_HTTP_UPSTREAM_FT_HTTP_404        0x00000040
 #define NGX_HTTP_UPSTREAM_FT_BUSY_LOCK       0x00000080
 #define NGX_HTTP_UPSTREAM_FT_MAX_WAITING     0x00000100
+#define NGX_HTTP_UPSTREAM_FT_NOLIVE          0x40000000
 #define NGX_HTTP_UPSTREAM_FT_OFF             0x80000000
 
 
--- a/src/http/ngx_http_upstream_round_robin.c
+++ b/src/http/ngx_http_upstream_round_robin.c
@@ -9,6 +9,7 @@
 #include <ngx_http.h>
 
 
+static int ngx_http_upstream_cmp_servers(const void *one, const void *two);
 static ngx_uint_t
 ngx_http_upstream_get_peer(ngx_http_upstream_rr_peers_t *peers);
 
@@ -20,15 +21,20 @@ ngx_http_upstream_init_round_robin(ngx_c
     ngx_url_t                      u;
     ngx_uint_t                     i, j, n;
     ngx_http_upstream_server_t    *server;
-    ngx_http_upstream_rr_peers_t  *peers;
+    ngx_http_upstream_rr_peers_t  *peers, *backup;
 
     us->peer.init = ngx_http_upstream_init_round_robin_peer;
 
     if (us->servers) {
-        n = 0;
         server = us->servers->elts;
 
+        n = 0;
+
         for (i = 0; i < us->servers->nelts; i++) {
+            if (server[i].backup) {
+                continue;
+            }
+
             n += server[i].naddrs;
         }
 
@@ -38,6 +44,7 @@ ngx_http_upstream_init_round_robin(ngx_c
             return NGX_ERROR;
         }
 
+        peers->single = (n == 1);
         peers->number = n;
         peers->name = &us->host;
 
@@ -45,20 +52,81 @@ ngx_http_upstream_init_round_robin(ngx_c
 
         for (i = 0; i < us->servers->nelts; i++) {
             for (j = 0; j < server[i].naddrs; j++) {
+                if (server[i].backup) {
+                    continue;
+                }
+
                 peers->peer[n].sockaddr = server[i].addrs[j].sockaddr;
                 peers->peer[n].socklen = server[i].addrs[j].socklen;
                 peers->peer[n].name = server[i].addrs[j].name;
-                peers->peer[n].weight = server[i].weight;
-                peers->peer[n].current_weight = server[i].weight;
                 peers->peer[n].max_fails = server[i].max_fails;
                 peers->peer[n].fail_timeout = server[i].fail_timeout;
                 peers->peer[n].down = server[i].down;
+                peers->peer[n].weight = server[i].down ? 0 : server[i].weight;
+                peers->peer[n].current_weight = peers->peer[n].weight;
                 n++;
             }
         }
 
         us->peer.data = peers;
 
+        ngx_sort(&peers->peer[0], (size_t) n,
+                 sizeof(ngx_http_upstream_rr_peer_t),
+                 ngx_http_upstream_cmp_servers);
+
+        /* backup servers */
+
+        n = 0;
+
+        for (i = 0; i < us->servers->nelts; i++) {
+            if (!server[i].backup) {
+                continue;
+            }
+
+            n += server[i].naddrs;
+        }
+
+        if (n == 0) {
+            return NGX_OK;
+        }
+
+        backup = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t)
+                              + sizeof(ngx_http_upstream_rr_peer_t) * (n - 1));
+        if (backup == NULL) {
+            return NGX_ERROR;
+        }
+
+        peers->single = 0;
+        backup->single = 0;
+        backup->number = n;
+        backup->name = &us->host;
+
+        n = 0;
+
+        for (i = 0; i < us->servers->nelts; i++) {
+            for (j = 0; j < server[i].naddrs; j++) {
+                if (!server[i].backup) {
+                    continue;
+                }
+
+                backup->peer[n].sockaddr = server[i].addrs[j].sockaddr;
+                backup->peer[n].socklen = server[i].addrs[j].socklen;
+                backup->peer[n].name = server[i].addrs[j].name;
+                backup->peer[n].weight = server[i].weight;
+                backup->peer[n].current_weight = server[i].weight;
+                backup->peer[n].max_fails = server[i].max_fails;
+                backup->peer[n].fail_timeout = server[i].fail_timeout;
+                backup->peer[n].down = server[i].down;
+                n++;
+            }
+        }
+
+        peers->next = backup;
+
+        ngx_sort(&backup->peer[0], (size_t) n,
+                 sizeof(ngx_http_upstream_rr_peer_t),
+                 ngx_http_upstream_cmp_servers);
+
         return NGX_OK;
     }
 
@@ -95,6 +163,7 @@ ngx_http_upstream_init_round_robin(ngx_c
         return NGX_ERROR;
     }
 
+    peers->single = (n == 1);
     peers->number = n;
     peers->name = &us->host;
 
@@ -113,10 +182,24 @@ ngx_http_upstream_init_round_robin(ngx_c
 
     us->peer.data = peers;
 
+    /* implicitly defined upstream has no backup servers */
+
     return NGX_OK;
 }
 
 
+static int
+ngx_http_upstream_cmp_servers(const void *one, const void *two)
+{
+    ngx_http_upstream_rr_peer_t  *first, *second;
+
+    first = (ngx_http_upstream_rr_peer_t *) one;
+    second = (ngx_http_upstream_rr_peer_t *) two;
+
+    return (first->weight < second->weight);
+}
+
+
 ngx_int_t
 ngx_http_upstream_init_round_robin_peer(ngx_http_request_t *r,
     ngx_http_upstream_srv_conf_t *us)
@@ -171,11 +254,13 @@ ngx_http_upstream_get_round_robin_peer(n
 {
     ngx_http_upstream_rr_peer_data_t  *rrp = data;
 
-    time_t                        now;
-    uintptr_t                     m;
-    ngx_uint_t                    i, n;
-    ngx_connection_t             *c;
-    ngx_http_upstream_rr_peer_t  *peer;
+    time_t                         now;
+    uintptr_t                      m;
+    ngx_int_t                      rc;
+    ngx_uint_t                     i, n;
+    ngx_connection_t              *c;
+    ngx_http_upstream_rr_peer_t   *peer;
+    ngx_http_upstream_rr_peers_t  *peers;
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
                    "get rr peer, try: %ui", pc->tries);
@@ -207,7 +292,7 @@ ngx_http_upstream_get_round_robin_peer(n
     pc->cached = 0;
     pc->connection = NULL;
 
-    if (rrp->peers->number == 1) {
+    if (rrp->peers->single) {
         peer = &rrp->peers->peer[0];
 
     } else {
@@ -319,19 +404,53 @@ ngx_http_upstream_get_round_robin_peer(n
 
     /* ngx_unlock_mutex(rrp->peers->mutex); */
 
+    if (pc->tries == 1 && rrp->peers->next) {
+        pc->tries += rrp->peers->next->number;
+
+        n = rrp->peers->next->number / (8 * sizeof(uintptr_t)) + 1;
+        for (i = 0; i < n; i++) {
+             rrp->tried[i] = 0;
+        }
+    }
+
     return NGX_OK;
 
 failed:
 
+    peers = rrp->peers;
+
+    if (peers->next) {
+
+        /* ngx_unlock_mutex(peers->mutex); */
+
+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, "backup servers");
+
+        rrp->peers = peers->next;
+        pc->tries = rrp->peers->number;
+
+        n = rrp->peers->number / (8 * sizeof(uintptr_t)) + 1;
+        for (i = 0; i < n; i++) {
+             rrp->tried[i] = 0;
+        }
+
+        rc = ngx_http_upstream_get_round_robin_peer(pc, rrp);
+
+        if (rc != NGX_BUSY) {
+            return rc;
+        }
+
+        /* ngx_lock_mutex(peers->mutex); */
+    }
+
     /* all peers failed, mark them as live for quick recovery */
 
-    for (i = 0; i < rrp->peers->number; i++) {
-        rrp->peers->peer[i].fails = 0;
+    for (i = 0; i < peers->number; i++) {
+        peers->peer[i].fails = 0;
     }
 
-    /* ngx_unlock_mutex(rrp->peers->mutex); */
+    /* ngx_unlock_mutex(peers->mutex); */
 
-    pc->name = rrp->peers->name;
+    pc->name = peers->name;
 
     return NGX_BUSY;
 }
@@ -410,7 +529,7 @@ ngx_http_upstream_free_round_robin_peer(
 
     /* TODO: NGX_PEER_KEEPALIVE */
 
-    if (rrp->peers->number == 1) {
+    if (rrp->peers->single) {
         pc->tries = 0;
         return;
     }
--- a/src/http/ngx_http_upstream_round_robin.h
+++ b/src/http/ngx_http_upstream_round_robin.h
@@ -35,7 +35,10 @@ typedef struct {
 } ngx_http_upstream_rr_peer_t;
 
 
-typedef struct {
+typedef struct ngx_http_upstream_rr_peers_s  ngx_http_upstream_rr_peers_t;
+
+struct ngx_http_upstream_rr_peers_s {
+    ngx_uint_t                      single;        /* unsigned  single:1; */
     ngx_uint_t                      number;
     ngx_uint_t                      last_cached;
 
@@ -44,8 +47,10 @@ typedef struct {
 
     ngx_str_t                      *name;
 
+    ngx_http_upstream_rr_peers_t   *next;
+
     ngx_http_upstream_rr_peer_t     peer[1];
-} ngx_http_upstream_rr_peers_t;
+};
 
 
 typedef struct {
--- a/src/mail/ngx_mail.c
+++ b/src/mail/ngx_mail.c
@@ -300,7 +300,7 @@ ngx_mail_block(ngx_conf_t *cf, ngx_comma
                 return NGX_CONF_ERROR;
             }
 
-            ls->backlog = -1;
+            ls->backlog = NGX_LISTEN_BACKLOG;
             ls->rcvbuf = -1;
             ls->sndbuf = -1;
 
--- a/src/mail/ngx_mail_auth_http_module.c
+++ b/src/mail/ngx_mail_auth_http_module.c
@@ -21,6 +21,9 @@ typedef struct {
     ngx_str_t                       header;
 
     ngx_array_t                    *headers;
+
+    u_char                         *file;
+    ngx_uint_t                      line;
 } ngx_mail_auth_http_conf_t;
 
 
@@ -1311,6 +1314,9 @@ ngx_mail_auth_http_create_conf(ngx_conf_
 
     ahcf->timeout = NGX_CONF_UNSET_MSEC;
 
+    ahcf->file = cf->conf_file->file.name.data;
+    ahcf->line = cf->conf_file->line;
+
     return ahcf;
 }
 
@@ -1330,6 +1336,14 @@ ngx_mail_auth_http_merge_conf(ngx_conf_t
         conf->peer = prev->peer;
         conf->host_header = prev->host_header;
         conf->uri = prev->uri;
+
+        if (conf->peer == NULL) {
+            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                          "no \"http_auth\" is defined for server in %s:%ui",
+                          conf->file, conf->line);
+
+            return NGX_CONF_ERROR;
+        }
     }
 
     ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000);
@@ -1383,11 +1397,18 @@ ngx_mail_auth_http(ngx_conf_t *cf, ngx_c
     u.uri_part = 1;
     u.one_addr = 1;
 
+    if (ngx_strncmp(u.url.data, "http://", 7) == 0) {
+        u.url.len -= 7;
+        u.url.data += 7;
+    }
+
     if (ngx_parse_url(cf, &u) != NGX_OK) {
         if (u.err) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                "%s in auth_http \"%V\"", u.err, &u.url);
         }
+
+        return NGX_CONF_ERROR;
     }
 
     ahcf->peer = u.addrs;
--- a/src/os/unix/ngx_freebsd_config.h
+++ b/src/os/unix/ngx_freebsd_config.h
@@ -77,6 +77,9 @@
 #endif
 
 
+#define NGX_LISTEN_BACKLOG        -1
+
+
 #if (defined SO_ACCEPTFILTER && !defined NGX_HAVE_DEFERRED_ACCEPT)
 #define NGX_HAVE_DEFERRED_ACCEPT  1
 #endif
--- a/src/os/unix/ngx_linux_config.h
+++ b/src/os/unix/ngx_linux_config.h
@@ -78,6 +78,9 @@ extern ssize_t sendfile(int s, int fd, i
 #endif
 
 
+#define NGX_LISTEN_BACKLOG        511
+
+
 #if defined TCP_DEFER_ACCEPT && !defined NGX_HAVE_DEFERRED_ACCEPT
 #define NGX_HAVE_DEFERRED_ACCEPT  1
 #endif
--- a/src/os/unix/ngx_posix_config.h
+++ b/src/os/unix/ngx_posix_config.h
@@ -88,6 +88,9 @@
 #endif
 
 
+#define NGX_LISTEN_BACKLOG  511
+
+
 #if (__FreeBSD__) && (__FreeBSD_version < 400017)
 
 #include <sys/param.h>          /* ALIGN() */
--- a/src/os/unix/ngx_solaris_config.h
+++ b/src/os/unix/ngx_solaris_config.h
@@ -82,6 +82,9 @@
 #endif
 
 
+#define NGX_LISTEN_BACKLOG           511
+
+
 #ifndef NGX_HAVE_INHERITED_NONBLOCK
 #define NGX_HAVE_INHERITED_NONBLOCK  1
 #endif