changeset 500:ed3d382670c7 NGINX_0_7_62

nginx 0.7.62 *) Security: a segmentation fault might occur in worker process while specially crafted request handling. Thanks to Chris Ries. *) Feature: the $upstream_cache_status variable. *) Bugfix: an expired cached response might stick in the "UPDATING" state. *) Bugfix: a segmentation fault might occur in worker process, if error_log was set to info or debug level. Thanks to Sergey Bochenkov. *) Bugfix: in handling FastCGI headers split in records. *) Bugfix: XSLT filter may fail with message "not well formed XML document" for valid XML document. Thanks to Kuramoto Eiji. *) Bugfix: now in MacOSX, Cygwin, and nginx/Windows locations given by a regular expression are always tested in case insensitive mode. *) Bugfix: now nginx/Windows ignores trailing dots in URI. Thanks to Hugo Leisink. *) Bugfix: name of file specified in --conf-path was not honored during installation; the bug had appeared in 0.6.6. Thanks to Maxim Dounin. *) Bugfix: a 500 error code was returned for invalid login/password while HTTP Basic authentication on Windows.
author Igor Sysoev <http://sysoev.ru>
date Mon, 14 Sep 2009 00:00:00 +0400
parents f2c782e5161f
children dc87c92181c7
files CHANGES CHANGES.ru auto/install auto/os/linux src/core/nginx.h src/core/ngx_buf.h src/core/ngx_string.c src/http/modules/ngx_http_fastcgi_module.c src/http/modules/ngx_http_xslt_filter_module.c src/http/modules/perl/nginx.pm src/http/ngx_http_cache.h 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_parse.c src/http/ngx_http_request.c src/http/ngx_http_script.c src/http/ngx_http_upstream.c src/http/ngx_http_upstream.h src/os/unix/ngx_process.c src/os/unix/ngx_process_cycle.c src/os/unix/ngx_process_cycle.h
diffstat 22 files changed, 298 insertions(+), 190 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,8 +1,42 @@
 
+Changes with nginx 0.7.62                                        14 Sep 2009
+
+    *) Security: a segmentation fault might occur in worker process while 
+       specially crafted request handling.
+       Thanks to Chris Ries.
+
+    *) Feature: the $upstream_cache_status variable.
+
+    *) Bugfix: an expired cached response might stick in the "UPDATING" 
+       state.
+
+    *) Bugfix: a segmentation fault might occur in worker process, if 
+       error_log was set to info or debug level.
+       Thanks to Sergey Bochenkov.
+
+    *) Bugfix: in handling FastCGI headers split in records.
+
+    *) Bugfix: XSLT filter may fail with message "not well formed XML 
+       document" for valid XML document.
+       Thanks to Kuramoto Eiji.
+
+    *) Bugfix: now in MacOSX, Cygwin, and nginx/Windows locations given by 
+       a regular expression are always tested in case insensitive mode.
+
+    *) Bugfix: now nginx/Windows ignores trailing dots in URI.
+       Thanks to Hugo Leisink.
+
+    *) Bugfix: name of file specified in --conf-path was not honored during 
+       installation; the bug had appeared in 0.6.6.
+       Thanks to Maxim Dounin.
+
+    *) Bugfix: a 500 error code was returned for invalid login/password 
+       while HTTP Basic authentication on Windows.
+
+
 Changes with nginx 0.7.61                                        22 Jun 2009
 
-    *) Bugfix: nginx could not be built on MacOSX 10.6. the bug had 
-       appeared in 0.8.2.
+    *) Bugfix: nginx could not be built on MacOSX 10.6.
 
     *) Bugfix: nginx could not be built --without-http-cache; the bug had 
        appeared in 0.7.60.
@@ -23,7 +57,8 @@ Changes with nginx 0.7.60               
     *) Bugfix: in open_file_cache and proxy/fastcgi cache interaction on 
        start up.
 
-    *) Bugfix: open_file_cache might cache open file descriptors too long.
+    *) Bugfix: open_file_cache might cache open file descriptors too long; 
+       the bug had appeared in 0.7.4.
 
     *) Bugfix: the "If-Modified-Since", "If-Range", etc. client request 
        header lines were passed to backend while caching if no 
@@ -1828,8 +1863,8 @@ Changes with nginx 0.5.12               
        amd64, sparc, and ppc; the bug had appeared in 0.5.8.
 
     *) Bugfix: a segmentation fault might occur in worker process if the 
-       temporarily files were used while working with FastCGI server; the 
-       bug had appeared in 0.5.8.
+       temporary files were used while working with FastCGI server; the bug 
+       had appeared in 0.5.8.
 
     *) Bugfix: a segmentation fault might occur in worker process if the 
        $fastcgi_script_name variable was logged.
@@ -2732,8 +2767,8 @@ Changes with nginx 0.3.31               
        in 0.3.18.
 
     *) Bugfix: if the HTTPS protocol was used in the "proxy_pass" directive 
