-
Notifications
You must be signed in to change notification settings - Fork 13.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Crash when Debug port is Disabled - HTTPClient destructor issue? #5734
Comments
@joysfera This comment is relevant: /*
* Since both begin() functions take a reference to client as a parameter, you need to
* ensure the client object lives the entire time of the HTTPClient
*/ HttpClient keeps a pointer to client, and tries to access it in the destructor. In your order of instantiation, client gets destroyed first. Then, when HttpClient gets destroyed, it tries to access client, but it no longer exists, so garbage => crash. Closing due to user error. |
This seems to be bad API design rather than simply blaming on user error.
|
Reopening per https://gitter.im/esp8266/Arduino?at=60bfcfe502f6ee5b91e06200 and per closed unfinished #5739 . |
Ok, please help me to understand the problem. First, reuse. I look at the ReuseConnection-Example from ESP8266HTTPClient. Where is the connection-resue? The loop() has "WiFiClient client;" so I assume the connection is created everytime. A valid reuse-example would look like: /**
reuseConnection.ino
Created on: 22.11.2015
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266HTTPClient.h>
ESP8266WiFiMulti WiFiMulti;
HTTPClient http;
WiFiClient client;
void setup() {
Serial.begin(115200);
// Serial.setDebugOutput(true);
Serial.println();
Serial.println();
Serial.println();
for (uint8_t t = 4; t > 0; t--) {
Serial.printf("[SETUP] WAIT %d...\n", t);
Serial.flush();
delay(1000);
}
WiFi.mode(WIFI_STA);
WiFiMulti.addAP("SSID", "PASSWORD");
// allow reuse (if server supports it)
http.setReuse(true);
}
void loop() {
// wait for WiFi connection
if ((WiFiMulti.run() == WL_CONNECTED)) {
if (client.connected()) {
Serial.println("\n!!Connected!!!\n");
}
http.begin(client, "http://jigsaw.w3.org/HTTP/connection.html");
//http.begin(client, "jigsaw.w3.org", 80, "/HTTP/connection.html");
int httpCode = http.GET();
if (httpCode > 0) {
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
// file found at server
if (httpCode == HTTP_CODE_OK) {
http.writeToStream(&Serial);
}
} else {
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
}
http.end();
}
delay(5000);
} Right? |
In the HTTP client class.
In your example it is not: it is declared globally. That's how it can be kept opened between loops. |
Really? In begin(client...) _client is assigned the newly created WiFiClient. |
It is given, not created. What http client does it it under the hood might be different from what you think (now I understand your question in gitter). |
Some time has passed. Broken example #8111 has been removed. Now that I know how reuse shall work, there is the situation that reuse is true by default. Let us do an experiment with BasicHttpClient-example :
Obviously (really?) this does not work, because the connection is still to w3.org and we only get something useful by chance. If I set reuse to false I get the result I expect. This is all quite obviously if you read the source code of HttpClient. But if I had to solve something requesting something from multiple different urls and without reading much source/docu I might have done a looping solution with begin()-end() and not so much have this reuse situation on my radar. Next experiment: change the construction order:
This works fine. Because in end()/disconnect() the connection is closed and the pointer to _client nulled. Soooooooo. Conclusion:
|
- =default for default ctor, destructor, move ctor and the assignment move - use `std::unique_ptr<WiFiClient>` instead of raw pointer to the client - implement `virtual std::unique_ptr<WiFiClient> WiFiClient::clone()` to safely copy the WiFiClientSecure instance, without accidentally slicing it (i.e. using the pointer with incorrect type, calling base WiFiClient virtual methods) - replace headers pointer array with `std::unique_ptr<T[]>` to simplify the move operations - substitute userAgent with the default one when it is empty (may be a subject to change though, b/c now there is a global static `String`) Allow HTTPClient to be placed inside of movable classes (e.g. std::optional, requested in the linked issue) or to be returned from functions. Class logic stays as-is, only the underlying member types are changed. Notice that WiFiClient connection object is now copied, and the internal ClientContext will be preserved even after the original WiFiClient object was destroyed. replaces #8236 resolves #8231 and, possibly #5734
- =default for default ctor, destructor, move ctor and the assignment move - use `std::unique_ptr<WiFiClient>` instead of raw pointer to the client - implement `virtual std::unique_ptr<WiFiClient> WiFiClient::clone()` to safely copy the WiFiClientSecure instance, without accidentally slicing it (i.e. using the pointer with incorrect type, calling base WiFiClient virtual methods) - replace headers pointer array with `std::unique_ptr<T[]>` to simplify the move operations - substitute userAgent with the default one when it is empty (may be a subject to change though, b/c now there is a global static `String`) Allow HTTPClient to be placed inside of movable classes (e.g. std::optional, requested in the linked issue) or to be returned from functions. Class logic stays as-is, only the underlying member types are changed. Notice that WiFiClient connection object is now copied, and the internal ClientContext will be preserved even after the original WiFiClient object was destroyed. replaces esp8266#8236 resolves esp8266#8231 and, possibly esp8266#5734
Basic Infos
Platform
Settings in IDE
Problem Description
In Arduino IDE when
Debug port
is set toDisabled
the ESP8266 reboots (most probably due to a crash) a short while after fetching JSON data via https, parsing it and displaying it on a LED matrix. However, if I set theDebug port
toSerial
then everything works properly. My application does not use any debugging stuff. It does not even use the Serial stuff, does not callSerial.begin()
, nothing of that.The
Debug level
is set toNone
all the time so there's no reason whyDebug port
setting should be significant or should affect the application in any way, IMHO.I cannot debug this because when I enable debugging and thus set the Debug port to Serial the issue disappears :-)
EDIT: here's the minimal code:
It's easy to reproduce that mere
Debug port
setting turns application crashing to not crashing and vice-versa.It's obvious from the source code above that the LED should blink 5 times at start, then wait for up to 5 seconds, and then blink (when the HTTPS request is invoked) once every five seconds.
What you observe is that immediately after the longer HTTPS blink there's the boot-up fast 5 blinks. That indicates the crash&reboot.
EDIT2: you might notice that my MCVE is an almost verbatim copy of the "BasicHttpsClient" example that is provided with Arduino core. Interesting is that the stock BasicHttpsClient does not crash. What is different? Well, the only interesting difference is the order of HTTPClient and BearSL client instantiation. And that's actually it! If you switch the order of those two lines in the BasicHttpsClient example (so that
HTTPClient
is first, thestd::unique_ptr
is second) it will start crashing as well, this time with a stack trace:The text was updated successfully, but these errors were encountered: