comparison src/http/modules/ngx_http_ssi_filter_module.c @ 50:72eb30262aac NGINX_0_1_25

nginx 0.1.25 *) Bugfix: nginx did run on Linux parisc. *) Feature: nginx now does not start under FreeBSD if the sysctl kern.ipc.somaxconn value is too big. *) Bugfix: if a request was internally redirected by the ngx_http_index_module module to the ngx_http_proxy_module or ngx_http_fastcgi_module modules, then the index file was not closed after request completion. *) Feature: the "proxy_pass" can be used in location with regular expression. *) Feature: the ngx_http_rewrite_filter_module module supports the condition like "if ($HTTP_USER_AGENT ~ MSIE)". *) Bugfix: nginx started too slow if the large number of addresses and text values were used in the "geo" directive. *) Change: a variable name must be declared as "$name" in the "geo" directive. The previous variant without "$" is still supported, but will be removed soon. *) Feature: the "%{VARIABLE}v" logging parameter. *) Feature: the "set $name value" directive. *) Bugfix: gcc 4.0 compatibility. *) Feature: the --with-openssl-opt=OPTIONS autoconfiguration directive.
author Igor Sysoev <http://sysoev.ru>
date Sat, 19 Mar 2005 00:00:00 +0300
parents src/http/modules/ngx_http_ssi_filter.c@6cfc63e68377
children b55cbf18157e
comparison
equal deleted inserted replaced
49:93dabbc9efb9 50:72eb30262aac
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 ngx_chain_t *busy;
45 ngx_chain_t *free;
46
47 ngx_uint_t state;
48 ngx_uint_t saved_state;
49 size_t saved;
50 size_t looked;
51
52 size_t value_len;
53 } ngx_http_ssi_ctx_t;
54
55
56 typedef ngx_int_t (*ngx_http_ssi_command_pt) (ngx_http_request_t *r,
57 ngx_http_ssi_ctx_t *ctx, ngx_str_t **);
58
59
60 typedef struct {
61 ngx_str_t name;
62 ngx_uint_t index;
63
64 ngx_uint_t mandatory;
65 } ngx_http_ssi_param_t;
66
67
68 typedef struct {
69 ngx_str_t name;
70 ngx_http_ssi_command_pt handler;
71 ngx_http_ssi_param_t *params;
72
73 ngx_uint_t flush; /* unsigned flush:1; */
74 } ngx_http_ssi_command_t;
75
76
77 typedef enum {
78 ssi_start_state = 0,
79 ssi_tag_state,
80 ssi_comment0_state,
81 ssi_comment1_state,
82 ssi_sharp_state,
83 ssi_precommand_state,
84 ssi_command_state,
85 ssi_preparam_state,
86 ssi_param_state,
87 ssi_preequal_state,
88 ssi_prevalue_state,
89 ssi_double_quoted_value_state,
90 ssi_quoted_value_state,
91 ssi_quoted_symbol_state,
92 ssi_postparam_state,
93 ssi_comment_end0_state,
94 ssi_comment_end1_state,
95 ssi_error_state,
96 ssi_error_end0_state,
97 ssi_error_end1_state
98 } ngx_http_ssi_state_e;
99
100
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 process */
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 ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_ssi_ctx_t));
216 if (ctx == NULL) {
217 return NGX_ERROR;
218 }
219
220 ngx_http_set_ctx(r, ctx, ngx_http_ssi_filter_module);
221
222
223 ctx->value_len = conf->value_len;
224 ctx->last_out = &ctx->out;
225
226 ctx->params.elts = ctx->params_array;
227 ctx->params.size = sizeof(ngx_table_elt_t);
228 ctx->params.nalloc = NGX_HTTP_SSI_PARAMS_N;
229 ctx->params.pool = r->pool;
230
231 r->headers_out.content_length_n = -1;
232 if (r->headers_out.content_length) {
233 r->headers_out.content_length->key.len = 0;
234 r->headers_out.content_length = NULL;
235 }
236
237 r->headers_out.last_modified_time = -1;
238 if (r->headers_out.last_modified) {
239 r->headers_out.last_modified->key.len = 0;
240 r->headers_out.last_modified = NULL;
241 }
242
243 r->filter_need_in_memory = 1;
244 r->filter_ssi_need_in_memory = 1;
245
246 return ngx_http_next_header_filter(r);
247 }
248
249
250 static ngx_int_t
251 ngx_http_ssi_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
252 {
253 ngx_int_t rc;
254 ngx_uint_t i;
255 ngx_buf_t *b;
256 ngx_chain_t *cl;
257 ngx_table_elt_t *param;
258 ngx_http_ssi_ctx_t *ctx;
259 ngx_http_ssi_conf_t *conf;
260 ngx_http_ssi_param_t *prm;
261 ngx_http_ssi_command_t *cmd;
262 ngx_str_t *params[NGX_HTTP_SSI_MAX_PARAMS];
263
264 ctx = ngx_http_get_module_ctx(r, ngx_http_ssi_filter_module);
265
266 if (ctx == NULL || (in == NULL && ctx->in == NULL && ctx->busy == NULL)) {
267 return ngx_http_next_body_filter(r, in);
268 }
269
270 /* add the incoming chain to the chain ctx->in */
271
272 if (in) {
273 if (ngx_chain_add_copy(r->pool, &ctx->in, in) == NGX_ERROR) {
274 return NGX_ERROR;
275 }
276 }
277
278 conf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module);
279
280 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
281 "http ssi filter");
282
283 while (ctx->in || ctx->buf) {
284
285 if (ctx->buf == NULL ){
286 ctx->buf = ctx->in->buf;
287 ctx->in = ctx->in->next;
288 ctx->pos = ctx->buf->pos;
289 }
290
291 if (ctx->state == ssi_start_state) {
292 ctx->copy_start = ctx->pos;
293 ctx->copy_end = ctx->pos;
294 }
295
296 b = NULL;
297
298 while (ctx->pos < ctx->buf->last) {
299
300 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
301 "saved: %d state: %d", ctx->saved, ctx->state);
302
303 rc = ngx_http_ssi_parse(r, ctx);
304
305 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
306 "parse: %d, looked: %d %p-%p",
307 rc, ctx->looked, ctx->copy_start, ctx->copy_end);
308
309 if (rc == NGX_ERROR) {
310 return rc;
311 }
312
313 if (ctx->copy_start != ctx->copy_end) {
314
315 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
316 "saved: %d", ctx->saved);
317
318 if (ctx->saved) {
319
320 if (ctx->free) {
321 cl = ctx->free;
322 ctx->free = ctx->free->next;
323 b = cl->buf;
324 ngx_memzero(b, sizeof(ngx_buf_t));
325
326 } else {
327 b = ngx_calloc_buf(r->pool);
328 if (b == NULL) {
329 return NGX_ERROR;
330 }
331
332 cl = ngx_alloc_chain_link(r->pool);
333 if (cl == NULL) {
334 return NGX_ERROR;
335 }
336
337 cl->buf = b;
338 }
339
340 b->memory = 1;
341 b->pos = ngx_http_ssi_string;
342 b->last = ngx_http_ssi_string + ctx->saved;
343
344 *ctx->last_out = cl;
345 ctx->last_out = &cl->next;
346
347 ctx->saved = 0;
348 }
349
350 if (ctx->free) {
351 cl = ctx->free;
352 ctx->free = ctx->free->next;
353 b = cl->buf;
354
355 } else {
356 b = ngx_alloc_buf(r->pool);
357 if (b == NULL) {
358 return NGX_ERROR;
359 }
360
361 cl = ngx_alloc_chain_link(r->pool);
362 if (cl == NULL) {
363 return NGX_ERROR;
364 }
365
366 cl->buf = b;
367 }
368
369 ngx_memcpy(b, ctx->buf, sizeof(ngx_buf_t));
370
371 b->last_buf = 0;
372 b->recycled = 0;
373 b->pos = ctx->copy_start;
374 b->last = ctx->copy_end;
375
376 if (b->in_file) {
377
378 if (conf->min_file_chunk < (size_t) (b->last - b->pos)) {
379 b->file_last = b->file_pos + (b->last - b->start);
380 b->file_pos += b->pos - b->start;
381
382 } else {
383 b->in_file = 0;
384 }
385 }
386
387 cl->next = NULL;
388 *ctx->last_out = cl;
389 ctx->last_out = &cl->next;
390 }
391
392 if (ctx->state == ssi_start_state) {
393 ctx->copy_start = ctx->pos;
394 ctx->copy_end = ctx->pos;
395
396 } else {
397 ctx->copy_start = NULL;
398 ctx->copy_end = NULL;
399 }
400
401 if (rc == NGX_AGAIN) {
402 continue;
403 }
404
405
406 if (rc == NGX_OK) {
407
408 for (cmd = ngx_http_ssi_commands; cmd->handler; cmd++) {
409 if (cmd->name.len == 0) {
410 cmd = (ngx_http_ssi_command_t *) cmd->handler;
411 }
412
413 if (cmd->name.len != ctx->command.len
414 || ngx_strncmp(cmd->name.data, ctx->command.data,
415 ctx->command.len) != 0)
416 {
417 continue;
418 }
419
420 break;
421 }
422
423 if (cmd->name.len == 0) {
424 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
425 "invalid SSI command: \"%V\"", &ctx->command);
426 goto ssi_error;
427 }
428
429 ngx_memzero(params,
430 NGX_HTTP_SSI_MAX_PARAMS * sizeof(ngx_str_t *));
431
432 param = ctx->params.elts;
433
434
435 for (i = 0; i < ctx->params.nelts; i++) {
436
437 for (prm = cmd->params; prm->name.len; prm++) {
438
439 if (param[i].key.len != prm->name.len
440 || ngx_strncmp(param[i].key.data, prm->name.data,
441 prm->name.len) != 0)
442 {
443 continue;
444 }
445
446 if (params[prm->index]) {
447 ngx_log_error(NGX_LOG_ERR,
448 r->connection->log, 0,
449 "duplicate \"%V\" parameter "
450 "in \"%V\" SSI command",
451 &param[i].key, &ctx->command);
452
453 goto ssi_error;
454 }
455
456 params[prm->index] = &param[i].value;
457
458 break;
459 }
460
461 if (prm->name.len == 0) {
462 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
463 "invalid parameter name: \"%V\" "
464 "in \"%V\" SSI command",
465 &param[i].key, &ctx->command);
466
467 goto ssi_error;
468 }
469 }
470
471 for (prm = cmd->params; prm->name.len; prm++) {
472 if (prm->mandatory && params[prm->index] == 0) {
473 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
474 "mandatory \"%V\" parameter is absent "
475 "in \"%V\" SSI command",
476 &prm->name, &ctx->command);
477
478 goto ssi_error;
479 }
480 }
481
482 if (cmd->handler(r, ctx, params) == NGX_OK) {
483 continue;
484 }
485 }
486
487
488 /* rc == NGX_HTTP_SSI_ERROR */
489
490 ssi_error:
491
492 if (conf->silent_errors) {
493 continue;
494 }
495
496 if (ctx->free) {
497 cl = ctx->free;
498 ctx->free = ctx->free->next;
499 b = cl->buf;
500 ngx_memzero(b, sizeof(ngx_buf_t));
501
502 } else {
503 b = ngx_calloc_buf(r->pool);
504 if (b == NULL) {
505 return NGX_ERROR;
506 }
507
508 cl = ngx_alloc_chain_link(r->pool);
509 if (cl == NULL) {
510 return NGX_ERROR;
511 }
512
513 cl->buf = b;
514 }
515
516 b->memory = 1;
517 b->pos = ngx_http_ssi_error_string;
518 b->last = ngx_http_ssi_error_string
519 + sizeof(ngx_http_ssi_error_string) - 1;
520
521 cl->next = NULL;
522 *ctx->last_out = cl;
523 ctx->last_out = &cl->next;
524
525 continue;
526 }
527
528 if (ctx->buf->recycled || ctx->buf->last_buf) {
529 if (b == NULL) {
530
531 if (ctx->free) {
532 cl = ctx->free;
533 ctx->free = ctx->free->next;
534 b = cl->buf;
535 ngx_memzero(b, sizeof(ngx_buf_t));
536
537 } else {
538 b = ngx_calloc_buf(r->pool);
539 if (b == NULL) {
540 return NGX_ERROR;
541 }
542
543 cl = ngx_alloc_chain_link(r->pool);
544 if (cl == NULL) {
545 return NGX_ERROR;
546 }
547
548 cl->buf = b;
549 }
550
551 cl->next = NULL;
552 *ctx->last_out = cl;
553 ctx->last_out = &cl->next;
554 }
555
556 b->last_buf = ctx->buf->last_buf;
557 b->flush = ctx->buf->recycled;
558 b->shadow = ctx->buf;
559 }
560
561 ctx->buf = NULL;
562
563 ctx->saved = ctx->looked;
564 }
565
566 if (ctx->out == NULL && ctx->busy == NULL) {
567 return NGX_OK;
568 }
569
570 rc = ngx_http_next_body_filter(r, ctx->out);
571
572 if (ctx->busy == NULL) {
573 ctx->busy = ctx->out;
574
575 } else {
576 for (cl = ctx->busy; cl->next; cl = cl->next) { /* void */ }
577 cl->next = ctx->out;
578 }
579
580 ctx->out = NULL;
581 ctx->last_out = &ctx->out;
582
583 while (ctx->busy) {
584
585 b = ctx->busy->buf;
586
587 if (ngx_buf_size(b) != 0) {
588 break;
589 }
590
591 #if (NGX_HAVE_WRITE_ZEROCOPY)
592 if (b->zerocopy_busy) {
593 break;
594 }
595 #endif
596
597 if (b->shadow) {
598 b->shadow->pos = b->shadow->last;
599 }
600
601 cl = ctx->busy;
602 ctx->busy = cl->next;
603 cl->next = ctx->free;
604 ctx->free = cl;
605 }
606
607 return rc;
608 }
609
610
611 static ngx_int_t
612 ngx_http_ssi_parse(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx)
613 {
614 u_char *p, *last, *copy_end, ch;
615 size_t looked;
616 ngx_http_ssi_state_e state;
617
618 state = ctx->state;
619 looked = ctx->looked;
620 last = ctx->buf->last;
621 copy_end = ctx->copy_end;
622
623 for (p = ctx->pos; p < last; p++) {
624
625 ch = *p;
626
627 if (state == ssi_start_state) {
628
629 /* the tight loop */
630
631 for ( ;; ) {
632 if (ch == '<') {
633 copy_end = p;
634 looked = 1;
635 state = ssi_tag_state;
636
637 goto tag_started;
638 }
639
640 if (++p == last) {
641 break;
642 }
643
644 ch = *p;
645 }
646
647 ctx->pos = p;
648 ctx->looked = looked;
649 ctx->copy_end = p;
650
651 if (ctx->copy_start == NULL) {
652 ctx->copy_start = ctx->buf->pos;
653 }
654
655 return NGX_AGAIN;
656
657 tag_started:
658
659 continue;
660 }
661
662 switch (state) {
663
664 case ssi_start_state:
665 break;
666
667 case ssi_tag_state:
668 switch (ch) {
669 case '!':
670 looked = 2;
671 state = ssi_comment0_state;
672 break;
673
674 case '<':
675 copy_end = p;
676 break;
677
678 default:
679 copy_end = p;
680 looked = 0;
681 state = ssi_start_state;
682 break;
683 }
684
685 break;
686
687 case ssi_comment0_state:
688 switch (ch) {
689 case '-':
690 looked = 3;
691 state = ssi_comment1_state;
692 break;
693
694 case '<':
695 copy_end = p;
696 looked = 1;
697 state = ssi_tag_state;
698 break;
699
700 default:
701 copy_end = p;
702 looked = 0;
703 state = ssi_start_state;
704 break;
705 }
706
707 break;
708
709 case ssi_comment1_state:
710 switch (ch) {
711 case '-':
712 looked = 4;
713 state = ssi_sharp_state;
714 break;
715
716 case '<':
717 copy_end = p;
718 looked = 1;
719 state = ssi_tag_state;
720 break;
721
722 default:
723 copy_end = p;
724 looked = 0;
725 state = ssi_start_state;
726 break;
727 }
728
729 break;
730
731 case ssi_sharp_state:
732 switch (ch) {
733 case '#':
734 if (ctx->copy_start) {
735 ctx->saved = 0;
736 }
737 looked = 0;
738 state = ssi_precommand_state;
739 break;
740
741 case '<':
742 copy_end = p;
743 looked = 1;
744 state = ssi_tag_state;
745 break;
746
747 default:
748 copy_end = p;
749 looked = 0;
750 state = ssi_start_state;
751 break;
752 }
753
754 break;
755
756 case ssi_precommand_state:
757 switch (ch) {
758 case ' ':
759 case CR:
760 case LF:
761 case '\t':
762 break;
763
764 default:
765 ctx->command.len = 1;
766 ctx->command.data = ngx_palloc(r->pool,
767 NGX_HTTP_SSI_COMMAND_LEN + 1);
768 if (ctx->command.data == NULL) {
769 return NGX_ERROR;
770 }
771
772 ctx->command.data[0] = ch;
773 ctx->params.nelts = 0;
774 state = ssi_command_state;
775 break;
776 }
777
778 break;
779
780 case ssi_command_state:
781 switch (ch) {
782 case ' ':
783 case CR:
784 case LF:
785 case '\t':
786 state = ssi_preparam_state;
787 break;
788
789 case '-':
790 state = ssi_comment_end0_state;
791 break;
792
793 default:
794 ctx->command.data[ctx->command.len++] = ch;
795
796 if (ctx->command.len == NGX_HTTP_SSI_COMMAND_LEN) {
797 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
798 "the \"%V\" SSI command is too long",
799 &ctx->command);
800
801 state = ssi_error_state;
802 break;
803 }
804 }
805
806 break;
807
808 case ssi_preparam_state:
809 switch (ch) {
810 case ' ':
811 case CR:
812 case LF:
813 case '\t':
814 break;
815
816 case '-':
817 state = ssi_comment_end0_state;
818 break;
819
820 default:
821 ctx->param = ngx_array_push(&ctx->params);
822 if (ctx->param == NULL) {
823 return NGX_ERROR;
824 }
825
826 ctx->param->key.len = 1;
827 ctx->param->key.data = ngx_palloc(r->pool,
828 NGX_HTTP_SSI_PARAM_LEN + 1);
829 if (ctx->param->key.data == NULL) {
830 return NGX_ERROR;
831 }
832
833 ctx->param->key.data[0] = ch;
834
835 ctx->param->value.len = 0;
836 ctx->param->value.data = ngx_palloc(r->pool,
837 ctx->value_len + 1);
838 if (ctx->param->value.data == NULL) {
839 return NGX_ERROR;
840 }
841
842 state = ssi_param_state;
843 break;
844 }
845
846 break;
847
848 case ssi_param_state:
849 switch (ch) {
850 case ' ':
851 case CR:
852 case LF:
853 case '\t':
854 state = ssi_preequal_state;
855 break;
856
857 case '=':
858 state = ssi_prevalue_state;
859 break;
860
861 case '-':
862 state = ssi_error_end0_state;
863
864 ctx->param->key.data[ctx->param->key.len++] = ch;
865 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
866 "invalid \"%V\" parameter in \"%V\" SSI command",
867 &ctx->param->key, &ctx->command);
868 break;
869
870 default:
871 ctx->param->key.data[ctx->param->key.len++] = ch;
872
873 if (ctx->param->key.len == NGX_HTTP_SSI_PARAM_LEN) {
874 state = ssi_error_state;
875 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
876 "too long \"%V\" parameter in "
877 "\"%V\" SSI command",
878 &ctx->param->key, &ctx->command);
879 break;
880 }
881 }
882
883 break;
884
885 case ssi_preequal_state:
886 switch (ch) {
887 case ' ':
888 case CR:
889 case LF:
890 case '\t':
891 break;
892
893 case '=':
894 state = ssi_prevalue_state;
895 break;
896
897 default:
898 if (ch == '-') {
899 state = ssi_error_end0_state;
900 } else {
901 state = ssi_error_state;
902 }
903
904 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
905 "unexpected \"%c\" symbol after \"%V\" "
906 "parameter in \"%V\" SSI command",
907 ch, &ctx->param->key, &ctx->command);
908 break;
909 }
910
911 break;
912
913 case ssi_prevalue_state:
914 switch (ch) {
915 case ' ':
916 case CR:
917 case LF:
918 case '\t':
919 break;
920
921 case '"':
922 state = ssi_double_quoted_value_state;
923 break;
924
925 case '\'':
926 state = ssi_quoted_value_state;
927 break;
928
929 default:
930 if (ch == '-') {
931 state = ssi_error_end0_state;
932 } else {
933 state = ssi_error_state;
934 }
935
936 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
937 "unexpected \"%c\" symbol before value of "
938 "\"%V\" parameter in \"%V\" SSI command",
939 ch, &ctx->param->key, &ctx->command);
940 break;
941 }
942
943 break;
944
945 case ssi_double_quoted_value_state:
946 switch (ch) {
947 case '\\':
948 ctx->saved_state = ssi_double_quoted_value_state;
949 state = ssi_quoted_symbol_state;
950 break;
951
952 case '"':
953 state = ssi_postparam_state;
954 break;
955
956 default:
957 ctx->param->value.data[ctx->param->value.len++] = ch;
958
959 if (ctx->param->value.len == ctx->value_len) {
960 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
961 "too long \"%V\" value of \"%V\" parameter "
962 "in \"%V\" SSI command",
963 &ctx->param->value, &ctx->param->key,
964 &ctx->command);
965 state = ssi_error_state;
966 break;
967 }
968 }
969
970 break;
971
972 case ssi_quoted_value_state:
973 switch (ch) {
974 case '\\':
975 ctx->saved_state = ssi_quoted_value_state;
976 state = ssi_quoted_symbol_state;
977 break;
978
979 case '\'':
980 state = ssi_postparam_state;
981 break;
982
983 default:
984 ctx->param->value.data[ctx->param->value.len++] = ch;
985
986 if (ctx->param->value.len == ctx->value_len) {
987 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
988 "too long \"%V\" value of \"%V\" parameter "
989 "in \"%V\" SSI command",
990 &ctx->param->value, &ctx->param->key,
991 &ctx->command);
992 state = ssi_error_state;
993 break;
994 }
995 }
996
997 break;
998
999 case ssi_quoted_symbol_state:
1000 ctx->param->value.data[ctx->param->value.len++] = ch;
1001
1002 if (ctx->param->value.len == ctx->value_len) {
1003 if (ctx->param->value.len == ctx->value_len) {
1004 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1005 "too long \"%V\" value of \"%V\" parameter "
1006 "in \"%V\" SSI command",
1007 &ctx->param->value, &ctx->param->key,
1008 &ctx->command);
1009 state = ssi_error_state;
1010 break;
1011 }
1012 }
1013
1014 state = ctx->saved_state;
1015 break;
1016
1017 case ssi_postparam_state:
1018 switch (ch) {
1019 case ' ':
1020 case CR:
1021 case LF:
1022 case '\t':
1023 state = ssi_preparam_state;
1024 break;
1025
1026 case '-':
1027 state = ssi_comment_end0_state;
1028 break;
1029
1030 default:
1031 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1032 "unexpected \"%c\" symbol after \"%V\" value "
1033 "of \"%V\" parameter in \"%V\" SSI command",
1034 ch, &ctx->param->value, &ctx->param->key,
1035 &ctx->command);
1036 state = ssi_error_state;
1037 break;
1038 }
1039
1040 break;
1041
1042 case ssi_comment_end0_state:
1043 switch (ch) {
1044 case '-':
1045 state = ssi_comment_end1_state;
1046 break;
1047
1048 default:
1049 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1050 "unexpected \"%c\" symbol in \"%V\" SSI command",
1051 ch, &ctx->command);
1052 state = ssi_error_state;
1053 break;
1054 }
1055
1056 break;
1057
1058 case ssi_comment_end1_state:
1059 switch (ch) {
1060 case '>':
1061 ctx->state = ssi_start_state;
1062 ctx->pos = p + 1;
1063 ctx->looked = looked;
1064 ctx->copy_end = copy_end;
1065
1066 if (ctx->copy_start == NULL && copy_end) {
1067 ctx->copy_start = ctx->buf->pos;
1068 }
1069
1070 return NGX_OK;
1071
1072 default:
1073 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1074 "unexpected \"%c\" symbol in \"%V\" SSI command",
1075 ch, &ctx->command);
1076 state = ssi_error_state;
1077 break;
1078 }
1079
1080 break;
1081
1082 case ssi_error_state:
1083 switch (ch) {
1084 case '-':
1085 state = ssi_error_end0_state;
1086 break;
1087
1088 default:
1089 break;
1090 }
1091
1092 break;
1093
1094 case ssi_error_end0_state:
1095 switch (ch) {
1096 case '-':
1097 state = ssi_error_end1_state;
1098 break;
1099
1100 default:
1101 state = ssi_error_state;
1102 break;
1103 }
1104
1105 break;
1106
1107 case ssi_error_end1_state:
1108 switch (ch) {
1109 case '>':
1110 ctx->state = ssi_start_state;
1111 ctx->pos = p + 1;
1112 ctx->looked = looked;
1113 ctx->copy_end = copy_end;
1114
1115 if (ctx->copy_start == NULL && copy_end) {
1116 ctx->copy_start = ctx->buf->pos;
1117 }
1118
1119 return NGX_HTTP_SSI_ERROR;
1120
1121 default:
1122 state = ssi_error_state;
1123 break;
1124 }
1125
1126 break;
1127 }
1128 }
1129
1130 ctx->state = state;
1131 ctx->pos = p;
1132 ctx->looked = looked;
1133
1134 ctx->copy_end = (state == ssi_start_state) ? p : copy_end;
1135
1136 if (ctx->copy_start == NULL && ctx->copy_end) {
1137 ctx->copy_start = ctx->buf->pos;
1138 }
1139
1140 return NGX_AGAIN;
1141 }
1142
1143
1144 static ngx_int_t
1145 ngx_http_ssi_echo(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
1146 ngx_str_t **params)
1147 {
1148 ngx_uint_t i;
1149 ngx_buf_t *b;
1150 ngx_str_t *var, *value;
1151 ngx_chain_t *cl;
1152 ngx_http_variable_value_t *vv;
1153
1154 var = params[NGX_HTTP_SSI_ECHO_VAR];
1155
1156 for (i = 0; i < var->len; i++) {
1157 var->data[i] = ngx_toupper(var->data[i]);
1158 }
1159
1160 vv = ngx_http_get_variable(r, var);
1161
1162 if (vv == NULL) {
1163 return NGX_HTTP_SSI_ERROR;
1164 }
1165
1166 if (vv == NGX_HTTP_VARIABLE_NOT_FOUND) {
1167 value = params[NGX_HTTP_SSI_ECHO_DEFAULT];
1168
1169 if (value == NULL) {
1170 value = &ngx_http_ssi_none;
1171
1172 } else if (value->len == 0) {
1173 return NGX_OK;
1174 }
1175
1176 } else {
1177 value = &vv->text;
1178
1179 if (value->len == 0) {
1180 return NGX_OK;
1181 }
1182 }
1183
1184 b = ngx_calloc_buf(r->pool);
1185 if (b == NULL) {
1186 return NGX_HTTP_SSI_ERROR;
1187 }
1188
1189 cl = ngx_alloc_chain_link(r->pool);
1190 if (cl == NULL) {
1191 return NGX_HTTP_SSI_ERROR;
1192 }
1193
1194 b->memory = 1;
1195 b->pos = value->data;
1196 b->last = value->data + value->len;
1197
1198 cl->buf = b;
1199 cl->next = NULL;
1200 *ctx->last_out = cl;
1201 ctx->last_out = &cl->next;
1202
1203 return NGX_OK;
1204 }
1205
1206
1207 static void *
1208 ngx_http_ssi_create_conf(ngx_conf_t *cf)
1209 {
1210 ngx_http_ssi_conf_t *conf;
1211
1212 conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssi_conf_t));
1213 if (conf == NULL) {
1214 return NGX_CONF_ERROR;
1215 }
1216
1217 conf->enable = NGX_CONF_UNSET;
1218 conf->silent_errors = NGX_CONF_UNSET;
1219
1220 conf->min_file_chunk = NGX_CONF_UNSET_SIZE;
1221 conf->value_len = NGX_CONF_UNSET_SIZE;
1222
1223 return conf;
1224 }
1225
1226
1227 static char *
1228 ngx_http_ssi_merge_conf(ngx_conf_t *cf, void *parent, void *child)
1229 {
1230 ngx_http_ssi_conf_t *prev = parent;
1231 ngx_http_ssi_conf_t *conf = child;
1232
1233 ngx_conf_merge_value(conf->enable, prev->enable, 0);
1234 ngx_conf_merge_value(conf->silent_errors, prev->silent_errors, 0);
1235
1236 ngx_conf_merge_size_value(conf->min_file_chunk, prev->min_file_chunk, 1024);
1237 ngx_conf_merge_size_value(conf->value_len, prev->value_len, 256);
1238
1239 return NGX_CONF_OK;
1240 }
1241
1242
1243 static ngx_int_t
1244 ngx_http_ssi_filter_init(ngx_cycle_t *cycle)
1245 {
1246 ngx_http_next_header_filter = ngx_http_top_header_filter;
1247 ngx_http_top_header_filter = ngx_http_ssi_header_filter;
1248
1249 ngx_http_next_body_filter = ngx_http_top_body_filter;
1250 ngx_http_top_body_filter = ngx_http_ssi_body_filter;
1251
1252 return NGX_OK;
1253 }