Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

287 lines
8.2 KiB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
  1. /*
  2. WEBSERVER MODULE
  3. Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #if WEB_SUPPORT
  6. #include <ESPAsyncTCP.h>
  7. #include <ESPAsyncWebServer.h>
  8. #include <Hash.h>
  9. #include <FS.h>
  10. #include <AsyncJson.h>
  11. #include <ArduinoJson.h>
  12. #include <Ticker.h>
  13. #if WEB_EMBEDDED
  14. #include "static/index.html.gz.h"
  15. #endif // WEB_EMBEDDED
  16. #if ASYNC_TCP_SSL_ENABLED & WEB_SSL_ENABLED
  17. #include "static/server.cer.h"
  18. #include "static/server.key.h"
  19. #endif // ASYNC_TCP_SSL_ENABLED & WEB_SSL_ENABLED
  20. // -----------------------------------------------------------------------------
  21. AsyncWebServer * _server;
  22. char _last_modified[50];
  23. Ticker _web_defer;
  24. // -----------------------------------------------------------------------------
  25. // HOOKS
  26. // -----------------------------------------------------------------------------
  27. void _onGetConfig(AsyncWebServerRequest *request) {
  28. webLog(request);
  29. if (!_authenticate(request)) return request->requestAuthentication();
  30. AsyncJsonResponse * response = new AsyncJsonResponse();
  31. JsonObject& root = response->getRoot();
  32. root["app"] = APP_NAME;
  33. root["version"] = APP_VERSION;
  34. unsigned int size = settingsKeyCount();
  35. for (unsigned int i=0; i<size; i++) {
  36. String key = settingsKeyName(i);
  37. String value = getSetting(key);
  38. root[key] = value;
  39. }
  40. char buffer[100];
  41. snprintf_P(buffer, sizeof(buffer), PSTR("attachment; filename=\"%s-backup.json\""), (char *) getSetting("hostname").c_str());
  42. response->addHeader("Content-Disposition", buffer);
  43. response->setLength();
  44. request->send(response);
  45. }
  46. #if WEB_EMBEDDED
  47. void _onHome(AsyncWebServerRequest *request) {
  48. webLog(request);
  49. if (!_authenticate(request)) return request->requestAuthentication();
  50. if (request->header("If-Modified-Since").equals(_last_modified)) {
  51. request->send(304);
  52. } else {
  53. #if ASYNC_TCP_SSL_ENABLED
  54. // Chunked response, we calculate the chunks based on free heap (in multiples of 32)
  55. // This is necessary when a TLS connection is open since it sucks too much memory
  56. DEBUG_MSG_P(PSTR("[MAIN] Free heap: %d bytes\n"), ESP.getFreeHeap());
  57. size_t max = (ESP.getFreeHeap() / 3) & 0xFFE0;
  58. AsyncWebServerResponse *response = request->beginChunkedResponse("text/html", [max](uint8_t *buffer, size_t maxLen, size_t index) -> size_t {
  59. // Get the chunk based on the index and maxLen
  60. size_t len = index_html_gz_len - index;
  61. if (len > maxLen) len = maxLen;
  62. if (len > max) len = max;
  63. if (len > 0) memcpy_P(buffer, index_html_gz + index, len);
  64. DEBUG_MSG_P(PSTR("[WEB] Sending %d%%%% (max chunk size: %4d)\r"), int(100 * index / index_html_gz_len), max);
  65. if (len == 0) DEBUG_MSG_P(PSTR("\n"));
  66. // Return the actual length of the chunk (0 for end of file)
  67. return len;
  68. });
  69. #else
  70. AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", index_html_gz, index_html_gz_len);
  71. #endif
  72. response->addHeader("Content-Encoding", "gzip");
  73. response->addHeader("Last-Modified", _last_modified);
  74. request->send(response);
  75. }
  76. }
  77. #endif
  78. #if ASYNC_TCP_SSL_ENABLED & WEB_SSL_ENABLED
  79. int _onCertificate(void * arg, const char *filename, uint8_t **buf) {
  80. #if WEB_EMBEDDED
  81. if (strcmp(filename, "server.cer") == 0) {
  82. uint8_t * nbuf = (uint8_t*) malloc(server_cer_len);
  83. memcpy_P(nbuf, server_cer, server_cer_len);
  84. *buf = nbuf;
  85. DEBUG_MSG_P(PSTR("[WEB] SSL File: %s - OK\n"), filename);
  86. return server_cer_len;
  87. }
  88. if (strcmp(filename, "server.key") == 0) {
  89. uint8_t * nbuf = (uint8_t*) malloc(server_key_len);
  90. memcpy_P(nbuf, server_key, server_key_len);
  91. *buf = nbuf;
  92. DEBUG_MSG_P(PSTR("[WEB] SSL File: %s - OK\n"), filename);
  93. return server_key_len;
  94. }
  95. DEBUG_MSG_P(PSTR("[WEB] SSL File: %s - ERROR\n"), filename);
  96. *buf = 0;
  97. return 0;
  98. #else
  99. File file = SPIFFS.open(filename, "r");
  100. if (file) {
  101. size_t size = file.size();
  102. uint8_t * nbuf = (uint8_t*) malloc(size);
  103. if (nbuf) {
  104. size = file.read(nbuf, size);
  105. file.close();
  106. *buf = nbuf;
  107. DEBUG_MSG_P(PSTR("[WEB] SSL File: %s - OK\n"), filename);
  108. return size;
  109. }
  110. file.close();
  111. }
  112. DEBUG_MSG_P(PSTR("[WEB] SSL File: %s - ERROR\n"), filename);
  113. *buf = 0;
  114. return 0;
  115. #endif
  116. }
  117. #endif
  118. void _onUpgrade(AsyncWebServerRequest *request) {
  119. webLog(request);
  120. if (!_authenticate(request)) return request->requestAuthentication();
  121. char buffer[10];
  122. if (!Update.hasError()) {
  123. sprintf_P(buffer, PSTR("OK"));
  124. } else {
  125. sprintf_P(buffer, PSTR("ERROR %d"), Update.getError());
  126. }
  127. AsyncWebServerResponse *response = request->beginResponse(200, "text/plain", buffer);
  128. response->addHeader("Connection", "close");
  129. if (!Update.hasError()) {
  130. _web_defer.once_ms(100, []() {
  131. customReset(CUSTOM_RESET_UPGRADE);
  132. ESP.restart();
  133. });
  134. }
  135. request->send(response);
  136. }
  137. void _onUpgradeData(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
  138. if (!index) {
  139. DEBUG_MSG_P(PSTR("[UPGRADE] Start: %s\n"), filename.c_str());
  140. Update.runAsync(true);
  141. if (!Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000)) {
  142. #ifdef DEBUG_PORT
  143. Update.printError(DEBUG_PORT);
  144. #endif
  145. }
  146. }
  147. if (!Update.hasError()) {
  148. if (Update.write(data, len) != len) {
  149. #ifdef DEBUG_PORT
  150. Update.printError(DEBUG_PORT);
  151. #endif
  152. }
  153. }
  154. if (final) {
  155. if (Update.end(true)){
  156. DEBUG_MSG_P(PSTR("[UPGRADE] Success: %u bytes\n"), index + len);
  157. } else {
  158. #ifdef DEBUG_PORT
  159. Update.printError(DEBUG_PORT);
  160. #endif
  161. }
  162. } else {
  163. DEBUG_MSG_P(PSTR("[UPGRADE] Progress: %u bytes\r"), index + len);
  164. }
  165. }
  166. // -----------------------------------------------------------------------------
  167. bool _authenticate(AsyncWebServerRequest *request) {
  168. String password = getSetting("adminPass", ADMIN_PASS);
  169. char httpPassword[password.length() + 1];
  170. password.toCharArray(httpPassword, password.length() + 1);
  171. return request->authenticate(WEB_USERNAME, httpPassword);
  172. }
  173. // -----------------------------------------------------------------------------
  174. AsyncWebServer * webServer() {
  175. return _server;
  176. }
  177. void webLog(AsyncWebServerRequest *request) {
  178. DEBUG_MSG_P(PSTR("[WEBSERVER] Request: %s %s\n"), request->methodToString(), request->url().c_str());
  179. }
  180. void webSetup() {
  181. // Cache the Last-Modifier header value
  182. snprintf_P(_last_modified, sizeof(_last_modified), PSTR("%s %s GMT"), __DATE__, __TIME__);
  183. // Create server
  184. #if ASYNC_TCP_SSL_ENABLED & WEB_SSL_ENABLED
  185. unsigned int port = 443;
  186. #else
  187. unsigned int port = getSetting("webPort", WEB_PORT).toInt();
  188. #endif
  189. _server = new AsyncWebServer(port);
  190. // Rewrites
  191. _server->rewrite("/", "/index.html");
  192. // Serve home (basic authentication protection)
  193. #if WEB_EMBEDDED
  194. _server->on("/index.html", HTTP_GET, _onHome);
  195. #endif
  196. _server->on("/config", HTTP_GET, _onGetConfig);
  197. _server->on("/upgrade", HTTP_POST, _onUpgrade, _onUpgradeData);
  198. // Serve static files
  199. #if SPIFFS_SUPPORT
  200. _server->serveStatic("/", SPIFFS, "/")
  201. .setLastModified(_last_modified)
  202. .setFilter([](AsyncWebServerRequest *request) -> bool {
  203. webLog(request);
  204. return true;
  205. });
  206. #endif
  207. // 404
  208. _server->onNotFound([](AsyncWebServerRequest *request){
  209. request->send(404);
  210. });
  211. // Run server
  212. #if ASYNC_TCP_SSL_ENABLED & WEB_SSL_ENABLED
  213. _server->onSslFileRequest(_onCertificate, NULL);
  214. _server->beginSecure("server.cer", "server.key", NULL);
  215. #else
  216. _server->begin();
  217. #endif
  218. DEBUG_MSG_P(PSTR("[WEBSERVER] Webserver running on port %d\n"), port);
  219. }
  220. #endif // WEB_SUPPORT