-       and the request body was in temporarily file then the request was 
-       not transferred.
+       and the request body was in temporary file then the request was not 
+       transferred.
 
     *) Bugfix: perl 5.8.8 compatibility.
 
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,41 @@
 
+Изменения в nginx 0.7.62                                          14.09.2009
+
+    *) Безопасность: при обработке специально созданного запроса в рабочем 
+       процессе мог произойти segmentation fault.
+       Спасибо Chris Ries.
+
+    *) Добавление: переменная $upstream_cache_status.
+
+    *) Исправление: устаревший закэшированный запрос мог залипнуть в 
+       состоянии "UPDATING".
+
+    *) Исправление: при использовании error_log на уровне info или debug в 
+       рабочем процессе мог произойти segmentation fault.
+       Спасибо Сергею Боченкову.
+
+    *) Исправление: в обработке заголовков ответа, разделённых в 
+       FastCGI-записях.
+
+    *) Исправление: XSLT-фильтр мог выдавать ошибку "not well formed XML 
+       document" для правильного документа.
+       Спасибо Kuramoto Eiji.
+
+    *) Исправление: в MacOSX, Cygwin и nginx/Windows при проверке 
+       location'ов, заданных регулярным выражением, теперь всегда делается 
+       сравнение без учёта регистра символов.
+
+    *) Исправление: теперь nginx/Windows игнорирует точки в конце URI.
+       Спасибо Hugo Leisink.
+
+    *) Исправление: имя файла указанного в --conf-path игнорировалось при 
+       установке; ошибка появилась в 0.6.6.
+       Спасибо Максиму Дунину.
+
+    *) Исправление: при использовании HTTP Basic-аутентификации на Windows 
+       для неверных имени/пароля возвращалась 500-ая ошибка.
+
+
 Изменения в nginx 0.7.61                                          22.06.2009
 
     *) Исправление: nginx не собирался на MacOSX 10.6.
@@ -6,9 +43,9 @@
     *) Исправление: nginx не собирался с параметром --without-http-cache; 
        ошибка появилась в 0.7.60.
 
-    *) Исправление: если было использовался перехват 401 ошибки от бэкенда 
-       и бэкенд не возвращал строку "WWW-Authenticate" в заголовке ответа, 
-       то в рабочем процессе происходил segmentation fault.
+    *) Исправление: если использовался перехват 401 ошибки от бэкенда и 
+       бэкенд не возвращал строку "WWW-Authenticate" в заголовке ответа, то 
+       в рабочем процессе происходил segmentation fault.
        Спасибо Евгению Мычло.
 
 
@@ -23,7 +60,7 @@
        на старте.
 
     *) Исправление: open_file_cache мог кэшировать открытые файлы очень 
-       долго.
+       долго; ошибка появилась в 0.7.4.
 
     *) Исправление: строки "If-Modified-Since", "If-Range" и им подобные в 
        заголовке запроса клиента передавались бэкенду при кэшировании, если 
