ngx_int_t
ngx_http_lua_body_filter_by_chunk(lua_State *L, ngx_http_request_t *r,
        ngx_chain_t *in)
{
    ngx_int_t        rc;
    u_char          *err_msg;
    size_t           len;
#if (NGX_PCRE)
    ngx_pool_t      *old_pool;
#endif

    dd("initialize nginx context in Lua VM, code chunk at stack top  sp = 1");
    ngx_http_lua_body_filter_by_lua_env(L, r, in);

#if (NGX_PCRE)
    /* XXX: work-around to nginx regex subsystem */
    old_pool = ngx_http_lua_pcre_malloc_init(r->pool);
#endif

    dd("protected call user code");
    rc = lua_pcall(L, 0, 1, 0);

#if (NGX_PCRE)
    /* XXX: work-around to nginx regex subsystem */
    ngx_http_lua_pcre_malloc_done(old_pool);
#endif

    if (rc != 0) {

        /*  error occured */
        err_msg = (u_char *) lua_tolstring(L, -1, &len);

        if (err_msg == NULL) {
            err_msg = (u_char *) "unknown reason";
            len = sizeof("unknown reason") - 1;
        }

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "failed to run body_filter_by_lua*: %*s", len, err_msg);

        lua_settop(L, 0);    /*  clear remaining elems on stack */

        return NGX_ERROR;
    }

    /* rc == 0 */

    rc = (ngx_int_t) lua_tointeger(L, -1);

    dd("got return value: %d", (int) rc);

    lua_settop(L, 0);

    if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
        return NGX_ERROR;
    }

    return NGX_OK;
}
ngx_int_t
ngx_http_lua_header_filter_by_chunk(lua_State *L, ngx_http_request_t *r)
{
    ngx_int_t        rc;
    u_char          *err_msg;
    size_t           len;
#if (NGX_PCRE)
    ngx_pool_t      *old_pool;
#endif

    /*  initialize nginx context in Lua VM, code chunk at stack top    sp = 1 */
    ngx_http_lua_header_filter_by_lua_env(L, r);

#if (NGX_PCRE)
    /* XXX: work-around to nginx regex subsystem */
    old_pool = ngx_http_lua_pcre_malloc_init(r->pool);
#endif

    lua_pushcfunction(L, ngx_http_lua_traceback);
    lua_insert(L, 1);  /* put it under chunk and args */

    /*  protected call user code */
    rc = lua_pcall(L, 0, 1, 1);

    lua_remove(L, 1);  /* remove traceback function */

#if (NGX_PCRE)
    /* XXX: work-around to nginx regex subsystem */
    ngx_http_lua_pcre_malloc_done(old_pool);
#endif

    if (rc != 0) {
        /*  error occured when running loaded code */
        err_msg = (u_char *) lua_tolstring(L, -1, &len);

        if (err_msg == NULL) {
            err_msg = (u_char *) "unknown reason";
            len = sizeof("unknown reason") - 1;
        }

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "failed to run header_filter_by_lua*: %*s", len, err_msg);

        lua_settop(L, 0); /*  clear remaining elems on stack */

        return NGX_ERROR;
    }

    /*  clear Lua stack */
    lua_settop(L, 0);

    return NGX_OK;
}
static ngx_int_t
ngx_lua_regex_compile(ngx_lua_regex_compile_t *rc)
{
    int           n, erroff;
    char         *p;
    const char   *errstr;
    pcre         *re;
    ngx_pool_t   *old_pool;

    old_pool = ngx_http_lua_pcre_malloc_init(rc->pool);

    re = pcre_compile((const char *) rc->pattern.data, (int) rc->options,
                      &errstr, &erroff, NULL);

    ngx_http_lua_pcre_malloc_done(old_pool);

    if (re == NULL) {
        if ((size_t) erroff == rc->pattern.len) {
           rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,
                              "pcre_compile() failed: %s in \"%V\"",
                               errstr, &rc->pattern)
                      - rc->err.data;

        } else {
           rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,
                              "pcre_compile() failed: %s in \"%V\" at \"%s\"",
                               errstr, &rc->pattern, rc->pattern.data + erroff)
                      - rc->err.data;
        }

        return NGX_ERROR;
    }

    rc->regex = re;

#if 1
    n = pcre_fullinfo(re, NULL, PCRE_INFO_CAPTURECOUNT, &rc->captures);
    if (n < 0) {
        p = "pcre_fullinfo(\"%V\", PCRE_INFO_CAPTURECOUNT) failed: %d";
        goto failed;
    }
#endif

    return NGX_OK;

