ngx_int_t
ngx_http_echo_exec_exec(ngx_http_request_t *r,
        ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args)
{
    /* ngx_int_t                       rc; */
    ngx_str_t                       *uri;
    ngx_str_t                       *user_args;
    ngx_str_t                       args;
    ngx_uint_t                      flags;
    ngx_str_t                       *computed_arg;

    computed_arg = computed_args->elts;
    uri = &computed_arg[0];

    if (uri->len == 0) {
        return NGX_HTTP_BAD_REQUEST;
    }

    if (computed_args->nelts > 1) {
        user_args = &computed_arg[1];
    } else {
        user_args = NULL;
    }

    args.data = NULL;
    args.len = 0;
    if (ngx_http_parse_unsafe_uri(r, uri, &args, &flags)
            != NGX_OK) {
        ctx->headers_sent = 1;
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    if (args.len > 0 && user_args == NULL) {
        user_args = &args;
    }

    /*
    rc = ngx_http_echo_send_header_if_needed(r, ctx);
    if (r->header_only || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
        return rc;
    }
    */

    if (uri->data[0] == '@') {
        if (user_args && user_args->len > 0) {
            ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
                    "query strings %V ignored when exec'ing named location %V",
                    user_args, uri);

        }

        return ngx_http_named_location(r, uri);
    }

    return ngx_http_internal_redirect(r, uri, user_args);
}
static ngx_int_t
ngx_http_sysguard_do_redirect(ngx_http_request_t *r, ngx_str_t *path)
{
    if (path->len == 0) {
        return NGX_HTTP_SERVICE_UNAVAILABLE;
    } else if (path->data[0] == '@') {
        (void) ngx_http_named_location(r, path);
    } else {
        (void) ngx_http_internal_redirect(r, path, &r->args);
    }

    ngx_http_finalize_request(r, NGX_DONE);

    return NGX_DONE;
}
static ngx_int_t
ngx_http_limit_req2_handler(ngx_http_request_t *r)
{
    size_t                         n, total_len;
    uint32_t                       hash;
    ngx_int_t                      rc;
    ngx_msec_t                     delay_time;
    ngx_uint_t                     excess, delay_excess, delay_postion,
                                   nodelay, i;
    ngx_time_t                    *tp;
    ngx_rbtree_node_t             *node;
    ngx_http_limit_req2_t         *limit_req2;
    ngx_http_limit_req2_ctx_t     *ctx;
    ngx_http_limit_req2_node_t    *lr;
    ngx_http_limit_req2_conf_t    *lrcf;

    delay_excess = 0;
    excess = 0;
    delay_postion = 0;
    nodelay = 0;
    ctx = NULL;
    rc = NGX_DECLINED;

    if (r->main->limit_req_set) {
        return NGX_DECLINED;
    }

    lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req2_module);
    if (lrcf->rules == NULL) {
        return NGX_DECLINED;
    }

    if (!lrcf->enable) {
        return NGX_DECLINED;
    }

    /* filter whitelist */
    if (ngx_http_limit_req2_ip_filter(r, lrcf) == NGX_OK) {
        return NGX_DECLINED;
    }

    /* to match limit_req2 rule*/
    limit_req2 = lrcf->rules->elts;
    for (i = 0; i < lrcf->rules->nelts; i++) {
        ctx = limit_req2[i].shm_zone->data;

        ngx_crc32_init(hash);
        total_len = 0;

        total_len = ngx_http_limit_req2_copy_variables(r, &hash, ctx, NULL);
        if (total_len == 0) {
            continue;
        }

        ngx_crc32_final(hash);

        ngx_shmtx_lock(&ctx->shpool->mutex);

        ngx_http_limit_req2_expire(r, ctx, 1);

        rc = ngx_http_limit_req2_lookup(r, &limit_req2[i], hash, &excess);

        ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "limit_req2 module: %i %ui.%03ui "
                       "hash is %ui total_len is %i",
                       rc, excess / 1000, excess % 1000, hash, total_len);

        /* first limit_req2 */
        if (rc == NGX_DECLINED) {

            n = offsetof(ngx_rbtree_node_t, color)
                + offsetof(ngx_http_limit_req2_node_t, data)
                + total_len;

            node = ngx_slab_alloc_locked(ctx->shpool, n);
            if (node == NULL) {
                ngx_http_limit_req2_expire(r, ctx, 0);
                node = ngx_slab_alloc_locked(ctx->shpool, n);
                if (node == NULL) {
                    ngx_shmtx_unlock(&ctx->shpool->mutex);
                    return NGX_HTTP_SERVICE_UNAVAILABLE;
                }
            }

            lr = (ngx_http_limit_req2_node_t *) &node->color;

            node->key = hash;
            lr->len = (u_char) total_len;

            tp = ngx_timeofday();
            lr->last = (ngx_msec_t) (tp->sec * 1000 + tp->msec);

            lr->excess = 0;
            ngx_http_limit_req2_copy_variables(r, &hash, ctx, lr);

            ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
            ngx_rbtree_insert(&ctx->sh->rbtree, node);

            ngx_shmtx_unlock(&ctx->shpool->mutex);

            continue;
        }

        ngx_shmtx_unlock(&ctx->shpool->mutex);

        if (rc == NGX_BUSY || rc == NGX_ERROR) {
            break;
        }

        /* NGX_AGAIN or NGX_OK */

        if (delay_excess < excess) {
            delay_excess = excess;
            nodelay = limit_req2[i].nodelay;
            delay_postion = i;
        }
    }

    r->main->limit_req_set = 1;

    if (rc == NGX_BUSY || rc == NGX_ERROR) {
        if (rc == NGX_BUSY) {
            ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,
                          "limiting requests, excess: %ui.%03ui by zone \"%V\"",
                          excess / 1000, excess % 1000,
                          &limit_req2[i].shm_zone->shm.name);
        }

        if (rc == NGX_ERROR || limit_req2[i].forbid_action.len == 0) {

            return NGX_HTTP_SERVICE_UNAVAILABLE;
        } else if (limit_req2[i].forbid_action.data[0] == '@') {

            ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,
                          "limiting requests, forbid_action is %V",
                          &limit_req2[i].forbid_action);
            (void) ngx_http_named_location(r, &limit_req2[i].forbid_action);

        } else {

            ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,
                          "limiting requests, forbid_action is %V",
                          &limit_req2[i].forbid_action);
            (void) ngx_http_internal_redirect(r,
                                             &limit_req2[i].forbid_action,
                                             &r->args);
        }

        ngx_http_finalize_request(r, NGX_DONE);
        return NGX_DONE;
    }

    /* rc = NGX_AGAIN */
    if (delay_excess != 0) {

        if (nodelay) {
            return NGX_DECLINED;
        }

        delay_time = (ngx_msec_t) delay_excess * 1000 / ctx->rate;
        ngx_log_error(lrcf->delay_log_level, r->connection->log, 0,
                      "delaying request,"
                      "excess: %ui.%03ui, by zone \"%V\", delay \"%ui\" s",
                      delay_excess / 1000, delay_excess % 1000,
                      &limit_req2[delay_postion].shm_zone->shm.name, delay_time);

        if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        r->read_event_handler = ngx_http_test_reading;
        r->write_event_handler = ngx_http_limit_req2_delay;
        ngx_add_timer(r->connection->write, delay_time);

        return NGX_AGAIN;
    }

    /* rc == NGX_OK or rc == NGX_DECLINED */

    return NGX_DECLINED;
}
static ngx_int_t
ngx_http_send_error_page(ngx_http_request_t *r, ngx_http_err_page_t *err_page)
{
    ngx_int_t                  overwrite;
    ngx_str_t                  uri, args;
    ngx_table_elt_t           *location;
    ngx_http_core_loc_conf_t  *clcf;

    overwrite = err_page->overwrite;

    if (overwrite && overwrite != NGX_HTTP_OK) {
        r->expect_tested = 1;
    }

    if (overwrite >= 0) {
        r->err_status = overwrite;
    }

    if (ngx_http_complex_value(r, &err_page->value, &uri) != NGX_OK) {
        return NGX_ERROR;
    }

    if (uri.len && uri.data[0] == '/') {

        if (err_page->value.lengths) {
            ngx_http_split_args(r, &uri, &args);

        } else {
            args = err_page->args;
        }

        if (r->method != NGX_HTTP_HEAD) {
            r->method = NGX_HTTP_GET;
            r->method_name = ngx_http_get_name;
        }

        return ngx_http_internal_redirect(r, &uri, &args);
    }

    if (uri.len && uri.data[0] == '@') {
        return ngx_http_named_location(r, &uri);
    }

    location = ngx_list_push(&r->headers_out.headers);

    if (location == NULL) {
        return NGX_ERROR;
    }

    if (overwrite != NGX_HTTP_MOVED_PERMANENTLY
        && overwrite != NGX_HTTP_MOVED_TEMPORARILY
        && overwrite != NGX_HTTP_SEE_OTHER
        && overwrite != NGX_HTTP_TEMPORARY_REDIRECT)
    {
        r->err_status = NGX_HTTP_MOVED_TEMPORARILY;
    }

    location->hash = 1;
    ngx_str_set(&location->key, "Location");
    location->value = uri;

    ngx_http_clear_location(r);

    r->headers_out.location = location;

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (clcf->msie_refresh && r->headers_in.msie) {
        return ngx_http_send_refresh(r);
    }

    return ngx_http_send_special_response(r, clcf, r->err_status
                                                   - NGX_HTTP_MOVED_PERMANENTLY
                                                   + NGX_HTTP_OFF_3XX);
}
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;

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

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

