# HG changeset patch # User Igor Sysoev # Date 1341864000 -14400 # Node ID 597573166f342c557a4dca398986e68e99c2e381 # Parent cad34cec7d3b26cde171412098f4ddee00d6b918 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. diff --git a/CHANGES b/CHANGES --- 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. diff --git a/CHANGES.ru b/CHANGES.ru --- 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 теперь игнорируется. diff --git a/auto/install b/auto/install --- 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 diff --git a/auto/lib/libatomic/make b/auto/lib/libatomic/make --- 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 diff --git a/auto/lib/perl/make b/auto/lib/perl/make --- 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 diff --git a/src/core/nginx.h b/src/core/nginx.h --- 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" diff --git a/src/core/ngx_shmtx.c b/src/core/ngx_shmtx.c --- 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, diff --git a/src/core/ngx_shmtx.h b/src/core/ngx_shmtx.h --- 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); diff --git a/src/http/modules/ngx_http_addition_filter_module.c b/src/http/modules/ngx_http_addition_filter_module.c --- 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); } diff --git a/src/http/modules/ngx_http_flv_module.c b/src/http/modules/ngx_http_flv_module.c --- 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; } diff --git a/src/http/modules/ngx_http_gzip_filter_module.c b/src/http/modules/ngx_http_gzip_filter_module.c --- 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); } diff --git a/src/http/modules/ngx_http_gzip_static_module.c b/src/http/modules/ngx_http_gzip_static_module.c --- 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; } diff --git a/src/http/modules/ngx_http_headers_filter_module.c b/src/http/modules/ngx_http_headers_filter_module.c --- 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; diff --git a/src/http/modules/ngx_http_map_module.c b/src/http/modules/ngx_http_map_module.c --- 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; diff --git a/src/http/modules/ngx_http_mp4_module.c b/src/http/modules/ngx_http_mp4_module.c --- 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; } diff --git a/src/http/modules/ngx_http_not_modified_filter_module.c b/src/http/modules/ngx_http_not_modified_filter_module.c --- 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 -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; } diff --git a/src/http/modules/ngx_http_range_filter_module.c b/src/http/modules/ngx_http_range_filter_module.c --- 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; diff --git a/src/http/modules/ngx_http_ssi_filter_module.c b/src/http/modules/ngx_http_ssi_filter_module.c --- 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); diff --git a/src/http/modules/ngx_http_static_module.c b/src/http/modules/ngx_http_static_module.c --- 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; } diff --git a/src/http/modules/ngx_http_sub_filter_module.c b/src/http/modules/ngx_http_sub_filter_module.c --- 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); diff --git a/src/http/modules/ngx_http_xslt_filter_module.c b/src/http/modules/ngx_http_xslt_filter_module.c --- 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); diff --git a/src/http/modules/perl/nginx.pm b/src/http/modules/perl/nginx.pm --- 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); diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c --- 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); diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h --- 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_ */ diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c --- 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); diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h --- 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; diff --git a/src/http/ngx_http_special_response.c b/src/http/ngx_http_special_response.c --- 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); diff --git a/src/os/unix/ngx_files.c b/src/os/unix/ngx_files.c --- 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;