upstream nginx-0.7.35
[nginx.git] / nginx / src / http / modules / ngx_http_fastcgi_module.c
1
2 /*
3  * Copyright (C) Igor Sysoev
4  */
5
6
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include <ngx_http.h>
10
11
12 typedef struct {
13     ngx_http_upstream_conf_t       upstream;
14
15     ngx_str_t                      index;
16
17     ngx_array_t                   *flushes;
18     ngx_array_t                   *params_len;
19     ngx_array_t                   *params;
20     ngx_array_t                   *params_source;
21     ngx_array_t                   *catch_stderr;
22
23     ngx_array_t                   *fastcgi_lengths;
24     ngx_array_t                   *fastcgi_values;
25
26 #if (NGX_PCRE)
27     ngx_regex_t                   *split_regex;
28     ngx_str_t                      split_name;
29 #endif
30 } ngx_http_fastcgi_loc_conf_t;
31
32
33 typedef enum {
34     ngx_http_fastcgi_st_version = 0,
35     ngx_http_fastcgi_st_type,
36     ngx_http_fastcgi_st_request_id_hi,
37     ngx_http_fastcgi_st_request_id_lo,
38     ngx_http_fastcgi_st_content_length_hi,
39     ngx_http_fastcgi_st_content_length_lo,
40     ngx_http_fastcgi_st_padding_length,
41     ngx_http_fastcgi_st_reserved,
42     ngx_http_fastcgi_st_data,
43     ngx_http_fastcgi_st_padding
44 } ngx_http_fastcgi_state_e;
45
46
47 typedef struct {
48     u_char                        *start;
49     u_char                        *end;
50 } ngx_http_fastcgi_split_part_t;
51
52
53 typedef struct {
54     ngx_http_fastcgi_state_e       state;
55     u_char                        *pos;
56     u_char                        *last;
57     ngx_uint_t                     type;
58     size_t                         length;
59     size_t                         padding;
60
61     ngx_uint_t                     fastcgi_stdout; /* unsigned :1 */
62
63     ngx_array_t                   *split_parts;
64
65     ngx_str_t                      script_name;
66     ngx_str_t                      path_info;
67 } ngx_http_fastcgi_ctx_t;
68
69
70 #define NGX_HTTP_FASTCGI_RESPONDER      1
71
72 #define NGX_HTTP_FASTCGI_BEGIN_REQUEST  1
73 #define NGX_HTTP_FASTCGI_ABORT_REQUEST  2
74 #define NGX_HTTP_FASTCGI_END_REQUEST    3
75 #define NGX_HTTP_FASTCGI_PARAMS         4
76 #define NGX_HTTP_FASTCGI_STDIN          5
77 #define NGX_HTTP_FASTCGI_STDOUT         6
78 #define NGX_HTTP_FASTCGI_STDERR         7
79 #define NGX_HTTP_FASTCGI_DATA           8
80
81
82 typedef struct {
83     u_char  version;
84     u_char  type;
85     u_char  request_id_hi;
86     u_char  request_id_lo;
87     u_char  content_length_hi;
88     u_char  content_length_lo;
89     u_char  padding_length;
90     u_char  reserved;
91 } ngx_http_fastcgi_header_t;
92
93
94 typedef struct {
95     u_char  role_hi;
96     u_char  role_lo;
97     u_char  flags;
98     u_char  reserved[5];
99 } ngx_http_fastcgi_begin_request_t;
100
101
102 typedef struct {
103     u_char  version;
104     u_char  type;
105     u_char  request_id_hi;
106     u_char  request_id_lo;
107 } ngx_http_fastcgi_header_small_t;
108
109
110 typedef struct {
111     ngx_http_fastcgi_header_t         h0;
112     ngx_http_fastcgi_begin_request_t  br;
113     ngx_http_fastcgi_header_small_t   h1;
114 } ngx_http_fastcgi_request_start_t;
115
116
117 static ngx_int_t ngx_http_fastcgi_eval(ngx_http_request_t *r,
118     ngx_http_fastcgi_loc_conf_t *flcf);
119 static ngx_int_t ngx_http_fastcgi_create_request(ngx_http_request_t *r);
120 static ngx_int_t ngx_http_fastcgi_reinit_request(ngx_http_request_t *r);
121 static ngx_int_t ngx_http_fastcgi_process_header(ngx_http_request_t *r);
122 static ngx_int_t ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p,
123     ngx_buf_t *buf);
124 static ngx_int_t ngx_http_fastcgi_process_record(ngx_http_request_t *r,
125     ngx_http_fastcgi_ctx_t *f);
126 static void ngx_http_fastcgi_abort_request(ngx_http_request_t *r);
127 static void ngx_http_fastcgi_finalize_request(ngx_http_request_t *r,
128     ngx_int_t rc);
129
130 static ngx_int_t ngx_http_fastcgi_add_variables(ngx_conf_t *cf);
131 static void *ngx_http_fastcgi_create_loc_conf(ngx_conf_t *cf);
132 static char *ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf,
133     void *parent, void *child);
134 static ngx_int_t ngx_http_fastcgi_script_name_variable(ngx_http_request_t *r,
135     ngx_http_variable_value_t *v, uintptr_t data);
136 static ngx_int_t ngx_http_fastcgi_path_info_variable(ngx_http_request_t *r,
137     ngx_http_variable_value_t *v, uintptr_t data);
138 static ngx_http_fastcgi_ctx_t *ngx_http_fastcgi_split(ngx_http_request_t *r,
139     ngx_http_fastcgi_loc_conf_t *flcf);
140
141 static char *ngx_http_fastcgi_pass(ngx_conf_t *cf, ngx_command_t *cmd,
142     void *conf);
143 static char *ngx_http_fastcgi_split_path_info(ngx_conf_t *cf,
144     ngx_command_t *cmd, void *conf);
145 static char *ngx_http_fastcgi_store(ngx_conf_t *cf, ngx_command_t *cmd,
146     void *conf);
147 static char *ngx_http_fastcgi_lowat_check(ngx_conf_t *cf, void *post,
148     void *data);
149
150 static char *ngx_http_fastcgi_upstream_max_fails_unsupported(ngx_conf_t *cf,
151     ngx_command_t *cmd, void *conf);
152 static char *ngx_http_fastcgi_upstream_fail_timeout_unsupported(ngx_conf_t *cf,
153     ngx_command_t *cmd, void *conf);
154
155
156 static ngx_conf_post_t  ngx_http_fastcgi_lowat_post =
157     { ngx_http_fastcgi_lowat_check };
158
159
160 static ngx_conf_bitmask_t  ngx_http_fastcgi_next_upstream_masks[] = {
161     { ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR },
162     { ngx_string("timeout"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },
163     { ngx_string("invalid_header"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },
164     { ngx_string("http_500"), NGX_HTTP_UPSTREAM_FT_HTTP_500 },
165     { ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 },
166     { ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },
167     { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF },
168     { ngx_null_string, 0 }
169 };
170
171
172 static ngx_command_t  ngx_http_fastcgi_commands[] = {
173
174     { ngx_string("fastcgi_pass"),
175       NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
176       ngx_http_fastcgi_pass,
177       NGX_HTTP_LOC_CONF_OFFSET,
178       0,
179       NULL },
180
181     { ngx_string("fastcgi_index"),
182       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
183       ngx_conf_set_str_slot,
184       NGX_HTTP_LOC_CONF_OFFSET,
185       offsetof(ngx_http_fastcgi_loc_conf_t, index),
186       NULL },
187
188     { ngx_string("fastcgi_split_path_info"),
189       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
190       ngx_http_fastcgi_split_path_info,
191       NGX_HTTP_LOC_CONF_OFFSET,
192       0,
193       NULL },
194
195     { ngx_string("fastcgi_store"),
196       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
197       ngx_http_fastcgi_store,
198       NGX_HTTP_LOC_CONF_OFFSET,
199       0,
200       NULL },
201
202     { ngx_string("fastcgi_store_access"),
203       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
204       ngx_conf_set_access_slot,
205       NGX_HTTP_LOC_CONF_OFFSET,
206       offsetof(ngx_http_fastcgi_loc_conf_t, upstream.store_access),
207       NULL },
208
209     { ngx_string("fastcgi_ignore_client_abort"),
210       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
211       ngx_conf_set_flag_slot,
212       NGX_HTTP_LOC_CONF_OFFSET,
213       offsetof(ngx_http_fastcgi_loc_conf_t, upstream.ignore_client_abort),
214       NULL },
215
216     { ngx_string("fastcgi_connect_timeout"),
217       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
218       ngx_conf_set_msec_slot,
219       NGX_HTTP_LOC_CONF_OFFSET,
220       offsetof(ngx_http_fastcgi_loc_conf_t, upstream.connect_timeout),
221       NULL },
222
223     { ngx_string("fastcgi_send_timeout"),
224       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
225       ngx_conf_set_msec_slot,
226       NGX_HTTP_LOC_CONF_OFFSET,
227       offsetof(ngx_http_fastcgi_loc_conf_t, upstream.send_timeout),
228       NULL },
229
230     { ngx_string("fastcgi_send_lowat"),
231       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
232       ngx_conf_set_size_slot,
233       NGX_HTTP_LOC_CONF_OFFSET,
234       offsetof(ngx_http_fastcgi_loc_conf_t, upstream.send_lowat),
235       &ngx_http_fastcgi_lowat_post },
236
237     { ngx_string("fastcgi_buffer_size"),
238       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
239       ngx_conf_set_size_slot,
240       NGX_HTTP_LOC_CONF_OFFSET,
241       offsetof(ngx_http_fastcgi_loc_conf_t, upstream.buffer_size),
242       NULL },
243
244     { ngx_string("fastcgi_pass_request_headers"),
245       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
246       ngx_conf_set_flag_slot,
247       NGX_HTTP_LOC_CONF_OFFSET,
248       offsetof(ngx_http_fastcgi_loc_conf_t, upstream.pass_request_headers),
249       NULL },
250
251     { ngx_string("fastcgi_pass_request_body"),
252       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
253       ngx_conf_set_flag_slot,
254       NGX_HTTP_LOC_CONF_OFFSET,
255       offsetof(ngx_http_fastcgi_loc_conf_t, upstream.pass_request_body),
256       NULL },
257
258     { ngx_string("fastcgi_intercept_errors"),
259       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
260       ngx_conf_set_flag_slot,
261       NGX_HTTP_LOC_CONF_OFFSET,
262       offsetof(ngx_http_fastcgi_loc_conf_t, upstream.intercept_errors),
263       NULL },
264
265     { ngx_string("fastcgi_read_timeout"),
266       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
267       ngx_conf_set_msec_slot,
268       NGX_HTTP_LOC_CONF_OFFSET,
269       offsetof(ngx_http_fastcgi_loc_conf_t, upstream.read_timeout),
270       NULL },
271
272     { ngx_string("fastcgi_buffers"),
273       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
274       ngx_conf_set_bufs_slot,
275       NGX_HTTP_LOC_CONF_OFFSET,
276       offsetof(ngx_http_fastcgi_loc_conf_t, upstream.bufs),
277       NULL },
278
279     { ngx_string("fastcgi_busy_buffers_size"),
280       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
281       ngx_conf_set_size_slot,
282       NGX_HTTP_LOC_CONF_OFFSET,
283       offsetof(ngx_http_fastcgi_loc_conf_t, upstream.busy_buffers_size_conf),
284       NULL },
285
286     { ngx_string("fastcgi_temp_path"),
287       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
288       ngx_conf_set_path_slot,
289       NGX_HTTP_LOC_CONF_OFFSET,
290       offsetof(ngx_http_fastcgi_loc_conf_t, upstream.temp_path),
291       (void *) ngx_garbage_collector_temp_handler },
292
293     { ngx_string("fastcgi_max_temp_file_size"),
294       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
295       ngx_conf_set_size_slot,
296       NGX_HTTP_LOC_CONF_OFFSET,
297       offsetof(ngx_http_fastcgi_loc_conf_t, upstream.max_temp_file_size_conf),
298       NULL },
299
300     { ngx_string("fastcgi_temp_file_write_size"),
301       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
302       ngx_conf_set_size_slot,
303       NGX_HTTP_LOC_CONF_OFFSET,
304       offsetof(ngx_http_fastcgi_loc_conf_t, upstream.temp_file_write_size_conf),
305       NULL },
306
307     { ngx_string("fastcgi_next_upstream"),
308       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
309       ngx_conf_set_bitmask_slot,
310       NGX_HTTP_LOC_CONF_OFFSET,
311       offsetof(ngx_http_fastcgi_loc_conf_t, upstream.next_upstream),
312       &ngx_http_fastcgi_next_upstream_masks },
313
314     { ngx_string("fastcgi_upstream_max_fails"),
315       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
316       ngx_http_fastcgi_upstream_max_fails_unsupported,
317       0,
318       0,
319       NULL },
320
321     { ngx_string("fastcgi_upstream_fail_timeout"),
322       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
323       ngx_http_fastcgi_upstream_fail_timeout_unsupported,
324       0,
325       0,
326       NULL },
327
328     { ngx_string("fastcgi_param"),
329       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
330       ngx_conf_set_keyval_slot,
331       NGX_HTTP_LOC_CONF_OFFSET,
332       offsetof(ngx_http_fastcgi_loc_conf_t, params_source),
333       NULL },
334
335     { ngx_string("fastcgi_pass_header"),
336       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
337       ngx_conf_set_str_array_slot,
338       NGX_HTTP_LOC_CONF_OFFSET,
339       offsetof(ngx_http_fastcgi_loc_conf_t, upstream.pass_headers),
340       NULL },
341
342     { ngx_string("fastcgi_hide_header"),
343       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
344       ngx_conf_set_str_array_slot,
345       NGX_HTTP_LOC_CONF_OFFSET,
346       offsetof(ngx_http_fastcgi_loc_conf_t, upstream.hide_headers),
347       NULL },
348
349     { ngx_string("fastcgi_catch_stderr"),
350       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
351       ngx_conf_set_str_array_slot,
352       NGX_HTTP_LOC_CONF_OFFSET,
353       offsetof(ngx_http_fastcgi_loc_conf_t, catch_stderr),
354       NULL },
355
356       ngx_null_command
357 };
358
359
360 static ngx_http_module_t  ngx_http_fastcgi_module_ctx = {
361     ngx_http_fastcgi_add_variables,        /* preconfiguration */
362     NULL,                                  /* postconfiguration */
363
364     NULL,                                  /* create main configuration */
365     NULL,                                  /* init main configuration */
366
367     NULL,                                  /* create server configuration */
368     NULL,                                  /* merge server configuration */
369
370     ngx_http_fastcgi_create_loc_conf,      /* create location configuration */
371     ngx_http_fastcgi_merge_loc_conf        /* merge location configuration */
372 };
373
374
375 ngx_module_t  ngx_http_fastcgi_module = {
376     NGX_MODULE_V1,
377     &ngx_http_fastcgi_module_ctx,          /* module context */
378     ngx_http_fastcgi_commands,             /* module directives */
379     NGX_HTTP_MODULE,                       /* module type */
380     NULL,                                  /* init master */
381     NULL,                                  /* init module */
382     NULL,                                  /* init process */
383     NULL,                                  /* init thread */
384     NULL,                                  /* exit thread */
385     NULL,                                  /* exit process */
386     NULL,                                  /* exit master */
387     NGX_MODULE_V1_PADDING
388 };
389
390
391 static ngx_http_fastcgi_request_start_t  ngx_http_fastcgi_request_start = {
392     { 1,                                               /* version */
393       NGX_HTTP_FASTCGI_BEGIN_REQUEST,                  /* type */
394       0,                                               /* request_id_hi */
395       1,                                               /* request_id_lo */
396       0,                                               /* content_length_hi */
397       sizeof(ngx_http_fastcgi_begin_request_t),        /* content_length_lo */
398       0,                                               /* padding_length */
399       0 },                                             /* reserved */
400
401     { 0,                                               /* role_hi */
402       NGX_HTTP_FASTCGI_RESPONDER,                      /* role_lo */
403       0, /* NGX_HTTP_FASTCGI_KEEP_CONN */              /* flags */
404       { 0, 0, 0, 0, 0 } },                             /* reserved[5] */
405
406     { 1,                                               /* version */
407       NGX_HTTP_FASTCGI_PARAMS,                         /* type */
408       0,                                               /* request_id_hi */
409       1 },                                             /* request_id_lo */
410
411 };
412
413
414 static ngx_http_variable_t  ngx_http_fastcgi_vars[] = {
415
416     { ngx_string("fastcgi_script_name"), NULL,
417       ngx_http_fastcgi_script_name_variable, 0,
418       NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
419
420     { ngx_string("fastcgi_path_info"), NULL,
421       ngx_http_fastcgi_path_info_variable, 0,
422       NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
423
424     { ngx_null_string, NULL, NULL, 0, 0, 0 }
425 };
426
427
428 static ngx_str_t  ngx_http_fastcgi_hide_headers[] = {
429     ngx_string("Status"),
430     ngx_string("X-Accel-Expires"),
431     ngx_string("X-Accel-Redirect"),
432     ngx_string("X-Accel-Limit-Rate"),
433     ngx_string("X-Accel-Buffering"),
434     ngx_string("X-Accel-Charset"),
435     ngx_null_string
436 };
437
438
439 static ngx_int_t
440 ngx_http_fastcgi_handler(ngx_http_request_t *r)
441 {
442     ngx_int_t                     rc;
443     ngx_http_upstream_t          *u;
444     ngx_http_fastcgi_ctx_t       *f;
445     ngx_http_fastcgi_loc_conf_t  *flcf;
446
447     if (r->subrequest_in_memory) {
448         ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
449                       "ngx_http_fastcgi_module does not support "
450                       "subrequest in memory");
451         return NGX_HTTP_INTERNAL_SERVER_ERROR;
452     }
453
454     f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t));
455     if (f == NULL) {
456         return NGX_ERROR;
457     }
458
459     ngx_http_set_ctx(r, f, ngx_http_fastcgi_module);
460
461     u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t));
462     if (u == NULL) {
463         return NGX_HTTP_INTERNAL_SERVER_ERROR;
464     }
465
466     r->upstream = u;
467
468     flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
469
470     if (flcf->fastcgi_lengths) {
471         if (ngx_http_fastcgi_eval(r, flcf) != NGX_OK) {
472             return NGX_HTTP_INTERNAL_SERVER_ERROR;
473         }
474     }
475
476     u->schema.len = sizeof("fastcgi://") - 1;
477     u->schema.data = (u_char *) "fastcgi://";
478
479     u->peer.log = r->connection->log;
480     u->peer.log_error = NGX_ERROR_ERR;
481 #if (NGX_THREADS)
482     u->peer.lock = &r->connection->lock;
483 #endif
484
485     u->output.tag = (ngx_buf_tag_t) &ngx_http_fastcgi_module;
486
487     u->conf = &flcf->upstream;
488
489     u->create_request = ngx_http_fastcgi_create_request;
490     u->reinit_request = ngx_http_fastcgi_reinit_request;
491     u->process_header = ngx_http_fastcgi_process_header;
492     u->abort_request = ngx_http_fastcgi_abort_request;
493     u->finalize_request = ngx_http_fastcgi_finalize_request;
494
495     u->buffering = 1;
496
497     u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));
498     if (u->pipe == NULL) {
499         return NGX_HTTP_INTERNAL_SERVER_ERROR;
500     }
501
502     u->pipe->input_filter = ngx_http_fastcgi_input_filter;
503     u->pipe->input_ctx = r;
504
505     rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
506
507     if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
508         return rc;
509     }
510
511     return NGX_DONE;
512 }
513
514
515 static ngx_int_t
516 ngx_http_fastcgi_eval(ngx_http_request_t *r, ngx_http_fastcgi_loc_conf_t *flcf)
517 {
518     ngx_url_t  u;
519
520     ngx_memzero(&u, sizeof(ngx_url_t));
521
522     if (ngx_http_script_run(r, &u.url, flcf->fastcgi_lengths->elts, 0,
523                             flcf->fastcgi_values->elts)
524         == NULL)
525     {
526         return NGX_ERROR;
527     }
528
529     u.no_resolve = 1;
530
531     if (ngx_parse_url(r->pool, &u) != NGX_OK) {
532          if (u.err) {
533             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
534                           "%s in upstream \"%V\"", u.err, &u.url);
535         }
536
537         return NGX_ERROR;
538     }
539
540     if (u.no_port) {
541         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
542                           "no port in upstream \"%V\"", &u.url);
543         return NGX_ERROR;
544     }
545
546     r->upstream->resolved = ngx_pcalloc(r->pool,
547                                         sizeof(ngx_http_upstream_resolved_t));
548     if (r->upstream->resolved == NULL) {
549         return NGX_ERROR;
550     }
551
552     if (u.addrs && u.addrs[0].sockaddr) {
553         r->upstream->resolved->sockaddr = u.addrs[0].sockaddr;
554         r->upstream->resolved->socklen = u.addrs[0].socklen;
555         r->upstream->resolved->naddrs = 1;
556         r->upstream->resolved->host = u.addrs[0].name;
557
558     } else {
559         r->upstream->resolved->host = u.host;
560         r->upstream->resolved->port = u.port;
561     }
562
563     return NGX_OK;
564 }
565
566
567 static ngx_int_t
568 ngx_http_fastcgi_create_request(ngx_http_request_t *r)
569 {
570     off_t                         file_pos;
571     u_char                        ch, *pos;
572     size_t                        size, len, key_len, val_len, padding;
573     ngx_uint_t                    i, n, next;
574     ngx_buf_t                    *b;
575     ngx_chain_t                  *cl, *body;
576     ngx_list_part_t              *part;
577     ngx_table_elt_t              *header;
578     ngx_http_script_code_pt       code;
579     ngx_http_script_engine_t      e, le;
580     ngx_http_fastcgi_header_t    *h;
581     ngx_http_fastcgi_loc_conf_t  *flcf;
582     ngx_http_script_len_code_pt   lcode;
583
584     len = 0;
585
586     flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
587
588     if (flcf->params_len) {
589         ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
590
591         ngx_http_script_flush_no_cacheable_variables(r, flcf->flushes);
592         le.flushed = 1;
593
594         le.ip = flcf->params_len->elts;
595         le.request = r;
596
597         while (*(uintptr_t *) le.ip) {
598
599             lcode = *(ngx_http_script_len_code_pt *) le.ip;
600             key_len = lcode(&le);
601
602             for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
603                 lcode = *(ngx_http_script_len_code_pt *) le.ip;
604             }
605             le.ip += sizeof(uintptr_t);
606
607             len += 1 + key_len + ((val_len > 127) ? 4 : 1) + val_len;
608         }
609     }
610
611     if (flcf->upstream.pass_request_headers) {
612
613         part = &r->headers_in.headers.part;
614         header = part->elts;
615
616         for (i = 0; /* void */; i++) {
617
618             if (i >= part->nelts) {
619                 if (part->next == NULL) {
620                     break;
621                 }
622
623                 part = part->next;
624                 header = part->elts;
625                 i = 0;
626             }
627
628             len += ((sizeof("HTTP_") - 1 + header[i].key.len > 127) ? 4 : 1)
629                 + ((header[i].value.len > 127) ? 4 : 1)
630                 + sizeof("HTTP_") - 1 + header[i].key.len + header[i].value.len;
631         }
632     }
633
634
635     if (len > 65535) {
636         ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
637                       "fastcgi request record is too big: %uz", len);
638         return NGX_ERROR;
639     }
640
641
642     padding = 8 - len % 8;
643     padding = (padding == 8) ? 0 : padding;
644
645
646     size = sizeof(ngx_http_fastcgi_header_t)
647            + sizeof(ngx_http_fastcgi_begin_request_t)
648
649            + sizeof(ngx_http_fastcgi_header_t)  /* NGX_HTTP_FASTCGI_PARAMS */
650            + len + padding
651            + sizeof(ngx_http_fastcgi_header_t)  /* NGX_HTTP_FASTCGI_PARAMS */
652
653            + sizeof(ngx_http_fastcgi_header_t); /* NGX_HTTP_FASTCGI_STDIN */
654
655
656     b = ngx_create_temp_buf(r->pool, size);
657     if (b == NULL) {
658         return NGX_ERROR;
659     }
660
661     cl = ngx_alloc_chain_link(r->pool);
662     if (cl == NULL) {
663         return NGX_ERROR;
664     }
665
666     cl->buf = b;
667
668     ngx_memcpy(b->pos, &ngx_http_fastcgi_request_start,
669                sizeof(ngx_http_fastcgi_request_start_t));
670
671     h = (ngx_http_fastcgi_header_t *)
672              (b->pos + sizeof(ngx_http_fastcgi_header_t)
673                      + sizeof(ngx_http_fastcgi_begin_request_t));
674
675     h->content_length_hi = (u_char) ((len >> 8) & 0xff);
676     h->content_length_lo = (u_char) (len & 0xff);
677     h->padding_length = (u_char) padding;
678     h->reserved = 0;
679
680     b->last = b->pos + sizeof(ngx_http_fastcgi_header_t)
681                      + sizeof(ngx_http_fastcgi_begin_request_t)
682                      + sizeof(ngx_http_fastcgi_header_t);
683
684
685     if (flcf->params_len) {
686         ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
687
688         e.ip = flcf->params->elts;
689         e.pos = b->last;
690         e.request = r;
691         e.flushed = 1;
692
693         le.ip = flcf->params_len->elts;
694
695         while (*(uintptr_t *) le.ip) {
696
697             lcode = *(ngx_http_script_len_code_pt *) le.ip;
698             key_len = (u_char) lcode(&le);
699
700             for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
701                 lcode = *(ngx_http_script_len_code_pt *) le.ip;
702             }
703             le.ip += sizeof(uintptr_t);
704
705             *e.pos++ = (u_char) key_len;
706
707             if (val_len > 127) {
708                 *e.pos++ = (u_char) (((val_len >> 24) & 0x7f) | 0x80);
709                 *e.pos++ = (u_char) ((val_len >> 16) & 0xff);
710                 *e.pos++ = (u_char) ((val_len >> 8) & 0xff);
711                 *e.pos++ = (u_char) (val_len & 0xff);
712
713             } else {
714                 *e.pos++ = (u_char) val_len;
715             }
716
717             while (*(uintptr_t *) e.ip) {
718                 code = *(ngx_http_script_code_pt *) e.ip;
719                 code((ngx_http_script_engine_t *) &e);
720             }
721             e.ip += sizeof(uintptr_t);
722
723             ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
724                            "fastcgi param: \"%*s: %*s\"",
725                            key_len, e.pos - (key_len + val_len),
726                            val_len, e.pos - val_len);
727         }
728
729         b->last = e.pos;
730     }
731
732
733     if (flcf->upstream.pass_request_headers) {
734
735         part = &r->headers_in.headers.part;
736         header = part->elts;
737
738         for (i = 0; /* void */; i++) {
739
740             if (i >= part->nelts) {
741                 if (part->next == NULL) {
742                     break;
743                 }
744
745                 part = part->next;
746                 header = part->elts;
747                 i = 0;
748             }
749
750             len = sizeof("HTTP_") - 1 + header[i].key.len;
751             if (len > 127) {
752                 *b->last++ = (u_char) (((len >> 24) & 0x7f) | 0x80);
753                 *b->last++ = (u_char) ((len >> 16) & 0xff);
754                 *b->last++ = (u_char) ((len >> 8) & 0xff);
755                 *b->last++ = (u_char) (len & 0xff);
756
757             } else {
758                 *b->last++ = (u_char) len;
759             }
760
761             len = header[i].value.len;
762             if (len > 127) {
763                 *b->last++ = (u_char) (((len >> 24) & 0x7f) | 0x80);
764                 *b->last++ = (u_char) ((len >> 16) & 0xff);
765                 *b->last++ = (u_char) ((len >> 8) & 0xff);
766                 *b->last++ = (u_char) (len & 0xff);
767
768             } else {
769                 *b->last++ = (u_char) len;
770             }
771
772             b->last = ngx_cpymem(b->last, "HTTP_", sizeof("HTTP_") - 1);
773
774             for (n = 0; n < header[i].key.len; n++) {
775                 ch = header[i].key.data[n];
776
777                 if (ch >= 'a' && ch <= 'z') {
778                     ch &= ~0x20;
779
780                 } else if (ch == '-') {
781                     ch = '_';
782                 }
783
784                 *b->last++ = ch;
785             }
786
787             b->last = ngx_copy(b->last, header[i].value.data,
788                                header[i].value.len);
789         }
790     }
791
792
793     if (padding) {
794         ngx_memzero(b->last, padding);
795         b->last += padding;
796     }
797
798
799     h = (ngx_http_fastcgi_header_t *) b->last;
800     b->last += sizeof(ngx_http_fastcgi_header_t);
801
802     h->version = 1;
803     h->type = NGX_HTTP_FASTCGI_PARAMS;
804     h->request_id_hi = 0;
805     h->request_id_lo = 1;
806     h->content_length_hi = 0;
807     h->content_length_lo = 0;
808     h->padding_length = 0;
809     h->reserved = 0;
810
811     h = (ngx_http_fastcgi_header_t *) b->last;
812     b->last += sizeof(ngx_http_fastcgi_header_t);
813
814     if (flcf->upstream.pass_request_body) {
815         body = r->upstream->request_bufs;
816         r->upstream->request_bufs = cl;
817
818 #if (NGX_SUPPRESS_WARN)
819         file_pos = 0;
820         pos = NULL;
821 #endif
822
823         while (body) {
824
825             if (body->buf->in_file) {
826                 file_pos = body->buf->file_pos;
827
828             } else {
829                 pos = body->buf->pos;
830             }
831
832             next = 0;
833
834             do {
835                 b = ngx_alloc_buf(r->pool);
836                 if (b == NULL) {
837                     return NGX_ERROR;
838                 }
839
840                 ngx_memcpy(b, body->buf, sizeof(ngx_buf_t));
841
842                 if (body->buf->in_file) {
843                     b->file_pos = file_pos;
844                     file_pos += 32 * 1024;
845
846                     if (file_pos >= body->buf->file_last) {
847                         file_pos = body->buf->file_last;
848                         next = 1;
849                     }
850
851                     b->file_last = file_pos;
852                     len = (ngx_uint_t) (file_pos - b->file_pos);
853
854                 } else {
855                     b->pos = pos;
856                     pos += 32 * 1024;
857
858                     if (pos >= body->buf->last) {
859                         pos = body->buf->last;
860                         next = 1;
861                     }
862
863                     b->last = pos;
864                     len = (ngx_uint_t) (pos - b->pos);
865                 }
866
867                 padding = 8 - len % 8;
868                 padding = (padding == 8) ? 0 : padding;
869
870                 h->version = 1;
871                 h->type = NGX_HTTP_FASTCGI_STDIN;
872                 h->request_id_hi = 0;
873                 h->request_id_lo = 1;
874                 h->content_length_hi = (u_char) ((len >> 8) & 0xff);
875                 h->content_length_lo = (u_char) (len & 0xff);
876                 h->padding_length = (u_char) padding;
877                 h->reserved = 0;
878
879                 cl->next = ngx_alloc_chain_link(r->pool);
880                 if (cl->next == NULL) {
881                     return NGX_ERROR;
882                 }
883
884                 cl = cl->next;
885                 cl->buf = b;
886
887                 b = ngx_create_temp_buf(r->pool,
888                                         sizeof(ngx_http_fastcgi_header_t)
889                                         + padding);
890                 if (b == NULL) {
891                     return NGX_ERROR;
892                 }
893
894                 if (padding) {
895                     ngx_memzero(b->last, padding);
896                     b->last += padding;
897                 }
898
899                 h = (ngx_http_fastcgi_header_t *) b->last;
900                 b->last += sizeof(ngx_http_fastcgi_header_t);
901
902                 cl->next = ngx_alloc_chain_link(r->pool);
903                 if (cl->next == NULL) {
904                     return NGX_ERROR;
905                 }
906
907                 cl = cl->next;
908                 cl->buf = b;
909
910             } while (!next);
911
912             body = body->next;
913         }
914
915     } else {
916         r->upstream->request_bufs = cl;
917     }
918
919     h->version = 1;
920     h->type = NGX_HTTP_FASTCGI_STDIN;
921     h->request_id_hi = 0;
922     h->request_id_lo = 1;
923     h->content_length_hi = 0;
924     h->content_length_lo = 0;
925     h->padding_length = 0;
926     h->reserved = 0;
927
928     cl->next = NULL;
929
930     return NGX_OK;
931 }
932
933
934 static ngx_int_t
935 ngx_http_fastcgi_reinit_request(ngx_http_request_t *r)
936 {
937     ngx_http_fastcgi_ctx_t  *f;
938
939     f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
940
941     if (f == NULL) {
942         return NGX_OK;
943     }
944
945     f->state = ngx_http_fastcgi_st_version;
946     f->fastcgi_stdout = 0;
947
948     return NGX_OK;
949 }
950
951
952 static ngx_int_t
953 ngx_http_fastcgi_process_header(ngx_http_request_t *r)
954 {
955     u_char                         *p, *start, *last, *part_start;
956     size_t                          size;
957     ngx_str_t                      *status_line, line, *pattern;
958     ngx_int_t                       rc, status;
959     ngx_buf_t                       buf;
960     ngx_uint_t                      i;
961     ngx_table_elt_t                *h;
962     ngx_http_upstream_t            *u;
963     ngx_http_fastcgi_ctx_t         *f;
964     ngx_http_upstream_header_t     *hh;
965     ngx_http_fastcgi_loc_conf_t    *flcf;
966     ngx_http_fastcgi_split_part_t  *part;
967     ngx_http_upstream_main_conf_t  *umcf;
968
969     f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
970
971     umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
972
973     u = r->upstream;
974
975     for ( ;; ) {
976
977         if (f->state < ngx_http_fastcgi_st_data) {
978
979             f->pos = u->buffer.pos;
980             f->last = u->buffer.last;
981
982             rc = ngx_http_fastcgi_process_record(r, f);
983
984             u->buffer.pos = f->pos;
985             u->buffer.last = f->last;
986
987             if (rc == NGX_AGAIN) {
988                 return NGX_AGAIN;
989             }
990
991             if (rc == NGX_ERROR) {
992                 return NGX_HTTP_UPSTREAM_INVALID_HEADER;
993             }
994
995             if (f->type != NGX_HTTP_FASTCGI_STDOUT
996                 && f->type != NGX_HTTP_FASTCGI_STDERR)
997             {
998                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
999                               "upstream sent unexpected FastCGI record: %d",
1000                               f->type);
1001
1002                 return NGX_HTTP_UPSTREAM_INVALID_HEADER;
1003             }
1004
1005             if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) {
1006                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1007                               "upstream closed prematurely FastCGI stdout");
1008
1009                 return NGX_HTTP_UPSTREAM_INVALID_HEADER;
1010             }
1011         }
1012
1013         if (f->state == ngx_http_fastcgi_st_padding) {
1014
1015             if (u->buffer.pos + f->padding < u->buffer.last) {
1016                 f->state = ngx_http_fastcgi_st_version;
1017                 u->buffer.pos += f->padding;
1018
1019                 continue;
1020             }
1021
1022             if (u->buffer.pos + f->padding == u->buffer.last) {
1023                 f->state = ngx_http_fastcgi_st_version;
1024                 u->buffer.pos = u->buffer.last;
1025
1026                 return NGX_AGAIN;
1027             }
1028
1029             f->padding -= u->buffer.last - u->buffer.pos;
1030             u->buffer.pos = u->buffer.last;
1031
1032             return NGX_AGAIN;
1033         }
1034
1035
1036         /* f->state == ngx_http_fastcgi_st_data */
1037
1038         if (f->type == NGX_HTTP_FASTCGI_STDERR) {
1039
1040             if (f->length) {
1041                 line.data = u->buffer.pos;
1042
1043                 if (u->buffer.pos + f->length <= u->buffer.last) {
1044                     line.len = f->length;
1045                     u->buffer.pos += f->length;
1046                     f->length = 0;
1047                     f->state = ngx_http_fastcgi_st_padding;
1048
1049                 } else {
1050                     line.len = u->buffer.last - u->buffer.pos;
1051                     f->length -= u->buffer.last - u->buffer.pos;
1052                     u->buffer.pos = u->buffer.last;
1053                 }
1054
1055                 while (line.data[line.len - 1] == LF
1056                        || line.data[line.len - 1] == CR
1057                        || line.data[line.len - 1] == '.'
1058                        || line.data[line.len - 1] == ' ')
1059                 {
1060                     line.len--;
1061                 }
1062
1063                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1064                               "FastCGI sent in stderr: \"%V\"", &line);
1065
1066                 flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
1067
1068                 if (flcf->catch_stderr) {
1069                     pattern = flcf->catch_stderr->elts;
1070
1071                     line.data[line.len - 1] = '\0';
1072
1073                     for (i = 0; i < flcf->catch_stderr->nelts; i++) {
1074                         if (ngx_strstr(line.data, pattern[i].data)) {
1075                             return NGX_HTTP_UPSTREAM_INVALID_HEADER;
1076                         }
1077                     }
1078                 }
1079
1080                 if (u->buffer.pos == u->buffer.last) {
1081
1082                     if (!f->fastcgi_stdout) {
1083
1084                         /*
1085                          * the special handling the large number
1086                          * of the PHP warnings to not allocate memory
1087                          */
1088
1089                         u->buffer.pos = u->buffer.start;
1090                         u->buffer.last = u->buffer.start;
1091                     }
1092
1093                     return NGX_AGAIN;
1094                 }
1095
1096             } else {
1097                 f->state = ngx_http_fastcgi_st_version;
1098             }
1099
1100             continue;
1101         }
1102
1103
1104         /* f->type == NGX_HTTP_FASTCGI_STDOUT */
1105
1106         f->fastcgi_stdout = 1;
1107
1108         start = u->buffer.pos;
1109
1110         if (u->buffer.pos + f->length < u->buffer.last) {
1111
1112             /*
1113              * set u->buffer.last to the end of the FastCGI record data
1114              * for ngx_http_parse_header_line()
1115              */
1116
1117             last = u->buffer.last;
1118             u->buffer.last = u->buffer.pos + f->length;
1119
1120         } else {
1121             last = NULL;
1122         }
1123
1124         for ( ;; ) {
1125
1126             part_start = u->buffer.pos;
1127
1128             rc = ngx_http_parse_header_line(r, &u->buffer, 1);
1129
1130             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1131                            "http fastcgi parser: %d", rc);
1132
1133             if (rc == NGX_AGAIN) {
1134                 break;
1135             }
1136
1137             if (rc == NGX_OK) {
1138
1139                 /* a header line has been parsed successfully */
1140
1141                 h = ngx_list_push(&u->headers_in.headers);
1142                 if (h == NULL) {
1143                     return NGX_ERROR;
1144                 }
1145
1146                 if (f->split_parts && f->split_parts->nelts) {
1147
1148                     part = f->split_parts->elts;
1149                     size = u->buffer.pos - part_start;
1150
1151                     for (i = 0; i < f->split_parts->nelts; i++) {
1152                         size += part[i].end - part[i].start;
1153                     }
1154
1155                     p = ngx_pnalloc(r->pool, size);
1156                     if (p == NULL) {
1157                         return NGX_ERROR;
1158                     }
1159
1160                     buf.pos = p;
1161
1162                     for (i = 0; i < f->split_parts->nelts; i++) {
1163                         p = ngx_cpymem(p, part[i].start,
1164                                        part[i].end - part[i].start);
1165                     }
1166
1167                     p = ngx_cpymem(p, part_start, u->buffer.pos - part_start);
1168
1169                     buf.last = p;
1170
1171                     f->split_parts->nelts = 0;
1172
1173                     rc = ngx_http_parse_header_line(r, &buf, 1);
1174
1175                     h->key.len = r->header_name_end - r->header_name_start;
1176                     h->key.data = r->header_name_start;
1177                     h->key.data[h->key.len] = '\0';
1178
1179                     h->value.len = r->header_end - r->header_start;
1180                     h->value.data = r->header_start;
1181                     h->value.data[h->value.len] = '\0';
1182
1183                     h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);
1184                     if (h->lowcase_key == NULL) {
1185                         return NGX_ERROR;
1186                     }
1187
1188                 } else {
1189
1190                     h->key.len = r->header_name_end - r->header_name_start;
1191                     h->value.len = r->header_end - r->header_start;
1192
1193                     h->key.data = ngx_pnalloc(r->pool,
1194                                               h->key.len + 1 + h->value.len + 1
1195                                               + h->key.len);
1196                     if (h->key.data == NULL) {
1197                         return NGX_ERROR;
1198                     }
1199
1200                     h->value.data = h->key.data + h->key.len + 1;
1201                     h->lowcase_key = h->key.data + h->key.len + 1
1202                                      + h->value.len + 1;
1203
1204                     ngx_cpystrn(h->key.data, r->header_name_start,
1205                                 h->key.len + 1);
1206                     ngx_cpystrn(h->value.data, r->header_start,
1207                                 h->value.len + 1);
1208                 }
1209
1210                 h->hash = r->header_hash;
1211
1212                 if (h->key.len == r->lowcase_index) {
1213                     ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
1214
1215                 } else {
1216                     ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
1217                 }
1218
1219                 hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
1220                                    h->lowcase_key, h->key.len);
1221
1222                 if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
1223                     return NGX_ERROR;
1224                 }
1225
1226                 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1227                                "http fastcgi header: \"%V: %V\"",
1228                                &h->key, &h->value);
1229
1230                 if (u->buffer.pos < u->buffer.last) {
1231                     continue;
1232                 }
1233
1234                 /* the end of the FastCGI record */
1235
1236                 break;
1237             }
1238
1239             if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
1240
1241                 /* a whole header has been parsed successfully */
1242
1243                 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1244                                "http fastcgi header done");
1245
1246                 if (u->headers_in.status) {
1247                     status_line = &u->headers_in.status->value;
1248
1249                     status = ngx_atoi(status_line->data, 3);
1250
1251                     if (status == NGX_ERROR) {
1252                         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1253                                       "upstream sent invalid status \"%V\"",
1254                                       status_line);
1255                         return NGX_HTTP_UPSTREAM_INVALID_HEADER;
1256                     }
1257
1258                     u->headers_in.status_n = status;
1259                     u->headers_in.status_line = *status_line;
1260
1261                 } else if (u->headers_in.location) {
1262                     u->headers_in.status_n = 302;
1263                     u->headers_in.status_line.len =
1264                                            sizeof("302 Moved Temporarily") - 1;
1265                     u->headers_in.status_line.data =
1266                                            (u_char *) "302 Moved Temporarily";
1267
1268                 } else {
1269                     u->headers_in.status_n = 200;
1270                     u->headers_in.status_line.len = sizeof("200 OK") - 1;
1271                     u->headers_in.status_line.data = (u_char *) "200 OK";
1272                 }
1273
1274                 u->state->status = u->headers_in.status_n;
1275 #if 0
1276                 if (u->cacheable) {
1277                     u->cacheable = ngx_http_upstream_is_cacheable(r);
1278                 }
1279 #endif
1280
1281                 break;
1282             }
1283
1284             /* there was error while a header line parsing */
1285
1286             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1287                           "upstream sent invalid header");
1288
1289             return NGX_HTTP_UPSTREAM_INVALID_HEADER;
1290         }
1291
1292         if (last) {
1293             u->buffer.last = last;
1294         }
1295
1296         f->length -= u->buffer.pos - start;
1297
1298         if (f->length == 0) {
1299             if (f->padding) {
1300                 f->state = ngx_http_fastcgi_st_padding;
1301             } else {
1302                 f->state = ngx_http_fastcgi_st_version;
1303             }
1304         }
1305
1306         if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
1307             return NGX_OK;
1308         }
1309
1310         if (rc == NGX_OK) {
1311             continue;
1312         }
1313
1314         /* rc == NGX_AGAIN */
1315
1316         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1317                        "upstream split a header line in FastCGI records");
1318
1319         if (f->split_parts == NULL) {
1320             f->split_parts = ngx_array_create(r->pool, 1,
1321                                         sizeof(ngx_http_fastcgi_split_part_t));
1322             if (f->split_parts == NULL) {
1323                 return NGX_ERROR;
1324             }
1325         }
1326
1327         part = ngx_array_push(f->split_parts);
1328
1329         part->start = part_start;
1330         part->end = u->buffer.last;
1331
1332         return NGX_AGAIN;
1333     }
1334 }
1335
1336
1337 static ngx_int_t
1338 ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
1339 {
1340     ngx_int_t                rc;
1341     ngx_buf_t               *b, **prev;
1342     ngx_str_t                line;
1343     ngx_chain_t             *cl;
1344     ngx_http_request_t      *r;
1345     ngx_http_fastcgi_ctx_t  *f;
1346
1347     if (buf->pos == buf->last) {
1348         return NGX_OK;
1349     }
1350
1351     r = p->input_ctx;
1352     f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
1353
1354     b = NULL;
1355     prev = &buf->shadow;
1356
1357     f->pos = buf->pos;
1358     f->last = buf->last;
1359
1360     for ( ;; ) {
1361         if (f->state < ngx_http_fastcgi_st_data) {
1362
1363             rc = ngx_http_fastcgi_process_record(r, f);
1364
1365             if (rc == NGX_AGAIN) {
1366                 break;
1367             }
1368
1369             if (rc == NGX_ERROR) {
1370                 return NGX_ERROR;
1371             }
1372
1373             if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) {
1374                 f->state = ngx_http_fastcgi_st_version;
1375                 p->upstream_done = 1;
1376
1377                 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,
1378                                "http fastcgi closed stdout");
1379
1380                 continue;
1381             }
1382
1383             if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {
1384                 f->state = ngx_http_fastcgi_st_version;
1385                 p->upstream_done = 1;
1386
1387                 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,
1388                                "http fastcgi sent end request");
1389
1390                 break;
1391             }
1392         }
1393
1394
1395         if (f->state == ngx_http_fastcgi_st_padding) {
1396
1397             if (f->pos + f->padding < f->last) {
1398                 f->state = ngx_http_fastcgi_st_version;
1399                 f->pos += f->padding;
1400
1401                 continue;
1402             }
1403
1404             if (f->pos + f->padding == f->last) {
1405                 f->state = ngx_http_fastcgi_st_version;
1406
1407                 break;
1408             }
1409
1410             f->padding -= f->last - f->pos;
1411
1412             break;
1413         }
1414
1415
1416         /* f->state == ngx_http_fastcgi_st_data */
1417
1418         if (f->type == NGX_HTTP_FASTCGI_STDERR) {
1419
1420             if (f->length) {
1421
1422                 if (f->pos == f->last) {
1423                     break;
1424                 }
1425
1426                 line.data = f->pos;
1427
1428                 if (f->pos + f->length <= f->last) {
1429                     line.len = f->length;
1430                     f->pos += f->length;
1431                     f->length = 0;
1432                     f->state = ngx_http_fastcgi_st_padding;
1433
1434                 } else {
1435                     line.len = f->last - f->pos;
1436                     f->length -= f->last - f->pos;
1437                     f->pos = f->last;
1438                 }
1439
1440                 while (line.data[line.len - 1] == LF
1441                        || line.data[line.len - 1] == CR
1442                        || line.data[line.len - 1] == '.'
1443                        || line.data[line.len - 1] == ' ')
1444                 {
1445                     line.len--;
1446                 }
1447
1448                 ngx_log_error(NGX_LOG_ERR, p->log, 0,
1449                               "FastCGI sent in stderr: \"%V\"", &line);
1450
1451                 if (f->pos == f->last) {
1452                     break;
1453                 }
1454
1455             } else {
1456                 f->state = ngx_http_fastcgi_st_version;
1457             }
1458
1459             continue;
1460         }
1461
1462
1463         /* f->type == NGX_HTTP_FASTCGI_STDOUT */
1464
1465         if (f->pos == f->last) {
1466             break;
1467         }
1468
1469         if (p->free) {
1470             b = p->free->buf;
1471             p->free = p->free->next;
1472
1473         } else {
1474             b = ngx_alloc_buf(p->pool);
1475             if (b == NULL) {
1476                 return NGX_ERROR;
1477             }
1478         }
1479
1480         ngx_memzero(b, sizeof(ngx_buf_t));
1481
1482         b->pos = f->pos;
1483         b->start = buf->start;
1484         b->end = buf->end;
1485         b->tag = p->tag;
1486         b->temporary = 1;
1487         b->recycled = 1;
1488
1489         *prev = b;
1490         prev = &b->shadow;
1491
1492         cl = ngx_alloc_chain_link(p->pool);
1493         if (cl == NULL) {
1494             return NGX_ERROR;
1495         }
1496
1497         cl->buf = b;
1498         cl->next = NULL;
1499
1500         if (p->in) {
1501             *p->last_in = cl;
1502         } else {
1503             p->in = cl;
1504         }
1505         p->last_in = &cl->next;
1506
1507
1508         /* STUB */ b->num = buf->num;
1509
1510         ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,
1511                        "input buf #%d %p", b->num, b->pos);
1512
1513         if (f->pos + f->length < f->last) {
1514
1515             if (f->padding) {
1516                 f->state = ngx_http_fastcgi_st_padding;
1517             } else {
1518                 f->state = ngx_http_fastcgi_st_version;
1519             }
1520
1521             f->pos += f->length;
1522             b->last = f->pos;
1523
1524             continue;
1525         }
1526
1527         if (f->pos + f->length == f->last) {
1528
1529             if (f->padding) {
1530                 f->state = ngx_http_fastcgi_st_padding;
1531             } else {
1532                 f->state = ngx_http_fastcgi_st_version;
1533             }
1534
1535             b->last = f->last;
1536
1537             break;
1538         }
1539
1540         f->length -= f->last - f->pos;
1541
1542         b->last = f->last;
1543
1544         break;
1545
1546     }
1547
1548     if (b) {
1549         b->shadow = buf;
1550         b->last_shadow = 1;
1551
1552         ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,
1553                        "input buf %p %z", b->pos, b->last - b->pos);
1554
1555         return NGX_OK;
1556     }
1557
1558     /* there is no data record in the buf, add it to free chain */
1559
1560     if (ngx_event_pipe_add_free_buf(p, buf) != NGX_OK) {
1561         return NGX_ERROR;
1562     }
1563
1564     return NGX_OK;
1565 }
1566
1567
1568 static ngx_int_t
1569 ngx_http_fastcgi_process_record(ngx_http_request_t *r,
1570     ngx_http_fastcgi_ctx_t *f)
1571 {
1572     u_char                     ch, *p;
1573     ngx_http_fastcgi_state_e   state;
1574
1575     state = f->state;
1576
1577     for (p = f->pos; p < f->last; p++) {
1578
1579         ch = *p;
1580
1581         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1582                        "http fastcgi record byte: %02Xd", ch);
1583
1584         switch (state) {
1585
1586         case ngx_http_fastcgi_st_version:
1587             if (ch != 1) {
1588                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1589                               "upstream sent unsupported FastCGI "
1590                               "protocol version: %d", ch);
1591                 return NGX_ERROR;
1592             }
1593             state = ngx_http_fastcgi_st_type;
1594             break;
1595
1596         case ngx_http_fastcgi_st_type:
1597             switch (ch) {
1598             case NGX_HTTP_FASTCGI_STDOUT:
1599             case NGX_HTTP_FASTCGI_STDERR:
1600             case NGX_HTTP_FASTCGI_END_REQUEST:
1601                  f->type = (ngx_uint_t) ch;
1602                  break;
1603             default:
1604                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1605                               "upstream sent invalid FastCGI "
1606                               "record type: %d", ch);
1607                 return NGX_ERROR;
1608
1609             }
1610             state = ngx_http_fastcgi_st_request_id_hi;
1611             break;
1612
1613         /* we support the single request per connection */
1614
1615         case ngx_http_fastcgi_st_request_id_hi:
1616             if (ch != 0) {
1617                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1618                               "upstream sent unexpected FastCGI "
1619                               "request id high byte: %d", ch);
1620                 return NGX_ERROR;
1621             }
1622             state = ngx_http_fastcgi_st_request_id_lo;
1623             break;
1624
1625         case ngx_http_fastcgi_st_request_id_lo:
1626             if (ch != 1) {
1627                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1628                               "upstream sent unexpected FastCGI "
1629                               "request id low byte: %d", ch);
1630                 return NGX_ERROR;
1631             }
1632             state = ngx_http_fastcgi_st_content_length_hi;
1633             break;
1634
1635         case ngx_http_fastcgi_st_content_length_hi:
1636             f->length = ch << 8;
1637             state = ngx_http_fastcgi_st_content_length_lo;
1638             break;
1639
1640         case ngx_http_fastcgi_st_content_length_lo:
1641             f->length |= (size_t) ch;
1642             state = ngx_http_fastcgi_st_padding_length;
1643             break;
1644
1645         case ngx_http_fastcgi_st_padding_length:
1646             f->padding = (size_t) ch;
1647             state = ngx_http_fastcgi_st_reserved;
1648             break;
1649
1650         case ngx_http_fastcgi_st_reserved:
1651             state = ngx_http_fastcgi_st_data;
1652
1653             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1654                            "http fastcgi record length: %z", f->length);
1655
1656             f->pos = p + 1;
1657             f->state = state;
1658
1659             return NGX_OK;
1660
1661         /* suppress warning */
1662         case ngx_http_fastcgi_st_data:
1663         case ngx_http_fastcgi_st_padding:
1664             break;
1665         }
1666     }
1667
1668     f->state = state;
1669
1670     return NGX_AGAIN;
1671 }
1672
1673
1674 static void
1675 ngx_http_fastcgi_abort_request(ngx_http_request_t *r)
1676 {
1677     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1678                    "abort http fastcgi request");
1679
1680     return;
1681 }
1682
1683
1684 static void
1685 ngx_http_fastcgi_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
1686 {
1687     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1688                    "finalize http fastcgi request");
1689
1690     return;
1691 }
1692
1693
1694 static ngx_int_t
1695 ngx_http_fastcgi_add_variables(ngx_conf_t *cf)
1696 {
1697    ngx_http_variable_t  *var, *v;
1698
1699     for (v = ngx_http_fastcgi_vars; v->name.len; v++) {
1700         var = ngx_http_add_variable(cf, &v->name, v->flags);
1701         if (var == NULL) {
1702             return NGX_ERROR;
1703         }
1704
1705         var->get_handler = v->get_handler;
1706         var->data = v->data;
1707     }
1708
1709     return NGX_OK;
1710 }
1711
1712
1713 static void *
1714 ngx_http_fastcgi_create_loc_conf(ngx_conf_t *cf)
1715 {
1716     ngx_http_fastcgi_loc_conf_t  *conf;
1717
1718     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_fastcgi_loc_conf_t));
1719     if (conf == NULL) {
1720         return NGX_CONF_ERROR;
1721     }
1722
1723     /*
1724      * set by ngx_pcalloc():
1725      *
1726      *     conf->upstream.bufs.num = 0;
1727      *     conf->upstream.next_upstream = 0;
1728      *     conf->upstream.temp_path = NULL;
1729      *     conf->upstream.hide_headers_hash = { NULL, 0 };
1730      *     conf->upstream.uri = { 0, NULL };
1731      *     conf->upstream.location = NULL;
1732      *     conf->upstream.store_lengths = NULL;
1733      *     conf->upstream.store_values = NULL;
1734      *
1735      *     conf->index.len = 0;
1736      *     conf->index.data = NULL;
1737      */
1738
1739     conf->upstream.store = NGX_CONF_UNSET;
1740     conf->upstream.store_access = NGX_CONF_UNSET_UINT;
1741     conf->upstream.buffering = NGX_CONF_UNSET;
1742     conf->upstream.ignore_client_abort = NGX_CONF_UNSET;
1743
1744     conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
1745     conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
1746     conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;
1747
1748     conf->upstream.send_lowat = NGX_CONF_UNSET_SIZE;
1749     conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;
1750
1751     conf->upstream.busy_buffers_size_conf = NGX_CONF_UNSET_SIZE;
1752     conf->upstream.max_temp_file_size_conf = NGX_CONF_UNSET_SIZE;
1753     conf->upstream.temp_file_write_size_conf = NGX_CONF_UNSET_SIZE;
1754
1755     conf->upstream.pass_request_headers = NGX_CONF_UNSET;
1756     conf->upstream.pass_request_body = NGX_CONF_UNSET;
1757
1758     conf->upstream.hide_headers = NGX_CONF_UNSET_PTR;
1759     conf->upstream.pass_headers = NGX_CONF_UNSET_PTR;
1760
1761     conf->upstream.intercept_errors = NGX_CONF_UNSET;
1762
1763     /* "fastcgi_cyclic_temp_file" is disabled */
1764     conf->upstream.cyclic_temp_file = 0;
1765
1766     conf->catch_stderr = NGX_CONF_UNSET_PTR;
1767
1768     return conf;
1769 }
1770
1771
1772 static char *
1773 ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
1774 {
1775     ngx_http_fastcgi_loc_conf_t *prev = parent;
1776     ngx_http_fastcgi_loc_conf_t *conf = child;
1777
1778     u_char                       *p;
1779     size_t                        size;
1780     uintptr_t                    *code;
1781     ngx_uint_t                    i;
1782     ngx_keyval_t                 *src;
1783     ngx_hash_init_t               hash;
1784     ngx_http_script_compile_t     sc;
1785     ngx_http_script_copy_code_t  *copy;
1786
1787     if (conf->upstream.store != 0) {
1788         ngx_conf_merge_value(conf->upstream.store,
1789                                   prev->upstream.store, 0);
1790
1791         if (conf->upstream.store_lengths == NULL) {
1792             conf->upstream.store_lengths = prev->upstream.store_lengths;
1793             conf->upstream.store_values = prev->upstream.store_values;
1794         }
1795     }
1796
1797     ngx_conf_merge_uint_value(conf->upstream.store_access,
1798                               prev->upstream.store_access, 0600);
1799
1800     ngx_conf_merge_value(conf->upstream.buffering,
1801                               prev->upstream.buffering, 1);
1802
1803     ngx_conf_merge_value(conf->upstream.ignore_client_abort,
1804                               prev->upstream.ignore_client_abort, 0);
1805
1806     ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
1807                               prev->upstream.connect_timeout, 60000);
1808
1809     ngx_conf_merge_msec_value(conf->upstream.send_timeout,
1810                               prev->upstream.send_timeout, 60000);
1811
1812     ngx_conf_merge_msec_value(conf->upstream.read_timeout,
1813                               prev->upstream.read_timeout, 60000);
1814
1815     ngx_conf_merge_size_value(conf->upstream.send_lowat,
1816                               prev->upstream.send_lowat, 0);
1817
1818     ngx_conf_merge_size_value(conf->upstream.buffer_size,
1819                               prev->upstream.buffer_size,
1820                               (size_t) ngx_pagesize);
1821
1822
1823     ngx_conf_merge_bufs_value(conf->upstream.bufs, prev->upstream.bufs,
1824                               8, ngx_pagesize);
1825
1826     if (conf->upstream.bufs.num < 2) {
1827         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1828                            "there must be at least 2 \"fastcgi_buffers\"");
1829         return NGX_CONF_ERROR;
1830     }
1831
1832
1833     size = conf->upstream.buffer_size;
1834     if (size < conf->upstream.bufs.size) {
1835         size = conf->upstream.bufs.size;
1836     }
1837
1838
1839     ngx_conf_merge_size_value(conf->upstream.busy_buffers_size_conf,
1840                               prev->upstream.busy_buffers_size_conf,
1841                               NGX_CONF_UNSET_SIZE);
1842
1843     if (conf->upstream.busy_buffers_size_conf == NGX_CONF_UNSET_SIZE) {
1844         conf->upstream.busy_buffers_size = 2 * size;
1845     } else {
1846         conf->upstream.busy_buffers_size =
1847                                          conf->upstream.busy_buffers_size_conf;
1848     }
1849
1850     if (conf->upstream.busy_buffers_size < size) {
1851         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1852              "\"fastcgi_busy_buffers_size\" must be equal or bigger than "
1853              "maximum of the value of \"fastcgi_buffer_size\" and "
1854              "one of the \"fastcgi_buffers\"");
1855
1856         return NGX_CONF_ERROR;
1857     }
1858
1859     if (conf->upstream.busy_buffers_size
1860         > (conf->upstream.bufs.num - 1) * conf->upstream.bufs.size)
1861     {
1862         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1863              "\"fastcgi_busy_buffers_size\" must be less than "
1864              "the size of all \"fastcgi_buffers\" minus one buffer");
1865
1866         return NGX_CONF_ERROR;
1867     }
1868
1869
1870     ngx_conf_merge_size_value(conf->upstream.temp_file_write_size_conf,
1871                               prev->upstream.temp_file_write_size_conf,
1872                               NGX_CONF_UNSET_SIZE);
1873
1874     if (conf->upstream.temp_file_write_size_conf == NGX_CONF_UNSET_SIZE) {
1875         conf->upstream.temp_file_write_size = 2 * size;
1876     } else {
1877         conf->upstream.temp_file_write_size =
1878                                       conf->upstream.temp_file_write_size_conf;
1879     }
1880
1881     if (conf->upstream.temp_file_write_size < size) {
1882         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1883              "\"fastcgi_temp_file_write_size\" must be equal or bigger than "
1884              "maximum of the value of \"fastcgi_buffer_size\" and "
1885              "one of the \"fastcgi_buffers\"");
1886
1887         return NGX_CONF_ERROR;
1888     }
1889
1890
1891     ngx_conf_merge_size_value(conf->upstream.max_temp_file_size_conf,
1892                               prev->upstream.max_temp_file_size_conf,
1893                               NGX_CONF_UNSET_SIZE);
1894
1895     if (conf->upstream.max_temp_file_size_conf == NGX_CONF_UNSET_SIZE) {
1896         conf->upstream.max_temp_file_size = 1024 * 1024 * 1024;
1897     } else {
1898         conf->upstream.max_temp_file_size =
1899                                         conf->upstream.max_temp_file_size_conf;
1900     }
1901
1902     if (conf->upstream.max_temp_file_size != 0
1903         && conf->upstream.max_temp_file_size < size)
1904     {
1905         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1906              "\"fastcgi_max_temp_file_size\" must be equal to zero to disable "
1907              "the temporary files usage or must be equal or bigger than "
1908              "maximum of the value of \"fastcgi_buffer_size\" and "
1909              "one of the \"fastcgi_buffers\"");
1910
1911         return NGX_CONF_ERROR;
1912     }
1913
1914
1915     ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,
1916                               prev->upstream.next_upstream,
1917                               (NGX_CONF_BITMASK_SET
1918                                |NGX_HTTP_UPSTREAM_FT_ERROR
1919                                |NGX_HTTP_UPSTREAM_FT_TIMEOUT));
1920
1921     if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {
1922         conf->upstream.next_upstream = NGX_CONF_BITMASK_SET
1923                                        |NGX_HTTP_UPSTREAM_FT_OFF;
1924     }
1925
1926     ngx_conf_merge_path_value(conf->upstream.temp_path,
1927                               prev->upstream.temp_path,
1928                               NGX_HTTP_FASTCGI_TEMP_PATH, 1, 2, 0,
1929                               ngx_garbage_collector_temp_handler, cf);
1930
1931     ngx_conf_merge_value(conf->upstream.pass_request_headers,
1932                               prev->upstream.pass_request_headers, 1);
1933     ngx_conf_merge_value(conf->upstream.pass_request_body,
1934                               prev->upstream.pass_request_body, 1);
1935
1936     ngx_conf_merge_value(conf->upstream.intercept_errors,
1937                               prev->upstream.intercept_errors, 0);
1938
1939     ngx_conf_merge_ptr_value(conf->catch_stderr, prev->catch_stderr, NULL);
1940
1941
1942     ngx_conf_merge_str_value(conf->index, prev->index, "");
1943
1944     hash.max_size = 512;
1945     hash.bucket_size = ngx_align(64, ngx_cacheline_size);
1946     hash.name = "fastcgi_hide_headers_hash";
1947
1948     if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream,
1949                                             &prev->upstream,
1950                                             ngx_http_fastcgi_hide_headers,
1951                                             &hash)
1952         != NGX_OK)
1953     {
1954         return NGX_CONF_ERROR;
1955     }
1956
1957     if (conf->upstream.upstream == NULL) {
1958         conf->upstream.upstream = prev->upstream.upstream;
1959     }
1960
1961     if (conf->fastcgi_lengths == NULL) {
1962         conf->fastcgi_lengths = prev->fastcgi_lengths;
1963         conf->fastcgi_values = prev->fastcgi_values;
1964     }
1965
1966 #if (NGX_PCRE)
1967     if (conf->split_regex == NULL) {
1968         conf->split_regex = prev->split_regex;
1969         conf->split_name = prev->split_name;
1970     }
1971 #endif
1972
1973     if (conf->params_source == NULL) {
1974         conf->flushes = prev->flushes;
1975         conf->params_len = prev->params_len;
1976         conf->params = prev->params;
1977         conf->params_source = prev->params_source;
1978
1979         if (conf->params_source == NULL) {
1980             return NGX_CONF_OK;
1981         }
1982     }
1983
1984     conf->params_len = ngx_array_create(cf->pool, 64, 1);
1985     if (conf->params_len == NULL) {
1986         return NGX_CONF_ERROR;
1987     }
1988
1989     conf->params = ngx_array_create(cf->pool, 512, 1);
1990     if (conf->params == NULL) {
1991         return NGX_CONF_ERROR;
1992     }
1993
1994     src = conf->params_source->elts;
1995     for (i = 0; i < conf->params_source->nelts; i++) {
1996
1997         if (ngx_http_script_variables_count(&src[i].value) == 0) {
1998             copy = ngx_array_push_n(conf->params_len,
1999                                     sizeof(ngx_http_script_copy_code_t));
2000             if (copy == NULL) {
2001                 return NGX_CONF_ERROR;
2002             }
2003
2004             copy->code = (ngx_http_script_code_pt)
2005                                                   ngx_http_script_copy_len_code;
2006             copy->len = src[i].key.len;
2007
2008
2009             copy = ngx_array_push_n(conf->params_len,
2010                                     sizeof(ngx_http_script_copy_code_t));
2011             if (copy == NULL) {
2012                 return NGX_CONF_ERROR;
2013             }
2014
2015             copy->code = (ngx_http_script_code_pt)
2016                                                  ngx_http_script_copy_len_code;
2017             copy->len = src[i].value.len;
2018
2019
2020             size = (sizeof(ngx_http_script_copy_code_t)
2021                        + src[i].key.len + src[i].value.len
2022                        + sizeof(uintptr_t) - 1)
2023                     & ~(sizeof(uintptr_t) - 1);
2024
2025             copy = ngx_array_push_n(conf->params, size);
2026             if (copy == NULL) {
2027                 return NGX_CONF_ERROR;
2028             }
2029
2030             copy->code = ngx_http_script_copy_code;
2031             copy->len = src[i].key.len + src[i].value.len;
2032
2033             p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);
2034
2035             p = ngx_cpymem(p, src[i].key.data, src[i].key.len);
2036             ngx_memcpy(p, src[i].value.data, src[i].value.len);
2037
2038         } else {
2039             copy = ngx_array_push_n(conf->params_len,
2040                                     sizeof(ngx_http_script_copy_code_t));
2041             if (copy == NULL) {
2042                 return NGX_CONF_ERROR;
2043             }
2044
2045             copy->code = (ngx_http_script_code_pt)
2046                                                  ngx_http_script_copy_len_code;
2047             copy->len = src[i].key.len;
2048
2049
2050             size = (sizeof(ngx_http_script_copy_code_t)
2051                     + src[i].key.len + sizeof(uintptr_t) - 1)
2052                     & ~(sizeof(uintptr_t) - 1);
2053
2054             copy = ngx_array_push_n(conf->params, size);
2055             if (copy == NULL) {
2056                 return NGX_CONF_ERROR;
2057             }
2058
2059             copy->code = ngx_http_script_copy_code;
2060             copy->len = src[i].key.len;
2061
2062             p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);
2063             ngx_memcpy(p, src[i].key.data, src[i].key.len);
2064
2065
2066             ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
2067
2068             sc.cf = cf;
2069             sc.source = &src[i].value;
2070             sc.flushes = &conf->flushes;
2071             sc.lengths = &conf->params_len;
2072             sc.values = &conf->params;
2073
2074             if (ngx_http_script_compile(&sc) != NGX_OK) {
2075                 return NGX_CONF_ERROR;
2076             }
2077         }
2078
2079         code = ngx_array_push_n(conf->params_len, sizeof(uintptr_t));
2080         if (code == NULL) {
2081             return NGX_CONF_ERROR;
2082         }
2083
2084         *code = (uintptr_t) NULL;
2085
2086
2087         code = ngx_array_push_n(conf->params, sizeof(uintptr_t));
2088         if (code == NULL) {
2089             return NGX_CONF_ERROR;
2090         }
2091
2092         *code = (uintptr_t) NULL;
2093     }
2094
2095     code = ngx_array_push_n(conf->params_len, sizeof(uintptr_t));
2096     if (code == NULL) {
2097         return NGX_CONF_ERROR;
2098     }
2099
2100     *code = (uintptr_t) NULL;
2101
2102     return NGX_CONF_OK;
2103 }
2104
2105
2106 static ngx_int_t
2107 ngx_http_fastcgi_script_name_variable(ngx_http_request_t *r,
2108     ngx_http_variable_value_t *v, uintptr_t data)
2109 {
2110     u_char                       *p;
2111     ngx_http_fastcgi_ctx_t       *f;
2112     ngx_http_fastcgi_loc_conf_t  *flcf;
2113
2114     flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
2115
2116     f = ngx_http_fastcgi_split(r, flcf);
2117
2118     if (f == NULL) {
2119         return NGX_ERROR;
2120     }
2121
2122     if (f->script_name.len == 0
2123         || f->script_name.data[f->script_name.len - 1] != '/')
2124     {
2125         v->len = f->script_name.len;
2126         v->valid = 1;
2127         v->no_cacheable = 0;
2128         v->not_found = 0;
2129         v->data = f->script_name.data;
2130
2131         return NGX_OK;
2132     }
2133
2134     v->len = f->script_name.len + flcf->index.len;
2135
2136     v->data = ngx_pnalloc(r->pool, v->len);
2137     if (v->data == NULL) {
2138         return NGX_ERROR;
2139     }
2140
2141     p = ngx_copy(v->data, f->script_name.data, f->script_name.len);
2142     ngx_memcpy(p, flcf->index.data, flcf->index.len);
2143
2144     return NGX_OK;
2145 }
2146
2147
2148 static ngx_int_t
2149 ngx_http_fastcgi_path_info_variable(ngx_http_request_t *r,
2150     ngx_http_variable_value_t *v, uintptr_t data)
2151 {
2152     ngx_http_fastcgi_ctx_t       *f;
2153     ngx_http_fastcgi_loc_conf_t  *flcf;
2154
2155     flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
2156
2157     f = ngx_http_fastcgi_split(r, flcf);
2158
2159     if (f == NULL) {
2160         return NGX_ERROR;
2161     }
2162
2163     v->len = f->path_info.len;
2164     v->valid = 1;
2165     v->no_cacheable = 0;
2166     v->not_found = 0;
2167     v->data = f->path_info.data;
2168
2169     return NGX_OK;
2170 }
2171
2172
2173 static ngx_http_fastcgi_ctx_t *
2174 ngx_http_fastcgi_split(ngx_http_request_t *r, ngx_http_fastcgi_loc_conf_t *flcf)
2175 {
2176     ngx_http_fastcgi_ctx_t       *f;
2177 #if (NGX_PCRE)
2178     ngx_int_t                     n;
2179     int                           captures[(1 + 2) * 3];
2180
2181     f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
2182
2183     if (f == NULL) {
2184         f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t));
2185         if (f == NULL) {
2186             return NULL;
2187         }
2188
2189         ngx_http_set_ctx(r, f, ngx_http_fastcgi_module);
2190     }
2191
2192     if (f->script_name.len) {
2193         return f;
2194     }
2195
2196     if (flcf->split_regex == NULL) {
2197         f->script_name = r->uri;
2198         return f;
2199     }
2200
2201     n = ngx_regex_exec(flcf->split_regex, &r->uri, captures, (1 + 2) * 3);
2202
2203     if (n == NGX_REGEX_NO_MATCHED) {
2204         f->script_name = r->uri;
2205         return f;
2206     }
2207
2208     if (n < 0) {
2209         ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
2210                       ngx_regex_exec_n " failed: %d on \"%V\" using \"%V\"",
2211                       n, &r->uri, &flcf->split_name);
2212         return NULL;
2213     }
2214
2215     /* match */
2216
2217     f->script_name.len = captures[3] - captures[2];
2218     f->script_name.data = r->uri.data;
2219
2220     f->path_info.len = captures[5] - captures[4];
2221     f->path_info.data = r->uri.data + f->script_name.len;
2222
2223     return f;
2224
2225 #else
2226
2227     f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
2228
2229     if (f == NULL) {
2230         f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t));
2231         if (f == NULL) {
2232             return NULL;
2233         }
2234
2235         ngx_http_set_ctx(r, f, ngx_http_fastcgi_module);
2236     }
2237
2238     f->script_name = r->uri;
2239
2240     return f;
2241
2242 #endif
2243 }
2244
2245
2246 static char *
2247 ngx_http_fastcgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
2248 {
2249     ngx_http_fastcgi_loc_conf_t *flcf = conf;
2250
2251     ngx_url_t                   u;
2252     ngx_str_t                  *value, *url;
2253     ngx_uint_t                  n;
2254     ngx_http_core_loc_conf_t   *clcf;
2255     ngx_http_script_compile_t   sc;
2256
2257     if (flcf->upstream.upstream || flcf->fastcgi_lengths) {
2258         return "is duplicate";
2259     }
2260
2261     clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
2262     clcf->handler = ngx_http_fastcgi_handler;
2263
2264     value = cf->args->elts;
2265
2266     url = &value[1];
2267
2268     n = ngx_http_script_variables_count(url);
2269
2270     if (n) {
2271
2272         ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
2273
2274         sc.cf = cf;
2275         sc.source = url;
2276         sc.lengths = &flcf->fastcgi_lengths;
2277         sc.values = &flcf->fastcgi_values;
2278         sc.variables = n;
2279         sc.complete_lengths = 1;
2280         sc.complete_values = 1;
2281
2282         if (ngx_http_script_compile(&sc) != NGX_OK) {
2283             return NGX_CONF_ERROR;
2284         }
2285
2286         return NGX_CONF_OK;
2287     }
2288
2289     ngx_memzero(&u, sizeof(ngx_url_t));
2290
2291     u.url = value[1];
2292     u.no_resolve = 1;
2293
2294     flcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
2295     if (flcf->upstream.upstream == NULL) {
2296         return NGX_CONF_ERROR;
2297     }
2298
2299     if (clcf->name.data[clcf->name.len - 1] == '/') {
2300         clcf->auto_redirect = 1;
2301     }
2302
2303     return NGX_CONF_OK;
2304 }
2305
2306
2307 static char *
2308 ngx_http_fastcgi_split_path_info(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
2309 {
2310 #if (NGX_PCRE)
2311     ngx_http_fastcgi_loc_conf_t *flcf = conf;
2312
2313     ngx_int_t   n;
2314     ngx_str_t  *value, err;
2315     u_char      errstr[NGX_MAX_CONF_ERRSTR];
2316
2317     value = cf->args->elts;
2318
2319     flcf->split_name = value[1];
2320
2321     err.len = NGX_MAX_CONF_ERRSTR;
2322     err.data = errstr;
2323
2324     flcf->split_regex = ngx_regex_compile(&value[1], 0, cf->pool, &err);
2325
2326     if (flcf->split_regex == NULL) {
2327         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data);
2328         return NGX_CONF_ERROR;
2329     }
2330
2331     n = ngx_regex_capture_count(flcf->split_regex);
2332
2333     if (n < 0) {
2334         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2335                            ngx_regex_capture_count_n " failed for "
2336                            "pattern \"%V\"", &value[1]);
2337         return NGX_CONF_ERROR;
2338     }
2339
2340     if (n != 2) {
2341         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2342                            "pattern \"%V\" must have 2 captures", &value[1]);
2343         return NGX_CONF_ERROR;
2344     }
2345
2346     return NGX_CONF_OK;
2347
2348 #else
2349
2350     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2351                        "\"%V\" requires PCRE library", &cmd->name);
2352     return NGX_CONF_ERROR;
2353
2354 #endif
2355 }
2356
2357
2358 static char *
2359 ngx_http_fastcgi_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
2360 {
2361     ngx_http_fastcgi_loc_conf_t *flcf = conf;
2362
2363     ngx_str_t                  *value;
2364     ngx_http_script_compile_t   sc;
2365
2366     if (flcf->upstream.store != NGX_CONF_UNSET || flcf->upstream.store_lengths)
2367     {
2368         return "is duplicate";
2369     }
2370
2371     value = cf->args->elts;
2372
2373     if (ngx_strcmp(value[1].data, "on") == 0) {
2374         flcf->upstream.store = 1;
2375         return NGX_CONF_OK;
2376     }
2377
2378     if (ngx_strcmp(value[1].data, "off") == 0) {
2379         flcf->upstream.store = 0;
2380         return NGX_CONF_OK;
2381     }
2382
2383     /* include the terminating '\0' into script */
2384     value[1].len++;
2385
2386     ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
2387
2388     sc.cf = cf;
2389     sc.source = &value[1];
2390     sc.lengths = &flcf->upstream.store_lengths;
2391     sc.values = &flcf->upstream.store_values;
2392     sc.variables = ngx_http_script_variables_count(&value[1]);
2393     sc.complete_lengths = 1;
2394     sc.complete_values = 1;
2395
2396     if (ngx_http_script_compile(&sc) != NGX_OK) {
2397         return NGX_CONF_ERROR;
2398     }
2399
2400     return NGX_CONF_OK;
2401 }
2402
2403
2404 static char *
2405 ngx_http_fastcgi_lowat_check(ngx_conf_t *cf, void *post, void *data)
2406 {
2407 #if (NGX_FREEBSD)
2408     ssize_t *np = data;
2409
2410     if ((u_long) *np >= ngx_freebsd_net_inet_tcp_sendspace) {
2411         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2412                            "\"fastcgi_send_lowat\" must be less than %d "
2413                            "(sysctl net.inet.tcp.sendspace)",
2414                            ngx_freebsd_net_inet_tcp_sendspace);
2415
2416         return NGX_CONF_ERROR;
2417     }
2418
2419 #elif !(NGX_HAVE_SO_SNDLOWAT)
2420     ssize_t *np = data;
2421
2422     ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
2423                        "\"fastcgi_send_lowat\" is not supported, ignored");
2424
2425     *np = 0;
2426
2427 #endif
2428
2429     return NGX_CONF_OK;
2430 }
2431
2432
2433 static char *
2434 ngx_http_fastcgi_upstream_max_fails_unsupported(ngx_conf_t *cf,
2435     ngx_command_t *cmd, void *conf)
2436 {
2437     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2438          "\"fastcgi_upstream_max_fails\" is not supported, "
2439          "use the \"max_fails\" parameter of the \"server\" directive ",
2440          "inside the \"upstream\" block");
2441
2442     return NGX_CONF_ERROR;
2443 }
2444
2445
2446 static char *
2447 ngx_http_fastcgi_upstream_fail_timeout_unsupported(ngx_conf_t *cf,
2448     ngx_command_t *cmd, void *conf)
2449 {
2450     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2451          "\"fastcgi_upstream_fail_timeout\" is not supported, "
2452          "use the \"fail_timeout\" parameter of the \"server\" directive ",
2453          "inside the \"upstream\" block");
2454
2455     return NGX_CONF_ERROR;
2456 }