failed:

    rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, p, &rc->pattern, n)
                  - rc->err.data;
    return NGX_OK;
}
static void
ngx_http_lua_regex_free_study_data(ngx_pool_t *pool, pcre_extra *sd)
{
    ngx_pool_t              *old_pool;

    old_pool = ngx_http_lua_pcre_malloc_init(pool);

#if LUA_HAVE_PCRE_JIT
    pcre_free_study(sd);
#else
    pcre_free(sd);
#endif

    ngx_http_lua_pcre_malloc_done(old_pool);
}
ngx_int_t
ngx_http_lua_header_filter_by_chunk(lua_State *L, ngx_http_request_t *r)
{
    ngx_int_t        rc;
    u_char          *err_msg;
#if (NGX_PCRE)
    ngx_pool_t      *old_pool;
#endif

    /*  set Lua VM panic handler */
    lua_atpanic(L, ngx_http_lua_atpanic);

    /*  initialize nginx context in Lua VM, code chunk at stack top    sp = 1 */
    ngx_http_lua_header_filter_by_lua_env(L, r);

#if (NGX_PCRE)
    /* XXX: work-around to nginx regex subsystem */
    old_pool = ngx_http_lua_pcre_malloc_init(r->pool);
#endif

    /*  protected call user code */
    rc = lua_pcall(L, 0, 1, 0);

#if (NGX_PCRE)
    /* XXX: work-around to nginx regex subsystem */
    ngx_http_lua_pcre_malloc_done(old_pool);
#endif

    if (rc != 0) {
        /*  error occured when running loaded code */
        err_msg = (u_char *) lua_tostring(L, -1);

        if (err_msg != NULL) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "(lua-error) %s",
                    err_msg);

            lua_settop(L, 0);    /*  clear remaining elems on stack */
        }

        return NGX_ERROR;
    }

    /*  clear Lua stack */
    lua_settop(L, 0);

    return NGX_OK;
}
ngx_int_t
ngx_http_lua_header_filter_by_chunk(lua_State *L, ngx_http_request_t *r)
{
    int              old_exit_code = 0;
    ngx_int_t        rc;
    u_char          *err_msg;
    size_t           len;
#if (NGX_PCRE)
    ngx_pool_t      *old_pool;
#endif
    ngx_http_lua_ctx_t          *ctx;

    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
    if (ctx->exited) {
        old_exit_code = ctx->exit_code;
    }

    /*  initialize nginx context in Lua VM, code chunk at stack top    sp = 1 */
    ngx_http_lua_header_filter_by_lua_env(L, r);

#if (NGX_PCRE)
    /* XXX: work-around to nginx regex subsystem */
    old_pool = ngx_http_lua_pcre_malloc_init(r->pool);
#endif

    lua_pushcfunction(L, ngx_http_lua_traceback);
    lua_insert(L, 1);  /* put it under chunk and args */

    /*  protected call user code */
    rc = lua_pcall(L, 0, 1, 1);

    lua_remove(L, 1);  /* remove traceback function */

#if (NGX_PCRE)
    /* XXX: work-around to nginx regex subsystem */
    ngx_http_lua_pcre_malloc_done(old_pool);
#endif

    dd("rc == %d", (int) rc);

    if (rc != 0) {
        /*  error occured when running loaded code */
        err_msg = (u_char *) lua_tolstring(L, -1, &len);

        if (err_msg == NULL) {
            err_msg = (u_char *) "unknown reason";
            len = sizeof("unknown reason") - 1;
        }

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "failed to run header_filter_by_lua*: %*s", len, err_msg);

        lua_settop(L, 0); /*  clear remaining elems on stack */

        return NGX_ERROR;
    }

    dd("exited: %d, exit code: %d, old exit code: %d",
       (int) ctx->exited, (int) ctx->exit_code, (int) old_exit_code);

    if (ctx->exited && ctx->exit_code != old_exit_code) {
        if (ctx->exit_code == NGX_ERROR) {
            return NGX_ERROR;
        }

        dd("finalize request with %d", (int) ctx->exit_code);

        rc = ngx_http_filter_finalize_request(r, &ngx_http_lua_module,
                                              ctx->exit_code);
        if (rc == NGX_ERROR || rc == NGX_AGAIN) {
            return rc;
        }

        return NGX_DECLINED;
    }

    /*  clear Lua stack */
    lua_settop(L, 0);

    return NGX_OK;
}
ngx_int_t
ngx_http_lua_set_by_chunk(lua_State *L, ngx_http_request_t *r, ngx_str_t *val,
        ngx_http_variable_value_t *args, size_t nargs)
{
    size_t           i;
    ngx_int_t        rc;
    u_char          *err_msg;
    size_t           rlen;
    u_char          *rdata;

    /*  set Lua VM panic handler */
    lua_atpanic(L, ngx_http_lua_atpanic);

    /*  initialize nginx context in Lua VM, code chunk at stack top    sp = 1 */
    ngx_http_lua_set_by_lua_env(L, r, nargs, args);

    /*  passing directive arguments to the user code */
    for (i = 0; i < nargs; i++) {
        lua_pushlstring(L, (const char *) args[i].data, args[i].len);
    }

#if (NGX_PCRE)
    /* XXX: work-around to nginx regex subsystem */
    ngx_http_lua_pcre_malloc_init(r->pool);
#endif

    /*  protected call user code */
    rc = lua_pcall(L, nargs, 1, 0);

#if (NGX_PCRE)
    /* XXX: work-around to nginx regex subsystem */
    ngx_http_lua_pcre_malloc_done();
#endif

    if (rc != 0) {
        /*  error occured when running loaded code */
        err_msg = (u_char *) lua_tostring(L, -1);

        if (err_msg != NULL) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "(lua-error) %s",
                    err_msg);

            lua_settop(L, 0);    /*  clear remaining elems on stack */
        }

        return NGX_ERROR;
    }

    NGX_LUA_EXCEPTION_TRY {
        rdata = (u_char *) lua_tolstring(L, -1, &rlen);

        if (rdata) {
            val->data = ngx_pcalloc(r->pool, rlen);
            if (val->data == NULL) {
                return NGX_ERROR;
            }

            ngx_memcpy(val->data, rdata, rlen);
            val->len = rlen;

        } else {
            val->data = NULL;
            val->len = 0;
        }

    } NGX_LUA_EXCEPTION_CATCH {
        dd("nginx execution restored");
    }

    /*  clear Lua stack */
    lua_settop(L, 0);

    return NGX_OK;
}
Example #8
0
ngx_int_t
ngx_http_lua_set_by_chunk(lua_State *L, ngx_http_request_t *r, ngx_str_t *val,
    ngx_http_variable_value_t *args, size_t nargs, ngx_str_t *script)
{
    size_t           i;
    ngx_int_t        rc;
    u_char          *err_msg;
    size_t           len;
    u_char          *data;
#if (NGX_PCRE)
    ngx_pool_t      *old_pool;
#endif

    dd("nargs: %d", (int) nargs);

    dd("set Lua VM panic handler");

    lua_atpanic(L, ngx_http_lua_atpanic);

    NGX_LUA_EXCEPTION_TRY {
        dd("initialize nginx context in Lua VM, code chunk at "
           "stack top    sp = 1");
        ngx_http_lua_set_by_lua_env(L, r, nargs, args);

        /*  passing directive arguments to the user code */
        for (i = 0; i < nargs; i++) {
            lua_pushlstring(L, (const char *) args[i].data, args[i].len);
        }

#if (NGX_PCRE)
        /* XXX: work-around to nginx regex subsystem */
        old_pool = ngx_http_lua_pcre_malloc_init(r->pool);
#endif

        lua_pushcfunction(L, ngx_http_lua_traceback);
        lua_insert(L, 1);  /* put it under chunk and args */

        dd("protected call user code");

        rc = lua_pcall(L, nargs, 1, 1);

        dd("after protected call user code");

        lua_remove(L, 1);  /* remove traceback function */

#if (NGX_PCRE)
        /* XXX: work-around to nginx regex subsystem */
        ngx_http_lua_pcre_malloc_done(old_pool);
#endif

        if (rc != 0) {
            /*  error occured when running loaded code */
            err_msg = (u_char *) lua_tolstring(L, -1, &len);

            if (err_msg == NULL) {
                err_msg = (u_char *) "unknown reason";
                len = sizeof("unknown reason") - 1;
            }

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "failed to run set_by_lua*: %*s", len, err_msg);

            lua_settop(L, 0);    /*  clear remaining elems on stack */

            return NGX_ERROR;
        }

        data = (u_char *) lua_tolstring(L, -1, &len);

        if (data) {
            val->data = ngx_palloc(r->pool, len);
            if (val->data == NULL) {
                return NGX_ERROR;
            }

            ngx_memcpy(val->data, data, len);
            val->len = len;

        } else {
            val->data = NULL;
            val->len = 0;
        }

    } NGX_LUA_EXCEPTION_CATCH {

        dd("nginx execution restored");
        return NGX_ERROR;
    }

    /*  clear Lua stack */
    lua_settop(L, 0);

    return NGX_OK;
}
ngx_int_t
ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r,
        ngx_http_lua_ctx_t *ctx, int nret)
{
    int                      rv;
    int                      cc_ref;
    lua_State               *cc;
    const char              *err, *msg;
    ngx_int_t                rc;

    /* set Lua VM panic handler */
    lua_atpanic(L, ngx_http_lua_atpanic);

    dd("ctx = %p", ctx);

    NGX_LUA_EXCEPTION_TRY {
        cc = ctx->cc;
        cc_ref = ctx->cc_ref;

        /* XXX: work-around to nginx regex subsystem */
        ngx_http_lua_pcre_malloc_init(r->pool);

        /*  run code */
        rv = lua_resume(cc, nret);

        /* XXX: work-around to nginx regex subsystem */
        ngx_http_lua_pcre_malloc_done();

        dd("lua resume returns %d", (int) rv);

        switch (rv) {
            case LUA_YIELD:
                /*  yielded, let event handler do the rest job */
                /*  FIXME: add io cmd dispatcher here */

                dd("lua coroutine yielded");

#if 0
                ngx_http_lua_dump_postponed(r);
#endif

                lua_settop(cc, 0);
                return NGX_AGAIN;

            case 0:
                dd("normal end %.*s", (int) r->uri.len, r->uri.data);

#if 0
                ngx_http_lua_dump_postponed(r);
#endif

                ngx_http_lua_del_thread(r, L, cc_ref, 0);
                ctx->cc_ref = LUA_NOREF;

                if (ctx->cleanup) {
                    dd("cleaning up cleanup");
                    *ctx->cleanup = NULL;
                    ctx->cleanup = NULL;
                }

                if (ctx->entered_content_phase) {
                    rc = ngx_http_lua_send_chain_link(r, ctx,
                            NULL /* indicate last_buf */);

                    if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
                        return rc;
                    }
                }

                return NGX_OK;

            case LUA_ERRRUN:
                err = "runtime error";
                break;

            case LUA_ERRSYNTAX:
                err = "syntax error";
                break;

            case LUA_ERRMEM:
                err = "memory allocation error";
                break;

            case LUA_ERRERR:
                err = "error handler error";
                break;

            default:
                err = "unknown error";
                break;
        }

        if (lua_isstring(cc, -1)) {
            dd("user custom error msg");
            msg = lua_tostring(cc, -1);

        } else {
            if (lua_isnil(cc, -1)) {
                if (ctx->exited) {
                    dd("run here...exiting... %d", (int) ctx->exit_code);

                    ngx_http_lua_del_thread(r, L, cc_ref, 0);
                    ctx->cc_ref = LUA_NOREF;

                    if (ctx->cleanup) {
                        *ctx->cleanup = NULL;
                        ctx->cleanup = NULL;
                    }

                    if ((ctx->exit_code == NGX_OK &&
                                ctx->entered_content_phase) ||
                                (ctx->exit_code >= NGX_HTTP_OK &&
                                ctx->exit_code < NGX_HTTP_SPECIAL_RESPONSE))
                    {
                        rc = ngx_http_lua_send_chain_link(r, ctx,
                                NULL /* indicate last_buf */);

                        if (rc == NGX_ERROR ||
                                rc >= NGX_HTTP_SPECIAL_RESPONSE)
                        {
                            return rc;
                        }
                    }

                    return ctx->exit_code;
                }

                if (ctx->exec_uri.len) {
                    ngx_http_lua_del_thread(r, L, cc_ref, 0);
                    ctx->cc_ref = LUA_NOREF;

                    if (ctx->cleanup) {
                        *ctx->cleanup = NULL;
                        ctx->cleanup = NULL;
                    }

                    if (ctx->exec_uri.data[0] == '@') {
                        if (ctx->exec_args.len > 0) {
                            ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
                                    "query strings %V ignored when exec'ing "
                                    "named location %V",
                                    &ctx->exec_args, &ctx->exec_uri);
                        }

                        r->write_event_handler = ngx_http_request_empty_handler;

                        rc = ngx_http_named_location(r, &ctx->exec_uri);
                        if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE)
                        {
                            return rc;
                        }

                        if (! ctx->entered_content_phase &&
                                r != r->connection->data)
                        {
                            /* XXX ensure the main request ref count
                             * is decreased because the current
                             * request will be quit */
                            r->main->count--;
                        }

                        return NGX_DONE;
                    }

                    dd("internal redirect to %.*s", (int) ctx->exec_uri.len,
                            ctx->exec_uri.data);

                    /* resume the write event handler */
                    r->write_event_handler = ngx_http_request_empty_handler;

                    rc = ngx_http_internal_redirect(r, &ctx->exec_uri,
                            &ctx->exec_args);

                    dd("internal redirect returned %d when in content phase? "
                            "%d", (int) rc, ctx->entered_content_phase);

                    if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
                        return rc;
                    }

                    dd("XXYY HERE %d\n", (int) r->main->count);

                    if (! ctx->entered_content_phase &&
                            r != r->connection->data)
                    {
                        /* XXX ensure the main request ref count
                         * is decreased because the current
                         * request will be quit */
                        r->main->count--;
                    }

                    return NGX_DONE;
                }
            }

            msg = "unknown reason";
        }

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                "lua handler aborted: %s: %s",
                err, msg);

        ngx_http_lua_del_thread(r, L, cc_ref, 0);
        ctx->cc_ref = LUA_NOREF;

        if (ctx->cleanup) {
            *ctx->cleanup = NULL;
            ctx->cleanup = NULL;
        }

        dd("headers sent? %d", ctx->headers_sent ? 1 : 0);

        return ctx->headers_sent ? NGX_ERROR : NGX_HTTP_INTERNAL_SERVER_ERROR;

    } NGX_LUA_EXCEPTION_CATCH {
        dd("nginx execution restored");
    }

    return NGX_ERROR;
}
Example #10
0
ngx_int_t
ngx_http_lua_set_by_chunk(lua_State *L, ngx_http_request_t *r, ngx_str_t *val,
        ngx_http_variable_value_t *args, size_t nargs, ngx_str_t *script)
{
    size_t           i;
    ngx_int_t        rc;
    u_char          *err_msg;
    size_t           rlen;
    u_char          *rdata;
#if (NGX_PCRE)
    ngx_pool_t      *old_pool;
#endif

    ngx_http_lua_ctx_t          *ctx;
    ngx_http_cleanup_t          *cln;

    dd("nargs: %d", (int) nargs);

    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);

    if (ctx == NULL) {
        ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_lua_ctx_t));
        if (ctx == NULL) {
            return NGX_ERROR;
        }

        dd("setting new ctx: ctx = %p", ctx);

        ctx->cc_ref = LUA_NOREF;
        ctx->ctx_ref = LUA_NOREF;

        ngx_http_set_ctx(r, ctx, ngx_http_lua_module);

    } else {
        ngx_http_lua_reset_ctx(r, L, ctx);
    }

    if (ctx->cleanup == NULL) {
        cln = ngx_http_cleanup_add(r, 0);
        if (cln == NULL) {
            return NGX_ERROR;
        }

        cln->handler = ngx_http_lua_request_cleanup;
        cln->data = r;
        ctx->cleanup = &cln->handler;
    }

    /*  set Lua VM panic handler */
    lua_atpanic(L, ngx_http_lua_atpanic);

    /*  initialize nginx context in Lua VM, code chunk at stack top    sp = 1 */
    ngx_http_lua_set_by_lua_env(L, r, nargs, args);

    /*  passing directive arguments to the user code */
    for (i = 0; i < nargs; i++) {
        lua_pushlstring(L, (const char *) args[i].data, args[i].len);
    }

