mirror of
https://github.com/wled/WLED.git
synced 2025-07-23 02:36:39 +00:00
Merge pull request #3639 from WoodyLetsCode/caching
Improve ETag Caching
This commit is contained in:
commit
6ad4493a91
@ -182,7 +182,7 @@ ${result}
|
|||||||
const result = hexdump(buf);
|
const result = hexdump(buf);
|
||||||
const chunk = `
|
const chunk = `
|
||||||
// Autogenerated from ${srcDir}/${s.file}, do not edit!!
|
// Autogenerated from ${srcDir}/${s.file}, do not edit!!
|
||||||
const uint16_t ${s.name}_length = ${result.length};
|
const uint16_t ${s.name}_length = ${buf.length};
|
||||||
const uint8_t ${s.name}[] PROGMEM = {
|
const uint8_t ${s.name}[] PROGMEM = {
|
||||||
${result}
|
${result}
|
||||||
};
|
};
|
||||||
|
@ -15,8 +15,9 @@
|
|||||||
* Integrated HTTP web server page declarations
|
* Integrated HTTP web server page declarations
|
||||||
*/
|
*/
|
||||||
|
|
||||||
bool handleIfNoneMatchCacheHeader(AsyncWebServerRequest* request);
|
bool handleIfNoneMatchCacheHeader(AsyncWebServerRequest* request, int code, uint16_t eTagSuffix = 0);
|
||||||
void setStaticContentCacheHeaders(AsyncWebServerResponse *response);
|
void setStaticContentCacheHeaders(AsyncWebServerResponse *response, int code, uint16_t eTagSuffix = 0);
|
||||||
|
void handleStaticContent(AsyncWebServerRequest *request, const String &path, int code, const String &contentType, const uint8_t *content, size_t len, bool gzip = true, uint16_t eTagSuffix = 0);
|
||||||
|
|
||||||
// define flash strings once (saves flash memory)
|
// define flash strings once (saves flash memory)
|
||||||
static const char s_redirecting[] PROGMEM = "Redirecting...";
|
static const char s_redirecting[] PROGMEM = "Redirecting...";
|
||||||
@ -113,22 +114,14 @@ void initServer()
|
|||||||
DefaultHeaders::Instance().addHeader(F("Access-Control-Allow-Headers"), "*");
|
DefaultHeaders::Instance().addHeader(F("Access-Control-Allow-Headers"), "*");
|
||||||
|
|
||||||
#ifdef WLED_ENABLE_WEBSOCKETS
|
#ifdef WLED_ENABLE_WEBSOCKETS
|
||||||
#ifndef WLED_DISABLE_2D
|
#ifndef WLED_DISABLE_2D
|
||||||
server.on("/liveview2D", HTTP_GET, [](AsyncWebServerRequest *request){
|
server.on("/liveview2D", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||||
if (handleIfNoneMatchCacheHeader(request)) return;
|
handleStaticContent(request, "", 200, "text/html", PAGE_liveviewws2D, PAGE_liveviewws2D_length);
|
||||||
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", PAGE_liveviewws2D, PAGE_liveviewws2D_length);
|
|
||||||
response->addHeader(FPSTR(s_content_enc),"gzip");
|
|
||||||
setStaticContentCacheHeaders(response);
|
|
||||||
request->send(response);
|
|
||||||
});
|
});
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
server.on("/liveview", HTTP_GET, [](AsyncWebServerRequest *request){
|
server.on("/liveview", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||||
if (handleIfNoneMatchCacheHeader(request)) return;
|
handleStaticContent(request, "", 200, "text/html", PAGE_liveview, PAGE_liveview_length);
|
||||||
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", PAGE_liveview, PAGE_liveview_length);
|
|
||||||
response->addHeader(FPSTR(s_content_enc),"gzip");
|
|
||||||
setStaticContentCacheHeaders(response);
|
|
||||||
request->send(response);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//settings page
|
//settings page
|
||||||
@ -138,17 +131,12 @@ void initServer()
|
|||||||
|
|
||||||
// "/settings/settings.js&p=x" request also handled by serveSettings()
|
// "/settings/settings.js&p=x" request also handled by serveSettings()
|
||||||
|
|
||||||
server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
|
server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||||
if (handleIfNoneMatchCacheHeader(request)) return;
|
handleStaticContent(request, "/style.css", 200, "text/css", PAGE_settingsCss, PAGE_settingsCss_length);
|
||||||
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/css", PAGE_settingsCss, PAGE_settingsCss_length);
|
|
||||||
response->addHeader(FPSTR(s_content_enc),"gzip");
|
|
||||||
setStaticContentCacheHeaders(response);
|
|
||||||
request->send(response);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
server.on("/favicon.ico", HTTP_GET, [](AsyncWebServerRequest *request) {
|
server.on("/favicon.ico", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||||
if (handleFileRead(request, "/favicon.ico")) return;
|
handleStaticContent(request, "/favicon.ico", 200, "image/x-icon", favicon, favicon_length, false);
|
||||||
request->send_P(200, "image/x-icon", favicon, 156);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
server.on("/skin.css", HTTP_GET, [](AsyncWebServerRequest *request) {
|
server.on("/skin.css", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||||
@ -236,12 +224,8 @@ void initServer()
|
|||||||
});
|
});
|
||||||
|
|
||||||
#ifdef WLED_ENABLE_USERMOD_PAGE
|
#ifdef WLED_ENABLE_USERMOD_PAGE
|
||||||
server.on("/u", HTTP_GET, [](AsyncWebServerRequest *request){
|
server.on("/u", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||||
if (handleIfNoneMatchCacheHeader(request)) return;
|
handleStaticContent(request, "", 200, "text/html", PAGE_usermod, PAGE_usermod_length);
|
||||||
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", PAGE_usermod, PAGE_usermod_length);
|
|
||||||
response->addHeader(FPSTR(s_content_enc),"gzip");
|
|
||||||
setStaticContentCacheHeaders(response);
|
|
||||||
request->send(response);
|
|
||||||
});
|
});
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -325,44 +309,29 @@ void initServer()
|
|||||||
});
|
});
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
|
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||||
if (captivePortal(request)) return;
|
if (captivePortal(request)) return;
|
||||||
if (!showWelcomePage || request->hasArg(F("sliders"))){
|
if (!showWelcomePage || request->hasArg(F("sliders"))) {
|
||||||
serveIndex(request);
|
handleStaticContent(request, "/index.htm", 200, "text/html", PAGE_index, PAGE_index_L);
|
||||||
} else {
|
} else {
|
||||||
serveSettings(request);
|
serveSettings(request);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
#ifdef WLED_ENABLE_PIXART
|
#ifdef WLED_ENABLE_PIXART
|
||||||
server.on("/pixart.htm", HTTP_GET, [](AsyncWebServerRequest *request){
|
server.on("/pixart.htm", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||||
if (handleFileRead(request, "/pixart.htm")) return;
|
handleStaticContent(request, "/pixart.htm", 200, "text/html", PAGE_pixart, PAGE_pixart_L);
|
||||||
if (handleIfNoneMatchCacheHeader(request)) return;
|
|
||||||
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", PAGE_pixart, PAGE_pixart_L);
|
|
||||||
response->addHeader(FPSTR(s_content_enc),"gzip");
|
|
||||||
setStaticContentCacheHeaders(response);
|
|
||||||
request->send(response);
|
|
||||||
});
|
});
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef WLED_DISABLE_PXMAGIC
|
#ifndef WLED_DISABLE_PXMAGIC
|
||||||
server.on("/pxmagic.htm", HTTP_GET, [](AsyncWebServerRequest *request){
|
server.on("/pxmagic.htm", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||||
if (handleFileRead(request, "/pxmagic.htm")) return;
|
handleStaticContent(request, "/pxmagic.htm", 200, "text/html", PAGE_pxmagic, PAGE_pxmagic_L);
|
||||||
if (handleIfNoneMatchCacheHeader(request)) return;
|
|
||||||
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", PAGE_pxmagic, PAGE_pxmagic_L);
|
|
||||||
response->addHeader(FPSTR(s_content_enc),"gzip");
|
|
||||||
setStaticContentCacheHeaders(response);
|
|
||||||
request->send(response);
|
|
||||||
});
|
});
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
server.on("/cpal.htm", HTTP_GET, [](AsyncWebServerRequest *request){
|
server.on("/cpal.htm", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||||
if (handleFileRead(request, "/cpal.htm")) return;
|
handleStaticContent(request, "/cpal.htm", 200, "text/html", PAGE_cpal, PAGE_cpal_L);
|
||||||
if (handleIfNoneMatchCacheHeader(request)) return;
|
|
||||||
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", PAGE_cpal, PAGE_cpal_L);
|
|
||||||
response->addHeader(FPSTR(s_content_enc),"gzip");
|
|
||||||
setStaticContentCacheHeaders(response);
|
|
||||||
request->send(response);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
#ifdef WLED_ENABLE_WEBSOCKETS
|
#ifdef WLED_ENABLE_WEBSOCKETS
|
||||||
@ -388,26 +357,34 @@ void initServer()
|
|||||||
#ifndef WLED_DISABLE_ALEXA
|
#ifndef WLED_DISABLE_ALEXA
|
||||||
if(espalexa.handleAlexaApiCall(request)) return;
|
if(espalexa.handleAlexaApiCall(request)) return;
|
||||||
#endif
|
#endif
|
||||||
if(handleFileRead(request, request->url())) return;
|
handleStaticContent(request, request->url(), 404, "text/html", PAGE_404, PAGE_404_length);
|
||||||
AsyncWebServerResponse *response = request->beginResponse_P(404, "text/html", PAGE_404, PAGE_404_length);
|
|
||||||
response->addHeader(FPSTR(s_content_enc),"gzip");
|
|
||||||
setStaticContentCacheHeaders(response);
|
|
||||||
request->send(response);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool handleIfNoneMatchCacheHeader(AsyncWebServerRequest *request) {
|
void generateEtag(char *etag, uint16_t eTagSuffix) {
|
||||||
|
sprintf_P(etag, PSTR("%7d-%02x-%04x"), VERSION, cacheInvalidate, eTagSuffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool handleIfNoneMatchCacheHeader(AsyncWebServerRequest *request, int code, uint16_t eTagSuffix) {
|
||||||
|
// Only send 304 (Not Modified) if response code is 200 (OK)
|
||||||
|
if (code != 200) return false;
|
||||||
|
|
||||||
AsyncWebHeader *header = request->getHeader("If-None-Match");
|
AsyncWebHeader *header = request->getHeader("If-None-Match");
|
||||||
char etag[11];
|
char etag[14];
|
||||||
sprintf_P(etag, PSTR("%7d-%02x"), VERSION, cacheInvalidate);
|
generateEtag(etag, eTagSuffix);
|
||||||
if (header && header->value() == etag) {
|
if (header && header->value() == etag) {
|
||||||
request->send(304);
|
AsyncWebServerResponse *response = request->beginResponse(304);
|
||||||
|
setStaticContentCacheHeaders(response, code, eTagSuffix);
|
||||||
|
request->send(response);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setStaticContentCacheHeaders(AsyncWebServerResponse *response) {
|
void setStaticContentCacheHeaders(AsyncWebServerResponse *response, int code, uint16_t eTagSuffix) {
|
||||||
|
// Only send ETag for 200 (OK) responses
|
||||||
|
if (code != 200) return;
|
||||||
|
|
||||||
// https://medium.com/@codebyamir/a-web-developers-guide-to-browser-caching-cc41f3b73e7c
|
// https://medium.com/@codebyamir/a-web-developers-guide-to-browser-caching-cc41f3b73e7c
|
||||||
#ifndef WLED_DEBUG
|
#ifndef WLED_DEBUG
|
||||||
// this header name is misleading, "no-cache" will not disable cache,
|
// this header name is misleading, "no-cache" will not disable cache,
|
||||||
@ -416,25 +393,37 @@ void setStaticContentCacheHeaders(AsyncWebServerResponse *response) {
|
|||||||
#else
|
#else
|
||||||
response->addHeader(F("Cache-Control"), "no-store,max-age=0"); // prevent caching if debug build
|
response->addHeader(F("Cache-Control"), "no-store,max-age=0"); // prevent caching if debug build
|
||||||
#endif
|
#endif
|
||||||
char etag[11];
|
char etag[14];
|
||||||
sprintf_P(etag, PSTR("%7d-%02x"), VERSION, cacheInvalidate);
|
generateEtag(etag, eTagSuffix);
|
||||||
response->addHeader(F("ETag"), etag);
|
response->addHeader(F("ETag"), etag);
|
||||||
}
|
}
|
||||||
|
|
||||||
void serveIndex(AsyncWebServerRequest* request)
|
/**
|
||||||
{
|
* Handels the request for a static file.
|
||||||
if (handleFileRead(request, "/index.htm")) return;
|
* If the file was found in the filesystem, it will be sent to the client.
|
||||||
|
* Otherwise it will be checked if the browser cached the file and if so, a 304 response will be sent.
|
||||||
if (handleIfNoneMatchCacheHeader(request)) return;
|
* If the file was not found in the filesystem and not in the browser cache, the request will be handled as a 200 response with the content of the page.
|
||||||
|
*
|
||||||
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", PAGE_index, PAGE_index_L);
|
* @param request The request object
|
||||||
|
* @param path If a file with this path exists in the filesystem, it will be sent to the client. Set to "" to skip this check.
|
||||||
response->addHeader(FPSTR(s_content_enc),"gzip");
|
* @param code The HTTP status code
|
||||||
setStaticContentCacheHeaders(response);
|
* @param contentType The content type of the web page
|
||||||
|
* @param content Content of the web page
|
||||||
|
* @param len Length of the content
|
||||||
|
* @param gzip Optional. Defaults to true. If false, the gzip header will not be added.
|
||||||
|
* @param eTagSuffix Optional. Defaults to 0. A suffix that will be added to the ETag header. This can be used to invalidate the cache for a specific page.
|
||||||
|
*/
|
||||||
|
void handleStaticContent(AsyncWebServerRequest *request, const String &path, int code, const String &contentType, const uint8_t *content, size_t len, bool gzip, uint16_t eTagSuffix) {
|
||||||
|
if (path != "" && handleFileRead(request, path)) return;
|
||||||
|
if (handleIfNoneMatchCacheHeader(request, code, eTagSuffix)) return;
|
||||||
|
AsyncWebServerResponse *response = request->beginResponse_P(code, contentType, content, len);
|
||||||
|
if (gzip) response->addHeader(FPSTR(s_content_enc), "gzip");
|
||||||
|
setStaticContentCacheHeaders(response, code, eTagSuffix);
|
||||||
request->send(response);
|
request->send(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
String msgProcessor(const String& var)
|
String msgProcessor(const String& var)
|
||||||
{
|
{
|
||||||
if (var == "MSG") {
|
if (var == "MSG") {
|
||||||
@ -536,13 +525,11 @@ void serveSettingsJS(AsyncWebServerRequest* request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void serveSettings(AsyncWebServerRequest* request, bool post)
|
void serveSettings(AsyncWebServerRequest* request, bool post) {
|
||||||
{
|
|
||||||
byte subPage = 0, originalSubPage = 0;
|
byte subPage = 0, originalSubPage = 0;
|
||||||
const String& url = request->url();
|
const String& url = request->url();
|
||||||
|
|
||||||
if (url.indexOf("sett") >= 0)
|
if (url.indexOf("sett") >= 0) {
|
||||||
{
|
|
||||||
if (url.indexOf(".js") > 0) subPage = SUBPAGE_JS;
|
if (url.indexOf(".js") > 0) subPage = SUBPAGE_JS;
|
||||||
else if (url.indexOf(".css") > 0) subPage = SUBPAGE_CSS;
|
else if (url.indexOf(".css") > 0) subPage = SUBPAGE_CSS;
|
||||||
else if (url.indexOf("wifi") > 0) subPage = SUBPAGE_WIFI;
|
else if (url.indexOf("wifi") > 0) subPage = SUBPAGE_WIFI;
|
||||||
@ -603,22 +590,25 @@ void serveSettings(AsyncWebServerRequest* request, bool post)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncWebServerResponse *response;
|
int code = 200;
|
||||||
switch (subPage)
|
String contentType = "text/html";
|
||||||
{
|
const uint8_t* content;
|
||||||
case SUBPAGE_WIFI : response = request->beginResponse_P(200, "text/html", PAGE_settings_wifi, PAGE_settings_wifi_length); break;
|
size_t len;
|
||||||
case SUBPAGE_LEDS : response = request->beginResponse_P(200, "text/html", PAGE_settings_leds, PAGE_settings_leds_length); break;
|
|
||||||
case SUBPAGE_UI : response = request->beginResponse_P(200, "text/html", PAGE_settings_ui, PAGE_settings_ui_length); break;
|
switch (subPage) {
|
||||||
case SUBPAGE_SYNC : response = request->beginResponse_P(200, "text/html", PAGE_settings_sync, PAGE_settings_sync_length); break;
|
case SUBPAGE_WIFI : content = PAGE_settings_wifi; len = PAGE_settings_wifi_length; break;
|
||||||
case SUBPAGE_TIME : response = request->beginResponse_P(200, "text/html", PAGE_settings_time, PAGE_settings_time_length); break;
|
case SUBPAGE_LEDS : content = PAGE_settings_leds; len = PAGE_settings_leds_length; break;
|
||||||
case SUBPAGE_SEC : response = request->beginResponse_P(200, "text/html", PAGE_settings_sec, PAGE_settings_sec_length); break;
|
case SUBPAGE_UI : content = PAGE_settings_ui; len = PAGE_settings_ui_length; break;
|
||||||
|
case SUBPAGE_SYNC : content = PAGE_settings_sync; len = PAGE_settings_sync_length; break;
|
||||||
|
case SUBPAGE_TIME : content = PAGE_settings_time; len = PAGE_settings_time_length; break;
|
||||||
|
case SUBPAGE_SEC : content = PAGE_settings_sec; len = PAGE_settings_sec_length; break;
|
||||||
#ifdef WLED_ENABLE_DMX
|
#ifdef WLED_ENABLE_DMX
|
||||||
case SUBPAGE_DMX : response = request->beginResponse_P(200, "text/html", PAGE_settings_dmx, PAGE_settings_dmx_length); break;
|
case SUBPAGE_DMX : content = PAGE_settings_dmx; len = PAGE_settings_dmx_length; break;
|
||||||
#endif
|
#endif
|
||||||
case SUBPAGE_UM : response = request->beginResponse_P(200, "text/html", PAGE_settings_um, PAGE_settings_um_length); break;
|
case SUBPAGE_UM : content = PAGE_settings_um; len = PAGE_settings_um_length; break;
|
||||||
case SUBPAGE_UPDATE : response = request->beginResponse_P(200, "text/html", PAGE_update, PAGE_update_length); break;
|
case SUBPAGE_UPDATE : content = PAGE_update; len = PAGE_update_length; break;
|
||||||
#ifndef WLED_DISABLE_2D
|
#ifndef WLED_DISABLE_2D
|
||||||
case SUBPAGE_2D : response = request->beginResponse_P(200, "text/html", PAGE_settings_2D, PAGE_settings_2D_length); break;
|
case SUBPAGE_2D : content = PAGE_settings_2D; len = PAGE_settings_2D_length; break;
|
||||||
#endif
|
#endif
|
||||||
case SUBPAGE_LOCK : {
|
case SUBPAGE_LOCK : {
|
||||||
correctPIN = !strlen(settingsPIN); // lock if a pin is set
|
correctPIN = !strlen(settingsPIN); // lock if a pin is set
|
||||||
@ -626,13 +616,11 @@ void serveSettings(AsyncWebServerRequest* request, bool post)
|
|||||||
serveMessage(request, 200, strlen(settingsPIN) > 0 ? PSTR("Settings locked") : PSTR("No PIN set"), FPSTR(s_redirecting), 1);
|
serveMessage(request, 200, strlen(settingsPIN) > 0 ? PSTR("Settings locked") : PSTR("No PIN set"), FPSTR(s_redirecting), 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case SUBPAGE_PINREQ : response = request->beginResponse_P(401, "text/html", PAGE_settings_pin, PAGE_settings_pin_length); break;
|
case SUBPAGE_PINREQ : content = PAGE_settings_pin; len = PAGE_settings_pin_length; code = 401; break;
|
||||||
case SUBPAGE_CSS : response = request->beginResponse_P(200, "text/css", PAGE_settingsCss, PAGE_settingsCss_length); break;
|
case SUBPAGE_CSS : content = PAGE_settingsCss; len = PAGE_settingsCss_length; contentType = "text/css"; break;
|
||||||
case SUBPAGE_JS : serveSettingsJS(request); return;
|
case SUBPAGE_JS : serveSettingsJS(request); return;
|
||||||
case SUBPAGE_WELCOME : response = request->beginResponse_P(200, "text/html", PAGE_welcome, PAGE_welcome_length); break;
|
case SUBPAGE_WELCOME : content = PAGE_welcome; len = PAGE_welcome_length; break;
|
||||||
default: response = request->beginResponse_P(200, "text/html", PAGE_settings, PAGE_settings_length); break;
|
default: content = PAGE_settings; len = PAGE_settings_length; break;
|
||||||
}
|
}
|
||||||
response->addHeader(FPSTR(s_content_enc),"gzip");
|
handleStaticContent(request, "", code, contentType, content, len);
|
||||||
setStaticContentCacheHeaders(response);
|
|
||||||
request->send(response);
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user