changeset 408:4243eecbd594

Merge with nginx 0.6.11.
author Maxim Dounin <mdounin@mdounin.ru>
date Mon, 11 Aug 2008 03:17:24 +0400
parents fa809001e681 (current diff) 3f511163d908 (diff)
children 52b28d322d76
files .hgtags src/mail/ngx_mail_auth_http_module.c src/mail/ngx_mail_parse.c
diffstat 68 files changed, 2701 insertions(+), 794 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags
+++ b/.hgtags
@@ -164,3 +164,8 @@ f9e6413396d47def780cf9600bbe35b35ce09e6e
 98c752b41cbc0f0ecaafced85d906effa27e8fc9 PATCH_NGINX_MAIL_0_3
 05c02cbce7be96d0b5e5b9cc581f7752f8ea2e71 PATCH_NGINX_MAIL_0_4
 d16d691432c9044f8b26ffccbd6ae68c11d0cde9 NGINX_0_6_5
+f7cd062ee035392cb44b3ec340d31f94ce4d83de NGINX_0_6_6
+9fc4ab6673f96b0d3eaefd820b6c2520dd8f103d NGINX_0_6_7
+390b8f8309d6eb6771fec2467145bb42b6b71868 NGINX_0_6_8
+5e3b425174f6aea903cfd1b7b5eb8d21bb780847 NGINX_0_6_9
+3a91bfeffabaa2cd1a89dab846803bc595d79460 NGINX_0_6_10
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,94 @@
 
+Changes with nginx 0.6.10                                        03 Sep 2007
+
+    *) Feature: the "open_file_cache", "open_file_cache_retest", and 
+       "open_file_cache_errors" directives.
+
+    *) Bugfix: socket leak; bug appeared in 0.6.7.
+
+    *) Bugfix: a charset set by the "charset" directive was not appended to 
+       the "Content-Type" header set by $r->send_http_header().
+
+    *) Bugfix: a segmentation fault might occur in worker process if 
+       /dev/poll method was used.
+
+
+Changes with nginx 0.6.9                                         28 Aug 2007
+
+    *) Bugfix: a worker process may got caught in an endless loop, if the 
+       HTTPS protocol was used; bug appeared in 0.6.7.
+
+    *) Bugfix: if server listened on two addresses or ports and trailing 
+       wildcard was used, then nginx did not run.
+
+    *) Bugfix: the "ip_hash" directive might incorrectly mark servers as 
+       down.
+
+    *) Bugfix: nginx could not be built on amd64; bug appeared in 0.6.8.
+
+
+Changes with nginx 0.6.8                                         20 Aug 2007
+
+    *) Change: now nginx tries to set the "worker_priority", 
+       "worker_rlimit_nofile", "worker_rlimit_core", and 
+       "worker_rlimit_sigpending" without super-user privileges.
+
+    *) Change: now nginx escapes space and "%" in request to a mail proxy 
+       authentication server.
+
+    *) Change: now nginx escapes "%" in $memcached_key variable.
+
+    *) Bugfix: nginx used path relative to configuration prefix for 
+       non-absolute configuration file path specified in the "-c" key; bug 
+       appeared in 0.6.6.
+
+    *) Bugfix: nginx did not work on FreeBSD/sparc64.
+
+
+Changes with nginx 0.6.7                                         15 Aug 2007
+
+    *) Change: now the paths specified in the "include", 
+       "auth_basic_user_file", "perl_modules", "ssl_certificate", 
+       "ssl_certificate_key", and "ssl_client_certificate" directives are 
+       relative to directory of nginx configuration file nginx.conf, but 
+       not to nginx prefix directory.
+
+    *) Change: the --sysconfdir=PATH option in configure was canceled.
+
+    *) Change: the special make target "upgrade1" was defined for online 
+       upgrade of 0.1.x versions.
+
+    *) Feature: the "server_name" and "valid_referers" directives support 
+       regular expressions.
+
+    *) Feature: the "server" directive in the "upstream" context supports 
+       the "backup" parameter.
+
+    *) Feature: the ngx_http_perl_module supports the 
+       $r->discard_request_body.
+
+    *) Feature: the "add_header Last-Modified ..." directive changes the 
+       "Last-Modified" response header line.
+
+    *) Bugfix: if an response different than 200 was returned to an request 
+       with body and connection went to the keep-alive state after the 
+       request, then nginx returned 400 for the next request.
+
+    *) Bugfix: a segmentation fault occurred in worker process if invalid 
+       address was set in the "auth_http" directive.
+
+    *) Bugfix: now nginx uses default listen backlog value 511 on all 
+       platforms except FreeBSD.
+       Thanks to Jiang Hong.
+
+    *) Bugfix: a worker process may got caught in an endless loop, if an 
+       "server" inside "upstream" block was marked as "down"; bug appeared 
+       in 0.6.6.
+
+    *) Bugfix: now Solaris sendfilev() is not used to transfer the client 
+       request body to FastCGI-server via the unix domain socket.
+
+
 Changes with nginx 0.6.6                                         30 Jul 2007
 
     *) Feature: the --sysconfdir=PATH option in configure.
@@ -106,7 +196,7 @@ Changes with nginx 0.6.1                
 Changes with nginx 0.6.0                                         14 Jun 2007
 
     *) Feature: the "server_name", "map", and "valid_referers" directives 
-       supports the "www.example.*" wildcards.
+       support the "www.example.*" wildcards.
 
 
 Changes with nginx 0.5.25                                        11 Jun 2007
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,95 @@
 
+Изменения в nginx 0.6.10                                          03.09.2007
+
+    *) Добавление: директивы open_file_cache, open_file_cache_retest и 
+       open_file_cache_errors.
+
+    *) Исправление: утечка сокетов; ошибка появилась в 0.6.7.
+
+    *) Исправление: В строку заголовка ответа "Content-Type", указанную в 
+       методе $r->send_http_header(), не добавлялась кодировка, указанная в 
+       директиве charset.
+
+    *) Исправление: при использовании метода /dev/poll в рабочем процессе 
+       мог произойти segmentation fault.
+
+
+Изменения в nginx 0.6.9                                           28.08.2007
+
+    *) Исправление: рабочий процесс мог зациклиться при использовании 
+       протокола HTTPS; ошибка появилась в 0.6.7.
+
+    *) Исправление: если сервер слушал на двух адресах или портах, то nginx 
+       не запускался при использовании wildcard в конце имени сервера.
+
+    *) Исправление: директива ip_hash могла неверно помечать сервера как 
+       нерабочие.
+
+    *) Исправление: nginx не собирался на amd64; ошибка появилась в 0.6.8.
+
+
+Изменения в nginx 0.6.8                                           20.08.2007
+
+    *) Изменение: теперь nginx пытается установить директивы 
+       worker_priority, worker_rlimit_nofile, worker_rlimit_core, 
+       worker_rlimit_sigpending без привилегий root'а.
+
+    *) Изменение: теперь nginx экранирует символы пробела и "%" при 
+       передаче запроса серверу аутентификации почтового прокси-сервера.
+
+    *) Изменение: теперь nginx экранирует символ "%" в переменной 
+       $memcached_key.
+
+    *) Исправление: при указании относительного пути к конфигурационному 
+       файлу в качестве параметра ключа -c nginx определял путь 
+       относительно конфигурационного префикса; ошибка появилась в 0.6.6.
+
+    *) Исправление: nginx не работал на FreeBSD/sparc64.
+
+
+Изменения в nginx 0.6.7                                           15.08.2007
+
+    *) Изменение: теперь пути, указанные в директивах include, 
+       auth_basic_user_file, perl_modules, ssl_certificate, 
+       ssl_certificate_key и ssl_client_certificate, определяются 
+       относительно каталога конфигурационного файла nginx.conf, а не 
+       относительно префикса.
+
+    *) Изменение: параметр --sysconfdir=PATH в configure упразднён.
+
+    *) Изменение: для обновления на лету версий 0.1.x создан специальный 
+       сценарий make upgrade1.
+
+    *) Добавление: директивы server_name и valid_referers поддерживают 
+       регулярные выражения.
+
+    *) Добавление: директива server в блоке upstream поддерживает параметр 
+       backup.
+
+    *) Добавление: модуль ngx_http_perl_module поддерживает метод 
+       $r->discard_request_body.
+
+    *) Добавление: директива "add_header Last-Modified ..." меняет строку 
+       "Last-Modified" в заголовке ответа.
+
+    *) Исправление: если на запрос с телом возвращался ответ с кодом HTTP 
+       отличным от 200, и после этого запроса соединение переходило в 
+       состояние keep-alive, то на следующий запрос nginx возвращал 400.
+
+    *) Исправление: если в директиве auth_http был задан неправильный 
+       адрес, то в рабочем процессе происходил segmentation fault.
+
+    *) Исправление: теперь по умолчанию nginx использует значение 511 для 
+       listen backlog на всех платформах, кроме FreeBSD.
+       Спасибо Jiang Hong.
+
+    *) Исправление: рабочий процесс мог зациклиться, если server в блоке 
+       upstream был помечен как down; ошибка появилась в 0.6.6.
+
+    *) Исправление: sendfilev() в Solaris теперь не используется при 
+       передаче тела запроса FastCGI-серверу через unix domain сокет.
+
+
 Изменения в nginx 0.6.6                                           30.07.2007
 
     *) Добавление: параметр --sysconfdir=PATH в configure.
--- a/auto/cc/sunc
+++ b/auto/cc/sunc
@@ -155,3 +155,6 @@ fi
 
 # stop on warning
 CFLAGS="$CFLAGS -errwarn=%all"
+
+# debug
+CFLAGS="$CFLAGS -g"
--- a/auto/init
+++ b/auto/init
@@ -55,15 +55,24 @@ clean:
 upgrade:
 	$NGX_SBIN_PATH -t
 
