Mercurial > hg > ngx_http_compose_filter_module
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 } |