changeset 332:3a91bfeffaba NGINX_0_6_10

nginx 0.6.10 *) 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.
author Igor Sysoev <http://sysoev.ru>
date Mon, 03 Sep 2007 00:00:00 +0400
parents b69d5e83bf82
children 1372b2a4ec2f
files CHANGES CHANGES.ru auto/sources src/core/nginx.h src/core/ngx_conf_file.c src/core/ngx_conf_file.h src/core/ngx_connection.c src/core/ngx_core.h src/core/ngx_open_file_cache.c src/core/ngx_open_file_cache.h src/event/modules/ngx_devpoll_module.c src/event/modules/ngx_kqueue_module.c src/event/ngx_event.h src/event/ngx_event_openssl.c src/http/modules/ngx_http_fastcgi_module.c src/http/modules/ngx_http_flv_module.c src/http/modules/ngx_http_index_module.c src/http/modules/ngx_http_static_module.c src/http/modules/perl/nginx.pm src/http/modules/perl/nginx.xs src/http/ngx_http_core_module.c src/http/ngx_http_core_module.h src/http/ngx_http_request.c src/http/ngx_http_request_body.c src/http/ngx_http_script.c src/http/ngx_http_upstream_round_robin.c src/os/unix/ngx_process_cycle.c
diffstat 27 files changed, 1363 insertions(+), 357 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,18 @@
 
+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 
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,19 @@
 
+Изменения в 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
 
     *) Исправление: рабочий процесс мог зациклиться при использовании 
@@ -63,7 +78,6 @@
 
     *) Исправление: если в директиве auth_http был задан неправильный 
        адрес, то в рабочем процессе происходил segmentation fault.
-       <br>
 
     *) Исправление: теперь по умолчанию nginx использует значение 511 для 
        listen backlog на всех платформах, кроме FreeBSD.
--- 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/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,7 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define NGINX_VERSION      "0.6.9"
+#define NGINX_VERSION      "0.6.10"
 #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
@@ -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>
 
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/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.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_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -1601,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_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))) {
@@ -95,64 +92,66 @@ ngx_http_flv_handler(ngx_http_request_t 
         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_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_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))) {
@@ -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/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -47,7 +47,7 @@ our @EXPORT = qw(
     HTTP_INSUFFICIENT_STORAGE
 );
 
-our $VERSION = '0.6.9';
+our $VERSION = '0.6.10';
 
 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;
@@ -603,15 +605,15 @@ 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);
 
@@ -634,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;
@@ -652,34 +670,15 @@ 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);
--- 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
 };
@@ -2360,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;
 }
@@ -2543,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;
 }
@@ -3162,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
@@ -290,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
@@ -75,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 },
--- a/src/http/ngx_http_request_body.c
+++ b/src/http/ngx_http_request_body.c
@@ -446,8 +446,6 @@ ngx_http_discard_request_body(ngx_http_r
         return NGX_OK;
     }
 
-    r->discard_body = 1;
-
     size = r->header_in->last - r->header_in->pos;
 
     if (size) {
@@ -461,6 +459,8 @@ ngx_http_discard_request_body(ngx_http_r
         }
     }
 
+    r->discard_body = 1;
+
     r->read_event_handler = ngx_http_read_discarded_request_body_handler;
 
     if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
@@ -521,6 +521,7 @@ ngx_http_read_discarded_request_body_han
     /* rc == NGX_AGAIN */
 
     if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+        c->error = 1;
         ngx_http_finalize_request(r, rc);
         return;
     }
--- 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;
--- a/src/http/ngx_http_upstream_round_robin.c
+++ b/src/http/ngx_http_upstream_round_robin.c
@@ -608,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); */
 
@@ -633,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];
 
@@ -648,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/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);
 }
@@ -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);
 }