#if (NGX_PCRE)
    /* XXX: work-around to nginx regex subsystem */
    old_pool = ngx_http_lua_pcre_malloc_init(r->pool);
#endif

    /*  protected call user code */
    rc = lua_pcall(L, nargs, 1, 0);

#if (NGX_PCRE)
    /* XXX: work-around to nginx regex subsystem */
    ngx_http_lua_pcre_malloc_done(old_pool);
#endif

    if (rc != 0) {
        /*  error occured when running loaded code */
        err_msg = (u_char *) lua_tostring(L, -1);

        if (err_msg != NULL) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "(lua-error) %s",
                    err_msg);

            lua_settop(L, 0);    /*  clear remaining elems on stack */
        }

        return NGX_ERROR;
    }

    NGX_LUA_EXCEPTION_TRY {
        rdata = (u_char *) lua_tolstring(L, -1, &rlen);

        if (rdata) {
            val->data = ngx_pcalloc(r->pool, rlen);
            if (val->data == NULL) {
                return NGX_ERROR;
            }

            ngx_memcpy(val->data, rdata, rlen);
            val->len = rlen;

        } else {
            val->data = NULL;
            val->len = 0;
        }

    } NGX_LUA_EXCEPTION_CATCH {
        dd("nginx execution restored");
    }

    /*  clear Lua stack */
    lua_settop(L, 0);

    return NGX_OK;
}
static int
ngx_http_lua_ngx_re_match(lua_State *L)
{
    /* u_char                      *p; */
    ngx_http_request_t          *r;
    ngx_str_t                    subj;
    ngx_str_t                    pat;
    ngx_str_t                    opts;
    ngx_lua_regex_compile_t      re_comp;
    ngx_http_lua_regex_t        *re;
    const char                  *msg;
    ngx_int_t                    rc;
    ngx_uint_t                   n;
    int                          i;
    ngx_int_t                    pos = 0;
    int                          nargs;
    int                         *cap = NULL;
    int                          ovecsize;
    ngx_uint_t                   flags;
    ngx_pool_t                  *pool, *old_pool;
    ngx_http_lua_main_conf_t    *lmcf = NULL;
    u_char                       errstr[NGX_MAX_CONF_ERRSTR + 1];
    pcre_extra                  *sd = NULL;

    nargs = lua_gettop(L);

    if (nargs != 2 && nargs != 3 && nargs != 4) {
        return luaL_error(L, "expecting two or three or four arguments, "
                "but got %d", nargs);
    }

    lua_pushlightuserdata(L, &ngx_http_lua_request_key);
    lua_rawget(L, LUA_GLOBALSINDEX);
    r = lua_touserdata(L, -1);
    lua_pop(L, 1);

    if (r == NULL) {
        return luaL_error(L, "no request object found");
    }

    subj.data = (u_char *) luaL_checklstring(L, 1, &subj.len);
    pat.data = (u_char *) luaL_checklstring(L, 2, &pat.len);

    ngx_memzero(&re_comp, sizeof(ngx_lua_regex_compile_t));

    if (nargs >= 3) {
        opts.data = (u_char *) luaL_checklstring(L, 3, &opts.len);

        if (nargs == 4) {
            luaL_checktype(L, 4, LUA_TTABLE);
            lua_getfield(L, 4, "pos");
            if (lua_isnumber(L, -1)) {
                pos = (ngx_int_t) lua_tointeger(L, -1);
                if (pos < 0) {
                    pos = 0;
                }

            } else if (lua_isnil(L, -1)) {
                pos = 0;

            } else {
                msg = lua_pushfstring(L, "bad pos field type in the ctx table "
                        "argument: %s",
                        luaL_typename(L, -1));

                return luaL_argerror(L, 4, msg);
            }

            lua_pop(L, 1);
        }

    } else {
        opts.data = (u_char *) "";
        opts.len = 0;
    }

    re_comp.options = 0;

    flags = ngx_http_lua_ngx_re_parse_opts(L, &re_comp, &opts, 3);

    if (flags & NGX_LUA_RE_COMPILE_ONCE) {
        lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);
        pool = lmcf->pool;

        dd("server pool %p", lmcf->pool);

        lua_pushlightuserdata(L, &ngx_http_lua_regex_cache_key);
        lua_rawget(L, LUA_REGISTRYINDEX); /* table */

        lua_pushliteral(L, "m");
        lua_pushvalue(L, 2); /* table regex */

        dd("options size: %d", (int) sizeof(re_comp.options));

        lua_pushlstring(L, (char *) &re_comp.options, sizeof(re_comp.options));
                /* table regex opts */

        lua_concat(L, 3); /* table key */
        lua_pushvalue(L, -1); /* table key key */

        dd("regex cache key: %.*s", (int) (pat.len + sizeof(re_comp.options)),
                lua_tostring(L, -1));

        lua_rawget(L, -3); /* table key re */
        re = lua_touserdata(L, -1);

        lua_pop(L, 1); /* table key */

        if (re) {
            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "lua regex cache hit for match regex \"%s\" with "
                    "options \"%s\"", pat.data, opts.data);

            lua_pop(L, 2);

            dd("restoring regex %p, ncaptures %d,  captures %p", re->regex,
                    re->ncaptures, re->captures);

            re_comp.regex = re->regex;
            sd = re->regex_sd;
            re_comp.captures = re->ncaptures;
            cap = re->captures;

            if (flags & NGX_LUA_RE_MODE_DFA) {
                ovecsize = 2;

            } else {
                ovecsize = (re->ncaptures + 1) * 3;
            }

            goto exec;
        }

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "lua regex cache miss for match regex \"%s\" "
                "with options \"%s\"", pat.data, opts.data);

        if (lmcf->regex_cache_entries >= lmcf->regex_cache_max_entries) {

            if (lmcf->regex_cache_entries == lmcf->regex_cache_max_entries) {
                ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
                        "lua exceeding regex cache max entries (%i)",
                        lmcf->regex_cache_max_entries);

                lmcf->regex_cache_entries++;
            }

            pool = r->pool;
            flags &= ~NGX_LUA_RE_COMPILE_ONCE;
        }

    } else {
        pool = r->pool;
    }

    dd("pool %p, r pool %p", pool, r->pool);

    re_comp.pattern = pat;
    re_comp.err.len = NGX_MAX_CONF_ERRSTR;
    re_comp.err.data = errstr;
    re_comp.pool = pool;

    ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
            "lua compiling match regex \"%s\" with options \"%s\" "
            "(compile once: %d) (dfa mode: %d) (jit mode: %d)",
            pat.data, opts.data,
            (flags & NGX_LUA_RE_COMPILE_ONCE) != 0,
            (flags & NGX_LUA_RE_MODE_DFA) != 0,
            (flags & NGX_LUA_RE_MODE_JIT) != 0);

    old_pool = ngx_http_lua_pcre_malloc_init(pool);

    rc = ngx_lua_regex_compile(&re_comp);

    ngx_http_lua_pcre_malloc_done(old_pool);

    if (rc != NGX_OK) {
        dd("compile failed");

        re_comp.err.data[re_comp.err.len] = '\0';
        msg = lua_pushfstring(L, "failed to compile regex \"%s\": %s",
                pat.data, re_comp.err.data);

        return luaL_argerror(L, 2, msg);
    }