#if defined(nginx_version) && nginx_version >= 8011
                        /* ngx_http_named_location always increments
                         * r->main->count, which is not we want for
                         * non-content phases */

                        if (! ctx->entered_content_phase) {
                            r->main->count--;
                        }
#endif

                        return ngx_http_named_location(r, &ctx->exec_uri);
                    }

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

#if defined(nginx_version) && nginx_version >= 8011
                    /* ngx_http_internal_redirect always increments
                     * r->main->count, which is not we want for
                     * non-content phases */

                    if (! ctx->entered_content_phase) {
                        r->main->count--;
                    }
#endif

                    return ngx_http_internal_redirect(r, &ctx->exec_uri,
                            &ctx->exec_args);
                }
            }

            msg = "unknown reason";
        }

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                "content_by_lua 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;
}
static ngx_int_t
ngx_http_limit_req_handler(ngx_http_request_t *r)
{
    uint32_t                     hash;
    ngx_str_t                    key;
    ngx_int_t                    rc;
    ngx_uint_t                   n, excess;
    ngx_msec_t                   delay;
    ngx_http_limit_req_ctx_t    *ctx;
    ngx_http_limit_req_conf_t   *lrcf;
    ngx_http_limit_req_limit_t  *limit, *limits;

    if (r->main->limit_req_set) {
        return NGX_DECLINED;
    }

    lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module);
    limits = lrcf->limits.elts;

