10Sigor@sysoev.ru 
20Sigor@sysoev.ru /*
30Sigor@sysoev.ru  * Copyright (C) Igor Sysoev
40Sigor@sysoev.ru  * Copyright (C) NGINX, Inc.
50Sigor@sysoev.ru  */
60Sigor@sysoev.ru 
70Sigor@sysoev.ru #include <nxt_main.h>
80Sigor@sysoev.ru 
90Sigor@sysoev.ru 
100Sigor@sysoev.ru #define NXT_FASTCGI_RESPONDER  1
110Sigor@sysoev.ru #define NXT_FASTCGI_KEEP_CONN  1
120Sigor@sysoev.ru 
130Sigor@sysoev.ru 
140Sigor@sysoev.ru typedef struct {
150Sigor@sysoev.ru     u_char    *buf;
160Sigor@sysoev.ru     uint32_t  len;
170Sigor@sysoev.ru     u_char    length[4];
180Sigor@sysoev.ru } nxt_fastcgi_param_t;
190Sigor@sysoev.ru 
200Sigor@sysoev.ru 
210Sigor@sysoev.ru #define                                                                       \
220Sigor@sysoev.ru nxt_fastcgi_set_record_length(p, length)                                      \
230Sigor@sysoev.ru     do {                                                                      \
240Sigor@sysoev.ru         uint32_t  len = length;                                               \
250Sigor@sysoev.ru                                                                               \
260Sigor@sysoev.ru         p[1] = (u_char) len;  len >>= 8;                                      \
270Sigor@sysoev.ru         p[0] = (u_char) len;                                                  \
280Sigor@sysoev.ru     } while (0)
290Sigor@sysoev.ru 
300Sigor@sysoev.ru 
310Sigor@sysoev.ru nxt_inline size_t
320Sigor@sysoev.ru nxt_fastcgi_param_length(u_char *p, uint32_t length)
330Sigor@sysoev.ru {
340Sigor@sysoev.ru     if (nxt_fast_path(length < 128)) {
350Sigor@sysoev.ru         *p = (u_char) length;
360Sigor@sysoev.ru         return 1;
370Sigor@sysoev.ru     }
380Sigor@sysoev.ru 
390Sigor@sysoev.ru     p[3] = (u_char) length;  length >>= 8;
400Sigor@sysoev.ru     p[2] = (u_char) length;  length >>= 8;
410Sigor@sysoev.ru     p[1] = (u_char) length;  length >>= 8;
420Sigor@sysoev.ru     p[0] = (u_char) (length | 0x80);
430Sigor@sysoev.ru 
440Sigor@sysoev.ru     return 4;
450Sigor@sysoev.ru }
460Sigor@sysoev.ru 
470Sigor@sysoev.ru 
480Sigor@sysoev.ru static nxt_buf_t *nxt_fastcgi_request_create(nxt_fastcgi_source_t *fs);
490Sigor@sysoev.ru static nxt_int_t nxt_fastcgi_next_param(nxt_fastcgi_source_t *fs,
500Sigor@sysoev.ru     nxt_fastcgi_param_t *param);
510Sigor@sysoev.ru 
52*1Sigor@sysoev.ru static void nxt_fastcgi_source_record_filter(nxt_task_t *task, void *obj,
530Sigor@sysoev.ru     void *data);
54*1Sigor@sysoev.ru static void nxt_fastcgi_source_record_error(nxt_task_t *task, void *obj,
550Sigor@sysoev.ru     void *data);
56*1Sigor@sysoev.ru static void nxt_fastcgi_source_header_filter(nxt_task_t *task, void *obj,
570Sigor@sysoev.ru     void *data);
58*1Sigor@sysoev.ru static void nxt_fastcgi_source_sync_buffer(nxt_task_t *task,
590Sigor@sysoev.ru     nxt_fastcgi_source_t *fs, nxt_buf_t *b);
600Sigor@sysoev.ru 
61*1Sigor@sysoev.ru static nxt_int_t nxt_fastcgi_source_header_process(nxt_task_t *task,
62*1Sigor@sysoev.ru     nxt_fastcgi_source_t *fs);
630Sigor@sysoev.ru static nxt_int_t nxt_fastcgi_source_status(nxt_upstream_source_t *us,
640Sigor@sysoev.ru     nxt_name_value_t *nv);
650Sigor@sysoev.ru static nxt_int_t nxt_fastcgi_source_content_length(nxt_upstream_source_t *us,
660Sigor@sysoev.ru     nxt_name_value_t *nv);
670Sigor@sysoev.ru 
680Sigor@sysoev.ru static void nxt_fastcgi_source_header_ready(nxt_fastcgi_source_t *fs,
690Sigor@sysoev.ru     nxt_buf_t *b);
70*1Sigor@sysoev.ru static void nxt_fastcgi_source_body_filter(nxt_task_t *task, void *obj,
710Sigor@sysoev.ru     void *data);
720Sigor@sysoev.ru static nxt_buf_t *nxt_fastcgi_source_last_buf(nxt_fastcgi_parse_t *fp);
73*1Sigor@sysoev.ru static void nxt_fastcgi_source_error(nxt_task_t *task,
74*1Sigor@sysoev.ru     nxt_stream_source_t *stream);
75*1Sigor@sysoev.ru static void nxt_fastcgi_source_fail(nxt_task_t *task, nxt_fastcgi_source_t *fs);
760Sigor@sysoev.ru 
770Sigor@sysoev.ru 
780Sigor@sysoev.ru /*
790Sigor@sysoev.ru  * A FastCGI request:
800Sigor@sysoev.ru  *   FCGI_BEGIN_REQUEST record;
810Sigor@sysoev.ru  *   Several FCGI_PARAMS records, the last FCGI_PARAMS record must have
820Sigor@sysoev.ru  *   zero content length,
830Sigor@sysoev.ru  *   Several FCGI_STDIN records, the last FCGI_STDIN record must have
840Sigor@sysoev.ru  *   zero content length.
850Sigor@sysoev.ru  */
860Sigor@sysoev.ru 
870Sigor@sysoev.ru static const uint8_t  nxt_fastcgi_begin_request[] = {
880Sigor@sysoev.ru     1,                                 /* FastCGI version.                   */
890Sigor@sysoev.ru     NXT_FASTCGI_BEGIN_REQUEST,         /* The BEGIN_REQUEST record type.     */
900Sigor@sysoev.ru     0, 1,                              /* Request ID.                        */
910Sigor@sysoev.ru     0, 8,                              /* Content length of the Role record. */
920Sigor@sysoev.ru     0,                                 /* Padding length.                    */
930Sigor@sysoev.ru     0,                                 /* Reserved.                          */
940Sigor@sysoev.ru 
950Sigor@sysoev.ru     0, NXT_FASTCGI_RESPONDER,          /* The Responder Role.                */
960Sigor@sysoev.ru     0,                                 /* Flags.                             */
970Sigor@sysoev.ru     0, 0, 0, 0, 0,                     /* Reserved.                          */
980Sigor@sysoev.ru };
990Sigor@sysoev.ru 
1000Sigor@sysoev.ru 
1010Sigor@sysoev.ru static const uint8_t  nxt_fastcgi_params_record[] = {
1020Sigor@sysoev.ru     1,                                 /* FastCGI version.                   */
1030Sigor@sysoev.ru     NXT_FASTCGI_PARAMS,                /* The PARAMS record type.            */
1040Sigor@sysoev.ru     0, 1,                              /* Request ID.                        */
1050Sigor@sysoev.ru     0, 0,                              /* Content length.                    */
1060Sigor@sysoev.ru     0,                                 /* Padding length.                    */
1070Sigor@sysoev.ru     0,                                 /* Reserved.                          */
1080Sigor@sysoev.ru };
1090Sigor@sysoev.ru 
1100Sigor@sysoev.ru 
1110Sigor@sysoev.ru static const uint8_t  nxt_fastcgi_stdin_record[] = {
1120Sigor@sysoev.ru     1,                                 /* FastCGI version.                   */
1130Sigor@sysoev.ru     NXT_FASTCGI_STDIN,                 /* The STDIN record type.             */
1140Sigor@sysoev.ru     0, 1,                              /* Request ID.                        */
1150Sigor@sysoev.ru     0, 0,                              /* Content length.                    */
1160Sigor@sysoev.ru     0,                                 /* Padding length.                    */
1170Sigor@sysoev.ru     0,                                 /* Reserved.                          */
1180Sigor@sysoev.ru };
1190Sigor@sysoev.ru 
1200Sigor@sysoev.ru 
1210Sigor@sysoev.ru void
122*1Sigor@sysoev.ru nxt_fastcgi_source_handler(nxt_task_t *task, nxt_upstream_source_t *us,
1230Sigor@sysoev.ru     nxt_fastcgi_source_request_create_t request_create)
1240Sigor@sysoev.ru {
1250Sigor@sysoev.ru     nxt_stream_source_t   *stream;
1260Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
1270Sigor@sysoev.ru 
1280Sigor@sysoev.ru     fs = nxt_mem_zalloc(us->buffers.mem_pool, sizeof(nxt_fastcgi_source_t));
1290Sigor@sysoev.ru     if (nxt_slow_path(fs == NULL)) {
1300Sigor@sysoev.ru         goto fail;
1310Sigor@sysoev.ru     }
1320Sigor@sysoev.ru 
1330Sigor@sysoev.ru     us->protocol_source = fs;
1340Sigor@sysoev.ru 
1350Sigor@sysoev.ru     fs->header_in.list = nxt_list_create(us->buffers.mem_pool, 8,
1360Sigor@sysoev.ru                                          sizeof(nxt_name_value_t));
1370Sigor@sysoev.ru     if (nxt_slow_path(fs->header_in.list == NULL)) {
1380Sigor@sysoev.ru         goto fail;
1390Sigor@sysoev.ru     }
1400Sigor@sysoev.ru 
1410Sigor@sysoev.ru     fs->header_in.hash = us->header_hash;
1420Sigor@sysoev.ru     fs->upstream = us;
1430Sigor@sysoev.ru     fs->request_create = request_create;
1440Sigor@sysoev.ru 
1450Sigor@sysoev.ru     stream = us->stream;
1460Sigor@sysoev.ru 
1470Sigor@sysoev.ru     if (stream == NULL) {
1480Sigor@sysoev.ru         stream = nxt_mem_zalloc(us->buffers.mem_pool,
1490Sigor@sysoev.ru                                 sizeof(nxt_stream_source_t));
1500Sigor@sysoev.ru         if (nxt_slow_path(stream == NULL)) {
1510Sigor@sysoev.ru             goto fail;
1520Sigor@sysoev.ru         }
1530Sigor@sysoev.ru 
1540Sigor@sysoev.ru         us->stream = stream;
1550Sigor@sysoev.ru         stream->upstream = us;
1560Sigor@sysoev.ru 
1570Sigor@sysoev.ru     } else {
1580Sigor@sysoev.ru         nxt_memzero(stream, sizeof(nxt_stream_source_t));
1590Sigor@sysoev.ru     }
1600Sigor@sysoev.ru 
1610Sigor@sysoev.ru     /*
1620Sigor@sysoev.ru      * Create the FastCGI source filter chain:
1630Sigor@sysoev.ru      *   stream source | FastCGI record filter | FastCGI HTTP header filter
1640Sigor@sysoev.ru      */
1650Sigor@sysoev.ru     stream->next = &fs->query;
1660Sigor@sysoev.ru     stream->error_handler = nxt_fastcgi_source_error;
1670Sigor@sysoev.ru 
1680Sigor@sysoev.ru     fs->record.next.context = fs;
1690Sigor@sysoev.ru     fs->record.next.filter = nxt_fastcgi_source_header_filter;
1700Sigor@sysoev.ru 
1710Sigor@sysoev.ru     fs->record.parse.last_buf = nxt_fastcgi_source_last_buf;
1720Sigor@sysoev.ru     fs->record.parse.data = fs;
1730Sigor@sysoev.ru     fs->record.parse.mem_pool = us->buffers.mem_pool;
1740Sigor@sysoev.ru 
1750Sigor@sysoev.ru     fs->query.context = &fs->record.parse;
1760Sigor@sysoev.ru     fs->query.filter = nxt_fastcgi_source_record_filter;
1770Sigor@sysoev.ru 
1780Sigor@sysoev.ru     fs->header_in.content_length = -1;
1790Sigor@sysoev.ru 
1800Sigor@sysoev.ru     stream->out = nxt_fastcgi_request_create(fs);
1810Sigor@sysoev.ru 
1820Sigor@sysoev.ru     if (nxt_fast_path(stream->out != NULL)) {
1830Sigor@sysoev.ru         nxt_memzero(&fs->u.header, sizeof(nxt_http_split_header_parse_t));
1840Sigor@sysoev.ru         fs->u.header.mem_pool = fs->upstream->buffers.mem_pool;
1850Sigor@sysoev.ru 
186*1Sigor@sysoev.ru         nxt_stream_source_connect(task, stream);
1870Sigor@sysoev.ru         return;
1880Sigor@sysoev.ru     }
1890Sigor@sysoev.ru 
1900Sigor@sysoev.ru fail:
1910Sigor@sysoev.ru 
192*1Sigor@sysoev.ru     nxt_fastcgi_source_fail(task, fs);
1930Sigor@sysoev.ru }
1940Sigor@sysoev.ru 
1950Sigor@sysoev.ru 
1960Sigor@sysoev.ru static nxt_buf_t *
1970Sigor@sysoev.ru nxt_fastcgi_request_create(nxt_fastcgi_source_t *fs)
1980Sigor@sysoev.ru {
1990Sigor@sysoev.ru     u_char               *p, *record_length;
2000Sigor@sysoev.ru     size_t               len, size, max_record_size;
2010Sigor@sysoev.ru     nxt_int_t            ret;
2020Sigor@sysoev.ru     nxt_buf_t            *b, *req, **prev;
2030Sigor@sysoev.ru     nxt_bool_t           begin_request;
2040Sigor@sysoev.ru     nxt_fastcgi_param_t  param;
2050Sigor@sysoev.ru 
2060Sigor@sysoev.ru     nxt_thread_log_debug("fastcgi request");
2070Sigor@sysoev.ru 
2080Sigor@sysoev.ru     begin_request = 1;
2090Sigor@sysoev.ru     param.len = 0;
2100Sigor@sysoev.ru     prev = &req;
2110Sigor@sysoev.ru 
2120Sigor@sysoev.ru new_buffer:
2130Sigor@sysoev.ru 
2140Sigor@sysoev.ru     ret = nxt_buf_pool_mem_alloc(&fs->upstream->buffers, 0);
2150Sigor@sysoev.ru     if (nxt_slow_path(ret != NXT_OK)) {
2160Sigor@sysoev.ru         return NULL;
2170Sigor@sysoev.ru     }
2180Sigor@sysoev.ru 
2190Sigor@sysoev.ru     b = fs->upstream->buffers.current;
2200Sigor@sysoev.ru     fs->upstream->buffers.current = NULL;
2210Sigor@sysoev.ru 
2220Sigor@sysoev.ru     *prev = b;
2230Sigor@sysoev.ru     prev = &b->next;
2240Sigor@sysoev.ru 
2250Sigor@sysoev.ru new_record:
2260Sigor@sysoev.ru 
2270Sigor@sysoev.ru     size = b->mem.end - b->mem.free;
2280Sigor@sysoev.ru     size = nxt_align_size(size, 8) - 8;
2290Sigor@sysoev.ru     /* The maximal FastCGI record content size is 65535.  65528 is 64K - 8. */
2300Sigor@sysoev.ru     max_record_size = nxt_min(65528, size);
2310Sigor@sysoev.ru 
2320Sigor@sysoev.ru     p = b->mem.free;
2330Sigor@sysoev.ru 
2340Sigor@sysoev.ru     if (begin_request) {
2350Sigor@sysoev.ru         /* TODO: fastcgi keep conn in flags. */
2360Sigor@sysoev.ru         p = nxt_cpymem(p, nxt_fastcgi_begin_request, 16);
2370Sigor@sysoev.ru         max_record_size -= 16;
2380Sigor@sysoev.ru         begin_request = 0;
2390Sigor@sysoev.ru     }
2400Sigor@sysoev.ru 
2410Sigor@sysoev.ru     b->mem.free = nxt_cpymem(p, nxt_fastcgi_params_record, 8);
2420Sigor@sysoev.ru     record_length = &p[4];
2430Sigor@sysoev.ru     size = 0;
2440Sigor@sysoev.ru 
2450Sigor@sysoev.ru     for ( ;; ) {
2460Sigor@sysoev.ru         if (param.len == 0) {
2470Sigor@sysoev.ru             ret = nxt_fastcgi_next_param(fs, &param);
2480Sigor@sysoev.ru 
2490Sigor@sysoev.ru             if (nxt_slow_path(ret != NXT_OK)) {
2500Sigor@sysoev.ru 
2510Sigor@sysoev.ru                 if (nxt_slow_path(ret == NXT_ERROR)) {
2520Sigor@sysoev.ru                     return NULL;
2530Sigor@sysoev.ru                 }
2540Sigor@sysoev.ru 
2550Sigor@sysoev.ru                 /* ret == NXT_DONE */
2560Sigor@sysoev.ru                 break;
2570Sigor@sysoev.ru             }
2580Sigor@sysoev.ru         }
2590Sigor@sysoev.ru 
2600Sigor@sysoev.ru         len = max_record_size;
2610Sigor@sysoev.ru 
2620Sigor@sysoev.ru         if (nxt_fast_path(len >= param.len)) {
2630Sigor@sysoev.ru             len = param.len;
2640Sigor@sysoev.ru             param.len = 0;
2650Sigor@sysoev.ru 
2660Sigor@sysoev.ru         } else {
2670Sigor@sysoev.ru             param.len -= len;
2680Sigor@sysoev.ru         }
2690Sigor@sysoev.ru 
2700Sigor@sysoev.ru         nxt_thread_log_debug("fastcgi copy len:%uz", len);
2710Sigor@sysoev.ru 
2720Sigor@sysoev.ru         b->mem.free = nxt_cpymem(b->mem.free, param.buf, len);
2730Sigor@sysoev.ru 
2740Sigor@sysoev.ru         size += len;
2750Sigor@sysoev.ru         max_record_size -= len;
2760Sigor@sysoev.ru 
2770Sigor@sysoev.ru         if (nxt_slow_path(param.len != 0)) {
2780Sigor@sysoev.ru             /* The record is full. */
2790Sigor@sysoev.ru 
2800Sigor@sysoev.ru             param.buf += len;
2810Sigor@sysoev.ru 
2820Sigor@sysoev.ru             nxt_thread_log_debug("fastcgi content size:%uz", size);
2830Sigor@sysoev.ru 
2840Sigor@sysoev.ru             nxt_fastcgi_set_record_length(record_length, size);
2850Sigor@sysoev.ru 
2860Sigor@sysoev.ru             /* The minimal size of aligned record with content is 16 bytes. */
2870Sigor@sysoev.ru             if (b->mem.end - b->mem.free >= 16) {
2880Sigor@sysoev.ru                 goto new_record;
2890Sigor@sysoev.ru             }
2900Sigor@sysoev.ru 
2910Sigor@sysoev.ru             nxt_thread_log_debug("\"%*s\"", b->mem.free - b->mem.pos,
2920Sigor@sysoev.ru                                  b->mem.pos);
2930Sigor@sysoev.ru             goto new_buffer;
2940Sigor@sysoev.ru         }
2950Sigor@sysoev.ru     }
2960Sigor@sysoev.ru 
2970Sigor@sysoev.ru     nxt_thread_log_debug("fastcgi content size:%uz", size);
2980Sigor@sysoev.ru 
2990Sigor@sysoev.ru     nxt_fastcgi_set_record_length(record_length, size);
3000Sigor@sysoev.ru 
3010Sigor@sysoev.ru     /* A padding length. */
3020Sigor@sysoev.ru     size = 8 - size % 8;
3030Sigor@sysoev.ru     record_length[2] = (u_char) size;
3040Sigor@sysoev.ru     nxt_memzero(b->mem.free, size);
3050Sigor@sysoev.ru     b->mem.free += size;
3060Sigor@sysoev.ru 
3070Sigor@sysoev.ru     nxt_thread_log_debug("fastcgi padding:%uz", size);
3080Sigor@sysoev.ru 
3090Sigor@sysoev.ru     if (b->mem.end - b->mem.free < 16) {
3100Sigor@sysoev.ru         nxt_thread_log_debug("\"%*s\"", b->mem.free - b->mem.pos, b->mem.pos);
3110Sigor@sysoev.ru 
3120Sigor@sysoev.ru         b = nxt_buf_mem_alloc(fs->upstream->buffers.mem_pool, 16, 0);
3130Sigor@sysoev.ru         if (nxt_slow_path(b == NULL)) {
3140Sigor@sysoev.ru             return NULL;
3150Sigor@sysoev.ru         }
3160Sigor@sysoev.ru 
3170Sigor@sysoev.ru         *prev = b;
3180Sigor@sysoev.ru         prev = &b->next;
3190Sigor@sysoev.ru     }
3200Sigor@sysoev.ru 
3210Sigor@sysoev.ru     /* The end of FastCGI params. */
3220Sigor@sysoev.ru     p = nxt_cpymem(b->mem.free, nxt_fastcgi_params_record, 8);
3230Sigor@sysoev.ru 
3240Sigor@sysoev.ru     /* The end of FastCGI stdin. */
3250Sigor@sysoev.ru     b->mem.free = nxt_cpymem(p, nxt_fastcgi_stdin_record, 8);
3260Sigor@sysoev.ru 
3270Sigor@sysoev.ru     nxt_thread_log_debug("\"%*s\"", b->mem.free - b->mem.pos, b->mem.pos);
3280Sigor@sysoev.ru 
3290Sigor@sysoev.ru     return req;
3300Sigor@sysoev.ru }
3310Sigor@sysoev.ru 
3320Sigor@sysoev.ru 
3330Sigor@sysoev.ru static nxt_int_t
3340Sigor@sysoev.ru nxt_fastcgi_next_param(nxt_fastcgi_source_t *fs, nxt_fastcgi_param_t *param)
3350Sigor@sysoev.ru {
3360Sigor@sysoev.ru     nxt_int_t  ret;
3370Sigor@sysoev.ru 
3380Sigor@sysoev.ru     enum {
3390Sigor@sysoev.ru          sw_name_length = 0,
3400Sigor@sysoev.ru          sw_value_length,
3410Sigor@sysoev.ru          sw_name,
3420Sigor@sysoev.ru          sw_value,
3430Sigor@sysoev.ru     };
3440Sigor@sysoev.ru 
3450Sigor@sysoev.ru     switch (fs->state) {
3460Sigor@sysoev.ru 
3470Sigor@sysoev.ru     case sw_name_length:
3480Sigor@sysoev.ru         ret = fs->request_create(fs);
3490Sigor@sysoev.ru 
3500Sigor@sysoev.ru         if (nxt_slow_path(ret != NXT_OK)) {
3510Sigor@sysoev.ru             return ret;
3520Sigor@sysoev.ru         }
3530Sigor@sysoev.ru 
3540Sigor@sysoev.ru         nxt_thread_log_debug("fastcgi param \"%V: %V\"",
3550Sigor@sysoev.ru                              &fs->u.request.name, &fs->u.request.value);
3560Sigor@sysoev.ru 
3570Sigor@sysoev.ru         fs->state = sw_value_length;
3580Sigor@sysoev.ru         param->buf = param->length;
3590Sigor@sysoev.ru         param->len = nxt_fastcgi_param_length(param->length,
3600Sigor@sysoev.ru                                               fs->u.request.name.len);
3610Sigor@sysoev.ru         break;
3620Sigor@sysoev.ru 
3630Sigor@sysoev.ru     case sw_value_length:
3640Sigor@sysoev.ru         fs->state = sw_name;
3650Sigor@sysoev.ru         param->buf = param->length;
3660Sigor@sysoev.ru         param->len = nxt_fastcgi_param_length(param->length,
3670Sigor@sysoev.ru                                               fs->u.request.value.len);
3680Sigor@sysoev.ru         break;
3690Sigor@sysoev.ru 
3700Sigor@sysoev.ru     case sw_name:
3710Sigor@sysoev.ru         fs->state = sw_value;
3720Sigor@sysoev.ru         param->buf = fs->u.request.name.data;
3730Sigor@sysoev.ru         param->len = fs->u.request.name.len;
3740Sigor@sysoev.ru         break;
3750Sigor@sysoev.ru 
3760Sigor@sysoev.ru     case sw_value:
3770Sigor@sysoev.ru         fs->state = sw_name_length;
3780Sigor@sysoev.ru         param->buf = fs->u.request.value.data;
3790Sigor@sysoev.ru         param->len = fs->u.request.value.len;
3800Sigor@sysoev.ru         break;
3810Sigor@sysoev.ru     }
3820Sigor@sysoev.ru 
3830Sigor@sysoev.ru     return NXT_OK;
3840Sigor@sysoev.ru }
3850Sigor@sysoev.ru 
3860Sigor@sysoev.ru 
3870Sigor@sysoev.ru static void
388*1Sigor@sysoev.ru nxt_fastcgi_source_record_filter(nxt_task_t *task, void *obj, void *data)
3890Sigor@sysoev.ru {
3900Sigor@sysoev.ru     size_t                       size;
3910Sigor@sysoev.ru     u_char                       *p;
3920Sigor@sysoev.ru     nxt_buf_t                    *b, *in;
3930Sigor@sysoev.ru     nxt_fastcgi_source_t         *fs;
3940Sigor@sysoev.ru     nxt_fastcgi_source_record_t  *fsr;
3950Sigor@sysoev.ru 
3960Sigor@sysoev.ru     fsr = obj;
3970Sigor@sysoev.ru     in = data;
3980Sigor@sysoev.ru 
399*1Sigor@sysoev.ru     nxt_debug(task, "fastcgi source record filter");
4000Sigor@sysoev.ru 
4010Sigor@sysoev.ru     if (nxt_slow_path(fsr->parse.done)) {
4020Sigor@sysoev.ru         return;
4030Sigor@sysoev.ru     }
4040Sigor@sysoev.ru 
405*1Sigor@sysoev.ru     nxt_fastcgi_record_parse(task, &fsr->parse, in);
4060Sigor@sysoev.ru 
4070Sigor@sysoev.ru     fs = nxt_container_of(fsr, nxt_fastcgi_source_t, record);
4080Sigor@sysoev.ru 
4090Sigor@sysoev.ru     if (fsr->parse.error) {
410*1Sigor@sysoev.ru         nxt_fastcgi_source_fail(task, fs);
4110Sigor@sysoev.ru         return;
4120Sigor@sysoev.ru     }
4130Sigor@sysoev.ru 
4140Sigor@sysoev.ru     if (fsr->parse.fastcgi_error) {
4150Sigor@sysoev.ru         /*
4160Sigor@sysoev.ru          * Output all parsed before a FastCGI record error and close upstream.
4170Sigor@sysoev.ru          */
418*1Sigor@sysoev.ru         nxt_thread_current_work_queue_add(task->thread,
419*1Sigor@sysoev.ru                                           nxt_fastcgi_source_record_error,
420*1Sigor@sysoev.ru                                           task, fs, NULL);
4210Sigor@sysoev.ru     }
4220Sigor@sysoev.ru 
4230Sigor@sysoev.ru     /* Log FastCGI stderr output. */
4240Sigor@sysoev.ru 
4250Sigor@sysoev.ru     for (b = fsr->parse.out[1]; b != NULL; b = b->next) {
4260Sigor@sysoev.ru 
4270Sigor@sysoev.ru         for (p = b->mem.free - 1; p >= b->mem.pos; p--) {
4280Sigor@sysoev.ru             if (*p != NXT_CR && *p != NXT_LF) {
4290Sigor@sysoev.ru                 break;
4300Sigor@sysoev.ru             }
4310Sigor@sysoev.ru         }
4320Sigor@sysoev.ru 
4330Sigor@sysoev.ru         size = (p + 1) - b->mem.pos;
4340Sigor@sysoev.ru 
4350Sigor@sysoev.ru         if (size != 0) {
436*1Sigor@sysoev.ru             nxt_log(task, NXT_LOG_ERR,
437*1Sigor@sysoev.ru                     "upstream sent in FastCGI stderr: \"%*s\"",
438*1Sigor@sysoev.ru                     size, b->mem.pos);
4390Sigor@sysoev.ru         }
4400Sigor@sysoev.ru 
441*1Sigor@sysoev.ru         b->completion_handler(task, b, b->parent);
4420Sigor@sysoev.ru     }
4430Sigor@sysoev.ru 
4440Sigor@sysoev.ru     /* Process FastCGI stdout output. */
4450Sigor@sysoev.ru 
4460Sigor@sysoev.ru     if (fsr->parse.out[0] != NULL) {
447*1Sigor@sysoev.ru         nxt_source_filter(task->thread, fs->upstream->work_queue, task,
448*1Sigor@sysoev.ru                           &fsr->next, fsr->parse.out[0]);
4490Sigor@sysoev.ru     }
4500Sigor@sysoev.ru }
4510Sigor@sysoev.ru 
4520Sigor@sysoev.ru 
4530Sigor@sysoev.ru static void
454*1Sigor@sysoev.ru nxt_fastcgi_source_record_error(nxt_task_t *task, void *obj, void *data)
4550Sigor@sysoev.ru {
4560Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
4570Sigor@sysoev.ru 
4580Sigor@sysoev.ru     fs = obj;
4590Sigor@sysoev.ru 
460*1Sigor@sysoev.ru     nxt_fastcgi_source_fail(task, fs);
4610Sigor@sysoev.ru }
4620Sigor@sysoev.ru 
4630Sigor@sysoev.ru 
4640Sigor@sysoev.ru static void
465*1Sigor@sysoev.ru nxt_fastcgi_source_header_filter(nxt_task_t *task, void *obj, void *data)
4660Sigor@sysoev.ru {
4670Sigor@sysoev.ru     nxt_int_t             ret;
4680Sigor@sysoev.ru     nxt_buf_t             *b;
4690Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
4700Sigor@sysoev.ru 
4710Sigor@sysoev.ru     fs = obj;
4720Sigor@sysoev.ru     b = data;
4730Sigor@sysoev.ru 
4740Sigor@sysoev.ru     do {
475*1Sigor@sysoev.ru         nxt_debug(task, "fastcgi source header filter");
4760Sigor@sysoev.ru 
4770Sigor@sysoev.ru         if (nxt_slow_path(nxt_buf_is_sync(b))) {
478*1Sigor@sysoev.ru             nxt_fastcgi_source_sync_buffer(task, fs, b);
4790Sigor@sysoev.ru             return;
4800Sigor@sysoev.ru         }
4810Sigor@sysoev.ru 
4820Sigor@sysoev.ru         for ( ;; ) {
4830Sigor@sysoev.ru             ret = nxt_http_split_header_parse(&fs->u.header, &b->mem);
4840Sigor@sysoev.ru 
4850Sigor@sysoev.ru             if (nxt_slow_path(ret != NXT_OK)) {
4860Sigor@sysoev.ru                 break;
4870Sigor@sysoev.ru             }
4880Sigor@sysoev.ru 
489*1Sigor@sysoev.ru             ret = nxt_fastcgi_source_header_process(task, fs);
4900Sigor@sysoev.ru 
4910Sigor@sysoev.ru             if (nxt_slow_path(ret != NXT_OK)) {
4920Sigor@sysoev.ru                 break;
4930Sigor@sysoev.ru             }
4940Sigor@sysoev.ru         }
4950Sigor@sysoev.ru 
4960Sigor@sysoev.ru         if (nxt_fast_path(ret == NXT_DONE)) {
497*1Sigor@sysoev.ru             nxt_debug(task, "fastcgi source header done");
4980Sigor@sysoev.ru             nxt_fastcgi_source_header_ready(fs, b);
4990Sigor@sysoev.ru             return;
5000Sigor@sysoev.ru         }
5010Sigor@sysoev.ru 
5020Sigor@sysoev.ru         if (nxt_fast_path(ret != NXT_AGAIN)) {
5030Sigor@sysoev.ru 
5040Sigor@sysoev.ru             if (ret != NXT_ERROR) {
5050Sigor@sysoev.ru                 /* n == NXT_DECLINED: "\r" is not followed by "\n" */
506*1Sigor@sysoev.ru                 nxt_log(task, NXT_LOG_ERR,
507*1Sigor@sysoev.ru                         "upstream sent invalid header line: \"%*s\\r...\"",
508*1Sigor@sysoev.ru                         fs->u.header.parse.header_end
509*1Sigor@sysoev.ru                             - fs->u.header.parse.header_name_start,
510*1Sigor@sysoev.ru                         fs->u.header.parse.header_name_start);
5110Sigor@sysoev.ru             }
5120Sigor@sysoev.ru 
5130Sigor@sysoev.ru             /* ret == NXT_ERROR */
5140Sigor@sysoev.ru 
515*1Sigor@sysoev.ru             nxt_fastcgi_source_fail(task, fs);
5160Sigor@sysoev.ru             return;
5170Sigor@sysoev.ru         }
5180Sigor@sysoev.ru 
5190Sigor@sysoev.ru         b = b->next;
5200Sigor@sysoev.ru 
5210Sigor@sysoev.ru     } while (b != NULL);
5220Sigor@sysoev.ru }
5230Sigor@sysoev.ru 
5240Sigor@sysoev.ru 
5250Sigor@sysoev.ru static void
526*1Sigor@sysoev.ru nxt_fastcgi_source_sync_buffer(nxt_task_t *task, nxt_fastcgi_source_t *fs,
5270Sigor@sysoev.ru     nxt_buf_t *b)
5280Sigor@sysoev.ru {
5290Sigor@sysoev.ru     if (nxt_buf_is_last(b)) {
530*1Sigor@sysoev.ru         nxt_log(task, NXT_LOG_ERR, "upstream closed prematurely connection");
5310Sigor@sysoev.ru 
5320Sigor@sysoev.ru     } else {
533*1Sigor@sysoev.ru         nxt_log(task, NXT_LOG_ERR, "%ui buffers %uz each are not "
534*1Sigor@sysoev.ru                 "enough to process upstream response header",
535*1Sigor@sysoev.ru                 fs->upstream->buffers.max, fs->upstream->buffers.size);
5360Sigor@sysoev.ru     }
5370Sigor@sysoev.ru 
5380Sigor@sysoev.ru     /* The stream source sends only the last and the nobuf sync buffer. */
5390Sigor@sysoev.ru 
540*1Sigor@sysoev.ru     nxt_fastcgi_source_fail(task, fs);
5410Sigor@sysoev.ru }
5420Sigor@sysoev.ru 
5430Sigor@sysoev.ru 
5440Sigor@sysoev.ru static nxt_int_t
545*1Sigor@sysoev.ru nxt_fastcgi_source_header_process(nxt_task_t *task, nxt_fastcgi_source_t *fs)
5460Sigor@sysoev.ru {
5470Sigor@sysoev.ru     size_t                     len;
5480Sigor@sysoev.ru     nxt_name_value_t           *nv;
5490Sigor@sysoev.ru     nxt_lvlhsh_query_t         lhq;
5500Sigor@sysoev.ru     nxt_http_header_parse_t    *hp;
5510Sigor@sysoev.ru     nxt_upstream_name_value_t  *unv;
5520Sigor@sysoev.ru 
5530Sigor@sysoev.ru     hp = &fs->u.header.parse;
5540Sigor@sysoev.ru 
5550Sigor@sysoev.ru     len = hp->header_name_end - hp->header_name_start;
5560Sigor@sysoev.ru 
5570Sigor@sysoev.ru     if (len > 255) {
558*1Sigor@sysoev.ru         nxt_log(task, NXT_LOG_INFO,
559*1Sigor@sysoev.ru                 "upstream sent too long header field name: \"%*s\"",
560*1Sigor@sysoev.ru                 len, hp->header_name_start);
5610Sigor@sysoev.ru         return NXT_ERROR;
5620Sigor@sysoev.ru     }
5630Sigor@sysoev.ru 
5640Sigor@sysoev.ru     nv = nxt_list_add(fs->header_in.list);
5650Sigor@sysoev.ru     if (nxt_slow_path(nv == NULL)) {
5660Sigor@sysoev.ru         return NXT_ERROR;
5670Sigor@sysoev.ru     }
5680Sigor@sysoev.ru 
5690Sigor@sysoev.ru     nv->hash = hp->header_hash;
5700Sigor@sysoev.ru     nv->skip = 0;
5710Sigor@sysoev.ru     nv->name_len = len;
5720Sigor@sysoev.ru     nv->name_start = hp->header_name_start;
5730Sigor@sysoev.ru     nv->value_len = hp->header_end - hp->header_start;
5740Sigor@sysoev.ru     nv->value_start = hp->header_start;
5750Sigor@sysoev.ru 
576*1Sigor@sysoev.ru     nxt_debug(task, "http header: \"%*s: %*s\"",
577*1Sigor@sysoev.ru               nv->name_len, nv->name_start, nv->value_len, nv->value_start);
5780Sigor@sysoev.ru 
5790Sigor@sysoev.ru     lhq.key_hash = nv->hash;
5800Sigor@sysoev.ru     lhq.key.len = nv->name_len;
5810Sigor@sysoev.ru     lhq.key.data = nv->name_start;
5820Sigor@sysoev.ru     lhq.proto = &nxt_upstream_header_hash_proto;
5830Sigor@sysoev.ru 
5840Sigor@sysoev.ru     if (nxt_lvlhsh_find(&fs->header_in.hash, &lhq) == NXT_OK) {
5850Sigor@sysoev.ru         unv = lhq.value;
5860Sigor@sysoev.ru 
5870Sigor@sysoev.ru         if (unv->handler(fs->upstream, nv) == NXT_OK) {
5880Sigor@sysoev.ru             return NXT_ERROR;
5890Sigor@sysoev.ru         }
5900Sigor@sysoev.ru     }
5910Sigor@sysoev.ru 
5920Sigor@sysoev.ru     return NXT_OK;
5930Sigor@sysoev.ru }
5940Sigor@sysoev.ru 
5950Sigor@sysoev.ru 
5960Sigor@sysoev.ru static const nxt_upstream_name_value_t  nxt_fastcgi_source_headers[]
5970Sigor@sysoev.ru     nxt_aligned(32) =
5980Sigor@sysoev.ru {
5990Sigor@sysoev.ru     { nxt_fastcgi_source_status,
6000Sigor@sysoev.ru       nxt_upstream_name_value("status") },
6010Sigor@sysoev.ru 
6020Sigor@sysoev.ru     { nxt_fastcgi_source_content_length,
6030Sigor@sysoev.ru       nxt_upstream_name_value("content-length") },
6040Sigor@sysoev.ru };
6050Sigor@sysoev.ru 
6060Sigor@sysoev.ru 
6070Sigor@sysoev.ru nxt_int_t
6080Sigor@sysoev.ru nxt_fastcgi_source_hash_create(nxt_mem_pool_t *mp, nxt_lvlhsh_t *lh)
6090Sigor@sysoev.ru {
6100Sigor@sysoev.ru     return nxt_upstream_header_hash_add(mp, lh, nxt_fastcgi_source_headers,
6110Sigor@sysoev.ru                                         nxt_nitems(nxt_fastcgi_source_headers));
6120Sigor@sysoev.ru }
6130Sigor@sysoev.ru 
6140Sigor@sysoev.ru 
6150Sigor@sysoev.ru static nxt_int_t
6160Sigor@sysoev.ru nxt_fastcgi_source_status(nxt_upstream_source_t *us, nxt_name_value_t *nv)
6170Sigor@sysoev.ru {
6180Sigor@sysoev.ru     nxt_int_t             n;
6190Sigor@sysoev.ru     nxt_str_t             s;
6200Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
6210Sigor@sysoev.ru 
6220Sigor@sysoev.ru     s.len = nv->value_len;
6230Sigor@sysoev.ru     s.data = nv->value_start;
6240Sigor@sysoev.ru 
6250Sigor@sysoev.ru     n = nxt_str_int_parse(&s);
6260Sigor@sysoev.ru 
6270Sigor@sysoev.ru     if (nxt_fast_path(n > 0)) {
6280Sigor@sysoev.ru         fs = us->protocol_source;
6290Sigor@sysoev.ru         fs->header_in.status = n;
6300Sigor@sysoev.ru         return NXT_OK;
6310Sigor@sysoev.ru     }
6320Sigor@sysoev.ru 
6330Sigor@sysoev.ru     return NXT_ERROR;
6340Sigor@sysoev.ru }
6350Sigor@sysoev.ru 
6360Sigor@sysoev.ru 
6370Sigor@sysoev.ru static nxt_int_t
6380Sigor@sysoev.ru nxt_fastcgi_source_content_length(nxt_upstream_source_t *us,
6390Sigor@sysoev.ru     nxt_name_value_t *nv)
6400Sigor@sysoev.ru {
6410Sigor@sysoev.ru     nxt_off_t             length;
6420Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
6430Sigor@sysoev.ru 
6440Sigor@sysoev.ru     length = nxt_off_t_parse(nv->value_start, nv->value_len);
6450Sigor@sysoev.ru 
6460Sigor@sysoev.ru     if (nxt_fast_path(length > 0)) {
6470Sigor@sysoev.ru         fs = us->protocol_source;
6480Sigor@sysoev.ru         fs->header_in.content_length = length;
6490Sigor@sysoev.ru         return NXT_OK;
6500Sigor@sysoev.ru     }
6510Sigor@sysoev.ru 
6520Sigor@sysoev.ru     return NXT_ERROR;
6530Sigor@sysoev.ru }
6540Sigor@sysoev.ru 
6550Sigor@sysoev.ru 
6560Sigor@sysoev.ru static void
6570Sigor@sysoev.ru nxt_fastcgi_source_header_ready(nxt_fastcgi_source_t *fs, nxt_buf_t *b)
6580Sigor@sysoev.ru {
6590Sigor@sysoev.ru     /*
6600Sigor@sysoev.ru      * Change the FastCGI source filter chain:
6610Sigor@sysoev.ru      *   stream source | FastCGI record filter | FastCGI body filter
6620Sigor@sysoev.ru      */
6630Sigor@sysoev.ru     fs->record.next.filter = nxt_fastcgi_source_body_filter;
6640Sigor@sysoev.ru 
6650Sigor@sysoev.ru     if (nxt_buf_mem_used_size(&b->mem) != 0) {
6660Sigor@sysoev.ru         fs->rest = b;
6670Sigor@sysoev.ru     }
6680Sigor@sysoev.ru 
6690Sigor@sysoev.ru     if (fs->header_in.status == 0) {
6700Sigor@sysoev.ru         /* The "200 OK" status by default. */
6710Sigor@sysoev.ru         fs->header_in.status = 200;
6720Sigor@sysoev.ru     }
6730Sigor@sysoev.ru 
6740Sigor@sysoev.ru     fs->upstream->state->ready_handler(fs);
6750Sigor@sysoev.ru }
6760Sigor@sysoev.ru 
6770Sigor@sysoev.ru 
6780Sigor@sysoev.ru /*
6790Sigor@sysoev.ru  * The FastCGI source body filter accumulates first body buffers before the next
6800Sigor@sysoev.ru  * filter will be established and sets completion handler for the last buffer.
6810Sigor@sysoev.ru  */
6820Sigor@sysoev.ru 
6830Sigor@sysoev.ru static void
684*1Sigor@sysoev.ru nxt_fastcgi_source_body_filter(nxt_task_t *task, void *obj, void *data)
6850Sigor@sysoev.ru {
6860Sigor@sysoev.ru     nxt_buf_t             *b, *in;
6870Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
6880Sigor@sysoev.ru 
6890Sigor@sysoev.ru     fs = obj;
6900Sigor@sysoev.ru     in = data;
6910Sigor@sysoev.ru 
692*1Sigor@sysoev.ru     nxt_debug(task, "fastcgi source body filter");
6930Sigor@sysoev.ru 
6940Sigor@sysoev.ru     for (b = in; b != NULL; b = b->next) {
6950Sigor@sysoev.ru 
6960Sigor@sysoev.ru         if (nxt_buf_is_last(b)) {
6970Sigor@sysoev.ru             b->data = fs->upstream->data;
6980Sigor@sysoev.ru             b->completion_handler = fs->upstream->state->completion_handler;
6990Sigor@sysoev.ru         }
7000Sigor@sysoev.ru     }
7010Sigor@sysoev.ru 
7020Sigor@sysoev.ru     if (fs->next != NULL) {
703*1Sigor@sysoev.ru         nxt_source_filter(task->thread, fs->upstream->work_queue, task,
704*1Sigor@sysoev.ru                           fs->next, in);
7050Sigor@sysoev.ru         return;
7060Sigor@sysoev.ru     }
7070Sigor@sysoev.ru 
7080Sigor@sysoev.ru     nxt_buf_chain_add(&fs->rest, in);
7090Sigor@sysoev.ru }
7100Sigor@sysoev.ru 
7110Sigor@sysoev.ru 
7120Sigor@sysoev.ru static nxt_buf_t *
7130Sigor@sysoev.ru nxt_fastcgi_source_last_buf(nxt_fastcgi_parse_t *fp)
7140Sigor@sysoev.ru {
7150Sigor@sysoev.ru     nxt_buf_t             *b;
7160Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
7170Sigor@sysoev.ru 
7180Sigor@sysoev.ru     fs = fp->data;
7190Sigor@sysoev.ru 
7200Sigor@sysoev.ru     b = nxt_buf_sync_alloc(fp->mem_pool, NXT_BUF_SYNC_LAST);
7210Sigor@sysoev.ru 
7220Sigor@sysoev.ru     if (nxt_fast_path(b != NULL)) {
7230Sigor@sysoev.ru         b->data = fs->upstream->data;
7240Sigor@sysoev.ru         b->completion_handler = fs->upstream->state->completion_handler;
7250Sigor@sysoev.ru     }
7260Sigor@sysoev.ru 
7270Sigor@sysoev.ru     return b;
7280Sigor@sysoev.ru }
7290Sigor@sysoev.ru 
7300Sigor@sysoev.ru 
7310Sigor@sysoev.ru static void
732*1Sigor@sysoev.ru nxt_fastcgi_source_error(nxt_task_t *task, nxt_stream_source_t *stream)
7330Sigor@sysoev.ru {
7340Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
7350Sigor@sysoev.ru 
7360Sigor@sysoev.ru     nxt_thread_log_debug("fastcgi source error");
7370Sigor@sysoev.ru 
7380Sigor@sysoev.ru     fs = stream->upstream->protocol_source;
7390Sigor@sysoev.ru 
740*1Sigor@sysoev.ru     nxt_fastcgi_source_fail(task, fs);
7410Sigor@sysoev.ru }
7420Sigor@sysoev.ru 
7430Sigor@sysoev.ru 
7440Sigor@sysoev.ru static void
745*1Sigor@sysoev.ru nxt_fastcgi_source_fail(nxt_task_t *task, nxt_fastcgi_source_t *fs)
7460Sigor@sysoev.ru {
747*1Sigor@sysoev.ru     nxt_debug(task, "fastcgi source fail");
7480Sigor@sysoev.ru 
7490Sigor@sysoev.ru     /* TODO: fail, next upstream, or bad gateway */
7500Sigor@sysoev.ru 
751*1Sigor@sysoev.ru     fs->upstream->state->error_handler(task, fs, NULL);
7520Sigor@sysoev.ru }
753