#if LUA_HAVE_PCRE_JIT

    if (flags & NGX_LUA_RE_MODE_JIT) {

        old_pool = ngx_http_lua_pcre_malloc_init(pool);

        sd = pcre_study(re_comp.regex, PCRE_STUDY_JIT_COMPILE, &msg);

        ngx_http_lua_pcre_malloc_done(old_pool);

#   if (NGX_DEBUG)
        dd("sd = %p", sd);

        if (msg != NULL) {
            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "pcre study failed with PCRE_STUDY_JIT_COMPILE: %s (%p)",
                msg, sd);
        }

        if (sd != NULL) {
            int         jitted;

            old_pool = ngx_http_lua_pcre_malloc_init(pool);

            pcre_fullinfo(re_comp.regex, sd, PCRE_INFO_JIT, &jitted);

            ngx_http_lua_pcre_malloc_done(old_pool);

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "pcre JIT compiling result: %d", jitted);
        }
#   endif /* NGX_DEBUG */

    } else {
        old_pool = ngx_http_lua_pcre_malloc_init(pool);

        sd = pcre_study(re_comp.regex, 0, &msg);

        ngx_http_lua_pcre_malloc_done(old_pool);

#   if (NGX_DEBUG)
        dd("sd = %p", sd);

        if (msg != NULL) {
            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "pcre_study failed with PCRE_STUDY_JIT_COMPILE: %s (%p)",
                msg, sd);
        }
#   endif /* NGX_DEBUG */
    }

#else  /* LUA_HAVE_PCRE_JIT */

    if (flags & NGX_LUA_RE_MODE_JIT) {
        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "your pcre build does not have JIT support and "
                "the \"j\" regex option is ignored");
    }