#if (T_LIMIT_REQ)
    if (!lrcf->enable) {
        return NGX_DECLINED;
    }

    /* filter whitelist */
    if (ngx_http_limit_req_ip_filter(r, lrcf) == NGX_OK) {
        return NGX_DECLINED;
    }
#endif

    excess = 0;

    rc = NGX_DECLINED;

#if (NGX_SUPPRESS_WARN)
    limit = NULL;
#endif

    for (n = 0; n < lrcf->limits.nelts; n++) {

        limit = &limits[n];

        ctx = limit->shm_zone->data;

#if (T_LIMIT_REQ_RATE_VAR)
        if (ngx_http_limit_req_rate_value(r, ctx) != NGX_OK) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
#endif

        if (ngx_http_complex_value(r, &ctx->key, &key) != NGX_OK) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        if (key.len == 0) {
            continue;
        }

        if (key.len > 65535) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "the value of the \"%V\" key "
                          "is more than 65535 bytes: \"%V\"",
                          &ctx->key.value, &key);
            continue;
        }

        hash = ngx_crc32_short(key.data, key.len);

        ngx_shmtx_lock(&ctx->shpool->mutex);

        rc = ngx_http_limit_req_lookup(limit, hash, &key, &excess,
                                       (n == lrcf->limits.nelts - 1));

        ngx_shmtx_unlock(&ctx->shpool->mutex);

        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "limit_req[%ui]: %i %ui.%03ui",
                       n, rc, excess / 1000, excess % 1000);

        if (rc != NGX_AGAIN) {
            break;
        }
    }

    if (rc == NGX_DECLINED) {
        return NGX_DECLINED;
    }

    r->main->limit_req_set = 1;

    if (rc == NGX_BUSY || rc == NGX_ERROR) {

        if (rc == NGX_BUSY) {
            ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,
                          "limiting requests, excess: %ui.%03ui by zone \"%V\"",
                          excess / 1000, excess % 1000,
                          &limit->shm_zone->shm.name);
        }

        while (n--) {
            ctx = limits[n].shm_zone->data;

            if (ctx->node == NULL) {
                continue;
            }

            ngx_shmtx_lock(&ctx->shpool->mutex);

            ctx->node->count--;

            ngx_shmtx_unlock(&ctx->shpool->mutex);

            ctx->node = NULL;
        }

