| 72 | } |
| 73 | |
| 74 | std::unique_ptr<char[]> receiveByteArray(httpd_req_t* request, size_t length, size_t& bytesRead) { |
| 75 | assert(length > 0); |
| 76 | bytesRead = 0; |
| 77 | |
| 78 | // We have to use malloc() because make_unique() throws an exception |
| 79 | // and we don't have exceptions enabled in the compiler settings |
| 80 | auto* buffer = static_cast<char*>(malloc(length)); |
| 81 | if (buffer == nullptr) { |
| 82 | LOGGER.error(LOG_MESSAGE_ALLOC_FAILED_FMT, length); |
| 83 | return nullptr; |
| 84 | } |
| 85 | |
| 86 | constexpr int MAX_TIMEOUT_RETRIES = 5; |
| 87 | int timeout_retries = 0; |
| 88 | while (bytesRead < length) { |
| 89 | size_t read_size = length - bytesRead; |
| 90 | int bytes_received = httpd_req_recv(request, buffer + bytesRead, read_size); |
| 91 | if (bytes_received == HTTPD_SOCK_ERR_TIMEOUT) { |
| 92 | // Timeout - retry with backoff |
| 93 | timeout_retries++; |
| 94 | if (timeout_retries >= MAX_TIMEOUT_RETRIES) { |
| 95 | LOGGER.warn("Recv timeout after {} retries, read {}/{} bytes", timeout_retries, bytesRead, length); |
| 96 | free(buffer); |
| 97 | return nullptr; |
| 98 | } |
| 99 | LOGGER.warn("Recv timeout, retry {}/{}", timeout_retries, MAX_TIMEOUT_RETRIES); |
| 100 | vTaskDelay(pdMS_TO_TICKS(100 * timeout_retries)); // Exponential backoff |
| 101 | continue; |
| 102 | } |
| 103 | if (bytes_received <= 0) { |
| 104 | LOGGER.warn("Received error {} after reading {}/{} bytes", bytes_received, bytesRead, length); |
| 105 | free(buffer); |
| 106 | return nullptr; |
| 107 | } |
| 108 | |
| 109 | // Successful read - reset timeout counter |
| 110 | timeout_retries = 0; |
| 111 | bytesRead += bytes_received; |
| 112 | } |
| 113 | |
| 114 | return std::unique_ptr<char[]>(buffer); |
| 115 | } |
| 116 | |
| 117 | std::string receiveTextUntil(httpd_req_t* request, const std::string& terminator) { |
| 118 | size_t read_index = 0; |
no test coverage detected