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, ¶m); 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