1e18a033bSKonstantin Ananyev
2e18a033bSKonstantin Ananyev/*
3e18a033bSKonstantin Ananyev * Copyright (C) Igor Sysoev
4e18a033bSKonstantin Ananyev * Copyright (C) Nginx, Inc.
5e18a033bSKonstantin Ananyev */
6e18a033bSKonstantin Ananyev
7e18a033bSKonstantin Ananyev
8e18a033bSKonstantin Ananyev#include <ngx_config.h>
9e18a033bSKonstantin Ananyev#include <ngx_core.h>
10e18a033bSKonstantin Ananyev#include <ngx_http.h>
11e18a033bSKonstantin Ananyev
12e18a033bSKonstantin Ananyev
13e18a033bSKonstantin Ananyevstatic ngx_uint_t ngx_http_test_if_unmodified(ngx_http_request_t *r);
14e18a033bSKonstantin Ananyevstatic ngx_uint_t ngx_http_test_if_modified(ngx_http_request_t *r);
15e18a033bSKonstantin Ananyevstatic ngx_uint_t ngx_http_test_if_match(ngx_http_request_t *r,
16e18a033bSKonstantin Ananyev    ngx_table_elt_t *header, ngx_uint_t weak);
17e18a033bSKonstantin Ananyevstatic ngx_int_t ngx_http_not_modified_filter_init(ngx_conf_t *cf);
18e18a033bSKonstantin Ananyev
19e18a033bSKonstantin Ananyev
20e18a033bSKonstantin Ananyevstatic ngx_http_module_t  ngx_http_not_modified_filter_module_ctx = {
21e18a033bSKonstantin Ananyev    NULL,                                  /* preconfiguration */
22e18a033bSKonstantin Ananyev    ngx_http_not_modified_filter_init,     /* postconfiguration */
23e18a033bSKonstantin Ananyev
24e18a033bSKonstantin Ananyev    NULL,                                  /* create main configuration */
25e18a033bSKonstantin Ananyev    NULL,                                  /* init main configuration */
26e18a033bSKonstantin Ananyev
27e18a033bSKonstantin Ananyev    NULL,                                  /* create server configuration */
28e18a033bSKonstantin Ananyev    NULL,                                  /* merge server configuration */
29e18a033bSKonstantin Ananyev
30e18a033bSKonstantin Ananyev    NULL,                                  /* create location configuration */
31e18a033bSKonstantin Ananyev    NULL                                   /* merge location configuration */
32e18a033bSKonstantin Ananyev};
33e18a033bSKonstantin Ananyev
34e18a033bSKonstantin Ananyev
35e18a033bSKonstantin Ananyevngx_module_t  ngx_http_not_modified_filter_module = {
36e18a033bSKonstantin Ananyev    NGX_MODULE_V1,
37e18a033bSKonstantin Ananyev    &ngx_http_not_modified_filter_module_ctx, /* module context */
38e18a033bSKonstantin Ananyev    NULL,                                  /* module directives */
39e18a033bSKonstantin Ananyev    NGX_HTTP_MODULE,                       /* module type */
40e18a033bSKonstantin Ananyev    NULL,                                  /* init master */
41e18a033bSKonstantin Ananyev    NULL,                                  /* init module */
42e18a033bSKonstantin Ananyev    NULL,                                  /* init process */
43e18a033bSKonstantin Ananyev    NULL,                                  /* init thread */
44e18a033bSKonstantin Ananyev    NULL,                                  /* exit thread */
45e18a033bSKonstantin Ananyev    NULL,                                  /* exit process */
46e18a033bSKonstantin Ananyev    NULL,                                  /* exit master */
47e18a033bSKonstantin Ananyev    NGX_MODULE_V1_PADDING
48e18a033bSKonstantin Ananyev};
49e18a033bSKonstantin Ananyev
50e18a033bSKonstantin Ananyev
51e18a033bSKonstantin Ananyevstatic ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
52e18a033bSKonstantin Ananyev
53e18a033bSKonstantin Ananyev
54e18a033bSKonstantin Ananyevstatic ngx_int_t
55e18a033bSKonstantin Ananyevngx_http_not_modified_header_filter(ngx_http_request_t *r)
56e18a033bSKonstantin Ananyev{
57e18a033bSKonstantin Ananyev    if (r->headers_out.status != NGX_HTTP_OK
58e18a033bSKonstantin Ananyev        || r != r->main
59e18a033bSKonstantin Ananyev        || r->disable_not_modified)
60e18a033bSKonstantin Ananyev    {
61e18a033bSKonstantin Ananyev        return ngx_http_next_header_filter(r);
62e18a033bSKonstantin Ananyev    }
63e18a033bSKonstantin Ananyev
64e18a033bSKonstantin Ananyev    if (r->headers_in.if_unmodified_since
65e18a033bSKonstantin Ananyev        && !ngx_http_test_if_unmodified(r))
66e18a033bSKonstantin Ananyev    {
67e18a033bSKonstantin Ananyev        return ngx_http_filter_finalize_request(r, NULL,
68e18a033bSKonstantin Ananyev                                                NGX_HTTP_PRECONDITION_FAILED);
69e18a033bSKonstantin Ananyev    }
70e18a033bSKonstantin Ananyev
71e18a033bSKonstantin Ananyev    if (r->headers_in.if_match
72e18a033bSKonstantin Ananyev        && !ngx_http_test_if_match(r, r->headers_in.if_match, 0))
73e18a033bSKonstantin Ananyev    {
74e18a033bSKonstantin Ananyev        return ngx_http_filter_finalize_request(r, NULL,
75e18a033bSKonstantin Ananyev                                                NGX_HTTP_PRECONDITION_FAILED);
76e18a033bSKonstantin Ananyev    }
77e18a033bSKonstantin Ananyev
78e18a033bSKonstantin Ananyev    if (r->headers_in.if_modified_since || r->headers_in.if_none_match) {
79e18a033bSKonstantin Ananyev
80e18a033bSKonstantin Ananyev        if (r->headers_in.if_modified_since
81e18a033bSKonstantin Ananyev            && ngx_http_test_if_modified(r))
82e18a033bSKonstantin Ananyev        {
83e18a033bSKonstantin Ananyev            return ngx_http_next_header_filter(r);
84e18a033bSKonstantin Ananyev        }
85e18a033bSKonstantin Ananyev
86e18a033bSKonstantin Ananyev        if (r->headers_in.if_none_match
87e18a033bSKonstantin Ananyev            && !ngx_http_test_if_match(r, r->headers_in.if_none_match, 1))
88e18a033bSKonstantin Ananyev        {
89e18a033bSKonstantin Ananyev            return ngx_http_next_header_filter(r);
90e18a033bSKonstantin Ananyev        }
91e18a033bSKonstantin Ananyev
92e18a033bSKonstantin Ananyev        /* not modified */
93e18a033bSKonstantin Ananyev
94e18a033bSKonstantin Ananyev        r->headers_out.status = NGX_HTTP_NOT_MODIFIED;
95e18a033bSKonstantin Ananyev        r->headers_out.status_line.len = 0;
96e18a033bSKonstantin Ananyev        r->headers_out.content_type.len = 0;
97e18a033bSKonstantin Ananyev        ngx_http_clear_content_length(r);
98e18a033bSKonstantin Ananyev        ngx_http_clear_accept_ranges(r);
99e18a033bSKonstantin Ananyev
100e18a033bSKonstantin Ananyev        if (r->headers_out.content_encoding) {
101e18a033bSKonstantin Ananyev            r->headers_out.content_encoding->hash = 0;
102e18a033bSKonstantin Ananyev            r->headers_out.content_encoding = NULL;
103e18a033bSKonstantin Ananyev        }
104e18a033bSKonstantin Ananyev
105e18a033bSKonstantin Ananyev        return ngx_http_next_header_filter(r);
106e18a033bSKonstantin Ananyev    }
107e18a033bSKonstantin Ananyev
108e18a033bSKonstantin Ananyev    return ngx_http_next_header_filter(r);
109e18a033bSKonstantin Ananyev}
110e18a033bSKonstantin Ananyev
111e18a033bSKonstantin Ananyev
112e18a033bSKonstantin Ananyevstatic ngx_uint_t
113e18a033bSKonstantin Ananyevngx_http_test_if_unmodified(ngx_http_request_t *r)
114e18a033bSKonstantin Ananyev{
115e18a033bSKonstantin Ananyev    time_t  iums;
116e18a033bSKonstantin Ananyev
117e18a033bSKonstantin Ananyev    if (r->headers_out.last_modified_time == (time_t) -1) {
118e18a033bSKonstantin Ananyev        return 0;
119e18a033bSKonstantin Ananyev    }
120e18a033bSKonstantin Ananyev
121e18a033bSKonstantin Ananyev    iums = ngx_parse_http_time(r->headers_in.if_unmodified_since->value.data,
122e18a033bSKonstantin Ananyev                               r->headers_in.if_unmodified_since->value.len);
123e18a033bSKonstantin Ananyev
124e18a033bSKonstantin Ananyev    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
125e18a033bSKonstantin Ananyev                 "http iums:%T lm:%T", iums, r->headers_out.last_modified_time);
126e18a033bSKonstantin Ananyev
127e18a033bSKonstantin Ananyev    if (iums >= r->headers_out.last_modified_time) {
128e18a033bSKonstantin Ananyev        return 1;
129e18a033bSKonstantin Ananyev    }
130e18a033bSKonstantin Ananyev
131e18a033bSKonstantin Ananyev    return 0;
132e18a033bSKonstantin Ananyev}
133e18a033bSKonstantin Ananyev
134e18a033bSKonstantin Ananyev
135e18a033bSKonstantin Ananyevstatic ngx_uint_t
136e18a033bSKonstantin Ananyevngx_http_test_if_modified(ngx_http_request_t *r)
137e18a033bSKonstantin Ananyev{
138e18a033bSKonstantin Ananyev    time_t                     ims;
139e18a033bSKonstantin Ananyev    ngx_http_core_loc_conf_t  *clcf;
140e18a033bSKonstantin Ananyev
141e18a033bSKonstantin Ananyev    if (r->headers_out.last_modified_time == (time_t) -1) {
142e18a033bSKonstantin Ananyev        return 1;
143e18a033bSKonstantin Ananyev    }
144e18a033bSKonstantin Ananyev
145e18a033bSKonstantin Ananyev    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
146e18a033bSKonstantin Ananyev
147e18a033bSKonstantin Ananyev    if (clcf->if_modified_since == NGX_HTTP_IMS_OFF) {
148e18a033bSKonstantin Ananyev        return 1;
149e18a033bSKonstantin Ananyev    }
150e18a033bSKonstantin Ananyev
151e18a033bSKonstantin Ananyev    ims = ngx_parse_http_time(r->headers_in.if_modified_since->value.data,
152e18a033bSKonstantin Ananyev                              r->headers_in.if_modified_since->value.len);
153e18a033bSKonstantin Ananyev
154e18a033bSKonstantin Ananyev    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
155e18a033bSKonstantin Ananyev                   "http ims:%T lm:%T", ims, r->headers_out.last_modified_time);
156e18a033bSKonstantin Ananyev
157e18a033bSKonstantin Ananyev    if (ims == r->headers_out.last_modified_time) {
158e18a033bSKonstantin Ananyev        return 0;
159e18a033bSKonstantin Ananyev    }
160e18a033bSKonstantin Ananyev
161e18a033bSKonstantin Ananyev    if (clcf->if_modified_since == NGX_HTTP_IMS_EXACT
162e18a033bSKonstantin Ananyev        || ims < r->headers_out.last_modified_time)
163e18a033bSKonstantin Ananyev    {
164e18a033bSKonstantin Ananyev        return 1;
165e18a033bSKonstantin Ananyev    }
166e18a033bSKonstantin Ananyev
167e18a033bSKonstantin Ananyev    return 0;
168e18a033bSKonstantin Ananyev}
169e18a033bSKonstantin Ananyev
170e18a033bSKonstantin Ananyev
171e18a033bSKonstantin Ananyevstatic ngx_uint_t
172e18a033bSKonstantin Ananyevngx_http_test_if_match(ngx_http_request_t *r, ngx_table_elt_t *header,
173e18a033bSKonstantin Ananyev    ngx_uint_t weak)
174e18a033bSKonstantin Ananyev{
175e18a033bSKonstantin Ananyev    u_char     *start, *end, ch;
176e18a033bSKonstantin Ananyev    ngx_str_t   etag, *list;
177e18a033bSKonstantin Ananyev
178e18a033bSKonstantin Ananyev    list = &header->value;
179e18a033bSKonstantin Ananyev
180e18a033bSKonstantin Ananyev    if (list->len == 1 && list->data[0] == '*') {
181e18a033bSKonstantin Ananyev        return 1;
182e18a033bSKonstantin Ananyev    }
183e18a033bSKonstantin Ananyev
184e18a033bSKonstantin Ananyev    if (r->headers_out.etag == NULL) {
185e18a033bSKonstantin Ananyev        return 0;
186e18a033bSKonstantin Ananyev    }
187e18a033bSKonstantin Ananyev
188e18a033bSKonstantin Ananyev    etag = r->headers_out.etag->value;
189e18a033bSKonstantin Ananyev
190e18a033bSKonstantin Ananyev    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
191e18a033bSKonstantin Ananyev                   "http im:\"%V\" etag:%V", list, &etag);
192e18a033bSKonstantin Ananyev
193e18a033bSKonstantin Ananyev    if (weak
194e18a033bSKonstantin Ananyev        && etag.len > 2
195e18a033bSKonstantin Ananyev        && etag.data[0] == 'W'
196e18a033bSKonstantin Ananyev        && etag.data[1] == '/')
197e18a033bSKonstantin Ananyev    {
198e18a033bSKonstantin Ananyev        etag.len -= 2;
199e18a033bSKonstantin Ananyev        etag.data += 2;
200e18a033bSKonstantin Ananyev    }
201e18a033bSKonstantin Ananyev
202e18a033bSKonstantin Ananyev    start = list->data;
203e18a033bSKonstantin Ananyev    end = list->data + list->len;
204e18a033bSKonstantin Ananyev
205e18a033bSKonstantin Ananyev    while (start < end) {
206e18a033bSKonstantin Ananyev
207e18a033bSKonstantin Ananyev        if (weak
208e18a033bSKonstantin Ananyev            && end - start > 2
209e18a033bSKonstantin Ananyev            && start[0] == 'W'
210e18a033bSKonstantin Ananyev            && start[1] == '/')
211e18a033bSKonstantin Ananyev        {
212e18a033bSKonstantin Ananyev            start += 2;
213e18a033bSKonstantin Ananyev        }
214e18a033bSKonstantin Ananyev
215e18a033bSKonstantin Ananyev        if (etag.len > (size_t) (end - start)) {
216e18a033bSKonstantin Ananyev            return 0;
217e18a033bSKonstantin Ananyev        }
218e18a033bSKonstantin Ananyev
219e18a033bSKonstantin Ananyev        if (ngx_strncmp(start, etag.data, etag.len) != 0) {
220e18a033bSKonstantin Ananyev            goto skip;
221e18a033bSKonstantin Ananyev        }
222e18a033bSKonstantin Ananyev
223e18a033bSKonstantin Ananyev        start += etag.len;
224e18a033bSKonstantin Ananyev
225e18a033bSKonstantin Ananyev        while (start < end) {
226e18a033bSKonstantin Ananyev            ch = *start;
227e18a033bSKonstantin Ananyev
228e18a033bSKonstantin Ananyev            if (ch == ' ' || ch == '\t') {
229e18a033bSKonstantin Ananyev                start++;
230e18a033bSKonstantin Ananyev                continue;
231e18a033bSKonstantin Ananyev            }
232e18a033bSKonstantin Ananyev
233e18a033bSKonstantin Ananyev            break;
234e18a033bSKonstantin Ananyev        }
235e18a033bSKonstantin Ananyev
236e18a033bSKonstantin Ananyev        if (start == end || *start == ',') {
237e18a033bSKonstantin Ananyev            return 1;
238e18a033bSKonstantin Ananyev        }
239e18a033bSKonstantin Ananyev
240e18a033bSKonstantin Ananyev    skip:
241e18a033bSKonstantin Ananyev
242e18a033bSKonstantin Ananyev        while (start < end && *start != ',') { start++; }
243e18a033bSKonstantin Ananyev        while (start < end) {
244e18a033bSKonstantin Ananyev            ch = *start;
245e18a033bSKonstantin Ananyev
246e18a033bSKonstantin Ananyev            if (ch == ' ' || ch == '\t' || ch == ',') {
247e18a033bSKonstantin Ananyev                start++;
248e18a033bSKonstantin Ananyev                continue;
249e18a033bSKonstantin Ananyev            }
250e18a033bSKonstantin Ananyev
251e18a033bSKonstantin Ananyev            break;
252e18a033bSKonstantin Ananyev        }
253e18a033bSKonstantin Ananyev    }
254e18a033bSKonstantin Ananyev
255e18a033bSKonstantin Ananyev    return 0;
256e18a033bSKonstantin Ananyev}
257e18a033bSKonstantin Ananyev
258e18a033bSKonstantin Ananyev
259e18a033bSKonstantin Ananyevstatic ngx_int_t
260e18a033bSKonstantin Ananyevngx_http_not_modified_filter_init(ngx_conf_t *cf)
261e18a033bSKonstantin Ananyev{
262e18a033bSKonstantin Ananyev    ngx_http_next_header_filter = ngx_http_top_header_filter;
263e18a033bSKonstantin Ananyev    ngx_http_top_header_filter = ngx_http_not_modified_header_filter;
264e18a033bSKonstantin Ananyev
265e18a033bSKonstantin Ananyev    return NGX_OK;
266e18a033bSKonstantin Ananyev}
267