#if (T_LIMIT_REQ)
        if (rc == NGX_ERROR || limit->forbid_action.len == 0) {
            return lrcf->status_code;

        } else if (limit->forbid_action.data[0] == '@') {

            ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,
                          "limiting requests, forbid_action is %V",
                          &limit->forbid_action);
            (void) ngx_http_named_location(r, &limit->forbid_action);

        } else {

            ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,
                          "limiting requests, forbid_action is %V",
                          &limit->forbid_action);
            (void) ngx_http_internal_redirect(r,
                                              &limit->forbid_action,
                                              &r->args);
        }

        ngx_http_finalize_request(r, NGX_DONE);
        return NGX_DONE;
#else
        return lrcf->status_code;
#endif
    }

    /* rc == NGX_AGAIN || rc == NGX_OK */

    if (rc == NGX_AGAIN) {
        excess = 0;
    }

    delay = ngx_http_limit_req_account(limits, n, &excess, &limit);

    if (!delay) {
        return NGX_DECLINED;
    }

    ngx_log_error(lrcf->delay_log_level, r->connection->log, 0,
                  "delaying request, excess: %ui.%03ui, by zone \"%V\"",
                  excess / 1000, excess % 1000, &limit->shm_zone->shm.name);

    if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    r->read_event_handler = ngx_http_test_reading;
    r->write_event_handler = ngx_http_limit_req_delay;

    r->connection->write->delayed = 1;
    ngx_add_timer(r->connection->write, delay);

    return NGX_AGAIN;
}
static ngx_int_t
ngx_http_limit_speed_handler(ngx_http_request_t *r)
{
    size_t                           len, n;
    uint32_t                         hash;
    ngx_int_t                        rc;
    ngx_uint_t                       speed;
    ngx_slab_pool_t                 *shpool;
    ngx_rbtree_node_t               *node, *sentinel;
    ngx_pool_cleanup_t              *cln;
    ngx_http_variable_value_t       *vv;
    ngx_http_limit_speed_ctx_t      *ctx;
    ngx_http_limit_speed_node_t     *ls;
    ngx_http_limit_speed_conf_t     *lscf;
    ngx_http_limit_speed_cleanup_t  *lscln;
    ngx_http_limit_speed_req_ctx_t  *rctx;


    lscf = ngx_http_get_module_loc_conf(r, ngx_http_limit_speed_module);

    if (lscf->shm_zone == NULL || lscf->speed == 0) {
        return NGX_DECLINED;
    }

    if (r->main->limit_rate) {
        return NGX_DECLINED;
    }

    ctx = lscf->shm_zone->data;

    vv = ngx_http_get_indexed_variable(r, ctx->index);

    if (vv == NULL || vv->not_found) {
        return NGX_DECLINED;
    }

    len = vv->len;

    if (len == 0) {
        return NGX_DECLINED;
    }

    if (lscf->var_max_len) {
        len = len > lscf->var_max_len ? lscf->var_max_len : len;
    }

    hash = ngx_crc32_short(vv->data, len);

    cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_http_limit_speed_cleanup_t));
    if (cln == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    shpool = (ngx_slab_pool_t *) lscf->shm_zone->shm.addr;

    ngx_shmtx_lock(&shpool->mutex);

    node = ctx->rbtree->root;
    sentinel = ctx->rbtree->sentinel;

    while (node != sentinel) {

        if (hash < node->key) {
            node = node->left;
            continue;
        }

        if (hash > node->key) {
            node = node->right;
            continue;
        }

        /* hash == node->key */

        do {
            ls = (ngx_http_limit_speed_node_t *) &node->color;

            rc = ngx_memn2cmp(vv->data, ls->data, len, (size_t) ls->len);

            if (rc == 0) {
                ls->conn++;
                goto done;
            }

            node = (rc < 0) ? node->left : node->right;

        } while (node != sentinel && hash == node->key);

        break;
    }

    n = offsetof(ngx_rbtree_node_t, color)
        + offsetof(ngx_http_limit_speed_node_t, data)
        + len;

    node = ngx_slab_alloc_locked(shpool, n);
    if (node == NULL) {
        ngx_shmtx_unlock(&shpool->mutex);
        return NGX_DECLINED;
    }

    ls = (ngx_http_limit_speed_node_t *) &node->color;

    node->key = hash;
    ls->len = (u_char) len;
    ls->conn = 1;
    ngx_memcpy(ls->data, vv->data, len);

    ngx_rbtree_insert(ctx->rbtree, node);

done:

    speed = lscf->speed;

    r->main->limit_rate = speed / ls->conn;

    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "limit speed zone: %08XD conn=%d, speed=%d",
                   node->key, ls->conn, r->main->limit_rate);

    ngx_shmtx_unlock(&shpool->mutex);

    cln->handler = ngx_http_limit_speed_cleanup;
    lscln = cln->data;

    lscln->shm_zone = lscf->shm_zone;
    lscln->node = node;

    if (lscf->minimum && r->main->limit_rate <= lscf->minimum) {
        r->main->limit_rate = 0;

        if (lscf->rewrite.len == 0) {
            return NGX_HTTP_SERVICE_UNAVAILABLE;
        }

        if (lscf->rewrite.data[0] == '@') {
            (void) ngx_http_named_location(r, &lscf->rewrite);
        } else {
            (void) ngx_http_internal_redirect(r, &lscf->rewrite, &r->args);
        }

        ngx_http_finalize_request(r, NGX_DONE);

        return NGX_DONE;
    }

    if (ngx_http_limit_speed_get_ctx(r) != NGX_OK) {
        return NGX_DECLINED;
    }

    rctx = ngx_http_get_module_ctx(r, ngx_http_limit_speed_module);
    rctx->speed = speed;
    rctx->ls = ls;

    return NGX_DECLINED;
}
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;
}
static ngx_int_t
ngx_http_send_error_page(ngx_http_request_t *r, ngx_http_err_page_t *err_page)
{
    u_char                     ch, *p, *last;
    ngx_str_t                 *uri, *args, u, a;
    ngx_table_elt_t           *location;
    ngx_http_core_loc_conf_t  *clcf;

    r->err_status = err_page->overwrite;

    r->method = NGX_HTTP_GET;
    r->method_name = ngx_http_get_name;

    r->zero_in_uri = 0;

    args = NULL;

    if (err_page->uri_lengths) {
        if (ngx_http_script_run(r, &u, err_page->uri_lengths->elts, 0,
                                err_page->uri_values->elts)
            == NULL)
        {
            return NGX_ERROR;
        }

        p = u.data;
        uri = &u;

        if (*p == '/') {

            last = p + uri->len;

            while (p < last) {

                ch = *p++;

                if (ch == '?') {
                    a.len = last - p;
                    a.data = p;
                    args = &a;

                    u.len = p - 1 - u.data;

                    while (p < last) {
                        if (*p++ == '\0') {
                            r->zero_in_uri = 1;
                            break;
                        }
                    }

                    break;
                }

                if (ch == '\0') {
                    r->zero_in_uri = 1;
                    continue;
                }
            }
        }

    } else {
        uri = &err_page->uri;
    }

    if (uri->data[0] == '/') {
        return ngx_http_internal_redirect(r, uri, args);
    }

    if (uri->data[0] == '@') {
        return ngx_http_named_location(r, uri);
    }

    location = ngx_list_push(&r->headers_out.headers);

    if (location == NULL) {
        return NGX_ERROR;
    }

    r->err_status = NGX_HTTP_MOVED_TEMPORARILY;

    location->hash = 1;
    location->key.len = sizeof("Location") - 1;
    location->key.data = (u_char *) "Location";
    location->value = *uri;

    r->headers_out.location = location;

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (clcf->msie_refresh && r->headers_in.msie) {
        return ngx_http_send_refresh(r);
    }

    return ngx_http_send_special_response(r, clcf, NGX_HTTP_MOVED_TEMPORARILY
                                                   - NGX_HTTP_MOVED_PERMANENTLY
                                                   + NGX_HTTP_LEVEL_200);
}
static ngx_int_t
ngx_http_jstore_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
    ngx_http_core_main_conf_t *cmcf;
    ngx_http_jstore_loc_conf_t *jlcf;
    ngx_http_jstore_ctx_t *ctx;
    ngx_http_request_t *sr = NULL;
    ngx_http_upstream_t *u;
    ngx_chain_t *cl, *jchain;
    ngx_buf_t *buf, *jbuf;
    ngx_int_t rc, rc_next;
    ssize_t n;
    off_t clen;
    off_t content_length = r->headers_out.content_length_n;

    if(in == NULL) {
        goto pipe;
    }

    /* skip jstore's subrequest's response body */
    if(ngx_http_jstore_is_subrequest(r)) {
        for(cl = in; cl; cl = cl->next) {
            buf = cl->buf;

            buf->file_pos = buf->file_last;
            buf->pos = buf->last;  
            buf->sync = 1;             
            buf->memory = 0;           
            buf->in_file = 0;          
        }
        return NGX_OK;
    }

    ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "jstore body filter");

    ctx = ngx_http_get_module_ctx(r, ngx_http_jstore_filter_module);
    if(ctx == NULL || !ctx->store_enable || ctx->subr_sent) {
        goto pipe;
    }

    for(cl = in; cl; cl = cl->next) {

        buf = cl->buf;
        if(ngx_buf_special(buf)) {
            continue;
        }

        clen = ngx_buf_size(buf);

        if(content_length >= 0 && clen + ctx->copied_length > content_length) {
            ngx_log_error(NGX_LOG_ERR,r->connection->log, 0,
                    "body(%d) is larger than Content-Length(%d)",
                    clen + ctx->copied_length, content_length);
            goto fail;
        }

        /* alloc new buffer, and link it */
        jbuf = ngx_create_temp_buf(r->pool, clen);
        if(jbuf == NULL) {
            goto fail;
        }

        jchain = ngx_alloc_chain_link(r->pool);
        if(jchain == NULL) {
            goto pipe;
        }

        if(ctx->buf_chain_head == NULL) {
            ctx->buf_chain_head = jchain;
        } else {
            ctx->buf_chain_tail->next = jchain;
        }
        ctx->buf_chain_tail = jchain;
        jchain->buf = jbuf;
        jchain->next = NULL;

        /* copy content */
        if(buf->in_file && buf->file){
            n = ngx_read_file(buf->file, jbuf->start, clen, buf->file_pos);
            if(n < 0){
                ngx_log_error(NGX_LOG_ERR, r->connection->log,
                        0, "ngx_read_file failed! ret: %d", n);
                goto fail;
            }
            buf->file->offset -= n;

        } else {
            ngx_memcpy(jbuf->start, buf->pos, clen);
        }

        jbuf->last += clen;
        ctx->copied_length += clen;
    }

    /* upstream done? buffering and non-buffering */
    u = r->upstream;
    if(!(u->pipe && u->pipe->upstream_done) && u->length != 0) {
        goto pipe;
    }

    /* should not happen */
    if(content_length >= 0 && ctx->copied_length != content_length) {
        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "JSTORE bug!!");
        goto fail;
    }

    /* now we have collect all response, so send a subrequest to store it */

    /* send the main request first */
    rc_next = ngx_http_next_body_filter(r, in);

    ctx->subr_sent = 1;

    /* create subrequest */
    rc = ngx_http_subrequest(r, &(r->uri), &(r->args), &sr, NULL, 0);
    if(rc == NGX_ERROR) {
        goto fail;
    }

    /* method */
    sr->method = NGX_HTTP_POST;
    sr->method_name.data = JSUBR_METHOD_STR;
    sr->method_name.len = JSUBR_METHOD_LEN;

    /* request headers */
    sr->headers_in.headers = u->headers_in.headers;
    sr->headers_in.content_length_n = ctx->copied_length;

    /* $proxy_internal_body_length is cacheable, and the subrequest
     * has different content-lenght with main request, so we have
     * to clear the variables. */
    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
    sr->variables = ngx_pcalloc(r->pool, cmcf->variables.nelts
                                        * sizeof(ngx_http_variable_value_t));

    /* request body */
    sr->request_body = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
    if(sr->request_body == NULL) {
        goto fail;
    }

    sr->request_body->bufs = ctx->buf_chain_head;

    /* ngx_http_subrequest() and ngx_http_named_location() both
     * increase @r->count, but we want it to be incresed once 
     * actually. */
    r->count--;

    /* jump to the named location */
    ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "jstore: named-location");
    jlcf = ngx_http_get_module_loc_conf(r, ngx_http_jstore_filter_module);
    ngx_http_named_location(sr, &jlcf->target);

    return rc_next;

