Skip to content

Reverted AsyncResponseStream buffer to cbuf which is considerably faster than StreamString #148

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions examples/AsyncResponseStream/AsyncResponseStream.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov

#include <DNSServer.h>
#ifdef ESP32
#include <AsyncTCP.h>
#include <WiFi.h>
#elif defined(ESP8266)
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
#include <RPAsyncTCP.h>
#include <WiFi.h>
#endif

#include <ESPAsyncWebServer.h>

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);
}
2 changes: 2 additions & 0 deletions platformio.ini
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions src/WebResponseImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#undef max
#endif
#include "literals.h"
#include <StreamString.h>
#include <cbuf.h>
#include <memory>
#include <vector>

Expand Down Expand Up @@ -157,7 +157,7 @@ class AsyncProgmemResponse : public AsyncAbstractResponse {

class AsyncResponseStream : public AsyncAbstractResponse, public Print {
private:
StreamString _content;
std::unique_ptr<cbuf> _content;

public:
AsyncResponseStream(const char *contentType, size_t bufferSize);
Expand All @@ -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;
};
Expand Down
12 changes: 9 additions & 3 deletions src/WebResponses.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -820,22 +820,28 @@ 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<cbuf>(new cbuf(bufferSize));
if (_content->size() != bufferSize) {
#ifdef ESP32
log_e("Failed to allocate");
#endif
}
}

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;
}
Expand Down