-	# upgrade compatibility from 0.1.x to 0.2.x
+	kill -USR2 \`cat $NGX_PID_PATH\`
+	sleep 1
+	test -f $NGX_PID_PATH.oldbin
+
+	kill -QUIT \`cat $NGX_PID_PATH.oldbin\`
+
+upgrade1:
+	# upgrade 0.1.x to 0.2+
+
+	$NGX_SBIN_PATH -t
+
 	cp $NGX_PID_PATH $NGX_PID_PATH.oldbin
 
 	kill -USR2 \`cat $NGX_PID_PATH\`
 	sleep 1
 	test -f $NGX_PID_PATH.oldbin
 
-	# upgrade compatibility from 0.1.x to 0.2.x
 	cp $NGX_PID_PATH $NGX_PID_PATH.newbin
 
-	kill -WINCH \`cat $NGX_PID_PATH.oldbin\`
+	kill -QUIT \`cat $NGX_PID_PATH.oldbin\`
 END
--- a/auto/install
+++ b/auto/install
@@ -34,11 +34,11 @@ install:	$NGX_OBJS${ngx_dirsep}nginx${ng
 
 	test -f '$NGX_CONF_PREFIX/mime.types' \
 		|| cp conf/mime.types '$NGX_CONF_PREFIX'
-	cp conf/mime.types '$NGX_CONF_PATH/mime.types.default'
+	cp conf/mime.types '$NGX_CONF_PREFIX/mime.types.default'
 
 	test -f '$NGX_CONF_PREFIX/fastcgi_params' \
 		|| cp conf/fastcgi_params '$NGX_CONF_PREFIX'
-	cp conf/fastcgi_params '$NGX_CONF_PATH/fastcgi_params.default'
+	cp conf/fastcgi_params '$NGX_CONF_PREFIX/fastcgi_params.default'
 
 	test -f '$NGX_CONF_PATH' || cp conf/nginx.conf '$NGX_CONF_PREFIX'
 	cp conf/nginx.conf '$NGX_CONF_PREFIX/nginx.conf.default'
--- a/auto/options
+++ b/auto/options
@@ -124,7 +124,6 @@ do
 
         --prefix=*)                      NGX_PREFIX="$value"        ;;
         --sbin-path=*)                   NGX_SBIN_PATH="$value"     ;;
-        --sysconfdir=*)                  NGX_CONF_PREFIX="$value"   ;;
         --conf-path=*)                   NGX_CONF_PATH="$value"     ;;
         --error-log-path=*)              NGX_ERROR_LOG_PATH="$value";;
         --pid-path=*)                    NGX_PID_PATH="$value"      ;;
@@ -242,7 +241,6 @@ cat << END
 
   --prefix=PATH                      set the installation prefix
   --sbin-path=PATH                   set path to the nginx binary file
-  --sysconfdir=PATH                  set the configuration prefix
   --conf-path=PATH                   set path to the nginx.conf file
   --error-log-path=PATH              set path to the error log
   --pid-path=PATH                    set path to nginx.pid file
@@ -363,7 +361,6 @@ fi
 
 
 NGX_PREFIX=${NGX_PREFIX:-/usr/local/nginx}
-NGX_CONF_PREFIX=${NGX_CONF_PREFIX:-$NGX_PREFIX}
 
 
 case ".$NGX_SBIN_PATH" in
@@ -385,15 +382,18 @@ case ".$NGX_CONF_PATH" in
     ;;
 
     .)
-        NGX_CONF_PATH=$NGX_CONF_PREFIX/conf/nginx.conf
+        NGX_CONF_PATH=$NGX_PREFIX/conf/nginx.conf
     ;;
 
     *)
-        NGX_CONF_PATH=$NGX_CONF_PREFIX/$NGX_CONF_PATH
+        NGX_CONF_PATH=$NGX_PREFIX/$NGX_CONF_PATH
     ;;
 esac
 
 
+NGX_CONF_PREFIX=`dirname $NGX_CONF_PATH`
+
+
 case ".$NGX_PID_PATH" in
     ./*)
     ;;
--- a/auto/sources
+++ b/auto/sources
@@ -29,6 +29,7 @@ CORE_DEPS="src/core/nginx.h \
            src/core/ngx_connection.h \
            src/core/ngx_cycle.h \
            src/core/ngx_conf_file.h \
+           src/core/ngx_open_file_cache.h \
            src/core/ngx_garbage_collector.h"
 
 
@@ -55,6 +56,7 @@ CORE_SRCS="src/core/nginx.c \
            src/core/ngx_spinlock.c \
            src/core/ngx_cpuinfo.c \
            src/core/ngx_conf_file.c \
+           src/core/ngx_open_file_cache.c \
            src/core/ngx_garbage_collector.c"
 
 
--- a/conf/mime.types
+++ b/conf/mime.types
@@ -20,6 +20,7 @@ types {
     image/x-icon                          ico;
     image/x-jng                           jng;
     image/x-ms-bmp                        bmp;
+    image/svg+xml                         svg;
 
     application/java-archive              jar war ear;
     application/mac-binhex40              hqx;
--- a/conf/nginx.conf
+++ b/conf/nginx.conf
@@ -15,7 +15,7 @@ events {
 
 
 http {
-    include       conf/mime.types;
+    include       mime.types;
     default_type  application/octet-stream;
 
     #log_format  main  '$remote_addr - $remote_user [$time_local] $request '
@@ -66,7 +66,7 @@ http {
         #    fastcgi_pass   127.0.0.1:9000;
         #    fastcgi_index  index.php;
         #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
-        #    include        conf/fastcgi_params;
+        #    include        fastcgi_params;
         #}
 
         # deny access to .htaccess files, if Apache's document root
--- a/src/core/nginx.c
+++ b/src/core/nginx.c
@@ -656,10 +656,6 @@ ngx_getopt(ngx_cycle_t *cycle, int argc,
         cycle->conf_file.data = (u_char *) NGX_CONF_PATH;
     }
 
-    if (ngx_conf_full_name(cycle, &cycle->conf_file, 1) == NGX_ERROR) {
-        return NGX_ERROR;
-    }
-
     return NGX_OK;
 }
 
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,7 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define NGINX_VERSION      "0.6.6"
+#define NGINX_VERSION      "0.6.11"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_conf_file.c
+++ b/src/core/ngx_conf_file.c
@@ -366,7 +366,7 @@ not_allowed:
 invalid:
 
     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                       "invalid number arguments in \"%s\" directive",
+                       "invalid number of arguments in \"%s\" directive",
                        name->data);
 
     return NGX_ERROR;
--- a/src/core/ngx_conf_file.h
+++ b/src/core/ngx_conf_file.h
@@ -257,8 +257,8 @@ char *ngx_conf_check_num_bounds(ngx_conf
     }
 
 #define ngx_conf_merge_ptr_value(conf, prev, default)                        \
-    if (conf == NULL) {                                                      \
-        conf = (prev == NULL) ? default : prev;                              \
+    if (conf == NGX_CONF_UNSET_PTR) {                                        \
+        conf = (prev == NGX_CONF_UNSET_PTR) ? default : prev;                \
     }
 
 #define ngx_conf_merge_uint_value(conf, prev, default)                       \
--- a/src/core/ngx_connection.c
+++ b/src/core/ngx_connection.c
@@ -123,7 +123,7 @@ ngx_set_inherited_sockets(ngx_cycle_t *c
                                           ntohs(sin->sin_port))
                               - ls[i].addr_text.data;
 
-        ls[i].backlog = -1;
+        ls[i].backlog = NGX_LISTEN_BACKLOG;
 
         olen = sizeof(int);
 
@@ -735,7 +735,7 @@ ngx_close_connection(ngx_connection_t *c
         /* we use ngx_cycle->log because c->log was in c->pool */
 
         ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno,
-                      ngx_close_socket_n " failed");
+                      ngx_close_socket_n " %d failed", fd);
     }
 }
 
--- a/src/core/ngx_core.h
+++ b/src/core/ngx_core.h
@@ -71,6 +71,7 @@ typedef void (*ngx_connection_handler_pt
 #endif
 #include <ngx_process_cycle.h>
 #include <ngx_conf_file.h>
+#include <ngx_open_file_cache.h>
 #include <ngx_os.h>
 #include <ngx_connection.h>
 
--- a/src/core/ngx_hash.c
+++ b/src/core/ngx_hash.c
@@ -889,10 +889,15 @@ wildcard:
 
         /* convert "www.example.*" to "www.example\0" */
 
-        p = key->data;
-        key->data[last] = '\0';
         last++;
 
+        p = ngx_palloc(ha->temp_pool, last);
+        if (p == NULL) {
+            return NGX_ERROR;
+        }
+
+        ngx_cpystrn(p, key->data, last - 1);
+
         hwc = &ha->dns_wc_tail;
         keys = &ha->dns_wc_tail_hash[k];
     }
--- a/src/core/ngx_inet.c
+++ b/src/core/ngx_inet.c
@@ -214,7 +214,13 @@ ngx_ptocidr(ngx_str_t *text, void *cidr)
 
     in_cidr->mask = htonl((ngx_uint_t) (0 - (1 << (32 - m))));
 
-    return NGX_OK;
+    if (in_cidr->addr == (in_cidr->addr & in_cidr->mask)) {
+        return NGX_OK;
+    }
+
+    in_cidr->addr &= in_cidr->mask;
+
+    return NGX_DONE;
 }
 
 
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_open_file_cache.c
@@ -0,0 +1,733 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+/*
+ * open file cache caches
+ *    open file handles with stat() info;
+ *    directories stat() info;
+ *    files and directories errors: not found, access denied, etc.
+ */
+
+
+static void ngx_open_file_cache_cleanup(void *data);
+static void ngx_open_file_cleanup(void *data);
+static void ngx_close_cached_file(ngx_open_file_cache_t *cache,
+    ngx_cached_open_file_t *file, ngx_log_t *log);
+static ngx_int_t ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of,
+    ngx_log_t *log);
+static void ngx_expire_old_cached_files(ngx_open_file_cache_t *cache,
+    ngx_uint_t n, ngx_log_t *log);
+static void ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
+    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
+static void ngx_open_file_cache_remove(ngx_event_t *ev);
+
+
+ngx_open_file_cache_t *
+ngx_open_file_cache_init(ngx_pool_t *pool, ngx_uint_t max, time_t inactive)
+{
+    ngx_rbtree_node_t      *sentinel;
+    ngx_pool_cleanup_t     *cln;
+    ngx_open_file_cache_t  *cache;
+
+    cache = ngx_palloc(pool, sizeof(ngx_open_file_cache_t));
+    if (cache == NULL) {
+        return NULL;
+    }
+
+    cache->list_head.prev = NULL;
+    cache->list_head.next = &cache->list_tail;
+
+    cache->list_tail.prev = &cache->list_head;
+    cache->list_tail.next = NULL;
+
+    sentinel = ngx_palloc(pool, sizeof(ngx_rbtree_node_t));
+    if (sentinel == NULL) {
+        return NULL;
+    }
+
+    ngx_rbtree_sentinel_init(sentinel);
+
+    cache->rbtree.root = sentinel;
+    cache->rbtree.sentinel = sentinel;
+    cache->rbtree.insert = ngx_open_file_cache_rbtree_insert_value;
+
+    cache->current = 0;
+    cache->max = max;
+    cache->inactive = inactive;
+
+    cln = ngx_pool_cleanup_add(pool, 0);
+    if (cln == NULL) {
+        return NULL;
+    }
+
+    cln->handler = ngx_open_file_cache_cleanup;
+    cln->data = cache;
+
+    return cache;
+}
+
+
+static void
+ngx_open_file_cache_cleanup(void *data)
+{
+    ngx_open_file_cache_t  *cache = data;
+
+    ngx_cached_open_file_t  *file;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
+                   "open file cache cleanup");
+
+    for ( ;; ) {
+
+        file = cache->list_tail.prev;
+
+        if (file == &cache->list_head) {
+            break;
+        }
+
+        file->next->prev = file->prev;
+        file->prev->next = file->next;
+
+        ngx_rbtree_delete(&cache->rbtree, &file->node);
+
+        cache->current--;
+
+        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
+                       "delete cached open file: %s", file->name);
+
+        if (!file->err && !file->is_dir) {
+            file->close = 1;
+            file->count = 0;
+            ngx_close_cached_file(cache, file, ngx_cycle->log);
+
+        } else {
+            ngx_free(file->name);
+            ngx_free(file);
+        }
+    }
+
+    if (cache->current) {
+        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
+                      "%d items still leave in open file cache",
+                      cache->current);
+    }
+
+    if (cache->rbtree.root != cache->rbtree.sentinel) {
+        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
+                      "rbtree still is not empty in open file cache");
+
+    }
+}
+
+
+ngx_int_t
+ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name,
+    ngx_open_file_info_t *of, ngx_pool_t *pool)
+{
+    time_t                          now;
+    uint32_t                        hash;
+    ngx_int_t                       rc;
+    ngx_rbtree_node_t              *node, *sentinel;
+    ngx_pool_cleanup_t             *cln;
+    ngx_cached_open_file_t         *file;
+    ngx_pool_cleanup_file_t        *clnf;
+    ngx_open_file_cache_event_t    *fev;
+    ngx_open_file_cache_cleanup_t  *ofcln;
+
+    of->err = 0;
+
+    if (cache == NULL) {
+
+        cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t));
+        if (cln == NULL) {
+            return NGX_ERROR;
+        }
+
+        rc = ngx_open_and_stat_file(name->data, of, pool->log);
+
+        if (rc == NGX_OK && !of->is_dir) {
+            cln->handler = ngx_pool_cleanup_file;
+            clnf = cln->data;
+
+            clnf->fd = of->fd;
+            clnf->name = name->data;
+            clnf->log = pool->log;
+        }
+
+        return rc;
+    }
+
+    cln = ngx_pool_cleanup_add(pool, sizeof(ngx_open_file_cache_cleanup_t));
+    if (cln == NULL) {
+        return NGX_ERROR;
+    }
+
+    hash = ngx_crc32_long(name->data, name->len);
+
+    node = cache->rbtree.root;
+    sentinel = cache->rbtree.sentinel;
+
+    now = ngx_time();
+
+    while (node != sentinel) {
+
+        if (hash < node->key) {
+            node = node->left;
+            continue;
+        }
+
+        if (hash > node->key) {
+            node = node->right;
+            continue;
+        }
+
+        /* hash == node->key */
+
+        do {
+            file = (ngx_cached_open_file_t *) node;
+
+            rc = ngx_strcmp(name->data, file->name);
+
+            if (rc == 0) {
+
+                file->next->prev = file->prev;
+                file->prev->next = file->next;
+
+                if (file->event || now - file->created < of->retest) {
+                    if (file->err == 0) {
+                        of->fd = file->fd;
+                        of->uniq = file->uniq;
+                        of->mtime = file->mtime;
+                        of->size = file->size;
+
+                        of->is_dir = file->is_dir;
+                        of->is_file = file->is_file;
+                        of->is_link = file->is_link;
+                        of->is_exec = file->is_exec;
+
+                        if (!file->is_dir) {
+                            file->count++;
+                        }
+
+                    } else {
+                        of->err = file->err;
+                    }
+
+                    goto found;
+                }
+
+                ngx_log_debug4(NGX_LOG_DEBUG_CORE, pool->log, 0,
+                               "retest open file: %s, fd:%d, c:%d, e:%d",
+                               file->name, file->fd, file->count, file->err);
+
+                if (file->is_dir) {
+
+                    /*
+                     * chances that directory became file are very small
+                     * so test_dir flag allows to use a single ngx_file_info()
+                     * syscall instead of three syscalls
+                     */
+
+                    of->test_dir = 1;
+                }
+
+                rc = ngx_open_and_stat_file(name->data, of, pool->log);
+
+                if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
+                    goto failed;
+                }
+
+                if (of->is_dir) {
+                    if (file->is_dir || file->err) {
+                        goto update;
+                    }
+
+                    /* file became directory */
+
+                } else if (of->err == 0) {  /* file */
+
+                    if (file->is_dir || file->err) {
+                        goto update;
+                    }
+
+                    if (of->uniq == file->uniq
+                        && of->mtime == file->mtime
+                        && of->size == file->size)
+                    {
+                        if (ngx_close_file(of->fd) == NGX_FILE_ERROR) {
+                            ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
+                                          ngx_close_file_n " \"%s\" failed",
+                                          name->data);
+                        }
+
+                        of->fd = file->fd;
+                        file->count++;
+
+                        goto renew;
+                    }
+
+                    /* file was changed */
+
+                } else { /* error to cache */
+
+                    if (file->err || file->is_dir) {
+                        goto update;
+                    }
+
+                    /* file was removed, etc. */
+                }
+
+                if (file->count == 0) {
+                    if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
+                        ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
+                                      ngx_close_file_n " \"%s\" failed",
+                                      name->data);
+                    }
+
+                    goto update;
+                }
+
+                ngx_rbtree_delete(&cache->rbtree, &file->node);
+
+                cache->current--;
+
+                file->close = 1;
+
+                goto create;
+            }
+
+            node = (rc < 0) ? node->left : node->right;
+
+        } while (node != sentinel && hash == node->key);
+
+        break;
+    }
+
+    /* not found */
+
+    file = NULL;
+
+    rc = ngx_open_and_stat_file(name->data, of, pool->log);
+
+    if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
+        goto failed;
+    }
+
+create:
+
+    if (cache->current >= cache->max) {
+        ngx_expire_old_cached_files(cache, 0, pool->log);
+    }
+
+    file = ngx_alloc(sizeof(ngx_cached_open_file_t), pool->log);
+
+    if (file == NULL) {
+        goto failed;
+    }
+
+    file->name = ngx_alloc(name->len + 1, pool->log);
+
+    if (file->name == NULL) {
+        ngx_free(file);
+        file = NULL;
+        goto failed;
+    }
+
+    ngx_cpystrn(file->name, name->data, name->len + 1);
+
+    file->node.key = hash;
+
+    ngx_rbtree_insert(&cache->rbtree, &file->node);
+
+    cache->current++;
+
+    file->count = 0;
+
+update:
+
+    if (of->events
+        && (ngx_event_flags & NGX_USE_VNODE_EVENT)
+        && of->fd != NGX_INVALID_FILE)
+    {
+        file->event = ngx_calloc(sizeof(ngx_event_t), pool->log);
+        if (file->event== NULL) {
+            goto failed;
+        }
+
+        fev = ngx_alloc(sizeof(ngx_open_file_cache_event_t), pool->log);
+        if (fev == NULL) {
+            goto failed;
+        }
+
+        fev->fd = of->fd;
+        fev->file = file;
+        fev->cache = cache;
+
+        file->event->handler = ngx_open_file_cache_remove;
+        file->event->data = fev;
+
+        /*
+         * although vnode event may be called while ngx_cycle->poll
+         * destruction; however, cleanup procedures are run before any
+         * memory freeing and events will be canceled.
+         */
+
+        file->event->log = ngx_cycle->log;
+
+        if (ngx_add_event(file->event, NGX_VNODE_EVENT, NGX_ONESHOT_EVENT)
+            != NGX_OK)
+        {
+            ngx_free(file->event->data);
+            ngx_free(file->event);
+            goto failed;
+        }
+
+    } else {
+        file->event = NULL;
+    }
+
+    file->fd = of->fd;
+    file->err = of->err;
+
+    if (of->err == 0) {
+        file->uniq = of->uniq;
+        file->mtime = of->mtime;
+        file->size = of->size;
+
+        file->close = 0;
+
+        file->is_dir = of->is_dir;
+        file->is_file = of->is_file;
+        file->is_link = of->is_link;
+        file->is_exec = of->is_exec;
+
+        if (!of->is_dir) {
+            file->count++;
+        }
+    }
+
+renew:
+
+    file->created = now;
+
+found:
+
+    file->accessed = now;
+
+    /* add to the inactive list head */
+
+    file->next = cache->list_head.next;
+    file->next->prev = file;
+    file->prev = &cache->list_head;
+    cache->list_head.next = file;
+
+    ngx_log_debug4(NGX_LOG_DEBUG_CORE, pool->log, 0,
+                   "cached open file: %s, fd:%d, c:%d, e:%d",
+                   file->name, file->fd, file->count, file->err);
+
+    if (of->err == 0) {
+
+        if (!of->is_dir) {
+            cln->handler = ngx_open_file_cleanup;
+            ofcln = cln->data;
+
+            ofcln->cache = cache;
+            ofcln->file = file;
+            ofcln->log = pool->log;
+        }
+
+        return NGX_OK;
+    }
+
+    return NGX_ERROR;
+
+failed:
+
+    if (file && file->count == 0) {
+        ngx_rbtree_delete(&cache->rbtree, &file->node);
+
+        cache->current--;
+
+        if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
+            ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
+                          ngx_close_file_n " \"%s\" failed", file->name);
+        }
+
+        ngx_free(file->name);
+        ngx_free(file);
+    }
+
+    if (of->fd != NGX_INVALID_FILE) {
+        if (ngx_close_file(of->fd) == NGX_FILE_ERROR) {
+            ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
+                          ngx_close_file_n " \"%s\" failed", name->data);
+        }
+    }
+
+    return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of, ngx_log_t *log)
+{
+    ngx_fd_t         fd;
+    ngx_file_info_t  fi;
+
+    of->fd = NGX_INVALID_FILE;
+
+    if (of->test_dir) {
+
+        if (ngx_file_info(name, &fi) == -1) {
+            of->err = ngx_errno;
+
+            return NGX_ERROR;
+        }
+
+        of->uniq = ngx_file_uniq(&fi);
+        of->mtime = ngx_file_mtime(&fi);
+        of->size = ngx_file_size(&fi);
+        of->is_dir = ngx_is_dir(&fi);
+        of->is_file = ngx_is_file(&fi);
+        of->is_link = ngx_is_link(&fi);
+        of->is_exec = ngx_is_exec(&fi);
+
+        if (of->is_dir) {
+            return NGX_OK;
+        }
+    }
+
+    fd = ngx_open_file(name, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
+
+    if (fd == NGX_INVALID_FILE) {
+        of->err = ngx_errno;
+        return NGX_ERROR;
+    }
+
+    if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
+        ngx_log_error(NGX_LOG_CRIT, log, ngx_errno,
+                      ngx_fd_info_n " \"%s\" failed", name);
+
+        if (ngx_close_file(fd) == NGX_FILE_ERROR) {
+            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+                          ngx_close_file_n " \"%s\" failed", name);
+        }
+
+        return NGX_ERROR;
+    }
+
+    if (ngx_is_dir(&fi)) {
+        if (ngx_close_file(fd) == NGX_FILE_ERROR) {
+            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+                          ngx_close_file_n " \"%s\" failed", name);
+        }
+
+        fd = NGX_INVALID_FILE;
+    }
+
+    of->fd = fd;
+    of->uniq = ngx_file_uniq(&fi);
+    of->mtime = ngx_file_mtime(&fi);
+    of->size = ngx_file_size(&fi);
+    of->is_dir = ngx_is_dir(&fi);
+    of->is_file = ngx_is_file(&fi);
+    of->is_link = ngx_is_link(&fi);
+    of->is_exec = ngx_is_exec(&fi);
+
+    return NGX_OK;
+}
+
+
+static void
+ngx_open_file_cleanup(void *data)
+{
+    ngx_open_file_cache_cleanup_t  *c = data;
+
+    c->file->count--;
+
+    ngx_close_cached_file(c->cache, c->file, c->log);
+
+    /* drop one or two expired open files */
+    ngx_expire_old_cached_files(c->cache, 1, c->log);
+}
+
+
+static void
+ngx_close_cached_file(ngx_open_file_cache_t *cache,
+    ngx_cached_open_file_t *file, ngx_log_t *log)
+{
+    ngx_log_debug4(NGX_LOG_DEBUG_CORE, log, 0,
+                   "close cached open file: %s, fd:%d, c:%d, %d",
+                   file->name, file->fd, file->count, file->close);
+
+    if (!file->close) {
+
+        file->accessed = ngx_time();
+
+        if (cache->list_head.next != file) {
+
+            /* delete from inactive list */
+
+            file->next->prev = file->prev;
+            file->prev->next = file->next;
+
+            /* add to the inactive list head */
+
+            file->next = cache->list_head.next;
+            file->next->prev = file;
+            file->prev = &cache->list_head;
+            cache->list_head.next = file;
+        }
+
+        return;
+    }
+
+    if (file->event) {
+        (void) ngx_del_event(file->event, NGX_VNODE_EVENT,
+                             file->count ? NGX_FLUSH_EVENT : NGX_CLOSE_EVENT);
+
+        ngx_free(file->event->data);
+        ngx_free(file->event);
+        file->event = NULL;
+    }
+
+    if (file->count) {
+        return;
+    }
+
+    if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
+        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+                      ngx_close_file_n " \"%s\" failed", file->name);
+    }
+
+    ngx_free(file->name);
+    ngx_free(file);
+}
+
+
+static void
+ngx_expire_old_cached_files(ngx_open_file_cache_t *cache, ngx_uint_t n,
+    ngx_log_t *log)
+{
+    time_t                   now;
+    ngx_cached_open_file_t  *file;
+
+    now = ngx_time();
+
+    /*
+     * n == 1 deletes one or two inactive files
+     * n == 0 deletes least recently used file by force
+     *        and one or two inactive files
+     */
+
+    while (n < 3) {
+
+        file = cache->list_tail.prev;
+
+        if (file == &cache->list_head) {
+            return;
+        }
+
+        if (n++ != 0 && now - file->accessed <= cache->inactive) {
+            return;
+        }
+
+        file->next->prev = file->prev;
+        file->prev->next = file->next;
+
+        ngx_rbtree_delete(&cache->rbtree, &file->node);
+
+        cache->current--;
+
+        ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0,
+                       "expire cached open file: %s", file->name);
+
+        if (!file->err && !file->is_dir) {
+            file->close = 1;
+            ngx_close_cached_file(cache, file, log);
+
+        } else {
+            ngx_free(file->name);
+            ngx_free(file);
+        }
+    }
+}
+
+
+static void
+ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
+    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
+{
+    ngx_rbtree_node_t       **p;
+    ngx_cached_open_file_t    *file, *file_temp;
+
+    for ( ;; ) {
+
+        if (node->key < temp->key) {
+
+            p = &temp->left;
+
+        } else if (node->key > temp->key) {
+
+            p = &temp->right;
+
+        } else { /* node->key == temp->key */
+
+            file = (ngx_cached_open_file_t *) node;
+            file_temp = (ngx_cached_open_file_t *) temp;
+
+            p = (ngx_strcmp(file->name, file_temp->name) < 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 void
+ngx_open_file_cache_remove(ngx_event_t *ev)
+{
+    ngx_cached_open_file_t       *file;
+    ngx_open_file_cache_event_t  *fev;
+
+    fev = ev->data;
+    file = fev->file;
+
+    file->next->prev = file->prev;
+    file->prev->next = file->next;
+
+    ngx_rbtree_delete(&fev->cache->rbtree, &file->node);
+
+    fev->cache->current--;
+
+    /* NGX_ONESHOT_EVENT was already deleted */
+    file->event = NULL;
+
+    file->close = 1;
+
+    ngx_close_cached_file(fev->cache, file, ev->log);
+
+    /* free memory only when fev->cache and fev->file are already not needed */
+
+    ngx_free(ev->data);
+    ngx_free(ev);
+}
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_open_file_cache.h
@@ -0,0 +1,101 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+#ifndef _NGX_OPEN_FILE_CACHE_H_INCLUDED_
+#define _NGX_OPEN_FILE_CACHE_H_INCLUDED_
+
+
+typedef struct {
+    ngx_fd_t                 fd;
+    ngx_file_uniq_t          uniq;
+    time_t                   mtime;
+    off_t                    size;
+    ngx_err_t                err;
+
+    time_t                   retest;
+
+    unsigned                 test_dir:1;
+    unsigned                 errors:1;
+    unsigned                 events:1;
+
+    unsigned                 is_dir:1;
+    unsigned                 is_file:1;
+    unsigned                 is_link:1;
+    unsigned                 is_exec:1;
+} ngx_open_file_info_t;
+
+
+typedef struct ngx_cached_open_file_s  ngx_cached_open_file_t;
+
+struct ngx_cached_open_file_s {
+    ngx_rbtree_node_t        node;
+    ngx_cached_open_file_t  *prev;
+    ngx_cached_open_file_t  *next;
+
+    u_char                  *name;
+    time_t                   created;
+    time_t                   accessed;
+
+    ngx_fd_t                 fd;
+    ngx_file_uniq_t          uniq;
+    time_t                   mtime;
+    off_t                    size;
+    ngx_err_t                err;
+
+    unsigned                 count:24;
+    unsigned                 close:1;
+
+    unsigned                 is_dir:1;
+    unsigned                 is_file:1;
+    unsigned                 is_link:1;
+    unsigned                 is_exec:1;
+
+    ngx_event_t             *event;
+};
+
+
+typedef struct {
+    ngx_rbtree_t             rbtree;
+    ngx_cached_open_file_t   list_head;
+    ngx_cached_open_file_t   list_tail;
+
+    ngx_uint_t               current;
+    ngx_uint_t               max;
+    time_t                   inactive;
+} ngx_open_file_cache_t;
+
+
+typedef struct {
+    ngx_open_file_cache_t   *cache;
+    ngx_cached_open_file_t  *file;
+    ngx_log_t               *log;
+} ngx_open_file_cache_cleanup_t;
+
+
+typedef struct {
+
+    /* ngx_connection_t stub to allow use c->fd as event ident */
+    void                    *data;
+    ngx_event_t             *read;
+    ngx_event_t             *write;
+    ngx_fd_t                 fd;
+
+    ngx_cached_open_file_t  *file;
+    ngx_open_file_cache_t   *cache;
+} ngx_open_file_cache_event_t;
+
+
+ngx_open_file_cache_t *ngx_open_file_cache_init(ngx_pool_t *pool,
+    ngx_uint_t max, time_t inactive);
+ngx_int_t ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name,
+    ngx_open_file_info_t *of, ngx_pool_t *pool);
+
+
+#endif /* _NGX_OPEN_FILE_CACHE_H_INCLUDED_ */
--- a/src/core/ngx_string.c
+++ b/src/core/ngx_string.c
@@ -59,8 +59,9 @@ ngx_pstrdup(ngx_pool_t *pool, ngx_str_t 
  *    %P                        ngx_pid_t
  *    %M                        ngx_msec_t
  *    %r                        rlim_t
- *    %p                        pointer
- *    %V                        pointer to ngx_str_t
+ *    %p                        void *
+ *    %V                        ngx_str_t *
+ *    %v                        ngx_variable_value_t *
  *    %s                        null-terminated string
  *    %Z                        '\0'
  *    %N                        '\n'
@@ -117,7 +118,8 @@ ngx_vsnprintf(u_char *buf, size_t max, c
     uint64_t               ui64;
     ngx_msec_t             ms;
     ngx_uint_t             width, sign, hexadecimal, max_width;
-    ngx_variable_value_t  *v;
+    ngx_str_t             *v;
+    ngx_variable_value_t  *vv;
     static u_char          hex[] = "0123456789abcdef";
     static u_char          HEX[] = "0123456789ABCDEF";
 
@@ -188,7 +190,7 @@ ngx_vsnprintf(u_char *buf, size_t max, c
             switch (*fmt) {
 
             case 'V':
-                v = va_arg(args, ngx_variable_value_t *);
+                v = va_arg(args, ngx_str_t *);
 
                 len = v->len;
                 len = (buf + len < last) ? len : (size_t) (last - buf);
@@ -198,6 +200,17 @@ ngx_vsnprintf(u_char *buf, size_t max, c
 
                 continue;
 
+            case 'v':
+                vv = va_arg(args, ngx_variable_value_t *);
+
+                len = vv->len;
+                len = (buf + len < last) ? len : (size_t) (last - buf);
+
+                buf = ngx_cpymem(buf, vv->data, len);
+                fmt++;
+
+                continue;
+
             case 's':
                 p = va_arg(args, u_char *);
 
@@ -1019,7 +1032,7 @@ ngx_escape_uri(u_char *dst, u_char *src,
         0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */
     };
 
-                    /* " ", """, "%", "'", %00-%1F, %7F-%FF */
+                    /* " ", "#", """, "%", "'", %00-%1F, %7F-%FF */
 
     static uint32_t   html[] = {
         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
@@ -1039,13 +1052,13 @@ ngx_escape_uri(u_char *dst, u_char *src,
         0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */
     };
 
-                    /* " ", """, "'", %00-%1F, %7F-%FF */
+                    /* " ", """, "%", "'", %00-%1F, %7F-%FF */
 
     static uint32_t   refresh[] = {
         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
 
                     /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
-        0x00000085, /* 0000 0000 0000 0000  0000 0000 1000 0101 */
+        0x000000a5, /* 0000 0000 0000 0000  0000 0000 1010 0101 */
 
                     /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
         0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
@@ -1059,13 +1072,13 @@ ngx_escape_uri(u_char *dst, u_char *src,
         0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */
     };
 
-                    /* " ", %00-%1F */
+                    /* " ", "%", %00-%1F */
 
     static uint32_t   memcached[] = {
         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
 
                     /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
-        0x00000001, /* 0000 0000 0000 0000  0000 0000 0000 0001 */
+        0x00000021, /* 0000 0000 0000 0000  0000 0000 0010 0001 */
 
                     /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
         0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
@@ -1079,7 +1092,10 @@ ngx_escape_uri(u_char *dst, u_char *src,
         0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
     };
 
-    static uint32_t  *map[] = { uri, args, html, refresh, memcached };
+                    /* mail_auth is the same as memcached */
+
+    static uint32_t  *map[] =
+        { uri, args, html, refresh, memcached, memcached };
 
 
     escape = map[type];
--- a/src/core/ngx_string.h
+++ b/src/core/ngx_string.h
@@ -155,6 +155,7 @@ u_char *ngx_utf_cpystrn(u_char *dst, u_c
 #define NGX_ESCAPE_HTML       2
 #define NGX_ESCAPE_REFRESH    3
 #define NGX_ESCAPE_MEMCACHED  4
+#define NGX_ESCAPE_MAIL_AUTH  5
 
 #define NGX_UNESCAPE_URI      1
 
--- a/src/event/modules/ngx_devpoll_module.c
+++ b/src/event/modules/ngx_devpoll_module.c
@@ -13,8 +13,9 @@
 
 /* Solaris declarations */
 
-#define POLLREMOVE  0x0800
-#define DP_POLL     0xD001
+#define POLLREMOVE   0x0800
+#define DP_POLL      0xD001
+#define DP_ISPOLLED  0xD002
 
 struct dvpoll {
     struct pollfd  *dp_fds;
@@ -255,10 +256,16 @@ ngx_devpoll_del_event(ngx_event_t *ev, n
     ev->active = 0;
 
     if (flags & NGX_CLOSE_EVENT) {
+        e = (event == POLLIN) ? c->write : c->read;
+
+        if (e) {
+            e->active = 0;
+        }
+
         return NGX_OK;
     }
 
-    /* restore the paired event if it exists */
+    /* restore the pair event if it exists */
 
     if (event == POLLIN) {
         e = c->write;
@@ -330,13 +337,15 @@ ngx_int_t
 ngx_devpoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
     ngx_uint_t flags)
 {
-    int                 events, revents;
+    int                 events, revents, rc;
     size_t              n;
+    ngx_fd_t            fd;
     ngx_err_t           err;
     ngx_int_t           i;
     ngx_uint_t          level;
     ngx_event_t        *rev, *wev, **queue;
     ngx_connection_t   *c;
+    struct pollfd       pfd;
     struct dvpoll       dvp;
 
     /* NGX_TIMER_INFINITE == INFTIM */
@@ -401,34 +410,77 @@ ngx_devpoll_process_events(ngx_cycle_t *
     ngx_mutex_lock(ngx_posted_events_mutex);
 
     for (i = 0; i < events; i++) {
-        c = ngx_cycle->files[event_list[i].fd];
+
+        fd = event_list[i].fd;
+        revents = event_list[i].revents;
+
+        c = ngx_cycle->files[fd];
+
+        if (c == NULL || c->fd == -1) {
+
+            pfd.fd = fd;
+            pfd.events = 0;
+            pfd.revents = 0;
+
+            rc = ioctl(dp, DP_ISPOLLED, &pfd);
+
+            switch (rc) {
+
+            case -1:
+                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                    "ioctl(DP_ISPOLLED) failed for socket %d, event",
+                    fd, revents);
+                break;
 
-        if (c->fd == -1) {
-            if (c->read->closed) {
-                continue;
+            case 0:
+                ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+                    "phantom event %04Xd for closed and removed socket %d",
+                    revents, fd);
+                break;
+
+            default:
+                ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+                    "unexpected event %04Xd for closed and removed socket %d, ",
+                    "ioctl(DP_ISPOLLED) returned rc:%d, fd:%d, event %04Xd",
+                    revents, fd, rc, pfd.fd, pfd.revents);
+
+                pfd.fd = fd;
+                pfd.events = POLLREMOVE;
+                pfd.revents = 0;
+
+                if (write(dp, &pfd, sizeof(struct pollfd))
+                    != (ssize_t) sizeof(struct pollfd))
+                {
+                    ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                                  "write(/dev/poll) for %d failed, fd");
+                }
+
+                if (close(fd) == -1) {
+                    ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                                  "close(%d) failed", fd);
+                }
+
+                break;
             }
 
-            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "unexpected event");
             continue;
         }
 
-        revents = event_list[i].revents;
-
         ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                        "devpoll: fd:%d, ev:%04Xd, rev:%04Xd",
-                       event_list[i].fd, event_list[i].events, revents);
+                       fd, event_list[i].events, revents);
 
         if (revents & (POLLERR|POLLHUP|POLLNVAL)) {
             ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                           "ioctl(DP_POLL) error fd:%d ev:%04Xd rev:%04Xd",
-                          event_list[i].fd, event_list[i].events, revents);
+                          fd, event_list[i].events, revents);
         }
 
         if (revents & ~(POLLIN|POLLOUT|POLLERR|POLLHUP|POLLNVAL)) {
             ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
                           "strange ioctl(DP_POLL) events "
                           "fd:%d ev:%04Xd rev:%04Xd",
-                          event_list[i].fd, event_list[i].events, revents);
+                          fd, event_list[i].events, revents);
         }
 
         if ((revents & (POLLERR|POLLHUP|POLLNVAL))
--- a/src/event/modules/ngx_kqueue_module.c
+++ b/src/event/modules/ngx_kqueue_module.c
@@ -200,7 +200,9 @@ ngx_kqueue_init(ngx_cycle_t *cycle, ngx_
         }
     }
 
-    ngx_event_flags = 0;
+    ngx_event_flags = NGX_USE_ONESHOT_EVENT
+                      |NGX_USE_KQUEUE_EVENT
+                      |NGX_USE_VNODE_EVENT;
 
 #if (NGX_HAVE_TIMER_EVENT)
 
@@ -226,8 +228,6 @@ ngx_kqueue_init(ngx_cycle_t *cycle, ngx_
 
 #endif
 
-    ngx_event_flags |= NGX_USE_ONESHOT_EVENT|NGX_USE_KQUEUE_EVENT;
-
 #if (NGX_HAVE_CLEAR_EVENT)
     ngx_event_flags |= NGX_USE_CLEAR_EVENT;
 #else
@@ -389,10 +389,12 @@ ngx_kqueue_del_event(ngx_event_t *ev, ng
 
     if (flags & NGX_DISABLE_EVENT) {
         ev->disabled = 1;
+
+    } else {
+        flags |= EV_DELETE;
     }
 
-    rc = ngx_kqueue_set_event(ev, event,
-                           flags & NGX_DISABLE_EVENT ? EV_DISABLE : EV_DELETE);
+    rc = ngx_kqueue_set_event(ev, event, flags);
 
     ngx_mutex_unlock(list_mutex);
 
@@ -409,7 +411,7 @@ ngx_kqueue_set_event(ngx_event_t *ev, ng
 
     c = ev->data;
 
-    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,
                    "kevent set event: %d: ft:%i fl:%04Xi",
                    c->fd, filter, flags);
 
@@ -466,6 +468,22 @@ ngx_kqueue_set_event(ngx_event_t *ev, ng
     ev->index = nchanges;
     nchanges++;
 
+    if (flags & NGX_FLUSH_EVENT) {
+        ts.tv_sec = 0;
+        ts.tv_nsec = 0;
+
+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "kevent flush");
+
+        if (kevent(ngx_kqueue, change_list, (int) nchanges, NULL, 0, &ts)
+            == -1)
+        {
+            ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, "kevent() failed");
+            return NGX_ERROR;
+        }
+
+        nchanges = 0;
+    }
+
     return NGX_OK;
 }
 
--- a/src/event/ngx_event.c
+++ b/src/event/ngx_event.c
@@ -1038,8 +1038,9 @@ ngx_event_debug_connection(ngx_conf_t *c
 #if (NGX_DEBUG)
     ngx_event_conf_t  *ecf = conf;
 
+    ngx_int_t           rc;
+    ngx_str_t          *value;
     ngx_event_debug_t  *dc;
-    ngx_str_t          *value;
     struct hostent     *h;
     ngx_inet_cidr_t     in_cidr;
 
@@ -1056,13 +1057,21 @@ ngx_event_debug_connection(ngx_conf_t *c
 
     if (dc->addr != INADDR_NONE) {
         dc->mask = 0xffffffff;
-        return NGX_OK;
+        return NGX_CONF_OK;
     }
 
-    if (ngx_ptocidr(&value[1], &in_cidr) == NGX_OK) {
+    rc = ngx_ptocidr(&value[1], &in_cidr);
+
+    if (rc == NGX_DONE) {
+        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                           "low address bits of %V are meaningless", &value[1]);
+        rc = NGX_OK;
+    }
+
+    if (rc == NGX_OK) {
         dc->mask = in_cidr.mask;
         dc->addr = in_cidr.addr;
-        return NGX_OK;
+        return NGX_CONF_OK;
     }
 
     h = gethostbyname((char *) value[1].data);
@@ -1084,7 +1093,7 @@ ngx_event_debug_connection(ngx_conf_t *c
 
 #endif
 
-    return NGX_OK;
+    return NGX_CONF_OK;
 }
 
 
--- a/src/event/ngx_event.h
+++ b/src/event/ngx_event.h
@@ -41,7 +41,7 @@ struct ngx_event_s {
 
     unsigned         accept:1;
 
-    /* used to detect the stale events in kqueue, rt signals and epoll */
+    /* used to detect the stale events in kqueue, rtsig, and epoll */
     unsigned         instance:1;
 
     /*
@@ -247,8 +247,7 @@ extern ngx_event_actions_t   ngx_event_a
 #define NGX_USE_LOWAT_EVENT      0x00000010
 
 /*
- * The event filter requires to do i/o operation until EAGAIN:
- * epoll, rt signals.
+ * The event filter requires to do i/o operation until EAGAIN: epoll, rtsig.
  */
 #define NGX_USE_GREEDY_EVENT     0x00000020
 
@@ -258,7 +257,7 @@ extern ngx_event_actions_t   ngx_event_a
 #define NGX_USE_EPOLL_EVENT      0x00000040
 
 /*
- * No need to add or delete the event filters: rt signals.
+ * No need to add or delete the event filters: rtsig.
  */
 #define NGX_USE_RTSIG_EVENT      0x00000080
 
@@ -276,13 +275,13 @@ extern ngx_event_actions_t   ngx_event_a
 
 /*
  * The event filter has no opaque data and requires file descriptors table:
- * poll, /dev/poll, rt signals.
+ * poll, /dev/poll, rtsig.
  */
 #define NGX_USE_FD_EVENT         0x00000400
 
 /*
  * The event module handles periodic or absolute timer event by itself:
- * kqueue in FreeBSD 4.4 and NetBSD 2.0, Solaris 10's event ports.
+ * kqueue in FreeBSD 4.4, NetBSD 2.0, and MacOSX 10.4, Solaris 10's event ports.
  */
 #define NGX_USE_TIMER_EVENT      0x00000800
 
@@ -290,19 +289,37 @@ extern ngx_event_actions_t   ngx_event_a
  * All event filters on file descriptor are deleted after a notification:
  * Solaris 10's event ports.
  */
-#define NGX_USE_EVENTPORT_EVENT    0x00001000
+#define NGX_USE_EVENTPORT_EVENT  0x00001000
 
+/*
+ * The event filter support vnode notifications: kqueue.
+ */
+#define NGX_USE_VNODE_EVENT      0x00002000
 
 
 /*
- * The event filter is deleted before the closing file.
- * Has no meaning for select, poll, kqueue, epoll.
- * /dev/poll:  we need to flush POLLREMOVE event before closing file
+ * The event filter is deleted just before the closing file.
+ * Has no meaning for select and poll.
+ * kqueue, epoll, rtsig, eventport:  allows to avoid explicit delete,
+ *                                   because filter automatically is deleted
+ *                                   on file close,
+ *
+ * /dev/poll:                        we need to flush POLLREMOVE event
+ *                                   before closing file.
  */
+#define NGX_CLOSE_EVENT    1
 
-#define NGX_CLOSE_EVENT    1
+/*
+ * disable temporarily event filter, this may avoid locks
+ * in kernel malloc()/free(): kqueue.
+ */
 #define NGX_DISABLE_EVENT  2
 
+/*
+ * event must be passed to kernel right now, do not wait until batch processing.
+ */
+#define NGX_FLUSH_EVENT    4
+
 
 /* these flags have a meaning only for kqueue */
 #define NGX_LOWAT_EVENT    0
@@ -318,11 +335,11 @@ extern ngx_event_actions_t   ngx_event_a
 #define NGX_VNODE_EVENT    EVFILT_VNODE
 
 /*
- * NGX_CLOSE_EVENT and NGX_LOWAT_EVENT are the module flags and they would
- * not go into a kernel so we need to choose the value that would not interfere
- * with any existent and future kqueue flags.  kqueue has such values -
- * EV_FLAG1, EV_EOF and EV_ERROR.  They are reserved and cleared on a kernel
- * entrance.
+ * NGX_CLOSE_EVENT, NGX_LOWAT_EVENT, and NGX_FLUSH_EVENT are the module flags
+ * and they must not go into a kernel so we need to choose the value
+ * that must not interfere with any existent and future kqueue flags.
+ * kqueue has such values - EV_FLAG1, EV_EOF, and EV_ERROR:
+ * they are reserved and cleared on a kernel entrance.
  */
 #undef  NGX_CLOSE_EVENT
 #define NGX_CLOSE_EVENT    EV_EOF
@@ -330,6 +347,9 @@ extern ngx_event_actions_t   ngx_event_a
 #undef  NGX_LOWAT_EVENT
 #define NGX_LOWAT_EVENT    EV_FLAG1
 
+#undef  NGX_FLUSH_EVENT
+#define NGX_FLUSH_EVENT    EV_ERROR
+
 #define NGX_LEVEL_EVENT    0
 #define NGX_ONESHOT_EVENT  EV_ONESHOT
 #define NGX_CLEAR_EVENT    EV_CLEAR
--- a/src/event/ngx_event_connect.c
+++ b/src/event/ngx_event_connect.c
@@ -85,6 +85,8 @@ ngx_event_connect_peer(ngx_peer_connecti
     c->recv_chain = ngx_recv_chain;
     c->send_chain = ngx_send_chain;
 
+    c->sendfile = 1;
+
     c->log_error = pc->log_error;
 
     if (pc->sockaddr->sa_family != AF_INET) {
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -593,10 +593,13 @@ ngx_ssl_recv(ngx_connection_t *c, u_char
     int  n, bytes;
 
     if (c->ssl->last == NGX_ERROR) {
+        c->read->error = 1;
         return NGX_ERROR;
     }
 
     if (c->ssl->last == NGX_DONE) {
+        c->read->ready = 0;
+        c->read->eof = 1;
         return 0;
     }
 
@@ -619,26 +622,38 @@ ngx_ssl_recv(ngx_connection_t *c, u_char
 
         c->ssl->last = ngx_ssl_handle_recv(c, n);
 
-        if (c->ssl->last != NGX_OK) {
-
-            if (bytes) {
+        if (c->ssl->last == NGX_OK) {
+
+            size -= n;
+
+            if (size == 0) {
                 return bytes;
             }
 
-            if (c->ssl->last == NGX_DONE) {
-                return 0;
-            }
-
+            buf += n;
+
+            continue;
+        }
+
+        if (bytes) {
+            return bytes;
+        }
+
+        switch (c->ssl->last) {
+
+        case NGX_DONE:
+            c->read->ready = 0;
+            c->read->eof = 1;
+            return 0;
+
+        case NGX_ERROR:
+            c->read->error = 1;
+
+            /* fall thruogh */
+
+        case NGX_AGAIN:
             return c->ssl->last;
         }
-
-        size -= n;
-
-        if (size == 0) {
-            return bytes;
-        }
-
-        buf += n;
     }
 }
 
@@ -703,7 +718,6 @@ ngx_ssl_handle_recv(ngx_connection_t *c,
 
     c->ssl->no_wait_shutdown = 1;
     c->ssl->no_send_shutdown = 1;
-    c->read->eof = 1;
 
     if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) {
         ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
@@ -711,7 +725,6 @@ ngx_ssl_handle_recv(ngx_connection_t *c,
         return NGX_DONE;
     }
 
-    c->read->error = 1;
     ngx_ssl_connection_error(c, sslerr, err, "SSL_read() failed");
 
     return NGX_ERROR;
@@ -1588,7 +1601,7 @@ ngx_ssl_expire_sessions(ngx_ssl_session_
         }
 
         if (n++ != 0 && sess_id->expire > tp->sec) {
-            break;
+            return;
         }
 
         sess_id->next->prev = sess_id->prev;
--- a/src/http/modules/ngx_http_access_module.c
+++ b/src/http/modules/ngx_http_access_module.c
@@ -137,6 +137,7 @@ ngx_http_access_rule(ngx_conf_t *cf, ngx
 {
     ngx_http_access_loc_conf_t *alcf = conf;
 
+    ngx_int_t                rc;
     ngx_str_t               *value;
     ngx_inet_cidr_t          in_cidr;
     ngx_http_access_rule_t  *rule;
@@ -173,12 +174,19 @@ ngx_http_access_rule(ngx_conf_t *cf, ngx
         return NGX_CONF_OK;
     }
 
-    if (ngx_ptocidr(&value[1], &in_cidr) == NGX_ERROR) {
+    rc = ngx_ptocidr(&value[1], &in_cidr);
+
+    if (rc == NGX_ERROR) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"",
                            &value[1]);
         return NGX_CONF_ERROR;
     }
 
+    if (rc == NGX_DONE) {
+        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                           "low address bits of %V are meaningless", &value[1]);
+    }
+
     rule->mask = in_cidr.mask;
     rule->addr = in_cidr.addr;
 
--- a/src/http/modules/ngx_http_dav_module.c
+++ b/src/http/modules/ngx_http_dav_module.c
@@ -353,9 +353,9 @@ ngx_http_dav_delete_handler(ngx_http_req
         return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
     }
 
-    rc = ngx_http_discard_body(r);
+    rc = ngx_http_discard_request_body(r);
 
-    if (rc != NGX_OK && rc != NGX_AGAIN) {
+    if (rc != NGX_OK) {
         return rc;
     }
 
@@ -469,9 +469,9 @@ ngx_http_dav_mkcol_handler(ngx_http_requ
         return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
     }
 
-    rc = ngx_http_discard_body(r);
+    rc = ngx_http_discard_request_body(r);
 
-    if (rc != NGX_OK && rc != NGX_AGAIN) {
+    if (rc != NGX_OK) {
         return rc;
     }
 
@@ -611,9 +611,9 @@ destination_done:
 
 overwrite_done:
 
-    rc = ngx_http_discard_body(r);
+    rc = ngx_http_discard_request_body(r);
 
-    if (rc != NGX_OK && rc != NGX_AGAIN) {
+    if (rc != NGX_OK) {
         return rc;
     }
 
--- a/src/http/modules/ngx_http_empty_gif_module.c
+++ b/src/http/modules/ngx_http_empty_gif_module.c
@@ -116,9 +116,9 @@ ngx_http_empty_gif_handler(ngx_http_requ
         return NGX_HTTP_NOT_ALLOWED;
     }
 
-    rc = ngx_http_discard_body(r);
+    rc = ngx_http_discard_request_body(r);
 
-    if (rc != NGX_OK && rc != NGX_AGAIN) {
+    if (rc != NGX_OK) {
         return rc;
     }
 
--- a/src/http/modules/ngx_http_fastcgi_module.c
+++ b/src/http/modules/ngx_http_fastcgi_module.c
@@ -1640,7 +1640,6 @@ ngx_http_fastcgi_create_loc_conf(ngx_con
      *     conf->upstream.hide_headers_hash = { NULL, 0 };
      *     conf->upstream.hide_headers = NULL;
      *     conf->upstream.pass_headers = NULL;
-     *     conf->upstream.catch_stderr = NULL;
      *     conf->upstream.schema = { 0, NULL };
      *     conf->upstream.uri = { 0, NULL };
      *     conf->upstream.location = NULL;
@@ -1675,6 +1674,8 @@ ngx_http_fastcgi_create_loc_conf(ngx_con
     /* "fastcgi_cyclic_temp_file" is disabled */
     conf->upstream.cyclic_temp_file = 0;
 
+    conf->catch_stderr = NGX_CONF_UNSET_PTR;
+
     return conf;
 }
 
--- a/src/http/modules/ngx_http_flv_module.c
+++ b/src/http/modules/ngx_http_flv_module.c
@@ -60,20 +60,17 @@ ngx_module_t  ngx_http_flv_module = {
 static ngx_int_t
 ngx_http_flv_handler(ngx_http_request_t *r)
 {
-    u_char                    *p;
+    u_char                    *p, *last;
     off_t                      start, len;
     size_t                     root;
     ngx_fd_t                   fd;
     ngx_int_t                  rc;
     ngx_uint_t                 level, i;
     ngx_str_t                  path;
-    ngx_err_t                  err;
     ngx_log_t                 *log;
     ngx_buf_t                 *b;
     ngx_chain_t                out[2];
-    ngx_file_info_t            fi;
-    ngx_pool_cleanup_t        *cln;
-    ngx_pool_cleanup_file_t   *clnf;
+    ngx_open_file_info_t       of;
     ngx_http_core_loc_conf_t  *clcf;
 
     if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
@@ -89,70 +86,72 @@ ngx_http_flv_handler(ngx_http_request_t 
         return NGX_DECLINED;
     }
 
-    rc = ngx_http_discard_body(r);
+    rc = ngx_http_discard_request_body(r);
 
-    if (rc != NGX_OK && rc != NGX_AGAIN) {
+    if (rc != NGX_OK) {
         return rc;
     }
 
-    if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
+    last = ngx_http_map_uri_to_path(r, &path, &root, 0);
+    if (last == NULL) {
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
     log = r->connection->log;
 
-    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
-                   "http flv filename: \"%s\"", path.data);
+    path.len = last - path.data;
 
-    cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_pool_cleanup_file_t));
-    if (cln == NULL) {
-        return NGX_HTTP_INTERNAL_SERVER_ERROR;
-    }
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
+                   "http flv filename: \"%V\"", &path);
+
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
 
-    fd = ngx_open_file(path.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
+    of.test_dir = 0;
+    of.retest = clcf->open_file_cache_retest;
+    of.errors = clcf->open_file_cache_errors;
+    of.events = clcf->open_file_cache_events;
+    
+    rc = ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool);
 
-    if (fd == NGX_INVALID_FILE) {
-        err = ngx_errno;
+    if (rc == NGX_ERROR) {
 
-        if (err == NGX_ENOENT
-            || err == NGX_ENOTDIR
-            || err == NGX_ENAMETOOLONG)
-        {
+        switch (of.err) {
+
+        case 0:
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+
+        case NGX_ENOENT:
+        case NGX_ENOTDIR:
+        case NGX_ENAMETOOLONG:
+
             level = NGX_LOG_ERR;
             rc = NGX_HTTP_NOT_FOUND;
+            break;
 
-        } else if (err == NGX_EACCES) {
+        case NGX_EACCES:
+
             level = NGX_LOG_ERR;
             rc = NGX_HTTP_FORBIDDEN;
+            break;
 
-        } else {
+        default:
+
             level = NGX_LOG_CRIT;
             rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+            break;
         }
 
-        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
-
         if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) {
-            ngx_log_error(level, log, err,
+            ngx_log_error(level, log, of.err,
                           ngx_open_file_n " \"%s\" failed", path.data);
         }
 
         return rc;
     }
 
-    if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
-        ngx_log_error(NGX_LOG_CRIT, log, ngx_errno,
-                      ngx_fd_info_n " \"%s\" failed", path.data);
+    fd = of.fd;
 
-        if (ngx_close_file(fd) == NGX_FILE_ERROR) {
-            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
-                          ngx_close_file_n " \"%s\" failed", path.data);
-        }
-
-        return NGX_HTTP_INTERNAL_SERVER_ERROR;
-    }
-
-    if (!ngx_is_file(&fi)) {
+    if (!of.is_file) {
 
         if (ngx_close_file(fd) == NGX_FILE_ERROR) {
             ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
@@ -163,7 +162,7 @@ ngx_http_flv_handler(ngx_http_request_t 
     }
 
     start = 0;
-    len = ngx_file_size(&fi);
+    len = of.size;
     i = 1;
 
     if (r->args.len) {
@@ -187,16 +186,9 @@ ngx_http_flv_handler(ngx_http_request_t 
 
     log->action = "sending flv to client";
 
-    cln->handler = ngx_pool_cleanup_file;
-    clnf = cln->data;
-
-    clnf->fd = fd;
-    clnf->name = path.data;
-    clnf->log = r->pool->log;
-
     r->headers_out.status = NGX_HTTP_OK;
     r->headers_out.content_length_n = len;
-    r->headers_out.last_modified_time = ngx_file_mtime(&fi);
+    r->headers_out.last_modified_time = of.mtime;
 
     if (ngx_http_set_content_type(r) != NGX_OK) {
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
@@ -237,7 +229,7 @@ ngx_http_flv_handler(ngx_http_request_t 
     }
 
     b->file_pos = start;
-    b->file_last = ngx_file_size(&fi);
+    b->file_last = of.size;
 
     b->in_file = b->file_last ? 1: 0;
     b->last_buf = 1;
--- a/src/http/modules/ngx_http_geo_module.c
+++ b/src/http/modules/ngx_http_geo_module.c
@@ -86,7 +86,7 @@ ngx_http_geo_variable(ngx_http_request_t
     *v = *vv;
 
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "http geo: %V %V", &r->connection->addr_text, v);
+                   "http geo: %V %v", &r->connection->addr_text, v);
 
     return NGX_OK;
 }
@@ -100,8 +100,8 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c
     ngx_conf_t                save;
     ngx_pool_t               *pool;
     ngx_radix_tree_t         *tree;
+    ngx_http_variable_t      *var;
     ngx_http_geo_conf_ctx_t   ctx;
-    ngx_http_variable_t  *var;
 
     value = cf->args->elts;
 
@@ -212,12 +212,20 @@ ngx_http_geo(ngx_conf_t *cf, ngx_command
         cidrin.mask = 0;
 
     } else {
-        if (ngx_ptocidr(&value[0], &cidrin) == NGX_ERROR) {
+        rc = ngx_ptocidr(&value[0], &cidrin);
+
+        if (rc == NGX_ERROR) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                "invalid parameter \"%V\"", &value[0]);
             return NGX_CONF_ERROR;
         }
 
+        if (rc == NGX_DONE) {
+            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                               "low address bits of %V are meaningless",
+                               &value[0]);
+        }
+
         cidrin.addr = ntohl(cidrin.addr);
         cidrin.mask = ntohl(cidrin.mask);
     }
@@ -277,9 +285,8 @@ ngx_http_geo(ngx_conf_t *cf, ngx_command
                     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 parameter \"%V\", value: \"%v\", old value: \"%v\"",
+                &value[0], var, old);
 
         rc = ngx_radix32tree_delete(ctx->tree, cidrin.addr, cidrin.mask);
 
--- a/src/http/modules/ngx_http_headers_filter_module.c
+++ b/src/http/modules/ngx_http_headers_filter_module.c
@@ -9,17 +9,31 @@
 #include <ngx_http.h>
 
 
-typedef struct {
-    ngx_table_elt_t   value;
-    ngx_array_t      *lengths;
-    ngx_array_t      *values;
-} ngx_http_header_val_t;
+typedef struct ngx_http_header_val_s  ngx_http_header_val_t;
+
+typedef ngx_int_t (*ngx_http_set_header_pt)(ngx_http_request_t *r,
+    ngx_http_header_val_t *hv, ngx_str_t *value);
 
 
 typedef struct {
-    time_t            expires;
-    ngx_str_t         cache_control;
-    ngx_array_t      *headers;
+    ngx_str_t                name;
+    ngx_uint_t               offset;
+    ngx_http_set_header_pt   handler;
+} ngx_http_set_header_t;
+
+
+struct ngx_http_header_val_s {
+    ngx_table_elt_t          value;
+    ngx_uint_t               offset;
+    ngx_http_set_header_pt   handler;
+    ngx_array_t             *lengths;
+    ngx_array_t             *values;
+};
+
+
+typedef struct {
+    time_t                   expires;
+    ngx_array_t             *headers;
 } ngx_http_headers_conf_t;
 
 
@@ -29,6 +43,13 @@ typedef struct {
 #define NGX_HTTP_EXPIRES_MAX     -2147483644
 
 
+static ngx_int_t ngx_http_set_expires(ngx_http_request_t *r,
+    ngx_http_headers_conf_t *conf);
+static ngx_int_t ngx_http_add_cache_control(ngx_http_request_t *r,
+    ngx_http_header_val_t *hv, ngx_str_t *value);
+static ngx_int_t ngx_http_set_last_modified(ngx_http_request_t *r,
+    ngx_http_header_val_t *hv, ngx_str_t *value);
+
 static void *ngx_http_headers_create_conf(ngx_conf_t *cf);
 static char *ngx_http_headers_merge_conf(ngx_conf_t *cf,
     void *parent, void *child);
@@ -39,6 +60,18 @@ static char *ngx_http_headers_add(ngx_co
     void *conf);
 
 
+static ngx_http_set_header_t  ngx_http_set_headers[] = {
+
+    { ngx_string("Cache-Control"), 0, ngx_http_add_cache_control },
+
+    { ngx_string("Last-Modified"),
+                 offsetof(ngx_http_headers_out_t, last_modified),
+                 ngx_http_set_last_modified },
+
+    { ngx_null_string, 0, NULL }
+};
+
+
 static ngx_command_t  ngx_http_headers_filter_commands[] = {
 
     { ngx_string("expires"),
@@ -98,13 +131,15 @@ static ngx_http_output_header_filter_pt 
 static ngx_int_t
 ngx_http_headers_filter(ngx_http_request_t *r)
 {
-    size_t                    len;
+    ngx_str_t                 value;
     ngx_uint_t                i;
-    ngx_table_elt_t          *expires, *cc, **ccp, *out;
     ngx_http_header_val_t    *h;
     ngx_http_headers_conf_t  *conf;
 
-    if (r != r->main
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module);
+
+    if ((conf->expires == NGX_HTTP_EXPIRES_OFF && conf->headers == NULL)
+        || r != r->main
         || (r->headers_out.status != NGX_HTTP_OK
             && r->headers_out.status != NGX_HTTP_NO_CONTENT
             && r->headers_out.status != NGX_HTTP_MOVED_PERMANENTLY
@@ -114,124 +149,73 @@ ngx_http_headers_filter(ngx_http_request
         return ngx_http_next_header_filter(r);
     }
 
-    conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module);
-
     if (conf->expires != NGX_HTTP_EXPIRES_OFF) {
-
-        expires = r->headers_out.expires;
-
-        if (expires == NULL) {
+        if (ngx_http_set_expires(r, conf) != NGX_OK) {
+            return NGX_ERROR;
+        }
+    }
 
-            expires = ngx_list_push(&r->headers_out.headers);
-            if (expires == NULL) {
-                return NGX_ERROR;
-            }
-
-            r->headers_out.expires = expires;
-
-            expires->hash = 1;
-            expires->key.len = sizeof("Expires") - 1;
-            expires->key.data = (u_char *) "Expires";
-        }
+    if (conf->headers) {
+        h = conf->headers->elts;
+        for (i = 0; i < conf->headers->nelts; i++) {
 
-        len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT");
-        expires->value.len = len - 1;
-
-        ccp = r->headers_out.cache_control.elts;
-
-        if (ccp == NULL) {
+            if (h[i].lengths == NULL) {
+                value = h[i].value.value;
 
-            if (ngx_array_init(&r->headers_out.cache_control, r->pool,
-                               1, sizeof(ngx_table_elt_t *))
-                != NGX_OK)
-            {
-                return NGX_ERROR;
-            }
-
-            ccp = ngx_array_push(&r->headers_out.cache_control);
-            if (ccp == NULL) {
-                return NGX_ERROR;
-            }
-
-            cc = ngx_list_push(&r->headers_out.headers);
-            if (cc == NULL) {
-                return NGX_ERROR;
+            } else {
+                if (ngx_http_script_run(r, &value, h[i].lengths->elts, 0,
+                                        h[i].values->elts)
+                    == NULL)
+                {
+                    return NGX_ERROR;
+                }
             }
 
-            cc->hash = 1;
-            cc->key.len = sizeof("Cache-Control") - 1;
-            cc->key.data = (u_char *) "Cache-Control";
-
-            *ccp = cc;
-
-        } else {
-            for (i = 1; i < r->headers_out.cache_control.nelts; i++) {
-                ccp[i]->hash = 0;
-            }
-
-            cc = ccp[0];
-        }
-
-        if (conf->expires == NGX_HTTP_EXPIRES_EPOCH) {
-            expires->value.data = (u_char *) "Thu, 01 Jan 1970 00:00:01 GMT";
-
-            cc->value.len = sizeof("no-cache") - 1;
-            cc->value.data = (u_char *) "no-cache";
-
-        } else if (conf->expires == NGX_HTTP_EXPIRES_MAX) {
-            expires->value.data = (u_char *) "Thu, 31 Dec 2037 23:55:55 GMT";
-
-            /* 10 years */
-            cc->value.len = sizeof("max-age=315360000") - 1;
-            cc->value.data = (u_char *) "max-age=315360000";
-
-        } else {
-            expires->value.data = ngx_palloc(r->pool, len);
-            if (expires->value.data == NULL) {
+            if (h[i].handler(r, &h[i], &value) != NGX_OK) {
                 return NGX_ERROR;
             }
-
-            if (conf->expires == 0) {
-                ngx_memcpy(expires->value.data, ngx_cached_http_time.data,
-                           ngx_cached_http_time.len + 1);
-
-                cc->value.len = sizeof("max-age=0") - 1;
-                cc->value.data = (u_char *) "max-age=0";
-
-            } else {
-                ngx_http_time(expires->value.data, ngx_time() + conf->expires);
-
-                if (conf->expires < 0) {
-                    cc->value.len = sizeof("no-cache") - 1;
-                    cc->value.data = (u_char *) "no-cache";
-
-                } else {
-                    cc->value.data = ngx_palloc(r->pool, sizeof("max-age=")
-                                                         + NGX_TIME_T_LEN + 1);
-                    if (cc->value.data == NULL) {
-                        return NGX_ERROR;
-                    }
-
-                    cc->value.len = ngx_sprintf(cc->value.data, "max-age=%T",
-                                                conf->expires)
-                                    - cc->value.data;
-                }
-            }
         }
     }
 
-    if (conf->cache_control.len) {
+    return ngx_http_next_header_filter(r);
+}
+
 
-        ccp = r->headers_out.cache_control.elts;
+static ngx_int_t
+ngx_http_set_expires(ngx_http_request_t *r, ngx_http_headers_conf_t *conf)
+{
+    size_t            len;
+    ngx_uint_t        i;
+    ngx_table_elt_t  *expires, *cc, **ccp;
 
-        if (ccp == NULL) {
+    expires = r->headers_out.expires;
+
+    if (expires == NULL) {
+
+        expires = ngx_list_push(&r->headers_out.headers);
+        if (expires == NULL) {
+            return NGX_ERROR;
+        }
 
-            if (ngx_array_init(&r->headers_out.cache_control, r->pool,
-                               1, sizeof(ngx_table_elt_t *))
-                != NGX_OK)
-            {
-                return NGX_ERROR;
-            }
+        r->headers_out.expires = expires;
+
+        expires->hash = 1;
+        expires->key.len = sizeof("Expires") - 1;
+        expires->key.data = (u_char *) "Expires";
+    }
+
+    len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT");
+    expires->value.len = len - 1;
+
+    ccp = r->headers_out.cache_control.elts;
+
+    if (ccp == NULL) {
+
+        if (ngx_array_init(&r->headers_out.cache_control, r->pool,
+                           1, sizeof(ngx_table_elt_t *))
+            != NGX_OK)
+        {
+            return NGX_ERROR;
         }
 
         ccp = ngx_array_push(&r->headers_out.cache_control);
@@ -247,37 +231,161 @@ ngx_http_headers_filter(ngx_http_request
         cc->hash = 1;
         cc->key.len = sizeof("Cache-Control") - 1;
         cc->key.data = (u_char *) "Cache-Control";
-        cc->value = conf->cache_control;
 
         *ccp = cc;
+
+    } else {
+        for (i = 1; i < r->headers_out.cache_control.nelts; i++) {
+            ccp[i]->hash = 0;
+        }
+
+        cc = ccp[0];
+    }
+
+    if (conf->expires == NGX_HTTP_EXPIRES_EPOCH) {
+        expires->value.data = (u_char *) "Thu, 01 Jan 1970 00:00:01 GMT";
+
+        cc->value.len = sizeof("no-cache") - 1;
+        cc->value.data = (u_char *) "no-cache";
+
+        return NGX_OK;
+    }
+
+    if (conf->expires == NGX_HTTP_EXPIRES_MAX) {
+        expires->value.data = (u_char *) "Thu, 31 Dec 2037 23:55:55 GMT";
+
+        /* 10 years */
+        cc->value.len = sizeof("max-age=315360000") - 1;
+        cc->value.data = (u_char *) "max-age=315360000";
+
+        return NGX_OK;
+    }
+
+    expires->value.data = ngx_palloc(r->pool, len);
+    if (expires->value.data == NULL) {
+        return NGX_ERROR;
+    }
+
+    if (conf->expires == 0) {
+        ngx_memcpy(expires->value.data, ngx_cached_http_time.data,
+                   ngx_cached_http_time.len + 1);
+
+        cc->value.len = sizeof("max-age=0") - 1;
+        cc->value.data = (u_char *) "max-age=0";
+
+        return NGX_OK;
     }
 
-    if (conf->headers) {
-        h = conf->headers->elts;
-        for (i = 0; i < conf->headers->nelts; i++) {
-            out = ngx_list_push(&r->headers_out.headers);
-            if (out == NULL) {
-                return NGX_ERROR;
-            }
+    ngx_http_time(expires->value.data, ngx_time() + conf->expires);
+
+    if (conf->expires < 0) {
+        cc->value.len = sizeof("no-cache") - 1;
+        cc->value.data = (u_char *) "no-cache";
+
+        return NGX_OK;
+    }
 
-            out->hash = h[i].value.hash;
-            out->key = h[i].value.key;
+    cc->value.data = ngx_palloc(r->pool,
+                                sizeof("max-age=") + NGX_TIME_T_LEN + 1);
+    if (cc->value.data == NULL) {
+        return NGX_ERROR;
+    }
+
+    cc->value.len = ngx_sprintf(cc->value.data, "max-age=%T", conf->expires)
+                    - cc->value.data;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_add_header(ngx_http_request_t *r, ngx_http_header_val_t *hv,
+    ngx_str_t *value)
+{
+    ngx_table_elt_t  *h;
 
-            if (h[i].lengths == NULL) {
-                out->value = h[i].value.value;
-                continue;
-            }
+    h = ngx_list_push(&r->headers_out.headers);
+    if (h == NULL) {
+        return NGX_ERROR;
+    }
+
+    h->hash = hv->value.hash;
+    h->key = hv->value.key;
+    h->value = *value;
+
+    return NGX_OK;
+}
+
 
-            if (ngx_http_script_run(r, &out->value, h[i].lengths->elts, 0,
-                                    h[i].values->elts)
-                == NULL)
-            {
-                return NGX_ERROR;
-            }
+static ngx_int_t
+ngx_http_add_cache_control(ngx_http_request_t *r, ngx_http_header_val_t *hv,
+    ngx_str_t *value)
+{
+    ngx_table_elt_t  *cc, **ccp;
+
+    ccp = r->headers_out.cache_control.elts;
+
+    if (ccp == NULL) {
+
+        if (ngx_array_init(&r->headers_out.cache_control, r->pool,
+                           1, sizeof(ngx_table_elt_t *))
+            != NGX_OK)
+        {
+            return NGX_ERROR;
         }
     }
 
-    return ngx_http_next_header_filter(r);
+    ccp = ngx_array_push(&r->headers_out.cache_control);
+    if (ccp == NULL) {
+        return NGX_ERROR;
+    }
+
+    cc = ngx_list_push(&r->headers_out.headers);
+    if (cc == NULL) {
+        return NGX_ERROR;
+    }
+
+    cc->hash = 1;
+    cc->key.len = sizeof("Cache-Control") - 1;
+    cc->key.data = (u_char *) "Cache-Control";
+    cc->value = *value;
+
+    *ccp = cc;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_set_last_modified(ngx_http_request_t *r, ngx_http_header_val_t *hv,
+    ngx_str_t *value)
+{
+    ngx_table_elt_t  *h, **old;
+
+    if (hv->offset) {
+        old = (ngx_table_elt_t **) ((char *) &r->headers_out + hv->offset);
+
+    } else {
+        old = NULL;
+    }
+
+    if (old == NULL || *old == NULL) {
+        h = ngx_list_push(&r->headers_out.headers);
+        if (h == NULL) {
+            return NGX_ERROR;
+        }
+
+    } else {
+        h = *old;
+    }
+
+    h->hash = hv->value.hash;
+    h->key = hv->value.key;
+    h->value = *value;
+
+    r->headers_out.last_modified_time = -1;
+
+    return NGX_OK;
 }
 
 
@@ -294,8 +402,6 @@ ngx_http_headers_create_conf(ngx_conf_t 
     /*
      * set by ngx_pcalloc():
      *
-     *     conf->cache_control.len = 0;
-     *     conf->cache_control.data = NULL;
      *     conf->headers = NULL;
      */
 
@@ -313,11 +419,7 @@ ngx_http_headers_merge_conf(ngx_conf_t *
 
     if (conf->expires == NGX_HTTP_EXPIRES_UNSET) {
         conf->expires = (prev->expires == NGX_HTTP_EXPIRES_UNSET) ?
-                                          NGX_HTTP_EXPIRES_OFF : prev->expires;
-    }
-
-    if (conf->cache_control.data == NULL) {
-        conf->cache_control = prev->cache_control;
+                            NGX_HTTP_EXPIRES_OFF : prev->expires;
     }
 
     if (conf->headers == NULL) {
@@ -406,16 +508,13 @@ ngx_http_headers_add(ngx_conf_t *cf, ngx
 
     ngx_int_t                   n;
     ngx_str_t                  *value;
+    ngx_uint_t                  i;
     ngx_http_header_val_t      *h;
+    ngx_http_set_header_t      *sh;
     ngx_http_script_compile_t   sc;
 
     value = cf->args->elts;
 
-    if (ngx_strcasecmp(value[1].data, (u_char *) "cache-control") == 0) {
-        hcf->cache_control = value[2];
-        return NGX_CONF_OK;
-    }
-
     if (hcf->headers == NULL) {
         hcf->headers = ngx_array_create(cf->pool, 1,
                                         sizeof(ngx_http_header_val_t));
@@ -432,9 +531,22 @@ ngx_http_headers_add(ngx_conf_t *cf, ngx
     h->value.hash = 1;
     h->value.key = value[1];
     h->value.value = value[2];
+    h->offset = 0;
+    h->handler = ngx_http_add_header;
     h->lengths = NULL;
     h->values = NULL;
 
+    sh = ngx_http_set_headers;
+    for (i = 0; sh[i].name.len; i++) {
+        if (ngx_strcasecmp(value[1].data, sh[i].name.data) != 0) {
+            continue;
+        }
+
+        h->offset = sh[i].offset;
+        h->handler = sh[i].handler;
+        break;
+    }
+
     n = ngx_http_script_variables_count(&value[2]);
 
     if (n == 0) {
--- a/src/http/modules/ngx_http_index_module.c
+++ b/src/http/modules/ngx_http_index_module.c
@@ -38,7 +38,7 @@ typedef struct {
 
 
 static ngx_int_t ngx_http_index_test_dir(ngx_http_request_t *r,
-    ngx_http_index_ctx_t *ctx);
+    ngx_http_core_loc_conf_t *clcf, ngx_http_index_ctx_t *ctx);
 static ngx_int_t ngx_http_index_error(ngx_http_request_t *r,
     ngx_http_index_ctx_t *ctx, ngx_err_t err);
 
@@ -59,17 +59,6 @@ static ngx_command_t  ngx_http_index_com
       0,
       NULL },
 
-#if (NGX_HTTP_CACHE)
-
-    { ngx_string("index_cache"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE3,
-      ngx_http_set_cache_slot,
-      NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_index_loc_conf_t, index_cache),
-      NULL },
-
-#endif
-
       ngx_null_command
 };
 
@@ -120,16 +109,13 @@ ngx_http_index_handler(ngx_http_request_
 {
     u_char                       *last;
     size_t                        len;
-    ngx_fd_t                      fd;
     ngx_int_t                     rc;
-    ngx_err_t                     err;
-    ngx_str_t                     uri;
+    ngx_str_t                     path, uri;
     ngx_log_t                    *log;
     ngx_uint_t                    i;
     ngx_http_index_t             *index;
     ngx_http_index_ctx_t         *ctx;
-    ngx_pool_cleanup_t           *cln;
-    ngx_pool_cleanup_file_t      *clnf;
+    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;
@@ -151,13 +137,14 @@ ngx_http_index_handler(ngx_http_request_
 
     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);
+
     /*
      * we use context because the handler supports an async file opening,
      * and may be called several times
      */
 
-    ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module);
-
     ctx = ngx_http_get_module_ctx(r, ngx_http_index_module);
     if (ctx == NULL) {
 
@@ -214,12 +201,16 @@ ngx_http_index_handler(ngx_http_request_
             ctx->index.data = last;
         }
 
+        path.data = ctx->path.data;
+
         if (index[i].values == NULL) {
 
             /* index[i].name.len includes the terminating '\0' */
 
             ngx_memcpy(ctx->index.data, index[i].name.data, index[i].name.len);
 
+            path.len = (ctx->index.data + index[i].name.len - 1) - path.data;
+
         } else {
             e.ip = index[i].values->elts;
             e.pos = ctx->index.data;
@@ -234,39 +225,46 @@ ngx_http_index_handler(ngx_http_request_
                 return ngx_http_internal_redirect(r, &ctx->index, &r->args);
             }
 
+            path.len = e.pos - path.data;
+
             *e.pos++ = '\0';
         }
 
         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
-                       "open index \"%s\"", ctx->path.data);
+                       "open index \"%V\"", &path);
 
-        cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_pool_cleanup_file_t));
-        if (cln == NULL) {
-            return NGX_HTTP_INTERNAL_SERVER_ERROR;
-        }
+        of.test_dir = 0;
+        of.retest = clcf->open_file_cache_retest;
+        of.errors = clcf->open_file_cache_errors;
+        of.events = clcf->open_file_cache_events;
 
-        fd = ngx_open_file(ctx->path.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
+        rc = ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool);
 
-        if (fd == (ngx_fd_t) NGX_AGAIN) {
+#if 0
+        if (rc == NGX_AGAIN) {
             ctx->current = i;
             return NGX_AGAIN;
         }
+#endif
 
-        if (fd == NGX_INVALID_FILE) {
-            err = ngx_errno;
+        if (rc == NGX_ERROR) {
 
-            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, err,
+            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, of.err,
                            ngx_open_file_n " \"%s\" failed", ctx->path.data);
 
-            if (err == NGX_ENOTDIR) {
-                return ngx_http_index_error(r, ctx, err);
+            if (of.err == 0) {
+                return NGX_HTTP_INTERNAL_SERVER_ERROR;
+            }
 
-            } else if (err == NGX_EACCES) {
-                return ngx_http_index_error(r, ctx, err);
+            if (of.err == NGX_ENOTDIR) {
+                return ngx_http_index_error(r, ctx, of.err);
+
+            } else if (of.err == NGX_EACCES) {
+                return ngx_http_index_error(r, ctx, of.err);
             }
 
             if (!ctx->tested) {
-                rc = ngx_http_index_test_dir(r, ctx);
+                rc = ngx_http_index_test_dir(r, clcf, ctx);
 
                 if (rc != NGX_OK) {
                     return rc;
@@ -275,25 +273,16 @@ ngx_http_index_handler(ngx_http_request_
                 ctx->tested = 1;
             }
 
-            if (err == NGX_ENOENT) {
+            if (of.err == NGX_ENOENT) {
                 continue;
             }
 
-            ngx_log_error(NGX_LOG_ERR, log, err,
+            ngx_log_error(NGX_LOG_ERR, log, of.err,
                           ngx_open_file_n " \"%s\" failed", ctx->path.data);
 
             return NGX_HTTP_INTERNAL_SERVER_ERROR;
         }
 
-        cln->handler = ngx_pool_cleanup_file;
-        clnf = cln->data;
-
-        clnf->fd = fd;
-        clnf->name = ctx->path.data;
-        clnf->log = r->pool->log;
-
-        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
-
         uri.len = r->uri.len + ctx->index.len - 1;
 
         if (!clcf->alias) {
@@ -317,43 +306,53 @@ ngx_http_index_handler(ngx_http_request_
 
 
 static ngx_int_t
-ngx_http_index_test_dir(ngx_http_request_t *r, ngx_http_index_ctx_t *ctx)
+ngx_http_index_test_dir(ngx_http_request_t *r, ngx_http_core_loc_conf_t *clcf,
+    ngx_http_index_ctx_t *ctx)
 {
-    u_char           c;
-    ngx_uint_t       i;
-    ngx_err_t        err;
-    ngx_file_info_t  fi;
+    u_char                c;
+    ngx_str_t             path;
+    ngx_uint_t            i;
+    ngx_open_file_info_t  of;
 
     c = *(ctx->index.data - 1);
     i = (c == '/') ? 1 : 0;
     *(ctx->index.data - i) = '\0';
 
-    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "http index check dir: \"%s\"", ctx->path.data);
+    path.len = (ctx->index.data - i) - ctx->path.data;
+    path.data = ctx->path.data;
 
-    if (ngx_file_info(ctx->path.data, &fi) == -1) {
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http index check dir: \"%V\"", &path);
 
-        err = ngx_errno;
+    of.test_dir = 1;
+    of.retest = clcf->open_file_cache_retest;
+    of.errors = clcf->open_file_cache_errors;
 
-        if (err == NGX_ENOENT) {
-            *(ctx->index.data - i) = c;
-            return ngx_http_index_error(r, ctx, err);
+    if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
+        != NGX_OK)
+    {
+        if (of.err) {
+
+            if (of.err == NGX_ENOENT) {
+                *(ctx->index.data - i) = c;
+                return ngx_http_index_error(r, ctx, of.err);
+            }
+
+            ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
+                          ngx_open_file_n " \"%s\" failed", path.data);
         }
 
-        ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
-                      ngx_file_info_n " \"%s\" failed", ctx->path.data);
-
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
     *(ctx->index.data - i) = c;
 
-    if (ngx_is_dir(&fi)) {
+    if (of.is_dir) {
         return NGX_OK;
     }
 
     ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
-                  "\"%s\" is not a directory", ctx->path.data);
+                  "\"%s\" is not a directory", path.data);
 
     return NGX_HTTP_INTERNAL_SERVER_ERROR;
 }
--- a/src/http/modules/ngx_http_limit_zone_module.c
+++ b/src/http/modules/ngx_http_limit_zone_module.c
@@ -141,7 +141,7 @@ ngx_http_limit_zone_handler(ngx_http_req
     if (len > 255) {
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                       "the value of the \"%V\" variable "
-                      "is more than 255 bytes: \"%V\"",
+                      "is more than 255 bytes: \"%v\"",
                       &ctx->var, vv);
         return NGX_DECLINED;
     }
--- a/src/http/modules/ngx_http_map_module.c
+++ b/src/http/modules/ngx_http_map_module.c
@@ -151,7 +151,7 @@ ngx_http_map_variable(ngx_http_request_t
     }
 
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "http map: \"%V\" \"%V\"", vv, v);
+                   "http map: \"%v\" \"%v\"", vv, v);
 
     return NGX_OK;
 }
--- a/src/http/modules/ngx_http_memcached_module.c
+++ b/src/http/modules/ngx_http_memcached_module.c
@@ -167,9 +167,9 @@ ngx_http_memcached_handler(ngx_http_requ
         return NGX_HTTP_NOT_ALLOWED;
     }
 
-    rc = ngx_http_discard_body(r);
+    rc = ngx_http_discard_request_body(r);
 
-    if (rc != NGX_OK && rc != NGX_AGAIN) {
+    if (rc != NGX_OK) {
         return rc;
     }
 
--- a/src/http/modules/ngx_http_realip_module.c
+++ b/src/http/modules/ngx_http_realip_module.c
@@ -188,6 +188,7 @@ ngx_http_realip_from(ngx_conf_t *cf, ngx
 {
     ngx_http_realip_loc_conf_t *rlcf = conf;
 
+    ngx_int_t                 rc;
     ngx_str_t                *value;
     ngx_inet_cidr_t           in_cidr;
     ngx_http_realip_from_t   *from;
@@ -215,12 +216,19 @@ ngx_http_realip_from(ngx_conf_t *cf, ngx
         return NGX_CONF_OK;
     }
 
-    if (ngx_ptocidr(&value[1], &in_cidr) == NGX_ERROR) {
+    rc = ngx_ptocidr(&value[1], &in_cidr);
+
+    if (rc == NGX_ERROR) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"",
                            &value[1]);
         return NGX_CONF_ERROR;
     }
 
+    if (rc == NGX_DONE) {
+        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                           "low address bits of %V are meaningless", &value[1]);
+    }
+
     from->mask = in_cidr.mask;
     from->addr = in_cidr.addr;
 
--- a/src/http/modules/ngx_http_referer_module.c
+++ b/src/http/modules/ngx_http_referer_module.c
@@ -11,9 +11,27 @@
 
 #define NGX_HTTP_REFERER_NO_URI_PART  ((void *) 4)
 
+#if (NGX_PCRE)
+
+typedef struct {
+    ngx_regex_t             *regex;
+    ngx_str_t                name;
+} ngx_http_referer_regex_t;
+
+#else
+
+#define ngx_regex_t          void
+
+#endif
+
+
 typedef struct {
     ngx_hash_combined_t      hash;
 
+#if (NGX_PCRE)
+    ngx_array_t             *regex;
+#endif
+
     ngx_flag_t               no_referer;
     ngx_flag_t               blocked_referer;
 
@@ -28,6 +46,8 @@ static char *ngx_http_valid_referers(ngx
     void *conf);
 static char *ngx_http_add_referer(ngx_conf_t *cf, ngx_hash_keys_arrays_t *keys,
     ngx_str_t *value, ngx_str_t *uri);
+static char *ngx_http_add_regex_referer(ngx_conf_t *cf,
+    ngx_http_referer_conf_t *rlcf, ngx_str_t *name, ngx_regex_t *regex);
 static int ngx_libc_cdecl ngx_http_cmp_referer_wildcards(const void *one,
     const void *two);
 
@@ -80,18 +100,27 @@ static ngx_int_t
 ngx_http_referer_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
      uintptr_t data)
 {
-    u_char                   *p, *ref, *last;
-    size_t                    len;
-    ngx_str_t                *uri;
-    ngx_uint_t                i, key;
-    ngx_http_referer_conf_t  *rlcf;
-    u_char                    buf[256];
+    u_char                    *p, *ref, *last;
+    size_t                     len;
+    ngx_str_t                 *uri;
+    ngx_uint_t                 i, key;
+    ngx_http_referer_conf_t   *rlcf;
+    u_char                     buf[256];
+#if (NGX_PCRE)
+    ngx_int_t                  n;
+    ngx_str_t                  referer;
+    ngx_http_referer_regex_t  *regex;
+#endif
 
     rlcf = ngx_http_get_module_loc_conf(r, ngx_http_referer_module);
 
     if (rlcf->hash.hash.buckets == NULL
         && rlcf->hash.wc_head == NULL
-        && rlcf->hash.wc_tail == NULL)
+        && rlcf->hash.wc_tail == NULL
+#if (NGX_PCRE)
+        && rlcf->regex == NULL
+#endif
+       )
     {
         goto valid;
     }
@@ -135,14 +164,44 @@ ngx_http_referer_variable(ngx_http_reque
         }
     }
 
-    len = p - ref;
-
-    uri = ngx_hash_find_combined(&rlcf->hash, key, buf, len);
+    uri = ngx_hash_find_combined(&rlcf->hash, key, buf, p - ref);
 
     if (uri) {
         goto uri;
     }
 
+#if (NGX_PCRE)
+
+    if (rlcf->regex) {
+
+        referer.len = len - 7;
+        referer.data = ref;
+
+        regex = rlcf->regex->elts;
+
+        for (i = 0; i < rlcf->regex->nelts; i++) {
+            n = ngx_regex_exec(regex[i].regex, &referer, NULL, 0);
+
+            if (n == NGX_REGEX_NO_MATCHED) {
+                continue;
+            }
+
+            if (n < 0) {
+                ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                              ngx_regex_exec_n
+                              " failed: %d on \"%V\" using \"%V\"",
+                              n, &referer, &regex[i].name);
+                return NGX_ERROR;
+            }
+
+            /* match */
+
+            goto valid;
+        }
+    }
+
+#endif
+
 invalid:
 
     *v = ngx_http_variable_true_value;
@@ -357,6 +416,21 @@ ngx_http_valid_referers(ngx_conf_t *cf, 
 
             sn = cscf->server_names.elts;
             for (n = 0; n < cscf->server_names.nelts; n++) {
+
+#if (NGX_PCRE)
+                if (sn[n].regex) {
+
+                    if (ngx_http_add_regex_referer(cf, rlcf, &sn[n].name,
+                                                   sn[n].regex)
+                        != NGX_OK)
+                    {
+                        return NGX_CONF_ERROR;
+                    }
+
+                    continue;
+                }
+#endif
+
                 if (ngx_http_add_referer(cf, rlcf->keys, &sn[n].name, &uri)
                     != NGX_OK)
                 {
@@ -367,6 +441,15 @@ ngx_http_valid_referers(ngx_conf_t *cf, 
             continue;
         }
 
+        if (value[i].data[0] == '~') {
+            if (ngx_http_add_regex_referer(cf, rlcf, &value[i], NULL) != NGX_OK)
+            {
+                return NGX_CONF_ERROR;
+            }
+
+            continue;
+        }
+
         p = (u_char *) ngx_strchr(value[i].data, '/');
 
         if (p) {
@@ -423,6 +506,64 @@ ngx_http_add_referer(ngx_conf_t *cf, ngx
 }
 
 
+static char *
+ngx_http_add_regex_referer(ngx_conf_t *cf, ngx_http_referer_conf_t *rlcf,
+    ngx_str_t *name, ngx_regex_t *regex)
+{
+#if (NGX_PCRE)
+    ngx_str_t                  err;
+    ngx_http_referer_regex_t  *rr;
+    u_char                     errstr[NGX_MAX_CONF_ERRSTR];
+
+    if (rlcf->regex == NULL) {
+        rlcf->regex = ngx_array_create(cf->pool, 2,
+                                       sizeof(ngx_http_referer_regex_t));
+        if (rlcf->regex == NULL) {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    rr = ngx_array_push(rlcf->regex);
+    if (rr == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    if (regex) {
+        rr->regex = regex;
+        rr->name = *name;
+
+        return NGX_CONF_OK;
+    }
+
+    err.len = NGX_MAX_CONF_ERRSTR;
+    err.data = errstr;
+
+    name->len--;
+    name->data++;
+
+    rr->regex = ngx_regex_compile(name, NGX_REGEX_CASELESS, cf->pool, &err);
+
+    if (rr->regex == NULL) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data);
+        return NGX_CONF_ERROR;
+    }
+
+    rr->name = *name;
+
+    return NGX_CONF_OK;
+
+#else
+
+    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                       "the using of the regex \"%V\" requires PCRE library",
+                       name);
+
+    return NGX_CONF_ERROR;
+
+#endif
+}
+
+
 static int ngx_libc_cdecl
 ngx_http_cmp_referer_wildcards(const void *one, const void *two)
 {
--- a/src/http/modules/ngx_http_static_module.c
+++ b/src/http/modules/ngx_http_static_module.c
@@ -9,35 +9,10 @@
 #include <ngx_http.h>
 
 
-typedef struct {
-    ngx_http_cache_hash_t  *redirect_cache;
-} ngx_http_static_loc_conf_t;
-
-
 static ngx_int_t ngx_http_static_handler(ngx_http_request_t *r);
-static void *ngx_http_static_create_loc_conf(ngx_conf_t *cf);
-static char *ngx_http_static_merge_loc_conf(ngx_conf_t *cf,
-    void *parent, void *child);
 static ngx_int_t ngx_http_static_init(ngx_conf_t *cf);
 
 
-static ngx_command_t  ngx_http_static_commands[] = {
-
-#if (NGX_HTTP_CACHE)
-
-    { ngx_string("redirect_cache"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE3,
-      ngx_http_set_cache_slot,
-      NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_static_loc_conf_t, redirect_cache),
-      NULL },
-
-#endif
-
-      ngx_null_command
-};
-
-
 ngx_http_module_t  ngx_http_static_module_ctx = {
     NULL,                                  /* preconfiguration */
     ngx_http_static_init,                  /* postconfiguration */
@@ -48,15 +23,15 @@ ngx_http_module_t  ngx_http_static_modul
     NULL,                                  /* create server configuration */
     NULL,                                  /* merge server configuration */
 
-    ngx_http_static_create_loc_conf,       /* create location configuration */
-    ngx_http_static_merge_loc_conf         /* merge location configuration */
+    NULL,                                  /* create location configuration */
+    NULL                                   /* merge location configuration */
 };
 
 
 ngx_module_t  ngx_http_static_module = {
     NGX_MODULE_V1,
     &ngx_http_static_module_ctx,           /* module context */
-    ngx_http_static_commands,              /* module directives */
+    NULL,                                  /* module directives */
     NGX_HTTP_MODULE,                       /* module type */
     NULL,                                  /* init master */
     NULL,                                  /* init module */
@@ -75,16 +50,13 @@ ngx_http_static_handler(ngx_http_request
     u_char                    *last, *location;
     size_t                     root;
     ngx_fd_t                   fd;
+    ngx_str_t                  path;
     ngx_int_t                  rc;
     ngx_uint_t                 level;
-    ngx_str_t                  path;
-    ngx_err_t                  err;
     ngx_log_t                 *log;
     ngx_buf_t                 *b;
     ngx_chain_t                out;
-    ngx_file_info_t            fi;
-    ngx_pool_cleanup_t        *cln;
-    ngx_pool_cleanup_file_t   *clnf;
+    ngx_open_file_info_t       of;
     ngx_http_core_loc_conf_t  *clcf;
 
     if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
@@ -100,9 +72,9 @@ ngx_http_static_handler(ngx_http_request
         return NGX_DECLINED;
     }
 
-    rc = ngx_http_discard_body(r);
+    rc = ngx_http_discard_request_body(r);
 
-    if (rc != NGX_OK && rc != NGX_AGAIN) {
+    if (rc != NGX_OK) {
         return rc;
     }
 
@@ -118,75 +90,69 @@ ngx_http_static_handler(ngx_http_request
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
+    path.len = last - path.data;
+
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
                    "http filename: \"%s\"", path.data);
 
-    cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_pool_cleanup_file_t));
-    if (cln == NULL) {
-        return NGX_HTTP_INTERNAL_SERVER_ERROR;
-    }
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
 
-    fd = ngx_open_file(path.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
+    of.test_dir = 0;
+    of.retest = clcf->open_file_cache_retest;
+    of.errors = clcf->open_file_cache_errors;
+    of.events = clcf->open_file_cache_events;
+
+    rc = ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool);
 
-    if (fd == NGX_INVALID_FILE) {
-        err = ngx_errno;
+    if (rc == NGX_ERROR) {
+
+        switch (of.err) {
 
-        if (err == NGX_ENOENT
-            || err == NGX_ENOTDIR
-            || err == NGX_ENAMETOOLONG)
-        {
+        case 0:
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+
+        case NGX_ENOENT:
+        case NGX_ENOTDIR:
+        case NGX_ENAMETOOLONG:
+
             level = NGX_LOG_ERR;
             rc = NGX_HTTP_NOT_FOUND;
+            break;
 
-        } else if (err == NGX_EACCES) {
+        case NGX_EACCES:
+
             level = NGX_LOG_ERR;
             rc = NGX_HTTP_FORBIDDEN;
+            break;
 
-        } else {
+        default:
+
             level = NGX_LOG_CRIT;
             rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+            break;
         }
 
-        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
-
         if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) {
-            ngx_log_error(level, log, err,
+            ngx_log_error(level, log, of.err,
                           ngx_open_file_n " \"%s\" failed", path.data);
         }
 
         return rc;
     }
 
+    fd = of.fd;
+
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http static fd: %d", fd);
 
-    if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
-        ngx_log_error(NGX_LOG_CRIT, log, ngx_errno,
-                      ngx_fd_info_n " \"%s\" failed", path.data);
-
-        if (ngx_close_file(fd) == NGX_FILE_ERROR) {
-            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
-                          ngx_close_file_n " \"%s\" failed", path.data);
-        }
-
-        return NGX_HTTP_INTERNAL_SERVER_ERROR;
-    }
-
-    if (ngx_is_dir(&fi)) {
+    if (of.is_dir) {
 
         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "http dir");
 
-        if (ngx_close_file(fd) == NGX_FILE_ERROR) {
-            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
-                          ngx_close_file_n " \"%s\" failed", path.data);
-        }
-
         r->headers_out.location = ngx_palloc(r->pool, sizeof(ngx_table_elt_t));
         if (r->headers_out.location == NULL) {
             return NGX_HTTP_INTERNAL_SERVER_ERROR;
         }
 
-        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
-
         if (!clcf->alias && clcf->root_lengths == NULL) {
             location = path.data + clcf->root.len;
 
@@ -214,15 +180,10 @@ ngx_http_static_handler(ngx_http_request
 
 #if !(NGX_WIN32) /* the not regular files are probably Unix specific */
 
-    if (!ngx_is_file(&fi)) {
+    if (!of.is_file) {
         ngx_log_error(NGX_LOG_CRIT, log, ngx_errno,
                       "\"%s\" is not a regular file", path.data);
 
-        if (ngx_close_file(fd) == NGX_FILE_ERROR) {
-            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
-                          ngx_close_file_n " \"%s\" failed", path.data);
-        }
-
         return NGX_HTTP_NOT_FOUND;
     }
 
@@ -230,22 +191,15 @@ ngx_http_static_handler(ngx_http_request
 
     log->action = "sending response to client";
 
-    cln->handler = ngx_pool_cleanup_file;
-    clnf = cln->data;
-
-    clnf->fd = fd;
-    clnf->name = path.data;
-    clnf->log = r->pool->log;
-
     r->headers_out.status = NGX_HTTP_OK;
-    r->headers_out.content_length_n = ngx_file_size(&fi);
-    r->headers_out.last_modified_time = ngx_file_mtime(&fi);
+    r->headers_out.content_length_n = of.size;
+    r->headers_out.last_modified_time = of.mtime;
 
     if (ngx_http_set_content_type(r) != NGX_OK) {
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
-    if (r != r->main && ngx_file_size(&fi) == 0) {
+    if (r != r->main && of.size == 0) {
         return ngx_http_send_header(r);
     }
 
@@ -270,7 +224,7 @@ ngx_http_static_handler(ngx_http_request
     }
 
     b->file_pos = 0;
-    b->file_last = ngx_file_size(&fi);
+    b->file_last = of.size;
 
     b->in_file = b->file_last ? 1: 0;
     b->last_buf = (r == r->main) ? 1: 0;
@@ -287,36 +241,6 @@ ngx_http_static_handler(ngx_http_request
 }
 
 
-static void *
-ngx_http_static_create_loc_conf(ngx_conf_t *cf)
-{
-    ngx_http_static_loc_conf_t  *conf;
-
-    conf = ngx_palloc(cf->pool, sizeof(ngx_http_static_loc_conf_t));
-    if (conf == NULL) {
-        return NGX_CONF_ERROR;
-    }
-
-    conf->redirect_cache = NULL;
-
-    return conf;
-}
-
-
-static char *
-ngx_http_static_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
-{
-    ngx_http_static_loc_conf_t  *prev = parent;
-    ngx_http_static_loc_conf_t  *conf = child;
-
-    if (conf->redirect_cache == NULL) {
-        conf->redirect_cache = prev->redirect_cache;
-    }
-
-    return NGX_CONF_OK;
-}
-
-
 static ngx_int_t
 ngx_http_static_init(ngx_conf_t *cf)
 {
--- a/src/http/modules/ngx_http_stub_status_module.c
+++ b/src/http/modules/ngx_http_stub_status_module.c
@@ -69,9 +69,9 @@ static ngx_int_t ngx_http_status_handler
         return NGX_HTTP_NOT_ALLOWED;
     }
 
-    rc = ngx_http_discard_body(r);
+    rc = ngx_http_discard_request_body(r);
 
-    if (rc != NGX_OK && rc != NGX_AGAIN) {
+    if (rc != NGX_OK) {
         return rc;
     }
 
--- a/src/http/modules/ngx_http_upstream_ip_hash_module.c
+++ b/src/http/modules/ngx_http_upstream_ip_hash_module.c
@@ -140,7 +140,7 @@ ngx_http_upstream_get_ip_hash_peer(ngx_p
 
     /* TODO: cached */
 
-    if (iphp->tries > 20 || iphp->rrp.peers->number == 1) {
+    if (iphp->tries > 20 || iphp->rrp.peers->single) {
         return iphp->get_rr_peer(pc, &iphp->rrp);
     }
 
@@ -160,7 +160,7 @@ ngx_http_upstream_get_ip_hash_peer(ngx_p
         p = hash % iphp->rrp.peers->number;
 
         n = p / (8 * sizeof(uintptr_t));
-        m = 1 << p % (8 * sizeof(uintptr_t));
+        m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));
 
         if (!(iphp->rrp.tried[n] & m)) {
 
@@ -195,6 +195,8 @@ ngx_http_upstream_get_ip_hash_peer(ngx_p
         }
     }
 
+    iphp->rrp.current = p;
+
     pc->sockaddr = peer->sockaddr;
     pc->socklen = peer->socklen;
     pc->name = &peer->name;
--- a/src/http/modules/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -47,7 +47,7 @@ our @EXPORT = qw(
     HTTP_INSUFFICIENT_STORAGE
 );
 
-our $VERSION = '0.6.6';
+our $VERSION = '0.6.11';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/modules/perl/nginx.xs
+++ b/src/http/modules/perl/nginx.xs
@@ -131,6 +131,8 @@ send_http_header(r, ...)
             XSRETURN_EMPTY;
         }
 
+        r->headers_out.content_type_len = r->headers_out.content_type.len;
+
     } else {
         if (ngx_http_set_content_type(r) != NGX_OK) {
             XSRETURN_EMPTY;
@@ -425,6 +427,17 @@ request_body_file(r)
 
 
 void
+discard_request_body(r)
+    CODE:
+
+    ngx_http_request_t  *r;
+
+    ngx_http_perl_set_request(r);
+
+    ngx_http_discard_request_body(r);
+
+
+void
 header_out(r, key, value)
     CODE:
 
@@ -461,8 +474,6 @@ header_out(r, key, value)
         r->headers_out.content_length = header;
     }
 
-    XSRETURN_EMPTY;
-
 
 void
 filename(r)
@@ -589,22 +600,20 @@ print(r, ...)
 
     (void) ngx_http_perl_output(r, b);
 
-    XSRETURN_EMPTY;
-
 
 void
 sendfile(r, filename, offset = -1, bytes = 0)
     CODE:
 
-    ngx_http_request_t       *r;
-    char                     *filename;
-    int                       offset;
-    size_t                    bytes;
-    ngx_fd_t                  fd;
-    ngx_buf_t                *b;
-    ngx_file_info_t           fi;
-    ngx_pool_cleanup_t       *cln;
-    ngx_pool_cleanup_file_t  *clnf;
+    ngx_http_request_t        *r;
+    char                      *filename;
+    int                        offset;
+    size_t                     bytes;
+    ngx_int_t                  rc;
+    ngx_str_t                  path;
+    ngx_buf_t                 *b;
+    ngx_open_file_info_t       of;
+    ngx_http_core_loc_conf_t  *clcf;
 
     ngx_http_perl_set_request(r);
 
@@ -627,14 +636,30 @@ sendfile(r, filename, offset = -1, bytes
         XSRETURN_EMPTY;
     }
 
-    cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_pool_cleanup_file_t));
-    if (cln == NULL) {
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+    
+    of.test_dir = 0;
+    of.retest = clcf->open_file_cache_retest; 
+    of.errors = clcf->open_file_cache_errors; 
+    of.events = clcf->open_file_cache_events; 
+
+    path.len = ngx_strlen(filename);
+
+    path.data = ngx_pcalloc(r->pool, path.len + 1);
+    if (path.data == NULL) {
         XSRETURN_EMPTY;
     }
 
-    fd = ngx_open_file((u_char *) filename, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
+    (void) ngx_cpystrn(path.data, filename, path.len + 1);
+ 
+    rc = ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool);
 
-    if (fd == NGX_INVALID_FILE) {
+    if (rc == NGX_ERROR) {
+
+        if (of.err == 0) {
+            XSRETURN_EMPTY;
+        }
+
         ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
                       ngx_open_file_n " \"%s\" failed", filename);
         XSRETURN_EMPTY;
@@ -645,40 +670,19 @@ sendfile(r, filename, offset = -1, bytes
     }
 
     if (bytes == 0) {
-        if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
-            ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
-                          ngx_fd_info_n " \"%s\" failed", filename);
-
-            if (ngx_close_file(fd) == NGX_FILE_ERROR) {
-                ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
-                              ngx_close_file_n " \"%s\" failed", filename);
-            }
-
-            XSRETURN_EMPTY;
-        }
-
-        bytes = ngx_file_size(&fi) - offset;
+        bytes = of.size - offset;
     }
 
-    cln->handler = ngx_pool_cleanup_file;
-    clnf = cln->data;
-
-    clnf->fd = fd;
-    clnf->name = (u_char *) "";
-    clnf->log = r->pool->log;
-
     b->in_file = 1;
 
     b->file_pos = offset;
     b->file_last = offset + bytes;
 
-    b->file->fd = fd;
+    b->file->fd = of.fd;
     b->file->log = r->connection->log;
 
     (void) ngx_http_perl_output(r, b);
 
-    XSRETURN_EMPTY;
-
 
 void
 flush(r)
@@ -744,8 +748,6 @@ allow_ranges(r)
 
     r->allow_ranges = 1;
 
-    XSRETURN_EMPTY;
-
 
 void
 unescape(r, text, type = 0)
@@ -942,8 +944,6 @@ sleep(r, sleep, next)
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "perl sleep: %d", ctx->sleep);
 
-    XSRETURN_EMPTY;
-
 
 void
 log_error(r, err, msg)
@@ -974,5 +974,3 @@ log_error(r, err, msg)
     p = (u_char *) SvPV(msg, len);
 
     ngx_log_error(NGX_LOG_ERR, r->connection->log, e, "perl: %s", p);
-
-    XSRETURN_EMPTY;
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -98,6 +98,9 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
     ngx_http_core_loc_conf_t    *clcf;
     ngx_http_phase_handler_pt    checker;
     ngx_http_core_main_conf_t   *cmcf;
+#if (NGX_PCRE)
+    ngx_uint_t                   regex;
+#endif
 #if (NGX_WIN32)
     ngx_iocp_conf_t             *iocpcf;
 #endif
@@ -655,10 +658,21 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
                 return NGX_CONF_ERROR;
             }
 
+#if (NGX_PCRE)
+            regex = 0;
+#endif
+
             name = in_addr[a].names.elts;
 
             for (s = 0; s < in_addr[a].names.nelts; s++) {
 
+#if (NGX_PCRE)
+                if (name[s].regex) {
+                    regex++;
+                    continue;
+                }
+#endif
+
                 rc = ngx_hash_add_key(&ha, &name[s].name, name[s].core_srv_conf,
                                       NGX_HASH_WILDCARD_KEY);
 
@@ -740,6 +754,27 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
             }
 
             ngx_destroy_pool(ha.temp_pool);
+
+#if (NGX_PCRE)
+
+            if (regex == 0) {
+                continue;
+            }
+
+            in_addr[a].nregex = regex;
+            in_addr[a].regex = ngx_palloc(cf->pool,
+                                       regex * sizeof(ngx_http_server_name_t));
+
+            if (in_addr[a].regex == NULL) {
+                return NGX_CONF_ERROR;
+            }
+
+            for (i = 0, s = 0; s < in_addr[a].names.nelts; s++) {
+                if (name[s].regex) {
+                    in_addr[a].regex[i++] = name[s];
+                }
+            }
+#endif
         }
 
         in_addr = in_port[p].addrs.elts;
@@ -871,9 +906,13 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
                 }
                 hip->addrs[i].virtual_names = vn;
 
-                vn->hash = in_addr[i].hash;
-                vn->wc_head = in_addr[i].wc_head;
-                vn->wc_tail = in_addr[i].wc_tail;
+                vn->names.hash = in_addr[i].hash;
+                vn->names.wc_head = in_addr[i].wc_head;
+                vn->names.wc_tail = in_addr[i].wc_tail;
+#if (NGX_PCRE)
+                vn->nregex = in_addr[i].nregex;
+                vn->regex = in_addr[i].regex;
+#endif
             }
 
             if (done) {
@@ -932,7 +971,8 @@ ngx_http_add_address(ngx_conf_t *cf, ngx
 
     if (in_port->addrs.elts == NULL) {
         if (ngx_array_init(&in_port->addrs, cf->temp_pool, 4,
-                           sizeof(ngx_http_conf_in_addr_t)) != NGX_OK)
+                           sizeof(ngx_http_conf_in_addr_t))
+            != NGX_OK)
         {
             return NGX_ERROR;
         }
@@ -949,6 +989,10 @@ ngx_http_add_address(ngx_conf_t *cf, ngx
     in_addr->wc_head = NULL;
     in_addr->wc_tail = NULL;
     in_addr->names.elts = NULL;
+#if (NGX_PCRE)
+    in_addr->nregex = 0;
+    in_addr->regex = NULL;
+#endif
     in_addr->core_srv_conf = cscf;
     in_addr->default_server = lscf->conf.default_server;
     in_addr->bind = lscf->conf.bind;
@@ -981,13 +1025,15 @@ ngx_http_add_names(ngx_conf_t *cf, ngx_h
 
     if (in_addr->names.elts == NULL) {
         if (ngx_array_init(&in_addr->names, cf->temp_pool, 4,
-                           sizeof(ngx_http_server_name_t)) != NGX_OK)
+                           sizeof(ngx_http_server_name_t))
+            != NGX_OK)
         {
             return NGX_ERROR;
         }
     }
 
     server_names = cscf->server_names.elts;
+
     for (i = 0; i < cscf->server_names.nelts; i++) {
 
         for (n = 0; n < server_names[i].name.len; n++) {
@@ -998,7 +1044,6 @@ ngx_http_add_names(ngx_conf_t *cf, ngx_h
         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, cf->log, 0,
                        "name: %V", &server_names[i].name);
 
-
         name = ngx_array_push(&in_addr->names);
         if (name == NULL) {
             return NGX_ERROR;
--- a/src/http/ngx_http.h
+++ b/src/http/ngx_http.h
@@ -98,7 +98,8 @@ size_t ngx_http_get_time(char *buf, time
 
 
 
-ngx_int_t ngx_http_discard_body(ngx_http_request_t *r);
+ngx_int_t ngx_http_discard_request_body(ngx_http_request_t *r);
+void ngx_http_block_reading(ngx_http_request_t *r);
 
 
 extern ngx_module_t  ngx_http_module;
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -61,6 +61,8 @@ static char *ngx_http_core_limit_except(
     void *conf);
 static char *ngx_http_core_error_page(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,
     void *conf);
 static char *ngx_http_core_keepalive(ngx_conf_t *cf, ngx_command_t *cmd,
@@ -448,16 +450,33 @@ static ngx_command_t  ngx_http_core_comm
       0,
       NULL },
 
-#if (NGX_HTTP_CACHE)
-
     { ngx_string("open_file_cache"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE4,
-      ngx_http_set_cache_slot,
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
+      ngx_http_core_open_file_cache,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_core_loc_conf_t, open_file_cache),
+      NULL },
+
+    { ngx_string("open_file_cache_retest"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_sec_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_core_loc_conf_t, open_files),
+      offsetof(ngx_http_core_loc_conf_t, open_file_cache_retest),
       NULL },
 
-#endif
+    { ngx_string("open_file_cache_errors"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_flag_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_core_loc_conf_t, open_file_cache_errors),
+      NULL },
+
+    { ngx_string("open_file_cache_events"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_flag_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_core_loc_conf_t, open_file_cache_events),
+      NULL },
 
       ngx_null_command
 };
@@ -620,6 +639,8 @@ ngx_int_t
 ngx_http_core_find_config_phase(ngx_http_request_t *r,
     ngx_http_phase_handler_t *ph)
 {
+    u_char                    *p;
+    size_t                     len;
     ngx_int_t                  rc;
     ngx_http_core_loc_conf_t  *clcf;
     ngx_http_core_srv_conf_t  *cscf;
@@ -680,7 +701,25 @@ ngx_http_core_find_config_phase(ngx_http
          * r->headers_out.location->key fields
          */
 
-        r->headers_out.location->value = clcf->name;
+        if (r->args.len == 0) {
+            r->headers_out.location->value = clcf->name;
+
+        } else {
+            len = clcf->name.len + 1 + r->args.len;
+            p = ngx_palloc(r->pool, len);
+
+            if (p == NULL) {
+                ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+                return NGX_OK;
+            }
+
+            r->headers_out.location->value.len = len;
+            r->headers_out.location->value.data = p;
+
+            p = ngx_cpymem(p, clcf->name.data, clcf->name.len);
+            *p++ = '?';
+            ngx_memcpy(p, r->args.data, r->args.len);
+        }
 
         ngx_http_finalize_request(r, NGX_HTTP_MOVED_PERMANENTLY);
         return NGX_OK;
@@ -933,14 +972,19 @@ ngx_http_core_find_location(ngx_http_req
     ngx_array_t *locations, ngx_uint_t regex_start, size_t len)
 {
     ngx_int_t                  n, rc;
-    ngx_uint_t                 i, found, noregex;
+    ngx_uint_t                 i, found;
     ngx_http_core_loc_conf_t  *clcf, **clcfp;
+#if (NGX_PCRE)
+    ngx_uint_t                 noregex;
+#endif
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "find location for \"%V\"", &r->uri);
 
     found = 0;
+#if (NGX_PCRE)
     noregex = 0;
+#endif
 
     clcfp = locations->elts;
     for (i = 0; i < locations->nelts; i++) {
@@ -998,9 +1042,12 @@ ngx_http_core_find_location(ngx_http_req
                 break;
             }
 
+            found = 1;
+
             r->loc_conf = clcfp[i]->loc_conf;
+#if (NGX_PCRE)
             noregex = clcfp[i]->noregex;
-            found = 1;
+#endif
         }
     }
 
@@ -2219,7 +2266,7 @@ ngx_http_core_merge_srv_conf(ngx_conf_t 
 #endif
         ls->family = AF_INET;
 
-        ls->conf.backlog = -1;
+        ls->conf.backlog = NGX_LISTEN_BACKLOG;
         ls->conf.rcvbuf = -1;
         ls->conf.sndbuf = -1;
     }
@@ -2332,6 +2379,10 @@ ngx_http_core_create_loc_conf(ngx_conf_t
     lcf->recursive_error_pages = NGX_CONF_UNSET;
     lcf->types_hash_max_size = NGX_CONF_UNSET_UINT;
     lcf->types_hash_bucket_size = NGX_CONF_UNSET_UINT;
+    lcf->open_file_cache = NGX_CONF_UNSET_PTR;
+    lcf->open_file_cache_retest = NGX_CONF_UNSET;
+    lcf->open_file_cache_errors = NGX_CONF_UNSET;
+    lcf->open_file_cache_events = NGX_CONF_UNSET;
 
     return lcf;
 }
@@ -2515,9 +2566,17 @@ ngx_http_core_merge_loc_conf(ngx_conf_t 
     ngx_conf_merge_value(conf->recursive_error_pages,
                               prev->recursive_error_pages, 0);
 
-    if (conf->open_files == NULL) {
-        conf->open_files = prev->open_files;
-    }
+    ngx_conf_merge_ptr_value(conf->open_file_cache,
+                             prev->open_file_cache, NULL);
+
+    ngx_conf_merge_sec_value(conf->open_file_cache_retest,
+                             prev->open_file_cache_retest, 60);
+
+    ngx_conf_merge_sec_value(conf->open_file_cache_errors,
+                             prev->open_file_cache_errors, 0);
+
+    ngx_conf_merge_sec_value(conf->open_file_cache_events,
+                             prev->open_file_cache_events, 0);
 
     return NGX_CONF_OK;
 }
@@ -2570,7 +2629,7 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx
     ls->port = u.port;
     ls->file_name = cf->conf_file->file.name;
     ls->line = cf->conf_file->line;
-    ls->conf.backlog = -1;
+    ls->conf.backlog = NGX_LISTEN_BACKLOG;
     ls->conf.rcvbuf = -1;
     ls->conf.sndbuf = -1;
 
@@ -2692,6 +2751,10 @@ ngx_http_core_server_name(ngx_conf_t *cf
     ngx_str_t               *value, name;
     ngx_uint_t               i;
     ngx_http_server_name_t  *sn;
+#if (NGX_PCRE)
+    ngx_str_t                err;
+    u_char                   errstr[NGX_MAX_CONF_ERRSTR];
+#endif
 
     value = cf->args->elts;
 
@@ -2705,6 +2768,13 @@ ngx_http_core_server_name(ngx_conf_t *cf
             return NGX_CONF_ERROR;
         }
 
+        if (value[1].data[0] == '~') {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "first server name \"%V\" "
+                               "must not be regular expression", &value[1]);
+            return NGX_CONF_ERROR;
+        }
+
         name = value[1];
 
         if (ch == '.') {
@@ -2748,9 +2818,42 @@ ngx_http_core_server_name(ngx_conf_t *cf
             return NGX_CONF_ERROR;
         }
 
+#if (NGX_PCRE)
+        sn->regex = NULL;
+#endif
+        sn->core_srv_conf = cscf;
         sn->name.len = value[i].len;
         sn->name.data = value[i].data;
-        sn->core_srv_conf = cscf;
+
+        if (value[i].data[0] != '~') {
+            continue;
+        }
+
+#if (NGX_PCRE)
+        err.len = NGX_MAX_CONF_ERRSTR;
+        err.data = errstr;
+
+        value[i].len--;
+        value[i].data++;
+
+        sn->regex = ngx_regex_compile(&value[i], NGX_REGEX_CASELESS, cf->pool,
+                                      &err);
+
+        if (sn->regex == NULL) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data);
+            return NGX_CONF_ERROR;
+        }
+
+        sn->name.len = value[i].len;
+        sn->name.data = value[i].data;
+
+#else
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "the using of the regex \"%V\" "
+                           "requires PCRE library", &value[i]);
+
+        return NGX_CONF_ERROR;
+#endif
     }
 
     return NGX_CONF_OK;
@@ -3090,6 +3193,82 @@ ngx_http_core_error_page(ngx_conf_t *cf,
 
 
 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;
+
+    time_t       inactive;
+    ngx_str_t   *value, s;
+    ngx_int_t    max;
+    ngx_uint_t   i;
+
+    if (lcf->open_file_cache != NGX_CONF_UNSET_PTR) {
+        return "is duplicate";
+    }
+
+    value = cf->args->elts;
+
+    max = 0;
+    inactive = 60;
+
+    for (i = 1; i < cf->args->nelts; i++) {
+
+        if (ngx_strncmp(value[i].data, "max=", 4) == 0) {
+
+            max = ngx_atoi(value[i].data + 4, value[i].len - 4);
+            if (max == NGX_ERROR) {
+                return NGX_CONF_ERROR;
+            }
+
+            continue;
+        }
+
+        if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) {
+
+            s.len = value[i].len - 9;
+            s.data = value[i].data + 9;
+
+            inactive = ngx_parse_time(&s, 1);
+            if (inactive < 0) {
+                return NGX_CONF_ERROR;
+            }
+
+            continue;
+        }
+
+        if (ngx_strcmp(value[i].data, "off") == 0) {
+
+            lcf->open_file_cache = NULL;
+
+            continue;
+        }
+
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid \"open_file_cache\" parameter \"%V\"",
+                           &value[i]);
+        return NGX_CONF_ERROR;
+    }
+
+    if (lcf->open_file_cache == NULL) {
+        return NGX_CONF_OK;
+    }
+
+    if (max == 0) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "\"open_file_cache\" must have \"max\" parameter");
+        return NGX_CONF_ERROR;
+    }
+
+    lcf->open_file_cache = ngx_open_file_cache_init(cf->pool, max, inactive);
+    if (lcf->open_file_cache) {
+        return NGX_CONF_OK;
+    }
+
+    return NGX_CONF_ERROR;
+}
+
+
+static char *
 ngx_http_core_error_log(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
@@ -151,8 +151,10 @@ typedef struct {
 
 typedef struct {
     in_addr_t                  addr;
+
     /* the default server configuration for this address:port */
     ngx_http_core_srv_conf_t  *core_srv_conf;
+
     ngx_http_virtual_names_t  *virtual_names;
 } ngx_http_in_addr_t;
 
@@ -180,6 +182,12 @@ typedef struct {
 
     ngx_array_t                names;      /* array of ngx_http_server_name_t */
 
+#if (NGX_PCRE)
+    ngx_uint_t                 nregex;
+    ngx_http_server_name_t    *regex;
+
+#endif
+
     /* the default server configuration for this address:port */
     ngx_http_core_srv_conf_t  *core_srv_conf;
 
@@ -190,10 +198,13 @@ typedef struct {
 } ngx_http_conf_in_addr_t;
 
 
-typedef struct {
+struct ngx_http_server_name_s {
+#if (NGX_PCRE)
+    ngx_regex_t               *regex;
+#endif
+    ngx_http_core_srv_conf_t  *core_srv_conf; /* virtual name server conf */
     ngx_str_t                  name;
-    ngx_http_core_srv_conf_t  *core_srv_conf; /* virtual name server conf */
-} ngx_http_server_name_t;
+};
 
 
 typedef struct {
@@ -279,7 +290,10 @@ struct ngx_http_core_loc_conf_s {
 
     ngx_path_t   *client_body_temp_path;   /* client_body_temp_path */
 
-    ngx_http_cache_hash_t  *open_files;
+    ngx_open_file_cache_t  *open_file_cache;
+    time_t        open_file_cache_retest;
+    ngx_flag_t    open_file_cache_errors;
+    ngx_flag_t    open_file_cache_events;
 
     ngx_log_t    *err_log;
 
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -33,8 +33,7 @@ static void ngx_http_request_handler(ngx
 static ngx_int_t ngx_http_set_write_handler(ngx_http_request_t *r);
 static void ngx_http_writer(ngx_http_request_t *r);
 
-static void ngx_http_block_read(ngx_http_request_t *r);
-static void ngx_http_test_read(ngx_http_request_t *r);
+static void ngx_http_test_reading(ngx_http_request_t *r);
 static void ngx_http_set_keepalive(ngx_http_request_t *r);
 static void ngx_http_keepalive_handler(ngx_event_t *ev);
 static void ngx_http_set_lingering_close(ngx_http_request_t *r);
@@ -76,7 +75,7 @@ ngx_http_header_t  ngx_http_headers_in[]
 
     { ngx_string("If-Modified-Since"),
                  offsetof(ngx_http_headers_in_t, if_modified_since),
-                 ngx_http_process_header_line },
+                 ngx_http_process_unique_header_line },
 
     { ngx_string("User-Agent"), offsetof(ngx_http_headers_in_t, user_agent),
                  ngx_http_process_header_line },
@@ -837,7 +836,7 @@ ngx_http_process_request_headers(ngx_eve
                     ngx_log_error(NGX_LOG_INFO, c->log, 0,
                                   "client sent too long header line: \"%V\"",
                                   &header);
-                    ngx_http_close_request(r, NGX_HTTP_BAD_REQUEST);
+                    ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
                     return;
                 }
             }
@@ -949,7 +948,7 @@ ngx_http_process_request_headers(ngx_eve
         ngx_log_error(NGX_LOG_INFO, c->log, 0,
                       "client sent invalid header line: \"%V\\r...\"",
                       &header);
-        ngx_http_close_request(r, NGX_HTTP_BAD_REQUEST);
+        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
         return;
     }
 }
@@ -1442,7 +1441,7 @@ ngx_http_process_request(ngx_http_reques
 
     c->read->handler = ngx_http_request_handler;
     c->write->handler = ngx_http_request_handler;
-    r->read_event_handler = ngx_http_block_read;
+    r->read_event_handler = ngx_http_block_reading;
 
     ngx_http_handler(r);
 
@@ -1454,18 +1453,56 @@ static void
 ngx_http_find_virtual_server(ngx_http_request_t *r, u_char *host, size_t len,
     ngx_uint_t hash)
 {
-    ngx_http_virtual_names_t  *vn;
     ngx_http_core_loc_conf_t  *clcf;
     ngx_http_core_srv_conf_t  *cscf;
-
-    vn = r->virtual_names;
-
-    cscf = ngx_hash_find_combined(vn, hash, host, len);
+#if (NGX_PCRE)
+    ngx_int_t                  n;
+    ngx_uint_t                 i;
+    ngx_str_t                  name;
+    ngx_http_server_name_t    *sn;
+#endif
+
+    cscf = ngx_hash_find_combined(&r->virtual_names->names, hash, host, len);
 
     if (cscf) {
         goto found;
     }
 
+#if (NGX_PCRE)
+
+    if (r->virtual_names->nregex) {
+
+        name.len = len;
+        name.data = host;
+
+        sn = r->virtual_names->regex;
+
+        for (i = 0; i < r->virtual_names->nregex; i++) {
+
+            n = ngx_regex_exec(sn[i].regex, &name, NULL, 0);
+
+            if (n == NGX_REGEX_NO_MATCHED) {
+                continue;
+            }
+
+            if (n < 0) {
+                ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                              ngx_regex_exec_n
+                              " failed: %d on \"%V\" using \"%V\"",
+                              n, &name, &sn[i].name);
+                return;
+            }
+
+            /* match */
+
+            cscf = sn[i].core_srv_conf;
+
+            goto found;
+        }
+    }
+
+#endif
+
     cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
 
     if (cscf->wildcard) {
@@ -1519,6 +1556,7 @@ ngx_http_request_handler(ngx_event_t *ev
 void
 ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
 {
+    ngx_connection_t          *c;
     ngx_http_request_t        *pr;
     ngx_http_log_ctx_t        *ctx;
     ngx_http_core_loc_conf_t  *clcf;
@@ -1528,7 +1566,9 @@ ngx_http_finalize_request(ngx_http_reque
         return;
     }
 
-    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 finalize request: %d, \"%V?%V\"",
                    rc, &r->uri, &r->args);
 
@@ -1543,10 +1583,7 @@ ngx_http_finalize_request(ngx_http_reque
         rc = r->post_subrequest->handler(r, r->post_subrequest->data, rc);
     }
 
-    if (rc == NGX_ERROR
-        || rc == NGX_HTTP_REQUEST_TIME_OUT
-        || r->connection->error)
-    {
+    if (rc == NGX_ERROR || rc == NGX_HTTP_REQUEST_TIME_OUT || c->error) {
         if (rc > 0 && r->headers_out.status == 0) {
             r->headers_out.status = rc;
         }
@@ -1569,12 +1606,12 @@ ngx_http_finalize_request(ngx_http_reque
         }
 
         if (r == r->main) {
-            if (r->connection->read->timer_set) {
-                ngx_del_timer(r->connection->read);
+            if (c->read->timer_set) {
+                ngx_del_timer(c->read);
             }
 
-            if (r->connection->write->timer_set) {
-                ngx_del_timer(r->connection->write);
+            if (c->write->timer_set) {
+                ngx_del_timer(c->write);
             }
         }
 
@@ -1590,8 +1627,8 @@ ngx_http_finalize_request(ngx_http_reque
 
     r->done = 1;
 
-    if (r != r->connection->data) {
-        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+    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;
@@ -1601,19 +1638,19 @@ ngx_http_finalize_request(ngx_http_reque
 
         pr = r->parent;
 
-        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
                        "http parent request: \"%V?%V\"", &pr->uri, &pr->args);
 
         if (rc != NGX_AGAIN) {
-            r->connection->data = pr;
+            c->data = pr;
         }
 
-        ctx = r->connection->log->data;
+        ctx = c->log->data;
         ctx->current_request = pr;
 
         if (pr->postponed) {
 
-            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
                            "http request: \"%V?%V\" has postponed",
                            &pr->uri, &pr->args);
 
@@ -1627,14 +1664,14 @@ ngx_http_finalize_request(ngx_http_reque
                     r->fast_subrequest = 0;
                 }
 
-                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 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, r->connection->log, 0,
+                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
                                "http wake parent request: \"%V?%V\"",
                                &pr->uri, &pr->args);
 
@@ -1649,7 +1686,7 @@ ngx_http_finalize_request(ngx_http_reque
         return;
     }
 
-    if (r->connection->buffered) {
+    if (c->buffered) {
         (void) ngx_http_set_write_handler(r);
         return;
     }
@@ -1662,16 +1699,21 @@ ngx_http_finalize_request(ngx_http_reque
         return;
     }
 
-    if (r->connection->read->timer_set) {
-        ngx_del_timer(r->connection->read);
+    if (c->read->timer_set) {
+        ngx_del_timer(c->read);
+    }
+
+    if (c->write->timer_set) {
+        c->write->delayed = 0;
+        ngx_del_timer(c->write);
     }
 
-    if (r->connection->write->timer_set) {
-        r->connection->write->delayed = 0;
-        ngx_del_timer(r->connection->write);
+    if (c->destroyed) {
+        return;
     }
 
-    if (r->connection->destroyed) {
+    if (c->read->eof) {
+        ngx_http_close_request(r, 0);
         return;
     }
 
@@ -1702,7 +1744,7 @@ ngx_http_set_write_handler(ngx_http_requ
 
     r->http_state = NGX_HTTP_WRITING_REQUEST_STATE;
 
-    r->read_event_handler = ngx_http_test_read;
+    r->read_event_handler = ngx_http_test_reading;
     r->write_event_handler = ngx_http_writer;
 
     wev = r->connection->write;
@@ -1812,11 +1854,11 @@ ngx_http_writer(ngx_http_request_t *r)
 }
 
 
-static void
-ngx_http_block_read(ngx_http_request_t *r)
+void
+ngx_http_block_reading(ngx_http_request_t *r)
 {
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "http read blocked");
+                   "http reading blocked");
 
     /* aio does not call this handler */
 
@@ -1833,7 +1875,7 @@ ngx_http_block_read(ngx_http_request_t *
 
 
 static void
-ngx_http_test_read(ngx_http_request_t *r)
+ngx_http_test_reading(ngx_http_request_t *r)
 {
     int                n;
     char               buf[1];
@@ -1844,7 +1886,7 @@ ngx_http_test_read(ngx_http_request_t *r
     c = r->connection;
     rev = c->read;
 
-    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http test read");
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http test reading");
 
 #if (NGX_HAVE_KQUEUE)
 
@@ -1922,8 +1964,16 @@ ngx_http_set_keepalive(ngx_http_request_
     c = r->connection;
     rev = c->read;
 
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "set http keepalive handler");
 
+    if (r->discard_body) {
+        r->lingering_time = ngx_time() + (time_t) (clcf->lingering_time / 1000);
+        ngx_add_timer(rev, clcf->lingering_timeout);
+        return;
+    }
+
     c->log->action = "closing request";
 
     hc = r->http_connection;
@@ -1967,8 +2017,6 @@ ngx_http_set_keepalive(ngx_http_request_
         }
     }
 
-    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
-
     ngx_http_request_done(r, 0);
 
     c->data = hc;
@@ -2381,7 +2429,7 @@ ngx_http_post_action(ngx_http_request_t 
     r->header_only = 1;
     r->post_action = 1;
 
-    r->read_event_handler = ngx_http_block_read;
+    r->read_event_handler = ngx_http_block_reading;
 
     ngx_http_internal_redirect(r, &clcf->post_action, NULL);
 
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -275,7 +275,15 @@ typedef struct {
 } ngx_http_connection_t;
 
 
-typedef ngx_hash_combined_t  ngx_http_virtual_names_t;
+typedef struct ngx_http_server_name_s  ngx_http_server_name_t;
+
+
+typedef struct {
+     ngx_hash_combined_t              names;
+
+     ngx_uint_t                       nregex;
+     ngx_http_server_name_t          *regex;
+} ngx_http_virtual_names_t;
 
 
 typedef void (*ngx_http_cleanup_pt)(void *data);
--- a/src/http/ngx_http_request_body.c
+++ b/src/http/ngx_http_request_body.c
@@ -14,8 +14,8 @@ static void ngx_http_read_client_request
 static ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r);
 static ngx_int_t ngx_http_write_request_body(ngx_http_request_t *r,
     ngx_chain_t *body);
-static void ngx_http_read_discarded_body_handler(ngx_http_request_t *r);
-static ngx_int_t ngx_http_read_discarded_body(ngx_http_request_t *r);
+static void ngx_http_read_discarded_request_body_handler(ngx_http_request_t *r);
+static ngx_int_t ngx_http_read_discarded_request_body(ngx_http_request_t *r);
 
 
 /*
@@ -425,7 +425,7 @@ ngx_http_write_request_body(ngx_http_req
 
 
 ngx_int_t
-ngx_http_discard_body(ngx_http_request_t *r)
+ngx_http_discard_request_body(ngx_http_request_t *r)
 {
     ssize_t       size;
     ngx_event_t  *rev;
@@ -442,8 +442,6 @@ ngx_http_discard_body(ngx_http_request_t
         ngx_del_timer(rev);
     }
 
-    r->discard_body = 1;
-
     if (r->headers_in.content_length_n <= 0) {
         return NGX_OK;
     }
@@ -461,38 +459,90 @@ ngx_http_discard_body(ngx_http_request_t
         }
     }
 
-    r->read_event_handler = ngx_http_read_discarded_body_handler;
+    r->discard_body = 1;
+
+    r->read_event_handler = ngx_http_read_discarded_request_body_handler;
 
     if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
-    return ngx_http_read_discarded_body(r);
+    (void) ngx_http_read_discarded_request_body(r);
+
+    return NGX_OK;
 }
 
 
 static void
-ngx_http_read_discarded_body_handler(ngx_http_request_t *r)
+ngx_http_read_discarded_request_body_handler(ngx_http_request_t *r)
 {
-    ngx_int_t  rc;
+    ngx_int_t                  rc;
+    ngx_msec_t                 timer;
+    ngx_event_t               *rev;
+    ngx_connection_t          *c;
+    ngx_http_core_loc_conf_t  *clcf;
+
+    c = r->connection;
+    rev = c->read;
 
-    rc = ngx_http_read_discarded_body(r);
+    if (rev->timedout) {
+        c->timedout = 1;
+        c->error = 1;
+        ngx_http_finalize_request(r, 0);
+        return;
+    }
 
-    if (rc == NGX_AGAIN) {
-        if (ngx_handle_read_event(r->connection->read, 0) == NGX_ERROR) {
-            ngx_http_finalize_request(r, rc);
+    if (r->lingering_time) {
+        timer = r->lingering_time - ngx_time();
+
+        if (timer <= 0) {
+            r->discard_body = 0;
+            ngx_http_finalize_request(r, 0);
             return;
         }
+
+    } else {
+        timer = 0;
+    }
+
+    rc = ngx_http_read_discarded_request_body(r);
+
+    if (rc == NGX_OK) {
+
+        r->discard_body = 0;
+
+        if (r->done) {
+            ngx_http_finalize_request(r, 0);
+        }
+
+        return;
     }
 
-    if (rc != NGX_OK) {
+    /* rc == NGX_AGAIN */
+
+    if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+        c->error = 1;
         ngx_http_finalize_request(r, rc);
+        return;
+    }
+
+    if (timer) {
+
+        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+        timer *= 1000;
+
+        if (timer > clcf->lingering_timeout) {
+            timer = clcf->lingering_timeout;
+        }
+
+        ngx_add_timer(rev, timer);
     }
 }
 
 
 static ngx_int_t
-ngx_http_read_discarded_body(ngx_http_request_t *r)
+ngx_http_read_discarded_request_body(ngx_http_request_t *r)
 {
     size_t   size;
     ssize_t  n;
@@ -501,33 +551,34 @@ ngx_http_read_discarded_body(ngx_http_re
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http read discarded body");
 
-    if (r->headers_in.content_length_n == 0) {
-        return NGX_OK;
-    }
+    do {
+        if (r->headers_in.content_length_n == 0) {
+            r->read_event_handler = ngx_http_block_reading;
+            return NGX_OK;
+        }
 
-    size = (r->headers_in.content_length_n > NGX_HTTP_DISCARD_BUFFER_SIZE) ?
-                NGX_HTTP_DISCARD_BUFFER_SIZE:
-                (size_t) r->headers_in.content_length_n;
+        size = (r->headers_in.content_length_n > NGX_HTTP_DISCARD_BUFFER_SIZE) ?
+                   NGX_HTTP_DISCARD_BUFFER_SIZE:
+                   (size_t) r->headers_in.content_length_n;
 
-    n = r->connection->recv(r->connection, buffer, size);
-
-    if (n == NGX_ERROR) {
-
-        r->connection->error = 1;
+        n = r->connection->recv(r->connection, buffer, size);
 
-        /*
-         * if a client request body is discarded then we already set
-         * some HTTP response code for client and we can ignore the error
-         */
+        if (n == NGX_ERROR) {
+            r->connection->error = 1;
+            return NGX_OK;
+        }
 
-        return NGX_OK;
-    }
+        if (n == NGX_AGAIN) {
+            return NGX_AGAIN;
+        }
 
-    if (n == NGX_AGAIN) {
-        return NGX_AGAIN;
-    }
+        if (n == 0) {
+            return NGX_OK;
+        }
 
-    r->headers_in.content_length_n -= n;
+        r->headers_in.content_length_n -= n;
 
-    return NGX_OK;
+    } while (r->connection->read->ready);
+
+    return NGX_AGAIN;
 }
--- a/src/http/ngx_http_script.c
+++ b/src/http/ngx_http_script.c
@@ -952,8 +952,10 @@ ngx_http_script_not_equal_code(ngx_http_
 void
 ngx_http_script_file_code(ngx_http_script_engine_t *e)
 {
-    ngx_err_t                     err;
-    ngx_file_info_t               fi;
+    ngx_str_t                     path;
+    ngx_http_request_t           *r;
+    ngx_open_file_info_t          of;
+    ngx_http_core_loc_conf_t     *clcf;
     ngx_http_variable_value_t    *value;
     ngx_http_script_file_code_t  *code;
 
@@ -962,14 +964,26 @@ ngx_http_script_file_code(ngx_http_scrip
     code = (ngx_http_script_file_code_t *) e->ip;
     e->ip += sizeof(ngx_http_script_file_code_t);
 
-    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
-                   "http script file op %p", code->op);
+    path.len = value->len - 1;
+    path.data = value->data;
+
+    r = e->request;
+
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http script file op %p \"%V\"", code->op, &path);
+
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
 
-    if (ngx_file_info(value->data, &fi) == -1) {
-        err = ngx_errno;
+    of.test_dir = 0;
+    of.retest = clcf->open_file_cache_retest;
+    of.errors = clcf->open_file_cache_errors;
+    of.events = clcf->open_file_cache_events;
 
-        if (err != NGX_ENOENT && err != NGX_ENOTDIR) {
-            ngx_log_error(NGX_LOG_CRIT, e->request->connection->log, err,
+    if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
+        == NGX_ERROR)
+    {
+        if (of.err != NGX_ENOENT && of.err != NGX_ENOTDIR) {
+            ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
                           ngx_file_info_n " \"%s\" failed", value->data);
         }
 
@@ -993,69 +1007,57 @@ ngx_http_script_file_code(ngx_http_scrip
 
     switch (code->op) {
     case ngx_http_script_file_plain:
-        if (ngx_is_file(&fi)) {
+        if (of.is_file) {
              goto true;
         }
         goto false;
 
     case ngx_http_script_file_not_plain:
-        if (ngx_is_file(&fi)) {
+        if (of.is_file) {
             goto false;
         }
         goto true;
 
     case ngx_http_script_file_dir:
-        if (ngx_is_dir(&fi)) {
+        if (of.is_dir) {
              goto true;
         }
         goto false;
 
     case ngx_http_script_file_not_dir:
-        if (ngx_is_dir(&fi)) {
+        if (of.is_dir) {
             goto false;
         }
         goto true;
 
     case ngx_http_script_file_exists:
-        if (ngx_is_file(&fi) || ngx_is_dir(&fi) || ngx_is_link(&fi)) {
+        if (of.is_file || of.is_dir || of.is_link) {
              goto true;
         }
         goto false;
 
     case ngx_http_script_file_not_exists:
-        if (ngx_is_file(&fi) || ngx_is_dir(&fi) || ngx_is_link(&fi)) {
+        if (of.is_file || of.is_dir || of.is_link) {
             goto false;
         }
         goto true;
 
-#if (NGX_WIN32)
-
     case ngx_http_script_file_exec:
-        goto false;
-
-    case ngx_http_script_file_not_exec:
-        goto true;
-
-#else
-
-    case ngx_http_script_file_exec:
-        if (ngx_is_exec(&fi)) {
+        if (of.is_exec) {
              goto true;
         }
         goto false;
 
     case ngx_http_script_file_not_exec:
-        if (ngx_is_exec(&fi)) {
+        if (of.is_exec) {
             goto false;
         }
         goto true;
-
-#endif
     }
 
 false:
 
-    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http script file op false");
 
     *value = ngx_http_variable_null_value;
@@ -1191,7 +1193,7 @@ ngx_http_script_var_code(ngx_http_script
 
     if (value && !value->not_found) {
         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
-                       "http script var: \"%V\"", value);
+                       "http script var: \"%v\"", value);
 
         *e->sp = *value;
         e->sp++;
--- a/src/http/ngx_http_special_response.c
+++ b/src/http/ngx_http_special_response.c
@@ -327,7 +327,7 @@ ngx_http_special_response_handler(ngx_ht
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http special response: %d, \"%V\"", error, &r->uri);
 
-    rc = ngx_http_discard_body(r);
+    rc = ngx_http_discard_request_body(r);
 
     if (rc == NGX_HTTP_INTERNAL_SERVER_ERROR) {
         error = NGX_HTTP_INTERNAL_SERVER_ERROR;
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -565,9 +565,11 @@ ngx_http_upstream_connect(ngx_http_reque
 
     if (rc == NGX_BUSY) {
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no live upstreams");
+        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_NOLIVE);
+        return;
     }
 
-    if (rc == NGX_BUSY || rc == NGX_DECLINED) {
+    if (rc == NGX_DECLINED) {
         ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
         return;
     }
@@ -581,7 +583,7 @@ ngx_http_upstream_connect(ngx_http_reque
     c->write->handler = ngx_http_upstream_send_request_handler;
     c->read->handler = ngx_http_upstream_process_header;
 
-    c->sendfile = r->connection->sendfile;
+    c->sendfile &= r->connection->sendfile;
 
     c->pool = r->pool;
     c->read->log = c->write->log = c->log = r->connection->log;
@@ -2153,7 +2155,9 @@ ngx_http_upstream_next(ngx_http_request_
         state = NGX_PEER_FAILED;
     }
 
-    u->peer.free(&u->peer, u->peer.data, state);
+    if (ft_type != NGX_HTTP_UPSTREAM_FT_NOLIVE) {
+        u->peer.free(&u->peer, u->peer.data, state);
+    }
 
     if (ft_type == NGX_HTTP_UPSTREAM_FT_TIMEOUT) {
         ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_ETIMEDOUT,
@@ -3118,6 +3122,17 @@ ngx_http_upstream_server(ngx_conf_t *cf,
             continue;
         }
 
+        if (ngx_strncmp(value[i].data, "backup", 6) == 0) {
+
+            if (!(uscf->flags & NGX_HTTP_UPSTREAM_BACKUP)) {
+                goto invalid;
+            }
+
+            us->backup = 1;
+
+            continue;
+        }
+
         if (ngx_strncmp(value[i].data, "down", 4) == 0) {
 
             if (!(uscf->flags & NGX_HTTP_UPSTREAM_DOWN)) {
--- a/src/http/ngx_http_upstream.h
+++ b/src/http/ngx_http_upstream.h
@@ -24,6 +24,7 @@
 #define NGX_HTTP_UPSTREAM_FT_HTTP_404        0x00000040
 #define NGX_HTTP_UPSTREAM_FT_BUSY_LOCK       0x00000080
 #define NGX_HTTP_UPSTREAM_FT_MAX_WAITING     0x00000100
+#define NGX_HTTP_UPSTREAM_FT_NOLIVE          0x40000000
 #define NGX_HTTP_UPSTREAM_FT_OFF             0x80000000
 
 
--- a/src/http/ngx_http_upstream_round_robin.c
+++ b/src/http/ngx_http_upstream_round_robin.c
@@ -9,6 +9,7 @@
 #include <ngx_http.h>
 
 
+static int ngx_http_upstream_cmp_servers(const void *one, const void *two);
 static ngx_uint_t
 ngx_http_upstream_get_peer(ngx_http_upstream_rr_peers_t *peers);
 
@@ -20,15 +21,20 @@ ngx_http_upstream_init_round_robin(ngx_c
     ngx_url_t                      u;
     ngx_uint_t                     i, j, n;
     ngx_http_upstream_server_t    *server;
-    ngx_http_upstream_rr_peers_t  *peers;
+    ngx_http_upstream_rr_peers_t  *peers, *backup;
 
     us->peer.init = ngx_http_upstream_init_round_robin_peer;
 
     if (us->servers) {
-        n = 0;
         server = us->servers->elts;
 
+        n = 0;
+
         for (i = 0; i < us->servers->nelts; i++) {
+            if (server[i].backup) {
+                continue;
+            }
+
             n += server[i].naddrs;
         }
 
@@ -38,6 +44,7 @@ ngx_http_upstream_init_round_robin(ngx_c
             return NGX_ERROR;
         }
 
+        peers->single = (n == 1);
         peers->number = n;
         peers->name = &us->host;
 
@@ -45,20 +52,81 @@ ngx_http_upstream_init_round_robin(ngx_c
 
         for (i = 0; i < us->servers->nelts; i++) {
             for (j = 0; j < server[i].naddrs; j++) {
+                if (server[i].backup) {
+                    continue;
+                }
+
                 peers->peer[n].sockaddr = server[i].addrs[j].sockaddr;
                 peers->peer[n].socklen = server[i].addrs[j].socklen;
                 peers->peer[n].name = server[i].addrs[j].name;
-                peers->peer[n].weight = server[i].weight;
-                peers->peer[n].current_weight = server[i].weight;
                 peers->peer[n].max_fails = server[i].max_fails;
                 peers->peer[n].fail_timeout = server[i].fail_timeout;
                 peers->peer[n].down = server[i].down;
+                peers->peer[n].weight = server[i].down ? 0 : server[i].weight;
+                peers->peer[n].current_weight = peers->peer[n].weight;
                 n++;
             }
         }
 
         us->peer.data = peers;
 
+        ngx_sort(&peers->peer[0], (size_t) n,
+                 sizeof(ngx_http_upstream_rr_peer_t),
+                 ngx_http_upstream_cmp_servers);
+
+        /* backup servers */
+
+        n = 0;
+
+        for (i = 0; i < us->servers->nelts; i++) {
+            if (!server[i].backup) {
+                continue;
+            }
+
+            n += server[i].naddrs;
+        }
+
+        if (n == 0) {
+            return NGX_OK;
+        }
+
+        backup = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t)
+                              + sizeof(ngx_http_upstream_rr_peer_t) * (n - 1));
+        if (backup == NULL) {
+            return NGX_ERROR;
+        }
+
+        peers->single = 0;
+        backup->single = 0;
+        backup->number = n;
+        backup->name = &us->host;
+
+        n = 0;
+
+        for (i = 0; i < us->servers->nelts; i++) {
+            for (j = 0; j < server[i].naddrs; j++) {
+                if (!server[i].backup) {
+                    continue;
+                }
+
+                backup->peer[n].sockaddr = server[i].addrs[j].sockaddr;
+                backup->peer[n].socklen = server[i].addrs[j].socklen;
+                backup->peer[n].name = server[i].addrs[j].name;
+                backup->peer[n].weight = server[i].weight;
+                backup->peer[n].current_weight = server[i].weight;
+                backup->peer[n].max_fails = server[i].max_fails;
+                backup->peer[n].fail_timeout = server[i].fail_timeout;
+                backup->peer[n].down = server[i].down;
+                n++;
+            }
+        }
+
+        peers->next = backup;
+
+        ngx_sort(&backup->peer[0], (size_t) n,
+                 sizeof(ngx_http_upstream_rr_peer_t),
+                 ngx_http_upstream_cmp_servers);
+
         return NGX_OK;
     }
 
@@ -95,6 +163,7 @@ ngx_http_upstream_init_round_robin(ngx_c
         return NGX_ERROR;
     }
 
+    peers->single = (n == 1);
     peers->number = n;
     peers->name = &us->host;
 
@@ -113,10 +182,24 @@ ngx_http_upstream_init_round_robin(ngx_c
 
     us->peer.data = peers;
 
+    /* implicitly defined upstream has no backup servers */
+
     return NGX_OK;
 }
 
 
+static int
+ngx_http_upstream_cmp_servers(const void *one, const void *two)
+{
+    ngx_http_upstream_rr_peer_t  *first, *second;
+
+    first = (ngx_http_upstream_rr_peer_t *) one;
+    second = (ngx_http_upstream_rr_peer_t *) two;
+
+    return (first->weight < second->weight);
+}
+
+
 ngx_int_t
 ngx_http_upstream_init_round_robin_peer(ngx_http_request_t *r,
     ngx_http_upstream_srv_conf_t *us)
@@ -171,11 +254,13 @@ ngx_http_upstream_get_round_robin_peer(n
 {
     ngx_http_upstream_rr_peer_data_t  *rrp = data;
 
-    time_t                        now;
-    uintptr_t                     m;
-    ngx_uint_t                    i, n;
-    ngx_connection_t             *c;
-    ngx_http_upstream_rr_peer_t  *peer;
+    time_t                         now;
+    uintptr_t                      m;
+    ngx_int_t                      rc;
+    ngx_uint_t                     i, n;
+    ngx_connection_t              *c;
+    ngx_http_upstream_rr_peer_t   *peer;
+    ngx_http_upstream_rr_peers_t  *peers;
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
                    "get rr peer, try: %ui", pc->tries);
@@ -207,7 +292,7 @@ ngx_http_upstream_get_round_robin_peer(n
     pc->cached = 0;
     pc->connection = NULL;
 
-    if (rrp->peers->number == 1) {
+    if (rrp->peers->single) {
         peer = &rrp->peers->peer[0];
 
     } else {
@@ -218,6 +303,8 @@ ngx_http_upstream_get_round_robin_peer(n
 
             /* it's a first try - get a current peer */
 
+            i = pc->tries;
+
             for ( ;; ) {
                 rrp->current = ngx_http_upstream_get_peer(rrp->peers);
 
@@ -254,16 +341,24 @@ ngx_http_upstream_get_round_robin_peer(n
                     pc->tries--;
                 }
 
-                if (pc->tries) {
-                    continue;
+                if (pc->tries == 0) {
+                    goto failed;
                 }
 
-                goto failed;
+                if (--i == 0) {
+                    ngx_log_error(NGX_LOG_ALERT, pc->log, 0,
+                                  "round robin upstream stuck on %ui tries",
+                                  pc->tries);
+                    goto failed;
+                }
             }
 
             peer->current_weight--;
 
         } else {
+
+            i = pc->tries;
+
             for ( ;; ) {
                 n = rrp->current / (8 * sizeof(uintptr_t));
                 m = (uintptr_t) 1 << rrp->current % (8 * sizeof(uintptr_t));
@@ -300,11 +395,16 @@ ngx_http_upstream_get_round_robin_peer(n
                     rrp->current = 0;
                 }
 
-                if (pc->tries) {
-                    continue;
+                if (pc->tries == 0) {
+                    goto failed;
                 }
 
-                goto failed;
+                if (--i == 0) {
+                    ngx_log_error(NGX_LOG_ALERT, pc->log, 0,
+                                  "round robin upstream stuck on %ui tries",
+                                  pc->tries);
+                    goto failed;
+                }
             }
 
             peer->current_weight--;
@@ -319,19 +419,53 @@ ngx_http_upstream_get_round_robin_peer(n
 
     /* ngx_unlock_mutex(rrp->peers->mutex); */
 
+    if (pc->tries == 1 && rrp->peers->next) {
+        pc->tries += rrp->peers->next->number;
+
+        n = rrp->peers->next->number / (8 * sizeof(uintptr_t)) + 1;
+        for (i = 0; i < n; i++) {
+             rrp->tried[i] = 0;
+        }
+    }
+
     return NGX_OK;
 
 failed:
 
+    peers = rrp->peers;
+
+    if (peers->next) {
+
+        /* ngx_unlock_mutex(peers->mutex); */
+
+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, "backup servers");
+
+        rrp->peers = peers->next;
+        pc->tries = rrp->peers->number;
+
+        n = rrp->peers->number / (8 * sizeof(uintptr_t)) + 1;
+        for (i = 0; i < n; i++) {
+             rrp->tried[i] = 0;
+        }
+
+        rc = ngx_http_upstream_get_round_robin_peer(pc, rrp);
+
+        if (rc != NGX_BUSY) {
+            return rc;
+        }
+
+        /* ngx_lock_mutex(peers->mutex); */
+    }
+
     /* all peers failed, mark them as live for quick recovery */
 
-    for (i = 0; i < rrp->peers->number; i++) {
-        rrp->peers->peer[i].fails = 0;
+    for (i = 0; i < peers->number; i++) {
+        peers->peer[i].fails = 0;
     }
 
-    /* ngx_unlock_mutex(rrp->peers->mutex); */
+    /* ngx_unlock_mutex(peers->mutex); */
 
-    pc->name = rrp->peers->name;
+    pc->name = peers->name;
 
     return NGX_BUSY;
 }
@@ -380,7 +514,7 @@ ngx_http_upstream_get_peer(ngx_http_upst
         }
 
         for (i = 0; i < peers->number; i++) {
-            if (peer[i].fails == 0) {
+            if (peer->max_fails == 0 || peer->fails < peer->max_fails) {
                 peer[i].current_weight += peer[i].weight;
 
             } else {
@@ -410,7 +544,7 @@ ngx_http_upstream_free_round_robin_peer(
 
     /* TODO: NGX_PEER_KEEPALIVE */
 
-    if (rrp->peers->number == 1) {
+    if (rrp->peers->single) {
         pc->tries = 0;
         return;
     }
@@ -474,8 +608,8 @@ ngx_http_upstream_set_round_robin_peer_s
     rc = ngx_ssl_set_session(pc->connection, ssl_session);
 
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
-                  "set session: %p:%d",
-                  ssl_session, ssl_session ? ssl_session->references : 0);
+                   "set session: %p:%d",
+                   ssl_session, ssl_session ? ssl_session->references : 0);
 
     /* ngx_unlock_mutex(rrp->peers->mutex); */
 
@@ -499,7 +633,7 @@ ngx_http_upstream_save_round_robin_peer_
     }
 
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
-                  "save session: %p:%d", ssl_session, ssl_session->references);
+                   "save session: %p:%d", ssl_session, ssl_session->references);
 
     peer = &rrp->peers->peer[rrp->current];
 
@@ -514,7 +648,7 @@ ngx_http_upstream_save_round_robin_peer_
     if (old_ssl_session) {
 
         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
-                      "old session: %p:%d",
+                       "old session: %p:%d",
                        old_ssl_session, old_ssl_session->references);
 
         /* TODO: may block */
--- a/src/http/ngx_http_upstream_round_robin.h
+++ b/src/http/ngx_http_upstream_round_robin.h
@@ -35,7 +35,10 @@ typedef struct {
 } ngx_http_upstream_rr_peer_t;
 
 
-typedef struct {
+typedef struct ngx_http_upstream_rr_peers_s  ngx_http_upstream_rr_peers_t;
+
+struct ngx_http_upstream_rr_peers_s {
+    ngx_uint_t                      single;        /* unsigned  single:1; */
     ngx_uint_t                      number;
     ngx_uint_t                      last_cached;
 
@@ -44,8 +47,10 @@ typedef struct {
 
     ngx_str_t                      *name;
 
+    ngx_http_upstream_rr_peers_t   *next;
+
     ngx_http_upstream_rr_peer_t     peer[1];
-} ngx_http_upstream_rr_peers_t;
+};
 
 
 typedef struct {
--- a/src/http/ngx_http_write_filter_module.c
+++ b/src/http/ngx_http_write_filter_module.c
@@ -249,7 +249,11 @@ ngx_http_write_filter(ngx_http_request_t
         c->write->delayed = 1;
         ngx_add_timer(c->write, (ngx_msec_t) (sent * 1000 / r->limit_rate + 1));
 
-    } else if (c->write->ready && clcf->sendfile_max_chunk) {
+    } else if (c->write->ready
+               && clcf->sendfile_max_chunk
+               && (size_t) (c->sent - sent)
+                                >= clcf->sendfile_max_chunk - 2 * ngx_pagesize)
+    {
         c->write->delayed = 1;
         ngx_add_timer(c->write, 1);
     }
--- a/src/mail/ngx_mail.c
+++ b/src/mail/ngx_mail.c
@@ -300,7 +300,7 @@ ngx_mail_block(ngx_conf_t *cf, ngx_comma
                 return NGX_CONF_ERROR;
             }
 
-            ls->backlog = -1;
+            ls->backlog = NGX_LISTEN_BACKLOG;
             ls->rcvbuf = -1;
             ls->sndbuf = -1;
 
--- a/src/mail/ngx_mail_auth_http_module.c
+++ b/src/mail/ngx_mail_auth_http_module.c
@@ -21,6 +21,9 @@ typedef struct {
     ngx_str_t                       header;
 
     ngx_array_t                    *headers;
+
+    u_char                         *file;
+    ngx_uint_t                      line;
 } ngx_mail_auth_http_conf_t;
 
 
@@ -1277,18 +1280,10 @@ ngx_mail_auth_http_create_request(ngx_ma
 static ngx_int_t
 ngx_mail_auth_http_escape(ngx_pool_t *pool, ngx_str_t *text, ngx_str_t *escaped)
 {
-    u_char      ch, *p;
-    ngx_uint_t  i, n;
-
-    n = 0;
+    u_char     *p;
+    uintptr_t   n;
 
-    for (i = 0; i < text->len; i++) {
-        ch = text->data[i];
-
-        if (ch == CR || ch == LF) {
-            n++;
-        }
-    }
+    n = ngx_escape_uri(NULL, text->data, text->len, NGX_ESCAPE_MAIL_AUTH);
 
     if (n == 0) {
         *escaped = *text;
@@ -1302,27 +1297,9 @@ ngx_mail_auth_http_escape(ngx_pool_t *po
         return NGX_ERROR;
     }
 
-    escaped->data = p;
-
-    for (i = 0; i < text->len; i++) {
-        ch = text->data[i];
+    (void) ngx_escape_uri(p, text->data, text->len, NGX_ESCAPE_MAIL_AUTH);
 
-        if (ch == CR) {
-            *p++ = '%';
-            *p++ = '0';
-            *p++ = 'D';
-            continue;
-        }
-
-        if (ch == LF) {
-            *p++ = '%';
-            *p++ = '0';
-            *p++ = 'A';
-            continue;
-        }
-
-        *p++ = ch;
-    }
+    escaped->data = p;
 
     return NGX_OK;
 }
@@ -1340,6 +1317,9 @@ ngx_mail_auth_http_create_conf(ngx_conf_
 
     ahcf->timeout = NGX_CONF_UNSET_MSEC;
 
+    ahcf->file = cf->conf_file->file.name.data;
+    ahcf->line = cf->conf_file->line;
+
     return ahcf;
 }
 
@@ -1359,6 +1339,14 @@ ngx_mail_auth_http_merge_conf(ngx_conf_t
         conf->peer = prev->peer;
         conf->host_header = prev->host_header;
         conf->uri = prev->uri;
+
+        if (conf->peer == NULL) {
+            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                          "no \"http_auth\" is defined for server in %s:%ui",
+                          conf->file, conf->line);
+
+            return NGX_CONF_ERROR;
+        }
     }
 
     ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000);
@@ -1412,11 +1400,18 @@ ngx_mail_auth_http(ngx_conf_t *cf, ngx_c
     u.uri_part = 1;
     u.one_addr = 1;
 
+    if (ngx_strncmp(u.url.data, "http://", 7) == 0) {
+        u.url.len -= 7;
+        u.url.data += 7;
+    }
+
     if (ngx_parse_url(cf, &u) != NGX_OK) {
         if (u.err) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                "%s in auth_http \"%V\"", u.err, &u.url);
         }
+
+        return NGX_CONF_ERROR;
     }
 
     ahcf->peer = u.addrs;
--- a/src/mail/ngx_mail_parse.c
+++ b/src/mail/ngx_mail_parse.c
@@ -434,6 +434,10 @@ ngx_int_t ngx_imap_parse_command(ngx_mai
             break;
 
         case sw_argument:
+            if (ch == ' ' && s->quoted) {
+                break;
+            }
+
             switch (ch) {
             case '"':
                 if (!s->quoted) {
--- a/src/os/unix/ngx_freebsd_config.h
+++ b/src/os/unix/ngx_freebsd_config.h
@@ -77,6 +77,9 @@
 #endif
 
 
+#define NGX_LISTEN_BACKLOG        -1
+
+
 #if (defined SO_ACCEPTFILTER && !defined NGX_HAVE_DEFERRED_ACCEPT)
 #define NGX_HAVE_DEFERRED_ACCEPT  1
 #endif
--- a/src/os/unix/ngx_linux_config.h
+++ b/src/os/unix/ngx_linux_config.h
@@ -78,6 +78,9 @@ extern ssize_t sendfile(int s, int fd, i
 #endif
 
 
+#define NGX_LISTEN_BACKLOG        511
+
+
 #if defined TCP_DEFER_ACCEPT && !defined NGX_HAVE_DEFERRED_ACCEPT
 #define NGX_HAVE_DEFERRED_ACCEPT  1
 #endif
--- a/src/os/unix/ngx_posix_config.h
+++ b/src/os/unix/ngx_posix_config.h
@@ -88,6 +88,9 @@
 #endif
 
 
+#define NGX_LISTEN_BACKLOG  511
+
+
 #if (__FreeBSD__) && (__FreeBSD_version < 400017)
 
 #include <sys/param.h>          /* ALIGN() */
--- a/src/os/unix/ngx_process_cycle.c
+++ b/src/os/unix/ngx_process_cycle.c
@@ -62,6 +62,11 @@ u_long         cpu_affinity;
 static u_char  master_process[] = "master process";
 
 
+static ngx_cycle_t      ngx_exit_cycle;
+static ngx_log_t        ngx_exit_log;
+static ngx_open_file_t  ngx_exit_log_file;
+
+
 void
 ngx_master_process_cycle(ngx_cycle_t *cycle)
 {
@@ -649,13 +654,21 @@ ngx_master_process_exit(ngx_cycle_t *cyc
     }
 
     /*
-     * we do not destroy cycle->pool here because a signal handler
-     * that uses cycle->log can be called at this point
+     * Copy ngx_cycle->log related data to the special static exit cycle,
+     * log, and log file structures enough to allow a signal handler to log.
+     * The handler may be called when standard ngx_cycle->log allocated from
+     * ngx_cycle->pool is already destroyed.
      */
 
-#if 0
+    ngx_exit_log_file.fd = ngx_cycle->log->file->fd;
+
+    ngx_exit_log = *ngx_cycle->log;
+    ngx_exit_log.file = &ngx_exit_log_file;
+
+    ngx_exit_cycle.log = &ngx_exit_log;
+    ngx_cycle = &ngx_exit_cycle;
+
     ngx_destroy_pool(cycle->pool);
-#endif
 
     exit(0);
 }
@@ -792,49 +805,49 @@ ngx_worker_process_init(ngx_cycle_t *cyc
 
     ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
 
-    if (geteuid() == 0) {
-        if (priority && ccf->priority != 0) {
-            if (setpriority(PRIO_PROCESS, 0, ccf->priority) == -1) {
-                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
-                              "setpriority(%d) failed", ccf->priority);
-            }
+    if (priority && ccf->priority != 0) {
+        if (setpriority(PRIO_PROCESS, 0, ccf->priority) == -1) {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                          "setpriority(%d) failed", ccf->priority);
         }
+    }
 
-        if (ccf->rlimit_nofile != NGX_CONF_UNSET) {
-            rlmt.rlim_cur = (rlim_t) ccf->rlimit_nofile;
-            rlmt.rlim_max = (rlim_t) ccf->rlimit_nofile;
+    if (ccf->rlimit_nofile != NGX_CONF_UNSET) {
+        rlmt.rlim_cur = (rlim_t) ccf->rlimit_nofile;
+        rlmt.rlim_max = (rlim_t) ccf->rlimit_nofile;
 
-            if (setrlimit(RLIMIT_NOFILE, &rlmt) == -1) {
-                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
-                              "setrlimit(RLIMIT_NOFILE, %i) failed",
-                              ccf->rlimit_nofile);
-            }
+        if (setrlimit(RLIMIT_NOFILE, &rlmt) == -1) {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                          "setrlimit(RLIMIT_NOFILE, %i) failed",
+                          ccf->rlimit_nofile);
         }
+    }
 
-        if (ccf->rlimit_core != NGX_CONF_UNSET_SIZE) {
-            rlmt.rlim_cur = (rlim_t) ccf->rlimit_core;
-            rlmt.rlim_max = (rlim_t) ccf->rlimit_core;
+    if (ccf->rlimit_core != NGX_CONF_UNSET_SIZE) {
+        rlmt.rlim_cur = (rlim_t) ccf->rlimit_core;
+        rlmt.rlim_max = (rlim_t) ccf->rlimit_core;
 
-            if (setrlimit(RLIMIT_CORE, &rlmt) == -1) {
-                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
-                              "setrlimit(RLIMIT_CORE, %i) failed",
-                              ccf->rlimit_core);
-            }
+        if (setrlimit(RLIMIT_CORE, &rlmt) == -1) {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                          "setrlimit(RLIMIT_CORE, %i) failed",
+                          ccf->rlimit_core);
         }
+    }
 
 #ifdef RLIMIT_SIGPENDING
-        if (ccf->rlimit_sigpending != NGX_CONF_UNSET) {
-            rlmt.rlim_cur = (rlim_t) ccf->rlimit_sigpending;
-            rlmt.rlim_max = (rlim_t) ccf->rlimit_sigpending;
+    if (ccf->rlimit_sigpending != NGX_CONF_UNSET) {
+        rlmt.rlim_cur = (rlim_t) ccf->rlimit_sigpending;
+        rlmt.rlim_max = (rlim_t) ccf->rlimit_sigpending;
 
-            if (setrlimit(RLIMIT_SIGPENDING, &rlmt) == -1) {
-                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
-                              "setrlimit(RLIMIT_SIGPENDING, %i) failed",
-                              ccf->rlimit_sigpending);
-            }
+        if (setrlimit(RLIMIT_SIGPENDING, &rlmt) == -1) {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                          "setrlimit(RLIMIT_SIGPENDING, %i) failed",
+                          ccf->rlimit_sigpending);
         }
+    }
 #endif
 
+    if (geteuid() == 0) {
         if (setgid(ccf->group) == -1) {
             ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
                           "setgid(%d) failed", ccf->group);
@@ -996,13 +1009,23 @@ ngx_worker_process_exit(ngx_cycle_t *cyc
     }
 
     /*
-     * we do not destroy cycle->pool here because a signal handler
-     * that uses cycle->log can be called at this point
+     * Copy ngx_cycle->log related data to the special static exit cycle,
+     * log, and log file structures enough to allow a signal handler to log.
+     * The handler may be called when standard ngx_cycle->log allocated from
+     * ngx_cycle->pool is already destroyed.
      */
 
-#if 0
+    ngx_exit_log_file.fd = ngx_cycle->log->file->fd;
+
+    ngx_exit_log = *ngx_cycle->log;
+    ngx_exit_log.file = &ngx_exit_log_file;
+
+    ngx_exit_cycle.log = &ngx_exit_log;
+    ngx_cycle = &ngx_exit_cycle;
+
     ngx_destroy_pool(cycle->pool);
-#endif
+
+    ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, "exit");
 
     exit(0);
 }
--- a/src/os/unix/ngx_solaris_config.h
+++ b/src/os/unix/ngx_solaris_config.h
@@ -82,6 +82,9 @@
 #endif
 
 
+#define NGX_LISTEN_BACKLOG           511
+
+
 #ifndef NGX_HAVE_INHERITED_NONBLOCK
 #define NGX_HAVE_INHERITED_NONBLOCK  1
 #endif