File Coverage

picohttpparser-git/picohttpparser.c
Criterion Covered Total %
statement 145 328 44.2
branch 125 288 43.4
condition n/a
subroutine n/a
pod n/a
total 270 616 43.8


line stmt bran cond sub pod time code
1             /*
2             * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
3             * Shigeo Mitsunari
4             *
5             * The software is licensed under either the MIT License (below) or the Perl
6             * license.
7             *
8             * Permission is hereby granted, free of charge, to any person obtaining a copy
9             * of this software and associated documentation files (the "Software"), to
10             * deal in the Software without restriction, including without limitation the
11             * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12             * sell copies of the Software, and to permit persons to whom the Software is
13             * furnished to do so, subject to the following conditions:
14             *
15             * The above copyright notice and this permission notice shall be included in
16             * all copies or substantial portions of the Software.
17             *
18             * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19             * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20             * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21             * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22             * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23             * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24             * IN THE SOFTWARE.
25             */
26              
27             #include
28             #include
29             #include
30             #ifdef __SSE4_2__
31             #ifdef _MSC_VER
32             #include
33             #else
34             #include
35             #endif
36             #endif
37             #include "picohttpparser.h"
38              
39             #if __GNUC__ >= 3
40             #define likely(x) __builtin_expect(!!(x), 1)
41             #define unlikely(x) __builtin_expect(!!(x), 0)
42             #else
43             #define likely(x) (x)
44             #define unlikely(x) (x)
45             #endif
46              
47             #ifdef _MSC_VER
48             #define ALIGNED(n) _declspec(align(n))
49             #else
50             #define ALIGNED(n) __attribute__((aligned(n)))
51             #endif
52              
53             #define IS_PRINTABLE_ASCII(c) ((unsigned char)(c)-040u < 0137u)
54              
55             #define CHECK_EOF() \
56             if (buf == buf_end) { \
57             *ret = -2; \
58             return NULL; \
59             }
60              
61             #define EXPECT_CHAR_NO_CHECK(ch) \
62             if (*buf++ != ch) { \
63             *ret = -1; \
64             return NULL; \
65             }
66              
67             #define EXPECT_CHAR(ch) \
68             CHECK_EOF(); \
69             EXPECT_CHAR_NO_CHECK(ch);
70              
71             #define ADVANCE_TOKEN(tok, toklen) \
72             do { \
73             const char *tok_start = buf; \
74             static const char ALIGNED(16) ranges2[16] = "\000\040\177\177"; \
75             int found2; \
76             buf = findchar_fast(buf, buf_end, ranges2, 4, &found2); \
77             if (!found2) { \
78             CHECK_EOF(); \
79             } \
80             while (1) { \
81             if (*buf == ' ') { \
82             break; \
83             } else if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { \
84             if ((unsigned char)*buf < '\040' || *buf == '\177') { \
85             *ret = -1; \
86             return NULL; \
87             } \
88             } \
89             ++buf; \
90             CHECK_EOF(); \
91             } \
92             tok = tok_start; \
93             toklen = buf - tok_start; \
94             } while (0)
95              
96             static const char *token_char_map = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
97             "\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0"
98             "\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1"
99             "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0"
100             "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
101             "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
102             "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
103             "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
104              
105 2720           static const char *findchar_fast(const char *buf, const char *buf_end, const char *ranges, size_t ranges_size, int *found)
106             {
107 2720           *found = 0;
108             #if __SSE4_2__
109             if (likely(buf_end - buf >= 16)) {
110             __m128i ranges16 = _mm_loadu_si128((const __m128i *)ranges);
111              
112             size_t left = (buf_end - buf) & ~15;
113             do {
114             __m128i b16 = _mm_loadu_si128((const __m128i *)buf);
115             int r = _mm_cmpestri(ranges16, ranges_size, b16, 16, _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES | _SIDD_UBYTE_OPS);
116             if (unlikely(r != 16)) {
117             buf += r;
118             *found = 1;
119             break;
120             }
121             buf += 16;
122             left -= 16;
123             } while (likely(left != 0));
124             }
125             #else
126             /* suppress unused parameter warning */
127             (void)buf_end;
128             (void)ranges;
129             (void)ranges_size;
130             #endif
131 2720           return buf;
132             }
133              
134 1562           static const char *get_token_to_eol(const char *buf, const char *buf_end, const char **token, size_t *token_len, int *ret)
135 3489           {
136 1562           const char *token_start = buf;
137              
138             #ifdef __SSE4_2__
139             static const char ALIGNED(16) ranges1[16] = "\0\010" /* allow HT */
140             "\012\037" /* allow SP and up to but not including DEL */
141             "\177\177"; /* allow chars w. MSB set */
142             int found;
143             buf = findchar_fast(buf, buf_end, ranges1, 6, &found);
144             if (found)
145             goto FOUND_CTL;
146             #else
147             /* find non-printable char within the next 8 bytes, this is the hottest code; manually inlined */
148 5051 100         while (likely(buf_end - buf >= 8)) {
149             #define DOIT() \
150             do { \
151             if (unlikely(!IS_PRINTABLE_ASCII(*buf))) \
152             goto NonPrintable; \
153             ++buf; \
154             } while (0)
155 4997 100         DOIT();
156 4981 100         DOIT();
157 4555 100         DOIT();
158 4518 100         DOIT();
159 4497 100         DOIT();
160 4453 100         DOIT();
161 4040 100         DOIT();
162 4017 100         DOIT();
163             #undef DOIT
164 3489           continue;
165 1508           NonPrintable:
166 1508 50         if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) {
    50          
    0          
167 1508           goto FOUND_CTL;
168             }
169 0           ++buf;
170             }
171             #endif
172 113           for (;; ++buf) {
173 167 100         CHECK_EOF();
174 165 100         if (unlikely(!IS_PRINTABLE_ASCII(*buf))) {
175 52 50         if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) {
    50          
    0          
176 52           goto FOUND_CTL;
177             }
178             }
179             }
180 1560           FOUND_CTL:
181 1560 100         if (likely(*buf == '\015')) {
182 1533           ++buf;
183 1533 50         EXPECT_CHAR('\012');
    50          
184 1533           *token_len = buf - 2 - token_start;
185 27 100         } else if (*buf == '\012') {
186 26           *token_len = buf - token_start;
187 26           ++buf;
188             } else {
189 1           *ret = -1;
190 1           return NULL;
191             }
192 1559           *token = token_start;
193              
194 1559           return buf;
195             }
196              
197 16           static const char *is_complete(const char *buf, const char *buf_end, size_t last_len, int *ret)
198             {
199 16           int ret_cnt = 0;
200 16 100         buf = last_len < 3 ? buf : buf + last_len - 3;
201              
202             while (1) {
203 29112 100         CHECK_EOF();
204 29102 100         if (*buf == '\015') {
205 21           ++buf;
206 21 50         CHECK_EOF();
207 21 50         EXPECT_CHAR('\012');
    50          
208 21           ++ret_cnt;
209 29081 50         } else if (*buf == '\012') {
210 0           ++buf;
211 0           ++ret_cnt;
212             } else {
213 29081           ++buf;
214 29081           ret_cnt = 0;
215             }
216 29102 100         if (ret_cnt == 2) {
217 6           return buf;
218             }
219             }
220              
221             *ret = -2;
222             return NULL;
223             }
224              
225             #define PARSE_INT(valp_, mul_) \
226             if (*buf < '0' || '9' < *buf) { \
227             buf++; \
228             *ret = -1; \
229             return NULL; \
230             } \
231             *(valp_) = (mul_) * (*buf++ - '0');
232              
233             #define PARSE_INT_3(valp_) \
234             do { \
235             int res_ = 0; \
236             PARSE_INT(&res_, 100) \
237             *valp_ = res_; \
238             PARSE_INT(&res_, 10) \
239             *valp_ += res_; \
240             PARSE_INT(&res_, 1) \
241             *valp_ += res_; \
242             } while (0)
243              
244             /* returned pointer is always within [buf, buf_end), or null */
245 2146           static const char *parse_token(const char *buf, const char *buf_end, const char **token, size_t *token_len, char next_char,
246             int *ret)
247             {
248             /* We use pcmpestri to detect non-token characters. This instruction can take no more than eight character ranges (8*2*8=128
249             * bits that is the size of a SSE register). Due to this restriction, characters `|` and `~` are handled in the slow loop. */
250             static const char ALIGNED(16) ranges[] = "\x00 " /* control chars and up to SP */
251             "\"\"" /* 0x22 */
252             "()" /* 0x28,0x29 */
253             ",," /* 0x2c */
254             "//" /* 0x2f */
255             ":@" /* 0x3a-0x40 */
256             "[]" /* 0x5b-0x5d */
257             "{\xff"; /* 0x7b-0xff */
258 2146           const char *buf_start = buf;
259             int found;
260 2146           buf = findchar_fast(buf, buf_end, ranges, sizeof(ranges) - 1, &found);
261 2146 50         if (!found) {
262 2146 50         CHECK_EOF();
263             }
264             while (1) {
265 17904 100         if (*buf == next_char) {
266 2136           break;
267 15768 100         } else if (!token_char_map[(unsigned char)*buf]) {
268 8           *ret = -1;
269 8           return NULL;
270             }
271 15760           ++buf;
272 15760 100         CHECK_EOF();
273             }
274 2136           *token = buf_start;
275 2136           *token_len = buf - buf_start;
276 2136           return buf;
277             }
278              
279             /* returned pointer is always within [buf, buf_end), or null */
280 566           static const char *parse_http_version(const char *buf, const char *buf_end, int *minor_version, int *ret)
281             {
282             /* we want at least [HTTP/1.] to try to parse */
283 566 50         if (buf_end - buf < 9) {
284 0           *ret = -2;
285 0           return NULL;
286             }
287 566 100         EXPECT_CHAR_NO_CHECK('H');
288 565 50         EXPECT_CHAR_NO_CHECK('T');
289 565 50         EXPECT_CHAR_NO_CHECK('T');
290 565 50         EXPECT_CHAR_NO_CHECK('P');
291 565 50         EXPECT_CHAR_NO_CHECK('/');
292 565 50         EXPECT_CHAR_NO_CHECK('1');
293 565 50         EXPECT_CHAR_NO_CHECK('.');
294 565 50         PARSE_INT(minor_version, 1);
    50          
295 565           return buf;
296             }
297              
298 565           static const char *parse_headers(const char *buf, const char *buf_end, struct phr_header *headers, size_t *num_headers,
299             size_t max_headers, int *ret)
300             {
301 1559           for (;; ++*num_headers) {
302 2130 100         CHECK_EOF();
303 2121 100         if (*buf == '\015') {
304 545           ++buf;
305 545 50         EXPECT_CHAR('\012');
    50          
306 545           break;
307 1576 100         } else if (*buf == '\012') {
308 11           ++buf;
309 11           break;
310             }
311 1565 100         if (*num_headers == max_headers) {
312 2           *ret = -1;
313 2           return NULL;
314             }
315 1563 100         if (!(*num_headers != 0 && (*buf == ' ' || *buf == '\t'))) {
    100          
    50          
316             /* parsing name, but do not discard SP before colon, see
317             * http://www.mozilla.org/security/announce/2006/mfsa2006-33.html */
318 1562 50         if ((buf = parse_token(buf, buf_end, &headers[*num_headers].name, &headers[*num_headers].name_len, ':', ret)) == NULL) {
319 0           return NULL;
320             }
321 1562 50         if (headers[*num_headers].name_len == 0) {
322 0           *ret = -1;
323 0           return NULL;
324             }
325 1562           ++buf;
326 1566           for (;; ++buf) {
327 3128 100         CHECK_EOF();
328 3127 100         if (!(*buf == ' ' || *buf == '\t')) {
    100          
329 1561           break;
330             }
331             }
332             } else {
333 1           headers[*num_headers].name = NULL;
334 1           headers[*num_headers].name_len = 0;
335             }
336             const char *value;
337             size_t value_len;
338 1562 100         if ((buf = get_token_to_eol(buf, buf_end, &value, &value_len, ret)) == NULL) {
339 3           return NULL;
340             }
341             /* remove trailing SPs and HTABs */
342 1559           const char *value_end = value + value_len;
343 1559 100         for (; value_end != value; --value_end) {
344 1558           const char c = *(value_end - 1);
345 1558 50         if (!(c == ' ' || c == '\t')) {
    50          
346 1558           break;
347             }
348             }
349 1559           headers[*num_headers].value = value;
350 1559           headers[*num_headers].value_len = value_end - value;
351             }
352 556           return buf;
353             }
354              
355 584           static const char *parse_request(const char *buf, const char *buf_end, const char **method, size_t *method_len, const char **path,
356             size_t *path_len, int *minor_version, struct phr_header *headers, size_t *num_headers,
357             size_t max_headers, int *ret)
358             {
359             /* skip first empty line (some clients add CRLF after POST content) */
360 584 50         CHECK_EOF();
361 584 50         if (*buf == '\015') {
362 0           ++buf;
363 0 0         EXPECT_CHAR('\012');
    0          
364 584 50         } else if (*buf == '\012') {
365 0           ++buf;
366             }
367              
368             /* parse request line */
369 584 100         if ((buf = parse_token(buf, buf_end, method, method_len, ' ', ret)) == NULL) {
370 10           return NULL;
371             }
372             do {
373 574           ++buf;
374 574 50         CHECK_EOF();
375 574 50         } while (*buf == ' ');
376 61725 50         ADVANCE_TOKEN(*path, *path_len);
    50          
    100          
    100          
    50          
    0          
    100          
377             do {
378 566           ++buf;
379 566 50         CHECK_EOF();
380 566 50         } while (*buf == ' ');
381 566 50         if (*method_len == 0 || *path_len == 0) {
    50          
382 0           *ret = -1;
383 0           return NULL;
384             }
385 566 100         if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) {
386 1           return NULL;
387             }
388 565 100         if (*buf == '\015') {
389 554           ++buf;
390 554 50         EXPECT_CHAR('\012');
    50          
391 11 50         } else if (*buf == '\012') {
392 11           ++buf;
393             } else {
394 0           *ret = -1;
395 0           return NULL;
396             }
397              
398 565           return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
399             }
400              
401 594           int phr_parse_request(const char *buf_start, size_t len, const char **method, size_t *method_len, const char **path,
402             size_t *path_len, int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len)
403             {
404 594           const char *buf = buf_start, *buf_end = buf_start + len;
405 594           size_t max_headers = *num_headers;
406             int r;
407              
408 594           *method = NULL;
409 594           *method_len = 0;
410 594           *path = NULL;
411 594           *path_len = 0;
412 594           *minor_version = -1;
413 594           *num_headers = 0;
414              
415             /* if last_len != 0, check if the request is complete (a fast countermeasure
416             againt slowloris */
417 594 100         if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
    100          
418 10           return r;
419             }
420              
421 584 100         if ((buf = parse_request(buf, buf_end, method, method_len, path, path_len, minor_version, headers, num_headers, max_headers,
422             &r)) == NULL) {
423 28           return r;
424             }
425              
426 556           return (int)(buf - buf_start);
427             }
428              
429 0           static const char *parse_response(const char *buf, const char *buf_end, int *minor_version, int *status, const char **msg,
430             size_t *msg_len, struct phr_header *headers, size_t *num_headers, size_t max_headers, int *ret)
431             {
432             /* parse "HTTP/1.x" */
433 0 0         if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) {
434 0           return NULL;
435             }
436             /* skip space */
437 0 0         if (*buf != ' ') {
438 0           *ret = -1;
439 0           return NULL;
440             }
441             do {
442 0           ++buf;
443 0 0         CHECK_EOF();
444 0 0         } while (*buf == ' ');
445             /* parse status code, we want at least [:digit:][:digit:][:digit:] to try to parse */
446 0 0         if (buf_end - buf < 4) {
447 0           *ret = -2;
448 0           return NULL;
449             }
450 0 0         PARSE_INT_3(status);
    0          
    0          
    0          
    0          
    0          
