/* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include #include #include "ngx_http_lua_common.h" #include "ngx_http_lua_cache.h" #include "ngx_http_lua_clfactory.h" #include "ngx_http_lua_util.h" /** * Find code chunk associated with the given key in code cache, * and push it to the top of Lua stack if found. * * Stack layout before call: * | ... | <- top * * Stack layout after call: * | code chunk | <- top * | ... | * * */ static ngx_int_t ngx_http_lua_cache_load_code(ngx_log_t *log, lua_State *L, const char *key) { int rc; u_char *err; /* get code cache table */ lua_pushlightuserdata(L, &ngx_http_lua_code_cache_key); lua_rawget(L, LUA_REGISTRYINDEX); /* sp++ */ dd("Code cache table to load: %p", lua_topointer(L, -1)); if (!lua_istable(L, -1)) { dd("Error: code cache table to load did not exist!!"); return NGX_ERROR; } lua_getfield(L, -1, key); /* sp++ */ if (lua_isfunction(L, -1)) { /* call closure factory to gen new closure */ rc = lua_pcall(L, 0, 1, 0); if (rc == 0) { /* remove cache table from stack, leave code chunk at * top of stack */ lua_remove(L, -2); /* sp-- */ return NGX_OK; } if (lua_isstring(L, -1)) { err = (u_char *) lua_tostring(L, -1); } else { err = (u_char *) "unknown error"; } ngx_log_error(NGX_LOG_ERR, log, 0, "lua: failed to run factory at key \"%s\": %s", key, err); lua_pop(L, 2); return NGX_ERROR; } dd("Value associated with given key in code cache table is not code " "chunk: stack top=%d, top value type=%s\n", lua_gettop(L), lua_typename(L, -1)); /* remove cache table and value from stack */ lua_pop(L, 2); /* sp-=2 */ return NGX_DECLINED; } /** * Store the closure factory at the top of Lua stack to code cache, and * associate it with the given key. Then generate new closure. * * Stack layout before call: * | code factory | <- top * | ... | * * Stack layout after call: * | code chunk | <- top * | ... | * * */ static ngx_int_t ngx_http_lua_cache_store_code(lua_State *L, const char *key) { int rc; /* get code cache table */ lua_pushlightuserdata(L, &ngx_http_lua_code_cache_key); lua_rawget(L, LUA_REGISTRYINDEX); dd("Code cache table to store: %p", lua_topointer(L, -1)); if (!lua_istable(L, -1)) { dd("Error: code cache table to load did not exist!!"); return NGX_ERROR; } lua_pushvalue(L, -2); /* closure cache closure */ lua_setfield(L, -2, key); /* closure cache */ /* remove cache table, leave closure factory at top of stack */ lua_pop(L, 1); /* closure */ /* call closure factory to generate new closure */ rc = lua_pcall(L, 0, 1, 0); if (rc != 0) { dd("Error: failed to call closure factory!!"); return NGX_ERROR; } return NGX_OK; } ngx_int_t ngx_http_lua_cache_loadbuffer(ngx_log_t *log, lua_State *L, const u_char *src, size_t src_len, const u_char *cache_key, const char *name) { int n; ngx_int_t rc; const char *err = NULL; n = lua_gettop(L); dd("XXX cache key: [%s]", cache_key); rc = ngx_http_lua_cache_load_code(log, L, (char *) cache_key); if (rc == NGX_OK) { /* code chunk loaded from cache, sp++ */ dd("Code cache hit! cache key='%s', stack top=%d, script='%.*s'", cache_key, lua_gettop(L), (int) src_len, src); return NGX_OK; } if (rc == NGX_ERROR) { return NGX_ERROR; } /* rc == NGX_DECLINED */ dd("Code cache missed! cache key='%s', stack top=%d, script='%.*s'", cache_key, lua_gettop(L), (int) src_len, src); /* load closure factory of inline script to the top of lua stack, sp++ */ rc = ngx_http_lua_clfactory_loadbuffer(L, (char *) src, src_len, name); if (rc != 0) { /* Oops! error occurred when loading Lua script */ if (rc == LUA_ERRMEM) { err = "memory allocation error"; } else { if (lua_isstring(L, -1)) { err = lua_tostring(L, -1); } else { err = "unknown error"; } } goto error; } /* store closure factory and gen new closure at the top of lua stack to * code cache */ rc = ngx_http_lua_cache_store_code(L, (char *) cache_key); if (rc != NGX_OK) { err = "fail to generate new closure from the closure factory"; goto error; } return NGX_OK; error: ngx_log_error(NGX_LOG_ERR, log, 0, "failed to load inlined Lua code: %s", err); lua_settop(L, n); return NGX_ERROR; } ngx_int_t ngx_http_lua_cache_loadfile(ngx_log_t *log, lua_State *L, const u_char *script, const u_char *cache_key) { int n; ngx_int_t rc, errcode = NGX_ERROR; u_char *p; u_char buf[NGX_HTTP_LUA_FILE_KEY_LEN + 1]; const char *err = NULL; n = lua_gettop(L); /* calculate digest of script file path */ if (cache_key == NULL) { dd("CACHE file key not pre-calculated...calculating"); p = ngx_copy(buf, NGX_HTTP_LUA_FILE_TAG, NGX_HTTP_LUA_FILE_TAG_LEN); p = ngx_http_lua_digest_hex(p, script, ngx_strlen(script)); *p = '\0'; cache_key = buf; } else { dd("CACHE file key already pre-calculated"); } dd("XXX cache key for file: [%s]", cache_key); rc = ngx_http_lua_cache_load_code(log, L, (char *) cache_key); if (rc == NGX_OK) { /* code chunk loaded from cache, sp++ */ dd("Code cache hit! cache key='%s', stack top=%d, file path='%s'", cache_key, lua_gettop(L), script); return NGX_OK; } if (rc == NGX_ERROR) { return NGX_ERROR; } /* rc == NGX_DECLINED */ dd("Code cache missed! cache key='%s', stack top=%d, file path='%s'", cache_key, lua_gettop(L), script); /* load closure factory of script file to the top of lua stack, sp++ */ rc = ngx_http_lua_clfactory_loadfile(L, (char *) script); dd("loadfile returns %d (%d)", (int) rc, LUA_ERRFILE); if (rc != 0) { /* Oops! error occurred when loading Lua script */ switch (rc) { case LUA_ERRMEM: err = "memory allocation error"; break; case LUA_ERRFILE: errcode = NGX_HTTP_NOT_FOUND; /* fall through */ default: if (lua_isstring(L, -1)) { err = lua_tostring(L, -1); } else { err = "unknown error"; } } goto error; } /* store closure factory and gen new closure at the top of lua stack * to code cache */ rc = ngx_http_lua_cache_store_code(L, (char *) cache_key); if (rc != NGX_OK) { err = "fail to generate new closure from the closure factory"; goto error; } return NGX_OK; error: ngx_log_error(NGX_LOG_ERR, log, 0, "failed to load external Lua file \"%s\": %s", script, err); lua_settop(L, n); return errcode; } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */