1 2 /* 3 * Copyright (C) Igor Sysoev 4 * Copyright (C) NGINX, Inc. 5 */ 6 7 #include <nxt_main.h> 8 9 10 #define NXT_HTTP_CHUNK_MIDDLE 0 11 #define NXT_HTTP_CHUNK_END_ON_BORDER 1 12 #define NXT_HTTP_CHUNK_END 2 13 14 15 #define \ 16 nxt_size_is_sufficient(cs) \ 17 (cs < ((__typeof__(cs)) 1 << (sizeof(cs) * 8 - 4))) 18 19 20 static nxt_int_t nxt_http_chunk_buffer(nxt_http_chunk_parse_t *hcp, 21 nxt_buf_t ***tail, nxt_buf_t *in); 22 23 24 nxt_buf_t * 25 nxt_http_chunk_parse(nxt_task_t *task, nxt_http_chunk_parse_t *hcp, 26 nxt_buf_t *in) 27 { 28 u_char c, ch; 29 nxt_int_t ret; 30 nxt_buf_t *b, *out, *nb, **tail; 31 enum { 32 sw_start = 0, 33 sw_chunk_size, 34 sw_chunk_size_linefeed, 35 sw_chunk_end_newline, 36 sw_chunk_end_linefeed, 37 sw_chunk, 38 } state; 39 40 out = NULL; 41 tail = &out; 42 43 state = hcp->state; 44 45 for (b = in; b != NULL; b = b->next) { 46 47 hcp->pos = b->mem.pos; 48 49 while (hcp->pos < b->mem.free) { 50 /* 51 * The sw_chunk state is tested outside the switch 52 * to preserve hcp->pos and to not touch memory. 53 */ 54 if (state == sw_chunk) { 55 ret = nxt_http_chunk_buffer(hcp, &tail, b); 56 57 if (ret == NXT_HTTP_CHUNK_MIDDLE) { 58 goto next; 59 } 60 61 if (nxt_slow_path(ret == NXT_ERROR)) { 62 hcp->error = 1; 63 goto done; 64 } 65 66 state = sw_chunk_end_newline; 67 68 if (ret == NXT_HTTP_CHUNK_END_ON_BORDER) { 69 goto next; 70 } 71 72 /* ret == NXT_HTTP_CHUNK_END_ON_BORDER */ 73 } 74 75 ch = *hcp->pos++; 76 77 switch (state) { 78 79 case sw_start: 80 state = sw_chunk_size; 81 82 c = ch - '0'; 83 84 if (c <= 9) { 85 hcp->chunk_size = c; 86 continue; 87 } 88 89 c = (ch | 0x20) - 'a'; 90 91 if (c <= 5) { 92 hcp->chunk_size = 0x0A + c; 93 continue; 94 } 95 96 goto chunk_error; 97 98 case sw_chunk_size: 99 100 c = ch - '0'; 101 102 if (c > 9) { 103 c = (ch | 0x20) - 'a'; 104 105 if (nxt_fast_path(c <= 5)) { 106 c += 0x0A; 107 108 } else if (nxt_fast_path(ch == '\r')) { 109 state = sw_chunk_size_linefeed; 110 continue; 111 112 } else { 113 goto chunk_error; 114 } 115 } 116 117 if (nxt_fast_path(nxt_size_is_sufficient(hcp->chunk_size))) { 118 hcp->chunk_size = (hcp->chunk_size << 4) + c; 119 continue; 120 } 121 122 goto chunk_error; 123 124 case sw_chunk_size_linefeed: 125 if (nxt_fast_path(ch == '\n')) { 126 127 if (hcp->chunk_size != 0) { 128 state = sw_chunk; 129 continue; 130 } 131 132 hcp->last = 1; 133 state = sw_chunk_end_newline; 134 continue; 135 } 136 137 goto chunk_error; 138 139 case sw_chunk_end_newline: 140 if (nxt_fast_path(ch == '\r')) { 141 state = sw_chunk_end_linefeed; 142 continue; 143 } 144 145 goto chunk_error; 146 147 case sw_chunk_end_linefeed: 148 if (nxt_fast_path(ch == '\n')) { 149 150 if (!hcp->last) { 151 state = sw_start; 152 continue; 153 } 154 155 goto done; 156 } 157 158 goto chunk_error; 159 160 case sw_chunk: 161 /* 162 * This state is processed before the switch. 163 * It added here just to suppress a warning. 164 */ 165 continue; 166 } 167 } 168 169 if (b->retain == 0) { 170 /* No chunk data was found in a buffer. */ 171 nxt_thread_current_work_queue_add(task->thread, 172 b->completion_handler, 173 task, b, b->parent); 174 175 } 176 177 next: 178 179 continue; 180 } 181 182 hcp->state = state; 183 184 return out; 185 186 chunk_error: 187 188 hcp->chunk_error = 1; 189 190 done: 191 192 nb = nxt_buf_sync_alloc(hcp->mem_pool, NXT_BUF_SYNC_LAST); 193 194 if (nxt_fast_path(nb != NULL)) { 195 *tail = nb; 196 197 } else { 198 hcp->error = 1; 199 } 200 201 // STUB: hcp->chunk_error = 1; 202 // STUB: hcp->error = 1; 203 204 return out; 205 } 206 207 208 static nxt_int_t 209 nxt_http_chunk_buffer(nxt_http_chunk_parse_t *hcp, nxt_buf_t ***tail, 210 nxt_buf_t *in) 211 { 212 u_char *p; 213 size_t size; 214 nxt_buf_t *b; 215 216 p = hcp->pos; 217 size = in->mem.free - p; 218 219 if (hcp->chunk_size >= size && in->retain == 0) { 220 /* 221 * Use original buffer if the buffer is lesser than or equal 222 * to a chunk size and this is the first chunk in the buffer. 223 */ 224 in->mem.pos = p; 225 **tail = in; 226 *tail = &in->next; 227 228 } else { 229 b = nxt_buf_mem_alloc(hcp->mem_pool, 0, 0); 230 if (nxt_slow_path(b == NULL)) { 231 return NXT_ERROR; 232 } 233 234 **tail = b; 235 *tail = &b->next; 236 237 b->parent = in; 238 in->retain++; 239 b->mem.pos = p; 240 b->mem.start = p; 241 242 if (hcp->chunk_size < size) { 243 p += hcp->chunk_size; 244 hcp->pos = p; 245 246 b->mem.free = p; 247 b->mem.end = p; 248 249 return NXT_HTTP_CHUNK_END; 250 } 251 252 b->mem.free = in->mem.free; 253 b->mem.end = in->mem.free; 254 } 255 256 hcp->chunk_size -= size; 257 258 if (hcp->chunk_size == 0) { 259 return NXT_HTTP_CHUNK_END_ON_BORDER; 260 } 261 262 return NXT_HTTP_CHUNK_MIDDLE; 263 } 264