xref: /unit/src/nxt_fastcgi_source.c (revision 521)
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 
521Sigor@sysoev.ru static void nxt_fastcgi_source_record_filter(nxt_task_t *task, void *obj,
530Sigor@sysoev.ru     void *data);
541Sigor@sysoev.ru static void nxt_fastcgi_source_record_error(nxt_task_t *task, void *obj,
550Sigor@sysoev.ru     void *data);
561Sigor@sysoev.ru static void nxt_fastcgi_source_header_filter(nxt_task_t *task, void *obj,
570Sigor@sysoev.ru     void *data);
581Sigor@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 
611Sigor@sysoev.ru static nxt_int_t nxt_fastcgi_source_header_process(nxt_task_t *task,
621Sigor@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);
701Sigor@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);
731Sigor@sysoev.ru static void nxt_fastcgi_source_error(nxt_task_t *task,
741Sigor@sysoev.ru     nxt_stream_source_t *stream);
751Sigor@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
1221Sigor@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 
12865Sigor@sysoev.ru     fs = nxt_mp_zget(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) {
14865Sigor@sysoev.ru         stream = nxt_mp_zget(us->buffers.mem_pool, sizeof(nxt_stream_source_t));
1490Sigor@sysoev.ru         if (nxt_slow_path(stream == NULL)) {
1500Sigor@sysoev.ru             goto fail;
1510Sigor@sysoev.ru         }
1520Sigor@sysoev.ru 
1530Sigor@sysoev.ru         us->stream = stream;
1540Sigor@sysoev.ru         stream->upstream = us;
1550Sigor@sysoev.ru 
1560Sigor@sysoev.ru     } else {
1570Sigor@sysoev.ru         nxt_memzero(stream, sizeof(nxt_stream_source_t));
1580Sigor@sysoev.ru     }
1590Sigor@sysoev.ru 
1600Sigor@sysoev.ru     /*
1610Sigor@sysoev.ru      * Create the FastCGI source filter chain:
1620Sigor@sysoev.ru      *   stream source | FastCGI record filter | FastCGI HTTP header filter
1630Sigor@sysoev.ru      */
1640Sigor@sysoev.ru     stream->next = &fs->query;
1650Sigor@sysoev.ru     stream->error_handler = nxt_fastcgi_source_error;
1660Sigor@sysoev.ru 
1670Sigor@sysoev.ru     fs->record.next.context = fs;
1680Sigor@sysoev.ru     fs->record.next.filter = nxt_fastcgi_source_header_filter;
1690Sigor@sysoev.ru 
1700Sigor@sysoev.ru     fs->record.parse.last_buf = nxt_fastcgi_source_last_buf;
1710Sigor@sysoev.ru     fs->record.parse.data = fs;
1720Sigor@sysoev.ru     fs->record.parse.mem_pool = us->buffers.mem_pool;
1730Sigor@sysoev.ru 
1740Sigor@sysoev.ru     fs->query.context = &fs->record.parse;
1750Sigor@sysoev.ru     fs->query.filter = nxt_fastcgi_source_record_filter;
1760Sigor@sysoev.ru 
1770Sigor@sysoev.ru     fs->header_in.content_length = -1;
1780Sigor@sysoev.ru 
1790Sigor@sysoev.ru     stream->out = nxt_fastcgi_request_create(fs);
1800Sigor@sysoev.ru 
1810Sigor@sysoev.ru     if (nxt_fast_path(stream->out != NULL)) {
1820Sigor@sysoev.ru         nxt_memzero(&fs->u.header, sizeof(nxt_http_split_header_parse_t));
1830Sigor@sysoev.ru         fs->u.header.mem_pool = fs->upstream->buffers.mem_pool;
1840Sigor@sysoev.ru 
1851Sigor@sysoev.ru         nxt_stream_source_connect(task, stream);
1860Sigor@sysoev.ru         return;
1870Sigor@sysoev.ru     }
1880Sigor@sysoev.ru 
1890Sigor@sysoev.ru fail:
1900Sigor@sysoev.ru 
1911Sigor@sysoev.ru     nxt_fastcgi_source_fail(task, fs);
1920Sigor@sysoev.ru }
1930Sigor@sysoev.ru 
1940Sigor@sysoev.ru 
1950Sigor@sysoev.ru static nxt_buf_t *
1960Sigor@sysoev.ru nxt_fastcgi_request_create(nxt_fastcgi_source_t *fs)
1970Sigor@sysoev.ru {
1980Sigor@sysoev.ru     u_char               *p, *record_length;
1990Sigor@sysoev.ru     size_t               len, size, max_record_size;
2000Sigor@sysoev.ru     nxt_int_t            ret;
2010Sigor@sysoev.ru     nxt_buf_t            *b, *req, **prev;
2020Sigor@sysoev.ru     nxt_bool_t           begin_request;
2030Sigor@sysoev.ru     nxt_fastcgi_param_t  param;
2040Sigor@sysoev.ru 
2050Sigor@sysoev.ru     nxt_thread_log_debug("fastcgi request");
2060Sigor@sysoev.ru 
2070Sigor@sysoev.ru     begin_request = 1;
2080Sigor@sysoev.ru     param.len = 0;
2090Sigor@sysoev.ru     prev = &req;
2100Sigor@sysoev.ru 
2110Sigor@sysoev.ru new_buffer:
2120Sigor@sysoev.ru 
2130Sigor@sysoev.ru     ret = nxt_buf_pool_mem_alloc(&fs->upstream->buffers, 0);
2140Sigor@sysoev.ru     if (nxt_slow_path(ret != NXT_OK)) {
2150Sigor@sysoev.ru         return NULL;
2160Sigor@sysoev.ru     }
2170Sigor@sysoev.ru 
2180Sigor@sysoev.ru     b = fs->upstream->buffers.current;
2190Sigor@sysoev.ru     fs->upstream->buffers.current = NULL;
2200Sigor@sysoev.ru 
2210Sigor@sysoev.ru     *prev = b;
2220Sigor@sysoev.ru     prev = &b->next;
2230Sigor@sysoev.ru 
2240Sigor@sysoev.ru new_record:
2250Sigor@sysoev.ru 
2260Sigor@sysoev.ru     size = b->mem.end - b->mem.free;
2270Sigor@sysoev.ru     size = nxt_align_size(size, 8) - 8;
2280Sigor@sysoev.ru     /* The maximal FastCGI record content size is 65535.  65528 is 64K - 8. */
2290Sigor@sysoev.ru     max_record_size = nxt_min(65528, size);
2300Sigor@sysoev.ru 
2310Sigor@sysoev.ru     p = b->mem.free;
2320Sigor@sysoev.ru 
2330Sigor@sysoev.ru     if (begin_request) {
2340Sigor@sysoev.ru         /* TODO: fastcgi keep conn in flags. */
2350Sigor@sysoev.ru         p = nxt_cpymem(p, nxt_fastcgi_begin_request, 16);
2360Sigor@sysoev.ru         max_record_size -= 16;
2370Sigor@sysoev.ru         begin_request = 0;
2380Sigor@sysoev.ru     }
2390Sigor@sysoev.ru 
2400Sigor@sysoev.ru     b->mem.free = nxt_cpymem(p, nxt_fastcgi_params_record, 8);
2410Sigor@sysoev.ru     record_length = &p[4];
2420Sigor@sysoev.ru     size = 0;
2430Sigor@sysoev.ru 
2440Sigor@sysoev.ru     for ( ;; ) {
2450Sigor@sysoev.ru         if (param.len == 0) {
2460Sigor@sysoev.ru             ret = nxt_fastcgi_next_param(fs, &param);
2470Sigor@sysoev.ru 
2480Sigor@sysoev.ru             if (nxt_slow_path(ret != NXT_OK)) {
2490Sigor@sysoev.ru 
2500Sigor@sysoev.ru                 if (nxt_slow_path(ret == NXT_ERROR)) {
2510Sigor@sysoev.ru                     return NULL;
2520Sigor@sysoev.ru                 }
2530Sigor@sysoev.ru 
2540Sigor@sysoev.ru                 /* ret == NXT_DONE */
2550Sigor@sysoev.ru                 break;
2560Sigor@sysoev.ru             }
2570Sigor@sysoev.ru         }
2580Sigor@sysoev.ru 
2590Sigor@sysoev.ru         len = max_record_size;
2600Sigor@sysoev.ru 
2610Sigor@sysoev.ru         if (nxt_fast_path(len >= param.len)) {
2620Sigor@sysoev.ru             len = param.len;
2630Sigor@sysoev.ru             param.len = 0;
2640Sigor@sysoev.ru 
2650Sigor@sysoev.ru         } else {
2660Sigor@sysoev.ru             param.len -= len;
2670Sigor@sysoev.ru         }
2680Sigor@sysoev.ru 
2690Sigor@sysoev.ru         nxt_thread_log_debug("fastcgi copy len:%uz", len);
2700Sigor@sysoev.ru 
2710Sigor@sysoev.ru         b->mem.free = nxt_cpymem(b->mem.free, param.buf, len);
2720Sigor@sysoev.ru 
2730Sigor@sysoev.ru         size += len;
2740Sigor@sysoev.ru         max_record_size -= len;
2750Sigor@sysoev.ru 
2760Sigor@sysoev.ru         if (nxt_slow_path(param.len != 0)) {
2770Sigor@sysoev.ru             /* The record is full. */
2780Sigor@sysoev.ru 
2790Sigor@sysoev.ru             param.buf += len;
2800Sigor@sysoev.ru 
2810Sigor@sysoev.ru             nxt_thread_log_debug("fastcgi content size:%uz", size);
2820Sigor@sysoev.ru 
2830Sigor@sysoev.ru             nxt_fastcgi_set_record_length(record_length, size);
2840Sigor@sysoev.ru 
2850Sigor@sysoev.ru             /* The minimal size of aligned record with content is 16 bytes. */
2860Sigor@sysoev.ru             if (b->mem.end - b->mem.free >= 16) {
2870Sigor@sysoev.ru                 goto new_record;
2880Sigor@sysoev.ru             }
2890Sigor@sysoev.ru 
2900Sigor@sysoev.ru             nxt_thread_log_debug("\"%*s\"", b->mem.free - b->mem.pos,
2910Sigor@sysoev.ru                                  b->mem.pos);
2920Sigor@sysoev.ru             goto new_buffer;
2930Sigor@sysoev.ru         }
2940Sigor@sysoev.ru     }
2950Sigor@sysoev.ru 
2960Sigor@sysoev.ru     nxt_thread_log_debug("fastcgi content size:%uz", size);
2970Sigor@sysoev.ru 
2980Sigor@sysoev.ru     nxt_fastcgi_set_record_length(record_length, size);
2990Sigor@sysoev.ru 
3000Sigor@sysoev.ru     /* A padding length. */
3010Sigor@sysoev.ru     size = 8 - size % 8;
3020Sigor@sysoev.ru     record_length[2] = (u_char) size;
3030Sigor@sysoev.ru     nxt_memzero(b->mem.free, size);
3040Sigor@sysoev.ru     b->mem.free += size;
3050Sigor@sysoev.ru 
3060Sigor@sysoev.ru     nxt_thread_log_debug("fastcgi padding:%uz", size);
3070Sigor@sysoev.ru 
3080Sigor@sysoev.ru     if (b->mem.end - b->mem.free < 16) {
3090Sigor@sysoev.ru         nxt_thread_log_debug("\"%*s\"", b->mem.free - b->mem.pos, b->mem.pos);
3100Sigor@sysoev.ru 
3110Sigor@sysoev.ru         b = nxt_buf_mem_alloc(fs->upstream->buffers.mem_pool, 16, 0);
3120Sigor@sysoev.ru         if (nxt_slow_path(b == NULL)) {
3130Sigor@sysoev.ru             return NULL;
3140Sigor@sysoev.ru         }
3150Sigor@sysoev.ru 
3160Sigor@sysoev.ru         *prev = b;
3170Sigor@sysoev.ru         prev = &b->next;
3180Sigor@sysoev.ru     }
3190Sigor@sysoev.ru 
3200Sigor@sysoev.ru     /* The end of FastCGI params. */
3210Sigor@sysoev.ru     p = nxt_cpymem(b->mem.free, nxt_fastcgi_params_record, 8);
3220Sigor@sysoev.ru 
3230Sigor@sysoev.ru     /* The end of FastCGI stdin. */
3240Sigor@sysoev.ru     b->mem.free = nxt_cpymem(p, nxt_fastcgi_stdin_record, 8);
3250Sigor@sysoev.ru 
3260Sigor@sysoev.ru     nxt_thread_log_debug("\"%*s\"", b->mem.free - b->mem.pos, b->mem.pos);
3270Sigor@sysoev.ru 
3280Sigor@sysoev.ru     return req;
3290Sigor@sysoev.ru }
3300Sigor@sysoev.ru 
3310Sigor@sysoev.ru 
3320Sigor@sysoev.ru static nxt_int_t
3330Sigor@sysoev.ru nxt_fastcgi_next_param(nxt_fastcgi_source_t *fs, nxt_fastcgi_param_t *param)
3340Sigor@sysoev.ru {
3350Sigor@sysoev.ru     nxt_int_t  ret;
3360Sigor@sysoev.ru 
3370Sigor@sysoev.ru     enum {
338*521Szelenkov@nginx.com         sw_name_length = 0,
339*521Szelenkov@nginx.com         sw_value_length,
340*521Szelenkov@nginx.com         sw_name,
341*521Szelenkov@nginx.com         sw_value,
3420Sigor@sysoev.ru     };
3430Sigor@sysoev.ru 
3440Sigor@sysoev.ru     switch (fs->state) {
3450Sigor@sysoev.ru 
3460Sigor@sysoev.ru     case sw_name_length:
3470Sigor@sysoev.ru         ret = fs->request_create(fs);
3480Sigor@sysoev.ru 
3490Sigor@sysoev.ru         if (nxt_slow_path(ret != NXT_OK)) {
3500Sigor@sysoev.ru             return ret;
3510Sigor@sysoev.ru         }
3520Sigor@sysoev.ru 
3530Sigor@sysoev.ru         nxt_thread_log_debug("fastcgi param \"%V: %V\"",
3540Sigor@sysoev.ru                              &fs->u.request.name, &fs->u.request.value);
3550Sigor@sysoev.ru 
3560Sigor@sysoev.ru         fs->state = sw_value_length;
3570Sigor@sysoev.ru         param->buf = param->length;
3580Sigor@sysoev.ru         param->len = nxt_fastcgi_param_length(param->length,
3590Sigor@sysoev.ru                                               fs->u.request.name.len);
3600Sigor@sysoev.ru         break;
3610Sigor@sysoev.ru 
3620Sigor@sysoev.ru     case sw_value_length:
3630Sigor@sysoev.ru         fs->state = sw_name;
3640Sigor@sysoev.ru         param->buf = param->length;
3650Sigor@sysoev.ru         param->len = nxt_fastcgi_param_length(param->length,
3660Sigor@sysoev.ru                                               fs->u.request.value.len);
3670Sigor@sysoev.ru         break;
3680Sigor@sysoev.ru 
3690Sigor@sysoev.ru     case sw_name:
3700Sigor@sysoev.ru         fs->state = sw_value;
3710Sigor@sysoev.ru         param->buf = fs->u.request.name.data;
3720Sigor@sysoev.ru         param->len = fs->u.request.name.len;
3730Sigor@sysoev.ru         break;
3740Sigor@sysoev.ru 
3750Sigor@sysoev.ru     case sw_value:
3760Sigor@sysoev.ru         fs->state = sw_name_length;
3770Sigor@sysoev.ru         param->buf = fs->u.request.value.data;
3780Sigor@sysoev.ru         param->len = fs->u.request.value.len;
3790Sigor@sysoev.ru         break;
3800Sigor@sysoev.ru     }
3810Sigor@sysoev.ru 
3820Sigor@sysoev.ru     return NXT_OK;
3830Sigor@sysoev.ru }
3840Sigor@sysoev.ru 
3850Sigor@sysoev.ru 
3860Sigor@sysoev.ru static void
3871Sigor@sysoev.ru nxt_fastcgi_source_record_filter(nxt_task_t *task, void *obj, void *data)
3880Sigor@sysoev.ru {
3890Sigor@sysoev.ru     size_t                       size;
3900Sigor@sysoev.ru     u_char                       *p;
3910Sigor@sysoev.ru     nxt_buf_t                    *b, *in;
3920Sigor@sysoev.ru     nxt_fastcgi_source_t         *fs;
3930Sigor@sysoev.ru     nxt_fastcgi_source_record_t  *fsr;
3940Sigor@sysoev.ru 
3950Sigor@sysoev.ru     fsr = obj;
3960Sigor@sysoev.ru     in = data;
3970Sigor@sysoev.ru 
3981Sigor@sysoev.ru     nxt_debug(task, "fastcgi source record filter");
3990Sigor@sysoev.ru 
4000Sigor@sysoev.ru     if (nxt_slow_path(fsr->parse.done)) {
4010Sigor@sysoev.ru         return;
4020Sigor@sysoev.ru     }
4030Sigor@sysoev.ru 
4041Sigor@sysoev.ru     nxt_fastcgi_record_parse(task, &fsr->parse, in);
4050Sigor@sysoev.ru 
4060Sigor@sysoev.ru     fs = nxt_container_of(fsr, nxt_fastcgi_source_t, record);
4070Sigor@sysoev.ru 
4080Sigor@sysoev.ru     if (fsr->parse.error) {
4091Sigor@sysoev.ru         nxt_fastcgi_source_fail(task, fs);
4100Sigor@sysoev.ru         return;
4110Sigor@sysoev.ru     }
4120Sigor@sysoev.ru 
4130Sigor@sysoev.ru     if (fsr->parse.fastcgi_error) {
4140Sigor@sysoev.ru         /*
4150Sigor@sysoev.ru          * Output all parsed before a FastCGI record error and close upstream.
4160Sigor@sysoev.ru          */
4171Sigor@sysoev.ru         nxt_thread_current_work_queue_add(task->thread,
4181Sigor@sysoev.ru                                           nxt_fastcgi_source_record_error,
4191Sigor@sysoev.ru                                           task, fs, NULL);
4200Sigor@sysoev.ru     }
4210Sigor@sysoev.ru 
4220Sigor@sysoev.ru     /* Log FastCGI stderr output. */
4230Sigor@sysoev.ru 
4240Sigor@sysoev.ru     for (b = fsr->parse.out[1]; b != NULL; b = b->next) {
4250Sigor@sysoev.ru 
4260Sigor@sysoev.ru         for (p = b->mem.free - 1; p >= b->mem.pos; p--) {
4270Sigor@sysoev.ru             if (*p != NXT_CR && *p != NXT_LF) {
4280Sigor@sysoev.ru                 break;
4290Sigor@sysoev.ru             }
4300Sigor@sysoev.ru         }
4310Sigor@sysoev.ru 
4320Sigor@sysoev.ru         size = (p + 1) - b->mem.pos;
4330Sigor@sysoev.ru 
4340Sigor@sysoev.ru         if (size != 0) {
4351Sigor@sysoev.ru             nxt_log(task, NXT_LOG_ERR,
4361Sigor@sysoev.ru                     "upstream sent in FastCGI stderr: \"%*s\"",
4371Sigor@sysoev.ru                     size, b->mem.pos);
4380Sigor@sysoev.ru         }
4390Sigor@sysoev.ru 
4401Sigor@sysoev.ru         b->completion_handler(task, b, b->parent);
4410Sigor@sysoev.ru     }
4420Sigor@sysoev.ru 
4430Sigor@sysoev.ru     /* Process FastCGI stdout output. */
4440Sigor@sysoev.ru 
4450Sigor@sysoev.ru     if (fsr->parse.out[0] != NULL) {
4461Sigor@sysoev.ru         nxt_source_filter(task->thread, fs->upstream->work_queue, task,
4471Sigor@sysoev.ru                           &fsr->next, fsr->parse.out[0]);
4480Sigor@sysoev.ru     }
4490Sigor@sysoev.ru }
4500Sigor@sysoev.ru 
4510Sigor@sysoev.ru 
4520Sigor@sysoev.ru static void
4531Sigor@sysoev.ru nxt_fastcgi_source_record_error(nxt_task_t *task, void *obj, void *data)
4540Sigor@sysoev.ru {
4550Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
4560Sigor@sysoev.ru 
4570Sigor@sysoev.ru     fs = obj;
4580Sigor@sysoev.ru 
4591Sigor@sysoev.ru     nxt_fastcgi_source_fail(task, fs);
4600Sigor@sysoev.ru }
4610Sigor@sysoev.ru 
4620Sigor@sysoev.ru 
4630Sigor@sysoev.ru static void
4641Sigor@sysoev.ru nxt_fastcgi_source_header_filter(nxt_task_t *task, void *obj, void *data)
4650Sigor@sysoev.ru {
4660Sigor@sysoev.ru     nxt_int_t             ret;
4670Sigor@sysoev.ru     nxt_buf_t             *b;
4680Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
4690Sigor@sysoev.ru 
4700Sigor@sysoev.ru     fs = obj;
4710Sigor@sysoev.ru     b = data;
4720Sigor@sysoev.ru 
4730Sigor@sysoev.ru     do {
4741Sigor@sysoev.ru         nxt_debug(task, "fastcgi source header filter");
4750Sigor@sysoev.ru 
4760Sigor@sysoev.ru         if (nxt_slow_path(nxt_buf_is_sync(b))) {
4771Sigor@sysoev.ru             nxt_fastcgi_source_sync_buffer(task, fs, b);
4780Sigor@sysoev.ru             return;
4790Sigor@sysoev.ru         }
4800Sigor@sysoev.ru 
4810Sigor@sysoev.ru         for ( ;; ) {
4820Sigor@sysoev.ru             ret = nxt_http_split_header_parse(&fs->u.header, &b->mem);
4830Sigor@sysoev.ru 
4840Sigor@sysoev.ru             if (nxt_slow_path(ret != NXT_OK)) {
4850Sigor@sysoev.ru                 break;
4860Sigor@sysoev.ru             }
4870Sigor@sysoev.ru 
4881Sigor@sysoev.ru             ret = nxt_fastcgi_source_header_process(task, fs);
4890Sigor@sysoev.ru 
4900Sigor@sysoev.ru             if (nxt_slow_path(ret != NXT_OK)) {
4910Sigor@sysoev.ru                 break;
4920Sigor@sysoev.ru             }
4930Sigor@sysoev.ru         }
4940Sigor@sysoev.ru 
4950Sigor@sysoev.ru         if (nxt_fast_path(ret == NXT_DONE)) {
4961Sigor@sysoev.ru             nxt_debug(task, "fastcgi source header done");
4970Sigor@sysoev.ru             nxt_fastcgi_source_header_ready(fs, b);
4980Sigor@sysoev.ru             return;
4990Sigor@sysoev.ru         }
5000Sigor@sysoev.ru 
5010Sigor@sysoev.ru         if (nxt_fast_path(ret != NXT_AGAIN)) {
5020Sigor@sysoev.ru 
5030Sigor@sysoev.ru             if (ret != NXT_ERROR) {
5040Sigor@sysoev.ru                 /* n == NXT_DECLINED: "\r" is not followed by "\n" */
5051Sigor@sysoev.ru                 nxt_log(task, NXT_LOG_ERR,
5061Sigor@sysoev.ru                         "upstream sent invalid header line: \"%*s\\r...\"",
5071Sigor@sysoev.ru                         fs->u.header.parse.header_end
5081Sigor@sysoev.ru                             - fs->u.header.parse.header_name_start,
5091Sigor@sysoev.ru                         fs->u.header.parse.header_name_start);
5100Sigor@sysoev.ru             }
5110Sigor@sysoev.ru 
5120Sigor@sysoev.ru             /* ret == NXT_ERROR */
5130Sigor@sysoev.ru 
5141Sigor@sysoev.ru             nxt_fastcgi_source_fail(task, fs);
5150Sigor@sysoev.ru             return;
5160Sigor@sysoev.ru         }
5170Sigor@sysoev.ru 
5180Sigor@sysoev.ru         b = b->next;
5190Sigor@sysoev.ru 
5200Sigor@sysoev.ru     } while (b != NULL);
5210Sigor@sysoev.ru }
5220Sigor@sysoev.ru 
5230Sigor@sysoev.ru 
5240Sigor@sysoev.ru static void
5251Sigor@sysoev.ru nxt_fastcgi_source_sync_buffer(nxt_task_t *task, nxt_fastcgi_source_t *fs,
5260Sigor@sysoev.ru     nxt_buf_t *b)
5270Sigor@sysoev.ru {
5280Sigor@sysoev.ru     if (nxt_buf_is_last(b)) {
5291Sigor@sysoev.ru         nxt_log(task, NXT_LOG_ERR, "upstream closed prematurely connection");
5300Sigor@sysoev.ru 
5310Sigor@sysoev.ru     } else {
5321Sigor@sysoev.ru         nxt_log(task, NXT_LOG_ERR, "%ui buffers %uz each are not "
5331Sigor@sysoev.ru                 "enough to process upstream response header",
5341Sigor@sysoev.ru                 fs->upstream->buffers.max, fs->upstream->buffers.size);
5350Sigor@sysoev.ru     }
5360Sigor@sysoev.ru 
5370Sigor@sysoev.ru     /* The stream source sends only the last and the nobuf sync buffer. */
5380Sigor@sysoev.ru 
5391Sigor@sysoev.ru     nxt_fastcgi_source_fail(task, fs);
5400Sigor@sysoev.ru }
5410Sigor@sysoev.ru 
5420Sigor@sysoev.ru 
5430Sigor@sysoev.ru static nxt_int_t
5441Sigor@sysoev.ru nxt_fastcgi_source_header_process(nxt_task_t *task, nxt_fastcgi_source_t *fs)
5450Sigor@sysoev.ru {
5460Sigor@sysoev.ru     size_t                     len;
5470Sigor@sysoev.ru     nxt_name_value_t           *nv;
5480Sigor@sysoev.ru     nxt_lvlhsh_query_t         lhq;
5490Sigor@sysoev.ru     nxt_http_header_parse_t    *hp;
5500Sigor@sysoev.ru     nxt_upstream_name_value_t  *unv;
5510Sigor@sysoev.ru 
5520Sigor@sysoev.ru     hp = &fs->u.header.parse;
5530Sigor@sysoev.ru 
5540Sigor@sysoev.ru     len = hp->header_name_end - hp->header_name_start;
5550Sigor@sysoev.ru 
5560Sigor@sysoev.ru     if (len > 255) {
5571Sigor@sysoev.ru         nxt_log(task, NXT_LOG_INFO,
5581Sigor@sysoev.ru                 "upstream sent too long header field name: \"%*s\"",
5591Sigor@sysoev.ru                 len, hp->header_name_start);
5600Sigor@sysoev.ru         return NXT_ERROR;
5610Sigor@sysoev.ru     }
5620Sigor@sysoev.ru 
5630Sigor@sysoev.ru     nv = nxt_list_add(fs->header_in.list);
5640Sigor@sysoev.ru     if (nxt_slow_path(nv == NULL)) {
5650Sigor@sysoev.ru         return NXT_ERROR;
5660Sigor@sysoev.ru     }
5670Sigor@sysoev.ru 
5680Sigor@sysoev.ru     nv->hash = hp->header_hash;
5690Sigor@sysoev.ru     nv->skip = 0;
5700Sigor@sysoev.ru     nv->name_len = len;
5710Sigor@sysoev.ru     nv->name_start = hp->header_name_start;
5720Sigor@sysoev.ru     nv->value_len = hp->header_end - hp->header_start;
5730Sigor@sysoev.ru     nv->value_start = hp->header_start;
5740Sigor@sysoev.ru 
5751Sigor@sysoev.ru     nxt_debug(task, "http header: \"%*s: %*s\"",
5761Sigor@sysoev.ru               nv->name_len, nv->name_start, nv->value_len, nv->value_start);
5770Sigor@sysoev.ru 
5780Sigor@sysoev.ru     lhq.key_hash = nv->hash;
5790Sigor@sysoev.ru     lhq.key.len = nv->name_len;
5800Sigor@sysoev.ru     lhq.key.data = nv->name_start;
5810Sigor@sysoev.ru     lhq.proto = &nxt_upstream_header_hash_proto;
5820Sigor@sysoev.ru 
5830Sigor@sysoev.ru     if (nxt_lvlhsh_find(&fs->header_in.hash, &lhq) == NXT_OK) {
5840Sigor@sysoev.ru         unv = lhq.value;
5850Sigor@sysoev.ru 
5860Sigor@sysoev.ru         if (unv->handler(fs->upstream, nv) == NXT_OK) {
5870Sigor@sysoev.ru             return NXT_ERROR;
5880Sigor@sysoev.ru         }
5890Sigor@sysoev.ru     }
5900Sigor@sysoev.ru 
5910Sigor@sysoev.ru     return NXT_OK;
5920Sigor@sysoev.ru }
5930Sigor@sysoev.ru 
5940Sigor@sysoev.ru 
5950Sigor@sysoev.ru static const nxt_upstream_name_value_t  nxt_fastcgi_source_headers[]
5960Sigor@sysoev.ru     nxt_aligned(32) =
5970Sigor@sysoev.ru {
5980Sigor@sysoev.ru     { nxt_fastcgi_source_status,
5990Sigor@sysoev.ru       nxt_upstream_name_value("status") },
6000Sigor@sysoev.ru 
6010Sigor@sysoev.ru     { nxt_fastcgi_source_content_length,
6020Sigor@sysoev.ru       nxt_upstream_name_value("content-length") },
6030Sigor@sysoev.ru };
6040Sigor@sysoev.ru 
6050Sigor@sysoev.ru 
6060Sigor@sysoev.ru nxt_int_t
60765Sigor@sysoev.ru nxt_fastcgi_source_hash_create(nxt_mp_t *mp, nxt_lvlhsh_t *lh)
6080Sigor@sysoev.ru {
6090Sigor@sysoev.ru     return nxt_upstream_header_hash_add(mp, lh, nxt_fastcgi_source_headers,
6100Sigor@sysoev.ru                                         nxt_nitems(nxt_fastcgi_source_headers));
6110Sigor@sysoev.ru }
6120Sigor@sysoev.ru 
6130Sigor@sysoev.ru 
6140Sigor@sysoev.ru static nxt_int_t
6150Sigor@sysoev.ru nxt_fastcgi_source_status(nxt_upstream_source_t *us, nxt_name_value_t *nv)
6160Sigor@sysoev.ru {
6170Sigor@sysoev.ru     nxt_int_t             n;
6180Sigor@sysoev.ru     nxt_str_t             s;
6190Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
6200Sigor@sysoev.ru 
6210Sigor@sysoev.ru     s.len = nv->value_len;
6220Sigor@sysoev.ru     s.data = nv->value_start;
6230Sigor@sysoev.ru 
6240Sigor@sysoev.ru     n = nxt_str_int_parse(&s);
6250Sigor@sysoev.ru 
6260Sigor@sysoev.ru     if (nxt_fast_path(n > 0)) {
6270Sigor@sysoev.ru         fs = us->protocol_source;
6280Sigor@sysoev.ru         fs->header_in.status = n;
6290Sigor@sysoev.ru         return NXT_OK;
6300Sigor@sysoev.ru     }
6310Sigor@sysoev.ru 
6320Sigor@sysoev.ru     return NXT_ERROR;
6330Sigor@sysoev.ru }
6340Sigor@sysoev.ru 
6350Sigor@sysoev.ru 
6360Sigor@sysoev.ru static nxt_int_t
6370Sigor@sysoev.ru nxt_fastcgi_source_content_length(nxt_upstream_source_t *us,
6380Sigor@sysoev.ru     nxt_name_value_t *nv)
6390Sigor@sysoev.ru {
6400Sigor@sysoev.ru     nxt_off_t             length;
6410Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
6420Sigor@sysoev.ru 
6430Sigor@sysoev.ru     length = nxt_off_t_parse(nv->value_start, nv->value_len);
6440Sigor@sysoev.ru 
6450Sigor@sysoev.ru     if (nxt_fast_path(length > 0)) {
6460Sigor@sysoev.ru         fs = us->protocol_source;
6470Sigor@sysoev.ru         fs->header_in.content_length = length;
6480Sigor@sysoev.ru         return NXT_OK;
6490Sigor@sysoev.ru     }
6500Sigor@sysoev.ru 
6510Sigor@sysoev.ru     return NXT_ERROR;
6520Sigor@sysoev.ru }
6530Sigor@sysoev.ru 
6540Sigor@sysoev.ru 
6550Sigor@sysoev.ru static void
6560Sigor@sysoev.ru nxt_fastcgi_source_header_ready(nxt_fastcgi_source_t *fs, nxt_buf_t *b)
6570Sigor@sysoev.ru {
6580Sigor@sysoev.ru     /*
6590Sigor@sysoev.ru      * Change the FastCGI source filter chain:
6600Sigor@sysoev.ru      *   stream source | FastCGI record filter | FastCGI body filter
6610Sigor@sysoev.ru      */
6620Sigor@sysoev.ru     fs->record.next.filter = nxt_fastcgi_source_body_filter;
6630Sigor@sysoev.ru 
6640Sigor@sysoev.ru     if (nxt_buf_mem_used_size(&b->mem) != 0) {
6650Sigor@sysoev.ru         fs->rest = b;
6660Sigor@sysoev.ru     }
6670Sigor@sysoev.ru 
6680Sigor@sysoev.ru     if (fs->header_in.status == 0) {
6690Sigor@sysoev.ru         /* The "200 OK" status by default. */
6700Sigor@sysoev.ru         fs->header_in.status = 200;
6710Sigor@sysoev.ru     }
6720Sigor@sysoev.ru 
6730Sigor@sysoev.ru     fs->upstream->state->ready_handler(fs);
6740Sigor@sysoev.ru }
6750Sigor@sysoev.ru 
6760Sigor@sysoev.ru 
6770Sigor@sysoev.ru /*
6780Sigor@sysoev.ru  * The FastCGI source body filter accumulates first body buffers before the next
6790Sigor@sysoev.ru  * filter will be established and sets completion handler for the last buffer.
6800Sigor@sysoev.ru  */
6810Sigor@sysoev.ru 
6820Sigor@sysoev.ru static void
6831Sigor@sysoev.ru nxt_fastcgi_source_body_filter(nxt_task_t *task, void *obj, void *data)
6840Sigor@sysoev.ru {
6850Sigor@sysoev.ru     nxt_buf_t             *b, *in;
6860Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
6870Sigor@sysoev.ru 
6880Sigor@sysoev.ru     fs = obj;
6890Sigor@sysoev.ru     in = data;
6900Sigor@sysoev.ru 
6911Sigor@sysoev.ru     nxt_debug(task, "fastcgi source body filter");
6920Sigor@sysoev.ru 
6930Sigor@sysoev.ru     for (b = in; b != NULL; b = b->next) {
6940Sigor@sysoev.ru 
6950Sigor@sysoev.ru         if (nxt_buf_is_last(b)) {
6960Sigor@sysoev.ru             b->data = fs->upstream->data;
6970Sigor@sysoev.ru             b->completion_handler = fs->upstream->state->completion_handler;
6980Sigor@sysoev.ru         }
6990Sigor@sysoev.ru     }
7000Sigor@sysoev.ru 
7010Sigor@sysoev.ru     if (fs->next != NULL) {
7021Sigor@sysoev.ru         nxt_source_filter(task->thread, fs->upstream->work_queue, task,
7031Sigor@sysoev.ru                           fs->next, in);
7040Sigor@sysoev.ru         return;
7050Sigor@sysoev.ru     }
7060Sigor@sysoev.ru 
7070Sigor@sysoev.ru     nxt_buf_chain_add(&fs->rest, in);
7080Sigor@sysoev.ru }
7090Sigor@sysoev.ru 
7100Sigor@sysoev.ru 
7110Sigor@sysoev.ru static nxt_buf_t *
7120Sigor@sysoev.ru nxt_fastcgi_source_last_buf(nxt_fastcgi_parse_t *fp)
7130Sigor@sysoev.ru {
7140Sigor@sysoev.ru     nxt_buf_t             *b;
7150Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
7160Sigor@sysoev.ru 
7170Sigor@sysoev.ru     fs = fp->data;
7180Sigor@sysoev.ru 
7190Sigor@sysoev.ru     b = nxt_buf_sync_alloc(fp->mem_pool, NXT_BUF_SYNC_LAST);
7200Sigor@sysoev.ru 
7210Sigor@sysoev.ru     if (nxt_fast_path(b != NULL)) {
7220Sigor@sysoev.ru         b->data = fs->upstream->data;
7230Sigor@sysoev.ru         b->completion_handler = fs->upstream->state->completion_handler;
7240Sigor@sysoev.ru     }
7250Sigor@sysoev.ru 
7260Sigor@sysoev.ru     return b;
7270Sigor@sysoev.ru }
7280Sigor@sysoev.ru 
7290Sigor@sysoev.ru 
7300Sigor@sysoev.ru static void
7311Sigor@sysoev.ru nxt_fastcgi_source_error(nxt_task_t *task, nxt_stream_source_t *stream)
7320Sigor@sysoev.ru {
7330Sigor@sysoev.ru     nxt_fastcgi_source_t  *fs;
7340Sigor@sysoev.ru 
7350Sigor@sysoev.ru     nxt_thread_log_debug("fastcgi source error");
7360Sigor@sysoev.ru 
7370Sigor@sysoev.ru     fs = stream->upstream->protocol_source;
7380Sigor@sysoev.ru 
7391Sigor@sysoev.ru     nxt_fastcgi_source_fail(task, fs);
7400Sigor@sysoev.ru }
7410Sigor@sysoev.ru 
7420Sigor@sysoev.ru 
7430Sigor@sysoev.ru static void
7441Sigor@sysoev.ru nxt_fastcgi_source_fail(nxt_task_t *task, nxt_fastcgi_source_t *fs)
7450Sigor@sysoev.ru {
7461Sigor@sysoev.ru     nxt_debug(task, "fastcgi source fail");
7470Sigor@sysoev.ru 
7480Sigor@sysoev.ru     /* TODO: fail, next upstream, or bad gateway */
7490Sigor@sysoev.ru 
7501Sigor@sysoev.ru     fs->upstream->state->error_handler(task, fs, NULL);
7510Sigor@sysoev.ru }
752