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.

147 lines
4.1 KiB

api: rework plain and JSON implementations (#2405) - match paths through a custom AsyncWebHandler instead of using generic not-found fallback handler - allow MQTT-like patterns when registering paths (`simple/path`, `path/+/something`, `path/#`) Replaces `relay/0`, `relay/1` etc. with `relay/+`. Magnitudes are plain paths, but using `/+` in case there's more than 1 magnitude of the same type. - restore `std::function` as callback container (no more single-byte arg nonsense). Still, limit to 1 type per handler type - adds JSON handlers which will receive JsonObject root as both input and output. Same logic as plain - GET returns resource data, PUT updates it. - breaking change to `apiAuthenticate(request)`, it no longer will do `request->send(403)` and expect this to be handled externally. - allow `Api-Key` header containing the key, works for both GET & PUT plain requests. The only way to set apikey for JSON. - add `ApiRequest::param` to retrieve both GET and PUT params (aka args), remove ApiBuffer - remove `API_BUFFER_SIZE`. Allow custom form-data key=value pairs for requests, allow to send basic `String`. - add `API_JSON_BUFFER_SIZE` for the JSON buffer (both input and output) - `/apis` replaced with `/api/list`, no longer uses custom handler and is an `apiRegister` callback - `/api/rpc` custom handler replaced with an `apiRegister` callback WIP further down: - no more `webLog` for API requests, unless `webAccessLog` / `WEB_ACCESS_LOG` is set to `1`. This also needs to happen to the other handlers. - migrate to ArduinoJson v6, since it become apparent it is actually a good upgrade :) - actually make use of JSON endpoints more, right now it's just existing GET for sensors and relays - fork ESPAsyncWebServer to cleanup path parsing and temporary objects attached to the request (also, fix things a lot of things based on PRs there...)
4 years ago
  1. /*
  2. Part of the WEBSERVER module
  3. Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
  4. Copyright (C) 2020 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
  5. */
  6. #include "ota.h"
  7. #if WEB_SUPPORT && OTA_WEB_SUPPORT
  8. #include "web.h"
  9. #include "ws.h"
  10. void _onUpgradeResponse(AsyncWebServerRequest *request, int code, const String& payload = "") {
  11. auto *response = request->beginResponseStream("text/plain", 256);
  12. response->addHeader("Connection", "close");
  13. response->addHeader("X-XSS-Protection", "1; mode=block");
  14. response->addHeader("X-Content-Type-Options", "nosniff");
  15. response->addHeader("X-Frame-Options", "deny");
  16. response->setCode(code);
  17. if (payload.length()) {
  18. response->printf("%s", payload.c_str());
  19. } else {
  20. if (!Update.hasError()) {
  21. response->print("OK");
  22. } else {
  23. #if defined(ARDUINO_ESP8266_RELEASE_2_3_0)
  24. Update.printError(reinterpret_cast<Stream&>(response));
  25. #else
  26. Update.printError(*response);
  27. #endif
  28. }
  29. }
  30. request->send(response);
  31. }
  32. void _onUpgradeStatusSet(AsyncWebServerRequest *request, int code, const String& payload = "") {
  33. _onUpgradeResponse(request, code, payload);
  34. request->_tempObject = malloc(sizeof(bool));
  35. }
  36. void _onUpgrade(AsyncWebServerRequest *request) {
  37. webLog(request);
  38. if (!webAuthenticate(request)) {
  39. return request->requestAuthentication(getSetting("hostname").c_str());
  40. }
  41. if (request->_tempObject) {
  42. return;
  43. }
  44. _onUpgradeResponse(request, 200);
  45. }
  46. void _onUpgradeFile(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
  47. if (!webAuthenticate(request)) {
  48. return request->requestAuthentication(getSetting("hostname").c_str());
  49. }
  50. // We set this after we are done with the request
  51. // It is still possible to re-enter this callback even after connection is already closed
  52. // 1.15.0: TODO: see https://github.com/me-no-dev/ESPAsyncWebServer/pull/660
  53. // remote close or request sending some data before finishing parsing of the body will leak 1460 bytes
  54. // waiting a bit for upstream. looks more and more we need to fork the server
  55. if (request->_tempObject) {
  56. return;
  57. }
  58. if (!index) {
  59. // TODO: stop network activity completely when handling Update through ArduinoOTA or `ota` command?
  60. if (Update.isRunning()) {
  61. _onUpgradeStatusSet(request, 400, F("ERROR: Upgrade in progress"));
  62. return;
  63. }
  64. // Check that header is correct and there is more data before anything is written to the flash
  65. if (final || !len) {
  66. _onUpgradeStatusSet(request, 400, F("ERROR: Invalid request"));
  67. return;
  68. }
  69. if (!otaVerifyHeader(data, len)) {
  70. _onUpgradeStatusSet(request, 400, F("ERROR: No magic byte / invalid flash config"));
  71. return;
  72. }
  73. // Disabling EEPROM rotation to prevent writing to EEPROM after the upgrade
  74. eepromRotate(false);
  75. DEBUG_MSG_P(PSTR("[UPGRADE] Start: %s\n"), filename.c_str());
  76. Update.runAsync(true);
  77. // Note: cannot use request->contentLength() for multipart/form-data
  78. if (!Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000)) {
  79. _onUpgradeStatusSet(request, 500);
  80. eepromRotate(true);
  81. return;
  82. }
  83. }
  84. if (request->_tempObject) {
  85. return;
  86. }
  87. // Any error will cancel the update, but request may still be alive
  88. if (!Update.isRunning()) {
  89. return;
  90. }
  91. if (Update.write(data, len) != len) {
  92. _onUpgradeStatusSet(request, 500);
  93. Update.end();
  94. eepromRotate(true);
  95. return;
  96. }
  97. if (final) {
  98. otaFinalize(index + len, CustomResetReason::Ota, true);
  99. } else {
  100. otaProgress(index + len);
  101. }
  102. }
  103. void otaWebSetup() {
  104. webServer().on("/upgrade", HTTP_POST, _onUpgrade, _onUpgradeFile);
  105. wsRegister().
  106. onVisible([](JsonObject& root) {
  107. root["otaVisible"] = 1;
  108. });
  109. }
  110. #endif // OTA_WEB_SUPPORT