pax_global_header00006660000000000000000000000064115600307730014514gustar00rootroot0000000000000052 comment=af6e0c73c81a79556cdd7153e347edc242a2433e cache_purge/000075500000000000000000000000001156003077300133235ustar00rootroot00000000000000cache_purge/CHANGES000064400000000000000000000013641156003077300143220ustar00rootroot000000000000002011-05-03 VERSION 1.3 * Fix compatibility with nginx-1.0.1. Reported by Sergey A. Osokin and Markus Linnala. 2010-08-29 * Fix compatibility with nginx-0.8.0 and versions older than nginx-0.7.60. 2010-08-11 VERSION 1.2 * Fix various build scenarios with disabled upstream modules. Reported by Johan Bergstroem. * Add ability to purge content from SCGI's cache. Requested by Johan Bergstroem. 2010-06-08 VERSION 1.1 * Fix compatibility with nginx-0.8.40+. * Add ability to purge content from uWSGI's cache. 2010-01-10 VERSION 1.0 * Initial module release. 2009-11-17 * Fix patch compatibility with nginx-0.8.11+. Reported by Bing Ran. 2009-08-11 * Initial patch release. cache_purge/LICENSE000064400000000000000000000026201156003077300143300ustar00rootroot00000000000000Copyright (c) 2009-2011, FRiCKLE Copyright (c) 2009-2011, Piotr Sikora All rights reserved. This project was fully funded by yo.se. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. cache_purge/README.md000064400000000000000000000071621156003077300146100ustar00rootroot00000000000000About ===== `ngx_cache_purge` is `nginx` module which adds ability to purge content from `FastCGI`, `proxy`, `SCGI` and `uWSGI` caches. Sponsors ======== Work on the original patch was fully funded by [yo.se](http://yo.se). Status ====== This module is production-ready and it's compatible with following nginx releases: - 0.7.x (tested with 0.7.60 to 0.7.68), - 0.8.x (tested with 0.8.0 to 0.8.54), - 0.9.x (tested with 0.9.0 to 0.9.7), - 1.0.x (tested with 1.0.0 to 1.0.1). Configuration directives ======================== fastcgi_cache_purge ------------------- * **syntax**: `fastcgi_cache_purge zone_name key` * **default**: `none` * **context**: `location` Sets area and key used for purging selected pages from `FastCGI`'s cache. proxy_cache_purge ----------------- * **syntax**: `proxy_cache_purge zone_name key` * **default**: `none` * **context**: `location` Sets area and key used for purging selected pages from `proxy`'s cache. scgi_cache_purge ---------------- * **syntax**: `scgi_cache_purge zone_name key` * **default**: `none` * **context**: `location` Sets area and key used for purging selected pages from `SCGI`'s cache. uwsgi_cache_purge ----------------- * **syntax**: `uwsgi_cache_purge zone_name key` * **default**: `none` * **context**: `location` Sets area and key used for purging selected pages from `uWSGI`'s cache. Sample configuration ==================== http { proxy_cache_path /tmp/cache keys_zone=tmpcache:10m; server { location / { proxy_pass http://127.0.0.1:8000; proxy_cache tmpcache; proxy_cache_key $uri$is_args$args; } location ~ /purge(/.*) { allow 127.0.0.1; deny all; proxy_cache_purge tmpcache $1$is_args$args; } } } Testing ======= `ngx_cache_purge` comes with complete test suite based on [Test::Nginx](http://github.com/agentzh/test-nginx). You can test it by running: `$ prove` License ======= Copyright (c) 2009-2011, FRiCKLE Copyright (c) 2009-2011, Piotr Sikora All rights reserved. This project was fully funded by yo.se. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. See also ======== - [ngx_slowfs_cache](http://github.com/FRiCKLE/ngx_slowfs_cache). cache_purge/TODO.md000064400000000000000000000015131156003077300144120ustar00rootroot00000000000000Features that sooner or later will be added to `ngx_cache_purge`: * Add support for alternative in-location cache purges using specified request method, with following configuration syntax: location / { proxy_pass http://127.0.0.1:8000; proxy_cache tmpcache; proxy_cache_key $uri$is_args$args; proxy_cache_purge DELETE from 127.0.0.1; } This will allow for purges using: curl -X DELETE http://example.com/logo.jpg instead of: curl http://example.com/purge/logo.jpg Features that __will not__ be added to `ngx_cache_purge`: * Support for prefixed purges (`/purge/images/*`). Reason: Impossible with current cache implementation. * Support for wildcard/regex purges (`/purge/*.jpg`). Reason: Impossible with current cache implementation. cache_purge/config000064400000000000000000000006711156003077300145170ustar00rootroot00000000000000if [ "$HTTP_FASTCGI" = "YES" ]; then have=NGX_HTTP_FASTCGI . auto/have fi if [ "$HTTP_SCGI" = "YES" ]; then have=NGX_HTTP_SCGI . auto/have fi if [ "$HTTP_UWSGI" = "YES" ]; then have=NGX_HTTP_UWSGI . auto/have fi ngx_addon_name=ngx_http_cache_purge_module HTTP_MODULES="$HTTP_MODULES ngx_http_cache_purge_module" NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_cache_purge_module.c" have=NGX_CACHE_PURGE_MODULE . auto/have cache_purge/ngx_cache_purge_module.c000064400000000000000000000545461156003077300201730ustar00rootroot00000000000000/* * Copyright (c) 2009-2011, FRiCKLE * Copyright (c) 2009-2011, Piotr Sikora * All rights reserved. * * This project was fully funded by yo.se. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY FRiCKLE PIOTR SIKORA AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL FRiCKLE PIOTR * SIKORA OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #if (NGX_HTTP_CACHE) # if (NGX_HTTP_FASTCGI) char *ngx_http_fastcgi_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); ngx_int_t ngx_http_fastcgi_cache_purge_handler(ngx_http_request_t *r); # endif /* NGX_HTTP_FASTCGI */ # if (NGX_HTTP_PROXY) char *ngx_http_proxy_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); ngx_int_t ngx_http_proxy_cache_purge_handler(ngx_http_request_t *r); # endif /* NGX_HTTP_PROXY */ # if (NGX_HTTP_SCGI) char *ngx_http_scgi_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); ngx_int_t ngx_http_scgi_cache_purge_handler(ngx_http_request_t *r); # endif /* NGX_HTTP_SCGI */ # if (NGX_HTTP_UWSGI) char *ngx_http_uwsgi_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); ngx_int_t ngx_http_uwsgi_cache_purge_handler(ngx_http_request_t *r); # endif /* NGX_HTTP_UWSGI */ ngx_int_t ngx_http_cache_purge_handler(ngx_http_request_t *r, ngx_http_file_cache_t *cache, ngx_http_complex_value_t *cache_key); ngx_int_t ngx_http_file_cache_purge(ngx_http_request_t *r, ngx_http_file_cache_t *cache, ngx_http_complex_value_t *cache_key); static ngx_command_t ngx_http_cache_purge_module_commands[] = { # if (NGX_HTTP_FASTCGI) { ngx_string("fastcgi_cache_purge"), NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, ngx_http_fastcgi_cache_purge_conf, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, # endif /* NGX_HTTP_FASTCGI */ # if (NGX_HTTP_PROXY) { ngx_string("proxy_cache_purge"), NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, ngx_http_proxy_cache_purge_conf, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, # endif /* NGX_HTTP_PROXY */ # if (NGX_HTTP_SCGI) { ngx_string("scgi_cache_purge"), NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, ngx_http_scgi_cache_purge_conf, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, # endif /* NGX_HTTP_SCGI */ # if (NGX_HTTP_UWSGI) { ngx_string("uwsgi_cache_purge"), NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, ngx_http_uwsgi_cache_purge_conf, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, # endif /* NGX_HTTP_UWSGI */ ngx_null_command }; static ngx_http_module_t ngx_http_cache_purge_module_ctx = { NULL, /* preconfiguration */ NULL, /* postconfiguration */ NULL, /* create main configuration */ NULL, /* init main configuration */ NULL, /* create server configuration */ NULL, /* merge server configuration */ NULL, /* create location configuration */ NULL /* merge location configuration */ }; ngx_module_t ngx_http_cache_purge_module = { NGX_MODULE_V1, &ngx_http_cache_purge_module_ctx, /* module context */ ngx_http_cache_purge_module_commands, /* module directives */ NGX_HTTP_MODULE, /* module type */ NULL, /* init master */ NULL, /* init module */ NULL, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ NULL, /* exit process */ NULL, /* exit master */ NGX_MODULE_V1_PADDING }; static char ngx_http_cache_purge_success_page_top[] = "" CRLF "Successful purge" CRLF "" CRLF "

Successful purge

" CRLF ; static char ngx_http_cache_purge_success_page_tail[] = CRLF "
" CRLF "
" NGINX_VER "
" CRLF "" CRLF "" CRLF ; # if (NGX_HTTP_FASTCGI) extern ngx_module_t ngx_http_fastcgi_module; typedef struct { ngx_http_upstream_conf_t upstream; ngx_str_t index; ngx_array_t *flushes; ngx_array_t *params_len; ngx_array_t *params; ngx_array_t *params_source; ngx_array_t *catch_stderr; ngx_array_t *fastcgi_lengths; ngx_array_t *fastcgi_values; # if defined(nginx_version) && (nginx_version >= 8040) ngx_hash_t headers_hash; ngx_uint_t header_params; # endif /* nginx_version >= 8040 */ ngx_http_complex_value_t cache_key; # if (NGX_PCRE) ngx_regex_t *split_regex; ngx_str_t split_name; # endif /* NGX_PCRE */ } ngx_http_fastcgi_loc_conf_t; char * ngx_http_fastcgi_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_compile_complex_value_t ccv; ngx_http_core_loc_conf_t *clcf; ngx_http_fastcgi_loc_conf_t *flcf; ngx_str_t *value; flcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_fastcgi_module); /* check for duplicates / collisions */ if (flcf->upstream.cache != NGX_CONF_UNSET_PTR && flcf->upstream.cache != NULL) { return "is either duplicate or collides with \"fastcgi_cache\""; } if (flcf->upstream.upstream || flcf->fastcgi_lengths) { return "is incompatible with \"fastcgi_pass\""; } if (flcf->upstream.store > 0 || flcf->upstream.store_lengths) { return "is incompatible with \"fastcgi_store\""; } value = cf->args->elts; /* set fastcgi_cache part */ flcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0, &ngx_http_fastcgi_module); if (flcf->upstream.cache == NULL) { return NGX_CONF_ERROR; } /* set fastcgi_cache_key part */ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); ccv.cf = cf; ccv.value = &value[2]; ccv.complex_value = &flcf->cache_key; if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { return NGX_CONF_ERROR; } /* set handler */ clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); clcf->handler = ngx_http_fastcgi_cache_purge_handler; return NGX_CONF_OK; } ngx_int_t ngx_http_fastcgi_cache_purge_handler(ngx_http_request_t *r) { ngx_http_fastcgi_loc_conf_t *flcf; if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_DELETE))) { return NGX_HTTP_NOT_ALLOWED; } flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); return ngx_http_cache_purge_handler(r, flcf->upstream.cache->data, &flcf->cache_key); } # endif /* NGX_HTTP_FASTCGI */ # if (NGX_HTTP_PROXY) extern ngx_module_t ngx_http_proxy_module; typedef struct { ngx_str_t key_start; ngx_str_t schema; ngx_str_t host_header; ngx_str_t port; ngx_str_t uri; } ngx_http_proxy_vars_t; typedef struct { ngx_http_upstream_conf_t upstream; ngx_array_t *flushes; ngx_array_t *body_set_len; ngx_array_t *body_set; ngx_array_t *headers_set_len; ngx_array_t *headers_set; ngx_hash_t headers_set_hash; ngx_array_t *headers_source; # if defined(nginx_version) && (nginx_version < 8040) ngx_array_t *headers_names; # endif /* nginx_version < 8040 */ ngx_array_t *proxy_lengths; ngx_array_t *proxy_values; ngx_array_t *redirects; ngx_str_t body_source; ngx_str_t method; ngx_str_t location; ngx_str_t url; ngx_http_complex_value_t cache_key; ngx_http_proxy_vars_t vars; ngx_flag_t redirect; ngx_uint_t headers_hash_max_size; ngx_uint_t headers_hash_bucket_size; } ngx_http_proxy_loc_conf_t; char * ngx_http_proxy_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_compile_complex_value_t ccv; ngx_http_core_loc_conf_t *clcf; ngx_http_proxy_loc_conf_t *plcf; ngx_str_t *value; plcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_proxy_module); /* check for duplicates / collisions */ if (plcf->upstream.cache != NGX_CONF_UNSET_PTR && plcf->upstream.cache != NULL) { return "is either duplicate or collides with \"proxy_cache\""; } if (plcf->upstream.upstream || plcf->proxy_lengths) { return "is incompatible with \"proxy_pass\""; } if (plcf->upstream.store > 0 || plcf->upstream.store_lengths) { return "is incompatible with \"proxy_store\""; } value = cf->args->elts; /* set proxy_cache part */ plcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0, &ngx_http_proxy_module); if (plcf->upstream.cache == NULL) { return NGX_CONF_ERROR; } /* set proxy_cache_key part */ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); ccv.cf = cf; ccv.value = &value[2]; ccv.complex_value = &plcf->cache_key; if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { return NGX_CONF_ERROR; } /* set handler */ clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); clcf->handler = ngx_http_proxy_cache_purge_handler; return NGX_CONF_OK; } ngx_int_t ngx_http_proxy_cache_purge_handler(ngx_http_request_t *r) { ngx_http_proxy_loc_conf_t *plcf; if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_DELETE))) { return NGX_HTTP_NOT_ALLOWED; } plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module); return ngx_http_cache_purge_handler(r, plcf->upstream.cache->data, &plcf->cache_key); } # endif /* NGX_HTTP_PROXY */ # if (NGX_HTTP_SCGI) extern ngx_module_t ngx_http_scgi_module; typedef struct { ngx_http_upstream_conf_t upstream; ngx_array_t *flushes; ngx_array_t *params_len; ngx_array_t *params; ngx_array_t *params_source; ngx_hash_t headers_hash; ngx_uint_t header_params; ngx_array_t *scgi_lengths; ngx_array_t *scgi_values; ngx_http_complex_value_t cache_key; } ngx_http_scgi_loc_conf_t; char * ngx_http_scgi_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_compile_complex_value_t ccv; ngx_http_core_loc_conf_t *clcf; ngx_http_scgi_loc_conf_t *slcf; ngx_str_t *value; slcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_scgi_module); /* check for duplicates / collisions */ if (slcf->upstream.cache != NGX_CONF_UNSET_PTR && slcf->upstream.cache != NULL) { return "is either duplicate or collides with \"scgi_cache\""; } if (slcf->upstream.upstream || slcf->scgi_lengths) { return "is incompatible with \"scgi_pass\""; } if (slcf->upstream.store > 0 || slcf->upstream.store_lengths) { return "is incompatible with \"scgi_store\""; } value = cf->args->elts; /* set scgi_cache part */ slcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0, &ngx_http_scgi_module); if (slcf->upstream.cache == NULL) { return NGX_CONF_ERROR; } /* set scgi_cache_key part */ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); ccv.cf = cf; ccv.value = &value[2]; ccv.complex_value = &slcf->cache_key; if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { return NGX_CONF_ERROR; } /* set handler */ clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); clcf->handler = ngx_http_scgi_cache_purge_handler; return NGX_CONF_OK; } ngx_int_t ngx_http_scgi_cache_purge_handler(ngx_http_request_t *r) { ngx_http_scgi_loc_conf_t *slcf; if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_DELETE))) { return NGX_HTTP_NOT_ALLOWED; } slcf = ngx_http_get_module_loc_conf(r, ngx_http_scgi_module); return ngx_http_cache_purge_handler(r, slcf->upstream.cache->data, &slcf->cache_key); } # endif /* NGX_HTTP_SCGI */ # if (NGX_HTTP_UWSGI) extern ngx_module_t ngx_http_uwsgi_module; typedef struct { ngx_http_upstream_conf_t upstream; ngx_array_t *flushes; ngx_array_t *params_len; ngx_array_t *params; ngx_array_t *params_source; ngx_hash_t headers_hash; ngx_uint_t header_params; ngx_array_t *uwsgi_lengths; ngx_array_t *uwsgi_values; ngx_http_complex_value_t cache_key; ngx_str_t uwsgi_string; ngx_uint_t modifier1; ngx_uint_t modifier2; } ngx_http_uwsgi_loc_conf_t; char * ngx_http_uwsgi_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_compile_complex_value_t ccv; ngx_http_core_loc_conf_t *clcf; ngx_http_uwsgi_loc_conf_t *ulcf; ngx_str_t *value; ulcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_uwsgi_module); /* check for duplicates / collisions */ if (ulcf->upstream.cache != NGX_CONF_UNSET_PTR && ulcf->upstream.cache != NULL) { return "is either duplicate or collides with \"uwsgi_cache\""; } if (ulcf->upstream.upstream || ulcf->uwsgi_lengths) { return "is incompatible with \"uwsgi_pass\""; } if (ulcf->upstream.store > 0 || ulcf->upstream.store_lengths) { return "is incompatible with \"uwsgi_store\""; } value = cf->args->elts; /* set uwsgi_cache part */ ulcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0, &ngx_http_uwsgi_module); if (ulcf->upstream.cache == NULL) { return NGX_CONF_ERROR; } /* set uwsgi_cache_key part */ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); ccv.cf = cf; ccv.value = &value[2]; ccv.complex_value = &ulcf->cache_key; if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { return NGX_CONF_ERROR; } /* set handler */ clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); clcf->handler = ngx_http_uwsgi_cache_purge_handler; return NGX_CONF_OK; } ngx_int_t ngx_http_uwsgi_cache_purge_handler(ngx_http_request_t *r) { ngx_http_uwsgi_loc_conf_t *ulcf; if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_DELETE))) { return NGX_HTTP_NOT_ALLOWED; } ulcf = ngx_http_get_module_loc_conf(r, ngx_http_uwsgi_module); return ngx_http_cache_purge_handler(r, ulcf->upstream.cache->data, &ulcf->cache_key); } # endif /* NGX_HTTP_UWSGI */ ngx_int_t ngx_http_cache_purge_handler(ngx_http_request_t *r, ngx_http_file_cache_t *cache, ngx_http_complex_value_t *cache_key) { ngx_chain_t out; ngx_buf_t *b; ngx_str_t *key; ngx_int_t rc; size_t len; rc = ngx_http_file_cache_purge(r, cache, cache_key); if (rc == NGX_ERROR) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } else if (rc == NGX_DECLINED) { return NGX_HTTP_NOT_FOUND; } key = r->cache->keys.elts; len = sizeof(ngx_http_cache_purge_success_page_top) - 1 + sizeof(ngx_http_cache_purge_success_page_tail) - 1 + sizeof("
Key : ") - 1 + sizeof(CRLF "
Path: ") - 1 + key[0].len + r->cache->file.name.len; r->headers_out.content_type.len = sizeof("text/html") - 1; r->headers_out.content_type.data = (u_char *) "text/html"; r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = len; if (r->method == NGX_HTTP_HEAD) { rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } } b = ngx_create_temp_buf(r->pool, len); if (b == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } out.buf = b; out.next = NULL; b->last = ngx_cpymem(b->last, ngx_http_cache_purge_success_page_top, sizeof(ngx_http_cache_purge_success_page_top) - 1); b->last = ngx_cpymem(b->last, "
Key : ", sizeof("
Key : ") - 1); b->last = ngx_cpymem(b->last, key[0].data, key[0].len); b->last = ngx_cpymem(b->last, CRLF "
Path: ", sizeof(CRLF "
Path: ") - 1); b->last = ngx_cpymem(b->last, r->cache->file.name.data, r->cache->file.name.len); b->last = ngx_cpymem(b->last, ngx_http_cache_purge_success_page_tail, sizeof(ngx_http_cache_purge_success_page_tail) - 1); b->last_buf = 1; rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } return ngx_http_output_filter(r, &out); } ngx_int_t ngx_http_file_cache_purge(ngx_http_request_t *r, ngx_http_file_cache_t *cache, ngx_http_complex_value_t *cache_key) { ngx_http_cache_t *c; ngx_str_t *key; ngx_int_t rc; rc = ngx_http_discard_request_body(r); if (rc != NGX_OK) { return NGX_ERROR; } c = ngx_pcalloc(r->pool, sizeof(ngx_http_cache_t)); if (c == NULL) { return NGX_ERROR; } rc = ngx_array_init(&c->keys, r->pool, 1, sizeof(ngx_str_t)); if (rc != NGX_OK) { return NGX_ERROR; } key = ngx_array_push(&c->keys); if (key == NULL) { return NGX_ERROR; } rc = ngx_http_complex_value(r, cache_key, key); if (rc != NGX_OK) { return NGX_ERROR; } r->cache = c; c->body_start = ngx_pagesize; c->file_cache = cache; c->file.log = r->connection->log; ngx_http_file_cache_create_key(r); rc = ngx_http_file_cache_open(r); # if defined(nginx_version) \ && ((nginx_version >= 8001) \ || ((nginx_version < 8000) && (nginx_version >= 7060))) if (rc == NGX_HTTP_CACHE_UPDATING || rc == NGX_HTTP_CACHE_STALE) { # else if (rc == NGX_HTTP_CACHE_STALE) { # endif rc = NGX_OK; } if (rc != NGX_OK) { if (rc == NGX_DECLINED) { return rc; } else { return NGX_ERROR; } } /* * delete file from disk but *keep* in-memory node, * because other requests might still point to it. */ ngx_shmtx_lock(&cache->shpool->mutex); if (!c->node->exists) { /* race between concurrent purges, backoff */ ngx_shmtx_unlock(&cache->shpool->mutex); return NGX_DECLINED; } # if defined(nginx_version) && (nginx_version >= 1000001) cache->sh->size -= c->node->fs_size; # else cache->sh->size -= (c->node->length + cache->bsize - 1) / cache->bsize; # endif c->node->exists = 0; # if defined(nginx_version) \ && ((nginx_version >= 8001) \ || ((nginx_version < 8000) && (nginx_version >= 7060))) c->node->updating = 0; # endif ngx_shmtx_unlock(&cache->shpool->mutex); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http file cache purge: \"%s\"", c->file.name.data); if (ngx_delete_file(c->file.name.data) == NGX_FILE_ERROR) { /* entry in error log is enough, don't notice client */ ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, ngx_delete_file_n " \"%s\" failed", c->file.name.data); } /* file deleted from cache */ return NGX_OK; } #else /* !NGX_HTTP_CACHE */ static ngx_http_module_t ngx_http_cache_purge_module_ctx = { NULL, /* preconfiguration */ NULL, /* postconfiguration */ NULL, /* create main configuration */ NULL, /* init main configuration */ NULL, /* create server configuration */ NULL, /* merge server configuration */ NULL, /* create location configuration */ NULL, /* merge location configuration */ }; ngx_module_t ngx_http_cache_purge_module = { NGX_MODULE_V1, &ngx_http_cache_purge_module_ctx, /* module context */ NULL, /* module directives */ NGX_HTTP_MODULE, /* module type */ NULL, /* init master */ NULL, /* init module */ NULL, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ NULL, /* exit process */ NULL, /* exit master */ NGX_MODULE_V1_PADDING }; #endif /* NGX_HTTP_CACHE */ cache_purge/t/000075500000000000000000000000001156003077300135665ustar00rootroot00000000000000cache_purge/t/proxy.t000064400000000000000000000050511156003077300151350ustar00rootroot00000000000000# vi:filetype=perl use lib 'lib'; use Test::Nginx::Socket; repeat_each(1); plan tests => repeat_each() * (blocks() * 3 + 3 * 1); our $http_config = <<'_EOC_'; proxy_cache_path /tmp/ngx_cache_purge_cache keys_zone=test_cache:10m; proxy_temp_path /tmp/ngx_cache_purge_temp 1 2; _EOC_ our $config = <<'_EOC_'; location /proxy { proxy_pass $scheme://127.0.0.1:$server_port/etc/passwd; proxy_cache test_cache; proxy_cache_key $uri$is_args$args; proxy_cache_valid 3m; add_header X-Cache-Status $upstream_cache_status; } location ~ /purge(/.*) { proxy_cache_purge test_cache $1$is_args$args; } location = /etc/passwd { root /; } _EOC_ worker_connections(128); no_shuffle(); run_tests(); no_diff(); __DATA__ === TEST 1: prepare --- http_config eval: $::http_config --- config eval: $::config --- request GET /proxy/passwd --- error_code: 200 --- response_headers Content-Type: text/plain --- response_body_like: root --- timeout: 10 --- skip_nginx2: 3: < 0.8.3 or < 0.7.62 === TEST 2: get from cache --- http_config eval: $::http_config --- config eval: $::config --- request GET /proxy/passwd --- error_code: 200 --- response_headers Content-Type: text/plain X-Cache-Status: HIT --- response_body_like: root --- timeout: 10 --- skip_nginx2: 4: < 0.8.3 or < 0.7.62 === TEST 3: purge from cache --- http_config eval: $::http_config --- config eval: $::config --- request DELETE /purge/proxy/passwd --- error_code: 200 --- response_headers Content-Type: text/html --- response_body_like: Successful purge --- timeout: 10 --- skip_nginx2: 3: < 0.8.3 or < 0.7.62 === TEST 4: purge from empty cache --- http_config eval: $::http_config --- config eval: $::config --- request DELETE /purge/proxy/passwd --- error_code: 404 --- response_headers Content-Type: text/html --- response_body_like: 404 Not Found --- timeout: 10 --- skip_nginx2: 3: < 0.8.3 or < 0.7.62 === TEST 5: get from source --- http_config eval: $::http_config --- config eval: $::config --- request GET /proxy/passwd --- error_code: 200 --- response_headers Content-Type: text/plain X-Cache-Status: MISS --- response_body_like: root --- timeout: 10 --- skip_nginx2: 4: < 0.8.3 or < 0.7.62 === TEST 6: get from cache (again) --- http_config eval: $::http_config --- config eval: $::config --- request GET /proxy/passwd --- error_code: 200 --- response_headers Content-Type: text/plain X-Cache-Status: HIT --- response_body_like: root --- timeout: 10 --- skip_nginx2: 4: < 0.8.3 or < 0.7.62