Mercurial > hg > nginx
comparison src/http/modules/ngx_http_headers_filter_module.c @ 1505:f552535e259b stable-0.5
r1364, r1365, r1366, r1367 merge:
add_header changes:
"Last-Modified", "Cache-Control" and "Expires" headers use specific handlers
author | Igor Sysoev <igor@sysoev.ru> |
---|---|
date | Sat, 22 Sep 2007 18:54:28 +0000 |
parents | db7c468c447d |
children |
comparison
equal
deleted
inserted
replaced
1504:840b81d13a2f | 1505:f552535e259b |
---|---|
7 #include <ngx_config.h> | 7 #include <ngx_config.h> |
8 #include <ngx_core.h> | 8 #include <ngx_core.h> |
9 #include <ngx_http.h> | 9 #include <ngx_http.h> |
10 | 10 |
11 | 11 |
12 typedef struct ngx_http_header_val_s ngx_http_header_val_t; | |
13 | |
14 typedef ngx_int_t (*ngx_http_set_header_pt)(ngx_http_request_t *r, | |
15 ngx_http_header_val_t *hv, ngx_str_t *value); | |
16 | |
17 | |
12 typedef struct { | 18 typedef struct { |
13 ngx_table_elt_t value; | 19 ngx_str_t name; |
14 ngx_array_t *lengths; | 20 ngx_uint_t offset; |
15 ngx_array_t *values; | 21 ngx_http_set_header_pt handler; |
16 } ngx_http_header_val_t; | 22 } ngx_http_set_header_t; |
23 | |
24 | |
25 struct ngx_http_header_val_s { | |
26 ngx_table_elt_t value; | |
27 ngx_uint_t offset; | |
28 ngx_http_set_header_pt handler; | |
29 ngx_array_t *lengths; | |
30 ngx_array_t *values; | |
31 }; | |
17 | 32 |
18 | 33 |
19 typedef struct { | 34 typedef struct { |
20 time_t expires; | 35 time_t expires; |
21 ngx_str_t cache_control; | 36 ngx_array_t *headers; |
22 ngx_array_t *headers; | |
23 } ngx_http_headers_conf_t; | 37 } ngx_http_headers_conf_t; |
24 | 38 |
25 | 39 |
26 #define NGX_HTTP_EXPIRES_UNSET -2147483647 | 40 #define NGX_HTTP_EXPIRES_UNSET -2147483647 |
27 #define NGX_HTTP_EXPIRES_OFF -2147483646 | 41 #define NGX_HTTP_EXPIRES_OFF -2147483646 |
28 #define NGX_HTTP_EXPIRES_EPOCH -2147483645 | 42 #define NGX_HTTP_EXPIRES_EPOCH -2147483645 |
29 #define NGX_HTTP_EXPIRES_MAX -2147483644 | 43 #define NGX_HTTP_EXPIRES_MAX -2147483644 |
30 | 44 |
45 | |
46 static ngx_int_t ngx_http_set_expires(ngx_http_request_t *r, | |
47 ngx_http_headers_conf_t *conf); | |
48 static ngx_int_t ngx_http_add_cache_control(ngx_http_request_t *r, | |
49 ngx_http_header_val_t *hv, ngx_str_t *value); | |
50 static ngx_int_t ngx_http_set_last_modified(ngx_http_request_t *r, | |
51 ngx_http_header_val_t *hv, ngx_str_t *value); | |
31 | 52 |
32 static void *ngx_http_headers_create_conf(ngx_conf_t *cf); | 53 static void *ngx_http_headers_create_conf(ngx_conf_t *cf); |
33 static char *ngx_http_headers_merge_conf(ngx_conf_t *cf, | 54 static char *ngx_http_headers_merge_conf(ngx_conf_t *cf, |
34 void *parent, void *child); | 55 void *parent, void *child); |
35 static ngx_int_t ngx_http_headers_filter_init(ngx_conf_t *cf); | 56 static ngx_int_t ngx_http_headers_filter_init(ngx_conf_t *cf); |
36 static char *ngx_http_headers_expires(ngx_conf_t *cf, ngx_command_t *cmd, | 57 static char *ngx_http_headers_expires(ngx_conf_t *cf, ngx_command_t *cmd, |
37 void *conf); | 58 void *conf); |
38 static char *ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd, | 59 static char *ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd, |
39 void *conf); | 60 void *conf); |
61 | |
62 | |
63 static ngx_http_set_header_t ngx_http_set_headers[] = { | |
64 | |
65 { ngx_string("Cache-Control"), 0, ngx_http_add_cache_control }, | |
66 | |
67 { ngx_string("Last-Modified"), | |
68 offsetof(ngx_http_headers_out_t, last_modified), | |
69 ngx_http_set_last_modified }, | |
70 | |
71 { ngx_null_string, 0, NULL } | |
72 }; | |
40 | 73 |
41 | 74 |
42 static ngx_command_t ngx_http_headers_filter_commands[] = { | 75 static ngx_command_t ngx_http_headers_filter_commands[] = { |
43 | 76 |
44 { ngx_string("expires"), | 77 { ngx_string("expires"), |
96 | 129 |
97 | 130 |
98 static ngx_int_t | 131 static ngx_int_t |
99 ngx_http_headers_filter(ngx_http_request_t *r) | 132 ngx_http_headers_filter(ngx_http_request_t *r) |
100 { | 133 { |
101 size_t len; | 134 ngx_str_t value; |
102 ngx_uint_t i; | 135 ngx_uint_t i; |
103 ngx_table_elt_t *expires, *cc, **ccp, *out; | |
104 ngx_http_header_val_t *h; | 136 ngx_http_header_val_t *h; |
105 ngx_http_headers_conf_t *conf; | 137 ngx_http_headers_conf_t *conf; |
106 | 138 |
107 if (r != r->main | 139 conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module); |
140 | |
141 if ((conf->expires == NGX_HTTP_EXPIRES_OFF && conf->headers == NULL) | |
142 || r != r->main | |
108 || (r->headers_out.status != NGX_HTTP_OK | 143 || (r->headers_out.status != NGX_HTTP_OK |
109 && r->headers_out.status != NGX_HTTP_NO_CONTENT | 144 && r->headers_out.status != NGX_HTTP_NO_CONTENT |
110 && r->headers_out.status != NGX_HTTP_MOVED_PERMANENTLY | 145 && r->headers_out.status != NGX_HTTP_MOVED_PERMANENTLY |
111 && r->headers_out.status != NGX_HTTP_MOVED_TEMPORARILY | 146 && r->headers_out.status != NGX_HTTP_MOVED_TEMPORARILY |
112 && r->headers_out.status != NGX_HTTP_NOT_MODIFIED)) | 147 && r->headers_out.status != NGX_HTTP_NOT_MODIFIED)) |
113 { | 148 { |
114 return ngx_http_next_header_filter(r); | 149 return ngx_http_next_header_filter(r); |
115 } | 150 } |
116 | 151 |
117 conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module); | |
118 | |
119 if (conf->expires != NGX_HTTP_EXPIRES_OFF) { | 152 if (conf->expires != NGX_HTTP_EXPIRES_OFF) { |
120 | 153 if (ngx_http_set_expires(r, conf) != NGX_OK) { |
121 expires = r->headers_out.expires; | 154 return NGX_ERROR; |
122 | 155 } |
123 if (expires == NULL) { | 156 } |
124 | 157 |
125 expires = ngx_list_push(&r->headers_out.headers); | 158 if (conf->headers) { |
126 if (expires == NULL) { | 159 h = conf->headers->elts; |
160 for (i = 0; i < conf->headers->nelts; i++) { | |
161 | |
162 if (h[i].lengths == NULL) { | |
163 value = h[i].value.value; | |
164 | |
165 } else { | |
166 if (ngx_http_script_run(r, &value, h[i].lengths->elts, 0, | |
167 h[i].values->elts) | |
168 == NULL) | |
169 { | |
170 return NGX_ERROR; | |
171 } | |
172 } | |
173 | |
174 if (h[i].handler(r, &h[i], &value) != NGX_OK) { | |
127 return NGX_ERROR; | 175 return NGX_ERROR; |
128 } | 176 } |
129 | 177 } |
130 r->headers_out.expires = expires; | 178 } |
131 | 179 |
132 expires->hash = 1; | 180 return ngx_http_next_header_filter(r); |
133 expires->key.len = sizeof("Expires") - 1; | 181 } |
134 expires->key.data = (u_char *) "Expires"; | 182 |
135 } | 183 |
136 | 184 static ngx_int_t |
137 len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT"); | 185 ngx_http_set_expires(ngx_http_request_t *r, ngx_http_headers_conf_t *conf) |
138 expires->value.len = len - 1; | 186 { |
139 | 187 size_t len; |
140 ccp = r->headers_out.cache_control.elts; | 188 ngx_uint_t i; |
141 | 189 ngx_table_elt_t *expires, *cc, **ccp; |
142 if (ccp == NULL) { | 190 |
143 | 191 expires = r->headers_out.expires; |
144 if (ngx_array_init(&r->headers_out.cache_control, r->pool, | 192 |
145 1, sizeof(ngx_table_elt_t *)) | 193 if (expires == NULL) { |
146 != NGX_OK) | 194 |
147 { | 195 expires = ngx_list_push(&r->headers_out.headers); |
148 return NGX_ERROR; | 196 if (expires == NULL) { |
149 } | 197 return NGX_ERROR; |
150 | 198 } |
151 ccp = ngx_array_push(&r->headers_out.cache_control); | 199 |
152 if (ccp == NULL) { | 200 r->headers_out.expires = expires; |
153 return NGX_ERROR; | 201 |
154 } | 202 expires->hash = 1; |
155 | 203 expires->key.len = sizeof("Expires") - 1; |
156 cc = ngx_list_push(&r->headers_out.headers); | 204 expires->key.data = (u_char *) "Expires"; |
157 if (cc == NULL) { | 205 } |
158 return NGX_ERROR; | 206 |
159 } | 207 len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT"); |
160 | 208 expires->value.len = len - 1; |
161 cc->hash = 1; | 209 |
162 cc->key.len = sizeof("Cache-Control") - 1; | 210 ccp = r->headers_out.cache_control.elts; |
163 cc->key.data = (u_char *) "Cache-Control"; | 211 |
164 | 212 if (ccp == NULL) { |
165 *ccp = cc; | 213 |
166 | 214 if (ngx_array_init(&r->headers_out.cache_control, r->pool, |
167 } else { | 215 1, sizeof(ngx_table_elt_t *)) |
168 for (i = 1; i < r->headers_out.cache_control.nelts; i++) { | 216 != NGX_OK) |
169 ccp[i]->hash = 0; | 217 { |
170 } | 218 return NGX_ERROR; |
171 | |
172 cc = ccp[0]; | |
173 } | |
174 | |
175 if (conf->expires == NGX_HTTP_EXPIRES_EPOCH) { | |
176 expires->value.data = (u_char *) "Thu, 01 Jan 1970 00:00:01 GMT"; | |
177 | |
178 cc->value.len = sizeof("no-cache") - 1; | |
179 cc->value.data = (u_char *) "no-cache"; | |
180 | |
181 } else if (conf->expires == NGX_HTTP_EXPIRES_MAX) { | |
182 expires->value.data = (u_char *) "Thu, 31 Dec 2037 23:55:55 GMT"; | |
183 | |
184 /* 10 years */ | |
185 cc->value.len = sizeof("max-age=315360000") - 1; | |
186 cc->value.data = (u_char *) "max-age=315360000"; | |
187 | |
188 } else { | |
189 expires->value.data = ngx_palloc(r->pool, len); | |
190 if (expires->value.data == NULL) { | |
191 return NGX_ERROR; | |
192 } | |
193 | |
194 if (conf->expires == 0) { | |
195 ngx_memcpy(expires->value.data, ngx_cached_http_time.data, | |
196 ngx_cached_http_time.len + 1); | |
197 | |
198 cc->value.len = sizeof("max-age=0") - 1; | |
199 cc->value.data = (u_char *) "max-age=0"; | |
200 | |
201 } else { | |
202 ngx_http_time(expires->value.data, ngx_time() + conf->expires); | |
203 | |
204 if (conf->expires < 0) { | |
205 cc->value.len = sizeof("no-cache") - 1; | |
206 cc->value.data = (u_char *) "no-cache"; | |
207 | |
208 } else { | |
209 cc->value.data = ngx_palloc(r->pool, sizeof("max-age=") | |
210 + NGX_TIME_T_LEN + 1); | |
211 if (cc->value.data == NULL) { | |
212 return NGX_ERROR; | |
213 } | |
214 | |
215 cc->value.len = ngx_sprintf(cc->value.data, "max-age=%T", | |
216 conf->expires) | |
217 - cc->value.data; | |
218 } | |
219 } | |
220 } | |
221 } | |
222 | |
223 if (conf->cache_control.len) { | |
224 | |
225 ccp = r->headers_out.cache_control.elts; | |
226 | |
227 if (ccp == NULL) { | |
228 | |
229 if (ngx_array_init(&r->headers_out.cache_control, r->pool, | |
230 1, sizeof(ngx_table_elt_t *)) | |
231 != NGX_OK) | |
232 { | |
233 return NGX_ERROR; | |
234 } | |
235 } | 219 } |
236 | 220 |
237 ccp = ngx_array_push(&r->headers_out.cache_control); | 221 ccp = ngx_array_push(&r->headers_out.cache_control); |
238 if (ccp == NULL) { | 222 if (ccp == NULL) { |
239 return NGX_ERROR; | 223 return NGX_ERROR; |
245 } | 229 } |
246 | 230 |
247 cc->hash = 1; | 231 cc->hash = 1; |
248 cc->key.len = sizeof("Cache-Control") - 1; | 232 cc->key.len = sizeof("Cache-Control") - 1; |
249 cc->key.data = (u_char *) "Cache-Control"; | 233 cc->key.data = (u_char *) "Cache-Control"; |
250 cc->value = conf->cache_control; | |
251 | 234 |
252 *ccp = cc; | 235 *ccp = cc; |
253 } | 236 |
254 | 237 } else { |
255 if (conf->headers) { | 238 for (i = 1; i < r->headers_out.cache_control.nelts; i++) { |
256 h = conf->headers->elts; | 239 ccp[i]->hash = 0; |
257 for (i = 0; i < conf->headers->nelts; i++) { | 240 } |
258 out = ngx_list_push(&r->headers_out.headers); | 241 |
259 if (out == NULL) { | 242 cc = ccp[0]; |
260 return NGX_ERROR; | 243 } |
261 } | 244 |
262 | 245 if (conf->expires == NGX_HTTP_EXPIRES_EPOCH) { |
263 out->hash = h[i].value.hash; | 246 expires->value.data = (u_char *) "Thu, 01 Jan 1970 00:00:01 GMT"; |
264 out->key = h[i].value.key; | 247 |
265 | 248 cc->value.len = sizeof("no-cache") - 1; |
266 if (h[i].lengths == NULL) { | 249 cc->value.data = (u_char *) "no-cache"; |
267 out->value = h[i].value.value; | 250 |
268 continue; | 251 return NGX_OK; |
269 } | 252 } |
270 | 253 |
271 if (ngx_http_script_run(r, &out->value, h[i].lengths->elts, 0, | 254 if (conf->expires == NGX_HTTP_EXPIRES_MAX) { |
272 h[i].values->elts) | 255 expires->value.data = (u_char *) "Thu, 31 Dec 2037 23:55:55 GMT"; |
273 == NULL) | 256 |
274 { | 257 /* 10 years */ |
275 return NGX_ERROR; | 258 cc->value.len = sizeof("max-age=315360000") - 1; |
276 } | 259 cc->value.data = (u_char *) "max-age=315360000"; |
277 } | 260 |
278 } | 261 return NGX_OK; |
279 | 262 } |
280 return ngx_http_next_header_filter(r); | 263 |
264 expires->value.data = ngx_palloc(r->pool, len); | |
265 if (expires->value.data == NULL) { | |
266 return NGX_ERROR; | |
267 } | |
268 | |
269 if (conf->expires == 0) { | |
270 ngx_memcpy(expires->value.data, ngx_cached_http_time.data, | |
271 ngx_cached_http_time.len + 1); | |
272 | |
273 cc->value.len = sizeof("max-age=0") - 1; | |
274 cc->value.data = (u_char *) "max-age=0"; | |
275 | |
276 return NGX_OK; | |
277 } | |
278 | |
279 ngx_http_time(expires->value.data, ngx_time() + conf->expires); | |
280 | |
281 if (conf->expires < 0) { | |
282 cc->value.len = sizeof("no-cache") - 1; | |
283 cc->value.data = (u_char *) "no-cache"; | |
284 | |
285 return NGX_OK; | |
286 } | |
287 | |
288 cc->value.data = ngx_palloc(r->pool, | |
289 sizeof("max-age=") + NGX_TIME_T_LEN + 1); | |
290 if (cc->value.data == NULL) { | |
291 return NGX_ERROR; | |
292 } | |
293 | |
294 cc->value.len = ngx_sprintf(cc->value.data, "max-age=%T", conf->expires) | |
295 - cc->value.data; | |
296 | |
297 return NGX_OK; | |
298 } | |
299 | |
300 | |
301 static ngx_int_t | |
302 ngx_http_add_header(ngx_http_request_t *r, ngx_http_header_val_t *hv, | |
303 ngx_str_t *value) | |
304 { | |
305 ngx_table_elt_t *h; | |
306 | |
307 h = ngx_list_push(&r->headers_out.headers); | |
308 if (h == NULL) { | |
309 return NGX_ERROR; | |
310 } | |
311 | |
312 h->hash = hv->value.hash; | |
313 h->key = hv->value.key; | |
314 h->value = *value; | |
315 | |
316 return NGX_OK; | |
317 } | |
318 | |
319 | |
320 static ngx_int_t | |
321 ngx_http_add_cache_control(ngx_http_request_t *r, ngx_http_header_val_t *hv, | |
322 ngx_str_t *value) | |
323 { | |
324 ngx_table_elt_t *cc, **ccp; | |
325 | |
326 ccp = r->headers_out.cache_control.elts; | |
327 | |
328 if (ccp == NULL) { | |
329 | |
330 if (ngx_array_init(&r->headers_out.cache_control, r->pool, | |
331 1, sizeof(ngx_table_elt_t *)) | |
332 != NGX_OK) | |
333 { | |
334 return NGX_ERROR; | |
335 } | |
336 } | |
337 | |
338 ccp = ngx_array_push(&r->headers_out.cache_control); | |
339 if (ccp == NULL) { | |
340 return NGX_ERROR; | |
341 } | |
342 | |
343 cc = ngx_list_push(&r->headers_out.headers); | |
344 if (cc == NULL) { | |
345 return NGX_ERROR; | |
346 } | |
347 | |
348 cc->hash = 1; | |
349 cc->key.len = sizeof("Cache-Control") - 1; | |
350 cc->key.data = (u_char *) "Cache-Control"; | |
351 cc->value = *value; | |
352 | |
353 *ccp = cc; | |
354 | |
355 return NGX_OK; | |
356 } | |
357 | |
358 | |
359 static ngx_int_t | |
360 ngx_http_set_last_modified(ngx_http_request_t *r, ngx_http_header_val_t *hv, | |
361 ngx_str_t *value) | |
362 { | |
363 ngx_table_elt_t *h, **old; | |
364 | |
365 if (hv->offset) { | |
366 old = (ngx_table_elt_t **) ((char *) &r->headers_out + hv->offset); | |
367 | |
368 } else { | |
369 old = NULL; | |
370 } | |
371 | |
372 if (old == NULL || *old == NULL) { | |
373 h = ngx_list_push(&r->headers_out.headers); | |
374 if (h == NULL) { | |
375 return NGX_ERROR; | |
376 } | |
377 | |
378 } else { | |
379 h = *old; | |
380 } | |
381 | |
382 h->hash = hv->value.hash; | |
383 h->key = hv->value.key; | |
384 h->value = *value; | |
385 | |
386 r->headers_out.last_modified_time = -1; | |
387 | |
388 return NGX_OK; | |
281 } | 389 } |
282 | 390 |
283 | 391 |
284 static void * | 392 static void * |
285 ngx_http_headers_create_conf(ngx_conf_t *cf) | 393 ngx_http_headers_create_conf(ngx_conf_t *cf) |
292 } | 400 } |
293 | 401 |
294 /* | 402 /* |
295 * set by ngx_pcalloc(): | 403 * set by ngx_pcalloc(): |
296 * | 404 * |
297 * conf->cache_control.len = 0; | |
298 * conf->cache_control.data = NULL; | |
299 * conf->headers = NULL; | 405 * conf->headers = NULL; |
300 */ | 406 */ |
301 | 407 |
302 conf->expires = NGX_HTTP_EXPIRES_UNSET; | 408 conf->expires = NGX_HTTP_EXPIRES_UNSET; |
303 | 409 |
311 ngx_http_headers_conf_t *prev = parent; | 417 ngx_http_headers_conf_t *prev = parent; |
312 ngx_http_headers_conf_t *conf = child; | 418 ngx_http_headers_conf_t *conf = child; |
313 | 419 |
314 if (conf->expires == NGX_HTTP_EXPIRES_UNSET) { | 420 if (conf->expires == NGX_HTTP_EXPIRES_UNSET) { |
315 conf->expires = (prev->expires == NGX_HTTP_EXPIRES_UNSET) ? | 421 conf->expires = (prev->expires == NGX_HTTP_EXPIRES_UNSET) ? |
316 NGX_HTTP_EXPIRES_OFF : prev->expires; | 422 NGX_HTTP_EXPIRES_OFF : prev->expires; |
317 } | |
318 | |
319 if (conf->cache_control.data == NULL) { | |
320 conf->cache_control = prev->cache_control; | |
321 } | 423 } |
322 | 424 |
323 if (conf->headers == NULL) { | 425 if (conf->headers == NULL) { |
324 conf->headers = prev->headers; | 426 conf->headers = prev->headers; |
325 } | 427 } |
404 { | 506 { |
405 ngx_http_headers_conf_t *hcf = conf; | 507 ngx_http_headers_conf_t *hcf = conf; |
406 | 508 |
407 ngx_int_t n; | 509 ngx_int_t n; |
408 ngx_str_t *value; | 510 ngx_str_t *value; |
511 ngx_uint_t i; | |
409 ngx_http_header_val_t *h; | 512 ngx_http_header_val_t *h; |
513 ngx_http_set_header_t *sh; | |
410 ngx_http_script_compile_t sc; | 514 ngx_http_script_compile_t sc; |
411 | 515 |
412 value = cf->args->elts; | 516 value = cf->args->elts; |
413 | |
414 if (ngx_strcasecmp(value[1].data, (u_char *) "cache-control") == 0) { | |
415 hcf->cache_control = value[2]; | |
416 return NGX_CONF_OK; | |
417 } | |
418 | 517 |
419 if (hcf->headers == NULL) { | 518 if (hcf->headers == NULL) { |
420 hcf->headers = ngx_array_create(cf->pool, 1, | 519 hcf->headers = ngx_array_create(cf->pool, 1, |
421 sizeof(ngx_http_header_val_t)); | 520 sizeof(ngx_http_header_val_t)); |
422 if (hcf->headers == NULL) { | 521 if (hcf->headers == NULL) { |
430 } | 529 } |
431 | 530 |
432 h->value.hash = 1; | 531 h->value.hash = 1; |
433 h->value.key = value[1]; | 532 h->value.key = value[1]; |
434 h->value.value = value[2]; | 533 h->value.value = value[2]; |
534 h->offset = 0; | |
535 h->handler = ngx_http_add_header; | |
435 h->lengths = NULL; | 536 h->lengths = NULL; |
436 h->values = NULL; | 537 h->values = NULL; |
538 | |
539 sh = ngx_http_set_headers; | |
540 for (i = 0; sh[i].name.len; i++) { | |
541 if (ngx_strcasecmp(value[1].data, sh[i].name.data) != 0) { | |
542 continue; | |
543 } | |
544 | |
545 h->offset = sh[i].offset; | |
546 h->handler = sh[i].handler; | |
547 break; | |
548 } | |
437 | 549 |
438 n = ngx_http_script_variables_count(&value[2]); | 550 n = ngx_http_script_variables_count(&value[2]); |
439 | 551 |
440 if (n == 0) { | 552 if (n == 0) { |
441 return NGX_CONF_OK; | 553 return NGX_CONF_OK; |