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