comparison src/http/modules/ngx_http_ssi_filter_module.c @ 58:b55cbf18157e NGINX_0_1_29

nginx 0.1.29 *) Feature: the ngx_http_ssi_module supports "include virtual" command. *) Feature: the ngx_http_ssi_module supports the condition command like 'if expr="$NAME"' and "else" and "endif" commands. Only one nested level is supported. *) Feature: the ngx_http_ssi_module supports the DATE_LOCAL and DATE_GMT variables and "config timefmt" command. *) Feature: the "ssi_ignore_recycled_buffers" directive. *) Bugfix: the "echo" command did not show the default value for the empty QUERY_STRING variable. *) Change: the ngx_http_proxy_module was rewritten. *) Feature: the "proxy_redirect", "proxy_pass_request_headers", "proxy_pass_request_body", and "proxy_method" directives. *) Feature: the "proxy_set_header" directive. The "proxy_x_var" was canceled and must be replaced with the proxy_set_header directive. *) Change: the "proxy_preserve_host" is canceled and must be replaced with the "proxy_set_header Host $host" and the "proxy_redirect off" directives, the "proxy_set_header Host $host:$proxy_port" directive and the appropriate proxy_redirect directives. *) Change: the "proxy_set_x_real_ip" is canceled and must be replaced with the "proxy_set_header X-Real-IP $remote_addr" directive. *) Change: the "proxy_add_x_forwarded_for" is canceled and must be replaced with the "proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for" directive. *) Change: the "proxy_set_x_url" is canceled and must be replaced with the "proxy_set_header X-URL http://$host:$server_port$request_uri" directive. *) Feature: the "fastcgi_param" directive. *) Change: the "fastcgi_root", "fastcgi_set_var" and "fastcgi_params" directive are canceled and must be replaced with the fastcgi_param directives. *) Feature: the "index" directive can use the variables. *) Feature: the "index" directive can be used at http and server levels. *) Change: the last index only in the "index" directive can be absolute. *) Feature: the "rewrite" directive can use the variables. *) Feature: the "internal" directive. *) Feature: the CONTENT_LENGTH, CONTENT_TYPE, REMOTE_PORT, SERVER_ADDR, SERVER_PORT, SERVER_PROTOCOL, DOCUMENT_ROOT, SERVER_NAME, REQUEST_METHOD, REQUEST_URI, and REMOTE_USER variables. *) Change: nginx now passes the invalid lines in a client request headers or a backend response header. *) Bugfix: if the backend did not transfer response for a long time and the "send_timeout" was less than "proxy_read_timeout", then nginx returned the 408 response. *) Bugfix: the segmentation fault was occurred if the backend sent an invalid line in response header; bug appeared in 0.1.26. *) Bugfix: the segmentation fault may occurred in FastCGI fault tolerance configuration. *) Bugfix: the "expires" directive did not remove the previous "Expires" and "Cache-Control" headers. *) Bugfix: nginx did not take into account trailing dot in "Host" header line. *) Bugfix: the ngx_http_auth_module did not work under Linux. *) Bugfix: the rewrite directive worked incorrectly, if the arguments were in a request. *) Bugfix: nginx could not be built on MacOS X.
author Igor Sysoev <http://sysoev.ru>
date Thu, 12 May 2005 00:00:00 +0400
parents 72eb30262aac
children 0790a8599248
comparison
equal deleted inserted replaced
57:5df375c55338 58:b55cbf18157e
14 #define NGX_HTTP_SSI_PARAM_LEN 31 14 #define NGX_HTTP_SSI_PARAM_LEN 31
15 #define NGX_HTTP_SSI_PARAMS_N 4 15 #define NGX_HTTP_SSI_PARAMS_N 4
16 16
17 #define NGX_HTTP_SSI_ERROR 1 17 #define NGX_HTTP_SSI_ERROR 1
18 18
19 #define NGX_HTTP_SSI_DATE_LEN 2048
20
19 21
20 typedef struct { 22 typedef struct {
21 ngx_flag_t enable; 23 ngx_flag_t enable;
22 ngx_flag_t silent_errors; 24 ngx_flag_t silent_errors;
25 ngx_flag_t ignore_recycled_buffers;
23 26
24 size_t min_file_chunk; 27 size_t min_file_chunk;
25 size_t value_len; 28 size_t value_len;
26 } ngx_http_ssi_conf_t; 29 } ngx_http_ssi_conf_t;
27 30
48 ngx_uint_t saved_state; 51 ngx_uint_t saved_state;
49 size_t saved; 52 size_t saved;
50 size_t looked; 53 size_t looked;
51 54
52 size_t value_len; 55 size_t value_len;
56
57 ngx_uint_t output; /* unsigned output:1; */
58
59 ngx_str_t timefmt;
53 } ngx_http_ssi_ctx_t; 60 } ngx_http_ssi_ctx_t;
54 61
55 62
56 typedef ngx_int_t (*ngx_http_ssi_command_pt) (ngx_http_request_t *r, 63 typedef ngx_int_t (*ngx_http_ssi_command_pt) (ngx_http_request_t *r,
57 ngx_http_ssi_ctx_t *ctx, ngx_str_t **); 64 ngx_http_ssi_ctx_t *ctx, ngx_str_t **);
68 typedef struct { 75 typedef struct {
69 ngx_str_t name; 76 ngx_str_t name;
70 ngx_http_ssi_command_pt handler; 77 ngx_http_ssi_command_pt handler;
71 ngx_http_ssi_param_t *params; 78 ngx_http_ssi_param_t *params;
72 79
73 ngx_uint_t flush; /* unsigned flush:1; */ 80 unsigned conditional:1;
81 unsigned flush:1;
74 } ngx_http_ssi_command_t; 82 } ngx_http_ssi_command_t;
75 83
76 84
77 typedef enum { 85 typedef enum {
78 ssi_start_state = 0, 86 ssi_start_state = 0,
96 ssi_error_end0_state, 104 ssi_error_end0_state,
97 ssi_error_end1_state 105 ssi_error_end1_state
98 } ngx_http_ssi_state_e; 106 } ngx_http_ssi_state_e;
99 107
100 108
109 static ngx_int_t ngx_http_ssi_output(ngx_http_request_t *r,
110 ngx_http_ssi_ctx_t *ctx);
101 static ngx_int_t ngx_http_ssi_parse(ngx_http_request_t *r, 111 static ngx_int_t ngx_http_ssi_parse(ngx_http_request_t *r,
102 ngx_http_ssi_ctx_t *ctx); 112 ngx_http_ssi_ctx_t *ctx);
103 113
104 static ngx_int_t ngx_http_ssi_echo(ngx_http_request_t *r, 114 static ngx_int_t ngx_http_ssi_echo(ngx_http_request_t *r,
105 ngx_http_ssi_ctx_t *ctx, ngx_str_t **params); 115 ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
106 116 static ngx_int_t ngx_http_ssi_config(ngx_http_request_t *r,
117 ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
118 static ngx_int_t ngx_http_ssi_include(ngx_http_request_t *r,
119 ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
120 static ngx_int_t ngx_http_ssi_if(ngx_http_request_t *r,
121 ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
122 static ngx_int_t ngx_http_ssi_else(ngx_http_request_t *r,
123 ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
124 static ngx_int_t ngx_http_ssi_endif(ngx_http_request_t *r,
125 ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
126
127 static ngx_http_variable_value_t *
128 ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t *r, uintptr_t gmt);
129
130 static ngx_int_t ngx_http_ssi_add_variables(ngx_conf_t *cf);
107 static void *ngx_http_ssi_create_conf(ngx_conf_t *cf); 131 static void *ngx_http_ssi_create_conf(ngx_conf_t *cf);
108 static char *ngx_http_ssi_merge_conf(ngx_conf_t *cf, 132 static char *ngx_http_ssi_merge_conf(ngx_conf_t *cf,
109 void *parent, void *child); 133 void *parent, void *child);
110 static ngx_int_t ngx_http_ssi_filter_init(ngx_cycle_t *cycle); 134 static ngx_int_t ngx_http_ssi_filter_init(ngx_cycle_t *cycle);
111 135
124 ngx_conf_set_flag_slot, 148 ngx_conf_set_flag_slot,
125 NGX_HTTP_LOC_CONF_OFFSET, 149 NGX_HTTP_LOC_CONF_OFFSET,
126 offsetof(ngx_http_ssi_conf_t, silent_errors), 150 offsetof(ngx_http_ssi_conf_t, silent_errors),
127 NULL }, 151 NULL },
128 152
153 { ngx_string("ssi_ignore_recycled_buffers"),
154 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
155 ngx_conf_set_flag_slot,
156 NGX_HTTP_LOC_CONF_OFFSET,
157 offsetof(ngx_http_ssi_conf_t, ignore_recycled_buffers),
158 NULL },
159
129 { ngx_string("ssi_min_file_chunk"), 160 { ngx_string("ssi_min_file_chunk"),
130 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 161 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
131 ngx_conf_set_size_slot, 162 ngx_conf_set_size_slot,
132 NGX_HTTP_LOC_CONF_OFFSET, 163 NGX_HTTP_LOC_CONF_OFFSET,
133 offsetof(ngx_http_ssi_conf_t, min_file_chunk), 164 offsetof(ngx_http_ssi_conf_t, min_file_chunk),
137 }; 168 };
138 169
139 170
140 171
141 static ngx_http_module_t ngx_http_ssi_filter_module_ctx = { 172 static ngx_http_module_t ngx_http_ssi_filter_module_ctx = {
142 NULL, /* pre conf */ 173 ngx_http_ssi_add_variables, /* preconfiguration */
174 NULL, /* postconfiguration */
143 175
144 NULL, /* create main configuration */ 176 NULL, /* create main configuration */
145 NULL, /* init main configuration */ 177 NULL, /* init main configuration */
146 178
147 NULL, /* create server configuration */ 179 NULL, /* create server configuration */
151 ngx_http_ssi_merge_conf /* merge location configuration */ 183 ngx_http_ssi_merge_conf /* merge location configuration */
152 }; 184 };
153 185
154 186
155 ngx_module_t ngx_http_ssi_filter_module = { 187 ngx_module_t ngx_http_ssi_filter_module = {
156 NGX_MODULE, 188 NGX_MODULE_V1,
157 &ngx_http_ssi_filter_module_ctx, /* module context */ 189 &ngx_http_ssi_filter_module_ctx, /* module context */
158 ngx_http_ssi_filter_commands, /* module directives */ 190 ngx_http_ssi_filter_commands, /* module directives */
159 NGX_HTTP_MODULE, /* module type */ 191 NGX_HTTP_MODULE, /* module type */
160 ngx_http_ssi_filter_init, /* init module */ 192 ngx_http_ssi_filter_init, /* init module */
161 NULL /* init process */ 193 NULL /* init process */
172 "[an error occurred while processing the directive]"; 204 "[an error occurred while processing the directive]";
173 205
174 static ngx_str_t ngx_http_ssi_none = ngx_string("(none)"); 206 static ngx_str_t ngx_http_ssi_none = ngx_string("(none)");
175 207
176 208
177 #define NGX_HTTP_SSI_ECHO_VAR 0 209 #define NGX_HTTP_SSI_ECHO_VAR 0
178 #define NGX_HTTP_SSI_ECHO_DEFAULT 1 210 #define NGX_HTTP_SSI_ECHO_DEFAULT 1
211
212 #define NGX_HTTP_SSI_CONFIG_TIMEFMT 0
213
214 #define NGX_HTTP_SSI_INCLUDE_VIRTUAL 0
215 #define NGX_HTTP_SSI_INCLUDE_FILE 1
216
217 #define NGX_HTTP_SSI_IF_EXPR 0
218
179 219
180 static ngx_http_ssi_param_t ngx_http_ssi_echo_params[] = { 220 static ngx_http_ssi_param_t ngx_http_ssi_echo_params[] = {
181 { ngx_string("var"), NGX_HTTP_SSI_ECHO_VAR, 1 }, 221 { ngx_string("var"), NGX_HTTP_SSI_ECHO_VAR, 1 },
182 { ngx_string("default"), NGX_HTTP_SSI_ECHO_DEFAULT, 0 }, 222 { ngx_string("default"), NGX_HTTP_SSI_ECHO_DEFAULT, 0 },
183 { ngx_null_string, 0, 0 } 223 { ngx_null_string, 0, 0 }
184 }; 224 };
185 225
226 static ngx_http_ssi_param_t ngx_http_ssi_include_params[] = {
227 { ngx_string("virtual"), NGX_HTTP_SSI_INCLUDE_VIRTUAL, 0 },
228 #if 0
229 { ngx_string("file"), NGX_HTTP_SSI_INCLUDE_FILE, 0 },
230 #endif
231 { ngx_null_string, 0, 0 }
232 };
233
234
235 static ngx_http_ssi_param_t ngx_http_ssi_config_params[] = {
236 { ngx_string("timefmt"), NGX_HTTP_SSI_CONFIG_TIMEFMT, 0 },
237 { ngx_null_string, 0, 0 }
238 };
239
240
241 static ngx_http_ssi_param_t ngx_http_ssi_if_params[] = {
242 { ngx_string("expr"), NGX_HTTP_SSI_IF_EXPR, 0 },
243 { ngx_null_string, 0, 0 }
244 };
245
246
247 static ngx_http_ssi_param_t ngx_http_ssi_no_params[] = {
248 { ngx_null_string, 0, 0 }
249 };
250
186 251
187 static ngx_http_ssi_command_t ngx_http_ssi_commands[] = { 252 static ngx_http_ssi_command_t ngx_http_ssi_commands[] = {
188 { ngx_string("echo"), ngx_http_ssi_echo, ngx_http_ssi_echo_params, 0 }, 253 { ngx_string("echo"), ngx_http_ssi_echo, ngx_http_ssi_echo_params, 0, 0 },
189 { ngx_null_string, NULL, NULL, 0 } 254 { ngx_string("config"), ngx_http_ssi_config,
255 ngx_http_ssi_config_params, 0, 0 },
256 { ngx_string("include"), ngx_http_ssi_include,
257 ngx_http_ssi_include_params, 0, 1 },
258
259 { ngx_string("if"), ngx_http_ssi_if, ngx_http_ssi_if_params, 0, 0 },
260 { ngx_string("else"), ngx_http_ssi_else, ngx_http_ssi_no_params, 1, 0 },
261 { ngx_string("endif"), ngx_http_ssi_endif, ngx_http_ssi_no_params, 1, 0 },
262
263 { ngx_null_string, NULL, NULL, 0, 0 }
190 }; 264 };
265
266
267 static ngx_http_variable_t ngx_http_ssi_vars[] = {
268
269 { ngx_string("date_local"), ngx_http_ssi_date_gmt_local_variable, 0,
270 NGX_HTTP_VAR_NOCACHABLE },
271
272 { ngx_string("date_gmt"), ngx_http_ssi_date_gmt_local_variable, 1,
273 NGX_HTTP_VAR_NOCACHABLE },
274
275 { ngx_null_string, NULL, 0, 0 }
276 };
277
191 278
192 279
193 static ngx_int_t 280 static ngx_int_t
194 ngx_http_ssi_header_filter(ngx_http_request_t *r) 281 ngx_http_ssi_header_filter(ngx_http_request_t *r)
195 { 282 {
202 return ngx_http_next_header_filter(r); 289 return ngx_http_next_header_filter(r);
203 } 290 }
204 291
205 /* TODO: "text/html" -> custom types */ 292 /* TODO: "text/html" -> custom types */
206 293
207 if (r->headers_out.content_type 294 if (r->headers_out.content_type.len == 0
208 && ngx_strncasecmp(r->headers_out.content_type->value.data, 295 || ngx_strncasecmp(r->headers_out.content_type.data, "text/html", 5)
209 "text/html", 5) != 0) 296 != 0)
210 { 297 {
211 return ngx_http_next_header_filter(r); 298 return ngx_http_next_header_filter(r);
212 } 299 }
213 300
214 301
220 ngx_http_set_ctx(r, ctx, ngx_http_ssi_filter_module); 307 ngx_http_set_ctx(r, ctx, ngx_http_ssi_filter_module);
221 308
222 309
223 ctx->value_len = conf->value_len; 310 ctx->value_len = conf->value_len;
224 ctx->last_out = &ctx->out; 311 ctx->last_out = &ctx->out;
312
313 ctx->output = 1;
225 314
226 ctx->params.elts = ctx->params_array; 315 ctx->params.elts = ctx->params_array;
227 ctx->params.size = sizeof(ngx_table_elt_t); 316 ctx->params.size = sizeof(ngx_table_elt_t);
228 ctx->params.nalloc = NGX_HTTP_SSI_PARAMS_N; 317 ctx->params.nalloc = NGX_HTTP_SSI_PARAMS_N;
229 ctx->params.pool = r->pool; 318 ctx->params.pool = r->pool;
230 319
231 r->headers_out.content_length_n = -1; 320 ctx->timefmt.len = sizeof("%A, %d-%b-%Y %H:%M:%S %Z") - 1;
232 if (r->headers_out.content_length) { 321 ctx->timefmt.data = (u_char *) "%A, %d-%b-%Y %H:%M:%S %Z";
233 r->headers_out.content_length->key.len = 0;
234 r->headers_out.content_length = NULL;
235 }
236
237 r->headers_out.last_modified_time = -1;
238 if (r->headers_out.last_modified) {
239 r->headers_out.last_modified->key.len = 0;
240 r->headers_out.last_modified = NULL;
241 }
242 322
243 r->filter_need_in_memory = 1; 323 r->filter_need_in_memory = 1;
244 r->filter_ssi_need_in_memory = 1; 324 r->filter_ssi_need_in_memory = 1;
325
326 if (r->main == NULL) {
327 r->headers_out.content_length_n = -1;
328 if (r->headers_out.content_length) {
329 r->headers_out.content_length->hash = 0;
330 r->headers_out.content_length = NULL;
331 }
332
333 r->headers_out.last_modified_time = -1;
334 if (r->headers_out.last_modified) {
335 r->headers_out.last_modified->hash = 0;
336 r->headers_out.last_modified = NULL;
337 }
338 }
245 339
246 return ngx_http_next_header_filter(r); 340 return ngx_http_next_header_filter(r);
247 } 341 }
248 342
249 343
275 } 369 }
276 } 370 }
277 371
278 conf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module); 372 conf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module);
279 373
280 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 374 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
281 "http ssi filter"); 375 "http ssi filter \"%V\"", &r->uri);
282 376
283 while (ctx->in || ctx->buf) { 377 while (ctx->in || ctx->buf) {
284 378
285 if (ctx->buf == NULL ){ 379 if (ctx->buf == NULL ){
286 ctx->buf = ctx->in->buf; 380 ctx->buf = ctx->in->buf;
310 return rc; 404 return rc;
311 } 405 }
312 406
313 if (ctx->copy_start != ctx->copy_end) { 407 if (ctx->copy_start != ctx->copy_end) {
314 408
315 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 409 if (ctx->output) {
316 "saved: %d", ctx->saved); 410
317 411 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
318 if (ctx->saved) { 412 "saved: %d", ctx->saved);
413
414 if (ctx->saved) {
415
416 if (ctx->free) {
417 cl = ctx->free;
418 ctx->free = ctx->free->next;
419 b = cl->buf;
420 ngx_memzero(b, sizeof(ngx_buf_t));
421
422 } else {
423 b = ngx_calloc_buf(r->pool);
424 if (b == NULL) {
425 return NGX_ERROR;
426 }
427
428 cl = ngx_alloc_chain_link(r->pool);
429 if (cl == NULL) {
430 return NGX_ERROR;
431 }
432
433 cl->buf = b;
434 }
435
436 b->memory = 1;
437 b->pos = ngx_http_ssi_string;
438 b->last = ngx_http_ssi_string + ctx->saved;
439
440 *ctx->last_out = cl;
441 ctx->last_out = &cl->next;
442
443 ctx->saved = 0;
444 }
319 445
320 if (ctx->free) { 446 if (ctx->free) {
321 cl = ctx->free; 447 cl = ctx->free;
322 ctx->free = ctx->free->next; 448 ctx->free = ctx->free->next;
323 b = cl->buf; 449 b = cl->buf;
324 ngx_memzero(b, sizeof(ngx_buf_t));
325 450
326 } else { 451 } else {
327 b = ngx_calloc_buf(r->pool); 452 b = ngx_alloc_buf(r->pool);
328 if (b == NULL) { 453 if (b == NULL) {
329 return NGX_ERROR; 454 return NGX_ERROR;
330 } 455 }
331 456
332 cl = ngx_alloc_chain_link(r->pool); 457 cl = ngx_alloc_chain_link(r->pool);
335 } 460 }
336 461
337 cl->buf = b; 462 cl->buf = b;
338 } 463 }
339 464
340 b->memory = 1; 465 ngx_memcpy(b, ctx->buf, sizeof(ngx_buf_t));
341 b->pos = ngx_http_ssi_string; 466
342 b->last = ngx_http_ssi_string + ctx->saved; 467 b->last_buf = 0;
343 468 b->recycled = 0;
469 b->pos = ctx->copy_start;
470 b->last = ctx->copy_end;
471
472 if (b->in_file) {
473 if (conf->min_file_chunk < (size_t) (b->last - b->pos))
474 {
475 b->file_last = b->file_pos + (b->last - b->start);
476 b->file_pos += b->pos - b->start;
477
478 } else {
479 b->in_file = 0;
480 }
481 }
482
483 cl->next = NULL;
344 *ctx->last_out = cl; 484 *ctx->last_out = cl;
345 ctx->last_out = &cl->next; 485 ctx->last_out = &cl->next;
346 486
487 } else {
347 ctx->saved = 0; 488 ctx->saved = 0;
348 } 489 }
349
350 if (ctx->free) {
351 cl = ctx->free;
352 ctx->free = ctx->free->next;
353 b = cl->buf;
354
355 } else {
356 b = ngx_alloc_buf(r->pool);
357 if (b == NULL) {
358 return NGX_ERROR;
359 }
360
361 cl = ngx_alloc_chain_link(r->pool);
362 if (cl == NULL) {
363 return NGX_ERROR;
364 }
365
366 cl->buf = b;
367 }
368
369 ngx_memcpy(b, ctx->buf, sizeof(ngx_buf_t));
370
371 b->last_buf = 0;
372 b->recycled = 0;
373 b->pos = ctx->copy_start;
374 b->last = ctx->copy_end;
375
376 if (b->in_file) {
377
378 if (conf->min_file_chunk < (size_t) (b->last - b->pos)) {
379 b->file_last = b->file_pos + (b->last - b->start);
380 b->file_pos += b->pos - b->start;
381
382 } else {
383 b->in_file = 0;
384 }
385 }
386
387 cl->next = NULL;
388 *ctx->last_out = cl;
389 ctx->last_out = &cl->next;
390 } 490 }
391 491
392 if (ctx->state == ssi_start_state) { 492 if (ctx->state == ssi_start_state) {
393 ctx->copy_start = ctx->pos; 493 ctx->copy_start = ctx->pos;
394 ctx->copy_end = ctx->pos; 494 ctx->copy_end = ctx->pos;
418 } 518 }
419 519
420 break; 520 break;
421 } 521 }
422 522
423 if (cmd->name.len == 0) { 523 if (cmd->name.len == 0 && ctx->output) {
424 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 524 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
425 "invalid SSI command: \"%V\"", &ctx->command); 525 "invalid SSI command: \"%V\"", &ctx->command);
426 goto ssi_error; 526 goto ssi_error;
527 }
528
529 if (!ctx->output && !cmd->conditional) {
530 continue;
427 } 531 }
428 532
429 ngx_memzero(params, 533 ngx_memzero(params,
430 NGX_HTTP_SSI_MAX_PARAMS * sizeof(ngx_str_t *)); 534 NGX_HTTP_SSI_MAX_PARAMS * sizeof(ngx_str_t *));
431 535
477 581
478 goto ssi_error; 582 goto ssi_error;
479 } 583 }
480 } 584 }
481 585
586 if (cmd->flush && ctx->out) {
587 rc = ngx_http_ssi_output(r, ctx);
588
589 if (rc == NGX_ERROR) {
590 return NGX_ERROR;
591 }
592 }
593
482 if (cmd->handler(r, ctx, params) == NGX_OK) { 594 if (cmd->handler(r, ctx, params) == NGX_OK) {
483 continue; 595 continue;
484 } 596 }
485 } 597 }
486 598
523 ctx->last_out = &cl->next; 635 ctx->last_out = &cl->next;
524 636
525 continue; 637 continue;
526 } 638 }
527 639
528 if (ctx->buf->recycled || ctx->buf->last_buf) { 640 if (ctx->buf->last_buf || ctx->buf->recycled) {
641
529 if (b == NULL) { 642 if (b == NULL) {
530
531 if (ctx->free) { 643 if (ctx->free) {
532 cl = ctx->free; 644 cl = ctx->free;
533 ctx->free = ctx->free->next; 645 ctx->free = ctx->free->next;
534 b = cl->buf; 646 b = cl->buf;
535 ngx_memzero(b, sizeof(ngx_buf_t)); 647 ngx_memzero(b, sizeof(ngx_buf_t));
546 } 658 }
547 659
548 cl->buf = b; 660 cl->buf = b;
549 } 661 }
550 662
663 b->sync = 1;
664
551 cl->next = NULL; 665 cl->next = NULL;
552 *ctx->last_out = cl; 666 *ctx->last_out = cl;
553 ctx->last_out = &cl->next; 667 ctx->last_out = &cl->next;
554 } 668 }
555 669
556 b->last_buf = ctx->buf->last_buf; 670 b->last_buf = ctx->buf->last_buf;
557 b->flush = ctx->buf->recycled;
558 b->shadow = ctx->buf; 671 b->shadow = ctx->buf;
672
673 if (conf->ignore_recycled_buffers == 0) {
674 b->recycled = ctx->buf->recycled;
675 }
559 } 676 }
560 677
561 ctx->buf = NULL; 678 ctx->buf = NULL;
562 679
563 ctx->saved = ctx->looked; 680 ctx->saved = ctx->looked;
564 } 681 }
565 682
566 if (ctx->out == NULL && ctx->busy == NULL) { 683 if (ctx->out == NULL && ctx->busy == NULL) {
567 return NGX_OK; 684 return NGX_OK;
568 } 685 }
686
687 return ngx_http_ssi_output(r, ctx);
688 }
689
690
691 static ngx_int_t
692 ngx_http_ssi_output(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx)
693 {
694 ngx_int_t rc;
695 ngx_buf_t *b;
696 ngx_chain_t *cl;
569 697
570 rc = ngx_http_next_body_filter(r, ctx->out); 698 rc = ngx_http_next_body_filter(r, ctx->out);
571 699
572 if (ctx->busy == NULL) { 700 if (ctx->busy == NULL) {
573 ctx->busy = ctx->out; 701 ctx->busy = ctx->out;
1152 ngx_http_variable_value_t *vv; 1280 ngx_http_variable_value_t *vv;
1153 1281
1154 var = params[NGX_HTTP_SSI_ECHO_VAR]; 1282 var = params[NGX_HTTP_SSI_ECHO_VAR];
1155 1283
1156 for (i = 0; i < var->len; i++) { 1284 for (i = 0; i < var->len; i++) {
1157 var->data[i] = ngx_toupper(var->data[i]); 1285 var->data[i] = ngx_tolower(var->data[i]);
1158 } 1286 }
1159 1287
1160 vv = ngx_http_get_variable(r, var); 1288 vv = ngx_http_get_variable(r, var);
1161 1289
1162 if (vv == NULL) { 1290 if (vv == NULL) {
1163 return NGX_HTTP_SSI_ERROR; 1291 return NGX_HTTP_SSI_ERROR;
1164 } 1292 }
1165 1293
1166 if (vv == NGX_HTTP_VARIABLE_NOT_FOUND) { 1294 if (vv == NGX_HTTP_VAR_NOT_FOUND) {
1167 value = params[NGX_HTTP_SSI_ECHO_DEFAULT]; 1295 value = params[NGX_HTTP_SSI_ECHO_DEFAULT];
1168 1296
1169 if (value == NULL) { 1297 if (value == NULL) {
1170 value = &ngx_http_ssi_none; 1298 value = &ngx_http_ssi_none;
1171 1299
1202 1330
1203 return NGX_OK; 1331 return NGX_OK;
1204 } 1332 }
1205 1333
1206 1334
1335 static ngx_int_t
1336 ngx_http_ssi_config(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
1337 ngx_str_t **params)
1338 {
1339 ngx_str_t *value;
1340
1341 value = params[NGX_HTTP_SSI_CONFIG_TIMEFMT];
1342
1343 if (value) {
1344 ctx->timefmt = *value;
1345 }
1346
1347 return NGX_OK;
1348 }
1349
1350
1351 static ngx_int_t
1352 ngx_http_ssi_include(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
1353 ngx_str_t **params)
1354 {
1355 u_char ch, *p, **value;
1356 size_t *size, len;
1357 ngx_uint_t i, j, n, bracket;
1358 ngx_str_t uri, args, name;
1359 ngx_array_t lengths, values;
1360 ngx_http_variable_value_t *vv;
1361
1362 /* TODO: file, virtual vs file */
1363
1364 uri = *params[NGX_HTTP_SSI_INCLUDE_VIRTUAL];
1365 args.len = 0;
1366 args.data = NULL;
1367
1368 n = ngx_http_script_variables_count(&uri);
1369
1370 if (n > 0) {
1371
1372 if (ngx_array_init(&lengths, r->pool, 8, sizeof(size_t *)) != NGX_OK) {
1373 return NGX_HTTP_SSI_ERROR;
1374 }
1375
1376 if (ngx_array_init(&values, r->pool, 8, sizeof(u_char *)) != NGX_OK) {
1377 return NGX_HTTP_SSI_ERROR;
1378 }
1379
1380 len = 0;
1381
1382 for (i = 0; i < uri.len; /* void */ ) {
1383
1384 name.len = 0;
1385
1386 if (uri.data[i] == '$') {
1387
1388 if (++i == uri.len) {
1389 goto invalid_variable;
1390 }
1391
1392 if (uri.data[i] == '{') {
1393 bracket = 1;
1394
1395 if (++i == uri.len) {
1396 goto invalid_variable;
1397 }
1398
1399 name.data = &uri.data[i];
1400
1401 } else {
1402 bracket = 0;
1403 name.data = &uri.data[i];
1404 }
1405
1406 for ( /* void */ ; i < uri.len; i++, name.len++) {
1407 ch = uri.data[i];
1408
1409 if (ch == '}' && bracket) {
1410 i++;
1411 bracket = 0;
1412 break;
1413 }
1414
1415 if ((ch >= 'A' && ch <= 'Z')
1416 || (ch >= 'a' && ch <= 'z')
1417 || (ch >= '0' && ch <= '9')
1418 || ch == '_')
1419 {
1420 continue;
1421 }
1422
1423 break;
1424 }
1425
1426 if (bracket) {
1427 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1428 "the closing bracket in \"%V\" "
1429 "variable is missing", &name);
1430 return NGX_HTTP_SSI_ERROR;
1431 }
1432
1433 if (name.len == 0) {
1434 goto invalid_variable;
1435 }
1436
1437 for (j = 0; j < name.len; j++) {
1438 name.data[j] = ngx_tolower(name.data[j]);
1439 }
1440
1441 vv = ngx_http_get_variable(r, &name);
1442
1443 if (vv == NULL) {
1444 return NGX_HTTP_SSI_ERROR;
1445 }
1446
1447 if (vv == NGX_HTTP_VAR_NOT_FOUND) {
1448 continue;
1449 }
1450
1451 name = vv->text;
1452
1453 } else {
1454 name.data = &uri.data[i];
1455
1456 while (i < uri.len && uri.data[i] != '$') {
1457 i++;
1458 name.len++;
1459 }
1460 }
1461
1462 len += name.len;
1463
1464 size = ngx_array_push(&lengths);
1465 if (size == NULL) {
1466 return NGX_HTTP_SSI_ERROR;
1467 }
1468
1469 *size = name.len;
1470
1471 value = ngx_array_push(&values);
1472 if (value == NULL) {
1473 return NGX_HTTP_SSI_ERROR;
1474 }
1475
1476 *value = name.data;
1477 }
1478
1479 p = ngx_palloc(r->pool, len);
1480 if (p == NULL) {
1481 return NGX_HTTP_SSI_ERROR;
1482 }
1483
1484 uri.len = len;
1485 uri.data = p;
1486
1487 size = lengths.elts;
1488 value = values.elts;
1489
1490 for (i = 0; i < values.nelts; i++) {
1491 p = ngx_cpymem(p, value[i], size[i]);
1492 }
1493 }
1494
1495 for (i = 0; i < uri.len; i++) {
1496 if (uri.data[i] == '?') {
1497 args.len = uri.len - i - 1;
1498 args.data = &uri.data[i + 1];
1499 uri.len -= args.len + 1;
1500
1501 break;
1502 }
1503 }
1504
1505 if (ngx_http_subrequest(r, &uri, &args) != NGX_OK) {
1506 return NGX_HTTP_SSI_ERROR;
1507 }
1508
1509 return NGX_OK;
1510
1511 invalid_variable:
1512
1513 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1514 "invalid variable name in \"%V\"", &uri);
1515
1516 return NGX_ERROR;
1517 }
1518
1519
1520 static ngx_int_t
1521 ngx_http_ssi_if(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
1522 ngx_str_t **params)
1523 {
1524 ngx_str_t *expr, var;
1525 ngx_uint_t i;
1526 ngx_http_variable_value_t *vv;
1527
1528 expr = params[NGX_HTTP_SSI_IF_EXPR];
1529
1530 if (expr->data[0] != '$') {
1531 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1532 "invalid variable name in \"%V\"", expr);
1533 return NGX_HTTP_SSI_ERROR;
1534 }
1535
1536 var.len = expr->len - 1;
1537 var.data = expr->data + 1;
1538
1539 for (i = 0; i < var.len; i++) {
1540 var.data[i] = ngx_tolower(var.data[i]);
1541 }
1542
1543 vv = ngx_http_get_variable(r, &var);
1544
1545 if (vv == NULL) {
1546 return NGX_HTTP_SSI_ERROR;
1547 }
1548
1549 if (vv != NGX_HTTP_VAR_NOT_FOUND && vv->text.len != 0) {
1550 ctx->output = 1;
1551
1552 } else {
1553 ctx->output = 0;
1554 }
1555
1556 return NGX_OK;
1557 }
1558
1559
1560 static ngx_int_t
1561 ngx_http_ssi_else(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
1562 ngx_str_t **params)
1563 {
1564 ctx->output = !ctx->output;
1565
1566 return NGX_OK;
1567 }
1568
1569
1570 static ngx_int_t
1571 ngx_http_ssi_endif(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
1572 ngx_str_t **params)
1573 {
1574 ctx->output = 1;
1575
1576 return NGX_OK;
1577 }
1578
1579
1580 static ngx_http_variable_value_t *
1581 ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t *r, uintptr_t gmt)
1582 {
1583 ngx_http_ssi_ctx_t *ctx;
1584 ngx_http_variable_value_t *vv;
1585 struct tm tm;
1586 char buf[NGX_HTTP_SSI_DATE_LEN];
1587
1588 vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));
1589 if (vv == NULL) {
1590 return NULL;
1591 }
1592
1593 ctx = ngx_http_get_module_ctx(r, ngx_http_ssi_filter_module);
1594
1595 if (ctx->timefmt.len == sizeof("%s") - 1
1596 && ctx->timefmt.data[0] == '%' && ctx->timefmt.data[1] == 's')
1597 {
1598 vv->value = ngx_time() + (gmt ? 0 : ngx_gmtoff);
1599
1600 vv->text.data = ngx_palloc(r->pool, NGX_TIME_T_LEN);
1601 if (vv->text.data == NULL) {
1602 return NULL;
1603 }
1604
1605 vv->text.len = ngx_sprintf(vv->text.data, "%T", vv->value)
1606 - vv->text.data;
1607 return vv;
1608 }
1609
1610 if (gmt) {
1611 ngx_libc_gmtime(&tm);
1612 } else {
1613 ngx_libc_localtime(&tm);
1614 }
1615
1616 vv->value = ngx_time() + (gmt ? 0 : ngx_gmtoff);
1617
1618 vv->text.len = strftime(buf, NGX_HTTP_SSI_DATE_LEN,
1619 (char *) ctx->timefmt.data, &tm);
1620 if (vv->text.len == 0) {
1621 return NULL;
1622 }
1623
1624 vv->text.data = ngx_palloc(r->pool, vv->text.len);
1625 if (vv->text.data == NULL) {
1626 return NULL;
1627 }
1628
1629 ngx_memcpy(vv->text.data, buf, vv->text.len);
1630
1631 return vv;
1632 }
1633
1634
1635 static ngx_int_t
1636 ngx_http_ssi_add_variables(ngx_conf_t *cf)
1637 {
1638 ngx_http_variable_t *var, *v;
1639
1640 for (v = ngx_http_ssi_vars; v->name.len; v++) {
1641 var = ngx_http_add_variable(cf, &v->name, v->flags);
1642 if (var == NULL) {
1643 return NGX_ERROR;
1644 }
1645
1646 var->handler = v->handler;
1647 var->data = v->data;
1648 }
1649
1650 return NGX_OK;
1651 }
1652
1653
1207 static void * 1654 static void *
1208 ngx_http_ssi_create_conf(ngx_conf_t *cf) 1655 ngx_http_ssi_create_conf(ngx_conf_t *cf)
1209 { 1656 {
1210 ngx_http_ssi_conf_t *conf; 1657 ngx_http_ssi_conf_t *conf;
1211 1658
1214 return NGX_CONF_ERROR; 1661 return NGX_CONF_ERROR;
1215 } 1662 }
1216 1663
1217 conf->enable = NGX_CONF_UNSET; 1664 conf->enable = NGX_CONF_UNSET;
1218 conf->silent_errors = NGX_CONF_UNSET; 1665 conf->silent_errors = NGX_CONF_UNSET;
1666 conf->ignore_recycled_buffers = NGX_CONF_UNSET;
1219 1667
1220 conf->min_file_chunk = NGX_CONF_UNSET_SIZE; 1668 conf->min_file_chunk = NGX_CONF_UNSET_SIZE;
1221 conf->value_len = NGX_CONF_UNSET_SIZE; 1669 conf->value_len = NGX_CONF_UNSET_SIZE;
1222 1670
1223 return conf; 1671 return conf;
1230 ngx_http_ssi_conf_t *prev = parent; 1678 ngx_http_ssi_conf_t *prev = parent;
1231 ngx_http_ssi_conf_t *conf = child; 1679 ngx_http_ssi_conf_t *conf = child;
1232 1680
1233 ngx_conf_merge_value(conf->enable, prev->enable, 0); 1681 ngx_conf_merge_value(conf->enable, prev->enable, 0);
1234 ngx_conf_merge_value(conf->silent_errors, prev->silent_errors, 0); 1682 ngx_conf_merge_value(conf->silent_errors, prev->silent_errors, 0);
1683 ngx_conf_merge_value(conf->ignore_recycled_buffers,
1684 prev->ignore_recycled_buffers, 0);
1235 1685
1236 ngx_conf_merge_size_value(conf->min_file_chunk, prev->min_file_chunk, 1024); 1686 ngx_conf_merge_size_value(conf->min_file_chunk, prev->min_file_chunk, 1024);
1237 ngx_conf_merge_size_value(conf->value_len, prev->value_len, 256); 1687 ngx_conf_merge_size_value(conf->value_len, prev->value_len, 256);
1238 1688
1239 return NGX_CONF_OK; 1689 return NGX_CONF_OK;