static ngx_int_t
ngx_extra_var_original_uri(ngx_http_request_t *r,
    ngx_http_variable_value_t *v, uintptr_t data)
{
    ngx_str_t    uri, args;
    u_char      *src, *dst, *p;
    ngx_uint_t   flags;

    uri = r->unparsed_uri;
    ngx_str_null(&args);
    flags = 0;

    if (NGX_OK != ngx_http_parse_unsafe_uri(r, &uri, &args, &flags)) {
        v->not_found = 1;
        return NGX_OK;
    }

    p = ngx_pnalloc(r->pool, uri.len);
    if (p == NULL) {
        return NGX_ERROR;
    }

    src = uri.data;
    dst = p;

    ngx_unescape_uri(&dst, &src, uri.len, 0);

    v->len = dst - p;
    v->valid = 1;
    v->no_cacheable = 0;
    v->not_found = 0;
    v->data = p;

    return NGX_OK;
}
ngx_int_t
ngx_http_echo_exec_echo_location_async(ngx_http_request_t *r,
    ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args)
{
    ngx_int_t                    rc;
    ngx_http_request_t          *sr; /* subrequest object */
    ngx_str_t                   *computed_arg_elts;
    ngx_str_t                    location;
    ngx_str_t                   *url_args;
    ngx_str_t                    args;
    ngx_uint_t                   flags = 0;

    dd_enter();

    computed_arg_elts = computed_args->elts;

    location = computed_arg_elts[0];

    if (location.len == 0) {
        return NGX_ERROR;
    }

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

    args.data = NULL;
    args.len = 0;

    if (ngx_http_parse_unsafe_uri(r, &location, &args, &flags) != NGX_OK) {
        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "echo_location_async sees unsafe uri: \"%V\"",
                       &location);
        return NGX_ERROR;
    }

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

    rc = ngx_http_echo_send_header_if_needed(r, ctx);
    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
        return rc;
    }

    rc = ngx_http_subrequest(r, &location, url_args, &sr, NULL, 0);

    if (rc != NGX_OK) {
        return NGX_ERROR;
    }

    rc = ngx_http_echo_adjust_subrequest(sr);
    if (rc != NGX_OK) {
        return NGX_ERROR;
    }

    return NGX_OK;
}
ngx_int_t
ngx_http_echo_exec_echo_location_async(ngx_http_request_t *r,
        ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args)
{
    ngx_int_t                    rc;
    ngx_http_request_t          *sr; /* subrequest object */
    ngx_str_t                   *computed_arg_elts;
    ngx_str_t                    location;
    ngx_str_t                   *url_args;
    ngx_str_t                    args;
    ngx_uint_t                   flags;

    computed_arg_elts = computed_args->elts;

    location = computed_arg_elts[0];

    if (location.len == 0) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

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

    dd("location: %s", location.data);
    dd("location args: %s", (char*) (url_args ? url_args->data : (u_char*)"NULL"));

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

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

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

    rc = ngx_http_subrequest(r, &location, url_args, &sr, NULL, 0);
    if (rc != NGX_OK) {
        return NGX_ERROR;
    }

    rc = ngx_http_echo_adjust_subrequest(sr);
    if (rc != NGX_OK) {
        return rc;
    }

    return NGX_OK;
}
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);
}
ngx_int_t
ngx_http_echo_exec_echo_subrequest(ngx_http_request_t *r,
        ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args)
{
    ngx_int_t                           rc;
    ngx_http_request_t                  *sr; /* subrequest object */
    ngx_http_post_subrequest_t          *psr;
    ngx_http_echo_subrequest_t          *parsed_sr;
    ngx_str_t                           args;
    ngx_uint_t                          flags;

    rc = ngx_http_echo_parse_subrequest_spec(r, computed_args, &parsed_sr);
    if (rc != NGX_OK) {
        return rc;
    }

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

    if (args.len > 0 && parsed_sr->query_string == NULL) {
        parsed_sr->query_string = &args;
    }

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

    psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
    if (psr == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    psr->handler = ngx_http_echo_post_subrequest;
    psr->data = ctx;

    rc = ngx_http_subrequest(r, parsed_sr->location, parsed_sr->query_string,
            &sr, psr, 0);
    if (rc != NGX_OK) {
        return NGX_ERROR;
    }

    rc = ngx_http_echo_adjust_subrequest(sr, parsed_sr);
    if (rc != NGX_OK) {
        return rc;
    }

    return NGX_OK;
}
ngx_int_t
ngx_http_echo_exec_echo_subrequest_async(ngx_http_request_t *r,
        ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args)
{
    ngx_int_t                       rc;
    ngx_http_echo_subrequest_t      *parsed_sr;
    ngx_http_request_t              *sr; /* subrequest object */
    ngx_str_t                       args;
    ngx_uint_t                      flags;

    rc = ngx_http_echo_parse_subrequest_spec(r, computed_args, &parsed_sr);
    if (rc != NGX_OK) {
        return rc;
    }

    dd("location: %s", parsed_sr->location->data);
    dd("location args: %s", (char*) (parsed_sr->query_string ?
                parsed_sr->query_string->data : (u_char*)"NULL"));

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

    if (args.len > 0 && parsed_sr->query_string == NULL) {
        parsed_sr->query_string = &args;
    }

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

    rc = ngx_http_subrequest(r, parsed_sr->location, parsed_sr->query_string,
            &sr, NULL, 0);
    if (rc != NGX_OK) {
        return NGX_ERROR;
    }

    rc = ngx_http_echo_adjust_subrequest(sr, parsed_sr);
    if (rc != NGX_OK) {
        return rc;
    }

    return NGX_OK;
}
static ngx_int_t
ngx_http_concat_add_path(ngx_http_request_t *r, ngx_array_t *uris,
    size_t max, ngx_str_t *path, u_char *p, u_char *v)
{
    u_char     *d;
    ngx_str_t  *uri, args;
    ngx_uint_t  flags;

    if (p == v) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "client sent zero concat filename");
        return NGX_HTTP_BAD_REQUEST;
    }

    if (uris->nelts >= max) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "client sent too many concat filenames");
        return NGX_HTTP_BAD_REQUEST;
    }

    uri = ngx_array_push(uris);
    if (uri == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    uri->len = path->len + p - v;
    uri->data = ngx_pnalloc(r->pool, uri->len + 1);  /* + '\0' */
    if (uri->data == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    d = ngx_cpymem(uri->data, path->data, path->len);
    d = ngx_cpymem(d, v, p - v);
    *d = '\0';

    args.len = 0;
    args.data = NULL;
    flags = NGX_HTTP_LOG_UNSAFE;

    if (ngx_http_parse_unsafe_uri(r, uri, &args, &flags) != NGX_OK) {
        return NGX_HTTP_BAD_REQUEST;
    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http concat add file: \"%s\"", uri->data);

    return NGX_OK;
}
static int
ngx_http_lua_ngx_location_capture_multi(lua_State *L)
{
    ngx_http_request_t              *r;
    ngx_http_request_t              *sr; /* subrequest object */
    ngx_http_post_subrequest_t      *psr;
    ngx_http_lua_ctx_t              *sr_ctx;
    ngx_http_lua_ctx_t              *ctx;
    ngx_array_t                     *extra_vars;
    ngx_str_t                        uri;
    ngx_str_t                        args;
    ngx_str_t                        extra_args;
    ngx_uint_t                       flags;
    u_char                          *p;
    u_char                          *q;
    size_t                           len;
    size_t                           nargs;
    int                              rc;
    int                              n;
    ngx_uint_t                       method;
    ngx_http_request_body_t         *body;
    int                              type;
    ngx_buf_t                       *b;
    unsigned                         vars_action;
    ngx_uint_t                       nsubreqs;
    ngx_uint_t                       index;
    size_t                           sr_statuses_len;
    size_t                           sr_headers_len;
    size_t                           sr_bodies_len;
    size_t                           sr_flags_len;
    unsigned                         custom_ctx;
    ngx_http_lua_co_ctx_t           *coctx;

    ngx_http_lua_post_subrequest_data_t      *psr_data;

    n = lua_gettop(L);
    if (n != 1) {
        return luaL_error(L, "only one argument is expected, but got %d", n);
    }

    luaL_checktype(L, 1, LUA_TTABLE);

    nsubreqs = lua_objlen(L, 1);
    if (nsubreqs == 0) {
        return luaL_error(L, "at least one subrequest should be specified");
    }

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

    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
    if (ctx == NULL) {
        return luaL_error(L, "no ctx found");
    }

    ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE
                               | NGX_HTTP_LUA_CONTEXT_ACCESS
                               | NGX_HTTP_LUA_CONTEXT_CONTENT);

    coctx = ctx->cur_co_ctx;
    if (coctx == NULL) {
        return luaL_error(L, "no co ctx found");
    }

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "lua location capture, uri:\"%V\" c:%ud", &r->uri,
                   r->main->count);

    sr_statuses_len = nsubreqs * sizeof(ngx_int_t);
    sr_headers_len  = nsubreqs * sizeof(ngx_http_headers_out_t *);
    sr_bodies_len   = nsubreqs * sizeof(ngx_str_t);
    sr_flags_len    = nsubreqs * sizeof(uint8_t);

    p = ngx_pcalloc(r->pool, sr_statuses_len + sr_headers_len +
                    sr_bodies_len + sr_flags_len);

    if (p == NULL) {
        return luaL_error(L, "out of memory");
    }

    coctx->sr_statuses = (void *) p;
    p += sr_statuses_len;

    coctx->sr_headers = (void *) p;
    p += sr_headers_len;

    coctx->sr_bodies = (void *) p;
    p += sr_bodies_len;

    coctx->sr_flags = (void *) p;

    coctx->nsubreqs = nsubreqs;

    coctx->pending_subreqs = 0;

    extra_vars = NULL;

    for (index = 0; index < nsubreqs; index++) {
        coctx->pending_subreqs++;

        lua_rawgeti(L, 1, index + 1);
        if (lua_isnil(L, -1)) {
            return luaL_error(L, "only array-like tables are allowed");
        }

        dd("queries query: top %d", lua_gettop(L));

        if (lua_type(L, -1) != LUA_TTABLE) {
            return luaL_error(L, "the query argument %d is not a table, "
                              "but a %s",
                              index, lua_typename(L, lua_type(L, -1)));
        }

        nargs = lua_objlen(L, -1);

        if (nargs != 1 && nargs != 2) {
            return luaL_error(L, "query argument %d expecting one or "
                              "two arguments", index);
        }

        lua_rawgeti(L, 2, 1); /* queries query uri */

        dd("queries query uri: %d", lua_gettop(L));

        dd("first arg in first query: %s", lua_typename(L, lua_type(L, -1)));

        body = NULL;

        extra_args.data = NULL;
        extra_args.len = 0;

        if (extra_vars != NULL) {
            /* flush out existing elements in the array */
            extra_vars->nelts = 0;
        }

        vars_action = 0;

        custom_ctx = 0;

        if (nargs == 2) {
            /* check out the options table */

            lua_rawgeti(L, 2, 2); /* queries query uri opts */

            dd("queries query uri opts: %d", lua_gettop(L));

            if (lua_type(L, 4) != LUA_TTABLE) {
                return luaL_error(L, "expecting table as the 2nd argument for "
                                  "subrequest %d, but got %s", index,
                                  luaL_typename(L, 4));
            }

            dd("queries query uri opts: %d", lua_gettop(L));

            /* check the args option */

            lua_getfield(L, 4, "args");

            type = lua_type(L, -1);

            switch (type) {
            case LUA_TTABLE:
                ngx_http_lua_process_args_option(r, L, -1, &extra_args);
                break;

            case LUA_TNIL:
                /* do nothing */
                break;

            case LUA_TNUMBER:
            case LUA_TSTRING:
                extra_args.data = (u_char *) lua_tolstring(L, -1, &len);
                extra_args.len = len;

                break;

            default:
                return luaL_error(L, "Bad args option value");
            }

            lua_pop(L, 1);

            dd("queries query uri opts: %d", lua_gettop(L));

            /* check the vars option */

            lua_getfield(L, 4, "vars");

            switch (lua_type(L, -1)) {
            case LUA_TTABLE:
                ngx_http_lua_process_vars_option(r, L, -1, &extra_vars);

                dd("post process vars top: %d", lua_gettop(L));
                break;

            case LUA_TNIL:
                /* do nothing */
                break;

            default:
                return luaL_error(L, "Bad vars option value");
            }

            lua_pop(L, 1);

            dd("queries query uri opts: %d", lua_gettop(L));

            /* check the share_all_vars option */

            lua_getfield(L, 4, "share_all_vars");

            switch (lua_type(L, -1)) {
            case LUA_TNIL:
                /* do nothing */
                break;

            case LUA_TBOOLEAN:
                if (lua_toboolean(L, -1)) {
                    vars_action |= NGX_HTTP_LUA_SHARE_ALL_VARS;
                }
                break;

            default:
                return luaL_error(L, "Bad share_all_vars option value");
            }

            lua_pop(L, 1);

            dd("queries query uri opts: %d", lua_gettop(L));

            /* check the copy_all_vars option */

            lua_getfield(L, 4, "copy_all_vars");

            switch (lua_type(L, -1)) {
            case LUA_TNIL:
                /* do nothing */
                break;

            case LUA_TBOOLEAN:
                if (lua_toboolean(L, -1)) {
                    vars_action |= NGX_HTTP_LUA_COPY_ALL_VARS;
                }
                break;

            default:
                return luaL_error(L, "Bad copy_all_vars option value");
            }

            lua_pop(L, 1);

            dd("queries query uri opts: %d", lua_gettop(L));

            /* check the "method" option */

            lua_getfield(L, 4, "method");

            type = lua_type(L, -1);

            if (type == LUA_TNIL) {
                method = NGX_HTTP_GET;

            } else {
                if (type != LUA_TNUMBER) {
                    return luaL_error(L, "Bad http request method");
                }

                method = (ngx_uint_t) lua_tonumber(L, -1);
            }

            lua_pop(L, 1);

            dd("queries query uri opts: %d", lua_gettop(L));

            /* check the "ctx" option */

            lua_getfield(L, 4, "ctx");

            type = lua_type(L, -1);

            if (type != LUA_TNIL) {
                if (type != LUA_TTABLE) {
                    return luaL_error(L, "Bad ctx option value type %s, "
                                      "expected a Lua table",
                                      lua_typename(L, type));
                }

                custom_ctx = 1;

            } else {
                lua_pop(L, 1);
            }

            dd("queries query uri opts ctx?: %d", lua_gettop(L));

            /* check the "body" option */

            lua_getfield(L, 4, "body");

            type = lua_type(L, -1);

            if (type != LUA_TNIL) {
                if (type != LUA_TSTRING && type != LUA_TNUMBER) {
                    return luaL_error(L, "Bad http request body");
                }

                body = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));

                if (body == NULL) {
                    return luaL_error(L, "out of memory");
                }

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

                dd("request body: [%.*s]", (int) len, q);

                if (len) {
                    b = ngx_create_temp_buf(r->pool, len);
                    if (b == NULL) {
                        return luaL_error(L, "out of memory");
                    }

                    b->last = ngx_copy(b->last, q, len);

                    body->bufs = ngx_alloc_chain_link(r->pool);
                    if (body->bufs == NULL) {
                        return luaL_error(L, "out of memory");
                    }

                    body->bufs->buf = b;
                    body->bufs->next = NULL;

                    body->buf = b;
                }
            }

            lua_pop(L, 1); /* pop the body */

            /* stack: queries query uri opts ctx? */

            lua_remove(L, 4);

            /* stack: queries query uri ctx? */

            dd("queries query uri ctx?: %d", lua_gettop(L));

        } else {
            method = NGX_HTTP_GET;
        }

        /* stack: queries query uri ctx? */

        p = (u_char *) luaL_checklstring(L, 3, &len);

        uri.data = ngx_palloc(r->pool, len);
        if (uri.data == NULL) {
            return luaL_error(L, "memory allocation error");
        }

        ngx_memcpy(uri.data, p, len);

        uri.len = len;

        args.data = NULL;
        args.len = 0;

        flags = 0;

        rc = ngx_http_parse_unsafe_uri(r, &uri, &args, &flags);
        if (rc != NGX_OK) {
            dd("rc = %d", (int) rc);

            return luaL_error(L, "unsafe uri in argument #1: %s", p);
        }

        if (args.len == 0) {
            if (extra_args.len) {
                p = ngx_palloc(r->pool, extra_args.len);
                if (p == NULL) {
                    return luaL_error(L, "out of memory");
                }

                ngx_memcpy(p, extra_args.data, extra_args.len);

                args.data = p;
                args.len = extra_args.len;
            }

        } else if (extra_args.len) {
            /* concatenate the two parts of args together */
            len = args.len + (sizeof("&") - 1) + extra_args.len;

            p = ngx_palloc(r->pool, len);
            if (p == NULL) {
                return luaL_error(L, "out of memory");
            }

            q = ngx_copy(p, args.data, args.len);
            *q++ = '&';
            ngx_memcpy(q, extra_args.data, extra_args.len);

            args.data = p;
            args.len = len;
        }

        p = ngx_pnalloc(r->pool, sizeof(ngx_http_post_subrequest_t)
                        + sizeof(ngx_http_lua_ctx_t)
                        + sizeof(ngx_http_lua_post_subrequest_data_t));
        if (p == NULL) {
            return luaL_error(L, "out of memory");
        }

        psr = (ngx_http_post_subrequest_t *) p;

        p += sizeof(ngx_http_post_subrequest_t);

        sr_ctx = (ngx_http_lua_ctx_t *) p;

        p += sizeof(ngx_http_lua_ctx_t);

        psr_data = (ngx_http_lua_post_subrequest_data_t *) p;

        ngx_memzero(sr_ctx, sizeof(ngx_http_lua_ctx_t));

        /* set by ngx_memzero:
         *      sr_ctx->run_post_subrequest = 0
         *      sr_ctx->free = NULL
         *      sr_ctx->body = NULL
         */

        ngx_http_lua_init_ctx(sr_ctx);

        sr_ctx->capture = 1;
        sr_ctx->index = index;
        sr_ctx->last_body = &sr_ctx->body;

        psr_data->ctx = sr_ctx;
        psr_data->pr_co_ctx = coctx;

        psr->handler = ngx_http_lua_post_subrequest;
        psr->data = psr_data;

        rc = ngx_http_lua_subrequest(r, &uri, &args, &sr, psr, 0);

        if (rc != NGX_OK) {
            return luaL_error(L, "failed to issue subrequest: %d", (int) rc);
        }

        ngx_http_set_ctx(sr, sr_ctx, ngx_http_lua_module);

        rc = ngx_http_lua_adjust_subrequest(sr, method, body, vars_action,
                                            extra_vars);

        if (rc != NGX_OK) {
            ngx_http_lua_cancel_subreq(sr);
            return luaL_error(L, "failed to adjust the subrequest: %d",
                              (int) rc);
        }

        dd("queries query uri opts ctx? %d", lua_gettop(L));

        /* stack: queries query uri ctx? */

        if (custom_ctx) {
            ngx_http_lua_ngx_set_ctx_helper(L, sr, sr_ctx, -1);
            lua_pop(L, 3);

        } else {
            lua_pop(L, 2);
        }

        /* stack: queries */
    }

    if (extra_vars) {
        ngx_array_destroy(extra_vars);
    }

    ctx->no_abort = 1;

    return lua_yield(L, 0);
}
static ngx_int_t
ngx_http_eval_handler(ngx_http_request_t *r)
{
    size_t                      loc_len;
    ngx_str_t                   args; 
    ngx_str_t                   subrequest_uri;
    ngx_uint_t                  flags;
    ngx_http_core_loc_conf_t   *clcf;
    ngx_http_eval_loc_conf_t   *ecf;
    ngx_http_eval_ctx_t        *ctx;
    ngx_http_request_t         *sr; 
    ngx_int_t                   rc;
    ngx_http_post_subrequest_t *psr;
    u_char                     *p;

    if(r != r->main && ngx_memn2cmp(r->uri.data, "/eval_", r->uri.len, 6 - 1) == 0) {
        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

        loc_len = r->valid_location ? clcf->name.len : 0;

        if(r->uri.len != loc_len) {
            r->uri.data += loc_len;
            r->uri.len -= loc_len;
        }
        else {
            r->uri.len = 1;
        }
    }

    ecf = ngx_http_get_module_loc_conf(r, ngx_http_eval_module);

    if(ecf->variables == NULL || !ecf->variables->nelts) {
        return NGX_DECLINED;
    }

    ctx = ngx_http_get_module_ctx(r, ngx_http_eval_module);

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

        ctx->base_conf = ecf;

        ngx_http_set_ctx(r, ctx, ngx_http_eval_module);
    }

    if(ctx->done) {
        if(!ecf->escalate || ctx->status == NGX_OK || ctx->status == NGX_HTTP_OK) {
            return NGX_DECLINED;
        }

        return ctx->status;
    }

    if(ctx->in_progress) {
        return NGX_AGAIN;
    }

    psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
    if (psr == NULL) {
        return NGX_ERROR;
    }

    if(ngx_http_eval_init_variables(r, ctx, ecf) != NGX_OK) {
        return NGX_ERROR;
    }

    args.len = 0;
    args.data = NULL;
    flags = 0;

    subrequest_uri.len = ecf->eval_location.len + r->uri.len;

    p = subrequest_uri.data = ngx_palloc(r->pool, subrequest_uri.len);

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

    p = ngx_copy(p, ecf->eval_location.data, ecf->eval_location.len);
    p = ngx_copy(p, r->uri.data, r->uri.len);

    if (ngx_http_parse_unsafe_uri(r, &subrequest_uri, &args, &flags) != NGX_OK) {
        return NGX_ERROR;
    }

    psr->handler = ngx_http_eval_post_subrequest_handler;
    psr->data = ctx;

    flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY|NGX_HTTP_SUBREQUEST_WAITED;

    rc = ngx_http_subrequest(r, &subrequest_uri, &args, &sr, psr, flags);

    if (rc == NGX_ERROR || rc == NGX_DONE) {
        return rc;
    }

    sr->discard_body = 1;

    ctx->in_progress = 1;

    /*
     * Wait for subrequest to complete
     */
    return NGX_AGAIN;
}
static ngx_int_t
ngx_http_srcache_fetch_subrequest(ngx_http_request_t *r,
    ngx_http_srcache_loc_conf_t *conf, ngx_http_srcache_ctx_t *ctx)
{
    ngx_http_srcache_ctx_t         *sr_ctx;
    ngx_http_post_subrequest_t     *psr;
    ngx_str_t                       args;
    ngx_uint_t                      flags = 0;
    ngx_http_request_t             *sr;
    ngx_int_t                       rc;

    ngx_http_srcache_parsed_request_t  *parsed_sr;

    dd_enter();

    parsed_sr = ngx_palloc(r->pool, sizeof(ngx_http_srcache_parsed_request_t));
    if (parsed_sr == NULL) {
        return NGX_ERROR;
    }

    if (conf->fetch == NULL) {
        return NGX_ERROR;
    }

    parsed_sr->method      = conf->fetch->method;
    parsed_sr->method_name = conf->fetch->method_name;

    parsed_sr->request_body = NULL;
    parsed_sr->content_length_n = -1;

    if (ngx_http_complex_value(r, &conf->fetch->location,
                               &parsed_sr->location) != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (parsed_sr->location.len == 0) {
        return NGX_ERROR;
    }

    if (ngx_http_complex_value(r, &conf->fetch->args, &parsed_sr->args)
            != NGX_OK)
    {
        return NGX_ERROR;
    }

    args.data = NULL;
    args.len = 0;

    if (ngx_http_parse_unsafe_uri(r, &parsed_sr->location, &args, &flags)
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (args.len > 0 && parsed_sr->args.len == 0) {
        parsed_sr->args = args;
    }

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

    sr_ctx->in_fetch_subrequest = 1;

    psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
    if (psr == NULL) {
        return NGX_ERROR;
    }

    psr->handler = ngx_http_srcache_fetch_post_subrequest;
    psr->data = sr_ctx;

    dd("firing the fetch subrequest");

    dd("fetch location: %.*s", (int) parsed_sr->location.len,
       parsed_sr->location.data);

    dd("fetch args: %.*s", (int) parsed_sr->args.len,
            parsed_sr->args.data);

    rc = ngx_http_subrequest(r, &parsed_sr->location, &parsed_sr->args,
                             &sr, psr, flags);

    if (rc != NGX_OK) {
        return NGX_ERROR;
    }

    rc = ngx_http_srcache_adjust_subrequest(sr, parsed_sr);

    if (rc != NGX_OK) {
        return NGX_ERROR;
    }

    ngx_http_set_ctx(sr, sr_ctx, ngx_http_srcache_filter_module);

    ctx->issued_fetch_subrequest = 1;

    return NGX_OK;
}
static ngx_int_t
ngx_http_eval_handler(ngx_http_request_t *r)
{
    size_t                      loc_len;
    ngx_str_t                   args; 
    ngx_str_t                   subrequest_uri;
    ngx_uint_t                  flags;
    ngx_http_core_loc_conf_t   *clcf;
    ngx_http_eval_loc_conf_t   *ecf;
    ngx_http_eval_ctx_t        *ctx;
    ngx_http_request_t         *sr; 
    ngx_int_t                   rc;
    ngx_http_post_subrequest_t *psr;
    ngx_http_eval_block_t      *block;
    u_char                     *p;

    if(r != r->main && r->uri.len > 6 && r->uri.data[0] == '/' && r->uri.data[1] == 'e'
        && r->uri.data[2] == 'v' && r->uri.data[3] == 'a' && r->uri.data[4] == 'l'
        && r->uri.data[5] == '_')
    {
        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

        loc_len = r->valid_location ? clcf->name.len : 0;

        if(r->uri.len != loc_len) {
            r->uri.data += loc_len;
            r->uri.len -= loc_len;
        }
        else {
            r->uri.len = 1;
        }
    }

    ecf = ngx_http_get_module_loc_conf(r, ngx_http_eval_module);

    if(ecf->blocks == NULL || !ecf->blocks->nelts) {
        return NGX_DECLINED;
    }

    ctx = ngx_http_get_module_ctx(r, ngx_http_eval_module);

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

        ctx->base_conf = ecf;

        ctx->current_block = ecf->blocks->elts;
        ctx->last_block = ctx->current_block + ecf->blocks->nelts - 1;

        ngx_http_set_ctx(r, ctx, ngx_http_eval_module);
    }

    if(ctx->done) {
        ctx->in_progress = 0;

        if(ctx->current_block == ctx->last_block) {
            if(!ecf->escalate || ctx->status == NGX_OK || ctx->status == NGX_HTTP_OK) {
                return NGX_DECLINED;
            }

            return ctx->status;
        }

        ctx->current_block++;
    }

    if(ctx->in_progress) {
#if defined nginx_version && nginx_version >= 8042
        return NGX_DONE;
#else
        return NGX_AGAIN;
#endif
    }

    /*
     * Advance to block which has at least one variable
     */
    while(ctx->current_block->variables == NULL || ctx->current_block->variables->nelts == 0) {
        if(ctx->current_block == ctx->last_block) {
            return NGX_DECLINED;
        }

        ctx->current_block++;
    }

    block = ctx->current_block;

    psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
    if (psr == NULL) {
        return NGX_ERROR;
    }

    if(ngx_http_eval_init_variables(r, ctx, block) != NGX_OK) {
        return NGX_ERROR;
    }

    args.len = r->args.len;
    args.data = r->args.data;
    flags = 0;

    subrequest_uri.len = block->eval_location.len + r->uri.len;

    p = subrequest_uri.data = ngx_palloc(r->pool, subrequest_uri.len);

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

    p = ngx_copy(p, block->eval_location.data, block->eval_location.len);
    p = ngx_copy(p, r->uri.data, r->uri.len);

    if (ngx_http_parse_unsafe_uri(r, &subrequest_uri, &args, &flags) != NGX_OK) {
        return NGX_ERROR;
    }

    psr->handler = ngx_http_eval_post_subrequest_handler;
    psr->data = ctx;

    flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY|NGX_HTTP_SUBREQUEST_WAITED;

    rc = ngx_http_subrequest(r, &subrequest_uri, &args, &sr, psr, flags);

    if (rc == NGX_ERROR || rc == NGX_DONE) {
        return rc;
    }

    /*
     * create a fake request body instead of discarding the real one
     * in order to avoid attempts to read it
     */
    sr->request_body = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
    if (sr->request_body == NULL) {
        return NGX_ERROR;
    }

    ctx->in_progress = 1;
    ctx->done = 0;

    /*
     * Wait for subrequest to complete
     */

#if defined nginx_version && nginx_version >= 8042
    return NGX_DONE;
#else
    return NGX_AGAIN;
#endif
}
static int
ngx_http_lua_ngx_exec(lua_State *L)
{
    int                          n;
    ngx_http_request_t          *r;
    ngx_http_lua_ctx_t          *ctx;
    ngx_str_t                    uri;
    ngx_str_t                    args, user_args;
    ngx_uint_t                   flags;
    u_char                      *p;
    u_char                      *q;
    size_t                       len;
    const char                  *msg;

    n = lua_gettop(L);
    if (n != 1 && n != 2) {
        return luaL_error(L, "expecting one or two arguments, but got %d",
                          n);
    }

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

    args.data = NULL;
    args.len = 0;

    /* read the 1st argument (uri) */

    p = (u_char *) luaL_checklstring(L, 1, &len);

    if (len == 0) {
        return luaL_error(L, "The uri argument is empty");
    }

    uri.data = ngx_palloc(r->pool, len);
    if (uri.data == NULL) {
        return luaL_error(L, "out of memory");
    }

    ngx_memcpy(uri.data, p, len);

    uri.len = len;

    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
    if (ctx == NULL) {
        return luaL_error(L, "no ctx found");
    }

    ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE
                               | NGX_HTTP_LUA_CONTEXT_ACCESS
                               | NGX_HTTP_LUA_CONTEXT_CONTENT);

    ngx_http_lua_check_if_abortable(L, ctx);

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

    if (n == 2) {
        /* read the 2nd argument (args) */
        dd("args type: %s", luaL_typename(L, 2));

        switch (lua_type(L, 2)) {
        case LUA_TNUMBER:
        case LUA_TSTRING:
            p = (u_char *) lua_tolstring(L, 2, &len);

            user_args.data = ngx_palloc(r->pool, len);
            if (user_args.data == NULL) {
                return luaL_error(L, "out of memory");
            }

            ngx_memcpy(user_args.data, p, len);

            user_args.len = len;
            break;

        case LUA_TTABLE:
            ngx_http_lua_process_args_option(r, L, 2, &user_args);

            dd("user_args: %.*s", (int) user_args.len, user_args.data);

            break;

        case LUA_TNIL:
            user_args.data = NULL;
            user_args.len = 0;
            break;

        default:
            msg = lua_pushfstring(L, "string, number, or table expected, "
                                  "but got %s", luaL_typename(L, 2));
            return luaL_argerror(L, 2, msg);
        }

    } else {
        user_args.data = NULL;
        user_args.len = 0;
    }

    if (user_args.len) {
        if (args.len == 0) {
            args = user_args;
        } else {
            p = ngx_palloc(r->pool, args.len + user_args.len + 1);
            if (p == NULL) {
                return luaL_error(L, "out of memory");
            }

            q = ngx_copy(p, args.data, args.len);
            *q++ = '&';
            ngx_memcpy(q, user_args.data, user_args.len);

            args.data = p;
            args.len += user_args.len + 1;
        }
    }

    if (ctx->headers_sent) {
        return luaL_error(L, "attempt to call ngx.exec after "
                          "sending out response headers");
    }

    ctx->exec_uri = uri;
    ctx->exec_args = args;

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "lua exec \"%V?%V\"",
                   &ctx->exec_uri, &ctx->exec_args);

    return lua_yield(L, 0);
}
ngx_int_t
ngx_http_echo_exec_echo_location(ngx_http_request_t *r,
    ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args)
{
    ngx_int_t                            rc;
    ngx_http_request_t                  *sr; /* subrequest object */
    ngx_str_t                           *computed_arg_elts;
    ngx_str_t                            location;
    ngx_str_t                           *url_args;
    ngx_http_post_subrequest_t          *psr;
    ngx_str_t                            args;
    ngx_uint_t                           flags = 0;
    ngx_http_echo_ctx_t                 *sr_ctx;

    computed_arg_elts = computed_args->elts;

    location = computed_arg_elts[0];

    if (location.len == 0) {
        return NGX_ERROR;
    }

    if (computed_args->nelts > 1) {
        url_args = &computed_arg_elts[1];

    } else {
        url_args = NULL;
    }

    args.data = NULL;
    args.len = 0;

    if (ngx_http_parse_unsafe_uri(r, &location, &args, &flags) != NGX_OK) {
        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "echo_location sees unsafe uri: \"%V\"",
                       &location);
        return NGX_ERROR;
    }

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

    rc = ngx_http_echo_send_header_if_needed(r, ctx);
    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
        return rc;
    }

    sr_ctx = ngx_http_echo_create_ctx(r);

    psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
    if (psr == NULL) {
        return NGX_ERROR;
    }

    psr->handler = ngx_http_echo_post_subrequest;
    psr->data = sr_ctx;

    rc = ngx_http_subrequest(r, &location, url_args, &sr, psr, 0);

    if (rc != NGX_OK) {
        return NGX_ERROR;
    }

    rc = ngx_http_echo_adjust_subrequest(sr);

    if (rc != NGX_OK) {
        return NGX_ERROR;
    }

    return NGX_AGAIN;
}
static ngx_int_t
ngx_http_mogilefs_put_handler(ngx_http_request_t *r)
{
    ngx_http_mogilefs_put_ctx_t        *ctx;
    ngx_str_t                           args; 
    ngx_uint_t                          flags;
    ngx_http_request_t                 *sr; 
    ngx_str_t                           spare_location = ngx_null_string, uri, value;
    ngx_int_t                           rc;
    u_char                             *p;
    ngx_http_core_loc_conf_t           *clcf;
    ngx_http_mogilefs_loc_conf_t       *mgcf;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "mogilefs put handler");

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
    mgcf = ngx_http_get_module_loc_conf(r, ngx_http_mogilefs_module);

    if (clcf->handler != ngx_http_mogilefs_handler ||
        (mgcf->location_type == NGX_MOGILEFS_MAIN && !(r->method & mgcf->methods)))
    {
        return NGX_DECLINED;
    }

    ctx = ngx_http_get_module_ctx(r, ngx_http_mogilefs_module);

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

        ctx->psr = NULL;
        ctx->state = START;
        ctx->status = 0;
        ctx->create_open_ctx = NULL;

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

        ngx_http_set_ctx(r, ctx, ngx_http_mogilefs_module);
    }

    if(ctx->psr == NULL) {
        ctx->psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
        if (ctx->psr == NULL) {
            return NGX_ERROR;
        }
    }

    if(r->request_body == NULL) {
        rc = ngx_http_read_client_request_body(r, ngx_http_mogilefs_body_handler);

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

        return NGX_DONE;
    }

    // Still receiving body?
    if(r->request_body->rest) {
        return NGX_DONE;
    }

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "mogilefs put handler state: %ui, status: %i", ctx->state, ctx->status);

    if(ctx->state == CREATE_OPEN || ctx->state == FETCH || ctx->state == CREATE_CLOSE) {
        if(ctx->status != NGX_OK && ctx->status != NGX_HTTP_CREATED && ctx->status != NGX_HTTP_NO_CONTENT) {
            return (ctx->status >= NGX_HTTP_SPECIAL_RESPONSE) ?
                ctx->status : NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
    }

    switch(ctx->state) {
        case START:
            spare_location = mgcf->create_open_spare_location;
            ctx->state = CREATE_OPEN;
            break;
        case CREATE_OPEN:
            spare_location = mgcf->fetch_location;
            ctx->state = FETCH;
            break;
        case FETCH:
            spare_location = mgcf->create_close_spare_location;
            ctx->state = CREATE_CLOSE;
            break;
        case CREATE_CLOSE:
            r->headers_out.content_length_n = 0;
            r->headers_out.status = NGX_HTTP_CREATED;

            r->header_only = 1;

            return ngx_http_send_header(r);
    }

    uri.len = spare_location.len + ctx->key.len;

    uri.data = ngx_palloc(r->pool, uri.len);

    p = ngx_cpymem(uri.data, spare_location.data, spare_location.len);

    p = ngx_cpymem(p, ctx->key.data, ctx->key.len);

    args.len = 0;
    args.data = NULL;
    flags = 0;

    if (ngx_http_parse_unsafe_uri(r, &uri, &args, &flags) != NGX_OK) {
        return NGX_ERROR;
    }

    ctx->psr->handler = ngx_http_mogilefs_finish_phase_handler;
    ctx->psr->data = ctx;

    flags |= NGX_HTTP_SUBREQUEST_WAITED;

    if(ctx->state == FETCH) {
        flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY;
    }

    rc = ngx_http_subrequest(r, &uri, &args, &sr, ctx->psr, flags);

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

    if(ctx->state == CREATE_CLOSE) {
        ngx_http_set_ctx(sr, ctx->create_open_ctx, ngx_http_mogilefs_module);

        value.data = ngx_palloc(r->pool, NGX_OFF_T_LEN);

        if(value.data == NULL) {
            return NGX_ERROR;
        }

        value.len = ngx_sprintf(value.data, "%O", r->headers_in.content_length_n)
            - value.data;

        if(ngx_http_mogilefs_add_aux_param(sr, &ngx_http_mogilefs_size, &value) != NGX_OK) {
            return NGX_ERROR;
        }
    }

    /*
     * Nginx closes temporary file with buffered body
     * whenever it starts sending reponse from upstream
     * and it is not doing subrequest in memory.
     *
     * Since the request body in create_open subrequest is
     * inherited from main request, it is necessary to prevent
     * nginx from closing the temporary file with request body,
     * before it could be passed to the storage node on fetch/store
     * stage.
     *
     * We do it by "hiding" the request body from nginx internals.
     */
    if(ctx->state == CREATE_OPEN) {
        sr->request_body = NULL;
    }

    sr->method = NGX_HTTP_PUT;
    sr->method_name = ngx_http_mogilefs_put_method;

    /*
     * Wait for subrequest to complete
     */
    return NGX_DONE;
}
static ngx_int_t
ngx_http_srcache_store_subrequest(ngx_http_request_t *r,
    ngx_http_srcache_ctx_t *ctx)
{
    ngx_http_srcache_ctx_t         *sr_ctx;
    ngx_str_t                       args;
    ngx_uint_t                      flags = 0;
    ngx_http_request_t             *sr;
    ngx_int_t                       rc;
    ngx_http_request_body_t        *rb = NULL;
    ngx_http_srcache_loc_conf_t    *conf;
    ngx_http_post_subrequest_t     *psr;

    ngx_http_srcache_parsed_request_t  *parsed_sr;

    dd("store subrequest");

    conf = ngx_http_get_module_loc_conf(r, ngx_http_srcache_filter_module);

    if (conf->store == NULL) {
        dd("conf store is NULL");
        return NGX_ERROR;
    }

    parsed_sr = ngx_palloc(r->pool, sizeof(ngx_http_srcache_parsed_request_t));
    if (parsed_sr == NULL) {
        return NGX_ERROR;
    }

    parsed_sr->method      = conf->store->method;
    parsed_sr->method_name = conf->store->method_name;

    if (ctx->body_to_cache) {
        dd("found body to cache (len %d)", (int) ctx->response_length);

        rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));

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

        rb->bufs = ctx->body_to_cache;
        rb->buf = ctx->body_to_cache->buf;

        parsed_sr->request_body = rb;

    } else {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "srcache_store: no request body for the subrequest");

        return NGX_ERROR;
    }

    parsed_sr->content_length_n = ctx->response_length;

    if (ngx_http_complex_value(r, &conf->store->location,
                               &parsed_sr->location) != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (parsed_sr->location.len == 0) {
        return NGX_ERROR;
    }

    if (ngx_http_complex_value(r, &conf->store->args, &parsed_sr->args)
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    args.data = NULL;
    args.len = 0;

    if (ngx_http_parse_unsafe_uri(r, &parsed_sr->location, &args, &flags)
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (args.len > 0 && parsed_sr->args.len == 0) {
        parsed_sr->args = args;
    }

    dd("firing the store subrequest");

    dd("store location: %.*s", (int) parsed_sr->location.len,
            parsed_sr->location.data);

    dd("store args: %.*s", (int) parsed_sr->args.len,
       parsed_sr->args.data);

    sr_ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_srcache_ctx_t));

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

    sr_ctx->in_store_subrequest = 1;

    psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
    if (psr == NULL) {
        return NGX_ERROR;
    }

    psr->handler = ngx_http_srcache_store_post_subrequest;
    psr->data = sr_ctx;

    rc = ngx_http_subrequest(r, &parsed_sr->location, &parsed_sr->args,
                             &sr, psr, flags);

    if (rc != NGX_OK) {
        return NGX_ERROR;
    }

    rc = ngx_http_srcache_adjust_subrequest(sr, parsed_sr);

    if (rc != NGX_OK) {
        return NGX_ERROR;
    }

    ngx_http_set_ctx(sr, sr_ctx, ngx_http_srcache_filter_module);

    return NGX_OK;
}
int
ngx_http_lua_ngx_exec(lua_State *L)
{
    int                          n;
    ngx_http_request_t          *r;
    ngx_http_lua_ctx_t          *ctx;
    ngx_str_t                    uri;
    ngx_str_t                    args, user_args;
    ngx_uint_t                   flags;
    u_char                      *p;
    u_char                      *q;
    size_t                       len;

    n = lua_gettop(L);
    if (n != 1 && n != 2) {
        return luaL_error(L, "expecting one or two arguments, but got %d",
                          n);
    }

    lua_getglobal(L, GLOBALS_SYMBOL_REQUEST);
    r = lua_touserdata(L, -1);
    lua_pop(L, 1);

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

    args.data = NULL;
    args.len = 0;

    /* read the 1st argument (uri) */

    p = (u_char *) luaL_checklstring(L, 1, &len);

    if (len == 0) {
        return luaL_error(L, "The uri argument is empty");
    }

    uri.data = ngx_palloc(r->pool, len);
    ngx_memcpy(uri.data, p, len);

    uri.len = len;

    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);

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

    if (n == 2) {
        /* read the 2nd argument (args) */
        p = (u_char *) luaL_checklstring(L, 2, &len);

        user_args.data = ngx_palloc(r->pool, len);
        ngx_memcpy(user_args.data, p, len);

        user_args.len = len;

    } else {
        user_args.data = NULL;
        user_args.len = 0;
    }

    if (user_args.len) {
        if (args.len == 0) {
            args = user_args;
        } else {
            p = ngx_palloc(r->pool, args.len + user_args.len + 1);
            if (p == NULL) {
                return luaL_error(L, "out of memory");
            }

            q = ngx_copy(p, args.data, args.len);
            *q++ = '&';
            q = ngx_copy(q, user_args.data, user_args.len);

            args.data = p;
            args.len += user_args.len + 1;
        }
    }

    if (ctx->headers_sent) {
        return luaL_error(L, "attempt to call ngx.exec after "
                          "sending out response headers");
    }

    ctx->exec_uri = uri;
    ctx->exec_args = args;

    lua_pushnil(L);
    return lua_error(L);
}
ngx_int_t
ngx_http_echo_exec_echo_location(ngx_http_request_t *r,
        ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args)
{
    ngx_int_t                           rc;
    ngx_http_request_t                  *sr; /* subrequest object */
    ngx_str_t                           *computed_arg_elts;
    ngx_str_t                           location;
    ngx_str_t                           *url_args;
    ngx_http_post_subrequest_t          *psr;
    ngx_str_t                           args;
    ngx_uint_t                          flags;

    computed_arg_elts = computed_args->elts;

    location = computed_arg_elts[0];

    if (location.len == 0) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

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

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

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

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

    psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
    if (psr == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    psr->handler = ngx_http_echo_post_subrequest;
    psr->data = ctx;

    rc = ngx_http_subrequest(r, &location, url_args, &sr, psr, 0);
    if (rc != NGX_OK) {
        return NGX_ERROR;
    }

    rc = ngx_http_echo_adjust_subrequest(sr);
    if (rc != NGX_OK) {
        return rc;
    }

    return NGX_OK;
}
static ngx_int_t
ngx_http_dav_copy_move_handler(ngx_http_request_t *r)
{
    u_char                   *p, *host, *last, ch;
    size_t                    len, root;
    ngx_err_t                 err;
    ngx_int_t                 rc, depth;
    ngx_uint_t                overwrite, slash, dir, flags;
    ngx_str_t                 path, uri, duri, args;
    ngx_tree_ctx_t            tree;
    ngx_copy_file_t           cf;
    ngx_file_info_t           fi;
    ngx_table_elt_t          *dest, *over;
    ngx_ext_rename_file_t     ext;
    ngx_http_dav_copy_ctx_t   copy;
    ngx_http_dav_loc_conf_t  *dlcf;

    if (r->headers_in.content_length_n > 0) {
        return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
    }

    dest = r->headers_in.destination;

    if (dest == NULL) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "client sent no \"Destination\" header");
        return NGX_HTTP_BAD_REQUEST;
    }

    p = dest->value.data;
    /* there is always '\0' even after empty header value */
    if (p[0] == '/') {
        last = p + dest->value.len;
        goto destination_done;
    }

    len = r->headers_in.server.len;

    if (len == 0) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "client sent no \"Host\" header");
        return NGX_HTTP_BAD_REQUEST;
    }

