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
nxt_fastcgi_record_parse(nxt_task_t * task,nxt_fastcgi_parse_t * fp,nxt_buf_t * in)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
nxt_fastcgi_buffer(nxt_fastcgi_parse_t * fp,nxt_buf_t *** tail,nxt_buf_t * in)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