#endif /* LUA_HAVE_PCRE_JIT */

    dd("compile done, captures %d", (int) re_comp.captures);

    if (flags & NGX_LUA_RE_MODE_DFA) {
        ovecsize = 2;

    } else {
        ovecsize = (re_comp.captures + 1) * 3;
    }

    dd("allocating cap with size: %d", (int) ovecsize);

    cap = ngx_palloc(pool, ovecsize * sizeof(int));

    if (cap == NULL) {
        flags &= ~NGX_LUA_RE_COMPILE_ONCE;
        msg = "out of memory";
        goto error;
    }

    if (flags & NGX_LUA_RE_COMPILE_ONCE) {

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "lua saving compiled regex (%d captures) into the cache "
                "(entries %i)", re_comp.captures,
                 lmcf ? lmcf->regex_cache_entries : 0);

        re = ngx_palloc(pool, sizeof(ngx_http_lua_regex_t));
        if (re == NULL) {
            return luaL_error(L, "out of memory");
        }

        dd("saving regex %p, ncaptures %d,  captures %p", re_comp.regex,
                re_comp.captures, cap);

        re->regex = re_comp.regex;
        re->regex_sd = sd;
        re->ncaptures = re_comp.captures;
        re->captures = cap;
        re->replace = NULL;

        lua_pushlightuserdata(L, re); /* table key value */
        lua_rawset(L, -3); /* table */
        lua_pop(L, 1);

        if (lmcf) {
            lmcf->regex_cache_entries++;
        }
    }

exec:
    if (flags & NGX_LUA_RE_MODE_DFA) {

#if LUA_HAVE_PCRE_DFA

        int ws[NGX_LUA_RE_DFA_MODE_WORKSPACE_COUNT];
        rc = ngx_http_lua_regex_dfa_exec(re_comp.regex, sd, &subj,
            (int) pos, cap, ovecsize, ws, NGX_LUA_RE_DFA_MODE_WORKSPACE_COUNT);

#else /* LUA_HAVE_PCRE_DFA */

        msg = "at least pcre 6.0 is required for the DFA mode";
        goto error;

#endif /* LUA_HAVE_PCRE_DFA */

    } else {
        rc = ngx_http_lua_regex_exec(re_comp.regex, sd, &subj, (int) pos, cap,
                ovecsize);
    }

    if (rc == NGX_REGEX_NO_MATCHED) {
        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "regex \"%s\" not matched on string \"%s\" starting from %z",
                pat.data, subj.data, pos);

        if (!(flags & NGX_LUA_RE_COMPILE_ONCE)) {
            if (sd) {
                ngx_http_lua_regex_free_study_data(pool, sd);
            }

            ngx_pfree(pool, re_comp.regex);
            ngx_pfree(pool, cap);
        }

        lua_pushnil(L);
        return 1;
    }

    if (rc < 0) {
        msg = lua_pushfstring(L, ngx_regex_exec_n " failed: %d on \"%s\" "
            "using \"%s\"", (int) rc, subj.data, pat.data);
        goto error;
    }

    if (rc == 0) {
        if (flags & NGX_LUA_RE_MODE_DFA) {
            rc = 1;

        } else {
            msg = "capture size too small";
            goto error;
        }
    }

    dd("rc = %d", (int) rc);

    lua_createtable(L, rc - 1 /* narr */, 1 /* nrec */);

    for (i = 0, n = 0; i < rc; i++, n += 2) {
        dd("capture %d: %d %d", i, cap[n], cap[n + 1]);
        if (cap[n] < 0) {
            lua_pushnil(L);

        } else {
            lua_pushlstring(L, (char *) &subj.data[cap[n]],
                    cap[n + 1] - cap[n]);

            dd("pushing capture %s at %d", lua_tostring(L, -1), (int) i);
        }

        lua_rawseti(L, -2, (int) i);
    }

    if (nargs == 4) { /* having ctx table */
        pos = cap[1];
        lua_pushinteger(L, (lua_Integer) pos);
        lua_setfield(L, 4, "pos");
    }

    if (!(flags & NGX_LUA_RE_COMPILE_ONCE)) {

        if (sd) {
            ngx_http_lua_regex_free_study_data(pool, sd);
        }

        ngx_pfree(pool, re_comp.regex);
        ngx_pfree(pool, cap);
    }

    return 1;

error:
    if (!(flags & NGX_LUA_RE_COMPILE_ONCE)) {
        if (sd) {
            ngx_http_lua_regex_free_study_data(pool, sd);
        }

        if (re_comp.regex) {
            ngx_pfree(pool, re_comp.regex);
        }

        if (cap) {
            ngx_pfree(pool, cap);
        }
    }

    return luaL_error(L, msg);
}
static int
ngx_http_lua_ngx_re_gmatch(lua_State *L)
{
    ngx_http_lua_main_conf_t    *lmcf = NULL;
    ngx_http_request_t          *r;
    ngx_str_t                    subj;
    ngx_str_t                    pat;
    ngx_str_t                    opts;
    int                          ovecsize;
    ngx_http_lua_regex_t        *re;
    ngx_lua_regex_compile_t      re_comp;
    ngx_http_lua_regex_ctx_t    *ctx;
    const char                  *msg;
    int                          nargs;
    ngx_int_t                    flags;
    int                         *cap = NULL;
    ngx_int_t                    rc;
    ngx_pool_t                  *pool, *old_pool;
    u_char                       errstr[NGX_MAX_CONF_ERRSTR + 1];
    pcre_extra                  *sd = NULL;
    ngx_http_cleanup_t          *cln;

    nargs = lua_gettop(L);

    if (nargs != 2 && nargs != 3) {
        return luaL_error(L, "expecting two or three arguments, but got %d",
                nargs);
    }

    lua_pushlightuserdata(L, &ngx_http_lua_request_key);
    lua_rawget(L, LUA_GLOBALSINDEX);
    r = lua_touserdata(L, -1);
    lua_pop(L, 1);

    if (r == NULL) {
        return luaL_error(L, "no request object found");
    }

    subj.data = (u_char *) luaL_checklstring(L, 1, &subj.len);
    pat.data = (u_char *) luaL_checklstring(L, 2, &pat.len);

    if (nargs == 3) {
        opts.data = (u_char *) luaL_checklstring(L, 3, &opts.len);
        lua_pop(L, 1);

    } else {
        opts.data = (u_char *) "";
        opts.len = 0;
    }

    /* stack: subj regex */

    re_comp.options = 0;

    flags = ngx_http_lua_ngx_re_parse_opts(L, &re_comp, &opts, 3);

    if (flags & NGX_LUA_RE_COMPILE_ONCE) {
        lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);
        pool = lmcf->pool;

        dd("server pool %p", lmcf->pool);

        lua_pushlightuserdata(L, &ngx_http_lua_regex_cache_key);
        lua_rawget(L, LUA_REGISTRYINDEX); /* table */

        lua_pushliteral(L, "m");
        lua_pushvalue(L, 2); /* table regex */

        dd("options size: %d", (int) sizeof(re_comp.options));

        lua_pushlstring(L, (char *) &re_comp.options,
                sizeof(re_comp.options)); /* table regex opts */

        lua_concat(L, 3); /* table key */
        lua_pushvalue(L, -1); /* table key key */

        dd("regex cache key: %.*s", (int) (pat.len + sizeof(re_comp.options)),
                lua_tostring(L, -1));

        lua_rawget(L, -3); /* table key re */
        re = lua_touserdata(L, -1);

        lua_pop(L, 1); /* table key */

        if (re) {
            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "lua regex cache hit for match regex \"%s\" "
                    "with options \"%s\"", pat.data, opts.data);

            lua_pop(L, 2);

            dd("restoring regex %p, ncaptures %d,  captures %p", re->regex,
                    re->ncaptures, re->captures);

            re_comp.regex = re->regex;
            sd = re->regex_sd;
            re_comp.captures = re->ncaptures;
            cap = re->captures;

            if (flags & NGX_LUA_RE_MODE_DFA) {
                ovecsize = 2;

            } else {
                ovecsize = (re->ncaptures + 1) * 3;
            }

            goto compiled;
        }

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "lua regex cache miss for match regex \"%s\" "
                "with options \"%s\"", pat.data, opts.data);

        if (lmcf->regex_cache_entries >= lmcf->regex_cache_max_entries) {

            if (lmcf->regex_cache_entries == lmcf->regex_cache_max_entries) {
                ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
                        "lua exceeding regex cache max entries (%i)",
                        lmcf->regex_cache_max_entries);

                lmcf->regex_cache_entries++;
            }

            pool = r->pool;
            flags &= ~NGX_LUA_RE_COMPILE_ONCE;
        }

    } else {
        pool = r->pool;
    }

    re_comp.pattern = pat;
    re_comp.err.len = NGX_MAX_CONF_ERRSTR;
    re_comp.err.data = errstr;
    re_comp.pool = pool;

    ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
            "lua compiling gmatch regex \"%s\" with options \"%s\" "
            "(compile once: %d) (dfa mode: %d) (jit mode: %d)",
            pat.data, opts.data,
            (flags & NGX_LUA_RE_COMPILE_ONCE) != 0,
            (flags & NGX_LUA_RE_MODE_DFA) != 0,
            (flags & NGX_LUA_RE_MODE_JIT) != 0);

    old_pool = ngx_http_lua_pcre_malloc_init(pool);

    rc = ngx_lua_regex_compile(&re_comp);

    ngx_http_lua_pcre_malloc_done(old_pool);

    if (rc != NGX_OK) {
        dd("compile failed");

        re_comp.err.data[re_comp.err.len] = '\0';
        msg = lua_pushfstring(L, "failed to compile regex \"%s\": %s",
                pat.data, re_comp.err.data);

        return luaL_argerror(L, 2, msg);
    }

