pax_global_header00006660000000000000000000000064143753215000014512gustar00rootroot0000000000000052 comment=a84b0f3f082025dec737a537a9a443bdd6d6af9d nginx-modules-ngx_cache_purge-a84b0f3/000077500000000000000000000000001437532150000200335ustar00rootroot00000000000000nginx-modules-ngx_cache_purge-a84b0f3/.astylerc000066400000000000000000000004041437532150000216600ustar00rootroot00000000000000# astylerc align-pointer=name align-reference=name break-after-logical #indent=spaces=2 max-code-length=120 style=google suffix=none # Indent indent-preproc-block # Padding pad-header unpad-paren # Formatting: add-brackets #convert-tabs # Output: formattednginx-modules-ngx_cache_purge-a84b0f3/.format.sh000077500000000000000000000007101437532150000217360ustar00rootroot00000000000000#!/bin/sh # Search in the script folder pushd "$(dirname $0)" >/dev/null CWD="$(pwd -P)" popd >/dev/null FILES='ngx_cache_purge_module.c' # The file format in accordance with the style defined in .astylerc astyle -v --options='.astylerc' ${FILES} || (echo 'astyle failed'; exit 1); # To correct this, the issuance dos2unix on each file # sometimes adds in Windows as a string-endins (\r\n). dos2unix --quiet ${FILES} || (echo 'dos2unix failed'; exit 2);nginx-modules-ngx_cache_purge-a84b0f3/.gitattributes000066400000000000000000000000321437532150000227210ustar00rootroot00000000000000*.t linguist-language=Textnginx-modules-ngx_cache_purge-a84b0f3/.travis.yml000066400000000000000000000033531437532150000221500ustar00rootroot00000000000000sudo: required os: linux dist: trusty language: c compiler: - gcc - clang cache: apt: true directories: - download-cache env: global: - JOBS=4 - NGINX_PREFIX=/opt/nginx matrix: - NGINX_VERSION=1.18.0 - NGINX_VERSION=1.19.2 before_install: - mkdir --parents download-cache - sudo apt-get update -qq - sudo apt-get install -qq zlib1g-dev libpcre3-dev cpanminus # Get OpenSSL 1.0.2 from Ubuntu Xenial # https://packages.ubuntu.com/xenial-updates/libssl1.0.0 - test -f download-cache/libssl1.0.0_1.0.2g-1ubuntu4.16_amd64.deb || wget -O download-cache/libssl1.0.0_1.0.2g-1ubuntu4.17_amd64.deb "http://de.archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.0.0_1.0.2g-1ubuntu4.17_amd64.deb" # https://packages.ubuntu.com/xenial/libssl-dev - test -f download-cache/libssl-dev_1.0.2g-1ubuntu4.16_amd64.deb || wget -O download-cache/libssl-dev_1.0.2g-1ubuntu4.17_amd64.deb "http://de.archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl-dev_1.0.2g-1ubuntu4.17_amd64.deb" - sudo dpkg -i download-cache/libssl*_amd64.deb # Test::Nginx - git clone https://github.com/openresty/test-nginx.git test-nginx - cd test-nginx/ && sudo cpanm . && cd .. # NGINX source - test -f download-cache/nginx-$NGINX_VERSION.tar.gz || wget -O download-cache/nginx-$NGINX_VERSION.tar.gz http://nginx.org/download/nginx-$NGINX_VERSION.tar.gz install: - tar -xzf download-cache/nginx-${NGINX_VERSION}.tar.gz - cd nginx-${NGINX_VERSION}/ - ./configure --prefix=${NGINX_PREFIX} --with-debug --with-http_ssl_module --add-module=${PWD}/.. - make -j${JOBS} - sudo make install - cd .. - export PATH="${NGINX_PREFIX}/sbin:$PATH" # - export LD_LIBRARY_PATH=${LD_LIBRARY_PATH} script: - nginx -V - ldd $(which nginx) - prove t nginx-modules-ngx_cache_purge-a84b0f3/CHANGES000066400000000000000000000073361437532150000210370ustar00rootroot000000000000002020-06-27 VERSION 2.5.1 * fix: empty key check - it coredumps when cache key is empty, Tuğrul Topuz * fix: purge report calloc fix - Report template has not cache file path but it's length is use in buffer memory allocation, Tuğrul Topuz 2018-08-04 VERSION 2.5 * feat/docs: cache_purge_response_type directive, selecting response type (html|json|xml|text) * break: changed status of HTTP code 404 (Not Found) to 412 (Precondition Failed) * fix: remove path information of response body (#4, 3a8c08a, #11) 2020-06-27 VERSION 2.4.3 * fix: empty key check - it coredumps when cache key is empty, Tuğrul Topuz 2017-09-28 VERSION 2.4.2 * fix: segfault in call to `ngx_read_file` of partial key purge, Frankie Dintino * fix: segfault in `cplcf->conf->purge_all` with separate location syntax, Frankie Dintino 2017-02-21 VERSION 2.4.1 * Fix compatibility with nginx-1.11.6+, Sułowicz Paweł 2016-11-20 VERSION 2.4 * Fix compatibility with nginx-1.7.12+. * explain the purge logic * feat(purge all): Include option to purge all the cached files This option can be slow if a lot of content is cached, or if the storage used for the cache is slow. But you really should be using RAM as your cache storage. * feat(partial keys): Support partial keys to purge multiple keys. Put an '*' at the end of your purge cache URL. e.g: proxy_cache_key $scheme$host$uri$is_args$args$cookie_JSESSIONID; curl -X PURGE https://example.com/pass* This will remove every cached page whose key cache starting with: httpsexample.com/pass* Be careful not passing any value for the values after the $uri, or put it at the end of your cache key. * Convert a config file to build a dynamic module 2014-12-23 VERSION 2.3 * Fix compatibility with nginx-1.7.9+. 2014-12-02 VERSION 2.2 * Fix compatibility with nginx-1.7.8+. 2014-05-19 * Fix build on Solaris with SunCC (Solaris Studio). Reported by Jussi Sallinen. 2013-03-19 VERSION 2.1 * When enabled, cache purge will now catch all requests with PURGE (or specified) method, even if cache isn't configured. Previously, it would pass such requests to the upstream. 2012-12-07 VERSION 2.0 * Add alternative "same location" syntax. From CloudFlare. 2012-07-02 VERSION 1.6 * Fix compatibility with nginx-1.3.2+. Reported by MagicBear, patch from Ruslan Ermilov. 2011-12-20 VERSION 1.5 * Fix on-disk cache size calculation. Since the initial release, recorded on-disk cache size was decreased twice for purged content (once during cache purge and once during subsequent cache update). This resulted in recorded on-disk cache size being much smaller than in reality, which could lead to on-disk cache outgrowing defined "max_size" parameter. Patch from Pyry Hakulinen. 2011-10-05 VERSION 1.4 * Add AIO support. Requested by Emin Hasanov. 2011-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. nginx-modules-ngx_cache_purge-a84b0f3/LICENSE000066400000000000000000000026201437532150000210400ustar00rootroot00000000000000Copyright (c) 2009-2014, FRiCKLE Copyright (c) 2009-2014, 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. nginx-modules-ngx_cache_purge-a84b0f3/README.md000066400000000000000000000220721437532150000213150ustar00rootroot00000000000000About ===== `ngx_cache_purge` is `nginx` module which adds ability to purge content from `FastCGI`, `proxy`, `SCGI` and `uWSGI` caches. A purge operation removes the content with the same cache key as the purge request has. Sponsors ======== Work on the original patch was fully funded by [yo.se](http://yo.se). Status ====== This module is production-ready. Configuration directives (same location syntax) =============================================== fastcgi_cache_purge ------------------- * **syntax**: `fastcgi_cache_purge on|off| [purge_all] [from all| [.. ]]` * **default**: `none` * **context**: `http`, `server`, `location` Allow purging of selected pages from `FastCGI`'s cache. proxy_cache_purge ----------------- * **syntax**: `proxy_cache_purge on|off| [purge_all] [from all| [.. ]]` * **default**: `none` * **context**: `http`, `server`, `location` Allow purging of selected pages from `proxy`'s cache. scgi_cache_purge ---------------- * **syntax**: `scgi_cache_purge on|off| [purge_all] [from all| [.. ]]` * **default**: `none` * **context**: `http`, `server`, `location` Allow purging of selected pages from `SCGI`'s cache. uwsgi_cache_purge ----------------- * **syntax**: `uwsgi_cache_purge on|off| [purge_all] [from all| [.. ]]` * **default**: `none` * **context**: `http`, `server`, `location` Allow purging of selected pages from `uWSGI`'s cache. Configuration directives (separate location syntax) =================================================== 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. Configuration directives (Optional) =================================================== cache_purge_response_type ----------------- * **syntax**: `cache_purge_response_type html|json|xml|text` * **default**: `html` * **context**: `http`, `server`, `location` Sets a response type of purging result. Partial Keys ============ Sometimes it's not possible to pass the exact key cache to purge a page. For example; when the content of a cookie or the params are part of the key. You can specify a partial key adding an asterisk at the end of the URL. curl -X PURGE /page* The asterisk must be the last character of the key, so you **must** put the $uri variable at the end. Sample configuration (same location syntax) =========================================== 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"; proxy_cache_purge PURGE from 127.0.0.1; } } } Sample configuration (same location syntax - purge all cached files) ==================================================================== 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"; proxy_cache_purge PURGE purge_all from 127.0.0.1 192.168.0.0/8; } } } Sample configuration (separate location syntax) =============================================== 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 tmpcache; proxy_cache_key "$1$is_args$args"; } } } Sample configuration (Optional) =============================================== http { proxy_cache_path /tmp/cache keys_zone=tmpcache:10m; cache_purge_response_type text; server { cache_purge_response_type json; location / { #json proxy_pass http://127.0.0.1:8000; proxy_cache tmpcache; proxy_cache_key "$uri$is_args$args"; } location ~ /purge(/.*) { #xml allow 127.0.0.1; deny all; proxy_cache tmpcache; proxy_cache_key "$1$is_args$args"; cache_purge_response_type xml; } location ~ /purge2(/.*) { # json allow 127.0.0.1; deny all; proxy_cache tmpcache; proxy_cache_key "$1$is_args$args"; } } server { location / { #text proxy_pass http://127.0.0.1:8000; proxy_cache tmpcache; proxy_cache_key "$uri$is_args$args"; } location ~ /purge(/.*) { #text allow 127.0.0.1; deny all; proxy_cache tmpcache; proxy_cache_key "$1$is_args$args"; } location ~ /purge2(/.*) { #html allow 127.0.0.1; deny all; proxy_cache tmpcache; proxy_cache_key "$1$is_args$args"; cache_purge_response_type html; } } } Solve problems ============== * Enabling [`gzip_vary`](https://nginx.org/r/gzip_vary) can lead to different results when clearing, when enabling it, you may have problems clearing the cache. For reliable operation, you can disable [`gzip_vary`](https://nginx.org/r/gzip_vary) inside the location [#20](https://github.com/nginx-modules/ngx_cache_purge/issues/20). 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-2014, FRiCKLE Copyright (c) 2009-2014, 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). - http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#purger - http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_cache_purge - https://github.com/wandenberg/nginx-selective-cache-purge-module - https://github.com/wandenberg/nginx-sorted-querystring-module - https://github.com/ledgetech/ledge - [Faking Surrogate Cache-Keys for Nginx Plus](https://www.innoq.com/en/blog/faking-surrogate-cache-keys-for-nginx-plus/) ([gist](https://gist.github.com/titpetric/2f142e89eaa0f36ba4e4383b16d61474)) - [Delete NGINX cached md5 items with a PURGE with wildcard support](https://gist.github.com/nosun/0cfb58d3164f829e2f027fd37b338ede) nginx-modules-ngx_cache_purge-a84b0f3/config000066400000000000000000000013131437532150000212210ustar00rootroot00000000000000if [ "$HTTP_PROXY" = "YES" ]; then have=NGX_HTTP_PROXY . auto/have fi if [ "$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 CACHE_PURGE_SRCS="$ngx_addon_dir/ngx_cache_purge_module.c" if [ -n "$ngx_module_link" ]; then ngx_module_type=HTTP ngx_module_name="$ngx_addon_name" ngx_module_srcs="$CACHE_PURGE_SRCS" . auto/module else HTTP_MODULES="$HTTP_MODULES $ngx_addon_name" NGX_ADDON_SRCS="$NGX_ADDON_SRCS $CACHE_PURGE_SRCS" fi have=NGX_CACHE_PURGE_MODULE . auto/have nginx-modules-ngx_cache_purge-a84b0f3/ngx_cache_purge_module.c000066400000000000000000002003771437532150000246760ustar00rootroot00000000000000/* * Copyright (c) 2009-2014, FRiCKLE * Copyright (c) 2009-2014, 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. */ #include #include #include #include #ifndef nginx_version #error This module cannot be build against an unknown nginx version. #endif #define NGX_REPONSE_TYPE_HTML 1 #define NGX_REPONSE_TYPE_XML 2 #define NGX_REPONSE_TYPE_JSON 3 #define NGX_REPONSE_TYPE_TEXT 4 static const char ngx_http_cache_purge_content_type_json[] = "application/json"; static const char ngx_http_cache_purge_content_type_html[] = "text/html"; static const char ngx_http_cache_purge_content_type_xml[] = "text/xml"; static const char ngx_http_cache_purge_content_type_text[] = "text/plain"; static size_t ngx_http_cache_purge_content_type_json_size = sizeof(ngx_http_cache_purge_content_type_json); static size_t ngx_http_cache_purge_content_type_html_size = sizeof(ngx_http_cache_purge_content_type_html); static size_t ngx_http_cache_purge_content_type_xml_size = sizeof(ngx_http_cache_purge_content_type_xml); static size_t ngx_http_cache_purge_content_type_text_size = sizeof(ngx_http_cache_purge_content_type_text); static const char ngx_http_cache_purge_body_templ_json[] = "{\"Key\": \"%s\"}"; static const char ngx_http_cache_purge_body_templ_html[] = "Successful purge