#if (NGX_HTTP_SSL)

    if (r->connection->ssl) {
        if (ngx_strncmp(dest->value.data, "https://", sizeof("https://") - 1)
            != 0)
        {
            goto invalid_destination;
        }

        host = dest->value.data + sizeof("https://") - 1;

    } else
#endif
    {
        if (ngx_strncmp(dest->value.data, "http://", sizeof("http://") - 1)
            != 0)
        {
            goto invalid_destination;
        }

        host = dest->value.data + sizeof("http://") - 1;
    }

    if (ngx_strncmp(host, r->headers_in.server.data, len) != 0) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "\"Destination\" URI \"%V\" is handled by "
                      "different repository than the source URI",
                      &dest->value);
        return NGX_HTTP_BAD_REQUEST;
    }

    last = dest->value.data + dest->value.len;

    for (p = host + len; p < last; p++) {
        if (*p == '/') {
            goto destination_done;
        }
    }

invalid_destination:

    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                  "client sent invalid \"Destination\" header: \"%V\"",
                  &dest->value);
    return NGX_HTTP_BAD_REQUEST;

destination_done:

    duri.len = last - p;
    duri.data = p;
    flags = NGX_HTTP_LOG_UNSAFE;

    if (ngx_http_parse_unsafe_uri(r, &duri, &args, &flags) != NGX_OK) {
        goto invalid_destination;
    }

    if ((r->uri.data[r->uri.len - 1] == '/' && *(last - 1) != '/')
        || (r->uri.data[r->uri.len - 1] != '/' && *(last - 1) == '/'))
    {
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                       "both URI \"%V\" and \"Destination\" URI \"%V\" "
                       "should be either collections or non-collections",
                       &r->uri, &dest->value);
         return NGX_HTTP_CONFLICT;
    }

    depth = ngx_http_dav_depth(r, NGX_HTTP_DAV_INFINITY_DEPTH);

    if (depth != NGX_HTTP_DAV_INFINITY_DEPTH) {

        if (r->method == NGX_HTTP_COPY) {
            if (depth != 0) {
                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                              "\"Depth\" header must be 0 or infinity");
                return NGX_HTTP_BAD_REQUEST;
            }

        } else {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "\"Depth\" header must be infinity");
            return NGX_HTTP_BAD_REQUEST;
        }
    }

    over = r->headers_in.overwrite;

    if (over) {
        if (over->value.len == 1) {
            ch = over->value.data[0];

            if (ch == 'T' || ch == 't') {
                overwrite = 1;
                goto overwrite_done;
            }

            if (ch == 'F' || ch == 'f') {
                overwrite = 0;
                goto overwrite_done;
            }

        }

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "client sent invalid \"Overwrite\" header: \"%V\"",
                      &over->value);
        return NGX_HTTP_BAD_REQUEST;
    }

    overwrite = 1;

