summaryrefslogtreecommitdiff
path: root/echo-nginx-module-0.63/src/ngx_http_echo_subrequest.c
diff options
context:
space:
mode:
authorkaiwu <kaiwu2004@gmail.com>2025-03-01 12:42:23 +0800
committerkaiwu <kaiwu2004@gmail.com>2025-03-01 12:42:23 +0800
commit3f33461e4948bf05e60bdff35ec6c57a649c7860 (patch)
tree284c2ba95a41536ae1bff6bea710db0709a64739 /echo-nginx-module-0.63/src/ngx_http_echo_subrequest.c
downloadopenresty-3f33461e4948bf05e60bdff35ec6c57a649c7860.tar.gz
openresty-3f33461e4948bf05e60bdff35ec6c57a649c7860.zip
openresty bundle
Diffstat (limited to 'echo-nginx-module-0.63/src/ngx_http_echo_subrequest.c')
-rw-r--r--echo-nginx-module-0.63/src/ngx_http_echo_subrequest.c791
1 files changed, 791 insertions, 0 deletions
diff --git a/echo-nginx-module-0.63/src/ngx_http_echo_subrequest.c b/echo-nginx-module-0.63/src/ngx_http_echo_subrequest.c
new file mode 100644
index 0000000..0980d58
--- /dev/null
+++ b/echo-nginx-module-0.63/src/ngx_http_echo_subrequest.c
@@ -0,0 +1,791 @@
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_http_echo_util.h"
+#include "ngx_http_echo_subrequest.h"
+#include "ngx_http_echo_handler.h"
+#include <nginx.h>
+
+
+#define ngx_http_echo_method_name(m) { sizeof(m) - 1, (u_char *) m " " }
+
+
+ngx_str_t ngx_http_echo_content_length_header_key =
+ ngx_string("Content-Length");
+
+ngx_str_t ngx_http_echo_get_method = ngx_http_echo_method_name("GET");
+ngx_str_t ngx_http_echo_put_method = ngx_http_echo_method_name("PUT");
+ngx_str_t ngx_http_echo_post_method = ngx_http_echo_method_name("POST");
+ngx_str_t ngx_http_echo_head_method = ngx_http_echo_method_name("HEAD");
+ngx_str_t ngx_http_echo_copy_method = ngx_http_echo_method_name("COPY");
+ngx_str_t ngx_http_echo_move_method = ngx_http_echo_method_name("MOVE");
+ngx_str_t ngx_http_echo_lock_method = ngx_http_echo_method_name("LOCK");
+ngx_str_t ngx_http_echo_mkcol_method = ngx_http_echo_method_name("MKCOL");
+ngx_str_t ngx_http_echo_trace_method = ngx_http_echo_method_name("TRACE");
+ngx_str_t ngx_http_echo_delete_method = ngx_http_echo_method_name("DELETE");
+ngx_str_t ngx_http_echo_unlock_method = ngx_http_echo_method_name("UNLOCK");
+ngx_str_t ngx_http_echo_options_method = ngx_http_echo_method_name("OPTIONS");
+ngx_str_t ngx_http_echo_propfind_method =
+ ngx_http_echo_method_name("PROPFIND");
+ngx_str_t ngx_http_echo_proppatch_method =
+ ngx_http_echo_method_name("PROPPATCH");
+
+
+typedef struct ngx_http_echo_subrequest_s {
+ ngx_uint_t method;
+ ngx_str_t *method_name;
+ ngx_str_t *location;
+ ngx_str_t *query_string;
+ ssize_t content_length_n;
+ ngx_http_request_body_t *request_body;
+} ngx_http_echo_subrequest_t;
+
+
+static ngx_int_t ngx_http_echo_parse_method_name(ngx_str_t **method_name_ptr);
+static ngx_int_t ngx_http_echo_adjust_subrequest(ngx_http_request_t *sr,
+ ngx_http_echo_subrequest_t *parsed_sr);
+static ngx_int_t ngx_http_echo_parse_subrequest_spec(ngx_http_request_t *r,
+ ngx_array_t *computed_args, ngx_http_echo_subrequest_t **parsed_sr_ptr);
+static ngx_int_t ngx_http_echo_set_content_length_header(ngx_http_request_t *r,
+ off_t len);
+
+
+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 = 0;
+
+ dd_enter();
+
+ rc = ngx_http_echo_parse_subrequest_spec(r, computed_args, &parsed_sr);
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ dd("location: %.*s",
+ (int) parsed_sr->location->len,
+ parsed_sr->location->data);
+
+ args.data = NULL;
+ args.len = 0;
+
+ if (ngx_http_parse_unsafe_uri(r, parsed_sr->location, &args, &flags)
+ != NGX_OK)
+ {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "echo_subrequest_async sees unsafe uri: \"%V\"",
+ parsed_sr->location);
+ return NGX_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 (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
+ 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_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 = 0;
+ ngx_http_echo_ctx_t *sr_ctx;
+
+ dd_enter();
+
+ 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)
+ {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "echo_subrequest sees unsafe uri: \"%V\"",
+ parsed_sr->location);
+ return NGX_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 (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
+ return rc;
+ }
+
+ sr_ctx = ngx_http_echo_create_ctx(r);
+
+ /* set by ngx_http_echo_create_ctx
+ * sr_ctx->run_post_subrequest = 0
+ */
+
+ dd("creating sr ctx for %.*s: %p", (int) parsed_sr->location->len,
+ parsed_sr->location->data, sr_ctx);
+
+ 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, parsed_sr->location, parsed_sr->query_string,
+ &sr, psr, 0);
+
+ if (rc != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ sr_ctx->sleep.data = sr;
+
+ ngx_http_set_ctx(sr, sr_ctx, ngx_http_echo_module);
+
+ rc = ngx_http_echo_adjust_subrequest(sr, parsed_sr);
+
+ if (rc != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ return NGX_AGAIN;
+}
+
+
+static ngx_int_t
+ngx_http_echo_parse_subrequest_spec(ngx_http_request_t *r,
+ ngx_array_t *computed_args, ngx_http_echo_subrequest_t **parsed_sr_ptr)
+{
+ ngx_str_t *computed_arg_elts, *arg;
+ ngx_str_t **to_write = NULL;
+ ngx_str_t *method_name;
+ ngx_str_t *body_str = NULL;
+ ngx_str_t *body_file = NULL;
+ ngx_uint_t i;
+ ngx_flag_t expecting_opt;
+ ngx_http_request_body_t *rb = NULL;
+ ngx_buf_t *b;
+ ngx_http_echo_subrequest_t *parsed_sr;
+ ngx_open_file_info_t of;
+ ngx_http_core_loc_conf_t *clcf;
+ size_t len;
+
+ *parsed_sr_ptr = ngx_pcalloc(r->pool, sizeof(ngx_http_echo_subrequest_t));
+ if (*parsed_sr_ptr == NULL) {
+ return NGX_ERROR;
+ }
+
+ parsed_sr = *parsed_sr_ptr;
+ computed_arg_elts = computed_args->elts;
+ method_name = &computed_arg_elts[0];
+ parsed_sr->location = &computed_arg_elts[1];
+
+ if (parsed_sr->location->len == 0) {
+ return NGX_ERROR;
+ }
+
+ expecting_opt = 1;
+
+ for (i = 2; i < computed_args->nelts; i++) {
+ arg = &computed_arg_elts[i];
+
+ if (!expecting_opt) {
+ if (to_write == NULL) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "echo_subrequest_async: to_write should NOT be NULL");
+ return NGX_ERROR;
+ }
+
+ *to_write = arg;
+ to_write = NULL;
+
+ expecting_opt = 1;
+
+ continue;
+ }
+
+ if (arg->len == 2) {
+ if (ngx_strncmp("-q", arg->data, arg->len) == 0) {
+ to_write = &parsed_sr->query_string;
+ expecting_opt = 0;
+ continue;
+ }
+
+ if (ngx_strncmp("-b", arg->data, arg->len) == 0) {
+ to_write = &body_str;
+ expecting_opt = 0;
+ continue;
+ }
+
+ if (ngx_strncmp("-f", arg->data, arg->len) == 0) {
+ dd("found option -f");
+ to_write = &body_file;
+ expecting_opt = 0;
+ continue;
+ }
+ }
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "unknown option for echo_subrequest*: %V", arg);
+
+ return NGX_ERROR;
+ }
+
+ if (body_str != NULL && body_str->len) {
+ rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
+
+ if (rb == NULL) {
+ return NGX_ERROR;
+ }
+
+ parsed_sr->content_length_n = body_str->len;
+
+ b = ngx_calloc_buf(r->pool);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ b->temporary = 1;
+ /* b->memory = 1; */
+ b->start = b->pos = body_str->data;
+ b->end = b->last = body_str->data + body_str->len;
+
+ rb->bufs = ngx_alloc_chain_link(r->pool);
+ if (rb->bufs == NULL) {
+ return NGX_ERROR;
+ }
+
+ rb->bufs->buf = b;
+ rb->bufs->next = NULL;
+
+ rb->buf = b;
+
+ } else if (body_file != NULL && body_file->len) {
+
+ dd("body_file defined %.*s", (int) body_file->len, body_file->data);
+
+ body_file->data = ngx_http_echo_rebase_path(r->pool, body_file->data,
+ body_file->len, &len);
+
+ if (body_file->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ body_file->len = len;
+
+ dd("after rebase, the path becomes %.*s", (int) body_file->len,
+ body_file->data);
+
+ rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
+ if (rb == NULL) {
+ return NGX_ERROR;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+ ngx_memzero(&of, sizeof(ngx_open_file_info_t));
+
+#if defined(nginx_version) && nginx_version >= 8018
+ of.read_ahead = clcf->read_ahead;
+#endif
+
+ of.directio = clcf->directio;
+ of.valid = clcf->open_file_cache_valid;
+ of.min_uses = clcf->open_file_cache_min_uses;
+ of.errors = clcf->open_file_cache_errors;
+ of.events = clcf->open_file_cache_events;
+
+ if (ngx_open_cached_file(clcf->open_file_cache, body_file, &of, r->pool)
+ != NGX_OK)
+ {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, of.err,
+ "%s \"%V\" failed",
+ of.failed, body_file);
+
+ return NGX_ERROR;
+ }
+
+ dd("file content size: %d", (int) of.size);
+
+ parsed_sr->content_length_n = (ssize_t) of.size;
+
+ b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
+ if (b->file == NULL) {
+ return NGX_ERROR;
+ }
+
+ b->file_pos = 0;
+ b->file_last = of.size;
+
+ b->in_file = b->file_last ? 1: 0;
+
+#if 0
+ b->last_buf = (r == r->main) ? 1: 0;
+ b->last_in_chain = 1;
+#endif
+
+ b->file->fd = of.fd;
+ b->file->name = *body_file;
+ b->file->log = r->connection->log;
+ b->file->directio = of.is_directio;
+
+ rb->bufs = ngx_alloc_chain_link(r->pool);
+ if (rb->bufs == NULL) {
+ return NGX_ERROR;
+ }
+
+ rb->bufs->buf = b;
+ rb->bufs->next = NULL;
+ rb->buf = b;
+ }
+
+ parsed_sr->request_body = rb;
+
+ parsed_sr->method = ngx_http_echo_parse_method_name(&method_name);
+ parsed_sr->method_name = method_name;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_echo_adjust_subrequest(ngx_http_request_t *sr,
+ ngx_http_echo_subrequest_t *parsed_sr)
+{
+ ngx_http_core_main_conf_t *cmcf;
+ ngx_http_request_t *r;
+ ngx_http_request_body_t *body;
+ ngx_int_t rc;
+
+ sr->method = parsed_sr->method;
+ sr->method_name = *(parsed_sr->method_name);
+
+ if (sr->method == NGX_HTTP_HEAD) {
+ sr->header_only = 1;
+ }
+
+ r = sr->parent;
+
+ sr->header_in = r->header_in;
+
+ /* XXX work-around a bug in ngx_http_subrequest */
+ if (r->headers_in.headers.last == &r->headers_in.headers.part) {
+ sr->headers_in.headers.last = &sr->headers_in.headers.part;
+ }
+
+ /* we do not inherit the parent request's variables */
+ cmcf = ngx_http_get_module_main_conf(sr, ngx_http_core_module);
+ sr->variables = ngx_pcalloc(sr->pool, cmcf->variables.nelts
+ * sizeof(ngx_http_variable_value_t));
+
+ if (sr->variables == NULL) {
+ return NGX_ERROR;
+ }
+
+ body = parsed_sr->request_body;
+ if (body) {
+ sr->request_body = body;
+
+ rc = ngx_http_echo_set_content_length_header(sr, body->buf ?
+ ngx_buf_size(body->buf)
+ : 0);
+
+ if (rc != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
+ dd("subrequest body: %p", sr->request_body);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_echo_parse_method_name(ngx_str_t **method_name_ptr)
+{
+ const ngx_str_t *method_name = *method_name_ptr;
+
+ switch (method_name->len) {
+ case 3:
+ if (ngx_http_echo_strcmp_const(method_name->data, "GET") == 0) {
+ *method_name_ptr = &ngx_http_echo_get_method;
+ return NGX_HTTP_GET;
+ }
+
+ if (ngx_http_echo_strcmp_const(method_name->data, "PUT") == 0) {
+ *method_name_ptr = &ngx_http_echo_put_method;
+ return NGX_HTTP_PUT;
+ }
+
+ return NGX_HTTP_UNKNOWN;
+
+ case 4:
+ if (ngx_http_echo_strcmp_const(method_name->data, "POST") == 0) {
+ *method_name_ptr = &ngx_http_echo_post_method;
+ return NGX_HTTP_POST;
+ }
+
+ if (ngx_http_echo_strcmp_const(method_name->data, "HEAD") == 0) {
+ *method_name_ptr = &ngx_http_echo_head_method;
+ return NGX_HTTP_HEAD;
+ }
+
+ if (ngx_http_echo_strcmp_const(method_name->data, "COPY") == 0) {
+ *method_name_ptr = &ngx_http_echo_copy_method;
+ return NGX_HTTP_COPY;
+ }
+
+ if (ngx_http_echo_strcmp_const(method_name->data, "MOVE") == 0) {
+ *method_name_ptr = &ngx_http_echo_move_method;
+ return NGX_HTTP_MOVE;
+ }
+
+ if (ngx_http_echo_strcmp_const(method_name->data, "LOCK") == 0) {
+ *method_name_ptr = &ngx_http_echo_lock_method;
+ return NGX_HTTP_LOCK;
+ }
+
+ return NGX_HTTP_UNKNOWN;
+
+ case 5:
+ if (ngx_http_echo_strcmp_const(method_name->data, "MKCOL") == 0) {
+ *method_name_ptr = &ngx_http_echo_mkcol_method;
+ return NGX_HTTP_MKCOL;
+ }
+
+ if (ngx_http_echo_strcmp_const(method_name->data, "TRACE") == 0) {
+ *method_name_ptr = &ngx_http_echo_trace_method;
+ return NGX_HTTP_TRACE;
+ }
+
+ return NGX_HTTP_UNKNOWN;
+
+ case 6:
+ if (ngx_http_echo_strcmp_const(method_name->data, "DELETE") == 0) {
+ *method_name_ptr = &ngx_http_echo_delete_method;
+ return NGX_HTTP_DELETE;
+ }
+
+ if (ngx_http_echo_strcmp_const(method_name->data, "UNLOCK") == 0) {
+ *method_name_ptr = &ngx_http_echo_unlock_method;
+ return NGX_HTTP_UNLOCK;
+ }
+
+ return NGX_HTTP_UNKNOWN;
+
+ case 7:
+ if (ngx_http_echo_strcmp_const(method_name->data, "OPTIONS") == 0) {
+ *method_name_ptr = &ngx_http_echo_options_method;
+ return NGX_HTTP_OPTIONS;
+ }
+
+ return NGX_HTTP_UNKNOWN;
+
+ case 8:
+ if (ngx_http_echo_strcmp_const(method_name->data, "PROPFIND") == 0) {
+ *method_name_ptr = &ngx_http_echo_propfind_method;
+ return NGX_HTTP_PROPFIND;
+ }
+
+ return NGX_HTTP_UNKNOWN;
+
+ case 9:
+ if (ngx_http_echo_strcmp_const(method_name->data, "PROPPATCH") == 0) {
+ *method_name_ptr = &ngx_http_echo_proppatch_method;
+ return NGX_HTTP_PROPPATCH;
+ }
+
+ return NGX_HTTP_UNKNOWN;
+
+ default:
+ return NGX_HTTP_UNKNOWN;
+ }
+}
+
+
+/* XXX extermely evil and not working yet */
+ngx_int_t
+ngx_http_echo_exec_abort_parent(ngx_http_request_t *r,
+ ngx_http_echo_ctx_t *ctx)
+{
+#if 0
+ ngx_http_postponed_request_t *pr, *ppr;
+ ngx_http_request_t *saved_data = NULL;
+ ngx_chain_t *out = NULL;
+ /* ngx_int_t rc; */
+
+ dd("aborting parent...");
+
+ if (r == r->main || r->parent == NULL) {
+ return NGX_OK;
+ }
+
+ if (r->parent->postponed) {
+ dd("Found parent->postponed...");
+
+ saved_data = r->connection->data;
+ ppr = NULL;
+ for (pr = r->parent->postponed; pr->next; pr = pr->next) {
+ if (pr->request == NULL) {
+ continue;
+ }
+
+ if (pr->request == r) {
+ /* r->parent->postponed->next = pr; */
+ dd("found the current subrequest");
+ out = pr->out;
+ continue;
+ }
+
+ /* r->connection->data = pr->request; */
+ dd("finalizing the subrequest...");
+ ngx_http_upstream_create(pr->request);
+ pr->request->upstream = NULL;
+
+ if (ppr == NULL) {
+ r->parent->postponed = pr->next;
+ ppr = pr->next;
+ } else {
+ ppr->next = pr->next;
+ ppr = pr->next;
+ }
+ }
+ }
+
+ r->parent->postponed->next = NULL;
+
+ /*
+ r->connection->data = r->parent;
+ r->connection->buffered = 0;
+
+ if (out != NULL) {
+ dd("trying to send more stuffs for the parent");
+ ngx_http_output_filter(r->parent, out);
+ }
+ */
+
+ /* ngx_http_send_special(r->parent, NGX_HTTP_LAST); */
+
+ if (saved_data) {
+ r->connection->data = saved_data;
+ }
+
+ dd("terminating the parent request");
+
+ return ngx_http_echo_send_chain_link(r, ctx, NULL /* indicate LAST */);
+
+ /* ngx_http_upstream_create(r); */
+
+ /* ngx_http_finalize_request(r->parent, NGX_ERROR); */
+#endif
+
+ 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_str_t *uri;
+ ngx_str_t *user_args;
+ ngx_str_t args;
+ ngx_uint_t flags = 0;
+ 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) {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "echo_exec sees unsafe uri: \"%V\"",
+ uri);
+ return NGX_ERROR;
+ }
+
+ if (args.len > 0 && user_args == NULL) {
+ user_args = &args;
+ }
+
+ r->write_event_handler = ngx_http_request_empty_handler;
+
+ if (uri->data[0] == '@') {
+
+ if (user_args && user_args->len > 0) {
+ ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
+ "querystring %V ignored when exec'ing named "
+ "location %V", user_args, uri);
+ }
+
+#if 1
+ /* clear the modules contexts */
+ ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module);
+#endif
+
+ dd("named location: %.*s, c:%d", (int) uri->len, uri->data,
+ (int) r->main->count);
+
+ return ngx_http_named_location(r, uri);
+ }
+
+ return ngx_http_internal_redirect(r, uri, user_args);
+}
+
+
+static ngx_int_t
+ngx_http_echo_set_content_length_header(ngx_http_request_t *r, off_t len)
+{
+ ngx_table_elt_t *h, *header;
+ u_char *p;
+ ngx_list_part_t *part;
+ ngx_http_request_t *pr;
+ ngx_uint_t i;
+
+ r->headers_in.content_length_n = len;
+
+ if (ngx_list_init(&r->headers_in.headers, r->pool, 20,
+ sizeof(ngx_table_elt_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ h = ngx_list_push(&r->headers_in.headers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ h->key = ngx_http_echo_content_length_header_key;
+ h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);
+ if (h->lowcase_key == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
+
+ r->headers_in.content_length = h;
+
+ p = ngx_palloc(r->pool, NGX_OFF_T_LEN);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ h->value.data = p;
+
+ h->value.len = ngx_sprintf(h->value.data, "%O", len) - h->value.data;
+
+ h->hash = ngx_http_echo_content_length_hash;
+
+ dd("r content length: %.*s",
+ (int) r->headers_in.content_length->value.len,
+ r->headers_in.content_length->value.data);
+
+ pr = r->parent;
+
+ if (pr == NULL) {
+ return NGX_OK;
+ }
+
+ /* forward the parent request's all other request headers */
+
+ part = &pr->headers_in.headers.part;
+ header = part->elts;
+
+ for (i = 0; /* void */; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ header = part->elts;
+ i = 0;
+ }
+
+ if (header[i].key.len == sizeof("Content-Length") - 1
+ && ngx_strncasecmp(header[i].key.data, (u_char *) "Content-Length",
+ sizeof("Content-Length") - 1)
+ == 0)
+ {
+ continue;
+ }
+
+ h = ngx_list_push(&r->headers_in.headers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ *h = header[i];
+ }
+
+ /* XXX maybe we should set those built-in header slot in
+ * ngx_http_headers_in_t too? */
+
+ return NGX_OK;
+}