arduino-libraries / ArduinoHttpClient

Arduino HTTP Client library
282 stars 170 forks source link

Status: -2 or -3 stack the loop in arduino #105

Open roysG opened 3 years ago

roysG commented 3 years ago

Hi, I am using you library, it works great when i get status code: 200. But if the server send error for some reason, then i get status code -2 or -3 and then it is not send again requests to server.

Code:

#include "config.h"
#include <HttpClient.h>
#include <TinyGsmClient.h>

#ifdef DEBUG_DUMP_AT_COMMAND
#include <StreamDebugger.h>
StreamDebugger debugger(SerialAT, SerialMon);
TinyGsm modem(debugger, AM7020_RESET);
#else
TinyGsm modem(SerialAT, AM7020_RESET);
#endif

TinyGsmClient tcpClient(modem);
HttpClient httpClient = HttpClient(tcpClient, HTTP_SERVER, HTTP_PORT);

void setup() {
  SerialMon.begin(MONITOR_BAUDRATE);
  SerialAT.begin(AM7020_BAUDRATE);

  httpClient.connectionKeepAlive();
  nbConnect();
}

void callAgain() {
  static unsigned long timer = 0;
  char buff[100];
  int state_code;
  String body;

  if (!modem.isNetworkConnected()) {
    nbConnect();
  }

  //if (millis() >= timer) {
  //  timer = millis() + UPLOAD_INTERVAL;

  /* Get Most Recent Data */
  SerialMon.println(F("HTTP Get..."));
  sprintf(buff, "/%s", HTTP_API);
  httpClient.get(buff);
  state_code = httpClient.responseStatusCode();
  body = httpClient.responseBody();

  SerialMon.print(F("GET state code = "));
  SerialMon.println(state_code);
  SerialMon.print(F("body = "));
  SerialMon.println(body);

  /* Create Data */
  /*SerialMon.println(F("HTTP Post..."));
        sprintf(buff, "/%s", HTTP_API);
        httpClient.post(buff, "application/json", "{\"value\": \"POST\"}");

        state_code = httpClient.responseStatusCode();
        body       = httpClient.responseBody();
        SerialMon.print(F("POST state code = "));
        SerialMon.println(state_code);
        SerialMon.print(F("body = "));
        SerialMon.println(body);*/
  //}
}

void loop() {
  callAgain();
  delay(100);
}

void nbConnect(void) {
  SerialMon.println(F("Initializing modem..."));
  while (!modem.init() || !modem.nbiotConnect(APN, BAND)) {
    SerialMon.print(F("."));
  }

  SerialMon.print(F("Waiting for network..."));
  while (!modem.waitForNetwork()) {
    SerialMon.print(F("."));
  }
  SerialMon.println(F(" success"));
}
kb173 commented 1 year ago

We have run into the same issue. From what I can tell reading the library's code, this is what is happening:

  1. We try reading the response status code with responseStatusCode().
  2. For some reason, this reaches timeout (-3). Not sure why this is happening in our case, but I assume it could be attributed to an unstable internet connection? (If anyone knows more about this, please let me know)
  3. The state of the library is now at "reading status code". Because of this, post() returns the error specifying that it has been called in an unexpected state (-2). This is because in startRequest, the state is reset in some cases (if (iState == eReadingBody || iState == eReadingChunkLength || iState == eReadingBodyChunk)), but since iState is eReadingStatusCode, it is not reset, resulting in an early return with -2.

Is there a good reason as to why the state is only reset in those three cases? Might simply adding an || iState == eReadingStatusCode solve the problem, or will that have other unexpected side effects? (Or am I wrong in my reconstruction altogether?)

@roysG did you perhaps find another way to solve or work around the issue?

kb173 commented 1 year ago

We did find a very simple workaround: simply construct new HttpClient objects before each post rather than keeping a global HttpClient object.

I actually think that this is generally more sensible: HTTP is stateless, so keeping a global (stateful) HttpClient object is error-prone and logically inconsistent. It might be better practice to create the object on-the-fly and keep them only for as long as the code depends on that request and its result.