changeset 680:597573166f34 NGINX_1_3_3

nginx 1.3.3 *) Feature: entity tags support and the "etag" directive. *) Bugfix: trailing dot in a source value was not ignored if the "map" directive was used with the "hostnames" parameter. *) Bugfix: incorrect location might be used to process a request if a URI was changed via a "rewrite" directive before an internal redirect to a named location.
author Igor Sysoev <http://sysoev.ru>
date Tue, 10 Jul 2012 00:00:00 +0400
parents cad34cec7d3b
children 625501f84a6b
files CHANGES CHANGES.ru auto/install auto/lib/libatomic/make auto/lib/perl/make src/core/nginx.h src/core/ngx_shmtx.c src/core/ngx_shmtx.h src/http/modules/ngx_http_addition_filter_module.c src/http/modules/ngx_http_flv_module.c src/http/modules/ngx_http_gzip_filter_module.c src/http/modules/ngx_http_gzip_static_module.c src/http/modules/ngx_http_headers_filter_module.c src/http/modules/ngx_http_map_module.c src/http/modules/ngx_http_mp4_module.c src/http/modules/ngx_http_not_modified_filter_module.c src/http/modules/ngx_http_range_filter_module.c src/http/modules/ngx_http_ssi_filter_module.c src/http/modules/ngx_http_static_module.c src/http/modules/ngx_http_sub_filter_module.c src/http/modules/ngx_http_xslt_filter_module.c src/http/modules/perl/nginx.pm src/http/ngx_http_core_module.c src/http/ngx_http_core_module.h src/http/ngx_http_request.c src/http/ngx_http_request.h src/http/ngx_http_special_response.c src/os/unix/ngx_files.c
diffstat 28 files changed, 345 insertions(+), 92 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,16 @@
 
+Changes with nginx 1.3.3                                         10 Jul 2012
+
+    *) Feature: entity tags support and the "etag" directive.
+
+    *) Bugfix: trailing dot in a source value was not ignored if the "map"
+       directive was used with the "hostnames" parameter.
+
+    *) Bugfix: incorrect location might be used to process a request if a
+       URI was changed via a "rewrite" directive before an internal redirect
+       to a named location.
+
+
 Changes with nginx 1.3.2                                         26 Jun 2012
 
     *) Change: the "single" parameter of the "keepalive" directive is now
@@ -41,7 +53,7 @@ Changes with nginx 1.3.1                
        directives, and the "server" directive inside the "upstream" block,
        now support IPv6 addresses.
 
-    *) Feature: the "resolver" directive now support IPv6 addresses and an
+    *) Feature: the "resolver" directive now supports IPv6 addresses and an
        optional port specification.
 
     *) Feature: the "least_conn" directive inside the "upstream" block.
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,16 @@
 
+Изменения в nginx 1.3.3                                           10.07.2012
+
+    *) Добавление: поддержка entity tags и директива etag.
+
+    *) Исправление: при использовании директивы map с параметром hostnames
+       не игнорировалась конечная точка в исходном значении.
+
+    *) Исправление: для обработки запроса мог использоваться неверный
+       location, если переход в именованный location происходил после
+       изменения URI с помощью директивы rewrite.
+
+
 Изменения в nginx 1.3.2                                           26.06.2012
 
     *) Изменение: параметр single директивы keepalive теперь игнорируется.
--- a/auto/install
+++ b/auto/install
@@ -8,7 +8,7 @@ if [ $USE_PERL = YES ]; then
     cat << END                                                >> $NGX_MAKEFILE
 
 install_perl_modules:
-	cd $NGX_OBJS/src/http/modules/perl && make install
+	cd $NGX_OBJS/src/http/modules/perl && \${MAKE} install
 END
 
     NGX_INSTALL_PERL_MODULES=install_perl_modules
--- a/auto/lib/libatomic/make
+++ b/auto/lib/libatomic/make
@@ -6,7 +6,7 @@
     cat << END                                            >> $NGX_MAKEFILE
 
 $NGX_LIBATOMIC/src/libatomic_ops.a:	$NGX_LIBATOMIC/Makefile
-	cd $NGX_LIBATOMIC && make
+	cd $NGX_LIBATOMIC && \${MAKE}
 
 $NGX_LIBATOMIC/Makefile:	$NGX_MAKEFILE
 	cd $NGX_LIBATOMIC && ./configure
