changeset 532:f7ec98e3caeb NGINX_0_8_18

nginx 0.8.18 *) Feature: the "read_ahead" directive. *) Feature: now several "perl_modules" directive may be used. *) Feature: the "limit_req_log_level" and "limit_conn_log_level" directives. *) Bugfix: now "limit_req" directive conforms to the leaky bucket algorithm. Thanks to Maxim Dounin. *) Bugfix: nginx did not work on Linux/sparc. Thanks to Marcus Ramberg. *) Bugfix: nginx sent '\0' in a "Location" response header line on MKCOL request. Thanks to Xie Zhenye. *) Bugfix: zero status code was logged instead of 499 status code; the bug had appeared in 0.8.11. *) Bugfix: socket leak; the bug had appeared in 0.8.11.
author Igor Sysoev <http://sysoev.ru>
date Tue, 06 Oct 2009 00:00:00 +0400
parents 7688992d2abb
children 4a44adbff19a
files CHANGES CHANGES.ru auto/os/features src/core/nginx.c src/core/nginx.h src/core/ngx_open_file_cache.c src/core/ngx_open_file_cache.h src/core/ngx_palloc.c src/http/modules/ngx_http_dav_module.c src/http/modules/ngx_http_flv_module.c src/http/modules/ngx_http_gzip_static_module.c src/http/modules/ngx_http_index_module.c src/http/modules/ngx_http_limit_req_module.c src/http/modules/ngx_http_limit_zone_module.c src/http/modules/ngx_http_rewrite_module.c src/http/modules/ngx_http_static_module.c src/http/modules/perl/nginx.pm src/http/modules/perl/nginx.xs src/http/modules/perl/ngx_http_perl_module.c src/http/ngx_http_core_module.c src/http/ngx_http_core_module.h src/http/ngx_http_file_cache.c src/http/ngx_http_request.c src/http/ngx_http_request_body.c src/http/ngx_http_script.c src/os/unix/ngx_files.h
diffstat 26 files changed, 266 insertions(+), 84 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,30 @@
 
+Changes with nginx 0.8.18                                        06 Oct 2009
+
+    *) Feature: the "read_ahead" directive.
+
+    *) Feature: now several "perl_modules" directive may be used.
+
+    *) Feature: the "limit_req_log_level" and "limit_conn_log_level" 
+       directives.
+
+    *) Bugfix: now "limit_req" directive conforms to the leaky bucket 
+       algorithm.
+       Thanks to Maxim Dounin.
+
+    *) Bugfix: nginx did not work on Linux/sparc.
+       Thanks to Marcus Ramberg.
+
+    *) Bugfix: nginx sent '\0' in a "Location" response header line on 
+       MKCOL request.
+       Thanks to Xie Zhenye.
+
+    *) Bugfix: zero status code was logged instead of 499 status code; the 
+       bug had appeared in 0.8.11.
+
+    *) Bugfix: socket leak; the bug had appeared in 0.8.11.
+
+
 Changes with nginx 0.8.17                                        28 Sep 2009
 
     *) Security: now "/../" are disabled in "Destination" request header 
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,30 @@
 
+Изменения в nginx 0.8.18                                          06.10.2009
+
+    *) Добавление: директива read_ahead.
+
+    *) Добавление: теперь можно использовать несколько директив 
+       perl_modules.
+
+    *) Добавление: директивы limit_req_log_level и limit_conn_log_level.
+
+    *) Исправление: Теперь директива limit_req соответствует алгоритму 
+       leaky bucket.
+       Спасибо Максиму Дунину.
+
+    *) Исправление: nginx не работал на Linux/sparc.
+       Спасибо Marcus Ramberg.
+
+    *) Исправление: nginx слал символ '\0' в строке "Location" в заголовке 
+       в ответе на запрос MKCOL.
+       Спасибо Xie Zhenye.
+
+    *) Исправление: вместо кода ответа 499 в лог записывался код 0; ошибка 
+       появилась в 0.8.11.
+
+    *) Исправление: утечки сокетов; ошибка появилась в 0.8.11.
+
+
 Изменения в nginx 0.8.17                                          28.09.2009
 
     *) Безопасность: теперь символы "/../" запрещены в строке "Destination" 
