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.

139 lines
3.3 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...)
3 years ago
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...)
3 years ago
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...)
3 years ago
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...)
3 years ago
  1. /*
  2. Part of the API MODULE
  3. Copyright (C) 2020 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
  4. */
  5. #pragma once
  6. #include <Arduino.h>
  7. #include <ESPAsyncWebServer.h>
  8. #include <algorithm>
  9. #include <memory>
  10. #include <vector>
  11. #include "api_path.h"
  12. // this is a purely temporary object, which we can only create while doing the API dispatch
  13. struct ApiRequest {
  14. ApiRequest() = delete;
  15. ApiRequest(const ApiRequest&) = default;
  16. ApiRequest(ApiRequest&&) noexcept = default;
  17. explicit ApiRequest(AsyncWebServerRequest& request, const PathParts& pattern, const PathParts& parts) :
  18. _request(request),
  19. _pattern(pattern),
  20. _parts(parts)
  21. {}
  22. template <typename T>
  23. void handle(T&& handler) {
  24. if (_done) return;
  25. _done = true;
  26. handler(&_request);
  27. }
  28. template <typename T>
  29. void param_foreach(T&& handler) {
  30. const size_t params { _request.params() };
  31. for (size_t current = 0; current < params; ++current) {
  32. auto* param = _request.getParam(current);
  33. handler(param->name(), param->value());
  34. }
  35. }
  36. template <typename T>
  37. void param_foreach(const String& name, T&& handler) {
  38. param_foreach([&](const String& param_name, const String& param_value) {
  39. if (param_name == name) {
  40. handler(param_value);
  41. }
  42. });
  43. }
  44. const String& param(const String& name) {
  45. auto* result = _request.getParam(name, HTTP_PUT == _request.method());
  46. if (result) {
  47. return result->value();
  48. }
  49. return _empty_string();
  50. }
  51. void send(const String& payload) {
  52. if (_done) return;
  53. _done = true;
  54. if (payload.length()) {
  55. _request.send(200, "text/plain", payload);
  56. } else {
  57. _request.send(204);
  58. }
  59. }
  60. bool done() const {
  61. return _done;
  62. }
  63. const PathParts& parts() const {
  64. return _parts;
  65. }
  66. String part(size_t index) const {
  67. return _parts[index];
  68. }
  69. // Only works when pattern cointains '+', retrieving the part at the same index from the real path
  70. // e.g. for the pair of `some/+/path` and `some/data/path`, calling `wildcard(0)` will return `data`
  71. String wildcard(int index) const;
  72. size_t wildcards() const;
  73. private:
  74. const String& _empty_string() const {
  75. static const String string;
  76. return string;
  77. }
  78. bool _done { false };
  79. AsyncWebServerRequest& _request;
  80. const PathParts& _pattern;
  81. const PathParts& _parts;
  82. };
  83. struct ApiRequestHelper {
  84. ApiRequestHelper(const ApiRequestHelper&) = delete;
  85. ApiRequestHelper(ApiRequestHelper&&) noexcept = default;
  86. // &path is expected to be request->url(), which is valid throughout the request's lifetime
  87. explicit ApiRequestHelper(AsyncWebServerRequest& request, const PathParts& pattern) :
  88. _request(request),
  89. _pattern(pattern),
  90. _path(request.url()),
  91. _match(_pattern.match(_path))
  92. {}
  93. ApiRequest request() const {
  94. return ApiRequest(_request, _pattern, _path);
  95. }
  96. const PathParts& parts() const {
  97. return _path;
  98. }
  99. bool match() const {
  100. return _match;
  101. }
  102. private:
  103. AsyncWebServerRequest& _request;
  104. const PathParts& _pattern;
  105. PathParts _path;
  106. bool _match;
  107. };