# HG changeset patch # User Maxim Dounin # Date 1341696027 0 # Node ID 5b93a9ac60ed124e7f291bad2792a8f523da9bc7 # Parent 84cc73e01aa82809226116177855981ca4eba49d Entity tags: basic support in not modified filter. This includes handling of ETag headers (if present in a response) with basic support for If-Match, If-None-Match conditionals in not modified filter. Note that the "r->headers_out.last_modified_time == -1" check in the not modified filter is left as is intentionally. It's to prevent handling of If-* headers in case of proxy without cache (much like currently done with If-Modified-Since). 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 @@ -12,6 +12,8 @@ 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); @@ -66,9 +68,27 @@ ngx_http_not_modified_header_filter(ngx_ NGX_HTTP_PRECONDITION_FAILED); } - if (r->headers_in.if_modified_since - && !ngx_http_test_if_modified(r)) + 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 || 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; @@ -140,6 +160,76 @@ ngx_http_test_if_modified(ngx_http_reque } +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; + + 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; + } + } + + return 0; +} + + static ngx_int_t ngx_http_not_modified_filter_init(ngx_conf_t *cf) { 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 }, 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;