3 * Copyright (C) Igor Sysoev
7 #include <ngx_config.h>
11 #define NGX_HTTP_SSI_ERROR 1
13 #define NGX_HTTP_SSI_DATE_LEN 2048
15 #define NGX_HTTP_SSI_ADD_PREFIX 1
16 #define NGX_HTTP_SSI_ADD_ZERO 2
17 #define NGX_HTTP_SSI_EXPR_TEST 4
22 ngx_flag_t silent_errors
;
23 ngx_flag_t ignore_recycled_buffers
;
25 ngx_array_t
*types
; /* array of ngx_str_t */
27 size_t min_file_chunk
;
29 } ngx_http_ssi_loc_conf_t
;
43 } ngx_http_ssi_block_t
;
58 ssi_double_quoted_value_state
,
59 ssi_quoted_value_state
,
60 ssi_quoted_symbol_state
,
62 ssi_comment_end0_state
,
63 ssi_comment_end1_state
,
67 } ngx_http_ssi_state_e
;
70 static ngx_int_t
ngx_http_ssi_output(ngx_http_request_t
*r
,
71 ngx_http_ssi_ctx_t
*ctx
);
72 static ngx_int_t
ngx_http_ssi_parse(ngx_http_request_t
*r
,
73 ngx_http_ssi_ctx_t
*ctx
);
74 static ngx_str_t
*ngx_http_ssi_get_variable(ngx_http_request_t
*r
,
75 ngx_str_t
*name
, ngx_uint_t key
);
76 static ngx_int_t
ngx_http_ssi_evaluate_string(ngx_http_request_t
*r
,
77 ngx_http_ssi_ctx_t
*ctx
, ngx_str_t
*text
, ngx_uint_t flags
);
79 static ngx_int_t
ngx_http_ssi_include(ngx_http_request_t
*r
,
80 ngx_http_ssi_ctx_t
*ctx
, ngx_str_t
**params
);
81 static ngx_int_t
ngx_http_ssi_stub_output(ngx_http_request_t
*r
, void *data
,
83 static ngx_int_t
ngx_http_ssi_set_variable(ngx_http_request_t
*r
, void *data
,
85 static ngx_int_t
ngx_http_ssi_echo(ngx_http_request_t
*r
,
86 ngx_http_ssi_ctx_t
*ctx
, ngx_str_t
**params
);
87 static ngx_int_t
ngx_http_ssi_config(ngx_http_request_t
*r
,
88 ngx_http_ssi_ctx_t
*ctx
, ngx_str_t
**params
);
89 static ngx_int_t
ngx_http_ssi_set(ngx_http_request_t
*r
,
90 ngx_http_ssi_ctx_t
*ctx
, ngx_str_t
**params
);
91 static ngx_int_t
ngx_http_ssi_if(ngx_http_request_t
*r
,
92 ngx_http_ssi_ctx_t
*ctx
, ngx_str_t
**params
);
93 static ngx_int_t
ngx_http_ssi_else(ngx_http_request_t
*r
,
94 ngx_http_ssi_ctx_t
*ctx
, ngx_str_t
**params
);
95 static ngx_int_t
ngx_http_ssi_endif(ngx_http_request_t
*r
,
96 ngx_http_ssi_ctx_t
*ctx
, ngx_str_t
**params
);
97 static ngx_int_t
ngx_http_ssi_block(ngx_http_request_t
*r
,
98 ngx_http_ssi_ctx_t
*ctx
, ngx_str_t
**params
);
99 static ngx_int_t
ngx_http_ssi_endblock(ngx_http_request_t
*r
,
100 ngx_http_ssi_ctx_t
*ctx
, ngx_str_t
**params
);
102 static ngx_int_t
ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t
*r
,
103 ngx_http_variable_value_t
*v
, uintptr_t gmt
);
105 static char *ngx_http_ssi_types(ngx_conf_t
*cf
, ngx_command_t
*cmd
, void *conf
);
107 static ngx_int_t
ngx_http_ssi_preconfiguration(ngx_conf_t
*cf
);
108 static void *ngx_http_ssi_create_main_conf(ngx_conf_t
*cf
);
109 static char *ngx_http_ssi_init_main_conf(ngx_conf_t
*cf
, void *conf
);
110 static void *ngx_http_ssi_create_loc_conf(ngx_conf_t
*cf
);
111 static char *ngx_http_ssi_merge_loc_conf(ngx_conf_t
*cf
,
112 void *parent
, void *child
);
113 static ngx_int_t
ngx_http_ssi_filter_init(ngx_conf_t
*cf
);
116 static ngx_command_t ngx_http_ssi_filter_commands
[] = {
119 NGX_HTTP_MAIN_CONF
|NGX_HTTP_SRV_CONF
|NGX_HTTP_LOC_CONF
|NGX_HTTP_LIF_CONF
121 ngx_conf_set_flag_slot
,
122 NGX_HTTP_LOC_CONF_OFFSET
,
123 offsetof(ngx_http_ssi_loc_conf_t
, enable
),
126 { ngx_string("ssi_silent_errors"),
127 NGX_HTTP_MAIN_CONF
|NGX_HTTP_SRV_CONF
|NGX_HTTP_LOC_CONF
|NGX_CONF_FLAG
,
128 ngx_conf_set_flag_slot
,
129 NGX_HTTP_LOC_CONF_OFFSET
,
130 offsetof(ngx_http_ssi_loc_conf_t
, silent_errors
),
133 { ngx_string("ssi_ignore_recycled_buffers"),
134 NGX_HTTP_MAIN_CONF
|NGX_HTTP_SRV_CONF
|NGX_HTTP_LOC_CONF
|NGX_CONF_FLAG
,
135 ngx_conf_set_flag_slot
,
136 NGX_HTTP_LOC_CONF_OFFSET
,
137 offsetof(ngx_http_ssi_loc_conf_t
, ignore_recycled_buffers
),
140 { ngx_string("ssi_min_file_chunk"),
141 NGX_HTTP_MAIN_CONF
|NGX_HTTP_SRV_CONF
|NGX_HTTP_LOC_CONF
|NGX_CONF_FLAG
,
142 ngx_conf_set_size_slot
,
143 NGX_HTTP_LOC_CONF_OFFSET
,
144 offsetof(ngx_http_ssi_loc_conf_t
, min_file_chunk
),
147 { ngx_string("ssi_value_length"),
148 NGX_HTTP_MAIN_CONF
|NGX_HTTP_SRV_CONF
|NGX_HTTP_LOC_CONF
|NGX_CONF_FLAG
,
149 ngx_conf_set_size_slot
,
150 NGX_HTTP_LOC_CONF_OFFSET
,
151 offsetof(ngx_http_ssi_loc_conf_t
, value_len
),
154 { ngx_string("ssi_types"),
155 NGX_HTTP_MAIN_CONF
|NGX_HTTP_SRV_CONF
|NGX_HTTP_LOC_CONF
|NGX_CONF_1MORE
,
157 NGX_HTTP_LOC_CONF_OFFSET
,
166 static ngx_http_module_t ngx_http_ssi_filter_module_ctx
= {
167 ngx_http_ssi_preconfiguration
, /* preconfiguration */
168 ngx_http_ssi_filter_init
, /* postconfiguration */
170 ngx_http_ssi_create_main_conf
, /* create main configuration */
171 ngx_http_ssi_init_main_conf
, /* init main configuration */
173 NULL
, /* create server configuration */
174 NULL
, /* merge server configuration */
176 ngx_http_ssi_create_loc_conf
, /* create location configuration */
177 ngx_http_ssi_merge_loc_conf
/* merge location configuration */
181 ngx_module_t ngx_http_ssi_filter_module
= {
183 &ngx_http_ssi_filter_module_ctx
, /* module context */
184 ngx_http_ssi_filter_commands
, /* module directives */
185 NGX_HTTP_MODULE
, /* module type */
186 NULL
, /* init master */
187 NULL
, /* init module */
188 NULL
, /* init process */
189 NULL
, /* init thread */
190 NULL
, /* exit thread */
191 NULL
, /* exit process */
192 NULL
, /* exit master */
193 NGX_MODULE_V1_PADDING
197 static ngx_http_output_header_filter_pt ngx_http_next_header_filter
;
198 static ngx_http_output_body_filter_pt ngx_http_next_body_filter
;
201 static u_char ngx_http_ssi_string
[] = "<!--";
203 static ngx_str_t ngx_http_ssi_none
= ngx_string("(none)");
204 static ngx_str_t ngx_http_ssi_null_string
= ngx_null_string
;
207 #define NGX_HTTP_SSI_INCLUDE_VIRTUAL 0
208 #define NGX_HTTP_SSI_INCLUDE_FILE 1
209 #define NGX_HTTP_SSI_INCLUDE_WAIT 2
210 #define NGX_HTTP_SSI_INCLUDE_SET 3
211 #define NGX_HTTP_SSI_INCLUDE_STUB 4
213 #define NGX_HTTP_SSI_ECHO_VAR 0
214 #define NGX_HTTP_SSI_ECHO_DEFAULT 1
215 #define NGX_HTTP_SSI_ECHO_ENCODING 2
217 #define NGX_HTTP_SSI_CONFIG_ERRMSG 0
218 #define NGX_HTTP_SSI_CONFIG_TIMEFMT 1
220 #define NGX_HTTP_SSI_SET_VAR 0
221 #define NGX_HTTP_SSI_SET_VALUE 1
223 #define NGX_HTTP_SSI_IF_EXPR 0
225 #define NGX_HTTP_SSI_BLOCK_NAME 0
228 static ngx_http_ssi_param_t ngx_http_ssi_include_params
[] = {
229 { ngx_string("virtual"), NGX_HTTP_SSI_INCLUDE_VIRTUAL
, 0, 0 },
230 { ngx_string("file"), NGX_HTTP_SSI_INCLUDE_FILE
, 0, 0 },
231 { ngx_string("wait"), NGX_HTTP_SSI_INCLUDE_WAIT
, 0, 0 },
232 { ngx_string("set"), NGX_HTTP_SSI_INCLUDE_SET
, 0, 0 },
233 { ngx_string("stub"), NGX_HTTP_SSI_INCLUDE_STUB
, 0, 0 },
234 { ngx_null_string
, 0, 0, 0 }
238 static ngx_http_ssi_param_t ngx_http_ssi_echo_params
[] = {
239 { ngx_string("var"), NGX_HTTP_SSI_ECHO_VAR
, 1, 0 },
240 { ngx_string("default"), NGX_HTTP_SSI_ECHO_DEFAULT
, 0, 0 },
241 { ngx_string("encoding"), NGX_HTTP_SSI_ECHO_ENCODING
, 0, 0 },
242 { ngx_null_string
, 0, 0, 0 }
246 static ngx_http_ssi_param_t ngx_http_ssi_config_params
[] = {
247 { ngx_string("errmsg"), NGX_HTTP_SSI_CONFIG_ERRMSG
, 0, 0 },
248 { ngx_string("timefmt"), NGX_HTTP_SSI_CONFIG_TIMEFMT
, 0, 0 },
249 { ngx_null_string
, 0, 0, 0 }
253 static ngx_http_ssi_param_t ngx_http_ssi_set_params
[] = {
254 { ngx_string("var"), NGX_HTTP_SSI_SET_VAR
, 1, 0 },
255 { ngx_string("value"), NGX_HTTP_SSI_SET_VALUE
, 1, 0 },
256 { ngx_null_string
, 0, 0, 0 }
260 static ngx_http_ssi_param_t ngx_http_ssi_if_params
[] = {
261 { ngx_string("expr"), NGX_HTTP_SSI_IF_EXPR
, 1, 0 },
262 { ngx_null_string
, 0, 0, 0 }
266 static ngx_http_ssi_param_t ngx_http_ssi_block_params
[] = {
267 { ngx_string("name"), NGX_HTTP_SSI_BLOCK_NAME
, 1, 0 },
268 { ngx_null_string
, 0, 0, 0 }
272 static ngx_http_ssi_param_t ngx_http_ssi_no_params
[] = {
273 { ngx_null_string
, 0, 0, 0 }
277 static ngx_http_ssi_command_t ngx_http_ssi_commands
[] = {
278 { ngx_string("include"), ngx_http_ssi_include
,
279 ngx_http_ssi_include_params
, 0, 0, 1 },
280 { ngx_string("echo"), ngx_http_ssi_echo
,
281 ngx_http_ssi_echo_params
, 0, 0, 0 },
282 { ngx_string("config"), ngx_http_ssi_config
,
283 ngx_http_ssi_config_params
, 0, 0, 0 },
284 { ngx_string("set"), ngx_http_ssi_set
, ngx_http_ssi_set_params
, 0, 0, 0 },
286 { ngx_string("if"), ngx_http_ssi_if
, ngx_http_ssi_if_params
, 0, 0, 0 },
287 { ngx_string("elif"), ngx_http_ssi_if
, ngx_http_ssi_if_params
,
288 NGX_HTTP_SSI_COND_IF
, 0, 0 },
289 { ngx_string("else"), ngx_http_ssi_else
, ngx_http_ssi_no_params
,
290 NGX_HTTP_SSI_COND_IF
, 0, 0 },
291 { ngx_string("endif"), ngx_http_ssi_endif
, ngx_http_ssi_no_params
,
292 NGX_HTTP_SSI_COND_ELSE
, 0, 0 },
294 { ngx_string("block"), ngx_http_ssi_block
,
295 ngx_http_ssi_block_params
, 0, 0, 0 },
296 { ngx_string("endblock"), ngx_http_ssi_endblock
,
297 ngx_http_ssi_no_params
, 0, 1, 0 },
299 { ngx_null_string
, NULL
, NULL
, 0, 0, 0 }
303 static ngx_http_variable_t ngx_http_ssi_vars
[] = {
305 { ngx_string("date_local"), NULL
, ngx_http_ssi_date_gmt_local_variable
, 0,
306 NGX_HTTP_VAR_NOCACHEABLE
, 0 },
308 { ngx_string("date_gmt"), NULL
, ngx_http_ssi_date_gmt_local_variable
, 1,
309 NGX_HTTP_VAR_NOCACHEABLE
, 0 },
311 { ngx_null_string
, NULL
, NULL
, 0, 0, 0 }
317 ngx_http_ssi_header_filter(ngx_http_request_t
*r
)
321 ngx_http_ssi_ctx_t
*ctx
;
322 ngx_http_ssi_loc_conf_t
*slcf
;
324 slcf
= ngx_http_get_module_loc_conf(r
, ngx_http_ssi_filter_module
);
327 || r
->headers_out
.content_type
.len
== 0
328 || r
->headers_out
.content_length_n
== 0)
330 return ngx_http_next_header_filter(r
);
334 type
= slcf
->types
->elts
;
335 for (i
= 0; i
< slcf
->types
->nelts
; i
++) {
336 if (r
->headers_out
.content_type
.len
>= type
[i
].len
337 && ngx_strncasecmp(r
->headers_out
.content_type
.data
,
338 type
[i
].data
, type
[i
].len
) == 0)
344 return ngx_http_next_header_filter(r
);
349 ctx
= ngx_pcalloc(r
->pool
, sizeof(ngx_http_ssi_ctx_t
));
354 ngx_http_set_ctx(r
, ctx
, ngx_http_ssi_filter_module
);
357 ctx
->value_len
= slcf
->value_len
;
358 ctx
->last_out
= &ctx
->out
;
360 ctx
->encoding
= NGX_HTTP_SSI_ENTITY_ENCODING
;
363 ctx
->params
.elts
= ctx
->params_array
;
364 ctx
->params
.size
= sizeof(ngx_table_elt_t
);
365 ctx
->params
.nalloc
= NGX_HTTP_SSI_PARAMS_N
;
366 ctx
->params
.pool
= r
->pool
;
368 ctx
->timefmt
.len
= sizeof("%A, %d-%b-%Y %H:%M:%S %Z") - 1;
369 ctx
->timefmt
.data
= (u_char
*) "%A, %d-%b-%Y %H:%M:%S %Z";
372 sizeof("[an error occurred while processing the directive]") - 1;
373 ctx
->errmsg
.data
= (u_char
*)
374 "[an error occurred while processing the directive]";
376 r
->filter_need_in_memory
= 1;
379 ngx_http_clear_content_length(r
);
380 ngx_http_clear_last_modified(r
);
383 return ngx_http_next_header_filter(r
);
388 ngx_http_ssi_body_filter(ngx_http_request_t
*r
, ngx_chain_t
*in
)
394 ngx_chain_t
*cl
, **ll
;
395 ngx_table_elt_t
*param
;
396 ngx_http_request_t
*pr
;
397 ngx_http_ssi_ctx_t
*ctx
, *mctx
;
398 ngx_http_ssi_block_t
*bl
;
399 ngx_http_ssi_param_t
*prm
;
400 ngx_http_ssi_command_t
*cmd
;
401 ngx_http_ssi_loc_conf_t
*slcf
;
402 ngx_http_ssi_main_conf_t
*smcf
;
403 ngx_str_t
*params
[NGX_HTTP_SSI_MAX_PARAMS
+ 1];
405 ctx
= ngx_http_get_module_ctx(r
, ngx_http_ssi_filter_module
);
411 && ctx
->busy
== NULL
))
413 return ngx_http_next_body_filter(r
, in
);
416 /* add the incoming chain to the chain ctx->in */
419 if (ngx_chain_add_copy(r
->pool
, &ctx
->in
, in
) == NGX_ERROR
) {
425 if (r
->connection
->data
!= r
) {
426 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
427 "http ssi filter \"%V\" wait", &r
->uri
);
431 for (pr
= ctx
->wait
->parent
; pr
; pr
= pr
->parent
) {
433 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
434 "http ssi filter \"%V\" flush", &r
->uri
);
436 rc
= ngx_http_next_body_filter(r
, NULL
);
438 if (ctx
->wait
->done
) {
442 if (rc
== NGX_ERROR
|| rc
== NGX_AGAIN
) {
450 if (ctx
->wait
== r
) {
451 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
452 "http ssi filter \"%V\" continue", &r
->uri
);
457 slcf
= ngx_http_get_module_loc_conf(r
, ngx_http_ssi_filter_module
);
459 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
460 "http ssi filter \"%V\"", &r
->uri
);
462 while (ctx
->in
|| ctx
->buf
) {
464 if (ctx
->buf
== NULL
){
465 ctx
->buf
= ctx
->in
->buf
;
466 ctx
->in
= ctx
->in
->next
;
467 ctx
->pos
= ctx
->buf
->pos
;
470 if (ctx
->state
== ssi_start_state
) {
471 ctx
->copy_start
= ctx
->pos
;
472 ctx
->copy_end
= ctx
->pos
;
477 while (ctx
->pos
< ctx
->buf
->last
) {
479 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
480 "saved: %d state: %d", ctx
->saved
, ctx
->state
);
482 rc
= ngx_http_ssi_parse(r
, ctx
);
484 ngx_log_debug4(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
485 "parse: %d, looked: %d %p-%p",
486 rc
, ctx
->looked
, ctx
->copy_start
, ctx
->copy_end
);
488 if (rc
== NGX_ERROR
) {
492 if (ctx
->copy_start
!= ctx
->copy_end
) {
496 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
497 "saved: %d", ctx
->saved
);
503 ctx
->free
= ctx
->free
->next
;
505 ngx_memzero(b
, sizeof(ngx_buf_t
));
508 b
= ngx_calloc_buf(r
->pool
);
513 cl
= ngx_alloc_chain_link(r
->pool
);
522 b
->pos
= ngx_http_ssi_string
;
523 b
->last
= ngx_http_ssi_string
+ ctx
->saved
;
526 ctx
->last_out
= &cl
->next
;
533 ctx
->free
= ctx
->free
->next
;
537 b
= ngx_alloc_buf(r
->pool
);
542 cl
= ngx_alloc_chain_link(r
->pool
);
550 ngx_memcpy(b
, ctx
->buf
, sizeof(ngx_buf_t
));
552 b
->pos
= ctx
->copy_start
;
553 b
->last
= ctx
->copy_end
;
559 if (slcf
->min_file_chunk
< (size_t) (b
->last
- b
->pos
))
561 b
->file_last
= b
->file_pos
562 + (b
->last
- ctx
->buf
->pos
);
563 b
->file_pos
+= b
->pos
- ctx
->buf
->pos
;
572 ctx
->last_out
= &cl
->next
;
576 && ctx
->saved
+ (ctx
->copy_end
- ctx
->copy_start
))
578 b
= ngx_create_temp_buf(r
->pool
,
579 ctx
->saved
+ (ctx
->copy_end
- ctx
->copy_start
));
586 b
->last
= ngx_cpymem(b
->pos
, ngx_http_ssi_string
,
590 b
->last
= ngx_cpymem(b
->last
, ctx
->copy_start
,
591 ctx
->copy_end
- ctx
->copy_start
);
593 cl
= ngx_alloc_chain_link(r
->pool
);
603 mctx
= ngx_http_get_module_ctx(r
->main
,
604 ngx_http_ssi_filter_module
);
605 bl
= mctx
->blocks
->elts
;
606 for (ll
= &bl
[mctx
->blocks
->nelts
- 1].bufs
;
620 if (ctx
->state
== ssi_start_state
) {
621 ctx
->copy_start
= ctx
->pos
;
622 ctx
->copy_end
= ctx
->pos
;
625 ctx
->copy_start
= NULL
;
626 ctx
->copy_end
= NULL
;
629 if (rc
== NGX_AGAIN
) {
638 smcf
= ngx_http_get_module_main_conf(r
,
639 ngx_http_ssi_filter_module
);
641 cmd
= ngx_hash_find(&smcf
->hash
, ctx
->key
, ctx
->command
.data
,
646 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
647 "invalid SSI command: \"%V\"",
656 && (ctx
->conditional
== 0
657 || ctx
->conditional
> cmd
->conditional
))
659 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
660 "invalid context of SSI command: \"%V\"",
665 if (!ctx
->output
&& !cmd
->block
) {
669 /* reconstruct the SSI command text */
671 len
= 5 + ctx
->command
.len
+ 4;
673 param
= ctx
->params
.elts
;
674 for (i
= 0; i
< ctx
->params
.nelts
; i
++) {
675 len
+= 1 + param
[i
].key
.len
+ 2
676 + param
[i
].value
.len
+ 1;
679 b
= ngx_create_temp_buf(r
->pool
, len
);
685 cl
= ngx_alloc_chain_link(r
->pool
);
699 b
->last
= ngx_cpymem(b
->last
, ctx
->command
.data
,
702 for (i
= 0; i
< ctx
->params
.nelts
; i
++) {
704 b
->last
= ngx_cpymem(b
->last
, param
[i
].key
.data
,
708 b
->last
= ngx_cpymem(b
->last
, param
[i
].value
.data
,
718 mctx
= ngx_http_get_module_ctx(r
->main
,
719 ngx_http_ssi_filter_module
);
720 bl
= mctx
->blocks
->elts
;
721 for (ll
= &bl
[mctx
->blocks
->nelts
- 1].bufs
;
735 if (cmd
->conditional
== 0) {
740 if (ctx
->params
.nelts
> NGX_HTTP_SSI_MAX_PARAMS
) {
741 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
742 "too many SSI command paramters: \"%V\"",
748 (NGX_HTTP_SSI_MAX_PARAMS
+ 1) * sizeof(ngx_str_t
*));
750 param
= ctx
->params
.elts
;
752 for (i
= 0; i
< ctx
->params
.nelts
; i
++) {
754 for (prm
= cmd
->params
; prm
->name
.len
; prm
++) {
756 if (param
[i
].key
.len
!= prm
->name
.len
757 || ngx_strncmp(param
[i
].key
.data
, prm
->name
.data
,
763 if (!prm
->multiple
) {
764 if (params
[prm
->index
]) {
765 ngx_log_error(NGX_LOG_ERR
,
766 r
->connection
->log
, 0,
767 "duplicate \"%V\" parameter "
768 "in \"%V\" SSI command",
769 ¶m
[i
].key
, &ctx
->command
);
774 params
[prm
->index
] = ¶m
[i
].value
;
779 for (index
= prm
->index
; params
[index
]; index
++) {
783 params
[index
] = ¶m
[i
].value
;
788 if (prm
->name
.len
== 0) {
789 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
790 "invalid parameter name: \"%V\" "
791 "in \"%V\" SSI command",
792 ¶m
[i
].key
, &ctx
->command
);
798 for (prm
= cmd
->params
; prm
->name
.len
; prm
++) {
799 if (prm
->mandatory
&& params
[prm
->index
] == 0) {
800 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
801 "mandatory \"%V\" parameter is absent "
802 "in \"%V\" SSI command",
803 &prm
->name
, &ctx
->command
);
812 rc
= ngx_http_ssi_output(r
, ctx
);
815 rc
= ngx_http_next_body_filter(r
, NULL
);
818 if (rc
== NGX_ERROR
) {
823 rc
= cmd
->handler(r
, ctx
, params
);
829 if (rc
== NGX_DONE
|| rc
== NGX_AGAIN
|| rc
== NGX_ERROR
) {
835 /* rc == NGX_HTTP_SSI_ERROR */
839 if (slcf
->silent_errors
) {
845 ctx
->free
= ctx
->free
->next
;
847 ngx_memzero(b
, sizeof(ngx_buf_t
));
850 b
= ngx_calloc_buf(r
->pool
);
855 cl
= ngx_alloc_chain_link(r
->pool
);
864 b
->pos
= ctx
->errmsg
.data
;
865 b
->last
= ctx
->errmsg
.data
+ ctx
->errmsg
.len
;
869 ctx
->last_out
= &cl
->next
;
874 if (ctx
->buf
->last_buf
|| ngx_buf_in_memory(ctx
->buf
)) {
878 ctx
->free
= ctx
->free
->next
;
880 ngx_memzero(b
, sizeof(ngx_buf_t
));
883 b
= ngx_calloc_buf(r
->pool
);
888 cl
= ngx_alloc_chain_link(r
->pool
);
900 ctx
->last_out
= &cl
->next
;
903 b
->last_buf
= ctx
->buf
->last_buf
;
904 b
->shadow
= ctx
->buf
;
906 if (slcf
->ignore_recycled_buffers
== 0) {
907 b
->recycled
= ctx
->buf
->recycled
;
913 ctx
->saved
= ctx
->looked
;
916 if (ctx
->out
== NULL
&& ctx
->busy
== NULL
) {
920 return ngx_http_ssi_output(r
, ctx
);
925 ngx_http_ssi_output(ngx_http_request_t
*r
, ngx_http_ssi_ctx_t
*ctx
)
933 for (cl
= ctx
->out
; cl
; cl
= cl
->next
) {
934 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
935 "ssi out: %p %p", cl
->buf
, cl
->buf
->pos
);
937 ngx_log_error(NGX_LOG_ALERT
, r
->connection
->log
, 0,
938 "the same buf was used in ssi");
946 rc
= ngx_http_next_body_filter(r
, ctx
->out
);
948 if (ctx
->busy
== NULL
) {
949 ctx
->busy
= ctx
->out
;
952 for (cl
= ctx
->busy
; cl
->next
; cl
= cl
->next
) { /* void */ }
957 ctx
->last_out
= &ctx
->out
;
964 if (ngx_buf_size(b
) != 0) {
968 #if (NGX_HAVE_WRITE_ZEROCOPY)
969 if (b
->zerocopy_busy
) {
975 b
->shadow
->pos
= b
->shadow
->last
;
978 ctx
->busy
= cl
->next
;
980 if (ngx_buf_in_memory(b
) || b
->in_file
) {
981 /* add data bufs only to the free buf chain */
983 cl
->next
= ctx
->free
;
988 if (ctx
->in
|| ctx
->buf
) {
989 r
->buffered
|= NGX_HTTP_SSI_BUFFERED
;
992 r
->buffered
&= ~NGX_HTTP_SSI_BUFFERED
;
1000 ngx_http_ssi_parse(ngx_http_request_t
*r
, ngx_http_ssi_ctx_t
*ctx
)
1002 u_char
*p
, *value
, *last
, *copy_end
, ch
;
1004 ngx_http_ssi_state_e state
;
1007 looked
= ctx
->looked
;
1008 last
= ctx
->buf
->last
;
1009 copy_end
= ctx
->copy_end
;
1011 for (p
= ctx
->pos
; p
< last
; p
++) {
1015 if (state
== ssi_start_state
) {
1017 /* the tight loop */
1023 state
= ssi_tag_state
;
1037 ctx
->looked
= looked
;
1040 if (ctx
->copy_start
== NULL
) {
1041 ctx
->copy_start
= ctx
->buf
->pos
;
1053 case ssi_start_state
:
1060 state
= ssi_comment0_state
;
1070 state
= ssi_start_state
;
1076 case ssi_comment0_state
:
1080 state
= ssi_comment1_state
;
1086 state
= ssi_tag_state
;
1092 state
= ssi_start_state
;
1098 case ssi_comment1_state
:
1102 state
= ssi_sharp_state
;
1108 state
= ssi_tag_state
;
1114 state
= ssi_start_state
;
1120 case ssi_sharp_state
:
1123 if (p
- ctx
->pos
< 4) {
1127 state
= ssi_precommand_state
;
1133 state
= ssi_tag_state
;
1139 state
= ssi_start_state
;
1145 case ssi_precommand_state
:
1154 ctx
->command
.len
= 1;
1155 ctx
->command
.data
= ngx_pnalloc(r
->pool
,
1156 NGX_HTTP_SSI_COMMAND_LEN
);
1157 if (ctx
->command
.data
== NULL
) {
1161 ctx
->command
.data
[0] = ch
;
1164 ctx
->key
= ngx_hash(ctx
->key
, ch
);
1166 ctx
->params
.nelts
= 0;
1168 state
= ssi_command_state
;
1174 case ssi_command_state
:
1180 state
= ssi_preparam_state
;
1184 state
= ssi_comment_end0_state
;
1188 if (ctx
->command
.len
== NGX_HTTP_SSI_COMMAND_LEN
) {
1189 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1190 "the \"%V%c...\" SSI command is too long",
1193 state
= ssi_error_state
;
1197 ctx
->command
.data
[ctx
->command
.len
++] = ch
;
1198 ctx
->key
= ngx_hash(ctx
->key
, ch
);
1203 case ssi_preparam_state
:
1212 state
= ssi_comment_end0_state
;
1216 ctx
->param
= ngx_array_push(&ctx
->params
);
1217 if (ctx
->param
== NULL
) {
1221 ctx
->param
->key
.len
= 1;
1222 ctx
->param
->key
.data
= ngx_pnalloc(r
->pool
,
1223 NGX_HTTP_SSI_PARAM_LEN
);
1224 if (ctx
->param
->key
.data
== NULL
) {
1228 ctx
->param
->key
.data
[0] = ch
;
1230 ctx
->param
->value
.len
= 0;
1232 if (ctx
->value_buf
== NULL
) {
1233 ctx
->param
->value
.data
= ngx_pnalloc(r
->pool
,
1235 if (ctx
->param
->value
.data
== NULL
) {
1240 ctx
->param
->value
.data
= ctx
->value_buf
;
1243 state
= ssi_param_state
;
1249 case ssi_param_state
:
1255 state
= ssi_preequal_state
;
1259 state
= ssi_prevalue_state
;
1263 state
= ssi_error_end0_state
;
1265 ctx
->param
->key
.data
[ctx
->param
->key
.len
++] = ch
;
1266 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1267 "invalid \"%V\" parameter in \"%V\" SSI command",
1268 &ctx
->param
->key
, &ctx
->command
);
1272 if (ctx
->param
->key
.len
== NGX_HTTP_SSI_PARAM_LEN
) {
1273 state
= ssi_error_state
;
1274 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1275 "too long \"%V%c...\" parameter in "
1276 "\"%V\" SSI command",
1277 &ctx
->param
->key
, ch
, &ctx
->command
);
1281 ctx
->param
->key
.data
[ctx
->param
->key
.len
++] = ch
;
1286 case ssi_preequal_state
:
1295 state
= ssi_prevalue_state
;
1300 state
= ssi_error_end0_state
;
1302 state
= ssi_error_state
;
1305 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1306 "unexpected \"%c\" symbol after \"%V\" "
1307 "parameter in \"%V\" SSI command",
1308 ch
, &ctx
->param
->key
, &ctx
->command
);
1314 case ssi_prevalue_state
:
1323 state
= ssi_double_quoted_value_state
;
1327 state
= ssi_quoted_value_state
;
1332 state
= ssi_error_end0_state
;
1334 state
= ssi_error_state
;
1337 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1338 "unexpected \"%c\" symbol before value of "
1339 "\"%V\" parameter in \"%V\" SSI command",
1340 ch
, &ctx
->param
->key
, &ctx
->command
);
1346 case ssi_double_quoted_value_state
:
1349 state
= ssi_postparam_state
;
1353 ctx
->saved_state
= ssi_double_quoted_value_state
;
1354 state
= ssi_quoted_symbol_state
;
1359 if (ctx
->param
->value
.len
== ctx
->value_len
) {
1360 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1361 "too long \"%V%c...\" value of \"%V\" "
1362 "parameter in \"%V\" SSI command",
1363 &ctx
->param
->value
, ch
, &ctx
->param
->key
,
1365 state
= ssi_error_state
;
1369 ctx
->param
->value
.data
[ctx
->param
->value
.len
++] = ch
;
1374 case ssi_quoted_value_state
:
1377 state
= ssi_postparam_state
;
1381 ctx
->saved_state
= ssi_quoted_value_state
;
1382 state
= ssi_quoted_symbol_state
;
1387 if (ctx
->param
->value
.len
== ctx
->value_len
) {
1388 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1389 "too long \"%V%c...\" value of \"%V\" "
1390 "parameter in \"%V\" SSI command",
1391 &ctx
->param
->value
, ch
, &ctx
->param
->key
,
1393 state
= ssi_error_state
;
1397 ctx
->param
->value
.data
[ctx
->param
->value
.len
++] = ch
;
1402 case ssi_quoted_symbol_state
:
1403 state
= ctx
->saved_state
;
1405 ctx
->param
->value
.data
[ctx
->param
->value
.len
++] = ch
;
1409 case ssi_postparam_state
:
1411 if (ctx
->param
->value
.len
+ 1 < ctx
->value_len
/ 2) {
1412 value
= ngx_pnalloc(r
->pool
, ctx
->param
->value
.len
+ 1);
1413 if (value
== NULL
) {
1417 ngx_memcpy(value
, ctx
->param
->value
.data
,
1418 ctx
->param
->value
.len
);
1420 ctx
->value_buf
= ctx
->param
->value
.data
;
1421 ctx
->param
->value
.data
= value
;
1424 ctx
->value_buf
= NULL
;
1432 state
= ssi_preparam_state
;
1436 state
= ssi_comment_end0_state
;
1440 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1441 "unexpected \"%c\" symbol after \"%V\" value "
1442 "of \"%V\" parameter in \"%V\" SSI command",
1443 ch
, &ctx
->param
->value
, &ctx
->param
->key
,
1445 state
= ssi_error_state
;
1451 case ssi_comment_end0_state
:
1454 state
= ssi_comment_end1_state
;
1458 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1459 "unexpected \"%c\" symbol in \"%V\" SSI command",
1461 state
= ssi_error_state
;
1467 case ssi_comment_end1_state
:
1470 ctx
->state
= ssi_start_state
;
1472 ctx
->looked
= looked
;
1473 ctx
->copy_end
= copy_end
;
1475 if (ctx
->copy_start
== NULL
&& copy_end
) {
1476 ctx
->copy_start
= ctx
->buf
->pos
;
1482 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1483 "unexpected \"%c\" symbol in \"%V\" SSI command",
1485 state
= ssi_error_state
;
1491 case ssi_error_state
:
1494 state
= ssi_error_end0_state
;
1503 case ssi_error_end0_state
:
1506 state
= ssi_error_end1_state
;
1510 state
= ssi_error_state
;
1516 case ssi_error_end1_state
:
1519 ctx
->state
= ssi_start_state
;
1521 ctx
->looked
= looked
;
1522 ctx
->copy_end
= copy_end
;
1524 if (ctx
->copy_start
== NULL
&& copy_end
) {
1525 ctx
->copy_start
= ctx
->buf
->pos
;
1528 return NGX_HTTP_SSI_ERROR
;
1531 state
= ssi_error_state
;
1541 ctx
->looked
= looked
;
1543 ctx
->copy_end
= (state
== ssi_start_state
) ? p
: copy_end
;
1545 if (ctx
->copy_start
== NULL
&& ctx
->copy_end
) {
1546 ctx
->copy_start
= ctx
->buf
->pos
;
1554 ngx_http_ssi_get_variable(ngx_http_request_t
*r
, ngx_str_t
*name
,
1558 ngx_list_part_t
*part
;
1559 ngx_http_ssi_var_t
*var
;
1560 ngx_http_ssi_ctx_t
*ctx
;
1562 ctx
= ngx_http_get_module_ctx(r
->main
, ngx_http_ssi_filter_module
);
1564 if (ctx
->variables
== NULL
) {
1568 part
= &ctx
->variables
->part
;
1571 for (i
= 0; /* void */ ; i
++) {
1573 if (i
>= part
->nelts
) {
1574 if (part
->next
== NULL
) {
1583 if (name
->len
!= var
[i
].name
.len
) {
1587 if (key
!= var
[i
].key
) {
1591 if (ngx_strncmp(name
->data
, var
[i
].name
.data
, name
->len
) == 0) {
1592 return &var
[i
].value
;
1601 ngx_http_ssi_evaluate_string(ngx_http_request_t
*r
, ngx_http_ssi_ctx_t
*ctx
,
1602 ngx_str_t
*text
, ngx_uint_t flags
)
1604 u_char ch
, *p
, **value
, *data
, *part_data
;
1605 size_t *size
, len
, prefix
, part_len
;
1606 ngx_str_t var
, *val
;
1608 ngx_uint_t i
, n
, bracket
, quoted
;
1609 ngx_array_t lengths
, values
;
1610 ngx_http_variable_value_t
*vv
;
1612 n
= ngx_http_script_variables_count(text
);
1619 if ((flags
& NGX_HTTP_SSI_ADD_PREFIX
) && text
->data
[0] != '/') {
1621 for (prefix
= r
->uri
.len
; prefix
; prefix
--) {
1622 if (r
->uri
.data
[prefix
- 1] == '/') {
1628 len
= prefix
+ text
->len
;
1630 data
= ngx_pnalloc(r
->pool
, len
);
1635 p
= ngx_copy(data
, r
->uri
.data
, prefix
);
1641 for (i
= 0; i
< text
->len
; i
++) {
1654 if (ch
!= '\\' && ch
!= '\'' && ch
!= '"' && ch
!= '$') {
1662 text
->len
= p
- data
;
1668 if (ngx_array_init(&lengths
, r
->pool
, 8, sizeof(size_t *)) != NGX_OK
) {
1672 if (ngx_array_init(&values
, r
->pool
, 8, sizeof(u_char
*)) != NGX_OK
) {
1679 while (i
< text
->len
) {
1681 if (text
->data
[i
] == '$') {
1685 if (++i
== text
->len
) {
1686 goto invalid_variable
;
1689 if (text
->data
[i
] == '{') {
1692 if (++i
== text
->len
) {
1693 goto invalid_variable
;
1696 var
.data
= &text
->data
[i
];
1700 var
.data
= &text
->data
[i
];
1703 for ( /* void */ ; i
< text
->len
; i
++, var
.len
++) {
1706 if (ch
== '}' && bracket
) {
1712 if ((ch
>= 'A' && ch
<= 'Z')
1713 || (ch
>= 'a' && ch
<= 'z')
1714 || (ch
>= '0' && ch
<= '9')
1724 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1725 "the closing bracket in \"%V\" "
1726 "variable is missing", &var
);
1727 return NGX_HTTP_SSI_ERROR
;
1731 goto invalid_variable
;
1734 key
= ngx_hash_strlow(var
.data
, var
.data
, var
.len
);
1736 val
= ngx_http_ssi_get_variable(r
, &var
, key
);
1739 vv
= ngx_http_get_variable(r
, &var
, key
,
1740 flags
& NGX_HTTP_SSI_EXPR_TEST
);
1745 if (vv
->not_found
) {
1749 part_data
= vv
->data
;
1753 part_data
= val
->data
;
1754 part_len
= val
->len
;
1758 part_data
= &text
->data
[i
];
1761 for (p
= part_data
; i
< text
->len
; i
++) {
1778 if (ch
!= '\\' && ch
!= '\'' && ch
!= '"' && ch
!= '$') {
1786 part_len
= p
- part_data
;
1791 size
= ngx_array_push(&lengths
);
1798 value
= ngx_array_push(&values
);
1799 if (value
== NULL
) {
1808 size
= lengths
.elts
;
1809 value
= values
.elts
;
1811 if (flags
& NGX_HTTP_SSI_ADD_PREFIX
) {
1812 for (i
= 0; i
< values
.nelts
; i
++) {
1814 if (*value
[i
] != '/') {
1815 for (prefix
= r
->uri
.len
; prefix
; prefix
--) {
1816 if (r
->uri
.data
[prefix
- 1] == '/') {
1828 p
= ngx_pnalloc(r
->pool
, len
+ ((flags
& NGX_HTTP_SSI_ADD_ZERO
) ? 1 : 0));
1836 p
= ngx_copy(p
, r
->uri
.data
, prefix
);
1838 for (i
= 0; i
< values
.nelts
; i
++) {
1839 p
= ngx_copy(p
, value
[i
], size
[i
]);
1846 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1847 "invalid variable name in \"%V\"", text
);
1849 return NGX_HTTP_SSI_ERROR
;
1854 ngx_http_ssi_include(ngx_http_request_t
*r
, ngx_http_ssi_ctx_t
*ctx
,
1860 ngx_str_t
*uri
, *file
, *wait
, *set
, *stub
, args
;
1862 ngx_uint_t flags
, i
;
1863 ngx_chain_t
*cl
, *tl
, **ll
, *out
;
1864 ngx_http_request_t
*sr
;
1865 ngx_http_ssi_var_t
*var
;
1866 ngx_http_ssi_ctx_t
*mctx
;
1867 ngx_http_ssi_block_t
*bl
;
1868 ngx_http_post_subrequest_t
*psr
;
1870 uri
= params
[NGX_HTTP_SSI_INCLUDE_VIRTUAL
];
1871 file
= params
[NGX_HTTP_SSI_INCLUDE_FILE
];
1872 wait
= params
[NGX_HTTP_SSI_INCLUDE_WAIT
];
1873 set
= params
[NGX_HTTP_SSI_INCLUDE_SET
];
1874 stub
= params
[NGX_HTTP_SSI_INCLUDE_STUB
];
1877 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1878 "inlcusion may be either virtual=\"%V\" or file=\"%V\"",
1880 return NGX_HTTP_SSI_ERROR
;
1883 if (uri
== NULL
&& file
== NULL
) {
1884 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1885 "no parameter in \"include\" SSI command");
1886 return NGX_HTTP_SSI_ERROR
;
1890 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1891 "\"set\" and \"stub\" may not be used together "
1892 "in \"include\" SSI command");
1893 return NGX_HTTP_SSI_ERROR
;
1898 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1899 "\"wait\" may not be used with file=\"%V\"", file
);
1900 return NGX_HTTP_SSI_ERROR
;
1904 && ngx_strncasecmp(wait
->data
, (u_char
*) "no", 2) == 0)
1908 } else if (wait
->len
!= 3
1909 || ngx_strncasecmp(wait
->data
, (u_char
*) "yes", 3) != 0)
1911 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1912 "invalid value \"%V\" in the \"wait\" parameter",
1914 return NGX_HTTP_SSI_ERROR
;
1922 rc
= ngx_http_ssi_evaluate_string(r
, ctx
, uri
, NGX_HTTP_SSI_ADD_PREFIX
);
1931 ngx_unescape_uri(&dst
, &src
, uri
->len
, NGX_UNESCAPE_URI
);
1933 len
= (uri
->data
+ uri
->len
) - src
;
1935 dst
= ngx_copy(dst
, src
, len
);
1938 uri
->len
= dst
- uri
->data
;
1940 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
1941 "ssi include: \"%V\"", uri
);
1947 if (ngx_http_parse_unsafe_uri(r
, uri
, &args
, &flags
) != NGX_OK
) {
1948 return NGX_HTTP_SSI_ERROR
;
1953 mctx
= ngx_http_get_module_ctx(r
->main
, ngx_http_ssi_filter_module
);
1957 bl
= mctx
->blocks
->elts
;
1958 for (i
= 0; i
< mctx
->blocks
->nelts
; i
++) {
1959 if (stub
->len
== bl
[i
].name
.len
1960 && ngx_strncmp(stub
->data
, bl
[i
].name
.data
, stub
->len
) == 0)
1967 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
1968 "\"stub\"=\"%V\" for \"include\" not found", stub
);
1969 return NGX_HTTP_SSI_ERROR
;
1973 psr
= ngx_palloc(r
->pool
, sizeof(ngx_http_post_subrequest_t
));
1978 psr
->handler
= ngx_http_ssi_stub_output
;
1980 if (bl
[i
].count
++) {
1985 for (tl
= bl
[i
].bufs
; tl
; tl
= tl
->next
) {
1989 ctx
->free
= ctx
->free
->next
;
1993 b
= ngx_alloc_buf(r
->pool
);
1998 cl
= ngx_alloc_chain_link(r
->pool
);
2006 ngx_memcpy(b
, tl
->buf
, sizeof(ngx_buf_t
));
2018 psr
->data
= bl
[i
].bufs
;
2023 key
= ngx_hash_strlow(set
->data
, set
->data
, set
->len
);
2025 psr
= ngx_palloc(r
->pool
, sizeof(ngx_http_post_subrequest_t
));
2030 psr
->handler
= ngx_http_ssi_set_variable
;
2031 psr
->data
= ngx_http_ssi_get_variable(r
, set
, key
);
2033 if (psr
->data
== NULL
) {
2035 if (mctx
->variables
== NULL
) {
2036 mctx
->variables
= ngx_list_create(r
->pool
, 4,
2037 sizeof(ngx_http_ssi_var_t
));
2038 if (mctx
->variables
== NULL
) {
2043 var
= ngx_list_push(mctx
->variables
);
2050 var
->value
= ngx_http_ssi_null_string
;
2051 psr
->data
= &var
->value
;
2054 flags
|= NGX_HTTP_SUBREQUEST_IN_MEMORY
;
2057 rc
= ngx_http_subrequest(r
, uri
, &args
, &sr
, psr
, flags
);
2059 if (rc
== NGX_DONE
) {
2063 if (rc
== NGX_ERROR
) {
2064 return NGX_HTTP_SSI_ERROR
;
2067 if (wait
== NULL
&& set
== NULL
) {
2071 if (rc
== NGX_AGAIN
) {
2072 if (ctx
->wait
== NULL
) {
2076 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
2077 "only one subrequest may be waited at the same time");
2086 ngx_http_ssi_stub_output(ngx_http_request_t
*r
, void *data
, ngx_int_t rc
)
2090 if (rc
== NGX_ERROR
|| r
->connection
->error
|| r
->request_output
) {
2094 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
2095 "ssi stub output: \"%V?%V\"", &r
->uri
, &r
->args
);
2099 if (!r
->header_sent
) {
2100 if (ngx_http_set_content_type(r
) == NGX_ERROR
) {
2104 if (ngx_http_send_header(r
) == NGX_ERROR
) {
2109 return ngx_http_output_filter(r
, out
);
2114 ngx_http_ssi_set_variable(ngx_http_request_t
*r
, void *data
, ngx_int_t rc
)
2116 ngx_str_t
*value
= data
;
2119 value
->len
= r
->upstream
->buffer
.last
- r
->upstream
->buffer
.pos
;
2120 value
->data
= r
->upstream
->buffer
.pos
;
2128 ngx_http_ssi_echo(ngx_http_request_t
*r
, ngx_http_ssi_ctx_t
*ctx
,
2135 ngx_str_t
*var
, *value
, *enc
, text
;
2137 ngx_http_variable_value_t
*vv
;
2139 var
= params
[NGX_HTTP_SSI_ECHO_VAR
];
2141 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
2142 "ssi echo \"%V\"", var
);
2144 key
= ngx_hash_strlow(var
->data
, var
->data
, var
->len
);
2146 value
= ngx_http_ssi_get_variable(r
, var
, key
);
2148 if (value
== NULL
) {
2149 vv
= ngx_http_get_variable(r
, var
, key
, 1);
2152 return NGX_HTTP_SSI_ERROR
;
2155 if (!vv
->not_found
) {
2156 text
.data
= vv
->data
;
2162 if (value
== NULL
) {
2163 value
= params
[NGX_HTTP_SSI_ECHO_DEFAULT
];
2165 if (value
== NULL
) {
2166 value
= &ngx_http_ssi_none
;
2168 } else if (value
->len
== 0) {
2173 if (value
->len
== 0) {
2178 enc
= params
[NGX_HTTP_SSI_ECHO_ENCODING
];
2181 if (enc
->len
== 4 && ngx_strncmp(enc
->data
, "none", 4) == 0) {
2183 ctx
->encoding
= NGX_HTTP_SSI_NO_ENCODING
;
2185 } else if (enc
->len
== 3 && ngx_strncmp(enc
->data
, "url", 3) == 0) {
2187 ctx
->encoding
= NGX_HTTP_SSI_URL_ENCODING
;
2189 } else if (enc
->len
== 6 && ngx_strncmp(enc
->data
, "entity", 6) == 0) {
2191 ctx
->encoding
= NGX_HTTP_SSI_ENTITY_ENCODING
;
2194 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
2195 "unknown encoding \"%V\" in the \"echo\" command",
2200 switch (ctx
->encoding
) {
2202 case NGX_HTTP_SSI_NO_ENCODING
:
2205 case NGX_HTTP_SSI_URL_ENCODING
:
2206 len
= 2 * ngx_escape_uri(NULL
, value
->data
, value
->len
,
2210 p
= ngx_pnalloc(r
->pool
, value
->len
+ len
);
2212 return NGX_HTTP_SSI_ERROR
;
2215 (void) ngx_escape_uri(p
, value
->data
, value
->len
, NGX_ESCAPE_HTML
);
2223 case NGX_HTTP_SSI_ENTITY_ENCODING
:
2224 len
= ngx_escape_html(NULL
, value
->data
, value
->len
);
2227 p
= ngx_pnalloc(r
->pool
, value
->len
+ len
);
2229 return NGX_HTTP_SSI_ERROR
;
2232 (void) ngx_escape_html(p
, value
->data
, value
->len
);
2241 b
= ngx_calloc_buf(r
->pool
);
2243 return NGX_HTTP_SSI_ERROR
;
2246 cl
= ngx_alloc_chain_link(r
->pool
);
2248 return NGX_HTTP_SSI_ERROR
;
2252 b
->pos
= value
->data
;
2253 b
->last
= value
->data
+ value
->len
;
2257 *ctx
->last_out
= cl
;
2258 ctx
->last_out
= &cl
->next
;
2265 ngx_http_ssi_config(ngx_http_request_t
*r
, ngx_http_ssi_ctx_t
*ctx
,
2270 value
= params
[NGX_HTTP_SSI_CONFIG_TIMEFMT
];
2273 ctx
->timefmt
.len
= value
->len
;
2274 ctx
->timefmt
.data
= ngx_pnalloc(r
->pool
, value
->len
+ 1);
2275 if (ctx
->timefmt
.data
== NULL
) {
2276 return NGX_HTTP_SSI_ERROR
;
2279 ngx_cpystrn(ctx
->timefmt
.data
, value
->data
, value
->len
+ 1);
2282 value
= params
[NGX_HTTP_SSI_CONFIG_ERRMSG
];
2285 ctx
->errmsg
= *value
;
2293 ngx_http_ssi_set(ngx_http_request_t
*r
, ngx_http_ssi_ctx_t
*ctx
,
2297 ngx_str_t
*name
, *value
, *vv
;
2298 ngx_http_ssi_var_t
*var
;
2299 ngx_http_ssi_ctx_t
*mctx
;
2301 mctx
= ngx_http_get_module_ctx(r
->main
, ngx_http_ssi_filter_module
);
2303 if (mctx
->variables
== NULL
) {
2304 mctx
->variables
= ngx_list_create(r
->pool
, 4,
2305 sizeof(ngx_http_ssi_var_t
));
2306 if (mctx
->variables
== NULL
) {
2311 name
= params
[NGX_HTTP_SSI_SET_VAR
];
2312 value
= params
[NGX_HTTP_SSI_SET_VALUE
];
2314 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
2315 "ssi set \"%V\" \"%V\"", name
, value
);
2317 rc
= ngx_http_ssi_evaluate_string(r
, ctx
, value
, 0);
2323 key
= ngx_hash_strlow(name
->data
, name
->data
, name
->len
);
2325 vv
= ngx_http_ssi_get_variable(r
, name
, key
);
2332 var
= ngx_list_push(mctx
->variables
);
2339 var
->value
= *value
;
2341 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
2342 "set: \"%V\"=\"%V\"", name
, value
);
2349 ngx_http_ssi_if(ngx_http_request_t
*r
, ngx_http_ssi_ctx_t
*ctx
,
2353 ngx_str_t
*expr
, left
, right
;
2355 ngx_uint_t negative
, noregex
, flags
;
2357 if (ctx
->command
.len
== 2) {
2358 if (ctx
->conditional
) {
2359 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
2360 "the \"if\" command inside the \"if\" command");
2361 return NGX_HTTP_SSI_ERROR
;
2365 if (ctx
->output_chosen
) {
2370 expr
= params
[NGX_HTTP_SSI_IF_EXPR
];
2372 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
2373 "ssi if expr=\"%V\"", expr
);
2375 left
.data
= expr
->data
;
2376 last
= expr
->data
+ expr
->len
;
2378 for (p
= left
.data
; p
< last
; p
++) {
2379 if (*p
>= 'A' && *p
<= 'Z') {
2384 if ((*p
>= 'a' && *p
<= 'z')
2385 || (*p
>= '0' && *p
<= '9')
2386 || *p
== '$' || *p
== '{' || *p
== '}' || *p
== '_'
2387 || *p
== '"' || *p
== '\'')
2395 left
.len
= p
- left
.data
;
2397 while (p
< last
&& *p
== ' ') {
2401 flags
= (p
== last
) ? NGX_HTTP_SSI_EXPR_TEST
: 0;
2403 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
2404 "left: \"%V\"", &left
);
2406 rc
= ngx_http_ssi_evaluate_string(r
, ctx
, &left
, flags
);
2412 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
2413 "evaluted left: \"%V\"", &left
);
2418 ctx
->output_chosen
= 1;
2424 ctx
->conditional
= NGX_HTTP_SSI_COND_IF
;
2429 if (p
< last
&& *p
== '=') {
2433 } else if (p
+ 1 < last
&& *p
== '!' && *(p
+ 1) == '=') {
2438 goto invalid_expression
;
2441 while (p
< last
&& *p
== ' ') {
2445 if (p
< last
- 1 && *p
== '/') {
2446 if (*(last
- 1) != '/') {
2447 goto invalid_expression
;
2451 flags
= NGX_HTTP_SSI_ADD_ZERO
;
2459 if (p
< last
- 1 && p
[0] == '\\' && p
[1] == '/') {
2464 right
.len
= last
- p
;
2467 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
2468 "right: \"%V\"", &right
);
2470 rc
= ngx_http_ssi_evaluate_string(r
, ctx
, &right
, flags
);
2476 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
2477 "evaluted right: \"%V\"", &right
);
2480 if (left
.len
!= right
.len
) {
2484 rc
= ngx_strncmp(left
.data
, right
.data
, right
.len
);
2491 u_char errstr
[NGX_MAX_CONF_ERRSTR
];
2493 err
.len
= NGX_MAX_CONF_ERRSTR
;
2496 right
.data
[right
.len
] = '\0';
2498 regex
= ngx_regex_compile(&right
, 0, r
->pool
, &err
);
2500 if (regex
== NULL
) {
2501 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0, "%s", err
.data
);
2502 return NGX_HTTP_SSI_ERROR
;
2505 rc
= ngx_regex_exec(regex
, &left
, NULL
, 0);
2507 if (rc
!= NGX_REGEX_NO_MATCHED
&& rc
< 0) {
2508 ngx_log_error(NGX_LOG_ALERT
, r
->connection
->log
, 0,
2509 ngx_regex_exec_n
" failed: %d on \"%V\" using \"%V\"",
2511 return NGX_HTTP_SSI_ERROR
;
2514 ngx_log_error(NGX_LOG_ALERT
, r
->connection
->log
, 0,
2515 "the using of the regex \"%V\" in SSI "
2516 "requires PCRE library", &right
);
2518 return NGX_HTTP_SSI_ERROR
;
2522 if ((rc
== 0 && !negative
) || (rc
!= 0 && negative
)) {
2524 ctx
->output_chosen
= 1;
2530 ctx
->conditional
= NGX_HTTP_SSI_COND_IF
;
2536 ngx_log_error(NGX_LOG_ERR
, r
->connection
->log
, 0,
2537 "invalid expression in \"%V\"", expr
);
2539 return NGX_HTTP_SSI_ERROR
;
2544 ngx_http_ssi_else(ngx_http_request_t
*r
, ngx_http_ssi_ctx_t
*ctx
,
2547 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
2550 if (ctx
->output_chosen
) {
2556 ctx
->conditional
= NGX_HTTP_SSI_COND_ELSE
;
2563 ngx_http_ssi_endif(ngx_http_request_t
*r
, ngx_http_ssi_ctx_t
*ctx
,
2566 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
2570 ctx
->output_chosen
= 0;
2571 ctx
->conditional
= 0;
2578 ngx_http_ssi_block(ngx_http_request_t
*r
, ngx_http_ssi_ctx_t
*ctx
,
2581 ngx_http_ssi_ctx_t
*mctx
;
2582 ngx_http_ssi_block_t
*bl
;
2584 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
2587 mctx
= ngx_http_get_module_ctx(r
->main
, ngx_http_ssi_filter_module
);
2589 if (mctx
->blocks
== NULL
) {
2590 mctx
->blocks
= ngx_array_create(r
->pool
, 4,
2591 sizeof(ngx_http_ssi_block_t
));
2592 if (mctx
->blocks
== NULL
) {
2593 return NGX_HTTP_SSI_ERROR
;
2597 bl
= ngx_array_push(mctx
->blocks
);
2599 return NGX_HTTP_SSI_ERROR
;
2602 bl
->name
= *params
[NGX_HTTP_SSI_BLOCK_NAME
];
2614 ngx_http_ssi_endblock(ngx_http_request_t
*r
, ngx_http_ssi_ctx_t
*ctx
,
2617 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
2628 ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t
*r
,
2629 ngx_http_variable_value_t
*v
, uintptr_t gmt
)
2631 ngx_http_ssi_ctx_t
*ctx
;
2634 char buf
[NGX_HTTP_SSI_DATE_LEN
];
2637 v
->no_cacheable
= 0;
2640 tp
= ngx_timeofday();
2642 ctx
= ngx_http_get_module_ctx(r
, ngx_http_ssi_filter_module
);
2645 || (ctx
->timefmt
.len
== sizeof("%s") - 1
2646 && ctx
->timefmt
.data
[0] == '%' && ctx
->timefmt
.data
[1] == 's'))
2648 v
->data
= ngx_pnalloc(r
->pool
, NGX_TIME_T_LEN
);
2649 if (v
->data
== NULL
) {
2653 v
->len
= ngx_sprintf(v
->data
, "%T", tp
->sec
+ (gmt
? 0 : tp
->gmtoff
))
2660 ngx_libc_gmtime(tp
->sec
, &tm
);
2662 ngx_libc_localtime(tp
->sec
, &tm
);
2665 v
->len
= strftime(buf
, NGX_HTTP_SSI_DATE_LEN
,
2666 (char *) ctx
->timefmt
.data
, &tm
);
2671 v
->data
= ngx_pnalloc(r
->pool
, v
->len
);
2672 if (v
->data
== NULL
) {
2676 ngx_memcpy(v
->data
, buf
, v
->len
);
2683 ngx_http_ssi_types(ngx_conf_t
*cf
, ngx_command_t
*cmd
, void *conf
)
2685 ngx_http_ssi_loc_conf_t
*slcf
= conf
;
2687 ngx_str_t
*value
, *type
;
2690 if (slcf
->types
== NULL
) {
2691 slcf
->types
= ngx_array_create(cf
->pool
, 4, sizeof(ngx_str_t
));
2692 if (slcf
->types
== NULL
) {
2693 return NGX_CONF_ERROR
;
2696 type
= ngx_array_push(slcf
->types
);
2698 return NGX_CONF_ERROR
;
2701 type
->len
= sizeof("text/html") - 1;
2702 type
->data
= (u_char
*) "text/html";
2705 value
= cf
->args
->elts
;
2707 for (i
= 1; i
< cf
->args
->nelts
; i
++) {
2709 if (ngx_strcmp(value
[i
].data
, "text/html") == 0) {
2713 type
= ngx_array_push(slcf
->types
);
2715 return NGX_CONF_ERROR
;
2718 type
->len
= value
[i
].len
;
2720 type
->data
= ngx_pnalloc(cf
->pool
, type
->len
+ 1);
2721 if (type
->data
== NULL
) {
2722 return NGX_CONF_ERROR
;
2725 ngx_cpystrn(type
->data
, value
[i
].data
, type
->len
+ 1);
2733 ngx_http_ssi_preconfiguration(ngx_conf_t
*cf
)
2736 ngx_http_variable_t
*var
, *v
;
2737 ngx_http_ssi_command_t
*cmd
;
2738 ngx_http_ssi_main_conf_t
*smcf
;
2740 for (v
= ngx_http_ssi_vars
; v
->name
.len
; v
++) {
2741 var
= ngx_http_add_variable(cf
, &v
->name
, v
->flags
);
2746 var
->get_handler
= v
->get_handler
;
2747 var
->data
= v
->data
;
2750 smcf
= ngx_http_conf_get_module_main_conf(cf
, ngx_http_ssi_filter_module
);
2752 for (cmd
= ngx_http_ssi_commands
; cmd
->name
.len
; cmd
++) {
2753 rc
= ngx_hash_add_key(&smcf
->commands
, &cmd
->name
, cmd
,
2754 NGX_HASH_READONLY_KEY
);
2760 if (rc
== NGX_BUSY
) {
2761 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
2762 "conflicting SSI command \"%V\"", &cmd
->name
);
2773 ngx_http_ssi_create_main_conf(ngx_conf_t
*cf
)
2775 ngx_http_ssi_main_conf_t
*smcf
;
2777 smcf
= ngx_pcalloc(cf
->pool
, sizeof(ngx_http_ssi_main_conf_t
));
2779 return NGX_CONF_ERROR
;
2782 smcf
->commands
.pool
= cf
->pool
;
2783 smcf
->commands
.temp_pool
= cf
->temp_pool
;
2785 if (ngx_hash_keys_array_init(&smcf
->commands
, NGX_HASH_SMALL
) != NGX_OK
) {
2786 return NGX_CONF_ERROR
;
2794 ngx_http_ssi_init_main_conf(ngx_conf_t
*cf
, void *conf
)
2796 ngx_http_ssi_main_conf_t
*smcf
= conf
;
2798 ngx_hash_init_t hash
;
2800 hash
.hash
= &smcf
->hash
;
2801 hash
.key
= ngx_hash_key
;
2802 hash
.max_size
= 1024;
2803 hash
.bucket_size
= ngx_cacheline_size
;
2804 hash
.name
= "ssi_command_hash";
2805 hash
.pool
= cf
->pool
;
2806 hash
.temp_pool
= NULL
;
2808 if (ngx_hash_init(&hash
, smcf
->commands
.keys
.elts
,
2809 smcf
->commands
.keys
.nelts
)
2812 return NGX_CONF_ERROR
;
2820 ngx_http_ssi_create_loc_conf(ngx_conf_t
*cf
)
2822 ngx_http_ssi_loc_conf_t
*slcf
;
2824 slcf
= ngx_pcalloc(cf
->pool
, sizeof(ngx_http_ssi_loc_conf_t
));
2826 return NGX_CONF_ERROR
;
2830 * set by ngx_pcalloc():
2832 * conf->types = NULL;
2835 slcf
->enable
= NGX_CONF_UNSET
;
2836 slcf
->silent_errors
= NGX_CONF_UNSET
;
2837 slcf
->ignore_recycled_buffers
= NGX_CONF_UNSET
;
2839 slcf
->min_file_chunk
= NGX_CONF_UNSET_SIZE
;
2840 slcf
->value_len
= NGX_CONF_UNSET_SIZE
;
2847 ngx_http_ssi_merge_loc_conf(ngx_conf_t
*cf
, void *parent
, void *child
)
2849 ngx_http_ssi_loc_conf_t
*prev
= parent
;
2850 ngx_http_ssi_loc_conf_t
*conf
= child
;
2854 ngx_conf_merge_value(conf
->enable
, prev
->enable
, 0);
2855 ngx_conf_merge_value(conf
->silent_errors
, prev
->silent_errors
, 0);
2856 ngx_conf_merge_value(conf
->ignore_recycled_buffers
,
2857 prev
->ignore_recycled_buffers
, 0);
2859 ngx_conf_merge_size_value(conf
->min_file_chunk
, prev
->min_file_chunk
, 1024);
2860 ngx_conf_merge_size_value(conf
->value_len
, prev
->value_len
, 256);
2862 if (conf
->types
== NULL
) {
2863 if (prev
->types
== NULL
) {
2864 conf
->types
= ngx_array_create(cf
->pool
, 1, sizeof(ngx_str_t
));
2865 if (conf
->types
== NULL
) {
2866 return NGX_CONF_ERROR
;
2869 type
= ngx_array_push(conf
->types
);
2871 return NGX_CONF_ERROR
;
2874 type
->len
= sizeof("text/html") - 1;
2875 type
->data
= (u_char
*) "text/html";
2878 conf
->types
= prev
->types
;
2887 ngx_http_ssi_filter_init(ngx_conf_t
*cf
)
2889 ngx_http_next_header_filter
= ngx_http_top_header_filter
;
2890 ngx_http_top_header_filter
= ngx_http_ssi_header_filter
;
2892 ngx_http_next_body_filter
= ngx_http_top_body_filter
;
2893 ngx_http_top_body_filter
= ngx_http_ssi_body_filter
;