changeset 615:c5122335e41d NGINX_0_8_53

nginx 0.8.53 *) Feature: now the "error_page" directive allows to change a status code in a redirect. *) Feature: the "gzip_disable" directive supports special "degradation" mask. *) Bugfix: a socket leak might occurred if file AIO was used. Thanks to Maxim Dounin. *) Bugfix: if the first server had no "listen" directive and there was no explicit default server, then a next server with a "listen" directive became the default server; the bug had appeared in 0.8.21.
author Igor Sysoev <http://sysoev.ru>
date Mon, 18 Oct 2010 00:00:00 +0400
parents b2afd36d87f4
children 94ea26a3b3aa
files CHANGES CHANGES.ru auto/modules src/core/nginx.h src/core/ngx_output_chain.c src/http/modules/ngx_http_degradation_module.c src/http/modules/ngx_http_gzip_filter_module.c src/http/modules/ngx_http_limit_req_module.c src/http/modules/perl/nginx.pm src/http/ngx_http.h src/http/ngx_http_copy_filter_module.c src/http/ngx_http_core_module.c src/http/ngx_http_core_module.h src/http/ngx_http_special_response.c src/os/unix/ngx_linux_aio_read.c
diffstat 15 files changed, 256 insertions(+), 153 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,20 @@
 
+Changes with nginx 0.8.53                                        18 Oct 2010
+
+    *) Feature: now the "error_page" directive allows to change a status 
+       code in a redirect.
+
+    *) Feature: the "gzip_disable" directive supports special "degradation" 
+       mask.
+
+    *) Bugfix: a socket leak might occurred if file AIO was used.
+       Thanks to Maxim Dounin.
+
+    *) Bugfix: if the first server had no "listen" directive and there was 
+       no explicit default server, then a next server with a "listen" 
+       directive became the default server; the bug had appeared in 0.8.21.
+
+
 Changes with nginx 0.8.52                                        28 Sep 2010
 
     *) Bugfix: nginx used SSL mode for a listen socket if any listen option 
@@ -131,7 +147,7 @@ Changes with nginx 0.8.44               
 
     *) Bugfix: compatibility with HP/UX.
 
-    *) Bugfix: compatibility with AIX xcl_r compiler.
+    *) Bugfix: compatibility with AIX xlC_r compiler.
 
     *) Bugfix: nginx treated large SSLv2 packets as plain requests.
        Thanks to Miroslaw Jaworski.
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,22 @@
 
+Изменения в nginx 0.8.53                                          18.10.2010
+
+    *) Добавление: теперь директива error_page позволяет менять код статуса 
+       у редиректа.
+
+    *) Добавление: директива gzip_disable поддерживает специальную маску 
+       degradation.
+
+    *) Исправление: при использовании файлового AIO, могла происходить 
+       утечка сокетов.
+       Спасибо Максиму Дунину.
+
+    *) Исправление: если в первом сервере не была описана директива listen 
+       и нигде явно не описан сервер по умолчанию, то сервером по умолчанию 
+       становился следующий сервер с директивой listen; ошибка появилась в 
+       0.8.21.
+
+
 Изменения в nginx 0.8.52                                          28.09.2010
 
     *) Исправление: nginx использовал режим SSL для listen сокета, если для 
@@ -129,7 +147,7 @@
 
     *) Исправление: совместимость с HP/UX.
 
-    *) Исправление: совместимость с компилятором AIX xcl_r.
+    *) Исправление: совместимость с компилятором AIX xlC_r.
 
     *) Исправление: nginx считал большие пакеты SSLv2 как обычные текстовые 
        запросы.
--- a/auto/modules
+++ b/auto/modules
@@ -322,6 +322,7 @@ if [ $HTTP_SECURE_LINK = YES ]; then
 fi
 
 if [ $HTTP_DEGRADATION = YES ]; then
+    have=NGX_HTTP_DEGRADATION . auto/have
     HTTP_MODULES="$HTTP_MODULES $HTTP_DEGRADATION_MODULE"
     HTTP_SRCS="$HTTP_SRCS $HTTP_DEGRADATION_SRCS"
 fi
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,8 +8,8 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define nginx_version         8052
-#define NGINX_VERSION      "0.8.52"
+#define nginx_version         8053
+#define NGINX_VERSION      "0.8.53"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_output_chain.c
+++ b/src/core/ngx_output_chain.c
@@ -74,18 +74,18 @@ ngx_output_chain(ngx_output_chain_ctx_t 
         }
     }
 
