xref: /unit/src/nxt_fastcgi_record_parse.c (revision 1:fdc027c56872)
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