Skip to content
This repository was archived by the owner on Jan 21, 2025. It is now read-only.

Commit 175e182

Browse files
committed
rework INM/IMS handling for AsyncStaticWebHandler
IMS template must contain GMT timezone, not local - "%a, %d %b %Y %H:%M:%S GMT" create etag based on timestamp + filesize INM header handling should have precedence over IMS
1 parent 33815af commit 175e182

File tree

2 files changed

+56
-36
lines changed

2 files changed

+56
-36
lines changed

src/WebHandlerImpl.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,19 @@ class AsyncStaticWebHandler : public AsyncWebHandler {
5757
AsyncStaticWebHandler& setIsDir(bool isDir);
5858
AsyncStaticWebHandler& setDefaultFile(const char* filename);
5959
AsyncStaticWebHandler& setCacheControl(const char* cache_control);
60+
61+
/**
62+
* @brief Set the Last-Modified time for the object
63+
*
64+
* @param last_modified
65+
* @return AsyncStaticWebHandler&
66+
*/
6067
AsyncStaticWebHandler& setLastModified(const char* last_modified);
6168
AsyncStaticWebHandler& setLastModified(struct tm* last_modified);
62-
#ifdef ESP8266
6369
AsyncStaticWebHandler& setLastModified(time_t last_modified);
64-
AsyncStaticWebHandler& setLastModified(); // sets to current time. Make sure sntp is runing and time is updated
65-
#endif
70+
// sets to current time. Make sure sntp is runing and time is updated
71+
AsyncStaticWebHandler& setLastModified();
72+
6673
AsyncStaticWebHandler& setTemplateProcessor(AwsTemplateProcessor newCallback);
6774
};
6875

src/WebHandlers.cpp

Lines changed: 46 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,12 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setIsDir(bool isDir) {
7070
}
7171

7272
AsyncStaticWebHandler& AsyncStaticWebHandler::setDefaultFile(const char* filename) {
73-
_default_file = String(filename);
73+
_default_file = filename;
7474
return *this;
7575
}
7676

7777
AsyncStaticWebHandler& AsyncStaticWebHandler::setCacheControl(const char* cache_control) {
78-
_cache_control = String(cache_control);
78+
_cache_control = cache_control;
7979
return *this;
8080
}
8181

@@ -85,16 +85,20 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(const char* last_m
8585
}
8686

8787
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(struct tm* last_modified) {
88-
auto formatP = PSTR("%a, %d %b %Y %H:%M:%S %Z");
88+
char result[30];
89+
#ifdef ESP8266
90+
auto formatP = PSTR("%a, %d %b %Y %H:%M:%S GMT");
8991
char format[strlen_P(formatP) + 1];
9092
strcpy_P(format, formatP);
93+
#else
94+
static constexpr const char* format = "%a, %d %b %Y %H:%M:%S GMT";
95+
#endif
9196

92-
char result[30];
9397
strftime(result, sizeof(result), format, last_modified);
94-
return setLastModified((const char*)result);
98+
_last_modified = result;
99+
return *this;
95100
}
96101

97-
#ifdef ESP8266
98102
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(time_t last_modified) {
99103
return setLastModified((struct tm*)gmtime(&last_modified));
100104
}
@@ -105,7 +109,7 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified() {
105109
return *this;
106110
return setLastModified(last_modified);
107111
}
108-
#endif
112+
109113
bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest* request) const {
110114
return request->isHTTP() && request->method() == HTTP_GET && request->url().startsWith(_uri) && _getFile(request);
111115
}
@@ -194,50 +198,59 @@ uint8_t AsyncStaticWebHandler::_countBits(const uint8_t value) const {
194198

195199
void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest* request) {
196200
// Get the filename from request->_tempObject and free it
197-
String filename = String((char*)request->_tempObject);
201+
String filename((char*)request->_tempObject);
198202
free(request->_tempObject);
199203
request->_tempObject = NULL;
200204

201-
if (request->_tempFile == true) {
205+
if (request->_tempFile != true){
206+
request->send(404);
207+
return;
208+
}
209+
202210
time_t lw = request->_tempFile.getLastWrite(); // get last file mod time (if supported by FS)
203211
// set etag to lastmod timestamp if available, otherwise to size
204212
String etag;
205213
if (lw) {
206-
setLastModified(gmtime(&lw));
214+
setLastModified(lw);
207215
#if defined(TARGET_RP2040)
208216
// time_t == long long int
209-
const size_t len = 1 + 8 * sizeof(time_t);
217+
constexpr size_t len = 1 + 8 * sizeof(time_t);
210218
char buf[len];
211-
char* ret = lltoa(lw, buf, len, 10);
219+
char* ret = lltoa(lw ^ request->_tempFile.size(), buf, len, 10);
212220
etag = ret ? String(ret) : String(request->_tempFile.size());
213221
#else
214-
etag = String(lw);
222+
etag = lw ^ request->_tempFile.size(); // etag combines file size and lastmod timestamp
215223
#endif
216224
} else {
217-
etag = String(request->_tempFile.size());
225+
etag = request->_tempFile.size();
218226
}
219-
if (_last_modified.length() && _last_modified == request->header(T_IMS)) {
220-
request->_tempFile.close();
221-
request->send(304); // Not modified
222-
} else if (_cache_control.length() && request->hasHeader(T_INM) && request->header(T_INM).equals(etag)) {
227+
228+
bool not_modified = false;
229+
230+
// if-none-match has precedence over if-modified-since
231+
if (request->hasHeader(T_INM))
232+
not_modified = request->header(T_INM).equals(etag);
233+
else if (_last_modified.length())
234+
not_modified = request->header(T_IMS).equals(_last_modified);
235+
236+
AsyncWebServerResponse* response;
237+
238+
if (not_modified){
223239
request->_tempFile.close();
224-
AsyncWebServerResponse* response = new AsyncBasicResponse(304); // Not modified
225-
response->addHeader(T_Cache_Control, _cache_control.c_str());
226-
response->addHeader(T_ETag, etag.c_str());
227-
request->send(response);
240+
response = new AsyncBasicResponse(304); // Not modified
228241
} else {
229-
AsyncWebServerResponse* response = new AsyncFileResponse(request->_tempFile, filename, String(), false, _callback);
230-
if (_last_modified.length())
231-
response->addHeader(T_Last_Modified, _last_modified.c_str());
232-
if (_cache_control.length()) {
233-
response->addHeader(T_Cache_Control, _cache_control.c_str());
234-
response->addHeader(T_ETag, etag.c_str());
235-
}
236-
request->send(response);
242+
response = new AsyncFileResponse(request->_tempFile, filename, emptyString, false, _callback);
237243
}
238-
} else {
239-
request->send(404);
240-
}
244+
245+
response->addHeader(T_ETag, etag.c_str());
246+
247+
if (_last_modified.length())
248+
response->addHeader(T_Last_Modified, _last_modified.c_str());
249+
if (_cache_control.length())
250+
response->addHeader(T_Cache_Control, _cache_control.c_str());
251+
252+
request->send(response);
253+
241254
}
242255

243256
AsyncStaticWebHandler& AsyncStaticWebHandler::setTemplateProcessor(AwsTemplateProcessor newCallback) {

0 commit comments

Comments
 (0)