overwrite_done:

    ngx_http_map_uri_to_path(r, &path, &root, 0);

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http copy from: \"%s\"", path.data);

    uri = r->uri;
    r->uri = duri;

    ngx_http_map_uri_to_path(r, &copy.path, &root, 0);

    r->uri = uri;

    copy.path.len--;  /* omit "\0" */

    if (copy.path.data[copy.path.len - 1] == '/') {
        slash = 1;
        copy.path.len--;
        copy.path.data[copy.path.len] = '\0';

    } else {
        slash = 0;
    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http copy to: \"%s\"", copy.path.data);

    if (ngx_link_info(copy.path.data, &fi) == NGX_FILE_ERROR) {
        err = ngx_errno;

        if (err != NGX_ENOENT) {
            return ngx_http_dav_error(r->connection->log, err,
                                      NGX_HTTP_NOT_FOUND, ngx_link_info_n,
                                      copy.path.data);
        }

        /* destination does not exist */

        overwrite = 0;
        dir = 0;

    } else {

        /* destination exists */

        if (ngx_is_dir(&fi) && !slash) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "\"%V\" could not be %Ved to collection \"%V\"",
                          &r->uri, &r->method_name, &dest->value);
            return NGX_HTTP_CONFLICT;
        }

        if (!overwrite) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EEXIST,
                          "\"%s\" could not be created", copy.path.data);
            return NGX_HTTP_PRECONDITION_FAILED;
        }

        dir = ngx_is_dir(&fi);
    }

    if (ngx_link_info(path.data, &fi) == NGX_FILE_ERROR) {
        return ngx_http_dav_error(r->connection->log, ngx_errno,
                                  NGX_HTTP_NOT_FOUND, ngx_link_info_n,
                                  path.data);
    }

    if (ngx_is_dir(&fi)) {

        if (r->uri.data[r->uri.len - 1] != '/') {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "\"%V\" is collection", &r->uri);
            return NGX_HTTP_BAD_REQUEST;
        }

        if (overwrite) {
            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                           "http delete: \"%s\"", copy.path.data);

            rc = ngx_http_dav_delete_path(r, &copy.path, dir);

            if (rc != NGX_OK) {
                return rc;
            }
        }
    }

    if (ngx_is_dir(&fi)) {

        path.len -= 2;  /* omit "/\0" */

        if (r->method == NGX_HTTP_MOVE) {
            if (ngx_rename_file(path.data, copy.path.data) != NGX_FILE_ERROR) {
                return NGX_HTTP_CREATED;
            }
        }

        if (ngx_create_dir(copy.path.data, ngx_file_access(&fi))
            == NGX_FILE_ERROR)
        {
            return ngx_http_dav_error(r->connection->log, ngx_errno,
                                      NGX_HTTP_NOT_FOUND,
                                      ngx_create_dir_n, copy.path.data);
        }

        copy.len = path.len;

        tree.init_handler = NULL;
        tree.file_handler = ngx_http_dav_copy_tree_file;
        tree.pre_tree_handler = ngx_http_dav_copy_dir;
        tree.post_tree_handler = ngx_http_dav_copy_dir_time;
        tree.spec_handler = ngx_http_dav_noop;
        tree.data = &copy;
        tree.alloc = 0;
        tree.log = r->connection->log;

        if (ngx_walk_tree(&tree, &path) == NGX_OK) {

            if (r->method == NGX_HTTP_MOVE) {
                rc = ngx_http_dav_delete_path(r, &path, 1);

                if (rc != NGX_OK) {
                    return rc;
                }
            }

            return NGX_HTTP_CREATED;
        }

    } else {

        if (r->method == NGX_HTTP_MOVE) {

            dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);

            ext.access = 0;
            ext.path_access = dlcf->access;
            ext.time = -1;
            ext.create_path = 1;
            ext.delete_file = 0;
            ext.log = r->connection->log;

            if (ngx_ext_rename_file(&path, &copy.path, &ext) == NGX_OK) {
                return NGX_HTTP_NO_CONTENT;
            }

            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);

        cf.size = ngx_file_size(&fi);
        cf.buf_size = 0;
        cf.access = dlcf->access;
        cf.time = ngx_file_mtime(&fi);
        cf.log = r->connection->log;

        if (ngx_copy_file(path.data, copy.path.data, &cf) == NGX_OK) {
            return NGX_HTTP_NO_CONTENT;
        }
    }

    return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