Successful purge

Key : %s

"; static const char ngx_http_cache_purge_body_templ_xml[] = ""; static const char ngx_http_cache_purge_body_templ_text[] = "Key:%s\n"; static size_t ngx_http_cache_purge_body_templ_json_size = sizeof(ngx_http_cache_purge_body_templ_json); static size_t ngx_http_cache_purge_body_templ_html_size = sizeof(ngx_http_cache_purge_body_templ_html); static size_t ngx_http_cache_purge_body_templ_xml_size = sizeof(ngx_http_cache_purge_body_templ_xml); static size_t ngx_http_cache_purge_body_templ_text_size = sizeof(ngx_http_cache_purge_body_templ_text); #if (NGX_HTTP_CACHE) typedef struct { ngx_flag_t enable; ngx_str_t method; ngx_flag_t purge_all; ngx_array_t *access; /* array of ngx_in_cidr_t */ ngx_array_t *access6; /* array of ngx_in6_cidr_t */ } ngx_http_cache_purge_conf_t; typedef struct { # if (NGX_HTTP_FASTCGI) ngx_http_cache_purge_conf_t fastcgi; # endif /* NGX_HTTP_FASTCGI */ # if (NGX_HTTP_PROXY) ngx_http_cache_purge_conf_t proxy; # endif /* NGX_HTTP_PROXY */ # if (NGX_HTTP_SCGI) ngx_http_cache_purge_conf_t scgi; # endif /* NGX_HTTP_SCGI */ # if (NGX_HTTP_UWSGI) ngx_http_cache_purge_conf_t uwsgi; # endif /* NGX_HTTP_UWSGI */ ngx_http_cache_purge_conf_t *conf; ngx_http_handler_pt handler; ngx_http_handler_pt original_handler; ngx_uint_t resptype; /* response content-type */ } ngx_http_cache_purge_loc_conf_t; # 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 */ char *ngx_http_cache_purge_response_type_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static ngx_int_t ngx_http_purge_file_cache_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path); static ngx_int_t ngx_http_purge_file_cache_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path); ngx_int_t ngx_http_cache_purge_access_handler(ngx_http_request_t *r); ngx_int_t ngx_http_cache_purge_access(ngx_array_t *a, ngx_array_t *a6, struct sockaddr *s); ngx_int_t ngx_http_cache_purge_send_response(ngx_http_request_t *r); # if (nginx_version >= 1007009) ngx_int_t ngx_http_cache_purge_cache_get(ngx_http_request_t *r, ngx_http_upstream_t *u, ngx_http_file_cache_t **cache); # endif /* nginx_version >= 1007009 */ ngx_int_t ngx_http_cache_purge_init(ngx_http_request_t *r, ngx_http_file_cache_t *cache, ngx_http_complex_value_t *cache_key); void ngx_http_cache_purge_handler(ngx_http_request_t *r); ngx_int_t ngx_http_file_cache_purge(ngx_http_request_t *r); void ngx_http_cache_purge_all(ngx_http_request_t *r, ngx_http_file_cache_t *cache); void ngx_http_cache_purge_partial(ngx_http_request_t *r, ngx_http_file_cache_t *cache); ngx_int_t ngx_http_cache_purge_is_partial(ngx_http_request_t *r); char *ngx_http_cache_purge_conf(ngx_conf_t *cf, ngx_http_cache_purge_conf_t *cpcf); void *ngx_http_cache_purge_create_loc_conf(ngx_conf_t *cf); char *ngx_http_cache_purge_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child); static ngx_command_t ngx_http_cache_purge_module_commands[] = { # if (NGX_HTTP_FASTCGI) { ngx_string("fastcgi_cache_purge"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, 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_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, 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_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, 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_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, ngx_http_uwsgi_cache_purge_conf, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, # endif /* NGX_HTTP_UWSGI */ { ngx_string("cache_purge_response_type"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_http_cache_purge_response_type_conf, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, 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 */ ngx_http_cache_purge_create_loc_conf, /* create location configuration */ ngx_http_cache_purge_merge_loc_conf /* 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 }; # if (NGX_HTTP_FASTCGI) extern ngx_module_t ngx_http_fastcgi_module; # if (nginx_version >= 1007009) typedef struct { ngx_array_t caches; /* ngx_http_file_cache_t * */ } ngx_http_fastcgi_main_conf_t; # endif /* nginx_version >= 1007009 */ # if (nginx_version >= 1007008) typedef struct { ngx_array_t *flushes; ngx_array_t *lengths; ngx_array_t *values; ngx_uint_t number; ngx_hash_t hash; } ngx_http_fastcgi_params_t; # endif /* nginx_version >= 1007008 */ typedef struct { ngx_http_upstream_conf_t upstream; ngx_str_t index; # if (nginx_version >= 1007008) ngx_http_fastcgi_params_t params; ngx_http_fastcgi_params_t params_cache; # else ngx_array_t *flushes; ngx_array_t *params_len; ngx_array_t *params; # endif /* nginx_version >= 1007008 */ ngx_array_t *params_source; ngx_array_t *catch_stderr; ngx_array_t *fastcgi_lengths; ngx_array_t *fastcgi_values; # if (nginx_version >= 8040) && (nginx_version < 1007008) ngx_hash_t headers_hash; ngx_uint_t header_params; # endif /* nginx_version >= 8040 && nginx_version < 1007008 */ # if (nginx_version >= 1001004) ngx_flag_t keep_conn; # endif /* nginx_version >= 1001004 */ 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_cache_purge_loc_conf_t *cplcf; ngx_http_core_loc_conf_t *clcf; ngx_http_fastcgi_loc_conf_t *flcf; ngx_str_t *value; # if (nginx_version >= 1007009) ngx_http_complex_value_t cv; # endif /* nginx_version >= 1007009 */ cplcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_cache_purge_module); /* check for duplicates / collisions */ if (cplcf->fastcgi.enable != NGX_CONF_UNSET) { return "is duplicate"; } if (cf->args->nelts != 3) { return ngx_http_cache_purge_conf(cf, &cplcf->fastcgi); } if (cf->cmd_type & (NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF)) { return "(separate location syntax) is not allowed here"; } flcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_fastcgi_module); # if (nginx_version >= 1007009) if (flcf->upstream.cache > 0) # else if (flcf->upstream.cache != NGX_CONF_UNSET_PTR && flcf->upstream.cache != NULL) # endif /* nginx_version >= 1007009 */ { return "is incompatible with \"fastcgi_cache\""; } if (flcf->upstream.upstream || flcf->fastcgi_lengths) { return "is incompatible with \"fastcgi_pass\""; } if (flcf->upstream.store > 0 # if (nginx_version < 1007009) || flcf->upstream.store_lengths # endif /* nginx_version >= 1007009 */ ) { return "is incompatible with \"fastcgi_store\""; } value = cf->args->elts; /* set fastcgi_cache part */ # if (nginx_version >= 1007009) flcf->upstream.cache = 1; ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); ccv.cf = cf; ccv.value = &value[1]; ccv.complex_value = &cv; if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { return NGX_CONF_ERROR; } if (cv.lengths != NULL) { flcf->upstream.cache_value = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t)); if (flcf->upstream.cache_value == NULL) { return NGX_CONF_ERROR; } *flcf->upstream.cache_value = cv; } else { flcf->upstream.cache_zone = ngx_shared_memory_add(cf, &value[1], 0, &ngx_http_fastcgi_module); if (flcf->upstream.cache_zone == NULL) { return NGX_CONF_ERROR; } } # else flcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0, &ngx_http_fastcgi_module); if (flcf->upstream.cache == NULL) { return NGX_CONF_ERROR; } # endif /* nginx_version >= 1007009 */ /* 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); cplcf->fastcgi.enable = 0; cplcf->conf = &cplcf->fastcgi; 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_file_cache_t *cache; ngx_http_fastcgi_loc_conf_t *flcf; ngx_http_cache_purge_loc_conf_t *cplcf; # if (nginx_version >= 1007009) ngx_http_fastcgi_main_conf_t *fmcf; ngx_int_t rc; # endif /* nginx_version >= 1007009 */ if (ngx_http_upstream_create(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); r->upstream->conf = &flcf->upstream; # if (nginx_version >= 1007009) fmcf = ngx_http_get_module_main_conf(r, ngx_http_fastcgi_module); r->upstream->caches = &fmcf->caches; rc = ngx_http_cache_purge_cache_get(r, r->upstream, &cache); if (rc != NGX_OK) { return rc; } # else cache = flcf->upstream.cache->data; # endif /* nginx_version >= 1007009 */ if (ngx_http_cache_purge_init(r, cache, &flcf->cache_key) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } /* Purge-all option */ cplcf = ngx_http_get_module_loc_conf(r, ngx_http_cache_purge_module); if (cplcf->conf->purge_all) { ngx_http_cache_purge_all(r, cache); } else { if (ngx_http_cache_purge_is_partial(r)) { ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http file cache purge with partial enabled"); ngx_http_cache_purge_partial(r, cache); } } # if (nginx_version >= 8011) r->main->count++; # endif ngx_http_cache_purge_handler(r); return NGX_DONE; } # 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; # if (nginx_version >= 1007009) typedef struct { ngx_array_t caches; /* ngx_http_file_cache_t * */ } ngx_http_proxy_main_conf_t; # endif /* nginx_version >= 1007009 */ # if (nginx_version >= 1007008) typedef struct { ngx_array_t *flushes; ngx_array_t *lengths; ngx_array_t *values; ngx_hash_t hash; } ngx_http_proxy_headers_t; # endif /* nginx_version >= 1007008 */ typedef struct { ngx_http_upstream_conf_t upstream; # if (nginx_version >= 1007008) ngx_array_t *body_flushes; ngx_array_t *body_lengths; ngx_array_t *body_values; ngx_str_t body_source; ngx_http_proxy_headers_t headers; ngx_http_proxy_headers_t headers_cache; # else 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; # endif /* nginx_version >= 1007008 */ ngx_array_t *headers_source; # if (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; # if (nginx_version >= 1001015) ngx_array_t *cookie_domains; ngx_array_t *cookie_paths; # endif /* nginx_version >= 1001015 */ # if (nginx_version >= 1019003) ngx_array_t *cookie_flags; # endif /* nginx_version >= 1019003 */ # if (nginx_version < 1007008) ngx_str_t body_source; # endif /* nginx_version < 1007008 */ # if (nginx_version >= 1011006) ngx_http_complex_value_t *method; # else ngx_str_t method; # endif /* nginx_version >= 1011006 */ 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; # if (nginx_version >= 1001004) ngx_uint_t http_version; # endif /* nginx_version >= 1001004 */ ngx_uint_t headers_hash_max_size; ngx_uint_t headers_hash_bucket_size; # if (NGX_HTTP_SSL) # if (nginx_version >= 1005006) ngx_uint_t ssl; ngx_uint_t ssl_protocols; ngx_str_t ssl_ciphers; # endif /* nginx_version >= 1005006 */ # if (nginx_version >= 1007000) ngx_uint_t ssl_verify_depth; ngx_str_t ssl_trusted_certificate; ngx_str_t ssl_crl; # endif /* nginx_version >= 1007000 */ # if ((nginx_version >= 1007008) && (nginx_version < 1021000)) ngx_str_t ssl_certificate; ngx_str_t ssl_certificate_key; ngx_array_t *ssl_passwords; # endif /* nginx_version >= 1007008 && nginx_version < 1021000 */ # if (nginx_version >= 1019004) ngx_array_t *ssl_conf_commands; # endif /*nginx_version >= 1019004 */ # endif } 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_cache_purge_loc_conf_t *cplcf; ngx_http_core_loc_conf_t *clcf; ngx_http_proxy_loc_conf_t *plcf; ngx_str_t *value; # if (nginx_version >= 1007009) ngx_http_complex_value_t cv; # endif /* nginx_version >= 1007009 */ cplcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_cache_purge_module); /* check for duplicates / collisions */ if (cplcf->proxy.enable != NGX_CONF_UNSET) { return "is duplicate"; } if (cf->args->nelts != 3) { return ngx_http_cache_purge_conf(cf, &cplcf->proxy); } if (cf->cmd_type & (NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF)) { return "(separate location syntax) is not allowed here"; } plcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_proxy_module); # if (nginx_version >= 1007009) if (plcf->upstream.cache > 0) # else if (plcf->upstream.cache != NGX_CONF_UNSET_PTR && plcf->upstream.cache != NULL) # endif /* nginx_version >= 1007009 */ { return "is incompatible with \"proxy_cache\""; } if (plcf->upstream.upstream || plcf->proxy_lengths) { return "is incompatible with \"proxy_pass\""; } if (plcf->upstream.store > 0 # if (nginx_version < 1007009) || plcf->upstream.store_lengths # endif /* nginx_version >= 1007009 */ ) { return "is incompatible with \"proxy_store\""; } value = cf->args->elts; /* set proxy_cache part */ # if (nginx_version >= 1007009) plcf->upstream.cache = 1; ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); ccv.cf = cf; ccv.value = &value[1]; ccv.complex_value = &cv; if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { return NGX_CONF_ERROR; } if (cv.lengths != NULL) { plcf->upstream.cache_value = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t)); if (plcf->upstream.cache_value == NULL) { return NGX_CONF_ERROR; } *plcf->upstream.cache_value = cv; } else { plcf->upstream.cache_zone = ngx_shared_memory_add(cf, &value[1], 0, &ngx_http_proxy_module); if (plcf->upstream.cache_zone == NULL) { return NGX_CONF_ERROR; } } # else plcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0, &ngx_http_proxy_module); if (plcf->upstream.cache == NULL) { return NGX_CONF_ERROR; } # endif /* nginx_version >= 1007009 */ /* 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); cplcf->proxy.enable = 0; cplcf->conf = &cplcf->proxy; 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_file_cache_t *cache; ngx_http_proxy_loc_conf_t *plcf; ngx_http_cache_purge_loc_conf_t *cplcf; # if (nginx_version >= 1007009) ngx_http_proxy_main_conf_t *pmcf; ngx_int_t rc; # endif /* nginx_version >= 1007009 */ if (ngx_http_upstream_create(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module); r->upstream->conf = &plcf->upstream; # if (nginx_version >= 1007009) pmcf = ngx_http_get_module_main_conf(r, ngx_http_proxy_module); r->upstream->caches = &pmcf->caches; rc = ngx_http_cache_purge_cache_get(r, r->upstream, &cache); if (rc != NGX_OK) { return rc; } # else cache = plcf->upstream.cache->data; # endif /* nginx_version >= 1007009 */ if (ngx_http_cache_purge_init(r, cache, &plcf->cache_key) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } /* Purge-all option */ cplcf = ngx_http_get_module_loc_conf(r, ngx_http_cache_purge_module); if (cplcf->conf->purge_all) { ngx_http_cache_purge_all(r, cache); } else { if (ngx_http_cache_purge_is_partial(r)) { ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http file cache purge with partial enabled"); ngx_http_cache_purge_partial(r, cache); } } # if (nginx_version >= 8011) r->main->count++; # endif ngx_http_cache_purge_handler(r); return NGX_DONE; } # endif /* NGX_HTTP_PROXY */ # if (NGX_HTTP_SCGI) extern ngx_module_t ngx_http_scgi_module; # if (nginx_version >= 1007009) typedef struct { ngx_array_t caches; /* ngx_http_file_cache_t * */ } ngx_http_scgi_main_conf_t; # endif /* nginx_version >= 1007009 */ # if (nginx_version >= 1007008) typedef struct { ngx_array_t *flushes; ngx_array_t *lengths; ngx_array_t *values; ngx_uint_t number; ngx_hash_t hash; } ngx_http_scgi_params_t; # endif /* nginx_version >= 1007008 */ typedef struct { ngx_http_upstream_conf_t upstream; # if (nginx_version >= 1007008) ngx_http_scgi_params_t params; ngx_http_scgi_params_t params_cache; ngx_array_t *params_source; # else 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; # endif /* nginx_version >= 1007008 */ 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_cache_purge_loc_conf_t *cplcf; ngx_http_core_loc_conf_t *clcf; ngx_http_scgi_loc_conf_t *slcf; ngx_str_t *value; # if (nginx_version >= 1007009) ngx_http_complex_value_t cv; # endif /* nginx_version >= 1007009 */ cplcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_cache_purge_module); /* check for duplicates / collisions */ if (cplcf->scgi.enable != NGX_CONF_UNSET) { return "is duplicate"; } if (cf->args->nelts != 3) { return ngx_http_cache_purge_conf(cf, &cplcf->scgi); } if (cf->cmd_type & (NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF)) { return "(separate location syntax) is not allowed here"; } slcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_scgi_module); # if (nginx_version >= 1007009) if (slcf->upstream.cache > 0) # else if (slcf->upstream.cache != NGX_CONF_UNSET_PTR && slcf->upstream.cache != NULL) # endif /* nginx_version >= 1007009 */ { return "is incompatible with \"scgi_cache\""; } if (slcf->upstream.upstream || slcf->scgi_lengths) { return "is incompatible with \"scgi_pass\""; } if (slcf->upstream.store > 0 # if (nginx_version < 1007009) || slcf->upstream.store_lengths # endif /* nginx_version >= 1007009 */ ) { return "is incompatible with \"scgi_store\""; } value = cf->args->elts; /* set scgi_cache part */ # if (nginx_version >= 1007009) slcf->upstream.cache = 1; ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); ccv.cf = cf; ccv.value = &value[1]; ccv.complex_value = &cv; if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { return NGX_CONF_ERROR; } if (cv.lengths != NULL) { slcf->upstream.cache_value = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t)); if (slcf->upstream.cache_value == NULL) { return NGX_CONF_ERROR; } *slcf->upstream.cache_value = cv; } else { slcf->upstream.cache_zone = ngx_shared_memory_add(cf, &value[1], 0, &ngx_http_scgi_module); if (slcf->upstream.cache_zone == NULL) { return NGX_CONF_ERROR; } } # else slcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0, &ngx_http_scgi_module); if (slcf->upstream.cache == NULL) { return NGX_CONF_ERROR; } # endif /* nginx_version >= 1007009 */ /* 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); cplcf->scgi.enable = 0; cplcf->conf = &cplcf->scgi; 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_file_cache_t *cache; ngx_http_scgi_loc_conf_t *slcf; ngx_http_cache_purge_loc_conf_t *cplcf; # if (nginx_version >= 1007009) ngx_http_scgi_main_conf_t *smcf; ngx_int_t rc; # endif /* nginx_version >= 1007009 */ if (ngx_http_upstream_create(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } slcf = ngx_http_get_module_loc_conf(r, ngx_http_scgi_module); r->upstream->conf = &slcf->upstream; # if (nginx_version >= 1007009) smcf = ngx_http_get_module_main_conf(r, ngx_http_scgi_module); r->upstream->caches = &smcf->caches; rc = ngx_http_cache_purge_cache_get(r, r->upstream, &cache); if (rc != NGX_OK) { return rc; } # else cache = slcf->upstream.cache->data; # endif /* nginx_version >= 1007009 */ if (ngx_http_cache_purge_init(r, cache, &slcf->cache_key) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } /* Purge-all option */ cplcf = ngx_http_get_module_loc_conf(r, ngx_http_cache_purge_module); if (cplcf->conf->purge_all) { ngx_http_cache_purge_all(r, cache); } else { if (ngx_http_cache_purge_is_partial(r)) { ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http file cache purge with partial enabled"); ngx_http_cache_purge_partial(r, cache); } } # if (nginx_version >= 8011) r->main->count++; # endif ngx_http_cache_purge_handler(r); return NGX_DONE; } # endif /* NGX_HTTP_SCGI */ # if (NGX_HTTP_UWSGI) extern ngx_module_t ngx_http_uwsgi_module; # if (nginx_version >= 1007009) typedef struct { ngx_array_t caches; /* ngx_http_file_cache_t * */ } ngx_http_uwsgi_main_conf_t; # endif /* nginx_version >= 1007009 */ # if (nginx_version >= 1007008) typedef struct { ngx_array_t *flushes; ngx_array_t *lengths; ngx_array_t *values; ngx_uint_t number; ngx_hash_t hash; } ngx_http_uwsgi_params_t; # endif /* nginx_version >= 1007008 */ typedef struct { ngx_http_upstream_conf_t upstream; # if (nginx_version >= 1007008) ngx_http_uwsgi_params_t params; ngx_http_uwsgi_params_t params_cache; ngx_array_t *params_source; # else 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; # endif /* nginx_version >= 1007008 */ 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; # if (NGX_HTTP_SSL) # if (nginx_version >= 1005008) ngx_uint_t ssl; ngx_uint_t ssl_protocols; ngx_str_t ssl_ciphers; # endif /* nginx_version >= 1005008 */ # if (nginx_version >= 1007000) ngx_uint_t ssl_verify_depth; ngx_str_t ssl_trusted_certificate; ngx_str_t ssl_crl; # endif /* nginx_version >= 1007000 */ # if ((nginx_version >= 1007008) && (nginx_version < 1021000)) ngx_str_t ssl_certificate; ngx_str_t ssl_certificate_key; ngx_array_t *ssl_passwords; # endif /* nginx_version >= 1007008 && nginx_version < 1021000 */ # if (nginx_version >= 1019004) ngx_array_t *ssl_conf_commands; # endif /*nginx_version >= 1019004 */ # endif } 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_cache_purge_loc_conf_t *cplcf; ngx_http_core_loc_conf_t *clcf; ngx_http_uwsgi_loc_conf_t *ulcf; ngx_str_t *value; # if (nginx_version >= 1007009) ngx_http_complex_value_t cv; # endif /* nginx_version >= 1007009 */ cplcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_cache_purge_module); /* check for duplicates / collisions */ if (cplcf->uwsgi.enable != NGX_CONF_UNSET) { return "is duplicate"; } if (cf->args->nelts != 3) { return ngx_http_cache_purge_conf(cf, &cplcf->uwsgi); } if (cf->cmd_type & (NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF)) { return "(separate location syntax) is not allowed here"; } ulcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_uwsgi_module); # if (nginx_version >= 1007009) if (ulcf->upstream.cache > 0) # else if (ulcf->upstream.cache != NGX_CONF_UNSET_PTR && ulcf->upstream.cache != NULL) # endif /* nginx_version >= 1007009 */ { return "is incompatible with \"uwsgi_cache\""; } if (ulcf->upstream.upstream || ulcf->uwsgi_lengths) { return "is incompatible with \"uwsgi_pass\""; } if (ulcf->upstream.store > 0 # if (nginx_version < 1007009) || ulcf->upstream.store_lengths # endif /* nginx_version >= 1007009 */ ) { return "is incompatible with \"uwsgi_store\""; } value = cf->args->elts; /* set uwsgi_cache part */ # if (nginx_version >= 1007009) ulcf->upstream.cache = 1; ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); ccv.cf = cf; ccv.value = &value[1]; ccv.complex_value = &cv; if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { return NGX_CONF_ERROR; } if (cv.lengths != NULL) { ulcf->upstream.cache_value = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t)); if (ulcf->upstream.cache_value == NULL) { return NGX_CONF_ERROR; } *ulcf->upstream.cache_value = cv; } else { ulcf->upstream.cache_zone = ngx_shared_memory_add(cf, &value[1], 0, &ngx_http_uwsgi_module); if (ulcf->upstream.cache_zone == NULL) { return NGX_CONF_ERROR; } } # else ulcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0, &ngx_http_uwsgi_module); if (ulcf->upstream.cache == NULL) { return NGX_CONF_ERROR; } # endif /* nginx_version >= 1007009 */ /* 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); cplcf->uwsgi.enable = 0; cplcf->conf = &cplcf->uwsgi; 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_file_cache_t *cache; ngx_http_uwsgi_loc_conf_t *ulcf; ngx_http_cache_purge_loc_conf_t *cplcf; # if (nginx_version >= 1007009) ngx_http_uwsgi_main_conf_t *umcf; ngx_int_t rc; # endif /* nginx_version >= 1007009 */ if (ngx_http_upstream_create(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } ulcf = ngx_http_get_module_loc_conf(r, ngx_http_uwsgi_module); r->upstream->conf = &ulcf->upstream; # if (nginx_version >= 1007009) umcf = ngx_http_get_module_main_conf(r, ngx_http_uwsgi_module); r->upstream->caches = &umcf->caches; rc = ngx_http_cache_purge_cache_get(r, r->upstream, &cache); if (rc != NGX_OK) { return rc; } # else cache = ulcf->upstream.cache->data; # endif /* nginx_version >= 1007009 */ if (ngx_http_cache_purge_init(r, cache, &ulcf->cache_key) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } /* Purge-all option */ cplcf = ngx_http_get_module_loc_conf(r, ngx_http_cache_purge_module); if (cplcf->conf->purge_all) { ngx_http_cache_purge_all(r, cache); } else { if (ngx_http_cache_purge_is_partial(r)) { ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http file cache purge with partial enabled"); ngx_http_cache_purge_partial(r, cache); } } # if (nginx_version >= 8011) r->main->count++; # endif ngx_http_cache_purge_handler(r); return NGX_DONE; } # endif /* NGX_HTTP_UWSGI */ char * ngx_http_cache_purge_response_type_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_cache_purge_loc_conf_t *cplcf; ngx_str_t *value; cplcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_cache_purge_module); /* check for duplicates / collisions */ if (cplcf->resptype != NGX_CONF_UNSET_UINT && cf->cmd_type == NGX_HTTP_LOC_CONF ) { return "is duplicate"; } /* sanity check */ if (cf->args->nelts < 2) { return "is invalid paramter, ex) cache_purge_response_type (html|json|xml|text)"; } if (cf->args->nelts > 2 ) { return "is required only 1 option, ex) cache_purge_response_type (html|json|xml|text)"; } value = cf->args->elts; if (ngx_strcmp(value[1].data, "html") != 0 && ngx_strcmp(value[1].data, "json") != 0 && ngx_strcmp(value[1].data, "xml") != 0 && ngx_strcmp(value[1].data, "text") != 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\", expected" " \"(html|json|xml|text)\" keyword", &value[1]); return NGX_CONF_ERROR; } if (cf->cmd_type == NGX_HTTP_MODULE) { return "(separate server or location syntax) is not allowed here"; } if (ngx_strcmp(value[1].data, "html") == 0) { cplcf->resptype = NGX_REPONSE_TYPE_HTML; } else if (ngx_strcmp(value[1].data, "xml") == 0) { cplcf->resptype = NGX_REPONSE_TYPE_XML; } else if (ngx_strcmp(value[1].data, "json") == 0) { cplcf->resptype = NGX_REPONSE_TYPE_JSON; } else if (ngx_strcmp(value[1].data, "text") == 0) { cplcf->resptype = NGX_REPONSE_TYPE_TEXT; } return NGX_CONF_OK; } static ngx_int_t ngx_http_purge_file_cache_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path) { return NGX_OK; } static ngx_int_t ngx_http_purge_file_cache_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "http file cache delete: \"%s\"", path->data); if (ngx_delete_file(path->data) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, ngx_delete_file_n " \"%s\" failed", path->data); } return NGX_OK; } static ngx_int_t ngx_http_purge_file_cache_delete_partial_file(ngx_tree_ctx_t *ctx, ngx_str_t *path) { u_char *key_partial; u_char *key_in_file; ngx_uint_t len; ngx_flag_t remove_file = 0; key_partial = ctx->data; len = ngx_strlen(key_partial); /* if key_partial is empty always match, because is a '*' */ if (len == 0) { ngx_log_debug(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "empty key_partial, forcing deletion"); remove_file = 1; } else { ngx_file_t file; ngx_memzero(&file, sizeof(ngx_file_t)); file.offset = file.sys_offset = 0; file.fd = ngx_open_file(path->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, NGX_FILE_DEFAULT_ACCESS); if (file.fd == -1) { return NGX_OK; } file.log = ctx->log; /* I don't know if it's a good idea to use the ngx_cycle pool for this, but the request is not available here */ key_in_file = ngx_pcalloc(ngx_cycle->pool, sizeof(u_char) * (len + 1)); /* KEY: /proxy/passwd */ /* since we don't need the "KEY: " ignore 5 + 1 extra u_char from last intro */ /* Optimization: we don't need to read the full key only the n chars included in key_partial */ ngx_read_file(&file, key_in_file, sizeof(u_char) * len, sizeof(ngx_http_file_cache_header_t) + sizeof(u_char) * 6); ngx_close_file(file.fd); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "http cache file \"%s\" key read: \"%s\"", path->data, key_in_file); if (ngx_strncasecmp(key_in_file, key_partial, len) == 0) { ngx_log_debug(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "match found, deleting file \"%s\"", path->data); remove_file = 1; } } if (remove_file && ngx_delete_file(path->data) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, ngx_delete_file_n " \"%s\" failed", path->data); } return NGX_OK; } ngx_int_t ngx_http_cache_purge_access_handler(ngx_http_request_t *r) { ngx_http_cache_purge_loc_conf_t *cplcf; cplcf = ngx_http_get_module_loc_conf(r, ngx_http_cache_purge_module); if (r->method_name.len != cplcf->conf->method.len || (ngx_strncmp(r->method_name.data, cplcf->conf->method.data, r->method_name.len))) { return cplcf->original_handler(r); } if ((cplcf->conf->access || cplcf->conf->access6) && ngx_http_cache_purge_access(cplcf->conf->access, cplcf->conf->access6, r->connection->sockaddr) != NGX_OK) { return NGX_HTTP_FORBIDDEN; } if (cplcf->handler == NULL) { return NGX_HTTP_NOT_FOUND; } return cplcf->handler(r); } ngx_int_t ngx_http_cache_purge_access(ngx_array_t *access, ngx_array_t *access6, struct sockaddr *s) { in_addr_t inaddr; ngx_in_cidr_t *a; ngx_uint_t i; # if (NGX_HAVE_INET6) struct in6_addr *inaddr6; ngx_in6_cidr_t *a6; u_char *p; ngx_uint_t n; # endif /* NGX_HAVE_INET6 */ switch (s->sa_family) { case AF_INET: if (access == NULL) { return NGX_DECLINED; } inaddr = ((struct sockaddr_in *) s)->sin_addr.s_addr; # if (NGX_HAVE_INET6) ipv4: # endif /* NGX_HAVE_INET6 */ a = access->elts; for (i = 0; i < access->nelts; i++) { if ((inaddr & a[i].mask) == a[i].addr) { return NGX_OK; } } return NGX_DECLINED; # if (NGX_HAVE_INET6) case AF_INET6: inaddr6 = &((struct sockaddr_in6 *) s)->sin6_addr; p = inaddr6->s6_addr; if (access && IN6_IS_ADDR_V4MAPPED(inaddr6)) { inaddr = p[12] << 24; inaddr += p[13] << 16; inaddr += p[14] << 8; inaddr += p[15]; inaddr = htonl(inaddr); goto ipv4; } if (access6 == NULL) { return NGX_DECLINED; } a6 = access6->elts; for (i = 0; i < access6->nelts; i++) { for (n = 0; n < 16; n++) { if ((p[n] & a6[i].mask.s6_addr[n]) != a6[i].addr.s6_addr[n]) { goto next; } } return NGX_OK; next: continue; } return NGX_DECLINED; # endif /* NGX_HAVE_INET6 */ } return NGX_DECLINED; } ngx_int_t ngx_http_cache_purge_send_response(ngx_http_request_t *r) { ngx_chain_t out; ngx_buf_t *b; ngx_str_t *key; ngx_int_t rc; size_t len; size_t body_len; size_t resp_tmpl_len; u_char *buf; u_char *buf_keydata; u_char *p; const char *resp_ct; size_t resp_ct_size; const char *resp_body; size_t resp_body_size; ngx_http_cache_purge_loc_conf_t *cplcf; cplcf = ngx_http_get_module_loc_conf(r, ngx_http_cache_purge_module); key = r->cache->keys.elts; buf_keydata = ngx_pcalloc(r->pool, key[0].len+1); if (buf_keydata == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } p = ngx_cpymem(buf_keydata, key[0].data, key[0].len); if (p == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } switch(cplcf->resptype) { case NGX_REPONSE_TYPE_JSON: resp_ct = ngx_http_cache_purge_content_type_json; resp_ct_size = ngx_http_cache_purge_content_type_json_size; resp_body = ngx_http_cache_purge_body_templ_json; resp_body_size = ngx_http_cache_purge_body_templ_json_size; break; case NGX_REPONSE_TYPE_XML: resp_ct = ngx_http_cache_purge_content_type_xml; resp_ct_size = ngx_http_cache_purge_content_type_xml_size; resp_body = ngx_http_cache_purge_body_templ_xml; resp_body_size = ngx_http_cache_purge_body_templ_xml_size; break; case NGX_REPONSE_TYPE_TEXT: resp_ct = ngx_http_cache_purge_content_type_text; resp_ct_size = ngx_http_cache_purge_content_type_text_size; resp_body = ngx_http_cache_purge_body_templ_text; resp_body_size = ngx_http_cache_purge_body_templ_text_size; break; default: case NGX_REPONSE_TYPE_HTML: resp_ct = ngx_http_cache_purge_content_type_html; resp_ct_size = ngx_http_cache_purge_content_type_html_size; resp_body = ngx_http_cache_purge_body_templ_html; resp_body_size = ngx_http_cache_purge_body_templ_html_size; break; } body_len = resp_body_size - 2 - 1; r->headers_out.content_type.len = resp_ct_size - 1; r->headers_out.content_type.data = (u_char *) resp_ct; resp_tmpl_len = body_len + key[0].len ; buf = ngx_pcalloc(r->pool, resp_tmpl_len); if (buf == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } p = ngx_snprintf(buf, resp_tmpl_len, resp_body , buf_keydata); if (p == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } len = body_len + key[0].len; 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, buf, resp_tmpl_len); 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); } # if (nginx_version >= 1007009) /* * Based on: ngx_http_upstream.c/ngx_http_upstream_cache_get * Copyright (C) Igor Sysoev * Copyright (C) Nginx, Inc. */ ngx_int_t ngx_http_cache_purge_cache_get(ngx_http_request_t *r, ngx_http_upstream_t *u, ngx_http_file_cache_t **cache) { ngx_str_t *name, val; ngx_uint_t i; ngx_http_file_cache_t **caches; if (u->conf->cache_zone) { *cache = u->conf->cache_zone->data; return NGX_OK; } if (ngx_http_complex_value(r, u->conf->cache_value, &val) != NGX_OK) { return NGX_ERROR; } if (val.len == 0 || (val.len == 3 && ngx_strncmp(val.data, "off", 3) == 0)) { return NGX_DECLINED; } caches = u->caches->elts; for (i = 0; i < u->caches->nelts; i++) { name = &caches[i]->shm_zone->shm.name; if (name->len == val.len && ngx_strncmp(name->data, val.data, val.len) == 0) { *cache = caches[i]; return NGX_OK; } } ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "cache \"%V\" not found", &val); return NGX_ERROR; } # endif /* nginx_version >= 1007009 */ ngx_int_t ngx_http_cache_purge_init(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); return NGX_OK; } void ngx_http_cache_purge_handler(ngx_http_request_t *r) { ngx_http_cache_purge_loc_conf_t *cplcf; ngx_int_t rc; # if (NGX_HAVE_FILE_AIO) if (r->aio) { return; } # endif cplcf = ngx_http_get_module_loc_conf(r, ngx_http_cache_purge_module); rc = NGX_OK; if (!cplcf->conf->purge_all && !ngx_http_cache_purge_is_partial(r)) { rc = ngx_http_file_cache_purge(r); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http file cache purge: %i, \"%s\"", rc, r->cache->file.name.data); } switch (rc) { case NGX_OK: r->write_event_handler = ngx_http_request_empty_handler; ngx_http_finalize_request(r, ngx_http_cache_purge_send_response(r)); return; case NGX_DECLINED: ngx_http_finalize_request(r, NGX_HTTP_PRECONDITION_FAILED); return; # if (NGX_HAVE_FILE_AIO) case NGX_AGAIN: r->write_event_handler = ngx_http_cache_purge_handler; return; # endif default: ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); } } ngx_int_t ngx_http_file_cache_purge(ngx_http_request_t *r) { ngx_http_file_cache_t *cache; ngx_http_cache_t *c; switch (ngx_http_file_cache_open(r)) { case NGX_OK: case NGX_HTTP_CACHE_STALE: # if (nginx_version >= 8001) \ || ((nginx_version < 8000) && (nginx_version >= 7060)) case NGX_HTTP_CACHE_UPDATING: # endif break; case NGX_DECLINED: return NGX_DECLINED; # if (NGX_HAVE_FILE_AIO) case NGX_AGAIN: return NGX_AGAIN; # endif default: return NGX_ERROR; } c = r->cache; cache = c->file_cache; /* * 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 (nginx_version >= 1000001) cache->sh->size -= c->node->fs_size; c->node->fs_size = 0; # else cache->sh->size -= (c->node->length + cache->bsize - 1) / cache->bsize; c->node->length = 0; # endif c->node->exists = 0; # if (nginx_version >= 8001) \ || ((nginx_version < 8000) && (nginx_version >= 7060)) c->node->updating = 0; # endif ngx_shmtx_unlock(&cache->shpool->mutex); 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; } void ngx_http_cache_purge_all(ngx_http_request_t *r, ngx_http_file_cache_t *cache) { ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "purge_all http in %s", cache->path->name.data); /* Walk the tree and remove all the files */ ngx_tree_ctx_t tree; tree.init_handler = NULL; tree.file_handler = ngx_http_purge_file_cache_delete_file; tree.pre_tree_handler = ngx_http_purge_file_cache_noop; tree.post_tree_handler = ngx_http_purge_file_cache_noop; tree.spec_handler = ngx_http_purge_file_cache_noop; tree.data = NULL; tree.alloc = 0; tree.log = ngx_cycle->log; ngx_walk_tree(&tree, &cache->path->name); } void ngx_http_cache_purge_partial(ngx_http_request_t *r, ngx_http_file_cache_t *cache) { ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "purge_partial http in %s", cache->path->name.data); u_char *key_partial; ngx_str_t *key; ngx_http_cache_t *c; ngx_uint_t len; c = r->cache; key = c->keys.elts; len = key[0].len; /* Only check the first key */ key_partial = ngx_pcalloc(r->pool, sizeof(u_char) * len); ngx_memcpy(key_partial, key[0].data, sizeof(u_char) * (len - 1)); /* Walk the tree and remove all the files matching key_partial */ ngx_tree_ctx_t tree; tree.init_handler = NULL; tree.file_handler = ngx_http_purge_file_cache_delete_partial_file; tree.pre_tree_handler = ngx_http_purge_file_cache_noop; tree.post_tree_handler = ngx_http_purge_file_cache_noop; tree.spec_handler = ngx_http_purge_file_cache_noop; tree.data = key_partial; tree.alloc = 0; tree.log = ngx_cycle->log; ngx_walk_tree(&tree, &cache->path->name); } ngx_int_t ngx_http_cache_purge_is_partial(ngx_http_request_t *r) { ngx_str_t *key; ngx_http_cache_t *c; c = r->cache; key = c->keys.elts; /* Only check the first key */ return c->keys.nelts > 0 // number of array elements && key[0].len > 0 // char length of the key && key[0].data[key[0].len - 1] == '*'; // is the last char an asterix char? } char * ngx_http_cache_purge_conf(ngx_conf_t *cf, ngx_http_cache_purge_conf_t *cpcf) { ngx_cidr_t cidr; ngx_in_cidr_t *access; # if (NGX_HAVE_INET6) ngx_in6_cidr_t *access6; # endif /* NGX_HAVE_INET6 */ ngx_str_t *value; ngx_int_t rc; ngx_uint_t i; ngx_uint_t from_position; from_position = 2; /* xxx_cache_purge on|off| [purge_all] [from all| [.. ]] */ value = cf->args->elts; if (ngx_strcmp(value[1].data, "off") == 0) { cpcf->enable = 0; return NGX_CONF_OK; } else if (ngx_strcmp(value[1].data, "on") == 0) { ngx_str_set(&cpcf->method, "PURGE"); } else { cpcf->method = value[1]; } if (cf->args->nelts < 4) { cpcf->enable = 1; return NGX_CONF_OK; } /* We will purge all the keys */ if (ngx_strcmp(value[from_position].data, "purge_all") == 0) { cpcf->purge_all = 1; from_position++; } /* sanity check */ if (ngx_strcmp(value[from_position].data, "from") != 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\", expected" " \"from\" keyword", &value[from_position]); return NGX_CONF_ERROR; } if (ngx_strcmp(value[from_position + 1].data, "all") == 0) { cpcf->enable = 1; return NGX_CONF_OK; } for (i = (from_position + 1); i < cf->args->nelts; i++) { rc = ngx_ptocidr(&value[i], &cidr); if (rc == NGX_ERROR) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"", &value[i]); return NGX_CONF_ERROR; } if (rc == NGX_DONE) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "low address bits of %V are meaningless", &value[i]); } switch (cidr.family) { case AF_INET: if (cpcf->access == NULL) { cpcf->access = ngx_array_create(cf->pool, cf->args->nelts - (from_position + 1), sizeof(ngx_in_cidr_t)); if (cpcf->access == NULL) { return NGX_CONF_ERROR; } } access = ngx_array_push(cpcf->access); if (access == NULL) { return NGX_CONF_ERROR; } access->mask = cidr.u.in.mask; access->addr = cidr.u.in.addr; break; # if (NGX_HAVE_INET6) case AF_INET6: if (cpcf->access6 == NULL) { cpcf->access6 = ngx_array_create(cf->pool, cf->args->nelts - (from_position + 1), sizeof(ngx_in6_cidr_t)); if (cpcf->access6 == NULL) { return NGX_CONF_ERROR; } } access6 = ngx_array_push(cpcf->access6); if (access6 == NULL) { return NGX_CONF_ERROR; } access6->mask = cidr.u.in6.mask; access6->addr = cidr.u.in6.addr; break; # endif /* NGX_HAVE_INET6 */ } } cpcf->enable = 1; return NGX_CONF_OK; } void ngx_http_cache_purge_merge_conf(ngx_http_cache_purge_conf_t *conf, ngx_http_cache_purge_conf_t *prev) { if (conf->enable == NGX_CONF_UNSET) { if (prev->enable == 1) { conf->enable = prev->enable; conf->method = prev->method; conf->purge_all = prev->purge_all; conf->access = prev->access; conf->access6 = prev->access6; } else { conf->enable = 0; } } } void * ngx_http_cache_purge_create_loc_conf(ngx_conf_t *cf) { ngx_http_cache_purge_loc_conf_t *conf; conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_cache_purge_loc_conf_t)); if (conf == NULL) { return NULL; } /* * set by ngx_pcalloc(): * * conf->*.method = { 0, NULL } * conf->*.access = NULL * conf->*.access6 = NULL * conf->handler = NULL * conf->original_handler = NULL */ # if (NGX_HTTP_FASTCGI) conf->fastcgi.enable = NGX_CONF_UNSET; # endif /* NGX_HTTP_FASTCGI */ # if (NGX_HTTP_PROXY) conf->proxy.enable = NGX_CONF_UNSET; # endif /* NGX_HTTP_PROXY */ # if (NGX_HTTP_SCGI) conf->scgi.enable = NGX_CONF_UNSET; # endif /* NGX_HTTP_SCGI */ # if (NGX_HTTP_UWSGI) conf->uwsgi.enable = NGX_CONF_UNSET; # endif /* NGX_HTTP_UWSGI */ conf->resptype = NGX_CONF_UNSET_UINT; conf->conf = NGX_CONF_UNSET_PTR; return conf; } char * ngx_http_cache_purge_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) { ngx_http_cache_purge_loc_conf_t *prev = parent; ngx_http_cache_purge_loc_conf_t *conf = child; ngx_http_core_loc_conf_t *clcf; # if (NGX_HTTP_FASTCGI) ngx_http_fastcgi_loc_conf_t *flcf; # endif /* NGX_HTTP_FASTCGI */ # if (NGX_HTTP_PROXY) ngx_http_proxy_loc_conf_t *plcf; # endif /* NGX_HTTP_PROXY */ # if (NGX_HTTP_SCGI) ngx_http_scgi_loc_conf_t *slcf; # endif /* NGX_HTTP_SCGI */ # if (NGX_HTTP_UWSGI) ngx_http_uwsgi_loc_conf_t *ulcf; # endif /* NGX_HTTP_UWSGI */ clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); ngx_conf_merge_uint_value(conf->resptype, prev->resptype, NGX_REPONSE_TYPE_HTML); # if (NGX_HTTP_FASTCGI) ngx_http_cache_purge_merge_conf(&conf->fastcgi, &prev->fastcgi); if (conf->fastcgi.enable && clcf->handler != NULL) { flcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_fastcgi_module); if (flcf->upstream.upstream || flcf->fastcgi_lengths) { conf->conf = &conf->fastcgi; conf->handler = flcf->upstream.cache ? ngx_http_fastcgi_cache_purge_handler : NULL; conf->original_handler = clcf->handler; clcf->handler = ngx_http_cache_purge_access_handler; return NGX_CONF_OK; } } # endif /* NGX_HTTP_FASTCGI */ # if (NGX_HTTP_PROXY) ngx_http_cache_purge_merge_conf(&conf->proxy, &prev->proxy); if (conf->proxy.enable && clcf->handler != NULL) { plcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_proxy_module); if (plcf->upstream.upstream || plcf->proxy_lengths) { conf->conf = &conf->proxy; conf->handler = plcf->upstream.cache ? ngx_http_proxy_cache_purge_handler : NULL; conf->original_handler = clcf->handler; clcf->handler = ngx_http_cache_purge_access_handler; return NGX_CONF_OK; } } # endif /* NGX_HTTP_PROXY */ # if (NGX_HTTP_SCGI) ngx_http_cache_purge_merge_conf(&conf->scgi, &prev->scgi); if (conf->scgi.enable && clcf->handler != NULL) { slcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_scgi_module); if (slcf->upstream.upstream || slcf->scgi_lengths) { conf->conf = &conf->scgi; conf->handler = slcf->upstream.cache ? ngx_http_scgi_cache_purge_handler : NULL; conf->original_handler = clcf->handler; clcf->handler = ngx_http_cache_purge_access_handler; return NGX_CONF_OK; } } # endif /* NGX_HTTP_SCGI */ # if (NGX_HTTP_UWSGI) ngx_http_cache_purge_merge_conf(&conf->uwsgi, &prev->uwsgi); if (conf->uwsgi.enable && clcf->handler != NULL) { ulcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_uwsgi_module); if (ulcf->upstream.upstream || ulcf->uwsgi_lengths) { conf->conf = &conf->uwsgi; conf->handler = ulcf->upstream.cache ? ngx_http_uwsgi_cache_purge_handler : NULL; conf->original_handler = clcf->handler; clcf->handler = ngx_http_cache_purge_access_handler; return NGX_CONF_OK; } } # endif /* NGX_HTTP_UWSGI */ ngx_conf_merge_ptr_value(conf->conf, prev->conf, NULL); if (conf->handler == NULL) { conf->handler = prev->handler; } if (conf->original_handler == NULL) { conf->original_handler = prev->original_handler; } return NGX_CONF_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 */ nginx-modules-ngx_cache_purge-a84b0f3/t/000077500000000000000000000000001437532150000202765ustar00rootroot00000000000000nginx-modules-ngx_cache_purge-a84b0f3/t/proxy1.t000066400000000000000000000056211437532150000217310ustar00rootroot00000000000000# vi:filetype=perl use lib 'lib'; use Test::Nginx::Socket; repeat_each(1); plan tests => repeat_each() * (blocks() * 4 + 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 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 4: < 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 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 5: < 0.8.3 or < 0.7.62 === TEST 3: purge from cache --- http_config eval: $::http_config --- config eval: $::config --- request PURGE /purge/proxy/passwd --- error_code: 200 --- response_headers Content-Type: text/html --- response_body_like: Successful purge --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 4: < 0.8.3 or < 0.7.62 === TEST 4: purge from empty cache --- http_config eval: $::http_config --- config eval: $::config --- request PURGE /purge/proxy/passwd --- error_code: 412 --- response_headers Content-Type: text/html --- response_body_like: 412 Precondition Failed --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 4: < 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 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 5: < 0.8.3 or < 0.7.62 === TEST 6: 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 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 5: < 0.8.3 or < 0.7.62 nginx-modules-ngx_cache_purge-a84b0f3/t/proxy1_vars.t000066400000000000000000000055301437532150000227630ustar00rootroot00000000000000# vi:filetype=perl use lib 'lib'; use Test::Nginx::Socket; repeat_each(1); plan tests => repeat_each() * (blocks() * 4 + 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_'; set $cache test_cache; location /proxy { proxy_pass $scheme://127.0.0.1:$server_port/etc/passwd; proxy_cache $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 $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 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx: 4: < 1.7.9 === 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 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx: 5: < 1.7.9 === TEST 3: purge from cache --- http_config eval: $::http_config --- config eval: $::config --- request PURGE /purge/proxy/passwd --- error_code: 200 --- response_headers Content-Type: text/html --- response_body_like: Successful purge --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx: 4: < 1.7.9 === TEST 4: purge from empty cache --- http_config eval: $::http_config --- config eval: $::config --- request PURGE /purge/proxy/passwd --- error_code: 412 --- response_headers Content-Type: text/html --- response_body_like: 412 Precondition Failed --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx: 4: < 1.7.9 === 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 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx: 5: < 1.7.9 === TEST 6: 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 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx: 5: < 1.7.9 nginx-modules-ngx_cache_purge-a84b0f3/t/proxy2.t000066400000000000000000000175171437532150000217410ustar00rootroot00000000000000# vi:filetype=perl use lib 'lib'; use Test::Nginx::Socket; repeat_each(1); plan tests => repeat_each() * (blocks() * 4 + 6 * 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_'; proxy_cache_purge on; 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; if ($uri) { } } location = /etc/passwd { root /; } _EOC_ our $config_allowed = <<'_EOC_'; proxy_cache_purge PURGE from 1.0.0.0/8 127.0.0.0/8 3.0.0.0/8; 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 = /etc/passwd { root /; } _EOC_ our $config_forbidden = <<'_EOC_'; proxy_cache_purge PURGE from 1.0.0.0/8; 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 = /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 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 4: < 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 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 5: < 0.8.3 or < 0.7.62 === TEST 3: purge from cache --- http_config eval: $::http_config --- config eval: $::config --- request PURGE /proxy/passwd --- error_code: 200 --- response_headers Content-Type: text/html --- response_body_like: Successful purge --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 4: < 0.8.3 or < 0.7.62 === TEST 4: purge from empty cache --- http_config eval: $::http_config --- config eval: $::config --- request PURGE /proxy/passwd --- error_code: 412 --- response_headers Content-Type: text/html --- response_body_like: 412 Precondition Failed --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 4: < 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 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 5: < 0.8.3 or < 0.7.62 === TEST 6: 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 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 5: < 0.8.3 or < 0.7.62 === TEST 7: purge from cache (PURGE allowed) --- http_config eval: $::http_config --- config eval: $::config_allowed --- request PURGE /proxy/passwd --- error_code: 200 --- response_headers Content-Type: text/html --- response_body_like: Successful purge --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 4: < 0.8.3 or < 0.7.62 === TEST 8: purge from empty cache (PURGE allowed) --- http_config eval: $::http_config --- config eval: $::config_allowed --- request PURGE /proxy/passwd --- error_code: 412 --- response_headers Content-Type: text/html --- response_body_like: 412 Precondition Failed --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 4: < 0.8.3 or < 0.7.62 === TEST 9: get from source (PURGE allowed) --- http_config eval: $::http_config --- config eval: $::config_allowed --- request GET /proxy/passwd --- error_code: 200 --- response_headers Content-Type: text/plain X-Cache-Status: MISS --- response_body_like: root --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 5: < 0.8.3 or < 0.7.62 === TEST 10: get from cache (PURGE allowed) --- http_config eval: $::http_config --- config eval: $::config_allowed --- request GET /proxy/passwd --- error_code: 200 --- response_headers Content-Type: text/plain X-Cache-Status: HIT --- response_body_like: root --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 5: < 0.8.3 or < 0.7.62 === TEST 11: purge from cache (PURGE not allowed) --- http_config eval: $::http_config --- config eval: $::config_forbidden --- request PURGE /proxy/passwd --- error_code: 403 --- response_headers Content-Type: text/html --- response_body_like: 403 Forbidden --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 4: < 0.8.3 or < 0.7.62 === TEST 12: get from cache (PURGE not allowed) --- http_config eval: $::http_config --- config eval: $::config_forbidden --- request GET /proxy/passwd --- error_code: 200 --- response_headers Content-Type: text/plain X-Cache-Status: HIT --- response_body_like: root --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 5: < 0.8.3 or < 0.7.62 === TEST 13: no cache (PURGE allowed) --- http_config eval: $::http_config --- config proxy_cache_purge PURGE from 1.0.0.0/8 127.0.0.0/8 3.0.0.0/8; location /proxy { proxy_pass $scheme://127.0.0.1:$server_port/etc/passwd; } location = /etc/passwd { root /; } --- request PURGE /proxy/passwd --- error_code: 404 --- response_headers Content-Type: text/html --- response_body_like: 404 Not Found --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 4: < 0.8.3 or < 0.7.62 === TEST 14: no cache (PURGE not allowed) --- http_config eval: $::http_config --- config proxy_cache_purge PURGE from 1.0.0.0/8; location /proxy { proxy_pass $scheme://127.0.0.1:$server_port/etc/passwd; } location = /etc/passwd { root /; } --- request PURGE /proxy/passwd --- error_code: 403 --- response_headers Content-Type: text/html --- response_body_like: 403 Forbidden --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 4: < 0.8.3 or < 0.7.62 === TEST 15: multiple cache purge directives --- http_config eval: $::http_config --- config fastcgi_cache_purge on; proxy_cache_purge on; 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; if ($uri) { } } location = /etc/passwd { root /; } --- request PURGE /proxy/passwd --- error_code: 200 --- response_headers Content-Type: text/html --- response_body_like: Successful purge --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 4: < 0.8.3 or < 0.7.62 nginx-modules-ngx_cache_purge-a84b0f3/t/proxy2_vars.t000066400000000000000000000174121437532150000227660ustar00rootroot00000000000000# vi:filetype=perl use lib 'lib'; use Test::Nginx::Socket; repeat_each(1); plan tests => repeat_each() * (blocks() * 4 + 6 * 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_'; proxy_cache_purge on; set $cache test_cache; location /proxy { proxy_pass $scheme://127.0.0.1:$server_port/etc/passwd; proxy_cache $cache; proxy_cache_key $uri$is_args$args; proxy_cache_valid 3m; add_header X-Cache-Status $upstream_cache_status; if ($uri) { } } location = /etc/passwd { root /; } _EOC_ our $config_allowed = <<'_EOC_'; proxy_cache_purge PURGE from 1.0.0.0/8 127.0.0.0/8 3.0.0.0/8; set $cache test_cache; location /proxy { proxy_pass $scheme://127.0.0.1:$server_port/etc/passwd; proxy_cache $cache; proxy_cache_key $uri$is_args$args; proxy_cache_valid 3m; add_header X-Cache-Status $upstream_cache_status; } location = /etc/passwd { root /; } _EOC_ our $config_forbidden = <<'_EOC_'; proxy_cache_purge PURGE from 1.0.0.0/8; set $cache test_cache; location /proxy { proxy_pass $scheme://127.0.0.1:$server_port/etc/passwd; proxy_cache $cache; proxy_cache_key $uri$is_args$args; proxy_cache_valid 3m; add_header X-Cache-Status $upstream_cache_status; } 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 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx: 4: < 1.7.9 === 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 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx: 5: < 1.7.9 === TEST 3: purge from cache --- http_config eval: $::http_config --- config eval: $::config --- request PURGE /proxy/passwd --- error_code: 200 --- response_headers Content-Type: text/html --- response_body_like: Successful purge --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx: 4: < 1.7.9 === TEST 4: purge from empty cache --- http_config eval: $::http_config --- config eval: $::config --- request PURGE /proxy/passwd --- error_code: 412 --- response_headers Content-Type: text/html --- response_body_like: 412 Precondition Failed --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx: 4: < 1.7.9 === 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 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx: 5: < 1.7.9 === TEST 6: 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 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx: 5: < 1.7.9 === TEST 7: purge from cache (PURGE allowed) --- http_config eval: $::http_config --- config eval: $::config_allowed --- request PURGE /proxy/passwd --- error_code: 200 --- response_headers Content-Type: text/html --- response_body_like: Successful purge --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx: 4: < 1.7.9 === TEST 8: purge from empty cache (PURGE allowed) --- http_config eval: $::http_config --- config eval: $::config_allowed --- request PURGE /proxy/passwd --- error_code: 412 --- response_headers Content-Type: text/html --- response_body_like: 412 Precondition Failed --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx: 4: < 1.7.9 === TEST 9: get from source (PURGE allowed) --- http_config eval: $::http_config --- config eval: $::config_allowed --- request GET /proxy/passwd --- error_code: 200 --- response_headers Content-Type: text/plain X-Cache-Status: MISS --- response_body_like: root --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx: 5: < 1.7.9 === TEST 10: get from cache (PURGE allowed) --- http_config eval: $::http_config --- config eval: $::config_allowed --- request GET /proxy/passwd --- error_code: 200 --- response_headers Content-Type: text/plain X-Cache-Status: HIT --- response_body_like: root --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx: 5: < 1.7.9 === TEST 11: purge from cache (PURGE not allowed) --- http_config eval: $::http_config --- config eval: $::config_forbidden --- request PURGE /proxy/passwd --- error_code: 403 --- response_headers Content-Type: text/html --- response_body_like: 403 Forbidden --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx: 4: < 1.7.9 === TEST 12: get from cache (PURGE not allowed) --- http_config eval: $::http_config --- config eval: $::config_forbidden --- request GET /proxy/passwd --- error_code: 200 --- response_headers Content-Type: text/plain X-Cache-Status: HIT --- response_body_like: root --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx: 5: < 1.7.9 === TEST 13: no cache (PURGE allowed) --- http_config eval: $::http_config --- config proxy_cache_purge PURGE from 1.0.0.0/8 127.0.0.0/8 3.0.0.0/8; location /proxy { proxy_pass $scheme://127.0.0.1:$server_port/etc/passwd; } location = /etc/passwd { root /; } --- request PURGE /proxy/passwd --- error_code: 404 --- response_headers Content-Type: text/html --- response_body_like: 404 Not Found --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx: 4: < 1.7.9 === TEST 14: no cache (PURGE not allowed) --- http_config eval: $::http_config --- config proxy_cache_purge PURGE from 1.0.0.0/8; location /proxy { proxy_pass $scheme://127.0.0.1:$server_port/etc/passwd; } location = /etc/passwd { root /; } --- request PURGE /proxy/passwd --- error_code: 403 --- response_headers Content-Type: text/html --- response_body_like: 403 Forbidden --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx: 4: < 1.7.9 === TEST 15: multiple cache purge directives --- http_config eval: $::http_config --- config fastcgi_cache_purge on; proxy_cache_purge on; set $cache test_cache; location /proxy { proxy_pass $scheme://127.0.0.1:$server_port/etc/passwd; proxy_cache $cache; proxy_cache_key $uri$is_args$args; proxy_cache_valid 3m; add_header X-Cache-Status $upstream_cache_status; if ($uri) { } } location = /etc/passwd { root /; } --- request PURGE /proxy/passwd --- error_code: 200 --- response_headers Content-Type: text/html --- response_body_like: Successful purge --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx: 4: < 1.7.9 nginx-modules-ngx_cache_purge-a84b0f3/t/proxy3.t000066400000000000000000000056561437532150000217430ustar00rootroot00000000000000# vi:filetype=perl use lib 'lib'; use Test::Nginx::Socket; repeat_each(1); plan tests => 32; 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; proxy_cache_purge PURGE purge_all from 1.0.0.0/8 127.0.0.0/8 3.0.0.0/8; } location = /etc/passwd { root /; } _EOC_ worker_connections(128); no_shuffle(); run_tests(); no_diff(); __DATA__ === TEST 1: prepare passwd --- 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 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ === TEST 2: prepare shadow --- http_config eval: $::http_config --- config eval: $::config --- request GET /proxy/shadow --- error_code: 200 --- response_headers Content-Type: text/plain --- response_body_like: root --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ === TEST 3: get from cache passwd --- 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 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ === TEST 4: get from cache shadow --- http_config eval: $::http_config --- config eval: $::config --- request GET /proxy/shadow --- error_code: 200 --- response_headers Content-Type: text/plain X-Cache-Status: HIT --- response_body_like: root --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ === TEST 5: purge from cache --- http_config eval: $::http_config --- config eval: $::config --- request PURGE /proxy/any --- error_code: 200 --- response_headers Content-Type: text/html --- response_body_like: Successful purge --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ === TEST 6: get from empty cache passwd --- 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 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ === TEST 7: get from empty cache shadow --- http_config eval: $::http_config --- config eval: $::config --- request GET /proxy/shadow --- error_code: 200 --- response_headers Content-Type: text/plain X-Cache-Status: MISS --- response_body_like: root --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ nginx-modules-ngx_cache_purge-a84b0f3/t/proxy4.t000066400000000000000000000070221437532150000217310ustar00rootroot00000000000000# vi:filetype=perl use lib 'lib'; use Test::Nginx::Socket; repeat_each(1); plan tests => 41; 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; proxy_cache_purge PURGE from 1.0.0.0/8 127.0.0.0/8 3.0.0.0/8; } location = /etc/passwd { root /; } _EOC_ worker_connections(128); no_shuffle(); run_tests(); no_diff(); __DATA__ === TEST 1: prepare passwd --- 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 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ === TEST 2: prepare passwd2 --- http_config eval: $::http_config --- config eval: $::config --- request GET /proxy/passwd2 --- error_code: 200 --- response_headers Content-Type: text/plain --- response_body_like: root --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ === TEST 3: prepare shadow --- http_config eval: $::http_config --- config eval: $::config --- request GET /proxy/shadow --- error_code: 200 --- response_headers Content-Type: text/plain --- response_body_like: root --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ === TEST 4: get from cache passwd --- 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 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ === TEST 5: get from cache passwd2 --- http_config eval: $::http_config --- config eval: $::config --- request GET /proxy/passwd2 --- error_code: 200 --- response_headers Content-Type: text/plain X-Cache-Status: HIT --- response_body_like: root --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ === TEST 6: purge from cache --- http_config eval: $::http_config --- config eval: $::config --- request PURGE /proxy/pass* --- error_code: 200 --- response_headers Content-Type: text/html --- response_body_like: Successful purge --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ === TEST 7: get from empty cache passwd --- 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 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ === TEST 8: get from empty cache passwd2 --- http_config eval: $::http_config --- config eval: $::config --- request GET /proxy/passwd2 --- error_code: 200 --- response_headers Content-Type: text/plain X-Cache-Status: MISS --- response_body_like: root --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ === TEST 9: get from cache shadow --- http_config eval: $::http_config --- config eval: $::config --- request GET /proxy/shadow --- error_code: 200 --- response_headers Content-Type: text/plain X-Cache-Status: HIT --- response_body_like: root --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ nginx-modules-ngx_cache_purge-a84b0f3/t/resptype1.t000066400000000000000000000131651437532150000224250ustar00rootroot00000000000000# vi:filetype=perl use lib 'lib'; use Test::Nginx::Socket; repeat_each(1); plan tests => repeat_each() * (blocks() * 4 + 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_'; cache_purge_response_type json; 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; cache_purge_response_type html; } location ~ /purge_json(/.*) { proxy_cache_purge test_cache $1$is_args$args; } location ~ /purge_xml(/.*) { proxy_cache_purge test_cache $1$is_args$args; cache_purge_response_type xml; } location ~ /purge_text(/.*) { proxy_cache_purge test_cache $1$is_args$args; cache_purge_response_type text; } 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 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 4: < 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 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 5: < 0.8.3 or < 0.7.62 === TEST 3: purge from cache --- http_config eval: $::http_config --- config eval: $::config --- request PURGE /purge/proxy/passwd --- error_code: 200 --- response_headers Content-Type: text/html --- response_body_like: Successful purge --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 4: < 0.8.3 or < 0.7.62 === TEST 4: purge from empty cache --- http_config eval: $::http_config --- config eval: $::config --- request PURGE /purge/proxy/passwd --- error_code: 412 --- response_headers Content-Type: text/html --- response_body_like: 412 Precondition Failed --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 4: < 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 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 5: < 0.8.3 or < 0.7.62 === TEST 6: 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 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 5: < 0.8.3 or < 0.7.62 === TEST 7-prepare: prepare purge --- http_config eval: $::http_config --- config eval: $::config --- request GET /proxy/passwd?t=7 --- error_code: 200 --- response_headers Content-Type: text/plain --- response_body_like: root --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 4: < 0.8.3 or < 0.7.62 === TEST 7: get a JSON response after purge from cache --- http_config eval: $::http_config --- config eval: $::config --- request PURGE /purge_json/proxy/passwd?t=7 --- error_code: 200 --- response_headers Content-Type: application/json --- response_body_like: {\"Key\": \"\/proxy\/passwd\?t=7\" --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 4: < 0.8.3 or < 0.7.62 === TEST 8-prepare: prepare purge --- http_config eval: $::http_config --- config eval: $::config --- request GET /proxy/passwd?t=8 --- error_code: 200 --- response_headers Content-Type: text/plain --- response_body_like: root --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 4: < 0.8.3 or < 0.7.62 === TEST 8: get a XML response after purge from cache --- http_config eval: $::http_config --- config eval: $::config --- request PURGE /purge_xml/proxy/passwd?t=8 --- error_code: 200 --- response_headers Content-Type: text/xml --- response_body_like: \<\?xml version=\"1.0\" encoding=\"UTF-8\"\?><\!\[CDATA\[\/proxy\/passwd\?t=8\]\]><\/Key> --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 4: < 0.8.3 or < 0.7.62 === TEST 9-prepare: prepare purge --- http_config eval: $::http_config --- config eval: $::config --- request GET /proxy/passwd?t=9 --- error_code: 200 --- response_headers Content-Type: text/plain --- response_body_like: root --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 4: < 0.8.3 or < 0.7.62 === TEST 9: get a TEXT response after purge from cache --- http_config eval: $::http_config --- config eval: $::config --- request PURGE /purge_text/proxy/passwd?t=9 --- error_code: 200 --- response_headers Content-Type: text/plain --- response_body_like: Key --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ --- skip_nginx2: 4: < 0.8.3 or < 0.7.62