ngx_int_t
ngx_http_lua_cache_loadfile(lua_State *L, const char *script, char **err)
{
    int             rc;

    u_char cache_key[FP_TAG_LEN + 2*MD5_DIGEST_LENGTH + 1] = FP_TAG;

    /*  calculate digest of script file path */
    ngx_http_lua_digest_hex(&cache_key[FP_TAG_LEN], (u_char *) script, ngx_strlen(script));

    dd("XXX cache key for file: [%s]", cache_key);

    if (ngx_http_lua_cache_load_code(L, (char *) cache_key) == 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;
    }

    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, script);

    if (rc != 0) {
        /*  Oops! error occured when loading Lua script */
        if (rc == LUA_ERRMEM) {
            *err = "memory allocation error";
        } else {
            if (lua_isstring(L, -1)) {
                *err = (char *) lua_tostring(L, -1);
            } else {
                *err = "syntax error";
            }
        }

        return NGX_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 genearte new closutre from the closutre factory";
        return NGX_ERROR;
    }

    return NGX_OK;
}
ngx_int_t
ngx_http_lua_cache_loadfile(lua_State *L, const u_char *script,
        const u_char *cache_key, char **err, unsigned enabled)
{
    int              rc;

    u_char           buf[NGX_HTTP_LUA_FILE_KEY_LEN + 1];
    u_char          *p;

    /*  calculate digest of script file path */
    dd("code cache enabled: %d", (int) enabled);

    if (enabled) {
        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);

        if (ngx_http_lua_cache_load_code(L, (char *) cache_key) == 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;
        }

        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);

    if (rc != 0) {
        /*  Oops! error occured when loading Lua script */
        if (rc == LUA_ERRMEM) {
            *err = "memory allocation error";

        } else {
            if (lua_isstring(L, -1)) {
                *err = (char *) lua_tostring(L, -1);
            } else {
                *err = "syntax error";
            }
        }

        return NGX_ERROR;
    }

    if (enabled) {
        /*  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";
            return NGX_ERROR;
        }

    } else {
        /*  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;
        }

        ngx_http_lua_clear_package_loaded(L);
    }

    return NGX_OK;
}