Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 28 additions & 6 deletions ngx_cache_purge_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -2887,6 +2887,12 @@ ngx_http_cache_purge_cache_get(ngx_http_request_t *r, ngx_http_upstream_t *u,
return NGX_OK;
}

if (u->conf->cache_value == NULL) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"ngx_cache_purge: no cache configured for this location");
return NGX_HTTP_NOT_FOUND;
}

if (ngx_http_complex_value(r, u->conf->cache_value, &val) != NGX_OK) {
return NGX_ERROR;
}
Expand Down Expand Up @@ -3348,14 +3354,21 @@ ngx_http_cache_purge_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
*
* was_set_* captures whether each protocol's purge directive was
* explicitly present in THIS location block BEFORE merging from the
* parent. This is the only reliable way to distinguish two cases:
* parent. Together with clcf->noname it distinguishes three cases:
*
* Case A -- explicit (enable == 1 before merge):
* proxy_cache_purge is in this location. clcf->handler is the
* real upstream handler (e.g. ngx_http_proxy_handler set by
* proxy_pass). Save it as original_handler and install ours.
*
* Case B -- inherited (enable == NGX_CONF_UNSET before merge):
* Case B -- inherited into a named location (enable ==
* NGX_CONF_UNSET, clcf->noname == 0):
* proxy_cache_purge is set at the server level or in an enclosing
* location. clcf->handler is this location's own handler, as in
* case A.
*
* Case C -- inherited into an anonymous location (enable ==
* NGX_CONF_UNSET, clcf->noname == 1):
* This is an anonymous if-child location synthesised by nginx when
* it encounters an "if" block. The if-block has no handler
* directive, so clcf->handler is NULL. Saving NULL as
Expand Down Expand Up @@ -3405,7 +3418,7 @@ ngx_http_cache_purge_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
if (conf->fastcgi.enable) {
conf->conf = &conf->fastcgi;
conf->handler = ngx_http_fastcgi_cache_purge_handler;
conf->original_handler = was_set_fastcgi
conf->original_handler = (was_set_fastcgi || !clcf->noname)
? clcf->handler
: prev->original_handler;
clcf->handler = ngx_http_cache_purge_access_handler;
Expand All @@ -3414,12 +3427,21 @@ ngx_http_cache_purge_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
# endif

# if (NGX_HTTP_PROXY)
if (conf->proxy.enable == NGX_CONF_UNSET
&& conf->proxy_separate_zone == NULL
&& conf->proxy_separate_value == NULL)
{
conf->proxy_separate_zone = prev->proxy_separate_zone;
conf->proxy_separate_value = prev->proxy_separate_value;
conf->proxy_separate_key = prev->proxy_separate_key;
}

ngx_http_cache_purge_merge_conf(&conf->proxy, &prev->proxy);

if (conf->proxy.enable) {
conf->conf = &conf->proxy;
conf->handler = ngx_http_proxy_cache_purge_handler;
conf->original_handler = was_set_proxy
conf->original_handler = (was_set_proxy || !clcf->noname)
? clcf->handler
: prev->original_handler;
clcf->handler = ngx_http_cache_purge_access_handler;
Expand All @@ -3433,7 +3455,7 @@ ngx_http_cache_purge_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
if (conf->scgi.enable) {
conf->conf = &conf->scgi;
conf->handler = ngx_http_scgi_cache_purge_handler;
conf->original_handler = was_set_scgi
conf->original_handler = (was_set_scgi || !clcf->noname)
? clcf->handler
: prev->original_handler;
clcf->handler = ngx_http_cache_purge_access_handler;
Expand All @@ -3447,7 +3469,7 @@ ngx_http_cache_purge_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
if (conf->uwsgi.enable) {
conf->conf = &conf->uwsgi;
conf->handler = ngx_http_uwsgi_cache_purge_handler;
conf->original_handler = was_set_uwsgi
conf->original_handler = (was_set_uwsgi || !clcf->noname)
? clcf->handler
: prev->original_handler;
clcf->handler = ngx_http_cache_purge_access_handler;
Expand Down
100 changes: 100 additions & 0 deletions t/basic.t
Original file line number Diff line number Diff line change
Expand Up @@ -499,3 +499,103 @@ X-Trigger-If: 1
["X-Cache-Status: MISS", "X-Cache-Status: HIT"]
--- no_error_log
[error]

=== TEST 21: PURGE in a location without proxy_cache returns 404 (was segfault)
# The inline form is accepted in a location that has no proxy_cache, so
# upstream.cache_zone and upstream.cache_value are both NULL at request
# time. Evaluating the NULL complex value crashed the worker.
--- http_config eval: $::HttpConfig
--- config
location /nocache {
proxy_pass http://backend/origin;
proxy_cache_purge PURGE from 127.0.0.1;
}
location /origin {
return 200 "ok";
}
--- request
PURGE /nocache/t21
--- error_code: 404
--- error_log
no cache configured for this location
--- no_error_log
[alert]

=== TEST 22: PURGE inherited from server level into a location without proxy_cache
--- http_config eval: $::HttpConfig
--- config
proxy_cache_purge PURGE from 127.0.0.1;
location /nocache {
proxy_pass http://backend/origin;
}
location /origin {
return 200 "ok";
}
--- request
PURGE /nocache/t22
--- error_code: 404
--- error_log
no cache configured for this location
--- no_error_log
[alert]

=== TEST 23: separate-location purge whose key is built inside an if block
# The if block creates an anonymous child location. The zone and key of
# the 3-arg form must be inherited by it, otherwise a request that takes
# the if-branch falls through to the inline code path with no cache
# configured (segfault before the NULL check, 404 after it).
--- http_config eval: $::HttpConfig
--- config
location /cache {
proxy_pass http://backend/origin;
proxy_cache cache_zone;
proxy_cache_key "$uri$is_args$args";
proxy_cache_valid 200 1m;
}
location ~ ^/purge(?<purge_uri>/.*)$ {
set $purge_args "";
if ($args != "") {
set $purge_args "?$args";
}
proxy_cache_purge cache_zone "$purge_uri$purge_args";
}
location /origin {
return 200 "if-separate";
}
--- request eval
[
"GET /cache/if23",
"GET /cache/if23?cno=1",
"GET /purge/cache/if23",
"GET /purge/cache/if23?cno=1",
"GET /purge/cache/if23?cno=1"
]
--- error_code eval
[200, 200, 200, 200, 412]
--- no_error_log
[alert]

=== TEST 24: non-PURGE request in a location that inherits the purge directive from the server level
# original_handler must come from the location's own clcf->handler
# (ngx_http_proxy_handler here). Only an anonymous "if" / "limit_except"
# child location has to take it from its parent.
--- http_config eval: $::HttpConfig
--- config
proxy_cache_purge PURGE from 127.0.0.1;
location /cache {
proxy_pass http://backend/origin;
proxy_cache cache_zone;
proxy_cache_key "$uri";
proxy_cache_valid 200 1m;
}
location /origin {
return 200 "inherited";
}
--- request eval
["GET /cache/t24", "PURGE /cache/t24", "PURGE /cache/t24"]
--- error_code eval
[200, 200, 412]
--- response_body eval
["inherited", qr/purged/i, qr/412/]
--- no_error_log
[alert]
Loading