--- a/auto/os/features
+++ b/auto/os/features
@@ -172,6 +172,26 @@ if [ $ngx_found = no ]; then
 fi
 
 
+ngx_feature="F_READAHEAD"
+ngx_feature_name="NGX_HAVE_F_READAHEAD"
+ngx_feature_run=no
+ngx_feature_incs="#include <fcntl.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="fcntl(0, F_READAHEAD, 1);"
+. auto/feature
+
+
+ngx_feature="posix_fadvise()"
+ngx_feature_name="NGX_HAVE_POSIX_FADVISE"
+ngx_feature_run=no
+ngx_feature_incs="#include <fcntl.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="posix_fadvise(0, 0, 0, POSIX_FADV_SEQUENTIAL);"
+. auto/feature
+
+
 ngx_feature="O_DIRECT"
 ngx_feature_name="NGX_HAVE_O_DIRECT"
 ngx_feature_run=no
--- a/src/core/nginx.c
+++ b/src/core/nginx.c
@@ -280,6 +280,9 @@ main(int argc, char *const *argv)
     init_cycle.log = log;
     ngx_cycle = &init_cycle;
 
+    /* dummy pagesize to create aligned pool */
+    ngx_pagesize = 1024;
+
     init_cycle.pool = ngx_create_pool(1024, log);
     if (init_cycle.pool == NULL) {
         return 1;
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,8 +8,8 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define nginx_version         8017
-#define NGINX_VERSION      "0.8.17"
+#define nginx_version         8018
+#define NGINX_VERSION      "0.8.18"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_open_file_cache.c
+++ b/src/core/ngx_open_file_cache.c
@@ -17,6 +17,9 @@
  */
 
 
+#define NGX_MIN_READ_AHEAD  (128 * 1024)
+
+
 static void ngx_open_file_cache_cleanup(void *data);
 static ngx_int_t ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of,
     ngx_log_t *log);
