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