Web server string optimisation

(saves 140B of RAM on ESP8266)
ETag bugfix
This commit is contained in:
Blaz Kristan 2024-02-08 19:32:23 +01:00
parent 6dcd9596a2
commit f6206d4c30

View File

@ -24,6 +24,13 @@ static const char s_redirecting[] PROGMEM = "Redirecting...";
static const char s_content_enc[] PROGMEM = "Content-Encoding"; 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_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_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? //Is this an IP?
bool isIp(String str) { 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) { void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) {
if (!correctPIN) { 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; return;
} }
if (!index) { if (!index) {
@ -50,7 +57,7 @@ void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t
request->_tempFile = WLED_FS.open(finalname, "w"); request->_tempFile = WLED_FS.open(finalname, "w");
DEBUG_PRINT(F("Uploading ")); DEBUG_PRINT(F("Uploading "));
DEBUG_PRINTLN(finalname); DEBUG_PRINTLN(finalname);
if (finalname.equals("/presets.json")) presetsModifiedTime = toki.second(); if (finalname.equals(F("/presets.json"))) presetsModifiedTime = toki.second();
} }
if (len) { if (len) {
request->_tempFile.write(data,len); request->_tempFile.write(data,len);
@ -59,10 +66,10 @@ void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t
request->_tempFile.close(); request->_tempFile.close();
if (filename.indexOf(F("cfg.json")) >= 0) { // check for filename with or without slash if (filename.indexOf(F("cfg.json")) >= 0) { // check for filename with or without slash
doReboot = true; doReboot = true;
request->send(200, "text/plain", F("Configuration restore successful.\nRebooting...")); request->send(200, FPSTR(s_plain), F("Configuration restore successful.\nRebooting..."));
} else { } else {
if (filename.indexOf(F("palette")) >= 0 && filename.indexOf(F(".json")) >= 0) strip.loadCustomPalettes(); 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++; cacheInvalidate++;
} }
@ -79,25 +86,24 @@ void createEditHandler(bool enable) {
#endif #endif
#else #else
editHandler = &server.on("/edit", HTTP_GET, [](AsyncWebServerRequest *request){ 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 #endif
} else { } else {
editHandler = &server.on("/edit", HTTP_ANY, [](AsyncWebServerRequest *request){ 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) bool captivePortal(AsyncWebServerRequest *request)
{ {
if (ON_STA_FILTER(request)) return false; //only serve captive in AP mode if (!apActive) return false; //only serve captive in AP mode
String hostH;
if (!request->hasHeader("Host")) return false; if (!request->hasHeader("Host")) return false;
hostH = request->getHeader("Host")->value();
if (!isIp(hostH) && hostH.indexOf("wled.me") < 0 && hostH.indexOf(cmDNS) < 0) { String hostH = request->getHeader("Host")->value();
DEBUG_PRINTLN("Captive portal"); 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); AsyncWebServerResponse *response = request->beginResponse(302);
response->addHeader(F("Location"), F("http://4.3.2.1")); response->addHeader(F("Location"), F("http://4.3.2.1"));
request->send(response); request->send(response);
@ -116,12 +122,12 @@ void initServer()
#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) {
handleStaticContent(request, "", 200, "text/html", PAGE_liveviewws2D, PAGE_liveviewws2D_length); handleStaticContent(request, "", 200, FPSTR(s_html), PAGE_liveviewws2D, PAGE_liveviewws2D_length);
}); });
#endif #endif
#endif #endif
server.on("/liveview", HTTP_GET, [](AsyncWebServerRequest *request) { 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 //settings page
@ -132,7 +138,7 @@ 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) {
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) { server.on("/favicon.ico", HTTP_GET, [](AsyncWebServerRequest *request) {
@ -141,7 +147,7 @@ void initServer()
server.on("/skin.css", HTTP_GET, [](AsyncWebServerRequest *request) { server.on("/skin.css", HTTP_GET, [](AsyncWebServerRequest *request) {
if (handleFileRead(request, "/skin.css")) return; if (handleFileRead(request, "/skin.css")) return;
AsyncWebServerResponse *response = request->beginResponse(200, "text/css"); AsyncWebServerResponse *response = request->beginResponse(200, FPSTR(s_css));
request->send(response); request->send(response);
}); });
@ -207,25 +213,25 @@ void initServer()
doSerializeConfig = true; //serializeConfig(); //Save new settings to FS 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); }, JSON_BUFFER_SIZE);
server.addHandler(handler); server.addHandler(handler);
server.on("/version", HTTP_GET, [](AsyncWebServerRequest *request){ 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){ 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){ 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 #ifdef WLED_ENABLE_USERMOD_PAGE
server.on("/u", HTTP_GET, [](AsyncWebServerRequest *request) { 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 #endif
@ -244,7 +250,7 @@ void initServer()
//init ota page //init ota page
server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request){
if (otaLock) { if (otaLock) {
serveMessage(request, 401, "Access Denied", FPSTR(s_unlock_ota), 254); serveMessage(request, 401, FPSTR(s_accessdenied), FPSTR(s_unlock_ota), 254);
} else } else
serveSettings(request); // checks for "upd" in URL and handles PIN serveSettings(request); // checks for "upd" in URL and handles PIN
}); });
@ -255,7 +261,7 @@ void initServer()
return; return;
} }
if (otaLock) { if (otaLock) {
serveMessage(request, 401, "Access Denied", FPSTR(s_unlock_ota), 254); serveMessage(request, 401, FPSTR(s_accessdenied), FPSTR(s_unlock_ota), 254);
return; return;
} }
if (Update.hasError()) { if (Update.hasError()) {
@ -296,25 +302,25 @@ void initServer()
}); });
#else #else
server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request){ 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 #endif
#ifdef WLED_ENABLE_DMX #ifdef WLED_ENABLE_DMX
server.on("/dmxmap", HTTP_GET, [](AsyncWebServerRequest *request){ 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){ 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) { 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"))) {
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 { } else {
serveSettings(request); serveSettings(request);
} }
@ -322,28 +328,27 @@ void initServer()
#ifdef WLED_ENABLE_PIXART #ifdef WLED_ENABLE_PIXART
server.on("/pixart.htm", HTTP_GET, [](AsyncWebServerRequest *request) { 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 #ifndef WLED_DISABLE_PXMAGIC
server.on("/pxmagic.htm", HTTP_GET, [](AsyncWebServerRequest *request) { 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 #endif
server.on("/cpal.htm", HTTP_GET, [](AsyncWebServerRequest *request) { 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); server.addHandler(&ws);
#endif #endif
//called when the url is not defined here, ajax-in; get-settings //called when the url is not defined here, ajax-in; get-settings
server.onNotFound([](AsyncWebServerRequest *request){ server.onNotFound([](AsyncWebServerRequest *request){
DEBUG_PRINTLN("Not-Found HTTP call:"); DEBUG_PRINT(F("Not-Found HTTP call: ")); DEBUG_PRINTLN(request->url());
DEBUG_PRINTLN("URI: " + request->url());
if (captivePortal(request)) return; if (captivePortal(request)) return;
//make API CORS compatible //make API CORS compatible
@ -359,7 +364,7 @@ void initServer()
#ifndef WLED_DISABLE_ALEXA #ifndef WLED_DISABLE_ALEXA
if(espalexa.handleAlexaApiCall(request)) return; if(espalexa.handleAlexaApiCall(request)) return;
#endif #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) // Only send 304 (Not Modified) if response code is 200 (OK)
if (code != 200) return false; if (code != 200) return false;
AsyncWebHeader *header = request->getHeader("If-None-Match"); AsyncWebHeader *header = request->getHeader(F("If-None-Match"));
char etag[14]; char etag[32];
generateEtag(etag, eTagSuffix); generateEtag(etag, eTagSuffix);
if (header && header->value() == etag) { if (header && header->value() == etag) {
AsyncWebServerResponse *response = request->beginResponse(304); AsyncWebServerResponse *response = request->beginResponse(304);
@ -391,11 +396,11 @@ void setStaticContentCacheHeaders(AsyncWebServerResponse *response, int code, ui
#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,
// it just revalidates on every load using the "If-None-Match" header with the last ETag value // 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 #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 #endif
char etag[14]; char etag[32];
generateEtag(etag, eTagSuffix); generateEtag(etag, eTagSuffix);
response->addHeader(F("ETag"), etag); response->addHeader(F("ETag"), etag);
} }
@ -419,7 +424,7 @@ void handleStaticContent(AsyncWebServerRequest *request, const String &path, int
if (path != "" && handleFileRead(request, path)) return; if (path != "" && handleFileRead(request, path)) return;
if (handleIfNoneMatchCacheHeader(request, code, eTagSuffix)) return; if (handleIfNoneMatchCacheHeader(request, code, eTagSuffix)) return;
AsyncWebServerResponse *response = request->beginResponse_P(code, contentType, content, len); 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); setStaticContentCacheHeaders(response, code, eTagSuffix);
request->send(response); request->send(response);
} }
@ -466,15 +471,15 @@ void serveMessage(AsyncWebServerRequest* request, uint16_t code, const String& h
messageSub = subl; messageSub = subl;
optionType = optionT; 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) void serveJsonError(AsyncWebServerRequest* request, uint16_t code, uint16_t error)
{ {
AsyncJsonResponse *response = new AsyncJsonResponse(64); AsyncJsonResponse *response = new AsyncJsonResponse(64);
if (error < ERR_NOT_IMPL) response->addHeader("Retry-After", "1"); if (error < ERR_NOT_IMPL) response->addHeader(F("Retry-After"), F("1"));
response->setContentType("application/json"); response->setContentType(FPSTR(s_json));
response->setCode(code); response->setCode(code);
JsonObject obj = response->getRoot(); JsonObject obj = response->getRoot();
obj[F("error")] = error; obj[F("error")] = error;
@ -487,16 +492,16 @@ String dmxProcessor(const String& var)
{ {
String mapJS; String mapJS;
#ifdef WLED_ENABLE_DMX #ifdef WLED_ENABLE_DMX
if (var == "DMXVARS") { if (var == F("DMXVARS")) {
mapJS += "\nCN=" + String(DMXChannels) + ";\n"; mapJS += F("\nCN=") + String(DMXChannels) + ";\n";
mapJS += "CS=" + String(DMXStart) + ";\n"; mapJS += F("CS=" )+ String(DMXStart) + ";\n";
mapJS += "CG=" + String(DMXGap) + ";\n"; mapJS += F("CG=") + String(DMXGap) + ";\n";
mapJS += "LC=" + String(strip.getLengthTotal()) + ";\n"; mapJS += F("LC=") + String(strip.getLengthTotal()) + ";\n";
mapJS += "var CH=["; mapJS += F("var CH=[");
for (int i=0;i<15;i++) { for (int i=0;i<15;i++) {
mapJS += String(DMXFixtureMap[i]) + ","; mapJS += String(DMXFixtureMap[i]) + ',';
} }
mapJS += "0];"; mapJS += F("0];");
} }
#endif #endif
@ -512,12 +517,12 @@ void serveSettingsJS(AsyncWebServerRequest* request)
byte subPage = request->arg(F("p")).toInt(); byte subPage = request->arg(F("p")).toInt();
if (subPage > 10) { if (subPage > 10) {
strcpy_P(buf, PSTR("alert('Settings for this request are not implemented.');")); 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; return;
} }
if (subPage > 0 && !correctPIN && strlen(settingsPIN)>0) { if (subPage > 0 && !correctPIN && strlen(settingsPIN)>0) {
strcpy_P(buf, PSTR("alert('PIN incorrect.');")); strcpy_P(buf, PSTR("alert('PIN incorrect.');"));
request->send(401, "application/javascript", buf); request->send(401, FPSTR(s_javascript), buf);
return; return;
} }
strcat_P(buf,PSTR("function GetV(){var d=document;")); strcat_P(buf,PSTR("function GetV(){var d=document;"));
@ -525,9 +530,9 @@ void serveSettingsJS(AsyncWebServerRequest* request)
strcat_P(buf,PSTR("}")); strcat_P(buf,PSTR("}"));
AsyncWebServerResponse *response; AsyncWebServerResponse *response;
response = request->beginResponse(200, "application/javascript", buf); response = request->beginResponse(200, FPSTR(s_javascript), buf);
response->addHeader(F("Cache-Control"),"no-store"); response->addHeader(F("Cache-Control"), F("no-store"));
response->addHeader(F("Expires"),"0"); response->addHeader(F("Expires"), F("0"));
request->send(response); 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 OTA locked or too frequent PIN entry requests fail hard
if ((subPage == SUBPAGE_WIFI && wifiLock && otaLock) || (post && !correctPIN && millis()-lastEditTime < PIN_RETRY_COOLDOWN)) 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 if (post) { //settings/set POST request, saving
@ -598,7 +603,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post) {
} }
int code = 200; int code = 200;
String contentType = "text/html"; String contentType = FPSTR(s_html);
const uint8_t* content; const uint8_t* content;
size_t len; 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); serveMessage(request, 200, strlen(settingsPIN) > 0 ? PSTR("Settings locked") : PSTR("No PIN set"), FPSTR(s_redirecting), 1);
return; return;
} }
case SUBPAGE_PINREQ : content = PAGE_settings_pin; len = PAGE_settings_pin_length; code = 401; 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 = "text/css"; break; case SUBPAGE_CSS : content = PAGE_settingsCss; len = PAGE_settingsCss_length; contentType = FPSTR(s_css); break;
case SUBPAGE_JS : serveSettingsJS(request); return; case SUBPAGE_JS : serveSettingsJS(request); return;
case SUBPAGE_WELCOME : content = PAGE_welcome; len = PAGE_welcome_length; break; case SUBPAGE_WELCOME : content = PAGE_welcome; len = PAGE_welcome_length; break;
default: content = PAGE_settings; len = PAGE_settings_length; break; default: content = PAGE_settings; len = PAGE_settings_length; break;