I am trying to create server and client interaction on Microcontroller ESP32. It'll consist of simple HTTP server and a client. So the client will send a POST request to the server, and the server will do some logic with the data it received.
And for more context, here is the server-side code:
if (client.available()) { // if there's bytes to read from the client,
char c = client.read(); // read a byte, then
Serial.write(c); // print it out the serial monitor
header += c;
if (c == '\n') { // if the byte is a newline character
// if the current line is blank, you got two newline characters in a row.
// that's the end of the client HTTP request, so send a response:
if (currentLine.length() == 0) {
int contentLength = header.indexOf("Content-Length: ");
if (contentLength != -1) {
contentLength = header.substring(contentLength + 16).toInt();
int bodyRead = 0;
while (bodyRead < contentLength && client.available()) {
char c = client.read();
requestBody += c;
bodyRead++;
}
}
// Separate direction and vehicle id
int sd = requestBody.indexOf('=');
int vd = requestBody.indexOf('&');
String direction = requestBody.substring(sd + 1, vd);
int pos = requestBody.indexOf('=', vd);
String vehicle = requestBody.substring(pos + 1);
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println("Connection: close");
client.println("Got it, " + direction + " for : " + vehicle);
client.println();
Serial.println("Request Body : " + requestBody);
Serial.println("Direction : " + direction);
Serial.println("Vehicle : " + vehicle);
// Break out of the while loop
break;
} else { // if you got a newline, then clear currentLine
currentLine = "";
}
} else if (c != '\r') { // if you got anything else but a carriage return character,
currentLine += c; // add it to the end of the currentLine
}
}
And here is the client-side code:
if(WiFi.status()== WL_CONNECTED){
WiFiClient client;
HTTPClient http;
// Your Domain name with URL path or IP address with path
http.begin(client, serverName);
// Specify content-type header
http.addHeader("Content-Type", "application/x-www-form-urlencoded");
// Data to send with HTTP POST
String httpRequestData = "from=south&id=military";
// Send HTTP POST request
int httpResponseCode = http.POST(httpRequestData);
Serial.print("HTTP Response code: ");
Serial.println(httpResponseCode);
// Free resources
http.end();
}
else {
Serial.println("WiFi Disconnected");
}
Now, the problem is the server cannot correctly read the POST request.
And before I tested the client, I use API Tester (mobile application) to test the server, and it works as expected. So my server prints these out in the serial monitor:
POST / HTTP/1.1
user-agent: apitester.org Android/7.5(641)
accept: */*
Content-Type: application/x-www-form-urlencoded
Content-Length: 22
Host: 192.168.4.1
Connection: Keep-Alive
Accept-Encoding: gzip
Request Body : from=south&id=military
Direction : south
Vehicle : military
Client disconnected.
But when I send the POST request from a client, my server doesn't return any data on the serial monitor:
POST / HTTP/1.1
Host: 192.168.4.1
User-Agent: ESP32HTTPClient
Connection: keep-alive
Accept-Encoding: identity;q=1,chunked;q=0.1,*;q=0
Content-Type: application/x-www-form-urlencoded
Content-Length: 23
Request Body :
Direction :
Vehicle :
Client disconnected.
I still haven't figured out why this happens. I guess the mistake is on the client side? Because the server works when used with API Tester. But from many tutorials I have read, my client code should be working correctly.
Also, because there is not something like an error code, I don't know from where I should fix this issue. I hope you can help me with this.
[EDIT]:
if (client.available()) { // if there's bytes to read from the client,
char c = client.read(); // read a byte, then
Serial.write(c); // print it out the serial monitor
header += c;
if (c == '\n') { // if the byte is a newline character
// if the current line is blank, you got two newline characters in a row.
// that's the end of the client HTTP request, so send a response:
if (currentLine.length() == 0) {
// Serial.print("STATUS REPORT <header> : ");
// Serial.println(header);
// Find the Content-Length header
int contentLength = header.indexOf("Content-Length: ");
if (contentLength != -1) {
Serial.print("STATUS REPORT <contentLength> : ");
Serial.println(contentLength);
// Find the end of the Content-Length line
int endOfContentLength = header.indexOf("\r\n", contentLength);
Serial.print("STATUS REPORT <endOfContentLength> : ");
Serial.println(endOfContentLength);
if (endOfContentLength != -1) {
// Extract the Content-Length value as an integer
contentLength = header.substring(contentLength + 16, endOfContentLength).toInt();
int bodyRead = 0;
while (bodyRead < contentLength && client.available()) {
if (client.available()) {
char c = client.read();
requestBody += c;
bodyRead++;
}
}
}
}
// Separate direction and vehicle id
int sd = requestBody.indexOf('=');
int vd = requestBody.indexOf('&');
String direction = requestBody.substring(sd + 1, vd);
int pos = requestBody.indexOf('=', vd);
String vehicle = requestBody.substring(pos + 1);
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/plain");
client.println("Connection: close");
client.println();
client.println("Got it, " + direction + " for : " + vehicle);
Serial.println("Request Body : " + requestBody);
Serial.println("Direction : " + direction);
Serial.println("Vehicle : " + vehicle);
// Break out of the while loop
break;
} else { // if you got a newline, then clear currentLine
currentLine = "";
}
} else if (c != '\r') { // if you got anything else but a carriage return character,
currentLine += c; // add it to the end of the currentLine
}
}
One problem I see is when you are extracting the
Content-Lengthvalue from yourheader, you are expectingContent-Lengthto be the last HTTP header stored in theheader, but it is not the case in your example.The reason you are making that assumption is because when you are calling
substring()to grab theContent-Length's integer value, you are extracting everything that immediately follows"Content-Length: "all the way to the end of theheader. So, that only works whenContent-Lengthis the last HTTP header.You need to instead stop extracting the integer value at the
'\r'or'\n'character that actually ends theContent-Lengthline. Which means you need to specify an end index when you callsubstring().But, you are not storing either of those characters in your
header, so you can't find them when you need them.Also, be aware that HTTP headers are case-insensitive, and that whitespace is optional following the
':'character after a header name.