changeset 406:79c5df00501e NGINX_0_7_15

nginx 0.7.15 *) Feature: the ngx_http_random_index_module. *) Feature: the "directio" directive has been optimized for file requests starting from arbitrary position. *) Feature: the "directio" directive turns off sendfile if it is necessary. *) Feature: now nginx allows underscores in a client request header line names.
author Igor Sysoev <http://sysoev.ru>
date Mon, 08 Sep 2008 00:00:00 +0400
parents 2993e60bc4e0
children 5d65f31d8ec2
files CHANGES CHANGES.ru auto/modules auto/options auto/sources auto/unix src/core/nginx.c src/core/nginx.h src/core/ngx_buf.h src/core/ngx_core.h src/core/ngx_file.h src/core/ngx_open_file_cache.c src/core/ngx_open_file_cache.h src/core/ngx_output_chain.c src/http/modules/ngx_http_fastcgi_module.c src/http/modules/ngx_http_flv_module.c src/http/modules/ngx_http_gzip_static_module.c src/http/modules/ngx_http_log_module.c src/http/modules/ngx_http_memcached_module.c src/http/modules/ngx_http_proxy_module.c src/http/modules/ngx_http_random_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.c src/http/ngx_http_core_module.c src/http/ngx_http_core_module.h src/http/ngx_http_parse.c src/http/ngx_http_parse_time.c src/http/ngx_http_request.c src/http/ngx_http_request_body.c src/http/ngx_http_variables.c src/http/ngx_http_variables.h src/http/ngx_http_write_filter_module.c src/os/unix/ngx_files.c src/os/unix/ngx_files.h src/os/unix/ngx_types.h
diffstat 37 files changed, 629 insertions(+), 141 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,18 @@
 
+Changes with nginx 0.7.15                                        08 Sep 2008
+
+    *) Feature: the ngx_http_random_index_module.
+
+    *) Feature: the "directio" directive has been optimized for file 
+       requests starting from arbitrary position.
+
+    *) Feature: the "directio" directive turns off sendfile if it is 
+       necessary.
+
+    *) Feature: now nginx allows underscores in a client request header 
+       line names.
+
+
 Changes with nginx 0.7.14                                        01 Sep 2008
 
     *) Change: now the ssl_certificate and ssl_certificate_key directives 
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,18 @@
 
+Изменения в nginx 0.7.15                                          08.09.2008
+
+    *) Добавление: модуль ngx_http_random_index_module.
+
+    *) Добавление: директива directio оптимизирована для запросов файлов, 
+       начинающихся с произвольной позиции.
+
+    *) Добавление: директива directio при необходимости запрещает 
+       использование sendfile.
+
+    *) Добавление: теперь nginx разрешает подчёркивания в именах строк в 
+       заголовке запроса клиента.
+
+
 Изменения в nginx 0.7.14                                          01.09.2008
 
     *) Изменение: теперь директивы ssl_certificate и ssl_certificate_key не 
--- a/auto/modules
+++ b/auto/modules
@@ -81,6 +81,7 @@ fi
 #     ngx_http_dav_module
 #     ngx_http_autoindex_module
 #     ngx_http_index_module
+#     ngx_http_random_index_module
 #
 #     ngx_http_access_module
 #     ngx_http_realip_module
@@ -177,6 +178,12 @@ fi
 
 HTTP_MODULES="$HTTP_MODULES $HTTP_INDEX_MODULE"
 
+if [ $HTTP_RANDOM_INDEX = YES ]; then
+    have=NGX_HTTP_RANDOM_INDEX . auto/have
+    HTTP_MODULES="$HTTP_MODULES $HTTP_RANDOM_INDEX_MODULE"
+    HTTP_SRCS="$HTTP_SRCS $HTTP_RANDOM_INDEX_SRCS"
+fi
+
 if [ $HTTP_AUTH_BASIC = YES ]; then
     have=NGX_HTTP_AUTH_BASIC . auto/have
     have=NGX_CRYPT . auto/have
--- a/auto/options
+++ b/auto/options
@@ -64,6 +64,7 @@ HTTP_ACCESS=YES
 HTTP_AUTH_BASIC=YES
 HTTP_USERID=YES
 HTTP_AUTOINDEX=YES
+HTTP_RANDOM_INDEX=NO
 HTTP_STATUS=NO
 HTTP_GEO=YES
 HTTP_MAP=YES
@@ -171,6 +172,7 @@ do
         --with-http_dav_module)          HTTP_DAV=YES               ;;
         --with-http_flv_module)          HTTP_FLV=YES               ;;
         --with-http_gzip_static_module)  HTTP_GZIP_STATIC=YES       ;;
+        --with-http_random_index_module) HTTP_RANDOM_INDEX=YES      ;;
 
         --without-http_charset_module)   HTTP_CHARSET=NO            ;;
         --without-http_gzip_module)      HTTP_GZIP=NO               ;;
@@ -287,6 +289,7 @@ cat << END
   --with-http_dav_module             enable ngx_http_dav_module
   --with-http_flv_module             enable ngx_http_flv_module
   --with-http_gzip_static_module     enable ngx_http_gzip_static_module
