-
Notifications
You must be signed in to change notification settings - Fork 67
Description
Platform
ESP32
IDE / Tooling
PlatformIO
What happened?
When calling the JSON handler with a scalar (number) the parsing fails and the handler returns a "HTTP/1.1 400 Bad Request" response instead of parsing the JSON correctly.
The curl command: curl -v -X POST -H 'Content-Type: application/json' -d '5' http://192.168.4.1/json2
Cause
AsyncCallbackJsonWebHandler
class in AsyncJson.cpp will deserialize the body by using deserializeJson
function of ArduinoJson. That function expects a null terminated string or the length of the data in the buffer as a third parameter. Instead, the caller passes a non null terminated buffer without a length parameter.
Reference
Proposed fix has been described here: me-no-dev#807
P.S. I will attempt a PR for the fix.
Discussion here: #182
With the fix described above, the issue is resolved.
Worth to note that there could be other unexpected behavior to this issue, including security issues, depending on the content that follows the buffer. This is however a re-producible case.
Stack Trace
curl result:
curl -v -X POST -H "Content-Type: application/json" -d "5" http://192.168.4.1/json2
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying xxx
* Connected to xxx port 80
> POST /json2 HTTP/1.1
> Host: xxxx
> User-Agent: curl/8.9.1
> Accept: */*
> Content-Type: application/json
> Content-Length: 1
>
* upload completely sent off: 1 bytes
< HTTP/1.1 400 Bad Request
< connection: close
< accept-ranges: none
< content-length: 0
<
* shutting down connection #
Nothing appears in ESP32 serial output because the handler blocks the response from getting to the main code.
With the fix described in original issue 807 applied:
curl result:
C:\Users\arik>curl -v -X POST -H "Content-Type: application/json" -d "5" http://192.168.4.1/json2
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying xxxxx
* Connected to xxxx port 80
> POST /json2 HTTP/1.1
> Host: xxxx
> User-Agent: curl/8.9.1
> Accept: */*
> Content-Type: application/json
> Content-Length: 1
>
* upload completely sent off: 1 bytes
< HTTP/1.1 200 OK
< connection: close
< accept-ranges: none
< content-length: 3
< content-type: text/plain
<
int* shutting down connection #0
ESP32 serial
12:01:59.418 > . Json:
12:04:39.267 > 5
12:04:39.267 > Got an int
Minimal Reproductible Example (MRE)
// based on https://github.com/ESP32Async/ESPAsyncWebServer/blob/main/examples/Json/Json.ino
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
#include <Arduino.h>
#include <AsyncTCP.h>
#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <ArduinoJson.h>
#include <AsyncJson.h>
#include <AsyncMessagePack.h>
static AsyncWebServer server(80);
static AsyncCallbackJsonWebHandler *handler = new AsyncCallbackJsonWebHandler("/json2");
void setup()
{
Serial.begin(115200);
// setup wifi
// AP
// WiFi.mode(WIFI_AP);
// WiFi.softAP("esp-captive");
// client
WiFi.mode(WIFI_STA); // for STA mode
WiFi.begin("x", "y");
while (WiFi.status() != WL_CONNECTED)
{
Serial.print(". ");
delay(500);
}
// test cases
// curl -v -X POST -H 'Content-Type: application/json' -d '{' http://192.168.4.1/json2
// curl -v -X POST -H 'Content-Type: application/json' -d '5' http://192.168.4.1/json2
handler->setMethod(HTTP_POST);
handler->onRequest([](AsyncWebServerRequest *request, JsonVariant &json)
{
Serial.println("Json:");
serializeJson(json, Serial);
Serial.println();
if(json.is<int>())
{
Serial.println("Got an int");
}
// response
request->send(200, "text/plain", json.is<int>() ? "int" : "not int"); });
server.addHandler(handler);
server.begin();
}
// not needed
void loop()
{
delay(100);
}
I confirm that:
- I have read the documentation.
- I have searched for similar discussions.
- I have searched for similar issues.
- I have looked at the examples.
- I have upgraded to the lasted version of ESPAsyncWebServer (and AsyncTCP for ESP32).