#if LUA_HAVE_PCRE_JIT

    if (flags & NGX_LUA_RE_MODE_JIT) {

        old_pool = ngx_http_lua_pcre_malloc_init(pool);

        sd = pcre_study(re_comp.regex, PCRE_STUDY_JIT_COMPILE, &msg);

        ngx_http_lua_pcre_malloc_done(old_pool);

#   if (NGX_DEBUG)
        dd("sd = %p", sd);

        if (msg != NULL) {
            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "pcre_study failed with PCRE_STUDY_JIT_COMPILE: %s (%p)",
                msg, sd);
        }

        if (sd != NULL) {
            int         jitted;

            old_pool = ngx_http_lua_pcre_malloc_init(pool);

            pcre_fullinfo(re_comp.regex, sd, PCRE_INFO_JIT, &jitted);

            ngx_http_lua_pcre_malloc_done(old_pool);

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "pcre JIT compiling result: %d", jitted);
        }
#   endif /* NGX_DEBUG */

    } else {

        old_pool = ngx_http_lua_pcre_malloc_init(pool);

        sd = pcre_study(re_comp.regex, 0, &msg);

        ngx_http_lua_pcre_malloc_done(old_pool);

#   if (NGX_DEBUG)
        dd("sd = %p", sd);

        if (msg != NULL) {
            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "pcre study failed with PCRE_STUDY_JIT_COMPILE: %s (%p)",
                msg, sd);
        }
#   endif /* NGX_DEBUG */
    }

#else  /* LUA_HAVE_PCRE_JIT */

    if (flags & NGX_LUA_RE_MODE_JIT) {
        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "your pcre build does not have JIT support and "
                "the \"j\" regex option is ignored");
    }

#endif /* LUA_HAVE_PCRE_JIT */

    dd("compile done, captures %d", re_comp.captures);

    if (flags & NGX_LUA_RE_MODE_DFA) {
        ovecsize = 2;

    } else {
        ovecsize = (re_comp.captures + 1) * 3;
    }

    cap = ngx_palloc(pool, ovecsize * sizeof(int));
    if (cap == NULL) {
        flags &= ~NGX_LUA_RE_COMPILE_ONCE;
        msg = "out of memory";
        goto error;
    }

    if (flags & NGX_LUA_RE_COMPILE_ONCE) {

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "lua saving compiled regex (%d captures) into the cache "
                "(entries %i)", re_comp.captures,
                lmcf ? lmcf->regex_cache_entries : 0);

        re = ngx_palloc(pool, sizeof(ngx_http_lua_regex_t));
        if (re == NULL) {
            return luaL_error(L, "out of memory");
        }

        dd("saving regex %p, ncaptures %d,  captures %p", re_comp.regex,
                re_comp.captures, cap);

        re->regex = re_comp.regex;
        re->regex_sd = sd;
        re->ncaptures = re_comp.captures;
        re->captures = cap;
        re->replace = NULL;

        lua_pushlightuserdata(L, re); /* table key value */
        lua_rawset(L, -3); /* table */
        lua_pop(L, 1);

        if (lmcf) {
            lmcf->regex_cache_entries++;
        }
    }

compiled:
    lua_settop(L, 1);

    ctx = lua_newuserdata(L, sizeof(ngx_http_lua_regex_ctx_t));

    ctx->request = r;
    ctx->regex = re_comp.regex;
    ctx->regex_sd = sd;
    ctx->ncaptures = re_comp.captures;
    ctx->captures = cap;
    ctx->captures_len = ovecsize;
    ctx->flags = (uint8_t) flags;

    if (!(flags & NGX_LUA_RE_COMPILE_ONCE)) {
        cln = ngx_http_cleanup_add(r, 0);
        if (cln == NULL) {
            return luaL_error(L, "out of memory");
        }

        cln->handler = ngx_http_lua_ngx_re_gmatch_cleanup;
        cln->data = ctx;
    }

    lua_pushinteger(L, 0);

    /* upvalues in order: subj ctx offset */
    lua_pushcclosure(L, ngx_http_lua_ngx_re_gmatch_iterator, 3);

    return 1;

