| 474 | } |
| 475 | |
| 476 | optional<Request> Server::read_request(ClientConnection& client) { |
| 477 | // Read data from socket |
| 478 | char buffer[4096]; |
| 479 | |
| 480 | #ifdef FL_IS_WIN |
| 481 | int bytes = recv(client.fd, buffer, sizeof(buffer), 0); |
| 482 | if (bytes <= 0) { |
| 483 | if (WSAGetLastError() != SOCKET_ERROR_WOULD_BLOCK) { |
| 484 | return nullopt; // Connection closed or error |
| 485 | } |
| 486 | return nullopt; // No data yet |
| 487 | } |
| 488 | #else |
| 489 | ssize_t bytes = recv(client.fd, buffer, sizeof(buffer), MSG_DONTWAIT); |
| 490 | if (bytes <= 0) { |
| 491 | if (errno != EWOULDBLOCK && errno != EAGAIN) { |
| 492 | return nullopt; // Connection closed or error |
| 493 | } |
| 494 | return nullopt; // No data yet |
| 495 | } |
| 496 | #endif |
| 497 | |
| 498 | client.buffer.append(buffer, static_cast<size_t>(bytes)); |
| 499 | |
| 500 | // Check if we have complete HTTP request (ends with \r\n\r\n) |
| 501 | size_t header_end = client.buffer.find("\r\n\r\n"); |
| 502 | if (header_end == string::npos) { |
| 503 | return nullopt; // Headers not complete yet |
| 504 | } |
| 505 | |
| 506 | // Parse request |
| 507 | Request req; |
| 508 | |
| 509 | // Split into lines |
| 510 | string header_section = client.buffer.substr(0, header_end); |
| 511 | vector<string> lines = split(header_section, '\n'); |
| 512 | |
| 513 | if (lines.empty()) { |
| 514 | return nullopt; |
| 515 | } |
| 516 | |
| 517 | // Parse request line: "GET /path HTTP/1.1\r" |
| 518 | string request_line = trim(lines[0]); |
| 519 | vector<string> parts = split(request_line, ' '); |
| 520 | if (parts.size() < 3) { |
| 521 | return nullopt; |
| 522 | } |
| 523 | |
| 524 | req.mMethod = parts[0]; |
| 525 | |
| 526 | // Split path and query string |
| 527 | string full_path = parts[1]; |
| 528 | size_t query_pos = full_path.find('?'); |
| 529 | if (query_pos != string::npos) { |
| 530 | req.mPath = full_path.substr(0, query_pos); |
| 531 | req.mQuery = parse_query_string(full_path.substr(query_pos)); |
| 532 | } else { |
| 533 | req.mPath = full_path; |
nothing calls this directly
no test coverage detected