├── .gitignore ├── src ├── ngx_http_lua_cache_control.h ├── ngx_http_lua_cache_module.c ├── ddebug.h └── ngx_http_lua_cache_control.c ├── config ├── t └── 00-simple.t ├── .github └── workflows │ └── semgrep.yml ├── LICENSE └── README /.gitignore: -------------------------------------------------------------------------------- 1 | t/servroot/ 2 | *~ -------------------------------------------------------------------------------- /src/ngx_http_lua_cache_control.h: -------------------------------------------------------------------------------- 1 | #ifndef NGX_HTTP_LUA_CACHE_CONTROL_H 2 | #define NGX_HTTP_LUA_CACHE_CONTROL_H 3 | 4 | #include "ngx_http_lua_api.h" 5 | #include "lauxlib.h" 6 | 7 | int ngx_http_lua_inject_cache_control_api(lua_State *L); 8 | 9 | 10 | #endif /* NGX_HTTP_LUA_CACHE_CONTROL_H */ 11 | 12 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | ngx_addon_name=ngx_http_lua_cache_module 2 | HTTP_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_MODULES ngx_http_lua_cache_module" 3 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/src/ngx_http_lua_cache_module.c $ngx_addon_dir/src/ngx_http_lua_cache_control.c" 4 | NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_addon_dir/src/ngx_http_lua_cache_control.h" 5 | -------------------------------------------------------------------------------- /t/00-simple.t: -------------------------------------------------------------------------------- 1 | use Test::Nginx::Socket; 2 | 3 | plan tests => repeat_each() * (blocks() * 2); 4 | 5 | no_long_string(); 6 | 7 | run_tests(); 8 | 9 | __DATA__ 10 | 11 | === TEST 1: example echo 12 | --- config 13 | location = /test { 14 | content_by_lua ' 15 | local example = require "nginx.example" 16 | ngx.say(example.get_uri()) 17 | '; 18 | } 19 | --- request 20 | GET /test 21 | --- response_body 22 | /test 23 | -------------------------------------------------------------------------------- /.github/workflows/semgrep.yml: -------------------------------------------------------------------------------- 1 | 2 | on: 3 | pull_request: {} 4 | workflow_dispatch: {} 5 | push: 6 | branches: 7 | - main 8 | - master 9 | name: Semgrep config 10 | jobs: 11 | semgrep: 12 | name: semgrep/ci 13 | runs-on: ubuntu-20.04 14 | env: 15 | SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} 16 | SEMGREP_URL: https://cloudflare.semgrep.dev 17 | SEMGREP_APP_URL: https://cloudflare.semgrep.dev 18 | SEMGREP_VERSION_CHECK_URL: https://cloudflare.semgrep.dev/api/check-version 19 | container: 20 | image: returntocorp/semgrep 21 | steps: 22 | - uses: actions/checkout@v3 23 | - run: semgrep ci 24 | -------------------------------------------------------------------------------- /src/ngx_http_lua_cache_module.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "ngx_http_lua_api.h" 4 | #include "ngx_http_lua_cache_control.h" 5 | 6 | ngx_module_t ngx_http_lua_cache_module; 7 | 8 | static ngx_int_t 9 | ngx_http_lua_cache_init(ngx_conf_t *cf); 10 | 11 | static ngx_http_module_t ngx_http_lua_cache_ctx = { 12 | NULL, /* preconfiguration */ 13 | ngx_http_lua_cache_init, /* postconfiguration */ 14 | NULL, /* create main configuration */ 15 | NULL, /* init main configuration */ 16 | NULL, /* create server configuration */ 17 | NULL, /* merge server configuration */ 18 | NULL, /* create location configuration */ 19 | NULL /* merge location configuration */ 20 | }; 21 | 22 | ngx_module_t ngx_http_lua_cache_module = { 23 | NGX_MODULE_V1, 24 | &ngx_http_lua_cache_ctx, /* module context */ 25 | NULL, /* module directives */ 26 | NGX_HTTP_MODULE, /* module type */ 27 | NULL, /* init master */ 28 | NULL, /* init module */ 29 | NULL, /* init process */ 30 | NULL, /* init thread */ 31 | NULL, /* exit thread */ 32 | NULL, /* exit process */ 33 | NULL, /* exit master */ 34 | NGX_MODULE_V1_PADDING 35 | }; 36 | 37 | 38 | static ngx_int_t 39 | ngx_http_lua_cache_init(ngx_conf_t *cf) 40 | { 41 | ngx_http_lua_add_package_preload(cf, "http_cache", 42 | ngx_http_lua_inject_cache_control_api); 43 | return NGX_OK; 44 | } 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright CloudFlare 2012 cloudflare.com 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | The views and conclusions contained in the software and documentation are those 25 | of the authors and should not be interpreted as representing official policies, 26 | either expressed or implied, of the FreeBSD Project. 27 | -------------------------------------------------------------------------------- /src/ddebug.h: -------------------------------------------------------------------------------- 1 | /* vim:set ft=c ts=4 sw=4 et fdm=marker: */ 2 | 3 | #ifndef DDEBUG_H 4 | #define DDEBUG_H 5 | 6 | #include 7 | #include 8 | 9 | #if defined(DDEBUG) && (DDEBUG) 10 | 11 | # if (NGX_HAVE_VARIADIC_MACROS) 12 | 13 | # define dd(...) fprintf(stderr, "lua *** %s: ", __func__); \ 14 | fprintf(stderr, __VA_ARGS__); \ 15 | fprintf(stderr, " at %s line %d.\n", __FILE__, __LINE__) 16 | 17 | # else 18 | 19 | #include 20 | #include 21 | 22 | #include 23 | 24 | static void dd(const char *fmt, ...) { 25 | } 26 | 27 | # endif 28 | 29 | #else 30 | 31 | # if (NGX_HAVE_VARIADIC_MACROS) 32 | 33 | # define dd(...) 34 | 35 | # else 36 | 37 | #include 38 | 39 | static void dd(const char *fmt, ...) { 40 | } 41 | 42 | # endif 43 | 44 | #endif 45 | 46 | #if defined(DDEBUG) && (DDEBUG) 47 | 48 | #define dd_check_read_event_handler(r) \ 49 | dd("r->read_event_handler = %s", \ 50 | r->read_event_handler == ngx_http_block_reading ? \ 51 | "ngx_http_block_reading" : \ 52 | r->read_event_handler == ngx_http_test_reading ? \ 53 | "ngx_http_test_reading" : \ 54 | r->read_event_handler == ngx_http_request_empty_handler ? \ 55 | "ngx_http_request_empty_handler" : "UNKNOWN") 56 | 57 | #define dd_check_write_event_handler(r) \ 58 | dd("r->write_event_handler = %s", \ 59 | r->write_event_handler == ngx_http_handler ? \ 60 | "ngx_http_handler" : \ 61 | r->write_event_handler == ngx_http_core_run_phases ? \ 62 | "ngx_http_core_run_phases" : \ 63 | r->write_event_handler == ngx_http_request_empty_handler ? \ 64 | "ngx_http_request_empty_handler" : "UNKNOWN") 65 | 66 | #else 67 | 68 | #define dd_check_read_event_handler(r) 69 | #define dd_check_write_event_handler(r) 70 | 71 | #endif 72 | 73 | #endif /* DDEBUG_H */ 74 | 75 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Name 2 | http_cache - Expose & modify the internal nginx cache metadata. 3 | 4 | *This module is not distributed with the Nginx source or in the lua-nginx 5 | module* 6 | Installation is similar to other nginx modules. 7 | 8 | Status 9 | This module is under development and is used in production. 10 | 11 | Version 12 | This document describes http_cache v0.1.1 13 | 14 | Synopsis 15 | # set search paths for pure Lua external libraries (';;' is the default path): 16 | lua_package_path '/foo/bar/?.lua;/blah/?.lua;;'; 17 | 18 | # set search paths for Lua external libraries written in C (can also use ';;'): 19 | lua_package_cpath '/bar/baz/?.so;/blah/blah/?.so;;'; 20 | 21 | server { 22 | listen 8000 default_server; 23 | server_name localhost; 24 | 25 | location / { 26 | proxy_cache pcache; 27 | proxy_pass http://127.0.0.1:8001/; 28 | 29 | # Force entries in cache to only be cached for 5 seconds 30 | # This is equivalent to: 31 | # proxy_ignore_headers "Expires" "Cache-Control; 32 | # proxy_cache_valid any 5s; 33 | header_filter_by_lua ' 34 | local http_cache = require("http_cache") 35 | local cache_status = (ngx.var.upstream_cache_status or "") 36 | 37 | if cache_status == "MISS" or cache_status == "EXPIRED" then 38 | local cache_data = http_cache.get_metadata() 39 | local new_expire = ngx.time() + 5 40 | 41 | if cache_data and cache_data["valid_sec"] then 42 | http_cache.set_metadata({ valid_sec = new_expire, 43 | fcn = { valid_sec = new_expire, 44 | expire = new_expire } }) 45 | end 46 | end 47 | '; 48 | } 49 | 50 | Description 51 | This module extends the lua-nginx Lua module. It adds the ability to read 52 | and set values from nginx's internal cache metadata. 53 | 54 | API 55 | get_metadata() - returns a table with all cache metadata. 56 | The fields are: 57 | Resource specific fields 58 | key: Hexadecimal cache key for this resource 59 | crc32: Numeric crc32 calculated by nginx to check cache collisions 60 | valid_sec: Absolute time, in seconds since Epoch, when this entry expires. 61 | last_modified: Absolute time of last-modified header in seconds, if present. 62 | date: Absolute time of date header in seconds, if present. 63 | length: Length of contents 64 | fs_size: Size of resource, in disk blocks. 65 | NOTE: This can be converted to bytes by multiplying by bsize below. 66 | min_uses: Minimum number of uses required to cache this resource. 67 | valid_msec: millisecond component of valid_secs (?) 68 | 69 | Shared Cache information, under "sh" 70 | size 71 | 72 | Cache Data, under "fcn" 73 | max_size: maximum allowable cache size in 512B blocks 74 | bsize: size of disk block in bytes. 75 | inactive: inactive value for cache. 76 | files: currently open files used by loader 77 | loader_files: maximum number of files for the loader to open at once 78 | loader_sleep: time between file opens 79 | loader_threshold: 80 | aggressive_purge: Flag for whether cache manager will use aggressive 81 | purge policy 82 | 83 | set_metadat(tbl) - sets cache metadata from a table similar to the one 84 | returned by get_metadata. Fields that will be written are: 85 | Resource specific fields 86 | valid_sec, last_modified, date, min_uses, valid_msec 87 | 88 | Cache Data, under "fcn" 89 | uses, valid_msec, expire, valid_sec 90 | -------------------------------------------------------------------------------- /src/ngx_http_lua_cache_control.c: -------------------------------------------------------------------------------- 1 | #ifndef DDEBUG 2 | #define DDEBUG 0 3 | #endif 4 | #include "ddebug.h" 5 | 6 | #include "ngx_http_lua_cache_control.h" 7 | 8 | #define ASSIGN_NUMBER_OR_RET(field, target, flag) \ 9 | do { \ 10 | lua_getfield(L, n, field); \ 11 | switch(lua_type(L, -1)) { \ 12 | case LUA_TNUMBER: \ 13 | target = lua_tonumber(L, -1); \ 14 | flag = 1; \ 15 | break; \ 16 | case LUA_TNIL: \ 17 | break; \ 18 | default: \ 19 | return luaL_error(L, "Bad args option value"); \ 20 | } \ 21 | } while(0) 22 | 23 | #define ASSIGN_IF_SET(field, value, flag) \ 24 | if (flag) { field = value; } 25 | 26 | static int ngx_http_lua_ngx_get_cache_data(lua_State *L); 27 | static int ngx_http_lua_ngx_set_cache_data(lua_State *L); 28 | static int ngx_http_lua_ngx_cache_purge(lua_State *L); 29 | 30 | int 31 | ngx_http_lua_inject_cache_control_api(lua_State *L) { 32 | /* register reference maps */ 33 | lua_newtable(L); /* http_cache */ 34 | 35 | /* .cache.purge */ 36 | lua_pushcfunction(L, ngx_http_lua_ngx_cache_purge); 37 | lua_setfield(L, -2, "purge"); 38 | 39 | /* .cache.get_metadata */ 40 | lua_pushcfunction(L, ngx_http_lua_ngx_get_cache_data); 41 | lua_setfield(L, -2, "get_metadata"); 42 | 43 | /* .cache.get_metadata */ 44 | lua_pushcfunction(L, ngx_http_lua_ngx_set_cache_data); 45 | lua_setfield(L, -2, "set_metadata"); 46 | 47 | //lua_setfield(L, -2, "cache"); 48 | 49 | return 1; 50 | } 51 | 52 | static int 53 | ngx_http_lua_ngx_get_cache_data(lua_State *L) { 54 | int n; 55 | ngx_http_request_t *r; 56 | ngx_http_cache_t *c; 57 | ngx_http_file_cache_t *cache, cache_tmp; 58 | ngx_http_file_cache_sh_t *sh, sh_tmp; 59 | ngx_http_file_cache_node_t *fcn, fcn_tmp; 60 | u_char *p; 61 | 62 | 63 | n = lua_gettop(L); 64 | 65 | if (n != 0) { 66 | return luaL_error(L, "expecting no arguments"); 67 | } 68 | 69 | r = ngx_http_lua_get_request(L); 70 | if (r == NULL) { 71 | return luaL_error(L, "no request object found"); 72 | } 73 | 74 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 75 | "lua cache.metadata"); 76 | 77 | // TODO setup empty return 78 | lua_createtable(L, 0, 2 /* nrec */); /* return table */ 79 | 80 | c = r->cache; 81 | if (!c) { 82 | /* empty response */ 83 | return 1; 84 | } 85 | 86 | /* make copies of all structs, to avoid locking for too long */ 87 | fcn = c->node; 88 | cache = c->file_cache; 89 | sh = cache ? cache->sh : NULL; 90 | memset(&cache_tmp, 0, sizeof(cache_tmp)); 91 | memset(&fcn_tmp, 0, sizeof(fcn_tmp)); 92 | memset(&sh_tmp, 0, sizeof(sh_tmp)); 93 | 94 | ngx_shmtx_lock(&c->file_cache->shpool->mutex); 95 | 96 | if (fcn) { 97 | fcn_tmp = *c->node; 98 | } 99 | 100 | if (cache) { 101 | cache_tmp = *c->file_cache; 102 | if (sh) { 103 | sh_tmp = *cache->sh; 104 | cache_tmp.sh = &sh_tmp; 105 | } 106 | } 107 | 108 | ngx_shmtx_unlock(&c->file_cache->shpool->mutex); 109 | 110 | p = ngx_pnalloc(r->pool, 2*NGX_HTTP_CACHE_KEY_LEN); 111 | if (!p) { 112 | return luaL_error(L, "Cannot allocate space for cache key string"); 113 | } 114 | 115 | ngx_hex_dump(p, c->key, NGX_HTTP_CACHE_KEY_LEN); 116 | lua_pushlstring(L, "key", sizeof("key")-1); 117 | lua_pushlstring(L, (char*)p, 2*NGX_HTTP_CACHE_KEY_LEN); 118 | lua_rawset(L, -3); 119 | 120 | lua_pushlstring(L, "crc32", sizeof("crc32")-1); 121 | lua_pushnumber(L, c->crc32); 122 | lua_rawset(L, -3); 123 | 124 | lua_pushlstring(L, "valid_sec", sizeof("valid_sec")-1); 125 | lua_pushnumber(L, c->valid_sec); 126 | lua_rawset(L, -3); 127 | 128 | lua_pushlstring(L, "last_modified", sizeof("last_modified")-1); 129 | lua_pushnumber(L, c->last_modified); 130 | lua_rawset(L, -3); 131 | 132 | lua_pushlstring(L, "date", sizeof("date")-1); 133 | lua_pushnumber(L, c->date); 134 | lua_rawset(L, -3); 135 | 136 | lua_pushlstring(L, "length", sizeof("length")-1); 137 | lua_pushnumber(L, c->length); 138 | lua_rawset(L, -3); 139 | 140 | lua_pushlstring(L, "fs_size", sizeof("fs_size")-1); 141 | lua_pushnumber(L, c->fs_size); 142 | lua_rawset(L, -3); 143 | 144 | lua_pushlstring(L, "min_uses", sizeof("min_uses")-1); 145 | lua_pushnumber(L, c->min_uses); 146 | lua_rawset(L, -3); 147 | 148 | lua_pushlstring(L, "error", sizeof("error")-1); 149 | lua_pushnumber(L, c->error); 150 | lua_rawset(L, -3); 151 | 152 | lua_pushlstring(L, "valid_msec", sizeof("valid_msec")-1); 153 | lua_pushnumber(L, c->valid_msec); 154 | lua_rawset(L, -3); 155 | 156 | /* shared memory block */ 157 | if (sh) { 158 | lua_createtable(L, 0, 2 /* nrec */); /* subtable */ 159 | 160 | lua_pushlstring(L, "size", sizeof("size")-1); 161 | lua_pushnumber(L, sh_tmp.size); 162 | lua_rawset(L, -3); 163 | 164 | lua_setfield(L, -2, "sh"); 165 | } 166 | 167 | /* cache entry */ 168 | if (cache) { 169 | lua_createtable(L, 0, 8 /* nrec */); /* subtable */ 170 | 171 | lua_pushlstring(L, "max_size", sizeof("max_size")-1); 172 | lua_pushnumber(L, cache_tmp.max_size); 173 | lua_rawset(L, -3); 174 | 175 | lua_pushlstring(L, "bsize", sizeof("bsize")-1); 176 | lua_pushnumber(L, cache_tmp.bsize); 177 | lua_rawset(L, -3); 178 | 179 | lua_pushlstring(L, "inactive", sizeof("inactive")-1); 180 | lua_pushnumber(L, cache_tmp.inactive); 181 | lua_rawset(L, -3); 182 | 183 | lua_pushlstring(L, "files", sizeof("files")-1); 184 | lua_pushnumber(L, cache_tmp.files); 185 | lua_rawset(L, -3); 186 | 187 | lua_pushlstring(L, "loader_files", sizeof("loader_files")-1); 188 | lua_pushnumber(L, cache_tmp.loader_files); 189 | lua_rawset(L, -3); 190 | 191 | lua_pushlstring(L, "loader_sleep", sizeof("loader_sleep")-1); 192 | lua_pushnumber(L, cache_tmp.loader_sleep); 193 | lua_rawset(L, -3); 194 | 195 | lua_pushlstring(L, "loader_threshold", sizeof("loader_threshold")-1); 196 | lua_pushnumber(L, cache_tmp.inactive); 197 | lua_rawset(L, -3); 198 | 199 | lua_setfield(L, -2, "cache"); 200 | } 201 | 202 | /* file_cache_node */ 203 | if (fcn) { 204 | lua_createtable(L, 0, 11 /* nrec */); /* subtable */ 205 | 206 | lua_pushlstring(L, "count", sizeof("count")-1); 207 | lua_pushnumber(L, fcn_tmp.count); 208 | lua_rawset(L, -3); 209 | 210 | lua_pushlstring(L, "uses", sizeof("uses")-1); 211 | lua_pushnumber(L, fcn_tmp.uses); 212 | lua_rawset(L, -3); 213 | 214 | lua_pushlstring(L, "valid_msec", sizeof("valid_msec")-1); 215 | lua_pushnumber(L, fcn_tmp.valid_msec); 216 | lua_rawset(L, -3); 217 | 218 | lua_pushlstring(L, "error", sizeof("error")-1); 219 | lua_pushnumber(L, fcn_tmp.error); 220 | lua_rawset(L, -3); 221 | 222 | lua_pushlstring(L, "exists", sizeof("exists")-1); 223 | lua_pushnumber(L, fcn_tmp.exists); 224 | lua_rawset(L, -3); 225 | 226 | lua_pushlstring(L, "updating", sizeof("updating")-1); 227 | lua_pushnumber(L, fcn_tmp.updating); 228 | lua_rawset(L, -3); 229 | 230 | lua_pushlstring(L, "deleting", sizeof("deleting")-1); 231 | lua_pushnumber(L, fcn_tmp.deleting); 232 | lua_rawset(L, -3); 233 | 234 | lua_pushlstring(L, "exists", sizeof("exists")-1); 235 | lua_pushnumber(L, fcn_tmp.exists); 236 | lua_rawset(L, -3); 237 | 238 | lua_pushlstring(L, "expire", sizeof("expire")-1); 239 | lua_pushnumber(L, fcn_tmp.expire); 240 | lua_rawset(L, -3); 241 | 242 | lua_pushlstring(L, "valid_sec", sizeof("valid_sec")-1); 243 | lua_pushnumber(L, fcn_tmp.valid_sec); 244 | lua_rawset(L, -3); 245 | 246 | lua_pushlstring(L, "fs_size", sizeof("fs_size")-1); 247 | lua_pushnumber(L, fcn_tmp.fs_size); 248 | lua_rawset(L, -3); 249 | 250 | lua_setfield(L, -2, "fcn"); 251 | } 252 | 253 | return 1; 254 | } 255 | 256 | static int 257 | ngx_http_lua_ngx_set_cache_data(lua_State *L) { 258 | ngx_http_request_t *r; 259 | ngx_http_cache_t *c, c_tmp; 260 | ngx_http_file_cache_node_t *fcn, fcn_tmp; 261 | int n; /* top of stack when we start. */ 262 | struct { 263 | uint valid_sec:1; 264 | uint last_modified:1; 265 | uint date:1; 266 | uint min_uses:1; 267 | uint valid_msec:1; 268 | uint fcn_uses:1; 269 | uint fcn_valid_msec:1; 270 | uint fcn_expire:1; 271 | uint fcn_valid_sec:1; 272 | } isset; 273 | 274 | n = lua_gettop(L); 275 | if (n != 1) { 276 | return luaL_error(L, "only one argument is expected, but got %d", n); 277 | } 278 | 279 | luaL_checktype(L, -1, LUA_TTABLE); 280 | 281 | r = ngx_http_lua_get_request(L); 282 | if (lua_type(L, -1) != LUA_TTABLE) { 283 | return luaL_error(L, "the argument is not a table, " 284 | "but a %s", 285 | lua_typename(L, lua_type(L, -1))); 286 | } 287 | 288 | c = r->cache; 289 | if (!c) { 290 | lua_pushboolean(L, 0); 291 | return 1; 292 | } 293 | 294 | /* setup dummy copies of structs, to write into */ 295 | fcn = c->node; 296 | memset(&c_tmp, 0, sizeof(c_tmp)); 297 | memset(&fcn_tmp, 0, sizeof(fcn_tmp)); 298 | memset(&isset, 0, sizeof(isset)); 299 | 300 | ASSIGN_NUMBER_OR_RET("valid_sec", c_tmp.valid_sec, isset.valid_sec); 301 | ASSIGN_NUMBER_OR_RET("last_modified", c_tmp.last_modified, 302 | isset.last_modified); 303 | ASSIGN_NUMBER_OR_RET("date", c_tmp.date, isset.date); 304 | ASSIGN_NUMBER_OR_RET("min_uses", c_tmp.min_uses, isset.min_uses); 305 | ASSIGN_NUMBER_OR_RET("valid_msec", c_tmp.valid_msec, isset.valid_msec); 306 | 307 | /* pop all we pushed on stack */ 308 | lua_pop(L, lua_gettop(L)-n); 309 | 310 | /* file_cache_node */ 311 | if (fcn && lua_type(L, n+1) == LUA_TTABLE) { 312 | /* push the fcn subtable onto the stack */ 313 | lua_getfield(L, n, "fcn"); 314 | ASSIGN_NUMBER_OR_RET("uses", fcn_tmp.uses, isset.fcn_uses); 315 | ASSIGN_NUMBER_OR_RET("valid_msec", fcn_tmp.valid_msec, 316 | isset.fcn_valid_msec); 317 | ASSIGN_NUMBER_OR_RET("expire", fcn_tmp.expire, isset.fcn_expire); 318 | ASSIGN_NUMBER_OR_RET("valid_sec", fcn_tmp.valid_sec, 319 | isset.fcn_valid_sec); 320 | 321 | /* pop all the entries we pushed on the stack*/ 322 | lua_pop(L, lua_gettop(L)-n); 323 | } 324 | 325 | /* write out changes */ 326 | ngx_shmtx_lock(&c->file_cache->shpool->mutex); 327 | 328 | if (isset.valid_sec) { 329 | c->valid_sec = c_tmp.valid_sec; 330 | if (c->buf && c->buf->pos) { 331 | ngx_http_file_cache_header_t *h; 332 | 333 | h = (ngx_http_file_cache_header_t *) c->buf->pos; 334 | h->valid_sec = c->valid_sec; 335 | } 336 | } 337 | ASSIGN_IF_SET(c->last_modified, c_tmp.last_modified, isset.last_modified); 338 | ASSIGN_IF_SET(c->date, c_tmp.date, isset.date); 339 | ASSIGN_IF_SET(c->min_uses, c_tmp.min_uses, isset.min_uses); 340 | ASSIGN_IF_SET(c->valid_msec, c_tmp.valid_msec, isset.valid_msec); 341 | 342 | if (fcn) { 343 | ASSIGN_IF_SET(fcn->uses, fcn_tmp.uses, isset.fcn_uses); 344 | ASSIGN_IF_SET(fcn->valid_msec, fcn_tmp.valid_msec,isset.fcn_valid_msec); 345 | ASSIGN_IF_SET(fcn->expire, fcn_tmp.expire, isset.fcn_expire); 346 | ASSIGN_IF_SET(fcn->valid_sec, fcn_tmp.valid_sec, isset.fcn_valid_sec); 347 | } 348 | 349 | ngx_shmtx_unlock(&c->file_cache->shpool->mutex); 350 | 351 | /* pop the parameter off */ 352 | lua_pop(L, 1); 353 | /* push a true as a return */ 354 | lua_pushboolean(L, 1); 355 | return 1; 356 | } 357 | 358 | static int 359 | ngx_http_lua_ngx_cache_purge(lua_State *L) { 360 | int n; 361 | ngx_http_request_t *r; 362 | ngx_http_cache_t *c; 363 | ngx_http_file_cache_t *cache; 364 | 365 | 366 | n = lua_gettop(L); 367 | 368 | if (n != 0) { 369 | return luaL_error(L, "expecting no arguments"); 370 | } 371 | 372 | r = ngx_http_lua_get_request(L); 373 | if (!r->cache || !r->cache->node || !r->cache->node->exists 374 | || r->cache->node->deleting) { 375 | lua_pushboolean (L, 0); 376 | return 1; 377 | } 378 | /* Inspired by Piotr Sikora's Purge module. 379 | * https://github.com/FRiCKLE/ngx_cache_purge 380 | */ 381 | ngx_shmtx_lock(&r->cache->file_cache->shpool->mutex); 382 | if (!r->cache || !r->cache->node || !r->cache->node->exists) { 383 | /* race between concurrent purges, backoff */ 384 | ngx_shmtx_unlock(&r->cache->file_cache->shpool->mutex); 385 | 386 | lua_pushboolean (L, 0); 387 | return 1; 388 | } 389 | 390 | c = r->cache; 391 | cache = c->file_cache; 392 | 393 | # if defined(nginx_version) && (nginx_version >= 1000001) 394 | cache->sh->size -= c->node->fs_size; 395 | c->node->fs_size = 0; 396 | # else 397 | cache->sh->size -= (c->node->length + cache->bsize - 1) / cache->bsize; 398 | c->node->length = 0; 399 | # endif 400 | 401 | c->node->exists = 0; 402 | # if defined(nginx_version) \ 403 | && ((nginx_version >= 8001) \ 404 | || ((nginx_version < 8000) && (nginx_version >= 7060))) 405 | c->node->updating = 0; 406 | # endif 407 | 408 | ngx_shmtx_unlock(&r->cache->file_cache->shpool->mutex); 409 | 410 | if (ngx_delete_file(r->cache->file.name.data) == NGX_FILE_ERROR) { 411 | ngx_log_error(NGX_LOG_WARN, r->connection->log, ngx_errno, 412 | ngx_delete_file_n " \"%s\" failed while " 413 | "attempting to purge bad stale.", 414 | r->cache->file.name.data); 415 | } 416 | 417 | lua_pushboolean (L, 1); 418 | return 1; 419 | } 420 | 421 | --------------------------------------------------------------------------------