diff --git a/examples/user_data/user_data.ino b/examples/user_data/user_data.ino new file mode 100644 index 000000000..f86f6afb1 --- /dev/null +++ b/examples/user_data/user_data.ino @@ -0,0 +1,77 @@ +// +// A simple server implementation showing how to: +// * serve arbitrary user data +// + +#include +#ifdef ESP32 +#include +#include +#elif defined(ESP8266) +#include +#include +#endif +#include + +#include +#include + +AsyncWebServer server(80); + +const char* ssid = "YOUR_SSID"; +const char* password = "YOUR_PASSWORD"; + +const std::vector tokens { + "Hello", " ", "World", "!" +}; + +void setup() { + + Serial.begin(115200); + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + if (WiFi.waitForConnectResult() != WL_CONNECTED) { + Serial.printf("WiFi Failed!\n"); + return; + } + + Serial.print("IP Address: "); + Serial.println(WiFi.localIP()); + + server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ + AsyncWebServerResponse *response = request->beginUserDataResponse("text/plain", [&](uint8_t *buffer, size_t maxLen, std::any& userData) -> size_t { + // If user data is not set yet, we start with index 0 and try again + if (!userData.has_value()) { + userData = (size_t)0; + return RESPONSE_TRY_AGAIN; + } else if (std::any_cast(userData) >= tokens.size()) { + return 0; + } + // Write up to "maxLen" bytes into "buffer" and return the amount written. + // You will be asked for more data until 0 is returned + size_t i = std::any_cast(userData); + const size_t tokenSize = tokens.at(i).size(); + if (tokenSize > maxLen) { + std::cout << "token " << i << " with length: " << tokenSize << " exceeds max length: " << maxLen << std::endl; + return 0; + } + + size_t bytesWritten = 0; + while (i < tokens.size() && (bytesWritten + tokens.at(i).size()) <= maxLen) { + const auto& token = tokens.at(i); + std::memcpy(buffer + bytesWritten, token.c_str(), token.size()); + bytesWritten += token.size(); + ++i; + } + + userData = i; + return bytesWritten; + }); + request->send(response); + }); + + server.begin(); +} + +void loop() { +} diff --git a/src/ESPAsyncWebServer.h b/src/ESPAsyncWebServer.h index 933091562..55e019a26 100644 --- a/src/ESPAsyncWebServer.h +++ b/src/ESPAsyncWebServer.h @@ -23,6 +23,7 @@ #include "Arduino.h" +#include #include #include "FS.h" @@ -129,6 +130,7 @@ class AsyncWebHeader { typedef enum { RCT_NOT_USED = -1, RCT_DEFAULT = 0, RCT_HTTP, RCT_WS, RCT_EVENT, RCT_MAX } RequestedConnectionType; typedef std::function AwsResponseFiller; +typedef std::function AwsUserDataFiller; typedef std::function AwsTemplateProcessor; class AsyncWebServerRequest { @@ -249,6 +251,7 @@ class AsyncWebServerRequest { AsyncWebServerResponse *beginResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr); AsyncWebServerResponse *beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr); AsyncWebServerResponse *beginChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr); + AsyncWebServerResponse *beginUserDataResponse(const String& contentType, AwsUserDataFiller callback); AsyncResponseStream *beginResponseStream(const String& contentType, size_t bufferSize=1460); AsyncWebServerResponse *beginResponse_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr); AsyncWebServerResponse *beginResponse_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback=nullptr); diff --git a/src/WebRequest.cpp b/src/WebRequest.cpp index bbce5ca4c..d24097df9 100644 --- a/src/WebRequest.cpp +++ b/src/WebRequest.cpp @@ -756,6 +756,10 @@ AsyncWebServerResponse * AsyncWebServerRequest::beginChunkedResponse(const Strin return new AsyncCallbackResponse(contentType, 0, callback, templateCallback); } +AsyncWebServerResponse * AsyncWebServerRequest::beginUserDataResponse(const String& contentType, AwsUserDataFiller callback){ + return new AsyncUserDataResponse(contentType, callback); +} + AsyncResponseStream * AsyncWebServerRequest::beginResponseStream(const String& contentType, size_t bufferSize){ return new AsyncResponseStream(contentType, bufferSize); } diff --git a/src/WebResponseImpl.h b/src/WebResponseImpl.h index 9a64e3a52..fdcbd8305 100644 --- a/src/WebResponseImpl.h +++ b/src/WebResponseImpl.h @@ -26,6 +26,7 @@ #undef min #undef max #endif +#include #include // It is possible to restore these defines, but one can use _min and _max instead. Or std::min, std::max. @@ -108,6 +109,16 @@ class AsyncChunkedResponse: public AsyncAbstractResponse { virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; }; +class AsyncUserDataResponse: public AsyncAbstractResponse { + private: + AwsUserDataFiller _content; + std::any _userData; + public: + AsyncUserDataResponse(const String& contentType, AwsUserDataFiller callback); + bool _sourceValid() const { return !!(_content); } + virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; +}; + class AsyncProgmemResponse: public AsyncAbstractResponse { private: const uint8_t * _content; diff --git a/src/WebResponses.cpp b/src/WebResponses.cpp index a22e991aa..eeabd4909 100644 --- a/src/WebResponses.cpp +++ b/src/WebResponses.cpp @@ -637,6 +637,23 @@ size_t AsyncChunkedResponse::_fillBuffer(uint8_t *data, size_t len){ return ret; } +/* + * User Data Response + * */ + +AsyncUserDataResponse::AsyncUserDataResponse(const String& contentType, AwsUserDataFiller callback): AsyncAbstractResponse() { + _code = 200; + _content = callback; + _contentLength = 0; + _contentType = contentType; + _sendContentLength = false; + _chunked = true; +} + +size_t AsyncUserDataResponse::_fillBuffer(uint8_t *data, size_t len){ + return _content(data, len, _userData); +} + /* * Progmem Response * */