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