Mercurial > hg > nginx-vendor-0-8
comparison src/http/modules/ngx_http_ssi_filter.c @ 46:9f3205d496a0 NGINX_0_1_23
nginx 0.1.23
*) Feature: the ngx_http_ssi_filter_module and the ssi,
ssi_silent_errors, and ssi_min_file_chunk directives. The 'echo
var="HTTP_..." default=""' and 'echo var="REMOTE_ADDR"' commands are
supported.
*) Feature: the %request_time log parameter.
*) Feature: if the request has no the "Host" header line, then the
"proxy_preserve_host" directive set this header line to the first
server name of the "server_name" directive.
*) Bugfix: nginx could not be built on platforms different from i386,
amd64, sparc É ppc; bug appeared in 0.1.22.
*) Bugfix: the ngx_http_autoindex_module now shows the information not
about the symlink, but about file or directory it points to.
*) Bugfix: the %apache_length parameter logged the negative length of
the response header if the no response was transferred to a client.
author | Igor Sysoev <http://sysoev.ru> |
---|---|
date | Tue, 01 Mar 2005 00:00:00 +0300 |
parents | |
children | 6cfc63e68377 |
comparison
equal
deleted
inserted
replaced
45:09c688b472a7 | 46:9f3205d496a0 |
---|---|
1 | |
2 /* | |
3 * Copyright (C) Igor Sysoev | |
4 */ | |
5 | |
6 | |
7 #include <ngx_config.h> | |
8 #include <ngx_core.h> | |
9 #include <ngx_http.h> | |
10 | |
11 #define NGX_HTTP_SSI_MAX_PARAMS 16 | |
12 | |
13 #define NGX_HTTP_SSI_COMMAND_LEN 31 | |
14 #define NGX_HTTP_SSI_PARAM_LEN 31 | |
15 #define NGX_HTTP_SSI_PARAMS_N 4 | |
16 | |
17 #define NGX_HTTP_SSI_ERROR 1 | |
18 | |
19 | |
20 typedef struct { | |
21 ngx_flag_t enable; | |
22 ngx_flag_t silent_errors; | |
23 | |
24 size_t min_file_chunk; | |
25 size_t value_len; | |
26 } ngx_http_ssi_conf_t; | |
27 | |
28 | |
29 typedef struct { | |
30 ngx_buf_t *buf; | |
31 | |
32 u_char *pos; | |
33 u_char *copy_start; | |
34 u_char *copy_end; | |
35 | |
36 ngx_str_t command; | |
37 ngx_array_t params; | |
38 ngx_table_elt_t *param; | |
39 ngx_table_elt_t params_array[NGX_HTTP_SSI_PARAMS_N]; | |
40 | |
41 ngx_chain_t *in; | |
42 ngx_chain_t *out; | |
43 ngx_chain_t **last_out; | |
44 | |
45 ngx_uint_t state; | |
46 ngx_uint_t saved_state; | |
47 size_t saved; | |
48 size_t looked; | |
49 | |
50 size_t value_len; | |
51 } ngx_http_ssi_ctx_t; | |
52 | |
53 | |
54 typedef ngx_int_t (*ngx_http_ssi_command_pt) (ngx_http_request_t *r, | |
55 ngx_http_ssi_ctx_t *ctx, ngx_str_t **); | |
56 | |
57 | |
58 typedef struct { | |
59 ngx_str_t name; | |
60 ngx_uint_t index; | |
61 | |
62 ngx_uint_t mandatory; | |
63 } ngx_http_ssi_param_t; | |
64 | |
65 | |
66 typedef struct { | |
67 ngx_str_t name; | |
68 ngx_http_ssi_command_pt handler; | |
69 ngx_http_ssi_param_t *params; | |
70 | |
71 ngx_uint_t flush; /* unsigned flush:1; */ | |
72 } ngx_http_ssi_command_t; | |
73 | |
74 | |
75 typedef enum { | |
76 ssi_start_state = 0, | |
77 ssi_tag_state, | |
78 ssi_comment0_state, | |
79 ssi_comment1_state, | |
80 ssi_sharp_state, | |
81 ssi_precommand_state, | |
82 ssi_command_state, | |
83 ssi_preparam_state, | |
84 ssi_param_state, | |
85 ssi_preequal_state, | |
86 ssi_prevalue_state, | |
87 ssi_double_quoted_value_state, | |
88 ssi_quoted_value_state, | |
89 ssi_quoted_symbol_state, | |
90 ssi_postparam_state, | |
91 ssi_comment_end0_state, | |
92 ssi_comment_end1_state, | |
93 ssi_error_state, | |
94 ssi_error_end0_state, | |
95 ssi_error_end1_state | |
96 } ngx_http_ssi_state_e; | |
97 | |
98 | |
99 static ngx_int_t ngx_http_ssi_error(ngx_http_request_t *r, | |
100 ngx_http_ssi_ctx_t *ctx); | |
101 static ngx_int_t ngx_http_ssi_parse(ngx_http_request_t *r, | |
102 ngx_http_ssi_ctx_t *ctx); | |
103 | |
104 static ngx_int_t ngx_http_ssi_echo(ngx_http_request_t *r, | |
105 ngx_http_ssi_ctx_t *ctx, ngx_str_t **params); | |
106 | |
107 static void *ngx_http_ssi_create_conf(ngx_conf_t *cf); | |
108 static char *ngx_http_ssi_merge_conf(ngx_conf_t *cf, | |
109 void *parent, void *child); | |
110 static ngx_int_t ngx_http_ssi_filter_init(ngx_cycle_t *cycle); | |
111 | |
112 | |
113 static ngx_command_t ngx_http_ssi_filter_commands[] = { | |
114 | |
115 { ngx_string("ssi"), | |
116 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, | |
117 ngx_conf_set_flag_slot, | |
118 NGX_HTTP_LOC_CONF_OFFSET, | |
119 offsetof(ngx_http_ssi_conf_t, enable), | |
120 NULL }, | |
121 | |
122 { ngx_string("ssi_silent_errors"), | |
123 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, | |
124 ngx_conf_set_flag_slot, | |
125 NGX_HTTP_LOC_CONF_OFFSET, | |
126 offsetof(ngx_http_ssi_conf_t, silent_errors), | |
127 NULL }, | |
128 | |
129 { ngx_string("ssi_min_file_chunk"), | |
130 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, | |
131 ngx_conf_set_size_slot, | |
132 NGX_HTTP_LOC_CONF_OFFSET, | |
133 offsetof(ngx_http_ssi_conf_t, min_file_chunk), | |
134 NULL }, | |
135 | |
136 ngx_null_command | |
137 }; | |
138 | |
139 | |
140 | |
141 static ngx_http_module_t ngx_http_ssi_filter_module_ctx = { | |
142 NULL, /* pre conf */ | |
143 | |
144 NULL, /* create main configuration */ | |
145 NULL, /* init main configuration */ | |
146 | |
147 NULL, /* create server configuration */ | |
148 NULL, /* merge server configuration */ | |
149 | |
150 ngx_http_ssi_create_conf, /* create location configuration */ | |
151 ngx_http_ssi_merge_conf /* merge location configuration */ | |
152 }; | |
153 | |
154 | |
155 ngx_module_t ngx_http_ssi_filter_module = { | |
156 NGX_MODULE, | |
157 &ngx_http_ssi_filter_module_ctx, /* module context */ | |
158 ngx_http_ssi_filter_commands, /* module directives */ | |
159 NGX_HTTP_MODULE, /* module type */ | |
160 ngx_http_ssi_filter_init, /* init module */ | |
161 NULL /* init child */ | |
162 }; | |
163 | |
164 | |
165 static ngx_int_t (*ngx_http_next_header_filter) (ngx_http_request_t *r); | |
166 static ngx_int_t (*ngx_http_next_body_filter) (ngx_http_request_t *r, | |
167 ngx_chain_t *in); | |
168 | |
169 | |
170 static u_char ngx_http_ssi_string[] = "<!--"; | |
171 static u_char ngx_http_ssi_error_string[] = | |
172 "[an error occurred while processing the directive]"; | |
173 | |
174 static ngx_str_t ngx_http_ssi_none = ngx_string("(none)"); | |
175 | |
176 | |
177 #define NGX_HTTP_SSI_ECHO_VAR 0 | |
178 #define NGX_HTTP_SSI_ECHO_DEFAULT 1 | |
179 | |
180 static ngx_http_ssi_param_t ngx_http_ssi_echo_params[] = { | |
181 { ngx_string("var"), NGX_HTTP_SSI_ECHO_VAR, 1 }, | |
182 { ngx_string("default"), NGX_HTTP_SSI_ECHO_DEFAULT, 0 }, | |
183 { ngx_null_string, 0, 0 } | |
184 }; | |
185 | |
186 | |
187 static ngx_http_ssi_command_t ngx_http_ssi_commands[] = { | |
188 { ngx_string("echo"), ngx_http_ssi_echo, ngx_http_ssi_echo_params, 0 }, | |
189 { ngx_null_string, NULL, NULL, 0 } | |
190 }; | |
191 | |
192 | |
193 static ngx_int_t | |
194 ngx_http_ssi_header_filter(ngx_http_request_t *r) | |
195 { | |
196 ngx_http_ssi_ctx_t *ctx; | |
197 ngx_http_ssi_conf_t *conf; | |
198 | |
199 conf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module); | |
200 | |
201 if (!conf->enable) { | |
202 return ngx_http_next_header_filter(r); | |
203 } | |
204 | |
205 /* TODO: "text/html" -> custom types */ | |
206 | |
207 if (r->headers_out.content_type | |
208 && ngx_strncasecmp(r->headers_out.content_type->value.data, | |
209 "text/html", 5) != 0) | |
210 { | |
211 return ngx_http_next_header_filter(r); | |
212 } | |
213 | |
214 | |
215 if (!(ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_ssi_ctx_t)))) { | |
216 return NGX_ERROR; | |
217 } | |
218 | |
219 ngx_http_set_ctx(r, ctx, ngx_http_ssi_filter_module); | |
220 | |
221 | |
222 ctx->value_len = conf->value_len; | |
223 ctx->last_out = &ctx->out; | |
224 | |
225 ctx->params.elts = ctx->params_array; | |
226 ctx->params.size = sizeof(ngx_table_elt_t); | |
227 ctx->params.nalloc = NGX_HTTP_SSI_PARAMS_N; | |
228 ctx->params.pool = r->pool; | |
229 | |
230 r->headers_out.content_length_n = -1; | |
231 if (r->headers_out.content_length) { | |
232 r->headers_out.content_length->key.len = 0; | |
233 r->headers_out.content_length = NULL; | |
234 } | |
235 | |
236 r->headers_out.last_modified_time = -1; | |
237 if (r->headers_out.last_modified) { | |
238 r->headers_out.last_modified->key.len = 0; | |
239 r->headers_out.last_modified = NULL; | |
240 } | |
241 | |
242 r->filter_need_in_memory = 1; | |
243 r->filter_ssi_need_in_memory = 1; | |
244 | |
245 return ngx_http_next_header_filter(r); | |
246 } | |
247 | |
248 | |
249 static ngx_int_t | |
250 ngx_http_ssi_body_filter(ngx_http_request_t *r, ngx_chain_t *in) | |
251 { | |
252 ngx_int_t rc; | |
253 ngx_uint_t i; | |
254 ngx_buf_t *b; | |
255 ngx_chain_t *cl; | |
256 ngx_table_elt_t *param; | |
257 ngx_http_ssi_ctx_t *ctx; | |
258 ngx_http_ssi_conf_t *conf; | |
259 ngx_http_ssi_param_t *prm; | |
260 ngx_http_ssi_command_t *cmd; | |
261 ngx_str_t *params[NGX_HTTP_SSI_MAX_PARAMS]; | |
262 | |
263 ctx = ngx_http_get_module_ctx(r, ngx_http_ssi_filter_module); | |
264 | |
265 if (ctx == NULL || (in == NULL && ctx->in == NULL)) { | |
266 return ngx_http_next_body_filter(r, in); | |
267 } | |
268 | |
269 /* add the incoming chain to the chain ctx->in */ | |
270 | |
271 if (in) { | |
272 if (ngx_chain_add_copy(r->pool, &ctx->in, in) == NGX_ERROR) { | |
273 return NGX_ERROR; | |
274 } | |
275 } | |
276 | |
277 conf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module); | |
278 | |
279 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
280 "http ssi filter"); | |
281 | |
282 b = NULL; | |
283 | |
284 while (ctx->in) { | |
285 | |
286 ctx->buf = ctx->in->buf; | |
287 ctx->in = ctx->in->next; | |
288 ctx->pos = ctx->buf->pos; | |
289 | |
290 if (ctx->state == ssi_start_state) { | |
291 ctx->copy_start = ctx->pos; | |
292 ctx->copy_end = ctx->pos; | |
293 } | |
294 | |
295 while (ctx->pos < ctx->buf->last) { | |
296 | |
297 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
298 "saved: %d state: %d", ctx->saved, ctx->state); | |
299 | |
300 rc = ngx_http_ssi_parse(r, ctx); | |
301 | |
302 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
303 "parse: %d, looked: %d %p-%p", | |
304 rc, ctx->looked, ctx->copy_start, ctx->copy_end); | |
305 | |
306 if (rc == NGX_ERROR) { | |
307 return rc; | |
308 } | |
309 | |
310 if (ctx->copy_start != ctx->copy_end) { | |
311 | |
312 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
313 "saved: %d", ctx->saved); | |
314 | |
315 if (ctx->saved) { | |
316 if (!(b = ngx_calloc_buf(r->pool))) { | |
317 return NGX_ERROR; | |
318 } | |
319 | |
320 b->memory = 1; | |
321 b->pos = ngx_http_ssi_string; | |
322 b->last = ngx_http_ssi_string + ctx->saved; | |
323 | |
324 if (!(cl = ngx_alloc_chain_link(r->pool))) { | |
325 return NGX_ERROR; | |
326 } | |
327 | |
328 cl->buf = b; | |
329 *ctx->last_out = cl; | |
330 ctx->last_out = &cl->next; | |
331 | |
332 ctx->saved = 0; | |
333 } | |
334 | |
335 if (!(b = ngx_calloc_buf(r->pool))) { | |
336 return NGX_ERROR; | |
337 } | |
338 | |
339 ngx_memcpy(b, ctx->buf, sizeof(ngx_buf_t)); | |
340 | |
341 b->last_buf = 0; | |
342 b->pos = ctx->copy_start; | |
343 b->last = ctx->copy_end; | |
344 | |
345 if (b->in_file) { | |
346 | |
347 if (conf->min_file_chunk < (size_t) (b->last - b->pos)) { | |
348 b->file_last = b->file_pos + (b->last - b->start); | |
349 b->file_pos += b->pos - b->start; | |
350 | |
351 } else { | |
352 b->in_file = 0; | |
353 } | |
354 } | |
355 | |
356 if (!(cl = ngx_alloc_chain_link(r->pool))) { | |
357 return NGX_ERROR; | |
358 } | |
359 | |
360 cl->buf = b; | |
361 cl->next = NULL; | |
362 *ctx->last_out = cl; | |
363 ctx->last_out = &cl->next; | |
364 } | |
365 | |
366 if (ctx->state == ssi_start_state) { | |
367 ctx->copy_start = ctx->pos; | |
368 ctx->copy_end = ctx->pos; | |
369 | |
370 } else { | |
371 ctx->copy_start = NULL; | |
372 ctx->copy_end = NULL; | |
373 } | |
374 | |
375 if (rc == NGX_AGAIN) { | |
376 continue; | |
377 } | |
378 | |
379 | |
380 if (rc == NGX_OK) { | |
381 | |
382 for (cmd = ngx_http_ssi_commands; cmd->handler; cmd++) { | |
383 if (cmd->name.len == 0) { | |
384 cmd = (ngx_http_ssi_command_t *) cmd->handler; | |
385 } | |
386 | |
387 if (cmd->name.len != ctx->command.len | |
388 || ngx_strncmp(cmd->name.data, ctx->command.data, | |
389 ctx->command.len) != 0) | |
390 { | |
391 continue; | |
392 } | |
393 | |
394 break; | |
395 } | |
396 | |
397 if (cmd->name.len == 0) { | |
398 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
399 "invalid SSI command: \"%V\"", &ctx->command); | |
400 goto ssi_error; | |
401 } | |
402 | |
403 ngx_memzero(params, | |
404 NGX_HTTP_SSI_MAX_PARAMS * sizeof(ngx_str_t *)); | |
405 | |
406 param = ctx->params.elts; | |
407 | |
408 | |
409 for (i = 0; i < ctx->params.nelts; i++) { | |
410 | |
411 for (prm = cmd->params; prm->name.len; prm++) { | |
412 | |
413 if (param[i].key.len != prm->name.len | |
414 || ngx_strncmp(param[i].key.data, prm->name.data, | |
415 prm->name.len) != 0) | |
416 { | |
417 continue; | |
418 } | |
419 | |
420 if (params[prm->index]) { | |
421 ngx_log_error(NGX_LOG_ERR, | |
422 r->connection->log, 0, | |
423 "duplicate \"%V\" parameter " | |
424 "in \"%V\" SSI command", | |
425 ¶m[i].key, &ctx->command); | |
426 | |
427 goto ssi_error; | |
428 } | |
429 | |
430 params[prm->index] = ¶m[i].value; | |
431 | |
432 break; | |
433 } | |
434 | |
435 if (prm->name.len == 0) { | |
436 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
437 "invalid parameter name: \"%V\" " | |
438 "in \"%V\" SSI command", | |
439 ¶m[i].key, &ctx->command); | |
440 | |
441 goto ssi_error; | |
442 } | |
443 } | |
444 | |
445 for (prm = cmd->params; prm->name.len; prm++) { | |
446 if (prm->mandatory && params[prm->index] == 0) { | |
447 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
448 "mandatory \"%V\" parameter is absent " | |
449 "in \"%V\" SSI command", | |
450 &prm->name, &ctx->command); | |
451 | |
452 goto ssi_error; | |
453 } | |
454 } | |
455 | |
456 if (cmd->handler(r, ctx, params) == NGX_OK) { | |
457 continue; | |
458 } | |
459 } | |
460 | |
461 | |
462 /* rc == NGX_HTTP_SSI_ERROR */ | |
463 | |
464 ssi_error: | |
465 | |
466 if (conf->silent_errors) { | |
467 continue; | |
468 } | |
469 | |
470 if (!(b = ngx_calloc_buf(r->pool))) { | |
471 return NGX_ERROR; | |
472 } | |
473 | |
474 b->memory = 1; | |
475 b->pos = ngx_http_ssi_error_string; | |
476 b->last = ngx_http_ssi_error_string | |
477 + sizeof(ngx_http_ssi_error_string) - 1; | |
478 | |
479 if (!(cl = ngx_alloc_chain_link(r->pool))) { | |
480 return NGX_ERROR; | |
481 } | |
482 | |
483 cl->buf = b; | |
484 cl->next = NULL; | |
485 *ctx->last_out = cl; | |
486 ctx->last_out = &cl->next; | |
487 | |
488 continue; | |
489 } | |
490 | |
491 ctx->buf->pos = ctx->buf->last; | |
492 | |
493 if (b && ctx->buf->last_buf) { | |
494 b->last_buf = 1; | |
495 } | |
496 | |
497 ctx->saved = ctx->looked; | |
498 } | |
499 | |
500 if (ctx->out == NULL) { | |
501 return NGX_OK; | |
502 } | |
503 | |
504 rc = ngx_http_next_body_filter(r, ctx->out); | |
505 | |
506 ctx->out = NULL; | |
507 ctx->last_out = &ctx->out; | |
508 | |
509 return rc; | |
510 } | |
511 | |
512 | |
513 static ngx_int_t | |
514 ngx_http_ssi_parse(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx) | |
515 { | |
516 u_char *p, *last, *copy_end, ch; | |
517 size_t looked; | |
518 ngx_http_ssi_state_e state; | |
519 | |
520 state = ctx->state; | |
521 looked = ctx->looked; | |
522 last = ctx->buf->last; | |
523 copy_end = ctx->copy_end; | |
524 | |
525 for (p = ctx->pos; p < last; p++) { | |
526 | |
527 ch = *p; | |
528 | |
529 if (state == ssi_start_state) { | |
530 | |
531 /* the tight loop */ | |
532 | |
533 for ( /* void */ ; p < last; ch = *(++p)) { | |
534 if (ch != '<') { | |
535 continue; | |
536 } | |
537 | |
538 copy_end = p; | |
539 looked = 1; | |
540 state = ssi_tag_state; | |
541 | |
542 goto tag_started; | |
543 } | |
544 | |
545 ctx->pos = p; | |
546 ctx->looked = looked; | |
547 ctx->copy_end = p; | |
548 | |
549 if (ctx->copy_start == NULL) { | |
550 ctx->copy_start = ctx->buf->pos; | |
551 } | |
552 | |
553 return NGX_AGAIN; | |
554 | |
555 tag_started: | |
556 continue; | |
557 } | |
558 | |
559 switch (state) { | |
560 | |
561 case ssi_start_state: | |
562 break; | |
563 | |
564 case ssi_tag_state: | |
565 switch (ch) { | |
566 case '!': | |
567 looked = 2; | |
568 state = ssi_comment0_state; | |
569 break; | |
570 | |
571 case '<': | |
572 copy_end = p; | |
573 break; | |
574 | |
575 default: | |
576 copy_end = p; | |
577 looked = 0; | |
578 state = ssi_start_state; | |
579 break; | |
580 } | |
581 | |
582 break; | |
583 | |
584 case ssi_comment0_state: | |
585 switch (ch) { | |
586 case '-': | |
587 looked = 3; | |
588 state = ssi_comment1_state; | |
589 break; | |
590 | |
591 case '<': | |
592 copy_end = p; | |
593 looked = 1; | |
594 state = ssi_tag_state; | |
595 break; | |
596 | |
597 default: | |
598 copy_end = p; | |
599 looked = 0; | |
600 state = ssi_start_state; | |
601 break; | |
602 } | |
603 | |
604 break; | |
605 | |
606 case ssi_comment1_state: | |
607 switch (ch) { | |
608 case '-': | |
609 looked = 4; | |
610 state = ssi_sharp_state; | |
611 break; | |
612 | |
613 case '<': | |
614 copy_end = p; | |
615 looked = 1; | |
616 state = ssi_tag_state; | |
617 break; | |
618 | |
619 default: | |
620 copy_end = p; | |
621 looked = 0; | |
622 state = ssi_start_state; | |
623 break; | |
624 } | |
625 | |
626 break; | |
627 | |
628 case ssi_sharp_state: | |
629 switch (ch) { | |
630 case '#': | |
631 if (ctx->copy_start) { | |
632 ctx->saved = 0; | |
633 } | |
634 looked = 0; | |
635 state = ssi_precommand_state; | |
636 break; | |
637 | |
638 case '<': | |
639 copy_end = p; | |
640 looked = 1; | |
641 state = ssi_tag_state; | |
642 break; | |
643 | |
644 default: | |
645 copy_end = p; | |
646 looked = 0; | |
647 state = ssi_start_state; | |
648 break; | |
649 } | |
650 | |
651 break; | |
652 | |
653 case ssi_precommand_state: | |
654 switch (ch) { | |
655 case ' ': | |
656 case CR: | |
657 case LF: | |
658 case '\t': | |
659 break; | |
660 | |
661 default: | |
662 ctx->command.len = 1; | |
663 ctx->command.data = ngx_palloc(r->pool, | |
664 NGX_HTTP_SSI_COMMAND_LEN + 1); | |
665 if (ctx->command.data == NULL) { | |
666 return NGX_ERROR; | |
667 } | |
668 | |
669 ctx->command.data[0] = ch; | |
670 ctx->params.nelts = 0; | |
671 state = ssi_command_state; | |
672 break; | |
673 } | |
674 | |
675 break; | |
676 | |
677 case ssi_command_state: | |
678 switch (ch) { | |
679 case ' ': | |
680 case CR: | |
681 case LF: | |
682 case '\t': | |
683 state = ssi_preparam_state; | |
684 break; | |
685 | |
686 case '-': | |
687 state = ssi_comment_end0_state; | |
688 break; | |
689 | |
690 default: | |
691 ctx->command.data[ctx->command.len++] = ch; | |
692 | |
693 if (ctx->command.len == NGX_HTTP_SSI_COMMAND_LEN) { | |
694 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
695 "the \"%V\" SSI command is too long", | |
696 &ctx->command); | |
697 | |
698 state = ssi_error_state; | |
699 break; | |
700 } | |
701 } | |
702 | |
703 break; | |
704 | |
705 case ssi_preparam_state: | |
706 switch (ch) { | |
707 case ' ': | |
708 case CR: | |
709 case LF: | |
710 case '\t': | |
711 break; | |
712 | |
713 case '-': | |
714 state = ssi_comment_end0_state; | |
715 break; | |
716 | |
717 default: | |
718 if (!(ctx->param = ngx_array_push(&ctx->params))) { | |
719 return NGX_ERROR; | |
720 } | |
721 | |
722 ctx->param->key.len = 1; | |
723 ctx->param->key.data = ngx_palloc(r->pool, | |
724 NGX_HTTP_SSI_PARAM_LEN + 1); | |
725 if (ctx->param->key.data == NULL) { | |
726 return NGX_ERROR; | |
727 } | |
728 | |
729 ctx->param->key.data[0] = ch; | |
730 | |
731 ctx->param->value.len = 0; | |
732 ctx->param->value.data = ngx_palloc(r->pool, | |
733 ctx->value_len + 1); | |
734 if (ctx->param->value.data == NULL) { | |
735 return NGX_ERROR; | |
736 } | |
737 | |
738 state = ssi_param_state; | |
739 break; | |
740 } | |
741 | |
742 break; | |
743 | |
744 case ssi_param_state: | |
745 switch (ch) { | |
746 case ' ': | |
747 case CR: | |
748 case LF: | |
749 case '\t': | |
750 state = ssi_preequal_state; | |
751 break; | |
752 | |
753 case '=': | |
754 state = ssi_prevalue_state; | |
755 break; | |
756 | |
757 case '-': | |
758 state = ssi_error_end0_state; | |
759 | |
760 ctx->param->key.data[ctx->param->key.len++] = ch; | |
761 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
762 "invalid \"%V\" parameter in \"%V\" SSI command", | |
763 &ctx->param->key, &ctx->command); | |
764 break; | |
765 | |
766 default: | |
767 ctx->param->key.data[ctx->param->key.len++] = ch; | |
768 | |
769 if (ctx->param->key.len == NGX_HTTP_SSI_PARAM_LEN) { | |
770 state = ssi_error_state; | |
771 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
772 "too long \"%V\" parameter in " | |
773 "\"%V\" SSI command", | |
774 &ctx->param->key, &ctx->command); | |
775 break; | |
776 } | |
777 } | |
778 | |
779 break; | |
780 | |
781 case ssi_preequal_state: | |
782 switch (ch) { | |
783 case ' ': | |
784 case CR: | |
785 case LF: | |
786 case '\t': | |
787 break; | |
788 | |
789 case '=': | |
790 state = ssi_prevalue_state; | |
791 break; | |
792 | |
793 default: | |
794 if (ch == '-') { | |
795 state = ssi_error_end0_state; | |
796 } else { | |
797 state = ssi_error_state; | |
798 } | |
799 | |
800 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
801 "unexpected \"%c\" symbol after \"%V\" " | |
802 "parameter in \"%V\" SSI command", | |
803 ch, &ctx->param->key, &ctx->command); | |
804 break; | |
805 } | |
806 | |
807 break; | |
808 | |
809 case ssi_prevalue_state: | |
810 switch (ch) { | |
811 case ' ': | |
812 case CR: | |
813 case LF: | |
814 case '\t': | |
815 break; | |
816 | |
817 case '"': | |
818 state = ssi_double_quoted_value_state; | |
819 break; | |
820 | |
821 case '\'': | |
822 state = ssi_quoted_value_state; | |
823 break; | |
824 | |
825 default: | |
826 if (ch == '-') { | |
827 state = ssi_error_end0_state; | |
828 } else { | |
829 state = ssi_error_state; | |
830 } | |
831 | |
832 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
833 "unexpected \"%c\" symbol before value of " | |
834 "\"%V\" parameter in \"%V\" SSI command", | |
835 ch, &ctx->param->key, &ctx->command); | |
836 break; | |
837 } | |
838 | |
839 break; | |
840 | |
841 case ssi_double_quoted_value_state: | |
842 switch (ch) { | |
843 case '\\': | |
844 ctx->saved_state = ssi_double_quoted_value_state; | |
845 state = ssi_quoted_symbol_state; | |
846 break; | |
847 | |
848 case '"': | |
849 state = ssi_postparam_state; | |
850 break; | |
851 | |
852 default: | |
853 ctx->param->value.data[ctx->param->value.len++] = ch; | |
854 | |
855 if (ctx->param->value.len == ctx->value_len) { | |
856 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
857 "too long \"%V\" value of \"%V\" parameter " | |
858 "in \"%V\" SSI command", | |
859 &ctx->param->value, &ctx->param->key, | |
860 &ctx->command); | |
861 state = ssi_error_state; | |
862 break; | |
863 } | |
864 } | |
865 | |
866 break; | |
867 | |
868 case ssi_quoted_value_state: | |
869 switch (ch) { | |
870 case '\\': | |
871 ctx->saved_state = ssi_quoted_value_state; | |
872 state = ssi_quoted_symbol_state; | |
873 break; | |
874 | |
875 case '\'': | |
876 state = ssi_postparam_state; | |
877 break; | |
878 | |
879 default: | |
880 ctx->param->value.data[ctx->param->value.len++] = ch; | |
881 | |
882 if (ctx->param->value.len == ctx->value_len) { | |
883 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
884 "too long \"%V\" value of \"%V\" parameter " | |
885 "in \"%V\" SSI command", | |
886 &ctx->param->value, &ctx->param->key, | |
887 &ctx->command); | |
888 state = ssi_error_state; | |
889 break; | |
890 } | |
891 } | |
892 | |
893 break; | |
894 | |
895 case ssi_quoted_symbol_state: | |
896 ctx->param->value.data[ctx->param->value.len++] = ch; | |
897 | |
898 if (ctx->param->value.len == ctx->value_len) { | |
899 if (ctx->param->value.len == ctx->value_len) { | |
900 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
901 "too long \"%V\" value of \"%V\" parameter " | |
902 "in \"%V\" SSI command", | |
903 &ctx->param->value, &ctx->param->key, | |
904 &ctx->command); | |
905 state = ssi_error_state; | |
906 break; | |
907 } | |
908 } | |
909 | |
910 state = ctx->saved_state; | |
911 break; | |
912 | |
913 case ssi_postparam_state: | |
914 switch (ch) { | |
915 case ' ': | |
916 case CR: | |
917 case LF: | |
918 case '\t': | |
919 state = ssi_preparam_state; | |
920 break; | |
921 | |
922 case '-': | |
923 state = ssi_comment_end0_state; | |
924 break; | |
925 | |
926 default: | |
927 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
928 "unexpected \"%c\" symbol after \"%V\" value " | |
929 "of \"%V\" parameter in \"%V\" SSI command", | |
930 ch, &ctx->param->value, &ctx->param->key, | |
931 &ctx->command); | |
932 state = ssi_error_state; | |
933 break; | |
934 } | |
935 | |
936 break; | |
937 | |
938 case ssi_comment_end0_state: | |
939 switch (ch) { | |
940 case '-': | |
941 state = ssi_comment_end1_state; | |
942 break; | |
943 | |
944 default: | |
945 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
946 "unexpected \"%c\" symbol in \"%V\" SSI command", | |
947 ch, &ctx->command); | |
948 state = ssi_error_state; | |
949 break; | |
950 } | |
951 | |
952 break; | |
953 | |
954 case ssi_comment_end1_state: | |
955 switch (ch) { | |
956 case '>': | |
957 ctx->state = ssi_start_state; | |
958 ctx->pos = p + 1; | |
959 ctx->looked = looked; | |
960 ctx->copy_end = copy_end; | |
961 | |
962 if (ctx->copy_start == NULL && copy_end) { | |
963 ctx->copy_start = ctx->buf->pos; | |
964 } | |
965 | |
966 return NGX_OK; | |
967 | |
968 default: | |
969 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
970 "unexpected \"%c\" symbol in \"%V\" SSI command", | |
971 ch, &ctx->command); | |
972 state = ssi_error_state; | |
973 break; | |
974 } | |
975 | |
976 break; | |
977 | |
978 case ssi_error_state: | |
979 switch (ch) { | |
980 case '-': | |
981 state = ssi_error_end0_state; | |
982 break; | |
983 | |
984 default: | |
985 break; | |
986 } | |
987 | |
988 break; | |
989 | |
990 case ssi_error_end0_state: | |
991 switch (ch) { | |
992 case '-': | |
993 state = ssi_error_end1_state; | |
994 break; | |
995 | |
996 default: | |
997 state = ssi_error_state; | |
998 break; | |
999 } | |
1000 | |
1001 break; | |
1002 | |
1003 case ssi_error_end1_state: | |
1004 switch (ch) { | |
1005 case '>': | |
1006 ctx->state = ssi_start_state; | |
1007 ctx->pos = p + 1; | |
1008 ctx->looked = looked; | |
1009 ctx->copy_end = copy_end; | |
1010 | |
1011 if (ctx->copy_start == NULL && copy_end) { | |
1012 ctx->copy_start = ctx->buf->pos; | |
1013 } | |
1014 | |
1015 return NGX_HTTP_SSI_ERROR; | |
1016 | |
1017 default: | |
1018 state = ssi_error_state; | |
1019 break; | |
1020 } | |
1021 | |
1022 break; | |
1023 } | |
1024 } | |
1025 | |
1026 ctx->state = state; | |
1027 ctx->pos = p; | |
1028 ctx->looked = looked; | |
1029 | |
1030 ctx->copy_end = (state == ssi_start_state) ? p : copy_end; | |
1031 | |
1032 if (ctx->copy_start == NULL && ctx->copy_end) { | |
1033 ctx->copy_start = ctx->buf->pos; | |
1034 } | |
1035 | |
1036 return NGX_AGAIN; | |
1037 } | |
1038 | |
1039 | |
1040 static ngx_int_t | |
1041 ngx_http_ssi_echo(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, | |
1042 ngx_str_t **params) | |
1043 { | |
1044 u_char ch; | |
1045 ngx_uint_t i, n; | |
1046 ngx_buf_t *b; | |
1047 ngx_str_t *var, *value; | |
1048 ngx_chain_t *cl; | |
1049 ngx_list_part_t *part; | |
1050 ngx_table_elt_t *header; | |
1051 | |
1052 var = params[NGX_HTTP_SSI_ECHO_VAR]; | |
1053 value = NULL; | |
1054 | |
1055 if (var->len > 5 && ngx_strncmp(var->data, "HTTP_", 5) == 0) { | |
1056 | |
1057 part = &r->headers_in.headers.part; | |
1058 header = part->elts; | |
1059 | |
1060 for (i = 0; /* void */ ; i++) { | |
1061 | |
1062 if (i >= part->nelts) { | |
1063 if (part->next == NULL) { | |
1064 break; | |
1065 } | |
1066 | |
1067 part = part->next; | |
1068 header = part->elts; | |
1069 i = 0; | |
1070 } | |
1071 | |
1072 for (n = 0; n + 5 < var->len && n < header[i].key.len; n++) | |
1073 { | |
1074 ch = header[i].key.data[n]; | |
1075 | |
1076 if (ch >= 'a' && ch <= 'z') { | |
1077 ch &= ~0x20; | |
1078 | |
1079 } else if (ch == '-') { | |
1080 ch = '_'; | |
1081 } | |
1082 | |
1083 if (var->data[n + 5] != ch) { | |
1084 break; | |
1085 } | |
1086 } | |
1087 | |
1088 if (n + 5 == var->len) { | |
1089 value = &header[i].value; | |
1090 break; | |
1091 } | |
1092 } | |
1093 | |
1094 } else if (var->len == sizeof("REMOTE_ADDR") - 1 | |
1095 && ngx_strncmp(var->data, "REMOTE_ADDR", | |
1096 sizeof("REMOTE_ADDR") - 1) == 0) | |
1097 { | |
1098 value = &r->connection->addr_text; | |
1099 } | |
1100 | |
1101 | |
1102 if (value == NULL) { | |
1103 value = params[NGX_HTTP_SSI_ECHO_DEFAULT]; | |
1104 } | |
1105 | |
1106 if (value == NULL) { | |
1107 value = &ngx_http_ssi_none; | |
1108 | |
1109 } else if (value->len == 0) { | |
1110 return NGX_OK; | |
1111 } | |
1112 | |
1113 if (!(b = ngx_calloc_buf(r->pool))) { | |
1114 return NGX_HTTP_SSI_ERROR; | |
1115 } | |
1116 | |
1117 if (!(cl = ngx_alloc_chain_link(r->pool))) { | |
1118 return NGX_HTTP_SSI_ERROR; | |
1119 } | |
1120 | |
1121 b->memory = 1; | |
1122 b->pos = value->data; | |
1123 b->last = value->data + value->len; | |
1124 | |
1125 cl->buf = b; | |
1126 cl->next = NULL; | |
1127 *ctx->last_out = cl; | |
1128 ctx->last_out = &cl->next; | |
1129 | |
1130 return NGX_OK; | |
1131 } | |
1132 | |
1133 | |
1134 static void * | |
1135 ngx_http_ssi_create_conf(ngx_conf_t *cf) | |
1136 { | |
1137 ngx_http_ssi_conf_t *conf; | |
1138 | |
1139 if (!(conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssi_conf_t)))) { | |
1140 return NGX_CONF_ERROR; | |
1141 } | |
1142 | |
1143 conf->enable = NGX_CONF_UNSET; | |
1144 conf->silent_errors = NGX_CONF_UNSET; | |
1145 | |
1146 conf->min_file_chunk = NGX_CONF_UNSET_SIZE; | |
1147 conf->value_len = NGX_CONF_UNSET_SIZE; | |
1148 | |
1149 return conf; | |
1150 } | |
1151 | |
1152 | |
1153 static char * | |
1154 ngx_http_ssi_merge_conf(ngx_conf_t *cf, void *parent, void *child) | |
1155 { | |
1156 ngx_http_ssi_conf_t *prev = parent; | |
1157 ngx_http_ssi_conf_t *conf = child; | |
1158 | |
1159 ngx_conf_merge_value(conf->enable, prev->enable, 0); | |
1160 ngx_conf_merge_value(conf->silent_errors, prev->silent_errors, 0); | |
1161 | |
1162 ngx_conf_merge_size_value(conf->min_file_chunk, prev->min_file_chunk, 1024); | |
1163 ngx_conf_merge_size_value(conf->value_len, prev->value_len, 256); | |
1164 | |
1165 return NGX_CONF_OK; | |
1166 } | |
1167 | |
1168 | |
1169 static ngx_int_t | |
1170 ngx_http_ssi_filter_init(ngx_cycle_t *cycle) | |
1171 { | |
1172 ngx_http_next_header_filter = ngx_http_top_header_filter; | |
1173 ngx_http_top_header_filter = ngx_http_ssi_header_filter; | |
1174 | |
1175 ngx_http_next_body_filter = ngx_http_top_body_filter; | |
1176 ngx_http_top_body_filter = ngx_http_ssi_body_filter; | |
1177 | |
1178 return NGX_OK; | |
1179 } |