-#if (NGX_HAVE_FILE_AIO)
-    if (ctx->aio) {
-        return NGX_AGAIN;
-    }
-#endif
-
     out = NULL;
     last_out = &out;
     last = NGX_NONE;
 
     for ( ;; ) {
 
+#if (NGX_HAVE_FILE_AIO)
+        if (ctx->aio) {
+            return NGX_AGAIN;
+        }
+#endif
+
         while (ctx->in) {
 
             /*
--- a/src/http/modules/ngx_http_degradation_module.c
+++ b/src/http/modules/ngx_http_degradation_module.c
@@ -86,25 +86,38 @@ ngx_module_t  ngx_http_degradation_modul
 };
 
 
+static ngx_uint_t  ngx_degraded;
+
+
 static ngx_int_t
 ngx_http_degradation_handler(ngx_http_request_t *r)
 {
-    time_t                             now;
-    static size_t                      sbrk_size;
-    static time_t                      sbrk_time;
-    ngx_http_degradation_loc_conf_t   *dlcf;
-    ngx_http_degradation_main_conf_t  *dmcf;
+    ngx_http_degradation_loc_conf_t  *dlcf;
 
     dlcf = ngx_http_get_module_loc_conf(r, ngx_http_degradation_module);
 
-    if (dlcf->degrade == 0) {
-        return NGX_DECLINED;
+    if (dlcf->degrade && ngx_http_degraded(r)) {
+        return dlcf->degrade;
     }
 
+    return NGX_DECLINED;
+}
+
+
+ngx_uint_t
+ngx_http_degraded(ngx_http_request_t *r)
+{
+    time_t                             now;
+    ngx_uint_t                         log;
+    static size_t                      sbrk_size;
+    static time_t                      sbrk_time;
+    ngx_http_degradation_main_conf_t  *dmcf;
+
     dmcf = ngx_http_get_module_main_conf(r, ngx_http_degradation_module);
 
     if (dmcf->sbrk_size) {
 
+        log = 0;
         now = ngx_time();
 
         /* lock mutex */
@@ -120,19 +133,27 @@ ngx_http_degradation_handler(ngx_http_re
 
             sbrk_size = (size_t) sbrk(0) - ((uintptr_t) ngx_palloc & ~0x3FFFFF);
             sbrk_time = now;
+            log = 1;
         }
 
         /* unlock mutex */
 
         if (sbrk_size >= dmcf->sbrk_size) {
-            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                           "degradation sbrk: %uz", sbrk_size);
+            ngx_degraded = 1;
 
-            return dlcf->degrade;
+            if (log) {
+                ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
+                              "degradation sbrk:%uzM",
+                              sbrk_size / (1024 * 1024));
+            }
+
+            return 1;
         }
     }
 
-    return NGX_DECLINED;
+    ngx_degraded = 0;
+
+    return 0;
 }
 
 
