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