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
15*2084Salx.manpages@gmail.com #define nxt_size_is_sufficient(cs) \
160Sigor@sysoev.ru (cs < ((__typeof__(cs)) 1 << (sizeof(cs) * 8 - 4)))
170Sigor@sysoev.ru
180Sigor@sysoev.ru
190Sigor@sysoev.ru static nxt_int_t nxt_http_chunk_buffer(nxt_http_chunk_parse_t *hcp,
200Sigor@sysoev.ru nxt_buf_t ***tail, nxt_buf_t *in);
210Sigor@sysoev.ru
220Sigor@sysoev.ru
231505Sigor@sysoev.ru static void nxt_http_chunk_buf_completion(nxt_task_t *task, void *obj,
241505Sigor@sysoev.ru void *data);
251505Sigor@sysoev.ru
261505Sigor@sysoev.ru
270Sigor@sysoev.ru nxt_buf_t *
nxt_http_chunk_parse(nxt_task_t * task,nxt_http_chunk_parse_t * hcp,nxt_buf_t * in)281Sigor@sysoev.ru nxt_http_chunk_parse(nxt_task_t *task, nxt_http_chunk_parse_t *hcp,
291Sigor@sysoev.ru nxt_buf_t *in)
300Sigor@sysoev.ru {
310Sigor@sysoev.ru u_char c, ch;
320Sigor@sysoev.ru nxt_int_t ret;
331505Sigor@sysoev.ru nxt_buf_t *b, *out, *next, **tail;
340Sigor@sysoev.ru enum {
350Sigor@sysoev.ru sw_start = 0,
360Sigor@sysoev.ru sw_chunk_size,
370Sigor@sysoev.ru sw_chunk_size_linefeed,
380Sigor@sysoev.ru sw_chunk_end_newline,
390Sigor@sysoev.ru sw_chunk_end_linefeed,
400Sigor@sysoev.ru sw_chunk,
410Sigor@sysoev.ru } state;
420Sigor@sysoev.ru
431505Sigor@sysoev.ru next = NULL;
440Sigor@sysoev.ru out = NULL;
450Sigor@sysoev.ru tail = &out;
460Sigor@sysoev.ru
470Sigor@sysoev.ru state = hcp->state;
480Sigor@sysoev.ru
491505Sigor@sysoev.ru for (b = in; b != NULL; b = next) {
500Sigor@sysoev.ru
510Sigor@sysoev.ru hcp->pos = b->mem.pos;
520Sigor@sysoev.ru
530Sigor@sysoev.ru while (hcp->pos < b->mem.free) {
540Sigor@sysoev.ru /*
550Sigor@sysoev.ru * The sw_chunk state is tested outside the switch
560Sigor@sysoev.ru * to preserve hcp->pos and to not touch memory.
570Sigor@sysoev.ru */
580Sigor@sysoev.ru if (state == sw_chunk) {
590Sigor@sysoev.ru ret = nxt_http_chunk_buffer(hcp, &tail, b);
600Sigor@sysoev.ru
610Sigor@sysoev.ru if (ret == NXT_HTTP_CHUNK_MIDDLE) {
620Sigor@sysoev.ru goto next;
630Sigor@sysoev.ru }
640Sigor@sysoev.ru
650Sigor@sysoev.ru if (nxt_slow_path(ret == NXT_ERROR)) {
660Sigor@sysoev.ru hcp->error = 1;
671505Sigor@sysoev.ru return out;
680Sigor@sysoev.ru }
690Sigor@sysoev.ru
700Sigor@sysoev.ru state = sw_chunk_end_newline;
710Sigor@sysoev.ru
720Sigor@sysoev.ru if (ret == NXT_HTTP_CHUNK_END_ON_BORDER) {
730Sigor@sysoev.ru goto next;
740Sigor@sysoev.ru }
750Sigor@sysoev.ru
761627Sigor@sysoev.ru /* ret == NXT_HTTP_CHUNK_END */
770Sigor@sysoev.ru }
780Sigor@sysoev.ru
790Sigor@sysoev.ru ch = *hcp->pos++;
800Sigor@sysoev.ru
810Sigor@sysoev.ru switch (state) {
820Sigor@sysoev.ru
830Sigor@sysoev.ru case sw_start:
840Sigor@sysoev.ru state = sw_chunk_size;
850Sigor@sysoev.ru
860Sigor@sysoev.ru c = ch - '0';
870Sigor@sysoev.ru
880Sigor@sysoev.ru if (c <= 9) {
890Sigor@sysoev.ru hcp->chunk_size = c;
900Sigor@sysoev.ru continue;
910Sigor@sysoev.ru }
920Sigor@sysoev.ru
930Sigor@sysoev.ru c = (ch | 0x20) - 'a';
940Sigor@sysoev.ru
950Sigor@sysoev.ru if (c <= 5) {
96611Svbart@nginx.com hcp->chunk_size = 0x0A + c;
970Sigor@sysoev.ru continue;
980Sigor@sysoev.ru }
990Sigor@sysoev.ru
1000Sigor@sysoev.ru goto chunk_error;
1010Sigor@sysoev.ru
1020Sigor@sysoev.ru case sw_chunk_size:
1030Sigor@sysoev.ru
1040Sigor@sysoev.ru c = ch - '0';
1050Sigor@sysoev.ru
1060Sigor@sysoev.ru if (c > 9) {
1070Sigor@sysoev.ru c = (ch | 0x20) - 'a';
1080Sigor@sysoev.ru
1090Sigor@sysoev.ru if (nxt_fast_path(c <= 5)) {
110611Svbart@nginx.com c += 0x0A;
1110Sigor@sysoev.ru
112704Sigor@sysoev.ru } else if (nxt_fast_path(ch == '\r')) {
1130Sigor@sysoev.ru state = sw_chunk_size_linefeed;
1140Sigor@sysoev.ru continue;
1150Sigor@sysoev.ru
1160Sigor@sysoev.ru } else {
1170Sigor@sysoev.ru goto chunk_error;
1180Sigor@sysoev.ru }
1190Sigor@sysoev.ru }
1200Sigor@sysoev.ru
1210Sigor@sysoev.ru if (nxt_fast_path(nxt_size_is_sufficient(hcp->chunk_size))) {
1220Sigor@sysoev.ru hcp->chunk_size = (hcp->chunk_size << 4) + c;
1230Sigor@sysoev.ru continue;
1240Sigor@sysoev.ru }
1250Sigor@sysoev.ru
1260Sigor@sysoev.ru goto chunk_error;
1270Sigor@sysoev.ru
1280Sigor@sysoev.ru case sw_chunk_size_linefeed:
129704Sigor@sysoev.ru if (nxt_fast_path(ch == '\n')) {
1300Sigor@sysoev.ru
1310Sigor@sysoev.ru if (hcp->chunk_size != 0) {
1320Sigor@sysoev.ru state = sw_chunk;
1330Sigor@sysoev.ru continue;
1340Sigor@sysoev.ru }
1350Sigor@sysoev.ru
1360Sigor@sysoev.ru hcp->last = 1;
1370Sigor@sysoev.ru state = sw_chunk_end_newline;
1380Sigor@sysoev.ru continue;
1390Sigor@sysoev.ru }
1400Sigor@sysoev.ru
1410Sigor@sysoev.ru goto chunk_error;
1420Sigor@sysoev.ru
1430Sigor@sysoev.ru case sw_chunk_end_newline:
144704Sigor@sysoev.ru if (nxt_fast_path(ch == '\r')) {
1450Sigor@sysoev.ru state = sw_chunk_end_linefeed;
1460Sigor@sysoev.ru continue;
1470Sigor@sysoev.ru }
1480Sigor@sysoev.ru
1490Sigor@sysoev.ru goto chunk_error;
1500Sigor@sysoev.ru
1510Sigor@sysoev.ru case sw_chunk_end_linefeed:
152704Sigor@sysoev.ru if (nxt_fast_path(ch == '\n')) {
1530Sigor@sysoev.ru
1540Sigor@sysoev.ru if (!hcp->last) {
1550Sigor@sysoev.ru state = sw_start;
1560Sigor@sysoev.ru continue;
1570Sigor@sysoev.ru }
1580Sigor@sysoev.ru
1591505Sigor@sysoev.ru return out;
1600Sigor@sysoev.ru }
1610Sigor@sysoev.ru
1620Sigor@sysoev.ru goto chunk_error;
1630Sigor@sysoev.ru
1640Sigor@sysoev.ru case sw_chunk:
1650Sigor@sysoev.ru /*
1660Sigor@sysoev.ru * This state is processed before the switch.
1670Sigor@sysoev.ru * It added here just to suppress a warning.
1680Sigor@sysoev.ru */
1690Sigor@sysoev.ru continue;
1700Sigor@sysoev.ru }
1710Sigor@sysoev.ru }
1720Sigor@sysoev.ru
1730Sigor@sysoev.ru if (b->retain == 0) {
1740Sigor@sysoev.ru /* No chunk data was found in a buffer. */
1751505Sigor@sysoev.ru nxt_work_queue_add(&task->thread->engine->fast_work_queue,
1761505Sigor@sysoev.ru b->completion_handler, task, b, b->parent);
1770Sigor@sysoev.ru
1780Sigor@sysoev.ru }
1790Sigor@sysoev.ru
1800Sigor@sysoev.ru next:
1810Sigor@sysoev.ru
1821505Sigor@sysoev.ru next = b->next;
1831505Sigor@sysoev.ru b->next = NULL;
1840Sigor@sysoev.ru }
1850Sigor@sysoev.ru
1860Sigor@sysoev.ru hcp->state = state;
1870Sigor@sysoev.ru
1880Sigor@sysoev.ru return out;
1890Sigor@sysoev.ru
1900Sigor@sysoev.ru chunk_error:
1910Sigor@sysoev.ru
1920Sigor@sysoev.ru hcp->chunk_error = 1;
1930Sigor@sysoev.ru
1940Sigor@sysoev.ru return out;
1950Sigor@sysoev.ru }
1960Sigor@sysoev.ru
1970Sigor@sysoev.ru
1980Sigor@sysoev.ru static nxt_int_t
nxt_http_chunk_buffer(nxt_http_chunk_parse_t * hcp,nxt_buf_t *** tail,nxt_buf_t * in)1990Sigor@sysoev.ru nxt_http_chunk_buffer(nxt_http_chunk_parse_t *hcp, nxt_buf_t ***tail,
2000Sigor@sysoev.ru nxt_buf_t *in)
2010Sigor@sysoev.ru {
2020Sigor@sysoev.ru u_char *p;
2030Sigor@sysoev.ru size_t size;
2040Sigor@sysoev.ru nxt_buf_t *b;
2050Sigor@sysoev.ru
2060Sigor@sysoev.ru p = hcp->pos;
2070Sigor@sysoev.ru size = in->mem.free - p;
2080Sigor@sysoev.ru
2091505Sigor@sysoev.ru b = nxt_buf_mem_alloc(hcp->mem_pool, 0, 0);
2101505Sigor@sysoev.ru if (nxt_slow_path(b == NULL)) {
2111505Sigor@sysoev.ru return NXT_ERROR;
2121505Sigor@sysoev.ru }
2130Sigor@sysoev.ru
2141505Sigor@sysoev.ru **tail = b;
2151505Sigor@sysoev.ru *tail = &b->next;
2160Sigor@sysoev.ru
2171505Sigor@sysoev.ru nxt_mp_retain(hcp->mem_pool);
2181505Sigor@sysoev.ru b->completion_handler = nxt_http_chunk_buf_completion;
2190Sigor@sysoev.ru
2201505Sigor@sysoev.ru b->parent = in;
2211505Sigor@sysoev.ru in->retain++;
2221505Sigor@sysoev.ru b->mem.pos = p;
2231505Sigor@sysoev.ru b->mem.start = p;
2240Sigor@sysoev.ru
2251505Sigor@sysoev.ru if (hcp->chunk_size < size) {
2261505Sigor@sysoev.ru p += hcp->chunk_size;
2271505Sigor@sysoev.ru hcp->pos = p;
2280Sigor@sysoev.ru
2291505Sigor@sysoev.ru b->mem.free = p;
2301505Sigor@sysoev.ru b->mem.end = p;
2310Sigor@sysoev.ru
2321505Sigor@sysoev.ru return NXT_HTTP_CHUNK_END;
2331505Sigor@sysoev.ru }
2340Sigor@sysoev.ru
2351505Sigor@sysoev.ru b->mem.free = in->mem.free;
2361505Sigor@sysoev.ru b->mem.end = in->mem.free;
2370Sigor@sysoev.ru
2380Sigor@sysoev.ru hcp->chunk_size -= size;
2390Sigor@sysoev.ru
2400Sigor@sysoev.ru if (hcp->chunk_size == 0) {
2410Sigor@sysoev.ru return NXT_HTTP_CHUNK_END_ON_BORDER;
2420Sigor@sysoev.ru }
2430Sigor@sysoev.ru
2440Sigor@sysoev.ru return NXT_HTTP_CHUNK_MIDDLE;
2450Sigor@sysoev.ru }
2461505Sigor@sysoev.ru
2471505Sigor@sysoev.ru
2481505Sigor@sysoev.ru static void
nxt_http_chunk_buf_completion(nxt_task_t * task,void * obj,void * data)2491505Sigor@sysoev.ru nxt_http_chunk_buf_completion(nxt_task_t *task, void *obj, void *data)
2501505Sigor@sysoev.ru {
2511505Sigor@sysoev.ru nxt_mp_t *mp;
2521505Sigor@sysoev.ru nxt_buf_t *b, *next, *parent;
2531505Sigor@sysoev.ru
2541505Sigor@sysoev.ru b = obj;
2551505Sigor@sysoev.ru
2561505Sigor@sysoev.ru nxt_debug(task, "buf completion: %p %p", b, b->mem.start);
2571505Sigor@sysoev.ru
2581505Sigor@sysoev.ru nxt_assert(data == b->parent);
2591505Sigor@sysoev.ru
2601505Sigor@sysoev.ru do {
2611505Sigor@sysoev.ru next = b->next;
2621505Sigor@sysoev.ru parent = b->parent;
2631505Sigor@sysoev.ru mp = b->data;
2641505Sigor@sysoev.ru
2651505Sigor@sysoev.ru nxt_mp_free(mp, b);
2661505Sigor@sysoev.ru nxt_mp_release(mp);
2671505Sigor@sysoev.ru
2681505Sigor@sysoev.ru nxt_buf_parent_completion(task, parent);
2691505Sigor@sysoev.ru
2701505Sigor@sysoev.ru b = next;
2711505Sigor@sysoev.ru } while (b != NULL);
2721505Sigor@sysoev.ru }
273