--- a/auto/lib/perl/make
+++ b/auto/lib/perl/make
@@ -12,7 +12,7 @@ cat << END                              
 		$NGX_OBJS/src/http/modules/perl/Makefile
 	cp -p src/http/modules/perl/nginx.* $NGX_OBJS/src/http/modules/perl/
 
-	cd $NGX_OBJS/src/http/modules/perl && make
+	cd $NGX_OBJS/src/http/modules/perl && \${MAKE}
 
 	rm -rf $NGX_OBJS/install_perl
 
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -9,8 +9,8 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define nginx_version      1003002
-#define NGINX_VERSION      "1.3.2"
+#define nginx_version      1003003
+#define NGINX_VERSION      "1.3.3"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_shmtx.c
+++ b/src/core/ngx_shmtx.c
@@ -44,7 +44,7 @@ ngx_shmtx_create(ngx_shmtx_t *mtx, ngx_s
 
 
 void
-ngx_shmtx_destory(ngx_shmtx_t *mtx)
+ngx_shmtx_destroy(ngx_shmtx_t *mtx)
 {
 #if (NGX_HAVE_POSIX_SEM)
 
@@ -208,7 +208,7 @@ ngx_shmtx_create(ngx_shmtx_t *mtx, ngx_s
             return NGX_OK;
         }
 
-        ngx_shmtx_destory(mtx);
+        ngx_shmtx_destroy(mtx);
     }
 
     mtx->fd = ngx_open_file(name, NGX_FILE_RDWR, NGX_FILE_CREATE_OR_OPEN,
@@ -232,7 +232,7 @@ ngx_shmtx_create(ngx_shmtx_t *mtx, ngx_s
 
 
 void
-ngx_shmtx_destory(ngx_shmtx_t *mtx)
+ngx_shmtx_destroy(ngx_shmtx_t *mtx)
 {
     if (ngx_close_file(mtx->fd) == NGX_FILE_ERROR) {
         ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,
--- a/src/core/ngx_shmtx.h
+++ b/src/core/ngx_shmtx.h
@@ -39,7 +39,7 @@ typedef struct {
 
 ngx_int_t ngx_shmtx_create(ngx_shmtx_t *mtx, ngx_shmtx_sh_t *addr,
     u_char *name);
-void ngx_shmtx_destory(ngx_shmtx_t *mtx);
+void ngx_shmtx_destroy(ngx_shmtx_t *mtx);
 ngx_uint_t ngx_shmtx_trylock(ngx_shmtx_t *mtx);
 void ngx_shmtx_lock(ngx_shmtx_t *mtx);
 void ngx_shmtx_unlock(ngx_shmtx_t *mtx);
--- a/src/http/modules/ngx_http_addition_filter_module.c
+++ b/src/http/modules/ngx_http_addition_filter_module.c
@@ -121,6 +121,7 @@ ngx_http_addition_header_filter(ngx_http
 
     ngx_http_clear_content_length(r);
     ngx_http_clear_accept_ranges(r);
+    ngx_http_clear_etag(r);
 
     return ngx_http_next_header_filter(r);
 }
--- a/src/http/modules/ngx_http_flv_module.c
+++ b/src/http/modules/ngx_http_flv_module.c
@@ -194,6 +194,10 @@ ngx_http_flv_handler(ngx_http_request_t 
     r->headers_out.content_length_n = len;
     r->headers_out.last_modified_time = of.mtime;
 
+    if (ngx_http_set_etag(r) != NGX_OK) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
     if (ngx_http_set_content_type(r) != NGX_OK) {
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
--- a/src/http/modules/ngx_http_gzip_filter_module.c
+++ b/src/http/modules/ngx_http_gzip_filter_module.c
@@ -306,6 +306,7 @@ ngx_http_gzip_header_filter(ngx_http_req
 
     ngx_http_clear_content_length(r);
     ngx_http_clear_accept_ranges(r);
+    ngx_http_clear_etag(r);
 
     return ngx_http_next_header_filter(r);
 }
--- a/src/http/modules/ngx_http_gzip_static_module.c
+++ b/src/http/modules/ngx_http_gzip_static_module.c
@@ -207,6 +207,10 @@ ngx_http_gzip_static_handler(ngx_http_re
     r->headers_out.content_length_n = of.size;
     r->headers_out.last_modified_time = of.mtime;
 
+    if (ngx_http_set_etag(r) != NGX_OK) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
     if (ngx_http_set_content_type(r) != NGX_OK) {
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
--- a/src/http/modules/ngx_http_headers_filter_module.c
+++ b/src/http/modules/ngx_http_headers_filter_module.c
@@ -57,6 +57,8 @@ static ngx_int_t ngx_http_add_header(ngx
     ngx_http_header_val_t *hv, ngx_str_t *value);
 static ngx_int_t ngx_http_set_last_modified(ngx_http_request_t *r,
     ngx_http_header_val_t *hv, ngx_str_t *value);
+static ngx_int_t ngx_http_set_response_header(ngx_http_request_t *r,
+    ngx_http_header_val_t *hv, ngx_str_t *value);
 
 static void *ngx_http_headers_create_conf(ngx_conf_t *cf);
 static char *ngx_http_headers_merge_conf(ngx_conf_t *cf,
@@ -72,9 +74,11 @@ static ngx_http_set_header_t  ngx_http_s
 
     { ngx_string("Cache-Control"), 0, ngx_http_add_cache_control },
 
-    { ngx_string("Last-Modified"),
-                 offsetof(ngx_http_headers_out_t, last_modified),
-                 ngx_http_set_last_modified },
+    { ngx_string("Last-Modified"), 0, ngx_http_set_last_modified },
+
+    { ngx_string("ETag"),
+                 offsetof(ngx_http_headers_out_t, etag),
+                 ngx_http_set_response_header },
 
     { ngx_null_string, 0, NULL }
 };
@@ -368,33 +372,55 @@ static ngx_int_t
 ngx_http_set_last_modified(ngx_http_request_t *r, ngx_http_header_val_t *hv,
     ngx_str_t *value)
 {
+    ngx_table_elt_t  *h;
+
+    ngx_http_clear_last_modified(r);
+
+    if (value->len == 0) {
+        return NGX_OK;
+    }
+
+    r->headers_out.last_modified_time = ngx_http_parse_time(value->data,
+                                                            value->len);
+
+    h = ngx_list_push(&r->headers_out.headers);
+    if (h == NULL) {
+        return NGX_ERROR;
+    }
+
+    r->headers_out.last_modified = h;
+
+    h->hash = 1;
+    h->key = hv->key;
+    h->value = *value;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_set_response_header(ngx_http_request_t *r, ngx_http_header_val_t *hv,
+    ngx_str_t *value)
+{
     ngx_table_elt_t  *h, **old;
 
     old = (ngx_table_elt_t **) ((char *) &r->headers_out + hv->offset);
 
-    r->headers_out.last_modified_time = -1;
-
-    if (*old == NULL) {
-
-        if (value->len == 0) {
-            return NGX_OK;
-        }
+    if (*old) {
+        (*old)->hash = 0;
+        *old = NULL;
+    }
 
-        h = ngx_list_push(&r->headers_out.headers);
-        if (h == NULL) {
-            return NGX_ERROR;
-        }
-
-        *old = h;
+    if (value->len == 0) {
+        return NGX_OK;
+    }
 
-    } else {
-        h = *old;
+    h = ngx_list_push(&r->headers_out.headers);
+    if (h == NULL) {
+        return NGX_ERROR;
+    }
 
-        if (value->len == 0) {
-            h->hash = 0;
-            return NGX_OK;
-        }
-    }
+    *old = h;
 
     h->hash = 1;
     h->key = hv->key;
--- a/src/http/modules/ngx_http_map_module.c
+++ b/src/http/modules/ngx_http_map_module.c
@@ -110,7 +110,6 @@ ngx_http_map_variable(ngx_http_request_t
 {
     ngx_http_map_ctx_t  *map = (ngx_http_map_ctx_t *) data;
 
-    size_t                      len;
     ngx_str_t                   val;
     ngx_http_variable_value_t  *value;
 
@@ -121,10 +120,8 @@ ngx_http_map_variable(ngx_http_request_t
         return NGX_ERROR;
     }
 
-    len = val.len;
-
-    if (len && map->hostnames && val.data[len - 1] == '.') {
-        len--;
+    if (map->hostnames && val.len > 0 && val.data[val.len - 1] == '.') {
+        val.len--;
     }
 
     value = ngx_http_map_find(r, &map->map, &val);
@@ -281,6 +278,8 @@ ngx_http_map_block(ngx_conf_t *cf, ngx_c
     map->default_value = ctx.default_value ? ctx.default_value:
                                              &ngx_http_variable_null_value;
 
+    map->hostnames = ctx.hostnames;
+
     hash.key = ngx_hash_key_lc;
     hash.max_size = mcf->hash_max_size;
     hash.bucket_size = mcf->hash_bucket_size;
--- a/src/http/modules/ngx_http_mp4_module.c
+++ b/src/http/modules/ngx_http_mp4_module.c
@@ -586,6 +586,10 @@ ngx_http_mp4_handler(ngx_http_request_t 
     r->headers_out.status = NGX_HTTP_OK;
     r->headers_out.last_modified_time = of.mtime;
 
+    if (ngx_http_set_etag(r) != NGX_OK) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
     if (ngx_http_set_content_type(r) != NGX_OK) {
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
--- a/src/http/modules/ngx_http_not_modified_filter_module.c
+++ b/src/http/modules/ngx_http_not_modified_filter_module.c
@@ -10,8 +10,10 @@
 #include <ngx_http.h>
 
 
-static ngx_int_t ngx_http_test_precondition(ngx_http_request_t *r);
-static ngx_int_t ngx_http_test_not_modified(ngx_http_request_t *r);
+static ngx_uint_t ngx_http_test_if_unmodified(ngx_http_request_t *r);
+static ngx_uint_t ngx_http_test_if_modified(ngx_http_request_t *r);
+static ngx_uint_t ngx_http_test_if_match(ngx_http_request_t *r,
+    ngx_table_elt_t *header);
 static ngx_int_t ngx_http_not_modified_filter_init(ngx_conf_t *cf);
 
 
@@ -59,20 +61,56 @@ ngx_http_not_modified_header_filter(ngx_
         return ngx_http_next_header_filter(r);
     }
 
-    if (r->headers_in.if_unmodified_since) {
-        return ngx_http_test_precondition(r);
+    if (r->headers_in.if_unmodified_since
+        && !ngx_http_test_if_unmodified(r))
+    {
+        return ngx_http_filter_finalize_request(r, NULL,
+                                                NGX_HTTP_PRECONDITION_FAILED);
+    }
+
+    if (r->headers_in.if_match
+        && !ngx_http_test_if_match(r, r->headers_in.if_match))
+    {
+        return ngx_http_filter_finalize_request(r, NULL,
+                                                NGX_HTTP_PRECONDITION_FAILED);
     }
 
-    if (r->headers_in.if_modified_since) {
-        return ngx_http_test_not_modified(r);
+    if (r->headers_in.if_modified_since || r->headers_in.if_none_match) {
+
+        if (r->headers_in.if_modified_since
+            && ngx_http_test_if_modified(r))
+        {
+            return ngx_http_next_header_filter(r);
+        }
+
+        if (r->headers_in.if_none_match
+            && !ngx_http_test_if_match(r, r->headers_in.if_none_match))
+        {
+            return ngx_http_next_header_filter(r);
+        }
+
+        /* not modified */
+
+        r->headers_out.status = NGX_HTTP_NOT_MODIFIED;
+        r->headers_out.status_line.len = 0;
+        r->headers_out.content_type.len = 0;
+        ngx_http_clear_content_length(r);
+        ngx_http_clear_accept_ranges(r);
+
+        if (r->headers_out.content_encoding) {
+            r->headers_out.content_encoding->hash = 0;
+            r->headers_out.content_encoding = NULL;
+        }
+
+        return ngx_http_next_header_filter(r);
     }
 
     return ngx_http_next_header_filter(r);
 }
 
 
-static ngx_int_t
-ngx_http_test_precondition(ngx_http_request_t *r)
+static ngx_uint_t
+ngx_http_test_if_unmodified(ngx_http_request_t *r)
 {
     time_t  iums;
 
@@ -83,16 +121,15 @@ ngx_http_test_precondition(ngx_http_requ
                  "http iums:%d lm:%d", iums, r->headers_out.last_modified_time);
 
     if (iums >= r->headers_out.last_modified_time) {
-        return ngx_http_next_header_filter(r);
+        return 1;
     }
 
-    return ngx_http_filter_finalize_request(r, NULL,
-                                            NGX_HTTP_PRECONDITION_FAILED);
+    return 0;
 }
 
 
-static ngx_int_t
-ngx_http_test_not_modified(ngx_http_request_t *r)
+static ngx_uint_t
+ngx_http_test_if_modified(ngx_http_request_t *r)
 {
     time_t                     ims;
     ngx_http_core_loc_conf_t  *clcf;
@@ -100,7 +137,7 @@ ngx_http_test_not_modified(ngx_http_requ
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
 
     if (clcf->if_modified_since == NGX_HTTP_IMS_OFF) {
-        return ngx_http_next_header_filter(r);
+        return 1;
     }
 
     ims = ngx_http_parse_time(r->headers_in.if_modified_since->value.data,
@@ -109,27 +146,87 @@ ngx_http_test_not_modified(ngx_http_requ
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http ims:%d lm:%d", ims, r->headers_out.last_modified_time);
 
-    if (ims != r->headers_out.last_modified_time) {
+    if (ims == r->headers_out.last_modified_time) {
+        return 0;
+    }
+
+    if (clcf->if_modified_since == NGX_HTTP_IMS_EXACT
+        || ims < r->headers_out.last_modified_time)
+    {
+        return 1;
+    }
+
+    return 0;
+}
+
+
+static ngx_uint_t
+ngx_http_test_if_match(ngx_http_request_t *r, ngx_table_elt_t *header)
+{
+    u_char     *start, *end, ch;
+    ngx_str_t  *etag, *list;
+
+    list = &header->value;
+
+    if (list->len == 1 && list->data[0] == '*') {
+        return 1;
+    }
+
+    if (r->headers_out.etag == NULL) {
+        return 0;
+    }
+
+    etag = &r->headers_out.etag->value;
+
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http im:\"%V\" etag:%V", list, etag);
+
+    start = list->data;
+    end = list->data + list->len;
 
-        if (clcf->if_modified_since == NGX_HTTP_IMS_EXACT
-            || ims < r->headers_out.last_modified_time)
-        {
-            return ngx_http_next_header_filter(r);
+    while (start < end) {
+
+        if (etag->len > (size_t) (end - start)) {
+            return 0;
+        }
+
+        if (ngx_strncmp(start, etag->data, etag->len) != 0) {
+            goto skip;
+        }
+
+        start += etag->len;
+
+        while (start < end) {
+            ch = *start;
+
+            if (ch == ' ' || ch == '\t') {
+                start++;
+                continue;
+            }
+
+            break;
+        }
+
+        if (start == end || *start == ',') {
+            return 1;
+        }
+
+    skip:
+
+        while (start < end && *start != ',') { start++; }
+        while (start < end) {
+            ch = *start;
+
+            if (ch == ' ' || ch == '\t' || ch == ',') {
+                start++;
+                continue;
+            }
+
+            break;
         }
     }
 
-    r->headers_out.status = NGX_HTTP_NOT_MODIFIED;
-    r->headers_out.status_line.len = 0;
-    r->headers_out.content_type.len = 0;
-    ngx_http_clear_content_length(r);
-    ngx_http_clear_accept_ranges(r);
-
-    if (r->headers_out.content_encoding) {
-        r->headers_out.content_encoding->hash = 0;
-        r->headers_out.content_encoding = NULL;
-    }
-
-    return ngx_http_next_header_filter(r);
+    return 0;
 }
 
 
--- a/src/http/modules/ngx_http_range_filter_module.c
+++ b/src/http/modules/ngx_http_range_filter_module.c
@@ -146,7 +146,8 @@ static ngx_http_output_body_filter_pt   
 static ngx_int_t
 ngx_http_range_header_filter(ngx_http_request_t *r)
 {
-    time_t                        if_range;
+    time_t                        if_range_time;
+    ngx_str_t                    *if_range, *etag;
     ngx_http_core_loc_conf_t     *clcf;
     ngx_http_range_filter_ctx_t  *ctx;
 
@@ -174,20 +175,47 @@ ngx_http_range_header_filter(ngx_http_re
         goto next_filter;
     }
 
-    if (r->headers_in.if_range && r->headers_out.last_modified_time != -1) {
+    if (r->headers_in.if_range) {
+
+        if_range = &r->headers_in.if_range->value;
+
+        if (if_range->len >= 2 && if_range->data[if_range->len - 1] == '"') {
+
+            if (r->headers_out.etag == NULL) {
+                goto next_filter;
+            }
+
+            etag = &r->headers_out.etag->value;
+
+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "http ir:%V etag:%V", if_range, etag);
 
-        if_range = ngx_http_parse_time(r->headers_in.if_range->value.data,
-                                       r->headers_in.if_range->value.len);
+            if (if_range->len != etag->len
+                || ngx_strncmp(if_range->data, etag->data, etag->len) != 0)
+            {
+                goto next_filter;
+            }
+
+            goto parse;
+        }
+
+        if (r->headers_out.last_modified_time == (time_t) -1) {
+            goto next_filter;
+        }
+
+        if_range_time = ngx_http_parse_time(if_range->data, if_range->len);
 
         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                        "http ir:%d lm:%d",
-                       if_range, r->headers_out.last_modified_time);
+                       if_range_time, r->headers_out.last_modified_time);
 
-        if (if_range != r->headers_out.last_modified_time) {
+        if (if_range_time != r->headers_out.last_modified_time) {
             goto next_filter;
         }
     }
 
+parse:
+
     ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_range_filter_ctx_t));
     if (ctx == NULL) {
         return NGX_ERROR;
--- a/src/http/modules/ngx_http_ssi_filter_module.c
+++ b/src/http/modules/ngx_http_ssi_filter_module.c
@@ -361,6 +361,7 @@ ngx_http_ssi_header_filter(ngx_http_requ
         ngx_http_clear_content_length(r);
         ngx_http_clear_last_modified(r);
         ngx_http_clear_accept_ranges(r);
+        ngx_http_clear_etag(r);
     }
 
     return ngx_http_next_header_filter(r);
--- a/src/http/modules/ngx_http_static_module.c
+++ b/src/http/modules/ngx_http_static_module.c
@@ -220,6 +220,10 @@ ngx_http_static_handler(ngx_http_request
     r->headers_out.content_length_n = of.size;
     r->headers_out.last_modified_time = of.mtime;
 
+    if (ngx_http_set_etag(r) != NGX_OK) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
     if (ngx_http_set_content_type(r) != NGX_OK) {
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
--- a/src/http/modules/ngx_http_sub_filter_module.c
+++ b/src/http/modules/ngx_http_sub_filter_module.c
@@ -168,6 +168,7 @@ ngx_http_sub_header_filter(ngx_http_requ
     if (r == r->main) {
         ngx_http_clear_content_length(r);
         ngx_http_clear_last_modified(r);
+        ngx_http_clear_etag(r);
     }
 
     return ngx_http_next_header_filter(r);
--- a/src/http/modules/ngx_http_xslt_filter_module.c
+++ b/src/http/modules/ngx_http_xslt_filter_module.c
@@ -328,6 +328,7 @@ ngx_http_xslt_send(ngx_http_request_t *r
         }
 
         ngx_http_clear_last_modified(r);
+        ngx_http_clear_etag(r);
     }
 
     rc = ngx_http_next_header_filter(r);
--- a/src/http/modules/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -50,7 +50,7 @@ our @EXPORT = qw(
     HTTP_INSUFFICIENT_STORAGE
 );
 
-our $VERSION = '1.3.2';
+our $VERSION = '1.3.3';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -650,6 +650,13 @@ static ngx_command_t  ngx_http_core_comm
       offsetof(ngx_http_core_loc_conf_t, chunked_transfer_encoding),
       NULL },
 
+    { ngx_string("etag"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_flag_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_core_loc_conf_t, etag),
+      NULL },
+
     { ngx_string("error_page"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
                         |NGX_CONF_2MORE,
@@ -1809,6 +1816,42 @@ ngx_http_set_exten(ngx_http_request_t *r
 
 
 ngx_int_t
+ngx_http_set_etag(ngx_http_request_t *r)
+{
+    ngx_table_elt_t           *etag;
+    ngx_http_core_loc_conf_t  *clcf;
+    
+    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+ 
+    if (!clcf->etag) {
+        return NGX_OK;
+    }
+
+    etag = ngx_list_push(&r->headers_out.headers);
+    if (etag == NULL) {
+        return NGX_ERROR;
+    }
+
+    etag->hash = 1;
+    ngx_str_set(&etag->key, "ETag");
+
+    etag->value.data = ngx_pnalloc(r->pool, NGX_OFF_T_LEN + NGX_TIME_T_LEN + 3);
+    if (etag->value.data == NULL) {
+        return NGX_ERROR;
+    }
+
+    etag->value.len = ngx_sprintf(etag->value.data, "\"%xT-%xO\"",
+                                  r->headers_out.last_modified_time,
+                                  r->headers_out.content_length_n)
+                      - etag->value.data;
+
+    r->headers_out.etag = etag;
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
 ngx_http_send_response(ngx_http_request_t *r, ngx_uint_t status,
     ngx_str_t *ct, ngx_http_complex_value_t *cv)
 {
@@ -2588,6 +2631,7 @@ ngx_http_named_location(ngx_http_request
 
             r->internal = 1;
             r->content_handler = NULL;
+            r->uri_changed = 0;
             r->loc_conf = (*clcfp)->loc_conf;
 
             /* clear the modules contexts */
@@ -3509,6 +3553,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t
     clcf->recursive_error_pages = NGX_CONF_UNSET;
     clcf->server_tokens = NGX_CONF_UNSET;
     clcf->chunked_transfer_encoding = NGX_CONF_UNSET;
+    clcf->etag = NGX_CONF_UNSET;
     clcf->types_hash_max_size = NGX_CONF_UNSET_UINT;
     clcf->types_hash_bucket_size = NGX_CONF_UNSET_UINT;
 
@@ -3770,6 +3815,7 @@ ngx_http_core_merge_loc_conf(ngx_conf_t 
     ngx_conf_merge_value(conf->server_tokens, prev->server_tokens, 1);
     ngx_conf_merge_value(conf->chunked_transfer_encoding,
                               prev->chunked_transfer_encoding, 1);
+    ngx_conf_merge_value(conf->etag, prev->etag, 1);
 
     ngx_conf_merge_ptr_value(conf->open_file_cache,
                               prev->open_file_cache, NULL);
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -392,6 +392,7 @@ struct ngx_http_core_loc_conf_s {
     ngx_flag_t    recursive_error_pages;   /* recursive_error_pages */
     ngx_flag_t    server_tokens;           /* server_tokens */
     ngx_flag_t    chunked_transfer_encoding; /* chunked_transfer_encoding */
+    ngx_flag_t    etag;                    /* etag */
 
 #if (NGX_HTTP_GZIP)
     ngx_flag_t    gzip_vary;               /* gzip_vary */
@@ -480,6 +481,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);
 void ngx_http_set_exten(ngx_http_request_t *r);
+ngx_int_t ngx_http_set_etag(ngx_http_request_t *r);
 ngx_int_t ngx_http_send_response(ngx_http_request_t *r, ngx_uint_t status,
     ngx_str_t *ct, ngx_http_complex_value_t *cv);
 u_char *ngx_http_map_uri_to_path(ngx_http_request_t *r, ngx_str_t *name,
@@ -555,5 +557,12 @@ extern ngx_str_t  ngx_http_core_get_meth
         r->headers_out.location = NULL;                                       \
     }
 
+#define ngx_http_clear_etag(r)                                                \
+                                                                              \
+    if (r->headers_out.etag) {                                                \
+        r->headers_out.etag->hash = 0;                                        \
+        r->headers_out.etag = NULL;                                           \
+    }
+
 
 #endif /* _NGX_HTTP_CORE_H_INCLUDED_ */
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -93,6 +93,14 @@ ngx_http_header_t  ngx_http_headers_in[]
                  offsetof(ngx_http_headers_in_t, if_unmodified_since),
                  ngx_http_process_unique_header_line },
 
+    { ngx_string("If-Match"),
+                 offsetof(ngx_http_headers_in_t, if_match),
+                 ngx_http_process_unique_header_line },
+
+    { ngx_string("If-None-Match"),
+                 offsetof(ngx_http_headers_in_t, if_none_match),
+                 ngx_http_process_unique_header_line },
+
     { ngx_string("User-Agent"), offsetof(ngx_http_headers_in_t, user_agent),
                  ngx_http_process_user_agent },
 
@@ -747,6 +755,7 @@ ngx_http_process_request_line(ngx_event_
 
             r->request_line.len = r->request_end - r->request_start;
             r->request_line.data = r->request_start;
+            r->request_length = r->header_in->pos - r->request_start;
 
 
             if (r->args_start) {
@@ -1056,6 +1065,8 @@ ngx_http_process_request_headers(ngx_eve
 
         if (rc == NGX_OK) {
 
+            r->request_length += r->header_in->pos - r->header_name_start;
+
             if (r->invalid_header && cscf->ignore_invalid_headers) {
 
                 /* there was error while a header line parsing */
@@ -1119,7 +1130,7 @@ ngx_http_process_request_headers(ngx_eve
             ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                            "http header done");
 
-            r->request_length += r->header_in->pos - r->header_in->start;
+            r->request_length += r->header_in->pos - r->header_name_start;
 
             r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;
 
@@ -1226,8 +1237,6 @@ ngx_http_alloc_large_header_buffer(ngx_h
 
         /* the client fills up the buffer with "\r\n" */
 
-        r->request_length += r->header_in->end - r->header_in->start;
-
         r->header_in->pos = r->header_in->start;
         r->header_in->last = r->header_in->start;
 
@@ -1287,8 +1296,6 @@ ngx_http_alloc_large_header_buffer(ngx_h
          * to relocate the parser header pointers
          */
 
-        r->request_length += r->header_in->end - r->header_in->start;
-
         r->header_in = b;
 
         return NGX_OK;
@@ -1297,8 +1304,6 @@ ngx_http_alloc_large_header_buffer(ngx_h
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http large header copy: %d", r->header_in->pos - old);
 
-    r->request_length += old - r->header_in->start;
-
     new = b->start;
 
     ngx_memcpy(new, old, r->header_in->pos - old);
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -172,6 +172,8 @@ typedef struct {
     ngx_table_elt_t                  *connection;
     ngx_table_elt_t                  *if_modified_since;
     ngx_table_elt_t                  *if_unmodified_since;
+    ngx_table_elt_t                  *if_match;
+    ngx_table_elt_t                  *if_none_match;
     ngx_table_elt_t                  *user_agent;
     ngx_table_elt_t                  *referer;
     ngx_table_elt_t                  *content_length;
--- a/src/http/ngx_http_special_response.c
+++ b/src/http/ngx_http_special_response.c
@@ -656,6 +656,7 @@ ngx_http_send_special_response(ngx_http_
 
     ngx_http_clear_accept_ranges(r);
     ngx_http_clear_last_modified(r);
+    ngx_http_clear_etag(r);
 
     rc = ngx_http_send_header(r);
 
@@ -754,6 +755,7 @@ ngx_http_send_refresh(ngx_http_request_t
 
     ngx_http_clear_accept_ranges(r);
     ngx_http_clear_last_modified(r);
+    ngx_http_clear_etag(r);
 
     rc = ngx_http_send_header(r);
 
--- a/src/os/unix/ngx_files.c
+++ b/src/os/unix/ngx_files.c
@@ -413,9 +413,7 @@ ngx_trylock_fd(ngx_fd_t fd)
 {
     struct flock  fl;
 
-    fl.l_start = 0;
-    fl.l_len = 0;
-    fl.l_pid = 0;
+    ngx_memzero(&fl, sizeof(struct flock));
     fl.l_type = F_WRLCK;
     fl.l_whence = SEEK_SET;
 
@@ -432,9 +430,7 @@ ngx_lock_fd(ngx_fd_t fd)
 {
     struct flock  fl;
 
-    fl.l_start = 0;
-    fl.l_len = 0;
-    fl.l_pid = 0;
+    ngx_memzero(&fl, sizeof(struct flock));
     fl.l_type = F_WRLCK;
     fl.l_whence = SEEK_SET;
 
@@ -451,9 +447,7 @@ ngx_unlock_fd(ngx_fd_t fd)
 {
     struct flock  fl;
 
-    fl.l_start = 0;
-    fl.l_len = 0;
-    fl.l_pid = 0;
+    ngx_memzero(&fl, sizeof(struct flock));
     fl.l_type = F_UNLCK;
     fl.l_whence = SEEK_SET;