mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-06-14 03:46:49 +03:00
merge all URI handlers into a single dispatch function
This commit is contained in:
126
src/ESP32React/SecurityManager.cpp
Normal file
126
src/ESP32React/SecurityManager.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
#include "SecurityManager.h"
|
||||
|
||||
void SecurityManager::addEndpoint(AsyncWebServer * server,
|
||||
const String & path,
|
||||
AuthenticationPredicate predicate,
|
||||
ArJsonRequestHandlerFunction function,
|
||||
WebRequestMethodComposite method) {
|
||||
ensureRestDispatcher(server);
|
||||
const bool requiresAuth = !isPublicPredicate(predicate);
|
||||
_restRoutes.push_back({AsyncURIMatcher(path), method, std::move(predicate), {}, std::move(function), true, requiresAuth});
|
||||
}
|
||||
|
||||
void SecurityManager::addEndpoint(AsyncWebServer * server,
|
||||
const String & path,
|
||||
AuthenticationPredicate predicate,
|
||||
ArRequestHandlerFunction function,
|
||||
WebRequestMethodComposite method) {
|
||||
ensureRestDispatcher(server);
|
||||
const bool requiresAuth = !isPublicPredicate(predicate);
|
||||
_restRoutes.push_back({AsyncURIMatcher(path), method, std::move(predicate), std::move(function), {}, false, requiresAuth});
|
||||
}
|
||||
|
||||
// Detects routes registered with AuthenticationPredicates::NONE_REQUIRED so we can
|
||||
// skip the JWT parse in dispatchRest. Relies on std::function::target returning the
|
||||
// raw function pointer when the predicate was assigned directly from the static fn;
|
||||
// if a caller wraps NONE_REQUIRED in a lambda this falls back to "requires auth"
|
||||
// which is correctness-preserving (just no optimization).
|
||||
bool SecurityManager::isPublicPredicate(const AuthenticationPredicate & predicate) {
|
||||
using Fn = bool (*)(const Authentication &);
|
||||
auto * fn = predicate.target<Fn>();
|
||||
return fn != nullptr && *fn == &AuthenticationPredicates::NONE_REQUIRED;
|
||||
}
|
||||
|
||||
// Lazily attach the single catch-all handler. Idempotent.
|
||||
void SecurityManager::ensureRestDispatcher(AsyncWebServer * server) {
|
||||
if (_restDispatcherInstalled || server == nullptr) {
|
||||
return;
|
||||
}
|
||||
_restDispatcherInstalled = true;
|
||||
server->addHandler(new RestCatchAllHandler(this));
|
||||
}
|
||||
|
||||
void SecurityManager::dispatchRest(AsyncWebServerRequest * request, JsonVariant json) {
|
||||
WebRequestMethod method = request->method();
|
||||
|
||||
for (auto & route : _restRoutes) {
|
||||
if (!route.method.matches(method)) {
|
||||
continue;
|
||||
}
|
||||
if (!route.uri.matches(request)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (route.requiresAuth) {
|
||||
Authentication authentication = authenticateRequest(request);
|
||||
if (!route.predicate(authentication)) {
|
||||
request->send(401);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (route.isJson) {
|
||||
route.jsonHandler(request, json);
|
||||
} else {
|
||||
route.plainHandler(request);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// canHandle returned true so some route matched the URI+method;
|
||||
// a mismatch here means the request slipped between the two checks.
|
||||
request->send(404);
|
||||
}
|
||||
|
||||
// ---- RestCatchAllHandler ----
|
||||
|
||||
bool SecurityManager::RestCatchAllHandler::canHandle(AsyncWebServerRequest * request) const {
|
||||
if (!request->isHTTP()) {
|
||||
return false;
|
||||
}
|
||||
for (const auto & route : _owner->_restRoutes) {
|
||||
if (route.method.matches(request->method()) && route.uri.matches(request)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returning false ensures the server invokes handleBody() so we can buffer JSON bodies.
|
||||
bool SecurityManager::RestCatchAllHandler::isRequestHandlerTrivial() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void SecurityManager::RestCatchAllHandler::handleBody(AsyncWebServerRequest * request, uint8_t * data, size_t len, size_t index, size_t total) {
|
||||
// Only buffer JSON bodies; everything else is routed with an empty JsonVariant.
|
||||
if (total == 0 || total > kMaxBodySize || !isJsonContent(request)) {
|
||||
return;
|
||||
}
|
||||
if (index == 0 && request->_tempObject == nullptr) {
|
||||
request->_tempObject = calloc(total + 1, sizeof(uint8_t)); // freed by request destructor
|
||||
if (request->_tempObject == nullptr) {
|
||||
request->abort();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (request->_tempObject != nullptr) {
|
||||
memcpy(static_cast<uint8_t *>(request->_tempObject) + index, data, len);
|
||||
}
|
||||
}
|
||||
|
||||
void SecurityManager::RestCatchAllHandler::handleRequest(AsyncWebServerRequest * request) {
|
||||
JsonDocument doc;
|
||||
JsonVariant json;
|
||||
if (request->_tempObject != nullptr) {
|
||||
if (deserializeJson(doc, static_cast<const char *>(request->_tempObject))) {
|
||||
request->send(400);
|
||||
return;
|
||||
}
|
||||
json = doc.as<JsonVariant>();
|
||||
}
|
||||
_owner->dispatchRest(request, json);
|
||||
}
|
||||
|
||||
bool SecurityManager::RestCatchAllHandler::isJsonContent(AsyncWebServerRequest * request) {
|
||||
return request->contentType().equalsIgnoreCase("application/json");
|
||||
}
|
||||
Reference in New Issue
Block a user