| 199 | } |
| 200 | |
| 201 | static void handleHttpRequest(s32 clientSocket) |
| 202 | { |
| 203 | // Read only up to and including the header terminator; the body is either |
| 204 | // streamed to disk (upload paths) or read into RAM afterwards (others). |
| 205 | std::string data; |
| 206 | data.reserve(4096); |
| 207 | constexpr size_t RECV_CHUNK = 32 * 1024; |
| 208 | std::unique_ptr<char[]> buffer(new char[RECV_CHUNK]); |
| 209 | size_t headerEnd = std::string::npos; |
| 210 | int idleMs = 0; |
| 211 | |
| 212 | while (true) { |
| 213 | ssize_t received = pollRecv(clientSocket, buffer.get(), RECV_CHUNK, idleMs, false); |
| 214 | if (received <= 0) { |
| 215 | break; |
| 216 | } |
| 217 | data.append(buffer.get(), received); |
| 218 | headerEnd = data.find("\r\n\r\n"); |
| 219 | if (headerEnd != std::string::npos) { |
| 220 | break; |
| 221 | } |
| 222 | if (data.size() > MAX_HEADER_SIZE) { |
| 223 | break; |
| 224 | } |
| 225 | } |
| 226 | if (headerEnd == std::string::npos) { |
| 227 | return; |
| 228 | } |
| 229 | |
| 230 | std::string headers = data.substr(0, headerEnd); |
| 231 | std::string path = extractPath(headers); |
| 232 | size_t contentLength = parseContentLength(headers); |
| 233 | size_t bodyStart = headerEnd + 4; |
| 234 | |
| 235 | // Streaming upload path. |
| 236 | std::string tmpPath; |
| 237 | Server::UploadHandler uploadHandler; |
| 238 | bool isUpload = false; |
| 239 | { |
| 240 | std::lock_guard<std::mutex> lock(handlersMutex); |
| 241 | auto it = uploadHandlers.find(path); |
| 242 | if (it != uploadHandlers.end()) { |
| 243 | tmpPath = it->second.first; |
| 244 | uploadHandler = it->second.second; |
| 245 | isUpload = true; |
| 246 | } |
| 247 | } |
| 248 | if (isUpload) { |
| 249 | std::u16string tmpU16 = StringUtils::UTF8toUTF16(tmpPath.c_str()); |
| 250 | const char* leftover = data.data() + bodyStart; |
| 251 | size_t leftoverLen = data.size() - bodyStart; |
| 252 | bool complete = streamBodyToFile(clientSocket, tmpU16, contentLength, leftover, leftoverLen); |
| 253 | if (!complete) { |
| 254 | // Cancelled, stalled, or dropped mid-upload: drop the request and |
| 255 | // clear the transfer UI; the handler never runs on a partial body. |
| 256 | FSUSER_DeleteFile(Archive::sdmc(), fsMakePath(PATH_UTF16, tmpU16.data())); |
| 257 | TransferStatus::end(); |
| 258 | Logging::info("Upload abandoned before the full body arrived."); |
no test coverage detected