diff --git a/README.md b/README.md index d3d3cf91b..610f88f6c 100644 --- a/README.md +++ b/README.md @@ -876,8 +876,11 @@ handler->setCacheControl("max-age=30"); ``` ### Specifying Date-Modified header -It is possible to specify Date-Modified header to enable the server to return Not-Modified (304) response for requests -with "If-Modified-Since" header with the same value, instead of responding with the actual file content. +Sever sets "Last-Modified" header automatically if FS driver supports file modification timestamps (LittleFS does). +Server returns "Not-Modified" (304) response for requests with "If-Modified-Since" header with _the same_ value as file's mod date, instead of responding with the actual file content. It does not perform date calculations checking if File's mod date is newer or later than in "If-Modified-Since" header. + +For FS not supporting file timestamps (like deprecated SPIFFS) it is possible to specify Date-Modified header manually. + ```cpp // Update the date modified string every time files are updated server.serveStatic("/", SPIFFS, "/www/").setLastModified("Mon, 20 Jun 2016 14:00:00 GMT"); diff --git a/library.json b/library.json index 332c1c34b..9ef4d1d8b 100644 --- a/library.json +++ b/library.json @@ -18,20 +18,10 @@ "platforms": ["espressif8266", "espressif32"], "dependencies": [ { - "owner": "me-no-dev", - "name": "ESPAsyncTCP", - "version": "^1.2.2", - "platforms": "espressif8266" - }, - { - "owner": "me-no-dev", - "name": "AsyncTCP", - "version": "^1.1.1", + "owner": "esphome", + "name": "AsyncTCP-esphome", + "version": "^2.1.1", "platforms": "espressif32" - }, - { - "name": "Hash", - "platforms": "espressif8266" } ] } diff --git a/src/AsyncWebSocket.cpp b/src/AsyncWebSocket.cpp index 12be5f83e..bb165144e 100644 --- a/src/AsyncWebSocket.cpp +++ b/src/AsyncWebSocket.cpp @@ -181,7 +181,7 @@ class AsyncWebSocketControl { */ -AsyncWebSocketMessage::AsyncWebSocketMessage(std::shared_ptr> buffer, uint8_t opcode, bool mask) : +AsyncWebSocketMessage::AsyncWebSocketMessage(AsyncWebSocketMessageBuffer buffer, uint8_t opcode, bool mask) : _WSbuffer{buffer}, _opcode(opcode & 0x07), _mask{mask}, @@ -643,39 +643,12 @@ size_t AsyncWebSocketClient::printf_P(PGM_P formatP, ...) } #endif -namespace { -std::shared_ptr> makeBuffer(const uint8_t *message, size_t len) +AsyncWebSocketMessageBuffer AsyncWebSocketClient::makeBuffer(const uint8_t *message, size_t len) { auto buffer = std::make_shared>(len); std::memcpy(buffer->data(), message, len); return buffer; } -} - -void AsyncWebSocketClient::text(std::shared_ptr> buffer) -{ - _queueMessage(buffer); -} - -void AsyncWebSocketClient::text(const uint8_t *message, size_t len) -{ - text(makeBuffer(message, len)); -} - -void AsyncWebSocketClient::text(const char *message, size_t len) -{ - text((const uint8_t *)message, len); -} - -void AsyncWebSocketClient::text(const char *message) -{ - text(message, strlen(message)); -} - -void AsyncWebSocketClient::text(const String &message) -{ - text(message.c_str(), message.length()); -} void AsyncWebSocketClient::text(const __FlashStringHelper *data) { @@ -698,31 +671,6 @@ void AsyncWebSocketClient::text(const __FlashStringHelper *data) } } -void AsyncWebSocketClient::binary(std::shared_ptr> buffer) -{ - _queueMessage(buffer, WS_BINARY); -} - -void AsyncWebSocketClient::binary(const uint8_t *message, size_t len) -{ - binary(makeBuffer(message, len)); -} - -void AsyncWebSocketClient::binary(const char *message, size_t len) -{ - binary((const uint8_t *)message, len); -} - -void AsyncWebSocketClient::binary(const char *message) -{ - binary(message, strlen(message)); -} - -void AsyncWebSocketClient::binary(const String &message) -{ - binary(message.c_str(), message.length()); -} - void AsyncWebSocketClient::binary(const __FlashStringHelper *data, size_t len) { PGM_P p = reinterpret_cast(data); @@ -737,7 +685,7 @@ void AsyncWebSocketClient::binary(const __FlashStringHelper *data, size_t len) IPAddress AsyncWebSocketClient::remoteIP() const { if (!_client) - return IPAddress(0U); + return IPAddress((uint32_t)0); return _client->remoteIP(); } @@ -850,23 +798,15 @@ void AsyncWebSocket::pingAll(const uint8_t *data, size_t len) c.ping(data, len); } +void AsyncWebSocket::text(uint32_t id, AsyncWebSocketMessageBuffer message){ + if (AsyncWebSocketClient * c = client(id)) + c->message(message); +}; void AsyncWebSocket::text(uint32_t id, const uint8_t *message, size_t len) { if (AsyncWebSocketClient * c = client(id)) c->text(makeBuffer(message, len)); } -void AsyncWebSocket::text(uint32_t id, const char *message, size_t len) -{ - text(id, (const uint8_t *)message, len); -} -void AsyncWebSocket::text(uint32_t id, const char * message) -{ - text(id, message, strlen(message)); -} -void AsyncWebSocket::text(uint32_t id, const String &message) -{ - text(id, message.c_str(), message.length()); -} void AsyncWebSocket::text(uint32_t id, const __FlashStringHelper *data) { PGM_P p = reinterpret_cast(data); @@ -889,7 +829,7 @@ void AsyncWebSocket::text(uint32_t id, const __FlashStringHelper *data) } } -void AsyncWebSocket::textAll(std::shared_ptr> buffer) +void AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer buffer) { for (auto &c : _clients) if (c.status() == WS_CONNECTED) @@ -932,6 +872,10 @@ void AsyncWebSocket::textAll(const __FlashStringHelper *data) } } +void AsyncWebSocket::binary(uint32_t id, AsyncWebSocketMessageBuffer message){ + if (AsyncWebSocketClient * c = client(id)) + c->binary(message); +}; void AsyncWebSocket::binary(uint32_t id, const uint8_t *message, size_t len) { if (AsyncWebSocketClient *c = client(id)) @@ -961,7 +905,7 @@ void AsyncWebSocket::binary(uint32_t id, const __FlashStringHelper *data, size_t } } -void AsyncWebSocket::binaryAll(std::shared_ptr> buffer) +void AsyncWebSocket::binaryAll(AsyncWebSocketMessageBuffer buffer) { for (auto &c : _clients) if (c.status() == WS_CONNECTED) @@ -1022,7 +966,7 @@ size_t AsyncWebSocket::printfAll(const char *format, ...) va_end(arg); delete[] temp; - std::shared_ptr> buffer = std::make_shared>(len); + AsyncWebSocketMessageBuffer buffer = std::make_shared>(len); va_start(arg, format); vsnprintf( (char *)buffer->data(), len + 1, format, arg); @@ -1058,7 +1002,7 @@ size_t AsyncWebSocket::printfAll_P(PGM_P formatP, ...) va_end(arg); delete[] temp; - std::shared_ptr> buffer = std::make_shared>(len + 1); + AsyncWebSocketMessageBuffer buffer = std::make_shared>(len + 1); va_start(arg, formatP); vsnprintf_P((char *)buffer->data(), len + 1, formatP, arg); @@ -1141,6 +1085,13 @@ void AsyncWebSocket::handleRequest(AsyncWebServerRequest *request) request->send(response); } +AsyncWebSocketMessageBuffer AsyncWebSocket::makeBuffer(const uint8_t *message, size_t len) +{ + auto buffer = std::make_shared>(len); + std::memcpy(buffer->data(), message, len); + return buffer; +} + /* * Response to Web Socket request - sends the authorization and detaches the TCP Client from the web server * Authentication code from https://github.com/Links2004/arduinoWebSockets/blob/master/src/WebSockets.cpp#L480 @@ -1208,3 +1159,4 @@ size_t AsyncWebSocketResponse::_ack(AsyncWebServerRequest *request, size_t len, return 0; } + diff --git a/src/AsyncWebSocket.h b/src/AsyncWebSocket.h index 9a0a3b45b..199c2c843 100644 --- a/src/AsyncWebSocket.h +++ b/src/AsyncWebSocket.h @@ -50,6 +50,8 @@ #define DEFAULT_MAX_WS_CLIENTS 4 #endif +using AsyncWebSocketMessageBuffer = std::shared_ptr>; + class AsyncWebSocket; class AsyncWebSocketResponse; class AsyncWebSocketClient; @@ -84,6 +86,7 @@ typedef enum { WS_CONTINUATION, WS_TEXT, WS_BINARY, WS_DISCONNECT = 0x08, WS_PIN typedef enum { WS_MSG_SENDING, WS_MSG_SENT, WS_MSG_ERROR } AwsMessageStatus; typedef enum { WS_EVT_CONNECT, WS_EVT_DISCONNECT, WS_EVT_PONG, WS_EVT_ERROR, WS_EVT_DATA } AwsEventType; + class AsyncWebSocketMessage { private: @@ -161,7 +164,26 @@ class AsyncWebSocketClient { } //data packets - void message(std::shared_ptr> buffer, uint8_t opcode=WS_TEXT, bool mask=false) { _queueMessage(buffer, opcode, mask); } + /** + * @brief allocate memory buffer owned by shared-pointer and copy provided data + * used to keep the data untill websocket send is complete for single/multiple clients + * + * @param message + * @param len + * @return AsyncWebSocketMessageBuffer + */ + AsyncWebSocketMessageBuffer makeBuffer(const uint8_t *message, size_t len); + + /** + * @brief allocate empty memory buffer owned by shared-pointer + * used to keep the data untill websocket send is complete for single/multiple clients + * + * @param len + * @return AsyncWebSocketMessageBuffer + */ + inline AsyncWebSocketMessageBuffer makeBuffer(size_t len){ return std::make_shared>(len); }; + + void message(AsyncWebSocketMessageBuffer buffer, uint8_t opcode=WS_TEXT, bool mask=false) { _queueMessage(buffer, opcode, mask); } bool queueIsFull() const; size_t queueLen() const; @@ -170,18 +192,18 @@ class AsyncWebSocketClient { size_t printf_P(PGM_P formatP, ...) __attribute__ ((format (printf, 2, 3))); #endif - void text(std::shared_ptr> buffer); - void text(const uint8_t *message, size_t len); - void text(const char *message, size_t len); - void text(const char *message); - void text(const String &message); + inline void text(AsyncWebSocketMessageBuffer buffer){ _queueMessage(buffer); }; + inline void text(const uint8_t *message, size_t len){ text(makeBuffer(message, len)); }; + inline void text(const char *message, size_t len){ text((const uint8_t *)message, len); }; + inline void text(const char *message){ text(message, strlen(message)); }; + inline void text(const String &message){ text(message.c_str(), message.length()); }; void text(const __FlashStringHelper *message); - void binary(std::shared_ptr> buffer); - void binary(const uint8_t *message, size_t len); - void binary(const char * message, size_t len); - void binary(const char * message); - void binary(const String &message); + inline void binary(AsyncWebSocketMessageBuffer buffer){ _queueMessage(buffer, WS_BINARY); }; + inline void binary(const uint8_t *message, size_t len){ binary(makeBuffer(message, len)); }; + inline void binary(const char * message, size_t len){ binary((const uint8_t *)message, len); }; + inline void binary(const char * message){ binary(message, strlen(message)); }; + inline void binary(const String &message){ binary(message.c_str(), message.length()); }; void binary(const __FlashStringHelper *message, size_t len); bool canSend() const; @@ -205,7 +227,7 @@ class AsyncWebSocket: public AsyncWebHandler { std::list _clients; uint32_t _cNextId; AwsEventHandler _eventHandler; - AwsHandshakeHandler _handshakeHandler; + AwsHandshakeHandler _handshakeHandler; bool _enabled; AsyncWebLock _lock; @@ -229,26 +251,48 @@ class AsyncWebSocket: public AsyncWebHandler { void ping(uint32_t id, const uint8_t *data=NULL, size_t len=0); void pingAll(const uint8_t *data=NULL, size_t len=0); // done + //data packets + /** + * @brief allocate memory buffer owned by shared-pointer and copy provided data + * used to keep the data untill websocket send is complete for single/multiple clients + * + * @param message + * @param len + * @return AsyncWebSocketMessageBuffer + */ + AsyncWebSocketMessageBuffer makeBuffer(const uint8_t *message, size_t len); + + /** + * @brief allocate empty memory buffer owned by shared-pointer + * used to keep the data untill websocket send is complete for single/multiple clients + * + * @param len + * @return AsyncWebSocketMessageBuffer + */ + inline AsyncWebSocketMessageBuffer makeBuffer(size_t len){ return std::make_shared>(len); }; + + void text(uint32_t id, AsyncWebSocketMessageBuffer message); void text(uint32_t id, const uint8_t * message, size_t len); - void text(uint32_t id, const char *message, size_t len); - void text(uint32_t id, const char *message); - void text(uint32_t id, const String &message); + inline void text(uint32_t id, const char *message, size_t len){ text(id, (const uint8_t *)message, len); }; + inline void text(uint32_t id, const char *message){ text(id, message, strlen(message)); }; + inline void text(uint32_t id, const String &message){ text(id, message.c_str(), message.length()); }; void text(uint32_t id, const __FlashStringHelper *message); - void textAll(std::shared_ptr> buffer); + void textAll(AsyncWebSocketMessageBuffer buffer); void textAll(const uint8_t *message, size_t len); void textAll(const char * message, size_t len); void textAll(const char * message); void textAll(const String &message); void textAll(const __FlashStringHelper *message); // need to convert + void binary(uint32_t id, AsyncWebSocketMessageBuffer message); void binary(uint32_t id, const uint8_t *message, size_t len); void binary(uint32_t id, const char *message, size_t len); void binary(uint32_t id, const char *message); void binary(uint32_t id, const String &message); void binary(uint32_t id, const __FlashStringHelper *message, size_t len); - void binaryAll(std::shared_ptr> buffer); + void binaryAll(AsyncWebSocketMessageBuffer buffer); void binaryAll(const uint8_t *message, size_t len); void binaryAll(const char *message, size_t len); void binaryAll(const char *message); diff --git a/src/WebHandlers.cpp b/src/WebHandlers.cpp index 8e87ff1cf..464ab984c 100644 --- a/src/WebHandlers.cpp +++ b/src/WebHandlers.cpp @@ -20,6 +20,7 @@ */ #include "ESPAsyncWebServer.h" #include "WebHandlerImpl.h" +#include AsyncStaticWebHandler::AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control) : _fs(fs), _uri(uri), _path(path), _default_file(F("index.htm")), _cache_control(cache_control), _last_modified(), _callback(nullptr) @@ -199,15 +200,17 @@ uint8_t AsyncStaticWebHandler::_countBits(const uint8_t value) const void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest *request) { // Get the filename from request->_tempObject and free it - String filename = String((char*)request->_tempObject); + String filename((char*)request->_tempObject); free(request->_tempObject); request->_tempObject = NULL; if((_username.length() && _password.length()) && !request->authenticate(_username.c_str(), _password.c_str())) return request->requestAuthentication(); if (request->_tempFile == true) { - String etag = String(request->_tempFile.size()); - if (_last_modified.length() && _last_modified == request->header(F("If-Modified-Since"))) { + time_t lw = request->_tempFile.getLastWrite(); // get last file mod time (if supported by FS) + if (lw) setLastModified(std::gmtime(&lw)); + String etag(lw ? lw : request->_tempFile.size()); // set etag to lastmod timestamp if available, otherwise to size + if (_last_modified.length() && _last_modified == request->header("If-Modified-Since")) { request->_tempFile.close(); request->send(304); // Not modified } else if (_cache_control.length() && request->hasHeader(F("If-None-Match")) && request->header(F("If-None-Match")).equals(etag)) {