diff --git a/examples/AsyncResponseStream/AsyncResponseStream.ino b/examples/AsyncResponseStream/AsyncResponseStream.ino new file mode 100644 index 00000000..62fa799b --- /dev/null +++ b/examples/AsyncResponseStream/AsyncResponseStream.ino @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov + +#include +#ifdef ESP32 +#include +#include +#elif defined(ESP8266) +#include +#include +#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350) +#include +#include +#endif + +#include + +static AsyncWebServer server(80); + +void setup() { + Serial.begin(115200); + +#ifndef CONFIG_IDF_TARGET_ESP32H2 + WiFi.mode(WIFI_AP); + WiFi.softAP("esp-captive"); +#endif + + // Shows how to use AsyncResponseStream. + // The internal buffer will be allocated and data appended to it, + // until the response is sent, then this buffer is read and committed on the network. + // + // curl -v http://192.168.4.1/ + // + server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { + AsyncResponseStream *response = request->beginResponseStream("plain/text", 40 * 1024); + for (int i = 0; i < 32 * 1024; i++) { + response->write('a'); + } + request->send(response); + }); + + server.begin(); +} + +void loop() { + delay(100); +} diff --git a/platformio.ini b/platformio.ini index b6352c20..a0129d45 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,6 +1,7 @@ [platformio] default_envs = arduino-2, arduino-3, esp8266, raspberrypi lib_dir = . +; src_dir = examples/AsyncResponseStream ; src_dir = examples/Auth ; src_dir = examples/CaptivePortal ; src_dir = examples/CatchAllHandler @@ -49,6 +50,7 @@ build_flags = -D CONFIG_ASYNC_TCP_QUEUE_SIZE=64 -D CONFIG_ASYNC_TCP_RUNNING_CORE=1 -D CONFIG_ASYNC_TCP_STACK_SIZE=4096 + ; -D CONFIG_ASYNC_TCP_USE_WDT=0 upload_protocol = esptool monitor_speed = 115200 monitor_filters = esp32_exception_decoder, log2file diff --git a/src/WebResponseImpl.h b/src/WebResponseImpl.h index 4a97492a..64086258 100644 --- a/src/WebResponseImpl.h +++ b/src/WebResponseImpl.h @@ -10,7 +10,7 @@ #undef max #endif #include "literals.h" -#include +#include #include #include @@ -157,7 +157,7 @@ class AsyncProgmemResponse : public AsyncAbstractResponse { class AsyncResponseStream : public AsyncAbstractResponse, public Print { private: - StreamString _content; + std::unique_ptr _content; public: AsyncResponseStream(const char *contentType, size_t bufferSize); @@ -172,7 +172,7 @@ class AsyncResponseStream : public AsyncAbstractResponse, public Print { * @brief Returns the number of bytes available in the stream. */ size_t available() const { - return _content.length(); // note: _content.available() is not const + return _content->available(); } using Print::write; }; diff --git a/src/WebResponses.cpp b/src/WebResponses.cpp index f5d46530..97981bd7 100644 --- a/src/WebResponses.cpp +++ b/src/WebResponses.cpp @@ -820,7 +820,9 @@ AsyncResponseStream::AsyncResponseStream(const char *contentType, size_t bufferS _code = 200; _contentLength = 0; _contentType = contentType; - if (!_content.reserve(bufferSize)) { + // internal buffer will be null on allocation failure + _content = std::unique_ptr(new cbuf(bufferSize)); + if (_content->size() != bufferSize) { #ifdef ESP32 log_e("Failed to allocate"); #endif @@ -828,14 +830,18 @@ AsyncResponseStream::AsyncResponseStream(const char *contentType, size_t bufferS } size_t AsyncResponseStream::_fillBuffer(uint8_t *buf, size_t maxLen) { - return _content.readBytes((char *)buf, maxLen); + return _content->read((char *)buf, maxLen); } size_t AsyncResponseStream::write(const uint8_t *data, size_t len) { if (_started()) { return 0; } - size_t written = _content.write(data, len); + if (len > _content->room()) { + size_t needed = len - _content->room(); + _content->resizeAdd(needed); + } + size_t written = _content->write((const char *)data, len); _contentLength += written; return written; }