+  --with-http_random_index_module    enable ngx_http_random_index_module
   --with-http_stub_status_module     enable ngx_http_stub_status_module
 
   --without-http_charset_module      disable ngx_http_charset_module
--- a/auto/sources
+++ b/auto/sources
@@ -131,7 +131,6 @@ UNIX_INCS="$CORE_INCS $EVENT_INCS src/os
 
 UNIX_DEPS="$CORE_DEPS $EVENT_DEPS \
             src/os/unix/ngx_time.h \
-            src/os/unix/ngx_types.h \
             src/os/unix/ngx_errno.h \
             src/os/unix/ngx_alloc.h \
             src/os/unix/ngx_files.h \
@@ -208,7 +207,6 @@ WIN32_INCS="$CORE_INCS $EVENT_INCS src/o
 WIN32_DEPS="$CORE_DEPS $EVENT_DEPS \
             src/os/win32/ngx_win32_config.h \
             src/os/win32/ngx_time.h \
-            src/os/win32/ngx_types.h \
             src/os/win32/ngx_errno.h \
             src/os/win32/ngx_alloc.h \
             src/os/win32/ngx_files.h \
@@ -368,6 +366,10 @@ HTTP_AUTOINDEX_MODULE=ngx_http_autoindex
 HTTP_AUTOINDEX_SRCS=src/http/modules/ngx_http_autoindex_module.c
 
 
+HTTP_RANDOM_INDEX_MODULE=ngx_http_random_index_module
+HTTP_RANDOM_INDEX_SRCS=src/http/modules/ngx_http_random_index_module.c
+
+
 HTTP_STATUS_MODULE=ngx_http_status_module
 HTTP_STATUS_SRCS=src/http/modules/ngx_http_status_module.c
 
--- a/auto/unix
+++ b/auto/unix
@@ -229,3 +229,23 @@ ngx_feature_path=
 ngx_feature_libs=
 ngx_feature_test="struct tm  tm; tm.tm_gmtoff = 0"
 . auto/feature
+
+
+ngx_feature="struct dirent.d_namlen"
+ngx_feature_name="NGX_HAVE_D_NAMLEN"
+ngx_feature_run=no
+ngx_feature_incs="#include <dirent.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="struct dirent  dir; dir.d_namlen = 0"
+. auto/feature
+
+
+ngx_feature="struct dirent.d_type"
+ngx_feature_name="NGX_HAVE_D_TYPE"
+ngx_feature_run=no
+ngx_feature_incs="#include <dirent.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="struct dirent  dir; dir.d_type = DT_REG"
+. auto/feature
--- a/src/core/nginx.c
+++ b/src/core/nginx.c
@@ -6,7 +6,6 @@
 
 #include <ngx_config.h>
 #include <ngx_core.h>
-#include <ngx_event.h>
 #include <nginx.h>
 
 
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,7 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define NGINX_VERSION      "0.7.14"
+#define NGINX_VERSION      "0.7.15"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_buf.h
+++ b/src/core/ngx_buf.h
@@ -78,6 +78,7 @@ typedef struct {
     ngx_chain_t                 *busy;
 
     unsigned                     sendfile;
+    unsigned                     directio;
     unsigned                     need_in_memory;
     unsigned                     need_in_temp;
 
--- a/src/core/ngx_core.h
+++ b/src/core/ngx_core.h
@@ -40,11 +40,11 @@ typedef void (*ngx_connection_handler_pt
 #include <ngx_rbtree.h>
 #include <ngx_time.h>
 #include <ngx_socket.h>
-#include <ngx_types.h>
+#include <ngx_string.h>
+#include <ngx_files.h>
 #include <ngx_shmem.h>
 #include <ngx_process.h>
 #include <ngx_user.h>
-#include <ngx_string.h>
 #include <ngx_parse.h>
 #include <ngx_log.h>
 #include <ngx_alloc.h>
@@ -55,7 +55,6 @@ typedef void (*ngx_connection_handler_pt
 #include <ngx_list.h>
 #include <ngx_hash.h>
 #include <ngx_file.h>
-#include <ngx_files.h>
 #include <ngx_crc.h>
 #include <ngx_crc32.h>
 #if (NGX_PCRE)
--- a/src/core/ngx_file.h
+++ b/src/core/ngx_file.h
@@ -26,7 +26,8 @@ struct ngx_file_s {
 
     ngx_log_t          *log;
 
-    ngx_uint_t          valid_info;  /* unsigned  valid_info:1; */
+    unsigned            valid_info:1;
+    unsigned            directio:1;
 };
 
 #define NGX_MAX_PATH_LEVEL  3
--- a/src/core/ngx_open_file_cache.c
+++ b/src/core/ngx_open_file_cache.c
@@ -502,6 +502,9 @@ ngx_open_and_stat_file(u_char *name, ngx
             if (ngx_directio(fd) == -1) {
                 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                               ngx_directio_n " \"%s\" failed", name);
+
+            } else {
+                of->is_directio = 1;
             }
         }
     }
--- a/src/core/ngx_open_file_cache.h
+++ b/src/core/ngx_open_file_cache.h
@@ -33,6 +33,7 @@ typedef struct {
     unsigned                 is_file:1;
     unsigned                 is_link:1;
     unsigned                 is_exec:1;
+    unsigned                 is_directio:1;
 } ngx_open_file_info_t;
 
 
--- a/src/core/ngx_output_chain.c
+++ b/src/core/ngx_output_chain.c
@@ -18,21 +18,21 @@
 
 
 static ngx_inline ngx_int_t
-    ngx_output_chain_need_to_copy(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf);
+    ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf);
 static ngx_int_t ngx_output_chain_add_copy(ngx_pool_t *pool,
     ngx_chain_t **chain, ngx_chain_t *in);
