Files
espurna/code/espurna/api_common.cpp
Max Prokhorov 8e80a7786c 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...)
2020-12-05 14:14:38 +03:00

97 lines
2.2 KiB
C++

/*
Part of the API MODULE
Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
Copyright (C) 2020 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
*/
#include "espurna.h"
#include "api.h"
#include "ws.h"
#include "web.h"
// -----------------------------------------------------------------------------
#if WEB_SUPPORT
namespace {
bool _apiWebSocketOnKeyCheck(const char * key, JsonVariant& value) {
return (strncmp(key, "api", 3) == 0);
}
void _apiWebSocketOnConnected(JsonObject& root) {
root["apiEnabled"] = apiEnabled();
root["apiKey"] = apiKey();
root["apiRestFul"] = apiRestFul();
root["apiRealTime"] = getSetting("apiRealTime", 1 == API_REAL_TIME_VALUES);
}
}
// -----------------------------------------------------------------------------
// Public API
// -----------------------------------------------------------------------------
bool apiEnabled() {
return getSetting("apiEnabled", 1 == API_ENABLED);
}
bool apiRestFul() {
return getSetting("apiRestFul", 1 == API_RESTFUL);
}
String apiKey() {
return getSetting("apiKey", API_KEY);
}
bool apiAuthenticateHeader(AsyncWebServerRequest* request, const String& key) {
if (apiEnabled() && key.length()) {
auto* header = request->getHeader(F("Api-Key"));
if (header && (key == header->value())) {
return true;
}
}
return false;
}
bool apiAuthenticateParam(AsyncWebServerRequest* request, const String& key) {
auto* param = request->getParam("apikey", (request->method() == HTTP_PUT));
if (param && (key == param->value())) {
return true;
}
return false;
}
bool apiAuthenticate(AsyncWebServerRequest* request) {
const auto key = apiKey();
if (!key.length()) {
return false;
}
if (apiAuthenticateHeader(request, key)) {
return true;
}
if (apiAuthenticateParam(request, key)) {
return true;
}
return false;
}
void apiCommonSetup() {
wsRegister()
.onVisible([](JsonObject& root) { root["apiVisible"] = 1; })
.onConnected(_apiWebSocketOnConnected)
.onKeyCheck(_apiWebSocketOnKeyCheck);
}
#endif // WEB_SUPPORT == 1