2
* Copyright (c) 2009-2011, FRiCKLE <info@frickle.com>
3
* Copyright (c) 2009-2011, Piotr Sikora <piotr.sikora@frickle.com>
6
* This project was fully funded by yo.se.
8
* Redistribution and use in source and binary forms, with or without
9
* modification, are permitted provided that the following conditions
11
* 1. Redistributions of source code must retain the above copyright
12
* notice, this list of conditions and the following disclaimer.
13
* 2. Redistributions in binary form must reproduce the above copyright
14
* notice, this list of conditions and the following disclaimer in the
15
* documentation and/or other materials provided with the distribution.
17
* THIS SOFTWARE IS PROVIDED BY FRiCKLE PIOTR SIKORA AND CONTRIBUTORS
18
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL FRiCKLE PIOTR
21
* SIKORA OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
#include <ngx_config.h>
38
# if (NGX_HTTP_FASTCGI)
39
char *ngx_http_fastcgi_cache_purge_conf(ngx_conf_t *cf,
40
ngx_command_t *cmd, void *conf);
41
ngx_int_t ngx_http_fastcgi_cache_purge_handler(ngx_http_request_t *r);
42
# endif /* NGX_HTTP_FASTCGI */
45
char *ngx_http_proxy_cache_purge_conf(ngx_conf_t *cf,
46
ngx_command_t *cmd, void *conf);
47
ngx_int_t ngx_http_proxy_cache_purge_handler(ngx_http_request_t *r);
48
# endif /* NGX_HTTP_PROXY */
51
char *ngx_http_scgi_cache_purge_conf(ngx_conf_t *cf,
52
ngx_command_t *cmd, void *conf);
53
ngx_int_t ngx_http_scgi_cache_purge_handler(ngx_http_request_t *r);
54
# endif /* NGX_HTTP_SCGI */
57
char *ngx_http_uwsgi_cache_purge_conf(ngx_conf_t *cf,
58
ngx_command_t *cmd, void *conf);
59
ngx_int_t ngx_http_uwsgi_cache_purge_handler(ngx_http_request_t *r);
60
# endif /* NGX_HTTP_UWSGI */
62
ngx_int_t ngx_http_cache_purge_init(ngx_http_request_t *r,
63
ngx_http_file_cache_t *cache, ngx_http_complex_value_t *cache_key);
64
void ngx_http_cache_purge_handler(ngx_http_request_t *r);
66
ngx_int_t ngx_http_file_cache_purge(ngx_http_request_t *r);
68
static ngx_command_t ngx_http_cache_purge_module_commands[] = {
70
# if (NGX_HTTP_FASTCGI)
71
{ ngx_string("fastcgi_cache_purge"),
72
NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
73
ngx_http_fastcgi_cache_purge_conf,
74
NGX_HTTP_LOC_CONF_OFFSET,
77
# endif /* NGX_HTTP_FASTCGI */
80
{ ngx_string("proxy_cache_purge"),
81
NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
82
ngx_http_proxy_cache_purge_conf,
83
NGX_HTTP_LOC_CONF_OFFSET,
86
# endif /* NGX_HTTP_PROXY */
89
{ ngx_string("scgi_cache_purge"),
90
NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
91
ngx_http_scgi_cache_purge_conf,
92
NGX_HTTP_LOC_CONF_OFFSET,
95
# endif /* NGX_HTTP_SCGI */
98
{ ngx_string("uwsgi_cache_purge"),
99
NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
100
ngx_http_uwsgi_cache_purge_conf,
101
NGX_HTTP_LOC_CONF_OFFSET,
104
# endif /* NGX_HTTP_UWSGI */
109
static ngx_http_module_t ngx_http_cache_purge_module_ctx = {
110
NULL, /* preconfiguration */
111
NULL, /* postconfiguration */
113
NULL, /* create main configuration */
114
NULL, /* init main configuration */
116
NULL, /* create server configuration */
117
NULL, /* merge server configuration */
119
NULL, /* create location configuration */
120
NULL /* merge location configuration */
123
ngx_module_t ngx_http_cache_purge_module = {
125
&ngx_http_cache_purge_module_ctx, /* module context */
126
ngx_http_cache_purge_module_commands, /* module directives */
127
NGX_HTTP_MODULE, /* module type */
128
NULL, /* init master */
129
NULL, /* init module */
130
NULL, /* init process */
131
NULL, /* init thread */
132
NULL, /* exit thread */
133
NULL, /* exit process */
134
NULL, /* exit master */
135
NGX_MODULE_V1_PADDING
138
static char ngx_http_cache_purge_success_page_top[] =
140
"<head><title>Successful purge</title></head>" CRLF
141
"<body bgcolor=\"white\">" CRLF
142
"<center><h1>Successful purge</h1>" CRLF
145
static char ngx_http_cache_purge_success_page_tail[] =
146
CRLF "</center>" CRLF
147
"<hr><center>" NGINX_VER "</center>" CRLF
152
# if (NGX_HTTP_FASTCGI)
153
extern ngx_module_t ngx_http_fastcgi_module;
156
ngx_http_upstream_conf_t upstream;
160
ngx_array_t *flushes;
161
ngx_array_t *params_len;
163
ngx_array_t *params_source;
164
ngx_array_t *catch_stderr;
166
ngx_array_t *fastcgi_lengths;
167
ngx_array_t *fastcgi_values;
169
# if defined(nginx_version) && (nginx_version >= 8040)
170
ngx_hash_t headers_hash;
171
ngx_uint_t header_params;
172
# endif /* nginx_version >= 8040 */
174
ngx_http_complex_value_t cache_key;
177
ngx_regex_t *split_regex;
178
ngx_str_t split_name;
179
# endif /* NGX_PCRE */
180
} ngx_http_fastcgi_loc_conf_t;
183
ngx_http_fastcgi_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd,
186
ngx_http_compile_complex_value_t ccv;
187
ngx_http_core_loc_conf_t *clcf;
188
ngx_http_fastcgi_loc_conf_t *flcf;
191
flcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_fastcgi_module);
193
/* check for duplicates / collisions */
194
if (flcf->upstream.cache != NGX_CONF_UNSET_PTR
195
&& flcf->upstream.cache != NULL)
197
return "is either duplicate or collides with \"fastcgi_cache\"";
200
if (flcf->upstream.upstream || flcf->fastcgi_lengths) {
201
return "is incompatible with \"fastcgi_pass\"";
204
if (flcf->upstream.store > 0 || flcf->upstream.store_lengths) {
205
return "is incompatible with \"fastcgi_store\"";
208
value = cf->args->elts;
210
/* set fastcgi_cache part */
211
flcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0,
212
&ngx_http_fastcgi_module);
213
if (flcf->upstream.cache == NULL) {
214
return NGX_CONF_ERROR;
217
/* set fastcgi_cache_key part */
218
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
221
ccv.value = &value[2];
222
ccv.complex_value = &flcf->cache_key;
224
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
225
return NGX_CONF_ERROR;
229
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
231
clcf->handler = ngx_http_fastcgi_cache_purge_handler;
237
ngx_http_fastcgi_cache_purge_handler(ngx_http_request_t *r)
239
ngx_http_fastcgi_loc_conf_t *flcf;
241
flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
243
if (ngx_http_cache_purge_init(r, flcf->upstream.cache->data,
247
return NGX_HTTP_INTERNAL_SERVER_ERROR;
250
# if defined(nginx_version) && (nginx_version >= 8011)
254
ngx_http_cache_purge_handler(r);
258
# endif /* NGX_HTTP_FASTCGI */
260
# if (NGX_HTTP_PROXY)
261
extern ngx_module_t ngx_http_proxy_module;
266
ngx_str_t host_header;
269
} ngx_http_proxy_vars_t;
272
ngx_http_upstream_conf_t upstream;
274
ngx_array_t *flushes;
275
ngx_array_t *body_set_len;
276
ngx_array_t *body_set;
277
ngx_array_t *headers_set_len;
278
ngx_array_t *headers_set;
279
ngx_hash_t headers_set_hash;
281
ngx_array_t *headers_source;
282
# if defined(nginx_version) && (nginx_version < 8040)
283
ngx_array_t *headers_names;
284
# endif /* nginx_version < 8040 */
286
ngx_array_t *proxy_lengths;
287
ngx_array_t *proxy_values;
289
ngx_array_t *redirects;
291
ngx_str_t body_source;
297
ngx_http_complex_value_t cache_key;
299
ngx_http_proxy_vars_t vars;
303
ngx_uint_t headers_hash_max_size;
304
ngx_uint_t headers_hash_bucket_size;
305
} ngx_http_proxy_loc_conf_t;
308
ngx_http_proxy_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
310
ngx_http_compile_complex_value_t ccv;
311
ngx_http_core_loc_conf_t *clcf;
312
ngx_http_proxy_loc_conf_t *plcf;
315
plcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_proxy_module);
317
/* check for duplicates / collisions */
318
if (plcf->upstream.cache != NGX_CONF_UNSET_PTR
319
&& plcf->upstream.cache != NULL)
321
return "is either duplicate or collides with \"proxy_cache\"";
324
if (plcf->upstream.upstream || plcf->proxy_lengths) {
325
return "is incompatible with \"proxy_pass\"";
328
if (plcf->upstream.store > 0 || plcf->upstream.store_lengths) {
329
return "is incompatible with \"proxy_store\"";
332
value = cf->args->elts;
334
/* set proxy_cache part */
335
plcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0,
336
&ngx_http_proxy_module);
337
if (plcf->upstream.cache == NULL) {
338
return NGX_CONF_ERROR;
341
/* set proxy_cache_key part */
342
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
345
ccv.value = &value[2];
346
ccv.complex_value = &plcf->cache_key;
348
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
349
return NGX_CONF_ERROR;
353
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
355
clcf->handler = ngx_http_proxy_cache_purge_handler;
361
ngx_http_proxy_cache_purge_handler(ngx_http_request_t *r)
363
ngx_http_proxy_loc_conf_t *plcf;
365
plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
367
if (ngx_http_cache_purge_init(r, plcf->upstream.cache->data,
371
return NGX_HTTP_INTERNAL_SERVER_ERROR;
374
# if defined(nginx_version) && (nginx_version >= 8011)
378
ngx_http_cache_purge_handler(r);
382
# endif /* NGX_HTTP_PROXY */
385
extern ngx_module_t ngx_http_scgi_module;
388
ngx_http_upstream_conf_t upstream;
390
ngx_array_t *flushes;
391
ngx_array_t *params_len;
393
ngx_array_t *params_source;
395
ngx_hash_t headers_hash;
396
ngx_uint_t header_params;
398
ngx_array_t *scgi_lengths;
399
ngx_array_t *scgi_values;
401
ngx_http_complex_value_t cache_key;
402
} ngx_http_scgi_loc_conf_t;
405
ngx_http_scgi_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
407
ngx_http_compile_complex_value_t ccv;
408
ngx_http_core_loc_conf_t *clcf;
409
ngx_http_scgi_loc_conf_t *slcf;
412
slcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_scgi_module);
414
/* check for duplicates / collisions */
415
if (slcf->upstream.cache != NGX_CONF_UNSET_PTR
416
&& slcf->upstream.cache != NULL)
418
return "is either duplicate or collides with \"scgi_cache\"";
421
if (slcf->upstream.upstream || slcf->scgi_lengths) {
422
return "is incompatible with \"scgi_pass\"";
425
if (slcf->upstream.store > 0 || slcf->upstream.store_lengths) {
426
return "is incompatible with \"scgi_store\"";
429
value = cf->args->elts;
431
/* set scgi_cache part */
432
slcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0,
433
&ngx_http_scgi_module);
434
if (slcf->upstream.cache == NULL) {
435
return NGX_CONF_ERROR;
438
/* set scgi_cache_key part */
439
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
442
ccv.value = &value[2];
443
ccv.complex_value = &slcf->cache_key;
445
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
446
return NGX_CONF_ERROR;
450
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
452
clcf->handler = ngx_http_scgi_cache_purge_handler;
458
ngx_http_scgi_cache_purge_handler(ngx_http_request_t *r)
460
ngx_http_scgi_loc_conf_t *slcf;
462
slcf = ngx_http_get_module_loc_conf(r, ngx_http_scgi_module);
464
if (ngx_http_cache_purge_init(r, slcf->upstream.cache->data,
468
return NGX_HTTP_INTERNAL_SERVER_ERROR;
471
# if defined(nginx_version) && (nginx_version >= 8011)
475
ngx_http_cache_purge_handler(r);
479
# endif /* NGX_HTTP_SCGI */
481
# if (NGX_HTTP_UWSGI)
482
extern ngx_module_t ngx_http_uwsgi_module;
485
ngx_http_upstream_conf_t upstream;
487
ngx_array_t *flushes;
488
ngx_array_t *params_len;
490
ngx_array_t *params_source;
492
ngx_hash_t headers_hash;
493
ngx_uint_t header_params;
495
ngx_array_t *uwsgi_lengths;
496
ngx_array_t *uwsgi_values;
498
ngx_http_complex_value_t cache_key;
500
ngx_str_t uwsgi_string;
502
ngx_uint_t modifier1;
503
ngx_uint_t modifier2;
504
} ngx_http_uwsgi_loc_conf_t;
507
ngx_http_uwsgi_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
509
ngx_http_compile_complex_value_t ccv;
510
ngx_http_core_loc_conf_t *clcf;
511
ngx_http_uwsgi_loc_conf_t *ulcf;
514
ulcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_uwsgi_module);
516
/* check for duplicates / collisions */
517
if (ulcf->upstream.cache != NGX_CONF_UNSET_PTR
518
&& ulcf->upstream.cache != NULL)
520
return "is either duplicate or collides with \"uwsgi_cache\"";
523
if (ulcf->upstream.upstream || ulcf->uwsgi_lengths) {
524
return "is incompatible with \"uwsgi_pass\"";
527
if (ulcf->upstream.store > 0 || ulcf->upstream.store_lengths) {
528
return "is incompatible with \"uwsgi_store\"";
531
value = cf->args->elts;
533
/* set uwsgi_cache part */
534
ulcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0,
535
&ngx_http_uwsgi_module);
536
if (ulcf->upstream.cache == NULL) {
537
return NGX_CONF_ERROR;
540
/* set uwsgi_cache_key part */
541
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
544
ccv.value = &value[2];
545
ccv.complex_value = &ulcf->cache_key;
547
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
548
return NGX_CONF_ERROR;
552
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
554
clcf->handler = ngx_http_uwsgi_cache_purge_handler;
560
ngx_http_uwsgi_cache_purge_handler(ngx_http_request_t *r)
562
ngx_http_uwsgi_loc_conf_t *ulcf;
564
ulcf = ngx_http_get_module_loc_conf(r, ngx_http_uwsgi_module);
566
if (ngx_http_cache_purge_init(r, ulcf->upstream.cache->data,
570
return NGX_HTTP_INTERNAL_SERVER_ERROR;
573
# if defined(nginx_version) && (nginx_version >= 8011)
577
ngx_http_cache_purge_handler(r);
581
# endif /* NGX_HTTP_UWSGI */
584
ngx_http_cache_purge_send_response(ngx_http_request_t *r)
592
key = r->cache->keys.elts;
594
len = sizeof(ngx_http_cache_purge_success_page_top) - 1
595
+ sizeof(ngx_http_cache_purge_success_page_tail) - 1
596
+ sizeof("<br>Key : ") - 1 + sizeof(CRLF "<br>Path: ") - 1
597
+ key[0].len + r->cache->file.name.len;
599
r->headers_out.content_type.len = sizeof("text/html") - 1;
600
r->headers_out.content_type.data = (u_char *) "text/html";
601
r->headers_out.status = NGX_HTTP_OK;
602
r->headers_out.content_length_n = len;
604
if (r->method == NGX_HTTP_HEAD) {
605
rc = ngx_http_send_header(r);
606
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
611
b = ngx_create_temp_buf(r->pool, len);
613
return NGX_HTTP_INTERNAL_SERVER_ERROR;
619
b->last = ngx_cpymem(b->last, ngx_http_cache_purge_success_page_top,
620
sizeof(ngx_http_cache_purge_success_page_top) - 1);
621
b->last = ngx_cpymem(b->last, "<br>Key : ", sizeof("<br>Key : ") - 1);
622
b->last = ngx_cpymem(b->last, key[0].data, key[0].len);
623
b->last = ngx_cpymem(b->last, CRLF "<br>Path: ",
624
sizeof(CRLF "<br>Path: ") - 1);
625
b->last = ngx_cpymem(b->last, r->cache->file.name.data,
626
r->cache->file.name.len);
627
b->last = ngx_cpymem(b->last, ngx_http_cache_purge_success_page_tail,
628
sizeof(ngx_http_cache_purge_success_page_tail) - 1);
631
rc = ngx_http_send_header(r);
632
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
636
return ngx_http_output_filter(r, &out);
640
ngx_http_cache_purge_init(ngx_http_request_t *r, ngx_http_file_cache_t *cache,
641
ngx_http_complex_value_t *cache_key)
647
rc = ngx_http_discard_request_body(r);
652
c = ngx_pcalloc(r->pool, sizeof(ngx_http_cache_t));
657
rc = ngx_array_init(&c->keys, r->pool, 1, sizeof(ngx_str_t));
662
key = ngx_array_push(&c->keys);
667
rc = ngx_http_complex_value(r, cache_key, key);
673
c->body_start = ngx_pagesize;
674
c->file_cache = cache;
675
c->file.log = r->connection->log;
677
ngx_http_file_cache_create_key(r);
683
ngx_http_cache_purge_handler(ngx_http_request_t *r)
687
# if (NGX_HAVE_FILE_AIO)
693
rc = ngx_http_file_cache_purge(r);
695
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
696
"http file cache purge: %i, \"%s\"",
697
rc, r->cache->file.name.data);
701
r->write_event_handler = ngx_http_request_empty_handler;
702
ngx_http_finalize_request(r, ngx_http_cache_purge_send_response(r));
705
ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);
707
# if (NGX_HAVE_FILE_AIO)
709
r->write_event_handler = ngx_http_cache_purge_handler;
713
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
718
ngx_http_file_cache_purge(ngx_http_request_t *r)
720
ngx_http_file_cache_t *cache;
723
switch (ngx_http_file_cache_open(r)) {
725
case NGX_HTTP_CACHE_STALE:
726
# if defined(nginx_version) \
727
&& ((nginx_version >= 8001) \
728
|| ((nginx_version < 8000) && (nginx_version >= 7060)))
729
case NGX_HTTP_CACHE_UPDATING:
734
# if (NGX_HAVE_FILE_AIO)
743
cache = c->file_cache;
746
* delete file from disk but *keep* in-memory node,
747
* because other requests might still point to it.
750
ngx_shmtx_lock(&cache->shpool->mutex);
752
if (!c->node->exists) {
753
/* race between concurrent purges, backoff */
754
ngx_shmtx_unlock(&cache->shpool->mutex);
758
# if defined(nginx_version) && (nginx_version >= 1000001)
759
cache->sh->size -= c->node->fs_size;
760
c->node->fs_size = 0;
762
cache->sh->size -= (c->node->length + cache->bsize - 1) / cache->bsize;
767
# if defined(nginx_version) \
768
&& ((nginx_version >= 8001) \
769
|| ((nginx_version < 8000) && (nginx_version >= 7060)))
770
c->node->updating = 0;
773
ngx_shmtx_unlock(&cache->shpool->mutex);
775
if (ngx_delete_file(c->file.name.data) == NGX_FILE_ERROR) {
776
/* entry in error log is enough, don't notice client */
777
ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
778
ngx_delete_file_n " \"%s\" failed", c->file.name.data);
781
/* file deleted from cache */
785
#else /* !NGX_HTTP_CACHE */
787
static ngx_http_module_t ngx_http_cache_purge_module_ctx = {
788
NULL, /* preconfiguration */
789
NULL, /* postconfiguration */
791
NULL, /* create main configuration */
792
NULL, /* init main configuration */
794
NULL, /* create server configuration */
795
NULL, /* merge server configuration */
797
NULL, /* create location configuration */
798
NULL, /* merge location configuration */
801
ngx_module_t ngx_http_cache_purge_module = {
803
&ngx_http_cache_purge_module_ctx, /* module context */
804
NULL, /* module directives */
805
NGX_HTTP_MODULE, /* module type */
806
NULL, /* init master */
807
NULL, /* init module */
808
NULL, /* init process */
809
NULL, /* init thread */
810
NULL, /* exit thread */
811
NULL, /* exit process */
812
NULL, /* exit master */
813
NGX_MODULE_V1_PADDING
816
#endif /* NGX_HTTP_CACHE */