diff --git a/.astylerc b/.astylerc new file mode 100644 index 0000000..3c009b3 --- /dev/null +++ b/.astylerc @@ -0,0 +1,22 @@ +# 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: +formatted \ No newline at end of file diff --git a/.format.sh b/.format.sh new file mode 100755 index 0000000..75209eb --- /dev/null +++ b/.format.sh @@ -0,0 +1,14 @@ +#!/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); \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9b27d9f --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.t linguist-language=Text \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..9df875e --- /dev/null +++ b/.travis.yml @@ -0,0 +1,50 @@ +sudo: required +os: linux +dist: trusty +language: c +compiler: + - gcc + - clang +cache: + apt: true + directories: + - download-cache +env: + global: + - JOBS=4 + - NGINX_VERSION=1.12.0 + - NGINX_PREFIX=/opt/nginx + - OPENSSL_PREFIX=/opt/ssl + - OPENSSL_LIB=$OPENSSL_PREFIX/lib + - OPENSSL_INC=$OPENSSL_PREFIX/include + - OPENSSL_VER=1.0.2k + +before_install: + - sudo apt-get install -qq -y cpanminus + +install: + - if [ ! -d /opt ]; then mkdir /opt; fi + - if [ ! -d download-cache ]; then mkdir download-cache; fi + - if [ ! -f download-cache/nginx-$NGINX_VERSION.tar.gz ]; then wget -O download-cache/nginx-$NGINX_VERSION.tar.gz http://nginx.org/download/nginx-$NGINX_VERSION.tar.gz; fi + - if [ ! -f download-cache/openssl-$OPENSSL_VER.tar.gz ]; then wget -O download-cache/openssl-$OPENSSL_VER.tar.gz https://www.openssl.org/source/openssl-$OPENSSL_VER.tar.gz; fi + - git clone https://github.com/openresty/test-nginx.git + +script: + - cd test-nginx/ && sudo cpanm . && cd .. + - tar zxf download-cache/openssl-$OPENSSL_VER.tar.gz + - cd openssl-$OPENSSL_VER/ + - ./config shared --prefix=$OPENSSL_PREFIX -DPURIFY > build.log 2>&1 || (cat build.log && exit 1) + - make -j$JOBS > build.log 2>&1 || (cat build.log && exit 1) + - sudo make PATH=$PATH install_sw > build.log 2>&1 || (cat build.log && exit 1) + - cd .. + - tar zxf download-cache/nginx-$NGINX_VERSION.tar.gz + - cd nginx-$NGINX_VERSION/ + - ./configure --prefix=$NGINX_PREFIX --with-debug --add-module=$PWD/.. > build.log 2>&1 || (cat build.log && exit 1) + - make -j$JOBS > build.log 2>&1 || (cat build.log && exit 1) + - sudo make install > build.log 2>&1 || (cat build.log && exit 1) + - cd .. + - export PATH=$NGINX_PREFIX/sbin:$PATH +# - export LD_LIBRARY_PATH=$LD_LIBRARY_PATH + - nginx -V + - ldd `which nginx` + - prove t diff --git a/CHANGES b/CHANGES index ead3cdf..ba96d46 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,24 @@ +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+. diff --git a/README.md b/README.md index 3b42e32..5812c90 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ About ===== `ngx_cache_purge` is `nginx` module which adds ability to purge content from -`FastCGI`, `proxy`, `SCGI` and `uWSGI` caches. +`FastCGI`, `proxy`, `SCGI` and `uWSGI` caches. A purge operation removes the +content with the same cache key as the purge request has. Sponsors @@ -18,7 +19,7 @@ Configuration directives (same location syntax) =============================================== fastcgi_cache_purge ------------------- -* **syntax**: `fastcgi_cache_purge on|off| [from all| [.. ]]` +* **syntax**: `fastcgi_cache_purge on|off| [purge_all] [from all| [.. ]]` * **default**: `none` * **context**: `http`, `server`, `location` @@ -27,7 +28,7 @@ Allow purging of selected pages from `FastCGI`'s cache. proxy_cache_purge ----------------- -* **syntax**: `proxy_cache_purge on|off| [from all| [.. ]]` +* **syntax**: `proxy_cache_purge on|off| [purge_all] [from all| [.. ]]` * **default**: `none` * **context**: `http`, `server`, `location` @@ -36,7 +37,7 @@ Allow purging of selected pages from `proxy`'s cache. scgi_cache_purge ---------------- -* **syntax**: `scgi_cache_purge on|off| [from all| [.. ]]` +* **syntax**: `scgi_cache_purge on|off| [purge_all] [from all| [.. ]]` * **default**: `none` * **context**: `http`, `server`, `location` @@ -45,7 +46,7 @@ Allow purging of selected pages from `SCGI`'s cache. uwsgi_cache_purge ----------------- -* **syntax**: `uwsgi_cache_purge on|off| [from all| [.. ]]` +* **syntax**: `uwsgi_cache_purge on|off| [purge_all] [from all| [.. ]]` * **default**: `none` * **context**: `http`, `server`, `location` @@ -89,6 +90,50 @@ uwsgi_cache_purge 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. + + + +Purging multiple cache entries with the same cache key +====================================================== +Caching requests that use the "Vary" header may result in multiple cache +entries with the same cache key. For example, when using the request header +"Vary: Accept-Encoding", a separate cache entry (with different file hash) +will be stored for each content encoding (like compressed or uncompressed), +but they will all have the exact same cache key. + +Trying to purge such cached content will fail unless both the "Vary" header +is specified in the purge request, plus all headers as listed in the "Vary" +header, with the exact same values as used when the request was cached. + +To be able to purge *all* pages with the same cache key, specify the key with +a "$" at the end, like this: + + curl -X PURGE /page$ + +This will purge all content with the exact key "/page" from the cache. + + Sample configuration (same location syntax) =========================================== @@ -106,6 +151,22 @@ Sample configuration (same location syntax) } +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; + } + } + } + + Sample configuration (separate location syntax) =============================================== http { @@ -126,6 +187,61 @@ Sample configuration (separate location syntax) } } +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_purge tmpcache $1$is_args$args; + cache_purge_response_type xml; + } + + location ~ /purge2(/.*) { # json + allow 127.0.0.1; + deny all; + proxy_cache_purge tmpcache $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_purge tmpcache $1$is_args$args; + } + + location ~ /purge2(/.*) { #html + allow 127.0.0.1; + deny all; + proxy_cache_purge tmpcache $1$is_args$args; + cache_purge_response_type html; + } + } + } + + Testing ======= diff --git a/config b/config index 34f42ec..b900680 100644 --- a/config +++ b/config @@ -15,7 +15,17 @@ if [ "$HTTP_UWSGI" = "YES" ]; then fi ngx_addon_name=ngx_http_cache_purge_module -HTTP_MODULES="$HTTP_MODULES ngx_http_cache_purge_module" -NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_cache_purge_module.c" +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 diff --git a/ngx_cache_purge_module.c b/ngx_cache_purge_module.c index 62d3818..9b65827 100644 --- a/ngx_cache_purge_module.c +++ b/ngx_cache_purge_module.c @@ -27,22 +27,47 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include #include +#include #include #include #ifndef nginx_version -#error This module cannot be build against an unknown 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; @@ -64,93 +89,123 @@ typedef struct { 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_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_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_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_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); + 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); + 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); + 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); + 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); + 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 }, + { + 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 }, + { + 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 }, + { + 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_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 }, -# endif /* NGX_HTTP_UWSGI */ - ngx_null_command + ngx_null_command }; static ngx_http_module_t ngx_http_cache_purge_module_ctx = { @@ -182,20 +237,6 @@ ngx_module_t ngx_http_cache_purge_module = { NGX_MODULE_V1_PADDING }; -static char ngx_http_cache_purge_success_page_top[] = -"" CRLF -"Successful purge" CRLF -"" CRLF -"

