| 377 | /* ── Request reading ──────────────────────────────────────────── */ |
| 378 | |
| 379 | int cbm_httpd_read_request(cbm_http_conn_t *c, cbm_http_req_t *req) { |
| 380 | if (!c || !req) |
| 381 | return -1; |
| 382 | |
| 383 | char *head = malloc(CBM_HTTP_MAX_HEAD); |
| 384 | if (!head) |
| 385 | return -1; |
| 386 | |
| 387 | int64_t deadline = now_ms() + c->recv_deadline_ms; |
| 388 | size_t have = 0; |
| 389 | size_t body_off = 0, clen = 0; |
| 390 | int rc = CBM_HTTP_NEED_MORE; |
| 391 | |
| 392 | /* Read until the head parses (or fails to). */ |
| 393 | for (;;) { |
| 394 | int64_t remaining = deadline - now_ms(); |
| 395 | if (remaining <= 0) { |
| 396 | free(head); |
| 397 | return 408; |
| 398 | } |
| 399 | int w = wait_readable(c->fd, (int)remaining); |
| 400 | if (w == 0) { |
| 401 | free(head); |
| 402 | return 408; |
| 403 | } |
| 404 | if (w < 0) { |
| 405 | free(head); |
| 406 | return -1; |
| 407 | } |
| 408 | #ifdef _WIN32 |
| 409 | int n = recv(c->fd, head + have, (int)(CBM_HTTP_MAX_HEAD - have), 0); |
| 410 | #else |
| 411 | ssize_t n = recv(c->fd, head + have, CBM_HTTP_MAX_HEAD - have, 0); |
| 412 | #endif |
| 413 | if (n <= 0) { |
| 414 | free(head); /* peer vanished mid-request — nothing to answer */ |
| 415 | return -1; |
| 416 | } |
| 417 | have += (size_t)n; |
| 418 | |
| 419 | rc = cbm_http_parse_head(head, have, req, &body_off, &clen); |
| 420 | if (rc == 0) |
| 421 | break; |
| 422 | if (rc != CBM_HTTP_NEED_MORE) { |
| 423 | free(head); |
| 424 | return rc; |
| 425 | } |
| 426 | if (have >= CBM_HTTP_MAX_HEAD) { |
| 427 | free(head); |
| 428 | return 431; |
| 429 | } |
| 430 | } |
| 431 | |
| 432 | /* Read the body per Content-Length (already capped by the parser). */ |
| 433 | if (clen > 0) { |
| 434 | char *body = malloc(clen + 1); |
| 435 | if (!body) { |
| 436 | free(head); |
no test coverage detected