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 240Sigor@sysoev.ru nxt_buf_t * 25*1Sigor@sysoev.ru nxt_http_chunk_parse(nxt_task_t *task, nxt_http_chunk_parse_t *hcp, 26*1Sigor@sysoev.ru nxt_buf_t *in) 270Sigor@sysoev.ru { 280Sigor@sysoev.ru u_char c, ch; 290Sigor@sysoev.ru nxt_int_t ret; 300Sigor@sysoev.ru nxt_buf_t *b, *out, *nb, **tail; 310Sigor@sysoev.ru enum { 320Sigor@sysoev.ru sw_start = 0, 330Sigor@sysoev.ru sw_chunk_size, 340Sigor@sysoev.ru sw_chunk_size_linefeed, 350Sigor@sysoev.ru sw_chunk_end_newline, 360Sigor@sysoev.ru sw_chunk_end_linefeed, 370Sigor@sysoev.ru sw_chunk, 380Sigor@sysoev.ru } state; 390Sigor@sysoev.ru 400Sigor@sysoev.ru out = NULL; 410Sigor@sysoev.ru tail = &out; 420Sigor@sysoev.ru 430Sigor@sysoev.ru state = hcp->state; 440Sigor@sysoev.ru 450Sigor@sysoev.ru for (b = in; b != NULL; b = b->next) { 460Sigor@sysoev.ru 470Sigor@sysoev.ru hcp->pos = b->mem.pos; 480Sigor@sysoev.ru 490Sigor@sysoev.ru while (hcp->pos < b->mem.free) { 500Sigor@sysoev.ru /* 510Sigor@sysoev.ru * The sw_chunk state is tested outside the switch 520Sigor@sysoev.ru * to preserve hcp->pos and to not touch memory. 530Sigor@sysoev.ru */ 540Sigor@sysoev.ru if (state == sw_chunk) { 550Sigor@sysoev.ru ret = nxt_http_chunk_buffer(hcp, &tail, b); 560Sigor@sysoev.ru 570Sigor@sysoev.ru if (ret == NXT_HTTP_CHUNK_MIDDLE) { 580Sigor@sysoev.ru goto next; 590Sigor@sysoev.ru } 600Sigor@sysoev.ru 610Sigor@sysoev.ru if (nxt_slow_path(ret == NXT_ERROR)) { 620Sigor@sysoev.ru hcp->error = 1; 630Sigor@sysoev.ru goto done; 640Sigor@sysoev.ru } 650Sigor@sysoev.ru 660Sigor@sysoev.ru state = sw_chunk_end_newline; 670Sigor@sysoev.ru 680Sigor@sysoev.ru if (ret == NXT_HTTP_CHUNK_END_ON_BORDER) { 690Sigor@sysoev.ru goto next; 700Sigor@sysoev.ru } 710Sigor@sysoev.ru 720Sigor@sysoev.ru /* ret == NXT_HTTP_CHUNK_END_ON_BORDER */ 730Sigor@sysoev.ru } 740Sigor@sysoev.ru 750Sigor@sysoev.ru ch = *hcp->pos++; 760Sigor@sysoev.ru 770Sigor@sysoev.ru switch (state) { 780Sigor@sysoev.ru 790Sigor@sysoev.ru case sw_start: 800Sigor@sysoev.ru state = sw_chunk_size; 810Sigor@sysoev.ru 820Sigor@sysoev.ru c = ch - '0'; 830Sigor@sysoev.ru 840Sigor@sysoev.ru if (c <= 9) { 850Sigor@sysoev.ru hcp->chunk_size = c; 860Sigor@sysoev.ru continue; 870Sigor@sysoev.ru } 880Sigor@sysoev.ru 890Sigor@sysoev.ru c = (ch | 0x20) - 'a'; 900Sigor@sysoev.ru 910Sigor@sysoev.ru if (c <= 5) { 920Sigor@sysoev.ru hcp->chunk_size = 0x0a + c; 930Sigor@sysoev.ru continue; 940Sigor@sysoev.ru } 950Sigor@sysoev.ru 960Sigor@sysoev.ru goto chunk_error; 970Sigor@sysoev.ru 980Sigor@sysoev.ru case sw_chunk_size: 990Sigor@sysoev.ru 1000Sigor@sysoev.ru c = ch - '0'; 1010Sigor@sysoev.ru 1020Sigor@sysoev.ru if (c > 9) { 1030Sigor@sysoev.ru c = (ch | 0x20) - 'a'; 1040Sigor@sysoev.ru 1050Sigor@sysoev.ru if (nxt_fast_path(c <= 5)) { 1060Sigor@sysoev.ru c += 0x0a; 1070Sigor@sysoev.ru 1080Sigor@sysoev.ru } else if (nxt_fast_path(ch == NXT_CR)) { 1090Sigor@sysoev.ru state = sw_chunk_size_linefeed; 1100Sigor@sysoev.ru continue; 1110Sigor@sysoev.ru 1120Sigor@sysoev.ru } else { 1130Sigor@sysoev.ru goto chunk_error; 1140Sigor@sysoev.ru } 1150Sigor@sysoev.ru } 1160Sigor@sysoev.ru 1170Sigor@sysoev.ru if (nxt_fast_path(nxt_size_is_sufficient(hcp->chunk_size))) { 1180Sigor@sysoev.ru hcp->chunk_size = (hcp->chunk_size << 4) + c; 1190Sigor@sysoev.ru continue; 1200Sigor@sysoev.ru } 1210Sigor@sysoev.ru 1220Sigor@sysoev.ru goto chunk_error; 1230Sigor@sysoev.ru 1240Sigor@sysoev.ru case sw_chunk_size_linefeed: 1250Sigor@sysoev.ru if (nxt_fast_path(ch == NXT_LF)) { 1260Sigor@sysoev.ru 1270Sigor@sysoev.ru if (hcp->chunk_size != 0) { 1280Sigor@sysoev.ru state = sw_chunk; 1290Sigor@sysoev.ru continue; 1300Sigor@sysoev.ru } 1310Sigor@sysoev.ru 1320Sigor@sysoev.ru hcp->last = 1; 1330Sigor@sysoev.ru state = sw_chunk_end_newline; 1340Sigor@sysoev.ru continue; 1350Sigor@sysoev.ru } 1360Sigor@sysoev.ru 1370Sigor@sysoev.ru goto chunk_error; 1380Sigor@sysoev.ru 1390Sigor@sysoev.ru case sw_chunk_end_newline: 1400Sigor@sysoev.ru if (nxt_fast_path(ch == NXT_CR)) { 1410Sigor@sysoev.ru state = sw_chunk_end_linefeed; 1420Sigor@sysoev.ru continue; 1430Sigor@sysoev.ru } 1440Sigor@sysoev.ru 1450Sigor@sysoev.ru goto chunk_error; 1460Sigor@sysoev.ru 1470Sigor@sysoev.ru case sw_chunk_end_linefeed: 1480Sigor@sysoev.ru if (nxt_fast_path(ch == NXT_LF)) { 1490Sigor@sysoev.ru 1500Sigor@sysoev.ru if (!hcp->last) { 1510Sigor@sysoev.ru state = sw_start; 1520Sigor@sysoev.ru continue; 1530Sigor@sysoev.ru } 1540Sigor@sysoev.ru 1550Sigor@sysoev.ru goto done; 1560Sigor@sysoev.ru } 1570Sigor@sysoev.ru 1580Sigor@sysoev.ru goto chunk_error; 1590Sigor@sysoev.ru 1600Sigor@sysoev.ru case sw_chunk: 1610Sigor@sysoev.ru /* 1620Sigor@sysoev.ru * This state is processed before the switch. 1630Sigor@sysoev.ru * It added here just to suppress a warning. 1640Sigor@sysoev.ru */ 1650Sigor@sysoev.ru continue; 1660Sigor@sysoev.ru } 1670Sigor@sysoev.ru } 1680Sigor@sysoev.ru 1690Sigor@sysoev.ru if (b->retain == 0) { 1700Sigor@sysoev.ru /* No chunk data was found in a buffer. */ 171*1Sigor@sysoev.ru nxt_thread_current_work_queue_add(task->thread, 172*1Sigor@sysoev.ru b->completion_handler, 173*1Sigor@sysoev.ru task, b, b->parent); 1740Sigor@sysoev.ru 1750Sigor@sysoev.ru } 1760Sigor@sysoev.ru 1770Sigor@sysoev.ru next: 1780Sigor@sysoev.ru 1790Sigor@sysoev.ru continue; 1800Sigor@sysoev.ru } 1810Sigor@sysoev.ru 1820Sigor@sysoev.ru hcp->state = state; 1830Sigor@sysoev.ru 1840Sigor@sysoev.ru return out; 1850Sigor@sysoev.ru 1860Sigor@sysoev.ru chunk_error: 1870Sigor@sysoev.ru 1880Sigor@sysoev.ru hcp->chunk_error = 1; 1890Sigor@sysoev.ru 1900Sigor@sysoev.ru done: 1910Sigor@sysoev.ru 1920Sigor@sysoev.ru nb = nxt_buf_sync_alloc(hcp->mem_pool, NXT_BUF_SYNC_LAST); 1930Sigor@sysoev.ru 1940Sigor@sysoev.ru if (nxt_fast_path(nb != NULL)) { 1950Sigor@sysoev.ru *tail = nb; 1960Sigor@sysoev.ru 1970Sigor@sysoev.ru } else { 1980Sigor@sysoev.ru hcp->error = 1; 1990Sigor@sysoev.ru } 2000Sigor@sysoev.ru 2010Sigor@sysoev.ru // STUB: hcp->chunk_error = 1; 2020Sigor@sysoev.ru // STUB: hcp->error = 1; 2030Sigor@sysoev.ru 2040Sigor@sysoev.ru return out; 2050Sigor@sysoev.ru } 2060Sigor@sysoev.ru 2070Sigor@sysoev.ru 2080Sigor@sysoev.ru static nxt_int_t 2090Sigor@sysoev.ru nxt_http_chunk_buffer(nxt_http_chunk_parse_t *hcp, nxt_buf_t ***tail, 2100Sigor@sysoev.ru nxt_buf_t *in) 2110Sigor@sysoev.ru { 2120Sigor@sysoev.ru u_char *p; 2130Sigor@sysoev.ru size_t size; 2140Sigor@sysoev.ru nxt_buf_t *b; 2150Sigor@sysoev.ru 2160Sigor@sysoev.ru p = hcp->pos; 2170Sigor@sysoev.ru size = in->mem.free - p; 2180Sigor@sysoev.ru 2190Sigor@sysoev.ru if (hcp->chunk_size >= size && in->retain == 0) { 2200Sigor@sysoev.ru /* 2210Sigor@sysoev.ru * Use original buffer if the buffer is lesser than or equal 2220Sigor@sysoev.ru * to a chunk size and this is the first chunk in the buffer. 2230Sigor@sysoev.ru */ 2240Sigor@sysoev.ru in->mem.pos = p; 2250Sigor@sysoev.ru **tail = in; 2260Sigor@sysoev.ru *tail = &in->next; 2270Sigor@sysoev.ru 2280Sigor@sysoev.ru } else { 2290Sigor@sysoev.ru b = nxt_buf_mem_alloc(hcp->mem_pool, 0, 0); 2300Sigor@sysoev.ru if (nxt_slow_path(b == NULL)) { 2310Sigor@sysoev.ru return NXT_ERROR; 2320Sigor@sysoev.ru } 2330Sigor@sysoev.ru 2340Sigor@sysoev.ru **tail = b; 2350Sigor@sysoev.ru *tail = &b->next; 2360Sigor@sysoev.ru 2370Sigor@sysoev.ru b->parent = in; 2380Sigor@sysoev.ru in->retain++; 2390Sigor@sysoev.ru b->mem.pos = p; 2400Sigor@sysoev.ru b->mem.start = p; 2410Sigor@sysoev.ru 2420Sigor@sysoev.ru if (hcp->chunk_size < size) { 2430Sigor@sysoev.ru p += hcp->chunk_size; 2440Sigor@sysoev.ru hcp->pos = p; 2450Sigor@sysoev.ru 2460Sigor@sysoev.ru b->mem.free = p; 2470Sigor@sysoev.ru b->mem.end = p; 2480Sigor@sysoev.ru 2490Sigor@sysoev.ru return NXT_HTTP_CHUNK_END; 2500Sigor@sysoev.ru } 2510Sigor@sysoev.ru 2520Sigor@sysoev.ru b->mem.free = in->mem.free; 2530Sigor@sysoev.ru b->mem.end = in->mem.free; 2540Sigor@sysoev.ru } 2550Sigor@sysoev.ru 2560Sigor@sysoev.ru hcp->chunk_size -= size; 2570Sigor@sysoev.ru 2580Sigor@sysoev.ru if (hcp->chunk_size == 0) { 2590Sigor@sysoev.ru return NXT_HTTP_CHUNK_END_ON_BORDER; 2600Sigor@sysoev.ru } 2610Sigor@sysoev.ru 2620Sigor@sysoev.ru return NXT_HTTP_CHUNK_MIDDLE; 2630Sigor@sysoev.ru } 264