--- a/auto/install
+++ b/auto/install
@@ -102,7 +102,7 @@ install:	$NGX_OBJS${ngx_dirsep}nginx${ng
 		'\$(DESTDIR)$NGX_CONF_PREFIX/fastcgi_params.default'
 
 	test -f '\$(DESTDIR)$NGX_CONF_PATH' \
-		|| cp conf/nginx.conf '\$(DESTDIR)$NGX_CONF_PREFIX'
+		|| cp conf/nginx.conf '\$(DESTDIR)$NGX_CONF_PATH'
 	cp conf/nginx.conf '\$(DESTDIR)$NGX_CONF_PREFIX/nginx.conf.default'
 
 	test -d '\$(DESTDIR)`dirname "$NGX_PID_PATH"`' \
--- a/auto/os/linux
+++ b/auto/os/linux
@@ -18,7 +18,7 @@ CC_AUX_FLAGS="$cc_aux_flags -D_GNU_SOURC
 # Linux kernel version
 
 version=$((`uname -r \
-         | sed 's/^\([^.]*\)\.\([^.]*\)\.\([^.-]*\).*/\1*256*256+\2*256+\3/'`))
+       | sed 's/^\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\).*/\1*256*256+\2*256+\3/'`))
 
 version=${version:-0}
 
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,8 +8,8 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define nginx_version         7061
-#define NGINX_VERSION      "0.7.61"
+#define nginx_version         7062
+#define NGINX_VERSION      "0.7.62"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_buf.h
+++ b/src/core/ngx_buf.h
@@ -75,13 +75,13 @@ typedef struct {
     ngx_chain_t                 *free;
     ngx_chain_t                 *busy;
 
-    unsigned                     sendfile;
-    unsigned                     directio;
+    unsigned                     sendfile:1;
+    unsigned                     directio:1;
 #if (NGX_HAVE_ALIGNED_DIRECTIO)
-    unsigned                     unaligned;
+    unsigned                     unaligned:1;
 #endif
-    unsigned                     need_in_memory;
-    unsigned                     need_in_temp;
+    unsigned                     need_in_memory:1;
+    unsigned                     need_in_temp:1;
 
     ngx_pool_t                  *pool;
     ngx_int_t                    allocated;
--- a/src/core/ngx_string.c
+++ b/src/core/ngx_string.c
@@ -87,7 +87,7 @@ ngx_pstrdup(ngx_pool_t *pool, ngx_str_t 
  *
  *  reserved:
  *    %t                        ptrdiff_t
- *    %S                        null-teminated wchar string
+ *    %S                        null-terminated wchar string
  *    %C                        wchar
  */
 
--- a/src/http/modules/ngx_http_fastcgi_module.c
+++ b/src/http/modules/ngx_http_fastcgi_module.c
@@ -1072,9 +1072,10 @@ ngx_http_fastcgi_reinit_request(ngx_http
 static ngx_int_t
 ngx_http_fastcgi_process_header(ngx_http_request_t *r)
 {
-    u_char                         *p, *start, *last, *part_start;
+    u_char                         *p, *msg, *start, *last,
+                                   *part_start, *part_end;
     size_t                          size;
-    ngx_str_t                      *status_line, line, *pattern;
+    ngx_str_t                      *status_line, *pattern;
     ngx_int_t                       rc, status;
     ngx_buf_t                       buf;
     ngx_uint_t                      i;
@@ -1158,40 +1159,39 @@ ngx_http_fastcgi_process_header(ngx_http
         if (f->type == NGX_HTTP_FASTCGI_STDERR) {
 
             if (f->length) {
-                line.data = u->buffer.pos;
+                msg = u->buffer.pos;
 
                 if (u->buffer.pos + f->length <= u->buffer.last) {
-                    line.len = f->length;
                     u->buffer.pos += f->length;
                     f->length = 0;
                     f->state = ngx_http_fastcgi_st_padding;
 
                 } else {
-                    line.len = u->buffer.last - u->buffer.pos;
                     f->length -= u->buffer.last - u->buffer.pos;
                     u->buffer.pos = u->buffer.last;
                 }
 
-                while (line.data[line.len - 1] == LF
-                       || line.data[line.len - 1] == CR
-                       || line.data[line.len - 1] == '.'
-                       || line.data[line.len - 1] == ' ')
-                {
-                    line.len--;
+                for (p = u->buffer.pos - 1; msg < p; p--) {
+                    if (*p != LF && *p != CR && *p != '.' && *p != ' ') {
+                        break;
+                    }
                 }
 
+                p++;
+
                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                              "FastCGI sent in stderr: \"%V\"", &line);
+                              "FastCGI sent in stderr: \"%*s\"", p - msg, msg);
 
                 flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
 
                 if (flcf->catch_stderr) {
                     pattern = flcf->catch_stderr->elts;
 
-                    line.data[line.len - 1] = '\0';
-
                     for (i = 0; i < flcf->catch_stderr->nelts; i++) {
-                        if (ngx_strstr(line.data, pattern[i].data)) {
+                        if (ngx_strnstr(msg, (char *) pattern[i].data,
+                                        p - msg)
+                            != NULL)
+                        {
                             return NGX_HTTP_UPSTREAM_INVALID_HEADER;
                         }
                     }
@@ -1244,6 +1244,7 @@ ngx_http_fastcgi_process_header(ngx_http
         for ( ;; ) {
 
             part_start = u->buffer.pos;
+            part_end = u->buffer.last;
 
             rc = ngx_http_parse_header_line(r, &u->buffer, 1);
 
@@ -1444,7 +1445,11 @@ ngx_http_fastcgi_process_header(ngx_http
         part = ngx_array_push(f->split_parts);
 
         part->start = part_start;
-        part->end = u->buffer.last;
+        part->end = part_end;
+
+        if (u->buffer.pos < u->buffer.last) {
+            continue;
+        }
 
         return NGX_AGAIN;
     }
@@ -1454,9 +1459,9 @@ ngx_http_fastcgi_process_header(ngx_http
 static ngx_int_t
 ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
 {
+    u_char                  *m, *msg;
     ngx_int_t                rc;
     ngx_buf_t               *b, **prev;
-    ngx_str_t                line;
     ngx_chain_t             *cl;
     ngx_http_request_t      *r;
     ngx_http_fastcgi_ctx_t  *f;
@@ -1540,30 +1545,27 @@ ngx_http_fastcgi_input_filter(ngx_event_
                     break;
                 }
 
-                line.data = f->pos;
+                msg = f->pos;
 
                 if (f->pos + f->length <= f->last) {
-                    line.len = f->length;
                     f->pos += f->length;
                     f->length = 0;
                     f->state = ngx_http_fastcgi_st_padding;
 
                 } else {
-                    line.len = f->last - f->pos;
                     f->length -= f->last - f->pos;
                     f->pos = f->last;
                 }
 
-                while (line.data[line.len - 1] == LF
-                       || line.data[line.len - 1] == CR
-                       || line.data[line.len - 1] == '.'
-                       || line.data[line.len - 1] == ' ')
-                {
-                    line.len--;
+                for (m = f->pos - 1; msg < m; m--) {
+                    if (*m != LF && *m != CR && *m != '.' && *m != ' ') {
+                        break;
+                    }
                 }
 
                 ngx_log_error(NGX_LOG_ERR, p->log, 0,
-                              "FastCGI sent in stderr: \"%V\"", &line);
+                              "FastCGI sent in stderr: \"%*s\"",
+                              m + 1 - msg, msg);
 
                 if (f->pos == f->last) {
                     break;
--- a/src/http/modules/ngx_http_xslt_filter_module.c
+++ b/src/http/modules/ngx_http_xslt_filter_module.c
@@ -194,7 +194,7 @@ ngx_module_t  ngx_http_xslt_filter_modul
     NULL,                                  /* init process */
     NULL,                                  /* init thread */
     NULL,                                  /* exit thread */
-    ngx_http_xslt_filter_exit,            /* exit process */
+    ngx_http_xslt_filter_exit,             /* exit process */
     ngx_http_xslt_filter_exit,             /* exit master */
     NGX_MODULE_V1_PADDING
 };
@@ -247,6 +247,7 @@ ngx_http_xslt_header_filter(ngx_http_req
 static ngx_int_t
 ngx_http_xslt_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
 {
+    int                          wellFormed;
     ngx_chain_t                 *cl;
     ngx_http_xslt_filter_ctx_t  *ctx;
 
@@ -288,9 +289,11 @@ ngx_http_xslt_body_filter(ngx_http_reque
             ctx->doc->extSubset = NULL;
 #endif
 
+            wellFormed = ctx->ctxt->wellFormed;
+
             xmlFreeParserCtxt(ctx->ctxt);
 
-            if (ctx->ctxt->wellFormed) {
+            if (wellFormed) {
                 return ngx_http_xslt_send(r, ctx,
                                        ngx_http_xslt_apply_stylesheet(r, ctx));
             }
@@ -717,7 +720,7 @@ ngx_http_xslt_sax_error(void *data, cons
     while (--n && (buf[n] == CR || buf[n] == LF)) { /* void */ }
 
     ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
-                  "libxml2 error: \"%*s\"", n, buf);
+                  "libxml2 error: \"%*s\"", n + 1, buf);
 }
 
 
--- 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.61';
+our $VERSION = '0.7.62';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/ngx_http_cache.h
+++ b/src/http/ngx_http_cache.h
@@ -13,8 +13,11 @@
 #include <ngx_http.h>
 
 
-#define NGX_HTTP_CACHE_STALE         1
-#define NGX_HTTP_CACHE_UPDATING      2
+#define NGX_HTTP_CACHE_MISS          1
+#define NGX_HTTP_CACHE_EXPIRED       2
+#define NGX_HTTP_CACHE_STALE         3
+#define NGX_HTTP_CACHE_UPDATING      4
+#define NGX_HTTP_CACHE_HIT           5
 
 #define NGX_HTTP_CACHE_KEY_LEN       16
 
@@ -124,11 +127,12 @@ ngx_int_t ngx_http_cache_send(ngx_http_r
 void ngx_http_file_cache_free(ngx_http_request_t *r, ngx_temp_file_t *tf);
 time_t ngx_http_file_cache_valid(ngx_array_t *cache_valid, ngx_uint_t status);
 
-
 char *ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
 char *ngx_http_file_cache_valid_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
 
+extern ngx_str_t  ngx_http_cache_status[];
+
 
 #endif /* _NGX_HTTP_CACHE_H_INCLUDED_ */
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -1220,10 +1220,7 @@ ngx_http_core_try_files_phase(ngx_http_r
             ngx_memcpy(p, name, path.len);
         }
 
-        if (ngx_http_set_exten(r) != NGX_OK) {
-            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
-            return NGX_OK;
-        }
+        ngx_http_set_exten(r);
 
         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                        "try file uri: \"%V\"", &r->uri);
@@ -1633,7 +1630,7 @@ ngx_http_set_content_type(ngx_http_reque
 }
 
 
-ngx_int_t
+void
 ngx_http_set_exten(ngx_http_request_t *r)
 {
     ngx_int_t  i;
@@ -1647,14 +1644,14 @@ ngx_http_set_exten(ngx_http_request_t *r
             r->exten.len = r->uri.len - i - 1;
             r->exten.data = &r->uri.data[i + 1];
 
-            break;
+            return;
 
         } else if (r->uri.data[i] == '/') {
-            break;
+            return;
         }
     }
 
-    return NGX_OK;
+    return;
 }
 
 
@@ -2079,9 +2076,7 @@ ngx_http_subrequest(ngx_http_request_t *
     sr->method_name = ngx_http_core_get_method;
     sr->http_protocol = r->http_protocol;
 
-    if (ngx_http_set_exten(sr) != NGX_OK) {
-        return NGX_ERROR;
-    }
+    ngx_http_set_exten(sr);
 
     sr->main = r->main;
     sr->parent = r;
@@ -2160,10 +2155,7 @@ ngx_http_internal_redirect(ngx_http_requ
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "internal redirect: \"%V?%V\"", uri, &r->args);
 
-    if (ngx_http_set_exten(r) != NGX_OK) {
-        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
-        return NGX_DONE;
-    }
+    ngx_http_set_exten(r);
 
     /* clear the modules contexts */
     ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module);
@@ -2566,6 +2558,10 @@ ngx_http_core_regex_location(ngx_conf_t 
     err.len = NGX_MAX_CONF_ERRSTR;
     err.data = errstr;
 
+#if (NGX_HAVE_CASELESS_FILESYSTEM)
+    caseless = 1;
+#endif
+
     clcf->regex = ngx_regex_compile(regex, caseless ? NGX_REGEX_CASELESS: 0,
                                     cf->pool, &err);
 
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -437,7 +437,7 @@ ngx_int_t ngx_http_core_content_phase(ng
 
 void *ngx_http_test_content_type(ngx_http_request_t *r, ngx_hash_t *types_hash);
 ngx_int_t ngx_http_set_content_type(ngx_http_request_t *r);
-ngx_int_t ngx_http_set_exten(ngx_http_request_t *r);
+void ngx_http_set_exten(ngx_http_request_t *r);
 u_char *ngx_http_map_uri_to_path(ngx_http_request_t *r, ngx_str_t *name,
     size_t *root_length, size_t reserved);
 ngx_int_t ngx_http_auth_basic_user(ngx_http_request_t *r);
--- a/src/http/ngx_http_file_cache.c
+++ b/src/http/ngx_http_file_cache.c
@@ -35,6 +35,15 @@ static ngx_int_t ngx_http_file_cache_del
     ngx_str_t *path);
 
 
+ngx_str_t  ngx_http_cache_status[] = {
+    ngx_string("MISS"),
+    ngx_string("EXPIRED"),
+    ngx_string("STALE"),
+    ngx_string("UPDATING"),
+    ngx_string("HIT")
+};
+
+
 static u_char  ngx_http_file_cache_key[] = { LF, 'K', 'E', 'Y', ':', ' ' };
 
 
--- a/src/http/ngx_http_parse.c
+++ b/src/http/ngx_http_parse.c
@@ -739,6 +739,7 @@ ngx_http_parse_header_line(ngx_http_requ
 
         /* first char */
         case sw_start:
+            r->header_name_start = p;
             r->invalid_header = 0;
 
             switch (ch) {
@@ -751,7 +752,6 @@ ngx_http_parse_header_line(ngx_http_requ
                 goto header_done;
             default:
                 state = sw_name;
-                r->header_name_start = p;
 
                 c = lowcase[ch];
 
@@ -950,9 +950,6 @@ ngx_http_parse_complex_uri(ngx_http_requ
         sw_slash,
         sw_dot,
         sw_dot_dot,
-#if (NGX_WIN32)
-        sw_dot_dot_dot,
-#endif
         sw_quoted,
         sw_quoted_second
     } state, quoted_state;
@@ -1137,65 +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--;
-                }
-                break;
-            case '%':
-                quoted_state = state;
-                state = sw_quoted;
-                break;
-            case '?':
-                r->args_start = p;
-                goto args;
-            case '#':
-                goto done;
-#if (NGX_WIN32)
-            case '.':
-                state = sw_dot_dot_dot;
-                *u++ = ch;
-                break;
-#endif
-            case '+':
-                r->plus_in_uri = 1;
-            default:
-                state = sw_usual;
-                *u++ = ch;
-                break;
-            }
-
-            ch = *p++;
-            break;
-
-#if (NGX_WIN32)
-        case sw_dot_dot_dot:
-
-            if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
-                state = sw_usual;
-                *u++ = ch;
-                ch = *p++;
-                break;
-            }
-
-            switch(ch) {
-            case '\\':
-            case '/':
-                state = sw_slash;
                 u -= 5;
-                if (u < r->uri.data) {
-                    return NGX_HTTP_PARSE_INVALID_REQUEST;
-                }
-                while (*u != '/') {
-                    u--;
-                }
-                if (u < r->uri.data) {
-                    return NGX_HTTP_PARSE_INVALID_REQUEST;
-                }
-                while (*(u - 1) != '/') {
+                for ( ;; ) {
+                    if (u < r->uri.data) {
+                        return NGX_HTTP_PARSE_INVALID_REQUEST;
+                    }
+                    if (*u == '/') {
+                        u++;
+                        break;
+                    }
                     u--;
                 }
                 break;
@@ -1218,7 +1165,6 @@ ngx_http_parse_complex_uri(ngx_http_requ
 
             ch = *p++;
             break;
-#endif
 
         case sw_quoted:
             r->quoted_uri = 1;
@@ -1369,20 +1315,6 @@ ngx_http_parse_unsafe_uri(ngx_http_reque
             if (p[0] == '.' && p[1] == '.' && ngx_path_separator(p[2])) {
                 goto unsafe;
             }
-
-#if (NGX_WIN32)
-
-            if (len > 3) {
-
-                /* detect "/.../" */
-
-                if (p[0] == '.' && p[1] == '.' && p[2] == '.'
-                    && ngx_path_separator(p[3]))
-                {
-                    goto unsafe;
-                }
-            }
-#endif
         }
     }
 
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -763,6 +763,22 @@ ngx_http_process_request_line(ngx_event_
                 r->args.data = r->args_start;
             }
 
+#if (NGX_WIN32)
+            {
+            u_char  *p;
+
+            p = r->uri.data + r->uri.len - 1;
+
+            if (*p == '.') {
+
+                while (--p > r->uri.data && *p == '.') { /* void */ }
+
+                r->uri.len = p + 1 - r->uri.data;
+
+                ngx_http_set_exten(r);
+            }
+            }
+#endif
 
             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
                            "http request line: \"%V\"", &r->request_line);
@@ -869,9 +885,10 @@ ngx_http_process_request_line(ngx_event_
 static void
 ngx_http_process_request_headers(ngx_event_t *rev)
 {
+    u_char                     *p;
+    size_t                      len;
     ssize_t                     n;
     ngx_int_t                   rc, rv;
-    ngx_str_t                   header;
     ngx_table_elt_t            *h;
     ngx_connection_t           *c;
     ngx_http_header_t          *hh;
@@ -911,19 +928,17 @@ ngx_http_process_request_headers(ngx_eve
                 }
 
                 if (rv == NGX_DECLINED) {
-                    header.len = r->header_in->end - r->header_name_start;
-                    header.data = r->header_name_start;
-
-                    if (header.len > NGX_MAX_ERROR_STR - 300) {
-                        header.len = NGX_MAX_ERROR_STR - 300;
-                        header.data[header.len++] = '.';
-                        header.data[header.len++] = '.';
-                        header.data[header.len++] = '.';
+                    len = r->header_in->end - r->header_name_start;
+                    p = r->header_name_start;
+
+                    if (len > NGX_MAX_ERROR_STR - 300) {
+                        len = NGX_MAX_ERROR_STR - 300;
+                        p[len++] = '.'; p[len++] = '.'; p[len++] = '.';
                     }
 
                     ngx_log_error(NGX_LOG_INFO, c->log, 0,
-                                  "client sent too long header line: \"%V\"",
-                                  &header);
+                                  "client sent too long header line: \"%*s\"",
+                                  len, r->header_name_start);
                     ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
                     return;
                 }
@@ -945,12 +960,10 @@ ngx_http_process_request_headers(ngx_eve
 
                 /* there was error while a header line parsing */
 
-                header.len = r->header_end - r->header_name_start;
-                header.data = r->header_name_start;
-
                 ngx_log_error(NGX_LOG_INFO, c->log, 0,
-                              "client sent invalid header line: \"%V\"",
-                              &header);
+                              "client sent invalid header line: \"%*s\"",
+                              r->header_end - r->header_name_start,
+                              r->header_name_start);
                 continue;
             }
 
@@ -1030,11 +1043,10 @@ ngx_http_process_request_headers(ngx_eve
 
         /* rc == NGX_HTTP_PARSE_INVALID_HEADER: "\r" is not followed by "\n" */
 
-        header.len = r->header_end - r->header_name_start;
-        header.data = r->header_name_start;
         ngx_log_error(NGX_LOG_INFO, c->log, 0,
-                      "client sent invalid header line: \"%V\\r...\"",
-                      &header);
+                      "client sent invalid header line: \"%*s\\r...\"",
+                      r->header_end - r->header_name_start,
+                      r->header_name_start);
         ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
         return;
     }
--- a/src/http/ngx_http_script.c
+++ b/src/http/ngx_http_script.c
@@ -1074,11 +1074,7 @@ ngx_http_script_regex_end_code(ngx_http_
             return;
         }
 
-        if (ngx_http_set_exten(r) != NGX_OK) {
-            e->ip = ngx_http_script_exit;
-            e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
-            return;
-        }
+        ngx_http_set_exten(r);
     }
 
     e->ip += sizeof(ngx_http_script_regex_end_code_t);
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -14,6 +14,8 @@ static ngx_int_t ngx_http_upstream_cache
     ngx_http_upstream_t *u);
 static ngx_int_t ngx_http_upstream_cache_send(ngx_http_request_t *r,
     ngx_http_upstream_t *u);
+static ngx_int_t ngx_http_upstream_cache_status(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
 #endif
 
 static void ngx_http_upstream_resolve_handler(ngx_resolver_ctx_t *ctx);
@@ -159,6 +161,12 @@ ngx_http_upstream_header_t  ngx_http_ups
                  offsetof(ngx_http_upstream_headers_in_t, last_modified),
                  ngx_http_upstream_copy_last_modified, 0, 0 },
 
+    { ngx_string("ETag"),
+                 ngx_http_upstream_process_header_line,
+                 offsetof(ngx_http_upstream_headers_in_t, etag),
+                 ngx_http_upstream_copy_header_line,
+                 offsetof(ngx_http_headers_out_t, etag), 0 },
+
     { ngx_string("Server"),
                  ngx_http_upstream_process_header_line,
                  offsetof(ngx_http_upstream_headers_in_t, server),
@@ -316,6 +324,14 @@ static ngx_http_variable_t  ngx_http_ups
       ngx_http_upstream_response_length_variable, 0,
       NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 },
 
+#if (NGX_HTTP_CACHE)
+
+    { ngx_string("upstream_cache_status"), NULL,
+      ngx_http_upstream_cache_status, 0,
+      NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+#endif
+
     { ngx_null_string, NULL, NULL, 0, 0, 0 }
 };
 
@@ -575,21 +591,34 @@ ngx_http_upstream_cache(ngx_http_request
     c->body_start = u->conf->buffer_size;
     c->file_cache = u->conf->cache->data;
 
+    u->cache_status = NGX_HTTP_CACHE_MISS;
+
     rc = ngx_http_file_cache_open(r);
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http upstream cache: %i", rc);
 
-    if (rc == NGX_HTTP_CACHE_UPDATING) {
+    switch (rc) {
+
+    case NGX_HTTP_CACHE_UPDATING:
+
         if (u->conf->cache_use_stale & NGX_HTTP_UPSTREAM_FT_UPDATING) {
+            u->cache_status = rc;
             rc = NGX_OK;
 
         } else {
             rc = NGX_HTTP_CACHE_STALE;
         }
+
+        break;
+
+    case NGX_OK:
+        u->cache_status = NGX_HTTP_CACHE_HIT;
     }
 
-    if (rc == NGX_OK) {
+    switch (rc) {
+
+    case NGX_OK:
 
         rc = ngx_http_upstream_cache_send(r, u);
 
@@ -597,17 +626,21 @@ ngx_http_upstream_cache(ngx_http_request
             return rc;
         }
 
-    } else if (rc == NGX_ERROR) {
+        break;
+
+    case NGX_ERROR:
 
         return NGX_ERROR;
 
-    } else if (rc == NGX_HTTP_CACHE_STALE) {
+    case NGX_HTTP_CACHE_STALE:
 
         c->valid_sec = 0;
-        u->stale_cache = 1;
         u->buffer.start = NULL;
-
-    } else if (rc == NGX_DECLINED) {
+        u->cache_status = NGX_HTTP_CACHE_EXPIRED;
+
+        break;
+
+    case NGX_DECLINED:
 
         if ((size_t) (u->buffer.end - u->buffer.start) < u->conf->buffer_size) {
             u->buffer.start = NULL;
@@ -617,14 +650,20 @@ ngx_http_upstream_cache(ngx_http_request
             u->buffer.last = u->buffer.pos;
         }
 
-    } else if (rc == NGX_AGAIN) {
+        break;
+
+    case NGX_AGAIN:
 
         u->cacheable = 0;
 
-    } else {
+        break;
+
+    default:
 
         /* cached NGX_HTTP_BAD_GATEWAY, NGX_HTTP_GATEWAY_TIME_OUT, etc. */
 
+        u->cache_status = NGX_HTTP_CACHE_HIT;
+
         return rc;
     }
 
@@ -640,6 +679,7 @@ ngx_http_upstream_cache_send(ngx_http_re
     ngx_int_t          rc;
     ngx_http_cache_t  *c;
 
+    r->cached = 1;
     c = r->cache;
 
     /* TODO: cache stack */
@@ -1521,12 +1561,15 @@ ngx_http_upstream_test_next(ngx_http_req
 
 #if (NGX_HTTP_CACHE)
 
-        if (u->stale_cache && (u->conf->cache_use_stale & un->mask)) {
+        if (u->cache_status == NGX_HTTP_CACHE_EXPIRED
+            && (u->conf->cache_use_stale & un->mask))
+        {
             ngx_int_t  rc;
 
             rc = u->reinit_request(r);
 
             if (rc == NGX_OK) {
+                u->cache_status = NGX_HTTP_CACHE_STALE;
                 rc = ngx_http_upstream_cache_send(r, u);
             }
 
@@ -2007,6 +2050,10 @@ ngx_http_upstream_send_response(ngx_http
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
                    "http cacheable: %d", u->cacheable);
 
+    if (u->cacheable == 0 && r->cache) {
+        ngx_http_file_cache_free(r, u->pipe->temp_file);
+    }
+
 #endif
 
     p = u->pipe;
@@ -2681,12 +2728,15 @@ ngx_http_upstream_next(ngx_http_request_
 
 #if (NGX_HTTP_CACHE)
 
-            if (u->stale_cache && (u->conf->cache_use_stale & ft_type)) {
+            if (u->cache_status == NGX_HTTP_CACHE_EXPIRED
+                && (u->conf->cache_use_stale & ft_type))
+            {
                 ngx_int_t  rc;
 
                 rc = u->reinit_request(r);
 
                 if (rc == NGX_OK) {
+                    u->cache_status = NGX_HTTP_CACHE_STALE;
                     rc = ngx_http_upstream_cache_send(r, u);
                 }
 
@@ -3263,7 +3313,7 @@ ngx_http_upstream_copy_last_modified(ngx
 
 #if (NGX_HTTP_CACHE)
 
-    if (r->cached || r->upstream->cacheable) {
+    if (r->upstream->cacheable) {
         r->headers_out.last_modified = ho;
         r->headers_out.last_modified_time = ngx_http_parse_time(h->value.data,
                                                                 h->value.len);
@@ -3714,6 +3764,33 @@ ngx_http_upstream_header_variable(ngx_ht
 }
 
 
+#if (NGX_HTTP_CACHE)
+
+ngx_int_t
+ngx_http_upstream_cache_status(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    ngx_uint_t  n;
+
+    if (r->upstream == NULL || r->upstream->cache_status == 0) {
+        v->not_found = 1;
+        return NGX_OK;
+    }
+
+    n = r->upstream->cache_status - 1;
+
+    v->valid = 1;
+    v->no_cacheable = 0;
+    v->not_found = 0;
+    v->len = ngx_http_cache_status[n].len;
+    v->data = ngx_http_cache_status[n].data;
+
+    return NGX_OK;
+}
+
+#endif
+
+
 static char *
 ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
 {
--- a/src/http/ngx_http_upstream.h
+++ b/src/http/ngx_http_upstream.h
@@ -298,7 +298,7 @@ struct ngx_http_upstream_s {
     unsigned                         accel:1;
     unsigned                         ssl:1;
 #if (NGX_HTTP_CACHE)
-    unsigned                         stale_cache:1;
+    unsigned                         cache_status:3;
 #endif
 
     unsigned                         buffering:1;
--- a/src/os/unix/ngx_process.c
+++ b/src/os/unix/ngx_process.c
@@ -359,6 +359,7 @@ ngx_signal_handler(int signo)
             break;
 
         case SIGALRM:
+            ngx_sigalrm = 1;
             break;
 
         case SIGIO:
--- a/src/os/unix/ngx_process_cycle.c
+++ b/src/os/unix/ngx_process_cycle.c
@@ -34,6 +34,7 @@ ngx_uint_t    ngx_threaded;
 
 sig_atomic_t  ngx_reap;
 sig_atomic_t  ngx_sigio;
+sig_atomic_t  ngx_sigalrm;
 sig_atomic_t  ngx_terminate;
 sig_atomic_t  ngx_quit;
 sig_atomic_t  ngx_debug_quit;
@@ -130,10 +131,13 @@ ngx_master_process_cycle(ngx_cycle_t *cy
 
     for ( ;; ) {
         if (delay) {
-            delay *= 2;
+            if (ngx_sigalrm) {
+                delay *= 2;
+                ngx_sigalrm = 0;
+            }
 
             ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
-                           "temination cycle: %d", delay);
+                           "termination cycle: %d", delay);
 
             itv.it_interval.tv_sec = 0;
             itv.it_interval.tv_usec = 0;
@@ -492,8 +496,7 @@ ngx_signal_worker_processes(ngx_cycle_t 
         if (kill(ngx_processes[i].pid, signo) == -1) {
             err = ngx_errno;
             ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
-                          "kill(%P, %d) failed",
-                          ngx_processes[i].pid, signo);
+                          "kill(%P, %d) failed", ngx_processes[i].pid, signo);
 
             if (err == NGX_ESRCH) {
                 ngx_processes[i].exited = 1;
--- a/src/os/unix/ngx_process_cycle.h
+++ b/src/os/unix/ngx_process_cycle.h
@@ -39,6 +39,7 @@ extern ngx_uint_t      ngx_exiting;
 
 extern sig_atomic_t    ngx_reap;
 extern sig_atomic_t    ngx_sigio;
+extern sig_atomic_t    ngx_sigalrm;
 extern sig_atomic_t    ngx_quit;
 extern sig_atomic_t    ngx_debug_quit;
 extern sig_atomic_t    ngx_terminate;