fail:
    ngx_http_set_ctx(r, NULL, ngx_http_jstore_filter_module);
pipe:
    return ngx_http_next_body_filter(r, in);
}
static ngx_int_t
ngx_http_internal_redirect_handler(ngx_http_request_t *r)
{
    u_char                                  *p;
    ngx_uint_t                               i;
    ngx_str_t                                uri, args;
    ngx_http_script_code_pt                  code;
    ngx_http_script_engine_t                 e;
    ngx_http_variable_value_t                stack[10];
    ngx_http_internal_redirect_entry_t      *redirects;
    ngx_http_internal_redirect_main_conf_t  *imcf;
    ngx_http_internal_redirect_loc_conf_t   *ilcf;
    ngx_http_core_main_conf_t               *cmcf;
    ngx_http_phase_handler_t                *ph, *cur_ph, *last_ph, tmp;

    imcf = ngx_http_get_module_main_conf(r, ngx_http_internal_redirect_module);

    if (!imcf->postponed) {

        imcf->postponed = 1;

        cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

        ph = cmcf->phase_engine.handlers;
        cur_ph = &ph[r->phase_handler];
        last_ph = &ph[cur_ph->next - 1];

        if (cur_ph < last_ph) {
            tmp = *cur_ph;

            ngx_memmove(cur_ph, cur_ph + 1,
                        (last_ph - cur_ph) * sizeof(ngx_http_phase_handler_t));

            *last_ph = tmp;
            r->phase_handler--; /* redo the current ph */

            return NGX_DECLINED;
        }
    }

    ilcf = ngx_http_get_module_loc_conf(r, ngx_http_internal_redirect_module);

    if (ilcf->redirects == NULL) {
        return NGX_DECLINED;
    }

    redirects = ilcf->redirects->elts;
    for (i = 0; i < ilcf->redirects->nelts; i++) {
        ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
        ngx_memzero(&stack, sizeof(stack));

        e.sp = stack;
        e.ip = redirects[i].codes->elts;
        e.request = r;
        e.quote = 1;
        e.log = 1;
        e.status = NGX_DECLINED;

        while (*(uintptr_t *) e.ip) {
            code = *(ngx_http_script_code_pt *) e.ip;
            code(&e);
        }

        e.sp--;

        if (e.sp->len && (e.sp->len != 1 || e.sp->data[0] != '0')) {
            break;
        }
    }

    if (i == ilcf->redirects->nelts) {
        return NGX_DECLINED;
    }

    if (redirects[i].code) {
        return redirects[i].code;
    }

    if (redirects[i].lengths) {

        if (ngx_http_script_run(r, &uri, redirects[i].lengths->elts, 0, 
                                redirects->values->elts)
            == NULL)
        {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

    } else {
        uri = redirects[i].name;
    }

    if (uri.data[0] == '@') {

        (void) ngx_http_named_location(r, &uri);

    } else {

        if (uri.data[0] != '/') {
            p = ngx_pcalloc(r->pool, uri.len + 1);
            if (p == NULL) {
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
            }

            uri.len++;
            *p = '/';
            ngx_memcpy(p + 1, uri.data, uri.len);
            uri.data = p;
        }

        ngx_http_split_args(r, &uri, &args);

        (void) ngx_http_internal_redirect(r, &uri, &args);
    }

    ngx_http_finalize_request(r, NGX_DONE);

    return NGX_DONE;
}
static ngx_int_t
ngx_http_send_error_page(ngx_http_request_t *r, ngx_http_err_page_t *err_page)
{
    ngx_int_t                  overwrite;
    ngx_str_t                  uri, args;
    ngx_table_elt_t           *location;
    ngx_http_core_loc_conf_t  *clcf;

    overwrite = err_page->overwrite;

    if (overwrite && overwrite != NGX_HTTP_OK) {
        r->expect_tested = 1;
    }

    r->err_status = overwrite;

    r->zero_in_uri = 0;

    if (ngx_http_complex_value(r, &err_page->value, &uri) != NGX_OK) {
        return NGX_ERROR;
    }

    if (uri.data[0] == '/') {

        if (err_page->value.lengths) {
            ngx_http_split_args(r, &uri, &args);

        } else {
            args = err_page->args;
        }

        if (r->method != NGX_HTTP_HEAD) {
            r->method = NGX_HTTP_GET;
            r->method_name = ngx_http_get_name;
        }

        return ngx_http_internal_redirect(r, &uri, &args);
    }

    if (uri.data[0] == '@') {
        return ngx_http_named_location(r, &uri);
    }

    location = ngx_list_push(&r->headers_out.headers);

    if (location == NULL) {
        return NGX_ERROR;
    }

    r->err_status = NGX_HTTP_MOVED_TEMPORARILY;

    location->hash = 1;
    location->key.len = sizeof("Location") - 1;
    location->key.data = (u_char *) "Location";
    location->value = uri;

    r->headers_out.location = location;

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (clcf->msie_refresh && r->headers_in.msie) {
        return ngx_http_send_refresh(r);
    }

    return ngx_http_send_special_response(r, clcf, NGX_HTTP_MOVED_TEMPORARILY
                                                   - NGX_HTTP_MOVED_PERMANENTLY
                                                   + NGX_HTTP_LEVEL_200);
}
static ngx_int_t
ngx_http_limit_req_handler(ngx_http_request_t *r)
{
    size_t                       len;
    uint32_t                     hash;
    ngx_int_t                    rc;
    ngx_uint_t                   n, excess;
    ngx_msec_t                   delay;
    ngx_http_limit_req_ctx_t    *ctx;
    ngx_http_limit_req_conf_t   *lrcf;
    ngx_http_limit_req_limit_t  *limit, *limits;

    if (r->main->limit_req_set) {
        return NGX_DECLINED;
    }

    lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module);
    limits = lrcf->limits.elts;

    /* filter whitelist */
    if (ngx_http_limit_req_ip_filter(r, lrcf) == NGX_OK) {
        return NGX_DECLINED;
    }

    excess = 0;

    rc = NGX_DECLINED;

#if (NGX_SUPPRESS_WARN)
    limit = NULL;
#endif

    for (n = 0; n < lrcf->limits.nelts; n++) {

        limit = &limits[n];

        ctx = limit->shm_zone->data;

        ngx_crc32_init(hash);

        len = ngx_http_limit_req_copy_variables(r, &hash, ctx, NULL);
        if (len == 0) {
            continue;
        }

        ngx_crc32_final(hash);

        ngx_shmtx_lock(&ctx->shpool->mutex);

        rc = ngx_http_limit_req_lookup(r, limit, hash, len, &excess,
                                       (n == lrcf->limits.nelts - 1));

        ngx_shmtx_unlock(&ctx->shpool->mutex);

        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "limits[%ui]: %i %ui.%03ui",
                       n, rc, excess / 1000, excess % 1000);

        if (rc != NGX_AGAIN) {
            break;
        }
    }

    if (rc == NGX_DECLINED) {
        return NGX_DECLINED;
    }

    r->main->limit_req_set = 1;

    if (rc == NGX_BUSY || rc == NGX_ERROR) {

        if (rc == NGX_BUSY) {
            ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,
                          "limiting requests, excess: %ui.%03ui by zone \"%V\"",
                          excess / 1000, excess % 1000,
                          &limit->shm_zone->shm.name);
        }

        while (n--) {
            ctx = limits[n].shm_zone->data;

            if (ctx->node == NULL) {
                continue;
            }

            ngx_shmtx_lock(&ctx->shpool->mutex);

            ctx->node->count--;

            ngx_shmtx_unlock(&ctx->shpool->mutex);

            ctx->node = NULL;
        }

        if (rc == NGX_ERROR || limit->forbid_action.len == 0) {
            return lrcf->status_code;
        } else if (limit->forbid_action.data[0] == '@') {

            ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,
                          "limiting requests, forbid_action is %V",
                          &limit->forbid_action);
            (void) ngx_http_named_location(r, &limit->forbid_action);

        } else {
            ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,
                          "limiting requests, forbid_action is %V",
                          &limit->forbid_action);
            (void) ngx_http_internal_redirect(r,
                                             &limit->forbid_action,
                                             &r->args);
        }

        ngx_http_finalize_request(r, NGX_DONE);
        return NGX_DONE;
    }

    /* rc == NGX_AGAIN || rc == NGX_OK */

    if (rc == NGX_AGAIN) {
        excess = 0;
    }

    delay = ngx_http_limit_req_account(limits, n, &excess, &limit);

    if (!delay) {
        return NGX_DECLINED;
    }

    ngx_log_error(lrcf->delay_log_level, r->connection->log, 0,
                  "delaying request, excess: %ui.%03ui, by zone \"%V\"",
                  excess / 1000, excess % 1000, &limit->shm_zone->shm.name);

    if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    r->read_event_handler = ngx_http_test_reading;
    r->write_event_handler = ngx_http_limit_req_delay;
    ngx_add_timer(r->connection->write, delay);

    return NGX_AGAIN;
}