| /* |
| * Copyright (c) 2009-2012, FRiCKLE <info@frickle.com> |
| * Copyright (c) 2009-2012, Piotr Sikora <piotr.sikora@frickle.com> |
| * 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 <nginx.h> |
| #include <ngx_config.h> |
| #include <ngx_core.h> |
| #include <ngx_http.h> |
| |
| |
| #if (NGX_HTTP_CACHE) |
| |
| typedef struct { |
| ngx_flag_t enable; |
| ngx_str_t method; |
| 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_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 */ |
| |
| 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); |
| 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); |
| |
| 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_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 |
| }; |
| |
| static char ngx_http_cache_purge_success_page_top[] = |
| "<html>" CRLF |
| "<head><title>Successful purge</title></head>" CRLF |
| "<body bgcolor=\"white\">" CRLF |
| "<center><h1>Successful purge</h1>" CRLF |
| ; |
| |
| static char ngx_http_cache_purge_success_page_tail[] = |
| CRLF "</center>" CRLF |
| "<hr><center>" NGINX_VER "</center>" CRLF |
| "</body>" CRLF |
| "</html>" CRLF |
| ; |
| |
| # if (NGX_HTTP_FASTCGI) |
| extern ngx_module_t ngx_http_fastcgi_module; |
| |
| typedef struct { |
| ngx_http_upstream_conf_t upstream; |
| |
| ngx_str_t index; |
| |
| ngx_array_t *flushes; |
| ngx_array_t *params_len; |
| ngx_array_t *params; |
| ngx_array_t *params_source; |
| ngx_array_t *catch_stderr; |
| |
| ngx_array_t *fastcgi_lengths; |
| ngx_array_t *fastcgi_values; |
| |
| # if defined(nginx_version) && (nginx_version >= 8040) |
| ngx_hash_t headers_hash; |
| ngx_uint_t header_params; |
| # endif /* nginx_version >= 8040 */ |
| |
| # if defined(nginx_version) && (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; |
| |
| 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 (flcf->upstream.cache != NGX_CONF_UNSET_PTR |
| && flcf->upstream.cache != NULL) |
| { |
| return "is incompatible with \"fastcgi_cache\""; |
| } |
| |
| if (flcf->upstream.upstream || flcf->fastcgi_lengths) { |
| return "is incompatible with \"fastcgi_pass\""; |
| } |
| |
| if (flcf->upstream.store > 0 || flcf->upstream.store_lengths) { |
| return "is incompatible with \"fastcgi_store\""; |
| } |
| |
| value = cf->args->elts; |
| |
| /* set fastcgi_cache part */ |
| flcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0, |
| &ngx_http_fastcgi_module); |
| if (flcf->upstream.cache == NULL) { |
| return NGX_CONF_ERROR; |
| } |
| |
| /* set fastcgi_cache_key part */ |
| ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); |
| |
| ccv.cf = cf; |
| ccv.value = &value[2]; |
| ccv.complex_value = &flcf->cache_key; |
| |
| if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { |
| return NGX_CONF_ERROR; |
| } |
| |
| /* set handler */ |
| clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); |
| |
| cplcf->fastcgi.enable = 0; |
| clcf->handler = ngx_http_fastcgi_cache_purge_handler; |
| |
| return NGX_CONF_OK; |
| } |
| |
| ngx_int_t |
| ngx_http_fastcgi_cache_purge_handler(ngx_http_request_t *r) |
| { |
| ngx_http_fastcgi_loc_conf_t *flcf; |
| |
| flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); |
| |
| if (ngx_http_cache_purge_init(r, flcf->upstream.cache->data, |
| &flcf->cache_key) |
| != NGX_OK) |
| { |
| return NGX_HTTP_INTERNAL_SERVER_ERROR; |
| } |
| |
| # if defined(nginx_version) && (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; |
| |
| typedef struct { |
| ngx_http_upstream_conf_t upstream; |
| |
| ngx_array_t *flushes; |
| ngx_array_t *body_set_len; |
| ngx_array_t *body_set; |
| ngx_array_t *headers_set_len; |
| ngx_array_t *headers_set; |
| ngx_hash_t headers_set_hash; |
| |
| ngx_array_t *headers_source; |
| # if defined(nginx_version) && (nginx_version < 8040) |
| ngx_array_t *headers_names; |
| # endif /* nginx_version < 8040 */ |
| |
| ngx_array_t *proxy_lengths; |
| ngx_array_t *proxy_values; |
| |
| ngx_array_t *redirects; |
| # if defined(nginx_version) && (nginx_version >= 1001015) |
| ngx_array_t *cookie_domains; |
| ngx_array_t *cookie_paths; |
| # endif /* nginx_version >= 1001015 */ |
| |
| ngx_str_t body_source; |
| |
| ngx_str_t method; |
| ngx_str_t location; |
| ngx_str_t url; |
| |
| ngx_http_complex_value_t cache_key; |
| |
| ngx_http_proxy_vars_t vars; |
| |
| ngx_flag_t redirect; |
| |
| # if defined(nginx_version) && (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; |
| } 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; |
| |
| 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 (plcf->upstream.cache != NGX_CONF_UNSET_PTR |
| && plcf->upstream.cache != NULL) |
| { |
| return "is incompatible with \"proxy_cache\""; |
| } |
| |
| if (plcf->upstream.upstream || plcf->proxy_lengths) { |
| return "is incompatible with \"proxy_pass\""; |
| } |
| |
| if (plcf->upstream.store > 0 || plcf->upstream.store_lengths) { |
| return "is incompatible with \"proxy_store\""; |
| } |
| |
| value = cf->args->elts; |
| |
| /* set proxy_cache part */ |
| plcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0, |
| &ngx_http_proxy_module); |
| if (plcf->upstream.cache == NULL) { |
| return NGX_CONF_ERROR; |
| } |
| |
| /* set proxy_cache_key part */ |
| ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); |
| |
| ccv.cf = cf; |
| ccv.value = &value[2]; |
| ccv.complex_value = &plcf->cache_key; |
| |
| if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { |
| return NGX_CONF_ERROR; |
| } |
| |
| /* set handler */ |
| clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); |
| |
| cplcf->proxy.enable = 0; |
| clcf->handler = ngx_http_proxy_cache_purge_handler; |
| |
| return NGX_CONF_OK; |
| } |
| |
| ngx_int_t |
| ngx_http_proxy_cache_purge_handler(ngx_http_request_t *r) |
| { |
| ngx_http_proxy_loc_conf_t *plcf; |
| |
| plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module); |
| |
| if (ngx_http_cache_purge_init(r, plcf->upstream.cache->data, |
| &plcf->cache_key) |
| != NGX_OK) |
| { |
| return NGX_HTTP_INTERNAL_SERVER_ERROR; |
| } |
| |
| # if defined(nginx_version) && (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; |
| |
| typedef struct { |
| ngx_http_upstream_conf_t upstream; |
| |
| ngx_array_t *flushes; |
| ngx_array_t *params_len; |
| ngx_array_t *params; |
| ngx_array_t *params_source; |
| |
| ngx_hash_t headers_hash; |
| ngx_uint_t header_params; |
| |
| ngx_array_t *scgi_lengths; |
| ngx_array_t *scgi_values; |
| |
| ngx_http_complex_value_t cache_key; |
| } ngx_http_scgi_loc_conf_t; |
| |
| char * |
| ngx_http_scgi_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) |
| { |
| ngx_http_compile_complex_value_t ccv; |
| ngx_http_cache_purge_loc_conf_t *cplcf; |
| ngx_http_core_loc_conf_t *clcf; |
| ngx_http_scgi_loc_conf_t *slcf; |
| ngx_str_t *value; |
| |
| 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 (slcf->upstream.cache != NGX_CONF_UNSET_PTR |
| && slcf->upstream.cache != NULL) |
| { |
| return "is incompatible with \"scgi_cache\""; |
| } |
| |
| if (slcf->upstream.upstream || slcf->scgi_lengths) { |
| return "is incompatible with \"scgi_pass\""; |
| } |
| |
| if (slcf->upstream.store > 0 || slcf->upstream.store_lengths) { |
| return "is incompatible with \"scgi_store\""; |
| } |
| |
| value = cf->args->elts; |
| |
| /* set scgi_cache part */ |
| slcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0, |
| &ngx_http_scgi_module); |
| if (slcf->upstream.cache == NULL) { |
| return NGX_CONF_ERROR; |
| } |
| |
| /* set scgi_cache_key part */ |
| ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); |
| |
| ccv.cf = cf; |
| ccv.value = &value[2]; |
| ccv.complex_value = &slcf->cache_key; |
| |
| if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { |
| return NGX_CONF_ERROR; |
| } |
| |
| /* set handler */ |
| clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); |
| |
| cplcf->scgi.enable = 0; |
| clcf->handler = ngx_http_scgi_cache_purge_handler; |
| |
| return NGX_CONF_OK; |
| } |
| |
| ngx_int_t |
| ngx_http_scgi_cache_purge_handler(ngx_http_request_t *r) |
| { |
| ngx_http_scgi_loc_conf_t *slcf; |
| |
| slcf = ngx_http_get_module_loc_conf(r, ngx_http_scgi_module); |
| |
| if (ngx_http_cache_purge_init(r, slcf->upstream.cache->data, |
| &slcf->cache_key) |
| != NGX_OK) |
| { |
| return NGX_HTTP_INTERNAL_SERVER_ERROR; |
| } |
| |
| # if defined(nginx_version) && (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; |
| |
| typedef struct { |
| ngx_http_upstream_conf_t upstream; |
| |
| ngx_array_t *flushes; |
| ngx_array_t *params_len; |
| ngx_array_t *params; |
| ngx_array_t *params_source; |
| |
| ngx_hash_t headers_hash; |
| ngx_uint_t header_params; |
| |
| ngx_array_t *uwsgi_lengths; |
| ngx_array_t *uwsgi_values; |
| |
| ngx_http_complex_value_t cache_key; |
| |
| ngx_str_t uwsgi_string; |
| |
| ngx_uint_t modifier1; |
| ngx_uint_t modifier2; |
| } ngx_http_uwsgi_loc_conf_t; |
| |
| char * |
| ngx_http_uwsgi_cache_purge_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) |
| { |
| ngx_http_compile_complex_value_t ccv; |
| ngx_http_cache_purge_loc_conf_t *cplcf; |
| ngx_http_core_loc_conf_t *clcf; |
| ngx_http_uwsgi_loc_conf_t *ulcf; |
| ngx_str_t *value; |
| |
| 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 (ulcf->upstream.cache != NGX_CONF_UNSET_PTR |
| && ulcf->upstream.cache != NULL) |
| { |
| return "is incompatible with \"uwsgi_cache\""; |
| } |
| |
| if (ulcf->upstream.upstream || ulcf->uwsgi_lengths) { |
| return "is incompatible with \"uwsgi_pass\""; |
| } |
| |
| if (ulcf->upstream.store > 0 || ulcf->upstream.store_lengths) { |
| return "is incompatible with \"uwsgi_store\""; |
| } |
| |
| value = cf->args->elts; |
| |
| /* set uwsgi_cache part */ |
| ulcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0, |
| &ngx_http_uwsgi_module); |
| if (ulcf->upstream.cache == NULL) { |
| return NGX_CONF_ERROR; |
| } |
| |
| /* set uwsgi_cache_key part */ |
| ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); |
| |
| ccv.cf = cf; |
| ccv.value = &value[2]; |
| ccv.complex_value = &ulcf->cache_key; |
| |
| if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { |
| return NGX_CONF_ERROR; |
| } |
| |
| /* set handler */ |
| clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); |
| |
| cplcf->uwsgi.enable = 0; |
| clcf->handler = ngx_http_uwsgi_cache_purge_handler; |
| |
| return NGX_CONF_OK; |
| } |
| |
| ngx_int_t |
| ngx_http_uwsgi_cache_purge_handler(ngx_http_request_t *r) |
| { |
| ngx_http_uwsgi_loc_conf_t *ulcf; |
| |
| ulcf = ngx_http_get_module_loc_conf(r, ngx_http_uwsgi_module); |
| |
| if (ngx_http_cache_purge_init(r, ulcf->upstream.cache->data, |
| &ulcf->cache_key) |
| != NGX_OK) |
| { |
| return NGX_HTTP_INTERNAL_SERVER_ERROR; |
| } |
| |
| # if defined(nginx_version) && (nginx_version >= 8011) |
| r->main->count++; |
| # endif |
| |
| ngx_http_cache_purge_handler(r); |
| |
| return NGX_DONE; |
| } |
| # endif /* NGX_HTTP_UWSGI */ |
| |
| 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; |
| |
| 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("<br>Key : ") - 1 + sizeof(CRLF "<br>Path: ") - 1 |
| + key[0].len + r->cache->file.name.len; |
| |
| r->headers_out.content_type.len = sizeof("text/html") - 1; |
| r->headers_out.content_type.data = (u_char *) "text/html"; |
| r->headers_out.status = NGX_HTTP_OK; |
| r->headers_out.content_length_n = len; |
| |
| if (r->method == NGX_HTTP_HEAD) { |
| rc = ngx_http_send_header(r); |
| if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { |
| return rc; |
| } |
| } |
| |
| b = ngx_create_temp_buf(r->pool, len); |
| if (b == NULL) { |
| return NGX_HTTP_INTERNAL_SERVER_ERROR; |
| } |
| |
| out.buf = b; |
| out.next = NULL; |
| |
| b->last = ngx_cpymem(b->last, ngx_http_cache_purge_success_page_top, |
| sizeof(ngx_http_cache_purge_success_page_top) - 1); |
| b->last = ngx_cpymem(b->last, "<br>Key : ", sizeof("<br>Key : ") - 1); |
| b->last = ngx_cpymem(b->last, key[0].data, key[0].len); |
| b->last = ngx_cpymem(b->last, CRLF "<br>Path: ", |
| sizeof(CRLF "<br>Path: ") - 1); |
| b->last = ngx_cpymem(b->last, r->cache->file.name.data, |
| r->cache->file.name.len); |
| b->last = ngx_cpymem(b->last, ngx_http_cache_purge_success_page_tail, |
| sizeof(ngx_http_cache_purge_success_page_tail) - 1); |
| b->last_buf = 1; |
| |
| rc = ngx_http_send_header(r); |
| if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { |
| return rc; |
| } |
| |
| return ngx_http_output_filter(r, &out); |
| } |
| |
| ngx_int_t |
| ngx_http_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_int_t rc; |
| |
| # if (NGX_HAVE_FILE_AIO) |
| if (r->aio) { |
| return; |
| } |
| # endif |
| |
| 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_NOT_FOUND); |
| 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 defined(nginx_version) \ |
| && ((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 defined(nginx_version) && (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 defined(nginx_version) \ |
| && ((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; |
| } |
| |
| 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; |
| |
| value = cf->args->elts; |
| |
| if (ngx_strcmp(value[1].data, (u_char *) "off") == 0) { |
| cpcf->enable = 0; |
| return NGX_CONF_OK; |
| |
| } else if (ngx_strcmp(value[1].data, (u_char *) "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; |
| } |
| |
| /* sanity check */ |
| if (ngx_strcmp(value[2].data, (u_char *) "from") != 0) { |
| ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, |
| "invalid parameter \"%V\", expected" |
| " \"from\" keyword", &value[2]); |
| return NGX_CONF_ERROR; |
| } |
| |
| if (ngx_strcmp(value[3].data, (u_char *) "all") == 0) { |
| cpcf->enable = 1; |
| return NGX_CONF_OK; |
| } |
| |
| for (i = 3; 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 - 3, |
| 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 - 3, |
| 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->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 |
| */ |
| |
| # 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->conf = NGX_CONF_UNSET_PTR; |
| conf->handler = NGX_CONF_UNSET_PTR; |
| conf->original_handler = 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); |
| |
| # 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); |
| ngx_conf_merge_ptr_value(conf->handler, prev->handler, NULL); |
| ngx_conf_merge_ptr_value(conf->original_handler, prev->original_handler, |
| NULL); |
| |
| 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 */ |