static ngx_int_t
ngx_http_eval_handler(ngx_http_request_t *r)
{
    /* size_t                      loc_len; */
    ngx_str_t                   args;
    ngx_str_t                   subrequest_uri;
    ngx_uint_t                  flags;
    /* ngx_http_core_loc_conf_t   *clcf; */
    ngx_http_eval_loc_conf_t   *ecf;
    ngx_http_eval_ctx_t        *ctx;
    ngx_http_eval_ctx_t        *sr_ctx;
    ngx_http_request_t         *sr;
    ngx_int_t                   rc;
    ngx_http_post_subrequest_t *psr;
    u_char                     *p;

    /*
    if(r != r->main) {
        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

        loc_len = r->valid_location ? clcf->name.len : 0;

        if(r->uri.len != loc_len) {
            r->uri.data += loc_len;
            r->uri.len -= loc_len;
        }
        else {
            r->uri.len = 1;
        }
    }
    */

    ecf = ngx_http_get_module_loc_conf(r, ngx_http_eval_module);

    if(ecf->variables == NULL || !ecf->variables->nelts) {
        return NGX_DECLINED;
    }

    ctx = ngx_http_get_module_ctx(r, ngx_http_eval_module);

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

        ctx->base_conf = ecf;

        ngx_http_set_ctx(r, ctx, ngx_http_eval_module);
    }

    if (ctx->done) {
        dd("subrequest done");
        if(!ecf->escalate || ctx->status == NGX_OK || ctx->status == NGX_HTTP_OK) {
            return NGX_DECLINED;
        }

        dd("status: %d", (int) ctx->status);

        return ctx->status;
    }

    if (ctx->in_progress) {
        dd("still in progress");
        return NGX_DONE;
    }

    psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
    if (psr == NULL) {
        return NGX_ERROR;
    }

    if(ngx_http_eval_init_variables(r, ctx, ecf) != NGX_OK) {
        return NGX_ERROR;
    }

    args = r->args;
    flags = 0;

    subrequest_uri.len = ecf->eval_location.len + r->uri.len;

    p = subrequest_uri.data = ngx_palloc(r->pool, subrequest_uri.len);

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

    p = ngx_copy(p, ecf->eval_location.data, ecf->eval_location.len);
    p = ngx_copy(p, r->uri.data, r->uri.len);

    if (ngx_http_parse_unsafe_uri(r, &subrequest_uri, &args, &flags) != NGX_OK) {
        return NGX_ERROR;
    }

    psr->handler = ngx_http_eval_post_subrequest_handler;
    psr->data = ctx;

    flags |= NGX_HTTP_SUBREQUEST_WAITED;

    dd("subrequest in memory : %d", (int) ecf->subrequest_in_memory);

    if (ecf->subrequest_in_memory) {
        flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY;
    } else {
    }

    dd("issue subrequest");

    rc = ngx_http_subrequest(r, &subrequest_uri, &args, &sr, psr, flags);

    if (rc == NGX_ERROR || rc == NGX_DONE) {
        return rc;
    }

    sr->discard_body = 1;

    ctx->in_progress = 1;

    /* XXX we don't allow eval in subrequests, i think? */
    sr_ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_eval_ctx_t));
    if (sr_ctx == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    ngx_http_set_ctx(sr, sr_ctx, ngx_http_eval_module);

    dd("wait for subrequest to complete");

    return NGX_DONE;
}