mirror of
https://github.com/wled/WLED.git
synced 2025-07-21 17:56:33 +00:00
Web server string optimisation
(saves 140B of RAM on ESP8266) ETag bugfix
This commit is contained in:
parent
6dcd9596a2
commit
f6206d4c30
@ -24,6 +24,13 @@ static const char s_redirecting[] PROGMEM = "Redirecting...";
|
||||
static const char s_content_enc[] PROGMEM = "Content-Encoding";
|
||||
static const char s_unlock_ota [] PROGMEM = "Please unlock OTA in security settings!";
|
||||
static const char s_unlock_cfg [] PROGMEM = "Please unlock settings using PIN code!";
|
||||
static const char s_notimplemented[] PROGMEM = "Not implemented";
|
||||
static const char s_accessdenied[] PROGMEM = "Access Denied";
|
||||
static const char s_javascript[] PROGMEM = "application/javascript";
|
||||
static const char s_json[] PROGMEM = "application/json";
|
||||
static const char s_html[] PROGMEM = "text/html";
|
||||
static const char s_plain[] PROGMEM = "text/plain";
|
||||
static const char s_css[] PROGMEM = "text/css";
|
||||
|
||||
//Is this an IP?
|
||||
bool isIp(String str) {
|
||||
@ -38,7 +45,7 @@ bool isIp(String str) {
|
||||
|
||||
void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) {
|
||||
if (!correctPIN) {
|
||||
if (final) request->send(401, "text/plain", FPSTR(s_unlock_cfg));
|
||||
if (final) request->send(401, FPSTR(s_plain), FPSTR(s_unlock_cfg));
|
||||
return;
|
||||
}
|
||||
if (!index) {
|
||||
@ -50,7 +57,7 @@ void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t
|
||||
request->_tempFile = WLED_FS.open(finalname, "w");
|
||||
DEBUG_PRINT(F("Uploading "));
|
||||
DEBUG_PRINTLN(finalname);
|
||||
if (finalname.equals("/presets.json")) presetsModifiedTime = toki.second();
|
||||
if (finalname.equals(F("/presets.json"))) presetsModifiedTime = toki.second();
|
||||
}
|
||||
if (len) {
|
||||
request->_tempFile.write(data,len);
|
||||
@ -59,10 +66,10 @@ void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t
|
||||
request->_tempFile.close();
|
||||
if (filename.indexOf(F("cfg.json")) >= 0) { // check for filename with or without slash
|
||||
doReboot = true;
|
||||
request->send(200, "text/plain", F("Configuration restore successful.\nRebooting..."));
|
||||
request->send(200, FPSTR(s_plain), F("Configuration restore successful.\nRebooting..."));
|
||||
} else {
|
||||
if (filename.indexOf(F("palette")) >= 0 && filename.indexOf(F(".json")) >= 0) strip.loadCustomPalettes();
|
||||
request->send(200, "text/plain", F("File Uploaded!"));
|
||||
request->send(200, FPSTR(s_plain), F("File Uploaded!"));
|
||||
}
|
||||
cacheInvalidate++;
|
||||
}
|
||||
@ -79,25 +86,24 @@ void createEditHandler(bool enable) {
|
||||
#endif
|
||||
#else
|
||||
editHandler = &server.on("/edit", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
serveMessage(request, 501, "Not implemented", F("The FS editor is disabled in this build."), 254);
|
||||
serveMessage(request, 501, FPSTR(s_notimplemented), F("The FS editor is disabled in this build."), 254);
|
||||
});
|
||||
#endif
|
||||
} else {
|
||||
editHandler = &server.on("/edit", HTTP_ANY, [](AsyncWebServerRequest *request){
|
||||
serveMessage(request, 401, "Access Denied", FPSTR(s_unlock_cfg), 254);
|
||||
serveMessage(request, 401, FPSTR(s_accessdenied), FPSTR(s_unlock_cfg), 254);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool captivePortal(AsyncWebServerRequest *request)
|
||||
{
|
||||
if (ON_STA_FILTER(request)) return false; //only serve captive in AP mode
|
||||
String hostH;
|
||||
if (!apActive) return false; //only serve captive in AP mode
|
||||
if (!request->hasHeader("Host")) return false;
|
||||
hostH = request->getHeader("Host")->value();
|
||||
|
||||
if (!isIp(hostH) && hostH.indexOf("wled.me") < 0 && hostH.indexOf(cmDNS) < 0) {
|
||||
DEBUG_PRINTLN("Captive portal");
|
||||
String hostH = request->getHeader("Host")->value();
|
||||
if (!isIp(hostH) && hostH.indexOf(F("wled.me")) < 0 && hostH.indexOf(cmDNS) < 0 && hostH.indexOf(':') < 0) {
|
||||
DEBUG_PRINTLN(F("Captive portal"));
|
||||
AsyncWebServerResponse *response = request->beginResponse(302);
|
||||
response->addHeader(F("Location"), F("http://4.3.2.1"));
|
||||
request->send(response);
|
||||
@ -116,12 +122,12 @@ void initServer()
|
||||
#ifdef WLED_ENABLE_WEBSOCKETS
|
||||
#ifndef WLED_DISABLE_2D
|
||||
server.on("/liveview2D", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
handleStaticContent(request, "", 200, "text/html", PAGE_liveviewws2D, PAGE_liveviewws2D_length);
|
||||
handleStaticContent(request, "", 200, FPSTR(s_html), PAGE_liveviewws2D, PAGE_liveviewws2D_length);
|
||||
});
|
||||
#endif
|
||||
#endif
|
||||
server.on("/liveview", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
handleStaticContent(request, "", 200, "text/html", PAGE_liveview, PAGE_liveview_length);
|
||||
handleStaticContent(request, "", 200, FPSTR(s_html), PAGE_liveview, PAGE_liveview_length);
|
||||
});
|
||||
|
||||
//settings page
|
||||
@ -132,7 +138,7 @@ void initServer()
|
||||
// "/settings/settings.js&p=x" request also handled by serveSettings()
|
||||
|
||||
server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
handleStaticContent(request, "/style.css", 200, "text/css", PAGE_settingsCss, PAGE_settingsCss_length);
|
||||
handleStaticContent(request, "/style.css", 200, FPSTR(s_css), PAGE_settingsCss, PAGE_settingsCss_length);
|
||||
});
|
||||
|
||||
server.on("/favicon.ico", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
@ -141,7 +147,7 @@ void initServer()
|
||||
|
||||
server.on("/skin.css", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
if (handleFileRead(request, "/skin.css")) return;
|
||||
AsyncWebServerResponse *response = request->beginResponse(200, "text/css");
|
||||
AsyncWebServerResponse *response = request->beginResponse(200, FPSTR(s_css));
|
||||
request->send(response);
|
||||
});
|
||||
|
||||
@ -207,25 +213,25 @@ void initServer()
|
||||
doSerializeConfig = true; //serializeConfig(); //Save new settings to FS
|
||||
}
|
||||
}
|
||||
request->send(200, "application/json", F("{\"success\":true}"));
|
||||
request->send(200, FPSTR(s_json), F("{\"success\":true}"));
|
||||
}, JSON_BUFFER_SIZE);
|
||||
server.addHandler(handler);
|
||||
|
||||
server.on("/version", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
request->send(200, "text/plain", (String)VERSION);
|
||||
request->send(200, FPSTR(s_plain), (String)VERSION);
|
||||
});
|
||||
|
||||
server.on("/uptime", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
request->send(200, "text/plain", (String)millis());
|
||||
request->send(200, FPSTR(s_plain), (String)millis());
|
||||
});
|
||||
|
||||
server.on("/freeheap", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
request->send(200, "text/plain", (String)ESP.getFreeHeap());
|
||||
request->send(200, FPSTR(s_plain), (String)ESP.getFreeHeap());
|
||||
});
|
||||
|
||||
#ifdef WLED_ENABLE_USERMOD_PAGE
|
||||
server.on("/u", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
handleStaticContent(request, "", 200, "text/html", PAGE_usermod, PAGE_usermod_length);
|
||||
handleStaticContent(request, "", 200, FPSTR(s_html), PAGE_usermod, PAGE_usermod_length);
|
||||
});
|
||||
#endif
|
||||
|
||||
@ -244,7 +250,7 @@ void initServer()
|
||||
//init ota page
|
||||
server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
if (otaLock) {
|
||||
serveMessage(request, 401, "Access Denied", FPSTR(s_unlock_ota), 254);
|
||||
serveMessage(request, 401, FPSTR(s_accessdenied), FPSTR(s_unlock_ota), 254);
|
||||
} else
|
||||
serveSettings(request); // checks for "upd" in URL and handles PIN
|
||||
});
|
||||
@ -255,7 +261,7 @@ void initServer()
|
||||
return;
|
||||
}
|
||||
if (otaLock) {
|
||||
serveMessage(request, 401, "Access Denied", FPSTR(s_unlock_ota), 254);
|
||||
serveMessage(request, 401, FPSTR(s_accessdenied), FPSTR(s_unlock_ota), 254);
|
||||
return;
|
||||
}
|
||||
if (Update.hasError()) {
|
||||
@ -296,25 +302,25 @@ void initServer()
|
||||
});
|
||||
#else
|
||||
server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
serveMessage(request, 501, "Not implemented", F("OTA updating is disabled in this build."), 254);
|
||||
serveMessage(request, 501, FPSTR(s_notimplemented), F("OTA updating is disabled in this build."), 254);
|
||||
});
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
server.on("/dmxmap", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
request->send_P(200, "text/html", PAGE_dmxmap , dmxProcessor);
|
||||
request->send_P(200, FPSTR(s_html), PAGE_dmxmap , dmxProcessor);
|
||||
});
|
||||
#else
|
||||
#else
|
||||
server.on("/dmxmap", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
serveMessage(request, 501, "Not implemented", F("DMX support is not enabled in this build."), 254);
|
||||
serveMessage(request, 501, FPSTR(s_notimplemented), F("DMX support is not enabled in this build."), 254);
|
||||
});
|
||||
#endif
|
||||
#endif
|
||||
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
if (captivePortal(request)) return;
|
||||
if (!showWelcomePage || request->hasArg(F("sliders"))) {
|
||||
handleStaticContent(request, "/index.htm", 200, "text/html", PAGE_index, PAGE_index_L);
|
||||
handleStaticContent(request, "/index.htm", 200, FPSTR(s_html), PAGE_index, PAGE_index_L);
|
||||
} else {
|
||||
serveSettings(request);
|
||||
}
|
||||
@ -322,28 +328,27 @@ void initServer()
|
||||
|
||||
#ifdef WLED_ENABLE_PIXART
|
||||
server.on("/pixart.htm", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
handleStaticContent(request, "/pixart.htm", 200, "text/html", PAGE_pixart, PAGE_pixart_L);
|
||||
handleStaticContent(request, "/pixart.htm", 200, FPSTR(s_html), PAGE_pixart, PAGE_pixart_L);
|
||||
});
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef WLED_DISABLE_PXMAGIC
|
||||
server.on("/pxmagic.htm", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
handleStaticContent(request, "/pxmagic.htm", 200, "text/html", PAGE_pxmagic, PAGE_pxmagic_L);
|
||||
handleStaticContent(request, "/pxmagic.htm", 200, FPSTR(s_html), PAGE_pxmagic, PAGE_pxmagic_L);
|
||||
});
|
||||
#endif
|
||||
|
||||
server.on("/cpal.htm", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
handleStaticContent(request, "/cpal.htm", 200, "text/html", PAGE_cpal, PAGE_cpal_L);
|
||||
handleStaticContent(request, "/cpal.htm", 200, FPSTR(s_html), PAGE_cpal, PAGE_cpal_L);
|
||||
});
|
||||
|
||||
#ifdef WLED_ENABLE_WEBSOCKETS
|
||||
#ifdef WLED_ENABLE_WEBSOCKETS
|
||||
server.addHandler(&ws);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//called when the url is not defined here, ajax-in; get-settings
|
||||
server.onNotFound([](AsyncWebServerRequest *request){
|
||||
DEBUG_PRINTLN("Not-Found HTTP call:");
|
||||
DEBUG_PRINTLN("URI: " + request->url());
|
||||
DEBUG_PRINT(F("Not-Found HTTP call: ")); DEBUG_PRINTLN(request->url());
|
||||
if (captivePortal(request)) return;
|
||||
|
||||
//make API CORS compatible
|
||||
@ -359,7 +364,7 @@ void initServer()
|
||||
#ifndef WLED_DISABLE_ALEXA
|
||||
if(espalexa.handleAlexaApiCall(request)) return;
|
||||
#endif
|
||||
handleStaticContent(request, request->url(), 404, "text/html", PAGE_404, PAGE_404_length);
|
||||
handleStaticContent(request, request->url(), 404, FPSTR(s_html), PAGE_404, PAGE_404_length);
|
||||
});
|
||||
}
|
||||
|
||||
@ -371,8 +376,8 @@ bool handleIfNoneMatchCacheHeader(AsyncWebServerRequest *request, int code, uint
|
||||
// Only send 304 (Not Modified) if response code is 200 (OK)
|
||||
if (code != 200) return false;
|
||||
|
||||
AsyncWebHeader *header = request->getHeader("If-None-Match");
|
||||
char etag[14];
|
||||
AsyncWebHeader *header = request->getHeader(F("If-None-Match"));
|
||||
char etag[32];
|
||||
generateEtag(etag, eTagSuffix);
|
||||
if (header && header->value() == etag) {
|
||||
AsyncWebServerResponse *response = request->beginResponse(304);
|
||||
@ -391,11 +396,11 @@ void setStaticContentCacheHeaders(AsyncWebServerResponse *response, int code, ui
|
||||
#ifndef WLED_DEBUG
|
||||
// this header name is misleading, "no-cache" will not disable cache,
|
||||
// it just revalidates on every load using the "If-None-Match" header with the last ETag value
|
||||
response->addHeader(F("Cache-Control"), "no-cache");
|
||||
response->addHeader(F("Cache-Control"), F("no-cache"));
|
||||
#else
|
||||
response->addHeader(F("Cache-Control"), "no-store,max-age=0"); // prevent caching if debug build
|
||||
response->addHeader(F("Cache-Control"), F("no-store,max-age=0")); // prevent caching if debug build
|
||||
#endif
|
||||
char etag[14];
|
||||
char etag[32];
|
||||
generateEtag(etag, eTagSuffix);
|
||||
response->addHeader(F("ETag"), etag);
|
||||
}
|
||||
@ -419,7 +424,7 @@ void handleStaticContent(AsyncWebServerRequest *request, const String &path, int
|
||||
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");
|
||||
if (gzip) response->addHeader(FPSTR(s_content_enc), F("gzip"));
|
||||
setStaticContentCacheHeaders(response, code, eTagSuffix);
|
||||
request->send(response);
|
||||
}
|
||||
@ -466,15 +471,15 @@ void serveMessage(AsyncWebServerRequest* request, uint16_t code, const String& h
|
||||
messageSub = subl;
|
||||
optionType = optionT;
|
||||
|
||||
request->send_P(code, "text/html", PAGE_msg, msgProcessor);
|
||||
request->send_P(code, FPSTR(s_html), PAGE_msg, msgProcessor);
|
||||
}
|
||||
|
||||
|
||||
void serveJsonError(AsyncWebServerRequest* request, uint16_t code, uint16_t error)
|
||||
{
|
||||
AsyncJsonResponse *response = new AsyncJsonResponse(64);
|
||||
if (error < ERR_NOT_IMPL) response->addHeader("Retry-After", "1");
|
||||
response->setContentType("application/json");
|
||||
if (error < ERR_NOT_IMPL) response->addHeader(F("Retry-After"), F("1"));
|
||||
response->setContentType(FPSTR(s_json));
|
||||
response->setCode(code);
|
||||
JsonObject obj = response->getRoot();
|
||||
obj[F("error")] = error;
|
||||
@ -487,16 +492,16 @@ String dmxProcessor(const String& var)
|
||||
{
|
||||
String mapJS;
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
if (var == "DMXVARS") {
|
||||
mapJS += "\nCN=" + String(DMXChannels) + ";\n";
|
||||
mapJS += "CS=" + String(DMXStart) + ";\n";
|
||||
mapJS += "CG=" + String(DMXGap) + ";\n";
|
||||
mapJS += "LC=" + String(strip.getLengthTotal()) + ";\n";
|
||||
mapJS += "var CH=[";
|
||||
if (var == F("DMXVARS")) {
|
||||
mapJS += F("\nCN=") + String(DMXChannels) + ";\n";
|
||||
mapJS += F("CS=" )+ String(DMXStart) + ";\n";
|
||||
mapJS += F("CG=") + String(DMXGap) + ";\n";
|
||||
mapJS += F("LC=") + String(strip.getLengthTotal()) + ";\n";
|
||||
mapJS += F("var CH=[");
|
||||
for (int i=0;i<15;i++) {
|
||||
mapJS += String(DMXFixtureMap[i]) + ",";
|
||||
mapJS += String(DMXFixtureMap[i]) + ',';
|
||||
}
|
||||
mapJS += "0];";
|
||||
mapJS += F("0];");
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -512,12 +517,12 @@ void serveSettingsJS(AsyncWebServerRequest* request)
|
||||
byte subPage = request->arg(F("p")).toInt();
|
||||
if (subPage > 10) {
|
||||
strcpy_P(buf, PSTR("alert('Settings for this request are not implemented.');"));
|
||||
request->send(501, "application/javascript", buf);
|
||||
request->send(501, FPSTR(s_javascript), buf);
|
||||
return;
|
||||
}
|
||||
if (subPage > 0 && !correctPIN && strlen(settingsPIN)>0) {
|
||||
strcpy_P(buf, PSTR("alert('PIN incorrect.');"));
|
||||
request->send(401, "application/javascript", buf);
|
||||
request->send(401, FPSTR(s_javascript), buf);
|
||||
return;
|
||||
}
|
||||
strcat_P(buf,PSTR("function GetV(){var d=document;"));
|
||||
@ -525,9 +530,9 @@ void serveSettingsJS(AsyncWebServerRequest* request)
|
||||
strcat_P(buf,PSTR("}"));
|
||||
|
||||
AsyncWebServerResponse *response;
|
||||
response = request->beginResponse(200, "application/javascript", buf);
|
||||
response->addHeader(F("Cache-Control"),"no-store");
|
||||
response->addHeader(F("Expires"),"0");
|
||||
response = request->beginResponse(200, FPSTR(s_javascript), buf);
|
||||
response->addHeader(F("Cache-Control"), F("no-store"));
|
||||
response->addHeader(F("Expires"), F("0"));
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
@ -562,7 +567,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post) {
|
||||
// if OTA locked or too frequent PIN entry requests fail hard
|
||||
if ((subPage == SUBPAGE_WIFI && wifiLock && otaLock) || (post && !correctPIN && millis()-lastEditTime < PIN_RETRY_COOLDOWN))
|
||||
{
|
||||
serveMessage(request, 401, "Access Denied", FPSTR(s_unlock_ota), 254); return;
|
||||
serveMessage(request, 401, FPSTR(s_accessdenied), FPSTR(s_unlock_ota), 254); return;
|
||||
}
|
||||
|
||||
if (post) { //settings/set POST request, saving
|
||||
@ -598,7 +603,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post) {
|
||||
}
|
||||
|
||||
int code = 200;
|
||||
String contentType = "text/html";
|
||||
String contentType = FPSTR(s_html);
|
||||
const uint8_t* content;
|
||||
size_t len;
|
||||
|
||||
@ -623,8 +628,8 @@ void serveSettings(AsyncWebServerRequest* request, bool post) {
|
||||
serveMessage(request, 200, strlen(settingsPIN) > 0 ? PSTR("Settings locked") : PSTR("No PIN set"), FPSTR(s_redirecting), 1);
|
||||
return;
|
||||
}
|
||||
case SUBPAGE_PINREQ : content = PAGE_settings_pin; len = PAGE_settings_pin_length; code = 401; break;
|
||||
case SUBPAGE_CSS : content = PAGE_settingsCss; len = PAGE_settingsCss_length; contentType = "text/css"; break;
|
||||
case SUBPAGE_PINREQ : content = PAGE_settings_pin; len = PAGE_settings_pin_length; code = 401; break;
|
||||
case SUBPAGE_CSS : content = PAGE_settingsCss; len = PAGE_settingsCss_length; contentType = FPSTR(s_css); break;
|
||||
case SUBPAGE_JS : serveSettingsJS(request); return;
|
||||
case SUBPAGE_WELCOME : content = PAGE_welcome; len = PAGE_welcome_length; break;
|
||||
default: content = PAGE_settings; len = PAGE_settings_length; break;
|
||||
|
Loading…
x
Reference in New Issue
Block a user