-static ngx_int_t ngx_output_chain_copy_buf(ngx_buf_t *dst, ngx_buf_t *src,
-    ngx_uint_t sendfile);
+static ngx_int_t ngx_output_chain_align_file_buf(ngx_output_chain_ctx_t *ctx,
+    off_t bsize);
+static ngx_int_t ngx_output_chain_get_buf(ngx_output_chain_ctx_t *ctx,
+    off_t bsize);
+static ngx_int_t ngx_output_chain_copy_buf(ngx_output_chain_ctx_t *ctx);
 
 
 ngx_int_t
 ngx_output_chain(ngx_output_chain_ctx_t *ctx, ngx_chain_t *in)
 {
     off_t         bsize;
-    size_t        size;
     ngx_int_t     rc, last;
-    ngx_uint_t    recycled;
-    ngx_buf_t    *b;
     ngx_chain_t  *cl, *out, **last_out;
 
     if (ctx->in == NULL && ctx->busy == NULL) {
@@ -51,7 +51,7 @@ ngx_output_chain(ngx_output_chain_ctx_t 
 #if (NGX_SENDFILE_LIMIT)
             && !(in->buf->in_file && in->buf->file_last > NGX_SENDFILE_LIMIT)
 #endif
-            && !ngx_output_chain_need_to_copy(ctx, in->buf))
+            && ngx_output_chain_as_is(ctx, in->buf))
         {
             return ctx->output_filter(ctx->filter_ctx, in);
         }
@@ -75,7 +75,7 @@ ngx_output_chain(ngx_output_chain_ctx_t 
 
             /*
              * cycle while there are the ctx->in bufs
-             * or there are the free output bufs to copy in
+             * and there are the free output bufs to copy in
              */
 
             bsize = ngx_buf_size(ctx->in->buf);
@@ -102,7 +102,7 @@ ngx_output_chain(ngx_output_chain_ctx_t 
                 continue;
             }
 
-            if (!ngx_output_chain_need_to_copy(ctx, ctx->in->buf)) {
+            if (ngx_output_chain_as_is(ctx, ctx->in->buf)) {
 
                 /* move the chain link to the output chain */
 
@@ -118,79 +118,35 @@ ngx_output_chain(ngx_output_chain_ctx_t 
 
             if (ctx->buf == NULL) {
 
-                /* get the free buf */
+                rc = ngx_output_chain_align_file_buf(ctx, bsize);
 
-                if (ctx->free) {
-                    cl = ctx->free;
-                    ctx->buf = cl->buf;
-                    ctx->free = cl->next;
-                    ngx_free_chain(ctx->pool, cl);
-
-                } else if (out || ctx->allocated == ctx->bufs.num) {
+                if (rc == NGX_ERROR) {
+                    return NGX_ERROR;
+                }
 
-                    break;
-
-                } else {
+                if (rc != NGX_OK) {
 
-                    size = ctx->bufs.size;
-                    recycled = 1;
+                    if (ctx->free) {
 
-                    if (ctx->in->buf->last_in_chain) {
-
-                        if (bsize < (off_t) ctx->bufs.size) {
+                        /* get the free buf */
 
-                           /*
-                            * allocate small temp buf for the small last buf
-                            * or its small last part
-                            */
+                        cl = ctx->free;
+                        ctx->buf = cl->buf;
+                        ctx->free = cl->next;
 
-                            size = (size_t) bsize;
-                            recycled = 0;
+                        ngx_free_chain(ctx->pool, cl);
 
-                        } else if (ctx->bufs.num == 1
-                                   && (bsize < (off_t) (ctx->bufs.size
-                                                     + (ctx->bufs.size >> 2))))
-                        {
-                            /*
-                             * allocate a temp buf that equals
-                             * to the last buf if the last buf size is lesser
-                             * than 1.25 of bufs.size and a temp buf is single
-                             */
+                    } else if (out || ctx->allocated == ctx->bufs.num) {
 
-                            size = (size_t) bsize;
-                            recycled = 0;
-                        }
-                    }
+                        break;
 
-                    b = ngx_calloc_buf(ctx->pool);
-                    if (b == NULL) {
+                    } else if (ngx_output_chain_get_buf(ctx, bsize) != NGX_OK) {
                         return NGX_ERROR;
                     }
-
-                    /*
-                     * allocate block aligned to a disk sector size
-                     * to enable O_DIRECT
-                     */
-
-                    b->start = ngx_pmemalign(ctx->pool, size, 512);
-                    if (b->start == NULL) {
-                        return NGX_ERROR;
-                    }
-
-                    b->pos = b->start;
-                    b->last = b->start;
-                    b->end = b->last + size;
-                    b->temporary = 1;
-                    b->tag = ctx->tag;
-                    b->recycled = recycled;
-
-                    ctx->buf = b;
-                    ctx->allocated++;
                 }
             }
 
-            rc = ngx_output_chain_copy_buf(ctx->buf, ctx->in->buf,
-                                           ctx->sendfile);
+            rc = ngx_output_chain_copy_buf(ctx);
 
             if (rc == NGX_ERROR) {
                 return rc;
@@ -244,11 +200,15 @@ ngx_output_chain(ngx_output_chain_ctx_t 
 
 
 static ngx_inline ngx_int_t
-ngx_output_chain_need_to_copy(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf)
+ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf)
 {
     ngx_uint_t  sendfile;
 
     if (ngx_buf_special(buf)) {
+        return 1;
+    }
+
+    if (buf->in_file && buf->file->directio) {
         return 0;
     }
 
@@ -265,21 +225,21 @@ ngx_output_chain_need_to_copy(ngx_output
     if (!sendfile) {
 
         if (!ngx_buf_in_memory(buf)) {
-            return 1;
+            return 0;
         }
 
         buf->in_file = 0;
     }
 
     if (ctx->need_in_memory && !ngx_buf_in_memory(buf)) {
-        return 1;
+        return 0;
     }
 
     if (ctx->need_in_temp && (buf->memory || buf->mmap)) {
-        return 1;
+        return 0;
     }
 
-    return 0;
+    return 1;
 }
 
 
@@ -313,6 +273,8 @@ ngx_output_chain_add_copy(ngx_pool_t *po
             && buf->file_pos < NGX_SENDFILE_LIMIT
             && buf->file_last > NGX_SENDFILE_LIMIT)
         {
+            /* split a file buf on two bufs by the sendfile limit */
+
             b = ngx_calloc_buf(pool);
             if (b == NULL) {
                 return NGX_ERROR;
@@ -352,10 +314,137 @@ ngx_output_chain_add_copy(ngx_pool_t *po
 
 
 static ngx_int_t
-ngx_output_chain_copy_buf(ngx_buf_t *dst, ngx_buf_t *src, ngx_uint_t sendfile)
+ngx_output_chain_align_file_buf(ngx_output_chain_ctx_t *ctx, off_t bsize)
+{
+    size_t      size;
+    ngx_buf_t  *in;
+
+    in = ctx->in->buf;
+
+    if (in->file == NULL || !in->file->directio) {
+        return NGX_DECLINED;
+    }
+
+    ctx->directio = 1;
+
+    size = (size_t) (in->file_pos - (in->file_pos & ~511));
+
+    if (size == 0) {
+
+        if (bsize >= ctx->bufs.size) {
+            return NGX_DECLINED;
+        }
+
+        size = (size_t) bsize;
+
+    } else {
+        size = 512 - size;
+
+        if (size > bsize) {
+            size = (size_t) bsize;
+        }
+    }
+
+    ctx->buf = ngx_create_temp_buf(ctx->pool, size);
+    if (ctx->buf == NULL) {
+        return NGX_ERROR;
+    }
+
+    /*
+     * we do not set ctx->buf->tag, because we do not want
+     * to reuse the buf via ctx->free list
+     */
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_output_chain_get_buf(ngx_output_chain_ctx_t *ctx, off_t bsize)
 {
-    off_t    size;
-    ssize_t  n;
+    size_t       size;
+    ngx_buf_t   *b, *in;
+    ngx_uint_t   recycled;
+
+    in = ctx->in->buf;
+    size = ctx->bufs.size;
+    recycled = 1;
+
+    if (in->last_in_chain) {
+
+        if (bsize < (off_t) size) {
+
+            /*
+             * allocate a small temp buf for a small last buf
+             * or its small last part
+             */
+
+            size = (size_t) bsize;
+            recycled = 0;
+
+        } else if (!ctx->directio
+                   && ctx->bufs.num == 1
+                   && (bsize < (off_t) (size + size / 4)))
+        {
+            /*
+             * allocate a temp buf that equals to a last buf,
+             * if there is no directio, the last buf size is lesser
+             * than 1.25 of bufs.size and the temp buf is single
+             */
+
+            size = (size_t) bsize;
+            recycled = 0;
+        }
+    }
+
+    b = ngx_calloc_buf(ctx->pool);
+    if (b == NULL) {
+        return NGX_ERROR;
+    }
+
+    if (ctx->directio) {
+
+        /*
+         * allocate block aligned to a disk sector size to enable
+         * userland buffer direct usage conjunctly with directio
+         */
+
+        b->start = ngx_pmemalign(ctx->pool, size, 512);
+        if (b->start == NULL) {
+            return NGX_ERROR;
+        }
+
+    } else {
+        b->start = ngx_palloc(ctx->pool, size);
+        if (b->start == NULL) {
+            return NGX_ERROR;
+        }
+    }
+
+    b->pos = b->start;
+    b->last = b->start;
+    b->end = b->last + size;
+    b->temporary = 1;
+    b->tag = ctx->tag;
+    b->recycled = recycled;
+
+    ctx->buf = b;
+    ctx->allocated++;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_output_chain_copy_buf(ngx_output_chain_ctx_t *ctx)
+{
+    off_t        size;
+    ssize_t      n;
+    ngx_buf_t   *src, *dst;
+    ngx_uint_t   sendfile;
+
+    src = ctx->in->buf;
+    dst = ctx->buf;
 
     size = ngx_buf_size(src);
 
@@ -363,6 +452,8 @@ ngx_output_chain_copy_buf(ngx_buf_t *dst
         size = dst->end - dst->pos;
     }
 
+    sendfile = ctx->sendfile & !ctx->directio;
+
 #if (NGX_SENDFILE_LIMIT)
 
     if (src->in_file && src->file_pos >= NGX_SENDFILE_LIMIT) {
@@ -413,7 +504,7 @@ ngx_output_chain_copy_buf(ngx_buf_t *dst
 #endif
 
         if (n != size) {
-            ngx_log_error(NGX_LOG_ALERT, src->file->log, 0,
+            ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0,
                           ngx_read_file_n " read only %z of %O from file",
                           n, size);
             if (n == 0) {
--- a/src/http/modules/ngx_http_fastcgi_module.c
+++ b/src/http/modules/ngx_http_fastcgi_module.c
@@ -7,7 +7,6 @@
 #include <ngx_config.h>
 #include <ngx_core.h>
 #include <ngx_http.h>
-#include <nginx.h>
 
 
 typedef struct {
--- a/src/http/modules/ngx_http_flv_module.c
+++ b/src/http/modules/ngx_http_flv_module.c
@@ -244,6 +244,7 @@ ngx_http_flv_handler(ngx_http_request_t 
     b->file->fd = of.fd;
     b->file->name = path;
     b->file->log = log;
+    b->file->directio = of.is_directio;
 
     out[1].buf = b;
     out[1].next = NULL;
--- a/src/http/modules/ngx_http_gzip_static_module.c
+++ b/src/http/modules/ngx_http_gzip_static_module.c
@@ -235,6 +235,7 @@ ngx_http_gzip_static_handler(ngx_http_re
     b->file->fd = of.fd;
     b->file->name = path;
     b->file->log = log;
+    b->file->directio = of.is_directio;
 
     out.buf = b;
     out.next = NULL;
--- a/src/http/modules/ngx_http_log_module.c
+++ b/src/http/modules/ngx_http_log_module.c
@@ -7,7 +7,6 @@
 #include <ngx_config.h>
 #include <ngx_core.h>
 #include <ngx_http.h>
-#include <nginx.h>
 
 
 typedef struct ngx_http_log_op_s  ngx_http_log_op_t;
--- a/src/http/modules/ngx_http_memcached_module.c
+++ b/src/http/modules/ngx_http_memcached_module.c
@@ -6,7 +6,6 @@
 
 #include <ngx_config.h>
 #include <ngx_core.h>
-#include <ngx_event.h>
 #include <ngx_http.h>
 
 
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -6,7 +6,6 @@
 
 #include <ngx_config.h>
 #include <ngx_core.h>
-#include <ngx_event.h>
 #include <ngx_http.h>
 
 
new file mode 100644
--- /dev/null
+++ b/src/http/modules/ngx_http_random_index_module.c
@@ -0,0 +1,321 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+    ngx_flag_t  enable;
+} ngx_http_random_index_loc_conf_t;
+
+
+#define NGX_HTTP_RANDOM_INDEX_PREALLOCATE  50
+
+
+static ngx_int_t ngx_http_random_index_error(ngx_http_request_t *r,
+    ngx_dir_t *dir, ngx_str_t *name);
+static ngx_int_t ngx_http_random_index_init(ngx_conf_t *cf);
+static void *ngx_http_random_index_create_loc_conf(ngx_conf_t *cf);
+static char *ngx_http_random_index_merge_loc_conf(ngx_conf_t *cf,
+    void *parent, void *child);
+
+
+static ngx_command_t  ngx_http_random_index_commands[] = {
+
+    { ngx_string("random_index"),
+      NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_flag_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_random_index_loc_conf_t, enable),
+      NULL },
+
+      ngx_null_command
+};
+
+
+static ngx_http_module_t  ngx_http_random_index_module_ctx = {
+    NULL,                                  /* preconfiguration */
+    ngx_http_random_index_init,            /* postconfiguration */
+
+    NULL,                                  /* create main configuration */
+    NULL,                                  /* init main configuration */
+
+    NULL,                                  /* create server configuration */
+    NULL,                                  /* merge server configuration */
+
+    ngx_http_random_index_create_loc_conf, /* create location configration */
+    ngx_http_random_index_merge_loc_conf   /* merge location configration */
+};
+
+
+ngx_module_t  ngx_http_random_index_module = {
+    NGX_MODULE_V1,
+    &ngx_http_random_index_module_ctx,     /* module context */
+    ngx_http_random_index_commands,        /* module directives */
+    NGX_HTTP_MODULE,                       /* module type */
+    NULL,                                  /* init master */
+    NULL,                                  /* init module */
+    NULL,                                  /* init process */
+    NULL,                                  /* init thread */
+    NULL,                                  /* exit thread */
+    NULL,                                  /* exit process */
+    NULL,                                  /* exit master */
+    NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_http_random_index_handler(ngx_http_request_t *r)
+{
+    u_char                            *last, *filename;
+    size_t                             len, allocated, root;
+    ngx_err_t                          err;
+    ngx_int_t                          rc;
+    ngx_str_t                          path, uri, *name;
+    ngx_dir_t                          dir;
+    ngx_uint_t                         n, level;
+    ngx_array_t                        names;
+    ngx_http_random_index_loc_conf_t  *rlcf;
+
+    if (r->uri.data[r->uri.len - 1] != '/') {
+        return NGX_DECLINED;
+    }
+
+    /* TODO: Win32 */
+    if (r->zero_in_uri) {
+        return NGX_DECLINED;
+    }
+
+    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {
+        return NGX_DECLINED;
+    }
+
+    rlcf = ngx_http_get_module_loc_conf(r, ngx_http_random_index_module);
+
+    if (!rlcf->enable) {
+        return NGX_DECLINED;
+    }
+
+#if (NGX_HAVE_D_TYPE)
+    len = NGX_DIR_MASK_LEN;
+#else
+    len = NGX_HTTP_RANDOM_INDEX_PREALLOCATE;
+#endif
+
+    last = ngx_http_map_uri_to_path(r, &path, &root, len);
+    if (last == NULL) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    allocated = path.len;
+
+    path.len = last - path.data - 1;
+    path.data[path.len] = '\0';
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http random index: \"%s\"", path.data);
+
+    if (ngx_open_dir(&path, &dir) == NGX_ERROR) {
+        err = ngx_errno;
+
+        if (err == NGX_ENOENT
+            || err == NGX_ENOTDIR
+            || err == NGX_ENAMETOOLONG)
+        {
+            level = NGX_LOG_ERR;
+            rc = NGX_HTTP_NOT_FOUND;
+
+        } else if (err == NGX_EACCES) {
+            level = NGX_LOG_ERR;
+            rc = NGX_HTTP_FORBIDDEN;
+
+        } else {
+            level = NGX_LOG_CRIT;
+            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        ngx_log_error(level, r->connection->log, err,
+                      ngx_open_dir_n " \"%s\" failed", path.data);
+
+        return rc;
+    }
+
+    if (ngx_array_init(&names, r->pool, 32, sizeof(ngx_str_t)) != NGX_OK) {
+        return ngx_http_random_index_error(r, &dir, &path);
+    }
+
+    filename = path.data;
+    filename[path.len] = '/';
+
+    for ( ;; ) {
+        ngx_set_errno(0);
+
+        if (ngx_read_dir(&dir) == NGX_ERROR) {
+            err = ngx_errno;
+
+            if (err != NGX_ENOMOREFILES) {
+                ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
+                              ngx_read_dir_n " \"%V\" failed", &path);
+                return ngx_http_random_index_error(r, &dir, &path);
+            }
+
+            break;
+        }
+
+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "http random index file: \"%s\"", ngx_de_name(&dir));
+
+        if (ngx_de_name(&dir)[0] == '.') {
+            continue;
+        }
+
+        len = ngx_de_namelen(&dir);
+
+        if (!dir.valid_type) {
+
+            /* 1 byte for '/' and 1 byte for terminating '\0' */
+
+            if (path.len + 1 + len + 1 > allocated) {
+                allocated = path.len + 1 + len + 1
+                                     + NGX_HTTP_RANDOM_INDEX_PREALLOCATE;
+
+                filename = ngx_pnalloc(r->pool, allocated);
+                if (filename == NULL) {
+                    return ngx_http_random_index_error(r, &dir, &path);
+                }
+
+                last = ngx_cpystrn(filename, path.data, path.len + 1);
+                *last++ = '/';
+            }
+
+            ngx_cpystrn(last, ngx_de_name(&dir), len + 1);
+
+            if (ngx_de_info(filename, &dir) == NGX_FILE_ERROR) {
+                err = ngx_errno;
+
+                if (err != NGX_ENOENT) {
+                    ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
+                                  ngx_de_info_n " \"%s\" failed", filename);
+                    return ngx_http_random_index_error(r, &dir, &path);
+                }
+
+                if (ngx_de_link_info(filename, &dir) == NGX_FILE_ERROR) {
+                    ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
+                                  ngx_de_link_info_n " \"%s\" failed",
+                                  filename);
+                    return ngx_http_random_index_error(r, &dir, &path);
+                }
+            }
+        }
+
+        if (!ngx_de_is_file(&dir)) {
+            continue;
+        }
+
+        name = ngx_array_push(&names);
+        if (name == NULL) {
+            return ngx_http_random_index_error(r, &dir, &path);
+        }
+
+        name->len = len;
+
+        name->data = ngx_pnalloc(r->pool, len);
+        if (name->data == NULL) {
+            return ngx_http_random_index_error(r, &dir, &path);
+        }
+
+        ngx_memcpy(name->data, ngx_de_name(&dir), len);
+    }
+
+    if (ngx_close_dir(&dir) == NGX_ERROR) {
+        ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
+                      ngx_close_dir_n " \"%s\" failed", &path);
+    }
+
+    n = names.nelts;
+
+    if (n == 0) {
+        return NGX_DECLINED;
+    }
+
+    name = names.elts;
+
+    n = (ngx_uint_t) (((uint64_t) ngx_random() * n) / 0x80000000);
+
+    uri.len = r->uri.len + name[n].len;
+
+    uri.data = ngx_pnalloc(r->pool, uri.len);
+    if (uri.data == NULL) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    last = ngx_copy(uri.data, r->uri.data, r->uri.len);
+    ngx_memcpy(last, name[n].data, name[n].len);
+
+    return ngx_http_internal_redirect(r, &uri, &r->args);
+}
+
+
+static ngx_int_t
+ngx_http_random_index_error(ngx_http_request_t *r, ngx_dir_t *dir,
+    ngx_str_t *name)
+{
+    if (ngx_close_dir(dir) == NGX_ERROR) {
+        ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
+                      ngx_close_dir_n " \"%V\" failed", name);
+    }
+
+    return NGX_HTTP_INTERNAL_SERVER_ERROR;
+}
+
+
+static void *
+ngx_http_random_index_create_loc_conf(ngx_conf_t *cf)
+{
+    ngx_http_random_index_loc_conf_t  *conf;
+
+    conf = ngx_palloc(cf->pool, sizeof(ngx_http_random_index_loc_conf_t));
+    if (conf == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    conf->enable = NGX_CONF_UNSET;
+
+    return conf;
+}
+
+
+static char *
+ngx_http_random_index_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+    ngx_http_random_index_loc_conf_t *prev = parent;
+    ngx_http_random_index_loc_conf_t *conf = child;
+
+    ngx_conf_merge_value(conf->enable, prev->enable, 0);
+
+    return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_random_index_init(ngx_conf_t *cf)
+{
+    ngx_http_handler_pt        *h;
+    ngx_http_core_main_conf_t  *cmcf;
+
+    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+    h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
+    if (h == NULL) {
+        return NGX_ERROR;
+    }
+
+    *h = ngx_http_random_index_handler;
+
+    return NGX_OK;
+}
--- a/src/http/modules/ngx_http_static_module.c
+++ b/src/http/modules/ngx_http_static_module.c
@@ -251,6 +251,7 @@ ngx_http_static_handler(ngx_http_request
     b->file->fd = of.fd;
     b->file->name = path;
     b->file->log = log;
+    b->file->directio = of.is_directio;
 
     out.buf = b;
     out.next = NULL;
--- a/src/http/modules/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -47,7 +47,7 @@ our @EXPORT = qw(
     HTTP_INSUFFICIENT_STORAGE
 );
 
-our $VERSION = '0.7.14';
+our $VERSION = '0.7.15';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/modules/perl/nginx.xs
+++ b/src/http/modules/perl/nginx.xs
@@ -681,6 +681,7 @@ sendfile(r, filename, offset = -1, bytes
 
     b->file->fd = of.fd;
     b->file->log = r->connection->log;
+    b->file->directio = of.is_directio;
 
     (void) ngx_http_perl_output(r, b);
 
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -6,7 +6,6 @@
 
 #include <ngx_config.h>
 #include <ngx_core.h>
-#include <ngx_event.h>
 #include <ngx_http.h>
 
 
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -6,9 +6,7 @@
 
 #include <ngx_config.h>
 #include <ngx_core.h>
-#include <ngx_event.h>
 #include <ngx_http.h>
-#include <nginx.h>
 
 
 typedef struct {
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -8,8 +8,8 @@
 #define _NGX_HTTP_CORE_H_INCLUDED_
 
 
-#include <ngx_string.h>
-#include <ngx_array.h>
+#include <ngx_config.h>
+#include <ngx_core.h>
 #include <ngx_http.h>
 
 
--- a/src/http/ngx_http_parse.c
+++ b/src/http/ngx_http_parse.c
@@ -720,7 +720,7 @@ ngx_http_parse_header_line(ngx_http_requ
     static u_char  lowcase[] =
         "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
         "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0"
-        "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
+        "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0_"
         "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
         "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
         "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
--- a/src/http/ngx_http_parse_time.c
+++ b/src/http/ngx_http_parse_time.c
@@ -6,7 +6,6 @@
 
 #include <ngx_config.h>
 #include <ngx_core.h>
-#include <ngx_types.h>
 
 
 static int mday[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -6,7 +6,6 @@
 
 #include <ngx_config.h>
 #include <ngx_core.h>
-#include <ngx_event.h>
 #include <ngx_http.h>
 
 
--- a/src/http/ngx_http_request_body.c
+++ b/src/http/ngx_http_request_body.c
@@ -6,7 +6,6 @@
 
 #include <ngx_config.h>
 #include <ngx_core.h>
-#include <ngx_event.h>
 #include <ngx_http.h>
 
 
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -6,7 +6,6 @@
 
 #include <ngx_config.h>
 #include <ngx_core.h>
-#include <ngx_event.h>
 #include <ngx_http.h>
 #include <nginx.h>
 
--- a/src/http/ngx_http_variables.h
+++ b/src/http/ngx_http_variables.h
@@ -10,7 +10,6 @@
 
 #include <ngx_config.h>
 #include <ngx_core.h>
-#include <ngx_event.h>
 #include <ngx_http.h>
 
 
--- a/src/http/ngx_http_write_filter_module.c
+++ b/src/http/ngx_http_write_filter_module.c
@@ -6,7 +6,6 @@
 
 #include <ngx_config.h>
 #include <ngx_core.h>
-#include <ngx_event.h>
 #include <ngx_http.h>
 
 
--- a/src/os/unix/ngx_files.c
+++ b/src/os/unix/ngx_files.c
@@ -249,12 +249,33 @@ ngx_open_dir(ngx_str_t *name, ngx_dir_t 
     }
 
     dir->valid_info = 0;
+#if (NGX_HAVE_D_TYPE)
+    dir->valid_type = 1;
+#else
+    dir->valid_type = 0;
+#endif
 
     return NGX_OK;
 }
 
 
 ngx_int_t
+ngx_read_dir(ngx_dir_t *dir)
+{
+    dir->de = readdir(dir->dir);
+
+    if (dir->de) {
+#if (NGX_HAVE_D_TYPE)
+        dir->type = dir->de->d_type;
+#endif
+        return NGX_OK;
+    }
+
+    return NGX_ERROR;
+}
+
+
+ngx_int_t
 ngx_open_glob(ngx_glob_t *gl)
 {
     int  n;
--- a/src/os/unix/ngx_files.h
+++ b/src/os/unix/ngx_files.h
@@ -12,6 +12,31 @@
 #include <ngx_core.h>
 
 
+typedef int                      ngx_fd_t;
+typedef struct stat              ngx_file_info_t;
+typedef ino_t                    ngx_file_uniq_t;
+
+
+typedef struct {
+    DIR                        *dir;
+    struct dirent              *de;
+    struct stat                 info;
+
+    unsigned                    type:8;
+    unsigned                    valid_info:1;
+    unsigned                    valid_type:1;
+} ngx_dir_t;
+
+
+typedef struct {
+    size_t                       n;
+    glob_t                       pglob;
+    u_char                      *pattern;
+    ngx_log_t                   *log;
+    ngx_uint_t                   test;
+} ngx_glob_t;
+
+
 #define NGX_INVALID_FILE         -1
 #define NGX_FILE_ERROR           -1
 
@@ -135,8 +160,7 @@ ngx_int_t ngx_open_dir(ngx_str_t *name, 
 #define ngx_close_dir_n          "closedir()"
 
 
-#define ngx_read_dir(d)                                                      \
-    (((d)->de = readdir((d)->dir)) ? NGX_OK : NGX_ERROR)
+ngx_int_t ngx_read_dir(ngx_dir_t *dir);
 #define ngx_read_dir_n           "readdir()"
 
 
@@ -152,7 +176,7 @@ ngx_int_t ngx_open_dir(ngx_str_t *name, 
 
 
 #define ngx_de_name(dir)         ((u_char *) (dir)->de->d_name)
-#if (NGX_FREEBSD)
+#if (NGX_HAVE_D_NAMLEN)
 #define ngx_de_namelen(dir)      (dir)->de->d_namlen
 #else
 #define ngx_de_namelen(dir)      ngx_strlen((dir)->de->d_name)
@@ -161,23 +185,26 @@ ngx_int_t ngx_open_dir(ngx_str_t *name, 
 #define ngx_de_info_n            "stat()"
 #define ngx_de_link_info(name, dir)  lstat((const char *) name, &(dir)->info)
 #define ngx_de_link_info_n       "lstat()"
+
+#if (NGX_HAVE_D_TYPE)
+
+#define ngx_de_is_dir(dir)       ((dir)->type == DT_DIR)
+#define ngx_de_is_file(dir)      ((dir)->type == DT_REG)
+#define ngx_de_is_link(dir)      ((dir)->type == DT_LINK)
+
+#else
+
 #define ngx_de_is_dir(dir)       (S_ISDIR((dir)->info.st_mode))
 #define ngx_de_is_file(dir)      (S_ISREG((dir)->info.st_mode))
 #define ngx_de_is_link(dir)      (S_ISLNK((dir)->info.st_mode))
+
+#endif
+
 #define ngx_de_access(dir)       (((dir)->info.st_mode) & 0777)
 #define ngx_de_size(dir)         (dir)->info.st_size
 #define ngx_de_mtime(dir)        (dir)->info.st_mtime
 
 
-typedef struct {
-    size_t       n;
-    glob_t       pglob;
-    u_char      *pattern;
-    ngx_log_t   *log;
-    ngx_uint_t   test;
-} ngx_glob_t;
-
-
 ngx_int_t ngx_open_glob(ngx_glob_t *gl);
 #define ngx_open_glob_n          "glob()"
 ngx_int_t ngx_read_glob(ngx_glob_t *gl, ngx_str_t *name);
deleted file mode 100644
--- a/src/os/unix/ngx_types.h
+++ /dev/null
@@ -1,27 +0,0 @@
-
-/*
- * Copyright (C) Igor Sysoev
- */
-
-
-#ifndef _NGX_TYPES_H_INCLUDED_
-#define _NGX_TYPES_H_INCLUDED_
-
-
-#include <ngx_config.h>
-
-
-typedef int            ngx_fd_t;
-typedef struct stat    ngx_file_info_t;
-typedef ino_t          ngx_file_uniq_t;
-
-typedef struct {
-    DIR              *dir;
-    struct dirent    *de;
-    struct stat       info;
-
-    ngx_uint_t        valid_info:1;  /* unsigned  valid_info:1; */
-} ngx_dir_t;
-
-
-#endif /* _NGX_TYPES_H_INCLUDED_ */