| 189 | } |
| 190 | |
| 191 | bool HttpStreamServer::readHttpRequestHeader(u32 clientId) { |
| 192 | ClientState* state = getOrCreateClientState(clientId); |
| 193 | if (!state) { |
| 194 | return false; |
| 195 | } |
| 196 | |
| 197 | if (state->httpHeaderReceived) { |
| 198 | return true; // Already received |
| 199 | } |
| 200 | |
| 201 | // Read HTTP request header in chunks, accumulating in state->headerBuffer |
| 202 | // across multiple calls (non-blocking sockets may return partial data) |
| 203 | u8 buffer[256]; |
| 204 | |
| 205 | // Maximum header size: 8KB (generous for HTTP headers) |
| 206 | const size_t MAX_HEADER_SIZE = 8192; |
| 207 | |
| 208 | while (state->headerBuffer.size() < MAX_HEADER_SIZE) { |
| 209 | int received = mNativeServer->recv(clientId, buffer); |
| 210 | if (received < 0) { |
| 211 | return false; |
| 212 | } |
| 213 | if (received == 0) { |
| 214 | return false; |
| 215 | } |
| 216 | |
| 217 | state->headerBuffer.append(reinterpret_cast<const char*>(buffer), received); // ok reinterpret cast |
| 218 | |
| 219 | // Check for \r\n\r\n pattern (end of headers) |
| 220 | if (state->headerBuffer.size() >= 4) { |
| 221 | size_t pos = state->headerBuffer.find("\r\n\r\n"); |
| 222 | if (pos != fl::string::npos) { |
| 223 | break; |
| 224 | } |
| 225 | } |
| 226 | } |
| 227 | |
| 228 | // Validate the header directly (don't use HttpRequestParser which waits for |
| 229 | // the chunked body to complete — we only need headers for the handshake). |
| 230 | const fl::string& hdr = state->headerBuffer; |
| 231 | |
| 232 | // Must start with "POST /rpc" |
| 233 | if (hdr.find("POST /rpc") != 0) { |
| 234 | return false; |
| 235 | } |
| 236 | |
| 237 | // Must have Content-Type: application/json (case-insensitive check) |
| 238 | if (hdr.find("Content-Type: application/json") == fl::string::npos && |
| 239 | hdr.find("content-type: application/json") == fl::string::npos) { |
| 240 | return false; |
| 241 | } |
| 242 | |
| 243 | // Must have Transfer-Encoding: chunked (case-insensitive check) |
| 244 | if (hdr.find("Transfer-Encoding: chunked") == fl::string::npos && |
| 245 | hdr.find("transfer-encoding: chunked") == fl::string::npos) { |
| 246 | return false; |
| 247 | } |
| 248 | |