451              
452             /* get message including preceding space */
453 0 0         if ((buf = get_token_to_eol(buf, buf_end, msg, msg_len, ret)) == NULL) {
454 0           return NULL;
455             }
456 0 0         if (*msg_len == 0) {
457             /* ok */
458 0 0         } else if (**msg == ' ') {
459             /* Remove preceding space. Successful return from `get_token_to_eol` guarantees that we would hit something other than SP
460             * before running past the end of the given buffer. */
461             do {
462 0           ++*msg;
463 0           --*msg_len;
464 0 0         } while (**msg == ' ');
465             } else {
466             /* garbage found after status code */
467 0           *ret = -1;
468 0           return NULL;
469             }
470              
471 0           return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
472             }
473              
474 0           int phr_parse_response(const char *buf_start, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len,
475             struct phr_header *headers, size_t *num_headers, size_t last_len)
476             {
477 0           const char *buf = buf_start, *buf_end = buf + len;
478 0           size_t max_headers = *num_headers;
479             int r;
480              
481 0           *minor_version = -1;
482 0           *status = 0;
483 0           *msg = NULL;
484 0           *msg_len = 0;
485 0           *num_headers = 0;
486              
487             /* if last_len != 0, check if the response is complete (a fast countermeasure
488             against slowloris */
489 0 0         if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
    0          
490 0           return r;
491             }
492              
493 0 0         if ((buf = parse_response(buf, buf_end, minor_version, status, msg, msg_len, headers, num_headers, max_headers, &r)) == NULL) {
494 0           return r;
495             }
496              
497 0           return (int)(buf - buf_start);
498             }
499              
500 0           int phr_parse_headers(const char *buf_start, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len)
501             {
502 0           const char *buf = buf_start, *buf_end = buf + len;
503 0           size_t max_headers = *num_headers;
504             int r;
505              
506 0           *num_headers = 0;
507              
508             /* if last_len != 0, check if the response is complete (a fast countermeasure
509             against slowloris */
510 0 0         if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
    0          
511 0           return r;
512             }
513              
514 0 0         if ((buf = parse_headers(buf, buf_end, headers, num_headers, max_headers, &r)) == NULL) {
515 0           return r;
516             }
517              
518 0           return (int)(buf - buf_start);
519             }
520              
521             enum {
522             CHUNKED_IN_CHUNK_SIZE,
523             CHUNKED_IN_CHUNK_EXT,
524             CHUNKED_IN_CHUNK_HEADER_EXPECT_LF,
525             CHUNKED_IN_CHUNK_DATA,
526             CHUNKED_IN_CHUNK_DATA_EXPECT_CR,
527             CHUNKED_IN_CHUNK_DATA_EXPECT_LF,
528             CHUNKED_IN_TRAILERS_LINE_HEAD,
529             CHUNKED_IN_TRAILERS_LINE_MIDDLE
530             };
531              
532 0           static int decode_hex(int ch)
533             {
534 0 0         if ('0' <= ch && ch <= '9') {
    0          
535 0           return ch - '0';
536 0 0         } else if ('A' <= ch && ch <= 'F') {
    0          
537 0           return ch - 'A' + 0xa;
538 0 0         } else if ('a' <= ch && ch <= 'f') {
    0          
539 0           return ch - 'a' + 0xa;
540             } else {
541 0           return -1;
542             }
543             }
544              
545 0           ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *_bufsz)
546             {
547 0           size_t dst = 0, src = 0, bufsz = *_bufsz;
548 0           ssize_t ret = -2; /* incomplete */
549              
550 0           decoder->_total_read += bufsz;
551              
552             while (1) {
553 0           switch (decoder->_state) {
554 0           case CHUNKED_IN_CHUNK_SIZE:
555 0           for (;; ++src) {
556             int v;
557 0 0         if (src == bufsz)
558 0           goto Exit;
559 0 0         if ((v = decode_hex(buf[src])) == -1) {
560 0 0         if (decoder->_hex_count == 0) {
561 0           ret = -1;
562 0           goto Exit;
563             }
564             /* the only characters that may appear after the chunk size are BWS, semicolon, or CRLF */
565 0 0         switch (buf[src]) {
566 0           case ' ':
567             case '\011':
568             case ';':
569             case '\012':
570             case '\015':
571 0           break;
572 0           default:
573 0           ret = -1;
574 0           goto Exit;
575             }
576 0           break;
577             }
578 0 0         if (decoder->_hex_count == sizeof(size_t) * 2) {
579 0           ret = -1;
580 0           goto Exit;
581             }
582 0           decoder->bytes_left_in_chunk = decoder->bytes_left_in_chunk * 16 + v;
583 0           ++decoder->_hex_count;
584             }
585 0           decoder->_hex_count = 0;
586 0           decoder->_state = CHUNKED_IN_CHUNK_EXT;
587             /* fallthru */
588 0           case CHUNKED_IN_CHUNK_EXT:
589             /* RFC 7230 A.2 "Line folding in chunk extensions is disallowed" */
590 0           for (;; ++src) {
591 0 0         if (src == bufsz)
592 0           goto Exit;
593 0 0         if (buf[src] == '\015') {
594 0           break;
595 0 0         } else if (buf[src] == '\012') {
596 0           ret = -1;
597 0           goto Exit;
598             }
599             }
600 0           ++src;
601 0           decoder->_state = CHUNKED_IN_CHUNK_HEADER_EXPECT_LF;
602             /* fallthru */
603 0           case CHUNKED_IN_CHUNK_HEADER_EXPECT_LF:
604 0 0         if (src == bufsz)
605 0           goto Exit;
606 0 0         if (buf[src] != '\012') {
607 0           ret = -1;
608 0           goto Exit;
609             }
610 0           ++src;
611 0 0         if (decoder->bytes_left_in_chunk == 0) {
612 0 0         if (decoder->consume_trailer) {
613 0           decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;
614 0           break;
615             } else {
616 0           goto Complete;
617             }
618             }
619 0           decoder->_state = CHUNKED_IN_CHUNK_DATA;
620             /* fallthru */
621 0           case CHUNKED_IN_CHUNK_DATA: {
622 0           size_t avail = bufsz - src;
623 0 0         if (avail < decoder->bytes_left_in_chunk) {
624 0 0         if (dst != src)
625 0           memmove(buf + dst, buf + src, avail);
626 0           src += avail;
627 0           dst += avail;
628 0           decoder->bytes_left_in_chunk -= avail;
629 0           goto Exit;
630             }
631 0 0         if (dst != src)
632 0           memmove(buf + dst, buf + src, decoder->bytes_left_in_chunk);
633 0           src += decoder->bytes_left_in_chunk;
634 0           dst += decoder->bytes_left_in_chunk;
635 0           decoder->bytes_left_in_chunk = 0;
636 0           decoder->_state = CHUNKED_IN_CHUNK_DATA_EXPECT_CR;
637             }
638             /* fallthru */
639 0           case CHUNKED_IN_CHUNK_DATA_EXPECT_CR:
640 0 0         if (src == bufsz)
641 0           goto Exit;
642 0 0         if (buf[src] != '\015') {
643 0           ret = -1;
644 0           goto Exit;
645             }
646 0           ++src;
647 0           decoder->_state = CHUNKED_IN_CHUNK_DATA_EXPECT_LF;
648             /* fallthru */
649 0           case CHUNKED_IN_CHUNK_DATA_EXPECT_LF:
650 0 0         if (src == bufsz)
651 0           goto Exit;
652 0 0         if (buf[src] != '\012') {
653 0           ret = -1;
654 0           goto Exit;
655             }
656 0           ++src;
657 0           decoder->_state = CHUNKED_IN_CHUNK_SIZE;
658 0           break;
659 0           case CHUNKED_IN_TRAILERS_LINE_HEAD:
660 0           for (;; ++src) {
661 0 0         if (src == bufsz)
662 0           goto Exit;
663 0 0         if (buf[src] != '\015')
664 0           break;
665             }
666 0 0         if (buf[src++] == '\012')
667 0           goto Complete;
668 0           decoder->_state = CHUNKED_IN_TRAILERS_LINE_MIDDLE;
669             /* fallthru */
670 0           case CHUNKED_IN_TRAILERS_LINE_MIDDLE:
671 0           for (;; ++src) {
672 0 0         if (src == bufsz)
673 0           goto Exit;
674 0 0         if (buf[src] == '\012')
675 0           break;
676             }
677 0           ++src;
678 0           decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;
679 0           break;
680 0           default:
681             assert(!"decoder is corrupt");
682             }
683             }
684              
685 0           Complete:
686 0           ret = bufsz - src;
687 0           Exit:
688 0 0         if (dst != src)
689 0           memmove(buf + dst, buf + src, bufsz - src);
690 0           *_bufsz = dst;
691             /* if incomplete but the overhead of the chunked encoding is >=100KB and >80%, signal an error */
692 0 0         if (ret == -2) {
693 0           decoder->_total_overhead += bufsz - dst;
694 0 0         if (decoder->_total_overhead >= 100 * 1024 && decoder->_total_read - decoder->_total_overhead < decoder->_total_read / 4)
    0          
695 0           ret = -1;
696             }
697 0           return ret;
698             }
699              
700 0           int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder)
701             {
702 0           return decoder->_state == CHUNKED_IN_CHUNK_DATA;
703             }
704              
705             #undef CHECK_EOF
706             #undef EXPECT_CHAR
707             #undef ADVANCE_TOKEN