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

tip
Last change on this file was 5874:2aff40b3dea2, checked in by Roman Arutyunyan <arut@…>, 6 days ago

Upstream: proxy_force_ranges and friends.

The directives enable byte ranges for both cached and uncached
responses regardless of backend headers.

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