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