error:
    if (!(flags & NGX_LUA_RE_COMPILE_ONCE)) {
        if (sd) {
            ngx_http_lua_regex_free_study_data(pool, sd);
        }

        if (re_comp.regex) {
            ngx_pfree(pool, re_comp.regex);
        }

        if (cap) {
            ngx_pfree(pool, cap);
        }
    }

    return luaL_error(L, msg);
}
static int
ngx_http_lua_ngx_re_sub_helper(lua_State *L, unsigned global)
{
    ngx_http_lua_regex_t        *re;
    ngx_http_request_t          *r;
    ngx_str_t                    subj;
    ngx_str_t                    pat;
    ngx_str_t                    opts;
    ngx_str_t                    tpl;
    ngx_http_lua_main_conf_t    *lmcf = NULL;
    ngx_pool_t                  *pool, *old_pool;
    ngx_lua_regex_compile_t      re_comp;
    const char                  *msg;
    ngx_int_t                    rc;
    ngx_uint_t                   n;
    ngx_int_t                    i;
    int                          nargs;
    int                         *cap = NULL;
    int                          ovecsize;
    int                          type;
    unsigned                     func;
    int                          offset;
    size_t                       count;
    luaL_Buffer                  luabuf;
    ngx_int_t                    flags;
    u_char                      *p;
    u_char                       errstr[NGX_MAX_CONF_ERRSTR + 1];
    pcre_extra                  *sd = NULL;

    ngx_http_lua_complex_value_t              *ctpl = NULL;
    ngx_http_lua_compile_complex_value_t       ccv;

    nargs = lua_gettop(L);

    if (nargs != 3 && nargs != 4) {
        return luaL_error(L, "expecting three or four arguments, but got %d",
                nargs);
    }

    lua_pushlightuserdata(L, &ngx_http_lua_request_key);
    lua_rawget(L, LUA_GLOBALSINDEX);
    r = lua_touserdata(L, -1);
    lua_pop(L, 1);

    if (r == NULL) {
        return luaL_error(L, "no request object found");
    }

    subj.data = (u_char *) luaL_checklstring(L, 1, &subj.len);
    pat.data = (u_char *) luaL_checklstring(L, 2, &pat.len);

    func = 0;

    type = lua_type(L, 3);
    switch (type) {
        case LUA_TFUNCTION:
            func = 1;
            tpl.len = 0;
            tpl.data = (u_char *) "";
            break;

        case LUA_TNUMBER:
        case LUA_TSTRING:
            tpl.data = (u_char *) lua_tolstring(L, 3, &tpl.len);
            break;

        default:
            msg = lua_pushfstring(L, "string, number, or function expected, "
                    "got %s", lua_typename(L, type));
            return luaL_argerror(L, 3, msg);
    }

    ngx_memzero(&re_comp, sizeof(ngx_lua_regex_compile_t));

    if (nargs == 4) {
        opts.data = (u_char *) luaL_checklstring(L, 4, &opts.len);
        lua_pop(L, 1);

    } else { /* nargs == 3 */
        opts.data = (u_char *) "";
        opts.len = 0;
    }

    /* stack: subj regex repl */

    re_comp.options = 0;

    flags = ngx_http_lua_ngx_re_parse_opts(L, &re_comp, &opts, 4);

    if (flags & NGX_LUA_RE_COMPILE_ONCE) {
        lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);
        pool = lmcf->pool;

        dd("server pool %p", lmcf->pool);

        lua_pushlightuserdata(L, &ngx_http_lua_regex_cache_key);
        lua_rawget(L, LUA_REGISTRYINDEX); /* table */

        lua_pushliteral(L, "s");
        lua_pushinteger(L, tpl.len);
        lua_pushliteral(L, ":");
        lua_pushvalue(L, 2);

        if (tpl.len != 0) {
            lua_pushvalue(L, 3);
        }

        dd("options size: %d", (int) sizeof(re_comp.options));

        lua_pushlstring(L, (char *) &re_comp.options, sizeof(re_comp.options));
                /* table regex opts */

        if (tpl.len == 0) {
            lua_concat(L, 5); /* table key */

        } else {
            lua_concat(L, 6); /* table key */
        }

        lua_pushvalue(L, -1); /* table key key */

        dd("regex cache key: %.*s", (int) (pat.len + sizeof(re_comp.options)),
                lua_tostring(L, -1));

        lua_rawget(L, -3); /* table key re */
        re = lua_touserdata(L, -1);

        lua_pop(L, 1); /* table key */

        if (re) {
            ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "lua regex cache hit for sub regex \"%s\" with options "
                    "\"%s\" and replace \"%s\"", pat.data, opts.data,
                    func ? (u_char *) "<func>" : tpl.data);

            lua_pop(L, 2);

            dd("restoring regex %p, ncaptures %d,  captures %p", re->regex,
                    re->ncaptures, re->captures);

            re_comp.regex = re->regex;
            sd = re->regex_sd;
            re_comp.captures = re->ncaptures;
            cap = re->captures;
            ctpl = re->replace;

            if (flags & NGX_LUA_RE_MODE_DFA) {
                ovecsize = 2;

            } else {
                ovecsize = (re->ncaptures + 1) * 3;
            }

            goto exec;
        }

        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "lua regex cache miss for %ssub regex \"%s\" with options "
                "\"%s\" and replace \"%s\"",
                global ? "g" : "",
                pat.data, opts.data,
                func ? (u_char *) "<func>" : tpl.data);

        if (lmcf->regex_cache_entries >= lmcf->regex_cache_max_entries) {

            if (lmcf->regex_cache_entries == lmcf->regex_cache_max_entries) {
                ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
                        "lua exceeding regex cache max entries (%i)",
                        lmcf->regex_cache_max_entries);

                lmcf->regex_cache_entries++;
            }

            pool = r->pool;
            flags &= ~NGX_LUA_RE_COMPILE_ONCE;
        }

    } else {
        pool = r->pool;
    }

    re_comp.pattern = pat;
    re_comp.err.len = NGX_MAX_CONF_ERRSTR;
    re_comp.err.data = errstr;
    re_comp.pool = pool;

    dd("compiling regex");

    ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
            "lua compiling %ssub regex \"%s\" with options \"%s\" "
            "(compile once: %d) (dfa mode: %d) (jit mode: %d)",
            global ? "g" : "", pat.data, opts.data,
            (flags & NGX_LUA_RE_COMPILE_ONCE) != 0,
            (flags & NGX_LUA_RE_MODE_DFA) != 0,
            (flags & NGX_LUA_RE_MODE_JIT) != 0);

    old_pool = ngx_http_lua_pcre_malloc_init(pool);

    rc = ngx_lua_regex_compile(&re_comp);

    ngx_http_lua_pcre_malloc_done(old_pool);

    if (rc != NGX_OK) {
        dd("compile failed");

        re_comp.err.data[re_comp.err.len] = '\0';
        msg = lua_pushfstring(L, "failed to compile regex \"%s\": %s",
                pat.data, re_comp.err.data);

        return luaL_argerror(L, 2, msg);
    }

#if LUA_HAVE_PCRE_JIT

    if (flags & NGX_LUA_RE_MODE_JIT) {

        old_pool = ngx_http_lua_pcre_malloc_init(pool);

        sd = pcre_study(re_comp.regex, PCRE_STUDY_JIT_COMPILE, &msg);

        ngx_http_lua_pcre_malloc_done(old_pool);

#   if (NGX_DEBUG)
        dd("sd = %p", sd);

        if (msg != NULL) {
            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "pcre study failed with PCRE_STUDY_JIT_COMPILE: %s (%p)",
                msg, sd);
        }

        if (sd != NULL) {
            int         jitted;

            old_pool = ngx_http_lua_pcre_malloc_init(pool);

            pcre_fullinfo(re_comp.regex, sd, PCRE_INFO_JIT, &jitted);

            ngx_http_lua_pcre_malloc_done(old_pool);

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "pcre JIT compiling result: %d", jitted);
        }
#   endif /* NGX_DEBUG */

    } else {

        old_pool = ngx_http_lua_pcre_malloc_init(pool);

        sd = pcre_study(re_comp.regex, 0, &msg);

        ngx_http_lua_pcre_malloc_done(old_pool);

#   if (NGX_DEBUG)
        dd("sd = %p", sd);

        if (msg != NULL) {
            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "pcre_study failed with PCRE_STUDY_JIT_COMPILE: %s (%p)",
                msg, sd);
        }
#   endif /* NGX_DEBUG */
    }

#else  /* LUA_HAVE_PCRE_JIT */

    if (flags & NGX_LUA_RE_MODE_JIT) {
        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "your pcre build does not have JIT support and "
                "the \"j\" regex option is ignored");
    }

