changeset 537:0161f3197817 NGINX_0_8_15

nginx 0.8.15 *) Security: a segmentation fault might occur in worker process while specially crafted request handling. Thanks to Chris Ries. *) Bugfix: if names .domain.tld, .sub.domain.tld, and .domain-some.tld were defined, then the name .sub.domain.tld was matched by .domain.tld. *) Bugfix: in transparency support in the ngx_http_image_filter_module. *) Bugfix: in file AIO. *) Bugfix: in X-Accel-Redirect usage; the bug had appeared in 0.8.11. *) Bugfix: in embedded perl module; the bug had appeared in 0.8.11.
author Igor Sysoev <http://sysoev.ru>
date Mon, 14 Sep 2009 00:00:00 +0400
parents 1b64f9884263
children a607f3a5aefe
files CHANGES CHANGES.ru auto/os/features src/core/nginx.h src/core/ngx_buf.h src/core/ngx_hash.c src/core/ngx_output_chain.c src/core/ngx_string.c src/core/ngx_string.h src/http/modules/ngx_http_geo_module.c src/http/modules/ngx_http_image_filter_module.c src/http/modules/ngx_http_log_module.c src/http/modules/ngx_http_map_module.c src/http/modules/ngx_http_referer_module.c src/http/modules/ngx_http_xslt_filter_module.c src/http/modules/perl/nginx.pm src/http/modules/perl/ngx_http_perl_module.c src/http/ngx_http.c src/http/ngx_http_copy_filter_module.c src/http/ngx_http_core_module.c src/http/ngx_http_parse.c src/http/ngx_http_upstream.c
diffstat 22 files changed, 184 insertions(+), 60 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,23 @@
 
+Changes with nginx 0.8.15                                        14 Sep 2009
+
+    *) Security: a segmentation fault might occur in worker process while 
+       specially crafted request handling.
+       Thanks to Chris Ries.
+
+    *) Bugfix: if names .domain.tld, .sub.domain.tld, and .domain-some.tld 
+       were defined, then the name .sub.domain.tld was matched by 
+       .domain.tld.
+
+    *) Bugfix: in transparency support in the ngx_http_image_filter_module.
+
+    *) Bugfix: in file AIO.
+
+    *) Bugfix: in X-Accel-Redirect usage; the bug had appeared in 0.8.11.
+
+    *) Bugfix: in embedded perl module; the bug had appeared in 0.8.11.
+
+
 Changes with nginx 0.8.14                                        07 Sep 2009
 
     *) Bugfix: an expired cached response might stick in the "UPDATING" 
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,26 @@
 
+Изменения в nginx 0.8.15                                          14.09.2009
+
+    *) Безопасность: при обработке специально созданного запроса в рабочем 
+       процессе мог произойти segmentation fault.
+       Спасибо Chris Ries.
+
+    *) Исправление: если были описаны имена .domain.tld, .sub.domain.tld и 
+       .domain-some.tld, то имя .sub.domain.tld попадало под маску 
+       .domain.tld.
+
+    *) Исправление: в поддержке прозрачности в модуле 
+       ngx_http_image_filter_module.
+
+    *) Исправление: в файловом AIO.
+
+    *) Исправление: ошибки при использовании X-Accel-Redirect; ошибка 
+       появилась в 0.8.11.
+
+    *) Исправление: ошибки при использовании встроенного перла; ошибка 
+       появилась в 0.8.11.
+
+
 Изменения в nginx 0.8.14                                          07.09.2009
 
     *) Исправление: устаревший закэшированный запрос мог залипнуть в 
--- a/auto/os/features
+++ b/auto/os/features
@@ -277,6 +277,7 @@ fi
 
 
 if [ $NGX_FILE_AIO = YES ]; then
+
     ngx_feature="kqueue AIO support"
     ngx_feature_name="NGX_HAVE_FILE_AIO"
     ngx_feature_run=no
@@ -290,27 +291,35 @@ if [ $NGX_FILE_AIO = YES ]; then
 
     if [ $ngx_found = yes ]; then
         CORE_SRCS="$CORE_SRCS $FILE_AIO_SRCS"
