Blame | Last modification | View Log | RSS feed
// SPDX-License-Identifier: LGPL-3.0-or-later// Copyright 2016-2026 Hristo Gochkov, Mathieu Carbou, Emil Muratov, Will Miles#include "AsyncJson.h"#include "AsyncWebServerLogging.h"#include <utility>#if ASYNC_JSON_SUPPORT == 1// Json content type response classes#if ARDUINOJSON_VERSION_MAJOR == 5AsyncJsonResponse::AsyncJsonResponse(bool isArray) : _isValid{false} {_code = 200;_contentType = asyncsrv::T_application_json;if (isArray) {_root = _jsonBuffer.createArray();} else {_root = _jsonBuffer.createObject();}}#elif ARDUINOJSON_VERSION_MAJOR == 6AsyncJsonResponse::AsyncJsonResponse(bool isArray, size_t maxJsonBufferSize) : _jsonBuffer(maxJsonBufferSize), _isValid{false} {_code = 200;_contentType = asyncsrv::T_application_json;if (isArray) {_root = _jsonBuffer.createNestedArray();} else {_root = _jsonBuffer.createNestedObject();}}#elseAsyncJsonResponse::AsyncJsonResponse(bool isArray) : _isValid{false} {_code = 200;_contentType = asyncsrv::T_application_json;if (isArray) {_root = _jsonBuffer.add<JsonArray>();} else {_root = _jsonBuffer.add<JsonObject>();}}#endifsize_t AsyncJsonResponse::setLength() {#if ARDUINOJSON_VERSION_MAJOR == 5_contentLength = _root.measureLength();#else_contentLength = measureJson(_root);#endifif (_contentLength) {_isValid = true;}return _contentLength;}size_t AsyncJsonResponse::_fillBuffer(uint8_t *data, size_t len) {ChunkPrint dest(data, _sentLength, len);#if ARDUINOJSON_VERSION_MAJOR == 5_root.printTo(dest);#elseserializeJson(_root, dest);#endifreturn dest.written();}#if ARDUINOJSON_VERSION_MAJOR == 6PrettyAsyncJsonResponse::PrettyAsyncJsonResponse(bool isArray, size_t maxJsonBufferSize) : AsyncJsonResponse{isArray, maxJsonBufferSize} {}#elsePrettyAsyncJsonResponse::PrettyAsyncJsonResponse(bool isArray) : AsyncJsonResponse{isArray} {}#endifsize_t PrettyAsyncJsonResponse::setLength() {#if ARDUINOJSON_VERSION_MAJOR == 5_contentLength = _root.measurePrettyLength();#else_contentLength = measureJsonPretty(_root);#endifif (_contentLength) {_isValid = true;}return _contentLength;}size_t PrettyAsyncJsonResponse::_fillBuffer(uint8_t *data, size_t len) {ChunkPrint dest(data, _sentLength, len);#if ARDUINOJSON_VERSION_MAJOR == 5_root.prettyPrintTo(dest);#elseserializeJsonPretty(_root, dest);#endifreturn dest.written();}// MessagePack content type response#if ASYNC_MSG_PACK_SUPPORT == 1size_t AsyncMessagePackResponse::setLength() {_contentLength = measureMsgPack(_root);if (_contentLength) {_isValid = true;}return _contentLength;}size_t AsyncMessagePackResponse::_fillBuffer(uint8_t *data, size_t len) {ChunkPrint dest(data, _sentLength, len);serializeMsgPack(_root, dest);return dest.written();}#endif// Body handler supporting both content types: JSON and MessagePackconstexpr static WebRequestMethodComposite JsonHandlerMethods =AsyncWebRequestMethod::HTTP_GET | AsyncWebRequestMethod::HTTP_POST | AsyncWebRequestMethod::HTTP_PUT | AsyncWebRequestMethod::HTTP_PATCH;#if ARDUINOJSON_VERSION_MAJOR == 6AsyncCallbackJsonWebHandler::AsyncCallbackJsonWebHandler(AsyncURIMatcher uri, ArJsonRequestHandlerFunction onRequest, size_t maxJsonBufferSize): _uri(std::move(uri)),_method(AsyncWebRequestMethod::HTTP_GET | AsyncWebRequestMethod::HTTP_POST | AsyncWebRequestMethod::HTTP_PUT | AsyncWebRequestMethod::HTTP_PATCH),_onRequest(onRequest), maxJsonBufferSize(maxJsonBufferSize), _maxContentLength(16384) {}#elseAsyncCallbackJsonWebHandler::AsyncCallbackJsonWebHandler(AsyncURIMatcher uri, ArJsonRequestHandlerFunction onRequest): _uri(std::move(uri)),_method(AsyncWebRequestMethod::HTTP_GET | AsyncWebRequestMethod::HTTP_POST | AsyncWebRequestMethod::HTTP_PUT | AsyncWebRequestMethod::HTTP_PATCH),_onRequest(onRequest), _maxContentLength(16384) {}#endifbool AsyncCallbackJsonWebHandler::canHandle(AsyncWebServerRequest *request) const {if (!_onRequest || !request->isHTTP() || !_method.matches(request->method())) {return false;}if (!_uri.matches(request)) {return false;}#if ASYNC_MSG_PACK_SUPPORT == 1return request->method() == AsyncWebRequestMethod::HTTP_GET || request->contentType().equalsIgnoreCase(asyncsrv::T_application_json)|| request->contentType().equalsIgnoreCase(asyncsrv::T_application_msgpack);#elsereturn request->method() == AsyncWebRequestMethod::HTTP_GET || request->contentType().equalsIgnoreCase(asyncsrv::T_application_json);#endif}void AsyncCallbackJsonWebHandler::handleRequest(AsyncWebServerRequest *request) {if (_onRequest) {// GET request:if (request->method() == AsyncWebRequestMethod::HTTP_GET) {JsonVariant json;_onRequest(request, json);return;}// POST / PUT / ... requests:// check if JSON body is too large, if it is, don't deserializeif (request->contentLength() > _maxContentLength) {async_ws_log_w("Content length exceeds maximum allowed");request->send(413);return;}if (request->_tempObject == NULL) {// there is no bodyrequest->send(400);return;}#if ARDUINOJSON_VERSION_MAJOR == 5DynamicJsonBuffer doc;#elif ARDUINOJSON_VERSION_MAJOR == 6DynamicJsonDocument doc(this->maxJsonBufferSize);#elseJsonDocument doc;#endif#if ARDUINOJSON_VERSION_MAJOR == 5JsonVariant json = doc.parse((const char *)request->_tempObject);if (json.success()) {_onRequest(request, json);return;}#elseDeserializationError error = request->contentType().equalsIgnoreCase(asyncsrv::T_application_msgpack)? deserializeMsgPack(doc, (uint8_t *)(request->_tempObject)): deserializeJson(doc, (const char *)request->_tempObject);if (!error) {JsonVariant json = doc.as<JsonVariant>();_onRequest(request, json);return;}#endif// error parsing the bodyrequest->send(400);}}void AsyncCallbackJsonWebHandler::handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {if (_onRequest) {// ignore callback if size is larger than maxContentLengthif (total > _maxContentLength) {return;}if (index == 0) {if (total == 0) {// If total is 0, it is probably a chunked request without an// X-Expected-Entity-Length header. In that case there is// no way to know the actual length in advance. The best// way to handle this would be to use a String instead of// a fixed-length buffer, but for now we just reject.async_ws_log_w("AsyncJson cannot handle chunked requests without X-Expected-Entity-Length");request->abort();return;}// this check allows request->_tempObject to be initialized from a middlewareif (request->_tempObject == NULL) {request->_tempObject = calloc(total + 1, sizeof(uint8_t)); // null-terminated stringif (request->_tempObject == NULL) {async_ws_log_e("Failed to allocate");request->abort();return;}}}if (request->_tempObject != NULL) {uint8_t *buffer = (uint8_t *)request->_tempObject;memcpy(buffer + index, data, len);}}}#endif // ASYNC_JSON_SUPPORT