| 601 | /* ── Request reading ──────────────────────────────────────────── */ |
| 602 | |
| 603 | int cbm_httpd_read_request(cbm_http_conn_t *c, cbm_http_req_t *req) { |
| 604 | if (!c || !req) |
| 605 | return -1; |
| 606 | |
| 607 | char *head = malloc(CBM_HTTP_MAX_HEAD); |
| 608 | if (!head) |
| 609 | return -1; |
| 610 | |
| 611 | int64_t deadline = now_ms() + c->recv_deadline_ms; |
| 612 | size_t have = 0; |
| 613 | size_t body_off = 0, clen = 0; |
| 614 | int rc = CBM_HTTP_NEED_MORE; |
| 615 | |
| 616 | /* Read until the head parses (or fails to). */ |
| 617 | for (;;) { |
| 618 | int w = wait_connection_ready(c, false, deadline); |
| 619 | if (w == 0) { |
| 620 | free(head); |
| 621 | return 408; |
| 622 | } |
| 623 | if (w < 0) { |
| 624 | free(head); |
| 625 | return -1; |
| 626 | } |
| 627 | #ifdef _WIN32 |
| 628 | int n = recv(c->fd, head + have, (int)(CBM_HTTP_MAX_HEAD - have), 0); |
| 629 | #else |
| 630 | ssize_t n = recv(c->fd, head + have, CBM_HTTP_MAX_HEAD - have, 0); |
| 631 | #endif |
| 632 | if (n < 0 && socket_would_block()) |
| 633 | continue; |
| 634 | if (n <= 0) { |
| 635 | free(head); /* peer vanished mid-request — nothing to answer */ |
| 636 | return -1; |
| 637 | } |
| 638 | have += (size_t)n; |
| 639 | |
| 640 | rc = cbm_http_parse_head(head, have, req, &body_off, &clen); |
| 641 | if (rc == 0) |
| 642 | break; |
| 643 | if (rc != CBM_HTTP_NEED_MORE) { |
| 644 | free(head); |
| 645 | return rc; |
| 646 | } |
| 647 | if (have >= CBM_HTTP_MAX_HEAD) { |
| 648 | free(head); |
| 649 | return 431; |
| 650 | } |
| 651 | } |
| 652 | |
| 653 | /* Read the body per Content-Length (already capped by the parser). */ |
| 654 | if (clen > 0) { |
| 655 | char *body = malloc(clen + 1); |
| 656 | if (!body) { |
| 657 | free(head); |
| 658 | return -1; |
| 659 | } |
| 660 | size_t got = have - body_off; |
no test coverage detected