3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
8 #include <ngx_config.h>
13 static ngx_uint_t ngx_http_test_if_unmodified(ngx_http_request_t *r);
14 static ngx_uint_t ngx_http_test_if_modified(ngx_http_request_t *r);
15 static ngx_uint_t ngx_http_test_if_match(ngx_http_request_t *r,
16 ngx_table_elt_t *header, ngx_uint_t weak);
17 static ngx_int_t ngx_http_not_modified_filter_init(ngx_conf_t *cf);
20 static ngx_http_module_t ngx_http_not_modified_filter_module_ctx = {
21 NULL, /* preconfiguration */
22 ngx_http_not_modified_filter_init, /* postconfiguration */
24 NULL, /* create main configuration */
25 NULL, /* init main configuration */
27 NULL, /* create server configuration */
28 NULL, /* merge server configuration */
30 NULL, /* create location configuration */
31 NULL /* merge location configuration */
35 ngx_module_t ngx_http_not_modified_filter_module = {
37 &ngx_http_not_modified_filter_module_ctx, /* module context */
38 NULL, /* module directives */
39 NGX_HTTP_MODULE, /* module type */
40 NULL, /* init master */
41 NULL, /* init module */
42 NULL, /* init process */
43 NULL, /* init thread */
44 NULL, /* exit thread */
45 NULL, /* exit process */
46 NULL, /* exit master */
51 static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
55 ngx_http_not_modified_header_filter(ngx_http_request_t *r)
57 if (r->headers_out.status != NGX_HTTP_OK
59 || r->disable_not_modified)
61 return ngx_http_next_header_filter(r);
64 if (r->headers_in.if_unmodified_since
65 && !ngx_http_test_if_unmodified(r))
67 return ngx_http_filter_finalize_request(r, NULL,
68 NGX_HTTP_PRECONDITION_FAILED);
71 if (r->headers_in.if_match
72 && !ngx_http_test_if_match(r, r->headers_in.if_match, 0))
74 return ngx_http_filter_finalize_request(r, NULL,
75 NGX_HTTP_PRECONDITION_FAILED);
78 if (r->headers_in.if_modified_since || r->headers_in.if_none_match) {
80 if (r->headers_in.if_modified_since
81 && ngx_http_test_if_modified(r))
83 return ngx_http_next_header_filter(r);
86 if (r->headers_in.if_none_match
87 && !ngx_http_test_if_match(r, r->headers_in.if_none_match, 1))
89 return ngx_http_next_header_filter(r);
94 r->headers_out.status = NGX_HTTP_NOT_MODIFIED;
95 r->headers_out.status_line.len = 0;
96 r->headers_out.content_type.len = 0;
97 ngx_http_clear_content_length(r);
98 ngx_http_clear_accept_ranges(r);
100 if (r->headers_out.content_encoding) {
101 r->headers_out.content_encoding->hash = 0;
102 r->headers_out.content_encoding = NULL;
105 return ngx_http_next_header_filter(r);
108 return ngx_http_next_header_filter(r);
113 ngx_http_test_if_unmodified(ngx_http_request_t *r)
117 if (r->headers_out.last_modified_time == (time_t) -1) {
121 iums = ngx_parse_http_time(r->headers_in.if_unmodified_since->value.data,
122 r->headers_in.if_unmodified_since->value.len);
124 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
125 "http iums:%T lm:%T", iums, r->headers_out.last_modified_time);
127 if (iums >= r->headers_out.last_modified_time) {
136 ngx_http_test_if_modified(ngx_http_request_t *r)
139 ngx_http_core_loc_conf_t *clcf;
141 if (r->headers_out.last_modified_time == (time_t) -1) {
145 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
147 if (clcf->if_modified_since == NGX_HTTP_IMS_OFF) {
151 ims = ngx_parse_http_time(r->headers_in.if_modified_since->value.data,
152 r->headers_in.if_modified_since->value.len);
154 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
155 "http ims:%T lm:%T", ims, r->headers_out.last_modified_time);
157 if (ims == r->headers_out.last_modified_time) {
161 if (clcf->if_modified_since == NGX_HTTP_IMS_EXACT
162 || ims < r->headers_out.last_modified_time)
172 ngx_http_test_if_match(ngx_http_request_t *r, ngx_table_elt_t *header,
175 u_char *start, *end, ch;
176 ngx_str_t etag, *list;
178 list = &header->value;
180 if (list->len == 1 && list->data[0] == '*') {
184 if (r->headers_out.etag == NULL) {
188 etag = r->headers_out.etag->value;
190 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
191 "http im:\"%V\" etag:%V", list, &etag);
195 && etag.data[0] == 'W'
196 && etag.data[1] == '/')
203 end = list->data + list->len;
205 while (start < end) {
215 if (etag.len > (size_t) (end - start)) {
219 if (ngx_strncmp(start, etag.data, etag.len) != 0) {
225 while (start < end) {
228 if (ch == ' ' || ch == '\t') {
236 if (start == end || *start == ',') {
242 while (start < end && *start != ',') { start++; }
243 while (start < end) {
246 if (ch == ' ' || ch == '\t' || ch == ',') {
260 ngx_http_not_modified_filter_init(ngx_conf_t *cf)
262 ngx_http_next_header_filter = ngx_http_top_header_filter;
263 ngx_http_top_header_filter = ngx_http_not_modified_header_filter;