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_DATA_MIDDLE 0 110Sigor@sysoev.ru #define NXT_FASTCGI_DATA_END_ON_BORDER 1 120Sigor@sysoev.ru #define NXT_FASTCGI_DATA_END 2 130Sigor@sysoev.ru 140Sigor@sysoev.ru 150Sigor@sysoev.ru static nxt_int_t nxt_fastcgi_buffer(nxt_fastcgi_parse_t *fp, nxt_buf_t ***tail, 160Sigor@sysoev.ru nxt_buf_t *in); 170Sigor@sysoev.ru 180Sigor@sysoev.ru 190Sigor@sysoev.ru void 20*1Sigor@sysoev.ru nxt_fastcgi_record_parse(nxt_task_t *task, nxt_fastcgi_parse_t *fp, 21*1Sigor@sysoev.ru nxt_buf_t *in) 220Sigor@sysoev.ru { 230Sigor@sysoev.ru u_char ch; 240Sigor@sysoev.ru nxt_int_t ret, stream; 250Sigor@sysoev.ru nxt_buf_t *b, *nb, **tail[2]; 260Sigor@sysoev.ru const char *msg; 270Sigor@sysoev.ru enum { 280Sigor@sysoev.ru sw_fastcgi_version = 0, 290Sigor@sysoev.ru sw_fastcgi_type, 300Sigor@sysoev.ru sw_fastcgi_request_id_high, 310Sigor@sysoev.ru sw_fastcgi_request_id_low, 320Sigor@sysoev.ru sw_fastcgi_content_length_high, 330Sigor@sysoev.ru sw_fastcgi_content_length_low, 340Sigor@sysoev.ru sw_fastcgi_padding_length, 350Sigor@sysoev.ru sw_fastcgi_reserved, 360Sigor@sysoev.ru sw_fastcgi_data, 370Sigor@sysoev.ru sw_fastcgi_padding, 380Sigor@sysoev.ru sw_fastcgi_end_request, 390Sigor@sysoev.ru } state; 400Sigor@sysoev.ru 410Sigor@sysoev.ru fp->out[0] = NULL; 420Sigor@sysoev.ru fp->out[1] = NULL; 430Sigor@sysoev.ru 440Sigor@sysoev.ru tail[0] = &fp->out[0]; 450Sigor@sysoev.ru tail[1] = &fp->out[1]; 460Sigor@sysoev.ru 470Sigor@sysoev.ru state = fp->state; 480Sigor@sysoev.ru 490Sigor@sysoev.ru for (b = in; b != NULL; b = b->next) { 500Sigor@sysoev.ru 510Sigor@sysoev.ru if (nxt_buf_is_sync(b)) { 520Sigor@sysoev.ru **tail = b; 530Sigor@sysoev.ru *tail = &b->next; 540Sigor@sysoev.ru continue; 550Sigor@sysoev.ru } 560Sigor@sysoev.ru 570Sigor@sysoev.ru fp->pos = b->mem.pos; 580Sigor@sysoev.ru 590Sigor@sysoev.ru while (fp->pos < b->mem.free) { 600Sigor@sysoev.ru /* 610Sigor@sysoev.ru * The sw_fastcgi_data state is tested outside the 620Sigor@sysoev.ru * switch to preserve fp->pos and to not touch memory. 630Sigor@sysoev.ru */ 640Sigor@sysoev.ru if (state == sw_fastcgi_data) { 650Sigor@sysoev.ru 660Sigor@sysoev.ru /* 670Sigor@sysoev.ru * fp->type here can be only NXT_FASTCGI_STDOUT 680Sigor@sysoev.ru * or NXT_FASTCGI_STDERR. NXT_FASTCGI_END_REQUEST 690Sigor@sysoev.ru * is tested in sw_fastcgi_reserved. 700Sigor@sysoev.ru */ 710Sigor@sysoev.ru stream = fp->type - NXT_FASTCGI_STDOUT; 720Sigor@sysoev.ru 730Sigor@sysoev.ru ret = nxt_fastcgi_buffer(fp, &tail[stream], b); 740Sigor@sysoev.ru 750Sigor@sysoev.ru if (ret == NXT_FASTCGI_DATA_MIDDLE) { 760Sigor@sysoev.ru goto next; 770Sigor@sysoev.ru } 780Sigor@sysoev.ru 790Sigor@sysoev.ru if (nxt_slow_path(ret == NXT_ERROR)) { 800Sigor@sysoev.ru fp->error = 1; 810Sigor@sysoev.ru goto done; 820Sigor@sysoev.ru } 830Sigor@sysoev.ru 840Sigor@sysoev.ru if (fp->padding == 0) { 850Sigor@sysoev.ru state = sw_fastcgi_version; 860Sigor@sysoev.ru 870Sigor@sysoev.ru } else { 880Sigor@sysoev.ru state = sw_fastcgi_padding; 890Sigor@sysoev.ru } 900Sigor@sysoev.ru 910Sigor@sysoev.ru if (ret == NXT_FASTCGI_DATA_END_ON_BORDER) { 920Sigor@sysoev.ru goto next; 930Sigor@sysoev.ru } 940Sigor@sysoev.ru 950Sigor@sysoev.ru /* ret == NXT_FASTCGI_DATA_END */ 960Sigor@sysoev.ru } 970Sigor@sysoev.ru 980Sigor@sysoev.ru ch = *fp->pos++; 990Sigor@sysoev.ru 1000Sigor@sysoev.ru nxt_thread_log_debug("fastcgi record byte: %02Xd", ch); 1010Sigor@sysoev.ru 1020Sigor@sysoev.ru switch (state) { 1030Sigor@sysoev.ru 1040Sigor@sysoev.ru case sw_fastcgi_version: 1050Sigor@sysoev.ru if (nxt_fast_path(ch == 1)) { 1060Sigor@sysoev.ru state = sw_fastcgi_type; 1070Sigor@sysoev.ru continue; 1080Sigor@sysoev.ru } 1090Sigor@sysoev.ru 1100Sigor@sysoev.ru msg = "unsupported FastCGI protocol version"; 1110Sigor@sysoev.ru goto fastcgi_error; 1120Sigor@sysoev.ru 1130Sigor@sysoev.ru case sw_fastcgi_type: 1140Sigor@sysoev.ru switch (ch) { 1150Sigor@sysoev.ru case NXT_FASTCGI_STDOUT: 1160Sigor@sysoev.ru case NXT_FASTCGI_STDERR: 1170Sigor@sysoev.ru case NXT_FASTCGI_END_REQUEST: 1180Sigor@sysoev.ru fp->type = ch; 1190Sigor@sysoev.ru state = sw_fastcgi_request_id_high; 1200Sigor@sysoev.ru continue; 1210Sigor@sysoev.ru default: 1220Sigor@sysoev.ru msg = "invalid FastCGI record type"; 1230Sigor@sysoev.ru goto fastcgi_error; 1240Sigor@sysoev.ru } 1250Sigor@sysoev.ru 1260Sigor@sysoev.ru case sw_fastcgi_request_id_high: 1270Sigor@sysoev.ru /* FastCGI multiplexing is not supported. */ 1280Sigor@sysoev.ru if (nxt_fast_path(ch == 0)) { 1290Sigor@sysoev.ru state = sw_fastcgi_request_id_low; 1300Sigor@sysoev.ru continue; 1310Sigor@sysoev.ru } 1320Sigor@sysoev.ru 1330Sigor@sysoev.ru msg = "unexpected FastCGI request ID high byte"; 1340Sigor@sysoev.ru goto fastcgi_error; 1350Sigor@sysoev.ru 1360Sigor@sysoev.ru case sw_fastcgi_request_id_low: 1370Sigor@sysoev.ru if (nxt_fast_path(ch == 1)) { 1380Sigor@sysoev.ru state = sw_fastcgi_content_length_high; 1390Sigor@sysoev.ru continue; 1400Sigor@sysoev.ru } 1410Sigor@sysoev.ru 1420Sigor@sysoev.ru msg = "unexpected FastCGI request ID low byte"; 1430Sigor@sysoev.ru goto fastcgi_error; 1440Sigor@sysoev.ru 1450Sigor@sysoev.ru case sw_fastcgi_content_length_high: 1460Sigor@sysoev.ru fp->length = ch << 8; 1470Sigor@sysoev.ru state = sw_fastcgi_content_length_low; 1480Sigor@sysoev.ru continue; 1490Sigor@sysoev.ru 1500Sigor@sysoev.ru case sw_fastcgi_content_length_low: 1510Sigor@sysoev.ru fp->length |= ch; 1520Sigor@sysoev.ru state = sw_fastcgi_padding_length; 1530Sigor@sysoev.ru continue; 1540Sigor@sysoev.ru 1550Sigor@sysoev.ru case sw_fastcgi_padding_length: 1560Sigor@sysoev.ru fp->padding = ch; 1570Sigor@sysoev.ru state = sw_fastcgi_reserved; 1580Sigor@sysoev.ru continue; 1590Sigor@sysoev.ru 1600Sigor@sysoev.ru case sw_fastcgi_reserved: 1610Sigor@sysoev.ru nxt_thread_log_debug("fastcgi record type:%d " 1620Sigor@sysoev.ru "length:%uz padding:%d", 1630Sigor@sysoev.ru fp->type, fp->length, fp->padding); 1640Sigor@sysoev.ru 1650Sigor@sysoev.ru if (nxt_fast_path(fp->type != NXT_FASTCGI_END_REQUEST)) { 1660Sigor@sysoev.ru state = sw_fastcgi_data; 1670Sigor@sysoev.ru continue; 1680Sigor@sysoev.ru } 1690Sigor@sysoev.ru 1700Sigor@sysoev.ru state = sw_fastcgi_end_request; 1710Sigor@sysoev.ru continue; 1720Sigor@sysoev.ru 1730Sigor@sysoev.ru case sw_fastcgi_data: 1740Sigor@sysoev.ru /* 1750Sigor@sysoev.ru * This state is processed before the switch. 1760Sigor@sysoev.ru * It added here just to suppress a warning. 1770Sigor@sysoev.ru */ 1780Sigor@sysoev.ru continue; 1790Sigor@sysoev.ru 1800Sigor@sysoev.ru case sw_fastcgi_padding: 1810Sigor@sysoev.ru /* 1820Sigor@sysoev.ru * No special fast processing of padding 1830Sigor@sysoev.ru * because it usually takes just 1-7 bytes. 1840Sigor@sysoev.ru */ 1850Sigor@sysoev.ru fp->padding--; 1860Sigor@sysoev.ru 1870Sigor@sysoev.ru if (fp->padding == 0) { 1880Sigor@sysoev.ru nxt_thread_log_debug("fastcgi record end"); 1890Sigor@sysoev.ru state = sw_fastcgi_version; 1900Sigor@sysoev.ru } 1910Sigor@sysoev.ru continue; 1920Sigor@sysoev.ru 1930Sigor@sysoev.ru case sw_fastcgi_end_request: 1940Sigor@sysoev.ru /* Just skip 8 bytes of END_REQUEST. */ 1950Sigor@sysoev.ru fp->length--; 1960Sigor@sysoev.ru 1970Sigor@sysoev.ru if (fp->length != 0) { 1980Sigor@sysoev.ru continue; 1990Sigor@sysoev.ru } 2000Sigor@sysoev.ru 2010Sigor@sysoev.ru fp->done = 1; 2020Sigor@sysoev.ru 2030Sigor@sysoev.ru nxt_thread_log_debug("fastcgi end request"); 2040Sigor@sysoev.ru 2050Sigor@sysoev.ru goto done; 2060Sigor@sysoev.ru } 2070Sigor@sysoev.ru } 2080Sigor@sysoev.ru 2090Sigor@sysoev.ru if (b->retain == 0) { 2100Sigor@sysoev.ru /* No record data was found in a buffer. */ 211*1Sigor@sysoev.ru nxt_thread_current_work_queue_add(task->thread, 212*1Sigor@sysoev.ru b->completion_handler, 213*1Sigor@sysoev.ru task, b, b->parent); 2140Sigor@sysoev.ru } 2150Sigor@sysoev.ru 2160Sigor@sysoev.ru next: 2170Sigor@sysoev.ru 2180Sigor@sysoev.ru continue; 2190Sigor@sysoev.ru } 2200Sigor@sysoev.ru 2210Sigor@sysoev.ru fp->state = state; 2220Sigor@sysoev.ru 2230Sigor@sysoev.ru return; 2240Sigor@sysoev.ru 2250Sigor@sysoev.ru fastcgi_error: 2260Sigor@sysoev.ru 2270Sigor@sysoev.ru nxt_thread_log_error(NXT_LOG_ERR, "upstream sent %s: %d", msg, ch); 2280Sigor@sysoev.ru 2290Sigor@sysoev.ru fp->fastcgi_error = 1; 2300Sigor@sysoev.ru 2310Sigor@sysoev.ru done: 2320Sigor@sysoev.ru 2330Sigor@sysoev.ru nb = fp->last_buf(fp); 2340Sigor@sysoev.ru 2350Sigor@sysoev.ru if (nxt_fast_path(nb != NULL)) { 2360Sigor@sysoev.ru *tail[0] = nb; 2370Sigor@sysoev.ru 2380Sigor@sysoev.ru } else { 2390Sigor@sysoev.ru fp->error = 1; 2400Sigor@sysoev.ru } 2410Sigor@sysoev.ru 2420Sigor@sysoev.ru // STUB: fp->fastcgi_error = 1; 2430Sigor@sysoev.ru // STUB: fp->error = 1; 2440Sigor@sysoev.ru 2450Sigor@sysoev.ru return; 2460Sigor@sysoev.ru } 2470Sigor@sysoev.ru 2480Sigor@sysoev.ru 2490Sigor@sysoev.ru static nxt_int_t 2500Sigor@sysoev.ru nxt_fastcgi_buffer(nxt_fastcgi_parse_t *fp, nxt_buf_t ***tail, nxt_buf_t *in) 2510Sigor@sysoev.ru { 2520Sigor@sysoev.ru u_char *p; 2530Sigor@sysoev.ru size_t size; 2540Sigor@sysoev.ru nxt_buf_t *b; 2550Sigor@sysoev.ru 2560Sigor@sysoev.ru if (fp->length == 0) { 2570Sigor@sysoev.ru return NXT_FASTCGI_DATA_END; 2580Sigor@sysoev.ru } 2590Sigor@sysoev.ru 2600Sigor@sysoev.ru p = fp->pos; 2610Sigor@sysoev.ru size = in->mem.free - p; 2620Sigor@sysoev.ru 2630Sigor@sysoev.ru if (fp->length >= size && in->retain == 0) { 2640Sigor@sysoev.ru /* 2650Sigor@sysoev.ru * Use original buffer if the buffer is lesser than or equal to 2660Sigor@sysoev.ru * FastCGI record size and this is the first record in the buffer. 2670Sigor@sysoev.ru */ 2680Sigor@sysoev.ru in->mem.pos = p; 2690Sigor@sysoev.ru **tail = in; 2700Sigor@sysoev.ru *tail = &in->next; 2710Sigor@sysoev.ru 2720Sigor@sysoev.ru } else { 2730Sigor@sysoev.ru b = nxt_buf_mem_alloc(fp->mem_pool, 0, 0); 2740Sigor@sysoev.ru if (nxt_slow_path(b == NULL)) { 2750Sigor@sysoev.ru return NXT_ERROR; 2760Sigor@sysoev.ru } 2770Sigor@sysoev.ru 2780Sigor@sysoev.ru **tail = b; 2790Sigor@sysoev.ru *tail = &b->next; 2800Sigor@sysoev.ru 2810Sigor@sysoev.ru b->parent = in; 2820Sigor@sysoev.ru in->retain++; 2830Sigor@sysoev.ru b->mem.pos = p; 2840Sigor@sysoev.ru b->mem.start = p; 2850Sigor@sysoev.ru 2860Sigor@sysoev.ru if (fp->length < size) { 2870Sigor@sysoev.ru p += fp->length; 2880Sigor@sysoev.ru fp->pos = p; 2890Sigor@sysoev.ru 2900Sigor@sysoev.ru b->mem.free = p; 2910Sigor@sysoev.ru b->mem.end = p; 2920Sigor@sysoev.ru 2930Sigor@sysoev.ru return NXT_FASTCGI_DATA_END; 2940Sigor@sysoev.ru } 2950Sigor@sysoev.ru 2960Sigor@sysoev.ru b->mem.free = in->mem.free; 2970Sigor@sysoev.ru b->mem.end = in->mem.free; 2980Sigor@sysoev.ru } 2990Sigor@sysoev.ru 3000Sigor@sysoev.ru fp->length -= size; 3010Sigor@sysoev.ru 3020Sigor@sysoev.ru if (fp->length == 0) { 3030Sigor@sysoev.ru return NXT_FASTCGI_DATA_END_ON_BORDER; 3040Sigor@sysoev.ru } 3050Sigor@sysoev.ru 3060Sigor@sysoev.ru return NXT_FASTCGI_DATA_MIDDLE; 3070Sigor@sysoev.ru } 308