3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
8 #include <ngx_config.h>
12 #define NGX_HTTP_SSI_ERROR 1
14 #define NGX_HTTP_SSI_DATE_LEN 2048
16 #define NGX_HTTP_SSI_ADD_PREFIX 1
17 #define NGX_HTTP_SSI_ADD_ZERO 2
22 ngx_flag_t silent_errors
;
23 ngx_flag_t ignore_recycled_buffers
;
27 size_t min_file_chunk
;
30 ngx_array_t
*types_keys
;
31 } ngx_http_ssi_loc_conf_t
;
45 } ngx_http_ssi_block_t
;
60 ssi_double_quoted_value_state
,
61 ssi_quoted_value_state
,
62 ssi_quoted_symbol_state
,
64 ssi_comment_end0_state
,
65 ssi_comment_end1_state
,
69 } ngx_http_ssi_state_e
;
72 static ngx_int_t
ngx_http_ssi_output(ngx_http_request_t
*r
,
73 ngx_http_ssi_ctx_t
*ctx
);
74 static void ngx_http_ssi_buffered(ngx_http_request_t
*r
,
75 ngx_http_ssi_ctx_t
*ctx
);
76 static ngx_int_t
ngx_http_ssi_parse(ngx_http_request_t
*r
,
77 ngx_http_ssi_ctx_t
*ctx
);
78 static ngx_str_t
*ngx_http_ssi_get_variable(ngx_http_request_t
*r
,
79 ngx_str_t
*name
, ngx_uint_t key
);
80 static ngx_int_t
ngx_http_ssi_evaluate_string(ngx_http_request_t
*r
,
81 ngx_http_ssi_ctx_t
*ctx
, ngx_str_t
*text
, ngx_uint_t flags
);
82 static ngx_int_t
ngx_http_ssi_regex_match(ngx_http_request_t
*r
,
83 ngx_str_t
*pattern
, ngx_str_t
*str
);
85 static ngx_int_t
ngx_http_ssi_include(ngx_http_request_t
*r
,
86 ngx_http_ssi_ctx_t
*ctx
, ngx_str_t
**params
);
87 static ngx_int_t
ngx_http_ssi_stub_output(ngx_http_request_t
*r
, void *data
,
89 static ngx_int_t
ngx_http_ssi_set_variable(ngx_http_request_t
*r
, void *data
,
91 static ngx_int_t
ngx_http_ssi_echo(ngx_http_request_t
*r
,
92 ngx_http_ssi_ctx_t
*ctx
, ngx_str_t
**params
);
93 static ngx_int_t
ngx_http_ssi_config(ngx_http_request_t
*r
,
94 ngx_http_ssi_ctx_t
*ctx
, ngx_str_t
**params
);
95 static ngx_int_t
ngx_http_ssi_set(ngx_http_request_t
*r
,
96 ngx_http_ssi_ctx_t
*ctx
, ngx_str_t
**params
);
97 static ngx_int_t
ngx_http_ssi_if(ngx_http_request_t
*r
,
98 ngx_http_ssi_ctx_t
*ctx
, ngx_str_t
**params
);
99 static ngx_int_t
ngx_http_ssi_else(ngx_http_request_t
*r
,
100 ngx_http_ssi_ctx_t
*ctx
, ngx_str_t
**params
);
101 static ngx_int_t
ngx_http_ssi_endif(ngx_http_request_t
*r
,
102 ngx_http_ssi_ctx_t
*ctx
, ngx_str_t
**params
);
103 static ngx_int_t
ngx_http_ssi_block(ngx_http_request_t
*r
,
104 ngx_http_ssi_ctx_t
*ctx
, ngx_str_t
**params
);
105 static ngx_int_t
ngx_http_ssi_endblock(ngx_http_request_t
*r
,
106 ngx_http_ssi_ctx_t
*ctx
, ngx_str_t
**params
);
108 static ngx_int_t
ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t
*r
,
109 ngx_http_variable_value_t
*v
, uintptr_t gmt
);
111 static ngx_int_t
ngx_http_ssi_preconfiguration(ngx_conf_t
*cf
);
112 static void *ngx_http_ssi_create_main_conf(ngx_conf_t
*cf
);
113 static char *ngx_http_ssi_init_main_conf(ngx_conf_t
*cf
, void *conf
);
114 static void *ngx_http_ssi_create_loc_conf(ngx_conf_t
*cf
);
115 static char *ngx_http_ssi_merge_loc_conf(ngx_conf_t
*cf
,
116 void *parent
, void *child
);
117 static ngx_int_t
ngx_http_ssi_filter_init(ngx_conf_t
*cf
);
120 static ngx_command_t ngx_http_ssi_filter_commands
[] = {
123 NGX_HTTP_MAIN_CONF
|NGX_HTTP_SRV_CONF
|NGX_HTTP_LOC_CONF
|NGX_HTTP_LIF_CONF
125 ngx_conf_set_flag_slot
,
126 NGX_HTTP_LOC_CONF_OFFSET
,
127 offsetof(ngx_http_ssi_loc_conf_t
, enable
),
130 { ngx_string("ssi_silent_errors"),
131 NGX_HTTP_MAIN_CONF
|NGX_HTTP_SRV_CONF
|NGX_HTTP_LOC_CONF
|NGX_CONF_FLAG
,
132 ngx_conf_set_flag_slot
,
133 NGX_HTTP_LOC_CONF_OFFSET
,
134 offsetof(ngx_http_ssi_loc_conf_t
, silent_errors
),
137 { ngx_string("ssi_ignore_recycled_buffers"),
138 NGX_HTTP_MAIN_CONF
|NGX_HTTP_SRV_CONF
|NGX_HTTP_LOC_CONF
|NGX_CONF_FLAG
,
139 ngx_conf_set_flag_slot
,
140 NGX_HTTP_LOC_CONF_OFFSET
,
141 offsetof(ngx_http_ssi_loc_conf_t
, ignore_recycled_buffers
),
144 { ngx_string("ssi_min_file_chunk"),
145 NGX_HTTP_MAIN_CONF
|NGX_HTTP_SRV_CONF
|NGX_HTTP_LOC_CONF
|NGX_CONF_TAKE1
,
146 ngx_conf_set_size_slot
,
147 NGX_HTTP_LOC_CONF_OFFSET
,
148 offsetof(ngx_http_ssi_loc_conf_t
, min_file_chunk
),
151 { ngx_string("ssi_value_length"),
152 NGX_HTTP_MAIN_CONF
|NGX_HTTP_SRV_CONF
|NGX_HTTP_LOC_CONF
|NGX_CONF_TAKE1
,
153 ngx_conf_set_size_slot
,
154 NGX_HTTP_LOC_CONF_OFFSET
,
155 offsetof(ngx_http_ssi_loc_conf_t
, value_len
),
158 { ngx_string("ssi_types"),
159 NGX_HTTP_MAIN_CONF
|NGX_HTTP_SRV_CONF
|NGX_HTTP_LOC_CONF
|NGX_CONF_1MORE
,
161 NGX_HTTP_LOC_CONF_OFFSET
,
162 offsetof(ngx_http_ssi_loc_conf_t
, types_keys
),
163 &ngx_http_html_default_types
[0] },
170 static ngx_http_module_t ngx_http_ssi_filter_module_ctx
= {
171 ngx_http_ssi_preconfiguration
, /* preconfiguration */
172 ngx_http_ssi_filter_init
, /* postconfiguration */
174 ngx_http_ssi_create_main_conf
, /* create main configuration */
175 ngx_http_ssi_init_main_conf
, /* init main configuration */
177 NULL
, /* create server configuration */
178 NULL
, /* merge server configuration */
180 ngx_http_ssi_create_loc_conf
, /* create location configuration */
181 ngx_http_ssi_merge_loc_conf
/* merge location configuration */
185 ngx_module_t ngx_http_ssi_filter_module
= {
187 &ngx_http_ssi_filter_module_ctx
, /* module context */
188 ngx_http_ssi_filter_commands
, /* module directives */
189 NGX_HTTP_MODULE
, /* module type */
190 NULL
, /* init master */
191 NULL
, /* init module */
192 NULL
, /* init process */
193 NULL
, /* init thread */
194 NULL
, /* exit thread */
195 NULL
, /* exit process */
196 NULL
, /* exit master */
197 NGX_MODULE_V1_PADDING
201 static ngx_http_output_header_filter_pt ngx_http_next_header_filter
;
202 static ngx_http_output_body_filter_pt ngx_http_next_body_filter
;
205 static u_char ngx_http_ssi_string
[] = "<!--";
207 static ngx_str_t ngx_http_ssi_none
= ngx_string("(none)");
208 static ngx_str_t ngx_http_ssi_null_string
= ngx_null_string
;
211 #define NGX_HTTP_SSI_INCLUDE_VIRTUAL 0
212 #define NGX_HTTP_SSI_INCLUDE_FILE 1
213 #define NGX_HTTP_SSI_INCLUDE_WAIT 2
214 #define NGX_HTTP_SSI_INCLUDE_SET 3
215 #define NGX_HTTP_SSI_INCLUDE_STUB 4
217 #define NGX_HTTP_SSI_ECHO_VAR 0
218 #define NGX_HTTP_SSI_ECHO_DEFAULT 1
219 #define NGX_HTTP_SSI_ECHO_ENCODING 2
221 #define NGX_HTTP_SSI_CONFIG_ERRMSG 0
222 #define NGX_HTTP_SSI_CONFIG_TIMEFMT 1
224 #define NGX_HTTP_SSI_SET_VAR 0
225 #define NGX_HTTP_SSI_SET_VALUE 1
227 #define NGX_HTTP_SSI_IF_EXPR 0
229 #define NGX_HTTP_SSI_BLOCK_NAME 0
232 static ngx_http_ssi_param_t ngx_http_ssi_include_params
[] = {
233 { ngx_string("virtual"), NGX_HTTP_SSI_INCLUDE_VIRTUAL
, 0, 0 },
234 { ngx_string("file"), NGX_HTTP_SSI_INCLUDE_FILE
, 0, 0 },
235 { ngx_string("wait"), NGX_HTTP_SSI_INCLUDE_WAIT
, 0, 0 },
236 { ngx_string("set"), NGX_HTTP_SSI_INCLUDE_SET
, 0, 0 },
237 { ngx_string("stub"), NGX_HTTP_SSI_INCLUDE_STUB
, 0, 0 },
238 { ngx_null_string
, 0, 0, 0 }
242 static ngx_http_ssi_param_t ngx_http_ssi_echo_params
[] = {
243 { ngx_string("var"), NGX_HTTP_SSI_ECHO_VAR
, 1, 0 },
244 { ngx_string("default"), NGX_HTTP_SSI_ECHO_DEFAULT
, 0, 0 },
245 { ngx_string("encoding"), NGX_HTTP_SSI_ECHO_ENCODING
, 0, 0 },
246 { ngx_null_string
, 0, 0, 0 }
250 static ngx_http_ssi_param_t ngx_http_ssi_config_params
[] = {
251 { ngx_string("errmsg"), NGX_HTTP_SSI_CONFIG_ERRMSG
, 0, 0 },
252 { ngx_string("timefmt"), NGX_HTTP_SSI_CONFIG_TIMEFMT
, 0, 0 },
253 { ngx_null_string
, 0, 0, 0 }
257 static ngx_http_ssi_param_t ngx_http_ssi_set_params
[] = {
258 { ngx_string("var"), NGX_HTTP_SSI_SET_VAR
, 1, 0 },
259 { ngx_string("value"), NGX_HTTP_SSI_SET_VALUE
, 1, 0 },
260 { ngx_null_string
, 0, 0, 0 }
264 static ngx_http_ssi_param_t ngx_http_ssi_if_params
[] = {
265 { ngx_string("expr"), NGX_HTTP_SSI_IF_EXPR
, 1, 0 },
266 { ngx_null_string
, 0, 0, 0 }
270 static ngx_http_ssi_param_t ngx_http_ssi_block_params
[] = {
271 { ngx_string("name"), NGX_HTTP_SSI_BLOCK_NAME
, 1, 0 },
272 { ngx_null_string
, 0, 0, 0 }
276 static ngx_http_ssi_param_t ngx_http_ssi_no_params
[] = {
277 { ngx_null_string
, 0, 0, 0 }
281 static ngx_http_ssi_command_t ngx_http_ssi_commands
[] = {
282 { ngx_string("include"), ngx_http_ssi_include
,
283 ngx_http_ssi_include_params
, 0, 0, 1 },
284 { ngx_string("echo"), ngx_http_ssi_echo
,
285 ngx_http_ssi_echo_params
, 0, 0, 0 },
286 { ngx_string("config"), ngx_http_ssi_config
,
287 ngx_http_ssi_config_params
, 0, 0, 0 },
288 { ngx_string("set"), ngx_http_ssi_set
, ngx_http_ssi_set_params
, 0, 0, 0 },
290 { ngx_string("if"), ngx_http_ssi_if
, ngx_http_ssi_if_params
, 0, 0, 0 },
291 { ngx_string("elif"), ngx_http_ssi_if
, ngx_http_ssi_if_params
,
292 NGX_HTTP_SSI_COND_IF
, 0, 0 },
293 { ngx_string("else"), ngx_http_ssi_else
, ngx_http_ssi_no_params
,
294 NGX_HTTP_SSI_COND_IF
, 0, 0 },
295 { ngx_string("endif"), ngx_http_ssi_endif
, ngx_http_ssi_no_params
,
296 NGX_HTTP_SSI_COND_ELSE
, 0, 0 },
298 { ngx_string("block"), ngx_http_ssi_block
,
299 ngx_http_ssi_block_params
, 0, 0, 0 },
300 { ngx_string("endblock"), ngx_http_ssi_endblock
,
301 ngx_http_ssi_no_params
, 0, 1, 0 },
303 { ngx_null_string
, NULL
, NULL
, 0, 0, 0 }
307 static ngx_http_variable_t ngx_http_ssi_vars
[] = {
309 { ngx_string("date_local"), NULL
, ngx_http_ssi_date_gmt_local_variable
, 0,
310 NGX_HTTP_VAR_NOCACHEABLE
, 0 },
312 { ngx_string("date_gmt"), NULL
, ngx_http_ssi_date_gmt_local_variable
, 1,
313 NGX_HTTP_VAR_NOCACHEABLE
, 0 },
315 { ngx_null_string
, NULL
, NULL
, 0, 0, 0 }
321 ngx_http_ssi_header_filter(ngx_http_request_t
*r
)
323 ngx_http_ssi_ctx_t
*ctx
;
324 ngx_http_ssi_loc_conf_t
*slcf
;
326 slcf
= ngx_http_get_module_loc_conf(r
, ngx_http_ssi_filter_module
);
329 || r
->headers_out
.content_length_n
== 0
330 || ngx_http_test_content_type(r
, &slcf
->types
) == NULL
)
332 return ngx_http_next_header_filter(r
);
335 ctx
= ngx_pcalloc(r
->pool
, sizeof(ngx_http_ssi_ctx_t
));
340 ngx_http_set_ctx(r
, ctx
, ngx_http_ssi_filter_module
);
343 ctx
->value_len
= slcf
->value_len
;
344 ctx
->last_out
= &ctx
->out
;
346 ctx
->encoding
= NGX_HTTP_SSI_ENTITY_ENCODING
;
349 ctx
->params
.elts
= ctx
->params_array
;
350 ctx
->params
.size
= sizeof(ngx_table_elt_t
);
351 ctx
->params
.nalloc
= NGX_HTTP_SSI_PARAMS_N
;
352 ctx
->params
.pool
= r
->pool
;
354 ngx_str_set(&ctx
->timefmt
, "%A, %d-%b-%Y %H:%M:%S %Z");
355 ngx_str_set(&ctx
->errmsg
,
356 "[an error occurred while processing the directive]");
358 r
->filter_need_in_memory
= 1;
361 ngx_http_clear_content_length(r
);
362 ngx_http_clear_last_modified(r
);
363 ngx_http_clear_accept_ranges(r
);
364 ngx_http_clear_etag(r
);
367 return ngx_http_next_header_filter(r
);
372 ngx_http_ssi_body_filter(ngx_http_request_t
*r
, ngx_chain_t
*in
)
378 ngx_chain_t
*cl
, **ll
;
379 ngx_table_elt_t
*param
;
380 ngx_http_ssi_ctx_t
*ctx
, *mctx
;
381 ngx_http_ssi_block_t
*bl
;
382 ngx_http_ssi_param_t
*prm
;
383 ngx_http_ssi_command_t
*cmd
;
384 ngx_http_ssi_loc_conf_t
*slcf
;
385 ngx_http_ssi_main_conf_t
*smcf
;
386 ngx_str_t
*params
[NGX_HTTP_SSI_MAX_PARAMS
+ 1];
388 ctx
= ngx_http_get_module_ctx(r
, ngx_http_ssi_filter_module
);
394 && ctx
->busy
== NULL
))
396 return ngx_http_next_body_filter(r
, in
);
399 /* add the incoming chain to the chain ctx->in */
402 if (ngx_chain_add_copy(r
->pool
, &ctx
->in
, in
) != NGX_OK
) {
407 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
408 "http ssi filter \"%V?%V\"", &r
->uri
, &r
->args
);
412 if (r
!= r
->connection
->data
) {
413 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
414 "http ssi filter wait \"%V?%V\" non-active",
415 &ctx
->wait
->uri
, &ctx
->wait
->args
);
420 if (ctx
->wait
->done
) {
421 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
422 "http ssi filter wait \"%V?%V\" done",
423 &ctx
->wait
->uri
, &ctx
->wait
->args
);
428 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
429 "http ssi filter wait \"%V?%V\"",
430 &ctx
->wait
->uri
, &ctx
->wait
->args
);
432 return ngx_http_next_body_filter(r
, NULL
);
436 slcf
= ngx_http_get_module_loc_conf(r
, ngx_http_ssi_filter_module
);
438 while (ctx
->in
|| ctx
->buf
) {
440 if (ctx
->buf
== NULL
) {
441 ctx
->buf
= ctx
->in
->buf
;
442 ctx
->in
= ctx
->in
->next
;
443 ctx
->pos
= ctx
->buf
->pos
;
446 if (ctx
->state
== ssi_start_state
) {
447 ctx
->copy_start
= ctx
->pos
;
448 ctx
->copy_end
= ctx
->pos
;
453 while (ctx
->pos
< ctx
->buf
->last
) {
455 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
456 "saved: %d state: %d", ctx
->saved
, ctx
->state
);
458 rc
= ngx_http_ssi_parse(r
, ctx
);
460 ngx_log_debug4(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
461 "parse: %d, looked: %d %p-%p",
462 rc
, ctx
->looked
, ctx
->copy_start
, ctx
->copy_end
);
464 if (rc
== NGX_ERROR
) {
468 if (ctx
->copy_start
!= ctx
->copy_end
) {
472 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
473 "saved: %d", ctx
->saved
);
479 ctx
->free
= ctx
->free
->next
;
481 ngx_memzero(b
, sizeof(ngx_buf_t
));
484 b
= ngx_calloc_buf(r
->pool
);
489 cl
= ngx_alloc_chain_link(r
->pool
);
498 b
->pos
= ngx_http_ssi_string
;
499 b
->last
= ngx_http_ssi_string
+ ctx
->saved
;
502 ctx
->last_out
= &cl
->next
;
509 ctx
->free
= ctx
->free
->next
;
513 b
= ngx_alloc_buf(r
->pool
);
518 cl
= ngx_alloc_chain_link(r
->pool
);
526 ngx_memcpy(b
, ctx
->buf
, sizeof(ngx_buf_t
));
528 b
->pos
= ctx
->copy_start
;
529 b
->last
= ctx
->copy_end
;
535 if (slcf
->min_file_chunk
< (size_t) (b
->last
- b
->pos
))
537 b
->file_last
= b
->file_pos
538 + (b
->last
- ctx
->buf
->pos
);
539 b
->file_pos
+= b
->pos
- ctx
->buf
->pos
;
548 ctx
->last_out
= &cl
->next
;
552 && ctx
->saved
+ (ctx
->copy_end
- ctx
->copy_start
))
554 b
= ngx_create_temp_buf(r
->pool
,
555 ctx
->saved
+ (ctx
->copy_end
- ctx
->copy_start
));
562 b
->last
= ngx_cpymem(b
->pos
, ngx_http_ssi_string
,
566 b
->last
= ngx_cpymem(b
->last
, ctx
->copy_start
,
567 ctx
->copy_end
- ctx
->copy_start
);
569 cl
= ngx_alloc_chain_link(r
->pool
);
579 mctx
= ngx_http_get_module_ctx(r
->main
,
580 ngx_http_ssi_filter_module
);
581 bl
= mctx
->blocks
->elts
;
582 for (ll
= &bl
[mctx
->blocks
->nelts
- 1].bufs
;
596 if (ctx
->state
== ssi_start_state
) {
597 ctx
->copy_start
= ctx
->pos
;
598 ctx
->copy_end
= ctx
->pos
;
601 ctx
->copy_start
= NULL
;
602 ctx
->copy_end
= NULL
;
605 if (rc
== NGX_AGAIN
) {
614 smcf
= ngx_http_get_module_main_conf(r
,
615 ngx_http_ssi_filter_module
);
617 cmd
= ngx_hash_find(&smcf
->hash
, ctx
->key
, ctx
->command
.data
,
622 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
623 "invalid SSI command: \"%V\"",
631 if (!ctx
->output
&& !cmd
->block
) {
635 /* reconstruct the SSI command text */
637 len
= 5 + ctx
->command
.len
+ 4;
639 param
= ctx
->params
.elts
;
640 for (i
= 0; i
< ctx
->params
.nelts
; i
++) {
641 len
+= 1 + param
[i
].key
.len
+ 2
642 + param
[i
].value
.len
+ 1;
645 b
= ngx_create_temp_buf(r
->pool
, len
);
651 cl
= ngx_alloc_chain_link(r
->pool
);
665 b
->last
= ngx_cpymem(b
->last
, ctx
->command
.data
,
668 for (i
= 0; i
< ctx
->params
.nelts
; i
++) {
670 b
->last
= ngx_cpymem(b
->last
, param
[i
].key
.data
,
674 b
->last
= ngx_cpymem(b
->last
, param
[i
].value
.data
,
684 mctx
= ngx_http_get_module_ctx(r
->main
,
685 ngx_http_ssi_filter_module
);
686 bl
= mctx
->blocks
->elts
;
687 for (ll
= &bl
[mctx
->blocks
->nelts
- 1].bufs
;
701 if (cmd
->conditional
== 0) {
707 && (ctx
->conditional
== 0
708 || ctx
->conditional
> cmd
->conditional
))
710 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
711 "invalid context of SSI command: \"%V\"",
716 if (ctx
->params
.nelts
> NGX_HTTP_SSI_MAX_PARAMS
) {
717 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
718 "too many SSI command parameters: \"%V\"",
724 (NGX_HTTP_SSI_MAX_PARAMS
+ 1) * sizeof(ngx_str_t
*));
726 param
= ctx
->params
.elts
;
728 for (i
= 0; i
< ctx
->params
.nelts
; i
++) {
730 for (prm
= cmd
->params
; prm
->name
.len
; prm
++) {
732 if (param
[i
].key
.len
!= prm
->name
.len
733 || ngx_strncmp(param
[i
].key
.data
, prm
->name
.data
,
739 if (!prm
->multiple
) {
740 if (params
[prm
->index
]) {
741 ngx_log_error(NGX_LOG_ERR
,
742 r
->connection
->log
, 0,
743 "duplicate \"%V\" parameter "
744 "in \"%V\" SSI command",
745 ¶m
[i
].key
, &ctx
->command
);
750 params
[prm
->index
] = ¶m
[i
].value
;
755 for (index
= prm
->index
; params
[index
]; index
++) {
759 params
[index
] = ¶m
[i
].value
;
764 if (prm
->name
.len
== 0) {
765 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
766 "invalid parameter name: \"%V\" "
767 "in \"%V\" SSI command",
768 ¶m
[i
].key
, &ctx
->command
);
774 for (prm
= cmd
->params
; prm
->name
.len
; prm
++) {
775 if (prm
->mandatory
&& params
[prm
->index
] == 0) {
776 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
777 "mandatory \"%V\" parameter is absent "
778 "in \"%V\" SSI command",
779 &prm
->name
, &ctx
->command
);
785 if (cmd
->flush
&& ctx
->out
) {
787 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
790 if (ngx_http_ssi_output(r
, ctx
) == NGX_ERROR
) {
795 rc
= cmd
->handler(r
, ctx
, params
);
801 if (rc
== NGX_DONE
|| rc
== NGX_AGAIN
|| rc
== NGX_ERROR
) {
802 ngx_http_ssi_buffered(r
, ctx
);
808 /* rc == NGX_HTTP_SSI_ERROR */
812 if (slcf
->silent_errors
) {
818 ctx
->free
= ctx
->free
->next
;
820 ngx_memzero(b
, sizeof(ngx_buf_t
));
823 b
= ngx_calloc_buf(r
->pool
);
828 cl
= ngx_alloc_chain_link(r
->pool
);
837 b
->pos
= ctx
->errmsg
.data
;
838 b
->last
= ctx
->errmsg
.data
+ ctx
->errmsg
.len
;
842 ctx
->last_out
= &cl
->next
;
847 if (ctx
->buf
->last_buf
|| ngx_buf_in_memory(ctx
->buf
)) {
851 ctx
->free
= ctx
->free
->next
;
853 ngx_memzero(b
, sizeof(ngx_buf_t
));
856 b
= ngx_calloc_buf(r
->pool
);
861 cl
= ngx_alloc_chain_link(r
->pool
);
873 ctx
->last_out
= &cl
->next
;
876 b
->last_buf
= ctx
->buf
->last_buf
;
877 b
->shadow
= ctx
->buf
;
879 if (slcf
->ignore_recycled_buffers
== 0) {
880 b
->recycled
= ctx
->buf
->recycled
;
886 ctx
->saved
= ctx
->looked
;
889 if (ctx
->out
== NULL
&& ctx
->busy
== NULL
) {
893 return ngx_http_ssi_output(r
, ctx
);
898 ngx_http_ssi_output(ngx_http_request_t
*r
, ngx_http_ssi_ctx_t
*ctx
)
906 for (cl
= ctx
->out
; cl
; cl
= cl
->next
) {
907 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
908 "ssi out: %p %p", cl
->buf
, cl
->buf
->pos
);
910 ngx_log_error(NGX_LOG_ALERT
, r
->connection
->log
, 0,
911 "the same buf was used in ssi");
919 rc
= ngx_http_next_body_filter(r
, ctx
->out
);
921 if (ctx
->busy
== NULL
) {
922 ctx
->busy
= ctx
->out
;
925 for (cl
= ctx
->busy
; cl
->next
; cl
= cl
->next
) { /* void */ }
930 ctx
->last_out
= &ctx
->out
;
937 if (ngx_buf_size(b
) != 0) {
942 b
->shadow
->pos
= b
->shadow
->last
;
945 ctx
->busy
= cl
->next
;
947 if (ngx_buf_in_memory(b
) || b
->in_file
) {
948 /* add data bufs only to the free buf chain */
950 cl
->next
= ctx
->free
;
955 ngx_http_ssi_buffered(r
, ctx
);
962 ngx_http_ssi_buffered(ngx_http_request_t
*r
, ngx_http_ssi_ctx_t
*ctx
)
964 if (ctx
->in
|| ctx
->buf
) {
965 r
->buffered
|= NGX_HTTP_SSI_BUFFERED
;
968 r
->buffered
&= ~NGX_HTTP_SSI_BUFFERED
;
974 ngx_http_ssi_parse(ngx_http_request_t
*r
, ngx_http_ssi_ctx_t
*ctx
)
976 u_char
*p
, *value
, *last
, *copy_end
, ch
;
978 ngx_http_ssi_state_e state
;
981 looked
= ctx
->looked
;
982 last
= ctx
->buf
->last
;
983 copy_end
= ctx
->copy_end
;
985 for (p
= ctx
->pos
; p
< last
; p
++) {
989 if (state
== ssi_start_state
) {
997 state
= ssi_tag_state
;
1011 ctx
->looked
= looked
;
1014 if (ctx
->copy_start
== NULL
) {
1015 ctx
->copy_start
= ctx
->buf
->pos
;
1027 case ssi_start_state
:
1035 state
= ssi_comment0_state
;
1045 state
= ssi_start_state
;
1051 case ssi_comment0_state
:
1055 state
= ssi_comment1_state
;
1061 state
= ssi_tag_state
;
1067 state
= ssi_start_state
;
1073 case ssi_comment1_state
:
1077 state
= ssi_sharp_state
;
1083 state
= ssi_tag_state
;
1089 state
= ssi_start_state
;
1095 case ssi_sharp_state
:
1098 if (p
- ctx
->pos
< 4) {
1102 state
= ssi_precommand_state
;
1108 state
= ssi_tag_state
;
1114 state
= ssi_start_state
;
1120 case ssi_precommand_state
:
1129 ctx
->command
.len
= 1;
1130 ctx
->command
.data
= ngx_pnalloc(r
->pool
,
1131 NGX_HTTP_SSI_COMMAND_LEN
);
1132 if (ctx
->command
.data
== NULL
) {
1136 ctx
->command
.data
[0] = ch
;
1139 ctx
->key
= ngx_hash(ctx
->key
, ch
);
1141 ctx
->params
.nelts
= 0;
1143 state
= ssi_command_state
;
1149 case ssi_command_state
:
1155 state
= ssi_preparam_state
;
1159 state
= ssi_comment_end0_state
;
1163 if (ctx
->command
.len
== NGX_HTTP_SSI_COMMAND_LEN
) {
1164 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1165 "the \"%V%c...\" SSI command is too long",
1168 state
= ssi_error_state
;
1172 ctx
->command
.data
[ctx
->command
.len
++] = ch
;
1173 ctx
->key
= ngx_hash(ctx
->key
, ch
);
1178 case ssi_preparam_state
:
1187 state
= ssi_comment_end0_state
;
1191 ctx
->param
= ngx_array_push(&ctx
->params
);
1192 if (ctx
->param
== NULL
) {
1196 ctx
->param
->key
.len
= 1;
1197 ctx
->param
->key
.data
= ngx_pnalloc(r
->pool
,
1198 NGX_HTTP_SSI_PARAM_LEN
);
1199 if (ctx
->param
->key
.data
== NULL
) {
1203 ctx
->param
->key
.data
[0] = ch
;
1205 ctx
->param
->value
.len
= 0;
1207 if (ctx
->value_buf
== NULL
) {
1208 ctx
->param
->value
.data
= ngx_pnalloc(r
->pool
,
1209 ctx
->value_len
+ 1);
1210 if (ctx
->param
->value
.data
== NULL
) {
1215 ctx
->param
->value
.data
= ctx
->value_buf
;
1218 state
= ssi_param_state
;
1224 case ssi_param_state
:
1230 state
= ssi_preequal_state
;
1234 state
= ssi_prevalue_state
;
1238 state
= ssi_error_end0_state
;
1240 ctx
->param
->key
.data
[ctx
->param
->key
.len
++] = ch
;
1241 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1242 "invalid \"%V\" parameter in \"%V\" SSI command",
1243 &ctx
->param
->key
, &ctx
->command
);
1247 if (ctx
->param
->key
.len
== NGX_HTTP_SSI_PARAM_LEN
) {
1248 state
= ssi_error_state
;
1249 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1250 "too long \"%V%c...\" parameter in "
1251 "\"%V\" SSI command",
1252 &ctx
->param
->key
, ch
, &ctx
->command
);
1256 ctx
->param
->key
.data
[ctx
->param
->key
.len
++] = ch
;
1261 case ssi_preequal_state
:
1270 state
= ssi_prevalue_state
;
1275 state
= ssi_error_end0_state
;
1277 state
= ssi_error_state
;
1280 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1281 "unexpected \"%c\" symbol after \"%V\" "
1282 "parameter in \"%V\" SSI command",
1283 ch
, &ctx
->param
->key
, &ctx
->command
);
1289 case ssi_prevalue_state
:
1298 state
= ssi_double_quoted_value_state
;
1302 state
= ssi_quoted_value_state
;
1307 state
= ssi_error_end0_state
;
1309 state
= ssi_error_state
;
1312 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1313 "unexpected \"%c\" symbol before value of "
1314 "\"%V\" parameter in \"%V\" SSI command",
1315 ch
, &ctx
->param
->key
, &ctx
->command
);
1321 case ssi_double_quoted_value_state
:
1324 state
= ssi_postparam_state
;
1328 ctx
->saved_state
= ssi_double_quoted_value_state
;
1329 state
= ssi_quoted_symbol_state
;
1334 if (ctx
->param
->value
.len
== ctx
->value_len
) {
1335 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1336 "too long \"%V%c...\" value of \"%V\" "
1337 "parameter in \"%V\" SSI command",
1338 &ctx
->param
->value
, ch
, &ctx
->param
->key
,
1340 state
= ssi_error_state
;
1344 ctx
->param
->value
.data
[ctx
->param
->value
.len
++] = ch
;
1349 case ssi_quoted_value_state
:
1352 state
= ssi_postparam_state
;
1356 ctx
->saved_state
= ssi_quoted_value_state
;
1357 state
= ssi_quoted_symbol_state
;
1362 if (ctx
->param
->value
.len
== ctx
->value_len
) {
1363 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1364 "too long \"%V%c...\" value of \"%V\" "
1365 "parameter in \"%V\" SSI command",
1366 &ctx
->param
->value
, ch
, &ctx
->param
->key
,
1368 state
= ssi_error_state
;
1372 ctx
->param
->value
.data
[ctx
->param
->value
.len
++] = ch
;
1377 case ssi_quoted_symbol_state
:
1378 state
= ctx
->saved_state
;
1380 if (ctx
->param
->value
.len
== ctx
->value_len
) {
1381 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1382 "too long \"%V%c...\" value of \"%V\" "
1383 "parameter in \"%V\" SSI command",
1384 &ctx
->param
->value
, ch
, &ctx
->param
->key
,
1386 state
= ssi_error_state
;
1390 ctx
->param
->value
.data
[ctx
->param
->value
.len
++] = ch
;
1394 case ssi_postparam_state
:
1396 if (ctx
->param
->value
.len
+ 1 < ctx
->value_len
/ 2) {
1397 value
= ngx_pnalloc(r
->pool
, ctx
->param
->value
.len
+ 1);
1398 if (value
== NULL
) {
1402 ngx_memcpy(value
, ctx
->param
->value
.data
,
1403 ctx
->param
->value
.len
);
1405 ctx
->value_buf
= ctx
->param
->value
.data
;
1406 ctx
->param
->value
.data
= value
;
1409 ctx
->value_buf
= NULL
;
1417 state
= ssi_preparam_state
;
1421 state
= ssi_comment_end0_state
;
1425 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1426 "unexpected \"%c\" symbol after \"%V\" value "
1427 "of \"%V\" parameter in \"%V\" SSI command",
1428 ch
, &ctx
->param
->value
, &ctx
->param
->key
,
1430 state
= ssi_error_state
;
1436 case ssi_comment_end0_state
:
1439 state
= ssi_comment_end1_state
;
1443 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1444 "unexpected \"%c\" symbol in \"%V\" SSI command",
1446 state
= ssi_error_state
;
1452 case ssi_comment_end1_state
:
1455 ctx
->state
= ssi_start_state
;
1457 ctx
->looked
= looked
;
1458 ctx
->copy_end
= copy_end
;
1460 if (ctx
->copy_start
== NULL
&& copy_end
) {
1461 ctx
->copy_start
= ctx
->buf
->pos
;
1467 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1468 "unexpected \"%c\" symbol in \"%V\" SSI command",
1470 state
= ssi_error_state
;
1476 case ssi_error_state
:
1479 state
= ssi_error_end0_state
;
1488 case ssi_error_end0_state
:
1491 state
= ssi_error_end1_state
;
1495 state
= ssi_error_state
;
1501 case ssi_error_end1_state
:
1504 ctx
->state
= ssi_start_state
;
1506 ctx
->looked
= looked
;
1507 ctx
->copy_end
= copy_end
;
1509 if (ctx
->copy_start
== NULL
&& copy_end
) {
1510 ctx
->copy_start
= ctx
->buf
->pos
;
1513 return NGX_HTTP_SSI_ERROR
;
1516 state
= ssi_error_state
;
1526 ctx
->looked
= looked
;
1528 ctx
->copy_end
= (state
== ssi_start_state
) ? p
: copy_end
;
1530 if (ctx
->copy_start
== NULL
&& ctx
->copy_end
) {
1531 ctx
->copy_start
= ctx
->buf
->pos
;
1539 ngx_http_ssi_get_variable(ngx_http_request_t
*r
, ngx_str_t
*name
,
1543 ngx_list_part_t
*part
;
1544 ngx_http_ssi_var_t
*var
;
1545 ngx_http_ssi_ctx_t
*ctx
;
1547 ctx
= ngx_http_get_module_ctx(r
->main
, ngx_http_ssi_filter_module
);
1553 if (key
>= '0' && key
<= '9') {
1556 if (i
< ctx
->ncaptures
) {
1557 value
= ngx_palloc(r
->pool
, sizeof(ngx_str_t
));
1558 if (value
== NULL
) {
1564 value
->data
= ctx
->captures_data
+ ctx
->captures
[i
];
1565 value
->len
= ctx
->captures
[i
+ 1] - ctx
->captures
[i
];
1573 if (ctx
->variables
== NULL
) {
1577 part
= &ctx
->variables
->part
;
1580 for (i
= 0; /* void */ ; i
++) {
1582 if (i
>= part
->nelts
) {
1583 if (part
->next
== NULL
) {
1592 if (name
->len
!= var
[i
].name
.len
) {
1596 if (key
!= var
[i
].key
) {
1600 if (ngx_strncmp(name
->data
, var
[i
].name
.data
, name
->len
) == 0) {
1601 return &var
[i
].value
;
1610 ngx_http_ssi_evaluate_string(ngx_http_request_t
*r
, ngx_http_ssi_ctx_t
*ctx
,
1611 ngx_str_t
*text
, ngx_uint_t flags
)
1613 u_char ch
, *p
, **value
, *data
, *part_data
;
1614 size_t *size
, len
, prefix
, part_len
;
1615 ngx_str_t var
, *val
;
1617 ngx_uint_t i
, n
, bracket
, quoted
;
1618 ngx_array_t lengths
, values
;
1619 ngx_http_variable_value_t
*vv
;
1621 n
= ngx_http_script_variables_count(text
);
1628 if ((flags
& NGX_HTTP_SSI_ADD_PREFIX
) && text
->data
[0] != '/') {
1630 for (prefix
= r
->uri
.len
; prefix
; prefix
--) {
1631 if (r
->uri
.data
[prefix
- 1] == '/') {
1637 len
= prefix
+ text
->len
;
1639 data
= ngx_pnalloc(r
->pool
, len
);
1644 p
= ngx_copy(data
, r
->uri
.data
, prefix
);
1650 for (i
= 0; i
< text
->len
; i
++) {
1663 if (ch
!= '\\' && ch
!= '\'' && ch
!= '"' && ch
!= '$') {
1671 text
->len
= p
- data
;
1677 if (ngx_array_init(&lengths
, r
->pool
, 8, sizeof(size_t *)) != NGX_OK
) {
1681 if (ngx_array_init(&values
, r
->pool
, 8, sizeof(u_char
*)) != NGX_OK
) {
1688 while (i
< text
->len
) {
1690 if (text
->data
[i
] == '$') {
1694 if (++i
== text
->len
) {
1695 goto invalid_variable
;
1698 if (text
->data
[i
] == '{') {
1701 if (++i
== text
->len
) {
1702 goto invalid_variable
;
1705 var
.data
= &text
->data
[i
];
1709 var
.data
= &text
->data
[i
];
1712 for ( /* void */ ; i
< text
->len
; i
++, var
.len
++) {
1715 if (ch
== '}' && bracket
) {
1721 if ((ch
>= 'A' && ch
<= 'Z')
1722 || (ch
>= 'a' && ch
<= 'z')
1723 || (ch
>= '0' && ch
<= '9')
1733 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1734 "the closing bracket in \"%V\" "
1735 "variable is missing", &var
);
1736 return NGX_HTTP_SSI_ERROR
;
1740 goto invalid_variable
;
1743 key
= ngx_hash_strlow(var
.data
, var
.data
, var
.len
);
1745 val
= ngx_http_ssi_get_variable(r
, &var
, key
);
1748 vv
= ngx_http_get_variable(r
, &var
, key
);
1753 if (vv
->not_found
) {
1757 part_data
= vv
->data
;
1761 part_data
= val
->data
;
1762 part_len
= val
->len
;
1766 part_data
= &text
->data
[i
];
1769 for (p
= part_data
; i
< text
->len
; i
++) {
1786 if (ch
!= '\\' && ch
!= '\'' && ch
!= '"' && ch
!= '$') {
1794 part_len
= p
- part_data
;
1799 size
= ngx_array_push(&lengths
);
1806 value
= ngx_array_push(&values
);
1807 if (value
== NULL
) {
1816 size
= lengths
.elts
;
1817 value
= values
.elts
;
1819 if (flags
& NGX_HTTP_SSI_ADD_PREFIX
) {
1820 for (i
= 0; i
< values
.nelts
; i
++) {
1822 if (*value
[i
] != '/') {
1823 for (prefix
= r
->uri
.len
; prefix
; prefix
--) {
1824 if (r
->uri
.data
[prefix
- 1] == '/') {
1836 p
= ngx_pnalloc(r
->pool
, len
+ ((flags
& NGX_HTTP_SSI_ADD_ZERO
) ? 1 : 0));
1844 p
= ngx_copy(p
, r
->uri
.data
, prefix
);
1846 for (i
= 0; i
< values
.nelts
; i
++) {
1847 p
= ngx_copy(p
, value
[i
], size
[i
]);
1854 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1855 "invalid variable name in \"%V\"", text
);
1857 return NGX_HTTP_SSI_ERROR
;
1862 ngx_http_ssi_regex_match(ngx_http_request_t
*r
, ngx_str_t
*pattern
,
1867 u_char
*p
, errstr
[NGX_MAX_CONF_ERRSTR
];
1870 ngx_str_t
*vv
, name
, value
;
1872 ngx_http_ssi_ctx_t
*ctx
;
1873 ngx_http_ssi_var_t
*var
;
1874 ngx_regex_compile_t rgc
;
1876 ngx_memzero(&rgc
, sizeof(ngx_regex_compile_t
));
1878 rgc
.pattern
= *pattern
;
1880 rgc
.err
.len
= NGX_MAX_CONF_ERRSTR
;
1881 rgc
.err
.data
= errstr
;
1883 if (ngx_regex_compile(&rgc
) != NGX_OK
) {
1884 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0, "%V", &rgc
.err
);
1885 return NGX_HTTP_SSI_ERROR
;
1888 n
= (rgc
.captures
+ 1) * 3;
1890 captures
= ngx_palloc(r
->pool
, n
* sizeof(int));
1891 if (captures
== NULL
) {
1895 rc
= ngx_regex_exec(rgc
.regex
, str
, captures
, n
);
1897 if (rc
< NGX_REGEX_NO_MATCHED
) {
1898 ngx_log_error(NGX_LOG_ALERT
, r
->connection
->log
, 0,
1899 ngx_regex_exec_n
" failed: %i on \"%V\" using \"%V\"",
1901 return NGX_HTTP_SSI_ERROR
;
1904 if (rc
== NGX_REGEX_NO_MATCHED
) {
1905 return NGX_DECLINED
;
1908 ctx
= ngx_http_get_module_ctx(r
->main
, ngx_http_ssi_filter_module
);
1910 ctx
->ncaptures
= rc
;
1911 ctx
->captures
= captures
;
1912 ctx
->captures_data
= str
->data
;
1914 if (rgc
.named_captures
> 0) {
1916 if (ctx
->variables
== NULL
) {
1917 ctx
->variables
= ngx_list_create(r
->pool
, 4,
1918 sizeof(ngx_http_ssi_var_t
));
1919 if (ctx
->variables
== NULL
) {
1924 size
= rgc
.name_size
;
1927 for (i
= 0; i
< (ngx_uint_t
) rgc
.named_captures
; i
++, p
+= size
) {
1930 name
.len
= ngx_strlen(name
.data
);
1932 n
= 2 * ((p
[0] << 8) + p
[1]);
1934 value
.data
= &str
->data
[captures
[n
]];
1935 value
.len
= captures
[n
+ 1] - captures
[n
];
1937 key
= ngx_hash_strlow(name
.data
, name
.data
, name
.len
);
1939 vv
= ngx_http_ssi_get_variable(r
, &name
, key
);
1946 var
= ngx_list_push(ctx
->variables
);
1961 ngx_log_error(NGX_LOG_ALERT
, r
->connection
->log
, 0,
1962 "the using of the regex \"%V\" in SSI requires PCRE library",
1964 return NGX_HTTP_SSI_ERROR
;
1971 ngx_http_ssi_include(ngx_http_request_t
*r
, ngx_http_ssi_ctx_t
*ctx
,
1977 ngx_str_t
*uri
, *file
, *wait
, *set
, *stub
, args
;
1979 ngx_uint_t flags
, i
;
1980 ngx_chain_t
*cl
, *tl
, **ll
, *out
;
1981 ngx_http_request_t
*sr
;
1982 ngx_http_ssi_var_t
*var
;
1983 ngx_http_ssi_ctx_t
*mctx
;
1984 ngx_http_ssi_block_t
*bl
;
1985 ngx_http_post_subrequest_t
*psr
;
1987 uri
= params
[NGX_HTTP_SSI_INCLUDE_VIRTUAL
];
1988 file
= params
[NGX_HTTP_SSI_INCLUDE_FILE
];
1989 wait
= params
[NGX_HTTP_SSI_INCLUDE_WAIT
];
1990 set
= params
[NGX_HTTP_SSI_INCLUDE_SET
];
1991 stub
= params
[NGX_HTTP_SSI_INCLUDE_STUB
];
1994 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1995 "inlcusion may be either virtual=\"%V\" or file=\"%V\"",
1997 return NGX_HTTP_SSI_ERROR
;
2000 if (uri
== NULL
&& file
== NULL
) {
2001 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
2002 "no parameter in \"include\" SSI command");
2003 return NGX_HTTP_SSI_ERROR
;
2007 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
2008 "\"set\" and \"stub\" cannot be used together "
2009 "in \"include\" SSI command");
2010 return NGX_HTTP_SSI_ERROR
;
2015 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
2016 "\"wait\" cannot be used with file=\"%V\"", file
);
2017 return NGX_HTTP_SSI_ERROR
;
2021 && ngx_strncasecmp(wait
->data
, (u_char
*) "no", 2) == 0)
2025 } else if (wait
->len
!= 3
2026 || ngx_strncasecmp(wait
->data
, (u_char
*) "yes", 3) != 0)
2028 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
2029 "invalid value \"%V\" in the \"wait\" parameter",
2031 return NGX_HTTP_SSI_ERROR
;
2037 wait
= (ngx_str_t
*) -1;
2040 rc
= ngx_http_ssi_evaluate_string(r
, ctx
, uri
, NGX_HTTP_SSI_ADD_PREFIX
);
2049 ngx_unescape_uri(&dst
, &src
, uri
->len
, NGX_UNESCAPE_URI
);
2051 len
= (uri
->data
+ uri
->len
) - src
;
2053 dst
= ngx_movemem(dst
, src
, len
);
2056 uri
->len
= dst
- uri
->data
;
2058 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
2059 "ssi include: \"%V\"", uri
);
2061 ngx_str_null(&args
);
2062 flags
= NGX_HTTP_LOG_UNSAFE
;
2064 if (ngx_http_parse_unsafe_uri(r
, uri
, &args
, &flags
) != NGX_OK
) {
2065 return NGX_HTTP_SSI_ERROR
;
2070 mctx
= ngx_http_get_module_ctx(r
->main
, ngx_http_ssi_filter_module
);
2074 bl
= mctx
->blocks
->elts
;
2075 for (i
= 0; i
< mctx
->blocks
->nelts
; i
++) {
2076 if (stub
->len
== bl
[i
].name
.len
2077 && ngx_strncmp(stub
->data
, bl
[i
].name
.data
, stub
->len
) == 0)
2084 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
2085 "\"stub\"=\"%V\" for \"include\" not found", stub
);
2086 return NGX_HTTP_SSI_ERROR
;
2090 psr
= ngx_palloc(r
->pool
, sizeof(ngx_http_post_subrequest_t
));
2095 psr
->handler
= ngx_http_ssi_stub_output
;
2097 if (bl
[i
].count
++) {
2102 for (tl
= bl
[i
].bufs
; tl
; tl
= tl
->next
) {
2106 ctx
->free
= ctx
->free
->next
;
2110 b
= ngx_alloc_buf(r
->pool
);
2115 cl
= ngx_alloc_chain_link(r
->pool
);
2123 ngx_memcpy(b
, tl
->buf
, sizeof(ngx_buf_t
));
2135 psr
->data
= bl
[i
].bufs
;
2140 flags
|= NGX_HTTP_SUBREQUEST_WAITED
;
2144 key
= ngx_hash_strlow(set
->data
, set
->data
, set
->len
);
2146 psr
= ngx_palloc(r
->pool
, sizeof(ngx_http_post_subrequest_t
));
2151 psr
->handler
= ngx_http_ssi_set_variable
;
2152 psr
->data
= ngx_http_ssi_get_variable(r
, set
, key
);
2154 if (psr
->data
== NULL
) {
2156 if (mctx
->variables
== NULL
) {
2157 mctx
->variables
= ngx_list_create(r
->pool
, 4,
2158 sizeof(ngx_http_ssi_var_t
));
2159 if (mctx
->variables
== NULL
) {
2164 var
= ngx_list_push(mctx
->variables
);
2171 var
->value
= ngx_http_ssi_null_string
;
2172 psr
->data
= &var
->value
;
2175 flags
|= NGX_HTTP_SUBREQUEST_IN_MEMORY
|NGX_HTTP_SUBREQUEST_WAITED
;
2178 if (ngx_http_subrequest(r
, uri
, &args
, &sr
, psr
, flags
) != NGX_OK
) {
2179 return NGX_HTTP_SSI_ERROR
;
2182 if (wait
== NULL
&& set
== NULL
) {
2186 if (ctx
->wait
== NULL
) {
2192 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
2193 "can only wait for one subrequest at a time");
2201 ngx_http_ssi_stub_output(ngx_http_request_t
*r
, void *data
, ngx_int_t rc
)
2205 if (rc
== NGX_ERROR
|| r
->connection
->error
|| r
->request_output
) {
2209 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
2210 "ssi stub output: \"%V?%V\"", &r
->uri
, &r
->args
);
2214 if (!r
->header_sent
) {
2215 r
->headers_out
.content_type_len
=
2216 r
->parent
->headers_out
.content_type_len
;
2217 r
->headers_out
.content_type
= r
->parent
->headers_out
.content_type
;
2219 if (ngx_http_send_header(r
) == NGX_ERROR
) {
2224 return ngx_http_output_filter(r
, out
);
2229 ngx_http_ssi_set_variable(ngx_http_request_t
*r
, void *data
, ngx_int_t rc
)
2231 ngx_str_t
*value
= data
;
2234 value
->len
= r
->upstream
->buffer
.last
- r
->upstream
->buffer
.pos
;
2235 value
->data
= r
->upstream
->buffer
.pos
;
2243 ngx_http_ssi_echo(ngx_http_request_t
*r
, ngx_http_ssi_ctx_t
*ctx
,
2250 ngx_str_t
*var
, *value
, *enc
, text
;
2252 ngx_http_variable_value_t
*vv
;
2254 var
= params
[NGX_HTTP_SSI_ECHO_VAR
];
2256 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
2257 "ssi echo \"%V\"", var
);
2259 key
= ngx_hash_strlow(var
->data
, var
->data
, var
->len
);
2261 value
= ngx_http_ssi_get_variable(r
, var
, key
);
2263 if (value
== NULL
) {
2264 vv
= ngx_http_get_variable(r
, var
, key
);
2267 return NGX_HTTP_SSI_ERROR
;
2270 if (!vv
->not_found
) {
2271 text
.data
= vv
->data
;
2277 if (value
== NULL
) {
2278 value
= params
[NGX_HTTP_SSI_ECHO_DEFAULT
];
2280 if (value
== NULL
) {
2281 value
= &ngx_http_ssi_none
;
2283 } else if (value
->len
== 0) {
2288 if (value
->len
== 0) {
2293 enc
= params
[NGX_HTTP_SSI_ECHO_ENCODING
];
2296 if (enc
->len
== 4 && ngx_strncmp(enc
->data
, "none", 4) == 0) {
2298 ctx
->encoding
= NGX_HTTP_SSI_NO_ENCODING
;
2300 } else if (enc
->len
== 3 && ngx_strncmp(enc
->data
, "url", 3) == 0) {
2302 ctx
->encoding
= NGX_HTTP_SSI_URL_ENCODING
;
2304 } else if (enc
->len
== 6 && ngx_strncmp(enc
->data
, "entity", 6) == 0) {
2306 ctx
->encoding
= NGX_HTTP_SSI_ENTITY_ENCODING
;
2309 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
2310 "unknown encoding \"%V\" in the \"echo\" command",
2317 switch (ctx
->encoding
) {
2319 case NGX_HTTP_SSI_URL_ENCODING
:
2320 len
= 2 * ngx_escape_uri(NULL
, value
->data
, value
->len
,
2324 p
= ngx_pnalloc(r
->pool
, value
->len
+ len
);
2326 return NGX_HTTP_SSI_ERROR
;
2329 (void) ngx_escape_uri(p
, value
->data
, value
->len
, NGX_ESCAPE_HTML
);
2335 case NGX_HTTP_SSI_ENTITY_ENCODING
:
2336 len
= ngx_escape_html(NULL
, value
->data
, value
->len
);
2339 p
= ngx_pnalloc(r
->pool
, value
->len
+ len
);
2341 return NGX_HTTP_SSI_ERROR
;
2344 (void) ngx_escape_html(p
, value
->data
, value
->len
);
2350 default: /* NGX_HTTP_SSI_NO_ENCODING */
2355 b
= ngx_calloc_buf(r
->pool
);
2357 return NGX_HTTP_SSI_ERROR
;
2360 cl
= ngx_alloc_chain_link(r
->pool
);
2362 return NGX_HTTP_SSI_ERROR
;
2371 *ctx
->last_out
= cl
;
2372 ctx
->last_out
= &cl
->next
;
2379 ngx_http_ssi_config(ngx_http_request_t
*r
, ngx_http_ssi_ctx_t
*ctx
,
2384 value
= params
[NGX_HTTP_SSI_CONFIG_TIMEFMT
];
2387 ctx
->timefmt
.len
= value
->len
;
2388 ctx
->timefmt
.data
= ngx_pnalloc(r
->pool
, value
->len
+ 1);
2389 if (ctx
->timefmt
.data
== NULL
) {
2390 return NGX_HTTP_SSI_ERROR
;
2393 ngx_cpystrn(ctx
->timefmt
.data
, value
->data
, value
->len
+ 1);
2396 value
= params
[NGX_HTTP_SSI_CONFIG_ERRMSG
];
2399 ctx
->errmsg
= *value
;
2407 ngx_http_ssi_set(ngx_http_request_t
*r
, ngx_http_ssi_ctx_t
*ctx
,
2411 ngx_str_t
*name
, *value
, *vv
;
2412 ngx_http_ssi_var_t
*var
;
2413 ngx_http_ssi_ctx_t
*mctx
;
2415 mctx
= ngx_http_get_module_ctx(r
->main
, ngx_http_ssi_filter_module
);
2417 if (mctx
->variables
== NULL
) {
2418 mctx
->variables
= ngx_list_create(r
->pool
, 4,
2419 sizeof(ngx_http_ssi_var_t
));
2420 if (mctx
->variables
== NULL
) {
2425 name
= params
[NGX_HTTP_SSI_SET_VAR
];
2426 value
= params
[NGX_HTTP_SSI_SET_VALUE
];
2428 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
2429 "ssi set \"%V\" \"%V\"", name
, value
);
2431 rc
= ngx_http_ssi_evaluate_string(r
, ctx
, value
, 0);
2437 key
= ngx_hash_strlow(name
->data
, name
->data
, name
->len
);
2439 vv
= ngx_http_ssi_get_variable(r
, name
, key
);
2446 var
= ngx_list_push(mctx
->variables
);
2453 var
->value
= *value
;
2455 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
2456 "set: \"%V\"=\"%V\"", name
, value
);
2463 ngx_http_ssi_if(ngx_http_request_t
*r
, ngx_http_ssi_ctx_t
*ctx
,
2467 ngx_str_t
*expr
, left
, right
;
2469 ngx_uint_t negative
, noregex
, flags
;
2471 if (ctx
->command
.len
== 2) {
2472 if (ctx
->conditional
) {
2473 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
2474 "the \"if\" command inside the \"if\" command");
2475 return NGX_HTTP_SSI_ERROR
;
2479 if (ctx
->output_chosen
) {
2484 expr
= params
[NGX_HTTP_SSI_IF_EXPR
];
2486 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
2487 "ssi if expr=\"%V\"", expr
);
2489 left
.data
= expr
->data
;
2490 last
= expr
->data
+ expr
->len
;
2492 for (p
= left
.data
; p
< last
; p
++) {
2493 if (*p
>= 'A' && *p
<= 'Z') {
2498 if ((*p
>= 'a' && *p
<= 'z')
2499 || (*p
>= '0' && *p
<= '9')
2500 || *p
== '$' || *p
== '{' || *p
== '}' || *p
== '_'
2501 || *p
== '"' || *p
== '\'')
2509 left
.len
= p
- left
.data
;
2511 while (p
< last
&& *p
== ' ') {
2517 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
2518 "left: \"%V\"", &left
);
2520 rc
= ngx_http_ssi_evaluate_string(r
, ctx
, &left
, flags
);
2526 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
2527 "evaluted left: \"%V\"", &left
);
2532 ctx
->output_chosen
= 1;
2538 ctx
->conditional
= NGX_HTTP_SSI_COND_IF
;
2543 if (p
< last
&& *p
== '=') {
2547 } else if (p
+ 1 < last
&& *p
== '!' && *(p
+ 1) == '=') {
2552 goto invalid_expression
;
2555 while (p
< last
&& *p
== ' ') {
2559 if (p
< last
- 1 && *p
== '/') {
2560 if (*(last
- 1) != '/') {
2561 goto invalid_expression
;
2565 flags
= NGX_HTTP_SSI_ADD_ZERO
;
2573 if (p
< last
- 1 && p
[0] == '\\' && p
[1] == '/') {
2578 right
.len
= last
- p
;
2581 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
2582 "right: \"%V\"", &right
);
2584 rc
= ngx_http_ssi_evaluate_string(r
, ctx
, &right
, flags
);
2590 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
2591 "evaluted right: \"%V\"", &right
);
2594 if (left
.len
!= right
.len
) {
2598 rc
= ngx_strncmp(left
.data
, right
.data
, right
.len
);
2602 right
.data
[right
.len
] = '\0';
2604 rc
= ngx_http_ssi_regex_match(r
, &right
, &left
);
2608 } else if (rc
== NGX_DECLINED
) {
2615 if ((rc
== 0 && !negative
) || (rc
!= 0 && negative
)) {
2617 ctx
->output_chosen
= 1;
2623 ctx
->conditional
= NGX_HTTP_SSI_COND_IF
;
2629 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
2630 "invalid expression in \"%V\"", expr
);
2632 return NGX_HTTP_SSI_ERROR
;
2637 ngx_http_ssi_else(ngx_http_request_t
*r
, ngx_http_ssi_ctx_t
*ctx
,
2640 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
2643 if (ctx
->output_chosen
) {
2649 ctx
->conditional
= NGX_HTTP_SSI_COND_ELSE
;
2656 ngx_http_ssi_endif(ngx_http_request_t
*r
, ngx_http_ssi_ctx_t
*ctx
,
2659 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
2663 ctx
->output_chosen
= 0;
2664 ctx
->conditional
= 0;
2671 ngx_http_ssi_block(ngx_http_request_t
*r
, ngx_http_ssi_ctx_t
*ctx
,
2674 ngx_http_ssi_ctx_t
*mctx
;
2675 ngx_http_ssi_block_t
*bl
;
2677 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
2680 mctx
= ngx_http_get_module_ctx(r
->main
, ngx_http_ssi_filter_module
);
2682 if (mctx
->blocks
== NULL
) {
2683 mctx
->blocks
= ngx_array_create(r
->pool
, 4,
2684 sizeof(ngx_http_ssi_block_t
));
2685 if (mctx
->blocks
== NULL
) {
2686 return NGX_HTTP_SSI_ERROR
;
2690 bl
= ngx_array_push(mctx
->blocks
);
2692 return NGX_HTTP_SSI_ERROR
;
2695 bl
->name
= *params
[NGX_HTTP_SSI_BLOCK_NAME
];
2707 ngx_http_ssi_endblock(ngx_http_request_t
*r
, ngx_http_ssi_ctx_t
*ctx
,
2710 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
2721 ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t
*r
,
2722 ngx_http_variable_value_t
*v
, uintptr_t gmt
)
2724 ngx_http_ssi_ctx_t
*ctx
;
2727 char buf
[NGX_HTTP_SSI_DATE_LEN
];
2730 v
->no_cacheable
= 0;
2733 tp
= ngx_timeofday();
2735 ctx
= ngx_http_get_module_ctx(r
, ngx_http_ssi_filter_module
);
2738 || (ctx
->timefmt
.len
== sizeof("%s") - 1
2739 && ctx
->timefmt
.data
[0] == '%' && ctx
->timefmt
.data
[1] == 's'))
2741 v
->data
= ngx_pnalloc(r
->pool
, NGX_TIME_T_LEN
);
2742 if (v
->data
== NULL
) {
2746 v
->len
= ngx_sprintf(v
->data
, "%T", tp
->sec
) - v
->data
;
2752 ngx_libc_gmtime(tp
->sec
, &tm
);
2754 ngx_libc_localtime(tp
->sec
, &tm
);
2757 v
->len
= strftime(buf
, NGX_HTTP_SSI_DATE_LEN
,
2758 (char *) ctx
->timefmt
.data
, &tm
);
2763 v
->data
= ngx_pnalloc(r
->pool
, v
->len
);
2764 if (v
->data
== NULL
) {
2768 ngx_memcpy(v
->data
, buf
, v
->len
);
2775 ngx_http_ssi_preconfiguration(ngx_conf_t
*cf
)
2778 ngx_http_variable_t
*var
, *v
;
2779 ngx_http_ssi_command_t
*cmd
;
2780 ngx_http_ssi_main_conf_t
*smcf
;
2782 for (v
= ngx_http_ssi_vars
; v
->name
.len
; v
++) {
2783 var
= ngx_http_add_variable(cf
, &v
->name
, v
->flags
);
2788 var
->get_handler
= v
->get_handler
;
2789 var
->data
= v
->data
;
2792 smcf
= ngx_http_conf_get_module_main_conf(cf
, ngx_http_ssi_filter_module
);
2794 for (cmd
= ngx_http_ssi_commands
; cmd
->name
.len
; cmd
++) {
2795 rc
= ngx_hash_add_key(&smcf
->commands
, &cmd
->name
, cmd
,
2796 NGX_HASH_READONLY_KEY
);
2802 if (rc
== NGX_BUSY
) {
2803 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
2804 "conflicting SSI command \"%V\"", &cmd
->name
);
2815 ngx_http_ssi_create_main_conf(ngx_conf_t
*cf
)
2817 ngx_http_ssi_main_conf_t
*smcf
;
2819 smcf
= ngx_pcalloc(cf
->pool
, sizeof(ngx_http_ssi_main_conf_t
));
2824 smcf
->commands
.pool
= cf
->pool
;
2825 smcf
->commands
.temp_pool
= cf
->temp_pool
;
2827 if (ngx_hash_keys_array_init(&smcf
->commands
, NGX_HASH_SMALL
) != NGX_OK
) {
2836 ngx_http_ssi_init_main_conf(ngx_conf_t
*cf
, void *conf
)
2838 ngx_http_ssi_main_conf_t
*smcf
= conf
;
2840 ngx_hash_init_t hash
;
2842 hash
.hash
= &smcf
->hash
;
2843 hash
.key
= ngx_hash_key
;
2844 hash
.max_size
= 1024;
2845 hash
.bucket_size
= ngx_cacheline_size
;
2846 hash
.name
= "ssi_command_hash";
2847 hash
.pool
= cf
->pool
;
2848 hash
.temp_pool
= NULL
;
2850 if (ngx_hash_init(&hash
, smcf
->commands
.keys
.elts
,
2851 smcf
->commands
.keys
.nelts
)
2854 return NGX_CONF_ERROR
;
2862 ngx_http_ssi_create_loc_conf(ngx_conf_t
*cf
)
2864 ngx_http_ssi_loc_conf_t
*slcf
;
2866 slcf
= ngx_pcalloc(cf
->pool
, sizeof(ngx_http_ssi_loc_conf_t
));
2872 * set by ngx_pcalloc():
2874 * conf->types = { NULL };
2875 * conf->types_keys = NULL;
2878 slcf
->enable
= NGX_CONF_UNSET
;
2879 slcf
->silent_errors
= NGX_CONF_UNSET
;
2880 slcf
->ignore_recycled_buffers
= NGX_CONF_UNSET
;
2882 slcf
->min_file_chunk
= NGX_CONF_UNSET_SIZE
;
2883 slcf
->value_len
= NGX_CONF_UNSET_SIZE
;
2890 ngx_http_ssi_merge_loc_conf(ngx_conf_t
*cf
, void *parent
, void *child
)
2892 ngx_http_ssi_loc_conf_t
*prev
= parent
;
2893 ngx_http_ssi_loc_conf_t
*conf
= child
;
2895 ngx_conf_merge_value(conf
->enable
, prev
->enable
, 0);
2896 ngx_conf_merge_value(conf
->silent_errors
, prev
->silent_errors
, 0);
2897 ngx_conf_merge_value(conf
->ignore_recycled_buffers
,
2898 prev
->ignore_recycled_buffers
, 0);
2900 ngx_conf_merge_size_value(conf
->min_file_chunk
, prev
->min_file_chunk
, 1024);
2901 ngx_conf_merge_size_value(conf
->value_len
, prev
->value_len
, 255);
2903 if (ngx_http_merge_types(cf
, &conf
->types_keys
, &conf
->types
,
2904 &prev
->types_keys
, &prev
->types
,
2905 ngx_http_html_default_types
)
2908 return NGX_CONF_ERROR
;
2916 ngx_http_ssi_filter_init(ngx_conf_t
*cf
)
2918 ngx_http_next_header_filter
= ngx_http_top_header_filter
;
2919 ngx_http_top_header_filter
= ngx_http_ssi_header_filter
;
2921 ngx_http_next_body_filter
= ngx_http_top_body_filter
;
2922 ngx_http_top_body_filter
= ngx_http_ssi_body_filter
;