changeset 467:d67e93e97b4a

Merge with nginx 0.7.30.
author Maxim Dounin <mdounin@mdounin.ru>
date Sun, 11 Jan 2009 17:17:57 +0300
parents 4c92e29a7375 (current diff) 5da91f7cde93 (diff)
children 2580fe1c5a9a
files .hgtags auto/modules src/mail/ngx_mail.h src/mail/ngx_mail_auth_http_module.c src/mail/ngx_mail_handler.c src/mail/ngx_mail_imap_module.c src/mail/ngx_mail_parse.c src/mail/ngx_mail_proxy_module.c src/mail/ngx_mail_smtp_handler.c src/mail/ngx_mail_smtp_module.c
diffstat 80 files changed, 4644 insertions(+), 1668 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags
+++ b/.hgtags
@@ -211,3 +211,14 @@ a2a3905c04abe7ff22d33176682cdba5eb6cf186
 a8e3f1441eec1b608b77f39110e16f0b82b983d5 NGINX_0_7_17
 b246022ef4543cf779e3a0e040922d069c109171 NGINX_0_7_18
 4b19c15c95eb9f49f48295d139dcdfd77bb22f38 NGINX_0_7_19
+b4f69f2ef02c188a31125d0774191becd3c3a10d NGINX_0_7_20
+ff86d646f9df4cf9ec14662fe09269d57d9598ea NGINX_0_7_21
+ad0a34a8efa6b727cfb4bbc7d94cab0e882fd87b NGINX_0_7_22
+88d3e895bdf94af51c38b5da5a986ff942dd52c5 NGINX_0_7_23
+9da1d9d94d1860d5a209110197fe4826766faa60 NGINX_0_7_24
+e7dbea1ee115c746298309e05501fc13ce942547 NGINX_0_7_25
+21aff1b3da48c501503d5e47d373c6382413c5e4 NGINX_0_7_26
+dac47e9ef0d5fc6ec8cd1e0ff433c86e96d1a358 NGINX_0_7_27
+fd759445d8a890a9db4ab645581358fdce277908 NGINX_0_7_28
+49a0eb7ce20c1114dd25a73373b9ad5f77d02ed7 NGINX_0_7_29
+dc98ed169c03366ef89869d49da3b21b4b6663fe NGINX_0_7_30
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,148 @@
 
+Changes with nginx 0.7.30                                        24 Dec 2008
+
+    *) Bugfix: a segmentation fault occurred in worker process, if 
+       variables were used in the "fastcgi_pass" and "proxy_pass" 
+       directives and host name must be resolved; the bug had appeared in 
+       0.7.29.
+
+
+Changes with nginx 0.7.29                                        24 Dec 2008
+
+    *) Bugfix: the "fastcgi_pass" and "proxy_pass" directives did not 
+       support variables if unix domain sockets were used.
+
+    *) Bugfixes in subrequest processing; the bugs had appeared in 0.7.25.
+
+    *) Bugfix: a "100 Continue" response was issued for HTTP/1.0 requests; 
+       Thanks to Maxim Dounin.
+
+    *) Bugfix: in memory allocation in the ngx_http_gzip_filter_module on 
+       Cygwin.
+
+
+Changes with nginx 0.7.28                                        22 Dec 2008
+
+    *) Change: in memory allocation in the ngx_http_gzip_filter_module.
+
+    *) Change: the default "gzip_buffers" directive values have been 
+       changed to 32 4k or 16 8k from 4 4k/8k.
+
+
+Changes with nginx 0.7.27                                        15 Dec 2008
+
+    *) Feature: the "try_files" directive.
+
+    *) Feature: variables support in the "fastcgi_pass" directive.
+
+    *) Feature: now the $geo variable may get an address from a 
+       variable.
+       Thanks to Andrei Nigmatulin.
+
+    *) Feature: now a location's modifier may be used without space before 
+       name.
+
+    *) Feature: the $upstream_response_length variable.
+
+    *) Bugfix: now a "add_header" directive does not add an empty value.
+
+    *) Bugfix: if zero length static file was requested, then nginx just 
+       closed connection; the bug had appeared in 0.7.25.
+
+    *) Bugfix: a MOVE method could not move file in non-existent directory.
+
+    *) Bugfix: a segmentation fault occurred in worker process, if no one 
+       named location was defined in server, but some one was used in an 
+       error_page directive.
+       Thanks to Sergey Bochenkov.
+
+
+Changes with nginx 0.7.26                                        08 Dec 2008
+
+    *) Bugfix: in subrequest processing; the bug had appeared in 0.7.25.
+
+
+Changes with nginx 0.7.25                                        08 Dec 2008
+
+    *) Change: in subrequest processing.
+
+    *) Change: now POSTs without "Content-Length" header line are allowed.
+
+    *) Bugfix: now the "limit_req" and "limit_conn" directives log a 
+       prohibition reason.
+
+    *) Bugfix: in the "delete" parameter of the "geo" directive.
+
+
+Changes with nginx 0.7.24                                        01 Dec 2008
+
+    *) Feature: the "if_modified_since" directive.
+
+    *) Bugfix: nginx did not process a FastCGI server response, if the 
+       server send too many messages to stderr before response.
+
+    *) Bugfix: the "$cookie_..." variables did not work in the SSI and the 
+       perl module.
+
+
+Changes with nginx 0.7.23                                        27 Nov 2008
+
+    *) Feature: the "delete" and "ranges" parameters in the "geo" directive.
+
+    *) Feature: speeding up loading of geo base with large number of values.
+
+    *) Feature: decrease of memory required for geo base load.
+
+
+Changes with nginx 0.7.22                                        20 Nov 2008
+
+    *) Feature: the "none" parameter in the "smtp_auth" directive.
+       Thanks to Maxim Dounin.
+
+    *) Feature: the "$cookie_..." variables.
+
+    *) Bugfix: the "directio" directive did not work in XFS filesystem.
+
+    *) Bugfix: the resolver did not understand big DNS responses.
+       Thanks to Zyb.
+
+
+Changes with nginx 0.7.21                                        11 Nov 2008
+
+    *) Changes in the ngx_http_limit_req_module.
+
+    *) Feature: the EXSLT support in the ngx_http_xslt_module.
+       Thanks to Denis F. Latypoff.
+
+    *) Workaround: compatibility with glibc 2.3.
+       Thanks to Eric Benson and Maxim Dounin.
+
+    *) Bugfix: nginx could not run on MacOSX 10.4 and earlier; the bug had 
+       appeared in 0.7.6.
+
+
+Changes with nginx 0.7.20                                        10 Nov 2008
+
+    *) Changes in the ngx_http_gzip_filter_module.
+
+    *) Feature: the ngx_http_limit_req_module.
+
+    *) Bugfix: worker processes might exit on a SIGBUS signal on sparc and 
+       ppc platforms; the bug had appeared in 0.7.3.
+       Thanks to Maxim Dounin.
+
+    *) Bugfix: the "proxy_pass http://host/some:uri" directives did not 
+       work; the bug had appeared in 0.7.12.
+
+    *) Bugfix: in HTTPS mode requests might fail with the "bad write retry" 
+       error.
+
+    *) Bugfix: the ngx_http_secure_link_module did not work inside 
+       locations, whose names are less than 3 characters.
+
+    *) Bugfix: $server_addr variable might have no value.
+
+
 Changes with nginx 0.7.19                                        13 Oct 2008
 
     *) Bugfix: version number update.
@@ -1716,7 +1860,7 @@ Changes with nginx 0.3.55               
     *) Bugfix: if the request contained "//" or "/./" and escaped symbols 
        after them, then the proxied request was sent unescaped.
 
-    *) Bugfix: the $r->headers_in("Cookie") of the ngx_http_perl_module now 
+    *) Bugfix: the $r->header_in("Cookie") of the ngx_http_perl_module now 
        returns all "Cookie" header lines.
 
     *) Bugfix: a segmentation fault occurred if 
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,149 @@
 
+Изменения в nginx 0.7.30                                          24.12.2008
+
+    *) Исправление: в рабочем процессе происходил segmentation fault, если 
+       в директивах fastcgi_pass и proxy_pass использовались переменные и 
+       имя хоста должно было резолвиться; ошибка появилась в 0.7.29.
+
+
+Изменения в nginx 0.7.29                                          24.12.2008
+
+    *) Исправление: директивы fastcgi_pass и proxy_pass не поддерживали 
+       переменные при использовании unix domain сокетов.
+
+    *) Исправления в обработке подзапросов; ошибки появились в 0.7.25.
+
+    *) Исправление: ответ "100 Continue" выдавался для запросов версии 
+       HTTP/1.0; Спасибо Максиму Дунину.
+
+    *) Исправление: в выделении памяти в модуле ngx_http_gzip_filter_module 
+       под Cygwin.
+
+
+Изменения в nginx 0.7.28                                          22.12.2008
+
+    *) Изменение: в выделении памяти в модуле ngx_http_gzip_filter_module.
+
+    *) Изменение: значения по умолчанию для директивы gzip_buffers изменены 
+       с 4 4k/8k на 32 4k или 16 8k.
+
+
+Изменения в nginx 0.7.27                                          15.12.2008
+
+    *) Добавление: директива try_files.
+
+    *) Добавление: директива fastcgi_pass поддерживает переменные.
+
+    *) Добавление: теперь директива geo может брать адрес из переменной.
+       Спасибо Андрею Нигматулину.
+
+    *) Добавление: теперь модификатор location'а можно указывать без 
+       пробела перед названием.
+
+    *) Добавление: переменная $upstream_response_length.
+
+    *) Исправление: теперь директива add_header не добавляет пустое 
+       значение.
+
+    *) Исправление: при запросе файла нулевой длины nginx закрывал 
+       соединение, ничего не передав; ошибка появилась в 0.7.25.
+
+    *) Исправление: метод MOVE не мог перемещать файл в несуществующий 
+       каталог.
+
+    *) Исправление: если в сервере не был описан ни один именованный 
+       location, но такой location использовался в директиве error_page, то 
+       в рабочем процессе происходил segmentation fault.
+       Спасибо Сергею Боченкову.
+
+
+Изменения в nginx 0.7.26                                          08.12.2008
+
+    *) Исправление: в обработке подзапросов; ошибка появилась в 0.7.25.
+
+
+Изменения в nginx 0.7.25                                          08.12.2008
+
+    *) Изменение: в обработке подзапросов.
+
+    *) Изменение: теперь разрешаются POST'ы без строки "Content-Length" в 
+       заголовке запроса.
+
+    *) Исправление: теперь директивы limit_req и limit_conn указывают 
+       причину запрета запроса.
+
+    *) Исправление: в параметре delete директивы geo.
+
+
+Изменения в nginx 0.7.24                                          01.12.2008
+
+    *) Добавление: директива if_modified_since.
+
+    *) Исправление: nginx не обрабатывал ответ FastCGI-сервера, если перед 
+       ответом сервер передавал много сообщений в stderr.
+
+    *) Исправление: переменные "$cookie_..." не работали в SSI and в 
+       перловом модуле.
+
+
+Изменения в nginx 0.7.23                                          27.11.2008
+
+    *) Добавление: параметры delete и ranges в директиве geo.
+
+    *) Добавление: ускорение загрузки geo-базы с большим числом значений.
+
+    *) Добавление: уменьшение памяти, необходимой для загрузки geo-базы.
+
+
+Изменения в nginx 0.7.22                                          20.11.2008
+
+    *) Добавление: параметр none в директиве smtp_auth.
+       Спасибо Максиму Дунину.
+
+    *) Добавление: переменные "$cookie_...".
+
+    *) Исправление: директива directio не работала с файловой системой XFS.
+
+    *) Исправление: resolver не понимал большие DNS-ответы.
+       Спасибо Zyb.
+
+
+Изменения в nginx 0.7.21                                          11.11.2008
+
+    *) Изменения в модуле ngx_http_limit_req_module.
+
+    *) Добавление: поддержка EXSLT в модуле ngx_http_xslt_module.
+       Спасибо Денису Латыпову.
+
+    *) Изменение: совместимость с glibc 2.3.
+       Спасибо Eric Benson и Максиму Дунину.
+
+    *) Исправление: nginx не запускался на MacOSX 10.4 и более ранних; 
+       ошибка появилась в 0.7.6.
+
+
+Изменения в nginx 0.7.20                                          10.11.2008
+
+    *) Изменения в модуле ngx_http_gzip_filter_module.
+
+    *) Добавление: модуль ngx_http_limit_req_module.
+
+    *) Исправление: на платформах sparc и ppc рабочие процессы могли 
+       выходить по сигналу SIGBUS; ошибка появилась в 0.7.3.
+       Спасибо Максиму Дунину.
+
+    *) Исправление: директивы вида "proxy_pass http://host/some:uri" не 
+       работали; ошибка появилась в 0.7.12.
+
+    *) Исправление: при использовании HTTPS запросы могли завершаться с 
+       ошибкой "bad write retry".
+
+    *) Исправление: модуль ngx_http_secure_link_module не работал внутри 
+       location'ов с именами меньше 3 символов.
+
+    *) Исправление: переменная $server_addr могла не иметь значения.
+
+
 Изменения в nginx 0.7.19                                          13.10.2008
 
     *) Исправление: обновление номера версии.
@@ -1763,7 +1908,7 @@
        закодированные символы в виде "%XX", то проксируемый запрос 
        передавался незакодированным.
 
-    *) Исправление: метод $r->headers_in("Cookie") модуля 
+    *) Исправление: метод $r->header_in("Cookie") модуля 
        ngx_http_perl_module теперь возвращает все строки "Cookie" в 
        заголовке запроса.
 
--- a/auto/lib/libxslt/conf
+++ b/auto/lib/libxslt/conf
@@ -88,3 +88,68 @@ END
 
     exit 1
 fi
+
+
+    ngx_feature="libexslt"
+    ngx_feature_name=NGX_HAVE_EXSLT
+    ngx_feature_run=no
+    ngx_feature_incs="#include <libexslt/exslt.h>"
+    ngx_feature_path="/usr/include/libxml2"
+    ngx_feature_libs="-lexslt"
+    ngx_feature_test="exsltRegisterAll();"
+    . auto/feature
+
+if [ $ngx_found = no ]; then
+
+    # FreeBSD port
+
+    ngx_feature="libexslt in /usr/local/"
+    ngx_feature_path="/usr/local/include/libxml2 /usr/local/include"
+
+    if [ $NGX_RPATH = YES ]; then
+        ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lexslt"
+    else
+        ngx_feature_libs="-L/usr/local/lib -lexslt"
+    fi
+
+    . auto/feature
+fi
+
+
+if [ $ngx_found = no ]; then
+
+    # NetBSD port
+
+    ngx_feature="libexslt in /usr/pkg/"
+    ngx_feature_path="/usr/pkg/include/libxml2 /usr/local/include"
+
+    if [ $NGX_RPATH = YES ]; then
+        ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lexslt"
+    else
+        ngx_feature_libs="-L/usr/pkg/lib -lexslt"
+    fi
+
+    . auto/feature
+fi
+
+
+if [ $ngx_found = no ]; then
+
+    # MacPorts
+
+    ngx_feature="libexslt in /opt/local/"
+    ngx_feature_path="/opt/local/include/libxml2 /opt/local/include"
+
+    if [ $NGX_RPATH = YES ]; then
+        ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lexslt"
+    else
+        ngx_feature_libs="-L/opt/local/lib -lexslt"
+    fi
+
+    . auto/feature
+fi
+
+
+if [ $ngx_found = yes ]; then
+    CORE_LIBS="$CORE_LIBS -lexslt"
+fi
--- a/auto/modules
+++ b/auto/modules
@@ -203,6 +203,11 @@ if [ $HTTP_LIMIT_ZONE = YES ]; then
     HTTP_SRCS="$HTTP_SRCS $HTTP_LIMIT_ZONE_SRCS"
 fi
 
+if [ $HTTP_LIMIT_REQ = YES ]; then
+    HTTP_MODULES="$HTTP_MODULES $HTTP_LIMIT_REQ_MODULE"
+    HTTP_SRCS="$HTTP_SRCS $HTTP_LIMIT_REQ_SRCS"
+fi
+
 if [ $HTTP_REALIP = YES ]; then
     have=NGX_HTTP_REALIP . auto/have
     HTTP_MODULES="$HTTP_MODULES $HTTP_REALIP_MODULE"
--- a/auto/options
+++ b/auto/options
@@ -75,6 +75,7 @@ HTTP_FASTCGI=YES
 HTTP_PERL=NO
 HTTP_MEMCACHED=YES
 HTTP_LIMIT_ZONE=YES
+HTTP_LIMIT_REQ=YES
 HTTP_EMPTY_GIF=YES
 HTTP_BROWSER=YES
 HTTP_SECURE_LINK=NO
@@ -192,6 +193,7 @@ do
         --without-http_fastcgi_module)   HTTP_FASTCGI=NO            ;;
         --without-http_memcached_module) HTTP_MEMCACHED=NO          ;;
         --without-http_limit_zone_module) HTTP_LIMIT_ZONE=NO        ;;
+        --without-http_limit_req_module) HTTP_LIMIT_REQ=NO         ;;
         --without-http_empty_gif_module) HTTP_EMPTY_GIF=NO          ;;
         --without-http_browser_module)   HTTP_BROWSER=NO            ;;
         --without-http_upstream_ip_hash_module) HTTP_UPSTREAM_IP_HASH=NO ;;
@@ -310,6 +312,7 @@ cat << END
   --without-http_fastcgi_module      disable ngx_http_fastcgi_module
   --without-http_memcached_module    disable ngx_http_memcached_module
   --without-http_limit_zone_module   disable ngx_http_limit_zone_module
+  --without-http_limit_req_module    disable ngx_http_limit_req_module
   --without-http_empty_gif_module    disable ngx_http_empty_gif_module
   --without-http_browser_module      disable ngx_http_browser_module
   --without-http_upstream_ip_hash_module
--- a/auto/sources
+++ b/auto/sources
@@ -417,6 +417,10 @@ HTTP_LIMIT_ZONE_MODULE=ngx_http_limit_zo
 HTTP_LIMIT_ZONE_SRCS=src/http/modules/ngx_http_limit_zone_module.c
 
 
+HTTP_LIMIT_REQ_MODULE=ngx_http_limit_req_module
+HTTP_LIMIT_REQ_SRCS=src/http/modules/ngx_http_limit_req_module.c
+
+
 HTTP_EMPTY_GIF_MODULE=ngx_http_empty_gif_module
 HTTP_EMPTY_GIF_SRCS=src/http/modules/ngx_http_empty_gif_module.c
 
