diff --git a/README.md b/README.md index ee7bb05..97abd49 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,10 @@ This fork is based on [yubox-node-org/ESPAsyncWebServer](https://github.com/yubo - [#5](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/5) ([@vortigont](https://github.com/vortigont)): set real "Last-Modified" header based on file's LastWrite time - [#13](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/13) ([@tueddy](https://github.com/tueddy)): Compile with Arduino 3 (ESP-IDF 5.1) - [#14](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/14) ([@nilo85](https://github.com/nilo85)): Add support for Auth & GET requests in AsyncCallbackJsonWebHandler -- Depends on `mathieucarbou/Async TCP @ ^3.1.2` +- Added `setAuthentication(const String& username, const String& password)` +- Added `StreamConcat` example to shoiw how to stream multiple files in one response +- Remove filename after inline in Content-Disposition header according to RFC2183 +- Depends on `mathieucarbou/Async TCP @ ^3.1.4` - Arduino 3 / ESP-IDF 5.1 compatibility ## Documentation diff --git a/docs/index.md b/docs/index.md index ee7bb05..97abd49 100644 --- a/docs/index.md +++ b/docs/index.md @@ -23,7 +23,10 @@ This fork is based on [yubox-node-org/ESPAsyncWebServer](https://github.com/yubo - [#5](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/5) ([@vortigont](https://github.com/vortigont)): set real "Last-Modified" header based on file's LastWrite time - [#13](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/13) ([@tueddy](https://github.com/tueddy)): Compile with Arduino 3 (ESP-IDF 5.1) - [#14](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/14) ([@nilo85](https://github.com/nilo85)): Add support for Auth & GET requests in AsyncCallbackJsonWebHandler -- Depends on `mathieucarbou/Async TCP @ ^3.1.2` +- Added `setAuthentication(const String& username, const String& password)` +- Added `StreamConcat` example to shoiw how to stream multiple files in one response +- Remove filename after inline in Content-Disposition header according to RFC2183 +- Depends on `mathieucarbou/Async TCP @ ^3.1.4` - Arduino 3 / ESP-IDF 5.1 compatibility ## Documentation diff --git a/examples/Filters/Filters.ino b/examples/Filters/Filters.ino new file mode 100644 index 0000000..218828a --- /dev/null +++ b/examples/Filters/Filters.ino @@ -0,0 +1,108 @@ +// Reproduced issue https://github.com/mathieucarbou/ESPAsyncWebServer/issues/26 + +#include +#ifdef ESP32 +#include +#include +#elif defined(ESP8266) +#include +#include +#endif +#include "ESPAsyncWebServer.h" + +DNSServer dnsServer; +AsyncWebServer server(80); + +class CaptiveRequestHandler : public AsyncWebHandler { + public: + CaptiveRequestHandler() {} + virtual ~CaptiveRequestHandler() {} + + bool canHandle(AsyncWebServerRequest* request) { + // request->addInterestingHeader("ANY"); + return true; + } + + void handleRequest(AsyncWebServerRequest* request) { + AsyncResponseStream* response = request->beginResponseStream("text/html"); + response->print("Captive Portal"); + response->print("

This is out captive portal front page.

"); + response->printf("

You were trying to reach: http://%s%s

", request->host().c_str(), request->url().c_str()); + response->printf("

Try opening this link instead

", WiFi.softAPIP().toString().c_str()); + response->print(""); + request->send(response); + } +}; + +bool hit1 = false; +bool hit2 = false; + +void setup() { + Serial.begin(115200); + + server + .on("/", HTTP_GET, [](AsyncWebServerRequest* request) { + Serial.println("Captive portal request..."); + Serial.println("WiFi.localIP(): " + WiFi.localIP().toString()); + Serial.println("request->client()->localIP(): " + request->client()->localIP().toString()); +#if ESP_IDF_VERSION_MAJOR >= 5 + Serial.println("WiFi.type(): " + String((int)WiFi.localIP().type())); + Serial.println("request->client()->type(): " + String((int)request->client()->localIP().type())); +#endif + Serial.println(WiFi.localIP() == request->client()->localIP() ? "should be: ON_STA_FILTER" : "should be: ON_AP_FILTER"); + Serial.println(WiFi.localIP() == request->client()->localIP()); + Serial.println(WiFi.localIP().toString() == request->client()->localIP().toString()); + request->send(200, "text/plain", "This is the captive portal"); + hit1 = true; + }) + .setFilter(ON_AP_FILTER); + + server + .on("/", HTTP_GET, [](AsyncWebServerRequest* request) { + Serial.println("Website request..."); + Serial.println("WiFi.localIP(): " + WiFi.localIP().toString()); + Serial.println("request->client()->localIP(): " + request->client()->localIP().toString()); +#if ESP_IDF_VERSION_MAJOR >= 5 + Serial.println("WiFi.type(): " + String((int)WiFi.localIP().type())); + Serial.println("request->client()->type(): " + String((int)request->client()->localIP().type())); +#endif + Serial.println(WiFi.localIP() == request->client()->localIP() ? "should be: ON_STA_FILTER" : "should be: ON_AP_FILTER"); + Serial.println(WiFi.localIP() == request->client()->localIP()); + Serial.println(WiFi.localIP().toString() == request->client()->localIP().toString()); + request->send(200, "text/plain", "This is the website"); + hit2 = true; + }) + .setFilter(ON_STA_FILTER); + + // assert(WiFi.softAP("esp-captive-portal")); + // dnsServer.start(53, "*", WiFi.softAPIP()); + // server.begin(); + // Serial.println("Captive portal started!"); + + // while (!hit1) { + // dnsServer.processNextRequest(); + // yield(); + // } + // delay(1000); // Wait for the client to process the response + + // Serial.println("Captive portal opened, stopping it and connecting to WiFi..."); + // dnsServer.stop(); + // WiFi.softAPdisconnect(); + + WiFi.persistent(false); + WiFi.begin("IoT"); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + } + Serial.println("Connected to WiFi with IP address: " + WiFi.localIP().toString()); + server.begin(); + + // while (!hit2) { + // delay(10); + // } + // delay(1000); // Wait for the client to process the response + // ESP.restart(); +} + +void loop() { +} diff --git a/examples/StreamFiles/StreamConcat.h b/examples/StreamFiles/StreamConcat.h new file mode 100644 index 0000000..ba95ebe --- /dev/null +++ b/examples/StreamFiles/StreamConcat.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +class StreamConcat : public Stream { + public: + StreamConcat(Stream* s1, Stream* s2) : _s1(s1), _s2(s2) {} + + size_t write(const uint8_t* p, size_t n) override { return 0; } + size_t write(uint8_t c) override { return 0; } + void flush() override {} + + int available() override { return _s1->available() + _s2->available(); } + + int read() override { + int c = _s1->read(); + return c != -1 ? c : _s2->read(); + } + + size_t readBytes(char* buffer, size_t length) override { + size_t count = _s1->readBytes(buffer, length); + return count > 0 ? count : _s2->readBytes(buffer, length); + } + + int peek() override { + int c = _s1->peek(); + return c != -1 ? c : _s2->peek(); + } + + private: + Stream* _s1; + Stream* _s2; +}; diff --git a/examples/StreamFiles/StreamFiles.ino b/examples/StreamFiles/StreamFiles.ino new file mode 100644 index 0000000..d5621f5 --- /dev/null +++ b/examples/StreamFiles/StreamFiles.ino @@ -0,0 +1,73 @@ +#include +#include +#ifdef ESP32 +#include +#include +#elif defined(ESP8266) +#include +#include +#endif +#include "StreamConcat.h" +#include "StreamString.h" +#include +#include + +DNSServer dnsServer; +AsyncWebServer server(80); + +void setup() { + Serial.begin(115200); + + LittleFS.begin(); + + WiFi.mode(WIFI_AP); + WiFi.softAP("esp-captive"); + dnsServer.start(53, "*", WiFi.softAPIP()); + + File file1 = LittleFS.open("/header.html", "w"); + file1.print("ESP Captive Portal"); + file1.close(); + + File file2 = LittleFS.open("/body.html", "w"); + file2.print("

Welcome to ESP Captive Portal

"); + file2.close(); + + File file3 = LittleFS.open("/footer.html", "w"); + file3.print(""); + file3.close(); + + server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) { + File header = LittleFS.open("/header.html", "r"); + File body = LittleFS.open("/body.html", "r"); + StreamConcat stream1(&header, &body); + + StreamString content; + content.printf("FreeHeap: %" PRIu32, ESP.getFreeHeap()); + StreamConcat stream2 = StreamConcat(&stream1, &content); + + File footer = LittleFS.open("/footer.html", "r"); + StreamConcat stream3 = StreamConcat(&stream2, &footer); + + request->send(stream3, "text/html", stream3.available()); + header.close(); + body.close(); + footer.close(); + }); + + server.onNotFound([](AsyncWebServerRequest* request) { + request->send(404, "text/plain", "Not found"); + }); + + server.begin(); +} + +uint32_t last = 0; + +void loop() { + // dnsServer.processNextRequest(); + + if (millis() - last > 2000) { + Serial.printf("FreeHeap: %" PRIu32, ESP.getFreeHeap()); + last = millis(); + } +} \ No newline at end of file diff --git a/examples/StreamFiles/StreamString.h b/examples/StreamFiles/StreamString.h new file mode 100644 index 0000000..f392b45 --- /dev/null +++ b/examples/StreamFiles/StreamString.h @@ -0,0 +1,36 @@ +#pragma once + +#include + +class StreamString : public Stream { + public: + size_t write(const uint8_t* p, size_t n) override { return _buffer.concat(reinterpret_cast(p), n) ? n : 0; } + size_t write(uint8_t c) override { return _buffer.concat(static_cast(c)) ? 1 : 0; } + void flush() override {} + + int available() override { return static_cast(_buffer.length()); } + + int read() override { + if (_buffer.length() == 0) + return -1; + char c = _buffer[0]; + _buffer.remove(0, 1); + return c; + } + + size_t readBytes(char* buffer, size_t length) override { + if (length > _buffer.length()) + length = _buffer.length(); + // Don't use _str.ToCharArray() because it inserts a terminator + memcpy(buffer, _buffer.c_str(), length); + _buffer.remove(0, static_cast(length)); + return length; + } + + int peek() override { return _buffer.length() > 0 ? _buffer[0] : -1; } + + const String& buffer() const { return _buffer; } + + private: + String _buffer; +}; diff --git a/library.json_ b/library.json_ index 9d18cf4..cc6cb9a 100644 --- a/library.json_ +++ b/library.json_ @@ -1,6 +1,6 @@ { "name": "ESP Async WebServer", - "version": "2.9.5", + "version": "2.10.1", "description": "Asynchronous HTTP and WebSocket Server Library for ESP32. Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc.", "keywords": "http,async,websocket,webserver", "homepage": "https://github.com/mathieucarbou/ESPAsyncWebServer", @@ -27,7 +27,7 @@ { "owner": "mathieucarbou", "name": "Async TCP", - "version": "^3.1.2", + "version": "^3.1.4", "platforms": "espressif32" }, { diff --git a/library.properties b/library.properties index 7d38349..e6ca366 100644 --- a/library.properties +++ b/library.properties @@ -1,10 +1,10 @@ name=ESP Async WebServer -version=2.9.5 +version=2.10.1 author=Me-No-Dev maintainer=Mathieu Carbou sentence=Asynchronous HTTP and WebSocket Server Library for ESP32 paragraph=Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc category=Other url=https://github.com/mathieucarbou/ESPAsyncWebServer -architectures=* +architectures=esp8266,esp32 license=LGPL-3.0 diff --git a/platformio.ini b/platformio.ini index 0bcd99e..daf31e4 100644 --- a/platformio.ini +++ b/platformio.ini @@ -3,39 +3,37 @@ framework = arduino build_flags = -Wall -Wextra -D CONFIG_ARDUHAL_LOG_COLORS - -D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG + -D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE lib_deps = bblanchon/ArduinoJson @ 7.0.4 - mathieucarbou/Async TCP @ ^3.1.2 + mathieucarbou/Async TCP @ ^3.1.4 + ; https://github.com/mathieucarbou/AsyncTCP + ; https://github.com/me-no-dev/AsyncTCP + esphome/ESPAsyncTCP-esphome @ 2.0.0 upload_protocol = esptool monitor_speed = 115200 monitor_filters = esp32_exception_decoder, log2file [platformio] lib_dir = . -src_dir = examples/CaptivePortal +; src_dir = examples/CaptivePortal ; src_dir = examples/SimpleServer +; src_dir = examples/StreamFiles +src_dir = examples/Filters -[env:v660] -platform = espressif32@6.6.0 +[env:arduino] +platform = espressif32 board = esp32dev -[env:latest-espressif32] -platform = https://github.com/platformio/platform-espressif32.git +[env:arduino-2] +platform = espressif32@6.7.0 board = esp32dev -[env:latest-arduino] +[env:arduino-3] platform = espressif32 platform_packages= - platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#master - platformio/framework-arduinoespressif32-libs @ https://github.com/espressif/esp32-arduino-libs.git#idf-release/v5.1 -board = esp32dev - -[env:v300-rc1] -platform = espressif32 -platform_packages= - platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.0-rc1 - platformio/framework-arduinoespressif32-libs @ https://github.com/espressif/esp32-arduino-libs.git#idf-release/v5.1 + platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.0 + platformio/framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.0/esp32-arduino-libs-3.0.0.zip board = esp32dev [env:esp8266] diff --git a/src/AsyncEventSource.cpp b/src/AsyncEventSource.cpp index 1c34c85..98f18de 100644 --- a/src/AsyncEventSource.cpp +++ b/src/AsyncEventSource.cpp @@ -193,7 +193,11 @@ void AsyncEventSourceClient::_queueMessage(AsyncEventSourceMessage *dataMessage) //length() is not thread-safe, thus acquiring the lock before this call.. _lockmq.lock(); if(_messageQueue.length() >= SSE_MAX_QUEUED_MESSAGES){ - ets_printf(String(F("ERROR: Too many messages queued\n")).c_str()); +#ifdef ESP8266 + ets_printf(String(F("ERROR: Too many messages queued\n")).c_str()); +#else + log_e("Too many messages queued: deleting message"); +#endif delete dataMessage; } else { _messageQueue.add(dataMessage); diff --git a/src/AsyncWebSocket.cpp b/src/AsyncWebSocket.cpp index 594ffb5..787b52a 100644 --- a/src/AsyncWebSocket.cpp +++ b/src/AsyncWebSocket.cpp @@ -461,7 +461,11 @@ void AsyncWebSocketClient::_queueMessage(std::shared_ptr> b if (_messageQueue.size() >= WS_MAX_QUEUED_MESSAGES) { l.unlock(); +#ifdef ESP8266 ets_printf("AsyncWebSocketClient::_queueMessage: Too many messages queued, closing connection\n"); +#else + log_e("Too many messages queued: closing connection"); +#endif _status = WS_DISCONNECTED; if (_client) _client->close(true); return; diff --git a/src/ESPAsyncWebServer.h b/src/ESPAsyncWebServer.h index ba3841a..7667e20 100644 --- a/src/ESPAsyncWebServer.h +++ b/src/ESPAsyncWebServer.h @@ -40,10 +40,10 @@ #error Platform not supported #endif -#define ASYNCWEBSERVER_VERSION "2.9.5" +#define ASYNCWEBSERVER_VERSION "2.10.1" #define ASYNCWEBSERVER_VERSION_MAJOR 2 -#define ASYNCWEBSERVER_VERSION_MINOR 9 -#define ASYNCWEBSERVER_VERSION_REVISION 5 +#define ASYNCWEBSERVER_VERSION_MINOR 10 +#define ASYNCWEBSERVER_VERSION_REVISION 1 #define ASYNCWEBSERVER_FORK_mathieucarbou #ifdef ASYNCWEBSERVER_REGEX @@ -363,6 +363,7 @@ class AsyncWebHandler { AsyncWebHandler():_username(""), _password(""){} AsyncWebHandler& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; } AsyncWebHandler& setAuthentication(const char *username, const char *password){ _username = String(username);_password = String(password); return *this; }; + AsyncWebHandler& setAuthentication(const String& username, const String& password){ _username = username;_password = password; return *this; }; bool filter(AsyncWebServerRequest *request){ return _filter == NULL || _filter(request); } virtual ~AsyncWebHandler(){} virtual bool canHandle(AsyncWebServerRequest *request __attribute__((unused))){