comparison ngx_http_compose_filter_module.c @ 1:ba5471a3c988

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.
author Maxim Dounin <mdounin@mdounin.ru>
date Tue, 15 Jul 2008 05:38:48 +0400
parents 6535d94ae07d
children ed63d0bd4d14
comparison
equal deleted inserted replaced
0:6535d94ae07d 1:ba5471a3c988
9 9
10 10
11 typedef struct { 11 typedef struct {
12 ngx_flag_t enable; 12 ngx_flag_t enable;
13 } ngx_http_compose_conf_t; 13 } ngx_http_compose_conf_t;
14
15
16 typedef struct {
17 ngx_uint_t done;
18 ngx_array_t parts;
19 } ngx_http_compose_ctx_t;
14 20
15 21
16 static void *ngx_http_compose_create_conf(ngx_conf_t *cf); 22 static void *ngx_http_compose_create_conf(ngx_conf_t *cf);
17 static char *ngx_http_compose_merge_conf(ngx_conf_t *cf, void *parent, 23 static char *ngx_http_compose_merge_conf(ngx_conf_t *cf, void *parent,
18 void *child); 24 void *child);
19 static ngx_int_t ngx_http_compose_init(ngx_conf_t *cf); 25 static ngx_int_t ngx_http_compose_init(ngx_conf_t *cf);
26 static ngx_int_t ngx_http_compose_body_init(ngx_conf_t *cf);
20 27
21 28
22 static ngx_command_t ngx_http_compose_commands[] = { 29 static ngx_command_t ngx_http_compose_commands[] = {
23 30
24 { ngx_string("compose"), 31 { ngx_string("compose"),
61 NULL, /* exit master */ 68 NULL, /* exit master */
62 NGX_MODULE_V1_PADDING 69 NGX_MODULE_V1_PADDING
63 }; 70 };
64 71
65 72
73 static ngx_http_module_t ngx_http_compose_body_module_ctx = {
74 NULL, /* preconfiguration */
75 ngx_http_compose_body_init, /* postconfiguration */
76
77 NULL, /* create main configuration */
78 NULL, /* init main configuration */
79
80 NULL, /* create server configuration */
81 NULL, /* merge server configuration */
82
83 NULL, /* create location configuration */
84 NULL /* merge location configuration */
85 };
86
87
88 ngx_module_t ngx_http_compose_body_filter_module = {
89 NGX_MODULE_V1,
90 &ngx_http_compose_body_module_ctx, /* module context */
91 NULL, /* module directives */
92 NGX_HTTP_MODULE, /* module type */
93 NULL, /* init master */
94 NULL, /* init module */
95 NULL, /* init process */
96 NULL, /* init thread */
97 NULL, /* exit thread */
98 NULL, /* exit process */
99 NULL, /* exit master */
100 NGX_MODULE_V1_PADDING
101 };
102
103
66 static ngx_http_output_header_filter_pt ngx_http_next_header_filter; 104 static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
67 static ngx_http_output_body_filter_pt ngx_http_next_body_filter; 105 static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
68 106
69 107
70 static ngx_int_t 108 static ngx_int_t
71 ngx_http_compose_header_filter(ngx_http_request_t *r) 109 ngx_http_compose_header_filter(ngx_http_request_t *r)
72 { 110 {
111 ngx_uint_t i;
112 ngx_str_t *uri;
113 ngx_list_part_t *part;
114 ngx_table_elt_t *header;
73 ngx_http_compose_conf_t *conf; 115 ngx_http_compose_conf_t *conf;
116 ngx_http_compose_ctx_t *ctx;
74 117
75 conf = ngx_http_get_module_loc_conf(r, ngx_http_compose_filter_module); 118 conf = ngx_http_get_module_loc_conf(r, ngx_http_compose_filter_module);
76 119
77 if (!conf->enable) { 120 if (!conf->enable) {
78 return ngx_http_next_header_filter(r); 121 return ngx_http_next_header_filter(r);
79 } 122 }
80 123
81 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 124 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
82 "compose header filter"); 125 "compose header filter");
83 126
127 /* create context */
128
129 ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_compose_ctx_t));
130 if (ctx == NULL) {
131 return NGX_ERROR;
132 }
133
134 if (ngx_array_init(&ctx->parts, r->pool, 1, sizeof(ngx_str_t))
135 == NGX_ERROR)
136 {
137 return NGX_ERROR;
138 }
139
140
141 /*
142 * Collect all X-Compose headers (or combined one?), store in context
143 * for our body filter to make actual subrequests. Hide them from the
144 * response.
145 */
146
147 part = &r->headers_out.headers.part;
148 header = part->elts;
149
150 for (i = 0; /* void */; i++) {
151
152 if (i >= part->nelts) {
153 if (part->next == NULL) {
154 break;
155 }
156
157 part = part->next;
158 header = part->elts;
159 i = 0;
160 }
161
162 if (header[i].hash == 0) {
163 continue;
164 }
165
166 if (header[i].key.len == sizeof("X-Compose-Length") - 1
167 && ngx_strncasecmp(header[i].key.data, "X-Compose-Length",
168 sizeof("X-Compose-Length") - 1)
169 == 0)
170 {
171 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
172 "compose body filter: bingo, %V, %V",
173 &header[i].key, &header[i].value);
174
175 header[i].hash = 0;
176
177 r->headers_out.content_length_n = ngx_atoof(header[i].value.data,
178 header[i].value.len);
179
180 if (r->headers_out.content_length) {
181 r->headers_out.content_length->hash = 0;
182 r->headers_out.content_length = NULL;
183 }
184 }
185
186 if (header[i].key.len == sizeof("X-Compose") - 1
187 && ngx_strncasecmp(header[i].key.data, "X-Compose",
188 sizeof("X-Compose") - 1)
189 == 0)
190 {
191 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
192 "compose body filter: bingo, %V, %V",
193 &header[i].key, &header[i].value);
194
195 header[i].hash = 0;
196
197 /*
198 * XXX multiple headers with the same name must be combinable,
199 * see RFC 2616 4.2 Message Headers
200 */
201
202 uri = ngx_array_push(&ctx->parts);
203 if (uri == NULL) {
204 return NGX_ERROR;
205 }
206
207 *uri = header[i].value;
208 }
209 }
210
211
212 ngx_http_set_ctx(r, ctx, ngx_http_compose_filter_module);
213
84 return ngx_http_next_header_filter(r); 214 return ngx_http_next_header_filter(r);
85 } 215 }
86 216
87 217
88 static ngx_int_t 218 static ngx_int_t
89 ngx_http_compose_body_filter(ngx_http_request_t *r, ngx_chain_t *in) 219 ngx_http_compose_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
90 { 220 {
221 ngx_str_t *uri, args;
222 ngx_int_t rc;
223 ngx_uint_t i, flags;
224 ngx_http_request_t *sr;
225 ngx_http_compose_ctx_t *ctx;
226
91 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 227 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
92 "compose body filter"); 228 "compose body filter");
93 229
94 return ngx_http_next_body_filter(r, in); 230 ctx = ngx_http_get_module_ctx(r, ngx_http_compose_filter_module);
231
232 if (ctx == NULL) {
233 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
234 "compose body filter: no ctx");
235 return ngx_http_next_body_filter(r, in);
236 }
237
238 if (ctx->done) {
239 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
240 "compose body filter: done");
241 /* XXX wrong: should skip data instead */
242 return ngx_http_next_body_filter(r, in);
243 }
244
245 ctx->done = 1;
246
247 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
248 "compose body filter, doing work");
249
250 /*
251 * Ignore body that comes to us, replace it with subrequests.
252 */
253
254 uri = ctx->parts.elts;
255
256 for (i = 0; i < ctx->parts.nelts; i++) {
257
258 args.len = 0;
259 args.data = NULL;
260 flags = 0;
261
262 if (ngx_http_parse_unsafe_uri(r, &uri[i], &args, &flags) != NGX_OK) {
263 return NGX_ERROR;
264 }
265
266 rc = ngx_http_subrequest(r, &uri[i], &args, &sr, NULL, flags);
267
268 if (rc == NGX_ERROR || rc == NGX_DONE) {
269 return rc;
270 }
271 }
272
273 for ( ; in; in = in->next) {
274 in->buf->pos = in->buf->last;
275 in->buf->last_buf = 0;
276 }
277
278 /*
279 * XXX: what to do if non-static data? probably we should use post
280 * subrequest hook instead
281 */
282
283 return ngx_http_send_special(r, NGX_HTTP_LAST);
95 } 284 }
96 285
97 286
98 static void * 287 static void *
99 ngx_http_compose_create_conf(ngx_conf_t *cf) 288 ngx_http_compose_create_conf(ngx_conf_t *cf)
127 ngx_http_compose_init(ngx_conf_t *cf) 316 ngx_http_compose_init(ngx_conf_t *cf)
128 { 317 {
129 ngx_http_next_header_filter = ngx_http_top_header_filter; 318 ngx_http_next_header_filter = ngx_http_top_header_filter;
130 ngx_http_top_header_filter = ngx_http_compose_header_filter; 319 ngx_http_top_header_filter = ngx_http_compose_header_filter;
131 320
321 return NGX_OK;
322 }
323
324
325 static ngx_int_t
326 ngx_http_compose_body_init(ngx_conf_t *cf)
327 {
132 ngx_http_next_body_filter = ngx_http_top_body_filter; 328 ngx_http_next_body_filter = ngx_http_top_body_filter;
133 ngx_http_top_body_filter = ngx_http_compose_body_filter; 329 ngx_http_top_body_filter = ngx_http_compose_body_filter;
134 330
135 return NGX_OK; 331 return NGX_OK;
136 } 332 }