# HG changeset patch # User Maxim Dounin # Date 1216085928 -14400 # Node ID ba5471a3c9883019fde64f95f37d82d143566b00 # Parent 6535d94ae07db6685c3920a38db4258b03ba9f54 First working code, still in progress. Body filtering isn't complete. Notably, it probably will fail with complex body replies and with non-static subrequests. diff --git a/config b/config --- a/config +++ b/config @@ -3,8 +3,11 @@ ngx_addon_name="ngx_http_compose_filter_module" +HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES \ + ngx_http_compose_filter_module" + HTTP_RANGE_BODY_FILTER_MODULE="$HTTP_RANGE_BODY_FILTER_MODULE \ - ngx_http_compose_filter_module" + ngx_http_compose_body_filter_module" NGX_ADDON_SRCS="$NGX_ADDON_SRCS \ $ngx_addon_dir/ngx_http_compose_filter_module.c" diff --git a/ngx_http_compose_filter_module.c b/ngx_http_compose_filter_module.c --- a/ngx_http_compose_filter_module.c +++ b/ngx_http_compose_filter_module.c @@ -13,10 +13,17 @@ typedef struct { } ngx_http_compose_conf_t; +typedef struct { + ngx_uint_t done; + ngx_array_t parts; +} ngx_http_compose_ctx_t; + + static void *ngx_http_compose_create_conf(ngx_conf_t *cf); static char *ngx_http_compose_merge_conf(ngx_conf_t *cf, void *parent, void *child); static ngx_int_t ngx_http_compose_init(ngx_conf_t *cf); +static ngx_int_t ngx_http_compose_body_init(ngx_conf_t *cf); static ngx_command_t ngx_http_compose_commands[] = { @@ -63,6 +70,37 @@ ngx_module_t ngx_http_compose_filter_mo }; +static ngx_http_module_t ngx_http_compose_body_module_ctx = { + NULL, /* preconfiguration */ + ngx_http_compose_body_init, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + NULL, /* create location configuration */ + NULL /* merge location configuration */ +}; + + +ngx_module_t ngx_http_compose_body_filter_module = { + NGX_MODULE_V1, + &ngx_http_compose_body_module_ctx, /* module context */ + NULL, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + static ngx_http_output_header_filter_pt ngx_http_next_header_filter; static ngx_http_output_body_filter_pt ngx_http_next_body_filter; @@ -70,7 +108,12 @@ static ngx_http_output_body_filter_pt static ngx_int_t ngx_http_compose_header_filter(ngx_http_request_t *r) { + ngx_uint_t i; + ngx_str_t *uri; + ngx_list_part_t *part; + ngx_table_elt_t *header; ngx_http_compose_conf_t *conf; + ngx_http_compose_ctx_t *ctx; conf = ngx_http_get_module_loc_conf(r, ngx_http_compose_filter_module); @@ -81,6 +124,93 @@ ngx_http_compose_header_filter(ngx_http_ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "compose header filter"); + /* create context */ + + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_compose_ctx_t)); + if (ctx == NULL) { + return NGX_ERROR; + } + + if (ngx_array_init(&ctx->parts, r->pool, 1, sizeof(ngx_str_t)) + == NGX_ERROR) + { + return NGX_ERROR; + } + + + /* + * Collect all X-Compose headers (or combined one?), store in context + * for our body filter to make actual subrequests. Hide them from the + * response. + */ + + part = &r->headers_out.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].hash == 0) { + continue; + } + + if (header[i].key.len == sizeof("X-Compose-Length") - 1 + && ngx_strncasecmp(header[i].key.data, "X-Compose-Length", + sizeof("X-Compose-Length") - 1) + == 0) + { + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "compose body filter: bingo, %V, %V", + &header[i].key, &header[i].value); + + header[i].hash = 0; + + r->headers_out.content_length_n = ngx_atoof(header[i].value.data, + header[i].value.len); + + if (r->headers_out.content_length) { + r->headers_out.content_length->hash = 0; + r->headers_out.content_length = NULL; + } + } + + if (header[i].key.len == sizeof("X-Compose") - 1 + && ngx_strncasecmp(header[i].key.data, "X-Compose", + sizeof("X-Compose") - 1) + == 0) + { + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "compose body filter: bingo, %V, %V", + &header[i].key, &header[i].value); + + header[i].hash = 0; + + /* + * XXX multiple headers with the same name must be combinable, + * see RFC 2616 4.2 Message Headers + */ + + uri = ngx_array_push(&ctx->parts); + if (uri == NULL) { + return NGX_ERROR; + } + + *uri = header[i].value; + } + } + + + ngx_http_set_ctx(r, ctx, ngx_http_compose_filter_module); + return ngx_http_next_header_filter(r); } @@ -88,10 +218,69 @@ ngx_http_compose_header_filter(ngx_http_ static ngx_int_t ngx_http_compose_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { + ngx_str_t *uri, args; + ngx_int_t rc; + ngx_uint_t i, flags; + ngx_http_request_t *sr; + ngx_http_compose_ctx_t *ctx; + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "compose body filter"); - return ngx_http_next_body_filter(r, in); + ctx = ngx_http_get_module_ctx(r, ngx_http_compose_filter_module); + + if (ctx == NULL) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "compose body filter: no ctx"); + return ngx_http_next_body_filter(r, in); + } + + if (ctx->done) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "compose body filter: done"); + /* XXX wrong: should skip data instead */ + return ngx_http_next_body_filter(r, in); + } + + ctx->done = 1; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "compose body filter, doing work"); + + /* + * Ignore body that comes to us, replace it with subrequests. + */ + + uri = ctx->parts.elts; + + for (i = 0; i < ctx->parts.nelts; i++) { + + args.len = 0; + args.data = NULL; + flags = 0; + + if (ngx_http_parse_unsafe_uri(r, &uri[i], &args, &flags) != NGX_OK) { + return NGX_ERROR; + } + + rc = ngx_http_subrequest(r, &uri[i], &args, &sr, NULL, flags); + + if (rc == NGX_ERROR || rc == NGX_DONE) { + return rc; + } + } + + for ( ; in; in = in->next) { + in->buf->pos = in->buf->last; + in->buf->last_buf = 0; + } + + /* + * XXX: what to do if non-static data? probably we should use post + * subrequest hook instead + */ + + return ngx_http_send_special(r, NGX_HTTP_LAST); } @@ -129,6 +318,13 @@ ngx_http_compose_init(ngx_conf_t *cf) ngx_http_next_header_filter = ngx_http_top_header_filter; ngx_http_top_header_filter = ngx_http_compose_header_filter; + return NGX_OK; +} + + +static ngx_int_t +ngx_http_compose_body_init(ngx_conf_t *cf) +{ ngx_http_next_body_filter = ngx_http_top_body_filter; ngx_http_top_body_filter = ngx_http_compose_body_filter;