--- a/conf/nginx.conf
+++ b/conf/nginx.conf
@@ -18,7 +18,7 @@ http {
     include       mime.types;
     default_type  application/octet-stream;
 
-    #log_format  main  '$remote_addr - $remote_user [$time_local] $request '
+    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
     #                  '"$status" $body_bytes_sent "$http_referer" '
     #                  '"$http_user_agent" "$http_x_forwarded_for"';
 
--- a/src/core/nginx.c
+++ b/src/core/nginx.c
@@ -191,6 +191,8 @@ static char **ngx_os_environ;
 int ngx_cdecl
 main(int argc, char *const *argv)
 {
+    char             *p;
+    ssize_t           n;
     ngx_int_t         i;
     ngx_log_t        *log;
     ngx_cycle_t      *cycle, init_cycle;
@@ -240,23 +242,30 @@ main(int argc, char *const *argv)
     }
 
     if (ngx_show_version) {
-        ngx_write_fd(ngx_stderr_fileno, "nginx version: " NGINX_VER CRLF,
-                     sizeof("nginx version: " NGINX_VER CRLF) - 1);
+
+        p = "nginx version: " NGINX_VER CRLF;
+        n = sizeof("nginx version: " NGINX_VER CRLF) - 1;
+
+        if (ngx_write_fd(ngx_stderr_fileno, p, n) != n) {
+            return 1;
+        }
 
         if (ngx_show_configure) {
 #ifdef NGX_COMPILER
-            ngx_write_fd(ngx_stderr_fileno, "built by " NGX_COMPILER CRLF,
-                         sizeof("built by " NGX_COMPILER CRLF) - 1);
+            p = "built by " NGX_COMPILER CRLF;
+            n = sizeof("built by " NGX_COMPILER CRLF) - 1;
+
+            if (ngx_write_fd(ngx_stderr_fileno, p, n) != n) {
+                return 1;
+            }
 #endif
 
-#ifndef __WATCOMC__
-
-            /* OpenWatcomC could not build the long NGX_CONFIGURE string */
+            p = "configure arguments: " NGX_CONFIGURE CRLF;
+            n = sizeof("configure arguments :" NGX_CONFIGURE CRLF) - 1;
 
-            ngx_write_fd(ngx_stderr_fileno,
-                        "configure arguments: " NGX_CONFIGURE CRLF,
-                        sizeof("configure arguments :" NGX_CONFIGURE CRLF) - 1);
-#endif
+            if (ngx_write_fd(ngx_stderr_fileno, p, n) != n) {
+                return 1;
+            }
         }
 
         if (!ngx_test_config) {
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,7 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define NGINX_VERSION      "0.7.19"
+#define NGINX_VERSION      "0.7.30"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_buf.c
+++ b/src/core/ngx_buf.c
@@ -201,12 +201,6 @@ ngx_chain_update_chains(ngx_chain_t **fr
             break;
         }
 
-#if (NGX_HAVE_WRITE_ZEROCOPY)
-        if ((*busy)->buf->zerocopy_busy) {
-            break;
-        }
-#endif
-
         if ((*busy)->buf->tag != tag) {
             *busy = (*busy)->next;
             continue;
--- a/src/core/ngx_buf.h
+++ b/src/core/ngx_buf.h
@@ -51,8 +51,6 @@ struct ngx_buf_s {
     unsigned         last_shadow:1;
     unsigned         temp_file:1;
 
-    unsigned         zerocopy_busy:1;
-
     /* STUB */ int   num;
 };
 
--- a/src/core/ngx_conf_file.c
+++ b/src/core/ngx_conf_file.c
@@ -98,8 +98,8 @@ ngx_conf_parse(ngx_conf_t *cf, ngx_str_t
     char             *rv;
     ngx_fd_t          fd;
     ngx_int_t         rc;
-    ngx_buf_t        *b;
-    ngx_conf_file_t  *prev;
+    ngx_buf_t         buf;
+    ngx_conf_file_t  *prev, conf_file;
     enum {
         parse_file = 0,
         parse_block,
@@ -125,32 +125,24 @@ ngx_conf_parse(ngx_conf_t *cf, ngx_str_t
 
         prev = cf->conf_file;
 
-        cf->conf_file = ngx_palloc(cf->pool, sizeof(ngx_conf_file_t));
-        if (cf->conf_file == NULL) {
-            return NGX_CONF_ERROR;
-        }
+        cf->conf_file = &conf_file;
 
         if (ngx_fd_info(fd, &cf->conf_file->file.info) == -1) {
             ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno,
                           ngx_fd_info_n " \"%s\" failed", filename->data);
         }
 
-        b = ngx_calloc_buf(cf->pool);
-        if (b == NULL) {
-            return NGX_CONF_ERROR;
+        cf->conf_file->buffer = &buf;
+
+        buf.start = ngx_alloc(NGX_CONF_BUFFER, cf->log);
+        if (buf.start == NULL) {
+            goto failed;
         }
 
-        cf->conf_file->buffer = b;
-
-        b->start = ngx_alloc(NGX_CONF_BUFFER, cf->log);
-        if (b->start == NULL) {
-            return NGX_CONF_ERROR;
-        }
-
-        b->pos = b->start;
-        b->last = b->start;
-        b->end = b->last + NGX_CONF_BUFFER;
-        b->temporary = 1;
+        buf.pos = buf.start;
+        buf.last = buf.start;
+        buf.end = buf.last + NGX_CONF_BUFFER;
+        buf.temporary = 1;
 
         cf->conf_file->file.fd = fd;
         cf->conf_file->file.name.len = filename->len;
@@ -256,9 +248,9 @@ failed:
 done:
 
     if (filename) {
-        ngx_free(cf->conf_file->buffer->start);
-
-        cf->conf_file = prev;
+        if (cf->conf_file->buffer->start) {
+            ngx_free(cf->conf_file->buffer->start);
+        }
 
         if (ngx_close_file(fd) == NGX_FILE_ERROR) {
             ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
@@ -266,6 +258,8 @@ done:
                           cf->conf_file->file.name.data);
             return NGX_CONF_ERROR;
         }
+
+        cf->conf_file = prev;
     }
 
     if (rc == NGX_ERROR) {
@@ -834,7 +828,7 @@ ngx_conf_full_name(ngx_cycle_t *cycle, n
     name->len = len + old.len;
     name->data = ngx_pnalloc(cycle->pool, name->len + 1);
     if (name->data == NULL) {
-        return  NGX_ERROR;
+        return NGX_ERROR;
     }
 
     p = ngx_cpymem(name->data, prefix, len);
@@ -912,6 +906,7 @@ ngx_conf_open_file(ngx_cycle_t *cycle, n
 static void
 ngx_conf_flush_files(ngx_cycle_t *cycle)
 {
+    ssize_t           n, len;
     ngx_uint_t        i;
     ngx_list_part_t  *part;
     ngx_open_file_t  *file;
@@ -932,11 +927,24 @@ ngx_conf_flush_files(ngx_cycle_t *cycle)
             i = 0;
         }
 
-        if (file[i].buffer == NULL || file[i].pos - file[i].buffer == 0) {
+        len = file[i].pos - file[i].buffer;
+
+        if (file[i].buffer == NULL || len == 0) {
             continue;
         }
 
-        ngx_write_fd(file[i].fd, file[i].buffer, file[i].pos - file[i].buffer);
+        n = ngx_write_fd(file[i].fd, file[i].buffer, len);
+
+        if (n == NGX_FILE_ERROR) {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                          ngx_write_fd_n " to \"%s\" failed",
+                          file[i].name.data);
+
+        } else if (n != len) {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+                          ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz",
+                          file[i].name.data, n, len);
+        }
     }
 }
 
@@ -945,31 +953,47 @@ void ngx_cdecl
 ngx_conf_log_error(ngx_uint_t level, ngx_conf_t *cf, ngx_err_t err,
     char *fmt, ...)
 {
-    u_char   errstr[NGX_MAX_CONF_ERRSTR], *buf, *last;
+    u_char   errstr[NGX_MAX_CONF_ERRSTR], *p, *last;
     va_list  args;
 
     last = errstr + NGX_MAX_CONF_ERRSTR;
 
     va_start(args, fmt);
-    buf = ngx_vsnprintf(errstr, last - errstr, fmt, args);
+    p = ngx_vsnprintf(errstr, last - errstr, fmt, args);
     va_end(args);
 
-    *buf = '\0';
+    if (err) {
+
+        if (p > last - 50) {
+
+            /* leave a space for an error code */
 
-    if (err) {
-        buf = ngx_snprintf(buf, last - buf - 1, " (%d: ", err);
-        buf = ngx_strerror_r(err, buf, last - buf - 1);
-        *buf++ = ')';
-        *buf = '\0';
+            p = last - 50;
+            *p++ = '.';
+            *p++ = '.';
+            *p++ = '.';
+        }
+
+#if (NGX_WIN32)
+        p = ngx_snprintf(p, last - p, ((unsigned) err < 0x80000000)
+                                           ? " (%d: " : " (%Xd: ", err);
+#else
+        p = ngx_snprintf(p, last - p, " (%d: ", err);
+#endif
+
+        p = ngx_strerror_r(err, p, last - p);
+
+        *p++ = ')';
     }
 
     if (cf->conf_file == NULL) {
-        ngx_log_error(level, cf->log, 0, "%s", errstr);
+        ngx_log_error(level, cf->log, 0, "%*s", p - errstr, errstr);
         return;
     }
 
-    ngx_log_error(level, cf->log, 0, "%s in %s:%ui",
-                  errstr, cf->conf_file->file.name.data, cf->conf_file->line);
+    ngx_log_error(level, cf->log, 0, "%*s in %s:%ui",
+                  p - errstr, errstr,
+                  cf->conf_file->file.name.data, cf->conf_file->line);
 }
 
 
--- a/src/core/ngx_conf_file.h
+++ b/src/core/ngx_conf_file.h
@@ -71,7 +71,7 @@
 #define NGX_CONF_MODULE      0x464E4F43  /* "CONF" */
 
 
-#define NGX_MAX_CONF_ERRSTR  256
+#define NGX_MAX_CONF_ERRSTR  1024
 
 
 struct ngx_command_s {
--- a/src/core/ngx_cycle.c
+++ b/src/core/ngx_cycle.c
@@ -996,6 +996,7 @@ ngx_test_lockfile(u_char *file, ngx_log_
 void
 ngx_reopen_files(ngx_cycle_t *cycle, ngx_uid_t user)
 {
+    ssize_t           n, len;
     ngx_fd_t          fd;
     ngx_uint_t        i;
     ngx_list_part_t  *part;
@@ -1019,9 +1020,23 @@ ngx_reopen_files(ngx_cycle_t *cycle, ngx
             continue;
         }
 
-        if (file[i].buffer && file[i].pos - file[i].buffer != 0) {
-            ngx_write_fd(file[i].fd, file[i].buffer,
-                         file[i].pos - file[i].buffer);
+        len = file[i].pos - file[i].buffer;
+
+        if (file[i].buffer && len != 0) {
+
+            n = ngx_write_fd(file[i].fd, file[i].buffer, len);
+
+            if (n == NGX_FILE_ERROR) {
+                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                              ngx_write_fd_n " to \"%s\" failed",
+                              file[i].name.data);
+
+            } else if (n != len) {
+                ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+                          ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz",
+                          file[i].name.data, n, len);
+            }
+
             file[i].pos = file[i].buffer;
         }
 
--- a/src/core/ngx_file.c
+++ b/src/core/ngx_file.c
@@ -487,11 +487,13 @@ ngx_ext_rename_file(ngx_str_t *src, ngx_
 
 #if !(NGX_WIN32)
 
-    if (ngx_change_file_access(src->data, ext->access) == NGX_FILE_ERROR) {
-        ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
-                      ngx_change_file_access_n " \"%s\" failed", src->data);
-        err = 0;
-        goto failed;
+    if (ext->access) {
+        if (ngx_change_file_access(src->data, ext->access) == NGX_FILE_ERROR) {
+            ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
+                          ngx_change_file_access_n " \"%s\" failed", src->data);
+            err = 0;
+            goto failed;
+        }
     }
 
 #endif
@@ -517,7 +519,7 @@ ngx_ext_rename_file(ngx_str_t *src, ngx_
             goto failed;
         }
 
-        err = ngx_create_full_path(to->data, ngx_dir_access(ext->access));
+        err = ngx_create_full_path(to->data, ngx_dir_access(ext->path_access));
 
         if (err) {
             ngx_log_error(NGX_LOG_CRIT, ext->log, err,
@@ -561,12 +563,14 @@ failed:
         }
     }
 
-    if (err) {
+    if (err && ext->log_rename_error) {
         ngx_log_error(NGX_LOG_CRIT, ext->log, err,
                       ngx_rename_file_n " \"%s\" to \"%s\" failed",
                       src->data, to->data);
     }
 
+    ext->rename_error = err;
+
     return NGX_ERROR;
 }
 
--- a/src/core/ngx_file.h
+++ b/src/core/ngx_file.h
@@ -60,11 +60,14 @@ typedef struct {
 
 typedef struct {
     ngx_uint_t          access;
+    ngx_uint_t          path_access;
     time_t              time;
     ngx_fd_t            fd;
+    ngx_err_t           rename_error;
 
     unsigned            create_path:1;
     unsigned            delete_file:1;
+    unsigned            log_rename_error:1;
 
     ngx_log_t          *log;
 } ngx_ext_rename_file_t;
--- a/src/core/ngx_inet.c
+++ b/src/core/ngx_inet.c
@@ -251,7 +251,7 @@ ngx_parse_unix_domain_url(ngx_pool_t *po
 static ngx_int_t
 ngx_parse_inet_url(ngx_pool_t *pool, ngx_url_t *u)
 {
-    u_char          *p, *host, *port, *last, *uri;
+    u_char          *p, *host, *port, *last, *uri, *args;
     size_t           len;
     ngx_int_t        n;
     struct hostent  *h;
@@ -264,7 +264,18 @@ ngx_parse_inet_url(ngx_pool_t *pool, ngx
 
     port = ngx_strlchr(host, last, ':');
 
-    uri = ngx_strlchr(port ? port : host, last, '/');
+    uri = ngx_strlchr(host, last, '/');
+
+    args = ngx_strlchr(host, last, '?');
+
+    if (args) {
+        if (uri == NULL) {
+            uri = args;
+
+        } else if (args < uri) {
+            uri = args;
+        }
+    }
 
     if (uri) {
         if (u->listen || !u->uri_part) {
@@ -276,6 +287,10 @@ ngx_parse_inet_url(ngx_pool_t *pool, ngx
         u->uri.data = uri;
 
         last = uri;
+
+        if (uri < port) {
+            port = NULL;
+        }
     }
 
     if (port) {
--- a/src/core/ngx_log.c
+++ b/src/core/ngx_log.c
@@ -127,18 +127,10 @@ ngx_log_error_core(ngx_uint_t level, ngx
         }
 
 #if (NGX_WIN32)
-
-        if ((unsigned) err >= 0x80000000) {
-            p = ngx_snprintf(p, last - p, " (%Xd: ", err);
-
-        } else {
-            p = ngx_snprintf(p, last - p, " (%d: ", err);
-        }
-
+        p = ngx_snprintf(p, last - p, ((unsigned) err < 0x80000000)
+                                           ? " (%d: " : " (%Xd: ", err);
 #else
-
         p = ngx_snprintf(p, last - p, " (%d: ", err);
-
 #endif
 
         p = ngx_strerror_r(err, p, last - p);
@@ -158,7 +150,7 @@ ngx_log_error_core(ngx_uint_t level, ngx
 
     ngx_linefeed(p);
 
-    ngx_write_fd(log->file->fd, errstr, p - errstr);
+    (void) ngx_write_fd(log->file->fd, errstr, p - errstr);
 }
 
 
--- a/src/core/ngx_open_file_cache.h
+++ b/src/core/ngx_open_file_cache.h
@@ -12,6 +12,9 @@
 #define _NGX_OPEN_FILE_CACHE_H_INCLUDED_
 
 
+#define NGX_OPEN_FILE_DIRECTIO_OFF  NGX_MAX_OFF_T_VALUE
+
+
 typedef struct {
     ngx_fd_t                 fd;
     ngx_file_uniq_t          uniq;
--- a/src/core/ngx_output_chain.c
+++ b/src/core/ngx_output_chain.c
@@ -13,6 +13,17 @@
 #define NGX_SENDFILE_LIMIT  4096
 #endif
 
+/*
+ * When DIRECTIO is enabled FreeBSD, Solaris, and MacOSX read directly
+ * to an application memory from a device if parameters are aligned
+ * to device sector boundary(512 bytes).  They fallback to usual read
+ * operation if the parameters are not aligned.
+ * Linux allows DIRECTIO only if the parameters are aligned to a filesystem
+ * sector boundary, otherwise it returns EINVAL.  The sector size is
+ * usually 512 bytes, however, on XFS it may be 4096 bytes.
+ */
+#define NGX_DIRECTIO_BLOCK  4096
+
 
 #define NGX_NONE            1
 
@@ -327,7 +338,7 @@ ngx_output_chain_align_file_buf(ngx_outp
 
     ctx->directio = 1;
 
-    size = (size_t) (in->file_pos - (in->file_pos & ~511));
+    size = (size_t) (in->file_pos - (in->file_pos & ~(NGX_DIRECTIO_BLOCK - 1)));
 
     if (size == 0) {
 
@@ -338,7 +349,7 @@ ngx_output_chain_align_file_buf(ngx_outp
         size = (size_t) bsize;
 
     } else {
-        size = 512 - size;
+        size = NGX_DIRECTIO_BLOCK - size;
 
         if ((off_t) size > bsize) {
             size = (size_t) bsize;
@@ -413,7 +424,7 @@ ngx_output_chain_get_buf(ngx_output_chai
          * userland buffer direct usage conjunctly with directio
          */
 
-        b->start = ngx_pmemalign(ctx->pool, size, 512);
+        b->start = ngx_pmemalign(ctx->pool, size, NGX_DIRECTIO_BLOCK);
         if (b->start == NULL) {
             return NGX_ERROR;
         }
--- a/src/core/ngx_palloc.c
+++ b/src/core/ngx_palloc.c
@@ -91,6 +91,26 @@ ngx_destroy_pool(ngx_pool_t *pool)
 }
 
 
+void
+ngx_reset_pool(ngx_pool_t *pool)
+{
+    ngx_pool_t        *p;
+    ngx_pool_large_t  *l;
+
+    for (l = pool->large; l; l = l->next) {
+        if (l->alloc) {
+            ngx_free(l->alloc);
+        }
+    }
+
+    pool->large = NULL;
+
+    for (p = pool; p; p = p->d.next) {
+        p->d.last = (u_char *) p + sizeof(ngx_pool_t);
+    }
+}
+
+
 void *
 ngx_palloc(ngx_pool_t *pool, size_t size)
 {
@@ -171,6 +191,7 @@ ngx_palloc_block(ngx_pool_t *pool, size_
     new->d.next = NULL;
 
     m += sizeof(ngx_pool_data_t);
+    m = ngx_align_ptr(m, NGX_ALIGNMENT);
     new->d.last = m + size;
 
     current = pool->current;
--- a/src/core/ngx_palloc.h
+++ b/src/core/ngx_palloc.h
@@ -14,7 +14,6 @@
 
 /*
  * NGX_MAX_ALLOC_FROM_POOL should be (ngx_pagesize - 1), i.e. 4095 on x86.
- * On FreeBSD 5.x it allows to use the zero copy sending.
  * On Windows NT it decreases a number of locked pages in a kernel.
  */
 #define NGX_MAX_ALLOC_FROM_POOL  (ngx_pagesize - 1)
@@ -73,6 +72,7 @@ void *ngx_calloc(size_t size, ngx_log_t 
 
 ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log);
 void ngx_destroy_pool(ngx_pool_t *pool);
+void ngx_reset_pool(ngx_pool_t *pool);
 
 void *ngx_palloc(ngx_pool_t *pool, size_t size);
 void *ngx_pnalloc(ngx_pool_t *pool, size_t size);
--- a/src/core/ngx_radix_tree.c
+++ b/src/core/ngx_radix_tree.c
@@ -42,13 +42,13 @@ ngx_radix_tree_create(ngx_pool_t *pool, 
     }
 
     /*
-     * The preallocation the first nodes: 0, 1, 00, 01, 10, 11, 000, 001, etc.
-     * increases the TLB hits even if for the first lookup iterations.
-     * On the 32-bit platforms the 7 preallocated bits takes continuous 4K,
-     * 8 - 8K, 9 - 16K, etc.  On the 64-bit platforms the 6 preallocated bits
+     * Preallocation of first nodes : 0, 1, 00, 01, 10, 11, 000, 001, etc.
+     * increases TLB hits even if for first lookup iterations.
+     * On 32-bit platforms the 7 preallocated bits takes continuous 4K,
+     * 8 - 8K, 9 - 16K, etc.  On 64-bit platforms the 6 preallocated bits
      * takes continuous 4K, 7 - 8K, 8 - 16K, etc.  There is no sense to
      * to preallocate more than one page, because further preallocation
-     * distributes the only bit per page.  Instead, the random insertion
+     * distributes the only bit per page.  Instead, a random insertion
      * may distribute several bits per page.
      *
      * Thus, by default we preallocate maximum
--- a/src/core/ngx_resolver.c
+++ b/src/core/ngx_resolver.c
@@ -956,11 +956,14 @@ ngx_resolver_process_response(ngx_resolv
 {
     char                  *err;
     size_t                 len;
-    ngx_uint_t             i, ident, flags, code, nqs, nan, qtype, qclass;
+    ngx_uint_t             i, times, ident, qident, flags, code, nqs, nan,
+                           qtype, qclass;
+    ngx_queue_t           *q;
     ngx_resolver_qs_t     *qs;
+    ngx_resolver_node_t   *rn;
     ngx_resolver_query_t  *query;
 
-    if ((size_t) n < sizeof(ngx_resolver_query_t) + 1) {
+    if ((size_t) n < sizeof(ngx_resolver_query_t)) {
         goto short_response;
     }
 
@@ -985,11 +988,31 @@ ngx_resolver_process_response(ngx_resolv
 
     code = flags & 0x7f;
 
-    if (code == NGX_RESOLVE_FORMERR || code > NGX_RESOLVE_REFUSED) {
-        ngx_log_error(r->log_level, r->log, 0,
-                      "DNS error (%ui: %s), query id:%ui",
-                      code, ngx_resolver_strerror(code), ident);
-        return;
+    if (code == NGX_RESOLVE_FORMERR) {
+
+        times = 0;
+
+        for (q = ngx_queue_head(&r->name_resend_queue);
+             q != ngx_queue_sentinel(&r->name_resend_queue) || times++ < 100;
+             q = ngx_queue_next(q))
+        {
+            rn = ngx_queue_data(q, ngx_resolver_node_t, queue);
+            qident = (rn->query[0] << 8) + rn->query[1];
+
+            if (qident == ident) {
+                ngx_log_error(r->log_level, r->log, 0,
+                              "DNS error (%ui: %s), query id:%ui, name:\"%*s\"",
+                              code, ngx_resolver_strerror(code), ident,
+                              rn->nlen, rn->name);
+                return;
+            }
+        }
+
+        goto dns_error;
+    }
+
+    if (code > NGX_RESOLVE_REFUSED) {
+        goto dns_error;
     }
 
     if (nqs != 1) {
@@ -1069,6 +1092,13 @@ done:
     ngx_log_error(r->log_level, r->log, 0, err);
 
     return;
+
+dns_error:
+
+    ngx_log_error(r->log_level, r->log, 0,
+                  "DNS error (%ui: %s), query id:%ui",
+                  code, ngx_resolver_strerror(code), ident);
+    return;
 }
 
 
@@ -1836,7 +1866,7 @@ ngx_resolver_copy(ngx_resolver_t *r, ngx
         }
 
         if (n & 0xc0) {
-            n = (n & 0x3f << 8) + *p;
+            n = ((n & 0x3f) << 8) + *p;
             p = &buf[n];
 
         } else {
@@ -1886,7 +1916,7 @@ done:
             }
 
         } else {
-            n = (n & 0x3f << 8) + *src;
+            n = ((n & 0x3f) << 8) + *src;
             src = &buf[n];
 
             n = *src++;
--- a/src/core/ngx_string.c
+++ b/src/core/ngx_string.c
@@ -8,6 +8,10 @@
 #include <ngx_core.h>
 
 
+static u_char *ngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64,
+    u_char zero, ngx_uint_t hexadecimal, ngx_uint_t width);
+
+
 void
 ngx_strlow(u_char *dst, u_char *src, size_t n)
 {
@@ -67,6 +71,7 @@ ngx_pstrdup(ngx_pool_t *pool, ngx_str_t 
  *    %[0][width][u][x|X]D      int32_t/uint32_t
  *    %[0][width][u][x|X]L      int64_t/uint64_t
  *    %[0][width|m][u][x|X]A    ngx_atomic_int_t/ngx_atomic_uint_t
+ *    %[0][width][.width]f      float
  *    %P                        ngx_pid_t
  *    %M                        ngx_msec_t
  *    %r                        rlim_t
@@ -118,22 +123,16 @@ ngx_snprintf(u_char *buf, size_t max, co
 u_char *
 ngx_vsnprintf(u_char *buf, size_t max, const char *fmt, va_list args)
 {
-    u_char                *p, zero, *last, temp[NGX_INT64_LEN + 1];
-                                    /*
-                                     * really we need temp[NGX_INT64_LEN] only,
-                                     * but icc issues the warning
-                                     */
+    u_char                *p, zero, *last;
     int                    d;
+    float                  f, scale;
     size_t                 len, slen;
-    uint32_t               ui32;
     int64_t                i64;
     uint64_t               ui64;
     ngx_msec_t             ms;
-    ngx_uint_t             width, sign, hexadecimal, max_width;
+    ngx_uint_t             width, sign, hex, max_width, frac_width, i;
     ngx_str_t             *v;
     ngx_variable_value_t  *vv;
-    static u_char          hex[] = "0123456789abcdef";
-    static u_char          HEX[] = "0123456789ABCDEF";
 
     if (max == 0) {
         return buf;
@@ -156,12 +155,11 @@ ngx_vsnprintf(u_char *buf, size_t max, c
             zero = (u_char) ((*++fmt == '0') ? '0' : ' ');
             width = 0;
             sign = 1;
-            hexadecimal = 0;
+            hex = 0;
             max_width = 0;
+            frac_width = 0;
             slen = (size_t) -1;
 
-            p = temp + NGX_INT64_LEN;
-
             while (*fmt >= '0' && *fmt <= '9') {
                 width = width * 10 + *fmt++ - '0';
             }
@@ -181,17 +179,26 @@ ngx_vsnprintf(u_char *buf, size_t max, c
                     continue;
 
                 case 'X':
-                    hexadecimal = 2;
+                    hex = 2;
                     sign = 0;
                     fmt++;
                     continue;
 
                 case 'x':
-                    hexadecimal = 1;
+                    hex = 1;
                     sign = 0;
                     fmt++;
                     continue;
 
+                case '.':
+                    fmt++;
+
+                    while (*fmt >= '0' && *fmt <= '9') {
+                        frac_width = frac_width * 10 + *fmt++ - '0';
+                    }
+
+                    break;
+
                 case '*':
                     slen = va_arg(args, size_t);
                     fmt++;
@@ -339,6 +346,43 @@ ngx_vsnprintf(u_char *buf, size_t max, c
 
                 break;
 
+            case 'f':
+                f = (float) va_arg(args, double);
+
+                if (f < 0) {
+                    *buf++ = '-';
+                    f = -f;
+                }
+
+                ui64 = (int64_t) f;
+
+                buf = ngx_sprintf_num(buf, last, ui64, zero, 0, width);
+
+                if (frac_width) {
+
+                    if (buf < last) {
+                        *buf++ = '.';
+                    }
+
+                    scale = 1.0;
+
+                    for (i = 0; i < frac_width; i++) {
+                        scale *= 10.0;
+                    }
+
+                    /*
+                     * (int64_t) cast is required for msvc6:
+                     * it can not convert uint64_t to double
+                     */
+                    ui64 = (uint64_t) ((f - (int64_t) ui64) * scale);
+
+                    buf = ngx_sprintf_num(buf, last, ui64, '0', 0, frac_width);
+                }
+
+                fmt++;
+
+                continue;
+
 #if !(NGX_WIN32)
             case 'r':
                 i64 = (int64_t) va_arg(args, rlim_t);
@@ -348,7 +392,7 @@ ngx_vsnprintf(u_char *buf, size_t max, c
 
             case 'p':
                 ui64 = (uintptr_t) va_arg(args, void *);
-                hexadecimal = 2;
+                hex = 2;
                 sign = 0;
                 zero = '0';
                 width = NGX_PTR_SIZE * 2;
@@ -398,63 +442,7 @@ ngx_vsnprintf(u_char *buf, size_t max, c
                 }
             }
 
-            if (hexadecimal == 1) {
-                do {
-
-                    /* the "(uint32_t)" cast disables the BCC's warning */
-                    *--p = hex[(uint32_t) (ui64 & 0xf)];
-
-                } while (ui64 >>= 4);
-
-            } else if (hexadecimal == 2) {
-                do {
-
-                    /* the "(uint32_t)" cast disables the BCC's warning */
-                    *--p = HEX[(uint32_t) (ui64 & 0xf)];
-
-                } while (ui64 >>= 4);
-
-            } else if (ui64 <= NGX_MAX_UINT32_VALUE) {
-
-                /*
-                 * To divide 64-bit number and to find the remainder
-                 * on the x86 platform gcc and icc call the libc functions
-                 * [u]divdi3() and [u]moddi3(), they call another function
-                 * in its turn.  On FreeBSD it is the qdivrem() function,
-                 * its source code is about 170 lines of the code.
-                 * The glibc counterpart is about 150 lines of the code.
-                 *
-                 * For 32-bit numbers and some divisors gcc and icc use
-                 * the inlined multiplication and shifts.  For example,
-                 * unsigned "i32 / 10" is compiled to
-                 *
-                 *     (i32 * 0xCCCCCCCD) >> 35
-                 */
-
-                ui32 = (uint32_t) ui64;
-
-                do {
-                    *--p = (u_char) (ui32 % 10 + '0');
-                } while (ui32 /= 10);
-
-            } else {
-                do {
-                    *--p = (u_char) (ui64 % 10 + '0');
-                } while (ui64 /= 10);
-            }
-
-            len = (temp + NGX_INT64_LEN) - p;
-
-            while (len++ < width && buf < last) {
-                *buf++ = zero;
-            }
-
-            len = (temp + NGX_INT64_LEN) - p;
-            if (buf + len > last) {
-                len = last - buf;
-            }
-
-            buf = ngx_cpymem(buf, p, len);
+            buf = ngx_sprintf_num(buf, last, ui64, zero, hex, width);
 
             fmt++;
 
@@ -467,6 +455,92 @@ ngx_vsnprintf(u_char *buf, size_t max, c
 }
 
 
+static u_char *
+ngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64, u_char zero,
+    ngx_uint_t hexadecimal, ngx_uint_t width)
+{
+    u_char         *p, temp[NGX_INT64_LEN + 1];
+                       /*
+                        * we need temp[NGX_INT64_LEN] only,
+                        * but icc issues the warning
+                        */
+    size_t          len;
+    uint32_t        ui32;
+    static u_char   hex[] = "0123456789abcdef";
+    static u_char   HEX[] = "0123456789ABCDEF";
+
+    p = temp + NGX_INT64_LEN;
+
+    if (hexadecimal == 0) {
+
+        if (ui64 <= NGX_MAX_UINT32_VALUE) {
+
+            /*
+             * To divide 64-bit numbers and to find remainders
+             * on the x86 platform gcc and icc call the libc functions
+             * [u]divdi3() and [u]moddi3(), they call another function
+             * in its turn.  On FreeBSD it is the qdivrem() function,
+             * its source code is about 170 lines of the code.
+             * The glibc counterpart is about 150 lines of the code.
+             *
+             * For 32-bit numbers and some divisors gcc and icc use
+             * a inlined multiplication and shifts.  For example,
+             * unsigned "i32 / 10" is compiled to
+             *
+             *     (i32 * 0xCCCCCCCD) >> 35
+             */
+
+            ui32 = (uint32_t) ui64;
+
+            do {
+                *--p = (u_char) (ui32 % 10 + '0');
+            } while (ui32 /= 10);
+
+        } else {
+            do {
+                *--p = (u_char) (ui64 % 10 + '0');
+            } while (ui64 /= 10);
+        }
+
+    } else if (hexadecimal == 1) {
+
+        do {
+
+            /* the "(uint32_t)" cast disables the BCC's warning */
+            *--p = hex[(uint32_t) (ui64 & 0xf)];
+
+        } while (ui64 >>= 4);
+
+    } else { /* hexadecimal == 2 */
+
+        do {
+
+            /* the "(uint32_t)" cast disables the BCC's warning */
+            *--p = HEX[(uint32_t) (ui64 & 0xf)];
+
+        } while (ui64 >>= 4);
+    }
+
+    /* zero or space padding */
+
+    len = (temp + NGX_INT64_LEN) - p;
+
+    while (len++ < width && buf < last) {
+        *buf++ = zero;
+    }
+
+    /* number safe copy */
+
+    len = (temp + NGX_INT64_LEN) - p;
+
+    if (buf + len > last) {
+        len = last - buf;
+    }
+
+    return ngx_cpymem(buf, p, len);
+}
+
+
 /*
  * We use ngx_strcasecmp()/ngx_strncasecmp() for 7-bit ASCII strings only,
  * and implement our own ngx_strcasecmp()/ngx_strncasecmp()
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -188,13 +188,6 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_
         SSL_CTX_set_options(ssl->ctx, ngx_ssl_protocols[protocols >> 1]);
     }
 
-    /*
-     * we need this option because in ngx_ssl_send_chain()
-     * we may switch to a buffered write and may copy leftover part of
-     * previously unbuffered data to our internal buffer
-     */
-    SSL_CTX_set_mode(ssl->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
-
     SSL_CTX_set_read_ahead(ssl->ctx, 1);
 
     return NGX_OK;
@@ -505,11 +498,11 @@ ngx_ssl_handshake(ngx_connection_t *c)
 
     if (n == 1) {
 
-        if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
             return NGX_ERROR;
         }
 
-        if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) {
+        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
             return NGX_ERROR;
         }
 
@@ -576,7 +569,7 @@ ngx_ssl_handshake(ngx_connection_t *c)
         c->read->handler = ngx_ssl_handshake_handler;
         c->write->handler = ngx_ssl_handshake_handler;
 
-        if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
             return NGX_ERROR;
         }
 
@@ -588,7 +581,7 @@ ngx_ssl_handshake(ngx_connection_t *c)
         c->read->handler = ngx_ssl_handshake_handler;
         c->write->handler = ngx_ssl_handshake_handler;
 
-        if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) {
+        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
             return NGX_ERROR;
         }
 
@@ -774,7 +767,7 @@ ngx_ssl_handle_recv(ngx_connection_t *c,
             c->ssl->saved_write_handler = NULL;
             c->write->ready = 1;
 
-            if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) {
+            if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
                 return NGX_ERROR;
             }
 
@@ -802,7 +795,7 @@ ngx_ssl_handle_recv(ngx_connection_t *c,
 
         c->write->ready = 0;
 
-        if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) {
+        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
             return NGX_ERROR;
         }
 
@@ -860,14 +853,7 @@ ngx_ssl_send_chain(ngx_connection_t *c, 
     ssize_t      send, size;
     ngx_buf_t   *buf;
 
-    if (!c->ssl->buffer
-        || (in && in->next == NULL && !(c->buffered & NGX_SSL_BUFFERED)))
-    {
-        /*
-         * we avoid a buffer copy if
-         *     we do not need to buffer the output
-         *     or the incoming buf is a single and our buffer is empty
-         */
+    if (!c->ssl->buffer) {
 
         while (in) {
             if (ngx_buf_special(in->buf)) {
@@ -1033,7 +1019,7 @@ ngx_ssl_write(ngx_connection_t *c, u_cha
             c->ssl->saved_read_handler = NULL;
             c->read->ready = 1;
 
-            if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
+            if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
                 return NGX_ERROR;
             }
 
@@ -1061,7 +1047,7 @@ ngx_ssl_write(ngx_connection_t *c, u_cha
 
         c->read->ready = 0;
 
-        if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
             return NGX_ERROR;
         }
 
@@ -1161,11 +1147,11 @@ ngx_ssl_shutdown(ngx_connection_t *c)
         c->read->handler = ngx_ssl_shutdown_handler;
         c->write->handler = ngx_ssl_shutdown_handler;
 
-        if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
             return NGX_ERROR;
         }
 
-        if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) {
+        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
             return NGX_ERROR;
         }
 
@@ -1254,23 +1240,37 @@ ngx_ssl_connection_error(ngx_connection_
         n = ERR_GET_REASON(ERR_peek_error());
 
             /* handshake failures */
-        if (n == SSL_R_DIGEST_CHECK_FAILED
-            || n == SSL_R_NO_SHARED_CIPHER
-            || n == SSL_R_UNEXPECTED_MESSAGE
-            || n == SSL_R_WRONG_VERSION_NUMBER
-            || n == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC
+        if (n == SSL_R_DIGEST_CHECK_FAILED                           /*  149 */
+            || n == SSL_R_NO_CIPHERS_PASSED                          /*  182 */
+            || n == SSL_R_NO_SHARED_CIPHER                           /*  193 */
+            || n == SSL_R_UNEXPECTED_MESSAGE                         /*  244 */
+            || n == SSL_R_UNEXPECTED_RECORD                          /*  245 */
+            || n == SSL_R_WRONG_VERSION_NUMBER                       /*  267 */
+            || n == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC        /*  281 */
             || n == 1000 /* SSL_R_SSLV3_ALERT_CLOSE_NOTIFY */
-            || n == SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE
-            || n == SSL_R_SSLV3_ALERT_BAD_RECORD_MAC
-            || n == SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE
-            || n == SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE
-            || n == SSL_R_SSLV3_ALERT_BAD_CERTIFICATE
-            || n == SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE
-            || n == SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED
-            || n == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED
-            || n == SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN
-            || n == SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER
-            || n == SSL_R_TLSV1_ALERT_UNKNOWN_CA)
+            || n == SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE             /* 1010 */
+            || n == SSL_R_SSLV3_ALERT_BAD_RECORD_MAC                 /* 1020 */
+            || n == SSL_R_TLSV1_ALERT_DECRYPTION_FAILED              /* 1021 */
+            || n == SSL_R_TLSV1_ALERT_RECORD_OVERFLOW                /* 1022 */
+            || n == SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE          /* 1030 */
+            || n == SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE              /* 1040 */
+            || n == SSL_R_SSLV3_ALERT_NO_CERTIFICATE                 /* 1041 */
+            || n == SSL_R_SSLV3_ALERT_BAD_CERTIFICATE                /* 1042 */
+            || n == SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE        /* 1043 */
+            || n == SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED            /* 1044 */
+            || n == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED            /* 1045 */
+            || n == SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN            /* 1046 */
+            || n == SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER              /* 1047 */
+            || n == SSL_R_TLSV1_ALERT_UNKNOWN_CA                     /* 1048 */
+            || n == SSL_R_TLSV1_ALERT_ACCESS_DENIED                  /* 1049 */
+            || n == SSL_R_TLSV1_ALERT_DECODE_ERROR                   /* 1050 */
+            || n == SSL_R_TLSV1_ALERT_DECRYPT_ERROR                  /* 1051 */
+            || n == SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION             /* 1060 */
+            || n == SSL_R_TLSV1_ALERT_PROTOCOL_VERSION               /* 1070 */
+            || n == SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY          /* 1071 */
+            || n == SSL_R_TLSV1_ALERT_INTERNAL_ERROR                 /* 1080 */
+            || n == SSL_R_TLSV1_ALERT_USER_CANCELLED                 /* 1090 */
+            || n == SSL_R_TLSV1_ALERT_NO_RENEGOTIATION)              /* 1100 */
         {
             switch (c->log_error) {
 
--- a/src/event/ngx_event_pipe.c
+++ b/src/event/ngx_event_pipe.c
@@ -56,7 +56,7 @@ ngx_event_pipe(ngx_event_pipe_t *p, ngx_
 
         flags = (rev->eof || rev->error) ? NGX_CLOSE_EVENT : 0;
 
-        if (ngx_handle_read_event(rev, flags) == NGX_ERROR) {
+        if (ngx_handle_read_event(rev, flags) != NGX_OK) {
             return NGX_ABORT;
         }
 
@@ -70,7 +70,7 @@ ngx_event_pipe(ngx_event_pipe_t *p, ngx_
 
     if (p->downstream->fd != -1 && p->downstream->data == p->output_ctx) {
         wev = p->downstream->write;
-        if (ngx_handle_write_event(wev, p->send_lowat) == NGX_ERROR) {
+        if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) {
             return NGX_ABORT;
         }
 
--- a/src/http/modules/ngx_http_addition_filter_module.c
+++ b/src/http/modules/ngx_http_addition_filter_module.c
@@ -151,10 +151,10 @@ ngx_http_addition_body_filter(ngx_http_r
         ctx->before_body_sent = 1;
 
         if (conf->before_body.len) {
-            rc = ngx_http_subrequest(r, &conf->before_body, NULL, &sr, NULL, 0);
-
-            if (rc == NGX_ERROR || rc == NGX_DONE) {
-                return rc;
+            if (ngx_http_subrequest(r, &conf->before_body, NULL, &sr, NULL, 0)
+                != NGX_OK)
+            {
+                return NGX_ERROR;
             }
         }
     }
@@ -180,10 +180,10 @@ ngx_http_addition_body_filter(ngx_http_r
         return rc;
     }
 
-    rc = ngx_http_subrequest(r, &conf->after_body, NULL, &sr, NULL, 0);
-
-    if (rc == NGX_ERROR || rc == NGX_DONE) {
-        return rc;
+    if (ngx_http_subrequest(r, &conf->after_body, NULL, &sr, NULL, 0)
+        != NGX_OK)
+    {
+        return NGX_ERROR;
     }
 
     ngx_http_set_ctx(r, NULL, ngx_http_addition_filter_module);
--- a/src/http/modules/ngx_http_autoindex_module.c
+++ b/src/http/modules/ngx_http_autoindex_module.c
@@ -299,6 +299,11 @@ ngx_http_autoindex_handler(ngx_http_requ
                 if (err != NGX_ENOENT) {
                     ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
                                   ngx_de_info_n " \"%s\" failed", filename);
+
+                    if (err == NGX_EACCES) {
+                        continue;
+                    }
+
                     return ngx_http_autoindex_error(r, &dir, &path);
                 }
 
--- a/src/http/modules/ngx_http_charset_filter_module.c
+++ b/src/http/modules/ngx_http_charset_filter_module.c
@@ -541,12 +541,6 @@ ngx_http_charset_body_filter(ngx_http_re
                 break;
             }
 
-#if (NGX_HAVE_WRITE_ZEROCOPY)
-            if (b->zerocopy_busy) {
-                break;
-            }
-#endif
-
             ctx->busy = cl->next;
 
             if (b->tag != (ngx_buf_tag_t) &ngx_http_charset_filter_module) {
--- a/src/http/modules/ngx_http_dav_module.c
+++ b/src/http/modules/ngx_http_dav_module.c
@@ -246,9 +246,11 @@ ngx_http_dav_put_handler(ngx_http_reques
     dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
 
     ext.access = dlcf->access;
+    ext.path_access = dlcf->access;
     ext.time = -1;
     ext.create_path = dlcf->create_full_put_path;
     ext.delete_file = 1;
+    ext.log_rename_error = 1;
     ext.log = r->connection->log;
 
     if (r->headers_in.date) {
@@ -521,6 +523,7 @@ ngx_http_dav_copy_move_handler(ngx_http_
     ngx_tree_ctx_t            tree;
     ngx_file_info_t           fi;
     ngx_table_elt_t          *dest, *over;
+    ngx_ext_rename_file_t     ext;
     ngx_http_dav_copy_ctx_t   copy;
     ngx_http_dav_loc_conf_t  *dlcf;
 
@@ -781,9 +784,32 @@ overwrite_done:
     } else {
 
         if (r->method == NGX_HTTP_MOVE) {
-            if (ngx_rename_file(path.data, copy.path.data) != NGX_FILE_ERROR) {
+
+            dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
+
+            ext.access = 0;
+            ext.path_access = dlcf->access;
+            ext.time = -1;
+            ext.create_path = 1;
+            ext.delete_file = 0;
+            ext.log_rename_error = 0;
+            ext.log = r->connection->log;
+
+            if (ngx_ext_rename_file(&path, &copy.path, &ext) == NGX_OK) {
                 return NGX_HTTP_NO_CONTENT;
             }
+
+            if (ext.rename_error != NGX_EXDEV) {
+
+                if (ext.rename_error) {
+                    ngx_log_error(NGX_LOG_CRIT, r->connection->log,
+                                  ext.rename_error,
+                                  ngx_rename_file_n " \"%s\" to \"%s\" failed",
+                                  path.data, copy.path.data);
+                }
+
+                return NGX_HTTP_INTERNAL_SERVER_ERROR;
+            }
         }
 
         dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
--- a/src/http/modules/ngx_http_fastcgi_module.c
+++ b/src/http/modules/ngx_http_fastcgi_module.c
@@ -19,6 +19,9 @@ typedef struct {
     ngx_array_t                   *params;
     ngx_array_t                   *params_source;
     ngx_array_t                   *catch_stderr;
+
+    ngx_array_t                   *fastcgi_lengths;
+    ngx_array_t                   *fastcgi_values;
 } ngx_http_fastcgi_loc_conf_t;
 
 
@@ -103,6 +106,8 @@ typedef struct {
 } ngx_http_fastcgi_request_start_t;
 
 
+static ngx_int_t ngx_http_fastcgi_eval(ngx_http_request_t *r,
+    ngx_http_fastcgi_loc_conf_t *flcf);
 static ngx_int_t ngx_http_fastcgi_create_request(ngx_http_request_t *r);
 static ngx_int_t ngx_http_fastcgi_reinit_request(ngx_http_request_t *r);
 static ngx_int_t ngx_http_fastcgi_process_header(ngx_http_request_t *r);
@@ -414,14 +419,25 @@ ngx_http_fastcgi_handler(ngx_http_reques
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
-    flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
+    ngx_http_set_ctx(r, NULL, ngx_http_fastcgi_module);
 
     u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t));
     if (u == NULL) {
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
-    u->schema = flcf->upstream.schema;
+    r->upstream = u;
+
+    flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
+
+    if (flcf->fastcgi_lengths) {
+        if (ngx_http_fastcgi_eval(r, flcf) != NGX_OK) {
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
+    }
+
+    u->schema.len = sizeof("fastcgi://") - 1;
+    u->schema.data = (u_char *) "fastcgi://";
 
     u->peer.log = r->connection->log;
     u->peer.log_error = NGX_ERROR_ERR;
@@ -449,8 +465,6 @@ ngx_http_fastcgi_handler(ngx_http_reques
     u->pipe->input_filter = ngx_http_fastcgi_input_filter;
     u->pipe->input_ctx = r;
 
-    r->upstream = u;
-
     rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
 
     if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
@@ -462,6 +476,58 @@ ngx_http_fastcgi_handler(ngx_http_reques
 
 
 static ngx_int_t
+ngx_http_fastcgi_eval(ngx_http_request_t *r, ngx_http_fastcgi_loc_conf_t *flcf)
+{
+    ngx_url_t  u;
+
+    ngx_memzero(&u, sizeof(ngx_url_t));
+
+    if (ngx_http_script_run(r, &u.url, flcf->fastcgi_lengths->elts, 0,
+                            flcf->fastcgi_values->elts)
+        == NULL)
+    {
+        return NGX_ERROR;
+    }
+
+    u.no_resolve = 1;
+
+    if (ngx_parse_url(r->pool, &u) != NGX_OK) {
+         if (u.err) {
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                          "%s in upstream \"%V\"", u.err, &u.url);
+        }
+
+        return NGX_ERROR;
+    }
+
+    if (u.no_port) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                          "no port in upstream \"%V\"", &u.url);
+        return NGX_ERROR;
+    }
+
+    r->upstream->resolved = ngx_pcalloc(r->pool,
+                                        sizeof(ngx_http_upstream_resolved_t));
+    if (r->upstream->resolved == NULL) {
+        return NGX_ERROR;
+    }
+
+    if (u.addrs && u.addrs[0].sockaddr) {
+        r->upstream->resolved->sockaddr = u.addrs[0].sockaddr;
+        r->upstream->resolved->socklen = u.addrs[0].socklen;
+        r->upstream->resolved->naddrs = 1;
+        r->upstream->resolved->host = u.addrs[0].name;
+
+    } else {
+        r->upstream->resolved->host = u.host;
+        r->upstream->resolved->port = u.port;
+    }
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_http_fastcgi_create_request(ngx_http_request_t *r)
 {
     off_t                         file_pos;
@@ -1631,7 +1697,6 @@ ngx_http_fastcgi_create_loc_conf(ngx_con
      *     conf->upstream.next_upstream = 0;
      *     conf->upstream.temp_path = NULL;
      *     conf->upstream.hide_headers_hash = { NULL, 0 };
-     *     conf->upstream.schema = { 0, NULL };
      *     conf->upstream.uri = { 0, NULL };
      *     conf->upstream.location = NULL;
      *     conf->upstream.store_lengths = NULL;
@@ -1861,7 +1926,11 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf
 
     if (conf->upstream.upstream == NULL) {
         conf->upstream.upstream = prev->upstream.upstream;
-        conf->upstream.schema = prev->upstream.schema;
+    }
+
+    if (conf->fastcgi_lengths == NULL) {
+        conf->fastcgi_lengths = prev->fastcgi_lengths;
+        conf->fastcgi_values = prev->fastcgi_values;
     }
 
     if (conf->params_source == NULL) {
@@ -2044,35 +2113,56 @@ ngx_http_fastcgi_script_name_variable(ng
 static char *
 ngx_http_fastcgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
-    ngx_http_fastcgi_loc_conf_t *lcf = conf;
-
-    ngx_url_t                    u;
-    ngx_str_t                   *value;
-    ngx_http_core_loc_conf_t    *clcf;
-
-    if (lcf->upstream.schema.len) {
+    ngx_http_fastcgi_loc_conf_t *flcf = conf;
+
+    ngx_url_t                   u;
+    ngx_str_t                  *value, *url;
+    ngx_uint_t                  n;
+    ngx_http_core_loc_conf_t   *clcf;
+    ngx_http_script_compile_t   sc;
+
+    if (flcf->upstream.upstream || flcf->fastcgi_lengths) {
         return "is duplicate";
     }
 
+    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
+    clcf->handler = ngx_http_fastcgi_handler;
+
     value = cf->args->elts;
 
+    url = &value[1];
+
+    n = ngx_http_script_variables_count(url);
+
+    if (n) {
+
+        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+        sc.cf = cf;
+        sc.source = url;
+        sc.lengths = &flcf->fastcgi_lengths;
+        sc.values = &flcf->fastcgi_values;
+        sc.variables = n;
+        sc.complete_lengths = 1;
+        sc.complete_values = 1;
+
+        if (ngx_http_script_compile(&sc) != NGX_OK) {
+            return NGX_CONF_ERROR;
+        }
+
+        return NGX_CONF_OK;
+    }
+
     ngx_memzero(&u, sizeof(ngx_url_t));
 
     u.url = value[1];
     u.no_resolve = 1;
 
-    lcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
-    if (lcf->upstream.upstream == NULL) {
+    flcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
+    if (flcf->upstream.upstream == NULL) {
         return NGX_CONF_ERROR;
     }
 
-    lcf->upstream.schema.len = sizeof("fastcgi://") - 1;
-    lcf->upstream.schema.data = (u_char *) "fastcgi://";
-
-    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
-
-    clcf->handler = ngx_http_fastcgi_handler;
-
     if (clcf->name.data[clcf->name.len - 1] == '/') {
         clcf->auto_redirect = 1;
     }
--- a/src/http/modules/ngx_http_flv_module.c
+++ b/src/http/modules/ngx_http_flv_module.c
@@ -60,12 +60,12 @@ ngx_module_t  ngx_http_flv_module = {
 static ngx_int_t
 ngx_http_flv_handler(ngx_http_request_t *r)
 {
-    u_char                    *p, *n, *last;
+    u_char                    *last;
     off_t                      start, len;
     size_t                     root;
     ngx_int_t                  rc;
     ngx_uint_t                 level, i;
-    ngx_str_t                  path;
+    ngx_str_t                  path, value;
     ngx_log_t                 *log;
     ngx_buf_t                 *b;
     ngx_chain_t                out[2];
@@ -167,18 +167,10 @@ ngx_http_flv_handler(ngx_http_request_t 
     i = 1;
 
     if (r->args.len) {
-        p = (u_char *) ngx_strnstr(r->args.data, "start=", r->args.len);
-
-        if (p) {
-            p += 6;
 
-            for (n = p; n < r->args.data + r->args.len; n++) {
-                if (*n == '&') {
-                    break;
-                }
-            }
+        if (ngx_http_arg(r, (u_char *) "start", 5, &value) == NGX_OK) {
 
-            start = ngx_atoof(p, n - p);
+            start = ngx_atoof(value.data, value.len);
 
             if (start == NGX_ERROR || start >= len) {
                 start = 0;
--- a/src/http/modules/ngx_http_geo_module.c
+++ b/src/http/modules/ngx_http_geo_module.c
@@ -10,20 +10,66 @@
 
 
 typedef struct {
-    ngx_radix_tree_t  *tree;
-    ngx_pool_t        *pool;
-    ngx_array_t        values;
+    u_short                          start;
+    u_short                          end;
+    ngx_http_variable_value_t       *value;
+} ngx_http_geo_range_t;
+
+
+typedef struct {
+    ngx_http_geo_range_t            *ranges;
+    ngx_uint_t                       n;
+} ngx_http_geo_low_ranges_t;
+
+
+typedef struct {
+    ngx_http_geo_low_ranges_t        low[0x10000];
+    ngx_http_variable_value_t       *default_value;
+} ngx_http_geo_high_ranges_t;
+
+
+typedef struct {
+    ngx_http_variable_value_t       *value;
+    ngx_str_t                       *net;
+    ngx_http_geo_high_ranges_t      *high;
+    ngx_radix_tree_t                *tree;
+    ngx_rbtree_t                     rbtree;
+    ngx_rbtree_node_t                sentinel;
+    ngx_pool_t                      *pool;
+    ngx_pool_t                      *temp_pool;
 } ngx_http_geo_conf_ctx_t;
 
 
+typedef struct {
+    union {
+        ngx_radix_tree_t            *tree;
+        ngx_http_geo_high_ranges_t  *high;
+    } u;
+
+    ngx_int_t                        index;
+} ngx_http_geo_ctx_t;
+
+
+static in_addr_t ngx_http_geo_addr(ngx_http_request_t *r,
+    ngx_http_geo_ctx_t *ctx);
 static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 static char *ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);
+static char *ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+    ngx_str_t *value);
+static char *ngx_http_geo_add_range(ngx_conf_t *cf,
+    ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);
+static ngx_uint_t ngx_http_geo_delete_range(ngx_conf_t *cf,
+    ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);
+static char *ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+    ngx_str_t *value);
+static ngx_http_variable_value_t *ngx_http_geo_value(ngx_conf_t *cf,
+    ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value);
 
 
 static ngx_command_t  ngx_http_geo_commands[] = {
 
     { ngx_string("geo"),
-      NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1,
+      NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12,
       ngx_http_geo_block,
       NGX_HTTP_MAIN_CONF_OFFSET,
       0,
@@ -67,53 +113,126 @@ ngx_module_t  ngx_http_geo_module = {
 /* AF_INET only */
 
 static ngx_int_t
-ngx_http_geo_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+ngx_http_geo_cidr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
     uintptr_t data)
 {
-    ngx_radix_tree_t *tree = (ngx_radix_tree_t *) data;
+    ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;
 
-    struct sockaddr_in         *sin;
     ngx_http_variable_value_t  *vv;
 
-    sin = (struct sockaddr_in *) r->connection->sockaddr;
-
-    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "http geo started");
-
     vv = (ngx_http_variable_value_t *)
-                       ngx_radix32tree_find(tree, ntohl(sin->sin_addr.s_addr));
+              ngx_radix32tree_find(ctx->u.tree, ngx_http_geo_addr(r, ctx));
 
     *v = *vv;
 
-    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "http geo: %V %v", &r->connection->addr_text, v);
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http geo: %v", v);
 
     return NGX_OK;
 }
 
 
+static ngx_int_t
+ngx_http_geo_range_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+    uintptr_t data)
+{
+    ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;
+
+    in_addr_t              addr;
+    ngx_uint_t             i, n;
+    ngx_http_geo_range_t  *range;
+
+    *v = *ctx->u.high->default_value;
+
+    addr = ngx_http_geo_addr(r, ctx);
+
+    range = ctx->u.high->low[addr >> 16].ranges;
+
+    n = addr & 0xffff;
+
+    for (i = 0; i < ctx->u.high->low[addr >> 16].n; i++) {
+        if (n >= (ngx_uint_t) range[i].start
+            && n <= (ngx_uint_t) range[i].end)
+        {
+            *v = *range[i].value;
+        }
+    }
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http geo: %v", v);
+
+    return NGX_OK;
+}
+
+
+static in_addr_t
+ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx)
+{
+    struct sockaddr_in         *sin;
+    ngx_http_variable_value_t  *v;
+
+    if (ctx->index == -1) {
+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "http geo started: %V", &r->connection->addr_text);
+
+        sin = (struct sockaddr_in *) r->connection->sockaddr;
+        return ntohl(sin->sin_addr.s_addr);
+    }
+
+    v = ngx_http_get_flushed_variable(r, ctx->index);
+
+    if (v == NULL || v->not_found) {
+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "http geo not found");
+
+        return 0;
+    }
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http geo started: %v", v);
+
+    return ntohl(ngx_inet_addr(v->data, v->len));
+}
+
+
 static char *
 ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
     char                     *rv;
+    size_t                    len;
     ngx_str_t                *value, name;
+    ngx_uint_t                i;
     ngx_conf_t                save;
     ngx_pool_t               *pool;
-    ngx_radix_tree_t         *tree;
+    ngx_array_t              *a;
     ngx_http_variable_t      *var;
+    ngx_http_geo_ctx_t       *geo;
     ngx_http_geo_conf_ctx_t   ctx;
 
     value = cf->args->elts;
 
+    geo = ngx_palloc(cf->pool, sizeof(ngx_http_geo_ctx_t));
+    if (geo == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
     name = value[1];
+    name.len--;
+    name.data++;
 
-    if (name.data[0] != '$') {
-        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
-                           "\"%V\" variable name should start with '$'",
-                           &value[1]);
-    } else {
+    if (cf->args->nelts == 3) {
+
+        geo->index = ngx_http_get_variable_index(cf, &name);
+        if (geo->index == NGX_ERROR) {
+            return NGX_CONF_ERROR;
+        }
+
+        name = value[2];
         name.len--;
         name.data++;
+
+    } else {
+        geo->index = -1;
     }
 
     var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
@@ -121,29 +240,21 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c
         return NGX_CONF_ERROR;
     }
 
-    tree = ngx_radix_tree_create(cf->pool, -1);
-
-    if (tree == NULL) {
-        return NGX_CONF_ERROR;
-    }
-
-    var->get_handler = ngx_http_geo_variable;
-    var->data = (uintptr_t) tree;
-
     pool = ngx_create_pool(16384, cf->log);
     if (pool == NULL) {
         return NGX_CONF_ERROR;
     }
 
-    if (ngx_array_init(&ctx.values, pool, 512,
-                       sizeof(ngx_http_variable_value_t *))
-        != NGX_OK)
-    {
-        ngx_destroy_pool(pool);
+    ctx.temp_pool = ngx_create_pool(16384, cf->log);
+    if (ctx.temp_pool == NULL) {
         return NGX_CONF_ERROR;
     }
 
-    ctx.tree = tree;
+    ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel,
+                    ngx_http_variable_value_rbtree_insert);
+
+    ctx.high = NULL;
+    ctx.tree = NULL;
     ctx.pool = cf->pool;
 
     save = *cf;
@@ -156,121 +267,475 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c
 
     *cf = save;
 
-    ngx_destroy_pool(pool);
+    if (ctx.high) {
+
+        for (i = 0; i < 0x10000; i++) {
+            a = (ngx_array_t *) ctx.high->low[i].ranges;
+
+            if (a == NULL || a->nelts == 0) {
+                continue;
+            }
+
+            ctx.high->low[i].n = a->nelts;
+
+            len = a->nelts * sizeof(ngx_http_geo_range_t);
 
-    if (ngx_radix32tree_find(tree, 0) != NGX_RADIX_NO_VALUE) {
-        return rv;
-    }
+            ctx.high->low[i].ranges = ngx_palloc(cf->pool, len);
+            if (ctx.high->low[i].ranges == NULL ){
+                return NGX_CONF_ERROR;
+            }
+
+            ngx_memcpy(ctx.high->low[i].ranges, a->elts, len);
+        }
+
+        geo->u.high = ctx.high;
+
+        var->get_handler = ngx_http_geo_range_variable;
+        var->data = (uintptr_t) geo;
+
+        ngx_destroy_pool(ctx.temp_pool);
+        ngx_destroy_pool(pool);
 
-    if (ngx_radix32tree_insert(tree, 0, 0,
-                               (uintptr_t) &ngx_http_variable_null_value)
-        == NGX_ERROR)
-    {
-        return NGX_CONF_ERROR;
+        if (ctx.high->default_value == NULL) {
+            ctx.high->default_value = &ngx_http_variable_null_value;
+        }
+
+    } else {
+        if (ctx.tree == NULL) {
+            ctx.tree = ngx_radix_tree_create(cf->pool, -1);
+            if (ctx.tree == NULL) {
+                return NGX_CONF_ERROR;
+            }
+        }
+
+        geo->u.tree = ctx.tree;
+
+        var->get_handler = ngx_http_geo_cidr_variable;
+        var->data = (uintptr_t) geo;
+
+        ngx_destroy_pool(ctx.temp_pool);
+        ngx_destroy_pool(pool);
+
+        if (ngx_radix32tree_find(ctx.tree, 0) != NGX_RADIX_NO_VALUE) {
+            return rv;
+        }
+
+        if (ngx_radix32tree_insert(ctx.tree, 0, 0,
+                                   (uintptr_t) &ngx_http_variable_null_value)
+            == NGX_ERROR)
+        {
+            return NGX_CONF_ERROR;
+        }
     }
 
     return rv;
 }
 
 
-/* AF_INET only */
-
 static char *
 ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
 {
-    ngx_int_t                   rc;
-    ngx_str_t                  *value, file;
-    ngx_uint_t                  i;
-    ngx_inet_cidr_t             cidrin;
-    ngx_http_geo_conf_ctx_t    *ctx;
-    ngx_http_variable_value_t  *var, *old, **v;
+    char                     *rv;
+    ngx_str_t                *value, file;
+    ngx_http_geo_conf_ctx_t  *ctx;
 
     ctx = cf->ctx;
 
+    value = cf->args->elts;
+
+    if (cf->args->nelts == 1) {
+
+        if (ngx_strcmp(value[0].data, "ranges") == 0) {
+
+            if (ctx->tree) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "the \"ranges\" directive must be "
+                                   "the first directive inside \"geo\" block");
+                goto failed;
+            }
+
+            ctx->high = ngx_pcalloc(ctx->pool,
+                                    sizeof(ngx_http_geo_high_ranges_t));
+            if (ctx->high == NULL) {
+                goto failed;
+            }
+
+            rv = NGX_CONF_OK;
+
+            goto done;
+        }
+    }
+
     if (cf->args->nelts != 2) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "invalid number of the geo parameters");
-        return NGX_CONF_ERROR;
+        goto failed;
     }
 
-    value = cf->args->elts;
+    if (ngx_strcmp(value[0].data, "include") == 0) {
+
+        file.len = value[1].len++;
 
-    if (ngx_strcmp(value[0].data, "include") == 0) {
-        file = value[1];
+        file.data = ngx_pstrdup(ctx->temp_pool, &value[1]);
+        if (file.data == NULL) {
+            goto failed;
+        }
 
-        if (ngx_conf_full_name(cf->cycle, &file, 1) == NGX_ERROR){
-            return NGX_CONF_ERROR;
+        if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK){
+            goto failed;
         }
 
         ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
 
-        return ngx_conf_parse(cf, &file);
+        rv = ngx_conf_parse(cf, &file);
+
+        goto done;
+    }
+
+    if (ctx->high) {
+        rv = ngx_http_geo_range(cf, ctx, value);
+
+    } else {
+        rv = ngx_http_geo_cidr(cf, ctx, value);
+    }
+
+done:
+
+    ngx_reset_pool(cf->pool);
+
+    return rv;
+
+failed:
+
+    ngx_reset_pool(cf->pool);
+
+    return NGX_CONF_ERROR;
+}
+
+
+static char *
+ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+    ngx_str_t *value)
+{
+    u_char                     *p, *last;
+    in_addr_t                   start, end;
+    ngx_str_t                  *net;
+    ngx_uint_t                  del;
+    ngx_http_variable_value_t  *old;
+
+    if (ngx_strcmp(value[0].data, "default") == 0) {
+
+        old = ctx->high->default_value;
+
+        ctx->high->default_value = ngx_http_geo_value(cf, ctx, &value[1]);
+        if (ctx->high->default_value == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        if (old) {
+            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                    "duplicate range \"%V\", value: \"%v\", old value: \"%v\"",
+                    &value[0], ctx->high->default_value, old);
+        }
+
+        return NGX_CONF_OK;
+    }
+
+    if (ngx_strcmp(value[0].data, "delete") == 0) {
+        net = &value[1];
+        del = 1;
+
+    } else {
+        net = &value[0];
+        del = 0;
+    }
+
+    last = net->data + net->len;
+
+    p = ngx_strlchr(net->data, last, '-');
+
+    if (p == NULL) {
+        goto invalid;
+    }
+
+    start = ngx_inet_addr(net->data, p - net->data);
+
+    if (start == INADDR_NONE) {
+        goto invalid;
+    }
+
+    start = ntohl(start);
+
+    p++;
+
+    end = ngx_inet_addr(p, last - p);
+
+    if (end == INADDR_NONE) {
+        goto invalid;
+    }
+
+    end = ntohl(end);
+
+    if (start > end) {
+        goto invalid;
+    }
+
+    if (del) {
+        if (ngx_http_geo_delete_range(cf, ctx, start, end)) {
+            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                               "no address range \"%V\" to delete", net);
+        }
+
+        return NGX_CONF_OK;
+    }
+
+    ctx->value = ngx_http_geo_value(cf, ctx, &value[1]);
+
+    if (ctx->value == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    ctx->net = net;
+
+    return ngx_http_geo_add_range(cf, ctx, start, end);
+
+invalid:
+
+    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid range \"%V\"", net);
+
+    return NGX_CONF_ERROR;
+}
+
+
+/* the add procedure is optimized to add a growing up sequence */
+
+static char *
+ngx_http_geo_add_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+    in_addr_t start, in_addr_t end)
+{
+    in_addr_t              n;
+    ngx_uint_t             h, i, s, e;
+    ngx_array_t           *a;
+    ngx_http_geo_range_t  *range;
+
+    for (n = start; n < end; n += 0x10000) {
+
+        h = n >> 16;
+        s = n & 0xffff;
+
+        if ((n | 0xffff) > end) {
+            e = end & 0xffff;
+
+        } else {
+            e = 0xffff;
+        }
+
+        a = (ngx_array_t *) ctx->high->low[h].ranges;
+
+        if (a == NULL) {
+            a = ngx_array_create(ctx->temp_pool, 64,
+                                 sizeof(ngx_http_geo_range_t));
+            if (a == NULL) {
+                return NGX_CONF_ERROR;
+            }
+
+            ctx->high->low[h].ranges = (ngx_http_geo_range_t *) a;
+        }
+
+        i = a->nelts;
+        range = a->elts;
+
+        while (i) {
+
+            i--;
+
+            if (e < (ngx_uint_t) range[i].start) {
+                continue;
+            }
+
+            if (s > (ngx_uint_t) range[i].end) {
+
+                /* add after the range */
+
+                range = ngx_array_push(a);
+                if (range == NULL) {
+                    return NGX_CONF_ERROR;
+                }
+
+                range = a->elts;
+
+                ngx_memcpy(&range[i + 2], &range[i + 1],
+                           (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t));
+
+                range = &range[i + 1];
+
+                goto next;
+            }
+
+            if (s == (ngx_uint_t) range[i].start
+                && e == (ngx_uint_t) range[i].end)
+            {
+                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                    "duplicate range \"%V\", value: \"%v\", old value: \"%v\"",
+                    ctx->net, ctx->value, range[i].value);
+                continue;
+            }
+
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "overlapped range \"%V\"", ctx->net);
+
+            return NGX_CONF_ERROR;
+        }
+
+        /* add the first range */
+
+        range = ngx_array_push(a);
+        if (range == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+    next:
+
+        range->start = (u_short) s;
+        range->end = (u_short) e;
+        range->value = ctx->value;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+static ngx_uint_t
+ngx_http_geo_delete_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+    in_addr_t start, in_addr_t end)
+{
+    in_addr_t              n;
+    ngx_uint_t             h, i, s, e, warn;
+    ngx_array_t           *a;
+    ngx_http_geo_range_t  *range;
+
+    warn = 0;
+
+    for (n = start; n < end; n += 0x10000) {
+
+        h = n >> 16;
+        s = n & 0xffff;
+
+        if ((n | 0xffff) > end) {
+            e = end & 0xffff;
+
+        } else {
+            e = 0xffff;
+        }
+
+        a = (ngx_array_t *) ctx->high->low[h].ranges;
+
+        if (a == NULL) {
+            warn = 1;
+            continue;
+        }
+
+        range = a->elts;
+        for (i = 0; i < a->nelts; i++) {
+
+            if (s == (ngx_uint_t) range[i].start
+                && e == (ngx_uint_t) range[i].end)
+            {
+                ngx_memcpy(&range[i], &range[i + 1],
+                           (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t));
+                break;
+            }
+
+            if (s != (ngx_uint_t) range[i].start
+                && e != (ngx_uint_t) range[i].end)
+            {
+                continue;
+            }
+
+            warn = 1;
+        }
+    }
+
+    return warn;
+}
+
+
+static char *
+ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+    ngx_str_t *value)
+{
+    ngx_int_t                        rc, del;
+    ngx_str_t                       *net;
+    ngx_uint_t                       i;
+    ngx_inet_cidr_t                  cidrin;
+    ngx_http_variable_value_t       *val, *old;
+
+    if (ctx->tree == NULL) {
+        ctx->tree = ngx_radix_tree_create(ctx->pool, -1);
+        if (ctx->tree == NULL) {
+            return NGX_CONF_ERROR;
+        }
     }
 
     if (ngx_strcmp(value[0].data, "default") == 0) {
         cidrin.addr = 0;
         cidrin.mask = 0;
+        net = &value[0];
 
     } else {
-        rc = ngx_ptocidr(&value[0], &cidrin);
+        if (ngx_strcmp(value[0].data, "delete") == 0) {
+            net = &value[1];
+            del = 1;
 
-        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]);
+        } else {
+            net = &value[0];
+            del = 0;
         }
 
-        cidrin.addr = ntohl(cidrin.addr);
-        cidrin.mask = ntohl(cidrin.mask);
-    }
+        if (ngx_strcmp(net->data, "255.255.255.255") == 0) {
+            cidrin.addr = 0xffffffff;
+            cidrin.mask = 0xffffffff;
+
+        } else {
+            rc = ngx_ptocidr(net, &cidrin);
 
-    var = NULL;
-    v = ctx->values.elts;
+            if (rc == NGX_ERROR) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "invalid network \"%V\"", net);
+                return NGX_CONF_ERROR;
+            }
 
-    for (i = 0; i < ctx->values.nelts; i++) {
-        if ((size_t) v[i]->len != value[1].len) {
-            continue;
+            if (rc == NGX_DONE) {
+                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                                   "low address bits of %V are meaningless",
+                                   net);
+            }
+
+            cidrin.addr = ntohl(cidrin.addr);
+            cidrin.mask = ntohl(cidrin.mask);
         }
 
-        if (ngx_strncmp(value[1].data, v[i]->data, value[1].len) == 0) {
-            var = v[i];
-            break;
+        if (del) {
+            if (ngx_radix32tree_delete(ctx->tree, cidrin.addr, cidrin.mask)
+                != NGX_OK)
+            {
+                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                                   "no network \"%V\" to delete", net);
+            }
+
+            return NGX_CONF_OK;
         }
     }
 
-    if (var == NULL) {
-        var = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t));
-        if (var == NULL) {
-            return NGX_CONF_ERROR;
-        }
+    val = ngx_http_geo_value(cf, ctx, &value[1]);
 
-        var->len = value[1].len;
-        var->data = ngx_pstrdup(ctx->pool, &value[1]);
-        if (var->data == NULL) {
-            return NGX_CONF_ERROR;
-        }
-
-        var->valid = 1;
-        var->no_cacheable = 0;
-        var->not_found = 0;
-
-        v = ngx_array_push(&ctx->values);
-        if (v == NULL) {
-            return NGX_CONF_ERROR;
-        }
-
-        *v = var;
+    if (val == NULL) {
+        return NGX_CONF_ERROR;
     }
 
     for (i = 2; i; i--) {
         rc = ngx_radix32tree_insert(ctx->tree, cidrin.addr, cidrin.mask,
-                                    (uintptr_t) var);
+                                    (uintptr_t) val);
         if (rc == NGX_OK) {
             return NGX_CONF_OK;
         }
@@ -282,18 +747,65 @@ ngx_http_geo(ngx_conf_t *cf, ngx_command
         /* rc == NGX_BUSY */
 
         old  = (ngx_http_variable_value_t *)
-                    ngx_radix32tree_find(ctx->tree, cidrin.addr & cidrin.mask);
+                   ngx_radix32tree_find(ctx->tree, cidrin.addr & cidrin.mask);
 
         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
-                "duplicate parameter \"%V\", value: \"%v\", old value: \"%v\"",
-                &value[0], var, old);
+                "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
+                net, val, old);
 
         rc = ngx_radix32tree_delete(ctx->tree, cidrin.addr, cidrin.mask);
 
         if (rc == NGX_ERROR) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");
             return NGX_CONF_ERROR;
         }
     }
 
     return NGX_CONF_ERROR;
 }
+
+
+static ngx_http_variable_value_t *
+ngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+    ngx_str_t *value)
+{
+    uint32_t                         hash;
+    ngx_http_variable_value_t       *val;
+    ngx_http_variable_value_node_t  *vvn;
+
+    hash = ngx_crc32_long(value->data, value->len);
+
+    val = ngx_http_variable_value_lookup(&ctx->rbtree, value, hash);
+
+    if (val) {
+        return val;
+    }
+
+    val = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t));
+    if (val == NULL) {
+        return NULL;
+    }
+
+    val->len = value->len;
+    val->data = ngx_pstrdup(ctx->pool, value);
+    if (val->data == NULL) {
+        return NULL;
+    }
+
+    val->valid = 1;
+    val->no_cacheable = 0;
+    val->not_found = 0;
+
+    vvn = ngx_palloc(ctx->temp_pool, sizeof(ngx_http_variable_value_node_t));
+    if (vvn == NULL) {
+        return NULL;
+    }
+
+    vvn->node.key = hash;
+    vvn->len = val->len;
+    vvn->value = val;
+
+    ngx_rbtree_insert(&ctx->rbtree, &vvn->node);
+
+    return val;
+}
--- a/src/http/modules/ngx_http_gzip_filter_module.c
+++ b/src/http/modules/ngx_http_gzip_filter_module.c
@@ -34,19 +34,27 @@ typedef struct {
     ngx_chain_t         *busy;
     ngx_chain_t         *out;
     ngx_chain_t        **last_out;
+
+    ngx_chain_t         *copied;
+    ngx_chain_t         *copy_buf;
+
     ngx_buf_t           *in_buf;
     ngx_buf_t           *out_buf;
     ngx_int_t            bufs;
 
-    off_t                length;
-
     void                *preallocated;
     char                *free_mem;
     ngx_uint_t           allocated;
 
+    int                  wbits;
+    int                  memlevel;
+
     unsigned             flush:4;
     unsigned             redo:1;
     unsigned             done:1;
+    unsigned             nomem:1;
+    unsigned             gzheader:1;
+    unsigned             buffering:1;
 
     size_t               zin;
     size_t               zout;
@@ -57,10 +65,45 @@ typedef struct {
 } ngx_http_gzip_ctx_t;
 
 
+#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
+
+struct gztrailer {
+    uint32_t  crc32;
+    uint32_t  zlen;
+};
+
+#else /* NGX_HAVE_BIG_ENDIAN || !NGX_HAVE_NONALIGNED */
+
+struct gztrailer {
+    u_char  crc32[4];
+    u_char  zlen[4];
+};
+
+#endif
+
+
+static void ngx_http_gzip_filter_memory(ngx_http_request_t *r,
+    ngx_http_gzip_ctx_t *ctx);
+static ngx_int_t ngx_http_gzip_filter_copy_recycled(ngx_http_gzip_ctx_t *ctx,
+    ngx_chain_t *in);
+static ngx_int_t ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r,
+    ngx_http_gzip_ctx_t *ctx);
+static ngx_int_t ngx_http_gzip_filter_gzheader(ngx_http_request_t *r,
+    ngx_http_gzip_ctx_t *ctx);
+static ngx_int_t ngx_http_gzip_filter_add_data(ngx_http_request_t *r,
+    ngx_http_gzip_ctx_t *ctx);
+static ngx_int_t ngx_http_gzip_filter_get_buf(ngx_http_request_t *r,
+    ngx_http_gzip_ctx_t *ctx);
+static ngx_int_t ngx_http_gzip_filter_deflate(ngx_http_request_t *r,
+    ngx_http_gzip_ctx_t *ctx);
+static ngx_int_t ngx_http_gzip_filter_deflate_end(ngx_http_request_t *r,
+    ngx_http_gzip_ctx_t *ctx);
+
 static void *ngx_http_gzip_filter_alloc(void *opaque, u_int items,
     u_int size);
 static void ngx_http_gzip_filter_free(void *opaque, void *address);
-static void ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx);
+static void ngx_http_gzip_filter_free_copy_buf(ngx_http_request_t *r,
+    ngx_http_gzip_ctx_t *ctx);
 
 static ngx_int_t ngx_http_gzip_add_variables(ngx_conf_t *cf);
 static ngx_int_t ngx_http_gzip_ratio_variable(ngx_http_request_t *r,
@@ -176,25 +219,6 @@ ngx_module_t  ngx_http_gzip_filter_modul
 };
 
 
-static u_char  gzheader[10] = { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 };
-
-#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
-
-struct gztrailer {
-    uint32_t  crc32;
-    uint32_t  zlen;
-};
-
-#else /* NGX_HAVE_BIG_ENDIAN || !NGX_HAVE_NONALIGNED */
-
-struct gztrailer {
-    u_char  crc32[4];
-    u_char  zlen[4];
-};
-
-#endif
-
-
 static ngx_str_t  ngx_http_gzip_ratio = ngx_string("gzip_ratio");
 
 static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
@@ -233,6 +257,9 @@ ngx_http_gzip_header_filter(ngx_http_req
     ngx_http_set_ctx(r, ctx, ngx_http_gzip_filter_module);
 
     ctx->request = r;
+    ctx->buffering = 1;
+
+    ngx_http_gzip_filter_memory(r, ctx);
 
     h = ngx_list_push(&r->headers_out.headers);
     if (h == NULL) {
@@ -247,8 +274,6 @@ ngx_http_gzip_header_filter(ngx_http_req
 
     r->headers_out.content_encoding = h;
 
-    ctx->length = r->headers_out.content_length_n;
-
     r->main_filter_need_in_memory = 1;
 
     ngx_http_clear_content_length(r);
@@ -261,13 +286,9 @@ ngx_http_gzip_header_filter(ngx_http_req
 static ngx_int_t
 ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
 {
-    int                    rc, wbits, memlevel;
-    ngx_int_t              last;
-    struct gztrailer      *trailer;
-    ngx_buf_t             *b;
-    ngx_chain_t           *cl, out;
-    ngx_http_gzip_ctx_t   *ctx;
-    ngx_http_gzip_conf_t  *conf;
+    int                   rc;
+    ngx_chain_t          *cl;
+    ngx_http_gzip_ctx_t  *ctx;
 
     ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module);
 
@@ -275,392 +296,635 @@ ngx_http_gzip_body_filter(ngx_http_reque
         return ngx_http_next_body_filter(r, in);
     }
 
-    conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http gzip filter");
+
+    if (ctx->buffering) {
+
+        if (in) {
+	    switch (ngx_http_gzip_filter_copy_recycled(ctx, in)) {
+
+	    case NGX_OK:
+		return NGX_OK;
+
+	    case NGX_DONE:
+		in = NULL;
+		break;
+
+	    default:  /* NGX_ERROR */
+		goto failed;
+	    }
+
+        } else {
+            ctx->buffering = 0;
+        }
+    }
 
     if (ctx->preallocated == NULL) {
-        wbits = conf->wbits;
-        memlevel = conf->memlevel;
-
-        if (ctx->length > 0) {
-
-            /* the actual zlib window size is smaller by 262 bytes */
-
-            while (ctx->length < ((1 << (wbits - 1)) - 262)) {
-                wbits--;
-                memlevel--;
-            }
-        }
-
-        /*
-         * We preallocate a memory for zlib in one buffer (200K-400K), this
-         * decreases a number of malloc() and free() calls and also probably
-         * decreases a number of syscalls (sbrk() and so on).
-         * Besides we free this memory as soon as the gzipping will complete
-         * and do not wait while a whole response will be sent to a client.
-         *
-         * 8K is for zlib deflate_state, it takes
-         *  *) 5816 bytes on i386 and sparc64 (32-bit mode)
-         *  *) 5920 bytes on amd64 and sparc64
-         */
-
-        ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9));
-
-        ctx->preallocated = ngx_palloc(r->pool, ctx->allocated);
-        if (ctx->preallocated == NULL) {
-            return NGX_ERROR;
+        if (ngx_http_gzip_filter_deflate_start(r, ctx) != NGX_OK) {
+            goto failed;
         }
-
-        ctx->free_mem = ctx->preallocated;
-
-        ctx->zstream.zalloc = ngx_http_gzip_filter_alloc;
-        ctx->zstream.zfree = ngx_http_gzip_filter_free;
-        ctx->zstream.opaque = ctx;
-
-        rc = deflateInit2(&ctx->zstream, (int) conf->level, Z_DEFLATED,
-                          -wbits, memlevel, Z_DEFAULT_STRATEGY);
-
-        if (rc != Z_OK) {
-            ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
-                          "deflateInit2() failed: %d", rc);
-            ngx_http_gzip_error(ctx);
-            return NGX_ERROR;
-        }
-
-        b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
-        if (b == NULL) {
-            ngx_http_gzip_error(ctx);
-            return NGX_ERROR;
-        }
-
-        b->memory = 1;
-        b->pos = gzheader;
-        b->last = b->pos + 10;
-
-        out.buf = b;
-        out.next = NULL;
-
-        /*
-         * We pass the gzheader to the next filter now to avoid its linking
-         * to the ctx->busy chain.  zlib does not usually output the compressed
-         * data in the initial iterations, so the gzheader that was linked
-         * to the ctx->busy chain would be flushed by ngx_http_write_filter().
-         */
-
-        if (ngx_http_next_body_filter(r, &out) == NGX_ERROR) {
-            ngx_http_gzip_error(ctx);
-            return NGX_ERROR;
-        }
-
-        r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED;
-
-        ctx->last_out = &ctx->out;
-
-        ctx->crc32 = crc32(0L, Z_NULL, 0);
-        ctx->flush = Z_NO_FLUSH;
     }
 
     if (in) {
         if (ngx_chain_add_copy(r->pool, &ctx->in, in) == NGX_ERROR) {
-            ngx_http_gzip_error(ctx);
-            return NGX_ERROR;
+            goto failed;
         }
     }
 
-    last = NGX_NONE;
+    if (ctx->nomem) {
+
+        /* flush busy buffers */
+
+        if (ngx_http_next_body_filter(r, NULL) == NGX_ERROR) {
+            goto failed;
+        }
+
+        cl = NULL;
+
+        ngx_chain_update_chains(&ctx->free, &ctx->busy, &cl,
+                                (ngx_buf_tag_t) &ngx_http_gzip_filter_module);
+        ctx->nomem = 0;
+    }
 
     for ( ;; ) {
 
+        /* cycle while we can write to a client */
+
         for ( ;; ) {
 
-            /* does zlib need a new data ? */
-
-            if (ctx->zstream.avail_in == 0
-                && ctx->flush == Z_NO_FLUSH
-                && !ctx->redo)
-            {
-                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                               "gzip in: %p", ctx->in);
+            /* cycle while there is data to feed zlib and ... */
 
-                if (ctx->in == NULL) {
-                    break;
-                }
-
-                ctx->in_buf = ctx->in->buf;
-                ctx->in = ctx->in->next;
-
-                ctx->zstream.next_in = ctx->in_buf->pos;
-                ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos;
-
-                ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                               "gzip in_buf:%p ni:%p ai:%ud",
-                               ctx->in_buf,
-                               ctx->zstream.next_in, ctx->zstream.avail_in);
+            rc = ngx_http_gzip_filter_add_data(r, ctx);
 
-                /* STUB */
-                if (ctx->in_buf->last < ctx->in_buf->pos) {
-                    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
-                                  "zstream.avail_in is huge");
-                    ctx->done = 1;
-                    return NGX_ERROR;
-                }
-                /**/
-
-                if (ctx->in_buf->last_buf) {
-                    ctx->flush = Z_FINISH;
+            if (rc == NGX_DECLINED) {
+                break;
+            }
 
-                } else if (ctx->in_buf->flush) {
-                    ctx->flush = Z_SYNC_FLUSH;
-                }
-
-                if (ctx->zstream.avail_in == 0) {
-                    if (ctx->flush == Z_NO_FLUSH) {
-                        continue;
-                    }
-
-                } else {
-                    ctx->crc32 = crc32(ctx->crc32, ctx->zstream.next_in,
-                                       ctx->zstream.avail_in);
-                }
+            if (rc == NGX_AGAIN) {
+                continue;
             }
 
 
-            /* is there a space for the gzipped data ? */
-
-            if (ctx->zstream.avail_out == 0) {
-
-                if (ctx->free) {
-                    ctx->out_buf = ctx->free->buf;
-                    ctx->free = ctx->free->next;
-
-                } else if (ctx->bufs < conf->bufs.num) {
-                    ctx->out_buf = ngx_create_temp_buf(r->pool,
-                                                       conf->bufs.size);
-                    if (ctx->out_buf == NULL) {
-                        ngx_http_gzip_error(ctx);
-                        return NGX_ERROR;
-                    }
-
-                    ctx->out_buf->tag = (ngx_buf_tag_t)
-                                                  &ngx_http_gzip_filter_module;
-                    ctx->out_buf->recycled = 1;
-                    ctx->bufs++;
+            /* ... there are buffers to write zlib output */
 
-                } else {
-                    break;
-                }
-
-                ctx->zstream.next_out = ctx->out_buf->pos;
-                ctx->zstream.avail_out = conf->bufs.size;
-            }
+            rc = ngx_http_gzip_filter_get_buf(r, ctx);
 
-            ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                         "deflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d",
-                         ctx->zstream.next_in, ctx->zstream.next_out,
-                         ctx->zstream.avail_in, ctx->zstream.avail_out,
-                         ctx->flush, ctx->redo);
-
-            rc = deflate(&ctx->zstream, ctx->flush);
-
-            if (rc != Z_OK && rc != Z_STREAM_END) {
-                ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
-                              "deflate() failed: %d, %d", ctx->flush, rc);
-                ngx_http_gzip_error(ctx);
-                return NGX_ERROR;
+            if (rc == NGX_DECLINED) {
+                break;
             }
 
-            ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                           "deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",
-                           ctx->zstream.next_in, ctx->zstream.next_out,
-                           ctx->zstream.avail_in, ctx->zstream.avail_out,
-                           rc);
-
-            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                           "gzip in_buf:%p pos:%p",
-                           ctx->in_buf, ctx->in_buf->pos);
-
-
-            if (ctx->zstream.next_in) {
-                ctx->in_buf->pos = ctx->zstream.next_in;
-
-                if (ctx->zstream.avail_in == 0) {
-                    ctx->zstream.next_in = NULL;
-                }
+            if (rc == NGX_ERROR) {
+                goto failed;
             }
 
-            ctx->out_buf->last = ctx->zstream.next_out;
-
-            if (ctx->zstream.avail_out == 0) {
-
-                /* zlib wants to output some more gzipped data */
-
-                cl = ngx_alloc_chain_link(r->pool);
-                if (cl == NULL) {
-                    ngx_http_gzip_error(ctx);
-                    return NGX_ERROR;
-                }
-
-                cl->buf = ctx->out_buf;
-                cl->next = NULL;
-                *ctx->last_out = cl;
-                ctx->last_out = &cl->next;
-
-                ctx->redo = 1;
 
-                continue;
-            }
-
-            ctx->redo = 0;
-
-            if (ctx->flush == Z_SYNC_FLUSH) {
-
-                ctx->zstream.avail_out = 0;
-                ctx->out_buf->flush = 1;
-                ctx->flush = Z_NO_FLUSH;
+            rc = ngx_http_gzip_filter_deflate(r, ctx);
 
-                cl = ngx_alloc_chain_link(r->pool);
-                if (cl == NULL) {
-                    ngx_http_gzip_error(ctx);
-                    return NGX_ERROR;
-                }
-
-                cl->buf = ctx->out_buf;
-                cl->next = NULL;
-                *ctx->last_out = cl;
-                ctx->last_out = &cl->next;
-
+            if (rc == NGX_OK) {
                 break;
             }
 
-            if (rc == Z_STREAM_END) {
-
-                ctx->zin = ctx->zstream.total_in;
-                ctx->zout = 10 + ctx->zstream.total_out + 8;
-
-                rc = deflateEnd(&ctx->zstream);
-
-                if (rc != Z_OK) {
-                    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
-                                  "deflateEnd() failed: %d", rc);
-                    ngx_http_gzip_error(ctx);
-                    return NGX_ERROR;
-                }
-
-                ngx_pfree(r->pool, ctx->preallocated);
-
-                cl = ngx_alloc_chain_link(r->pool);
-                if (cl == NULL) {
-                    ngx_http_gzip_error(ctx);
-                    return NGX_ERROR;
-                }
-
-                cl->buf = ctx->out_buf;
-                cl->next = NULL;
-                *ctx->last_out = cl;
-                ctx->last_out = &cl->next;
-
-                if (ctx->zstream.avail_out >= 8) {
-                    trailer = (struct gztrailer *) ctx->out_buf->last;
-                    ctx->out_buf->last += 8;
-                    ctx->out_buf->last_buf = 1;
-
-                } else {
-                    b = ngx_create_temp_buf(r->pool, 8);
-                    if (b == NULL) {
-                        ngx_http_gzip_error(ctx);
-                        return NGX_ERROR;
-                    }
-
-                    b->last_buf = 1;
-
-                    cl = ngx_alloc_chain_link(r->pool);
-                    if (cl == NULL) {
-                        ngx_http_gzip_error(ctx);
-                        return NGX_ERROR;
-                    }
-
-                    cl->buf = b;
-                    cl->next = NULL;
-                    *ctx->last_out = cl;
-                    ctx->last_out = &cl->next;
-                    trailer = (struct gztrailer *) b->pos;
-                    b->last += 8;
-                }
-
-#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
-
-                trailer->crc32 = ctx->crc32;
-                trailer->zlen = ctx->zin;
-
-#else
-                trailer->crc32[0] = (u_char) (ctx->crc32 & 0xff);
-                trailer->crc32[1] = (u_char) ((ctx->crc32 >> 8) & 0xff);
-                trailer->crc32[2] = (u_char) ((ctx->crc32 >> 16) & 0xff);
-                trailer->crc32[3] = (u_char) ((ctx->crc32 >> 24) & 0xff);
-
-                trailer->zlen[0] = (u_char) (ctx->zin & 0xff);
-                trailer->zlen[1] = (u_char) ((ctx->zin >> 8) & 0xff);
-                trailer->zlen[2] = (u_char) ((ctx->zin >> 16) & 0xff);
-                trailer->zlen[3] = (u_char) ((ctx->zin >> 24) & 0xff);
-#endif
-
-                ctx->zstream.avail_in = 0;
-                ctx->zstream.avail_out = 0;
-
-                ctx->done = 1;
-
-                r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED;
-
-                break;
+            if (rc == NGX_ERROR) {
+                goto failed;
             }
 
-            if (conf->no_buffer && ctx->in == NULL) {
+            /* rc == NGX_AGAIN */
+        }
+
+        if (ctx->out == NULL) {
+            ngx_http_gzip_filter_free_copy_buf(r, ctx);
 
-                cl = ngx_alloc_chain_link(r->pool);
-                if (cl == NULL) {
-                    ngx_http_gzip_error(ctx);
-                    return NGX_ERROR;
-                }
+            return ctx->busy ? NGX_AGAIN : NGX_OK;
+        }
 
-                cl->buf = ctx->out_buf;
-                cl->next = NULL;
-                *ctx->last_out = cl;
-                ctx->last_out = &cl->next;
-
-                break;
+        if (!ctx->gzheader) {
+            if (ngx_http_gzip_filter_gzheader(r, ctx) != NGX_OK) {
+                goto failed;
             }
         }
 
-        if (ctx->out == NULL) {
+        rc = ngx_http_next_body_filter(r, ctx->out);
 
-            if (last == NGX_AGAIN) {
-                return NGX_AGAIN;
-            }
-
-            if (ctx->busy == NULL) {
-                return NGX_OK;
-            }
+        if (rc == NGX_ERROR) {
+            goto failed;
         }
 
-        last = ngx_http_next_body_filter(r, ctx->out);
-
-        /*
-         * we do not check NGX_AGAIN here because the downstream filters
-         * may free some buffers and zlib may compress some data into them
-         */
-
-        if (last == NGX_ERROR) {
-            ngx_http_gzip_error(ctx);
-            return NGX_ERROR;
-        }
+        ngx_http_gzip_filter_free_copy_buf(r, ctx);
 
         ngx_chain_update_chains(&ctx->free, &ctx->busy, &ctx->out,
                                 (ngx_buf_tag_t) &ngx_http_gzip_filter_module);
         ctx->last_out = &ctx->out;
 
+        ctx->nomem = 0;
+
         if (ctx->done) {
-            return last;
+            return rc;
+        }
+    }
+
+    /* unreachable */
+
+failed:
+
+    ctx->done = 1;
+
+    if (ctx->preallocated) {
+        deflateEnd(&ctx->zstream);
+
+        ngx_pfree(r->pool, ctx->preallocated);
+    }
+
+    ngx_http_gzip_filter_free_copy_buf(r, ctx);
+
+    return NGX_ERROR;
+}
+
+
+static void
+ngx_http_gzip_filter_memory(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
+{
+    int                    wbits, memlevel;
+    ngx_http_gzip_conf_t  *conf;
+
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
+
+    wbits = conf->wbits;
+    memlevel = conf->memlevel;
+
+    if (r->headers_out.content_length_n > 0) {
+
+        /* the actual zlib window size is smaller by 262 bytes */
+
+        while (r->headers_out.content_length_n < ((1 << (wbits - 1)) - 262)) {
+            wbits--;
+            memlevel--;
         }
     }
+
+    ctx->wbits = wbits;
+    ctx->memlevel = memlevel;
+
+    /*
+     * We preallocate a memory for zlib in one buffer (200K-400K), this
+     * decreases a number of malloc() and free() calls and also probably
+     * decreases a number of syscalls (sbrk()/mmap() and so on).
+     * Besides we free the memory as soon as a gzipping will complete
+     * and do not wait while a whole response will be sent to a client.
+     *
+     * 8K is for zlib deflate_state, it takes
+     *  *) 5816 bytes on i386 and sparc64 (32-bit mode)
+     *  *) 5920 bytes on amd64 and sparc64
+     */
+
+    ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9));
+}
+
+
+static ngx_int_t
+ngx_http_gzip_filter_copy_recycled(ngx_http_gzip_ctx_t *ctx, ngx_chain_t *in)
+{
+    size_t               size, buffered;
+    ngx_buf_t           *b, *buf;
+    ngx_chain_t         *cl, **ll;
+    ngx_http_request_t  *r;
+
+    r = ctx->request;
+
+    r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED;
+
+    buffered = 0;
+    ll = &ctx->in;
+
+    for (cl = ctx->in; cl; cl = cl->next) {
+        buffered += cl->buf->last - cl->buf->pos;
+        ll = &cl->next;
+    }
+
+    while (in) {
+        cl = ngx_alloc_chain_link(r->pool);
+        if (cl == NULL) {
+            return NGX_ERROR;
+        }
+
+        b = in->buf;
+
+        size = b->last - b->pos;
+        buffered += size;
+
+        if (b->flush || b->last_buf) {
+            ctx->buffering = 0;
+
+        } else if (buffered > ctx->allocated / 2) {
+
+            /*
+             * With default memory settings zlib starts to output gzipped data
+             * only after it has got about 90K, so it makes sense to allocate
+             * zlib memory (200-400K) only after we have enough data
+             * to compress.  Although we copy recycled buffers, nevertheless
+             * for responses up to 120K this allows to allocate zlib memory,
+             * to compress and to output the response in one step
+             * using hot CPU cache.
+             */
+
+            ctx->buffering = 0;
+        }
+
+        if (ctx->buffering && size && b->recycled) {
+
+            buf = ngx_create_temp_buf(r->pool, size);
+            if (buf == NULL) {
+                return NGX_ERROR;
+            }
+
+            buf->last = ngx_cpymem(buf->pos, b->pos, size);
+            b->pos = b->last;
+
+            buf->last_buf = b->last_buf;
+            buf->tag = (ngx_buf_tag_t) &ngx_http_gzip_filter_module;
+
+            cl->buf = buf;
+
+        } else {
+            cl->buf = b;
+        }
+
+        *ll = cl;
+        ll = &cl->next;
+        in = in->next;
+    }
+
+    *ll = NULL;
+
+    return ctx->buffering ? NGX_OK : NGX_DONE;
+}
+
+
+static ngx_int_t
+ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r,
+    ngx_http_gzip_ctx_t *ctx)
+{
+    int                    rc;
+    ngx_http_gzip_conf_t  *conf;
+
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
+
+    ctx->preallocated = ngx_palloc(r->pool, ctx->allocated);
+    if (ctx->preallocated == NULL) {
+        return NGX_ERROR;
+    }
+
+    ctx->free_mem = ctx->preallocated;
+
+    ctx->zstream.zalloc = ngx_http_gzip_filter_alloc;
+    ctx->zstream.zfree = ngx_http_gzip_filter_free;
+    ctx->zstream.opaque = ctx;
+
+    rc = deflateInit2(&ctx->zstream, (int) conf->level, Z_DEFLATED,
+                      - ctx->wbits, ctx->memlevel, Z_DEFAULT_STRATEGY);
+
+    if (rc != Z_OK) {
+        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                      "deflateInit2() failed: %d", rc);
+        return NGX_ERROR;
+    }
+
+    ctx->last_out = &ctx->out;
+    ctx->crc32 = crc32(0L, Z_NULL, 0);
+    ctx->flush = Z_NO_FLUSH;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_gzip_filter_gzheader(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
+{
+    ngx_buf_t      *b;
+    ngx_chain_t    *cl;
+    static u_char  gzheader[10] =
+                               { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 };
+
+    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
+    if (b == NULL) {
+        return NGX_ERROR;
+    }
+
+    b->memory = 1;
+    b->pos = gzheader;
+    b->last = b->pos + 10;
+
+    cl = ngx_alloc_chain_link(r->pool);
+    if (cl == NULL) {
+        return NGX_ERROR;
+    }
+
+    cl->buf = b;
+    cl->next = ctx->out;
+    ctx->out = cl;
+
+    ctx->gzheader = 1;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_gzip_filter_add_data(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
+{
+    if (ctx->zstream.avail_in || ctx->flush != Z_NO_FLUSH || ctx->redo) {
+        return NGX_OK;
+    }
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "gzip in: %p", ctx->in);
+
+    if (ctx->in == NULL) {
+        return NGX_DECLINED;
+    }
+
+    if (ctx->copy_buf) {
+
+        /*
+         * to avoid CPU cache trashing we do not free() just quit buf,
+         * but postpone free()ing after zlib compressing and data output
+         */
+
+        ctx->copy_buf->next = ctx->copied;
+        ctx->copied = ctx->copy_buf;
+        ctx->copy_buf = NULL;
+    }
+
+    ctx->in_buf = ctx->in->buf;
+
+    if (ctx->in_buf->tag == (ngx_buf_tag_t) &ngx_http_gzip_filter_module) {
+        ctx->copy_buf = ctx->in;
+    }
+
+    ctx->in = ctx->in->next;
+
+    ctx->zstream.next_in = ctx->in_buf->pos;
+    ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos;
+
+    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "gzip in_buf:%p ni:%p ai:%ud",
+                   ctx->in_buf,
+                   ctx->zstream.next_in, ctx->zstream.avail_in);
+
+    if (ctx->in_buf->last_buf) {
+        ctx->flush = Z_FINISH;
+
+    } else if (ctx->in_buf->flush) {
+        ctx->flush = Z_SYNC_FLUSH;
+    }
+
+    if (ctx->zstream.avail_in) {
+
+        ctx->crc32 = crc32(ctx->crc32, ctx->zstream.next_in,
+                           ctx->zstream.avail_in);
+
+    } else if (ctx->flush == Z_NO_FLUSH) {
+        return NGX_AGAIN;
+    }
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_gzip_filter_get_buf(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
+{
+    ngx_http_gzip_conf_t  *conf;
+
+    if (ctx->zstream.avail_out) {
+        return NGX_OK;
+    }
+
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
+
+    if (ctx->free) {
+        ctx->out_buf = ctx->free->buf;
+        ctx->free = ctx->free->next;
+
+    } else if (ctx->bufs < conf->bufs.num) {
+
+        ctx->out_buf = ngx_create_temp_buf(r->pool, conf->bufs.size);
+        if (ctx->out_buf == NULL) {
+            return NGX_ERROR;
+        }
+
+        ctx->out_buf->tag = (ngx_buf_tag_t) &ngx_http_gzip_filter_module;
+        ctx->out_buf->recycled = 1;
+        ctx->bufs++;
+
+    } else {
+        ctx->nomem = 1;
+        return NGX_DECLINED;
+    }
+
+    ctx->zstream.next_out = ctx->out_buf->pos;
+    ctx->zstream.avail_out = conf->bufs.size;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_gzip_filter_deflate(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
+{
+    int                    rc;
+    ngx_chain_t           *cl;
+    ngx_http_gzip_conf_t  *conf;
+
+    ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                 "deflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d",
+                 ctx->zstream.next_in, ctx->zstream.next_out,
+                 ctx->zstream.avail_in, ctx->zstream.avail_out,
+                 ctx->flush, ctx->redo);
+
+    rc = deflate(&ctx->zstream, ctx->flush);
+
+    if (rc != Z_OK && rc != Z_STREAM_END) {
+        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                      "deflate() failed: %d, %d", ctx->flush, rc);
+        return NGX_ERROR;
+    }
+
+    ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",
+                   ctx->zstream.next_in, ctx->zstream.next_out,
+                   ctx->zstream.avail_in, ctx->zstream.avail_out,
+                   rc);
+
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "gzip in_buf:%p pos:%p",
+                   ctx->in_buf, ctx->in_buf->pos);
+
+    if (ctx->zstream.next_in) {
+        ctx->in_buf->pos = ctx->zstream.next_in;
+
+        if (ctx->zstream.avail_in == 0) {
+            ctx->zstream.next_in = NULL;
+        }
+    }
+
+    ctx->out_buf->last = ctx->zstream.next_out;
+
+    if (ctx->zstream.avail_out == 0) {
+
+        /* zlib wants to output some more gzipped data */
+
+        cl = ngx_alloc_chain_link(r->pool);
+        if (cl == NULL) {
+            return NGX_ERROR;
+        }
+
+        cl->buf = ctx->out_buf;
+        cl->next = NULL;
+        *ctx->last_out = cl;
+        ctx->last_out = &cl->next;
+
+        ctx->redo = 1;
+
+        return NGX_AGAIN;
+    }
+
+    ctx->redo = 0;
+
+    if (ctx->flush == Z_SYNC_FLUSH) {
+
+        ctx->zstream.avail_out = 0;
+        ctx->out_buf->flush = 1;
+        ctx->flush = Z_NO_FLUSH;
+
+        cl = ngx_alloc_chain_link(r->pool);
+        if (cl == NULL) {
+            return NGX_ERROR;
+        }
+
+        cl->buf = ctx->out_buf;
+        cl->next = NULL;
+        *ctx->last_out = cl;
+        ctx->last_out = &cl->next;
+
+        return NGX_OK;
+    }
+
+    if (rc == Z_STREAM_END) {
+
+        if (ngx_http_gzip_filter_deflate_end(r, ctx) != NGX_OK) {
+            return NGX_ERROR;
+        }
+
+        return NGX_OK;
+    }
+
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
+
+    if (conf->no_buffer && ctx->in == NULL) {
+
+        cl = ngx_alloc_chain_link(r->pool);
+        if (cl == NULL) {
+            return NGX_ERROR;
+        }
+
+        cl->buf = ctx->out_buf;
+        cl->next = NULL;
+        *ctx->last_out = cl;
+        ctx->last_out = &cl->next;
+
+        return NGX_OK;
+    }
+
+    return NGX_AGAIN;
+}
+
+
+static ngx_int_t
+ngx_http_gzip_filter_deflate_end(ngx_http_request_t *r,
+    ngx_http_gzip_ctx_t *ctx)
+{
+    int                rc;
+    ngx_buf_t         *b;
+    ngx_chain_t       *cl;
+    struct gztrailer  *trailer;
+
+    ctx->zin = ctx->zstream.total_in;
+    ctx->zout = 10 + ctx->zstream.total_out + 8;
+
+    rc = deflateEnd(&ctx->zstream);
+
+    if (rc != Z_OK) {
+        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                      "deflateEnd() failed: %d", rc);
+        return NGX_ERROR;
+    }
+
+    ngx_pfree(r->pool, ctx->preallocated);
+
+    cl = ngx_alloc_chain_link(r->pool);
+    if (cl == NULL) {
+        return NGX_ERROR;
+    }
+
+    cl->buf = ctx->out_buf;
+    cl->next = NULL;
+    *ctx->last_out = cl;
+    ctx->last_out = &cl->next;
+
+    if (ctx->zstream.avail_out >= 8) {
+        trailer = (struct gztrailer *) ctx->out_buf->last;
+        ctx->out_buf->last += 8;
+        ctx->out_buf->last_buf = 1;
+
+    } else {
+        b = ngx_create_temp_buf(r->pool, 8);
+        if (b == NULL) {
+            return NGX_ERROR;
+        }
+
+        b->last_buf = 1;
+
+        cl = ngx_alloc_chain_link(r->pool);
+        if (cl == NULL) {
+            return NGX_ERROR;
+        }
+
+        cl->buf = b;
+        cl->next = NULL;
+        *ctx->last_out = cl;
+        ctx->last_out = &cl->next;
+        trailer = (struct gztrailer *) b->pos;
+        b->last += 8;
+    }
+
+#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
+
+    trailer->crc32 = ctx->crc32;
+    trailer->zlen = ctx->zin;
+
+#else
+
+    trailer->crc32[0] = (u_char) (ctx->crc32 & 0xff);
+    trailer->crc32[1] = (u_char) ((ctx->crc32 >> 8) & 0xff);
+    trailer->crc32[2] = (u_char) ((ctx->crc32 >> 16) & 0xff);
+    trailer->crc32[3] = (u_char) ((ctx->crc32 >> 24) & 0xff);
+
+    trailer->zlen[0] = (u_char) (ctx->zin & 0xff);
+    trailer->zlen[1] = (u_char) ((ctx->zin >> 8) & 0xff);
+    trailer->zlen[2] = (u_char) ((ctx->zin >> 16) & 0xff);
+    trailer->zlen[3] = (u_char) ((ctx->zin >> 24) & 0xff);
+
+#endif
+
+    ctx->zstream.avail_in = 0;
+    ctx->zstream.avail_out = 0;
+
+    ctx->done = 1;
+
+    r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED;
+
+    return NGX_OK;
 }
 
 
@@ -674,14 +938,14 @@ ngx_http_gzip_filter_alloc(void *opaque,
 
     alloc = items * size;
 
-    if (alloc % 512 != 0) {
+    if (alloc % 512 != 0 && alloc < 8192) {
 
         /*
          * The zlib deflate_state allocation, it takes about 6K,
          * we allocate 8K.  Other allocations are divisible by 512.
          */
 
-        alloc = (alloc + ngx_pagesize - 1) & ~(ngx_pagesize - 1);
+        alloc = 8192;
     }
 
     if (alloc <= ctx->allocated) {
@@ -719,20 +983,16 @@ ngx_http_gzip_filter_free(void *opaque, 
 
 
 static void
-ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx)
+ngx_http_gzip_filter_free_copy_buf(ngx_http_request_t *r,
+    ngx_http_gzip_ctx_t *ctx)
 {
-    deflateEnd(&ctx->zstream);
+    ngx_chain_t  *cl;
 
-    if (ctx->preallocated) {
-        ngx_pfree(ctx->request->pool, ctx->preallocated);
+    for (cl = ctx->copied; cl; cl = cl->next) {
+        ngx_pfree(r->pool, cl->buf->start);
     }
 
-    ctx->zstream.avail_in = 0;
-    ctx->zstream.avail_out = 0;
-
-    ctx->done = 1;
-
-    return;
+    ctx->copied = NULL;
 }
 
 
@@ -834,7 +1094,8 @@ ngx_http_gzip_merge_conf(ngx_conf_t *cf,
 
     ngx_conf_merge_value(conf->enable, prev->enable, 0);
 
-    ngx_conf_merge_bufs_value(conf->bufs, prev->bufs, 4, ngx_pagesize);
+    ngx_conf_merge_bufs_value(conf->bufs, prev->bufs,
+                              (128 * 1024) / ngx_pagesize, ngx_pagesize);
 
     ngx_conf_merge_value(conf->level, prev->level, 1);
     ngx_conf_merge_size_value(conf->wbits, prev->wbits, MAX_WBITS);
@@ -871,9 +1132,9 @@ ngx_http_gzip_filter_init(ngx_conf_t *cf
 static char *
 ngx_http_gzip_window(ngx_conf_t *cf, void *post, void *data)
 {
-    int *np = data;
+    size_t *np = data;
 
-    int  wbits, wsize;
+    size_t  wbits, wsize;
 
     wbits = 15;
 
@@ -895,9 +1156,9 @@ ngx_http_gzip_window(ngx_conf_t *cf, voi
 static char *
 ngx_http_gzip_hash(ngx_conf_t *cf, void *post, void *data)
 {
-    int *np = data;
+    size_t *np = data;
 
-    int  memlevel, hsize;
+    size_t  memlevel, hsize;
 
     memlevel = 9;
 
--- a/src/http/modules/ngx_http_headers_filter_module.c
+++ b/src/http/modules/ngx_http_headers_filter_module.c
@@ -325,15 +325,17 @@ ngx_http_add_header(ngx_http_request_t *
 {
     ngx_table_elt_t  *h;
 
-    h = ngx_list_push(&r->headers_out.headers);
-    if (h == NULL) {
-        return NGX_ERROR;
+    if (value->len) {
+        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;
     }
 
-    h->hash = hv->value.hash;
-    h->key = hv->value.key;
-    h->value = *value;
-
     return NGX_OK;
 }
 
--- a/src/http/modules/ngx_http_index_module.c
+++ b/src/http/modules/ngx_http_index_module.c
@@ -99,7 +99,6 @@ ngx_http_index_handler(ngx_http_request_
     size_t                        len, nlen, root, allocated;
     ngx_int_t                     rc;
     ngx_str_t                     path, uri;
-    ngx_log_t                    *log;
     ngx_uint_t                    i, dir_tested;
     ngx_http_index_t             *index;
     ngx_open_file_info_t          of;
@@ -122,8 +121,6 @@ ngx_http_index_handler(ngx_http_request_
         return NGX_DECLINED;
     }
 
-    log = r->connection->log;
-
     ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module);
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
 
@@ -206,7 +203,8 @@ ngx_http_index_handler(ngx_http_request_
             *e.pos++ = '\0';
         }
 
-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "open index \"%V\"", &path);
+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "open index \"%V\"", &path);
 
         ngx_memzero(&of, sizeof(ngx_open_file_info_t));
 
@@ -219,7 +217,7 @@ ngx_http_index_handler(ngx_http_request_
         if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
             != NGX_OK)
         {
-            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, of.err,
+            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, of.err,
                            ngx_open_file_n " \"%s\" failed", path.data);
 
             if (of.err == 0) {
@@ -244,7 +242,7 @@ ngx_http_index_handler(ngx_http_request_
                 continue;
             }
 
-            ngx_log_error(NGX_LOG_ERR, log, of.err,
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, of.err,
                           ngx_open_file_n " \"%s\" failed", path.data);
 
             return NGX_HTTP_INTERNAL_SERVER_ERROR;
new file mode 100644
--- /dev/null
+++ b/src/http/modules/ngx_http_limit_req_module.c
@@ -0,0 +1,767 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+    u_char              color;
+    u_char              dummy;
+    u_short             len;
+    ngx_queue_t         queue;
+    ngx_msec_t          last;
+    ngx_uint_t          excess; /* integer value, 1 corresponds to 0.001 r/s */
+    u_char              data[1];
+} ngx_http_limit_req_node_t;
+
+
+typedef struct {
+    ngx_rbtree_t       *rbtree;
+    ngx_queue_t        *queue;
+    ngx_slab_pool_t    *shpool;
+    ngx_uint_t          rate;   /* integer value, 1 corresponds to 0.001 r/s */
+    ngx_int_t           index;
+    ngx_str_t           var;
+} ngx_http_limit_req_ctx_t;
+
+
+typedef struct {
+    ngx_shm_zone_t     *shm_zone;
+    ngx_uint_t          burst;  /* integer value, 1 corresponds to 0.001 r/s */
+    ngx_uint_t          nodelay;/* unsigned  nodelay:1 */
+} ngx_http_limit_req_conf_t;
+
+
+static void ngx_http_limit_req_delay(ngx_http_request_t *r);
+static ngx_int_t ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lrcf,
+    ngx_uint_t hash, u_char *data, size_t len, ngx_http_limit_req_node_t **lrp);
+static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx,
+    ngx_uint_t n);
+
+static void *ngx_http_limit_req_create_conf(ngx_conf_t *cf);
+static char *ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent,
+    void *child);
+static char *ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+static char *ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+static ngx_int_t ngx_http_limit_req_init(ngx_conf_t *cf);
+
+
+static ngx_command_t  ngx_http_limit_req_commands[] = {
+
+    { ngx_string("limit_req_zone"),
+      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE3,
+      ngx_http_limit_req_zone,
+      0,
+      0,
+      NULL },
+
+    { ngx_string("limit_req"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
+      ngx_http_limit_req,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
+      ngx_null_command
+};
+
+
+static ngx_http_module_t  ngx_http_limit_req_module_ctx = {
+    NULL,                                  /* preconfiguration */
+    ngx_http_limit_req_init,               /* postconfiguration */
+
+    NULL,                                  /* create main configuration */
+    NULL,                                  /* init main configuration */
+
+    NULL,                                  /* create server configuration */
+    NULL,                                  /* merge server configuration */
+
+    ngx_http_limit_req_create_conf,        /* create location configration */
+    ngx_http_limit_req_merge_conf          /* merge location configration */
+};
+
+
+ngx_module_t  ngx_http_limit_req_module = {
+    NGX_MODULE_V1,
+    &ngx_http_limit_req_module_ctx,        /* module context */
+    ngx_http_limit_req_commands,           /* module directives */
+    NGX_HTTP_MODULE,                       /* module type */
+    NULL,                                  /* init master */
+    NULL,                                  /* init module */
+    NULL,                                  /* init process */
+    NULL,                                  /* init thread */
+    NULL,                                  /* exit thread */
+    NULL,                                  /* exit process */
+    NULL,                                  /* exit master */
+    NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_http_limit_req_handler(ngx_http_request_t *r)
+{
+    size_t                      len, n;
+    uint32_t                    hash;
+    ngx_int_t                   rc;
+    ngx_uint_t                  excess;
+    ngx_time_t                 *tp;
+    ngx_rbtree_node_t          *node;
+    ngx_http_variable_value_t  *vv;
+    ngx_http_limit_req_ctx_t   *ctx;
+    ngx_http_limit_req_node_t  *lr;
+    ngx_http_limit_req_conf_t  *lrcf;
+
+    if (r->main->limit_req_set) {
+        return NGX_DECLINED;
+    }
+
+    lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module);
+
+    if (lrcf->shm_zone == NULL) {
+        return NGX_DECLINED;
+    }
+
+    ctx = lrcf->shm_zone->data;
+
+    vv = ngx_http_get_indexed_variable(r, ctx->index);
+
+    if (vv == NULL || vv->not_found) {
+        return NGX_DECLINED;
+    }
+
+    len = vv->len;
+
+    if (len == 0) {
+        return NGX_DECLINED;
+    }
+
+    if (len > 65535) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "the value of the \"%V\" variable "
+                      "is more than 65535 bytes: \"%v\"",
+                      &ctx->var, vv);
+        return NGX_DECLINED;
+    }
+
+    r->main->limit_req_set = 1;
+
+    hash = ngx_crc32_short(vv->data, len);
+
+    ngx_shmtx_lock(&ctx->shpool->mutex);
+
+    ngx_http_limit_req_expire(ctx, 1);
+
+    rc = ngx_http_limit_req_lookup(lrcf, hash, vv->data, len, &lr);
+
+    if (lr) {
+        ngx_queue_remove(&lr->queue);
+
+        ngx_queue_insert_head(ctx->queue, &lr->queue);
+
+        excess = lr->excess;
+
+    } else {
+        excess = 0;
+    }
+
+    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                  "limit_req: %i %ui.%03ui", rc, excess / 1000, excess % 1000);
+
+    if (rc == NGX_BUSY) {
+        ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "limiting requests, excess: %ui.%03ui by zone \"%V\"",
+                      excess / 1000, excess % 1000, &lrcf->shm_zone->name);
+
+        return NGX_HTTP_SERVICE_UNAVAILABLE;
+    }
+
+    if (rc == NGX_AGAIN) {
+        ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+        if (lrcf->nodelay) {
+            return NGX_DECLINED;
+        }
+
+        ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
+                      "delaying request, excess: %ui.%03ui, by zone \"%V\"",
+                      excess / 1000, excess % 1000, &lrcf->shm_zone->name);
+
+        if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        r->read_event_handler = ngx_http_test_reading;
+        r->write_event_handler = ngx_http_limit_req_delay;
+        ngx_add_timer(r->connection->write, (ngx_msec_t) excess);
+
+        return NGX_AGAIN;
+    }
+
+    if (rc == NGX_OK) {
+        goto done;
+    }
+
+    /* rc == NGX_DECLINED */
+
+    n = offsetof(ngx_rbtree_node_t, color)
+        + offsetof(ngx_http_limit_req_node_t, data)
+        + len;
+
+    node = ngx_slab_alloc_locked(ctx->shpool, n);
+    if (node == NULL) {
+
+        ngx_http_limit_req_expire(ctx, 0);
+
+        node = ngx_slab_alloc_locked(ctx->shpool, n);
+        if (node == NULL) {
+            ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+            ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
+                          "could not allocate memory in zone \"%V\"",
+                          &lrcf->shm_zone->name);
+
+            return NGX_HTTP_SERVICE_UNAVAILABLE;
+        }
+    }
+
+    lr = (ngx_http_limit_req_node_t *) &node->color;
+
+    node->key = hash;
+    lr->len = (u_char) len;
+
+    tp = ngx_timeofday();
+    lr->last = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
+
+    lr->excess = 0;
+    ngx_memcpy(lr->data, vv->data, len);
+
+    ngx_rbtree_insert(ctx->rbtree, node);
+
+    ngx_queue_insert_head(ctx->queue, &lr->queue);
+
+done:
+
+    ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+    return NGX_DECLINED;
+}
+
+
+static void
+ngx_http_limit_req_delay(ngx_http_request_t *r)
+{
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                  "limit_req delay");
+
+    if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
+        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+        return;
+    }
+
+    r->read_event_handler = ngx_http_block_reading;
+    r->write_event_handler = ngx_http_core_run_phases;
+
+    ngx_http_core_run_phases(r);
+}
+
+
+static void
+ngx_http_limit_req_rbtree_insert_value(ngx_rbtree_node_t *temp,
+    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
+{
+    ngx_rbtree_node_t          **p;
+    ngx_http_limit_req_node_t   *lrn, *lrnt;
+
+    for ( ;; ) {
+
+        if (node->key < temp->key) {
+
+            p = &temp->left;
+
+        } else if (node->key > temp->key) {
+
+            p = &temp->right;
+
+        } else { /* node->key == temp->key */
+
+            lrn = (ngx_http_limit_req_node_t *) &node->color;
+            lrnt = (ngx_http_limit_req_node_t *) &temp->color;
+
+            p = (ngx_memn2cmp(lrn->data, lrnt->data, lrn->len, lrnt->len) < 0)
+                ? &temp->left : &temp->right;
+        }
+
+        if (*p == sentinel) {
+            break;
+        }
+
+        temp = *p;
+    }
+
+    *p = node;
+    node->parent = temp;
+    node->left = sentinel;
+    node->right = sentinel;
+    ngx_rbt_red(node);
+}
+
+
+static ngx_int_t
+ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lrcf, ngx_uint_t hash,
+    u_char *data, size_t len, ngx_http_limit_req_node_t **lrp)
+{
+    ngx_int_t                   rc, excess;
+    ngx_time_t                 *tp;
+    ngx_msec_t                  now;
+    ngx_msec_int_t              ms;
+    ngx_rbtree_node_t          *node, *sentinel;
+    ngx_http_limit_req_ctx_t   *ctx;
+    ngx_http_limit_req_node_t  *lr;
+
+    ctx = lrcf->shm_zone->data;
+
+    node = ctx->rbtree->root;
+    sentinel = ctx->rbtree->sentinel;
+
+    while (node != sentinel) {
+
+        if (hash < node->key) {
+            node = node->left;
+            continue;
+        }
+
+        if (hash > node->key) {
+            node = node->right;
+            continue;
+        }
+
+        /* hash == node->key */
+
+        do {
+            lr = (ngx_http_limit_req_node_t *) &node->color;
+
+            rc = ngx_memn2cmp(data, lr->data, len, (size_t) lr->len);
+
+            if (rc == 0) {
+
+                tp = ngx_timeofday();
+
+                now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
+                ms = (ngx_msec_int_t) (now - lr->last);
+
+                excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
+
+                if (excess < 0) {
+                    excess = 0;
+                }
+
+                lr->excess = excess;
+                lr->last = now;
+
+                *lrp = lr;
+
+                if ((ngx_uint_t) excess > lrcf->burst) {
+                    return NGX_BUSY;
+                }
+
+                if (excess) {
+                    return NGX_AGAIN;
+                }
+
+                return NGX_OK;
+            }
+
+            node = (rc < 0) ? node->left : node->right;
+
+        } while (node != sentinel && hash == node->key);
+
+        break;
+    }
+
+    *lrp = NULL;
+
+    return NGX_DECLINED;
+}
+
+
+static void
+ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n)
+{
+    ngx_int_t                   excess;
+    ngx_time_t                 *tp;
+    ngx_msec_t                  now;
+    ngx_queue_t                *q;
+    ngx_msec_int_t              ms;
+    ngx_rbtree_node_t          *node;
+    ngx_http_limit_req_node_t  *lr;
+
+    tp = ngx_timeofday();
+
+    now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
+
+    /*
+     * n == 1 deletes one or two zero rate entries
+     * n == 0 deletes oldest entry by force
+     *        and one or two zero rate entries
+     */
+
+    while (n < 3) {
+
+        if (ngx_queue_empty(ctx->queue)) {
+            return;
+        }
+
+        q = ngx_queue_last(ctx->queue);
+
+        lr = ngx_queue_data(q, ngx_http_limit_req_node_t, queue);
+
+        if (n++ != 0) {
+
+            ms = (ngx_msec_int_t) (now - lr->last);
+            ms = ngx_abs(ms);
+
+            if (ms < 60000) {
+                return;
+            }
+
+            excess = lr->excess - ctx->rate * ms / 1000;
+
+            if (excess > 0) {
+                return;
+            }
+        }
+
+        ngx_queue_remove(q);
+
+        node = (ngx_rbtree_node_t *)
+                   ((u_char *) lr - offsetof(ngx_rbtree_node_t, color));
+
+        ngx_rbtree_delete(ctx->rbtree, node);
+
+        ngx_slab_free_locked(ctx->shpool, node);
+    }
+}
+
+
+static ngx_int_t
+ngx_http_limit_req_init_zone(ngx_shm_zone_t *shm_zone, void *data)
+{
+    ngx_http_limit_req_ctx_t  *octx = data;
+
+    ngx_rbtree_node_t         *sentinel;
+    ngx_http_limit_req_ctx_t  *ctx;
+
+    ctx = shm_zone->data;
+
+    if (octx) {
+        if (ngx_strcmp(ctx->var.data, octx->var.data) != 0) {
+            ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
+                          "limit_req \"%V\" uses the \"%V\" variable "
+                          "while previously it used the \"%V\" variable",
+                          &shm_zone->name, &ctx->var, &octx->var);
+            return NGX_ERROR;
+        }
+
+        ctx->rbtree = octx->rbtree;
+        ctx->queue = octx->queue;
+        ctx->shpool = octx->shpool;
+
+        return NGX_OK;
+    }
+
+    ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
+
+    ctx->rbtree = ngx_slab_alloc(ctx->shpool, sizeof(ngx_rbtree_t));
+    if (ctx->rbtree == NULL) {
+        return NGX_ERROR;
+    }
+
+    sentinel = ngx_slab_alloc(ctx->shpool, sizeof(ngx_rbtree_node_t));
+    if (sentinel == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_rbtree_init(ctx->rbtree, sentinel,
+                    ngx_http_limit_req_rbtree_insert_value);
+
+    ctx->queue = ngx_slab_alloc(ctx->shpool, sizeof(ngx_queue_t));
+    if (ctx->queue == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_queue_init(ctx->queue);
+
+    return NGX_OK;
+}
+
+
+static void *
+ngx_http_limit_req_create_conf(ngx_conf_t *cf)
+{
+    ngx_http_limit_req_conf_t  *conf;
+
+    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_conf_t));
+    if (conf == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    /*
+     * set by ngx_pcalloc():
+     *
+     *     conf->shm_zone = NULL;
+     *     conf->burst = 0;
+     *     conf->nodelay = 0;
+     */
+
+    return conf;
+}
+
+
+static char *
+ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+    ngx_http_limit_req_conf_t *prev = parent;
+    ngx_http_limit_req_conf_t *conf = child;
+
+    if (conf->shm_zone == NULL) {
+        *conf = *prev;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    u_char                    *p;
+    size_t                     size, len;
+    ngx_str_t                 *value, name, s;
+    ngx_int_t                  rate, scale;
+    ngx_uint_t                 i;
+    ngx_shm_zone_t            *shm_zone;
+    ngx_http_limit_req_ctx_t  *ctx;
+
+    value = cf->args->elts;
+
+    ctx = NULL;
+    size = 0;
+    rate = 1;
+    scale = 1;
+    name.len = 0;
+
+    for (i = 1; i < cf->args->nelts; i++) {
+
+        if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {
+
+            name.data = value[i].data + 5;
+
+            p = (u_char *) ngx_strchr(name.data, ':');
+
+            if (p) {
+                name.len = p - name.data;
+
+                p++;
+
+                s.len = value[i].data + value[i].len - p;
+                s.data = p;
+
+                size = ngx_parse_size(&s);
+                if (size > 8191) {
+                    continue;
+                }
+            }
+
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "invalid zone size \"%V\"", &value[i]);
+            return NGX_CONF_ERROR;
+        }
+
+        if (ngx_strncmp(value[i].data, "rate=", 5) == 0) {
+
+            len = value[i].len;
+            p = value[i].data + len - 3;
+
+            if (ngx_strncmp(p, "r/s", 3) == 0) {
+                scale = 1;
+                len -= 3;
+
+            } else if (ngx_strncmp(p, "r/m", 3) == 0) {
+                scale = 60;
+                len -= 3;
+            }
+
+            rate = ngx_atoi(value[i].data + 5, len - 5);
+            if (rate <= NGX_ERROR) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "invalid rate \"%V\"", &value[i]);
+                return NGX_CONF_ERROR;
+            }
+
+            continue;
+        }
+
+        if (value[i].data[0] == '$') {
+
+            value[i].len--;
+            value[i].data++;
+
+            ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_ctx_t));
+            if (ctx == NULL) {
+                return NGX_CONF_ERROR;
+            }
+
+            ctx->index = ngx_http_get_variable_index(cf, &value[i]);
+            if (ctx->index == NGX_ERROR) {
+                return NGX_CONF_ERROR;
+            }
+
+            ctx->var = value[i];
+
+            continue;
+        }
+
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid parameter \"%V\"", &value[i]);
+        return NGX_CONF_ERROR;
+    }
+
+    if (name.len == 0 || size == 0) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "\"%V\" must have \"zone\" parameter",
+                           &cmd->name);
+        return NGX_CONF_ERROR;
+    }
+
+    if (ctx == NULL) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "no variable is defined for limit_req_zone \"%V\"",
+                           &cmd->name);
+        return NGX_CONF_ERROR;
+    }
+
+    ctx->rate = rate * 1000 / scale;
+
+    shm_zone = ngx_shared_memory_add(cf, &name, size,
+                                     &ngx_http_limit_req_module);
+    if (shm_zone == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    if (shm_zone->data) {
+        ctx = shm_zone->data;
+
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                   "limit_req_zone \"%V\" is already bound to variable \"%V\"",
+                   &value[1], &ctx->var);
+        return NGX_CONF_ERROR;
+    }
+
+    shm_zone->init = ngx_http_limit_req_init_zone;
+    shm_zone->data = ctx;
+
+    return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_limit_req_conf_t  *lrcf = conf;
+
+    ngx_int_t    burst;
+    ngx_str_t   *value, s;
+    ngx_uint_t   i;
+
+    if (lrcf->shm_zone) {
+        return "is duplicate";
+    }
+
+    value = cf->args->elts;
+
+    burst = 0;
+
+    for (i = 1; i < cf->args->nelts; i++) {
+
+        if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {
+
+            s.len = value[i].len - 5;
+            s.data = value[i].data + 5;
+
+            lrcf->shm_zone = ngx_shared_memory_add(cf, &s, 0,
+                                                   &ngx_http_limit_req_module);
+            if (lrcf->shm_zone == NULL) {
+                return NGX_CONF_ERROR;
+            }
+
+            continue;
+        }
+
+        if (ngx_strncmp(value[i].data, "burst=", 6) == 0) {
+
+            burst = ngx_atoi(value[i].data + 6, value[i].len - 6);
+            if (burst <= 0) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "invalid burst rate \"%V\"", &value[i]);
+                return NGX_CONF_ERROR;
+            }
+
+            continue;
+        }
+
+        if (ngx_strncmp(value[i].data, "nodelay", 7) == 0) {
+            lrcf->nodelay = 1;
+            continue;
+        }
+
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid parameter \"%V\"", &value[i]);
+        return NGX_CONF_ERROR;
+    }
+
+    if (lrcf->shm_zone == NULL) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "\"%V\" must have \"zone\" parameter",
+                           &cmd->name);
+        return NGX_CONF_ERROR;
+    }
+
+    if (lrcf->shm_zone->data == NULL) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "unknown limit_req_zone \"%V\"",
+                           &lrcf->shm_zone->name);
+        return NGX_CONF_ERROR;
+    }
+
+    lrcf->burst = burst * 1000;
+
+    return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_limit_req_init(ngx_conf_t *cf)
+{
+    ngx_http_handler_pt        *h;
+    ngx_http_core_main_conf_t  *cmcf;
+
+    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+    h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
+    if (h == NULL) {
+        return NGX_ERROR;
+    }
+
+    *h = ngx_http_limit_req_handler;
+
+    return NGX_OK;
+}
--- a/src/http/modules/ngx_http_limit_zone_module.c
+++ b/src/http/modules/ngx_http_limit_zone_module.c
@@ -189,6 +189,10 @@ ngx_http_limit_zone_handler(ngx_http_req
 
                 ngx_shmtx_unlock(&shpool->mutex);
 
+                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                              "limiting connections by zone \"%V\"",
+                              &lzcf->shm_zone->name);
+
                 return NGX_HTTP_SERVICE_UNAVAILABLE;
             }
 
@@ -206,6 +210,11 @@ ngx_http_limit_zone_handler(ngx_http_req
     node = ngx_slab_alloc_locked(shpool, n);
     if (node == NULL) {
         ngx_shmtx_unlock(&shpool->mutex);
+
+        ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
+                      "could not allocate memory in zone \"%V\"",
+                      &lzcf->shm_zone->name);
+
         return NGX_HTTP_SERVICE_UNAVAILABLE;
     }
 
@@ -462,6 +471,10 @@ ngx_http_limit_conn(ngx_conf_t *cf, ngx_
     ngx_int_t   n;
     ngx_str_t  *value;
 
+    if (lzcf->shm_zone) {
+        return "is duplicate";
+    }
+
     value = cf->args->elts;
 
     lzcf->shm_zone = ngx_shared_memory_add(cf, &value[1], 0,
--- a/src/http/modules/ngx_http_log_module.c
+++ b/src/http/modules/ngx_http_log_module.c
@@ -433,7 +433,7 @@ ngx_http_log_script_write(ngx_http_reque
     of.log = 1;
     of.valid = llcf->open_file_cache_valid;
     of.min_uses = llcf->open_file_cache_min_uses;
-    of.directio = NGX_MAX_OFF_T_VALUE;
+    of.directio = NGX_OPEN_FILE_DIRECTIO_OFF;
 
     if (ngx_open_cached_file(llcf->open_file_cache, &log, &of, r->pool)
         != NGX_OK)
--- a/src/http/modules/ngx_http_memcached_module.c
+++ b/src/http/modules/ngx_http_memcached_module.c
@@ -183,7 +183,8 @@ ngx_http_memcached_handler(ngx_http_requ
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
-    u->schema = mlcf->upstream.schema;
+    u->schema.len = sizeof("memcached://") - 1;
+    u->schema.data = (u_char *) "memcached://";
 
     u->peer.log = r->connection->log;
     u->peer.log_error = NGX_ERROR_ERR;
@@ -521,7 +522,6 @@ ngx_http_memcached_create_loc_conf(ngx_c
      *     conf->upstream.bufs.num = 0;
      *     conf->upstream.next_upstream = 0;
      *     conf->upstream.temp_path = NULL;
-     *     conf->upstream.schema = { 0, NULL };
      *     conf->upstream.uri = { 0, NULL };
      *     conf->upstream.location = NULL;
      */
@@ -584,7 +584,6 @@ ngx_http_memcached_merge_loc_conf(ngx_co
 
     if (conf->upstream.upstream == NULL) {
         conf->upstream.upstream = prev->upstream.upstream;
-        conf->upstream.schema = prev->upstream.schema;
     }
 
     if (conf->index == NGX_CONF_UNSET) {
@@ -598,13 +597,13 @@ ngx_http_memcached_merge_loc_conf(ngx_co
 static char *
 ngx_http_memcached_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
-    ngx_http_memcached_loc_conf_t *lcf = conf;
+    ngx_http_memcached_loc_conf_t *mlcf = conf;
 
     ngx_str_t                 *value;
     ngx_url_t                  u;
     ngx_http_core_loc_conf_t  *clcf;
 
-    if (lcf->upstream.schema.len) {
+    if (mlcf->upstream.upstream) {
         return "is duplicate";
     }
 
@@ -615,14 +614,11 @@ ngx_http_memcached_pass(ngx_conf_t *cf, 
     u.url = value[1];
     u.no_resolve = 1;
 
-    lcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
-    if (lcf->upstream.upstream == NULL) {
+    mlcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
+    if (mlcf->upstream.upstream == NULL) {
         return NGX_CONF_ERROR;
     }
 
-    lcf->upstream.schema.len = sizeof("memcached://") - 1;
-    lcf->upstream.schema.data = (u_char *) "memcached://";
-
     clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
 
     clcf->handler = ngx_http_memcached_handler;
@@ -631,9 +627,9 @@ ngx_http_memcached_pass(ngx_conf_t *cf, 
         clcf->auto_redirect = 1;
     }
 
-    lcf->index = ngx_http_get_variable_index(cf, &ngx_http_memcached_key);
+    mlcf->index = ngx_http_get_variable_index(cf, &ngx_http_memcached_key);
 
-    if (lcf->index == NGX_ERROR) {
+    if (mlcf->index == NGX_ERROR) {
         return NGX_CONF_ERROR;
     }
 
--- a/src/http/modules/ngx_http_not_modified_filter_module.c
+++ b/src/http/modules/ngx_http_not_modified_filter_module.c
@@ -50,7 +50,8 @@ static ngx_http_output_header_filter_pt 
 static
 ngx_int_t ngx_http_not_modified_header_filter(ngx_http_request_t *r)
 {
-    time_t  ims;
+    time_t                     ims;
+    ngx_http_core_loc_conf_t  *clcf;
 
     if (r->headers_out.status != NGX_HTTP_OK
         || r != r->main
@@ -66,17 +67,22 @@ ngx_int_t ngx_http_not_modified_header_f
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http ims:%d lm:%d", ims, r->headers_out.last_modified_time);
 
-    /*
-     * I think that the equality of the dates is correcter
-     */
+    if (ims != r->headers_out.last_modified_time) {
+
+        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
 
-    if (ims == r->headers_out.last_modified_time) {
-        r->headers_out.status = NGX_HTTP_NOT_MODIFIED;
-        r->headers_out.content_type.len = 0;
-        ngx_http_clear_content_length(r);
-        ngx_http_clear_accept_ranges(r);
+        if (clcf->if_modified_since == 0
+            || ims < r->headers_out.last_modified_time)
+        {
+            return ngx_http_next_header_filter(r);
+        }
     }
 
+    r->headers_out.status = NGX_HTTP_NOT_MODIFIED;
+    r->headers_out.content_type.len = 0;
+    ngx_http_clear_content_length(r);
+    ngx_http_clear_accept_ranges(r);
+
     return ngx_http_next_header_filter(r);
 }
 
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -32,6 +32,7 @@ struct ngx_http_proxy_redirect_s {
 
 
 typedef struct {
+    ngx_str_t                      schema;
     ngx_str_t                      host_header;
     ngx_str_t                      port;
     ngx_str_t                      uri;
@@ -480,7 +481,7 @@ ngx_http_proxy_handler(ngx_http_request_
 
     if (plcf->proxy_lengths == 0) {
         ctx->vars = plcf->vars;
-        u->schema = plcf->upstream.schema;
+        u->schema = plcf->vars.schema;
 #if (NGX_HTTP_SSL)
         u->ssl = (plcf->upstream.ssl != NULL);
 #endif
@@ -536,10 +537,11 @@ static ngx_int_t
 ngx_http_proxy_eval(ngx_http_request_t *r, ngx_http_proxy_ctx_t *ctx,
     ngx_http_proxy_loc_conf_t *plcf)
 {
-    size_t     add;
-    u_short    port;
-    ngx_str_t  proxy;
-    ngx_url_t  u;
+    u_char     *p;
+    size_t      add;
+    u_short     port;
+    ngx_str_t   proxy;
+    ngx_url_t   u;
 
     if (ngx_http_script_run(r, &proxy, plcf->proxy_lengths->elts, 0,
                             plcf->proxy_values->elts)
@@ -589,6 +591,19 @@ ngx_http_proxy_eval(ngx_http_request_t *
         return NGX_ERROR;
     }
 
+    if (u.uri.len && u.uri.data[0] == '?') {
+        p = ngx_pnalloc(r->pool, u.uri.len + 1);
+        if (p == NULL) {
+            return NGX_ERROR;
+        }
+
+        *p++ = '/';
+        ngx_memcpy(p, u.uri.data, u.uri.len);
+
+        u.uri.len++;
+        u.uri.data = p - 1;
+    }
+
     if (ngx_http_proxy_set_vars(r->pool, &u, &ctx->vars) != NGX_OK) {
         return NGX_ERROR;
     }
@@ -599,10 +614,18 @@ ngx_http_proxy_eval(ngx_http_request_t *
         return NGX_ERROR;
     }
 
-    r->upstream->resolved->host = u.host;
-    r->upstream->resolved->port = (in_port_t) (u.no_port ? u.default_port:
-                                                           u.port);
-    r->upstream->resolved->default_port = u.default_port;
+    if (u.addrs && u.addrs[0].sockaddr) {
+        r->upstream->resolved->sockaddr = u.addrs[0].sockaddr;
+        r->upstream->resolved->socklen = u.addrs[0].socklen;
+        r->upstream->resolved->naddrs = 1;
+        r->upstream->resolved->host = u.addrs[0].name;
+
+    } else {
+        r->upstream->resolved->host = u.host;
+        r->upstream->resolved->port = (in_port_t) (u.no_port ? u.default_port:
+                                                               u.port);
+        r->upstream->resolved->no_port = u.no_port;
+    }
 
     return NGX_OK;
 }
@@ -1617,7 +1640,6 @@ ngx_http_proxy_create_loc_conf(ngx_conf_
      *     conf->upstream.next_upstream = 0;
      *     conf->upstream.temp_path = NULL;
      *     conf->upstream.hide_headers_hash = { NULL, 0 };
-     *     conf->upstream.schema = { 0, NULL };
      *     conf->upstream.uri = { 0, NULL };
      *     conf->upstream.location = NULL;
      *     conf->upstream.store_lengths = NULL;
@@ -1917,9 +1939,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
 
     if (conf->upstream.upstream == NULL) {
         conf->upstream.upstream = prev->upstream.upstream;
-
         conf->vars = prev->vars;
-        conf->upstream.schema = prev->upstream.schema;
     }
 
 
@@ -2202,7 +2222,7 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_
     ngx_http_core_loc_conf_t   *clcf;
     ngx_http_script_compile_t   sc;
 
-    if (plcf->upstream.schema.len) {
+    if (plcf->upstream.upstream || plcf->proxy_lengths) {
         return "is duplicate";
     }
 
@@ -2282,8 +2302,8 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_
         return NGX_CONF_ERROR;
     }
 
-    plcf->upstream.schema.len = add;
-    plcf->upstream.schema.data = url->data;
+    plcf->vars.schema.len = add;
+    plcf->vars.schema.data = url->data;
     plcf->location = clcf->name;
 
     clcf->handler = ngx_http_proxy_handler;
--- a/src/http/modules/ngx_http_secure_link_module.c
+++ b/src/http/modules/ngx_http_secure_link_module.c
@@ -113,7 +113,7 @@ url_start:
 
     len = last - p;
 
-    if (end - start != 32 || len < 3) {
+    if (end - start != 32 || len == 0) {
         goto not_found;
     }
 
--- a/src/http/modules/ngx_http_ssi_filter_module.c
+++ b/src/http/modules/ngx_http_ssi_filter_module.c
@@ -375,7 +375,6 @@ ngx_http_ssi_body_filter(ngx_http_reques
     ngx_uint_t                 i, index;
     ngx_chain_t               *cl, **ll;
     ngx_table_elt_t           *param;
-    ngx_http_request_t        *pr;
     ngx_http_ssi_ctx_t        *ctx, *mctx;
     ngx_http_ssi_block_t      *bl;
     ngx_http_ssi_param_t      *prm;
@@ -403,44 +402,37 @@ ngx_http_ssi_body_filter(ngx_http_reques
         }
     }
 
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http ssi filter \"%V?%V\"", &r->uri, &r->args);
+
     if (ctx->wait) {
-        if (r->connection->data != r) {
-            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                           "http ssi filter \"%V\" wait", &r->uri);
+
+        if (r != r->connection->data) {
+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "http ssi filter wait \"%V?%V\" non-active",
+                           &ctx->wait->uri, &ctx->wait->args);
+
             return NGX_AGAIN;
         }
 
-        for (pr = ctx->wait->parent; pr; pr = pr->parent) {
-            if (pr == r) {
-                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                               "http ssi filter \"%V\" flush", &r->uri);
-
-                rc = ngx_http_next_body_filter(r, NULL);
-
-                if (ctx->wait->done) {
-                    ctx->wait = NULL;
-                }
-
-                if (rc == NGX_ERROR || rc == NGX_AGAIN) {
-                    return rc;
-                }
-
-                break;
-            }
-        }
-
-        if (ctx->wait == r) {
-            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                           "http ssi filter \"%V\" continue", &r->uri);
+        if (ctx->wait->done) {
+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "http ssi filter wait \"%V?%V\" done",
+                           &ctx->wait->uri, &ctx->wait->args);
+
             ctx->wait = NULL;
+
+        } else {
+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "http ssi filter wait \"%V?%V\"",
+                           &ctx->wait->uri, &ctx->wait->args);
+
+            return ngx_http_next_body_filter(r, NULL);
         }
     }
 
     slcf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module);
 
-    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "http ssi filter \"%V\"", &r->uri);
-
     while (ctx->in || ctx->buf) {
 
         if (ctx->buf == NULL ){
@@ -788,16 +780,12 @@ ngx_http_ssi_body_filter(ngx_http_reques
                     }
                 }
 
-                if (cmd->flush) {
-
-                    if (ctx->out) {
-                        rc = ngx_http_ssi_output(r, ctx);
-
-                    } else {
-                        rc = ngx_http_next_body_filter(r, NULL);
-                    }
-
-                    if (rc == NGX_ERROR) {
+                if (cmd->flush && ctx->out) {
+
+                    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                                   "ssi flush");
+
+                    if (ngx_http_ssi_output(r, ctx) == NGX_ERROR) {
                         return NGX_ERROR;
                     }
                 }
@@ -947,12 +935,6 @@ ngx_http_ssi_output(ngx_http_request_t *
             break;
         }
 
-#if (NGX_HAVE_WRITE_ZEROCOPY)
-        if (b->zerocopy_busy) {
-            break;
-        }
-#endif
-
         if (b->shadow) {
             b->shadow->pos = b->shadow->last;
         }
@@ -1899,6 +1881,7 @@ ngx_http_ssi_include(ngx_http_request_t 
 
     if (uri == NULL) {
         uri = file;
+        wait = (ngx_str_t *) -1;
     }
 
     rc = ngx_http_ssi_evaluate_string(r, ctx, uri, NGX_HTTP_SSI_ADD_PREFIX);
@@ -2001,6 +1984,10 @@ ngx_http_ssi_include(ngx_http_request_t 
         }
     }
 
+    if (wait) {
+        flags |= NGX_HTTP_SUBREQUEST_WAITED;
+    }
+
     if (set) {
         key = ngx_hash_strlow(set->data, set->data, set->len);
 
@@ -2033,16 +2020,10 @@ ngx_http_ssi_include(ngx_http_request_t 
             psr->data = &var->value;
         }
 
-        flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY;
+        flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY|NGX_HTTP_SUBREQUEST_WAITED;
     }
 
-    rc = ngx_http_subrequest(r, uri, &args, &sr, psr, flags);
-
-    if (rc == NGX_DONE) {
-        return NGX_DONE;
-    }
-
-    if (rc == NGX_ERROR) {
+    if (ngx_http_subrequest(r, uri, &args, &sr, psr, flags) != NGX_OK) {
         return NGX_HTTP_SSI_ERROR;
     }
 
@@ -2050,17 +2031,17 @@ ngx_http_ssi_include(ngx_http_request_t 
         return NGX_OK;
     }
 
-    if (rc == NGX_AGAIN) {
-        if (ctx->wait == NULL) {
-            ctx->wait = sr;
-
-        } else {
-            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                          "only one subrequest may be waited at the same time");
-        }
+    if (ctx->wait == NULL) {
+        ctx->wait = sr;
+
+        return NGX_AGAIN;
+
+    } else {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "only one subrequest may be waited at the same time");
     }
 
-    return rc;
+    return NGX_OK;
 }
 
 
--- a/src/http/modules/ngx_http_sub_filter_module.c
+++ b/src/http/modules/ngx_http_sub_filter_module.c
@@ -465,12 +465,6 @@ ngx_http_sub_output(ngx_http_request_t *
             break;
         }
 
-#if (NGX_HAVE_WRITE_ZEROCOPY)
-        if (b->zerocopy_busy) {
-            break;
-        }
-#endif
-
         if (b->shadow) {
             b->shadow->pos = b->shadow->last;
         }
--- a/src/http/modules/ngx_http_xslt_filter_module.c
+++ b/src/http/modules/ngx_http_xslt_filter_module.c
@@ -15,6 +15,10 @@
 #include <libxslt/transform.h>
 #include <libxslt/xsltutils.h>
 
+#if (NGX_HAVE_EXSLT)
+#include <libexslt/exslt.h>
+#endif
+
 
 #ifndef NGX_HTTP_XSLT_REUSE_DTD
 #define NGX_HTTP_XSLT_REUSE_DTD  1
@@ -1269,6 +1273,10 @@ ngx_http_xslt_filter_init(ngx_conf_t *cf
 {
     xmlInitParser();
 
+#if (NGX_HAVE_EXSLT)
+    exsltRegisterAll();
+#endif
+
     ngx_http_next_header_filter = ngx_http_top_header_filter;
     ngx_http_top_header_filter = ngx_http_xslt_header_filter;
 
--- 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.7.19';
+our $VERSION = '0.7.30';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/modules/perl/ngx_http_perl_module.c
+++ b/src/http/modules/perl/ngx_http_perl_module.c
@@ -285,7 +285,7 @@ ngx_http_perl_sleep_handler(ngx_http_req
         return;
     }
 
-    if (ngx_handle_write_event(wev, 0) == NGX_ERROR) {
+    if (ngx_handle_write_event(wev, 0) != NGX_OK) {
         ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
     }
 }
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -489,7 +489,7 @@ ngx_http_init_phase_handlers(ngx_conf_t 
     use_rewrite = cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers.nelts ? 1 : 0;
     use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0;
 
-    n = use_rewrite + use_access + 1; /* find config phase */
+    n = use_rewrite + use_access + cmcf->try_files + 1 /* find config phase */;
 
     for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
         n += cmcf->phases[i].handlers.nelts;
@@ -558,6 +558,15 @@ ngx_http_init_phase_handlers(ngx_conf_t 
 
             continue;
 
+        case NGX_HTTP_TRY_FILES_PHASE:
+            if (cmcf->try_files) {
+                ph->checker = ngx_http_core_try_files_phase;
+                n++;
+                ph++;
+            }
+
+            continue;
+
         case NGX_HTTP_CONTENT_PHASE:
             checker = ngx_http_core_content_phase;
             break;
--- a/src/http/ngx_http.h
+++ b/src/http/ngx_http.h
@@ -76,15 +76,21 @@ ngx_int_t ngx_http_parse_header_line(ngx
     ngx_uint_t allow_underscores);
 ngx_int_t ngx_http_parse_multi_header_lines(ngx_array_t *headers,
     ngx_str_t *name, ngx_str_t *value);
+ngx_int_t ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len,
+    ngx_str_t *value);
+
 
 ngx_int_t ngx_http_find_server_conf(ngx_http_request_t *r);
 void ngx_http_update_location_config(ngx_http_request_t *r);
 void ngx_http_handler(ngx_http_request_t *r);
+void ngx_http_run_posted_requests(ngx_connection_t *c);
+ngx_int_t ngx_http_post_request(ngx_http_request_t *r);
 void ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc);
 
 void ngx_http_empty_handler(ngx_event_t *wev);
 void ngx_http_request_empty_handler(ngx_http_request_t *r);
 
+
 #define NGX_HTTP_LAST   1
 #define NGX_HTTP_FLUSH  2
 
@@ -106,6 +112,7 @@ size_t ngx_http_get_time(char *buf, time
 
 ngx_int_t ngx_http_discard_request_body(ngx_http_request_t *r);
 void ngx_http_block_reading(ngx_http_request_t *r);
+void ngx_http_test_reading(ngx_http_request_t *r);
 
 
 char *ngx_http_types_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
--- a/src/http/ngx_http_copy_filter_module.c
+++ b/src/http/ngx_http_copy_filter_module.c
@@ -117,10 +117,6 @@ ngx_http_copy_filter(ngx_http_request_t 
             r->buffered |= NGX_HTTP_COPY_BUFFERED;
         }
 
-        if (r != r->main) {
-            r->out = ctx->in;
-        }
-
         ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                        "copy filter: %i \"%V?%V\"", rc, &r->uri, &r->args);
     }
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -39,6 +39,8 @@ static char *ngx_http_core_server(ngx_co
     void *dummy);
 static char *ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd,
     void *dummy);
+static ngx_int_t ngx_http_core_regex_location(ngx_conf_t *cf,
+    ngx_http_core_loc_conf_t *clcf, ngx_str_t *regex, ngx_uint_t caseless);
 
 static char *ngx_http_core_types(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
@@ -56,6 +58,8 @@ static char *ngx_http_core_directio(ngx_
     void *conf);
 static char *ngx_http_core_error_page(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
+static char *ngx_http_core_try_files(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
 static char *ngx_http_core_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
 static char *ngx_http_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd,
@@ -108,6 +112,13 @@ static ngx_conf_enum_t  ngx_http_core_sa
 };
 
 
+static ngx_conf_enum_t  ngx_http_core_if_modified_since[] = {
+    { ngx_string("exact"), 0 },
+    { ngx_string("before"), 1 },
+    { ngx_null_string, 0 }
+};
+
+
 #if (NGX_HTTP_GZIP)
 
 static ngx_conf_enum_t  ngx_http_gzip_http_version[] = {
@@ -515,6 +526,13 @@ static ngx_command_t  ngx_http_core_comm
       offsetof(ngx_http_core_loc_conf_t, server_tokens),
       NULL },
 
+    { ngx_string("if_modified_since"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_enum_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_core_loc_conf_t, if_modified_since),
+      &ngx_http_core_if_modified_since },
+
     { ngx_string("error_page"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
                         |NGX_CONF_2MORE,
@@ -523,6 +541,13 @@ static ngx_command_t  ngx_http_core_comm
       0,
       NULL },
 
+    { ngx_string("try_files"),
+      NGX_HTTP_LOC_CONF|NGX_CONF_2MORE,
+      ngx_http_core_try_files,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
     { ngx_string("post_action"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
                         |NGX_CONF_TAKE1,
@@ -837,7 +862,7 @@ ngx_http_core_find_config_phase(ngx_http
         return NGX_OK;
     }
 
-    if (r->headers_in.expect) {
+    if (r->headers_in.expect && r->http_version > NGX_HTTP_VERSION_10) {
         expect = ngx_http_core_send_continue(r);
 
         if (expect != NGX_OK) {
@@ -1016,6 +1041,177 @@ ngx_http_core_post_access_phase(ngx_http
 
 
 ngx_int_t
+ngx_http_core_try_files_phase(ngx_http_request_t *r,
+    ngx_http_phase_handler_t *ph)
+{
+    size_t                        len, root, alias;
+    ssize_t                       reserve, allocated;
+    u_char                       *p, *name;
+    ngx_str_t                     path;
+    ngx_http_try_file_t          *tf;
+    ngx_open_file_info_t          of;
+    ngx_http_script_code_pt       code;
+    ngx_http_script_engine_t      e;
+    ngx_http_core_loc_conf_t     *clcf;
+    ngx_http_script_len_code_pt   lcode;
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "try files phase: %ui", r->phase_handler);
+
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+    if (clcf->try_files == NULL) {
+        r->phase_handler++;
+        return NGX_AGAIN;
+    }
+
+    allocated = 0;
+    root = 0;
+    name = NULL;
+    path.len = 0;
+    path.data = NULL;
+
+    tf = clcf->try_files;
+
+    alias = clcf->alias ? clcf->name.len : 0;
+
+    for ( ;; ) {
+
+        if (tf->lengths) {
+            ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
+
+            e.ip = tf->lengths->elts;
+            e.request = r;
+
+            /* 1 is for terminating '\0' as in static names */
+            len = 1;
+
+            while (*(uintptr_t *) e.ip) {
+                lcode = *(ngx_http_script_len_code_pt *) e.ip;
+                len += lcode(&e);
+            }
+
+        } else {
+            len = tf->name.len;
+        }
+
+        reserve = len - r->uri.len;
+
+        /* 16 bytes are preallocation */
+        reserve = reserve < 16 ? 16 : reserve + 16;
+
+        reserve += alias;
+
+        if (reserve > allocated) {
+
+            /* we just need to allocate path and to copy a root */
+
+            if (ngx_http_map_uri_to_path(r, &path, &root, reserve) == NULL) {
+                ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+                return NGX_OK;
+            }
+
+            name = path.data + root;
+            allocated = path.len - root - (r->uri.len - alias);
+         }
+
+        if (tf->values == NULL) {
+
+            /* tf->name.len includes the terminating '\0' */
+
+            ngx_memcpy(name, tf->name.data, tf->name.len);
+
+            path.len = (name + tf->name.len - 1) - path.data;
+
+        } else {
+            e.ip = tf->values->elts;
+            e.pos = name;
+            e.flushed = 1;
+
+            while (*(uintptr_t *) e.ip) {
+                code = *(ngx_http_script_code_pt *) e.ip;
+                code((ngx_http_script_engine_t *) &e);
+            }
+
+            path.len = e.pos - path.data;
+
+            *e.pos++ = '\0';
+
+            if (alias && ngx_strncmp(name, clcf->name.data, alias) == 0) {
+                ngx_memcpy(name, name + alias, len - alias);
+                path.len -= alias;
+            }
+        }
+
+        tf++;
+
+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "try to use file: \"%s\"", name);
+
+        if (tf->lengths == NULL && tf->name.len == 0) {
+
+            path.len -= root;
+            path.data += root;
+
+            if (path.data[0] == '@') {
+                (void) ngx_http_named_location(r, &path);
+
+            } else {
+                (void) ngx_http_internal_redirect(r, &path, NULL);
+            }
+
+            return NGX_OK;
+        }
+
+        ngx_memzero(&of, sizeof(ngx_open_file_info_t));
+
+        of.directio = clcf->directio;
+        of.valid = clcf->open_file_cache_valid;
+        of.min_uses = clcf->open_file_cache_min_uses;
+        of.errors = clcf->open_file_cache_errors;
+        of.events = clcf->open_file_cache_events;
+
+        if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
+            != NGX_OK)
+        {
+            if (of.err != NGX_ENOENT && of.err != NGX_ENOTDIR) {
+                ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
+                              ngx_open_file_n " \"%s\" failed", path.data);
+            }
+
+            continue;
+        }
+
+        path.len -= root;
+        path.data += root;
+
+        if (!alias) {
+            r->uri = path;
+
+        } else {
+            r->uri.len = alias + path.len;
+            r->uri.data = ngx_pnalloc(r->pool, r->uri.len);
+            if (r->uri.data == NULL) {
+                ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+                return NGX_OK;
+            }
+
+            p = ngx_copy(r->uri.data, clcf->name.data, alias);
+            ngx_memcpy(p, name, path.len);
+        }
+
+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "try file uri: \"%V\"", &r->uri);
+
+        r->phase_handler++;
+        return NGX_AGAIN;
+    }
+
+    /* not reached */
+}
+
+
+ngx_int_t
 ngx_http_core_content_phase(ngx_http_request_t *r,
     ngx_http_phase_handler_t *ph)
 {
@@ -1631,6 +1827,10 @@ ngx_http_server_addr(ngx_http_request_t 
         }
 
         r->in_addr = sin.sin_addr.s_addr;
+
+    } else {
+        sin.sin_family = c->sockaddr->sa_family;
+        sin.sin_addr.s_addr = r->in_addr;
     }
 
     if (s == NULL) {
@@ -1813,7 +2013,6 @@ ngx_http_subrequest(ngx_http_request_t *
 {
     ngx_connection_t              *c;
     ngx_http_request_t            *sr;
-    ngx_http_log_ctx_t            *ctx;
     ngx_http_core_srv_conf_t      *cscf;
     ngx_http_postponed_request_t  *pr, *p;
 
@@ -1878,6 +2077,7 @@ ngx_http_subrequest(ngx_http_request_t *
 
     sr->zero_in_uri = (flags & NGX_HTTP_ZERO_IN_URI) != 0;
     sr->subrequest_in_memory = (flags & NGX_HTTP_SUBREQUEST_IN_MEMORY) != 0;
+    sr->waited = (flags & NGX_HTTP_SUBREQUEST_WAITED) != 0;
 
     sr->unparsed_uri = r->unparsed_uri;
     sr->method_name = ngx_http_core_get_method;
@@ -1891,9 +2091,9 @@ ngx_http_subrequest(ngx_http_request_t *
     sr->parent = r;
     sr->post_subrequest = ps;
     sr->read_event_handler = ngx_http_request_empty_handler;
-    sr->write_event_handler = ngx_http_request_empty_handler;
-
-    if (c->data == r) {
+    sr->write_event_handler = ngx_http_handler;
+
+    if (c->data == r && r->postponed == NULL) {
         c->data = sr;
     }
 
@@ -1922,39 +2122,18 @@ ngx_http_subrequest(ngx_http_request_t *
         r->postponed = pr;
     }
 
-    ctx = c->log->data;
-    ctx->current_request = sr;
-
     sr->internal = 1;
-    sr->fast_subrequest = 1;
 
     sr->discard_body = r->discard_body;
     sr->main_filter_need_in_memory = r->main_filter_need_in_memory;
 
     sr->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1;
 
-    ngx_http_handler(sr);
-
-    if (!c->destroyed) {
-        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http subrequest done \"%V?%V\"", uri, &sr->args);
-
-        r->main->subrequests++;
-
-        *psr = sr;
-
-        if (sr->fast_subrequest) {
-            sr->fast_subrequest = 0;
-
-            if (sr->done) {
-                return NGX_OK;
-            }
-        }
-
-        return NGX_AGAIN;
-    }
-
-    return NGX_DONE;
+    r->main->subrequests++;
+
+    *psr = sr;
+
+    return ngx_http_post_request(sr);
 }
 
 
@@ -2018,33 +2197,37 @@ ngx_http_named_location(ngx_http_request
 
     cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
 
-    for (clcfp = cscf->named_locations; *clcfp; clcfp++) {
-
-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                       "test location: \"%V\"", &(*clcfp)->name);
-
-        if (name->len != (*clcfp)->name.len
-            || ngx_strncmp(name->data, (*clcfp)->name.data, name->len) != 0)
-        {
-            continue;
+    if (cscf->named_locations) {
+
+        for (clcfp = cscf->named_locations; *clcfp; clcfp++) {
+
+            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "test location: \"%V\"", &(*clcfp)->name);
+
+            if (name->len != (*clcfp)->name.len
+                || ngx_strncmp(name->data, (*clcfp)->name.data, name->len) != 0)
+            {
+                continue;
+            }
+
+            ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "using location: %V \"%V?%V\"",
+                           name, &r->uri, &r->args);
+
+            r->internal = 1;
+            r->content_handler = NULL;
+            r->loc_conf = (*clcfp)->loc_conf;
+
+            ngx_http_update_location_config(r);
+
+            cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+
+            r->phase_handler = cmcf->phase_engine.location_rewrite_index;
+
+            ngx_http_core_run_phases(r);
+
+            return NGX_DONE;
         }
-
-        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                       "using location: %V \"%V?%V\"", name, &r->uri, &r->args);
-
-        r->internal = 1;
-        r->content_handler = NULL;
-        r->loc_conf = (*clcfp)->loc_conf;
-
-        ngx_http_update_location_config(r);
-
-        cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
-
-        r->phase_handler = cmcf->phase_engine.location_rewrite_index;
-
-        ngx_http_core_run_phases(r);
-
-        return NGX_DONE;
     }
 
     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
@@ -2185,8 +2368,10 @@ static char *
 ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
 {
     char                      *rv;
+    u_char                    *mod;
+    size_t                     len;
+    ngx_str_t                 *value, *name;
     ngx_uint_t                 i;
-    ngx_str_t                 *value;
     ngx_conf_t                 save;
     ngx_http_module_t         *module;
     ngx_http_conf_ctx_t       *ctx, *pctx;
@@ -2228,45 +2413,32 @@ ngx_http_core_location(ngx_conf_t *cf, n
     value = cf->args->elts;
 
     if (cf->args->nelts == 3) {
-        if (value[1].len == 1 && value[1].data[0] == '=') {
-            clcf->name = value[2];
+
+        len = value[1].len;
+        mod = value[1].data;
+        name = &value[2];
+
+        if (len == 1 && mod[0] == '=') {
+
+            clcf->name = *name;
             clcf->exact_match = 1;
 
-        } else if (value[1].len == 2
-                   && value[1].data[0] == '^'
-                   && value[1].data[1] == '~')
-        {
-            clcf->name = value[2];
+        } else if (len == 2 && mod[0] == '^' && mod[1] == '~') {
+
+            clcf->name = *name;
             clcf->noregex = 1;
 
-        } else if ((value[1].len == 1 && value[1].data[0] == '~')
-                   || (value[1].len == 2
-                       && value[1].data[0] == '~'
-                       && value[1].data[1] == '*'))
-        {
-#if (NGX_PCRE)
-            ngx_str_t  err;
-            u_char     errstr[NGX_MAX_CONF_ERRSTR];
-
-            err.len = NGX_MAX_CONF_ERRSTR;
-            err.data = errstr;
-
-            clcf->regex = ngx_regex_compile(&value[2],
-                                     value[1].len == 2 ? NGX_REGEX_CASELESS: 0,
-                                     cf->pool, &err);
-
-            if (clcf->regex == NULL) {
-                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data);
+        } else if (len == 1 && mod[0] == '~') {
+
+            if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) {
                 return NGX_CONF_ERROR;
             }
 
-            clcf->name = value[2];
-#else
-            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                               "the using of the regex \"%V\" "
-                               "requires PCRE library", &value[2]);
-            return NGX_CONF_ERROR;
-#endif
+        } else if (len == 2 && mod[0] == '~' && mod[1] == '*') {
+
+            if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) {
+                return NGX_CONF_ERROR;
+            }
 
         } else {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
@@ -2276,10 +2448,47 @@ ngx_http_core_location(ngx_conf_t *cf, n
 
     } else {
 
-        clcf->name = value[1];
-
-        if (value[1].data[0] == '@') {
-            clcf->named = 1;
+        name = &value[1];
+
+        if (name->data[0] == '=') {
+
+            clcf->name.len = name->len - 1;
+            clcf->name.data = name->data + 1;
+            clcf->exact_match = 1;
+
+        } else if (name->data[0] == '^' && name->data[1] == '~') {
+
+            clcf->name.len = name->len - 2;
+            clcf->name.data = name->data + 2;
+            clcf->noregex = 1;
+
+        } else if (name->data[0] == '~') {
+
+            name->len--;
+            name->data++;
+
+            if (name->data[0] == '*') {
+
+                name->len--;
+                name->data++;
+
+                if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) {
+                    return NGX_CONF_ERROR;
+                }
+
+            } else {
+                if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) {
+                    return NGX_CONF_ERROR;
+                }
+            }
+
+        } else {
+
+            clcf->name = *name;
+
+            if (name->data[0] == '@') {
+                clcf->named = 1;
+            }
         }
     }
 
@@ -2317,13 +2526,13 @@ ngx_http_core_location(ngx_conf_t *cf, n
             return NGX_CONF_ERROR;
         }
 
+        len = pclcf->name.len;
+
 #if (NGX_PCRE)
         if (clcf->regex == NULL
-            && ngx_strncmp(clcf->name.data, pclcf->name.data, pclcf->name.len)
-               != 0)
+            && ngx_strncmp(clcf->name.data, pclcf->name.data, len) != 0)
 #else
-        if (ngx_strncmp(clcf->name.data, pclcf->name.data, pclcf->name.len)
-            != 0)
+        if (ngx_strncmp(clcf->name.data, pclcf->name.data, len) != 0)
 #endif
         {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
@@ -2349,6 +2558,40 @@ ngx_http_core_location(ngx_conf_t *cf, n
 }
 
 
+static ngx_int_t
+ngx_http_core_regex_location(ngx_conf_t *cf, ngx_http_core_loc_conf_t *clcf,
+    ngx_str_t *regex, ngx_uint_t caseless)
+{
+#if (NGX_PCRE)
+    ngx_str_t  err;
+    u_char     errstr[NGX_MAX_CONF_ERRSTR];
+
+    err.len = NGX_MAX_CONF_ERRSTR;
+    err.data = errstr;
+
+    clcf->regex = ngx_regex_compile(regex, caseless ? NGX_REGEX_CASELESS: 0,
+                                    cf->pool, &err);
+
+    if (clcf->regex == NULL) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data);
+        return NGX_ERROR;
+    }
+
+    clcf->name = *regex;
+
+    return NGX_OK;
+
+#else
+
+    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                       "the using of the regex \"%V\" requires PCRE library",
+                       regex);
+    return NGX_ERROR;
+
+#endif
+}
+
+
 static char *
 ngx_http_core_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
@@ -2647,6 +2890,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t
      *     lcf->default_type = { 0, NULL };
      *     lcf->err_log = NULL;
      *     lcf->error_pages = NULL;
+     *     lcf->try_files = NULL;
      *     lcf->client_body_path = NULL;
      *     lcf->regex = NULL;
      *     lcf->exact_match = 0;
@@ -2659,6 +2903,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t
     lcf->client_body_buffer_size = NGX_CONF_UNSET_SIZE;
     lcf->client_body_timeout = NGX_CONF_UNSET_MSEC;
     lcf->satisfy = NGX_CONF_UNSET_UINT;
+    lcf->if_modified_since = NGX_CONF_UNSET_UINT;
     lcf->internal = NGX_CONF_UNSET;
     lcf->client_body_in_file_only = NGX_CONF_UNSET;
     lcf->sendfile = NGX_CONF_UNSET;
@@ -2848,6 +3093,8 @@ ngx_http_core_merge_loc_conf(ngx_conf_t 
 
     ngx_conf_merge_uint_value(conf->satisfy, prev->satisfy,
                               NGX_HTTP_SATISFY_ALL);
+    ngx_conf_merge_uint_value(conf->if_modified_since, prev->if_modified_since,
+                              0);
     ngx_conf_merge_value(conf->internal, prev->internal, 0);
     ngx_conf_merge_value(conf->client_body_in_file_only,
                               prev->client_body_in_file_only, 0);
@@ -3470,7 +3717,7 @@ ngx_http_core_directio(ngx_conf_t *cf, n
     value = cf->args->elts;
 
     if (ngx_strcmp(value[1].data, "off") == 0) {
-        clcf->directio = NGX_MAX_OFF_T_VALUE;
+        clcf->directio = NGX_OPEN_FILE_DIRECTIO_OFF;
         return NGX_CONF_OK;
     }
 
@@ -3619,6 +3866,65 @@ ngx_http_core_error_page(ngx_conf_t *cf,
 
 
 static char *
+ngx_http_core_try_files(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_core_loc_conf_t *clcf = conf;
+
+    ngx_str_t                  *value;
+    ngx_uint_t                  i, n;
+    ngx_http_try_file_t        *tf;
+    ngx_http_script_compile_t   sc;
+    ngx_http_core_main_conf_t  *cmcf;
+
+    if (clcf->try_files) {
+        return "is duplicate";
+    }
+
+    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+    cmcf->try_files = 1;
+
+    tf = ngx_pcalloc(cf->pool, cf->args->nelts * sizeof(ngx_http_try_file_t));
+    if (tf == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    clcf->try_files = tf;
+
+    value = cf->args->elts;
+
+    for (i = 0; i < cf->args->nelts - 1; i++) {
+
+        tf[i].name = value[i + 1];
+
+        n = ngx_http_script_variables_count(&tf[i].name);
+
+        if (n) {
+            ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+            sc.cf = cf;
+            sc.source = &tf[i].name;
+            sc.lengths = &tf[i].lengths;
+            sc.values = &tf[i].values;
+            sc.variables = n;
+            sc.complete_lengths = 1;
+            sc.complete_values = 1;
+
+            if (ngx_http_script_compile(&sc) != NGX_OK) {
+                return NGX_CONF_ERROR;
+            }
+
+        } else {
+            /* add trailing '\0' to length */
+            tf[i].name.len++;
+        }
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+static char *
 ngx_http_core_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
     ngx_http_core_loc_conf_t *lcf = conf;
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -81,6 +81,7 @@ typedef enum {
     NGX_HTTP_ACCESS_PHASE,
     NGX_HTTP_POST_ACCESS_PHASE,
 
+    NGX_HTTP_TRY_FILES_PHASE,
     NGX_HTTP_CONTENT_PHASE,
 
     NGX_HTTP_LOG_PHASE
@@ -129,6 +130,8 @@ typedef struct {
 
     ngx_hash_keys_arrays_t    *variables_keys;
 
+    ngx_uint_t                 try_files;       /* unsigned  try_files:1 */
+
     ngx_http_phase_t           phases[NGX_HTTP_LOG_PHASE + 1];
 } ngx_http_core_main_conf_t;
 
@@ -238,6 +241,13 @@ typedef struct {
 } ngx_http_err_page_t;
 
 
+typedef struct {
+    ngx_array_t               *lengths;
+    ngx_array_t               *values;
+    ngx_str_t                  name;
+} ngx_http_try_file_t;
+
+
 struct ngx_http_core_loc_conf_s {
     ngx_str_t     name;          /* location name */
 
@@ -299,6 +309,7 @@ struct ngx_http_core_loc_conf_s {
     time_t        keepalive_header;        /* keepalive_timeout */
 
     ngx_uint_t    satisfy;                 /* satisfy */
+    ngx_uint_t    if_modified_since;       /* if_modified_since */
 
     ngx_flag_t    internal;                /* internal */
     ngx_flag_t    client_body_in_file_only; /* client_body_in_file_only */
@@ -327,6 +338,7 @@ struct ngx_http_core_loc_conf_s {
 #endif
 
     ngx_array_t  *error_pages;             /* error_page */
+    ngx_http_try_file_t    *try_files;     /* try_files */
 
     ngx_path_t   *client_body_temp_path;   /* client_body_temp_path */
 
@@ -385,6 +397,8 @@ ngx_int_t ngx_http_core_access_phase(ngx
     ngx_http_phase_handler_t *ph);
 ngx_int_t ngx_http_core_post_access_phase(ngx_http_request_t *r,
     ngx_http_phase_handler_t *ph);
+ngx_int_t ngx_http_core_try_files_phase(ngx_http_request_t *r,
+    ngx_http_phase_handler_t *ph);
 ngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r,
     ngx_http_phase_handler_t *ph);
 
--- a/src/http/ngx_http_parse.c
+++ b/src/http/ngx_http_parse.c
@@ -1481,3 +1481,45 @@ ngx_http_parse_multi_header_lines(ngx_ar
 
     return NGX_DECLINED;
 }
+
+
+ngx_int_t
+ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len, ngx_str_t *value)
+{
+    u_char  *p;
+
+    if (r->args.len == 0) {
+        return NGX_DECLINED;
+    }
+
+    for (p = r->args.data; *p && *p != ' '; p++) {
+
+        /*
+         * although r->args.data is not null-terminated by itself,
+         * however, there is null in the end of request line
+         */
+
+        p = ngx_strcasestrn(p, (char *) name, len - 1);
+
+        if (p == NULL) {
+            return NGX_DECLINED;
+        }
+
+        if ((p == r->args.data || *(p - 1) == '&') && *(p + len) == '=') {
+
+            value->data = p + len + 1;
+
+            p = (u_char *) ngx_strchr(p, '&');
+
+            if (p == NULL) {
+                p = r->args.data + r->args.len;
+            }
+
+            value->len = p - value->data;
+
+            return NGX_OK;
+        }
+    }
+
+    return NGX_DECLINED;
+}
--- a/src/http/ngx_http_postpone_filter_module.c
+++ b/src/http/ngx_http_postpone_filter_module.c
@@ -9,8 +9,8 @@
 #include <ngx_http.h>
 
 
-static ngx_int_t
-    ngx_http_postpone_filter_output_postponed_request(ngx_http_request_t *r);
+static ngx_int_t ngx_http_postpone_filter_add(ngx_http_request_t *r,
+    ngx_chain_t *in);
 static ngx_int_t ngx_http_postpone_filter_init(ngx_conf_t *cf);
 
 
@@ -51,181 +51,119 @@ static ngx_http_output_body_filter_pt   
 static ngx_int_t
 ngx_http_postpone_filter(ngx_http_request_t *r, ngx_chain_t *in)
 {
-    ngx_int_t                      rc;
-    ngx_chain_t                   *out;
-    ngx_http_postponed_request_t  *pr, **ppr;
+    ngx_connection_t              *c;
+    ngx_http_postponed_request_t  *pr;
 
-    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+    c = r->connection;
+
+    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
                    "http postpone filter \"%V?%V\" %p", &r->uri, &r->args, in);
 
-    if (r != r->connection->data || (r->postponed && in)) {
-
-        if (r->postponed) {
-            for (pr = r->postponed; pr->next; pr = pr->next) { /* void */ }
-
-            ppr = pr->request ? &pr->next : NULL;
-
-        } else {
-            ppr = &r->postponed;
-#if (NGX_SUPPRESS_WARN)
-            pr = NULL;
-#endif
-        }
+    if (r != c->data) {
 
-        if (ppr) {
-            pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t));
-            if (pr == NULL) {
-                return NGX_ERROR;
-            }
-
-            *ppr = pr;
-
-            pr->request = NULL;
-            pr->out = NULL;
-            pr->next = NULL;
-        }
-
-        if (ngx_chain_add_copy(r->pool, &pr->out, in) == NGX_ERROR) {
-            return NGX_ERROR;
+        if (in) {
+            ngx_http_postpone_filter_add(r, in);
+            return NGX_OK;
         }
 
-#if 1
-        {
-        ngx_chain_t  *cl;
-        ngx_buf_t    *b = NULL;
-        for (cl = pr->out; cl; cl = cl->next) {
-            if (cl->buf == b) {
-                ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
-                              "the same buf was used in postponed %p %p",
-                               b, b->pos);
-                ngx_debug_point();
-                return NGX_ERROR;
-            }
-            b = cl->buf;
-        }
-        }
+#if 0
+        /* TODO: SSI may pass NULL */
+        ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+                      "http postpone filter NULL inactive request",
+                      &r->uri, &r->args);
 #endif
 
-        if (r != r->connection->data || r->postponed->request) {
-            return NGX_AGAIN;
-        }
-    }
-
-    if (r->postponed) {
-        out = r->postponed->out;
-        if (out) {
-            r->postponed = r->postponed->next;
-        }
-
-    } else {
-        out = in;
-    }
-
-    rc = NGX_OK;
-
-    if (out
-        || (r->connection->buffered
-            & (NGX_HTTP_LOWLEVEL_BUFFERED|NGX_LOWLEVEL_BUFFERED)))
-    {
-        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                       "http postpone filter out \"%V?%V\"", &r->uri, &r->args);
-
-        if (!(out && out->next == NULL && ngx_buf_sync_only(out->buf))) {
-
-            rc = ngx_http_next_filter(r->main, out);
-
-            if (rc == NGX_ERROR) {
-                /* NGX_ERROR may be returned by any filter */
-                r->connection->error = 1;
-            }
-        }
+        return NGX_OK;
     }
 
     if (r->postponed == NULL) {
-        return rc;
+
+        if (in || c->buffered) {
+            return ngx_http_next_filter(r->main, in);
+        }
+
+        return NGX_OK;
+    }
+
+    if (in) {
+        ngx_http_postpone_filter_add(r, in);
     }
 
-    rc = ngx_http_postpone_filter_output_postponed_request(r);
+    do {
+        pr = r->postponed;
+
+        if (pr->request) {
+
+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                           "http postpone filter wake \"%V?%V\"",
+                           &pr->request->uri, &pr->request->args);
+
+            r->postponed = pr->next;
+
+            c->data = pr->request;
+
+            return ngx_http_post_request(pr->request);
+        }
 
-    if (rc == NGX_ERROR) {
-        /* NGX_ERROR may be returned by any filter */
-        r->connection->error = 1;
-    }
+        if (pr->out == NULL) {
+            ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+                          "http postpone filter NULL output",
+                          &r->uri, &r->args);
+
+        } else {
+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                           "http postpone filter output \"%V?%V\"",
+                           &r->uri, &r->args);
 
-    return rc;
+            if (ngx_http_next_filter(r->main, pr->out) == NGX_ERROR) {
+                return NGX_ERROR;
+            }
+        }
+
+        r->postponed = pr->next;
+
+    } while (r->postponed);
+
+    return NGX_OK;
 }
 
 
 static ngx_int_t
-ngx_http_postpone_filter_output_postponed_request(ngx_http_request_t *r)
+ngx_http_postpone_filter_add(ngx_http_request_t *r, ngx_chain_t *in)
 {
-    ngx_int_t                      rc;
-    ngx_chain_t                   *out;
-    ngx_http_log_ctx_t            *ctx;
-    ngx_http_postponed_request_t  *pr;
+    ngx_http_postponed_request_t  *pr, **ppr;
 
-    for ( ;; ) {
-        pr = r->postponed;
+    if (r->postponed) {
+        for (pr = r->postponed; pr->next; pr = pr->next) { /* void */ }
 
-        if (pr == NULL) {
-            break;
+        if (pr->request == NULL) {
+            goto found;
         }
 
-        if (pr->request) {
-
-            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                           "http postpone filter handle \"%V?%V\"",
-                           &pr->request->uri, &pr->request->args);
-
-            ctx = r->connection->log->data;
-            ctx->current_request = pr->request;
-
-            if (!pr->request->done) {
-                r->connection->data = pr->request;
-                return NGX_AGAIN;
-            }
-
-            rc = ngx_http_postpone_filter_output_postponed_request(pr->request);
-
-            if (rc == NGX_AGAIN || rc == NGX_ERROR) {
-                return rc;
-            }
+        ppr = &pr->next;
 
-            r->postponed = r->postponed->next;
-            pr = r->postponed;
-        }
-
-        if (pr == NULL) {
-            break;
-        }
-
-        out = pr->out;
+    } else {
+        ppr = &r->postponed;
+    }
 
-        if (out) {
-            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                           "http postpone filter out postponed \"%V?%V\"",
-                           &r->uri, &r->args);
-
-            if (!(out && out->next == NULL && ngx_buf_sync_only(out->buf))) {
-                if (ngx_http_next_filter(r->main, out) == NGX_ERROR) {
-                    return NGX_ERROR;
-                }
-            }
-        }
-
-        r->postponed = r->postponed->next;
+    pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t));
+    if (pr == NULL) {
+        return NGX_ERROR;
     }
 
-    if (r->out) {
-        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                       "http postpone filter out again \"%V?%V\"",
-                       &r->uri, &r->args);
+    *ppr = pr;
 
-        r->connection->data = r;
-        return NGX_AGAIN;
+    pr->request = NULL;
+    pr->out = NULL;
+    pr->next = NULL;
+
+found:
+
+    if (ngx_chain_add_copy(r->pool, &pr->out, in) == NGX_OK) {
+        return NGX_OK;
     }
 
-    return NGX_OK;
+    return NGX_ERROR;
 }
 
 
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -38,8 +38,8 @@ static ngx_int_t ngx_http_find_virtual_s
 static void ngx_http_request_handler(ngx_event_t *ev);
 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_test_reading(ngx_http_request_t *r);
+static void ngx_http_request_finalizer(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);
@@ -215,7 +215,7 @@ ngx_http_init_connection(ngx_connection_
 
     ngx_add_timer(rev, c->listening->post_accept_timeout);
 
-    if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(rev, 0) != NGX_OK) {
 #if (NGX_STAT_STUB)
         ngx_atomic_fetch_add(ngx_stat_reading, -1);
 #endif
@@ -504,7 +504,7 @@ ngx_http_ssl_handshake(ngx_event_t *rev)
             ngx_add_timer(rev, c->listening->post_accept_timeout);
         }
 
-        if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+        if (ngx_handle_read_event(rev, 0) != NGX_OK) {
             ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
         }
 
@@ -1038,7 +1038,7 @@ ngx_http_read_request_header(ngx_http_re
             ngx_add_timer(rev, cscf->client_header_timeout);
         }
 
-        if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+        if (ngx_handle_read_event(rev, 0) != NGX_OK) {
             ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
             return NGX_ERROR;
         }
@@ -1412,9 +1412,7 @@ ngx_http_process_request_header(ngx_http
         }
     }
 
-    if (r->method & (NGX_HTTP_POST|NGX_HTTP_PUT)
-        && r->headers_in.content_length_n == -1)
-    {
+    if (r->method & NGX_HTTP_PUT && r->headers_in.content_length_n == -1) {
         ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
                   "client sent %V method without \"Content-Length\" header",
                   &r->method_name);
@@ -1525,7 +1523,7 @@ ngx_http_process_request(ngx_http_reques
 
     ngx_http_handler(r);
 
-    return;
+    ngx_http_run_posted_requests(c);
 }
 
 
@@ -1678,12 +1676,73 @@ ngx_http_request_handler(ngx_event_t *ev
     ctx = c->log->data;
     ctx->current_request = r;
 
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                   "http run request: \"%V?%V\"", &r->uri, &r->args);
+
     if (ev->write) {
         r->write_event_handler(r);
 
     } else {
         r->read_event_handler(r);
     }
+
+    ngx_http_run_posted_requests(c);
+}
+
+
+void
+ngx_http_run_posted_requests(ngx_connection_t *c)
+{
+    ngx_http_request_t         *r;
+    ngx_http_log_ctx_t         *ctx;
+    ngx_http_posted_request_t  *pr;
+
+    for ( ;; ) {
+
+        if (c->destroyed) {
+            return;
+        }
+
+        r = c->data;
+        pr = r->main->posted_requests;
+
+        if (pr == NULL) {
+            return;
+        }
+
+        r->main->posted_requests = pr->next;
+
+        r = pr->request;
+
+        ctx = c->log->data;
+        ctx->current_request = r;
+
+        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                       "http posted request: \"%V?%V\"", &r->uri, &r->args);
+
+        r->write_event_handler(r);
+    }
+}
+
+
+ngx_int_t
+ngx_http_post_request(ngx_http_request_t *r)
+{
+    ngx_http_posted_request_t  *pr, **p;
+
+    pr = ngx_palloc(r->pool, sizeof(ngx_http_posted_request_t));
+    if (pr == NULL) {
+        return NGX_ERROR;
+    }
+
+    pr->request = r;
+    pr->next = NULL;
+
+    for (p = &r->main->posted_requests; *p; p = &(*p)->next) { /* void */ }
+
+    *p = pr;
+
+    return NGX_OK;
 }
 
 
@@ -1692,7 +1751,6 @@ ngx_http_finalize_request(ngx_http_reque
 {
     ngx_connection_t          *c;
     ngx_http_request_t        *pr;
-    ngx_http_log_ctx_t        *ctx;
     ngx_http_core_loc_conf_t  *clcf;
 
     if (rc == NGX_DONE) {
@@ -1702,9 +1760,9 @@ ngx_http_finalize_request(ngx_http_reque
 
     c = r->connection;
 
-    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                   "http finalize request: %d, \"%V?%V\"",
-                   rc, &r->uri, &r->args);
+    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                   "http finalize request: %d, \"%V?%V\" %d",
+                   rc, &r->uri, &r->args, r == c->data);
 
     if (rc == NGX_DECLINED) {
         r->content_handler = NULL;
@@ -1760,88 +1818,91 @@ ngx_http_finalize_request(ngx_http_reque
         return;
     }
 
-    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
-
-    if (r != r->main && !r->logged) {
-
-        if (clcf->log_subrequest) {
-            ngx_http_log_request(r);
-        }
-
-        r->logged = 1;
-    }
-
-    if (r != r->main || rc == NGX_AGAIN) {
-        if (ngx_http_set_write_handler(r) != NGX_OK) {
+    if (r != r->main) {
+
+        if (r->buffered || r->postponed) {
+
+            if (ngx_http_set_write_handler(r) != NGX_OK) {
+                ngx_http_close_request(r->main, 0);
+            }
+
             return;
         }
-    }
-
-    r->done = 1;
-
-    if (r != c->data) {
-        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http finalize non-active request: \"%V?%V\"",
-                       &r->uri, &r->args);
-        return;
-    }
-
-    if (r != r->main) {
+
+#if (NGX_DEBUG)
+        if (r != c->data) {
+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                           "http finalize non-active request: \"%V?%V\"",
+                           &r->uri, &r->args);
+        }
+#endif
 
         pr = r->parent;
 
+        if (r == c->data) {
+
+            if (!r->logged) {
+
+                clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+                if (clcf->log_subrequest) {
+                    ngx_http_log_request(r);
+                }
+
+                r->logged = 1;
+
+            } else {
+                ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+                              "subrequest: \"%V?%V\" logged again",
+                              &r->uri, &r->args);
+            }
+
+            r->done = 1;
+
+            if (pr->postponed && pr->postponed->request == r) {
+                pr->postponed = pr->postponed->next;
+            }
+
+            c->data = pr;
+
+        } else {
+
+            r->write_event_handler = ngx_http_request_finalizer;
+
+            if (r->waited) {
+                r->done = 1;
+            }
+        }
+
+        if (ngx_http_post_request(pr) != NGX_OK) {
+            ngx_http_close_request(r->main, 0);
+            return;
+        }
+
         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http parent request: \"%V?%V\"", &pr->uri, &pr->args);
-
-        if (rc != NGX_AGAIN) {
-            c->data = pr;
-        }
-
-        ctx = c->log->data;
-        ctx->current_request = pr;
-
-        if (pr->postponed) {
-
-            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                           "http request: \"%V?%V\" has postponed",
-                           &pr->uri, &pr->args);
-
-            if (rc != NGX_AGAIN && pr->postponed->request == r) {
-                pr->postponed = pr->postponed->next;
-            }
-
-            if (r->fast_subrequest) {
-
-                if (rc == NGX_AGAIN) {
-                    r->fast_subrequest = 0;
-                }
-
-                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                               "http fast subrequest: \"%V?%V\" done",
-                               &r->uri, &r->args);
-                return;
-            }
-
-            if (rc != NGX_AGAIN) {
-                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                               "http wake parent request: \"%V?%V\"",
-                               &pr->uri, &pr->args);
-
-                pr->write_event_handler(pr);
-            }
+                       "http wake parent request: \"%V?%V\"",
+                       &pr->uri, &pr->args);
+
+        return;
+    }
+
+    if (r->buffered || c->buffered || r->postponed) {
+
+        if (ngx_http_set_write_handler(r) != NGX_OK) {
+            ngx_http_close_request(r, 0);
         }
 
         return;
     }
 
-    if (rc == NGX_AGAIN) {
+    if (r != c->data) {
+        ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+                      "http finalize non-active request: \"%V?%V\"",
+                      &r->uri, &r->args);
         return;
     }
 
-    if (c->buffered) {
-        (void) ngx_http_set_write_handler(r);
-        return;
-    }
+    r->done = 1;
 
     if (!r->post_action) {
         r->request_complete = 1;
@@ -1869,6 +1930,8 @@ ngx_http_finalize_request(ngx_http_reque
         return;
     }
 
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
     if (!ngx_terminate
          && !ngx_exiting
          && r->keepalive
@@ -1908,7 +1971,7 @@ ngx_http_set_write_handler(ngx_http_requ
         ngx_add_timer(wev, clcf->send_timeout);
     }
 
-    if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) {
+    if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {
         ngx_http_close_request(r, 0);
         return NGX_ERROR;
     }
@@ -1931,6 +1994,8 @@ ngx_http_writer(ngx_http_request_t *r)
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, wev->log, 0,
                    "http writer handler: \"%V?%V\"", &r->uri, &r->args);
 
+    clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module);
+
     if (wev->timedout) {
         if (!wev->delayed) {
             ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
@@ -1945,10 +2010,9 @@ ngx_http_writer(ngx_http_request_t *r)
         wev->delayed = 0;
 
         if (!wev->ready) {
-            clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module);
             ngx_add_timer(wev, clcf->send_timeout);
 
-            if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) {
+            if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {
                 ngx_http_close_request(r, 0);
             }
 
@@ -1960,9 +2024,7 @@ ngx_http_writer(ngx_http_request_t *r)
             ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0,
                            "http writer delayed");
 
-            clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module);
-
-            if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) {
+            if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {
                 ngx_http_close_request(r, 0);
             }
 
@@ -1980,21 +2042,17 @@ ngx_http_writer(ngx_http_request_t *r)
                    "http writer output filter: %d, \"%V?%V\"",
                    rc, &r->uri, &r->args);
 
-    if (rc == NGX_AGAIN) {
-        clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module);
+    if (r->buffered || r->postponed || (r == r->main && c->buffered)) {
+
         if (!wev->ready && !wev->delayed) {
             ngx_add_timer(wev, clcf->send_timeout);
         }
 
-        if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) {
+        if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {
             ngx_http_close_request(r, 0);
         }
 
-        if (r == r->main || r->buffered) {
-            return;
-        }
-
-        rc = NGX_OK;
+        return;
     }
 
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, wev->log, 0,
@@ -2004,6 +2062,16 @@ ngx_http_writer(ngx_http_request_t *r)
 }
 
 
+static void
+ngx_http_request_finalizer(ngx_http_request_t *r)
+{
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http finalizer done: \"%V?%V\"", &r->uri, &r->args);
+
+    ngx_http_finalize_request(r, 0);
+}
+
+
 void
 ngx_http_block_reading(ngx_http_request_t *r)
 {
@@ -2015,16 +2083,14 @@ ngx_http_block_reading(ngx_http_request_
     if ((ngx_event_flags & NGX_USE_LEVEL_EVENT)
         && r->connection->read->active)
     {
-        if (ngx_del_event(r->connection->read, NGX_READ_EVENT, 0)
-            == NGX_ERROR)
-        {
+        if (ngx_del_event(r->connection->read, NGX_READ_EVENT, 0) != NGX_OK) {
             ngx_http_close_request(r, 0);
         }
     }
 }
 
 
-static void
+void
 ngx_http_test_reading(ngx_http_request_t *r)
 {
     int                n;
@@ -2079,7 +2145,7 @@ ngx_http_test_reading(ngx_http_request_t
 
     if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && rev->active) {
 
-        if (ngx_del_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
+        if (ngx_del_event(rev, NGX_READ_EVENT, 0) != NGX_OK) {
             ngx_http_close_request(r, 0);
         }
     }
@@ -2173,7 +2239,7 @@ ngx_http_set_keepalive(ngx_http_request_
 
     ngx_add_timer(rev, clcf->keepalive_timeout);
 
-    if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(rev, 0) != NGX_OK) {
         ngx_http_close_connection(c);
         return;
     }
@@ -2260,7 +2326,7 @@ ngx_http_set_keepalive(ngx_http_request_
     rev->handler = ngx_http_keepalive_handler;
 
     if (wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) {
-        if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) == NGX_ERROR) {
+        if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) {
             ngx_http_close_connection(c);
             return;
         }
@@ -2385,7 +2451,7 @@ ngx_http_keepalive_handler(ngx_event_t *
     c->log_error = NGX_ERROR_INFO;
 
     if (n == NGX_AGAIN) {
-        if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+        if (ngx_handle_read_event(rev, 0) != NGX_OK) {
             ngx_http_close_connection(c);
         }
 
@@ -2438,7 +2504,7 @@ ngx_http_set_lingering_close(ngx_http_re
     r->lingering_time = ngx_time() + (time_t) (clcf->lingering_time / 1000);
     ngx_add_timer(rev, clcf->lingering_timeout);
 
-    if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(rev, 0) != NGX_OK) {
         ngx_http_close_request(r, 0);
         return;
     }
@@ -2447,7 +2513,7 @@ ngx_http_set_lingering_close(ngx_http_re
     wev->handler = ngx_http_empty_handler;
 
     if (wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) {
-        if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) == NGX_ERROR) {
+        if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) {
             ngx_http_close_request(r, 0);
             return;
         }
@@ -2506,7 +2572,7 @@ ngx_http_lingering_close_handler(ngx_eve
 
     } while (rev->ready);
 
-    if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(rev, 0) != NGX_OK) {
         ngx_http_close_request(r, 0);
         return;
     }
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -58,6 +58,7 @@
 
 #define NGX_HTTP_ZERO_IN_URI               1
 #define NGX_HTTP_SUBREQUEST_IN_MEMORY      2
+#define NGX_HTTP_SUBREQUEST_WAITED         4
 
 
 #define NGX_HTTP_OK                        200
@@ -321,6 +322,14 @@ struct ngx_http_postponed_request_s {
 };
 
 
+typedef struct ngx_http_posted_request_s  ngx_http_posted_request_t;
+
+struct ngx_http_posted_request_s {
+    ngx_http_request_t               *request;
+    ngx_http_posted_request_t        *next;
+};
+
+
 typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r);
 typedef void (*ngx_http_event_handler_pt)(ngx_http_request_t *r);
 
@@ -373,6 +382,7 @@ struct ngx_http_request_s {
     ngx_http_request_t               *parent;
     ngx_http_postponed_request_t     *postponed;
     ngx_http_post_subrequest_t       *post_subrequest;
+    ngx_http_posted_request_t        *posted_requests;
 
     uint32_t                          in_addr;
     ngx_uint_t                        port;
@@ -428,8 +438,8 @@ struct ngx_http_request_s {
     unsigned                          request_body_file_group_access:1;
     unsigned                          request_body_file_log_level:3;
 
-    unsigned                          fast_subrequest:1;
     unsigned                          subrequest_in_memory:1;
+    unsigned                          waited:1;
 
     unsigned                          gzip:2;
 
@@ -438,10 +448,12 @@ struct ngx_http_request_s {
     unsigned                          no_cache:1;
 
     /*
-     * instead of using the request context data in ngx_http_limit_zone_module
-     * we use the single bit in the request structure
+     * instead of using the request context data in
+     * ngx_http_limit_zone_module and ngx_http_limit_req_module
+     * we use the single bits in the request structure
      */
     unsigned                          limit_zone_set:1;
+    unsigned                          limit_req_set:1;
 
 #if 0
     unsigned                          cacheable:1;
--- a/src/http/ngx_http_request_body.c
+++ b/src/http/ngx_http_request_body.c
@@ -327,7 +327,7 @@ ngx_http_do_read_client_request_body(ngx
             clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
             ngx_add_timer(c->read, clcf->client_body_timeout);
 
-            if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
+            if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
                 return NGX_HTTP_INTERNAL_SERVER_ERROR;
             }
 
@@ -462,7 +462,7 @@ ngx_http_discard_request_body(ngx_http_r
 
     r->read_event_handler = ngx_http_read_discarded_request_body_handler;
 
-    if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(rev, 0) != NGX_OK) {
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
@@ -519,7 +519,7 @@ ngx_http_read_discarded_request_body_han
 
     /* rc == NGX_AGAIN */
 
-    if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(rev, 0) != NGX_OK) {
         c->error = 1;
         ngx_http_finalize_request(r, rc);
         return;
--- a/src/http/ngx_http_script.c
+++ b/src/http/ngx_http_script.c
@@ -430,20 +430,23 @@ ngx_http_script_copy_len_code(ngx_http_s
 void
 ngx_http_script_copy_code(ngx_http_script_engine_t *e)
 {
+    u_char                       *p;
     ngx_http_script_copy_code_t  *code;
 
     code = (ngx_http_script_copy_code_t *) e->ip;
 
+    p = e->pos;
+
     if (!e->skip) {
-        e->pos = ngx_copy(e->pos, e->ip + sizeof(ngx_http_script_copy_code_t),
+        e->pos = ngx_copy(p, e->ip + sizeof(ngx_http_script_copy_code_t),
                           code->len);
     }
 
     e->ip += sizeof(ngx_http_script_copy_code_t)
           + ((code->len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1));
 
-    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
-                   "http script copy: \"%V\"", &e->buf);
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+                   "http script copy: \"%*s\"", e->pos - p, p);
 }
 
 
@@ -475,6 +478,7 @@ ngx_http_script_copy_var_len_code(ngx_ht
 void
 ngx_http_script_copy_var_code(ngx_http_script_engine_t *e)
 {
+    u_char                      *p;
     ngx_http_variable_value_t   *value;
     ngx_http_script_var_code_t  *code;
 
@@ -492,11 +496,12 @@ ngx_http_script_copy_var_code(ngx_http_s
         }
 
         if (value && !value->not_found) {
-            e->pos = ngx_copy(e->pos, value->data, value->len);
+            p = e->pos;
+            e->pos = ngx_copy(p, value->data, value->len);
 
-            ngx_log_debug1(NGX_LOG_DEBUG_HTTP,
+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP,
                            e->request->connection->log, 0,
-                           "http script var: \"%V\"", &e->buf);
+                           "http script var: \"%*s\"", e->pos - p, p);
         }
     }
 }
@@ -532,29 +537,32 @@ ngx_http_script_copy_capture_len_code(ng
 void
 ngx_http_script_copy_capture_code(ngx_http_script_engine_t *e)
 {
+    u_char                               *p;
     ngx_http_script_copy_capture_code_t  *code;
 
     code = (ngx_http_script_copy_capture_code_t *) e->ip;
 
     e->ip += sizeof(ngx_http_script_copy_capture_code_t);
 
+    p = e->pos;
+
     if (code->n < e->ncaptures) {
         if ((e->is_args || e->quote)
             && (e->request->quoted_uri || e->request->plus_in_uri))
         {
-            e->pos = (u_char *) ngx_escape_uri(e->pos,
+            e->pos = (u_char *) ngx_escape_uri(p,
                                 &e->line.data[e->captures[code->n]],
                                 e->captures[code->n + 1] - e->captures[code->n],
                                 NGX_ESCAPE_ARGS);
         } else {
-            e->pos = ngx_copy(e->pos,
+            e->pos = ngx_copy(p,
                               &e->line.data[e->captures[code->n]],
                               e->captures[code->n + 1] - e->captures[code->n]);
         }
     }
 
-    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
-                   "http script capture: \"%V\"", &e->buf);
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+                   "http script capture: \"%*s\"", e->pos - p, p);
 }
 
 
@@ -612,7 +620,7 @@ ngx_http_script_regex_start_code(ngx_htt
     rc = ngx_regex_exec(code->regex, &e->line, e->captures, code->ncaptures);
 
     if (rc == NGX_REGEX_NO_MATCHED) {
-        if (e->log) {
+        if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) {
             ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
                           "\"%V\" does not match \"%V\"",
                           &code->name, &e->line);
@@ -650,7 +658,7 @@ ngx_http_script_regex_start_code(ngx_htt
         return;
     }
 
-    if (e->log) {
+    if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) {
         ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
                       "\"%V\" matches \"%V\"", &code->name, &e->line);
     }
@@ -786,7 +794,7 @@ ngx_http_script_regex_end_code(ngx_http_
 
         e->buf.len = e->pos - e->buf.data;
 
-        if (e->log) {
+        if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) {
             ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
                           "rewritten redirect: \"%V\"", &e->buf);
         }
@@ -828,7 +836,7 @@ ngx_http_script_regex_end_code(ngx_http_
         }
     }
 
-    if (e->log) {
+    if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) {
         ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
                       "rewritten data: \"%V\", args: \"%V\"",
                       &e->buf, &r->args);
@@ -928,8 +936,8 @@ ngx_http_script_equal_code(ngx_http_scri
 
     e->ip += sizeof(uintptr_t);
 
-    if (val->len == res->len && ngx_strncmp(val->data, res->data, res->len)
-        == 0)
+    if (val->len == res->len
+        && ngx_strncmp(val->data, res->data, res->len) == 0)
     {
         *res = ngx_http_variable_true_value;
         return;
@@ -956,8 +964,8 @@ ngx_http_script_not_equal_code(ngx_http_
 
     e->ip += sizeof(uintptr_t);
 
-    if (val->len == res->len && ngx_strncmp(val->data, res->data, res->len)
-        == 0)
+    if (val->len == res->len
+        && ngx_strncmp(val->data, res->data, res->len) == 0)
     {
         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
                        "http script not equal: no");
@@ -1163,9 +1171,6 @@ ngx_http_script_set_var_code(ngx_http_sc
     ngx_http_request_t          *r;
     ngx_http_script_var_code_t  *code;
 
-    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
-                   "http script set var");
-
     code = (ngx_http_script_var_code_t *) e->ip;
 
     e->ip += sizeof(ngx_http_script_var_code_t);
@@ -1179,6 +1184,20 @@ ngx_http_script_set_var_code(ngx_http_sc
     r->variables[code->index].no_cacheable = 0;
     r->variables[code->index].not_found = 0;
     r->variables[code->index].data = e->sp->data;
+
+#if (NGX_DEBUG)
+    {
+    ngx_http_variable_t        *v;
+    ngx_http_core_main_conf_t  *cmcf;
+
+    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+
+    v = cmcf->variables.elts;
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+                   "http script set $%V", &v[code->index].name);
+    }
+#endif
 }
 
 
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -20,27 +20,38 @@ static ngx_int_t ngx_http_upstream_reini
     ngx_http_upstream_t *u);
 static void ngx_http_upstream_send_request(ngx_http_request_t *r,
     ngx_http_upstream_t *u);
-static void ngx_http_upstream_send_request_handler(ngx_event_t *wev);
-static void ngx_http_upstream_process_header(ngx_event_t *rev);
+static void ngx_http_upstream_send_request_handler(ngx_http_request_t *r,
+    ngx_http_upstream_t *u);
+static void ngx_http_upstream_process_header(ngx_http_request_t *r,
+    ngx_http_upstream_t *u);
 static ngx_int_t ngx_http_upstream_test_next(ngx_http_request_t *r,
     ngx_http_upstream_t *u);
 static ngx_int_t ngx_http_upstream_intercept_errors(ngx_http_request_t *r,
     ngx_http_upstream_t *u);
 static ngx_int_t ngx_http_upstream_test_connect(ngx_connection_t *c);
-static void ngx_http_upstream_process_body_in_memory(ngx_event_t *rev);
+static void ngx_http_upstream_process_body_in_memory(ngx_http_request_t *r,
+    ngx_http_upstream_t *u);
 static void ngx_http_upstream_send_response(ngx_http_request_t *r,
     ngx_http_upstream_t *u);
 static void
     ngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r);
-static void ngx_http_upstream_process_non_buffered_body(ngx_event_t *ev);
+static void
+    ngx_http_upstream_process_non_buffered_upstream(ngx_http_request_t *r,
+    ngx_http_upstream_t *u);
+static void
+    ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r,
+    ngx_uint_t do_write);
 static ngx_int_t ngx_http_upstream_non_buffered_filter_init(void *data);
 static ngx_int_t ngx_http_upstream_non_buffered_filter(void *data,
     ssize_t bytes);
 static void ngx_http_upstream_process_downstream(ngx_http_request_t *r);
-static void ngx_http_upstream_process_body(ngx_event_t *ev);
+static void ngx_http_upstream_process_upstream(ngx_http_request_t *r,
+    ngx_http_upstream_t *u);
+static void ngx_http_upstream_process_request(ngx_http_request_t *r);
 static void ngx_http_upstream_store(ngx_http_request_t *r,
     ngx_http_upstream_t *u);
-static void ngx_http_upstream_dummy_handler(ngx_event_t *wev);
+static void ngx_http_upstream_dummy_handler(ngx_http_request_t *r,
+    ngx_http_upstream_t *u);
 static void ngx_http_upstream_next(ngx_http_request_t *r,
     ngx_http_upstream_t *u, ngx_uint_t ft_type);
 static void ngx_http_upstream_cleanup(void *data);
@@ -85,6 +96,8 @@ static ngx_int_t ngx_http_upstream_statu
     ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_upstream_response_time_variable(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_upstream_response_length_variable(
+    ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data);
 
 static char *ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy);
 static char *ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd,
@@ -274,13 +287,20 @@ ngx_module_t  ngx_http_upstream_module =
 static ngx_http_variable_t  ngx_http_upstream_vars[] = {
 
     { ngx_string("upstream_addr"), NULL,
-      ngx_http_upstream_addr_variable, 0, NGX_HTTP_VAR_NOHASH, 0 },
+      ngx_http_upstream_addr_variable, 0,
+      NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 },
 
     { ngx_string("upstream_status"), NULL,
-      ngx_http_upstream_status_variable, 0, NGX_HTTP_VAR_NOHASH, 0 },
+      ngx_http_upstream_status_variable, 0,
+      NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 },
 
     { ngx_string("upstream_response_time"), NULL,
-      ngx_http_upstream_response_time_variable, 0, NGX_HTTP_VAR_NOHASH, 0 },
+      ngx_http_upstream_response_time_variable, 0,
+      NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+    { ngx_string("upstream_response_length"), NULL,
+      ngx_http_upstream_response_length_variable, 0,
+      NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 },
 
     { ngx_null_string, NULL, NULL, 0, 0, 0 }
 };
@@ -394,6 +414,20 @@ ngx_http_upstream_init(ngx_http_request_
 
     } else {
 
+        if (u->resolved->sockaddr) {
+
+            if (ngx_http_upstream_create_round_robin_peer(r, u->resolved)
+                != NGX_OK)
+            {
+                ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+                return;
+            }
+
+            ngx_http_upstream_connect(r, u);
+
+            return;
+        }
+
         host = &u->resolved->host;
 
         umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
@@ -405,7 +439,7 @@ ngx_http_upstream_init(ngx_http_request_
             uscf = uscfp[i];
 
             if (uscf->host.len == host->len
-                && ((uscf->port == 0 && u->resolved->default_port)
+                && ((uscf->port == 0 && u->resolved->no_port)
                      || uscf->port == u->resolved->port)
                 && ngx_memcmp(uscf->host.data, host->data, host->len) == 0)
             {
@@ -511,6 +545,37 @@ ngx_http_upstream_resolve_handler(ngx_re
 
 
 static void
+ngx_http_upstream_handler(ngx_event_t *ev)
+{
+    ngx_connection_t     *c;
+    ngx_http_request_t   *r;
+    ngx_http_log_ctx_t   *ctx;
+    ngx_http_upstream_t  *u;
+
+    c = ev->data;
+    r = c->data;
+
+    u = r->upstream;
+    c = r->connection;
+
+    ctx = c->log->data;
+    ctx->current_request = r;
+
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                   "http upstream request: \"%V?%V\"", &r->uri, &r->args);
+
+    if (ev->write) {
+        u->write_event_handler(r, u);
+
+    } else {
+        u->read_event_handler(r, u);
+    }
+
+    ngx_http_run_posted_requests(c);
+}
+
+
+static void
 ngx_http_upstream_rd_check_broken_connection(ngx_http_request_t *r)
 {
     ngx_http_upstream_check_broken_connection(r, r->connection->read);
@@ -712,8 +777,11 @@ ngx_http_upstream_connect(ngx_http_reque
 
     c->data = r;
 
-    c->write->handler = ngx_http_upstream_send_request_handler;
-    c->read->handler = ngx_http_upstream_process_header;
+    c->write->handler = ngx_http_upstream_handler;
+    c->read->handler = ngx_http_upstream_handler;
+
+    u->write_event_handler = ngx_http_upstream_send_request_handler;
+    u->read_event_handler = ngx_http_upstream_process_header;
 
     c->sendfile &= r->connection->sendfile;
     u->output.sendfile = c->sendfile;
@@ -838,8 +906,8 @@ ngx_http_upstream_ssl_handshake(ngx_conn
             u->peer.save_session(&u->peer, u->peer.data);
         }
 
-        c->write->handler = ngx_http_upstream_send_request_handler;
-        c->read->handler = ngx_http_upstream_process_header;
+        c->write->handler = ngx_http_upstream_handler;
+        c->read->handler = ngx_http_upstream_handler;
 
         ngx_http_upstream_send_request(r, u);
 
@@ -955,8 +1023,7 @@ ngx_http_upstream_send_request(ngx_http_
     if (rc == NGX_AGAIN) {
         ngx_add_timer(c->write, u->conf->send_timeout);
 
-        if (ngx_handle_write_event(c->write, u->conf->send_lowat) == NGX_ERROR)
-        {
+        if (ngx_handle_write_event(c->write, u->conf->send_lowat) != NGX_OK) {
             ngx_http_upstream_finalize_request(r, u,
                                                NGX_HTTP_INTERNAL_SERVER_ERROR);
             return;
@@ -993,14 +1060,14 @@ ngx_http_upstream_send_request(ngx_http_
          * it's better to do here because we postpone header buffer allocation
          */
 
-        ngx_http_upstream_process_header(c->read);
+        ngx_http_upstream_process_header(r, u);
         return;
     }
 #endif
 
-    c->write->handler = ngx_http_upstream_dummy_handler;
-
-    if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) {
+    u->write_event_handler = ngx_http_upstream_dummy_handler;
+
+    if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
         ngx_http_upstream_finalize_request(r, u,
                                            NGX_HTTP_INTERNAL_SERVER_ERROR);
         return;
@@ -1009,20 +1076,17 @@ ngx_http_upstream_send_request(ngx_http_
 
 
 static void
-ngx_http_upstream_send_request_handler(ngx_event_t *wev)
+ngx_http_upstream_send_request_handler(ngx_http_request_t *r,
+    ngx_http_upstream_t *u)
 {
-    ngx_connection_t     *c;
-    ngx_http_request_t   *r;
-    ngx_http_upstream_t  *u;
-
-    c = wev->data;
-    r = c->data;
-    u = r->upstream;
-
-    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0,
+    ngx_connection_t  *c;
+
+    c = u->peer.connection;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http upstream send request handler");
 
-    if (wev->timedout) {
+    if (c->write->timedout) {
         ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT);
         return;
     }
@@ -1037,9 +1101,9 @@ ngx_http_upstream_send_request_handler(n
 #endif
 
     if (u->header_sent) {
-        wev->handler = ngx_http_upstream_dummy_handler;
-
-        (void) ngx_handle_write_event(wev, 0);
+        u->write_event_handler = ngx_http_upstream_dummy_handler;
+
+        (void) ngx_handle_write_event(c->write, 0);
 
         return;
     }
@@ -1049,7 +1113,7 @@ ngx_http_upstream_send_request_handler(n
 
 
 static void
-ngx_http_upstream_process_header(ngx_event_t *rev)
+ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u)
 {
     ssize_t                         n;
     ngx_int_t                       rc;
@@ -1058,21 +1122,17 @@ ngx_http_upstream_process_header(ngx_eve
     ngx_list_part_t                *part;
     ngx_table_elt_t                *h;
     ngx_connection_t               *c;
-    ngx_http_request_t             *r;
-    ngx_http_upstream_t            *u;
     ngx_http_upstream_header_t     *hh;
     ngx_http_upstream_main_conf_t  *umcf;
 
-    c = rev->data;
-    r = c->data;
-    u = r->upstream;
-
-    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,
+    c = u->peer.connection;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
                    "http upstream process header");
 
     c->log->action = "reading response header from upstream";
 
-    if (rev->timedout) {
+    if (c->read->timedout) {
         ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT);
         return;
     }
@@ -1114,62 +1174,59 @@ ngx_http_upstream_process_header(ngx_eve
 #endif
     }
 
-    n = c->recv(c, u->buffer.last, u->buffer.end - u->buffer.last);
-
-    if (n == NGX_AGAIN) {
+    for ( ;; ) {
+
+        n = c->recv(c, u->buffer.last, u->buffer.end - u->buffer.last);
+
+        if (n == NGX_AGAIN) {
 #if 0
-        ngx_add_timer(rev, u->read_timeout);
+            ngx_add_timer(rev, u->read_timeout);
 #endif
 
-        if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
-            ngx_http_upstream_finalize_request(r, u,
+            if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+                ngx_http_upstream_finalize_request(r, u,
                                                NGX_HTTP_INTERNAL_SERVER_ERROR);
+                return;
+            }
+
+            return;
+        }
+
+        if (n == 0) {
+            ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                          "upstream prematurely closed connection");
+        }
+
+        if (n == NGX_ERROR || n == 0) {
+            ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
             return;
         }
 
-        return;
-    }
-
-    if (n == 0) {
-        ngx_log_error(NGX_LOG_ERR, rev->log, 0,
-                      "upstream prematurely closed connection");
-    }
-
-    if (n == NGX_ERROR || n == 0) {
-        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
-        return;
-    }
-
-    u->buffer.last += n;
+        u->buffer.last += n;
 
 #if 0
-    u->valid_header_in = 0;
-
-    u->peer.cached = 0;
-#endif
-
-    rc = u->process_header(r);
-
-    if (rc == NGX_AGAIN) {
-#if 0
-        ngx_add_timer(rev, u->read_timeout);
+        u->valid_header_in = 0;
+
+        u->peer.cached = 0;
 #endif
 
-        if (u->buffer.pos == u->buffer.end) {
-            ngx_log_error(NGX_LOG_ERR, rev->log, 0,
-                          "upstream sent too big header");
-
-            ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_INVALID_HEADER);
-            return;
+        rc = u->process_header(r);
+
+        if (rc == NGX_AGAIN) {
+
+            if (u->buffer.pos == u->buffer.end) {
+                ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                              "upstream sent too big header");
+
+                ngx_http_upstream_next(r, u,
+                                       NGX_HTTP_UPSTREAM_FT_INVALID_HEADER);
+                return;
+            }
+
+            continue;
         }
 
-        if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
-            ngx_http_upstream_finalize_request(r, u,
-                                               NGX_HTTP_INTERNAL_SERVER_ERROR);
-            return;
-        }
-
-        return;
+        break;
     }
 
     if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) {
@@ -1337,19 +1394,27 @@ ngx_http_upstream_process_header(ngx_eve
         return;
     }
 
-    if (u->buffer.last - u->buffer.pos >= (ssize_t) u->length) {
-        if (u->input_filter(u->input_filter_ctx, 0) == NGX_ERROR) {
+    n = u->buffer.last - u->buffer.pos;
+
+    if (n) {
+        u->buffer.last -= n;
+
+        u->state->response_length += n;
+
+        if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) {
             ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
             return;
         }
 
-        ngx_http_upstream_finalize_request(r, u, 0);
-        return;
+        if (u->length == 0) {
+            ngx_http_upstream_finalize_request(r, u, 0);
+            return;
+        }
     }
 
-    rev->handler = ngx_http_upstream_process_body_in_memory;
-
-    ngx_http_upstream_process_body_in_memory(rev);
+    u->read_event_handler = ngx_http_upstream_process_body_in_memory;
+
+    ngx_http_upstream_process_body_in_memory(r, u);
 }
 
 
@@ -1493,18 +1558,17 @@ ngx_http_upstream_test_connect(ngx_conne
 
 
 static void
-ngx_http_upstream_process_body_in_memory(ngx_event_t *rev)
+ngx_http_upstream_process_body_in_memory(ngx_http_request_t *r,
+    ngx_http_upstream_t *u)
 {
-    size_t                size;
-    ssize_t               n;
-    ngx_buf_t            *b;
-    ngx_connection_t     *c;
-    ngx_http_request_t   *r;
-    ngx_http_upstream_t  *u;
-
-    c = rev->data;
-    r = c->data;
-    u = r->upstream;
+    size_t             size;
+    ssize_t            n;
+    ngx_buf_t         *b;
+    ngx_event_t       *rev;
+    ngx_connection_t  *c;
+
+    c = u->peer.connection;
+    rev = c->read;
 
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
                    "http upstream process body on memory");
@@ -1539,6 +1603,8 @@ ngx_http_upstream_process_body_in_memory
             return;
         }
 
+        u->state->response_length += n;
+
         if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) {
             ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
             return;
@@ -1549,7 +1615,7 @@ ngx_http_upstream_process_body_in_memory
         }
     }
 
-    if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(rev, 0) != NGX_OK) {
         ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
         return;
     }
@@ -1567,7 +1633,7 @@ static void
 ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u)
 {
     int                        tcp_nodelay;
-    ssize_t                    size;
+    ssize_t                    n;
     ngx_int_t                  rc;
     ngx_event_pipe_t          *p;
     ngx_connection_t          *c;
@@ -1611,8 +1677,7 @@ ngx_http_upstream_send_response(ngx_http
             u->input_filter_ctx = r;
         }
 
-        u->peer.connection->read->handler =
-                                   ngx_http_upstream_process_non_buffered_body;
+        u->read_event_handler = ngx_http_upstream_process_non_buffered_upstream;
         r->write_event_handler =
                              ngx_http_upstream_process_non_buffered_downstream;
 
@@ -1640,17 +1705,19 @@ ngx_http_upstream_send_response(ngx_http
             c->tcp_nodelay = NGX_TCP_NODELAY_SET;
         }
 
-        size = u->buffer.last - u->buffer.pos;
-
-        if (size) {
+        n = u->buffer.last - u->buffer.pos;
+
+        if (n) {
             u->buffer.last = u->buffer.pos;
 
-            if (u->input_filter(u->input_filter_ctx, size) == NGX_ERROR) {
+            u->state->response_length += n;
+
+            if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) {
                 ngx_http_upstream_finalize_request(r, u, 0);
                 return;
             }
 
-            ngx_http_upstream_process_non_buffered_body(c->write);
+            ngx_http_upstream_process_non_buffered_downstream(r);
 
         } else {
             u->buffer.pos = u->buffer.start;
@@ -1662,8 +1729,7 @@ ngx_http_upstream_send_response(ngx_http
             }
 
             if (u->peer.connection->read->ready) {
-                ngx_http_upstream_process_non_buffered_body(
-                                                     u->peer.connection->read);
+                ngx_http_upstream_process_non_buffered_upstream(r, u);
             }
         }
 
@@ -1793,69 +1859,82 @@ ngx_http_upstream_send_response(ngx_http
     p->send_timeout = clcf->send_timeout;
     p->send_lowat = clcf->send_lowat;
 
-    u->peer.connection->read->handler = ngx_http_upstream_process_body;
+    u->read_event_handler = ngx_http_upstream_process_upstream;
     r->write_event_handler = ngx_http_upstream_process_downstream;
 
-    ngx_http_upstream_process_body(u->peer.connection->read);
+    ngx_http_upstream_process_upstream(r, u);
 }
 
 
 static void
 ngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r)
 {
-    ngx_http_upstream_process_non_buffered_body(r->connection->write);
+    ngx_event_t          *wev;
+    ngx_connection_t     *c;
+    ngx_http_upstream_t  *u;
+
+    c = r->connection;
+    u = r->upstream;
+    wev = c->write;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                   "http upstream process non buffered downstream");
+
+    c->log->action = "sending to client";
+
+    if (wev->timedout) {
+        c->timedout = 1;
+        ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out");
+        ngx_http_upstream_finalize_request(r, u, 0);
+        return;
+    }
+
+    ngx_http_upstream_process_non_buffered_request(r, 1);
 }
 
 
 static void
-ngx_http_upstream_process_non_buffered_body(ngx_event_t *ev)
+ngx_http_upstream_process_non_buffered_upstream(ngx_http_request_t *r,
+    ngx_http_upstream_t *u)
+{
+    ngx_connection_t  *c;
+
+    c = u->peer.connection;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                   "http upstream process non buffered upstream");
+
+    c->log->action = "reading upstream";
+
+    if (c->read->timedout) {
+        ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out");
+        ngx_http_upstream_finalize_request(r, u, 0);
+        return;
+    }
+
+    ngx_http_upstream_process_non_buffered_request(r, 0);
+}
+
+
+static void
+ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r,
+    ngx_uint_t do_write)
 {
     size_t                     size;
     ssize_t                    n;
     ngx_buf_t                 *b;
     ngx_int_t                  rc;
-    ngx_uint_t                 do_write;
-    ngx_connection_t          *c, *downstream, *upstream;
-    ngx_http_request_t        *r;
+    ngx_connection_t          *downstream, *upstream;
     ngx_http_upstream_t       *u;
     ngx_http_core_loc_conf_t  *clcf;
 
-    c = ev->data;
-    r = c->data;
     u = r->upstream;
-
-    if (ev->write) {
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http upstream process non buffered downstream");
-        c->log->action = "sending to client";
-
-    } else {
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http upstream process non buffered upstream");
-        c->log->action = "reading upstream";
-    }
-
-    if (ev->timedout) {
-        if (ev->write) {
-            c->timedout = 1;
-            ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out");
-
-        } else {
-            ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out");
-        }
-
-        ngx_http_upstream_finalize_request(r, u, 0);
-        return;
-    }
-
     downstream = r->connection;
     upstream = u->peer.connection;
 
     b = &u->buffer;
 
-    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
-
-    do_write = ev->write || u->length == 0;
+    do_write = do_write || u->length == 0;
 
     for ( ;; ) {
 
@@ -1907,6 +1986,8 @@ ngx_http_upstream_process_non_buffered_b
             }
 
             if (n > 0) {
+                u->state->response_length += n;
+
                 if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) {
                     ngx_http_upstream_finalize_request(r, u, 0);
                     return;
@@ -1921,9 +2002,11 @@ ngx_http_upstream_process_non_buffered_b
         break;
     }
 
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
     if (downstream->data == r) {
         if (ngx_handle_write_event(downstream->write, clcf->send_lowat)
-            == NGX_ERROR)
+            != NGX_OK)
         {
             ngx_http_upstream_finalize_request(r, u, 0);
             return;
@@ -1937,7 +2020,7 @@ ngx_http_upstream_process_non_buffered_b
         ngx_del_timer(downstream->write);
     }
 
-    if (ngx_handle_read_event(upstream->read, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(upstream->read, 0) != NGX_OK) {
         ngx_http_upstream_finalize_request(r, u, 0);
         return;
     }
@@ -2003,96 +2086,71 @@ ngx_http_upstream_non_buffered_filter(vo
 static void
 ngx_http_upstream_process_downstream(ngx_http_request_t *r)
 {
-    ngx_http_upstream_process_body(r->connection->write);
-}
-
-
-static void
-ngx_http_upstream_process_body(ngx_event_t *ev)
-{
-    ngx_temp_file_t      *tf;
+    ngx_event_t          *wev;
+    ngx_connection_t     *c;
     ngx_event_pipe_t     *p;
-    ngx_connection_t     *c, *downstream;
-    ngx_http_log_ctx_t   *ctx;
-    ngx_http_request_t   *r;
     ngx_http_upstream_t  *u;
 
-    c = ev->data;
-    r = c->data;
+    c = r->connection;
     u = r->upstream;
-    downstream = r->connection;
-
-    if (ev->write) {
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http upstream process downstream");
-        c->log->action = "sending to client";
-
-    } else {
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "http upstream process upstream");
-        c->log->action = "reading upstream";
-
-        ctx = c->log->data;
-        ctx->current_request = r;
-    }
-
     p = u->pipe;
-
-    if (ev->timedout) {
-        if (ev->write) {
-            if (ev->delayed) {
-
-                ev->timedout = 0;
-                ev->delayed = 0;
-
-                if (!ev->ready) {
-                    ngx_add_timer(ev, p->send_timeout);
-
-                    if (ngx_handle_write_event(ev, p->send_lowat) == NGX_ERROR)
-                    {
-                        ngx_http_upstream_finalize_request(r, u, 0);
-                        return;
-                    }
-
+    wev = c->write;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                   "http upstream process downstream");
+
+    c->log->action = "sending to client";
+
+    if (wev->timedout) {
+
+        if (wev->delayed) {
+
+            wev->timedout = 0;
+            wev->delayed = 0;
+
+            if (!wev->ready) {
+                ngx_add_timer(wev, p->send_timeout);
+
+                if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) {
+                    ngx_http_upstream_finalize_request(r, u, 0);
+                }
+
+                return;
+            }
+
+            if (ngx_event_pipe(p, wev->write) == NGX_ABORT) {
+
+                if (c->destroyed) {
                     return;
                 }
 
-                if (ngx_event_pipe(p, ev->write) == NGX_ABORT) {
-
-                    if (downstream->destroyed) {
-                        return;
-                    }
-
-                    ngx_http_upstream_finalize_request(r, u, 0);
-                    return;
-                }
-
-            } else {
-                p->downstream_error = 1;
-                c->timedout = 1;
-                ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out");
+                ngx_http_upstream_finalize_request(r, u, 0);
+                return;
             }
 
         } else {
-            p->upstream_error = 1;
-            ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out");
+            p->downstream_error = 1;
+            c->timedout = 1;
+            ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out");
         }
 
     } else {
-        if (ev->write && ev->delayed) {
+
+        if (wev->delayed) {
+
             ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
                            "http downstream delayed");
 
-            if (ngx_handle_write_event(ev, p->send_lowat) == NGX_ERROR) {
-                return;
+            if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) {
+                ngx_http_upstream_finalize_request(r, u, 0);
             }
 
             return;
         }
 
-        if (ngx_event_pipe(p, ev->write) == NGX_ABORT) {
-
-            if (downstream->destroyed) {
+        if (ngx_event_pipe(p, 1) == NGX_ABORT) {
+
+            if (c->destroyed) {
                 return;
             }
 
@@ -2101,6 +2159,55 @@ ngx_http_upstream_process_body(ngx_event
         }
     }
 
+    ngx_http_upstream_process_request(r);
+}
+
+
+static void
+ngx_http_upstream_process_upstream(ngx_http_request_t *r,
+    ngx_http_upstream_t *u)
+{
+    ngx_connection_t  *c;
+
+    c = u->peer.connection;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                   "http upstream process upstream");
+
+    c->log->action = "reading upstream";
+
+    if (c->read->timedout) {
+        u->pipe->upstream_error = 1;
+        ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out");
+
+    } else {
+        c = r->connection;
+
+        if (ngx_event_pipe(u->pipe, 0) == NGX_ABORT) {
+
+            if (c->destroyed) {
+                return;
+            }
+
+            ngx_http_upstream_finalize_request(r, u, 0);
+            return;
+        }
+    }
+
+    ngx_http_upstream_process_request(r);
+}
+
+
+static void
+ngx_http_upstream_process_request(ngx_http_request_t *r)
+{
+    ngx_temp_file_t      *tf;
+    ngx_event_pipe_t     *p;
+    ngx_http_upstream_t  *u;
+
+    u = r->upstream;
+    p = u->pipe;
+
     if (u->peer.connection) {
 
         if (u->store) {
@@ -2151,7 +2258,7 @@ ngx_http_upstream_process_body(ngx_event
 #endif
 
         if (p->upstream_done || p->upstream_eof || p->upstream_error) {
-            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                            "http upstream exit: %p", p->out);
 #if 0
             ngx_http_busy_unlock(u->conf->busy_lock, &u->busy_lock);
@@ -2162,7 +2269,7 @@ ngx_http_upstream_process_body(ngx_event
     }
 
     if (p->downstream_error) {
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                        "http upstream downstream error");
 
         if (!u->cacheable && u->peer.connection) {
@@ -2209,9 +2316,11 @@ ngx_http_upstream_store(ngx_http_request
     }
 
     ext.access = u->conf->store_access;
+    ext.path_access = u->conf->store_access;
     ext.time = -1;
     ext.create_path = 1;
     ext.delete_file = 1;
+    ext.log_rename_error = 1;
     ext.log = r->connection->log;
 
     if (u->headers_in.last_modified) {
@@ -2247,9 +2356,9 @@ ngx_http_upstream_store(ngx_http_request
 
 
 static void
-ngx_http_upstream_dummy_handler(ngx_event_t *wev)
+ngx_http_upstream_dummy_handler(ngx_http_request_t *r, ngx_http_upstream_t *u)
 {
-    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0,
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http upstream dummy handler");
 }
 
@@ -2393,12 +2502,18 @@ ngx_http_upstream_finalize_request(ngx_h
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "finalize http upstream request: %i", rc);
 
-    *u->cleanup = NULL;
+    if (u->cleanup) {
+        *u->cleanup = NULL;
+    }
 
     if (u->state && u->state->response_sec) {
         tp = ngx_timeofday();
         u->state->response_sec = tp->sec - u->state->response_sec;
         u->state->response_msec = tp->msec - u->state->response_msec;
+
+        if (u->pipe) {
+            u->state->response_length = u->pipe->read_length;
+        }
     }
 
     u->finalize_request(r, rc);
@@ -2461,17 +2576,8 @@ ngx_http_upstream_finalize_request(ngx_h
 
     r->connection->log->action = "sending to client";
 
-    if (rc == 0) {
-        if (r == r->main) {
-            if (!r->post_action) {
-                rc = ngx_http_send_special(r, NGX_HTTP_LAST);
-            }
-
-        } else {
-            if (r->out) {
-                rc = NGX_AGAIN;
-            }
-        }
+    if (rc == 0 && r == r->main && !r->post_action) {
+        rc = ngx_http_send_special(r, NGX_HTTP_LAST);
     }
 
     ngx_http_finalize_request(r, rc);
@@ -3051,6 +3157,66 @@ ngx_http_upstream_response_time_variable
 }
 
 
+static ngx_int_t
+ngx_http_upstream_response_length_variable(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    u_char                     *p;
+    size_t                      len;
+    ngx_uint_t                  i;
+    ngx_http_upstream_state_t  *state;
+
+    v->valid = 1;
+    v->no_cacheable = 0;
+    v->not_found = 0;
+
+    if (r->upstream_states == NULL || r->upstream_states->nelts == 0) {
+        v->not_found = 1;
+        return NGX_OK;
+    }
+
+    len = r->upstream_states->nelts * (NGX_OFF_T_LEN + 2);
+
+    p = ngx_pnalloc(r->pool, len);
+    if (p == NULL) {
+        return NGX_ERROR;
+    }
+
+    v->data = p;
+
+    i = 0;
+    state = r->upstream_states->elts;
+
+    for ( ;; ) {
+        p = ngx_sprintf(p, "%O", state[i].response_length);
+
+        if (++i == r->upstream_states->nelts) {
+            break;
+        }
+
+        if (state[i].peer) {
+            *p++ = ',';
+            *p++ = ' ';
+
+        } else {
+            *p++ = ' ';
+            *p++ = ':';
+            *p++ = ' ';
+
+            if (++i == r->upstream_states->nelts) {
+                break;
+            }
+
+            continue;
+        }
+    }
+
+    v->len = p - v->data;
+
+    return NGX_OK;
+}
+
+
 ngx_int_t
 ngx_http_upstream_header_variable(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data)
--- a/src/http/ngx_http_upstream.h
+++ b/src/http/ngx_http_upstream.h
@@ -45,6 +45,7 @@ typedef struct {
     ngx_uint_t                      status;
     time_t                          response_sec;
     ngx_uint_t                      response_msec;
+    off_t                           response_length;
 
     ngx_str_t                      *peer;
 } ngx_http_upstream_state_t;
@@ -144,8 +145,6 @@ typedef struct {
     ngx_array_t                    *hide_headers;
     ngx_array_t                    *pass_headers;
 
-    ngx_str_t                       schema;
-
     ngx_array_t                    *store_lengths;
     ngx_array_t                    *store_values;
 
@@ -209,14 +208,26 @@ typedef struct {
 typedef struct {
     ngx_str_t                       host;
     in_port_t                       port;
-    ngx_uint_t                      default_port; /* unsigned  default_port:1 */
+    ngx_uint_t                      no_port; /* unsigned  no_port:1 */
+
     ngx_uint_t                      naddrs;
     in_addr_t                      *addrs;
+
+    struct sockaddr                *sockaddr;
+    socklen_t                       socklen;
+
     ngx_resolver_ctx_t             *ctx;
 } ngx_http_upstream_resolved_t;
 
 
+typedef void (*ngx_http_upstream_handler_pt)(ngx_http_request_t *r,
+    ngx_http_upstream_t *u);
+
+
 struct ngx_http_upstream_s {
+    ngx_http_upstream_handler_pt    read_event_handler;
+    ngx_http_upstream_handler_pt    write_event_handler;
+
     ngx_peer_connection_t           peer;
 
     ngx_event_pipe_t               *pipe;
--- a/src/http/ngx_http_upstream_round_robin.c
+++ b/src/http/ngx_http_upstream_round_robin.c
@@ -279,35 +279,47 @@ ngx_http_upstream_create_round_robin_pee
     peers->number = ur->naddrs;
     peers->name = &ur->host;
 
-    for (i = 0; i < ur->naddrs; i++) {
-
-        len = NGX_INET_ADDRSTRLEN + sizeof(":65536") - 1;
+    if (ur->sockaddr) {
+        peers->peer[0].sockaddr = ur->sockaddr;
+        peers->peer[0].socklen = ur->socklen;
+        peers->peer[0].name = ur->host;
+        peers->peer[0].weight = 1;
+        peers->peer[0].current_weight = 1;
+        peers->peer[0].max_fails = 1;
+        peers->peer[0].fail_timeout = 10;
 
-        p = ngx_pnalloc(r->pool, len);
-        if (p == NULL) {
-            return NGX_ERROR;
-        }
+    } else {
+
+        for (i = 0; i < ur->naddrs; i++) {
 
-        len = ngx_inet_ntop(AF_INET, &ur->addrs[i], p, NGX_INET_ADDRSTRLEN);
-        len = ngx_sprintf(&p[len], ":%d", ur->port) - p;
+            len = NGX_INET_ADDRSTRLEN + sizeof(":65536") - 1;
+
+            p = ngx_pnalloc(r->pool, len);
+            if (p == NULL) {
+                return NGX_ERROR;
+            }
 
-        sin = ngx_pcalloc(r->pool, sizeof(struct sockaddr_in));
-        if (sin == NULL) {
-            return NGX_ERROR;
-        }
+            len = ngx_inet_ntop(AF_INET, &ur->addrs[i], p, NGX_INET_ADDRSTRLEN);
+            len = ngx_sprintf(&p[len], ":%d", ur->port) - p;
 
-        sin->sin_family = AF_INET;
-        sin->sin_port = htons(ur->port);
-        sin->sin_addr.s_addr = ur->addrs[i];
+            sin = ngx_pcalloc(r->pool, sizeof(struct sockaddr_in));
+            if (sin == NULL) {
+                return NGX_ERROR;
+            }
 
-        peers->peer[i].sockaddr = (struct sockaddr *) sin;
-        peers->peer[i].socklen = sizeof(struct sockaddr_in);
-        peers->peer[i].name.len = len;
-        peers->peer[i].name.data = p;
-        peers->peer[i].weight = 1;
-        peers->peer[i].current_weight = 1;
-        peers->peer[i].max_fails = 1;
-        peers->peer[i].fail_timeout = 10;
+            sin->sin_family = AF_INET;
+            sin->sin_port = htons(ur->port);
+            sin->sin_addr.s_addr = ur->addrs[i];
+
+            peers->peer[i].sockaddr = (struct sockaddr *) sin;
+            peers->peer[i].socklen = sizeof(struct sockaddr_in);
+            peers->peer[i].name.len = len;
+            peers->peer[i].name.data = p;
+            peers->peer[i].weight = 1;
+            peers->peer[i].current_weight = 1;
+            peers->peer[i].max_fails = 1;
+            peers->peer[i].fail_timeout = 10;
+        }
     }
 
     rrp->peers = peers;
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -25,6 +25,8 @@ static ngx_int_t ngx_http_variable_unkno
     ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_variable_unknown_header_out(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_cookie(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_variable_argument(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
 
@@ -488,6 +490,15 @@ ngx_http_get_variable(ngx_http_request_t
         return NULL;
     }
 
+    if (ngx_strncmp(name->data, "cookie_", 7) == 0) {
+
+        if (ngx_http_variable_cookie(r, vv, (uintptr_t) name) == NGX_OK) {
+            return vv;
+        }
+
+        return NULL;
+    }
+
     if (ngx_strncmp(name->data, "arg_", 4) == 0) {
 
         if (ngx_http_variable_argument(r, vv, (uintptr_t) name) == NGX_OK) {
@@ -728,56 +739,56 @@ ngx_http_variable_unknown_header(ngx_htt
 
 
 static ngx_int_t
+ngx_http_variable_cookie(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+    uintptr_t data)
+{
+    ngx_str_t *name = (ngx_str_t *) data;
+
+    ngx_str_t  cookie, s;
+
+    s.len = name->len - (sizeof("cookie_") - 1);
+    s.data = name->data + sizeof("cookie_") - 1;
+
+    if (ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &s, &cookie)
+        == NGX_DECLINED)
+    {
+        v->not_found = 1;
+        return NGX_OK;
+    }
+
+    v->len = cookie.len;
+    v->valid = 1;
+    v->no_cacheable = 0;
+    v->not_found = 0;
+    v->data = cookie.data;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_http_variable_argument(ngx_http_request_t *r, ngx_http_variable_value_t *v,
     uintptr_t data)
 {
     ngx_str_t *name = (ngx_str_t *) data;
 
-    u_char  *p, *arg;
-    size_t   len;
+    u_char     *arg;
+    size_t      len;
+    ngx_str_t   value;
 
-    if (r->args.len == 0) {
+    len = name->len - (sizeof("arg_") - 1);
+    arg = name->data + sizeof("arg_") - 1;
+
+    if (ngx_http_arg(r, arg, len, &value) != NGX_OK) {
         v->not_found = 1;
         return NGX_OK;
     }
 
-    len = name->len - 1 - (sizeof("arg_") - 1);
-    arg = name->data + sizeof("arg_") - 1;
-
-    for (p = r->args.data; *p && *p != ' '; p++) {
-
-        /*
-         * although r->args.data is not null-terminated by itself,
-         * however, there is null in the end of request line
-         */
-
-        p = ngx_strcasestrn(p, (char *) arg, len);
-
-        if (p == NULL) {
-            v->not_found = 1;
-            return NGX_OK;
-        }
-
-        if ((p == r->args.data || *(p - 1) == '&') && *(p + len + 1) == '=') {
-
-            v->data = p + len + 2;
-
-            p = (u_char *) ngx_strchr(p, '&');
-
-            if (p == NULL) {
-                p = r->args.data + r->args.len;
-            }
-
-            v->len = p - v->data;
-            v->valid = 1;
-            v->no_cacheable = 0;
-            v->not_found = 0;
-
-            return NGX_OK;
-        }
-    }
-
-    v->not_found = 1;
+    v->data = value.data;
+    v->len = value.len;
+    v->valid = 1;
+    v->no_cacheable = 0;
+    v->not_found = 0;
 
     return NGX_OK;
 }
@@ -1544,6 +1555,13 @@ ngx_http_variables_init_vars(ngx_conf_t 
             continue;
         }
 
+        if (ngx_strncmp(v[i].name.data, "cookie_", 7) == 0) {
+            v[i].get_handler = ngx_http_variable_cookie;
+            v[i].data = (uintptr_t) &v[i].name;
+
+            continue;
+        }
+
         if (ngx_strncmp(v[i].name.data, "arg_", 4) == 0) {
             v[i].get_handler = ngx_http_variable_argument;
             v[i].data = (uintptr_t) &v[i].name;
@@ -1589,3 +1607,87 @@ ngx_http_variables_init_vars(ngx_conf_t 
 
     return NGX_OK;
 }
+
+
+void
+ngx_http_variable_value_rbtree_insert(ngx_rbtree_node_t *temp,
+    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
+{
+    ngx_rbtree_node_t               **p;
+    ngx_http_variable_value_node_t   *vvn, *vvt;
+
+    for ( ;; ) {
+
+        vvn = (ngx_http_variable_value_node_t *) node;
+        vvt = (ngx_http_variable_value_node_t *) temp;
+
+        if (node->key != temp->key) {
+
+            p = (node->key < temp->key) ? &temp->left : &temp->right;
+
+        } else if (vvn->len != vvt->len) {
+
+            p = (vvn->len < vvt->len) ? &temp->left : &temp->right;
+
+        } else {
+            p = (ngx_memcmp(vvn->value->data, vvt->value->data, vvn->len) < 0)
+                 ? &temp->left : &temp->right;
+        }
+
+        if (*p == sentinel) {
+            break;
+        }
+
+        temp = *p;
+    }
+
+    *p = node;
+    node->parent = temp;
+    node->left = sentinel;
+    node->right = sentinel;
+    ngx_rbt_red(node);
+}
+
+
+ngx_http_variable_value_t *
+ngx_http_variable_value_lookup(ngx_rbtree_t *rbtree, ngx_str_t *val,
+    uint32_t hash)
+{
+    ngx_int_t                        rc;
+    ngx_rbtree_node_t               *node, *sentinel;
+    ngx_http_variable_value_node_t  *vvn;
+
+    node = rbtree->root;
+    sentinel = rbtree->sentinel;
+
+    while (node != sentinel) {
+
+        vvn = (ngx_http_variable_value_node_t *) node;
+
+        if (hash != node->key) {
+            node = (hash < node->key) ? node->left : node->right;
+            continue;
+        }
+
+        if (val->len != vvn->len) {
+            node = (val->len < vvn->len) ? node->left : node->right;
+            continue;
+        }
+
+        rc = ngx_memcmp(val->data, vvn->value->data, val->len);
+
+        if (rc < 0) {
+            node = node->left;
+            continue;
+        }
+
+        if (rc > 0) {
+            node = node->right;
+            continue;
+        }
+
+        return vvn->value;
+    }
+
+    return NULL;
+}
--- a/src/http/ngx_http_variables.h
+++ b/src/http/ngx_http_variables.h
@@ -63,6 +63,19 @@ ngx_int_t ngx_http_variables_add_core_va
 ngx_int_t ngx_http_variables_init_vars(ngx_conf_t *cf);
 
 
+typedef struct {
+    ngx_rbtree_node_t             node;
+    size_t                        len;
+    ngx_http_variable_value_t    *value;
+} ngx_http_variable_value_node_t;
+
+
+void ngx_http_variable_value_rbtree_insert(ngx_rbtree_node_t *temp,
+    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
+ngx_http_variable_value_t *ngx_http_variable_value_lookup(ngx_rbtree_t *rbtree,
+    ngx_str_t *name, uint32_t hash);
+
+
 extern ngx_http_variable_value_t  ngx_http_variable_null_value;
 extern ngx_http_variable_value_t  ngx_http_variable_true_value;
 
--- a/src/mail/ngx_mail_auth_http_module.c
+++ b/src/mail/ngx_mail_auth_http_module.c
@@ -40,7 +40,6 @@ struct ngx_mail_auth_http_ctx_s {
     ngx_mail_auth_http_handler_pt   handler;
 
     ngx_uint_t                      state;
-    ngx_uint_t                      hash;   /* no needed ? */
 
     u_char                         *header_name_start;
     u_char                         *header_name_end;
@@ -269,7 +268,7 @@ ngx_mail_auth_http_write_handler(ngx_eve
                 ngx_del_timer(wev);
             }
 
-            if (ngx_handle_write_event(wev, 0) == NGX_ERROR) {
+            if (ngx_handle_write_event(wev, 0) != NGX_OK) {
                 ngx_close_connection(c);
                 ngx_destroy_pool(ctx->pool);
                 ngx_mail_session_internal_server_error(s);
@@ -895,7 +894,7 @@ ngx_mail_auth_sleep_handler(ngx_event_t 
             return;
         }
 
-        if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+        if (ngx_handle_read_event(rev, 0) != NGX_OK) {
             ngx_mail_close_connection(c);
         }
 
@@ -903,7 +902,7 @@ ngx_mail_auth_sleep_handler(ngx_event_t 
     }
 
     if (rev->active) {
-        if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+        if (ngx_handle_read_event(rev, 0) != NGX_OK) {
             ngx_mail_close_connection(c);
         }
     }
@@ -915,7 +914,6 @@ ngx_mail_auth_http_parse_header_line(ngx
     ngx_mail_auth_http_ctx_t *ctx)
 {
     u_char      c, ch, *p;
-    ngx_uint_t  hash;
     enum {
         sw_start = 0,
         sw_name,
@@ -927,7 +925,6 @@ ngx_mail_auth_http_parse_header_line(ngx
     } state;
 
     state = ctx->state;
-    hash = ctx->hash;
 
     for (p = ctx->response->pos; p < ctx->response->last; p++) {
         ch = *p;
@@ -951,12 +948,10 @@ ngx_mail_auth_http_parse_header_line(ngx
 
                 c = (u_char) (ch | 0x20);
                 if (c >= 'a' && c <= 'z') {
-                    hash = c;
                     break;
                 }
 
                 if (ch >= '0' && ch <= '9') {
-                    hash = ch;
                     break;
                 }
 
@@ -968,7 +963,6 @@ ngx_mail_auth_http_parse_header_line(ngx
         case sw_name:
             c = (u_char) (ch | 0x20);
             if (c >= 'a' && c <= 'z') {
-                hash += c;
                 break;
             }
 
@@ -979,12 +973,10 @@ ngx_mail_auth_http_parse_header_line(ngx
             }
 
             if (ch == '-') {
-                hash += ch;
                 break;
             }
 
             if (ch >= '0' && ch <= '9') {
-                hash += ch;
                 break;
             }
 
@@ -1081,7 +1073,6 @@ ngx_mail_auth_http_parse_header_line(ngx
 
     ctx->response->pos = p;
     ctx->state = state;
-    ctx->hash = hash;
 
     return NGX_AGAIN;
 
@@ -1089,7 +1080,6 @@ done:
 
     ctx->response->pos = p + 1;
     ctx->state = sw_start;
-    ctx->hash = hash;
 
     return NGX_OK;
 
@@ -1112,7 +1102,7 @@ ngx_mail_auth_http_block_read(ngx_event_
     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
                    "mail auth http block read");
 
-    if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(rev, 0) != NGX_OK) {
         c = rev->data;
         s = c->data;
 
@@ -1166,6 +1156,7 @@ ngx_mail_auth_http_create_request(ngx_ma
                 + sizeof(CRLF) - 1
           + sizeof("Client-IP: ") - 1 + s->connection->addr_text.len
                 + sizeof(CRLF) - 1
+          + sizeof("Client-Host: ") - 1 + s->host.len + sizeof(CRLF) - 1
           + sizeof("Auth-SMTP-Helo: ") - 1 + s->smtp_helo.len
           + sizeof("Auth-SMTP-From: ") - 1 + s->smtp_from.len
           + sizeof("Auth-SMTP-To: ") - 1 + s->smtp_to.len
@@ -1220,12 +1211,19 @@ ngx_mail_auth_http_create_request(ngx_ma
 
     b->last = ngx_cpymem(b->last, "Client-IP: ", sizeof("Client-IP: ") - 1);
     b->last = ngx_copy(b->last, s->connection->addr_text.data,
-                         s->connection->addr_text.len);
+                       s->connection->addr_text.len);
     *b->last++ = CR; *b->last++ = LF;
 
+    if (s->host.len) {
+        b->last = ngx_cpymem(b->last, "Client-Host: ",
+                             sizeof("Client-Host: ") - 1);
+        b->last = ngx_copy(b->last, s->host.data, s->host.len);
+        *b->last++ = CR; *b->last++ = LF;
+    }
+
     if (s->auth_method == NGX_MAIL_AUTH_NONE) {
 
-        /* HELO / MAIL FROM / RCPT TO can't contain CRLF, no need to escape */
+        /* HELO, MAIL FROM, and RCPT TO can't contain CRLF, no need to escape */
 
         b->last = ngx_cpymem(b->last, "Auth-SMTP-Helo: ",
                              sizeof("Auth-SMTP-Helo: ") - 1);
--- a/src/mail/ngx_mail_handler.c
+++ b/src/mail/ngx_mail_handler.c
@@ -513,7 +513,7 @@ ngx_mail_send(ngx_event_t *wev)
     }
 
     if (s->out.len == 0) {
-        if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) {
+        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
             ngx_mail_close_connection(c);
         }
 
@@ -552,7 +552,7 @@ ngx_mail_send(ngx_event_t *wev)
 
     ngx_add_timer(c->write, cscf->timeout);
 
-    if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) {
+    if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
         ngx_mail_close_connection(c);
         return;
     }
@@ -579,7 +579,7 @@ ngx_mail_read_command(ngx_mail_session_t
     }
 
     if (n == NGX_AGAIN) {
-        if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
             ngx_mail_session_internal_server_error(s);
             return NGX_ERROR;
         }
--- a/src/mail/ngx_mail_imap_handler.c
+++ b/src/mail/ngx_mail_imap_handler.c
@@ -46,7 +46,7 @@ ngx_mail_imap_init_session(ngx_mail_sess
 
     ngx_add_timer(c->read, cscf->timeout);
 
-    if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
         ngx_mail_close_connection(c);
     }
 
--- a/src/mail/ngx_mail_pop3_handler.c
+++ b/src/mail/ngx_mail_pop3_handler.c
@@ -67,7 +67,7 @@ ngx_mail_pop3_init_session(ngx_mail_sess
 
     ngx_add_timer(c->read, cscf->timeout);
 
-    if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
         ngx_mail_close_connection(c);
     }
 
--- a/src/mail/ngx_mail_proxy_module.c
+++ b/src/mail/ngx_mail_proxy_module.c
@@ -104,7 +104,7 @@ ngx_module_t  ngx_mail_proxy_module = {
 };
 
 
-static u_char  smtp_ok[] = "235 2.0.0 OK" CRLF;
+static u_char  smtp_ok[] = "250 2.0.0 OK" CRLF;
 
 
 void
@@ -201,7 +201,7 @@ ngx_mail_proxy_block_read(ngx_event_t *r
 
     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy block read");
 
-    if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(rev, 0) != NGX_OK) {
         c = rev->data;
         s = c->data;
 
@@ -465,6 +465,7 @@ ngx_mail_proxy_smtp_handler(ngx_event_t 
     u_char                    *p;
     ngx_int_t                  rc;
     ngx_str_t                  line;
+    ngx_buf_t                 *b;
     ngx_connection_t          *c;
     ngx_mail_session_t        *s;
     ngx_mail_proxy_conf_t     *pcf;
@@ -520,9 +521,15 @@ ngx_mail_proxy_smtp_handler(ngx_event_t 
         p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len);
         *p++ = CR; *p = LF;
 
-        s->mail_state = pcf->xclient ? ngx_smtp_helo_xclient :
-                        s->auth_method == NGX_MAIL_AUTH_NONE ?
-                        ngx_smtp_helo_from : ngx_smtp_helo;
+        if (pcf->xclient) {
+            s->mail_state = ngx_smtp_helo_xclient;
+
+        } else if (s->auth_method == NGX_MAIL_AUTH_NONE) {
+            s->mail_state = ngx_smtp_helo_from;
+
+        } else {
+            s->mail_state = ngx_smtp_helo;
+        }
 
         break;
 
@@ -544,16 +551,15 @@ ngx_mail_proxy_smtp_handler(ngx_event_t 
         }
 
         line.len = ngx_sprintf(line.data,
-                       "XCLIENT PROTO=%sSMTP%s%V ADDR=%V%s%V "
-                       "NAME=%V" CRLF,
-                       (s->esmtp ? "E" : ""), 
+                       "XCLIENT PROTO=%sSMTP%s%V ADDR=%V%s%V NAME=%V" CRLF,
+                       (s->esmtp ? "E" : ""),
                        (s->smtp_helo.len ? " HELO=" : ""), &s->smtp_helo,
                        &s->connection->addr_text,
                        (s->login.len ? " LOGIN=" : ""), &s->login, &s->host)
                    - line.data;
 
-        s->mail_state = s->auth_method == NGX_MAIL_AUTH_NONE ?
-                        ngx_smtp_xclient_from : ngx_smtp_xclient;
+        s->mail_state = (s->auth_method == NGX_MAIL_AUTH_NONE) ?
+                            ngx_smtp_xclient_from : ngx_smtp_xclient;
 
         break;
 
@@ -602,15 +608,16 @@ ngx_mail_proxy_smtp_handler(ngx_event_t 
     case ngx_smtp_xclient:
     case ngx_smtp_to:
 
-        if (s->auth_method != NGX_MAIL_AUTH_NONE) {
-            ngx_memcpy(s->proxy->buffer->start, smtp_ok,
-                       sizeof(smtp_ok) - 1);
-            s->proxy->buffer->last = s->proxy->buffer->start
-                + sizeof(smtp_ok) - 1;
-            s->proxy->buffer->pos = s->proxy->buffer->start;
+        b = s->proxy->buffer;
+
+        if (s->auth_method == NGX_MAIL_AUTH_NONE) {
+            b->pos = b->start;
+
+        } else {
+            ngx_memcpy(b->start, smtp_ok, sizeof(smtp_ok) - 1);
+            b->last = b->start + sizeof(smtp_ok) - 1;
         }
 
-
         s->connection->read->handler = ngx_mail_proxy_handler;
         s->connection->write->handler = ngx_mail_proxy_handler;
         rev->handler = ngx_mail_proxy_handler;
@@ -662,7 +669,7 @@ ngx_mail_proxy_dummy_handler(ngx_event_t
 
     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, wev->log, 0, "mail proxy dummy handler");
 
-    if (ngx_handle_write_event(wev, 0) == NGX_ERROR) {
+    if (ngx_handle_write_event(wev, 0) != NGX_OK) {
         c = wev->data;
         s = c->data;
 
@@ -753,20 +760,17 @@ ngx_mail_proxy_read_response(ngx_mail_se
     default: /* NGX_MAIL_SMTP_PROTOCOL */
         switch (state) {
 
-        case ngx_smtp_to:
-            return NGX_OK;
-
-        case ngx_smtp_helo:
-        case ngx_smtp_helo_from:
-        case ngx_smtp_helo_xclient:
-        case ngx_smtp_from:
-            if (p[0] == '2' && p[1] == '5' && p[2] == '0') {
+        case ngx_smtp_start:
+            if (p[0] == '2' && p[1] == '2' && p[2] == '0') {
                 return NGX_OK;
             }
             break;
 
-        case ngx_smtp_start:
-            if (p[0] == '2' && p[1] == '2' && p[2] == '0') {
+        case ngx_smtp_helo:
+        case ngx_smtp_helo_xclient:
+        case ngx_smtp_helo_from:
+        case ngx_smtp_from:
+            if (p[0] == '2' && p[1] == '5' && p[2] == '0') {
                 return NGX_OK;
             }
             break;
@@ -777,6 +781,9 @@ ngx_mail_proxy_read_response(ngx_mail_se
                 return NGX_OK;
             }
             break;
+
+        case ngx_smtp_to:
+            return NGX_OK;
         }
 
         break;
@@ -945,22 +952,22 @@ ngx_mail_proxy_handler(ngx_event_t *ev)
         return;
     }
 
-    if (ngx_handle_write_event(dst->write, 0) == NGX_ERROR) {
+    if (ngx_handle_write_event(dst->write, 0) != NGX_OK) {
         ngx_mail_proxy_close_session(s);
         return;
     }
 
-    if (ngx_handle_read_event(dst->read, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(dst->read, 0) != NGX_OK) {
         ngx_mail_proxy_close_session(s);
         return;
     }
 
-    if (ngx_handle_write_event(src->write, 0) == NGX_ERROR) {
+    if (ngx_handle_write_event(src->write, 0) != NGX_OK) {
         ngx_mail_proxy_close_session(s);
         return;
     }
 
-    if (ngx_handle_read_event(src->read, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(src->read, 0) != NGX_OK) {
         ngx_mail_proxy_close_session(s);
         return;
     }
--- a/src/mail/ngx_mail_smtp_handler.c
+++ b/src/mail/ngx_mail_smtp_handler.c
@@ -228,7 +228,7 @@ ngx_mail_smtp_greeting(ngx_mail_session_
     timeout = sscf->greeting_delay ? sscf->greeting_delay : cscf->timeout;
     ngx_add_timer(c->read, timeout);
 
-    if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
         ngx_mail_close_connection(c);
     }
 
@@ -270,7 +270,7 @@ ngx_mail_smtp_invalid_pipelining(ngx_eve
 
         ngx_add_timer(c->read, cscf->timeout);
 
-        if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
             ngx_mail_close_connection(c);
             return;
         }
@@ -647,8 +647,7 @@ ngx_mail_smtp_mail(ngx_mail_session_t *s
     sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);
 
     if (!(sscf->auth_methods & NGX_MAIL_AUTH_NONE_ENABLED)) {
-        ngx_mail_smtp_log_rejected_command(s, c,
-                                           "client was rejected: \"%V\"");
+        ngx_mail_smtp_log_rejected_command(s, c, "client was rejected: \"%V\"");
 
         s->out.len = sizeof(smtp_auth_required) - 1;
         s->out.data = smtp_auth_required;
@@ -672,13 +671,16 @@ ngx_mail_smtp_mail(ngx_mail_session_t *s
 
     s->smtp_from.len = cmd.len;
 
-    s->smtp_from.data = ngx_palloc(c->pool, cmd.len);
+    s->smtp_from.data = ngx_pnalloc(c->pool, cmd.len);
     if (s->smtp_from.data == NULL) {
         return NGX_ERROR;
     }
 
     ngx_memcpy(s->smtp_from.data, cmd.data, cmd.len);
 
+    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+                   "smtp mail from:\"%V\"", &s->smtp_from);
+
     s->out.len = sizeof(smtp_ok) - 1;
     s->out.data = smtp_ok;
 
@@ -705,13 +707,16 @@ ngx_mail_smtp_rcpt(ngx_mail_session_t *s
 
     s->smtp_to.len = cmd.len;
 
-    s->smtp_to.data = ngx_palloc(c->pool, cmd.len);
+    s->smtp_to.data = ngx_pnalloc(c->pool, cmd.len);
     if (s->smtp_to.data == NULL) {
         return NGX_ERROR;
     }
 
     ngx_memcpy(s->smtp_to.data, cmd.data, cmd.len);
 
+    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+                   "smtp rcpt to:\"%V\"", &s->smtp_to);
+
     s->auth_method = NGX_MAIL_AUTH_NONE;
 
     return NGX_DONE;
@@ -784,7 +789,7 @@ ngx_mail_smtp_discard_command(ngx_mail_s
     }
 
     if (n == NGX_AGAIN) {
-        if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
             ngx_mail_session_internal_server_error(s);
             return NGX_ERROR;
         }
--- a/src/mail/ngx_mail_smtp_module.c
+++ b/src/mail/ngx_mail_smtp_module.c
@@ -141,7 +141,7 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t 
     u_char                    *p, *auth, *last;
     size_t                     size;
     ngx_str_t                 *c;
-    ngx_uint_t                 i, m, smtp_auth_enabled;
+    ngx_uint_t                 i, m, auth_enabled;
     ngx_mail_core_srv_conf_t  *cscf;
 
     ngx_conf_merge_size_value(conf->client_buffer_size,
@@ -201,18 +201,19 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t 
         size += sizeof("250 ") - 1 + c[i].len + sizeof(CRLF) - 1;
     }
 
-    smtp_auth_enabled = 0;
+    auth_enabled = 0;
+
     for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
          m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED;
          m <<= 1, i++)
     {
         if (m & conf->auth_methods) {
             size += 1 + ngx_mail_smtp_auth_methods_names[i].len;
-            smtp_auth_enabled = 1;
+            auth_enabled = 1;
         }
     }
 
-    if (smtp_auth_enabled) {
+    if (auth_enabled) {
         size += sizeof("250 AUTH") - 1 + sizeof(CRLF) - 1;
     }
 
@@ -225,6 +226,7 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t 
     conf->capability.data = p;
 
     last = p;
+
     *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = '-';
     p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len);
     *p++ = CR; *p++ = LF;
@@ -238,7 +240,7 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t 
 
     auth = p;
 
-    if (smtp_auth_enabled) {
+    if (auth_enabled) {
         last = p;
 
         *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' ';
@@ -271,8 +273,7 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t 
     conf->starttls_capability.len = size;
     conf->starttls_capability.data = p;
 
-    p = ngx_cpymem(p, conf->capability.data,
-                   conf->capability.len);
+    p = ngx_cpymem(p, conf->capability.data, conf->capability.len);
 
     p = ngx_cpymem(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1);
     *p++ = CR; *p = LF;
@@ -292,8 +293,7 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t 
     conf->starttls_only_capability.len = size;
     conf->starttls_only_capability.data = p;
 
-    p = ngx_cpymem(p, conf->capability.data,
-                   auth - conf->capability.data);
+    p = ngx_cpymem(p, conf->capability.data, auth - conf->capability.data);
 
     ngx_memcpy(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1);
 
--- a/src/os/unix/ngx_darwin_init.c
+++ b/src/os/unix/ngx_darwin_init.c
@@ -64,32 +64,44 @@ ngx_os_specific_init(ngx_log_t *log)
     ngx_uint_t  i;
 
     size = sizeof(ngx_darwin_kern_ostype);
-    if (sysctlbyname("kern.ostype",
-                     ngx_darwin_kern_ostype, &size, NULL, 0) == -1) {
-        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
-                      "sysctlbyname(kern.ostype) failed");
+    if (sysctlbyname("kern.ostype", ngx_darwin_kern_ostype, &size, NULL, 0)
+        == -1)
+    {
+        err = ngx_errno;
+
+        if (err != NGX_ENOENT) {
 
-        if (ngx_errno != NGX_ENOMEM) {
-            return NGX_ERROR;
+            ngx_log_error(NGX_LOG_ALERT, log, err,
+                          "sysctlbyname(kern.ostype) failed");
+
+            if (err != NGX_ENOMEM) {
+                return NGX_ERROR;
+            }
+
+            ngx_darwin_kern_ostype[size - 1] = '\0';
         }
-
-        ngx_darwin_kern_ostype[size - 1] = '\0';
     }
 
     size = sizeof(ngx_darwin_kern_osrelease);
-    if (sysctlbyname("kern.osrelease",
-                     ngx_darwin_kern_osrelease, &size, NULL, 0) == -1) {
-        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
-                      "sysctlbyname(kern.osrelease) failed");
+    if (sysctlbyname("kern.osrelease", ngx_darwin_kern_osrelease, &size,
+                     NULL, 0)
+        == -1)
+    {
+        err = ngx_errno;
+
+        if (err != NGX_ENOENT) {
 
-        if (ngx_errno != NGX_ENOMEM) {
-            return NGX_ERROR;
-        }
+            ngx_log_error(NGX_LOG_ALERT, log, err,
+                          "sysctlbyname(kern.osrelease) failed");
 
-        ngx_darwin_kern_osrelease[size - 1] = '\0';
+            if (err != NGX_ENOMEM) {
+                return NGX_ERROR;
+            }
+
+            ngx_darwin_kern_osrelease[size - 1] = '\0';
+        }
     }
 
-
     for (i = 0; sysctls[i].name; i++) {
         size = sysctls[i].size;
 
@@ -136,8 +148,10 @@ ngx_os_specific_status(ngx_log_t *log)
     u_long      value;
     ngx_uint_t  i;
 
-    ngx_log_error(NGX_LOG_NOTICE, log, 0, "OS: %s %s",
-                  ngx_darwin_kern_ostype, ngx_darwin_kern_osrelease);
+    if (ngx_darwin_kern_ostype[0]) {
+        ngx_log_error(NGX_LOG_NOTICE, log, 0, "OS: %s %s",
+                      ngx_darwin_kern_ostype, ngx_darwin_kern_osrelease);
+    }
 
     for (i = 0; sysctls[i].name; i++) {
         if (sysctls[i].exists) {
--- a/src/os/unix/ngx_errno.c
+++ b/src/os/unix/ngx_errno.c
@@ -10,10 +10,11 @@
 
 #if (NGX_HAVE_STRERROR_R)
 
-u_char *ngx_strerror_r(int err, u_char *errstr, size_t size)
+u_char *
+ngx_strerror_r(int err, u_char *errstr, size_t size)
 {
     if (size == 0) {
-        return 0;
+        return errstr;
     }
 
     errstr[0] = '\0';
@@ -32,12 +33,13 @@ u_char *ngx_strerror_r(int err, u_char *
 
 /* Linux strerror_r() */
 
-u_char *ngx_strerror_r(int err, u_char *errstr, size_t size)
+u_char *
+ngx_strerror_r(int err, u_char *errstr, size_t size)
 {
     char  *str;
 
     if (size == 0) {
-        return 0;
+        return errstr;
     }
 
     errstr[0] = '\0';
--- a/src/os/unix/ngx_errno.h
+++ b/src/os/unix/ngx_errno.h
@@ -23,6 +23,7 @@ typedef int               ngx_err_t;
 #define NGX_EACCES        EACCES
 #define NGX_EBUSY         EBUSY
 #define NGX_EEXIST        EEXIST
+#define NGX_EXDEV         EXDEV
 #define NGX_ENOTDIR       ENOTDIR
 #define NGX_EISDIR        EISDIR
 #define NGX_EINVAL        EINVAL
--- a/src/os/unix/ngx_files.h
+++ b/src/os/unix/ngx_files.h
@@ -99,7 +99,17 @@ ssize_t ngx_write_chain_to_file(ngx_file
 #define ngx_read_fd              read
 #define ngx_read_fd_n            "read()"
 
-#define ngx_write_fd             write
+/*
+ * we use inlined function instead of simple #define
+ * because glibc 2.3 sets warn_unused_result attribute for write()
+ * and in this case gcc 4.3 ignores (void) cast
+ */
+static ngx_inline ssize_t
+ngx_write_fd(ngx_fd_t fd, void *buf, size_t n)
+{
+    return write(fd, buf, n);
+}
+
 #define ngx_write_fd_n           "write()"
 
 #define ngx_linefeed(p)          *p++ = LF;
--- a/src/os/unix/ngx_freebsd.h
+++ b/src/os/unix/ngx_freebsd.h
@@ -14,7 +14,6 @@ ngx_chain_t *ngx_freebsd_sendfile_chain(
 extern int         ngx_freebsd_kern_osreldate;
 extern int         ngx_freebsd_hw_ncpu;
 extern u_long      ngx_freebsd_net_inet_tcp_sendspace;
-extern int         ngx_freebsd_kern_ipc_zero_copy_send;
 
 extern ngx_uint_t  ngx_freebsd_sendfile_nbytes_bug;
 extern ngx_uint_t  ngx_freebsd_use_tcp_nopush;
--- a/src/os/unix/ngx_freebsd_init.c
+++ b/src/os/unix/ngx_freebsd_init.c
@@ -19,9 +19,6 @@ u_long  ngx_freebsd_net_inet_tcp_sendspa
 /* FreeBSD 4.9 */
 int     ngx_freebsd_machdep_hlt_logical_cpus;
 
-/* FreeBSD 5.0 */
-int     ngx_freebsd_kern_ipc_zero_copy_send;
-
 
 ngx_uint_t  ngx_freebsd_sendfile_nbytes_bug;
 ngx_uint_t  ngx_freebsd_use_tcp_nopush;
@@ -68,10 +65,6 @@ sysctl_t sysctls[] = {
       &ngx_freebsd_kern_ipc_somaxconn,
       sizeof(ngx_freebsd_kern_ipc_somaxconn), 0 },
 
-    { "kern.ipc.zero_copy.send",
-      &ngx_freebsd_kern_ipc_zero_copy_send,
-      sizeof(ngx_freebsd_kern_ipc_zero_copy_send), 0 },
-
     { NULL, NULL, 0, 0 }
 };
 
--- a/src/os/unix/ngx_os.h
+++ b/src/os/unix/ngx_os.h
@@ -13,7 +13,6 @@
 
 
 #define NGX_IO_SENDFILE    1
-#define NGX_IO_ZEROCOPY    2
 
 
 typedef ssize_t (*ngx_recv_pt)(ngx_connection_t *c, u_char *buf, size_t size);