xref: /unit/src/nxt_http_chunk_parse.c (revision 1505)
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_HTTP_CHUNK_MIDDLE         0
110Sigor@sysoev.ru #define NXT_HTTP_CHUNK_END_ON_BORDER  1
120Sigor@sysoev.ru #define NXT_HTTP_CHUNK_END            2
130Sigor@sysoev.ru 
140Sigor@sysoev.ru 
150Sigor@sysoev.ru #define                                                                       \
160Sigor@sysoev.ru nxt_size_is_sufficient(cs)                                                    \
170Sigor@sysoev.ru     (cs < ((__typeof__(cs)) 1 << (sizeof(cs) * 8 - 4)))
180Sigor@sysoev.ru 
190Sigor@sysoev.ru 
200Sigor@sysoev.ru static nxt_int_t nxt_http_chunk_buffer(nxt_http_chunk_parse_t *hcp,
210Sigor@sysoev.ru     nxt_buf_t ***tail, nxt_buf_t *in);
220Sigor@sysoev.ru 
230Sigor@sysoev.ru 
24*1505Sigor@sysoev.ru static void nxt_http_chunk_buf_completion(nxt_task_t *task, void *obj,
25*1505Sigor@sysoev.ru     void *data);
26*1505Sigor@sysoev.ru 
27*1505Sigor@sysoev.ru 
280Sigor@sysoev.ru nxt_buf_t *
291Sigor@sysoev.ru nxt_http_chunk_parse(nxt_task_t *task, nxt_http_chunk_parse_t *hcp,
301Sigor@sysoev.ru     nxt_buf_t *in)
310Sigor@sysoev.ru {
320Sigor@sysoev.ru     u_char        c, ch;
330Sigor@sysoev.ru     nxt_int_t     ret;
34*1505Sigor@sysoev.ru     nxt_buf_t     *b, *out, *next, **tail;
350Sigor@sysoev.ru     enum {
360Sigor@sysoev.ru         sw_start = 0,
370Sigor@sysoev.ru         sw_chunk_size,
380Sigor@sysoev.ru         sw_chunk_size_linefeed,
390Sigor@sysoev.ru         sw_chunk_end_newline,
400Sigor@sysoev.ru         sw_chunk_end_linefeed,
410Sigor@sysoev.ru         sw_chunk,
420Sigor@sysoev.ru     } state;
430Sigor@sysoev.ru 
44*1505Sigor@sysoev.ru     next = NULL;
450Sigor@sysoev.ru     out = NULL;
460Sigor@sysoev.ru     tail = &out;
470Sigor@sysoev.ru 
480Sigor@sysoev.ru     state = hcp->state;
490Sigor@sysoev.ru 
50*1505Sigor@sysoev.ru     for (b = in; b != NULL; b = next) {
510Sigor@sysoev.ru 
520Sigor@sysoev.ru         hcp->pos = b->mem.pos;
530Sigor@sysoev.ru 
540Sigor@sysoev.ru         while (hcp->pos < b->mem.free) {
550Sigor@sysoev.ru             /*
560Sigor@sysoev.ru              * The sw_chunk state is tested outside the switch
570Sigor@sysoev.ru              * to preserve hcp->pos and to not touch memory.
580Sigor@sysoev.ru              */
590Sigor@sysoev.ru             if (state == sw_chunk) {
600Sigor@sysoev.ru                 ret = nxt_http_chunk_buffer(hcp, &tail, b);
610Sigor@sysoev.ru 
620Sigor@sysoev.ru                 if (ret == NXT_HTTP_CHUNK_MIDDLE) {
630Sigor@sysoev.ru                     goto next;
640Sigor@sysoev.ru                 }
650Sigor@sysoev.ru 
660Sigor@sysoev.ru                 if (nxt_slow_path(ret == NXT_ERROR)) {
670Sigor@sysoev.ru                     hcp->error = 1;
68*1505Sigor@sysoev.ru                     return out;
690Sigor@sysoev.ru                 }
700Sigor@sysoev.ru 
710Sigor@sysoev.ru                 state = sw_chunk_end_newline;
720Sigor@sysoev.ru 
730Sigor@sysoev.ru                 if (ret == NXT_HTTP_CHUNK_END_ON_BORDER) {
740Sigor@sysoev.ru                     goto next;
750Sigor@sysoev.ru                 }
760Sigor@sysoev.ru 
770Sigor@sysoev.ru                 /* ret == NXT_HTTP_CHUNK_END_ON_BORDER */
780Sigor@sysoev.ru             }
790Sigor@sysoev.ru 
800Sigor@sysoev.ru             ch = *hcp->pos++;
810Sigor@sysoev.ru 
820Sigor@sysoev.ru             switch (state) {
830Sigor@sysoev.ru 
840Sigor@sysoev.ru             case sw_start:
850Sigor@sysoev.ru                 state = sw_chunk_size;
860Sigor@sysoev.ru 
870Sigor@sysoev.ru                 c = ch - '0';
880Sigor@sysoev.ru 
890Sigor@sysoev.ru                 if (c <= 9) {
900Sigor@sysoev.ru                     hcp->chunk_size = c;
910Sigor@sysoev.ru                     continue;
920Sigor@sysoev.ru                 }
930Sigor@sysoev.ru 
940Sigor@sysoev.ru                 c = (ch | 0x20) - 'a';
950Sigor@sysoev.ru 
960Sigor@sysoev.ru                 if (c <= 5) {
97611Svbart@nginx.com                     hcp->chunk_size = 0x0A + c;
980Sigor@sysoev.ru                     continue;
990Sigor@sysoev.ru                 }
1000Sigor@sysoev.ru 
1010Sigor@sysoev.ru                 goto chunk_error;
1020Sigor@sysoev.ru 
1030Sigor@sysoev.ru             case sw_chunk_size:
1040Sigor@sysoev.ru 
1050Sigor@sysoev.ru                 c = ch - '0';
1060Sigor@sysoev.ru 
1070Sigor@sysoev.ru                 if (c > 9) {
1080Sigor@sysoev.ru                     c = (ch | 0x20) - 'a';
1090Sigor@sysoev.ru 
1100Sigor@sysoev.ru                     if (nxt_fast_path(c <= 5)) {
111611Svbart@nginx.com                         c += 0x0A;
1120Sigor@sysoev.ru 
113704Sigor@sysoev.ru                     } else if (nxt_fast_path(ch == '\r')) {
1140Sigor@sysoev.ru                         state = sw_chunk_size_linefeed;
1150Sigor@sysoev.ru                         continue;
1160Sigor@sysoev.ru 
1170Sigor@sysoev.ru                     } else {
1180Sigor@sysoev.ru                         goto chunk_error;
1190Sigor@sysoev.ru                     }
1200Sigor@sysoev.ru                 }
1210Sigor@sysoev.ru 
1220Sigor@sysoev.ru                 if (nxt_fast_path(nxt_size_is_sufficient(hcp->chunk_size))) {
1230Sigor@sysoev.ru                     hcp->chunk_size = (hcp->chunk_size << 4) + c;
1240Sigor@sysoev.ru                     continue;
1250Sigor@sysoev.ru                 }
1260Sigor@sysoev.ru 
1270Sigor@sysoev.ru                 goto chunk_error;
1280Sigor@sysoev.ru 
1290Sigor@sysoev.ru             case sw_chunk_size_linefeed:
130704Sigor@sysoev.ru                 if (nxt_fast_path(ch == '\n')) {
1310Sigor@sysoev.ru 
1320Sigor@sysoev.ru                     if (hcp->chunk_size != 0) {
1330Sigor@sysoev.ru                         state = sw_chunk;
1340Sigor@sysoev.ru                         continue;
1350Sigor@sysoev.ru                     }
1360Sigor@sysoev.ru 
1370Sigor@sysoev.ru                     hcp->last = 1;
1380Sigor@sysoev.ru                     state = sw_chunk_end_newline;
1390Sigor@sysoev.ru                     continue;
1400Sigor@sysoev.ru                 }
1410Sigor@sysoev.ru 
1420Sigor@sysoev.ru                 goto chunk_error;
1430Sigor@sysoev.ru 
1440Sigor@sysoev.ru             case sw_chunk_end_newline:
145704Sigor@sysoev.ru                 if (nxt_fast_path(ch == '\r')) {
1460Sigor@sysoev.ru                     state = sw_chunk_end_linefeed;
1470Sigor@sysoev.ru                     continue;
1480Sigor@sysoev.ru                 }
1490Sigor@sysoev.ru 
1500Sigor@sysoev.ru                 goto chunk_error;
1510Sigor@sysoev.ru 
1520Sigor@sysoev.ru             case sw_chunk_end_linefeed:
153704Sigor@sysoev.ru                 if (nxt_fast_path(ch == '\n')) {
1540Sigor@sysoev.ru 
1550Sigor@sysoev.ru                     if (!hcp->last) {
1560Sigor@sysoev.ru                         state = sw_start;
1570Sigor@sysoev.ru                         continue;
1580Sigor@sysoev.ru                     }
1590Sigor@sysoev.ru 
160*1505Sigor@sysoev.ru                     return out;
1610Sigor@sysoev.ru                 }
1620Sigor@sysoev.ru 
1630Sigor@sysoev.ru                 goto chunk_error;
1640Sigor@sysoev.ru 
1650Sigor@sysoev.ru             case sw_chunk:
1660Sigor@sysoev.ru                 /*
1670Sigor@sysoev.ru                  * This state is processed before the switch.
1680Sigor@sysoev.ru                  * It added here just to suppress a warning.
1690Sigor@sysoev.ru                  */
1700Sigor@sysoev.ru                 continue;
1710Sigor@sysoev.ru             }
1720Sigor@sysoev.ru         }
1730Sigor@sysoev.ru 
1740Sigor@sysoev.ru         if (b->retain == 0) {
1750Sigor@sysoev.ru             /* No chunk data was found in a buffer. */
176*1505Sigor@sysoev.ru             nxt_work_queue_add(&task->thread->engine->fast_work_queue,
177*1505Sigor@sysoev.ru                                b->completion_handler, task, b, b->parent);
1780Sigor@sysoev.ru 
1790Sigor@sysoev.ru         }
1800Sigor@sysoev.ru 
1810Sigor@sysoev.ru     next:
1820Sigor@sysoev.ru 
183*1505Sigor@sysoev.ru         next = b->next;
184*1505Sigor@sysoev.ru         b->next = NULL;
1850Sigor@sysoev.ru     }
1860Sigor@sysoev.ru 
1870Sigor@sysoev.ru     hcp->state = state;
1880Sigor@sysoev.ru 
1890Sigor@sysoev.ru     return out;
1900Sigor@sysoev.ru 
1910Sigor@sysoev.ru chunk_error:
1920Sigor@sysoev.ru 
1930Sigor@sysoev.ru     hcp->chunk_error = 1;
1940Sigor@sysoev.ru 
1950Sigor@sysoev.ru     return out;
1960Sigor@sysoev.ru }
1970Sigor@sysoev.ru 
1980Sigor@sysoev.ru 
1990Sigor@sysoev.ru static nxt_int_t
2000Sigor@sysoev.ru nxt_http_chunk_buffer(nxt_http_chunk_parse_t *hcp, nxt_buf_t ***tail,
2010Sigor@sysoev.ru     nxt_buf_t *in)
2020Sigor@sysoev.ru {
2030Sigor@sysoev.ru     u_char     *p;
2040Sigor@sysoev.ru     size_t     size;
2050Sigor@sysoev.ru     nxt_buf_t  *b;
2060Sigor@sysoev.ru 
2070Sigor@sysoev.ru     p = hcp->pos;
2080Sigor@sysoev.ru     size = in->mem.free - p;
2090Sigor@sysoev.ru 
210*1505Sigor@sysoev.ru     b = nxt_buf_mem_alloc(hcp->mem_pool, 0, 0);
211*1505Sigor@sysoev.ru     if (nxt_slow_path(b == NULL)) {
212*1505Sigor@sysoev.ru         return NXT_ERROR;
213*1505Sigor@sysoev.ru     }
2140Sigor@sysoev.ru 
215*1505Sigor@sysoev.ru     **tail = b;
216*1505Sigor@sysoev.ru     *tail = &b->next;
2170Sigor@sysoev.ru 
218*1505Sigor@sysoev.ru     nxt_mp_retain(hcp->mem_pool);
219*1505Sigor@sysoev.ru     b->completion_handler = nxt_http_chunk_buf_completion;
2200Sigor@sysoev.ru 
221*1505Sigor@sysoev.ru     b->parent = in;
222*1505Sigor@sysoev.ru     in->retain++;
223*1505Sigor@sysoev.ru     b->mem.pos = p;
224*1505Sigor@sysoev.ru     b->mem.start = p;
2250Sigor@sysoev.ru 
226*1505Sigor@sysoev.ru     if (hcp->chunk_size < size) {
227*1505Sigor@sysoev.ru         p += hcp->chunk_size;
228*1505Sigor@sysoev.ru         hcp->pos = p;
2290Sigor@sysoev.ru 
230*1505Sigor@sysoev.ru         b->mem.free = p;
231*1505Sigor@sysoev.ru         b->mem.end = p;
2320Sigor@sysoev.ru 
233*1505Sigor@sysoev.ru         return NXT_HTTP_CHUNK_END;
234*1505Sigor@sysoev.ru     }
2350Sigor@sysoev.ru 
236*1505Sigor@sysoev.ru     b->mem.free = in->mem.free;
237*1505Sigor@sysoev.ru     b->mem.end = in->mem.free;
2380Sigor@sysoev.ru 
2390Sigor@sysoev.ru     hcp->chunk_size -= size;
2400Sigor@sysoev.ru 
2410Sigor@sysoev.ru     if (hcp->chunk_size == 0) {
2420Sigor@sysoev.ru         return NXT_HTTP_CHUNK_END_ON_BORDER;
2430Sigor@sysoev.ru     }
2440Sigor@sysoev.ru 
2450Sigor@sysoev.ru     return NXT_HTTP_CHUNK_MIDDLE;
2460Sigor@sysoev.ru }
247*1505Sigor@sysoev.ru 
248*1505Sigor@sysoev.ru 
249*1505Sigor@sysoev.ru static void
250*1505Sigor@sysoev.ru nxt_http_chunk_buf_completion(nxt_task_t *task, void *obj, void *data)
251*1505Sigor@sysoev.ru {
252*1505Sigor@sysoev.ru     nxt_mp_t   *mp;
253*1505Sigor@sysoev.ru     nxt_buf_t  *b, *next, *parent;
254*1505Sigor@sysoev.ru 
255*1505Sigor@sysoev.ru     b = obj;
256*1505Sigor@sysoev.ru     parent = data;
257*1505Sigor@sysoev.ru 
258*1505Sigor@sysoev.ru     nxt_debug(task, "buf completion: %p %p", b, b->mem.start);
259*1505Sigor@sysoev.ru 
260*1505Sigor@sysoev.ru     nxt_assert(data == b->parent);
261*1505Sigor@sysoev.ru 
262*1505Sigor@sysoev.ru     do {
263*1505Sigor@sysoev.ru         next = b->next;
264*1505Sigor@sysoev.ru         parent = b->parent;
265*1505Sigor@sysoev.ru         mp = b->data;
266*1505Sigor@sysoev.ru 
267*1505Sigor@sysoev.ru         nxt_mp_free(mp, b);
268*1505Sigor@sysoev.ru         nxt_mp_release(mp);
269*1505Sigor@sysoev.ru 
270*1505Sigor@sysoev.ru         nxt_buf_parent_completion(task, parent);
271*1505Sigor@sysoev.ru 
272*1505Sigor@sysoev.ru         b = next;
273*1505Sigor@sysoev.ru     } while (b != NULL);
274*1505Sigor@sysoev.ru }
275