ngx_int_t
ngx_http_echo_send_chain_link(ngx_http_request_t *r,
    ngx_http_echo_ctx_t *ctx, ngx_chain_t *in)
{
    ngx_int_t        rc;

    rc = ngx_http_echo_send_header_if_needed(r, ctx);

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

    if (in == NULL) {

#if defined(nginx_version) && nginx_version <= 8004

        /* earlier versions of nginx does not allow subrequests
            to send last_buf themselves */
        if (r != r->main) {
            return NGX_OK;
        }

#endif

        rc = ngx_http_send_special(r, NGX_HTTP_LAST);
        if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
            return rc;
        }

        return NGX_OK;
    }

    /* FIXME we should udpate chains to recycle chain links and bufs */
    return ngx_http_output_filter(r, in);
}
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;
}
static ngx_int_t 
ngx_http_rj_cdn_com_handler(ngx_http_request_t *r)
{
    ngx_int_t   rc;
    ngx_str_t   uri;
    ngx_http_request_t *sr;     /*subrequest object*/

    uri.data = ngx_pcalloc(r->pool, r->args.len);
    uri.len = r->args.len;
    memcpy(uri.data, r->args.data, r->args.len);

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

    rc = ngx_http_subrequest(r, &uri, NULL, &sr, NULL, 0);
    if (rc != NGX_OK)
        return NGX_ERROR;

    rc = ngx_http_rj_cdn_com_adjust_subrequest(sr);
    if (rc != NGX_OK)
        return NGX_ERROR;

    rc = ngx_http_send_special(r, NGX_HTTP_LAST);
    if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE){
        return rc;
    }

    return rc;
}
ngx_int_t
ngx_http_echo_exec_echo_duplicate(ngx_http_request_t *r,
        ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args)
{
    ngx_str_t                   *computed_arg;
    ngx_str_t                   *computed_arg_elts;
    ssize_t                     i, count;
    ngx_str_t                   *str;
    u_char                      *p;
    ngx_int_t                   rc;

    ngx_buf_t                   *buf;
    ngx_chain_t                 *cl;


    dd_enter();

    computed_arg_elts = computed_args->elts;

    computed_arg = &computed_arg_elts[0];

    count = ngx_http_echo_atosz(computed_arg->data, computed_arg->len);

    if (count == NGX_ERROR) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   "invalid size specified: \"%V\"", computed_arg);

        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    str = &computed_arg_elts[1];

    if (count == 0 || str->len == 0) {
        rc = ngx_http_echo_send_header_if_needed(r, ctx);
        if (r->header_only || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
            return rc;
        }
        return NGX_OK;
    }

    buf = ngx_create_temp_buf(r->pool, count * str->len);
    if (buf == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    p = buf->pos;
    for (i = 0; i < count; i++) {
        p = ngx_copy(p, str->data, str->len);
    }
    buf->last = p;

    cl = ngx_alloc_chain_link(r->pool);
    if (cl == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }
    cl->next = NULL;
    cl->buf = buf;

    return ngx_http_echo_send_chain_link(r, ctx, cl);
}
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_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;
}
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;
}
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;
}
ngx_int_t
ngx_http_echo_send_chain_link(ngx_http_request_t* r,
        ngx_http_echo_ctx_t *ctx, ngx_chain_t *cl)
{
    ngx_int_t       rc;
    size_t          size;
    ngx_chain_t     *p;

    rc = ngx_http_echo_send_header_if_needed(r, ctx);

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

    if (r->http_version < NGX_HTTP_VERSION_11 && !ctx->headers_sent) {
        ctx->headers_sent = 1;

        size = 0;

        for (p = cl; p; p = p->next) {
            if (p->buf->memory) {
                size += p->buf->last - p->buf->pos;
            }
        }

        r->headers_out.content_length_n = (off_t) size;

        if (r->headers_out.content_length) {
            r->headers_out.content_length->hash = 0;
        }

        r->headers_out.content_length = NULL;

        rc = ngx_http_send_header(r);

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

    if (cl == NULL) {

#if defined(nginx_version) && nginx_version <= 8004

        /* earlier versions of nginx does not allow subrequests
            to send last_buf themselves */
        if (r != r->main) {
            return NGX_OK;
        }

#endif

        rc = ngx_http_send_special(r, NGX_HTTP_LAST);
        if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
            return rc;
        }

        return NGX_OK;
    }

    return ngx_http_output_filter(r, cl);
}