Skip to content

Commit

Permalink
set real "Last-Modified" header based on file's LastWrite time
Browse files Browse the repository at this point in the history
Get file's LastWrite timestamp for file handlers (if supported by FS driver)
and construct proper "Last-Modified" header. Works fine for LittleFS.
If not supported by FS than fallback to previous implementation with manual value for "Last-Modified".

an example code to serve static files from LittleFS with IMS revalidation
```
// serve all static files from LittleFS root /
server.serveStatic("/", LittleFS, "/")
        .setDefaultFile("index.html")
        .setCacheControl("must-revalidate");  // revalidate based on etag/IMS headers
```

Signed-off-by: Emil Muratov <[email protected]>
  • Loading branch information
vortigont committed Jan 26, 2024
1 parent ed538f9 commit 3355186
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 12 deletions.
3 changes: 2 additions & 1 deletion src/WebHandlerImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

#include "stddef.h"
#include <time.h>
#include <ctime>

class AsyncStaticWebHandler: public AsyncWebHandler {
using File = fs::File;
Expand Down Expand Up @@ -55,7 +56,7 @@ class AsyncStaticWebHandler: public AsyncWebHandler {
AsyncStaticWebHandler& setDefaultFile(const char* filename);
AsyncStaticWebHandler& setCacheControl(const char* cache_control);
AsyncStaticWebHandler& setLastModified(const char* last_modified);
AsyncStaticWebHandler& setLastModified(struct tm* last_modified);
AsyncStaticWebHandler& setLastModified(const std::tm* last_modified);
#ifdef ESP8266
AsyncStaticWebHandler& setLastModified(time_t last_modified);
AsyncStaticWebHandler& setLastModified(); //sets to current time. Make sure sntp is runing and time is updated
Expand Down
21 changes: 10 additions & 11 deletions src/WebHandlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,15 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setCacheControl(const char* cache_
}

AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(const char* last_modified){
_last_modified = String(last_modified);
_last_modified = last_modified;
return *this;
}

AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(struct tm* last_modified){
auto formatP = PSTR("%a, %d %b %Y %H:%M:%S %Z");
char format[strlen_P(formatP) + 1];
strcpy_P(format, formatP);

char result[30];
strftime(result, sizeof(result), format, last_modified);
return setLastModified((const char *)result);
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(const std::tm* last_modified){
constexpr size_t buffsize = sizeof("Fri, 27 Jan 2023 15:50:27 GMT"); // a format for LM header
char result[buffsize];
std::strftime(result, buffsize, "%a, %d %b %Y %H:%M:%S GMT", last_modified);
return setLastModified(static_cast<const char *>(result));
}

#ifdef ESP8266
Expand Down Expand Up @@ -198,14 +195,16 @@ 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());
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(F("If-Modified-Since"))) {
request->_tempFile.close();
request->send(304); // Not modified
Expand Down

0 comments on commit 3355186

Please sign in to comment.