--- a/src/http/modules/ngx_http_gzip_filter_module.c
+++ b/src/http/modules/ngx_http_gzip_filter_module.c
@@ -258,6 +258,18 @@ ngx_http_gzip_header_filter(ngx_http_req
 
     r->gzip_vary = 1;
 
+#if (NGX_HTTP_DEGRADATION)
+    {
+    ngx_http_core_loc_conf_t  *clcf;
+
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+    if (clcf->gzip_disable_degradation && ngx_http_degraded(r)) {
+        return ngx_http_next_header_filter(r);
+    }
+    }
+#endif
+
     if (!r->gzip_tested) {
         if (ngx_http_gzip_ok(r) != NGX_OK) {
             return ngx_http_next_header_filter(r);
--- a/src/http/modules/ngx_http_limit_req_module.c
+++ b/src/http/modules/ngx_http_limit_req_module.c
@@ -51,7 +51,7 @@ typedef struct {
 
 static void ngx_http_limit_req_delay(ngx_http_request_t *r);
 static ngx_int_t ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lrcf,
-    ngx_uint_t hash, u_char *data, size_t len, ngx_http_limit_req_node_t **lrp);
+    ngx_uint_t hash, u_char *data, size_t len, ngx_uint_t *ep);
 static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx,
     ngx_uint_t n);
 
@@ -186,25 +186,56 @@ ngx_http_limit_req_handler(ngx_http_requ
 
     ngx_http_limit_req_expire(ctx, 1);
 
-    rc = ngx_http_limit_req_lookup(lrcf, hash, vv->data, len, &lr);
-
-    if (lr) {
-        ngx_queue_remove(&lr->queue);
-
-        ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
-
-        excess = lr->excess;
-
-    } else {
-        excess = 0;
-    }
+    rc = ngx_http_limit_req_lookup(lrcf, hash, vv->data, len, &excess);
 
     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "limit_req: %i %ui.%03ui", rc, excess / 1000, excess % 1000);
 
-    if (rc == NGX_BUSY) {
+    if (rc == NGX_DECLINED) {
+
+        n = offsetof(ngx_rbtree_node_t, color)
+            + offsetof(ngx_http_limit_req_node_t, data)
+            + len;
+
+        node = ngx_slab_alloc_locked(ctx->shpool, n);
+        if (node == NULL) {
+
+            ngx_http_limit_req_expire(ctx, 0);
+
+            node = ngx_slab_alloc_locked(ctx->shpool, n);
+            if (node == NULL) {
+                ngx_shmtx_unlock(&ctx->shpool->mutex);
+                return NGX_HTTP_SERVICE_UNAVAILABLE;
+            }
+        }
+
+        lr = (ngx_http_limit_req_node_t *) &node->color;
+
+        node->key = hash;
+        lr->len = (u_char) len;
+
+        tp = ngx_timeofday();
+        lr->last = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
+
+        lr->excess = 0;
+        ngx_memcpy(lr->data, vv->data, len);
+
+        ngx_rbtree_insert(&ctx->sh->rbtree, node);
+
+        ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
+
         ngx_shmtx_unlock(&ctx->shpool->mutex);
 
+        return NGX_DECLINED;
+    }
+
+    ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+    if (rc == NGX_OK) {
+        return NGX_DECLINED;
+    }
+
+    if (rc == NGX_BUSY) {
         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);
@@ -212,71 +243,26 @@ ngx_http_limit_req_handler(ngx_http_requ
         return NGX_HTTP_SERVICE_UNAVAILABLE;
     }
 
-    if (rc == NGX_AGAIN) {
-        ngx_shmtx_unlock(&ctx->shpool->mutex);
-
-        if (lrcf->nodelay) {
-            return NGX_DECLINED;
-        }
-
-        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);
+    /* rc == NGX_AGAIN */
 
-        if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
-            return NGX_HTTP_INTERNAL_SERVER_ERROR;
-        }
-
-        r->read_event_handler = ngx_http_test_reading;
-        r->write_event_handler = ngx_http_limit_req_delay;
-        ngx_add_timer(r->connection->write,
-                      (ngx_msec_t) excess * 1000 / ctx->rate);
-
-        return NGX_AGAIN;
-    }
-
-    if (rc == NGX_OK) {
-        goto done;
+    if (lrcf->nodelay) {
+        return NGX_DECLINED;
     }
 
-    /* rc == NGX_DECLINED */
-
-    n = offsetof(ngx_rbtree_node_t, color)
-        + offsetof(ngx_http_limit_req_node_t, data)
-        + len;
+    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);
 
-    node = ngx_slab_alloc_locked(ctx->shpool, n);
-    if (node == NULL) {
-
-        ngx_http_limit_req_expire(ctx, 0);
-
-        node = ngx_slab_alloc_locked(ctx->shpool, n);
-        if (node == NULL) {
-            ngx_shmtx_unlock(&ctx->shpool->mutex);
-            return NGX_HTTP_SERVICE_UNAVAILABLE;
-        }
+    if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
-    lr = (ngx_http_limit_req_node_t *) &node->color;
-
-    node->key = hash;
-    lr->len = (u_char) len;
-
-    tp = ngx_timeofday();
-    lr->last = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
+    r->read_event_handler = ngx_http_test_reading;
+    r->write_event_handler = ngx_http_limit_req_delay;
+    ngx_add_timer(r->connection->write,
+                  (ngx_msec_t) excess * 1000 / ctx->rate);
 
-    lr->excess = 0;
-    ngx_memcpy(lr->data, vv->data, len);
-
-    ngx_rbtree_insert(&ctx->sh->rbtree, node);
-
-    ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
-
-done:
-
-    ngx_shmtx_unlock(&ctx->shpool->mutex);
-
-    return NGX_DECLINED;
+    return NGX_AGAIN;
 }
 
 