#endif /* LUA_HAVE_PCRE_JIT */

    dd("compile done, captures %d", re_comp.captures);

    if (flags & NGX_LUA_RE_MODE_DFA) {
        ovecsize = 2;

    } else {
        ovecsize = (re_comp.captures + 1) * 3;
    }

    cap = ngx_palloc(pool, ovecsize * sizeof(int));
    if (cap == NULL) {
        flags &= ~NGX_LUA_RE_COMPILE_ONCE;
        msg = "out of memory";
        goto error;
    }

    if (func) {
        ctpl = NULL;

    } else {
        ctpl = ngx_palloc(pool, sizeof(ngx_http_lua_complex_value_t));
        if (ctpl == NULL) {
            flags &= ~NGX_LUA_RE_COMPILE_ONCE;
            msg = "out of memory";
            goto error;
        }

        if ((flags & NGX_LUA_RE_COMPILE_ONCE) && tpl.len != 0) {
            /* copy the string buffer pointed to by tpl.data from Lua VM */
            p = ngx_palloc(pool, tpl.len + 1);
            if (p == NULL) {
                flags &= ~NGX_LUA_RE_COMPILE_ONCE;
                msg = "out of memory";
                goto error;
            }

            ngx_memcpy(p, tpl.data, tpl.len);
            p[tpl.len] = '\0';

            tpl.data = p;
        }

        ngx_memzero(&ccv, sizeof(ngx_http_lua_compile_complex_value_t));
        ccv.pool = pool;
        ccv.log = r->connection->log;
        ccv.value = &tpl;
        ccv.complex_value = ctpl;

        if (ngx_http_lua_compile_complex_value(&ccv) != NGX_OK) {
            ngx_pfree(pool, cap);
            ngx_pfree(pool, ctpl);

            if ((flags & NGX_LUA_RE_COMPILE_ONCE) && tpl.len != 0) {
                ngx_pfree(pool, tpl.data);
            }

            if (sd) {
                ngx_http_lua_regex_free_study_data(pool, sd);
            }

            ngx_pfree(pool, re_comp.regex);

            return luaL_error(L, "bad template for substitution: \"%s\"",
                    lua_tostring(L, 3));
        }
    }

    if (flags & NGX_LUA_RE_COMPILE_ONCE) {

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "lua saving compiled sub regex (%d captures) into the cache "
                "(entries %i)", re_comp.captures,
                lmcf ? lmcf->regex_cache_entries : 0);

        re = ngx_palloc(pool, sizeof(ngx_http_lua_regex_t));
        if (re == NULL) {
            return luaL_error(L, "out of memory");
        }

        dd("saving regex %p, ncaptures %d,  captures %p", re_comp.regex,
                re_comp.captures, cap);

        re->regex = re_comp.regex;
        re->regex_sd = sd;
        re->ncaptures = re_comp.captures;
        re->captures = cap;
        re->replace = ctpl;

        lua_pushlightuserdata(L, re); /* table key value */
        lua_rawset(L, -3); /* table */
        lua_pop(L, 1);

        if (lmcf) {
            lmcf->regex_cache_entries++;
        }
    }

exec:
    count = 0;
    offset = 0;

    for (;;) {
        if (subj.len == 0) {
            break;
        }

        if (flags & NGX_LUA_RE_MODE_DFA) {

#if LUA_HAVE_PCRE_DFA

            int ws[NGX_LUA_RE_DFA_MODE_WORKSPACE_COUNT];
            rc = ngx_http_lua_regex_dfa_exec(re_comp.regex, sd, &subj,
                offset, cap, ovecsize, ws, NGX_LUA_RE_DFA_MODE_WORKSPACE_COUNT);

#else /* LUA_HAVE_PCRE_DFA */

        msg = "at least pcre 6.0 is required for the DFA mode";
        goto error;

#endif /* LUA_HAVE_PCRE_DFA */

        } else {
            rc = ngx_http_lua_regex_exec(re_comp.regex, sd, &subj, offset, cap,
                    ovecsize);
        }

        if (rc == NGX_REGEX_NO_MATCHED) {
            break;
        }

        if (rc < 0) {
            msg = lua_pushfstring(L, ngx_regex_exec_n " failed: %d on \"%s\" "
                "using \"%s\"", (int) rc, subj.data, pat.data);
            goto error;
        }

        if (rc == 0) {
            if (flags & NGX_LUA_RE_MODE_DFA) {
                rc = 1;

            } else {
                msg = "capture size too small";
                goto error;
            }
        }

        dd("rc = %d", (int) rc);

        count++;

        if (count == 1) {
            luaL_buffinit(L, &luabuf);
        }

        if (func) {
            lua_pushvalue(L, -1);

            lua_createtable(L, rc - 1 /* narr */, 1 /* nrec */);

            for (i = 0, n = 0; i < rc; i++, n += 2) {
                dd("capture %d: %d %d", (int) i, cap[n], cap[n + 1]);
                if (cap[n] < 0) {
                    lua_pushnil(L);

                } else {
                    lua_pushlstring(L, (char *) &subj.data[cap[n]],
                            cap[n + 1] - cap[n]);

                    dd("pushing capture %s at %d", lua_tostring(L, -1),
                            (int) i);
                }

                lua_rawseti(L, -2, (int) i);
            }

            dd("stack size at call: %d", lua_gettop(L));

            lua_call(L, 1 /* nargs */, 1 /* nresults */);
            type = lua_type(L, -1);
            switch (type) {
                case LUA_TNUMBER:
                case LUA_TSTRING:
                    tpl.data = (u_char *) lua_tolstring(L, -1, &tpl.len);
                    break;

                default:
                    msg = lua_pushfstring(L, "string or number expected to be "
                            "returned by the replace function, got %s",
                            lua_typename(L, type));
                    return luaL_argerror(L, 3, msg);
            }

            luaL_addlstring(&luabuf, (char *) &subj.data[offset],
                    cap[0] - offset);

            luaL_addlstring(&luabuf, (char *) tpl.data, tpl.len);

            lua_pop(L, 1);

            offset = cap[1];

            if (global) {
                continue;
            }

            break;
        }

        rc = ngx_http_lua_complex_value(r, &subj, offset, rc, cap, ctpl,
                &luabuf);

        if (rc != NGX_OK) {
            msg = lua_pushfstring(L, "failed to eval the template for "
                "replacement: \"%s\"", tpl.data);
            goto error;
        }

        offset = cap[1];

        if (global) {
            continue;
        }

        break;
    }

    if (count == 0) {
        dd("no match, just the original subject");
        lua_settop(L, 1);

    } else {
        if (offset != (int) subj.len) {
            dd("adding trailer: %s (len %d)", &subj.data[offset],
                    (int) (subj.len - offset));

            luaL_addlstring(&luabuf, (char *) &subj.data[offset],
                    subj.len - offset);
        }

        luaL_pushresult(&luabuf);

        dd("the dst string: %s", lua_tostring(L, -1));
    }

    if (!(flags & NGX_LUA_RE_COMPILE_ONCE)) {
        if (sd) {
            ngx_http_lua_regex_free_study_data(pool, sd);
        }

        if (re_comp.regex) {
            ngx_pfree(pool, re_comp.regex);
        }

        if (ctpl) {
            ngx_pfree(pool, ctpl);
        }

        if (cap) {
            ngx_pfree(pool, cap);
        }
    }

    lua_pushinteger(L, count);
    return 2;

error:
    if (!(flags & NGX_LUA_RE_COMPILE_ONCE)) {
        if (sd) {
            ngx_http_lua_regex_free_study_data(pool, sd);
        }

        if (re_comp.regex) {
            ngx_pfree(pool, re_comp.regex);
        }

        if (ctpl) {
            ngx_pfree(pool, ctpl);
        }

        if (cap) {
            ngx_pfree(pool, cap);
        }
    }

    return luaL_error(L, msg);
}