@@ -524,6 +527,13 @@ ngx_open_and_stat_file(u_char *name, ngx
     } else {
         of->fd = fd;
 
+        if (of->read_ahead && ngx_file_size(&fi) > NGX_MIN_READ_AHEAD) {
+            if (ngx_read_ahead(fd, of->read_ahead) == NGX_ERROR) {
+                ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+                              ngx_read_ahead_n " \"%s\" failed", name);
+            }
+        }
+
         if (of->directio <= ngx_file_size(&fi)) {
             if (ngx_directio_on(fd) == NGX_FILE_ERROR) {
                 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
--- a/src/core/ngx_open_file_cache.h
+++ b/src/core/ngx_open_file_cache.h
@@ -21,6 +21,7 @@ typedef struct {
     time_t                   mtime;
     off_t                    size;
     off_t                    directio;
+    size_t                   read_ahead;
 
     ngx_err_t                err;
     char                    *failed;
--- a/src/core/ngx_palloc.c
+++ b/src/core/ngx_palloc.c
@@ -17,7 +17,7 @@ ngx_create_pool(size_t size, ngx_log_t *
 {
     ngx_pool_t  *p;
 
-    p = ngx_alloc(size, log);
+    p = ngx_memalign(ngx_pagesize, size, log);
     if (p == NULL) {
         return NULL;
     }
@@ -181,7 +181,7 @@ ngx_palloc_block(ngx_pool_t *pool, size_
 
     psize = (size_t) (pool->d.end - (u_char *) pool);
 
-    m = ngx_alloc(psize, pool->log);
+    m = ngx_memalign(ngx_pagesize, psize, pool->log);
     if (m == NULL) {
         return NULL;
     }
@@ -219,7 +219,7 @@ ngx_palloc_large(ngx_pool_t *pool, size_
     ngx_uint_t         n;
     ngx_pool_large_t  *large;
 
-    p = ngx_alloc(size, pool->log);
+    p = ngx_memalign(ngx_pagesize, size, pool->log);
     if (p == NULL) {
         return NULL;
     }
--- a/src/http/modules/ngx_http_dav_module.c
+++ b/src/http/modules/ngx_http_dav_module.c
@@ -490,6 +490,7 @@ ngx_http_dav_mkcol_handler(ngx_http_requ
     p = ngx_http_map_uri_to_path(r, &path, &root, 0);
 
     *(p - 1) = '\0';
+    r->uri.len--;
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http mkcol path: \"%s\"", path.data);
--- a/src/http/modules/ngx_http_flv_module.c
+++ b/src/http/modules/ngx_http_flv_module.c
@@ -106,6 +106,7 @@ ngx_http_flv_handler(ngx_http_request_t 
 
     ngx_memzero(&of, sizeof(ngx_open_file_info_t));
 
+    of.read_ahead = clcf->read_ahead;
     of.directio = clcf->directio;
     of.valid = clcf->open_file_cache_valid;
     of.min_uses = clcf->open_file_cache_min_uses;
--- a/src/http/modules/ngx_http_gzip_static_module.c
+++ b/src/http/modules/ngx_http_gzip_static_module.c
@@ -124,6 +124,7 @@ ngx_http_gzip_static_handler(ngx_http_re
 
     ngx_memzero(&of, sizeof(ngx_open_file_info_t));
 
+    of.read_ahead = clcf->read_ahead;
     of.directio = clcf->directio;
     of.valid = clcf->open_file_cache_valid;
     of.min_uses = clcf->open_file_cache_min_uses;
--- a/src/http/modules/ngx_http_index_module.c
+++ b/src/http/modules/ngx_http_index_module.c
@@ -205,6 +205,7 @@ ngx_http_index_handler(ngx_http_request_
 
         ngx_memzero(&of, sizeof(ngx_open_file_info_t));
 
+        of.read_ahead = clcf->read_ahead;
         of.directio = clcf->directio;
         of.valid = clcf->open_file_cache_valid;
         of.min_uses = clcf->open_file_cache_min_uses;
--- a/src/http/modules/ngx_http_limit_req_module.c
+++ b/src/http/modules/ngx_http_limit_req_module.c
@@ -42,7 +42,10 @@ typedef struct {
     ngx_shm_zone_t              *shm_zone;
     /* integer value, 1 corresponds to 0.001 r/s */
     ngx_uint_t                   burst;
-    ngx_uint_t                   nodelay;/* unsigned  nodelay:1 */
+    ngx_uint_t                   limit_log_level;
+    ngx_uint_t                   delay_log_level;
+
+    ngx_uint_t                   nodelay; /* unsigned  nodelay:1 */
 } ngx_http_limit_req_conf_t;
 
 
@@ -62,6 +65,15 @@ static char *ngx_http_limit_req(ngx_conf
 static ngx_int_t ngx_http_limit_req_init(ngx_conf_t *cf);
 
 
+static ngx_conf_enum_t  ngx_http_limit_req_log_levels[] = {
+    { ngx_string("info"), NGX_LOG_INFO },
+    { ngx_string("notice"), NGX_LOG_NOTICE },
+    { ngx_string("warn"), NGX_LOG_WARN },
+    { ngx_string("error"), NGX_LOG_ERR },
+    { ngx_null_string, 0 }
+};
+
+
 static ngx_command_t  ngx_http_limit_req_commands[] = {
 
     { ngx_string("limit_req_zone"),
@@ -78,6 +90,13 @@ static ngx_command_t  ngx_http_limit_req
       0,
       NULL },
 
+    { ngx_string("limit_req_log_level"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_enum_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_limit_req_conf_t, limit_log_level),
+      &ngx_http_limit_req_log_levels },
+
       ngx_null_command
 };
 
@@ -186,7 +205,7 @@ ngx_http_limit_req_handler(ngx_http_requ
     if (rc == NGX_BUSY) {
         ngx_shmtx_unlock(&ctx->shpool->mutex);
 
-        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+        ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,
                       "limiting requests, excess: %ui.%03ui by zone \"%V\"",
                       excess / 1000, excess % 1000, &lrcf->shm_zone->shm.name);
 
@@ -200,7 +219,7 @@ ngx_http_limit_req_handler(ngx_http_requ
             return NGX_DECLINED;
         }
 
-        ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
+        ngx_log_error(lrcf->delay_log_level, r->connection->log, 0,
                       "delaying request, excess: %ui.%03ui, by zone \"%V\"",
                       excess / 1000, excess % 1000, &lrcf->shm_zone->shm.name);
 
@@ -379,6 +398,11 @@ ngx_http_limit_req_lookup(ngx_http_limit
 
                 excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
 
+                if ((ngx_uint_t) excess > lrcf->burst) {
+                    *lrp = lr;
+                    return NGX_BUSY;
+                }
+
                 if (excess < 0) {
                     excess = 0;
                 }
@@ -388,10 +412,6 @@ ngx_http_limit_req_lookup(ngx_http_limit
 
                 *lrp = lr;
 
-                if ((ngx_uint_t) excess > lrcf->burst) {
-                    return NGX_BUSY;
-                }
-
                 if (excess) {
                     return NGX_AGAIN;
                 }
@@ -548,6 +568,8 @@ ngx_http_limit_req_create_conf(ngx_conf_
      *     conf->nodelay = 0;
      */
 
+    conf->limit_log_level = NGX_CONF_UNSET_UINT;
+
     return conf;
 }
 
@@ -562,6 +584,12 @@ ngx_http_limit_req_merge_conf(ngx_conf_t
         *conf = *prev;
     }
 
+    ngx_conf_merge_uint_value(conf->limit_log_level, prev->limit_log_level,
+                              NGX_LOG_ERR);
+
+    conf->delay_log_level = (conf->limit_log_level == NGX_LOG_INFO) ?
+                                NGX_LOG_INFO : conf->limit_log_level + 1;
+
     return NGX_CONF_OK;
 }
 
--- a/src/http/modules/ngx_http_limit_zone_module.c
+++ b/src/http/modules/ngx_http_limit_zone_module.c
@@ -33,6 +33,7 @@ typedef struct {
 typedef struct {
     ngx_shm_zone_t     *shm_zone;
     ngx_uint_t          conn;
+    ngx_uint_t          log_level;
 } ngx_http_limit_zone_conf_t;
 
 
@@ -48,6 +49,15 @@ static char *ngx_http_limit_conn(ngx_con
 static ngx_int_t ngx_http_limit_zone_init(ngx_conf_t *cf);
 
 
+static ngx_conf_enum_t  ngx_http_limit_conn_log_levels[] = {
+    { ngx_string("info"), NGX_LOG_INFO },
+    { ngx_string("notice"), NGX_LOG_NOTICE },
+    { ngx_string("warn"), NGX_LOG_WARN },
+    { ngx_string("error"), NGX_LOG_ERR },
+    { ngx_null_string, 0 }
+};
+
+
 static ngx_command_t  ngx_http_limit_zone_commands[] = {
 
     { ngx_string("limit_zone"),
@@ -64,6 +74,13 @@ static ngx_command_t  ngx_http_limit_zon
       0,
       NULL },
 
+    { ngx_string("limit_conn_log_level"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_enum_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_limit_zone_conf_t, log_level),
+      &ngx_http_limit_conn_log_levels },
+
       ngx_null_command
 };
 
@@ -189,7 +206,7 @@ ngx_http_limit_zone_handler(ngx_http_req
 
                 ngx_shmtx_unlock(&shpool->mutex);
 
-                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                ngx_log_error(lzcf->log_level, r->connection->log, 0,
                               "limiting connections by zone \"%V\"",
                               &lzcf->shm_zone->shm.name);
 
@@ -391,6 +408,8 @@ ngx_http_limit_zone_create_conf(ngx_conf
      *     conf->conn = 0;
      */
 
+    conf->log_level = NGX_CONF_UNSET_UINT;
+
     return conf;
 }
 
@@ -405,6 +424,8 @@ ngx_http_limit_zone_merge_conf(ngx_conf_
         *conf = *prev;
     }
 
+    ngx_conf_merge_uint_value(conf->log_level, prev->log_level, NGX_LOG_ERR);
+
     return NGX_CONF_OK;
 }
 
--- a/src/http/modules/ngx_http_rewrite_module.c
+++ b/src/http/modules/ngx_http_rewrite_module.c
@@ -446,7 +446,7 @@ ngx_http_rewrite(ngx_conf_t *cf, ngx_com
             return NGX_CONF_ERROR;
         }
 
-        *code = (uintptr_t) NULL;
+        *code = NULL;
     }
 
     regex->next = (u_char *) lcf->codes->elts + lcf->codes->nelts
--- a/src/http/modules/ngx_http_static_module.c
+++ b/src/http/modules/ngx_http_static_module.c
@@ -91,6 +91,7 @@ ngx_http_static_handler(ngx_http_request
 
     ngx_memzero(&of, sizeof(ngx_open_file_info_t));
 
+    of.read_ahead = clcf->read_ahead;
     of.directio = clcf->directio;
     of.valid = clcf->open_file_cache_valid;
     of.min_uses = clcf->open_file_cache_min_uses;
--- 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.8.17';
+our $VERSION = '0.8.18';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/modules/perl/nginx.xs
+++ b/src/http/modules/perl/nginx.xs
@@ -648,6 +648,7 @@ sendfile(r, filename, offset = -1, bytes
 
     ngx_memzero(&of, sizeof(ngx_open_file_info_t));
 
+    of.read_ahead = clcf->read_ahead;
     of.directio = clcf->directio;
     of.valid = clcf->open_file_cache_valid;
     of.min_uses = clcf->open_file_cache_min_uses;
--- a/src/http/modules/perl/ngx_http_perl_module.c
+++ b/src/http/modules/perl/ngx_http_perl_module.c
@@ -13,8 +13,8 @@
 typedef struct {
     PerlInterpreter   *perl;
     HV                *nginx;
-    ngx_str_t          modules;
-    ngx_array_t        requires;
+    ngx_array_t       *modules;
+    ngx_array_t       *requires;
 } ngx_http_perl_main_conf_t;
 
 
@@ -57,8 +57,6 @@ static char *ngx_http_perl_init_main_con
 static void *ngx_http_perl_create_loc_conf(ngx_conf_t *cf);
 static char *ngx_http_perl_merge_loc_conf(ngx_conf_t *cf, void *parent,
     void *child);
-static char *ngx_http_perl_require(ngx_conf_t *cf, ngx_command_t *cmd,
-    void *conf);
 static char *ngx_http_perl(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 static char *ngx_http_perl_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 
@@ -74,16 +72,16 @@ static ngx_command_t  ngx_http_perl_comm
 
     { ngx_string("perl_modules"),
       NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
-      ngx_conf_set_str_slot,
+      ngx_conf_set_str_array_slot,
       NGX_HTTP_MAIN_CONF_OFFSET,
       offsetof(ngx_http_perl_main_conf_t, modules),
       NULL },
 
     { ngx_string("perl_require"),
       NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
-      ngx_http_perl_require,
+      ngx_conf_set_str_array_slot,
       NGX_HTTP_MAIN_CONF_OFFSET,
-      0,
+      offsetof(ngx_http_perl_main_conf_t, requires),
       NULL },
 
     { ngx_string("perl"),
@@ -463,8 +461,10 @@ ngx_http_perl_ssi(ngx_http_request_t *r,
 static char *
 ngx_http_perl_init_interpreter(ngx_conf_t *cf, ngx_http_perl_main_conf_t *pmcf)
 {
+    ngx_str_t               *m;
+    ngx_uint_t               i;
 #if (NGX_HAVE_PERL_MULTIPLICITY)
-    ngx_pool_cleanup_t       *cln;
+    ngx_pool_cleanup_t      *cln;
 
     cln = ngx_pool_cleanup_add(cf->pool, 0);
     if (cln == NULL) {
@@ -476,14 +476,29 @@ ngx_http_perl_init_interpreter(ngx_conf_
 #endif
 
 #ifdef NGX_PERL_MODULES
-    if (pmcf->modules.data == NULL) {
-        pmcf->modules.data = NGX_PERL_MODULES;
+    if (pmcf->modules == NGX_CONF_UNSET_PTR) {
+
+        pmcf->modules = ngx_array_create(cf->pool, 1, sizeof(ngx_str_t));
+        if (pmcf->modules == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        m = ngx_array_push(pmcf->modules);
+        if (m == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        m->len = sizeof(NGX_PERL_MODULES) - 1;
+        m->data = NGX_PERL_MODULES;
     }
 #endif
 
-    if (pmcf->modules.data) {
-        if (ngx_conf_full_name(cf->cycle, &pmcf->modules, 0) != NGX_OK) {
-            return NGX_CONF_ERROR;
+    if (pmcf->modules != NGX_CONF_UNSET_PTR) {
+        m = pmcf->modules->elts;
+        for (i = 0; i < pmcf->modules->nelts; i++) {
+            if (ngx_conf_full_name(cf->cycle, &m[i], 0) != NGX_OK) {
+                return NGX_CONF_ERROR;
+            }
         }
     }
 
@@ -495,7 +510,7 @@ ngx_http_perl_init_interpreter(ngx_conf_
             return NGX_CONF_ERROR;
         }
 
-        if (ngx_http_perl_run_requires(aTHX_ &pmcf->requires, cf->log)
+        if (ngx_http_perl_run_requires(aTHX_ pmcf->requires, cf->log)
             != NGX_OK)
         {
             return NGX_CONF_ERROR;
@@ -543,7 +558,9 @@ ngx_http_perl_create_interpreter(ngx_con
     int                n;
     STRLEN             len;
     SV                *sv;
-    char              *ver, *embedding[6];
+    char              *ver, **embedding;
+    ngx_str_t         *m;
+    ngx_uint_t         i;
     PerlInterpreter   *perl;
 
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0, "create perl interpreter");
@@ -569,15 +586,21 @@ ngx_http_perl_create_interpreter(ngx_con
     PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
 #endif
 
+    n = (pmcf->modules != NGX_CONF_UNSET_PTR) ? pmcf->modules->nelts * 2 : 0;
+
+    embedding = ngx_palloc(cf->pool, (4 + n) * sizeof(char *));
+    if (embedding == NULL) {
+        goto fail;
+    }
+
     embedding[0] = "";
 
-    if (pmcf->modules.data) {
-        embedding[1] = "-I";
-        embedding[2] = (char *) pmcf->modules.data;
-        n = 3;
-
-    } else {
-        n = 1;
+    if (n++) {
+        m = pmcf->modules->elts;
+        for (i = 0; i < pmcf->modules->nelts; i++) {
+            embedding[2 * i + 1] = "-I";
+            embedding[2 * i + 2] = (char *) m[i].data;
+        }
     }
 
     embedding[n++] = "-Mnginx";
@@ -601,7 +624,7 @@ ngx_http_perl_create_interpreter(ngx_con
         goto fail;
     }
 
-    if (ngx_http_perl_run_requires(aTHX_ &pmcf->requires, cf->log) != NGX_OK) {
+    if (ngx_http_perl_run_requires(aTHX_ pmcf->requires, cf->log) != NGX_OK) {
         goto fail;
     }
 
@@ -622,26 +645,28 @@ fail:
 static ngx_int_t
 ngx_http_perl_run_requires(pTHX_ ngx_array_t *requires, ngx_log_t *log)
 {
-    char       **script;
+    u_char      *err;
     STRLEN       len;
-    ngx_str_t    err;
+    ngx_str_t   *script;
     ngx_uint_t   i;
 
+    if (requires == NGX_CONF_UNSET_PTR) {
+        return NGX_OK;
+    }
+
     script = requires->elts;
     for (i = 0; i < requires->nelts; i++) {
 
-        require_pv(script[i]);
+        require_pv((char *) script[i].data);
 
         if (SvTRUE(ERRSV)) {
 
-            err.data = (u_char *) SvPV(ERRSV, len);
-            for (len--; err.data[len] == LF || err.data[len] == CR; len--) {
-                /* void */
-            }
-            err.len = len + 1;
+            err = (u_char *) SvPV(ERRSV, len);
+            while (--len && (err[len] == CR || err[len] == LF)) { /* void */ }
 
             ngx_log_error(NGX_LOG_EMERG, log, 0,
-                          "require_pv(\"%s\") failed: \"%V\"", script[i], &err);
+                          "require_pv(\"%s\") failed: \"%*s\"",
+                          script[i].data, len + 1, err);
 
             return NGX_ERROR;
         }
@@ -658,8 +683,8 @@ ngx_http_perl_call_handler(pTHX_ ngx_htt
     SV                *sv;
     int                n, status;
     char              *line;
+    u_char            *err;
     STRLEN             len, n_a;
-    ngx_str_t          err;
     ngx_uint_t         i;
     ngx_connection_t  *c;
 
@@ -720,14 +745,11 @@ ngx_http_perl_call_handler(pTHX_ ngx_htt
 
     if (SvTRUE(ERRSV)) {
 
-        err.data = (u_char *) SvPV(ERRSV, len);
-        for (len--; err.data[len] == LF || err.data[len] == CR; len--) {
-            /* void */
-        }
-        err.len = len + 1;
+        err = (u_char *) SvPV(ERRSV, len);
+        while (--len && (err[len] == CR || err[len] == LF)) { /* void */ }
 
         ngx_log_error(NGX_LOG_ERR, c->log, 0,
-                      "call_sv(\"%V\") failed: \"%V\"", handler, &err);
+                      "call_sv(\"%V\") failed: \"%*s\"", handler, len + 1, err);
 
         if (rv) {
             return NGX_ERROR;
@@ -786,11 +808,8 @@ ngx_http_perl_create_main_conf(ngx_conf_
         return NULL;
     }
 
-    if (ngx_array_init(&pmcf->requires, cf->pool, 1, sizeof(u_char *))
-        != NGX_OK)
-    {
-        return NULL;
-    }
+    pmcf->modules = NGX_CONF_UNSET_PTR;
+    pmcf->requires = NGX_CONF_UNSET_PTR;
 
     return pmcf;
 }
@@ -897,28 +916,6 @@ ngx_http_perl_merge_loc_conf(ngx_conf_t 
 
 
 static char *
-ngx_http_perl_require(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
-{
-    ngx_http_perl_main_conf_t *pmcf = conf;
-
-    u_char     **p;
-    ngx_str_t   *value;
-
-    value = cf->args->elts;
-
-    p = ngx_array_push(&pmcf->requires);
-
-    if (p == NULL) {
-        return NGX_CONF_ERROR;
-    }
-
-    *p = value[1].data;
-
-    return NGX_CONF_OK;
-}
-
-
-static char *
 ngx_http_perl(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
     ngx_http_perl_loc_conf_t *plcf = conf;
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -408,6 +408,13 @@ static ngx_command_t  ngx_http_core_comm
 
 #endif
 
+    { ngx_string("read_ahead"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_size_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_core_loc_conf_t, read_ahead),
+      NULL },
+
     { ngx_string("directio"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_http_core_directio,
@@ -2957,6 +2964,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t
 #if (NGX_HAVE_FILE_AIO)
     lcf->aio = NGX_CONF_UNSET;
 #endif
+    lcf->read_ahead = NGX_CONF_UNSET_SIZE;
     lcf->directio = NGX_CONF_UNSET;
     lcf->directio_alignment = NGX_CONF_UNSET;
     lcf->tcp_nopush = NGX_CONF_UNSET;
@@ -3158,6 +3166,7 @@ ngx_http_core_merge_loc_conf(ngx_conf_t 
 #if (NGX_HAVE_FILE_AIO)
     ngx_conf_merge_value(conf->aio, prev->aio, 0);
 #endif
+    ngx_conf_merge_size_value(conf->read_ahead, prev->read_ahead, 0);
     ngx_conf_merge_off_value(conf->directio, prev->directio,
                               NGX_MAX_OFF_T_VALUE);
     ngx_conf_merge_off_value(conf->directio_alignment, prev->directio_alignment,
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -332,6 +332,7 @@ struct ngx_http_core_loc_conf_s {
     size_t        limit_rate;              /* limit_rate */
     size_t        limit_rate_after;        /* limit_rate_after */
     size_t        sendfile_max_chunk;      /* sendfile_max_chunk */
+    size_t        read_ahead;              /* read_ahead */
 
     ngx_msec_t    client_body_timeout;     /* client_body_timeout */
     ngx_msec_t    send_timeout;            /* send_timeout */
--- a/src/http/ngx_http_file_cache.c
+++ b/src/http/ngx_http_file_cache.c
@@ -281,6 +281,7 @@ ngx_http_file_cache_open(ngx_http_reques
     of.min_uses = clcf->open_file_cache_min_uses;
     of.events = clcf->open_file_cache_events;
     of.directio = NGX_OPEN_FILE_DIRECTIO_OFF;
+    of.read_ahead = clcf->read_ahead;
 
     if (ngx_open_cached_file(clcf->open_file_cache, &c->file.name, &of, r->pool)
         != NGX_OK)
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -2048,6 +2048,10 @@ ngx_http_terminate_request(ngx_http_requ
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http terminate request count:%d", mr->count);
 
+    if (rc > 0 && (mr->headers_out.status == 0 || mr->connection->sent == 0)) {
+        mr->headers_out.status = rc;
+    }
+
     cln = mr->cleanup;
     mr->cleanup = NULL;
 
@@ -2407,6 +2411,8 @@ ngx_http_set_keepalive(ngx_http_request_
         }
     }
 
+    r->keepalive = 0;
+
     ngx_http_free_request(r, 0);
 
     c->data = hc;
--- a/src/http/ngx_http_request_body.c
+++ b/src/http/ngx_http_request_body.c
@@ -469,16 +469,18 @@ 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_OK) {
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
-    if (ngx_http_read_discarded_request_body(r) != NGX_OK) {
+    if (ngx_http_read_discarded_request_body(r) == NGX_OK) {
+        r->lingering_close = 0;
+
+    } else {
         r->count++;
+        r->discard_body = 1;
     }
 
     return NGX_OK;
@@ -509,6 +511,7 @@ ngx_http_read_discarded_request_body_han
 
         if (timer <= 0) {
             r->discard_body = 0;
+            r->lingering_close = 0;
             ngx_http_finalize_request(r, 0);
             return;
         }
@@ -522,6 +525,7 @@ ngx_http_read_discarded_request_body_han
     if (rc == NGX_OK) {
 
         r->discard_body = 0;
+        r->lingering_close = 0;
 
         if (r->done) {
             ngx_http_finalize_request(r, 0);
--- a/src/http/ngx_http_script.c
+++ b/src/http/ngx_http_script.c
@@ -1407,6 +1407,7 @@ ngx_http_script_file_code(ngx_http_scrip
 
     ngx_memzero(&of, sizeof(ngx_open_file_info_t));
 
+    of.read_ahead = clcf->read_ahead;
     of.directio = clcf->directio;
     of.valid = clcf->open_file_cache_valid;
     of.min_uses = clcf->open_file_cache_min_uses;
--- a/src/os/unix/ngx_files.h
+++ b/src/os/unix/ngx_files.h
@@ -254,6 +254,28 @@ ngx_err_t ngx_unlock_fd(ngx_fd_t fd);
 #define ngx_unlock_fd_n          "fcntl(F_SETLK, F_UNLCK)"
 
 
+#if (NGX_HAVE_F_READAHEAD)
+
+#define NGX_HAVE_READ_AHEAD      1
+
+#define ngx_read_ahead(fd, n)    fcntl(fd, F_READAHEAD, (int) n)
+#define ngx_read_ahead_n         "fcntl(fd, F_READAHEAD)"
+
+#elif (NGX_HAVE_POSIX_FADVISE)
+
+#define NGX_HAVE_READ_AHEAD      1
+
+#define ngx_read_ahead(fd, n)    posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL)
+#define ngx_read_ahead_n         "posix_fadvise(POSIX_FADV_SEQUENTIAL)"
+
+#else
+
+#define ngx_read_ahead(fd, n)    0
+#define ngx_read_ahead_n         "ngx_read_ahead_n"
+
+#endif
+
+
 #if (NGX_HAVE_O_DIRECT)
 
 ngx_int_t ngx_directio_on(ngx_fd_t fd);