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

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