xref: /unit/src/nxt_fastcgi_source.c (revision 2084)
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 
21*2084Salx.manpages@gmail.com #define nxt_fastcgi_set_record_length(p, length)                              \
220Sigor@sysoev.ru     do {                                                                      \
230Sigor@sysoev.ru         uint32_t  len = length;                                               \
240Sigor@sysoev.ru                                                                               \
250Sigor@sysoev.ru         p[1] = (u_char) len;  len >>= 8;                                      \
260Sigor@sysoev.ru         p[0] = (u_char) len;                                                  \
270Sigor@sysoev.ru     } while (0)
280Sigor@sysoev.ru 
290Sigor@sysoev.ru 
300Sigor@sysoev.ru nxt_inline size_t
310Sigor@sysoev.ru nxt_fastcgi_param_length(u_char *p, uint32_t length)
320Sigor@sysoev.ru {
330Sigor@sysoev.ru     if (nxt_fast_path(length < 128)) {
340Sigor@sysoev.ru         *p = (u_char) length;
350Sigor@sysoev.ru         return 1;
360Sigor@sysoev.ru     }
370Sigor@sysoev.ru 
380Sigor@sysoev.ru     p[3] = (u_char) length;  length >>= 8;
390Sigor@sysoev.ru     p[2] = (u_char) length;  length >>= 8;
400Sigor@sysoev.ru     p[1] = (u_char) length;  length >>= 8;
410Sigor@sysoev.ru     p[0] = (u_char) (length | 0x80);
420Sigor@sysoev.ru 
430Sigor@sysoev.ru     return 4;
440Sigor@sysoev.ru }
450Sigor@sysoev.ru 
460Sigor@sysoev.ru 
470Sigor@sysoev.ru static nxt_buf_t *nxt_fastcgi_request_create(nxt_fastcgi_source_t *fs);
480Sigor@sysoev.ru static nxt_int_t nxt_fastcgi_next_param(nxt_fastcgi_source_t *fs,
490Sigor@sysoev.ru     nxt_fastcgi_param_t *param);
500Sigor@sysoev.ru 
511Sigor@sysoev.ru static void nxt_fastcgi_source_record_filter(nxt_task_t *task, void *obj,
520Sigor@sysoev.ru     void *data);
531Sigor@sysoev.ru static void nxt_fastcgi_source_record_error(nxt_task_t *task, void *obj,
540Sigor@sysoev.ru     void *data);
551Sigor@sysoev.ru static void nxt_fastcgi_source_header_filter(nxt_task_t *task, void *obj,
560Sigor@sysoev.ru     void *data);
571Sigor@sysoev.ru static void nxt_fastcgi_source_sync_buffer(nxt_task_t *task,
580Sigor@sysoev.ru     nxt_fastcgi_source_t *fs, nxt_buf_t *b);
590Sigor@sysoev.ru 
601Sigor@sysoev.ru static nxt_int_t nxt_fastcgi_source_header_process(nxt_task_t *task,
611Sigor@sysoev.ru     nxt_fastcgi_source_t *fs);
620Sigor@sysoev.ru static nxt_int_t nxt_fastcgi_source_status(nxt_upstream_source_t *us,
630Sigor@sysoev.ru     nxt_name_value_t *nv);
640Sigor@sysoev.ru static nxt_int_t nxt_fastcgi_source_content_length(nxt_upstream_source_t *us,
650Sigor@sysoev.ru     nxt_name_value_t *nv);
660Sigor@sysoev.ru 
670Sigor@sysoev.ru static void nxt_fastcgi_source_header_ready(nxt_fastcgi_source_t *fs,
680Sigor@sysoev.ru     nxt_buf_t *b);
691Sigor@sysoev.ru static void nxt_fastcgi_source_body_filter(nxt_task_t *task, void *obj,
700Sigor@sysoev.ru     void *data);
710Sigor@sysoev.ru static nxt_buf_t *nxt_fastcgi_source_last_buf(nxt_fastcgi_parse_t *fp);
721Sigor@sysoev.ru static void nxt_fastcgi_source_error(nxt_task_t *task,
731Sigor@sysoev.ru     nxt_stream_source_t *stream);
741Sigor@sysoev.ru static void nxt_fastcgi_source_fail(nxt_task_t *task, nxt_fastcgi_source_t *fs);
750Sigor@sysoev.ru 
760Sigor@sysoev.ru 
770Sigor@sysoev.ru /*
780Sigor@sysoev.ru  * A FastCGI request:
790Sigor@sysoev.ru  *   FCGI_BEGIN_REQUEST record;
800Sigor@sysoev.ru  *   Several FCGI_PARAMS records, the last FCGI_PARAMS record must have
810Sigor@sysoev.ru  *   zero content length,
820Sigor@sysoev.ru  *   Several FCGI_STDIN records, the last FCGI_STDIN record must have
830Sigor@sysoev.ru  *   zero content length.
840Sigor@sysoev.ru  */
850Sigor@sysoev.ru 
860Sigor@sysoev.ru static const uint8_t  nxt_fastcgi_begin_request[] = {
870Sigor@sysoev.ru     1,                                 /* FastCGI version.                   */
880Sigor@sysoev.ru     NXT_FASTCGI_BEGIN_REQUEST,         /* The BEGIN_REQUEST record type.     */
890Sigor@sysoev.ru     0, 1,                              /* Request ID.                        */
900Sigor@sysoev.ru     0, 8,                              /* Content length of the Role record. */
910Sigor@sysoev.ru     0,                                 /* Padding length.                    */
920Sigor@sysoev.ru     0,                                 /* Reserved.                          */
930Sigor@sysoev.ru 
940Sigor@sysoev.ru     0, NXT_FASTCGI_RESPONDER,          /* The Responder Role.                */
950Sigor@sysoev.ru     0,                                 /* Flags.                             */
960Sigor@sysoev.ru     0, 0, 0, 0, 0,                     /* Reserved.                          */
970Sigor@sysoev.ru };
980Sigor@sysoev.ru 
990Sigor@sysoev.ru 
1000Sigor@sysoev.ru static const uint8_t  nxt_fastcgi_params_record[] = {
1010Sigor@sysoev.ru     1,                                 /* FastCGI version.                   */
1020Sigor@sysoev.ru     NXT_FASTCGI_PARAMS,                /* The PARAMS record type.            */
1030Sigor@sysoev.ru     0, 1,                              /* Request ID.                        */
1040Sigor@sysoev.ru     0, 0,                              /* Content length.                    */
1050Sigor@sysoev.ru     0,                                 /* Padding length.                    */
1060Sigor@sysoev.ru     0,                                 /* Reserved.                          */
1070Sigor@sysoev.ru };
1080Sigor@sysoev.ru 
1090Sigor@sysoev.ru 
1100Sigor@sysoev.ru static const uint8_t  nxt_fastcgi_stdin_record[] = {
1110Sigor@sysoev.ru     1,                                 /* FastCGI version.                   */
1120Sigor@sysoev.ru     NXT_FASTCGI_STDIN,                 /* The STDIN record type.             */
1130Sigor@sysoev.ru     0, 1,                              /* Request ID.                        */
1140Sigor@sysoev.ru     0, 0,                              /* Content length.                    */
1150Sigor@sysoev.ru     0,                                 /* Padding length.                    */
1160Sigor@sysoev.ru     0,                                 /* Reserved.                          */
1170Sigor@sysoev.ru };
1180Sigor@sysoev.ru 
1190Sigor@sysoev.ru 
1200Sigor@sysoev.ru void
1211Sigor@sysoev.ru nxt_fastcgi_source_handler(nxt_task_t *task, nxt_upstream_source_t *us,
1220Sigor@sysoev.ru     nxt_fastcgi_source_request_create_t request_create)
1230Sigor@sysoev.ru {
1240Sigor@sysoev.ru     nxt_stream_source_t   *stream;
1250Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
1260Sigor@sysoev.ru 
12765Sigor@sysoev.ru     fs = nxt_mp_zget(us->buffers.mem_pool, sizeof(nxt_fastcgi_source_t));
1280Sigor@sysoev.ru     if (nxt_slow_path(fs == NULL)) {
1290Sigor@sysoev.ru         goto fail;
1300Sigor@sysoev.ru     }
1310Sigor@sysoev.ru 
1320Sigor@sysoev.ru     us->protocol_source = fs;
1330Sigor@sysoev.ru 
1340Sigor@sysoev.ru     fs->header_in.list = nxt_list_create(us->buffers.mem_pool, 8,
1350Sigor@sysoev.ru                                          sizeof(nxt_name_value_t));
1360Sigor@sysoev.ru     if (nxt_slow_path(fs->header_in.list == NULL)) {
1370Sigor@sysoev.ru         goto fail;
1380Sigor@sysoev.ru     }
1390Sigor@sysoev.ru 
1400Sigor@sysoev.ru     fs->header_in.hash = us->header_hash;
1410Sigor@sysoev.ru     fs->upstream = us;
1420Sigor@sysoev.ru     fs->request_create = request_create;
1430Sigor@sysoev.ru 
1440Sigor@sysoev.ru     stream = us->stream;
1450Sigor@sysoev.ru 
1460Sigor@sysoev.ru     if (stream == NULL) {
14765Sigor@sysoev.ru         stream = nxt_mp_zget(us->buffers.mem_pool, sizeof(nxt_stream_source_t));
1480Sigor@sysoev.ru         if (nxt_slow_path(stream == NULL)) {
1490Sigor@sysoev.ru             goto fail;
1500Sigor@sysoev.ru         }
1510Sigor@sysoev.ru 
1520Sigor@sysoev.ru         us->stream = stream;
1530Sigor@sysoev.ru         stream->upstream = us;
1540Sigor@sysoev.ru 
1550Sigor@sysoev.ru     } else {
1560Sigor@sysoev.ru         nxt_memzero(stream, sizeof(nxt_stream_source_t));
1570Sigor@sysoev.ru     }
1580Sigor@sysoev.ru 
1590Sigor@sysoev.ru     /*
1600Sigor@sysoev.ru      * Create the FastCGI source filter chain:
1610Sigor@sysoev.ru      *   stream source | FastCGI record filter | FastCGI HTTP header filter
1620Sigor@sysoev.ru      */
1630Sigor@sysoev.ru     stream->next = &fs->query;
1640Sigor@sysoev.ru     stream->error_handler = nxt_fastcgi_source_error;
1650Sigor@sysoev.ru 
1660Sigor@sysoev.ru     fs->record.next.context = fs;
1670Sigor@sysoev.ru     fs->record.next.filter = nxt_fastcgi_source_header_filter;
1680Sigor@sysoev.ru 
1690Sigor@sysoev.ru     fs->record.parse.last_buf = nxt_fastcgi_source_last_buf;
1700Sigor@sysoev.ru     fs->record.parse.data = fs;
1710Sigor@sysoev.ru     fs->record.parse.mem_pool = us->buffers.mem_pool;
1720Sigor@sysoev.ru 
1730Sigor@sysoev.ru     fs->query.context = &fs->record.parse;
1740Sigor@sysoev.ru     fs->query.filter = nxt_fastcgi_source_record_filter;
1750Sigor@sysoev.ru 
1760Sigor@sysoev.ru     fs->header_in.content_length = -1;
1770Sigor@sysoev.ru 
1780Sigor@sysoev.ru     stream->out = nxt_fastcgi_request_create(fs);
1790Sigor@sysoev.ru 
1800Sigor@sysoev.ru     if (nxt_fast_path(stream->out != NULL)) {
1810Sigor@sysoev.ru         nxt_memzero(&fs->u.header, sizeof(nxt_http_split_header_parse_t));
1820Sigor@sysoev.ru         fs->u.header.mem_pool = fs->upstream->buffers.mem_pool;
1830Sigor@sysoev.ru 
1841Sigor@sysoev.ru         nxt_stream_source_connect(task, stream);
1850Sigor@sysoev.ru         return;
1860Sigor@sysoev.ru     }
1870Sigor@sysoev.ru 
1880Sigor@sysoev.ru fail:
1890Sigor@sysoev.ru 
1901Sigor@sysoev.ru     nxt_fastcgi_source_fail(task, fs);
1910Sigor@sysoev.ru }
1920Sigor@sysoev.ru 
1930Sigor@sysoev.ru 
1940Sigor@sysoev.ru static nxt_buf_t *
1950Sigor@sysoev.ru nxt_fastcgi_request_create(nxt_fastcgi_source_t *fs)
1960Sigor@sysoev.ru {
1970Sigor@sysoev.ru     u_char               *p, *record_length;
1980Sigor@sysoev.ru     size_t               len, size, max_record_size;
1990Sigor@sysoev.ru     nxt_int_t            ret;
2000Sigor@sysoev.ru     nxt_buf_t            *b, *req, **prev;
2010Sigor@sysoev.ru     nxt_bool_t           begin_request;
2020Sigor@sysoev.ru     nxt_fastcgi_param_t  param;
2030Sigor@sysoev.ru 
2040Sigor@sysoev.ru     nxt_thread_log_debug("fastcgi request");
2050Sigor@sysoev.ru 
2060Sigor@sysoev.ru     begin_request = 1;
2070Sigor@sysoev.ru     param.len = 0;
2080Sigor@sysoev.ru     prev = &req;
2090Sigor@sysoev.ru 
2100Sigor@sysoev.ru new_buffer:
2110Sigor@sysoev.ru 
2120Sigor@sysoev.ru     ret = nxt_buf_pool_mem_alloc(&fs->upstream->buffers, 0);
2130Sigor@sysoev.ru     if (nxt_slow_path(ret != NXT_OK)) {
2140Sigor@sysoev.ru         return NULL;
2150Sigor@sysoev.ru     }
2160Sigor@sysoev.ru 
2170Sigor@sysoev.ru     b = fs->upstream->buffers.current;
2180Sigor@sysoev.ru     fs->upstream->buffers.current = NULL;
2190Sigor@sysoev.ru 
2200Sigor@sysoev.ru     *prev = b;
2210Sigor@sysoev.ru     prev = &b->next;
2220Sigor@sysoev.ru 
2230Sigor@sysoev.ru new_record:
2240Sigor@sysoev.ru 
2250Sigor@sysoev.ru     size = b->mem.end - b->mem.free;
2260Sigor@sysoev.ru     size = nxt_align_size(size, 8) - 8;
2270Sigor@sysoev.ru     /* The maximal FastCGI record content size is 65535.  65528 is 64K - 8. */
2280Sigor@sysoev.ru     max_record_size = nxt_min(65528, size);
2290Sigor@sysoev.ru 
2300Sigor@sysoev.ru     p = b->mem.free;
2310Sigor@sysoev.ru 
2320Sigor@sysoev.ru     if (begin_request) {
2330Sigor@sysoev.ru         /* TODO: fastcgi keep conn in flags. */
2340Sigor@sysoev.ru         p = nxt_cpymem(p, nxt_fastcgi_begin_request, 16);
2350Sigor@sysoev.ru         max_record_size -= 16;
2360Sigor@sysoev.ru         begin_request = 0;
2370Sigor@sysoev.ru     }
2380Sigor@sysoev.ru 
2390Sigor@sysoev.ru     b->mem.free = nxt_cpymem(p, nxt_fastcgi_params_record, 8);
2400Sigor@sysoev.ru     record_length = &p[4];
2410Sigor@sysoev.ru     size = 0;
2420Sigor@sysoev.ru 
2430Sigor@sysoev.ru     for ( ;; ) {
2440Sigor@sysoev.ru         if (param.len == 0) {
2450Sigor@sysoev.ru             ret = nxt_fastcgi_next_param(fs, &param);
2460Sigor@sysoev.ru 
2470Sigor@sysoev.ru             if (nxt_slow_path(ret != NXT_OK)) {
2480Sigor@sysoev.ru 
2490Sigor@sysoev.ru                 if (nxt_slow_path(ret == NXT_ERROR)) {
2500Sigor@sysoev.ru                     return NULL;
2510Sigor@sysoev.ru                 }
2520Sigor@sysoev.ru 
2530Sigor@sysoev.ru                 /* ret == NXT_DONE */
2540Sigor@sysoev.ru                 break;
2550Sigor@sysoev.ru             }
2560Sigor@sysoev.ru         }
2570Sigor@sysoev.ru 
2580Sigor@sysoev.ru         len = max_record_size;
2590Sigor@sysoev.ru 
2600Sigor@sysoev.ru         if (nxt_fast_path(len >= param.len)) {
2610Sigor@sysoev.ru             len = param.len;
2620Sigor@sysoev.ru             param.len = 0;
2630Sigor@sysoev.ru 
2640Sigor@sysoev.ru         } else {
2650Sigor@sysoev.ru             param.len -= len;
2660Sigor@sysoev.ru         }
2670Sigor@sysoev.ru 
2680Sigor@sysoev.ru         nxt_thread_log_debug("fastcgi copy len:%uz", len);
2690Sigor@sysoev.ru 
2700Sigor@sysoev.ru         b->mem.free = nxt_cpymem(b->mem.free, param.buf, len);
2710Sigor@sysoev.ru 
2720Sigor@sysoev.ru         size += len;
2730Sigor@sysoev.ru         max_record_size -= len;
2740Sigor@sysoev.ru 
2750Sigor@sysoev.ru         if (nxt_slow_path(param.len != 0)) {
2760Sigor@sysoev.ru             /* The record is full. */
2770Sigor@sysoev.ru 
2780Sigor@sysoev.ru             param.buf += len;
2790Sigor@sysoev.ru 
2800Sigor@sysoev.ru             nxt_thread_log_debug("fastcgi content size:%uz", size);
2810Sigor@sysoev.ru 
2820Sigor@sysoev.ru             nxt_fastcgi_set_record_length(record_length, size);
2830Sigor@sysoev.ru 
2840Sigor@sysoev.ru             /* The minimal size of aligned record with content is 16 bytes. */
2850Sigor@sysoev.ru             if (b->mem.end - b->mem.free >= 16) {
2860Sigor@sysoev.ru                 goto new_record;
2870Sigor@sysoev.ru             }
2880Sigor@sysoev.ru 
2890Sigor@sysoev.ru             nxt_thread_log_debug("\"%*s\"", b->mem.free - b->mem.pos,
2900Sigor@sysoev.ru                                  b->mem.pos);
2910Sigor@sysoev.ru             goto new_buffer;
2920Sigor@sysoev.ru         }
2930Sigor@sysoev.ru     }
2940Sigor@sysoev.ru 
2950Sigor@sysoev.ru     nxt_thread_log_debug("fastcgi content size:%uz", size);
2960Sigor@sysoev.ru 
2970Sigor@sysoev.ru     nxt_fastcgi_set_record_length(record_length, size);
2980Sigor@sysoev.ru 
2990Sigor@sysoev.ru     /* A padding length. */
3000Sigor@sysoev.ru     size = 8 - size % 8;
3010Sigor@sysoev.ru     record_length[2] = (u_char) size;
3020Sigor@sysoev.ru     nxt_memzero(b->mem.free, size);
3030Sigor@sysoev.ru     b->mem.free += size;
3040Sigor@sysoev.ru 
3050Sigor@sysoev.ru     nxt_thread_log_debug("fastcgi padding:%uz", size);
3060Sigor@sysoev.ru 
3070Sigor@sysoev.ru     if (b->mem.end - b->mem.free < 16) {
3080Sigor@sysoev.ru         nxt_thread_log_debug("\"%*s\"", b->mem.free - b->mem.pos, b->mem.pos);
3090Sigor@sysoev.ru 
3100Sigor@sysoev.ru         b = nxt_buf_mem_alloc(fs->upstream->buffers.mem_pool, 16, 0);
3110Sigor@sysoev.ru         if (nxt_slow_path(b == NULL)) {
3120Sigor@sysoev.ru             return NULL;
3130Sigor@sysoev.ru         }
3140Sigor@sysoev.ru 
3150Sigor@sysoev.ru         *prev = b;
3160Sigor@sysoev.ru         prev = &b->next;
3170Sigor@sysoev.ru     }
3180Sigor@sysoev.ru 
3190Sigor@sysoev.ru     /* The end of FastCGI params. */
3200Sigor@sysoev.ru     p = nxt_cpymem(b->mem.free, nxt_fastcgi_params_record, 8);
3210Sigor@sysoev.ru 
3220Sigor@sysoev.ru     /* The end of FastCGI stdin. */
3230Sigor@sysoev.ru     b->mem.free = nxt_cpymem(p, nxt_fastcgi_stdin_record, 8);
3240Sigor@sysoev.ru 
3250Sigor@sysoev.ru     nxt_thread_log_debug("\"%*s\"", b->mem.free - b->mem.pos, b->mem.pos);
3260Sigor@sysoev.ru 
3270Sigor@sysoev.ru     return req;
3280Sigor@sysoev.ru }
3290Sigor@sysoev.ru 
3300Sigor@sysoev.ru 
3310Sigor@sysoev.ru static nxt_int_t
3320Sigor@sysoev.ru nxt_fastcgi_next_param(nxt_fastcgi_source_t *fs, nxt_fastcgi_param_t *param)
3330Sigor@sysoev.ru {
3340Sigor@sysoev.ru     nxt_int_t  ret;
3350Sigor@sysoev.ru 
3360Sigor@sysoev.ru     enum {
337521Szelenkov@nginx.com         sw_name_length = 0,
338521Szelenkov@nginx.com         sw_value_length,
339521Szelenkov@nginx.com         sw_name,
340521Szelenkov@nginx.com         sw_value,
3410Sigor@sysoev.ru     };
3420Sigor@sysoev.ru 
3430Sigor@sysoev.ru     switch (fs->state) {
3440Sigor@sysoev.ru 
3450Sigor@sysoev.ru     case sw_name_length:
3460Sigor@sysoev.ru         ret = fs->request_create(fs);
3470Sigor@sysoev.ru 
3480Sigor@sysoev.ru         if (nxt_slow_path(ret != NXT_OK)) {
3490Sigor@sysoev.ru             return ret;
3500Sigor@sysoev.ru         }
3510Sigor@sysoev.ru 
3520Sigor@sysoev.ru         nxt_thread_log_debug("fastcgi param \"%V: %V\"",
3530Sigor@sysoev.ru                              &fs->u.request.name, &fs->u.request.value);
3540Sigor@sysoev.ru 
3550Sigor@sysoev.ru         fs->state = sw_value_length;
3560Sigor@sysoev.ru         param->buf = param->length;
3570Sigor@sysoev.ru         param->len = nxt_fastcgi_param_length(param->length,
3580Sigor@sysoev.ru                                               fs->u.request.name.len);
3590Sigor@sysoev.ru         break;
3600Sigor@sysoev.ru 
3610Sigor@sysoev.ru     case sw_value_length:
3620Sigor@sysoev.ru         fs->state = sw_name;
3630Sigor@sysoev.ru         param->buf = param->length;
3640Sigor@sysoev.ru         param->len = nxt_fastcgi_param_length(param->length,
3650Sigor@sysoev.ru                                               fs->u.request.value.len);
3660Sigor@sysoev.ru         break;
3670Sigor@sysoev.ru 
3680Sigor@sysoev.ru     case sw_name:
3690Sigor@sysoev.ru         fs->state = sw_value;
3700Sigor@sysoev.ru         param->buf = fs->u.request.name.data;
3710Sigor@sysoev.ru         param->len = fs->u.request.name.len;
3720Sigor@sysoev.ru         break;
3730Sigor@sysoev.ru 
3740Sigor@sysoev.ru     case sw_value:
3750Sigor@sysoev.ru         fs->state = sw_name_length;
3760Sigor@sysoev.ru         param->buf = fs->u.request.value.data;
3770Sigor@sysoev.ru         param->len = fs->u.request.value.len;
3780Sigor@sysoev.ru         break;
3790Sigor@sysoev.ru     }
3800Sigor@sysoev.ru 
3810Sigor@sysoev.ru     return NXT_OK;
3820Sigor@sysoev.ru }
3830Sigor@sysoev.ru 
3840Sigor@sysoev.ru 
3850Sigor@sysoev.ru static void
3861Sigor@sysoev.ru nxt_fastcgi_source_record_filter(nxt_task_t *task, void *obj, void *data)
3870Sigor@sysoev.ru {
3880Sigor@sysoev.ru     size_t                       size;
3890Sigor@sysoev.ru     u_char                       *p;
3900Sigor@sysoev.ru     nxt_buf_t                    *b, *in;
3910Sigor@sysoev.ru     nxt_fastcgi_source_t         *fs;
3920Sigor@sysoev.ru     nxt_fastcgi_source_record_t  *fsr;
3930Sigor@sysoev.ru 
3940Sigor@sysoev.ru     fsr = obj;
3950Sigor@sysoev.ru     in = data;
3960Sigor@sysoev.ru 
3971Sigor@sysoev.ru     nxt_debug(task, "fastcgi source record filter");
3980Sigor@sysoev.ru 
3990Sigor@sysoev.ru     if (nxt_slow_path(fsr->parse.done)) {
4000Sigor@sysoev.ru         return;
4010Sigor@sysoev.ru     }
4020Sigor@sysoev.ru 
4031Sigor@sysoev.ru     nxt_fastcgi_record_parse(task, &fsr->parse, in);
4040Sigor@sysoev.ru 
4050Sigor@sysoev.ru     fs = nxt_container_of(fsr, nxt_fastcgi_source_t, record);
4060Sigor@sysoev.ru 
4070Sigor@sysoev.ru     if (fsr->parse.error) {
4081Sigor@sysoev.ru         nxt_fastcgi_source_fail(task, fs);
4090Sigor@sysoev.ru         return;
4100Sigor@sysoev.ru     }
4110Sigor@sysoev.ru 
4120Sigor@sysoev.ru     if (fsr->parse.fastcgi_error) {
4130Sigor@sysoev.ru         /*
4140Sigor@sysoev.ru          * Output all parsed before a FastCGI record error and close upstream.
4150Sigor@sysoev.ru          */
4161Sigor@sysoev.ru         nxt_thread_current_work_queue_add(task->thread,
4171Sigor@sysoev.ru                                           nxt_fastcgi_source_record_error,
4181Sigor@sysoev.ru                                           task, fs, NULL);
4190Sigor@sysoev.ru     }
4200Sigor@sysoev.ru 
4210Sigor@sysoev.ru     /* Log FastCGI stderr output. */
4220Sigor@sysoev.ru 
4230Sigor@sysoev.ru     for (b = fsr->parse.out[1]; b != NULL; b = b->next) {
4240Sigor@sysoev.ru 
4250Sigor@sysoev.ru         for (p = b->mem.free - 1; p >= b->mem.pos; p--) {
426704Sigor@sysoev.ru             if (*p != '\r' && *p != '\n') {
4270Sigor@sysoev.ru                 break;
4280Sigor@sysoev.ru             }
4290Sigor@sysoev.ru         }
4300Sigor@sysoev.ru 
4310Sigor@sysoev.ru         size = (p + 1) - b->mem.pos;
4320Sigor@sysoev.ru 
4330Sigor@sysoev.ru         if (size != 0) {
4341Sigor@sysoev.ru             nxt_log(task, NXT_LOG_ERR,
4351Sigor@sysoev.ru                     "upstream sent in FastCGI stderr: \"%*s\"",
4361Sigor@sysoev.ru                     size, b->mem.pos);
4370Sigor@sysoev.ru         }
4380Sigor@sysoev.ru 
4391Sigor@sysoev.ru         b->completion_handler(task, b, b->parent);
4400Sigor@sysoev.ru     }
4410Sigor@sysoev.ru 
4420Sigor@sysoev.ru     /* Process FastCGI stdout output. */
4430Sigor@sysoev.ru 
4440Sigor@sysoev.ru     if (fsr->parse.out[0] != NULL) {
4451Sigor@sysoev.ru         nxt_source_filter(task->thread, fs->upstream->work_queue, task,
4461Sigor@sysoev.ru                           &fsr->next, fsr->parse.out[0]);
4470Sigor@sysoev.ru     }
4480Sigor@sysoev.ru }
4490Sigor@sysoev.ru 
4500Sigor@sysoev.ru 
4510Sigor@sysoev.ru static void
4521Sigor@sysoev.ru nxt_fastcgi_source_record_error(nxt_task_t *task, void *obj, void *data)
4530Sigor@sysoev.ru {
4540Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
4550Sigor@sysoev.ru 
4560Sigor@sysoev.ru     fs = obj;
4570Sigor@sysoev.ru 
4581Sigor@sysoev.ru     nxt_fastcgi_source_fail(task, fs);
4590Sigor@sysoev.ru }
4600Sigor@sysoev.ru 
4610Sigor@sysoev.ru 
4620Sigor@sysoev.ru static void
4631Sigor@sysoev.ru nxt_fastcgi_source_header_filter(nxt_task_t *task, void *obj, void *data)
4640Sigor@sysoev.ru {
4650Sigor@sysoev.ru     nxt_int_t             ret;
4660Sigor@sysoev.ru     nxt_buf_t             *b;
4670Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
4680Sigor@sysoev.ru 
4690Sigor@sysoev.ru     fs = obj;
4700Sigor@sysoev.ru     b = data;
4710Sigor@sysoev.ru 
4720Sigor@sysoev.ru     do {
4731Sigor@sysoev.ru         nxt_debug(task, "fastcgi source header filter");
4740Sigor@sysoev.ru 
4750Sigor@sysoev.ru         if (nxt_slow_path(nxt_buf_is_sync(b))) {
4761Sigor@sysoev.ru             nxt_fastcgi_source_sync_buffer(task, fs, b);
4770Sigor@sysoev.ru             return;
4780Sigor@sysoev.ru         }
4790Sigor@sysoev.ru 
4800Sigor@sysoev.ru         for ( ;; ) {
4810Sigor@sysoev.ru             ret = nxt_http_split_header_parse(&fs->u.header, &b->mem);
4820Sigor@sysoev.ru 
4830Sigor@sysoev.ru             if (nxt_slow_path(ret != NXT_OK)) {
4840Sigor@sysoev.ru                 break;
4850Sigor@sysoev.ru             }
4860Sigor@sysoev.ru 
4871Sigor@sysoev.ru             ret = nxt_fastcgi_source_header_process(task, fs);
4880Sigor@sysoev.ru 
4890Sigor@sysoev.ru             if (nxt_slow_path(ret != NXT_OK)) {
4900Sigor@sysoev.ru                 break;
4910Sigor@sysoev.ru             }
4920Sigor@sysoev.ru         }
4930Sigor@sysoev.ru 
4940Sigor@sysoev.ru         if (nxt_fast_path(ret == NXT_DONE)) {
4951Sigor@sysoev.ru             nxt_debug(task, "fastcgi source header done");
4960Sigor@sysoev.ru             nxt_fastcgi_source_header_ready(fs, b);
4970Sigor@sysoev.ru             return;
4980Sigor@sysoev.ru         }
4990Sigor@sysoev.ru 
5000Sigor@sysoev.ru         if (nxt_fast_path(ret != NXT_AGAIN)) {
5010Sigor@sysoev.ru 
5020Sigor@sysoev.ru             if (ret != NXT_ERROR) {
5030Sigor@sysoev.ru                 /* n == NXT_DECLINED: "\r" is not followed by "\n" */
5041Sigor@sysoev.ru                 nxt_log(task, NXT_LOG_ERR,
5051Sigor@sysoev.ru                         "upstream sent invalid header line: \"%*s\\r...\"",
5061Sigor@sysoev.ru                         fs->u.header.parse.header_end
5071Sigor@sysoev.ru                             - fs->u.header.parse.header_name_start,
5081Sigor@sysoev.ru                         fs->u.header.parse.header_name_start);
5090Sigor@sysoev.ru             }
5100Sigor@sysoev.ru 
5110Sigor@sysoev.ru             /* ret == NXT_ERROR */
5120Sigor@sysoev.ru 
5131Sigor@sysoev.ru             nxt_fastcgi_source_fail(task, fs);
5140Sigor@sysoev.ru             return;
5150Sigor@sysoev.ru         }
5160Sigor@sysoev.ru 
5170Sigor@sysoev.ru         b = b->next;
5180Sigor@sysoev.ru 
5190Sigor@sysoev.ru     } while (b != NULL);
5200Sigor@sysoev.ru }
5210Sigor@sysoev.ru 
5220Sigor@sysoev.ru 
5230Sigor@sysoev.ru static void
5241Sigor@sysoev.ru nxt_fastcgi_source_sync_buffer(nxt_task_t *task, nxt_fastcgi_source_t *fs,
5250Sigor@sysoev.ru     nxt_buf_t *b)
5260Sigor@sysoev.ru {
5270Sigor@sysoev.ru     if (nxt_buf_is_last(b)) {
5281Sigor@sysoev.ru         nxt_log(task, NXT_LOG_ERR, "upstream closed prematurely connection");
5290Sigor@sysoev.ru 
5300Sigor@sysoev.ru     } else {
5311Sigor@sysoev.ru         nxt_log(task, NXT_LOG_ERR, "%ui buffers %uz each are not "
5321Sigor@sysoev.ru                 "enough to process upstream response header",
5331Sigor@sysoev.ru                 fs->upstream->buffers.max, fs->upstream->buffers.size);
5340Sigor@sysoev.ru     }
5350Sigor@sysoev.ru 
5360Sigor@sysoev.ru     /* The stream source sends only the last and the nobuf sync buffer. */
5370Sigor@sysoev.ru 
5381Sigor@sysoev.ru     nxt_fastcgi_source_fail(task, fs);
5390Sigor@sysoev.ru }
5400Sigor@sysoev.ru 
5410Sigor@sysoev.ru 
5420Sigor@sysoev.ru static nxt_int_t
5431Sigor@sysoev.ru nxt_fastcgi_source_header_process(nxt_task_t *task, nxt_fastcgi_source_t *fs)
5440Sigor@sysoev.ru {
5450Sigor@sysoev.ru     size_t                     len;
5460Sigor@sysoev.ru     nxt_name_value_t           *nv;
5470Sigor@sysoev.ru     nxt_lvlhsh_query_t         lhq;
5480Sigor@sysoev.ru     nxt_http_header_parse_t    *hp;
5490Sigor@sysoev.ru     nxt_upstream_name_value_t  *unv;
5500Sigor@sysoev.ru 
5510Sigor@sysoev.ru     hp = &fs->u.header.parse;
5520Sigor@sysoev.ru 
5530Sigor@sysoev.ru     len = hp->header_name_end - hp->header_name_start;
5540Sigor@sysoev.ru 
5550Sigor@sysoev.ru     if (len > 255) {
5561Sigor@sysoev.ru         nxt_log(task, NXT_LOG_INFO,
5571Sigor@sysoev.ru                 "upstream sent too long header field name: \"%*s\"",
5581Sigor@sysoev.ru                 len, hp->header_name_start);
5590Sigor@sysoev.ru         return NXT_ERROR;
5600Sigor@sysoev.ru     }
5610Sigor@sysoev.ru 
5620Sigor@sysoev.ru     nv = nxt_list_add(fs->header_in.list);
5630Sigor@sysoev.ru     if (nxt_slow_path(nv == NULL)) {
5640Sigor@sysoev.ru         return NXT_ERROR;
5650Sigor@sysoev.ru     }
5660Sigor@sysoev.ru 
5670Sigor@sysoev.ru     nv->hash = hp->header_hash;
5680Sigor@sysoev.ru     nv->skip = 0;
5690Sigor@sysoev.ru     nv->name_len = len;
5700Sigor@sysoev.ru     nv->name_start = hp->header_name_start;
5710Sigor@sysoev.ru     nv->value_len = hp->header_end - hp->header_start;
5720Sigor@sysoev.ru     nv->value_start = hp->header_start;
5730Sigor@sysoev.ru 
5741Sigor@sysoev.ru     nxt_debug(task, "http header: \"%*s: %*s\"",
5751Sigor@sysoev.ru               nv->name_len, nv->name_start, nv->value_len, nv->value_start);
5760Sigor@sysoev.ru 
5770Sigor@sysoev.ru     lhq.key_hash = nv->hash;
5780Sigor@sysoev.ru     lhq.key.len = nv->name_len;
5790Sigor@sysoev.ru     lhq.key.data = nv->name_start;
5800Sigor@sysoev.ru     lhq.proto = &nxt_upstream_header_hash_proto;
5810Sigor@sysoev.ru 
5820Sigor@sysoev.ru     if (nxt_lvlhsh_find(&fs->header_in.hash, &lhq) == NXT_OK) {
5830Sigor@sysoev.ru         unv = lhq.value;
5840Sigor@sysoev.ru 
5850Sigor@sysoev.ru         if (unv->handler(fs->upstream, nv) == NXT_OK) {
5860Sigor@sysoev.ru             return NXT_ERROR;
5870Sigor@sysoev.ru         }
5880Sigor@sysoev.ru     }
5890Sigor@sysoev.ru 
5900Sigor@sysoev.ru     return NXT_OK;
5910Sigor@sysoev.ru }
5920Sigor@sysoev.ru 
5930Sigor@sysoev.ru 
5940Sigor@sysoev.ru static const nxt_upstream_name_value_t  nxt_fastcgi_source_headers[]
5950Sigor@sysoev.ru     nxt_aligned(32) =
5960Sigor@sysoev.ru {
5970Sigor@sysoev.ru     { nxt_fastcgi_source_status,
5980Sigor@sysoev.ru       nxt_upstream_name_value("status") },
5990Sigor@sysoev.ru 
6000Sigor@sysoev.ru     { nxt_fastcgi_source_content_length,
6010Sigor@sysoev.ru       nxt_upstream_name_value("content-length") },
6020Sigor@sysoev.ru };
6030Sigor@sysoev.ru 
6040Sigor@sysoev.ru 
6050Sigor@sysoev.ru nxt_int_t
60665Sigor@sysoev.ru nxt_fastcgi_source_hash_create(nxt_mp_t *mp, nxt_lvlhsh_t *lh)
6070Sigor@sysoev.ru {
6080Sigor@sysoev.ru     return nxt_upstream_header_hash_add(mp, lh, nxt_fastcgi_source_headers,
6090Sigor@sysoev.ru                                         nxt_nitems(nxt_fastcgi_source_headers));
6100Sigor@sysoev.ru }
6110Sigor@sysoev.ru 
6120Sigor@sysoev.ru 
6130Sigor@sysoev.ru static nxt_int_t
6140Sigor@sysoev.ru nxt_fastcgi_source_status(nxt_upstream_source_t *us, nxt_name_value_t *nv)
6150Sigor@sysoev.ru {
6160Sigor@sysoev.ru     nxt_int_t             n;
6170Sigor@sysoev.ru     nxt_str_t             s;
6180Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
6190Sigor@sysoev.ru 
6200Sigor@sysoev.ru     s.len = nv->value_len;
6210Sigor@sysoev.ru     s.data = nv->value_start;
6220Sigor@sysoev.ru 
6230Sigor@sysoev.ru     n = nxt_str_int_parse(&s);
6240Sigor@sysoev.ru 
6250Sigor@sysoev.ru     if (nxt_fast_path(n > 0)) {
6260Sigor@sysoev.ru         fs = us->protocol_source;
6270Sigor@sysoev.ru         fs->header_in.status = n;
6280Sigor@sysoev.ru         return NXT_OK;
6290Sigor@sysoev.ru     }
6300Sigor@sysoev.ru 
6310Sigor@sysoev.ru     return NXT_ERROR;
6320Sigor@sysoev.ru }
6330Sigor@sysoev.ru 
6340Sigor@sysoev.ru 
6350Sigor@sysoev.ru static nxt_int_t
6360Sigor@sysoev.ru nxt_fastcgi_source_content_length(nxt_upstream_source_t *us,
6370Sigor@sysoev.ru     nxt_name_value_t *nv)
6380Sigor@sysoev.ru {
6390Sigor@sysoev.ru     nxt_off_t             length;
6400Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
6410Sigor@sysoev.ru 
6420Sigor@sysoev.ru     length = nxt_off_t_parse(nv->value_start, nv->value_len);
6430Sigor@sysoev.ru 
6440Sigor@sysoev.ru     if (nxt_fast_path(length > 0)) {
6450Sigor@sysoev.ru         fs = us->protocol_source;
6460Sigor@sysoev.ru         fs->header_in.content_length = length;
6470Sigor@sysoev.ru         return NXT_OK;
6480Sigor@sysoev.ru     }
6490Sigor@sysoev.ru 
6500Sigor@sysoev.ru     return NXT_ERROR;
6510Sigor@sysoev.ru }
6520Sigor@sysoev.ru 
6530Sigor@sysoev.ru 
6540Sigor@sysoev.ru static void
6550Sigor@sysoev.ru nxt_fastcgi_source_header_ready(nxt_fastcgi_source_t *fs, nxt_buf_t *b)
6560Sigor@sysoev.ru {
6570Sigor@sysoev.ru     /*
6580Sigor@sysoev.ru      * Change the FastCGI source filter chain:
6590Sigor@sysoev.ru      *   stream source | FastCGI record filter | FastCGI body filter
6600Sigor@sysoev.ru      */
6610Sigor@sysoev.ru     fs->record.next.filter = nxt_fastcgi_source_body_filter;
6620Sigor@sysoev.ru 
6630Sigor@sysoev.ru     if (nxt_buf_mem_used_size(&b->mem) != 0) {
6640Sigor@sysoev.ru         fs->rest = b;
6650Sigor@sysoev.ru     }
6660Sigor@sysoev.ru 
6670Sigor@sysoev.ru     if (fs->header_in.status == 0) {
6680Sigor@sysoev.ru         /* The "200 OK" status by default. */
6690Sigor@sysoev.ru         fs->header_in.status = 200;
6700Sigor@sysoev.ru     }
6710Sigor@sysoev.ru 
6720Sigor@sysoev.ru     fs->upstream->state->ready_handler(fs);
6730Sigor@sysoev.ru }
6740Sigor@sysoev.ru 
6750Sigor@sysoev.ru 
6760Sigor@sysoev.ru /*
6770Sigor@sysoev.ru  * The FastCGI source body filter accumulates first body buffers before the next
6780Sigor@sysoev.ru  * filter will be established and sets completion handler for the last buffer.
6790Sigor@sysoev.ru  */
6800Sigor@sysoev.ru 
6810Sigor@sysoev.ru static void
6821Sigor@sysoev.ru nxt_fastcgi_source_body_filter(nxt_task_t *task, void *obj, void *data)
6830Sigor@sysoev.ru {
6840Sigor@sysoev.ru     nxt_buf_t             *b, *in;
6850Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
6860Sigor@sysoev.ru 
6870Sigor@sysoev.ru     fs = obj;
6880Sigor@sysoev.ru     in = data;
6890Sigor@sysoev.ru 
6901Sigor@sysoev.ru     nxt_debug(task, "fastcgi source body filter");
6910Sigor@sysoev.ru 
6920Sigor@sysoev.ru     for (b = in; b != NULL; b = b->next) {
6930Sigor@sysoev.ru 
6940Sigor@sysoev.ru         if (nxt_buf_is_last(b)) {
6950Sigor@sysoev.ru             b->data = fs->upstream->data;
6960Sigor@sysoev.ru             b->completion_handler = fs->upstream->state->completion_handler;
6970Sigor@sysoev.ru         }
6980Sigor@sysoev.ru     }
6990Sigor@sysoev.ru 
7000Sigor@sysoev.ru     if (fs->next != NULL) {
7011Sigor@sysoev.ru         nxt_source_filter(task->thread, fs->upstream->work_queue, task,
7021Sigor@sysoev.ru                           fs->next, in);
7030Sigor@sysoev.ru         return;
7040Sigor@sysoev.ru     }
7050Sigor@sysoev.ru 
7060Sigor@sysoev.ru     nxt_buf_chain_add(&fs->rest, in);
7070Sigor@sysoev.ru }
7080Sigor@sysoev.ru 
7090Sigor@sysoev.ru 
7100Sigor@sysoev.ru static nxt_buf_t *
7110Sigor@sysoev.ru nxt_fastcgi_source_last_buf(nxt_fastcgi_parse_t *fp)
7120Sigor@sysoev.ru {
7130Sigor@sysoev.ru     nxt_buf_t             *b;
7140Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
7150Sigor@sysoev.ru 
7160Sigor@sysoev.ru     fs = fp->data;
7170Sigor@sysoev.ru 
7180Sigor@sysoev.ru     b = nxt_buf_sync_alloc(fp->mem_pool, NXT_BUF_SYNC_LAST);
7190Sigor@sysoev.ru 
7200Sigor@sysoev.ru     if (nxt_fast_path(b != NULL)) {
7210Sigor@sysoev.ru         b->data = fs->upstream->data;
7220Sigor@sysoev.ru         b->completion_handler = fs->upstream->state->completion_handler;
7230Sigor@sysoev.ru     }
7240Sigor@sysoev.ru 
7250Sigor@sysoev.ru     return b;
7260Sigor@sysoev.ru }
7270Sigor@sysoev.ru 
7280Sigor@sysoev.ru 
7290Sigor@sysoev.ru static void
7301Sigor@sysoev.ru nxt_fastcgi_source_error(nxt_task_t *task, nxt_stream_source_t *stream)
7310Sigor@sysoev.ru {
7320Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
7330Sigor@sysoev.ru 
7340Sigor@sysoev.ru     nxt_thread_log_debug("fastcgi source error");
7350Sigor@sysoev.ru 
7360Sigor@sysoev.ru     fs = stream->upstream->protocol_source;
7370Sigor@sysoev.ru 
7381Sigor@sysoev.ru     nxt_fastcgi_source_fail(task, fs);
7390Sigor@sysoev.ru }
7400Sigor@sysoev.ru 
7410Sigor@sysoev.ru 
7420Sigor@sysoev.ru static void
7431Sigor@sysoev.ru nxt_fastcgi_source_fail(nxt_task_t *task, nxt_fastcgi_source_t *fs)
7440Sigor@sysoev.ru {
7451Sigor@sysoev.ru     nxt_debug(task, "fastcgi source fail");
7460Sigor@sysoev.ru 
7470Sigor@sysoev.ru     /* TODO: fail, next upstream, or bad gateway */
7480Sigor@sysoev.ru 
7491Sigor@sysoev.ru     fs->upstream->state->error_handler(task, fs, NULL);
7500Sigor@sysoev.ru }
751