@@ -356,7 +342,7 @@ ngx_http_limit_req_rbtree_insert_value(n
 
 static ngx_int_t
 ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lrcf, ngx_uint_t hash,
-    u_char *data, size_t len, ngx_http_limit_req_node_t **lrp)
+    u_char *data, size_t len, ngx_uint_t *ep)
 {
     ngx_int_t                   rc, excess;
     ngx_time_t                 *tp;
@@ -391,6 +377,8 @@ ngx_http_limit_req_lookup(ngx_http_limit
             rc = ngx_memn2cmp(data, lr->data, len, (size_t) lr->len);
 
             if (rc == 0) {
+                ngx_queue_remove(&lr->queue);
+                ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
 
                 tp = ngx_timeofday();
 
@@ -403,16 +391,15 @@ ngx_http_limit_req_lookup(ngx_http_limit
                     excess = 0;
                 }
 
+                *ep = excess;
+
                 if ((ngx_uint_t) excess > lrcf->burst) {
-                    *lrp = lr;
                     return NGX_BUSY;
                 }
 
                 lr->excess = excess;
                 lr->last = now;
 
-                *lrp = lr;
-
                 if (excess) {
                     return NGX_AGAIN;
                 }
@@ -427,7 +414,7 @@ ngx_http_limit_req_lookup(ngx_http_limit
         break;
     }
 
-    *lrp = NULL;
+    *ep = 0;
 
     return NGX_DECLINED;
 }
--- a/src/http/modules/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -48,7 +48,7 @@ our @EXPORT = qw(
     HTTP_INSUFFICIENT_STORAGE
 );
 
-our $VERSION = '0.8.52';
+our $VERSION = '0.8.53';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/ngx_http.h
+++ b/src/http/ngx_http.h
@@ -142,6 +142,10 @@ char *ngx_http_merge_types(ngx_conf_t *c
 ngx_int_t ngx_http_set_default_types(ngx_conf_t *cf, ngx_array_t **types,
     ngx_str_t *default_type);
 
+#if (NGX_HTTP_DEGRADATION)
+ngx_uint_t  ngx_http_degraded(ngx_http_request_t *);
+#endif
+
 
 extern ngx_module_t  ngx_http_module;
 
--- a/src/http/ngx_http_copy_filter_module.c
+++ b/src/http/ngx_http_copy_filter_module.c
@@ -118,8 +118,10 @@ ngx_http_copy_filter(ngx_http_request_t 
         ctx->filter_ctx = r;
 
 #if (NGX_HAVE_FILE_AIO)
-        if (ngx_file_aio && clcf->aio) {
-            ctx->aio_handler = ngx_http_copy_aio_handler;
+        if (ngx_file_aio) {
+            if (clcf->aio) {
+                ctx->aio_handler = ngx_http_copy_aio_handler;
+            }
 #if (NGX_HAVE_AIO_SENDFILE)
             c->aio_sendfile = (clcf->aio == NGX_HTTP_AIO_SENDFILE);
 #endif
@@ -211,6 +213,7 @@ ngx_http_copy_aio_handler(ngx_output_cha
 
     r->main->blocked++;
     r->aio = 1;
+    ctx->aio = 1;
 }
 
 
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -2427,7 +2427,9 @@ ngx_http_core_server(ngx_conf_t *cf, ngx
     ngx_uint_t                   i;
     ngx_conf_t                   pcf;
     ngx_http_module_t           *module;
+    struct sockaddr_in          *sin;
     ngx_http_conf_ctx_t         *ctx, *http_ctx;
+    ngx_http_listen_opt_t        lsopt;
     ngx_http_core_srv_conf_t    *cscf, **cscfp;
     ngx_http_core_main_conf_t   *cmcf;
 
@@ -2506,6 +2508,37 @@ ngx_http_core_server(ngx_conf_t *cf, ngx
 
     *cf = pcf;
 
+    if (rv == NGX_CONF_OK && !cscf->listen) {
+        ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t));
+
+        sin = &lsopt.u.sockaddr_in;
+
+        sin->sin_family = AF_INET;
+#if (NGX_WIN32)
+        sin->sin_port = htons(80);
+#else
+        sin->sin_port = htons((getuid() == 0) ? 80 : 8000);
+#endif
+        sin->sin_addr.s_addr = INADDR_ANY;
+
+        lsopt.socklen = sizeof(struct sockaddr_in);
+
+        lsopt.backlog = NGX_LISTEN_BACKLOG;
+        lsopt.rcvbuf = -1;
+        lsopt.sndbuf = -1;
+#if (NGX_HAVE_SETFIB)
+        lsopt.setfib = -1;
+#endif
+        lsopt.wildcard = 1;
+
+        (void) ngx_sock_ntop(&lsopt.u.sockaddr, lsopt.addr,
+                             NGX_SOCKADDR_STRLEN, 1);
+
+        if (ngx_http_add_listen(cf, cscf, &lsopt) != NGX_OK) {
+            return NGX_CONF_ERROR;
+        }
+    }
+
     return rv;
 }
 
@@ -2946,8 +2979,6 @@ ngx_http_core_merge_srv_conf(ngx_conf_t 
     ngx_http_core_srv_conf_t *prev = parent;
     ngx_http_core_srv_conf_t *conf = child;
 
-    struct sockaddr_in      *sin;
-    ngx_http_listen_opt_t    lsopt;
     ngx_http_server_name_t  *sn;
 
     /* TODO: it does not merge, it inits only */
@@ -2979,37 +3010,6 @@ ngx_http_core_merge_srv_conf(ngx_conf_t 
     ngx_conf_merge_value(conf->underscores_in_headers,
                               prev->underscores_in_headers, 0);
 
-    if (!conf->listen) {
-        ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t));
-
-        sin = &lsopt.u.sockaddr_in;
-
-        sin->sin_family = AF_INET;
-#if (NGX_WIN32)
-        sin->sin_port = htons(80);
-#else
-        sin->sin_port = htons((getuid() == 0) ? 80 : 8000);
-#endif
-        sin->sin_addr.s_addr = INADDR_ANY;
-
-        lsopt.socklen = sizeof(struct sockaddr_in);
-
-        lsopt.backlog = NGX_LISTEN_BACKLOG;
-        lsopt.rcvbuf = -1;
-        lsopt.sndbuf = -1;
-#if (NGX_HAVE_SETFIB)
-        lsopt.setfib = -1;
-#endif
-        lsopt.wildcard = 1;
-
-        (void) ngx_sock_ntop(&lsopt.u.sockaddr, lsopt.addr,
-                             NGX_SOCKADDR_STRLEN, 1);
-
-        if (ngx_http_add_listen(cf, conf, &lsopt) == NGX_OK) {
-            return NGX_CONF_OK;
-        }
-    }
-
     if (conf->server_name.data == NULL) {
         ngx_str_set(&conf->server_name, "");
 
@@ -3113,6 +3113,9 @@ ngx_http_core_create_loc_conf(ngx_conf_t
     clcf->gzip_disable = NGX_CONF_UNSET_PTR;
 #endif
     clcf->gzip_disable_msie6 = 3;
+#if (NGX_HTTP_DEGRADATION)
+    clcf->gzip_disable_degradation = 3;
+#endif
 #endif
 
     return clcf;
@@ -3373,6 +3376,15 @@ ngx_http_core_merge_loc_conf(ngx_conf_t 
             (prev->gzip_disable_msie6 == 3) ? 0 : prev->gzip_disable_msie6;
     }
 
+#if (NGX_HTTP_DEGRADATION)
+
+    if (conf->gzip_disable_degradation == 3) {
+        conf->gzip_disable_degradation =
+            (prev->gzip_disable_degradation == 3) ?
+                 0 : prev->gzip_disable_degradation;
+    }
+
+#endif
 #endif
 
     return NGX_CONF_OK;
@@ -4057,19 +4069,15 @@ ngx_http_core_error_page(ngx_conf_t *cf,
             return NGX_CONF_ERROR;
         }
 
-        if (overwrite >= 0) {
-            err->overwrite = overwrite;
-
-        } else {
+        err->overwrite = overwrite;
+
+        if (overwrite == -1) {
             switch (err->status) {
                 case NGX_HTTP_TO_HTTPS:
                 case NGX_HTTPS_CERT_ERROR:
                 case NGX_HTTPS_NO_CERT:
                     err->overwrite = NGX_HTTP_BAD_REQUEST;
-                    break;
-
                 default:
-                    err->overwrite = err->status;
                     break;
             }
         }
@@ -4394,6 +4402,15 @@ ngx_http_gzip_disable(ngx_conf_t *cf, ng
             continue;
         }
 
+#if (NGX_HTTP_DEGRADATION)
+
+        if (ngx_strcmp(value[i].data, "degradation") == 0) {
+            clcf->gzip_disable_degradation = 1;
+            continue;
+        }
+
+#endif
+
         re = ngx_array_push(clcf->gzip_disable);
         if (re == NULL) {
             return NGX_CONF_ERROR;
@@ -4414,20 +4431,35 @@ ngx_http_gzip_disable(ngx_conf_t *cf, ng
     return NGX_CONF_OK;
 
 #else
-    ngx_str_t  *value;
+    ngx_str_t   *value;
+    ngx_uint_t   i;
 
     value = cf->args->elts;
 
-    if (cf->args->nelts == 2 && ngx_strcmp(value[1].data, "msie6") == 0) {
-        clcf->gzip_disable_msie6 = 1;
-        return NGX_CONF_OK;
+    for (i = 1; i < cf->args->nelts; i++) {
+        if (ngx_strcmp(value[i].data, "msie6") == 0) {
+            clcf->gzip_disable_msie6 = 1;
+            continue;
+        }
+
+#if (NGX_HTTP_DEGRADATION)
+
+        if (ngx_strcmp(value[i].data, "degradation") == 0) {
+            clcf->gzip_disable_degradation = 1;
+            continue;
+        }
+
+#endif
+
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "without PCRE library \"gzip_disable\" supports "
+                           "builtin \"msie6\" and \"degradation\" mask only");
+
+        return NGX_CONF_ERROR;
     }
 
-    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                       "without PCRE library \"gzip_disable\" supports "
-                       "builtin \"msie6\" mask only");
-
-    return NGX_CONF_ERROR;
+    return NGX_CONF_OK;
+
 #endif
 }
 
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -295,6 +295,9 @@ struct ngx_http_core_loc_conf_s {
     unsigned      auto_redirect:1;
 #if (NGX_HTTP_GZIP)
     unsigned      gzip_disable_msie6:2;
+#if (NGX_HTTP_DEGRADATION)
+    unsigned      gzip_disable_degradation:2;
+#endif
 #endif
 
     ngx_http_location_tree_node_t   *static_locations;
--- a/src/http/ngx_http_special_response.c
+++ b/src/http/ngx_http_special_response.c
@@ -523,7 +523,9 @@ ngx_http_send_error_page(ngx_http_reques
         r->expect_tested = 1;
     }
 
-    r->err_status = overwrite;
+    if (overwrite >= 0) {
+        r->err_status = overwrite;
+    }
 
     if (ngx_http_complex_value(r, &err_page->value, &uri) != NGX_OK) {
         return NGX_ERROR;
@@ -556,7 +558,7 @@ ngx_http_send_error_page(ngx_http_reques
         return NGX_ERROR;
     }
 
-    r->err_status = NGX_HTTP_MOVED_TEMPORARILY;
+    r->err_status = overwrite > 0 ? overwrite : NGX_HTTP_MOVED_TEMPORARILY;
 
     location->hash = 1;
     ngx_str_set(&location->key, "Location");
@@ -570,7 +572,7 @@ ngx_http_send_error_page(ngx_http_reques
         return ngx_http_send_refresh(r);
     }
 
-    return ngx_http_send_special_response(r, clcf, NGX_HTTP_MOVED_TEMPORARILY
+    return ngx_http_send_special_response(r, clcf, r->err_status
                                                    - NGX_HTTP_MOVED_PERMANENTLY
                                                    + NGX_HTTP_LEVEL_200);
 }
--- a/src/os/unix/ngx_linux_aio_read.c
+++ b/src/os/unix/ngx_linux_aio_read.c
@@ -95,6 +95,10 @@ ngx_file_aio_read(ngx_file_t *file, u_ch
     n = io_submit(ngx_aio_ctx, 1, piocb);
 
     if (n == 1) {
+        ev->active = 1;
+        ev->ready = 0;
+        ev->complete = 0;
+
         return NGX_AGAIN;
     }