Successful purge

" CRLF -; - -static char ngx_http_cache_purge_success_page_tail[] = -CRLF "
" CRLF -"
" NGINX_VER "
" CRLF -"" CRLF -"" CRLF -; - # if (NGX_HTTP_FASTCGI) extern ngx_module_t ngx_http_fastcgi_module; @@ -258,8 +299,7 @@ typedef struct { char * ngx_http_fastcgi_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, - void *conf) -{ + void *conf) { ngx_http_compile_complex_value_t ccv; ngx_http_cache_purge_loc_conf_t *cplcf; ngx_http_core_loc_conf_t *clcf; @@ -290,7 +330,7 @@ ngx_http_fastcgi_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, if (flcf->upstream.cache > 0) # else if (flcf->upstream.cache != NGX_CONF_UNSET_PTR - && flcf->upstream.cache != NULL) + && flcf->upstream.cache != NULL) # endif /* nginx_version >= 1007009 */ { return "is incompatible with \"fastcgi_cache\""; @@ -302,10 +342,9 @@ ngx_http_fastcgi_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, if (flcf->upstream.store > 0 # if (nginx_version < 1007009) - || flcf->upstream.store_lengths + || flcf->upstream.store_lengths # endif /* nginx_version >= 1007009 */ - ) - { + ) { return "is incompatible with \"fastcgi_store\""; } @@ -329,7 +368,7 @@ ngx_http_fastcgi_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, if (cv.lengths != NULL) { flcf->upstream.cache_value = ngx_palloc(cf->pool, - sizeof(ngx_http_complex_value_t)); + sizeof(ngx_http_complex_value_t)); if (flcf->upstream.cache_value == NULL) { return NGX_CONF_ERROR; } @@ -339,7 +378,7 @@ ngx_http_fastcgi_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, } else { flcf->upstream.cache_zone = ngx_shared_memory_add(cf, &value[1], 0, - &ngx_http_fastcgi_module); + &ngx_http_fastcgi_module); if (flcf->upstream.cache_zone == NULL) { return NGX_CONF_ERROR; } @@ -348,7 +387,7 @@ ngx_http_fastcgi_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, # else flcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0, - &ngx_http_fastcgi_module); + &ngx_http_fastcgi_module); if (flcf->upstream.cache == NULL) { return NGX_CONF_ERROR; } @@ -370,19 +409,20 @@ ngx_http_fastcgi_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, 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_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; + ngx_http_fastcgi_main_conf_t *fmcf; + ngx_int_t rc; # endif /* nginx_version >= 1007009 */ if (ngx_http_upstream_create(r) != NGX_OK) { @@ -414,6 +454,19 @@ ngx_http_fastcgi_cache_purge_handler(ngx_http_request_t *r) 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 @@ -492,7 +545,11 @@ typedef struct { 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; @@ -529,8 +586,7 @@ typedef struct { } 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_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; @@ -561,7 +617,7 @@ ngx_http_proxy_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) if (plcf->upstream.cache > 0) # else if (plcf->upstream.cache != NGX_CONF_UNSET_PTR - && plcf->upstream.cache != NULL) + && plcf->upstream.cache != NULL) # endif /* nginx_version >= 1007009 */ { return "is incompatible with \"proxy_cache\""; @@ -573,10 +629,9 @@ ngx_http_proxy_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) if (plcf->upstream.store > 0 # if (nginx_version < 1007009) - || plcf->upstream.store_lengths + || plcf->upstream.store_lengths # endif /* nginx_version >= 1007009 */ - ) - { + ) { return "is incompatible with \"proxy_store\""; } @@ -600,7 +655,7 @@ ngx_http_proxy_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) if (cv.lengths != NULL) { plcf->upstream.cache_value = ngx_palloc(cf->pool, - sizeof(ngx_http_complex_value_t)); + sizeof(ngx_http_complex_value_t)); if (plcf->upstream.cache_value == NULL) { return NGX_CONF_ERROR; } @@ -610,7 +665,7 @@ ngx_http_proxy_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } else { plcf->upstream.cache_zone = ngx_shared_memory_add(cf, &value[1], 0, - &ngx_http_proxy_module); + &ngx_http_proxy_module); if (plcf->upstream.cache_zone == NULL) { return NGX_CONF_ERROR; } @@ -619,7 +674,7 @@ ngx_http_proxy_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) # else plcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0, - &ngx_http_proxy_module); + &ngx_http_proxy_module); if (plcf->upstream.cache == NULL) { return NGX_CONF_ERROR; } @@ -641,19 +696,20 @@ ngx_http_proxy_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 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_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; + ngx_http_proxy_main_conf_t *pmcf; + ngx_int_t rc; # endif /* nginx_version >= 1007009 */ if (ngx_http_upstream_create(r) != NGX_OK) { @@ -685,6 +741,19 @@ ngx_http_proxy_cache_purge_handler(ngx_http_request_t *r) 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 @@ -742,8 +811,7 @@ typedef struct { } 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_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; @@ -774,7 +842,7 @@ ngx_http_scgi_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) if (slcf->upstream.cache > 0) # else if (slcf->upstream.cache != NGX_CONF_UNSET_PTR - && slcf->upstream.cache != NULL) + && slcf->upstream.cache != NULL) # endif /* nginx_version >= 1007009 */ { return "is incompatible with \"scgi_cache\""; @@ -786,10 +854,9 @@ ngx_http_scgi_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) if (slcf->upstream.store > 0 # if (nginx_version < 1007009) - || slcf->upstream.store_lengths + || slcf->upstream.store_lengths # endif /* nginx_version >= 1007009 */ - ) - { + ) { return "is incompatible with \"scgi_store\""; } @@ -813,7 +880,7 @@ ngx_http_scgi_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) if (cv.lengths != NULL) { slcf->upstream.cache_value = ngx_palloc(cf->pool, - sizeof(ngx_http_complex_value_t)); + sizeof(ngx_http_complex_value_t)); if (slcf->upstream.cache_value == NULL) { return NGX_CONF_ERROR; } @@ -823,7 +890,7 @@ ngx_http_scgi_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } else { slcf->upstream.cache_zone = ngx_shared_memory_add(cf, &value[1], 0, - &ngx_http_scgi_module); + &ngx_http_scgi_module); if (slcf->upstream.cache_zone == NULL) { return NGX_CONF_ERROR; } @@ -832,7 +899,7 @@ ngx_http_scgi_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) # else slcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0, - &ngx_http_scgi_module); + &ngx_http_scgi_module); if (slcf->upstream.cache == NULL) { return NGX_CONF_ERROR; } @@ -854,19 +921,20 @@ ngx_http_scgi_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 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_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; + ngx_http_scgi_main_conf_t *smcf; + ngx_int_t rc; # endif /* nginx_version >= 1007009 */ if (ngx_http_upstream_create(r) != NGX_OK) { @@ -898,6 +966,19 @@ ngx_http_scgi_cache_purge_handler(ngx_http_request_t *r) 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 @@ -978,8 +1059,7 @@ typedef struct { } 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_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; @@ -1010,7 +1090,7 @@ ngx_http_uwsgi_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) if (ulcf->upstream.cache > 0) # else if (ulcf->upstream.cache != NGX_CONF_UNSET_PTR - && ulcf->upstream.cache != NULL) + && ulcf->upstream.cache != NULL) # endif /* nginx_version >= 1007009 */ { return "is incompatible with \"uwsgi_cache\""; @@ -1022,10 +1102,9 @@ ngx_http_uwsgi_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) if (ulcf->upstream.store > 0 # if (nginx_version < 1007009) - || ulcf->upstream.store_lengths + || ulcf->upstream.store_lengths # endif /* nginx_version >= 1007009 */ - ) - { + ) { return "is incompatible with \"uwsgi_store\""; } @@ -1049,7 +1128,7 @@ ngx_http_uwsgi_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) if (cv.lengths != NULL) { ulcf->upstream.cache_value = ngx_palloc(cf->pool, - sizeof(ngx_http_complex_value_t)); + sizeof(ngx_http_complex_value_t)); if (ulcf->upstream.cache_value == NULL) { return NGX_CONF_ERROR; } @@ -1059,7 +1138,7 @@ ngx_http_uwsgi_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } else { ulcf->upstream.cache_zone = ngx_shared_memory_add(cf, &value[1], 0, - &ngx_http_uwsgi_module); + &ngx_http_uwsgi_module); if (ulcf->upstream.cache_zone == NULL) { return NGX_CONF_ERROR; } @@ -1068,7 +1147,7 @@ ngx_http_uwsgi_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) # else ulcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0, - &ngx_http_uwsgi_module); + &ngx_http_uwsgi_module); if (ulcf->upstream.cache == NULL) { return NGX_CONF_ERROR; } @@ -1090,19 +1169,21 @@ ngx_http_uwsgi_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 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_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; + ngx_http_uwsgi_main_conf_t *umcf; + ngx_int_t rc; # endif /* nginx_version >= 1007009 */ if (ngx_http_upstream_create(r) != NGX_OK) { @@ -1134,6 +1215,19 @@ ngx_http_uwsgi_cache_purge_handler(ngx_http_request_t *r) 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 @@ -1144,25 +1238,146 @@ ngx_http_uwsgi_cache_purge_handler(ngx_http_request_t *r) } # endif /* NGX_HTTP_UWSGI */ -ngx_int_t -ngx_http_cache_purge_access_handler(ngx_http_request_t *r) + +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); + 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))) - { + || (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) - { + && ngx_http_cache_purge_access(cplcf->conf->access, + cplcf->conf->access6, + r->connection->sockaddr) != NGX_OK) { return NGX_HTTP_FORBIDDEN; } @@ -1175,8 +1390,7 @@ ngx_http_cache_purge_access_handler(ngx_http_request_t *r) ngx_int_t ngx_http_cache_purge_access(ngx_array_t *access, ngx_array_t *access6, - struct sockaddr *s) -{ + struct sockaddr *s) { in_addr_t inaddr; ngx_in_cidr_t *a; ngx_uint_t i; @@ -1196,7 +1410,7 @@ ngx_http_cache_purge_access(ngx_array_t *access, ngx_array_t *access6, inaddr = ((struct sockaddr_in *) s)->sin_addr.s_addr; # if (NGX_HAVE_INET6) - ipv4: +ipv4: # endif /* NGX_HAVE_INET6 */ a = access->elts; @@ -1237,7 +1451,7 @@ ngx_http_cache_purge_access(ngx_array_t *access, ngx_array_t *access6, return NGX_OK; - next: +next: continue; } @@ -1249,23 +1463,89 @@ ngx_http_cache_purge_access(ngx_array_t *access, ngx_array_t *access6, } ngx_int_t -ngx_http_cache_purge_send_response(ngx_http_request_t *r) -{ +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; - len = sizeof(ngx_http_cache_purge_success_page_top) - 1 - + sizeof(ngx_http_cache_purge_success_page_tail) - 1 - + sizeof("
Key : ") - 1 + sizeof(CRLF "
Path: ") - 1 - + key[0].len + r->cache->file.name.len; + 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 + r->cache->file.name.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, r->cache->file.name.data); + p = ngx_snprintf(buf, resp_tmpl_len, resp_body , buf_keydata, buf_keydata); + if (p == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + len = body_len + key[0].len + r->cache->file.name.len; - r->headers_out.content_type.len = sizeof("text/html") - 1; - r->headers_out.content_type.data = (u_char *) "text/html"; r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = len; @@ -1280,25 +1560,17 @@ ngx_http_cache_purge_send_response(ngx_http_request_t *r) if (b == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } + out.buf = b; out.next = NULL; - b->last = ngx_cpymem(b->last, ngx_http_cache_purge_success_page_top, - sizeof(ngx_http_cache_purge_success_page_top) - 1); - b->last = ngx_cpymem(b->last, "
Key : ", sizeof("
Key : ") - 1); - b->last = ngx_cpymem(b->last, key[0].data, key[0].len); - b->last = ngx_cpymem(b->last, CRLF "
Path: ", - sizeof(CRLF "
Path: ") - 1); - b->last = ngx_cpymem(b->last, r->cache->file.name.data, - r->cache->file.name.len); - b->last = ngx_cpymem(b->last, ngx_http_cache_purge_success_page_tail, - sizeof(ngx_http_cache_purge_success_page_tail) - 1); + b->last = 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 rc; } return ngx_http_output_filter(r, &out); @@ -1313,8 +1585,7 @@ ngx_http_cache_purge_send_response(ngx_http_request_t *r) */ 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_http_file_cache_t **cache) { ngx_str_t *name, val; ngx_uint_t i; ngx_http_file_cache_t **caches; @@ -1329,8 +1600,7 @@ ngx_http_cache_purge_cache_get(ngx_http_request_t *r, ngx_http_upstream_t *u, } if (val.len == 0 - || (val.len == 3 && ngx_strncmp(val.data, "off", 3) == 0)) - { + || (val.len == 3 && ngx_strncmp(val.data, "off", 3) == 0)) { return NGX_DECLINED; } @@ -1340,8 +1610,7 @@ ngx_http_cache_purge_cache_get(ngx_http_request_t *r, ngx_http_upstream_t *u, name = &caches[i]->shm_zone->shm.name; if (name->len == val.len - && ngx_strncmp(name->data, val.data, val.len) == 0) - { + && ngx_strncmp(name->data, val.data, val.len) == 0) { *cache = caches[i]; return NGX_OK; } @@ -1357,8 +1626,7 @@ ngx_http_cache_purge_cache_get(ngx_http_request_t *r, ngx_http_upstream_t *u, 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_complex_value_t *cache_key) { ngx_http_cache_t *c; ngx_str_t *key; ngx_int_t rc; @@ -1399,8 +1667,8 @@ ngx_http_cache_purge_init(ngx_http_request_t *r, ngx_http_file_cache_t *cache, } void -ngx_http_cache_purge_handler(ngx_http_request_t *r) -{ +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) @@ -1409,11 +1677,15 @@ ngx_http_cache_purge_handler(ngx_http_request_t *r) } # endif - rc = ngx_http_file_cache_purge(r); + 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); + 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: @@ -1421,7 +1693,7 @@ ngx_http_cache_purge_handler(ngx_http_request_t *r) ngx_http_finalize_request(r, ngx_http_cache_purge_send_response(r)); return; case NGX_DECLINED: - ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND); + ngx_http_finalize_request(r, NGX_HTTP_PRECONDITION_FAILED); return; # if (NGX_HAVE_FILE_AIO) case NGX_AGAIN: @@ -1434,8 +1706,7 @@ ngx_http_cache_purge_handler(ngx_http_request_t *r) } ngx_int_t -ngx_http_file_cache_purge(ngx_http_request_t *r) -{ +ngx_http_file_cache_purge(ngx_http_request_t *r) { ngx_http_file_cache_t *cache; ngx_http_cache_t *c; @@ -1499,9 +1770,83 @@ ngx_http_file_cache_purge(ngx_http_request_t *r) 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 */ + if (key[0].data[key[0].len - 1] == '$') { + /* Check key for exact match (key terminated by newline) */ + key_partial = ngx_pcalloc(r->pool, sizeof(u_char) * (len + 1)); + ngx_memcpy(key_partial, key[0].data, sizeof(u_char) * (len - 1)); + key_partial[len - 1] = '\n'; + } else { + /* Check key for prefix match */ + 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 (key[0].data[key[0].len - 1] == '*' || + key[0].data[key[0].len - 1] == '$'); +} + char * -ngx_http_cache_purge_conf(ngx_conf_t *cf, ngx_http_cache_purge_conf_t *cpcf) -{ +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) @@ -1510,7 +1855,11 @@ ngx_http_cache_purge_conf(ngx_conf_t *cf, ngx_http_cache_purge_conf_t *cpcf) 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) { @@ -1529,20 +1878,27 @@ ngx_http_cache_purge_conf(ngx_conf_t *cf, ngx_http_cache_purge_conf_t *cpcf) 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[2].data, "from") != 0) { + 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[2]); + " \"from\" keyword", &value[from_position]); return NGX_CONF_ERROR; } - if (ngx_strcmp(value[3].data, "all") == 0) { + if (ngx_strcmp(value[from_position + 1].data, "all") == 0) { cpcf->enable = 1; return NGX_CONF_OK; } - for (i = 3; i < cf->args->nelts; i++) { + for (i = (from_position + 1); i < cf->args->nelts; i++) { rc = ngx_ptocidr(&value[i], &cidr); if (rc == NGX_ERROR) { @@ -1560,7 +1916,7 @@ ngx_http_cache_purge_conf(ngx_conf_t *cf, ngx_http_cache_purge_conf_t *cpcf) switch (cidr.family) { case AF_INET: if (cpcf->access == NULL) { - cpcf->access = ngx_array_create(cf->pool, cf->args->nelts - 3, + 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; @@ -1580,7 +1936,7 @@ ngx_http_cache_purge_conf(ngx_conf_t *cf, ngx_http_cache_purge_conf_t *cpcf) # if (NGX_HAVE_INET6) case AF_INET6: if (cpcf->access6 == NULL) { - cpcf->access6 = ngx_array_create(cf->pool, cf->args->nelts - 3, + 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; @@ -1607,15 +1963,14 @@ ngx_http_cache_purge_conf(ngx_conf_t *cf, ngx_http_cache_purge_conf_t *cpcf) void ngx_http_cache_purge_merge_conf(ngx_http_cache_purge_conf_t *conf, - ngx_http_cache_purge_conf_t *prev) -{ + 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; } @@ -1623,8 +1978,7 @@ ngx_http_cache_purge_merge_conf(ngx_http_cache_purge_conf_t *conf, } void * -ngx_http_cache_purge_create_loc_conf(ngx_conf_t *cf) -{ +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)); @@ -1655,14 +2009,15 @@ ngx_http_cache_purge_create_loc_conf(ngx_conf_t *cf) 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_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; @@ -1681,6 +2036,8 @@ ngx_http_cache_purge_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) 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); @@ -1690,7 +2047,7 @@ ngx_http_cache_purge_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) if (flcf->upstream.upstream || flcf->fastcgi_lengths) { conf->conf = &conf->fastcgi; conf->handler = flcf->upstream.cache - ? ngx_http_fastcgi_cache_purge_handler : NULL; + ? ngx_http_fastcgi_cache_purge_handler : NULL; conf->original_handler = clcf->handler; clcf->handler = ngx_http_cache_purge_access_handler; @@ -1709,7 +2066,7 @@ ngx_http_cache_purge_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) if (plcf->upstream.upstream || plcf->proxy_lengths) { conf->conf = &conf->proxy; conf->handler = plcf->upstream.cache - ? ngx_http_proxy_cache_purge_handler : NULL; + ? ngx_http_proxy_cache_purge_handler : NULL; conf->original_handler = clcf->handler; clcf->handler = ngx_http_cache_purge_access_handler; @@ -1728,7 +2085,7 @@ ngx_http_cache_purge_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) if (slcf->upstream.upstream || slcf->scgi_lengths) { conf->conf = &conf->scgi; conf->handler = slcf->upstream.cache - ? ngx_http_scgi_cache_purge_handler : NULL; + ? ngx_http_scgi_cache_purge_handler : NULL; conf->original_handler = clcf->handler; clcf->handler = ngx_http_cache_purge_access_handler; @@ -1746,7 +2103,7 @@ ngx_http_cache_purge_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) if (ulcf->upstream.upstream || ulcf->uwsgi_lengths) { conf->conf = &conf->uwsgi; conf->handler = ulcf->upstream.cache - ? ngx_http_uwsgi_cache_purge_handler : NULL; + ? ngx_http_uwsgi_cache_purge_handler : NULL; conf->original_handler = clcf->handler; clcf->handler = ngx_http_cache_purge_access_handler; diff --git a/t/proxy1.t b/t/proxy1.t index e5c0054..fefe9b8 100644 --- a/t/proxy1.t +++ b/t/proxy1.t @@ -92,10 +92,10 @@ qr/\[(warn|error|crit|alert|emerg)\]/ --- config eval: $::config --- request PURGE /purge/proxy/passwd ---- error_code: 404 +--- error_code: 412 --- response_headers Content-Type: text/html ---- response_body_like: 404 Not Found +--- response_body_like: 412 Precondition Failed --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ diff --git a/t/proxy1_vars.t b/t/proxy1_vars.t index af3553d..b9da04b 100644 --- a/t/proxy1_vars.t +++ b/t/proxy1_vars.t @@ -94,10 +94,10 @@ qr/\[(warn|error|crit|alert|emerg)\]/ --- config eval: $::config --- request PURGE /purge/proxy/passwd ---- error_code: 404 +--- error_code: 412 --- response_headers Content-Type: text/html ---- response_body_like: 404 Not Found +--- response_body_like: 412 Precondition Failed --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ diff --git a/t/proxy2.t b/t/proxy2.t index c07b042..4c7e19f 100644 --- a/t/proxy2.t +++ b/t/proxy2.t @@ -124,10 +124,10 @@ qr/\[(warn|error|crit|alert|emerg)\]/ --- config eval: $::config --- request PURGE /proxy/passwd ---- error_code: 404 +--- error_code: 412 --- response_headers Content-Type: text/html ---- response_body_like: 404 Not Found +--- response_body_like: 412 Precondition Failed --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ @@ -190,10 +190,10 @@ qr/\[(warn|error|crit|alert|emerg)\]/ --- config eval: $::config_allowed --- request PURGE /proxy/passwd ---- error_code: 404 +--- error_code: 412 --- response_headers Content-Type: text/html ---- response_body_like: 404 Not Found +--- response_body_like: 412 Precondition Failed --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ diff --git a/t/proxy2_vars.t b/t/proxy2_vars.t index d09c08e..08e3e1e 100644 --- a/t/proxy2_vars.t +++ b/t/proxy2_vars.t @@ -127,10 +127,10 @@ qr/\[(warn|error|crit|alert|emerg)\]/ --- config eval: $::config --- request PURGE /proxy/passwd ---- error_code: 404 +--- error_code: 412 --- response_headers Content-Type: text/html ---- response_body_like: 404 Not Found +--- response_body_like: 412 Precondition Failed --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ @@ -193,10 +193,10 @@ qr/\[(warn|error|crit|alert|emerg)\]/ --- config eval: $::config_allowed --- request PURGE /proxy/passwd ---- error_code: 404 +--- error_code: 412 --- response_headers Content-Type: text/html ---- response_body_like: 404 Not Found +--- response_body_like: 412 Precondition Failed --- timeout: 10 --- no_error_log eval qr/\[(warn|error|crit|alert|emerg)\]/ diff --git a/t/proxy3.t b/t/proxy3.t new file mode 100644 index 0000000..c8d71d9 --- /dev/null +++ b/t/proxy3.t @@ -0,0 +1,140 @@ +# 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)\]/ diff --git a/t/proxy4.t b/t/proxy4.t new file mode 100644 index 0000000..e4d2f30 --- /dev/null +++ b/t/proxy4.t @@ -0,0 +1,168 @@ +# 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)\]/ diff --git a/t/resptype1.t b/t/resptype1.t new file mode 100644 index 0000000..816c3ff --- /dev/null +++ b/t/resptype1.t @@ -0,0 +1,245 @@ +# 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 + +