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

Last change on this file was 6906:1aeaae6e9446, checked in by Roman Arutyunyan <arut@…>, 3 weeks ago

Upstream: proxy_cache_background_update and friends.

The directives enable cache updates in subrequests.

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