1*0Sigor@sysoev.ru 
2*0Sigor@sysoev.ru /*
3*0Sigor@sysoev.ru  * Copyright (C) Igor Sysoev
4*0Sigor@sysoev.ru  * Copyright (C) NGINX, Inc.
5*0Sigor@sysoev.ru  */
6*0Sigor@sysoev.ru 
7*0Sigor@sysoev.ru #include <nxt_main.h>
8*0Sigor@sysoev.ru 
9*0Sigor@sysoev.ru 
10*0Sigor@sysoev.ru #define NXT_FASTCGI_RESPONDER  1
11*0Sigor@sysoev.ru #define NXT_FASTCGI_KEEP_CONN  1
12*0Sigor@sysoev.ru 
13*0Sigor@sysoev.ru 
14*0Sigor@sysoev.ru typedef struct {
15*0Sigor@sysoev.ru     u_char    *buf;
16*0Sigor@sysoev.ru     uint32_t  len;
17*0Sigor@sysoev.ru     u_char    length[4];
18*0Sigor@sysoev.ru } nxt_fastcgi_param_t;
19*0Sigor@sysoev.ru 
20*0Sigor@sysoev.ru 
21*0Sigor@sysoev.ru #define                                                                       \
22*0Sigor@sysoev.ru nxt_fastcgi_set_record_length(p, length)                                      \
23*0Sigor@sysoev.ru     do {                                                                      \
24*0Sigor@sysoev.ru         uint32_t  len = length;                                               \
25*0Sigor@sysoev.ru                                                                               \
26*0Sigor@sysoev.ru         p[1] = (u_char) len;  len >>= 8;                                      \
27*0Sigor@sysoev.ru         p[0] = (u_char) len;                                                  \
28*0Sigor@sysoev.ru     } while (0)
29*0Sigor@sysoev.ru 
30*0Sigor@sysoev.ru 
31*0Sigor@sysoev.ru nxt_inline size_t
32*0Sigor@sysoev.ru nxt_fastcgi_param_length(u_char *p, uint32_t length)
33*0Sigor@sysoev.ru {
34*0Sigor@sysoev.ru     if (nxt_fast_path(length < 128)) {
35*0Sigor@sysoev.ru         *p = (u_char) length;
36*0Sigor@sysoev.ru         return 1;
37*0Sigor@sysoev.ru     }
38*0Sigor@sysoev.ru 
39*0Sigor@sysoev.ru     p[3] = (u_char) length;  length >>= 8;
40*0Sigor@sysoev.ru     p[2] = (u_char) length;  length >>= 8;
41*0Sigor@sysoev.ru     p[1] = (u_char) length;  length >>= 8;
42*0Sigor@sysoev.ru     p[0] = (u_char) (length | 0x80);
43*0Sigor@sysoev.ru 
44*0Sigor@sysoev.ru     return 4;
45*0Sigor@sysoev.ru }
46*0Sigor@sysoev.ru 
47*0Sigor@sysoev.ru 
48*0Sigor@sysoev.ru static nxt_buf_t *nxt_fastcgi_request_create(nxt_fastcgi_source_t *fs);
49*0Sigor@sysoev.ru static nxt_int_t nxt_fastcgi_next_param(nxt_fastcgi_source_t *fs,
50*0Sigor@sysoev.ru     nxt_fastcgi_param_t *param);
51*0Sigor@sysoev.ru 
52*0Sigor@sysoev.ru static void nxt_fastcgi_source_record_filter(nxt_thread_t *thr, void *obj,
53*0Sigor@sysoev.ru     void *data);
54*0Sigor@sysoev.ru static void nxt_fastcgi_source_record_error(nxt_thread_t *thr, void *obj,
55*0Sigor@sysoev.ru     void *data);
56*0Sigor@sysoev.ru static void nxt_fastcgi_source_header_filter(nxt_thread_t *thr, void *obj,
57*0Sigor@sysoev.ru     void *data);
58*0Sigor@sysoev.ru static void nxt_fastcgi_source_sync_buffer(nxt_thread_t *thr,
59*0Sigor@sysoev.ru     nxt_fastcgi_source_t *fs, nxt_buf_t *b);
60*0Sigor@sysoev.ru 
61*0Sigor@sysoev.ru static nxt_int_t nxt_fastcgi_source_header_process(nxt_fastcgi_source_t *fs);
62*0Sigor@sysoev.ru static nxt_int_t nxt_fastcgi_source_status(nxt_upstream_source_t *us,
63*0Sigor@sysoev.ru     nxt_name_value_t *nv);
64*0Sigor@sysoev.ru static nxt_int_t nxt_fastcgi_source_content_length(nxt_upstream_source_t *us,
65*0Sigor@sysoev.ru     nxt_name_value_t *nv);
66*0Sigor@sysoev.ru 
67*0Sigor@sysoev.ru static void nxt_fastcgi_source_header_ready(nxt_fastcgi_source_t *fs,
68*0Sigor@sysoev.ru     nxt_buf_t *b);
69*0Sigor@sysoev.ru static void nxt_fastcgi_source_body_filter(nxt_thread_t *thr, void *obj,
70*0Sigor@sysoev.ru     void *data);
71*0Sigor@sysoev.ru static nxt_buf_t *nxt_fastcgi_source_last_buf(nxt_fastcgi_parse_t *fp);
72*0Sigor@sysoev.ru static void nxt_fastcgi_source_error(nxt_stream_source_t *stream);
73*0Sigor@sysoev.ru static void nxt_fastcgi_source_fail(nxt_fastcgi_source_t *fs);
74*0Sigor@sysoev.ru 
75*0Sigor@sysoev.ru 
76*0Sigor@sysoev.ru /*
77*0Sigor@sysoev.ru  * A FastCGI request:
78*0Sigor@sysoev.ru  *   FCGI_BEGIN_REQUEST record;
79*0Sigor@sysoev.ru  *   Several FCGI_PARAMS records, the last FCGI_PARAMS record must have
80*0Sigor@sysoev.ru  *   zero content length,
81*0Sigor@sysoev.ru  *   Several FCGI_STDIN records, the last FCGI_STDIN record must have
82*0Sigor@sysoev.ru  *   zero content length.
83*0Sigor@sysoev.ru  */
84*0Sigor@sysoev.ru 
85*0Sigor@sysoev.ru static const uint8_t  nxt_fastcgi_begin_request[] = {
86*0Sigor@sysoev.ru     1,                                 /* FastCGI version.                   */
87*0Sigor@sysoev.ru     NXT_FASTCGI_BEGIN_REQUEST,         /* The BEGIN_REQUEST record type.     */
88*0Sigor@sysoev.ru     0, 1,                              /* Request ID.                        */
89*0Sigor@sysoev.ru     0, 8,                              /* Content length of the Role record. */
90*0Sigor@sysoev.ru     0,                                 /* Padding length.                    */
91*0Sigor@sysoev.ru     0,                                 /* Reserved.                          */
92*0Sigor@sysoev.ru 
93*0Sigor@sysoev.ru     0, NXT_FASTCGI_RESPONDER,          /* The Responder Role.                */
94*0Sigor@sysoev.ru     0,                                 /* Flags.                             */
95*0Sigor@sysoev.ru     0, 0, 0, 0, 0,                     /* Reserved.                          */
96*0Sigor@sysoev.ru };
97*0Sigor@sysoev.ru 
98*0Sigor@sysoev.ru 
99*0Sigor@sysoev.ru static const uint8_t  nxt_fastcgi_params_record[] = {
100*0Sigor@sysoev.ru     1,                                 /* FastCGI version.                   */
101*0Sigor@sysoev.ru     NXT_FASTCGI_PARAMS,                /* The PARAMS record type.            */
102*0Sigor@sysoev.ru     0, 1,                              /* Request ID.                        */
103*0Sigor@sysoev.ru     0, 0,                              /* Content length.                    */
104*0Sigor@sysoev.ru     0,                                 /* Padding length.                    */
105*0Sigor@sysoev.ru     0,                                 /* Reserved.                          */
106*0Sigor@sysoev.ru };
107*0Sigor@sysoev.ru 
108*0Sigor@sysoev.ru 
109*0Sigor@sysoev.ru static const uint8_t  nxt_fastcgi_stdin_record[] = {
110*0Sigor@sysoev.ru     1,                                 /* FastCGI version.                   */
111*0Sigor@sysoev.ru     NXT_FASTCGI_STDIN,                 /* The STDIN record type.             */
112*0Sigor@sysoev.ru     0, 1,                              /* Request ID.                        */
113*0Sigor@sysoev.ru     0, 0,                              /* Content length.                    */
114*0Sigor@sysoev.ru     0,                                 /* Padding length.                    */
115*0Sigor@sysoev.ru     0,                                 /* Reserved.                          */
116*0Sigor@sysoev.ru };
117*0Sigor@sysoev.ru 
118*0Sigor@sysoev.ru 
119*0Sigor@sysoev.ru void
120*0Sigor@sysoev.ru nxt_fastcgi_source_handler(nxt_upstream_source_t *us,
121*0Sigor@sysoev.ru     nxt_fastcgi_source_request_create_t request_create)
122*0Sigor@sysoev.ru {
123*0Sigor@sysoev.ru     nxt_stream_source_t   *stream;
124*0Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
125*0Sigor@sysoev.ru 
126*0Sigor@sysoev.ru     fs = nxt_mem_zalloc(us->buffers.mem_pool, sizeof(nxt_fastcgi_source_t));
127*0Sigor@sysoev.ru     if (nxt_slow_path(fs == NULL)) {
128*0Sigor@sysoev.ru         goto fail;
129*0Sigor@sysoev.ru     }
130*0Sigor@sysoev.ru 
131*0Sigor@sysoev.ru     us->protocol_source = fs;
132*0Sigor@sysoev.ru 
133*0Sigor@sysoev.ru     fs->header_in.list = nxt_list_create(us->buffers.mem_pool, 8,
134*0Sigor@sysoev.ru                                          sizeof(nxt_name_value_t));
135*0Sigor@sysoev.ru     if (nxt_slow_path(fs->header_in.list == NULL)) {
136*0Sigor@sysoev.ru         goto fail;
137*0Sigor@sysoev.ru     }
138*0Sigor@sysoev.ru 
139*0Sigor@sysoev.ru     fs->header_in.hash = us->header_hash;
140*0Sigor@sysoev.ru     fs->upstream = us;
141*0Sigor@sysoev.ru     fs->request_create = request_create;
142*0Sigor@sysoev.ru 
143*0Sigor@sysoev.ru     stream = us->stream;
144*0Sigor@sysoev.ru 
145*0Sigor@sysoev.ru     if (stream == NULL) {
146*0Sigor@sysoev.ru         stream = nxt_mem_zalloc(us->buffers.mem_pool,
147*0Sigor@sysoev.ru                                 sizeof(nxt_stream_source_t));
148*0Sigor@sysoev.ru         if (nxt_slow_path(stream == NULL)) {
149*0Sigor@sysoev.ru             goto fail;
150*0Sigor@sysoev.ru         }
151*0Sigor@sysoev.ru 
152*0Sigor@sysoev.ru         us->stream = stream;
153*0Sigor@sysoev.ru         stream->upstream = us;
154*0Sigor@sysoev.ru 
155*0Sigor@sysoev.ru     } else {
156*0Sigor@sysoev.ru         nxt_memzero(stream, sizeof(nxt_stream_source_t));
157*0Sigor@sysoev.ru     }
158*0Sigor@sysoev.ru 
159*0Sigor@sysoev.ru     /*
160*0Sigor@sysoev.ru      * Create the FastCGI source filter chain:
161*0Sigor@sysoev.ru      *   stream source | FastCGI record filter | FastCGI HTTP header filter
162*0Sigor@sysoev.ru      */
163*0Sigor@sysoev.ru     stream->next = &fs->query;
164*0Sigor@sysoev.ru     stream->error_handler = nxt_fastcgi_source_error;
165*0Sigor@sysoev.ru 
166*0Sigor@sysoev.ru     fs->record.next.context = fs;
167*0Sigor@sysoev.ru     fs->record.next.filter = nxt_fastcgi_source_header_filter;
168*0Sigor@sysoev.ru 
169*0Sigor@sysoev.ru     fs->record.parse.last_buf = nxt_fastcgi_source_last_buf;
170*0Sigor@sysoev.ru     fs->record.parse.data = fs;
171*0Sigor@sysoev.ru     fs->record.parse.mem_pool = us->buffers.mem_pool;
172*0Sigor@sysoev.ru 
173*0Sigor@sysoev.ru     fs->query.context = &fs->record.parse;
174*0Sigor@sysoev.ru     fs->query.filter = nxt_fastcgi_source_record_filter;
175*0Sigor@sysoev.ru 
176*0Sigor@sysoev.ru     fs->header_in.content_length = -1;
177*0Sigor@sysoev.ru 
178*0Sigor@sysoev.ru     stream->out = nxt_fastcgi_request_create(fs);
179*0Sigor@sysoev.ru 
180*0Sigor@sysoev.ru     if (nxt_fast_path(stream->out != NULL)) {
181*0Sigor@sysoev.ru         nxt_memzero(&fs->u.header, sizeof(nxt_http_split_header_parse_t));
182*0Sigor@sysoev.ru         fs->u.header.mem_pool = fs->upstream->buffers.mem_pool;
183*0Sigor@sysoev.ru 
184*0Sigor@sysoev.ru         nxt_stream_source_connect(stream);
185*0Sigor@sysoev.ru         return;
186*0Sigor@sysoev.ru     }
187*0Sigor@sysoev.ru 
188*0Sigor@sysoev.ru fail:
189*0Sigor@sysoev.ru 
190*0Sigor@sysoev.ru     nxt_fastcgi_source_fail(fs);
191*0Sigor@sysoev.ru }
192*0Sigor@sysoev.ru 
193*0Sigor@sysoev.ru 
194*0Sigor@sysoev.ru static nxt_buf_t *
195*0Sigor@sysoev.ru nxt_fastcgi_request_create(nxt_fastcgi_source_t *fs)
196*0Sigor@sysoev.ru {
197*0Sigor@sysoev.ru     u_char               *p, *record_length;
198*0Sigor@sysoev.ru     size_t               len, size, max_record_size;
199*0Sigor@sysoev.ru     nxt_int_t            ret;
200*0Sigor@sysoev.ru     nxt_buf_t            *b, *req, **prev;
201*0Sigor@sysoev.ru     nxt_bool_t           begin_request;
202*0Sigor@sysoev.ru     nxt_fastcgi_param_t  param;
203*0Sigor@sysoev.ru 
204*0Sigor@sysoev.ru     nxt_thread_log_debug("fastcgi request");
205*0Sigor@sysoev.ru 
206*0Sigor@sysoev.ru     begin_request = 1;
207*0Sigor@sysoev.ru     param.len = 0;
208*0Sigor@sysoev.ru     prev = &req;
209*0Sigor@sysoev.ru 
210*0Sigor@sysoev.ru new_buffer:
211*0Sigor@sysoev.ru 
212*0Sigor@sysoev.ru     ret = nxt_buf_pool_mem_alloc(&fs->upstream->buffers, 0);
213*0Sigor@sysoev.ru     if (nxt_slow_path(ret != NXT_OK)) {
214*0Sigor@sysoev.ru         return NULL;
215*0Sigor@sysoev.ru     }
216*0Sigor@sysoev.ru 
217*0Sigor@sysoev.ru     b = fs->upstream->buffers.current;
218*0Sigor@sysoev.ru     fs->upstream->buffers.current = NULL;
219*0Sigor@sysoev.ru 
220*0Sigor@sysoev.ru     *prev = b;
221*0Sigor@sysoev.ru     prev = &b->next;
222*0Sigor@sysoev.ru 
223*0Sigor@sysoev.ru new_record:
224*0Sigor@sysoev.ru 
225*0Sigor@sysoev.ru     size = b->mem.end - b->mem.free;
226*0Sigor@sysoev.ru     size = nxt_align_size(size, 8) - 8;
227*0Sigor@sysoev.ru     /* The maximal FastCGI record content size is 65535.  65528 is 64K - 8. */
228*0Sigor@sysoev.ru     max_record_size = nxt_min(65528, size);
229*0Sigor@sysoev.ru 
230*0Sigor@sysoev.ru     p = b->mem.free;
231*0Sigor@sysoev.ru 
232*0Sigor@sysoev.ru     if (begin_request) {
233*0Sigor@sysoev.ru         /* TODO: fastcgi keep conn in flags. */
234*0Sigor@sysoev.ru         p = nxt_cpymem(p, nxt_fastcgi_begin_request, 16);
235*0Sigor@sysoev.ru         max_record_size -= 16;
236*0Sigor@sysoev.ru         begin_request = 0;
237*0Sigor@sysoev.ru     }
238*0Sigor@sysoev.ru 
239*0Sigor@sysoev.ru     b->mem.free = nxt_cpymem(p, nxt_fastcgi_params_record, 8);
240*0Sigor@sysoev.ru     record_length = &p[4];
241*0Sigor@sysoev.ru     size = 0;
242*0Sigor@sysoev.ru 
243*0Sigor@sysoev.ru     for ( ;; ) {
244*0Sigor@sysoev.ru         if (param.len == 0) {
245*0Sigor@sysoev.ru             ret = nxt_fastcgi_next_param(fs, &param);
246*0Sigor@sysoev.ru 
247*0Sigor@sysoev.ru             if (nxt_slow_path(ret != NXT_OK)) {
248*0Sigor@sysoev.ru 
249*0Sigor@sysoev.ru                 if (nxt_slow_path(ret == NXT_ERROR)) {
250*0Sigor@sysoev.ru                     return NULL;
251*0Sigor@sysoev.ru                 }
252*0Sigor@sysoev.ru 
253*0Sigor@sysoev.ru                 /* ret == NXT_DONE */
254*0Sigor@sysoev.ru                 break;
255*0Sigor@sysoev.ru             }
256*0Sigor@sysoev.ru         }
257*0Sigor@sysoev.ru 
258*0Sigor@sysoev.ru         len = max_record_size;
259*0Sigor@sysoev.ru 
260*0Sigor@sysoev.ru         if (nxt_fast_path(len >= param.len)) {
261*0Sigor@sysoev.ru             len = param.len;
262*0Sigor@sysoev.ru             param.len = 0;
263*0Sigor@sysoev.ru 
264*0Sigor@sysoev.ru         } else {
265*0Sigor@sysoev.ru             param.len -= len;
266*0Sigor@sysoev.ru         }
267*0Sigor@sysoev.ru 
268*0Sigor@sysoev.ru         nxt_thread_log_debug("fastcgi copy len:%uz", len);
269*0Sigor@sysoev.ru 
270*0Sigor@sysoev.ru         b->mem.free = nxt_cpymem(b->mem.free, param.buf, len);
271*0Sigor@sysoev.ru 
272*0Sigor@sysoev.ru         size += len;
273*0Sigor@sysoev.ru         max_record_size -= len;
274*0Sigor@sysoev.ru 
275*0Sigor@sysoev.ru         if (nxt_slow_path(param.len != 0)) {
276*0Sigor@sysoev.ru             /* The record is full. */
277*0Sigor@sysoev.ru 
278*0Sigor@sysoev.ru             param.buf += len;
279*0Sigor@sysoev.ru 
280*0Sigor@sysoev.ru             nxt_thread_log_debug("fastcgi content size:%uz", size);
281*0Sigor@sysoev.ru 
282*0Sigor@sysoev.ru             nxt_fastcgi_set_record_length(record_length, size);
283*0Sigor@sysoev.ru 
284*0Sigor@sysoev.ru             /* The minimal size of aligned record with content is 16 bytes. */
285*0Sigor@sysoev.ru             if (b->mem.end - b->mem.free >= 16) {
286*0Sigor@sysoev.ru                 goto new_record;
287*0Sigor@sysoev.ru             }
288*0Sigor@sysoev.ru 
289*0Sigor@sysoev.ru             nxt_thread_log_debug("\"%*s\"", b->mem.free - b->mem.pos,
290*0Sigor@sysoev.ru                                  b->mem.pos);
291*0Sigor@sysoev.ru             goto new_buffer;
292*0Sigor@sysoev.ru         }
293*0Sigor@sysoev.ru     }
294*0Sigor@sysoev.ru 
295*0Sigor@sysoev.ru     nxt_thread_log_debug("fastcgi content size:%uz", size);
296*0Sigor@sysoev.ru 
297*0Sigor@sysoev.ru     nxt_fastcgi_set_record_length(record_length, size);
298*0Sigor@sysoev.ru 
299*0Sigor@sysoev.ru     /* A padding length. */
300*0Sigor@sysoev.ru     size = 8 - size % 8;
301*0Sigor@sysoev.ru     record_length[2] = (u_char) size;
302*0Sigor@sysoev.ru     nxt_memzero(b->mem.free, size);
303*0Sigor@sysoev.ru     b->mem.free += size;
304*0Sigor@sysoev.ru 
305*0Sigor@sysoev.ru     nxt_thread_log_debug("fastcgi padding:%uz", size);
306*0Sigor@sysoev.ru 
307*0Sigor@sysoev.ru     if (b->mem.end - b->mem.free < 16) {
308*0Sigor@sysoev.ru         nxt_thread_log_debug("\"%*s\"", b->mem.free - b->mem.pos, b->mem.pos);
309*0Sigor@sysoev.ru 
310*0Sigor@sysoev.ru         b = nxt_buf_mem_alloc(fs->upstream->buffers.mem_pool, 16, 0);
311*0Sigor@sysoev.ru         if (nxt_slow_path(b == NULL)) {
312*0Sigor@sysoev.ru             return NULL;
313*0Sigor@sysoev.ru         }
314*0Sigor@sysoev.ru 
315*0Sigor@sysoev.ru         *prev = b;
316*0Sigor@sysoev.ru         prev = &b->next;
317*0Sigor@sysoev.ru     }
318*0Sigor@sysoev.ru 
319*0Sigor@sysoev.ru     /* The end of FastCGI params. */
320*0Sigor@sysoev.ru     p = nxt_cpymem(b->mem.free, nxt_fastcgi_params_record, 8);
321*0Sigor@sysoev.ru 
322*0Sigor@sysoev.ru     /* The end of FastCGI stdin. */
323*0Sigor@sysoev.ru     b->mem.free = nxt_cpymem(p, nxt_fastcgi_stdin_record, 8);
324*0Sigor@sysoev.ru 
325*0Sigor@sysoev.ru     nxt_thread_log_debug("\"%*s\"", b->mem.free - b->mem.pos, b->mem.pos);
326*0Sigor@sysoev.ru 
327*0Sigor@sysoev.ru     return req;
328*0Sigor@sysoev.ru }
329*0Sigor@sysoev.ru 
330*0Sigor@sysoev.ru 
331*0Sigor@sysoev.ru static nxt_int_t
332*0Sigor@sysoev.ru nxt_fastcgi_next_param(nxt_fastcgi_source_t *fs, nxt_fastcgi_param_t *param)
333*0Sigor@sysoev.ru {
334*0Sigor@sysoev.ru     nxt_int_t  ret;
335*0Sigor@sysoev.ru 
336*0Sigor@sysoev.ru     enum {
337*0Sigor@sysoev.ru          sw_name_length = 0,
338*0Sigor@sysoev.ru          sw_value_length,
339*0Sigor@sysoev.ru          sw_name,
340*0Sigor@sysoev.ru          sw_value,
341*0Sigor@sysoev.ru     };
342*0Sigor@sysoev.ru 
343*0Sigor@sysoev.ru     switch (fs->state) {
344*0Sigor@sysoev.ru 
345*0Sigor@sysoev.ru     case sw_name_length:
346*0Sigor@sysoev.ru         ret = fs->request_create(fs);
347*0Sigor@sysoev.ru 
348*0Sigor@sysoev.ru         if (nxt_slow_path(ret != NXT_OK)) {
349*0Sigor@sysoev.ru             return ret;
350*0Sigor@sysoev.ru         }
351*0Sigor@sysoev.ru 
352*0Sigor@sysoev.ru         nxt_thread_log_debug("fastcgi param \"%V: %V\"",
353*0Sigor@sysoev.ru                              &fs->u.request.name, &fs->u.request.value);
354*0Sigor@sysoev.ru 
355*0Sigor@sysoev.ru         fs->state = sw_value_length;
356*0Sigor@sysoev.ru         param->buf = param->length;
357*0Sigor@sysoev.ru         param->len = nxt_fastcgi_param_length(param->length,
358*0Sigor@sysoev.ru                                               fs->u.request.name.len);
359*0Sigor@sysoev.ru         break;
360*0Sigor@sysoev.ru 
361*0Sigor@sysoev.ru     case sw_value_length:
362*0Sigor@sysoev.ru         fs->state = sw_name;
363*0Sigor@sysoev.ru         param->buf = param->length;
364*0Sigor@sysoev.ru         param->len = nxt_fastcgi_param_length(param->length,
365*0Sigor@sysoev.ru                                               fs->u.request.value.len);
366*0Sigor@sysoev.ru         break;
367*0Sigor@sysoev.ru 
368*0Sigor@sysoev.ru     case sw_name:
369*0Sigor@sysoev.ru         fs->state = sw_value;
370*0Sigor@sysoev.ru         param->buf = fs->u.request.name.data;
371*0Sigor@sysoev.ru         param->len = fs->u.request.name.len;
372*0Sigor@sysoev.ru         break;
373*0Sigor@sysoev.ru 
374*0Sigor@sysoev.ru     case sw_value:
375*0Sigor@sysoev.ru         fs->state = sw_name_length;
376*0Sigor@sysoev.ru         param->buf = fs->u.request.value.data;
377*0Sigor@sysoev.ru         param->len = fs->u.request.value.len;
378*0Sigor@sysoev.ru         break;
379*0Sigor@sysoev.ru     }
380*0Sigor@sysoev.ru 
381*0Sigor@sysoev.ru     return NXT_OK;
382*0Sigor@sysoev.ru }
383*0Sigor@sysoev.ru 
384*0Sigor@sysoev.ru 
385*0Sigor@sysoev.ru static void
386*0Sigor@sysoev.ru nxt_fastcgi_source_record_filter(nxt_thread_t *thr, void *obj, void *data)
387*0Sigor@sysoev.ru {
388*0Sigor@sysoev.ru     size_t                       size;
389*0Sigor@sysoev.ru     u_char                       *p;
390*0Sigor@sysoev.ru     nxt_buf_t                    *b, *in;
391*0Sigor@sysoev.ru     nxt_fastcgi_source_t         *fs;
392*0Sigor@sysoev.ru     nxt_fastcgi_source_record_t  *fsr;
393*0Sigor@sysoev.ru 
394*0Sigor@sysoev.ru     fsr = obj;
395*0Sigor@sysoev.ru     in = data;
396*0Sigor@sysoev.ru 
397*0Sigor@sysoev.ru     nxt_log_debug(thr->log, "fastcgi source record filter");
398*0Sigor@sysoev.ru 
399*0Sigor@sysoev.ru     if (nxt_slow_path(fsr->parse.done)) {
400*0Sigor@sysoev.ru         return;
401*0Sigor@sysoev.ru     }
402*0Sigor@sysoev.ru 
403*0Sigor@sysoev.ru     nxt_fastcgi_record_parse(&fsr->parse, in);
404*0Sigor@sysoev.ru 
405*0Sigor@sysoev.ru     fs = nxt_container_of(fsr, nxt_fastcgi_source_t, record);
406*0Sigor@sysoev.ru 
407*0Sigor@sysoev.ru     if (fsr->parse.error) {
408*0Sigor@sysoev.ru         nxt_fastcgi_source_fail(fs);
409*0Sigor@sysoev.ru         return;
410*0Sigor@sysoev.ru     }
411*0Sigor@sysoev.ru 
412*0Sigor@sysoev.ru     if (fsr->parse.fastcgi_error) {
413*0Sigor@sysoev.ru         /*
414*0Sigor@sysoev.ru          * Output all parsed before a FastCGI record error and close upstream.
415*0Sigor@sysoev.ru          */
416*0Sigor@sysoev.ru         nxt_thread_current_work_queue_add(thr, nxt_fastcgi_source_record_error,
417*0Sigor@sysoev.ru                                           fs, NULL, thr->log);
418*0Sigor@sysoev.ru     }
419*0Sigor@sysoev.ru 
420*0Sigor@sysoev.ru     /* Log FastCGI stderr output. */
421*0Sigor@sysoev.ru 
422*0Sigor@sysoev.ru     for (b = fsr->parse.out[1]; b != NULL; b = b->next) {
423*0Sigor@sysoev.ru 
424*0Sigor@sysoev.ru         for (p = b->mem.free - 1; p >= b->mem.pos; p--) {
425*0Sigor@sysoev.ru             if (*p != NXT_CR && *p != NXT_LF) {
426*0Sigor@sysoev.ru                 break;
427*0Sigor@sysoev.ru             }
428*0Sigor@sysoev.ru         }
429*0Sigor@sysoev.ru 
430*0Sigor@sysoev.ru         size = (p + 1) - b->mem.pos;
431*0Sigor@sysoev.ru 
432*0Sigor@sysoev.ru         if (size != 0) {
433*0Sigor@sysoev.ru             nxt_log_error(NXT_LOG_ERR, thr->log,
434*0Sigor@sysoev.ru                           "upstream sent in FastCGI stderr: \"%*s\"",
435*0Sigor@sysoev.ru                           size, b->mem.pos);
436*0Sigor@sysoev.ru         }
437*0Sigor@sysoev.ru 
438*0Sigor@sysoev.ru         b->completion_handler(thr, b, b->parent);
439*0Sigor@sysoev.ru     }
440*0Sigor@sysoev.ru 
441*0Sigor@sysoev.ru     /* Process FastCGI stdout output. */
442*0Sigor@sysoev.ru 
443*0Sigor@sysoev.ru     if (fsr->parse.out[0] != NULL) {
444*0Sigor@sysoev.ru         nxt_source_filter(thr, fs->upstream->work_queue, &fsr->next,
445*0Sigor@sysoev.ru                           fsr->parse.out[0]);
446*0Sigor@sysoev.ru     }
447*0Sigor@sysoev.ru }
448*0Sigor@sysoev.ru 
449*0Sigor@sysoev.ru 
450*0Sigor@sysoev.ru static void
451*0Sigor@sysoev.ru nxt_fastcgi_source_record_error(nxt_thread_t *thr, void *obj, void *data)
452*0Sigor@sysoev.ru {
453*0Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
454*0Sigor@sysoev.ru 
455*0Sigor@sysoev.ru     fs = obj;
456*0Sigor@sysoev.ru 
457*0Sigor@sysoev.ru     nxt_fastcgi_source_fail(fs);
458*0Sigor@sysoev.ru }
459*0Sigor@sysoev.ru 
460*0Sigor@sysoev.ru 
461*0Sigor@sysoev.ru static void
462*0Sigor@sysoev.ru nxt_fastcgi_source_header_filter(nxt_thread_t *thr, void *obj, void *data)
463*0Sigor@sysoev.ru {
464*0Sigor@sysoev.ru     nxt_int_t             ret;
465*0Sigor@sysoev.ru     nxt_buf_t             *b;
466*0Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
467*0Sigor@sysoev.ru 
468*0Sigor@sysoev.ru     fs = obj;
469*0Sigor@sysoev.ru     b = data;
470*0Sigor@sysoev.ru 
471*0Sigor@sysoev.ru     do {
472*0Sigor@sysoev.ru         nxt_log_debug(thr->log, "fastcgi source header filter");
473*0Sigor@sysoev.ru 
474*0Sigor@sysoev.ru         if (nxt_slow_path(nxt_buf_is_sync(b))) {
475*0Sigor@sysoev.ru             nxt_fastcgi_source_sync_buffer(thr, fs, b);
476*0Sigor@sysoev.ru             return;
477*0Sigor@sysoev.ru         }
478*0Sigor@sysoev.ru 
479*0Sigor@sysoev.ru         for ( ;; ) {
480*0Sigor@sysoev.ru             ret = nxt_http_split_header_parse(&fs->u.header, &b->mem);
481*0Sigor@sysoev.ru 
482*0Sigor@sysoev.ru             if (nxt_slow_path(ret != NXT_OK)) {
483*0Sigor@sysoev.ru                 break;
484*0Sigor@sysoev.ru             }
485*0Sigor@sysoev.ru 
486*0Sigor@sysoev.ru             ret = nxt_fastcgi_source_header_process(fs);
487*0Sigor@sysoev.ru 
488*0Sigor@sysoev.ru             if (nxt_slow_path(ret != NXT_OK)) {
489*0Sigor@sysoev.ru                 break;
490*0Sigor@sysoev.ru             }
491*0Sigor@sysoev.ru         }
492*0Sigor@sysoev.ru 
493*0Sigor@sysoev.ru         if (nxt_fast_path(ret == NXT_DONE)) {
494*0Sigor@sysoev.ru             nxt_log_debug(thr->log, "fastcgi source header done");
495*0Sigor@sysoev.ru             nxt_fastcgi_source_header_ready(fs, b);
496*0Sigor@sysoev.ru             return;
497*0Sigor@sysoev.ru         }
498*0Sigor@sysoev.ru 
499*0Sigor@sysoev.ru         if (nxt_fast_path(ret != NXT_AGAIN)) {
500*0Sigor@sysoev.ru 
501*0Sigor@sysoev.ru             if (ret != NXT_ERROR) {
502*0Sigor@sysoev.ru                 /* n == NXT_DECLINED: "\r" is not followed by "\n" */
503*0Sigor@sysoev.ru                 nxt_log_error(NXT_LOG_ERR, thr->log,
504*0Sigor@sysoev.ru                            "upstream sent invalid header line: \"%*s\\r...\"",
505*0Sigor@sysoev.ru                            fs->u.header.parse.header_end
506*0Sigor@sysoev.ru                                - fs->u.header.parse.header_name_start,
507*0Sigor@sysoev.ru                            fs->u.header.parse.header_name_start);
508*0Sigor@sysoev.ru             }
509*0Sigor@sysoev.ru 
510*0Sigor@sysoev.ru             /* ret == NXT_ERROR */
511*0Sigor@sysoev.ru 
512*0Sigor@sysoev.ru             nxt_fastcgi_source_fail(fs);
513*0Sigor@sysoev.ru             return;
514*0Sigor@sysoev.ru         }
515*0Sigor@sysoev.ru 
516*0Sigor@sysoev.ru         b = b->next;
517*0Sigor@sysoev.ru 
518*0Sigor@sysoev.ru     } while (b != NULL);
519*0Sigor@sysoev.ru }
520*0Sigor@sysoev.ru 
521*0Sigor@sysoev.ru 
522*0Sigor@sysoev.ru static void
523*0Sigor@sysoev.ru nxt_fastcgi_source_sync_buffer(nxt_thread_t *thr, nxt_fastcgi_source_t *fs,
524*0Sigor@sysoev.ru     nxt_buf_t *b)
525*0Sigor@sysoev.ru {
526*0Sigor@sysoev.ru     if (nxt_buf_is_last(b)) {
527*0Sigor@sysoev.ru         nxt_log_error(NXT_LOG_ERR, thr->log,
528*0Sigor@sysoev.ru                       "upstream closed prematurely connection");
529*0Sigor@sysoev.ru 
530*0Sigor@sysoev.ru     } else {
531*0Sigor@sysoev.ru         nxt_log_error(NXT_LOG_ERR, thr->log, "%ui buffers %uz each are not "
532*0Sigor@sysoev.ru                       "enough to process upstream response header",
533*0Sigor@sysoev.ru                       fs->upstream->buffers.max,
534*0Sigor@sysoev.ru                       fs->upstream->buffers.size);
535*0Sigor@sysoev.ru     }
536*0Sigor@sysoev.ru 
537*0Sigor@sysoev.ru     /* The stream source sends only the last and the nobuf sync buffer. */
538*0Sigor@sysoev.ru 
539*0Sigor@sysoev.ru     nxt_fastcgi_source_fail(fs);
540*0Sigor@sysoev.ru }
541*0Sigor@sysoev.ru 
542*0Sigor@sysoev.ru 
543*0Sigor@sysoev.ru static nxt_int_t
544*0Sigor@sysoev.ru nxt_fastcgi_source_header_process(nxt_fastcgi_source_t *fs)
545*0Sigor@sysoev.ru {
546*0Sigor@sysoev.ru     size_t                     len;
547*0Sigor@sysoev.ru     nxt_thread_t               *thr;
548*0Sigor@sysoev.ru     nxt_name_value_t           *nv;
549*0Sigor@sysoev.ru     nxt_lvlhsh_query_t         lhq;
550*0Sigor@sysoev.ru     nxt_http_header_parse_t    *hp;
551*0Sigor@sysoev.ru     nxt_upstream_name_value_t  *unv;
552*0Sigor@sysoev.ru 
553*0Sigor@sysoev.ru     thr = nxt_thread();
554*0Sigor@sysoev.ru     hp = &fs->u.header.parse;
555*0Sigor@sysoev.ru 
556*0Sigor@sysoev.ru     len = hp->header_name_end - hp->header_name_start;
557*0Sigor@sysoev.ru 
558*0Sigor@sysoev.ru     if (len > 255) {
559*0Sigor@sysoev.ru         nxt_log_error(NXT_LOG_INFO, thr->log,
560*0Sigor@sysoev.ru                       "upstream sent too long header field name: \"%*s\"",
561*0Sigor@sysoev.ru                       len, hp->header_name_start);
562*0Sigor@sysoev.ru         return NXT_ERROR;
563*0Sigor@sysoev.ru     }
564*0Sigor@sysoev.ru 
565*0Sigor@sysoev.ru     nv = nxt_list_add(fs->header_in.list);
566*0Sigor@sysoev.ru     if (nxt_slow_path(nv == NULL)) {
567*0Sigor@sysoev.ru         return NXT_ERROR;
568*0Sigor@sysoev.ru     }
569*0Sigor@sysoev.ru 
570*0Sigor@sysoev.ru     nv->hash = hp->header_hash;
571*0Sigor@sysoev.ru     nv->skip = 0;
572*0Sigor@sysoev.ru     nv->name_len = len;
573*0Sigor@sysoev.ru     nv->name_start = hp->header_name_start;
574*0Sigor@sysoev.ru     nv->value_len = hp->header_end - hp->header_start;
575*0Sigor@sysoev.ru     nv->value_start = hp->header_start;
576*0Sigor@sysoev.ru 
577*0Sigor@sysoev.ru     nxt_log_debug(thr->log, "http header: \"%*s: %*s\"",
578*0Sigor@sysoev.ru                   nv->name_len, nv->name_start, nv->value_len, nv->value_start);
579*0Sigor@sysoev.ru 
580*0Sigor@sysoev.ru     lhq.key_hash = nv->hash;
581*0Sigor@sysoev.ru     lhq.key.len = nv->name_len;
582*0Sigor@sysoev.ru     lhq.key.data = nv->name_start;
583*0Sigor@sysoev.ru     lhq.proto = &nxt_upstream_header_hash_proto;
584*0Sigor@sysoev.ru 
585*0Sigor@sysoev.ru     if (nxt_lvlhsh_find(&fs->header_in.hash, &lhq) == NXT_OK) {
586*0Sigor@sysoev.ru         unv = lhq.value;
587*0Sigor@sysoev.ru 
588*0Sigor@sysoev.ru         if (unv->handler(fs->upstream, nv) == NXT_OK) {
589*0Sigor@sysoev.ru             return NXT_ERROR;
590*0Sigor@sysoev.ru         }
591*0Sigor@sysoev.ru     }
592*0Sigor@sysoev.ru 
593*0Sigor@sysoev.ru     return NXT_OK;
594*0Sigor@sysoev.ru }
595*0Sigor@sysoev.ru 
596*0Sigor@sysoev.ru 
597*0Sigor@sysoev.ru static const nxt_upstream_name_value_t  nxt_fastcgi_source_headers[]
598*0Sigor@sysoev.ru     nxt_aligned(32) =
599*0Sigor@sysoev.ru {
600*0Sigor@sysoev.ru     { nxt_fastcgi_source_status,
601*0Sigor@sysoev.ru       nxt_upstream_name_value("status") },
602*0Sigor@sysoev.ru 
603*0Sigor@sysoev.ru     { nxt_fastcgi_source_content_length,
604*0Sigor@sysoev.ru       nxt_upstream_name_value("content-length") },
605*0Sigor@sysoev.ru };
606*0Sigor@sysoev.ru 
607*0Sigor@sysoev.ru 
608*0Sigor@sysoev.ru nxt_int_t
609*0Sigor@sysoev.ru nxt_fastcgi_source_hash_create(nxt_mem_pool_t *mp, nxt_lvlhsh_t *lh)
610*0Sigor@sysoev.ru {
611*0Sigor@sysoev.ru     return nxt_upstream_header_hash_add(mp, lh, nxt_fastcgi_source_headers,
612*0Sigor@sysoev.ru                                         nxt_nitems(nxt_fastcgi_source_headers));
613*0Sigor@sysoev.ru }
614*0Sigor@sysoev.ru 
615*0Sigor@sysoev.ru 
616*0Sigor@sysoev.ru static nxt_int_t
617*0Sigor@sysoev.ru nxt_fastcgi_source_status(nxt_upstream_source_t *us, nxt_name_value_t *nv)
618*0Sigor@sysoev.ru {
619*0Sigor@sysoev.ru     nxt_int_t             n;
620*0Sigor@sysoev.ru     nxt_str_t             s;
621*0Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
622*0Sigor@sysoev.ru 
623*0Sigor@sysoev.ru     s.len = nv->value_len;
624*0Sigor@sysoev.ru     s.data = nv->value_start;
625*0Sigor@sysoev.ru 
626*0Sigor@sysoev.ru     n = nxt_str_int_parse(&s);
627*0Sigor@sysoev.ru 
628*0Sigor@sysoev.ru     if (nxt_fast_path(n > 0)) {
629*0Sigor@sysoev.ru         fs = us->protocol_source;
630*0Sigor@sysoev.ru         fs->header_in.status = n;
631*0Sigor@sysoev.ru         return NXT_OK;
632*0Sigor@sysoev.ru     }
633*0Sigor@sysoev.ru 
634*0Sigor@sysoev.ru     return NXT_ERROR;
635*0Sigor@sysoev.ru }
636*0Sigor@sysoev.ru 
637*0Sigor@sysoev.ru 
638*0Sigor@sysoev.ru static nxt_int_t
639*0Sigor@sysoev.ru nxt_fastcgi_source_content_length(nxt_upstream_source_t *us,
640*0Sigor@sysoev.ru     nxt_name_value_t *nv)
641*0Sigor@sysoev.ru {
642*0Sigor@sysoev.ru     nxt_off_t             length;
643*0Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
644*0Sigor@sysoev.ru 
645*0Sigor@sysoev.ru     length = nxt_off_t_parse(nv->value_start, nv->value_len);
646*0Sigor@sysoev.ru 
647*0Sigor@sysoev.ru     if (nxt_fast_path(length > 0)) {
648*0Sigor@sysoev.ru         fs = us->protocol_source;
649*0Sigor@sysoev.ru         fs->header_in.content_length = length;
650*0Sigor@sysoev.ru         return NXT_OK;
651*0Sigor@sysoev.ru     }
652*0Sigor@sysoev.ru 
653*0Sigor@sysoev.ru     return NXT_ERROR;
654*0Sigor@sysoev.ru }
655*0Sigor@sysoev.ru 
656*0Sigor@sysoev.ru 
657*0Sigor@sysoev.ru static void
658*0Sigor@sysoev.ru nxt_fastcgi_source_header_ready(nxt_fastcgi_source_t *fs, nxt_buf_t *b)
659*0Sigor@sysoev.ru {
660*0Sigor@sysoev.ru     /*
661*0Sigor@sysoev.ru      * Change the FastCGI source filter chain:
662*0Sigor@sysoev.ru      *   stream source | FastCGI record filter | FastCGI body filter
663*0Sigor@sysoev.ru      */
664*0Sigor@sysoev.ru     fs->record.next.filter = nxt_fastcgi_source_body_filter;
665*0Sigor@sysoev.ru 
666*0Sigor@sysoev.ru     if (nxt_buf_mem_used_size(&b->mem) != 0) {
667*0Sigor@sysoev.ru         fs->rest = b;
668*0Sigor@sysoev.ru     }
669*0Sigor@sysoev.ru 
670*0Sigor@sysoev.ru     if (fs->header_in.status == 0) {
671*0Sigor@sysoev.ru         /* The "200 OK" status by default. */
672*0Sigor@sysoev.ru         fs->header_in.status = 200;
673*0Sigor@sysoev.ru     }
674*0Sigor@sysoev.ru 
675*0Sigor@sysoev.ru     fs->upstream->state->ready_handler(fs);
676*0Sigor@sysoev.ru }
677*0Sigor@sysoev.ru 
678*0Sigor@sysoev.ru 
679*0Sigor@sysoev.ru /*
680*0Sigor@sysoev.ru  * The FastCGI source body filter accumulates first body buffers before the next
681*0Sigor@sysoev.ru  * filter will be established and sets completion handler for the last buffer.
682*0Sigor@sysoev.ru  */
683*0Sigor@sysoev.ru 
684*0Sigor@sysoev.ru static void
685*0Sigor@sysoev.ru nxt_fastcgi_source_body_filter(nxt_thread_t *thr, void *obj, void *data)
686*0Sigor@sysoev.ru {
687*0Sigor@sysoev.ru     nxt_buf_t             *b, *in;
688*0Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
689*0Sigor@sysoev.ru 
690*0Sigor@sysoev.ru     fs = obj;
691*0Sigor@sysoev.ru     in = data;
692*0Sigor@sysoev.ru 
693*0Sigor@sysoev.ru     nxt_log_debug(thr->log, "fastcgi source body filter");
694*0Sigor@sysoev.ru 
695*0Sigor@sysoev.ru     for (b = in; b != NULL; b = b->next) {
696*0Sigor@sysoev.ru 
697*0Sigor@sysoev.ru         if (nxt_buf_is_last(b)) {
698*0Sigor@sysoev.ru             b->data = fs->upstream->data;
699*0Sigor@sysoev.ru             b->completion_handler = fs->upstream->state->completion_handler;
700*0Sigor@sysoev.ru         }
701*0Sigor@sysoev.ru     }
702*0Sigor@sysoev.ru 
703*0Sigor@sysoev.ru     if (fs->next != NULL) {
704*0Sigor@sysoev.ru         nxt_source_filter(thr, fs->upstream->work_queue, fs->next, in);
705*0Sigor@sysoev.ru         return;
706*0Sigor@sysoev.ru     }
707*0Sigor@sysoev.ru 
708*0Sigor@sysoev.ru     nxt_buf_chain_add(&fs->rest, in);
709*0Sigor@sysoev.ru }
710*0Sigor@sysoev.ru 
711*0Sigor@sysoev.ru 
712*0Sigor@sysoev.ru static nxt_buf_t *
713*0Sigor@sysoev.ru nxt_fastcgi_source_last_buf(nxt_fastcgi_parse_t *fp)
714*0Sigor@sysoev.ru {
715*0Sigor@sysoev.ru     nxt_buf_t             *b;
716*0Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
717*0Sigor@sysoev.ru 
718*0Sigor@sysoev.ru     fs = fp->data;
719*0Sigor@sysoev.ru 
720*0Sigor@sysoev.ru     b = nxt_buf_sync_alloc(fp->mem_pool, NXT_BUF_SYNC_LAST);
721*0Sigor@sysoev.ru 
722*0Sigor@sysoev.ru     if (nxt_fast_path(b != NULL)) {
723*0Sigor@sysoev.ru         b->data = fs->upstream->data;
724*0Sigor@sysoev.ru         b->completion_handler = fs->upstream->state->completion_handler;
725*0Sigor@sysoev.ru     }
726*0Sigor@sysoev.ru 
727*0Sigor@sysoev.ru     return b;
728*0Sigor@sysoev.ru }
729*0Sigor@sysoev.ru 
730*0Sigor@sysoev.ru 
731*0Sigor@sysoev.ru static void
732*0Sigor@sysoev.ru nxt_fastcgi_source_error(nxt_stream_source_t *stream)
733*0Sigor@sysoev.ru {
734*0Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
735*0Sigor@sysoev.ru 
736*0Sigor@sysoev.ru     nxt_thread_log_debug("fastcgi source error");
737*0Sigor@sysoev.ru 
738*0Sigor@sysoev.ru     fs = stream->upstream->protocol_source;
739*0Sigor@sysoev.ru 
740*0Sigor@sysoev.ru     nxt_fastcgi_source_fail(fs);
741*0Sigor@sysoev.ru }
742*0Sigor@sysoev.ru 
743*0Sigor@sysoev.ru 
744*0Sigor@sysoev.ru static void
745*0Sigor@sysoev.ru nxt_fastcgi_source_fail(nxt_fastcgi_source_t *fs)
746*0Sigor@sysoev.ru {
747*0Sigor@sysoev.ru     nxt_thread_t  *thr;
748*0Sigor@sysoev.ru 
749*0Sigor@sysoev.ru     thr = nxt_thread();
750*0Sigor@sysoev.ru 
751*0Sigor@sysoev.ru     nxt_log_debug(thr->log, "fastcgi source fail");
752*0Sigor@sysoev.ru 
753*0Sigor@sysoev.ru     /* TODO: fail, next upstream, or bad gateway */
754*0Sigor@sysoev.ru 
755*0Sigor@sysoev.ru     fs->upstream->state->error_handler(thr, fs, NULL);
756*0Sigor@sysoev.ru }
757