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 &param[i].key, &ctx->command);
426
427 goto ssi_error;
428 }
429
430 params[prm->index] = &param[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 &param[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 }