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