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

Last change on this file was 6966:fa56ab75cffc, checked in by Piotr Sikora <piotrsikora@…>, 5 weeks ago

Upstream: allow recovery from "429 Too Many Requests" response.

This change adds "http_429" parameter to "proxy_next_upstream" for
retrying rate-limited requests, and to "proxy_cache_use_stale" for
serving stale cached responses after being rate-limited.

Signed-off-by: Piotr Sikora <piotrsikora@…>

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