@@ -113,6 +113,8 @@ ngx_int_t ngx_http_file_cache_purge(ngx_http_request_t *r);
113113
114114
115115void ngx_http_cache_purge_all (ngx_http_request_t * r , ngx_http_file_cache_t * cache );
116+ void ngx_http_cache_purge_partial (ngx_http_request_t * r , ngx_http_file_cache_t * cache );
117+ ngx_int_t ngx_http_cache_purge_is_partial (ngx_http_request_t * r );
116118
117119char * ngx_http_cache_purge_conf (ngx_conf_t * cf ,
118120 ngx_http_cache_purge_conf_t * cpcf );
@@ -429,6 +431,14 @@ ngx_http_fastcgi_cache_purge_handler(ngx_http_request_t *r)
429431 if (cplcf -> conf -> purge_all ) {
430432 ngx_http_cache_purge_all (r , cache );
431433 }
434+ else {
435+ if (ngx_http_cache_purge_is_partial (r )) {
436+ ngx_log_debug (NGX_LOG_DEBUG_HTTP , r -> connection -> log , 0 ,
437+ "http file cache purge with partial enabled" );
438+
439+ ngx_http_cache_purge_partial (r , cache );
440+ }
441+ }
432442
433443# if (nginx_version >= 8011 )
434444 r -> main -> count ++ ;
@@ -707,6 +717,14 @@ ngx_http_proxy_cache_purge_handler(ngx_http_request_t *r)
707717 if (cplcf -> conf -> purge_all ) {
708718 ngx_http_cache_purge_all (r , cache );
709719 }
720+ else {
721+ if (ngx_http_cache_purge_is_partial (r )) {
722+ ngx_log_debug (NGX_LOG_DEBUG_HTTP , r -> connection -> log , 0 ,
723+ "http file cache purge with partial enabled" );
724+
725+ ngx_http_cache_purge_partial (r , cache );
726+ }
727+ }
710728
711729# if (nginx_version >= 8011 )
712730 r -> main -> count ++ ;
@@ -927,6 +945,14 @@ ngx_http_scgi_cache_purge_handler(ngx_http_request_t *r)
927945 if (cplcf -> conf -> purge_all ) {
928946 ngx_http_cache_purge_all (r , cache );
929947 }
948+ else {
949+ if (ngx_http_cache_purge_is_partial (r )) {
950+ ngx_log_debug (NGX_LOG_DEBUG_HTTP , r -> connection -> log , 0 ,
951+ "http file cache purge with partial enabled" );
952+
953+ ngx_http_cache_purge_partial (r , cache );
954+ }
955+ }
930956
931957# if (nginx_version >= 8011 )
932958 r -> main -> count ++ ;
@@ -1170,6 +1196,14 @@ ngx_http_uwsgi_cache_purge_handler(ngx_http_request_t *r)
11701196 if (cplcf -> conf -> purge_all ) {
11711197 ngx_http_cache_purge_all (r , cache );
11721198 }
1199+ else {
1200+ if (ngx_http_cache_purge_is_partial (r )) {
1201+ ngx_log_debug (NGX_LOG_DEBUG_HTTP , r -> connection -> log , 0 ,
1202+ "http file cache purge with partial enabled" );
1203+
1204+ ngx_http_cache_purge_partial (r , cache );
1205+ }
1206+ }
11731207
11741208# if (nginx_version >= 8011 )
11751209 r -> main -> count ++ ;
@@ -1202,6 +1236,59 @@ ngx_http_purge_file_cache_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
12021236 return NGX_OK ;
12031237}
12041238
1239+
1240+ static ngx_int_t
1241+ ngx_http_purge_file_cache_delete_partial_file (ngx_tree_ctx_t * ctx , ngx_str_t * path )
1242+ {
1243+ u_char * key_partial ;
1244+ u_char * key_in_file ;
1245+ ngx_uint_t len ;
1246+ ngx_flag_t remove_file = 0 ;
1247+
1248+ key_partial = ctx -> data ;
1249+ len = ngx_strlen (key_partial );
1250+
1251+ // if key_partial is empty always match, because is a *
1252+ if (len == 0 ) {
1253+ ngx_log_debug (NGX_LOG_DEBUG_HTTP , ctx -> log , 0 ,
1254+ "empty key_partial, forcing deletion" );
1255+ remove_file = 1 ;
1256+ }
1257+ else {
1258+ ngx_file_t file ;
1259+
1260+ file .offset = file .sys_offset = 0 ;
1261+ file .fd = ngx_open_file (path -> data , NGX_FILE_RDONLY , NGX_FILE_OPEN ,
1262+ NGX_FILE_DEFAULT_ACCESS );
1263+
1264+ // 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
1265+ key_in_file = ngx_pcalloc (ngx_cycle -> pool , sizeof (u_char ) * (len + 1 ));
1266+
1267+ // KEY: /proxy/passwd
1268+ // since we don't need the "KEY: " ignore 5 + 1 extra u_char from last intro
1269+ // Optimization: we don't need to read the full key only the n chars included in key_partial
1270+ ngx_read_file (& file , key_in_file , sizeof (u_char ) * len ,
1271+ sizeof (ngx_http_file_cache_header_t ) + sizeof (u_char ) * 6 );
1272+ ngx_close_file (file .fd );
1273+
1274+ ngx_log_debug2 (NGX_LOG_DEBUG_HTTP , ctx -> log , 0 ,
1275+ "http cache file \"%s\" key read: \"%s\"" , path -> data , key_in_file );
1276+
1277+ if (ngx_strncasecmp (key_in_file , key_partial , len ) == 0 ) {
1278+ ngx_log_debug (NGX_LOG_DEBUG_HTTP , ctx -> log , 0 ,
1279+ "match found, deleting file \"%s\"" , path -> data );
1280+ remove_file = 1 ;
1281+ }
1282+ }
1283+
1284+ if (remove_file && ngx_delete_file (path -> data ) == NGX_FILE_ERROR ) {
1285+ ngx_log_error (NGX_LOG_CRIT , ctx -> log , ngx_errno ,
1286+ ngx_delete_file_n " \"%s\" failed" , path -> data );
1287+ }
1288+
1289+ return NGX_OK ;
1290+ }
1291+
12051292ngx_int_t
12061293ngx_http_cache_purge_access_handler (ngx_http_request_t * r )
12071294{
@@ -1469,15 +1556,13 @@ ngx_http_cache_purge_handler(ngx_http_request_t *r)
14691556# endif
14701557
14711558 cplcf = ngx_http_get_module_loc_conf (r , ngx_http_cache_purge_module );
1472- if (cplcf -> conf -> purge_all ) {
1473- rc = NGX_OK ;
1474- }
1475- else {
1559+ rc = NGX_OK ;
1560+ if (!cplcf -> conf -> purge_all && !ngx_http_cache_purge_is_partial (r )) {
14761561 rc = ngx_http_file_cache_purge (r );
14771562
14781563 ngx_log_debug2 (NGX_LOG_DEBUG_HTTP , r -> connection -> log , 0 ,
1479- "http file cache purge: %i, \"%s\"" ,
1480- rc , r -> cache -> file .name .data );
1564+ "http file cache purge: %i, \"%s\"" ,
1565+ rc , r -> cache -> file .name .data );
14811566 }
14821567
14831568 switch (rc ) {
@@ -1578,13 +1663,59 @@ ngx_http_cache_purge_all(ngx_http_request_t *r, ngx_http_file_cache_t *cache) {
15781663 tree .pre_tree_handler = ngx_http_purge_file_cache_noop ;
15791664 tree .post_tree_handler = ngx_http_purge_file_cache_noop ;
15801665 tree .spec_handler = ngx_http_purge_file_cache_noop ;
1581- tree .data = cache ;
1666+ tree .data = NULL ;
15821667 tree .alloc = 0 ;
15831668 tree .log = ngx_cycle -> log ;
15841669
15851670 ngx_walk_tree (& tree , & cache -> path -> name );
15861671}
15871672
1673+ void
1674+ ngx_http_cache_purge_partial (ngx_http_request_t * r , ngx_http_file_cache_t * cache ) {
1675+ ngx_log_debug (NGX_LOG_DEBUG_HTTP , r -> connection -> log , 0 ,
1676+ "purge_partial http in %s" ,
1677+ cache -> path -> name .data );
1678+
1679+ u_char * key_partial ;
1680+ ngx_str_t * key ;
1681+ ngx_http_cache_t * c ;
1682+ ngx_uint_t len ;
1683+
1684+ c = r -> cache ;
1685+ key = c -> keys .elts ;
1686+ len = key [0 ].len ;
1687+
1688+ // Only check the first key
1689+ key_partial = ngx_pcalloc (r -> pool , sizeof (u_char ) * len );
1690+ ngx_memcpy (key_partial , key [0 ].data , sizeof (u_char ) * (len - 1 ));
1691+
1692+ // Walk the tree and remove all the files matching key_partial
1693+ ngx_tree_ctx_t tree ;
1694+ tree .init_handler = NULL ;
1695+ tree .file_handler = ngx_http_purge_file_cache_delete_partial_file ;
1696+ tree .pre_tree_handler = ngx_http_purge_file_cache_noop ;
1697+ tree .post_tree_handler = ngx_http_purge_file_cache_noop ;
1698+ tree .spec_handler = ngx_http_purge_file_cache_noop ;
1699+ tree .data = key_partial ;
1700+ tree .alloc = 0 ;
1701+ tree .log = ngx_cycle -> log ;
1702+
1703+ ngx_walk_tree (& tree , & cache -> path -> name );
1704+ }
1705+
1706+ ngx_int_t
1707+ ngx_http_cache_purge_is_partial (ngx_http_request_t * r )
1708+ {
1709+ ngx_str_t * key ;
1710+ ngx_http_cache_t * c ;
1711+
1712+ c = r -> cache ;
1713+ key = c -> keys .elts ;
1714+
1715+ // Only check the first key
1716+ return key [0 ].data [key [0 ].len - 1 ] == '*' ;
1717+ }
1718+
15881719char *
15891720ngx_http_cache_purge_conf (ngx_conf_t * cf , ngx_http_cache_purge_conf_t * cpcf )
15901721{
0 commit comments