| 228 | } |
| 229 | |
| 230 | void handleHttpRequest(s32 clientSocket) |
| 231 | { |
| 232 | // Read only up to and including the header terminator; the body is either |
| 233 | // streamed to disk (upload paths) or read into RAM afterwards (others). |
| 234 | std::string data; |
| 235 | data.reserve(4096); |
| 236 | constexpr size_t RECV_CHUNK = 64 * 1024; |
| 237 | std::vector<char> buffer(RECV_CHUNK); |
| 238 | size_t headerEnd = std::string::npos; |
| 239 | int idleMs = 0; |
| 240 | |
| 241 | while (true) { |
| 242 | ssize_t received = pollRecv(clientSocket, buffer.data(), buffer.size(), idleMs, false, HEADER_TIMEOUT_MS); |
| 243 | if (received <= 0) { |
| 244 | break; |
| 245 | } |
| 246 | data.append(buffer.data(), received); |
| 247 | headerEnd = data.find("\r\n\r\n"); |
| 248 | if (headerEnd != std::string::npos) { |
| 249 | break; |
| 250 | } |
| 251 | if (data.size() > MAX_HEADER_SIZE) { |
| 252 | break; // no terminator within the cap: abandon |
| 253 | } |
| 254 | } |
| 255 | if (headerEnd == std::string::npos) { |
| 256 | return; // never got a complete header block |
| 257 | } |
| 258 | |
| 259 | std::string headers = data.substr(0, headerEnd); |
| 260 | std::string path = extractPath(headers); |
| 261 | size_t contentLength = parseContentLength(headers); |
| 262 | size_t bodyStart = headerEnd + 4; |
| 263 | |
| 264 | // Streaming upload path. |
| 265 | std::string tmpPath; |
| 266 | Server::UploadHandler uploadHandler; |
| 267 | bool isUpload = false; |
| 268 | { |
| 269 | std::lock_guard<std::mutex> lock(handlersMutex); |
| 270 | auto it = uploadHandlers.find(path); |
| 271 | if (it != uploadHandlers.end()) { |
| 272 | tmpPath = it->second.first; |
| 273 | uploadHandler = it->second.second; |
| 274 | isUpload = true; |
| 275 | } |
| 276 | } |
| 277 | if (isUpload) { |
| 278 | const char* leftover = data.data() + bodyStart; |
| 279 | size_t leftoverLen = data.size() - bodyStart; |
| 280 | bool complete = streamBodyToFile(clientSocket, tmpPath, contentLength, leftover, leftoverLen); |
| 281 | if (!complete) { |
| 282 | // Cancelled, stalled, or dropped mid-upload: drop the request and |
| 283 | // clear the transfer UI; the handler never runs on a partial body. |
| 284 | std::remove(tmpPath.c_str()); |
| 285 | TransferStatus::end(); |
| 286 | Logging::info("Upload to {} abandoned before the full body arrived.", path); |
| 287 | return; |
no test coverage detected