source: nginx/src/http/modules/ngx_http_fastcgi_module.c

tip
Last change on this file was 5847:52b4984d2b3c, checked in by Roman Arutyunyan <arut@…>, 3 days ago

FastCGI: fixed start pointers in request buffers.

The start pointers are used in ngx_http_upstream_reinit() to
reinit FastCGI requests.

File size: 94.4 KB
Line 
1
2/*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
5 */
6
7
8#include <ngx_config.h>
9#include <ngx_core.h>
10#include <ngx_http.h>
11
12
13typedef struct {
14    ngx_http_upstream_conf_t       upstream;
15
16    ngx_str_t                      index;
17
18    ngx_array_t                   *flushes;
19    ngx_array_t                   *params_len;
20    ngx_array_t                   *params;
21    ngx_array_t                   *params_source;
22    ngx_array_t                   *catch_stderr;
23
24    ngx_array_t                   *fastcgi_lengths;
25    ngx_array_t                   *fastcgi_values;
26
27    ngx_hash_t                     headers_hash;
28    ngx_uint_t                     header_params;
29
30    ngx_flag_t                     keep_conn;
31
32#if (NGX_HTTP_CACHE)
33    ngx_http_complex_value_t       cache_key;
34#endif
35
36#if (NGX_PCRE)
37    ngx_regex_t                   *split_regex;
38    ngx_str_t                      split_name;
39#endif
40} ngx_http_fastcgi_loc_conf_t;
41
42
43typedef enum {
44    ngx_http_fastcgi_st_version = 0,
45    ngx_http_fastcgi_st_type,
46    ngx_http_fastcgi_st_request_id_hi,
47    ngx_http_fastcgi_st_request_id_lo,
48    ngx_http_fastcgi_st_content_length_hi,
49    ngx_http_fastcgi_st_content_length_lo,
50    ngx_http_fastcgi_st_padding_length,
51    ngx_http_fastcgi_st_reserved,
52    ngx_http_fastcgi_st_data,
53    ngx_http_fastcgi_st_padding
54} ngx_http_fastcgi_state_e;
55
56
57typedef struct {
58    u_char                        *start;
59    u_char                        *end;
60} ngx_http_fastcgi_split_part_t;
61
62
63typedef struct {
64    ngx_http_fastcgi_state_e       state;
65    u_char                        *pos;
66    u_char                        *last;
67    ngx_uint_t                     type;
68    size_t                         length;
69    size_t                         padding;
70
71    unsigned                       fastcgi_stdout:1;
72    unsigned                       large_stderr:1;
73
74    ngx_array_t                   *split_parts;
75
76    ngx_str_t                      script_name;
77    ngx_str_t                      path_info;
78} ngx_http_fastcgi_ctx_t;
79
80
81#define NGX_HTTP_FASTCGI_RESPONDER      1
82
83#define NGX_HTTP_FASTCGI_KEEP_CONN      1
84
85#define NGX_HTTP_FASTCGI_BEGIN_REQUEST  1
86#define NGX_HTTP_FASTCGI_ABORT_REQUEST  2
87#define NGX_HTTP_FASTCGI_END_REQUEST    3
88#define NGX_HTTP_FASTCGI_PARAMS         4
89#define NGX_HTTP_FASTCGI_STDIN          5
90#define NGX_HTTP_FASTCGI_STDOUT         6
91#define NGX_HTTP_FASTCGI_STDERR         7
92#define NGX_HTTP_FASTCGI_DATA           8
93
94
95typedef struct {
96    u_char  version;
97    u_char  type;
98    u_char  request_id_hi;
99    u_char  request_id_lo;
100    u_char  content_length_hi;
101    u_char  content_length_lo;
102    u_char  padding_length;
103    u_char  reserved;
104} ngx_http_fastcgi_header_t;
105
106
107typedef struct {
108    u_char  role_hi;
109    u_char  role_lo;
110    u_char  flags;
111    u_char  reserved[5];
112} ngx_http_fastcgi_begin_request_t;
113
114
115typedef struct {
116    u_char  version;
117    u_char  type;
118    u_char  request_id_hi;
119    u_char  request_id_lo;
120} ngx_http_fastcgi_header_small_t;
121
122
123typedef struct {
124    ngx_http_fastcgi_header_t         h0;
125    ngx_http_fastcgi_begin_request_t  br;
126    ngx_http_fastcgi_header_small_t   h1;
127} ngx_http_fastcgi_request_start_t;
128
129
130static ngx_int_t ngx_http_fastcgi_eval(ngx_http_request_t *r,
131    ngx_http_fastcgi_loc_conf_t *flcf);
132#if (NGX_HTTP_CACHE)
133static ngx_int_t ngx_http_fastcgi_create_key(ngx_http_request_t *r);
134#endif
135static ngx_int_t ngx_http_fastcgi_create_request(ngx_http_request_t *r);
136static ngx_int_t ngx_http_fastcgi_reinit_request(ngx_http_request_t *r);
137static ngx_int_t ngx_http_fastcgi_process_header(ngx_http_request_t *r);
138static ngx_int_t ngx_http_fastcgi_input_filter_init(void *data);
139static ngx_int_t ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p,
140    ngx_buf_t *buf);
141static ngx_int_t ngx_http_fastcgi_non_buffered_filter(void *data,
142    ssize_t bytes);
143static ngx_int_t ngx_http_fastcgi_process_record(ngx_http_request_t *r,
144    ngx_http_fastcgi_ctx_t *f);
145static void ngx_http_fastcgi_abort_request(ngx_http_request_t *r);
146static void ngx_http_fastcgi_finalize_request(ngx_http_request_t *r,
147    ngx_int_t rc);
148
149static ngx_int_t ngx_http_fastcgi_add_variables(ngx_conf_t *cf);
150static void *ngx_http_fastcgi_create_loc_conf(ngx_conf_t *cf);
151static char *ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf,
152    void *parent, void *child);
153static ngx_int_t ngx_http_fastcgi_merge_params(ngx_conf_t *cf,
154    ngx_http_fastcgi_loc_conf_t *conf, ngx_http_fastcgi_loc_conf_t *prev);
155
156static ngx_int_t ngx_http_fastcgi_script_name_variable(ngx_http_request_t *r,
157    ngx_http_variable_value_t *v, uintptr_t data);
158static ngx_int_t ngx_http_fastcgi_path_info_variable(ngx_http_request_t *r,
159    ngx_http_variable_value_t *v, uintptr_t data);
160static ngx_http_fastcgi_ctx_t *ngx_http_fastcgi_split(ngx_http_request_t *r,
161    ngx_http_fastcgi_loc_conf_t *flcf);
162
163static char *ngx_http_fastcgi_pass(ngx_conf_t *cf, ngx_command_t *cmd,
164    void *conf);
165static char *ngx_http_fastcgi_split_path_info(ngx_conf_t *cf,
166    ngx_command_t *cmd, void *conf);
167static char *ngx_http_fastcgi_store(ngx_conf_t *cf, ngx_command_t *cmd,
168    void *conf);
169#if (NGX_HTTP_CACHE)
170static char *ngx_http_fastcgi_cache(ngx_conf_t *cf, ngx_command_t *cmd,
171    void *conf);
172static char *ngx_http_fastcgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd,
173    void *conf);
174#endif
175
176static char *ngx_http_fastcgi_lowat_check(ngx_conf_t *cf, void *post,
177    void *data);
178
179
180static ngx_conf_post_t  ngx_http_fastcgi_lowat_post =
181    { ngx_http_fastcgi_lowat_check };
182
183
184static ngx_conf_bitmask_t  ngx_http_fastcgi_next_upstream_masks[] = {
185    { ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR },
186    { ngx_string("timeout"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },
187    { ngx_string("invalid_header"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },
188    { ngx_string("http_500"), NGX_HTTP_UPSTREAM_FT_HTTP_500 },
189    { ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 },
190    { ngx_string("http_403"), NGX_HTTP_UPSTREAM_FT_HTTP_403 },
191    { ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },
192    { ngx_string("updating"), NGX_HTTP_UPSTREAM_FT_UPDATING },
193    { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF },
194    { ngx_null_string, 0 }
195};
196
197
198ngx_module_t  ngx_http_fastcgi_module;
199
200
201static ngx_command_t  ngx_http_fastcgi_commands[] = {
202
203    { ngx_string("fastcgi_pass"),
204      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
205      ngx_http_fastcgi_pass,
206      NGX_HTTP_LOC_CONF_OFFSET,
207      0,
208      NULL },
209
210    { ngx_string("fastcgi_index"),
211      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
212      ngx_conf_set_str_slot,
213      NGX_HTTP_LOC_CONF_OFFSET,
214      offsetof(ngx_http_fastcgi_loc_conf_t, index),
215      NULL },
216
217    { ngx_string("fastcgi_split_path_info"),
218      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
219      ngx_http_fastcgi_split_path_info,
220      NGX_HTTP_LOC_CONF_OFFSET,
221      0,
222      NULL },
223
224    { ngx_string("fastcgi_store"),
225      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
226      ngx_http_fastcgi_store,
227      NGX_HTTP_LOC_CONF_OFFSET,
228      0,
229      NULL },
230
231    { ngx_string("fastcgi_store_access"),
232      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
233      ngx_conf_set_access_slot,
234      NGX_HTTP_LOC_CONF_OFFSET,
235      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.store_access),
236      NULL },
237
238    { ngx_string("fastcgi_buffering"),
239      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
240      ngx_conf_set_flag_slot,
241      NGX_HTTP_LOC_CONF_OFFSET,
242      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.buffering),
243      NULL },
244
245    { ngx_string("fastcgi_ignore_client_abort"),
246      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
247      ngx_conf_set_flag_slot,
248      NGX_HTTP_LOC_CONF_OFFSET,
249      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.ignore_client_abort),
250      NULL },
251
252    { ngx_string("fastcgi_bind"),
253      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
254      ngx_http_upstream_bind_set_slot,
255      NGX_HTTP_LOC_CONF_OFFSET,
256      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.local),
257      NULL },
258
259    { ngx_string("fastcgi_connect_timeout"),
260      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
261      ngx_conf_set_msec_slot,
262      NGX_HTTP_LOC_CONF_OFFSET,
263      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.connect_timeout),
264      NULL },
265
266    { ngx_string("fastcgi_send_timeout"),
267      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
268      ngx_conf_set_msec_slot,
269      NGX_HTTP_LOC_CONF_OFFSET,
270      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.send_timeout),
271      NULL },
272
273    { ngx_string("fastcgi_send_lowat"),
274      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
275      ngx_conf_set_size_slot,
276      NGX_HTTP_LOC_CONF_OFFSET,
277      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.send_lowat),
278      &ngx_http_fastcgi_lowat_post },
279
280    { ngx_string("fastcgi_buffer_size"),
281      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
282      ngx_conf_set_size_slot,
283      NGX_HTTP_LOC_CONF_OFFSET,
284      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.buffer_size),
285      NULL },
286
287    { ngx_string("fastcgi_pass_request_headers"),
288      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
289      ngx_conf_set_flag_slot,
290      NGX_HTTP_LOC_CONF_OFFSET,
291      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.pass_request_headers),
292      NULL },
293
294    { ngx_string("fastcgi_pass_request_body"),
295      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
296      ngx_conf_set_flag_slot,
297      NGX_HTTP_LOC_CONF_OFFSET,
298      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.pass_request_body),
299      NULL },
300
301    { ngx_string("fastcgi_intercept_errors"),
302      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
303      ngx_conf_set_flag_slot,
304      NGX_HTTP_LOC_CONF_OFFSET,
305      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.intercept_errors),
306      NULL },
307
308    { ngx_string("fastcgi_read_timeout"),
309      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
310      ngx_conf_set_msec_slot,
311      NGX_HTTP_LOC_CONF_OFFSET,
312      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.read_timeout),
313      NULL },
314
315    { ngx_string("fastcgi_buffers"),
316      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
317      ngx_conf_set_bufs_slot,
318      NGX_HTTP_LOC_CONF_OFFSET,
319      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.bufs),
320      NULL },
321
322    { ngx_string("fastcgi_busy_buffers_size"),
323      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
324      ngx_conf_set_size_slot,
325      NGX_HTTP_LOC_CONF_OFFSET,
326      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.busy_buffers_size_conf),
327      NULL },
328
329#if (NGX_HTTP_CACHE)
330
331    { ngx_string("fastcgi_cache"),
332      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
333      ngx_http_fastcgi_cache,
334      NGX_HTTP_LOC_CONF_OFFSET,
335      0,
336      NULL },
337
338    { ngx_string("fastcgi_cache_key"),
339      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
340      ngx_http_fastcgi_cache_key,
341      NGX_HTTP_LOC_CONF_OFFSET,
342      0,
343      NULL },
344
345    { ngx_string("fastcgi_cache_path"),
346      NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE,
347      ngx_http_file_cache_set_slot,
348      0,
349      0,
350      &ngx_http_fastcgi_module },
351
352    { ngx_string("fastcgi_cache_bypass"),
353      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
354      ngx_http_set_predicate_slot,
355      NGX_HTTP_LOC_CONF_OFFSET,
356      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_bypass),
357      NULL },
358
359    { ngx_string("fastcgi_no_cache"),
360      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
361      ngx_http_set_predicate_slot,
362      NGX_HTTP_LOC_CONF_OFFSET,
363      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.no_cache),
364      NULL },
365
366    { ngx_string("fastcgi_cache_valid"),
367      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
368      ngx_http_file_cache_valid_set_slot,
369      NGX_HTTP_LOC_CONF_OFFSET,
370      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_valid),
371      NULL },
372
373    { ngx_string("fastcgi_cache_min_uses"),
374      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
375      ngx_conf_set_num_slot,
376      NGX_HTTP_LOC_CONF_OFFSET,
377      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_min_uses),
378      NULL },
379
380    { ngx_string("fastcgi_cache_use_stale"),
381      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
382      ngx_conf_set_bitmask_slot,
383      NGX_HTTP_LOC_CONF_OFFSET,
384      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_use_stale),
385      &ngx_http_fastcgi_next_upstream_masks },
386
387    { ngx_string("fastcgi_cache_methods"),
388      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
389      ngx_conf_set_bitmask_slot,
390      NGX_HTTP_LOC_CONF_OFFSET,
391      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_methods),
392      &ngx_http_upstream_cache_method_mask },
393
394    { ngx_string("fastcgi_cache_lock"),
395      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
396      ngx_conf_set_flag_slot,
397      NGX_HTTP_LOC_CONF_OFFSET,
398      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_lock),
399      NULL },
400
401    { ngx_string("fastcgi_cache_lock_timeout"),
402      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
403      ngx_conf_set_msec_slot,
404      NGX_HTTP_LOC_CONF_OFFSET,
405      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_lock_timeout),
406      NULL },
407
408    { ngx_string("fastcgi_cache_revalidate"),
409      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
410      ngx_conf_set_flag_slot,
411      NGX_HTTP_LOC_CONF_OFFSET,
412      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_revalidate),
413      NULL },
414
415#endif
416
417    { ngx_string("fastcgi_temp_path"),
418      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
419      ngx_conf_set_path_slot,
420      NGX_HTTP_LOC_CONF_OFFSET,
421      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.temp_path),
422      NULL },
423
424    { ngx_string("fastcgi_max_temp_file_size"),
425      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
426      ngx_conf_set_size_slot,
427      NGX_HTTP_LOC_CONF_OFFSET,
428      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.max_temp_file_size_conf),
429      NULL },
430
431    { ngx_string("fastcgi_temp_file_write_size"),
432      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
433      ngx_conf_set_size_slot,
434      NGX_HTTP_LOC_CONF_OFFSET,
435      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.temp_file_write_size_conf),
436      NULL },
437
438    { ngx_string("fastcgi_next_upstream"),
439      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
440      ngx_conf_set_bitmask_slot,
441      NGX_HTTP_LOC_CONF_OFFSET,
442      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.next_upstream),
443      &ngx_http_fastcgi_next_upstream_masks },
444
445    { ngx_string("fastcgi_next_upstream_tries"),
446      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
447      ngx_conf_set_num_slot,
448      NGX_HTTP_LOC_CONF_OFFSET,
449      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.next_upstream_tries),
450      NULL },
451
452    { ngx_string("fastcgi_next_upstream_timeout"),
453      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
454      ngx_conf_set_msec_slot,
455      NGX_HTTP_LOC_CONF_OFFSET,
456      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.next_upstream_timeout),
457      NULL },
458
459    { ngx_string("fastcgi_param"),
460      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE23,
461      ngx_http_upstream_param_set_slot,
462      NGX_HTTP_LOC_CONF_OFFSET,
463      offsetof(ngx_http_fastcgi_loc_conf_t, params_source),
464      NULL },
465
466    { ngx_string("fastcgi_pass_header"),
467      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
468      ngx_conf_set_str_array_slot,
469      NGX_HTTP_LOC_CONF_OFFSET,
470      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.pass_headers),
471      NULL },
472
473    { ngx_string("fastcgi_hide_header"),
474      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
475      ngx_conf_set_str_array_slot,
476      NGX_HTTP_LOC_CONF_OFFSET,
477      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.hide_headers),
478      NULL },
479
480    { ngx_string("fastcgi_ignore_headers"),
481      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
482      ngx_conf_set_bitmask_slot,
483      NGX_HTTP_LOC_CONF_OFFSET,
484      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.ignore_headers),
485      &ngx_http_upstream_ignore_headers_masks },
486
487    { ngx_string("fastcgi_catch_stderr"),
488      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
489      ngx_conf_set_str_array_slot,
490      NGX_HTTP_LOC_CONF_OFFSET,
491      offsetof(ngx_http_fastcgi_loc_conf_t, catch_stderr),
492      NULL },
493
494    { ngx_string("fastcgi_keep_conn"),
495      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
496      ngx_conf_set_flag_slot,
497      NGX_HTTP_LOC_CONF_OFFSET,
498      offsetof(ngx_http_fastcgi_loc_conf_t, keep_conn),
499      NULL },
500
501      ngx_null_command
502};
503
504
505static ngx_http_module_t  ngx_http_fastcgi_module_ctx = {
506    ngx_http_fastcgi_add_variables,        /* preconfiguration */
507    NULL,                                  /* postconfiguration */
508
509    NULL,                                  /* create main configuration */
510    NULL,                                  /* init main configuration */
511
512    NULL,                                  /* create server configuration */
513    NULL,                                  /* merge server configuration */
514
515    ngx_http_fastcgi_create_loc_conf,      /* create location configuration */
516    ngx_http_fastcgi_merge_loc_conf        /* merge location configuration */
517};
518
519
520ngx_module_t  ngx_http_fastcgi_module = {
521    NGX_MODULE_V1,
522    &ngx_http_fastcgi_module_ctx,          /* module context */
523    ngx_http_fastcgi_commands,             /* module directives */
524    NGX_HTTP_MODULE,                       /* module type */
525    NULL,                                  /* init master */
526    NULL,                                  /* init module */
527    NULL,                                  /* init process */
528    NULL,                                  /* init thread */
529    NULL,                                  /* exit thread */
530    NULL,                                  /* exit process */
531    NULL,                                  /* exit master */
532    NGX_MODULE_V1_PADDING
533};
534
535
536static ngx_http_fastcgi_request_start_t  ngx_http_fastcgi_request_start = {
537    { 1,                                               /* version */
538      NGX_HTTP_FASTCGI_BEGIN_REQUEST,                  /* type */
539      0,                                               /* request_id_hi */
540      1,                                               /* request_id_lo */
541      0,                                               /* content_length_hi */
542      sizeof(ngx_http_fastcgi_begin_request_t),        /* content_length_lo */
543      0,                                               /* padding_length */
544      0 },                                             /* reserved */
545
546    { 0,                                               /* role_hi */
547      NGX_HTTP_FASTCGI_RESPONDER,                      /* role_lo */
548      0, /* NGX_HTTP_FASTCGI_KEEP_CONN */              /* flags */
549      { 0, 0, 0, 0, 0 } },                             /* reserved[5] */
550
551    { 1,                                               /* version */
552      NGX_HTTP_FASTCGI_PARAMS,                         /* type */
553      0,                                               /* request_id_hi */
554      1 },                                             /* request_id_lo */
555
556};
557
558
559static ngx_http_variable_t  ngx_http_fastcgi_vars[] = {
560
561    { ngx_string("fastcgi_script_name"), NULL,
562      ngx_http_fastcgi_script_name_variable, 0,
563      NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
564
565    { ngx_string("fastcgi_path_info"), NULL,
566      ngx_http_fastcgi_path_info_variable, 0,
567      NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
568
569    { ngx_null_string, NULL, NULL, 0, 0, 0 }
570};
571
572
573static ngx_str_t  ngx_http_fastcgi_hide_headers[] = {
574    ngx_string("Status"),
575    ngx_string("X-Accel-Expires"),
576    ngx_string("X-Accel-Redirect"),
577    ngx_string("X-Accel-Limit-Rate"),
578    ngx_string("X-Accel-Buffering"),
579    ngx_string("X-Accel-Charset"),
580    ngx_null_string
581};
582
583
584#if (NGX_HTTP_CACHE)
585
586static ngx_keyval_t  ngx_http_fastcgi_cache_headers[] = {
587    { ngx_string("HTTP_IF_MODIFIED_SINCE"),
588      ngx_string("$upstream_cache_last_modified") },
589    { ngx_string("HTTP_IF_UNMODIFIED_SINCE"), ngx_string("") },
590    { ngx_string("HTTP_IF_NONE_MATCH"), ngx_string("$upstream_cache_etag") },
591    { ngx_string("HTTP_IF_MATCH"), ngx_string("") },
592    { ngx_string("HTTP_RANGE"), ngx_string("") },
593    { ngx_string("HTTP_IF_RANGE"), ngx_string("") },
594    { ngx_null_string, ngx_null_string }
595};
596
597#endif
598
599
600static ngx_path_init_t  ngx_http_fastcgi_temp_path = {
601    ngx_string(NGX_HTTP_FASTCGI_TEMP_PATH), { 1, 2, 0 }
602};
603
604
605static ngx_int_t
606ngx_http_fastcgi_handler(ngx_http_request_t *r)
607{
608    ngx_int_t                     rc;
609    ngx_http_upstream_t          *u;
610    ngx_http_fastcgi_ctx_t       *f;
611    ngx_http_fastcgi_loc_conf_t  *flcf;
612
613    if (ngx_http_upstream_create(r) != NGX_OK) {
614        return NGX_HTTP_INTERNAL_SERVER_ERROR;
615    }
616
617    f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t));
618    if (f == NULL) {
619        return NGX_HTTP_INTERNAL_SERVER_ERROR;
620    }
621
622    ngx_http_set_ctx(r, f, ngx_http_fastcgi_module);
623
624    flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
625
626    if (flcf->fastcgi_lengths) {
627        if (ngx_http_fastcgi_eval(r, flcf) != NGX_OK) {
628            return NGX_HTTP_INTERNAL_SERVER_ERROR;
629        }
630    }
631
632    u = r->upstream;
633
634    ngx_str_set(&u->schema, "fastcgi://");
635    u->output.tag = (ngx_buf_tag_t) &ngx_http_fastcgi_module;
636
637    u->conf = &flcf->upstream;
638
639#if (NGX_HTTP_CACHE)
640    u->create_key = ngx_http_fastcgi_create_key;
641#endif
642    u->create_request = ngx_http_fastcgi_create_request;
643    u->reinit_request = ngx_http_fastcgi_reinit_request;
644    u->process_header = ngx_http_fastcgi_process_header;
645    u->abort_request = ngx_http_fastcgi_abort_request;
646    u->finalize_request = ngx_http_fastcgi_finalize_request;
647    r->state = 0;
648
649    u->buffering = flcf->upstream.buffering;
650
651    u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));
652    if (u->pipe == NULL) {
653        return NGX_HTTP_INTERNAL_SERVER_ERROR;
654    }
655
656    u->pipe->input_filter = ngx_http_fastcgi_input_filter;
657    u->pipe->input_ctx = r;
658
659    u->input_filter_init = ngx_http_fastcgi_input_filter_init;
660    u->input_filter = ngx_http_fastcgi_non_buffered_filter;
661    u->input_filter_ctx = r;
662
663    rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
664
665    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
666        return rc;
667    }
668
669    return NGX_DONE;
670}
671
672
673static ngx_int_t
674ngx_http_fastcgi_eval(ngx_http_request_t *r, ngx_http_fastcgi_loc_conf_t *flcf)
675{
676    ngx_url_t             url;
677    ngx_http_upstream_t  *u;
678
679    ngx_memzero(&url, sizeof(ngx_url_t));
680
681    if (ngx_http_script_run(r, &url.url, flcf->fastcgi_lengths->elts, 0,
682                            flcf->fastcgi_values->elts)
683        == NULL)
684    {
685        return NGX_ERROR;
686    }
687
688    url.no_resolve = 1;
689
690    if (ngx_parse_url(r->pool, &url) != NGX_OK) {
691         if (url.err) {
692            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
693                          "%s in upstream \"%V\"", url.err, &url.url);
694        }
695
696        return NGX_ERROR;
697    }
698
699    u = r->upstream;
700
701    u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t));
702    if (u->resolved == NULL) {
703        return NGX_ERROR;
704    }
705
706    if (url.addrs && url.addrs[0].sockaddr) {
707        u->resolved->sockaddr = url.addrs[0].sockaddr;
708        u->resolved->socklen = url.addrs[0].socklen;
709        u->resolved->naddrs = 1;
710        u->resolved->host = url.addrs[0].name;
711
712    } else {
713        u->resolved->host = url.host;
714        u->resolved->port = url.port;
715        u->resolved->no_port = url.no_port;
716    }
717
718    return NGX_OK;
719}
720
721
722#if (NGX_HTTP_CACHE)
723
724static ngx_int_t
725ngx_http_fastcgi_create_key(ngx_http_request_t *r)
726{
727    ngx_str_t                    *key;
728    ngx_http_fastcgi_loc_conf_t  *flcf;
729
730    key = ngx_array_push(&r->cache->keys);
731    if (key == NULL) {
732        return NGX_ERROR;
733    }
734
735    flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
736
737    if (ngx_http_complex_value(r, &flcf->cache_key, key) != NGX_OK) {
738        return NGX_ERROR;
739    }
740
741    return NGX_OK;
742}
743
744#endif
745
746
747static ngx_int_t
748ngx_http_fastcgi_create_request(ngx_http_request_t *r)
749{
750    off_t                         file_pos;
751    u_char                        ch, *pos, *lowcase_key;
752    size_t                        size, len, key_len, val_len, padding,
753                                  allocated;
754    ngx_uint_t                    i, n, next, hash, skip_empty, header_params;
755    ngx_buf_t                    *b;
756    ngx_chain_t                  *cl, *body;
757    ngx_list_part_t              *part;
758    ngx_table_elt_t              *header, **ignored;
759    ngx_http_script_code_pt       code;
760    ngx_http_script_engine_t      e, le;
761    ngx_http_fastcgi_header_t    *h;
762    ngx_http_fastcgi_loc_conf_t  *flcf;
763    ngx_http_script_len_code_pt   lcode;
764
765    len = 0;
766    header_params = 0;
767    ignored = NULL;
768
769    flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
770
771    if (flcf->params_len) {
772        ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
773
774        ngx_http_script_flush_no_cacheable_variables(r, flcf->flushes);
775        le.flushed = 1;
776
777        le.ip = flcf->params_len->elts;
778        le.request = r;
779
780        while (*(uintptr_t *) le.ip) {
781
782            lcode = *(ngx_http_script_len_code_pt *) le.ip;
783            key_len = lcode(&le);
784
785            lcode = *(ngx_http_script_len_code_pt *) le.ip;
786            skip_empty = lcode(&le);
787
788            for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
789                lcode = *(ngx_http_script_len_code_pt *) le.ip;
790            }
791            le.ip += sizeof(uintptr_t);
792
793            if (skip_empty && val_len == 0) {
794                continue;
795            }
796
797            len += 1 + key_len + ((val_len > 127) ? 4 : 1) + val_len;
798        }
799    }
800
801    if (flcf->upstream.pass_request_headers) {
802
803        allocated = 0;
804        lowcase_key = NULL;
805
806        if (flcf->header_params) {
807            n = 0;
808            part = &r->headers_in.headers.part;
809
810            while (part) {
811                n += part->nelts;
812                part = part->next;
813            }
814
815            ignored = ngx_palloc(r->pool, n * sizeof(void *));
816            if (ignored == NULL) {
817                return NGX_ERROR;
818            }
819        }
820
821        part = &r->headers_in.headers.part;
822        header = part->elts;
823
824        for (i = 0; /* void */; i++) {
825
826            if (i >= part->nelts) {
827                if (part->next == NULL) {
828                    break;
829                }
830
831                part = part->next;
832                header = part->elts;
833                i = 0;
834            }
835
836            if (flcf->header_params) {
837                if (allocated < header[i].key.len) {
838                    allocated = header[i].key.len + 16;
839                    lowcase_key = ngx_pnalloc(r->pool, allocated);
840                    if (lowcase_key == NULL) {
841                        return NGX_ERROR;
842                    }
843                }
844
845                hash = 0;
846
847                for (n = 0; n < header[i].key.len; n++) {
848                    ch = header[i].key.data[n];
849
850                    if (ch >= 'A' && ch <= 'Z') {
851                        ch |= 0x20;
852
853                    } else if (ch == '-') {
854                        ch = '_';
855                    }
856
857                    hash = ngx_hash(hash, ch);
858                    lowcase_key[n] = ch;
859                }
860
861                if (ngx_hash_find(&flcf->headers_hash, hash, lowcase_key, n)) {
862                    ignored[header_params++] = &header[i];
863                    continue;
864                }
865
866                n += sizeof("HTTP_") - 1;
867
868            } else {
869                n = sizeof("HTTP_") - 1 + header[i].key.len;
870            }
871
872            len += ((n > 127) ? 4 : 1) + ((header[i].value.len > 127) ? 4 : 1)
873                + n + header[i].value.len;
874        }
875    }
876
877
878    if (len > 65535) {
879        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
880                      "fastcgi request record is too big: %uz", len);
881        return NGX_ERROR;
882    }
883
884
885    padding = 8 - len % 8;
886    padding = (padding == 8) ? 0 : padding;
887
888
889    size = sizeof(ngx_http_fastcgi_header_t)
890           + sizeof(ngx_http_fastcgi_begin_request_t)
891
892           + sizeof(ngx_http_fastcgi_header_t)  /* NGX_HTTP_FASTCGI_PARAMS */
893           + len + padding
894           + sizeof(ngx_http_fastcgi_header_t)  /* NGX_HTTP_FASTCGI_PARAMS */
895
896           + sizeof(ngx_http_fastcgi_header_t); /* NGX_HTTP_FASTCGI_STDIN */
897
898
899    b = ngx_create_temp_buf(r->pool, size);
900    if (b == NULL) {
901        return NGX_ERROR;
902    }
903
904    cl = ngx_alloc_chain_link(r->pool);
905    if (cl == NULL) {
906        return NGX_ERROR;
907    }
908
909    cl->buf = b;
910
911    ngx_http_fastcgi_request_start.br.flags =
912        flcf->keep_conn ? NGX_HTTP_FASTCGI_KEEP_CONN : 0;
913
914    ngx_memcpy(b->pos, &ngx_http_fastcgi_request_start,
915               sizeof(ngx_http_fastcgi_request_start_t));
916
917    h = (ngx_http_fastcgi_header_t *)
918             (b->pos + sizeof(ngx_http_fastcgi_header_t)
919                     + sizeof(ngx_http_fastcgi_begin_request_t));
920
921    h->content_length_hi = (u_char) ((len >> 8) & 0xff);
922    h->content_length_lo = (u_char) (len & 0xff);
923    h->padding_length = (u_char) padding;
924    h->reserved = 0;
925
926    b->last = b->pos + sizeof(ngx_http_fastcgi_header_t)
927                     + sizeof(ngx_http_fastcgi_begin_request_t)
928                     + sizeof(ngx_http_fastcgi_header_t);
929
930
931    if (flcf->params_len) {
932        ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
933
934        e.ip = flcf->params->elts;
935        e.pos = b->last;
936        e.request = r;
937        e.flushed = 1;
938
939        le.ip = flcf->params_len->elts;
940
941        while (*(uintptr_t *) le.ip) {
942
943            lcode = *(ngx_http_script_len_code_pt *) le.ip;
944            key_len = (u_char) lcode(&le);
945
946            lcode = *(ngx_http_script_len_code_pt *) le.ip;
947            skip_empty = lcode(&le);
948
949            for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
950                lcode = *(ngx_http_script_len_code_pt *) le.ip;
951            }
952            le.ip += sizeof(uintptr_t);
953
954            if (skip_empty && val_len == 0) {
955                e.skip = 1;
956
957                while (*(uintptr_t *) e.ip) {
958                    code = *(ngx_http_script_code_pt *) e.ip;
959                    code((ngx_http_script_engine_t *) &e);
960                }
961                e.ip += sizeof(uintptr_t);
962
963                e.skip = 0;
964
965                continue;
966            }
967
968            *e.pos++ = (u_char) key_len;
969
970            if (val_len > 127) {
971                *e.pos++ = (u_char) (((val_len >> 24) & 0x7f) | 0x80);
972                *e.pos++ = (u_char) ((val_len >> 16) & 0xff);
973                *e.pos++ = (u_char) ((val_len >> 8) & 0xff);
974                *e.pos++ = (u_char) (val_len & 0xff);
975
976            } else {
977                *e.pos++ = (u_char) val_len;
978            }
979
980            while (*(uintptr_t *) e.ip) {
981                code = *(ngx_http_script_code_pt *) e.ip;
982                code((ngx_http_script_engine_t *) &e);
983            }
984            e.ip += sizeof(uintptr_t);
985
986            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
987                           "fastcgi param: \"%*s: %*s\"",
988                           key_len, e.pos - (key_len + val_len),
989                           val_len, e.pos - val_len);
990        }
991
992        b->last = e.pos;
993    }
994
995
996    if (flcf->upstream.pass_request_headers) {
997
998        part = &r->headers_in.headers.part;
999        header = part->elts;
1000
1001        for (i = 0; /* void */; i++) {
1002
1003            if (i >= part->nelts) {
1004                if (part->next == NULL) {
1005                    break;
1006                }
1007
1008                part = part->next;
1009                header = part->elts;
1010                i = 0;
1011            }
1012
1013            for (n = 0; n < header_params; n++) {
1014                if (&header[i] == ignored[n]) {
1015                    goto next;
1016                }
1017            }
1018
1019            key_len = sizeof("HTTP_") - 1 + header[i].key.len;
1020            if (key_len > 127) {
1021                *b->last++ = (u_char) (((key_len >> 24) & 0x7f) | 0x80);
1022                *b->last++ = (u_char) ((key_len >> 16) & 0xff);
1023                *b->last++ = (u_char) ((key_len >> 8) & 0xff);
1024                *b->last++ = (u_char) (key_len & 0xff);
1025
1026            } else {
1027                *b->last++ = (u_char) key_len;
1028            }
1029
1030            val_len = header[i].value.len;
1031            if (val_len > 127) {
1032                *b->last++ = (u_char) (((val_len >> 24) & 0x7f) | 0x80);
1033                *b->last++ = (u_char) ((val_len >> 16) & 0xff);
1034                *b->last++ = (u_char) ((val_len >> 8) & 0xff);
1035                *b->last++ = (u_char) (val_len & 0xff);
1036
1037            } else {
1038                *b->last++ = (u_char) val_len;
1039            }
1040
1041            b->last = ngx_cpymem(b->last, "HTTP_", sizeof("HTTP_") - 1);
1042
1043            for (n = 0; n < header[i].key.len; n++) {
1044                ch = header[i].key.data[n];
1045
1046                if (ch >= 'a' && ch <= 'z') {
1047                    ch &= ~0x20;
1048
1049                } else if (ch == '-') {
1050                    ch = '_';
1051                }
1052
1053                *b->last++ = ch;
1054            }
1055
1056            b->last = ngx_copy(b->last, header[i].value.data, val_len);
1057
1058            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1059                           "fastcgi param: \"%*s: %*s\"",
1060                           key_len, b->last - (key_len + val_len),
1061                           val_len, b->last - val_len);
1062        next:
1063
1064            continue;
1065        }
1066    }
1067
1068
1069    if (padding) {
1070        ngx_memzero(b->last, padding);
1071        b->last += padding;
1072    }
1073
1074
1075    h = (ngx_http_fastcgi_header_t *) b->last;
1076    b->last += sizeof(ngx_http_fastcgi_header_t);
1077
1078    h->version = 1;
1079    h->type = NGX_HTTP_FASTCGI_PARAMS;
1080    h->request_id_hi = 0;
1081    h->request_id_lo = 1;
1082    h->content_length_hi = 0;
1083    h->content_length_lo = 0;
1084    h->padding_length = 0;
1085    h->reserved = 0;
1086
1087    h = (ngx_http_fastcgi_header_t *) b->last;
1088    b->last += sizeof(ngx_http_fastcgi_header_t);
1089
1090    if (flcf->upstream.pass_request_body) {
1091        body = r->upstream->request_bufs;
1092        r->upstream->request_bufs = cl;
1093
1094#if (NGX_SUPPRESS_WARN)
1095        file_pos = 0;
1096        pos = NULL;
1097#endif
1098
1099        while (body) {
1100
1101            if (body->buf->in_file) {
1102                file_pos = body->buf->file_pos;
1103
1104            } else {
1105                pos = body->buf->pos;
1106            }
1107
1108            next = 0;
1109
1110            do {
1111                b = ngx_alloc_buf(r->pool);
1112                if (b == NULL) {
1113                    return NGX_ERROR;
1114                }
1115
1116                ngx_memcpy(b, body->buf, sizeof(ngx_buf_t));
1117
1118                if (body->buf->in_file) {
1119                    b->file_pos = file_pos;
1120                    file_pos += 32 * 1024;
1121
1122                    if (file_pos >= body->buf->file_last) {
1123                        file_pos = body->buf->file_last;
1124                        next = 1;
1125                    }
1126
1127                    b->file_last = file_pos;
1128                    len = (ngx_uint_t) (file_pos - b->file_pos);
1129
1130                } else {
1131                    b->pos = pos;
1132                    b->start = pos;
1133                    pos += 32 * 1024;
1134
1135                    if (pos >= body->buf->last) {
1136                        pos = body->buf->last;
1137                        next = 1;
1138                    }
1139
1140                    b->last = pos;
1141                    len = (ngx_uint_t) (pos - b->pos);
1142                }
1143
1144                padding = 8 - len % 8;
1145                padding = (padding == 8) ? 0 : padding;
1146
1147                h->version = 1;
1148                h->type = NGX_HTTP_FASTCGI_STDIN;
1149                h->request_id_hi = 0;
1150                h->request_id_lo = 1;
1151                h->content_length_hi = (u_char) ((len >> 8) & 0xff);
1152                h->content_length_lo = (u_char) (len & 0xff);
1153                h->padding_length = (u_char) padding;
1154                h->reserved = 0;
1155
1156                cl->next = ngx_alloc_chain_link(r->pool);
1157                if (cl->next == NULL) {
1158                    return NGX_ERROR;
1159                }
1160
1161                cl = cl->next;
1162                cl->buf = b;
1163
1164                b = ngx_create_temp_buf(r->pool,
1165                                        sizeof(ngx_http_fastcgi_header_t)
1166                                        + padding);
1167                if (b == NULL) {
1168                    return NGX_ERROR;
1169                }
1170
1171                if (padding) {
1172                    ngx_memzero(b->last, padding);
1173                    b->last += padding;
1174                }
1175
1176                h = (ngx_http_fastcgi_header_t *) b->last;
1177                b->last += sizeof(ngx_http_fastcgi_header_t);
1178
1179                cl->next = ngx_alloc_chain_link(r->pool);
1180                if (cl->next == NULL) {
1181                    return NGX_ERROR;
1182                }
1183
1184                cl = cl->next;
1185                cl->buf = b;
1186
1187            } while (!next);
1188
1189            body = body->next;
1190        }
1191
1192    } else {
1193        r->upstream->request_bufs = cl;
1194    }
1195
1196    h->version = 1;
1197    h->type = NGX_HTTP_FASTCGI_STDIN;
1198    h->request_id_hi = 0;
1199    h->request_id_lo = 1;
1200    h->content_length_hi = 0;
1201    h->content_length_lo = 0;
1202    h->padding_length = 0;
1203    h->reserved = 0;
1204
1205    cl->next = NULL;
1206
1207    return NGX_OK;
1208}
1209
1210
1211static ngx_int_t
1212ngx_http_fastcgi_reinit_request(ngx_http_request_t *r)
1213{
1214    ngx_http_fastcgi_ctx_t  *f;
1215
1216    f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
1217
1218    if (f == NULL) {
1219        return NGX_OK;
1220    }
1221
1222    f->state = ngx_http_fastcgi_st_version;
1223    f->fastcgi_stdout = 0;
1224    f->large_stderr = 0;
1225
1226    if (f->split_parts) {
1227        f->split_parts->nelts = 0;
1228    }
1229
1230    r->state = 0;
1231
1232    return NGX_OK;
1233}
1234
1235
1236static ngx_int_t
1237ngx_http_fastcgi_process_header(ngx_http_request_t *r)
1238{
1239    u_char                         *p, *msg, *start, *last,
1240                                   *part_start, *part_end;
1241    size_t                          size;
1242    ngx_str_t                      *status_line, *pattern;
1243    ngx_int_t                       rc, status;
1244    ngx_buf_t                       buf;
1245    ngx_uint_t                      i;
1246    ngx_table_elt_t                *h;
1247    ngx_http_upstream_t            *u;
1248    ngx_http_fastcgi_ctx_t         *f;
1249    ngx_http_upstream_header_t     *hh;
1250    ngx_http_fastcgi_loc_conf_t    *flcf;
1251    ngx_http_fastcgi_split_part_t  *part;
1252    ngx_http_upstream_main_conf_t  *umcf;
1253
1254    f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
1255
1256    umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
1257
1258    u = r->upstream;
1259
1260    for ( ;; ) {
1261
1262        if (f->state < ngx_http_fastcgi_st_data) {
1263
1264            f->pos = u->buffer.pos;
1265            f->last = u->buffer.last;
1266
1267            rc = ngx_http_fastcgi_process_record(r, f);
1268
1269            u->buffer.pos = f->pos;
1270            u->buffer.last = f->last;
1271
1272            if (rc == NGX_AGAIN) {
1273                return NGX_AGAIN;
1274            }
1275
1276            if (rc == NGX_ERROR) {
1277                return NGX_HTTP_UPSTREAM_INVALID_HEADER;
1278            }
1279
1280            if (f->type != NGX_HTTP_FASTCGI_STDOUT
1281                && f->type != NGX_HTTP_FASTCGI_STDERR)
1282            {
1283                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1284                              "upstream sent unexpected FastCGI record: %d",
1285                              f->type);
1286
1287                return NGX_HTTP_UPSTREAM_INVALID_HEADER;
1288            }
1289
1290            if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) {
1291                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1292                              "upstream prematurely closed FastCGI stdout");
1293
1294                return NGX_HTTP_UPSTREAM_INVALID_HEADER;
1295            }
1296        }
1297
1298        if (f->state == ngx_http_fastcgi_st_padding) {
1299
1300            if (u->buffer.pos + f->padding < u->buffer.last) {
1301                f->state = ngx_http_fastcgi_st_version;
1302                u->buffer.pos += f->padding;
1303
1304                continue;
1305            }
1306
1307            if (u->buffer.pos + f->padding == u->buffer.last) {
1308                f->state = ngx_http_fastcgi_st_version;
1309                u->buffer.pos = u->buffer.last;
1310
1311                return NGX_AGAIN;
1312            }
1313
1314            f->padding -= u->buffer.last - u->buffer.pos;
1315            u->buffer.pos = u->buffer.last;
1316
1317            return NGX_AGAIN;
1318        }
1319
1320
1321        /* f->state == ngx_http_fastcgi_st_data */
1322
1323        if (f->type == NGX_HTTP_FASTCGI_STDERR) {
1324
1325            if (f->length) {
1326                msg = u->buffer.pos;
1327
1328                if (u->buffer.pos + f->length <= u->buffer.last) {
1329                    u->buffer.pos += f->length;
1330                    f->length = 0;
1331                    f->state = ngx_http_fastcgi_st_padding;
1332
1333                } else {
1334                    f->length -= u->buffer.last - u->buffer.pos;
1335                    u->buffer.pos = u->buffer.last;
1336                }
1337
1338                for (p = u->buffer.pos - 1; msg < p; p--) {
1339                    if (*p != LF && *p != CR && *p != '.' && *p != ' ') {
1340                        break;
1341                    }
1342                }
1343
1344                p++;
1345
1346                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1347                              "FastCGI sent in stderr: \"%*s\"", p - msg, msg);
1348
1349                flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
1350
1351                if (flcf->catch_stderr) {
1352                    pattern = flcf->catch_stderr->elts;
1353
1354                    for (i = 0; i < flcf->catch_stderr->nelts; i++) {
1355                        if (ngx_strnstr(msg, (char *) pattern[i].data,
1356                                        p - msg)
1357                            != NULL)
1358                        {
1359                            return NGX_HTTP_UPSTREAM_INVALID_HEADER;
1360                        }
1361                    }
1362                }
1363
1364                if (u->buffer.pos == u->buffer.last) {
1365
1366                    if (!f->fastcgi_stdout) {
1367
1368                        /*
1369                         * the special handling the large number
1370                         * of the PHP warnings to not allocate memory
1371                         */
1372
1373#if (NGX_HTTP_CACHE)
1374                        if (r->cache) {
1375                            u->buffer.pos = u->buffer.start
1376                                                     + r->cache->header_start;
1377                        } else {
1378                            u->buffer.pos = u->buffer.start;
1379                        }
1380#else
1381                        u->buffer.pos = u->buffer.start;
1382#endif
1383                        u->buffer.last = u->buffer.pos;
1384                        f->large_stderr = 1;
1385                    }
1386
1387                    return NGX_AGAIN;
1388                }
1389
1390            } else {
1391                f->state = ngx_http_fastcgi_st_padding;
1392            }
1393
1394            continue;
1395        }
1396
1397
1398        /* f->type == NGX_HTTP_FASTCGI_STDOUT */
1399
1400#if (NGX_HTTP_CACHE)
1401
1402        if (f->large_stderr && r->cache) {
1403            u_char                     *start;
1404            ssize_t                     len;
1405            ngx_http_fastcgi_header_t  *fh;
1406
1407            start = u->buffer.start + r->cache->header_start;
1408
1409            len = u->buffer.pos - start - 2 * sizeof(ngx_http_fastcgi_header_t);
1410
1411            /*
1412             * A tail of large stderr output before HTTP header is placed
1413             * in a cache file without a FastCGI record header.
1414             * To workaround it we put a dummy FastCGI record header at the
1415             * start of the stderr output or update r->cache_header_start,
1416             * if there is no enough place for the record header.
1417             */
1418
1419            if (len >= 0) {
1420                fh = (ngx_http_fastcgi_header_t *) start;
1421                fh->version = 1;
1422                fh->type = NGX_HTTP_FASTCGI_STDERR;
1423                fh->request_id_hi = 0;
1424                fh->request_id_lo = 1;
1425                fh->content_length_hi = (u_char) ((len >> 8) & 0xff);
1426                fh->content_length_lo = (u_char) (len & 0xff);
1427                fh->padding_length = 0;
1428                fh->reserved = 0;
1429
1430            } else {
1431                r->cache->header_start += u->buffer.pos - start
1432                                           - sizeof(ngx_http_fastcgi_header_t);
1433            }
1434
1435            f->large_stderr = 0;
1436        }
1437
1438#endif
1439
1440        f->fastcgi_stdout = 1;
1441
1442        start = u->buffer.pos;
1443
1444        if (u->buffer.pos + f->length < u->buffer.last) {
1445
1446            /*
1447             * set u->buffer.last to the end of the FastCGI record data
1448             * for ngx_http_parse_header_line()
1449             */
1450
1451            last = u->buffer.last;
1452            u->buffer.last = u->buffer.pos + f->length;
1453
1454        } else {
1455            last = NULL;
1456        }
1457
1458        for ( ;; ) {
1459
1460            part_start = u->buffer.pos;
1461            part_end = u->buffer.last;
1462
1463            rc = ngx_http_parse_header_line(r, &u->buffer, 1);
1464
1465            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1466                           "http fastcgi parser: %d", rc);
1467
1468            if (rc == NGX_AGAIN) {
1469                break;
1470            }
1471
1472            if (rc == NGX_OK) {
1473
1474                /* a header line has been parsed successfully */
1475
1476                h = ngx_list_push(&u->headers_in.headers);
1477                if (h == NULL) {
1478                    return NGX_ERROR;
1479                }
1480
1481                if (f->split_parts && f->split_parts->nelts) {
1482
1483                    part = f->split_parts->elts;
1484                    size = u->buffer.pos - part_start;
1485
1486                    for (i = 0; i < f->split_parts->nelts; i++) {
1487                        size += part[i].end - part[i].start;
1488                    }
1489
1490                    p = ngx_pnalloc(r->pool, size);
1491                    if (p == NULL) {
1492                        return NGX_ERROR;
1493                    }
1494
1495                    buf.pos = p;
1496
1497                    for (i = 0; i < f->split_parts->nelts; i++) {
1498                        p = ngx_cpymem(p, part[i].start,
1499                                       part[i].end - part[i].start);
1500                    }
1501
1502                    p = ngx_cpymem(p, part_start, u->buffer.pos - part_start);
1503
1504                    buf.last = p;
1505
1506                    f->split_parts->nelts = 0;
1507
1508                    rc = ngx_http_parse_header_line(r, &buf, 1);
1509
1510                    if (rc != NGX_OK) {
1511                        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
1512                                      "invalid header after joining "
1513                                      "FastCGI records");
1514                        return NGX_ERROR;
1515                    }
1516
1517                    h->key.len = r->header_name_end - r->header_name_start;
1518                    h->key.data = r->header_name_start;
1519                    h->key.data[h->key.len] = '\0';
1520
1521                    h->value.len = r->header_end - r->header_start;
1522                    h->value.data = r->header_start;
1523                    h->value.data[h->value.len] = '\0';
1524
1525                    h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);
1526                    if (h->lowcase_key == NULL) {
1527                        return NGX_ERROR;
1528                    }
1529
1530                } else {
1531
1532                    h->key.len = r->header_name_end - r->header_name_start;
1533                    h->value.len = r->header_end - r->header_start;
1534
1535                    h->key.data = ngx_pnalloc(r->pool,
1536                                              h->key.len + 1 + h->value.len + 1
1537                                              + h->key.len);
1538                    if (h->key.data == NULL) {
1539                        return NGX_ERROR;
1540                    }
1541
1542                    h->value.data = h->key.data + h->key.len + 1;
1543                    h->lowcase_key = h->key.data + h->key.len + 1
1544                                     + h->value.len + 1;
1545
1546                    ngx_memcpy(h->key.data, r->header_name_start, h->key.len);
1547                    h->key.data[h->key.len] = '\0';
1548                    ngx_memcpy(h->value.data, r->header_start, h->value.len);
1549                    h->value.data[h->value.len] = '\0';
1550                }
1551
1552                h->hash = r->header_hash;
1553
1554                if (h->key.len == r->lowcase_index) {
1555                    ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
1556
1557                } else {
1558                    ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
1559                }
1560
1561                hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
1562                                   h->lowcase_key, h->key.len);
1563
1564                if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
1565                    return NGX_ERROR;
1566                }
1567
1568                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1569                               "http fastcgi header: \"%V: %V\"",
1570                               &h->key, &h->value);
1571
1572                if (u->buffer.pos < u->buffer.last) {
1573                    continue;
1574                }
1575
1576                /* the end of the FastCGI record */
1577
1578                break;
1579            }
1580
1581            if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
1582
1583                /* a whole header has been parsed successfully */
1584
1585                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1586                               "http fastcgi header done");
1587
1588                if (u->headers_in.status) {
1589                    status_line = &u->headers_in.status->value;
1590
1591                    status = ngx_atoi(status_line->data, 3);
1592
1593                    if (status == NGX_ERROR) {
1594                        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1595                                      "upstream sent invalid status \"%V\"",
1596                                      status_line);
1597                        return NGX_HTTP_UPSTREAM_INVALID_HEADER;
1598                    }
1599
1600                    u->headers_in.status_n = status;
1601                    u->headers_in.status_line = *status_line;
1602
1603                } else if (u->headers_in.location) {
1604                    u->headers_in.status_n = 302;
1605                    ngx_str_set(&u->headers_in.status_line,
1606                                "302 Moved Temporarily");
1607
1608                } else {
1609                    u->headers_in.status_n = 200;
1610                    ngx_str_set(&u->headers_in.status_line, "200 OK");
1611                }
1612
1613                if (u->state && u->state->status == 0) {
1614                    u->state->status = u->headers_in.status_n;
1615                }
1616
1617                break;
1618            }
1619
1620            /* there was error while a header line parsing */
1621
1622            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1623                          "upstream sent invalid header");
1624
1625            return NGX_HTTP_UPSTREAM_INVALID_HEADER;
1626        }
1627
1628        if (last) {
1629            u->buffer.last = last;
1630        }
1631
1632        f->length -= u->buffer.pos - start;
1633
1634        if (f->length == 0) {
1635            f->state = ngx_http_fastcgi_st_padding;
1636        }
1637
1638        if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
1639            return NGX_OK;
1640        }
1641
1642        if (rc == NGX_OK) {
1643            continue;
1644        }
1645
1646        /* rc == NGX_AGAIN */
1647
1648        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1649                       "upstream split a header line in FastCGI records");
1650
1651        if (f->split_parts == NULL) {
1652            f->split_parts = ngx_array_create(r->pool, 1,
1653                                        sizeof(ngx_http_fastcgi_split_part_t));
1654            if (f->split_parts == NULL) {
1655                return NGX_ERROR;
1656            }
1657        }
1658
1659        part = ngx_array_push(f->split_parts);
1660        if (part == NULL) {
1661            return NGX_ERROR;
1662        }
1663
1664        part->start = part_start;
1665        part->end = part_end;
1666
1667        if (u->buffer.pos < u->buffer.last) {
1668            continue;
1669        }
1670
1671        return NGX_AGAIN;
1672    }
1673}
1674
1675
1676static ngx_int_t
1677ngx_http_fastcgi_input_filter_init(void *data)
1678{
1679    ngx_http_request_t           *r = data;
1680    ngx_http_fastcgi_loc_conf_t  *flcf;
1681
1682    flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
1683
1684    r->upstream->pipe->length = flcf->keep_conn ?
1685                                (off_t) sizeof(ngx_http_fastcgi_header_t) : -1;
1686
1687    return NGX_OK;
1688}
1689
1690
1691static ngx_int_t
1692ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
1693{
1694    u_char                       *m, *msg;
1695    ngx_int_t                     rc;
1696    ngx_buf_t                    *b, **prev;
1697    ngx_chain_t                  *cl;
1698    ngx_http_request_t           *r;
1699    ngx_http_fastcgi_ctx_t       *f;
1700    ngx_http_fastcgi_loc_conf_t  *flcf;
1701
1702    if (buf->pos == buf->last) {
1703        return NGX_OK;
1704    }
1705
1706    r = p->input_ctx;
1707    f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
1708    flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
1709
1710    b = NULL;
1711    prev = &buf->shadow;
1712
1713    f->pos = buf->pos;
1714    f->last = buf->last;
1715
1716    for ( ;; ) {
1717        if (f->state < ngx_http_fastcgi_st_data) {
1718
1719            rc = ngx_http_fastcgi_process_record(r, f);
1720
1721            if (rc == NGX_AGAIN) {
1722                break;
1723            }
1724
1725            if (rc == NGX_ERROR) {
1726                return NGX_ERROR;
1727            }
1728
1729            if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) {
1730                f->state = ngx_http_fastcgi_st_padding;
1731
1732                if (!flcf->keep_conn) {
1733                    p->upstream_done = 1;
1734                }
1735
1736                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,
1737                               "http fastcgi closed stdout");
1738
1739                continue;
1740            }
1741
1742            if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {
1743
1744                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,
1745                               "http fastcgi sent end request");
1746
1747                if (!flcf->keep_conn) {
1748                    p->upstream_done = 1;
1749                    break;
1750                }
1751
1752                continue;
1753            }
1754        }
1755
1756
1757        if (f->state == ngx_http_fastcgi_st_padding) {
1758
1759            if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {
1760
1761                if (f->pos + f->padding < f->last) {
1762                    p->upstream_done = 1;
1763                    break;
1764                }
1765
1766                if (f->pos + f->padding == f->last) {
1767                    p->upstream_done = 1;
1768                    r->upstream->keepalive = 1;
1769                    break;
1770                }
1771
1772                f->padding -= f->last - f->pos;
1773
1774                break;
1775            }
1776
1777            if (f->pos + f->padding < f->last) {
1778                f->state = ngx_http_fastcgi_st_version;
1779                f->pos += f->padding;
1780
1781                continue;
1782            }
1783
1784            if (f->pos + f->padding == f->last) {
1785                f->state = ngx_http_fastcgi_st_version;
1786
1787                break;
1788            }
1789
1790            f->padding -= f->last - f->pos;
1791
1792            break;
1793        }
1794
1795
1796        /* f->state == ngx_http_fastcgi_st_data */
1797
1798        if (f->type == NGX_HTTP_FASTCGI_STDERR) {
1799
1800            if (f->length) {
1801
1802                if (f->pos == f->last) {
1803                    break;
1804                }
1805
1806                msg = f->pos;
1807
1808                if (f->pos + f->length <= f->last) {
1809                    f->pos += f->length;
1810                    f->length = 0;
1811                    f->state = ngx_http_fastcgi_st_padding;
1812
1813                } else {
1814                    f->length -= f->last - f->pos;
1815                    f->pos = f->last;
1816                }
1817
1818                for (m = f->pos - 1; msg < m; m--) {
1819                    if (*m != LF && *m != CR && *m != '.' && *m != ' ') {
1820                        break;
1821                    }
1822                }
1823
1824                ngx_log_error(NGX_LOG_ERR, p->log, 0,
1825                              "FastCGI sent in stderr: \"%*s\"",
1826                              m + 1 - msg, msg);
1827
1828            } else {
1829                f->state = ngx_http_fastcgi_st_padding;
1830            }
1831
1832            continue;
1833        }
1834
1835        if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {
1836
1837            if (f->pos + f->length <= f->last) {
1838                f->state = ngx_http_fastcgi_st_padding;
1839                f->pos += f->length;
1840
1841                continue;
1842            }
1843
1844            f->length -= f->last - f->pos;
1845
1846            break;
1847        }
1848
1849
1850        /* f->type == NGX_HTTP_FASTCGI_STDOUT */
1851
1852        if (f->pos == f->last) {
1853            break;
1854        }
1855
1856        cl = ngx_chain_get_free_buf(p->pool, &p->free);
1857        if (cl == NULL) {
1858            return NGX_ERROR;
1859        }
1860
1861        b = cl->buf;
1862
1863        ngx_memzero(b, sizeof(ngx_buf_t));
1864
1865        b->pos = f->pos;
1866        b->start = buf->start;
1867        b->end = buf->end;
1868        b->tag = p->tag;
1869        b->temporary = 1;
1870        b->recycled = 1;
1871
1872        *prev = b;
1873        prev = &b->shadow;
1874
1875        if (p->in) {
1876            *p->last_in = cl;
1877        } else {
1878            p->in = cl;
1879        }
1880        p->last_in = &cl->next;
1881
1882
1883        /* STUB */ b->num = buf->num;
1884
1885        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,
1886                       "input buf #%d %p", b->num, b->pos);
1887
1888        if (f->pos + f->length <= f->last) {
1889            f->state = ngx_http_fastcgi_st_padding;
1890            f->pos += f->length;
1891            b->last = f->pos;
1892
1893            continue;
1894        }
1895
1896        f->length -= f->last - f->pos;
1897
1898        b->last = f->last;
1899
1900        break;
1901
1902    }
1903
1904    if (flcf->keep_conn) {
1905
1906        /* set p->length, minimal amount of data we want to see */
1907
1908        if (f->state < ngx_http_fastcgi_st_data) {
1909            p->length = 1;
1910
1911        } else if (f->state == ngx_http_fastcgi_st_padding) {
1912            p->length = f->padding;
1913
1914        } else {
1915            /* ngx_http_fastcgi_st_data */
1916
1917            p->length = f->length;
1918        }
1919    }
1920
1921    if (b) {
1922        b->shadow = buf;
1923        b->last_shadow = 1;
1924
1925        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,
1926                       "input buf %p %z", b->pos, b->last - b->pos);
1927
1928        return NGX_OK;
1929    }
1930
1931    /* there is no data record in the buf, add it to free chain */
1932
1933    if (ngx_event_pipe_add_free_buf(p, buf) != NGX_OK) {
1934        return NGX_ERROR;
1935    }
1936
1937    return NGX_OK;
1938}
1939
1940
1941static ngx_int_t
1942ngx_http_fastcgi_non_buffered_filter(void *data, ssize_t bytes)
1943{
1944    u_char                  *m, *msg;
1945    ngx_int_t                rc;
1946    ngx_buf_t               *b, *buf;
1947    ngx_chain_t             *cl, **ll;
1948    ngx_http_request_t      *r;
1949    ngx_http_upstream_t     *u;
1950    ngx_http_fastcgi_ctx_t  *f;
1951
1952    r = data;
1953    f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
1954
1955    u = r->upstream;
1956    buf = &u->buffer;
1957
1958    buf->pos = buf->last;
1959    buf->last += bytes;
1960
1961    for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {
1962        ll = &cl->next;
1963    }
1964
1965    f->pos = buf->pos;
1966    f->last = buf->last;
1967
1968    for ( ;; ) {
1969        if (f->state < ngx_http_fastcgi_st_data) {
1970
1971            rc = ngx_http_fastcgi_process_record(r, f);
1972
1973            if (rc == NGX_AGAIN) {
1974                break;
1975            }
1976
1977            if (rc == NGX_ERROR) {
1978                return NGX_ERROR;
1979            }
1980
1981            if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) {
1982                f->state = ngx_http_fastcgi_st_padding;
1983
1984                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1985                               "http fastcgi closed stdout");
1986
1987                continue;
1988            }
1989        }
1990
1991        if (f->state == ngx_http_fastcgi_st_padding) {
1992
1993            if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {
1994
1995                if (f->pos + f->padding < f->last) {
1996                    u->length = 0;
1997                    break;
1998                }
1999
2000                if (f->pos + f->padding == f->last) {
2001                    u->length = 0;
2002                    u->keepalive = 1;
2003                    break;
2004                }
2005
2006                f->padding -= f->last - f->pos;
2007
2008                break;
2009            }
2010
2011            if (f->pos + f->padding < f->last) {
2012                f->state = ngx_http_fastcgi_st_version;
2013                f->pos += f->padding;
2014
2015                continue;
2016            }
2017
2018            if (f->pos + f->padding == f->last) {
2019                f->state = ngx_http_fastcgi_st_version;
2020
2021                break;
2022            }
2023
2024            f->padding -= f->last - f->pos;
2025
2026            break;
2027        }
2028
2029
2030        /* f->state == ngx_http_fastcgi_st_data */
2031
2032        if (f->type == NGX_HTTP_FASTCGI_STDERR) {
2033
2034            if (f->length) {
2035
2036                if (f->pos == f->last) {
2037                    break;
2038                }
2039
2040                msg = f->pos;
2041
2042                if (f->pos + f->length <= f->last) {
2043                    f->pos += f->length;
2044                    f->length = 0;
2045                    f->state = ngx_http_fastcgi_st_padding;
2046
2047                } else {
2048                    f->length -= f->last - f->pos;
2049                    f->pos = f->last;
2050                }
2051
2052                for (m = f->pos - 1; msg < m; m--) {
2053                    if (*m != LF && *m != CR && *m != '.' && *m != ' ') {
2054                        break;
2055                    }
2056                }
2057
2058                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
2059                              "FastCGI sent in stderr: \"%*s\"",
2060                              m + 1 - msg, msg);
2061
2062            } else {
2063                f->state = ngx_http_fastcgi_st_padding;
2064            }
2065
2066            continue;
2067        }
2068
2069        if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {
2070
2071            if (f->pos + f->length <= f->last) {
2072                f->state = ngx_http_fastcgi_st_padding;
2073                f->pos += f->length;
2074
2075                continue;
2076            }
2077
2078            f->length -= f->last - f->pos;
2079
2080            break;
2081        }
2082
2083
2084        /* f->type == NGX_HTTP_FASTCGI_STDOUT */
2085
2086        if (f->pos == f->last) {
2087            break;
2088        }
2089
2090        cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs);
2091        if (cl == NULL) {
2092            return NGX_ERROR;
2093        }
2094
2095        *ll = cl;
2096        ll = &cl->next;
2097
2098        b = cl->buf;
2099
2100        b->flush = 1;
2101        b->memory = 1;
2102
2103        b->pos = f->pos;
2104        b->tag = u->output.tag;
2105
2106        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2107                       "http fastcgi output buf %p", b->pos);
2108
2109        if (f->pos + f->length <= f->last) {
2110            f->state = ngx_http_fastcgi_st_padding;
2111            f->pos += f->length;
2112            b->last = f->pos;
2113
2114            continue;
2115        }
2116
2117        f->length -= f->last - f->pos;
2118        b->last = f->last;
2119
2120        break;
2121    }
2122
2123    /* provide continuous buffer for subrequests in memory */
2124
2125    if (r->subrequest_in_memory) {
2126
2127        cl = u->out_bufs;
2128
2129        if (cl) {
2130            buf->pos = cl->buf->pos;
2131        }
2132
2133        buf->last = buf->pos;
2134
2135        for (cl = u->out_bufs; cl; cl = cl->next) {
2136            ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2137                           "http fastcgi in memory %p-%p %uz",
2138                           cl->buf->pos, cl->buf->last, ngx_buf_size(cl->buf));
2139
2140            if (buf->last == cl->buf->pos) {
2141                buf->last = cl->buf->last;
2142                continue;
2143            }
2144
2145            buf->last = ngx_movemem(buf->last, cl->buf->pos,
2146                                    cl->buf->last - cl->buf->pos);
2147
2148            cl->buf->pos = buf->last - (cl->buf->last - cl->buf->pos);
2149            cl->buf->last = buf->last;
2150        }
2151    }
2152
2153    return NGX_OK;
2154}
2155
2156
2157static ngx_int_t
2158ngx_http_fastcgi_process_record(ngx_http_request_t *r,
2159    ngx_http_fastcgi_ctx_t *f)
2160{
2161    u_char                     ch, *p;
2162    ngx_http_fastcgi_state_e   state;
2163
2164    state = f->state;
2165
2166    for (p = f->pos; p < f->last; p++) {
2167
2168        ch = *p;
2169
2170        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2171                       "http fastcgi record byte: %02Xd", ch);
2172
2173        switch (state) {
2174
2175        case ngx_http_fastcgi_st_version:
2176            if (ch != 1) {
2177                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
2178                              "upstream sent unsupported FastCGI "
2179                              "protocol version: %d", ch);
2180                return NGX_ERROR;
2181            }
2182            state = ngx_http_fastcgi_st_type;
2183            break;
2184
2185        case ngx_http_fastcgi_st_type:
2186            switch (ch) {
2187            case NGX_HTTP_FASTCGI_STDOUT:
2188            case NGX_HTTP_FASTCGI_STDERR:
2189            case NGX_HTTP_FASTCGI_END_REQUEST:
2190                 f->type = (ngx_uint_t) ch;
2191                 break;
2192            default:
2193                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
2194                              "upstream sent invalid FastCGI "
2195                              "record type: %d", ch);
2196                return NGX_ERROR;
2197
2198            }
2199            state = ngx_http_fastcgi_st_request_id_hi;
2200            break;
2201
2202        /* we support the single request per connection */
2203
2204        case ngx_http_fastcgi_st_request_id_hi:
2205            if (ch != 0) {
2206                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
2207                              "upstream sent unexpected FastCGI "
2208                              "request id high byte: %d", ch);
2209                return NGX_ERROR;
2210            }
2211            state = ngx_http_fastcgi_st_request_id_lo;
2212            break;
2213
2214        case ngx_http_fastcgi_st_request_id_lo:
2215            if (ch != 1) {
2216                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
2217                              "upstream sent unexpected FastCGI "
2218                              "request id low byte: %d", ch);
2219                return NGX_ERROR;
2220            }
2221            state = ngx_http_fastcgi_st_content_length_hi;
2222            break;
2223
2224        case ngx_http_fastcgi_st_content_length_hi:
2225            f->length = ch << 8;
2226            state = ngx_http_fastcgi_st_content_length_lo;
2227            break;
2228
2229        case ngx_http_fastcgi_st_content_length_lo:
2230            f->length |= (size_t) ch;
2231            state = ngx_http_fastcgi_st_padding_length;
2232            break;
2233
2234        case ngx_http_fastcgi_st_padding_length:
2235            f->padding = (size_t) ch;
2236            state = ngx_http_fastcgi_st_reserved;
2237            break;
2238
2239        case ngx_http_fastcgi_st_reserved:
2240            state = ngx_http_fastcgi_st_data;
2241
2242            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2243                           "http fastcgi record length: %z", f->length);
2244
2245            f->pos = p + 1;
2246            f->state = state;
2247
2248            return NGX_OK;
2249
2250        /* suppress warning */
2251        case ngx_http_fastcgi_st_data:
2252        case ngx_http_fastcgi_st_padding:
2253            break;
2254        }
2255    }
2256
2257    f->state = state;
2258
2259    return NGX_AGAIN;
2260}
2261
2262
2263static void
2264ngx_http_fastcgi_abort_request(ngx_http_request_t *r)
2265{
2266    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2267                   "abort http fastcgi request");
2268
2269    return;
2270}
2271
2272
2273static void
2274ngx_http_fastcgi_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
2275{
2276    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2277                   "finalize http fastcgi request");
2278
2279    return;
2280}
2281
2282
2283static ngx_int_t
2284ngx_http_fastcgi_add_variables(ngx_conf_t *cf)
2285{
2286   ngx_http_variable_t  *var, *v;
2287
2288    for (v = ngx_http_fastcgi_vars; v->name.len; v++) {
2289        var = ngx_http_add_variable(cf, &v->name, v->flags);
2290        if (var == NULL) {
2291            return NGX_ERROR;
2292        }
2293
2294        var->get_handler = v->get_handler;
2295        var->data = v->data;
2296    }
2297
2298    return NGX_OK;
2299}
2300
2301
2302static void *
2303ngx_http_fastcgi_create_loc_conf(ngx_conf_t *cf)
2304{
2305    ngx_http_fastcgi_loc_conf_t  *conf;
2306
2307    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_fastcgi_loc_conf_t));
2308    if (conf == NULL) {
2309        return NULL;
2310    }
2311
2312    /*
2313     * set by ngx_pcalloc():
2314     *
2315     *     conf->upstream.bufs.num = 0;
2316     *     conf->upstream.ignore_headers = 0;
2317     *     conf->upstream.next_upstream = 0;
2318     *     conf->upstream.cache_use_stale = 0;
2319     *     conf->upstream.cache_methods = 0;
2320     *     conf->upstream.temp_path = NULL;
2321     *     conf->upstream.hide_headers_hash = { NULL, 0 };
2322     *     conf->upstream.uri = { 0, NULL };
2323     *     conf->upstream.location = NULL;
2324     *     conf->upstream.store_lengths = NULL;
2325     *     conf->upstream.store_values = NULL;
2326     *
2327     *     conf->index.len = { 0, NULL };
2328     */
2329
2330    conf->upstream.store = NGX_CONF_UNSET;
2331    conf->upstream.store_access = NGX_CONF_UNSET_UINT;
2332    conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT;
2333    conf->upstream.buffering = NGX_CONF_UNSET;
2334    conf->upstream.ignore_client_abort = NGX_CONF_UNSET;
2335
2336    conf->upstream.local = NGX_CONF_UNSET_PTR;
2337
2338    conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
2339    conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
2340    conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;
2341    conf->upstream.next_upstream_timeout = NGX_CONF_UNSET_MSEC;
2342
2343    conf->upstream.send_lowat = NGX_CONF_UNSET_SIZE;
2344    conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;
2345
2346    conf->upstream.busy_buffers_size_conf = NGX_CONF_UNSET_SIZE;
2347    conf->upstream.max_temp_file_size_conf = NGX_CONF_UNSET_SIZE;
2348    conf->upstream.temp_file_write_size_conf = NGX_CONF_UNSET_SIZE;
2349
2350    conf->upstream.pass_request_headers = NGX_CONF_UNSET;
2351    conf->upstream.pass_request_body = NGX_CONF_UNSET;
2352
2353#if (NGX_HTTP_CACHE)
2354    conf->upstream.cache = NGX_CONF_UNSET_PTR;
2355    conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT;
2356    conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR;
2357    conf->upstream.no_cache = NGX_CONF_UNSET_PTR;
2358    conf->upstream.cache_valid = NGX_CONF_UNSET_PTR;
2359    conf->upstream.cache_lock = NGX_CONF_UNSET;
2360    conf->upstream.cache_lock_timeout = NGX_CONF_UNSET_MSEC;
2361    conf->upstream.cache_revalidate = NGX_CONF_UNSET;
2362#endif
2363
2364    conf->upstream.hide_headers = NGX_CONF_UNSET_PTR;
2365    conf->upstream.pass_headers = NGX_CONF_UNSET_PTR;
2366
2367    conf->upstream.intercept_errors = NGX_CONF_UNSET;
2368
2369    /* "fastcgi_cyclic_temp_file" is disabled */
2370    conf->upstream.cyclic_temp_file = 0;
2371
2372    conf->upstream.change_buffering = 1;
2373
2374    conf->catch_stderr = NGX_CONF_UNSET_PTR;
2375
2376    conf->keep_conn = NGX_CONF_UNSET;
2377
2378    ngx_str_set(&conf->upstream.module, "fastcgi");
2379
2380    return conf;
2381}
2382
2383
2384static char *
2385ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
2386{
2387    ngx_http_fastcgi_loc_conf_t *prev = parent;
2388    ngx_http_fastcgi_loc_conf_t *conf = child;
2389
2390    size_t                        size;
2391    ngx_hash_init_t               hash;
2392    ngx_http_core_loc_conf_t     *clcf;
2393
2394    if (conf->upstream.store != 0) {
2395        ngx_conf_merge_value(conf->upstream.store,
2396                              prev->upstream.store, 0);
2397
2398        if (conf->upstream.store_lengths == NULL) {
2399            conf->upstream.store_lengths = prev->upstream.store_lengths;
2400            conf->upstream.store_values = prev->upstream.store_values;
2401        }
2402    }
2403
2404    ngx_conf_merge_uint_value(conf->upstream.store_access,
2405                              prev->upstream.store_access, 0600);
2406
2407    ngx_conf_merge_uint_value(conf->upstream.next_upstream_tries,
2408                              prev->upstream.next_upstream_tries, 0);
2409
2410    ngx_conf_merge_value(conf->upstream.buffering,
2411                              prev->upstream.buffering, 1);
2412
2413    ngx_conf_merge_value(conf->upstream.ignore_client_abort,
2414                              prev->upstream.ignore_client_abort, 0);
2415
2416    ngx_conf_merge_ptr_value(conf->upstream.local,
2417                              prev->upstream.local, NULL);
2418
2419    ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
2420                              prev->upstream.connect_timeout, 60000);
2421
2422    ngx_conf_merge_msec_value(conf->upstream.send_timeout,
2423                              prev->upstream.send_timeout, 60000);
2424
2425    ngx_conf_merge_msec_value(conf->upstream.read_timeout,
2426                              prev->upstream.read_timeout, 60000);
2427
2428    ngx_conf_merge_msec_value(conf->upstream.next_upstream_timeout,
2429                              prev->upstream.next_upstream_timeout, 0);
2430
2431    ngx_conf_merge_size_value(conf->upstream.send_lowat,
2432                              prev->upstream.send_lowat, 0);
2433
2434    ngx_conf_merge_size_value(conf->upstream.buffer_size,
2435                              prev->upstream.buffer_size,
2436                              (size_t) ngx_pagesize);
2437
2438
2439    ngx_conf_merge_bufs_value(conf->upstream.bufs, prev->upstream.bufs,
2440                              8, ngx_pagesize);
2441
2442    if (conf->upstream.bufs.num < 2) {
2443        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2444                           "there must be at least 2 \"fastcgi_buffers\"");
2445        return NGX_CONF_ERROR;
2446    }
2447
2448
2449    size = conf->upstream.buffer_size;
2450    if (size < conf->upstream.bufs.size) {
2451        size = conf->upstream.bufs.size;
2452    }
2453
2454
2455    ngx_conf_merge_size_value(conf->upstream.busy_buffers_size_conf,
2456                              prev->upstream.busy_buffers_size_conf,
2457                              NGX_CONF_UNSET_SIZE);
2458
2459    if (conf->upstream.busy_buffers_size_conf == NGX_CONF_UNSET_SIZE) {
2460        conf->upstream.busy_buffers_size = 2 * size;
2461    } else {
2462        conf->upstream.busy_buffers_size =
2463                                         conf->upstream.busy_buffers_size_conf;
2464    }
2465
2466    if (conf->upstream.busy_buffers_size < size) {
2467        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2468             "\"fastcgi_busy_buffers_size\" must be equal to or greater than "
2469             "the maximum of the value of \"fastcgi_buffer_size\" and "
2470             "one of the \"fastcgi_buffers\"");
2471
2472        return NGX_CONF_ERROR;
2473    }
2474
2475    if (conf->upstream.busy_buffers_size
2476        > (conf->upstream.bufs.num - 1) * conf->upstream.bufs.size)
2477    {
2478        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2479             "\"fastcgi_busy_buffers_size\" must be less than "
2480             "the size of all \"fastcgi_buffers\" minus one buffer");
2481
2482        return NGX_CONF_ERROR;
2483    }
2484
2485
2486    ngx_conf_merge_size_value(conf->upstream.temp_file_write_size_conf,
2487                              prev->upstream.temp_file_write_size_conf,
2488                              NGX_CONF_UNSET_SIZE);
2489
2490    if (conf->upstream.temp_file_write_size_conf == NGX_CONF_UNSET_SIZE) {
2491        conf->upstream.temp_file_write_size = 2 * size;
2492    } else {
2493        conf->upstream.temp_file_write_size =
2494                                      conf->upstream.temp_file_write_size_conf;
2495    }
2496
2497    if (conf->upstream.temp_file_write_size < size) {
2498        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2499             "\"fastcgi_temp_file_write_size\" must be equal to or greater "
2500             "than the maximum of the value of \"fastcgi_buffer_size\" and "
2501             "one of the \"fastcgi_buffers\"");
2502
2503        return NGX_CONF_ERROR;
2504    }
2505
2506
2507    ngx_conf_merge_size_value(conf->upstream.max_temp_file_size_conf,
2508                              prev->upstream.max_temp_file_size_conf,
2509                              NGX_CONF_UNSET_SIZE);
2510
2511    if (conf->upstream.max_temp_file_size_conf == NGX_CONF_UNSET_SIZE) {
2512        conf->upstream.max_temp_file_size = 1024 * 1024 * 1024;
2513    } else {
2514        conf->upstream.max_temp_file_size =
2515                                        conf->upstream.max_temp_file_size_conf;
2516    }
2517
2518    if (conf->upstream.max_temp_file_size != 0
2519        && conf->upstream.max_temp_file_size < size)
2520    {
2521        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2522             "\"fastcgi_max_temp_file_size\" must be equal to zero to disable "
2523             "temporary files usage or must be equal to or greater than "
2524             "the maximum of the value of \"fastcgi_buffer_size\" and "
2525             "one of the \"fastcgi_buffers\"");
2526
2527        return NGX_CONF_ERROR;
2528    }
2529
2530
2531    ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers,
2532                              prev->upstream.ignore_headers,
2533                              NGX_CONF_BITMASK_SET);
2534
2535
2536    ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,
2537                              prev->upstream.next_upstream,
2538                              (NGX_CONF_BITMASK_SET
2539                               |NGX_HTTP_UPSTREAM_FT_ERROR
2540                               |NGX_HTTP_UPSTREAM_FT_TIMEOUT));
2541
2542    if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {
2543        conf->upstream.next_upstream = NGX_CONF_BITMASK_SET
2544                                       |NGX_HTTP_UPSTREAM_FT_OFF;
2545    }
2546
2547    if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,
2548                              prev->upstream.temp_path,
2549                              &ngx_http_fastcgi_temp_path)
2550        != NGX_OK)
2551    {
2552        return NGX_CONF_ERROR;
2553    }
2554
2555#if (NGX_HTTP_CACHE)
2556
2557    ngx_conf_merge_ptr_value(conf->upstream.cache,
2558                              prev->upstream.cache, NULL);
2559
2560    if (conf->upstream.cache && conf->upstream.cache->data == NULL) {
2561        ngx_shm_zone_t  *shm_zone;
2562
2563        shm_zone = conf->upstream.cache;
2564
2565        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2566                           "\"fastcgi_cache\" zone \"%V\" is unknown",
2567                           &shm_zone->shm.name);
2568
2569        return NGX_CONF_ERROR;
2570    }
2571
2572    ngx_conf_merge_uint_value(conf->upstream.cache_min_uses,
2573                              prev->upstream.cache_min_uses, 1);
2574
2575    ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale,
2576                              prev->upstream.cache_use_stale,
2577                              (NGX_CONF_BITMASK_SET
2578                               |NGX_HTTP_UPSTREAM_FT_OFF));
2579
2580    if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_OFF) {
2581        conf->upstream.cache_use_stale = NGX_CONF_BITMASK_SET
2582                                         |NGX_HTTP_UPSTREAM_FT_OFF;
2583    }
2584
2585    if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_ERROR) {
2586        conf->upstream.cache_use_stale |= NGX_HTTP_UPSTREAM_FT_NOLIVE;
2587    }
2588
2589    if (conf->upstream.cache_methods == 0) {
2590        conf->upstream.cache_methods = prev->upstream.cache_methods;
2591    }
2592
2593    conf->upstream.cache_methods |= NGX_HTTP_GET|NGX_HTTP_HEAD;
2594
2595    ngx_conf_merge_ptr_value(conf->upstream.cache_bypass,
2596                             prev->upstream.cache_bypass, NULL);
2597
2598    ngx_conf_merge_ptr_value(conf->upstream.no_cache,
2599                             prev->upstream.no_cache, NULL);
2600
2601    ngx_conf_merge_ptr_value(conf->upstream.cache_valid,
2602                             prev->upstream.cache_valid, NULL);
2603
2604    if (conf->cache_key.value.data == NULL) {
2605        conf->cache_key = prev->cache_key;
2606    }
2607
2608    if (conf->upstream.cache && conf->cache_key.value.data == NULL) {
2609        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
2610                           "no \"fastcgi_cache_key\" for \"fastcgi_cache\"");
2611    }
2612
2613    ngx_conf_merge_value(conf->upstream.cache_lock,
2614                              prev->upstream.cache_lock, 0);
2615
2616    ngx_conf_merge_msec_value(conf->upstream.cache_lock_timeout,
2617                              prev->upstream.cache_lock_timeout, 5000);
2618
2619    ngx_conf_merge_value(conf->upstream.cache_revalidate,
2620                              prev->upstream.cache_revalidate, 0);
2621
2622#endif
2623
2624    ngx_conf_merge_value(conf->upstream.pass_request_headers,
2625                              prev->upstream.pass_request_headers, 1);
2626    ngx_conf_merge_value(conf->upstream.pass_request_body,
2627                              prev->upstream.pass_request_body, 1);
2628
2629    ngx_conf_merge_value(conf->upstream.intercept_errors,
2630                              prev->upstream.intercept_errors, 0);
2631
2632    ngx_conf_merge_ptr_value(conf->catch_stderr, prev->catch_stderr, NULL);
2633
2634    ngx_conf_merge_value(conf->keep_conn, prev->keep_conn, 0);
2635
2636
2637    ngx_conf_merge_str_value(conf->index, prev->index, "");
2638
2639    hash.max_size = 512;
2640    hash.bucket_size = ngx_align(64, ngx_cacheline_size);
2641    hash.name = "fastcgi_hide_headers_hash";
2642
2643    if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream,
2644             &prev->upstream, ngx_http_fastcgi_hide_headers, &hash)
2645        != NGX_OK)
2646    {
2647        return NGX_CONF_ERROR;
2648    }
2649
2650    if (conf->upstream.upstream == NULL) {
2651        conf->upstream.upstream = prev->upstream.upstream;
2652    }
2653
2654    if (conf->fastcgi_lengths == NULL) {
2655        conf->fastcgi_lengths = prev->fastcgi_lengths;
2656        conf->fastcgi_values = prev->fastcgi_values;
2657    }
2658
2659    if (conf->upstream.upstream || conf->fastcgi_lengths) {
2660        clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
2661        if (clcf->handler == NULL && clcf->lmt_excpt) {
2662            clcf->handler = ngx_http_fastcgi_handler;
2663        }
2664    }
2665
2666#if (NGX_PCRE)
2667    if (conf->split_regex == NULL) {
2668        conf->split_regex = prev->split_regex;
2669        conf->split_name = prev->split_name;
2670    }
2671#endif
2672
2673    if (ngx_http_fastcgi_merge_params(cf, conf, prev) != NGX_OK) {
2674        return NGX_CONF_ERROR;
2675    }
2676
2677    return NGX_CONF_OK;
2678}
2679
2680
2681static ngx_int_t
2682ngx_http_fastcgi_merge_params(ngx_conf_t *cf,
2683    ngx_http_fastcgi_loc_conf_t *conf, ngx_http_fastcgi_loc_conf_t *prev)
2684{
2685    u_char                       *p;
2686    size_t                        size;
2687    uintptr_t                    *code;
2688    ngx_uint_t                    i, nsrc;
2689    ngx_array_t                   headers_names;
2690#if (NGX_HTTP_CACHE)
2691    ngx_array_t                   params_merged;
2692#endif
2693    ngx_hash_key_t               *hk;
2694    ngx_hash_init_t               hash;
2695    ngx_http_upstream_param_t    *src;
2696    ngx_http_script_compile_t     sc;
2697    ngx_http_script_copy_code_t  *copy;
2698
2699    if (conf->params_source == NULL) {
2700        conf->params_source = prev->params_source;
2701
2702        if (prev->headers_hash.buckets
2703#if (NGX_HTTP_CACHE)
2704            && ((conf->upstream.cache == NULL)
2705                == (prev->upstream.cache == NULL))
2706#endif
2707           )
2708        {
2709            conf->flushes = prev->flushes;
2710            conf->params_len = prev->params_len;
2711            conf->params = prev->params;
2712            conf->headers_hash = prev->headers_hash;
2713            conf->header_params = prev->header_params;
2714
2715            return NGX_OK;
2716        }
2717    }
2718
2719    if (conf->params_source == NULL
2720#if (NGX_HTTP_CACHE)
2721        && (conf->upstream.cache == NULL)
2722#endif
2723       )
2724    {
2725        conf->headers_hash.buckets = (void *) 1;
2726        return NGX_OK;
2727    }
2728
2729    conf->params_len = ngx_array_create(cf->pool, 64, 1);
2730    if (conf->params_len == NULL) {
2731        return NGX_ERROR;
2732    }
2733
2734    conf->params = ngx_array_create(cf->pool, 512, 1);
2735    if (conf->params == NULL) {
2736        return NGX_ERROR;
2737    }
2738
2739    if (ngx_array_init(&headers_names, cf->temp_pool, 4, sizeof(ngx_hash_key_t))
2740        != NGX_OK)
2741    {
2742        return NGX_ERROR;
2743    }
2744
2745    if (conf->params_source) {
2746        src = conf->params_source->elts;
2747        nsrc = conf->params_source->nelts;
2748
2749    } else {
2750        src = NULL;
2751        nsrc = 0;
2752    }
2753
2754#if (NGX_HTTP_CACHE)
2755
2756    if (conf->upstream.cache) {
2757        ngx_keyval_t               *h;
2758        ngx_http_upstream_param_t  *s;
2759
2760        if (ngx_array_init(&params_merged, cf->temp_pool, 4,
2761                           sizeof(ngx_http_upstream_param_t))
2762            != NGX_OK)
2763        {
2764            return NGX_ERROR;
2765        }
2766
2767        for (i = 0; i < nsrc; i++) {
2768
2769            s = ngx_array_push(&params_merged);
2770            if (s == NULL) {
2771                return NGX_ERROR;
2772            }
2773
2774            *s = src[i];
2775        }
2776
2777        h = ngx_http_fastcgi_cache_headers;
2778
2779        while (h->key.len) {
2780
2781            src = params_merged.elts;
2782            nsrc = params_merged.nelts;
2783
2784            for (i = 0; i < nsrc; i++) {
2785                if (ngx_strcasecmp(h->key.data, src[i].key.data) == 0) {
2786                    goto next;
2787                }
2788            }
2789
2790            s = ngx_array_push(&params_merged);
2791            if (s == NULL) {
2792                return NGX_ERROR;
2793            }
2794
2795            s->key = h->key;
2796            s->value = h->value;
2797            s->skip_empty = 1;
2798
2799        next:
2800
2801            h++;
2802        }
2803
2804        src = params_merged.elts;
2805        nsrc = params_merged.nelts;
2806    }
2807
2808#endif
2809
2810    for (i = 0; i < nsrc; i++) {
2811
2812        if (src[i].key.len > sizeof("HTTP_") - 1
2813            && ngx_strncmp(src[i].key.data, "HTTP_", sizeof("HTTP_") - 1) == 0)
2814        {
2815            hk = ngx_array_push(&headers_names);
2816            if (hk == NULL) {
2817                return NGX_ERROR;
2818            }
2819
2820            hk->key.len = src[i].key.len - 5;
2821            hk->key.data = src[i].key.data + 5;
2822            hk->key_hash = ngx_hash_key_lc(hk->key.data, hk->key.len);
2823            hk->value = (void *) 1;
2824
2825            if (src[i].value.len == 0) {
2826                continue;
2827            }
2828        }
2829
2830        copy = ngx_array_push_n(conf->params_len,
2831                                sizeof(ngx_http_script_copy_code_t));
2832        if (copy == NULL) {
2833            return NGX_ERROR;
2834        }
2835
2836        copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
2837        copy->len = src[i].key.len;
2838
2839        copy = ngx_array_push_n(conf->params_len,
2840                                sizeof(ngx_http_script_copy_code_t));
2841        if (copy == NULL) {
2842            return NGX_ERROR;
2843        }
2844
2845        copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
2846        copy->len = src[i].skip_empty;
2847
2848
2849        size = (sizeof(ngx_http_script_copy_code_t)
2850                + src[i].key.len + sizeof(uintptr_t) - 1)
2851               & ~(sizeof(uintptr_t) - 1);
2852
2853        copy = ngx_array_push_n(conf->params, size);
2854        if (copy == NULL) {
2855            return NGX_ERROR;
2856        }
2857
2858        copy->code = ngx_http_script_copy_code;
2859        copy->len = src[i].key.len;
2860
2861        p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);
2862        ngx_memcpy(p, src[i].key.data, src[i].key.len);
2863
2864
2865        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
2866
2867        sc.cf = cf;
2868        sc.source = &src[i].value;
2869        sc.flushes = &conf->flushes;
2870        sc.lengths = &conf->params_len;
2871        sc.values = &conf->params;
2872
2873        if (ngx_http_script_compile(&sc) != NGX_OK) {
2874            return NGX_ERROR;
2875        }
2876
2877        code = ngx_array_push_n(conf->params_len, sizeof(uintptr_t));
2878        if (code == NULL) {
2879            return NGX_ERROR;
2880        }
2881
2882        *code = (uintptr_t) NULL;
2883
2884
2885        code = ngx_array_push_n(conf->params, sizeof(uintptr_t));
2886        if (code == NULL) {
2887            return NGX_ERROR;
2888        }
2889
2890        *code = (uintptr_t) NULL;
2891    }
2892
2893    code = ngx_array_push_n(conf->params_len, sizeof(uintptr_t));
2894    if (code == NULL) {
2895        return NGX_ERROR;
2896    }
2897
2898    *code = (uintptr_t) NULL;
2899
2900    conf->header_params = headers_names.nelts;
2901
2902    hash.hash = &conf->headers_hash;
2903    hash.key = ngx_hash_key_lc;
2904    hash.max_size = 512;
2905    hash.bucket_size = 64;
2906    hash.name = "fastcgi_params_hash";
2907    hash.pool = cf->pool;
2908    hash.temp_pool = NULL;
2909
2910    return ngx_hash_init(&hash, headers_names.elts, headers_names.nelts);
2911}
2912
2913
2914static ngx_int_t
2915ngx_http_fastcgi_script_name_variable(ngx_http_request_t *r,
2916    ngx_http_variable_value_t *v, uintptr_t data)
2917{
2918    u_char                       *p;
2919    ngx_http_fastcgi_ctx_t       *f;
2920    ngx_http_fastcgi_loc_conf_t  *flcf;
2921
2922    flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
2923
2924    f = ngx_http_fastcgi_split(r, flcf);
2925
2926    if (f == NULL) {
2927        return NGX_ERROR;
2928    }
2929
2930    if (f->script_name.len == 0
2931        || f->script_name.data[f->script_name.len - 1] != '/')
2932    {
2933        v->len = f->script_name.len;
2934        v->valid = 1;
2935        v->no_cacheable = 0;
2936        v->not_found = 0;
2937        v->data = f->script_name.data;
2938
2939        return NGX_OK;
2940    }
2941
2942    v->len = f->script_name.len + flcf->index.len;
2943
2944    v->data = ngx_pnalloc(r->pool, v->len);
2945    if (v->data == NULL) {
2946        return NGX_ERROR;
2947    }
2948
2949    p = ngx_copy(v->data, f->script_name.data, f->script_name.len);
2950    ngx_memcpy(p, flcf->index.data, flcf->index.len);
2951
2952    return NGX_OK;
2953}
2954
2955
2956static ngx_int_t
2957ngx_http_fastcgi_path_info_variable(ngx_http_request_t *r,
2958    ngx_http_variable_value_t *v, uintptr_t data)
2959{
2960    ngx_http_fastcgi_ctx_t       *f;
2961    ngx_http_fastcgi_loc_conf_t  *flcf;
2962
2963    flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
2964
2965    f = ngx_http_fastcgi_split(r, flcf);
2966
2967    if (f == NULL) {
2968        return NGX_ERROR;
2969    }
2970
2971    v->len = f->path_info.len;
2972    v->valid = 1;
2973    v->no_cacheable = 0;
2974    v->not_found = 0;
2975    v->data = f->path_info.data;
2976
2977    return NGX_OK;
2978}
2979
2980
2981static ngx_http_fastcgi_ctx_t *
2982ngx_http_fastcgi_split(ngx_http_request_t *r, ngx_http_fastcgi_loc_conf_t *flcf)
2983{
2984    ngx_http_fastcgi_ctx_t       *f;
2985#if (NGX_PCRE)
2986    ngx_int_t                     n;
2987    int                           captures[(1 + 2) * 3];
2988
2989    f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
2990
2991    if (f == NULL) {
2992        f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t));
2993        if (f == NULL) {
2994            return NULL;
2995        }
2996
2997        ngx_http_set_ctx(r, f, ngx_http_fastcgi_module);
2998    }
2999
3000    if (f->script_name.len) {
3001        return f;
3002    }
3003
3004    if (flcf->split_regex == NULL) {
3005        f->script_name = r->uri;
3006        return f;
3007    }
3008
3009    n = ngx_regex_exec(flcf->split_regex, &r->uri, captures, (1 + 2) * 3);
3010
3011    if (n >= 0) { /* match */
3012        f->script_name.len = captures[3] - captures[2];
3013        f->script_name.data = r->uri.data + captures[2];
3014
3015        f->path_info.len = captures[5] - captures[4];
3016        f->path_info.data = r->uri.data + captures[4];
3017
3018        return f;
3019    }
3020
3021    if (n == NGX_REGEX_NO_MATCHED) {
3022        f->script_name = r->uri;
3023        return f;
3024    }
3025
3026    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
3027                  ngx_regex_exec_n " failed: %i on \"%V\" using \"%V\"",
3028                  n, &r->uri, &flcf->split_name);
3029    return NULL;
3030
3031#else
3032
3033    f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
3034
3035    if (f == NULL) {
3036        f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t));
3037        if (f == NULL) {
3038            return NULL;
3039        }
3040
3041        ngx_http_set_ctx(r, f, ngx_http_fastcgi_module);
3042    }
3043
3044    f->script_name = r->uri;
3045
3046    return f;
3047
3048#endif
3049}
3050
3051
3052static char *
3053ngx_http_fastcgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
3054{
3055    ngx_http_fastcgi_loc_conf_t *flcf = conf;
3056
3057    ngx_url_t                   u;
3058    ngx_str_t                  *value, *url;
3059    ngx_uint_t                  n;
3060    ngx_http_core_loc_conf_t   *clcf;
3061    ngx_http_script_compile_t   sc;
3062
3063    if (flcf->upstream.upstream || flcf->fastcgi_lengths) {
3064        return "is duplicate";
3065    }
3066
3067    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
3068
3069    clcf->handler = ngx_http_fastcgi_handler;
3070
3071    if (clcf->name.data[clcf->name.len - 1] == '/') {
3072        clcf->auto_redirect = 1;
3073    }
3074
3075    value = cf->args->elts;
3076
3077    url = &value[1];
3078
3079    n = ngx_http_script_variables_count(url);
3080
3081    if (n) {
3082
3083        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
3084
3085        sc.cf = cf;
3086        sc.source = url;
3087        sc.lengths = &flcf->fastcgi_lengths;
3088        sc.values = &flcf->fastcgi_values;
3089        sc.variables = n;
3090        sc.complete_lengths = 1;
3091        sc.complete_values = 1;
3092
3093        if (ngx_http_script_compile(&sc) != NGX_OK) {
3094            return NGX_CONF_ERROR;
3095        }
3096
3097        return NGX_CONF_OK;
3098    }
3099
3100    ngx_memzero(&u, sizeof(ngx_url_t));
3101
3102    u.url = value[1];
3103    u.no_resolve = 1;
3104
3105    flcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
3106    if (flcf->upstream.upstream == NULL) {
3107        return NGX_CONF_ERROR;
3108    }
3109
3110    return NGX_CONF_OK;
3111}
3112
3113
3114static char *
3115ngx_http_fastcgi_split_path_info(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
3116{
3117#if (NGX_PCRE)
3118    ngx_http_fastcgi_loc_conf_t *flcf = conf;
3119
3120    ngx_str_t            *value;
3121    ngx_regex_compile_t   rc;
3122    u_char                errstr[NGX_MAX_CONF_ERRSTR];
3123
3124    value = cf->args->elts;
3125
3126    flcf->split_name = value[1];
3127
3128    ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
3129
3130    rc.pattern = value[1];
3131    rc.pool = cf->pool;
3132    rc.err.len = NGX_MAX_CONF_ERRSTR;
3133    rc.err.data = errstr;
3134
3135    if (ngx_regex_compile(&rc) != NGX_OK) {
3136        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc.err);
3137        return NGX_CONF_ERROR;
3138    }
3139
3140    if (rc.captures != 2) {
3141        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
3142                           "pattern \"%V\" must have 2 captures", &value[1]);
3143        return NGX_CONF_ERROR;
3144    }
3145
3146    flcf->split_regex = rc.regex;
3147
3148    return NGX_CONF_OK;
3149
3150#else
3151
3152    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
3153                       "\"%V\" requires PCRE library", &cmd->name);
3154    return NGX_CONF_ERROR;
3155
3156#endif
3157}
3158
3159
3160static char *
3161ngx_http_fastcgi_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
3162{
3163    ngx_http_fastcgi_loc_conf_t *flcf = conf;
3164
3165    ngx_str_t                  *value;
3166    ngx_http_script_compile_t   sc;
3167
3168    if (flcf->upstream.store != NGX_CONF_UNSET
3169        || flcf->upstream.store_lengths)
3170    {
3171        return "is duplicate";
3172    }
3173
3174    value = cf->args->elts;
3175
3176    if (ngx_strcmp(value[1].data, "off") == 0) {
3177        flcf->upstream.store = 0;
3178        return NGX_CONF_OK;
3179    }
3180
3181#if (NGX_HTTP_CACHE)
3182
3183    if (flcf->upstream.cache != NGX_CONF_UNSET_PTR
3184        && flcf->upstream.cache != NULL)
3185    {
3186        return "is incompatible with \"fastcgi_cache\"";
3187    }
3188
3189#endif
3190
3191    if (ngx_strcmp(value[1].data, "on") == 0) {
3192        flcf->upstream.store = 1;
3193        return NGX_CONF_OK;
3194    }
3195
3196    /* include the terminating '\0' into script */
3197    value[1].len++;
3198
3199    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
3200
3201    sc.cf = cf;
3202    sc.source = &value[1];
3203    sc.lengths = &flcf->upstream.store_lengths;
3204    sc.values = &flcf->upstream.store_values;
3205    sc.variables = ngx_http_script_variables_count(&value[1]);
3206    sc.complete_lengths = 1;
3207    sc.complete_values = 1;
3208
3209    if (ngx_http_script_compile(&sc) != NGX_OK) {
3210        return NGX_CONF_ERROR;
3211    }
3212
3213    return NGX_CONF_OK;
3214}
3215
3216
3217#if (NGX_HTTP_CACHE)
3218
3219static char *
3220ngx_http_fastcgi_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
3221{
3222    ngx_http_fastcgi_loc_conf_t *flcf = conf;
3223
3224    ngx_str_t  *value;
3225
3226    value = cf->args->elts;
3227
3228    if (flcf->upstream.cache != NGX_CONF_UNSET_PTR) {
3229        return "is duplicate";
3230    }
3231
3232    if (ngx_strcmp(value[1].data, "off") == 0) {
3233        flcf->upstream.cache = NULL;
3234        return NGX_CONF_OK;
3235    }
3236
3237    if (flcf->upstream.store > 0 || flcf->upstream.store_lengths) {
3238        return "is incompatible with \"fastcgi_store\"";
3239    }
3240
3241    flcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0,
3242                                                 &ngx_http_fastcgi_module);
3243    if (flcf->upstream.cache == NULL) {
3244        return NGX_CONF_ERROR;
3245    }
3246
3247    return NGX_CONF_OK;
3248}
3249
3250
3251static char *
3252ngx_http_fastcgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
3253{
3254    ngx_http_fastcgi_loc_conf_t *flcf = conf;
3255
3256    ngx_str_t                         *value;
3257    ngx_http_compile_complex_value_t   ccv;
3258
3259    value = cf->args->elts;
3260
3261    if (flcf->cache_key.value.data) {
3262        return "is duplicate";
3263    }
3264
3265    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
3266
3267    ccv.cf = cf;
3268    ccv.value = &value[1];
3269    ccv.complex_value = &flcf->cache_key;
3270
3271    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
3272        return NGX_CONF_ERROR;
3273    }
3274
3275    return NGX_CONF_OK;
3276}
3277
3278#endif
3279
3280
3281static char *
3282ngx_http_fastcgi_lowat_check(ngx_conf_t *cf, void *post, void *data)
3283{
3284#if (NGX_FREEBSD)
3285    ssize_t *np = data;
3286
3287    if ((u_long) *np >= ngx_freebsd_net_inet_tcp_sendspace) {
3288        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
3289                           "\"fastcgi_send_lowat\" must be less than %d "
3290                           "(sysctl net.inet.tcp.sendspace)",
3291                           ngx_freebsd_net_inet_tcp_sendspace);
3292
3293        return NGX_CONF_ERROR;
3294    }
3295
3296#elif !(NGX_HAVE_SO_SNDLOWAT)
3297    ssize_t *np = data;
3298
3299    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
3300                       "\"fastcgi_send_lowat\" is not supported, ignored");
3301
3302    *np = 0;
3303
3304#endif
3305
3306    return NGX_CONF_OK;
3307}
Note: See TracBrowser for help on using the repository browser.