+
+    elif [ $ngx_found = no ]; then
+
+        ngx_feature="Linux AIO support"
+        ngx_feature_name="NGX_HAVE_FILE_AIO"
+        ngx_feature_run=no
+        ngx_feature_incs="#include <linux/aio_abi.h>
+                          #include <sys/syscall.h>"
+        ngx_feature_path=
+        ngx_feature_libs=
+        ngx_feature_test="int  n = SYS_eventfd;
+                          struct iocb  iocb;
+                          iocb.aio_lio_opcode = IOCB_CMD_PREAD;
+                          iocb.aio_flags = IOCB_FLAG_RESFD;
+                          iocb.aio_resfd = -1;"
+        . auto/feature
+
+        if [ $ngx_found = yes ]; then
+            have=NGX_HAVE_EVENTFD . auto/have
+            CORE_SRCS="$CORE_SRCS $LINUX_AIO_SRCS"
+
+        else
+            cat << END
+
+$0: no supported file AIO was found
+Currently file AIO is supported on FreeBSD 4.3+ and Linux 2.6.22+ only
+
+END
+           exit 1
+        fi
     fi
 fi
-
-
-if [ $NGX_FILE_AIO = YES ]; then
-    ngx_feature="Linux AIO support"
-    ngx_feature_name="NGX_HAVE_FILE_AIO"
-    ngx_feature_run=no
-    ngx_feature_incs="#include <linux/aio_abi.h>
-                      #include <sys/syscall.h>"
-    ngx_feature_path=
-    ngx_feature_libs=
-    ngx_feature_test="int  n = SYS_eventfd;
-                      struct iocb  iocb;
-                      iocb.aio_lio_opcode = IOCB_CMD_PREAD;
-                      iocb.aio_flags = IOCB_FLAG_RESFD;
-                      iocb.aio_resfd = -1;"
-    . auto/feature
-
-    if [ $ngx_found = yes ]; then
-        have=NGX_HAVE_EVENTFD . auto/have
-        CORE_SRCS="$CORE_SRCS $LINUX_AIO_SRCS"
-    fi
-fi
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,8 +8,8 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define nginx_version         8014
-#define NGINX_VERSION      "0.8.14"
+#define nginx_version         8015
+#define NGINX_VERSION      "0.8.15"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_buf.h
+++ b/src/core/ngx_buf.h
@@ -89,6 +89,11 @@ struct ngx_output_chain_ctx_s {
 #endif
     unsigned                     need_in_memory:1;
     unsigned                     need_in_temp:1;
+#if (NGX_HAVE_FILE_AIO)
+    unsigned                     aio:1;
+
+    ngx_output_chain_aio_pt      aio_handler;
+#endif
 
     off_t                        alignment;
 
@@ -99,10 +104,6 @@ struct ngx_output_chain_ctx_s {
 
     ngx_output_chain_filter_pt   output_filter;
     void                        *filter_ctx;
-
-#if (NGX_HAVE_FILE_AIO)
-    ngx_output_chain_aio_pt      aio;
-#endif
 };
 
 
--- a/src/core/ngx_hash.c
+++ b/src/core/ngx_hash.c
@@ -534,7 +534,7 @@ ngx_hash_wildcard_init(ngx_hash_init_t *
 
             next_name->key.len = names[n].key.len - len;
             next_name->key.data = names[n].key.data + len;
-            next_name->key_hash= 0;
+            next_name->key_hash = 0;
             next_name->value = names[n].value;
 
 #if 0
@@ -562,7 +562,7 @@ ngx_hash_wildcard_init(ngx_hash_init_t *
 
             next_name->key.len = names[i].key.len - dot_len;
             next_name->key.data = names[i].key.data + dot_len;
-            next_name->key_hash= 0;
+            next_name->key_hash = 0;
             next_name->value = names[i].value;
 
 #if 0
--- a/src/core/ngx_output_chain.c
+++ b/src/core/ngx_output_chain.c
@@ -74,6 +74,12 @@ 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;
@@ -519,11 +525,11 @@ ngx_output_chain_copy_buf(ngx_output_cha
 
 #if (NGX_HAVE_FILE_AIO)
 
-        if (ctx->aio) {
+        if (ctx->aio_handler) {
             n = ngx_file_aio_read(src->file, dst->pos, (size_t) size,
                                   src->file_pos, ctx->pool);
             if (n == NGX_AGAIN) {
-                ctx->aio(ctx, src->file);
+                ctx->aio_handler(ctx, src->file);
                 return NGX_AGAIN;
             }
 
--- a/src/core/ngx_string.c
+++ b/src/core/ngx_string.c
@@ -568,8 +568,8 @@ ngx_strcasecmp(u_char *s1, u_char *s2)
         c1 = (ngx_uint_t) *s1++;
         c2 = (ngx_uint_t) *s2++;
 
-        c1  = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1;
-        c2  = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2;
+        c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1;
+        c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2;
 
         if (c1 == c2) {
 
@@ -594,8 +594,8 @@ ngx_strncasecmp(u_char *s1, u_char *s2, 
         c1 = (ngx_uint_t) *s1++;
         c2 = (ngx_uint_t) *s2++;
 
-        c1  = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1;
-        c2  = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2;
+        c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1;
+        c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2;
 
         if (c1 == c2) {
 
@@ -683,7 +683,7 @@ ngx_strcasestrn(u_char *s1, char *s2, si
     ngx_uint_t  c1, c2;
 
     c2 = (ngx_uint_t) *s2++;
-    c2  = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2;
+    c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2;
 
     do {
         do {
@@ -693,7 +693,7 @@ ngx_strcasestrn(u_char *s1, char *s2, si
                 return NULL;
             }
 
-            c1  = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1;
+            c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1;
 
         } while (c1 != c2);
 
@@ -715,7 +715,7 @@ ngx_strlcasestrn(u_char *s1, u_char *las
     ngx_uint_t  c1, c2;
 
     c2 = (ngx_uint_t) *s2++;
-    c2  = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2;
+    c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2;
     last -= n;
 
     do {
@@ -726,7 +726,7 @@ ngx_strlcasestrn(u_char *s1, u_char *las
 
             c1 = (ngx_uint_t) *s1++;
 
-            c1  = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1;
+            c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1;
 
         } while (c1 != c2);
 
@@ -820,6 +820,37 @@ ngx_memn2cmp(u_char *s1, u_char *s2, siz
 
 
 ngx_int_t
+ngx_dns_strcmp(u_char *s1, u_char *s2)
+{
+    ngx_uint_t  c1, c2;
+
+    for ( ;; ) {
+        c1 = (ngx_uint_t) *s1++;
+        c2 = (ngx_uint_t) *s2++;
+
+        c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1;
+        c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2;
+
+        if (c1 == c2) {
+
+            if (c1) {
+                continue;
+            }
+
+            return 0;
+        }
+
+        /* in ASCII '.' > '-', but we need '.' to be the lowest character */
+
+        c1 = (c1 == '.') ? ' ' : c1;
+        c2 = (c2 == '.') ? ' ' : c2;
+
+        return c1 - c2;
+    }
+}
+
+
+ngx_int_t
 ngx_atoi(u_char *line, size_t n)
 {
     ngx_int_t  value;
@@ -1340,7 +1371,7 @@ ngx_escape_uri(u_char *dst, u_char *src,
 
         /* find the number of the characters to be escaped */
 
-        n  = 0;
+        n = 0;
 
         for (i = 0; i < size; i++) {
             if (escape[*src >> 5] & (1 << (*src & 0x1f))) {
--- a/src/core/ngx_string.h
+++ b/src/core/ngx_string.h
@@ -158,6 +158,7 @@ u_char *ngx_strlcasestrn(u_char *s1, u_c
 ngx_int_t ngx_rstrncmp(u_char *s1, u_char *s2, size_t n);
 ngx_int_t ngx_rstrncasecmp(u_char *s1, u_char *s2, size_t n);
 ngx_int_t ngx_memn2cmp(u_char *s1, u_char *s2, size_t n1, size_t n2);
+ngx_int_t ngx_dns_strcmp(u_char *s1, u_char *s2);
 
 ngx_int_t ngx_atoi(u_char *line, size_t n);
 ssize_t ngx_atosz(u_char *line, size_t n);
--- a/src/http/modules/ngx_http_geo_module.c
+++ b/src/http/modules/ngx_http_geo_module.c
@@ -906,7 +906,7 @@ ngx_http_geo_cidr(ngx_conf_t *cf, ngx_ht
 
         /* rc == NGX_BUSY */
 
-        old  = (ngx_http_variable_value_t *)
+        old = (ngx_http_variable_value_t *)
               ngx_radix32tree_find(ctx->tree, cidr.u.in.addr & cidr.u.in.mask);
 
         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
--- a/src/http/modules/ngx_http_image_filter_module.c
+++ b/src/http/modules/ngx_http_image_filter_module.c
@@ -679,7 +679,7 @@ static ngx_buf_t *
 ngx_http_image_resize(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
 {
     int                            sx, sy, dx, dy, ox, oy,
-                                   colors, transparent, size;
+                                   colors, transparent, red, green, blue, size;
     u_char                        *out;
     ngx_buf_t                     *b;
     ngx_uint_t                     resize;
@@ -708,6 +708,16 @@ ngx_http_image_resize(ngx_http_request_t
     colors = gdImageColorsTotal(src);
     transparent = gdImageGetTransparent(src);
 
+    if (transparent != -1 && colors) {
+        red = gdImageRed(src, transparent);
+        green = gdImageGreen(src, transparent);
+        blue = gdImageBlue(src, transparent);
+        gdImageColorTransparent(src, -1);
+
+    } else {
+        red = 0; green = 0; blue = 0;
+    }
+
     dx = sx;
     dy = sy;
 
@@ -806,7 +816,9 @@ ngx_http_image_resize(ngx_http_request_t
         }
     }
 
-    gdImageColorTransparent(dst, transparent);
+    if (transparent != -1 && colors) {
+        gdImageColorTransparent(dst, gdImageColorExact(dst, red, green, blue));
+    }
 
     out = ngx_http_image_out(r, ctx->type, dst, &size);
 
--- a/src/http/modules/ngx_http_log_module.c
+++ b/src/http/modules/ngx_http_log_module.c
@@ -676,7 +676,7 @@ ngx_http_log_escape(u_char *dst, u_char 
 
         /* find the number of the characters to be escaped */
 
-        n  = 0;
+        n = 0;
 
         for (i = 0; i < size; i++) {
             if (escape[*src >> 5] & (1 << (*src & 0x1f))) {
--- a/src/http/modules/ngx_http_map_module.c
+++ b/src/http/modules/ngx_http_map_module.c
@@ -337,7 +337,7 @@ ngx_http_map_cmp_dns_wildcards(const voi
     first = (ngx_hash_key_t *) one;
     second = (ngx_hash_key_t *) two;
 
-    return ngx_strcmp(first->key.data, second->key.data);
+    return ngx_dns_strcmp(first->key.data, second->key.data);
 }
 
 
--- a/src/http/modules/ngx_http_referer_module.c
+++ b/src/http/modules/ngx_http_referer_module.c
@@ -506,6 +506,11 @@ ngx_http_add_regex_referer(ngx_conf_t *c
     ngx_regex_elt_t  *re;
     u_char            errstr[NGX_MAX_CONF_ERRSTR];
 
+    if (name->len == 1) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty regex in \"%V\"", name);
+        return NGX_CONF_ERROR;
+    }
+
     if (rlcf->regex == NGX_CONF_UNSET_PTR) {
         rlcf->regex = ngx_array_create(cf->pool, 2, sizeof(ngx_regex_elt_t));
         if (rlcf->regex == NULL) {
@@ -562,5 +567,5 @@ ngx_http_cmp_referer_wildcards(const voi
     first = (ngx_hash_key_t *) one;
     second = (ngx_hash_key_t *) two;
 
-    return ngx_strcmp(first->key.data, second->key.data);
+    return ngx_dns_strcmp(first->key.data, second->key.data);
 }
--- a/src/http/modules/ngx_http_xslt_filter_module.c
+++ b/src/http/modules/ngx_http_xslt_filter_module.c
@@ -404,7 +404,7 @@ ngx_http_xslt_add_chunk(ngx_http_request
         sax->endElementNs = ngx_http_xslt_sax_end_element;
 
         sax->characters = ngx_http_xslt_sax_characters;
-        sax->ignorableWhitespace  = ngx_http_xslt_sax_characters;
+        sax->ignorableWhitespace = ngx_http_xslt_sax_characters;
         sax->cdataBlock = ngx_http_xslt_sax_cdata_block;
         sax->getEntity = ngx_http_xslt_sax_get_entity;
         sax->resolveEntity = ngx_http_xslt_sax_resolve_entity;
--- 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.14';
+our $VERSION = '0.8.15';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/modules/perl/ngx_http_perl_module.c
+++ b/src/http/modules/perl/ngx_http_perl_module.c
@@ -238,6 +238,7 @@ ngx_http_perl_handle_request(ngx_http_re
                    "perl handler done: %i", rc);
 
     if (rc == NGX_DONE) {
+        ngx_http_finalize_request(r, rc);
         return;
     }
 
@@ -257,11 +258,13 @@ ngx_http_perl_handle_request(ngx_http_re
     ctx->redirect_uri.len = 0;
 
     if (ctx->done || ctx->next) {
+        ngx_http_finalize_request(r, NGX_DONE);
         return;
     }
 
     if (uri.len) {
         ngx_http_internal_redirect(r, &uri, &args);
+        ngx_http_finalize_request(r, NGX_DONE);
         return;
     }
 
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -1601,7 +1601,7 @@ ngx_http_cmp_dns_wildcards(const void *o
     first = (ngx_hash_key_t *) one;
     second = (ngx_hash_key_t *) two;
 
-    return ngx_strcmp(first->key.data, second->key.data);
+    return ngx_dns_strcmp(first->key.data, second->key.data);
 }
 
 
--- a/src/http/ngx_http_copy_filter_module.c
+++ b/src/http/ngx_http_copy_filter_module.c
@@ -87,10 +87,6 @@ ngx_http_copy_filter(ngx_http_request_t 
 
     c = r->connection;
 
-    if (r->aio) {
-        return NGX_AGAIN;
-    }
-
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
                    "http copy filter: \"%V?%V\"", &r->uri, &r->args);
 
@@ -123,7 +119,7 @@ ngx_http_copy_filter(ngx_http_request_t 
 
 #if (NGX_HAVE_FILE_AIO)
         if (clcf->aio) {
-            ctx->aio = ngx_http_copy_aio_handler;
+            ctx->aio_handler = ngx_http_copy_aio_handler;
 #if (NGX_HAVE_AIO_SENDFILE)
             c->aio_sendfile = (clcf->aio == NGX_HTTP_AIO_SENDFILE);
 #endif
@@ -133,6 +129,10 @@ ngx_http_copy_filter(ngx_http_request_t 
         r->request_output = 1;
     }
 
+#if (NGX_HAVE_FILE_AIO)
+    ctx->aio = r->aio;
+#endif
+
     for ( ;; ) {
         rc = ngx_output_chain(ctx, in);
 
@@ -174,6 +174,7 @@ ngx_http_copy_filter(ngx_http_request_t 
             n = ngx_file_aio_read(file, &e->aio_preload, 1, offset, r->pool);
 
             if (n > 0) {
+                in = NULL;
                 continue;
             }
 
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -994,6 +994,7 @@ ngx_http_core_post_rewrite_phase(ngx_htt
                       "rewrite or internal redirection cycle "
                       "while processing \"%V\"", &r->uri);
 
+        r->main->count++;
         ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
         return NGX_OK;
     }
@@ -2172,6 +2173,7 @@ ngx_http_internal_redirect(ngx_http_requ
                       "rewrite or internal redirection cycle "
                       "while internal redirect to \"%V\"", uri);
 
+        r->main->count++;
         ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
         return NGX_DONE;
     }
@@ -3549,6 +3551,12 @@ ngx_http_core_server_name(ngx_conf_t *cf
         ngx_str_t  err;
         u_char     errstr[NGX_MAX_CONF_ERRSTR];
 
+        if (value[i].len == 1) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "empty regex in server name \"%V\"", &value[i]);
+            return NGX_CONF_ERROR;
+        }
+
         err.len = NGX_MAX_CONF_ERRSTR;
         err.data = errstr;
 
--- a/src/http/ngx_http_parse.c
+++ b/src/http/ngx_http_parse.c
@@ -1134,11 +1134,15 @@ ngx_http_parse_complex_uri(ngx_http_requ
 #endif
             case '/':
                 state = sw_slash;
-                u -= 4;
-                if (u < r->uri.data) {
-                    return NGX_HTTP_PARSE_INVALID_REQUEST;
-                }
-                while (*(u - 1) != '/') {
+                u -= 5;
+                for ( ;; ) {
+                    if (u < r->uri.data) {
+                        return NGX_HTTP_PARSE_INVALID_REQUEST;
+                    }
+                    if (*u == '/') {
+                        u++;
+                        break;
+                    }
                     u--;
                 }
                 break;
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -363,6 +363,7 @@ ngx_http_upstream_create(ngx_http_reques
     u = r->upstream;
 
     if (u && u->cleanup) {
+        r->main->count++;
         ngx_http_upstream_cleanup(r);
         *u->cleanup = NULL;
     }
@@ -1814,6 +1815,7 @@ ngx_http_upstream_process_headers(ngx_ht
         r->valid_unparsed_uri = 0;
 
         ngx_http_internal_redirect(r, uri, &args);
+        ngx_http_finalize_request(r, NGX_DONE);
         return NGX_DONE;
     }