From e67cc90b7a4914c6c3a6fd795183fefc5e910094 Mon Sep 17 00:00:00 2001
From: david gauchard <gauchard@laas.fr>
Date: Mon, 6 May 2019 21:26:17 +0200
Subject: [PATCH] Honor timeout in HTTPClient (#6056)

* check for timeout in ESP8266HTTPClient::writeToStreamDataBlock
---
 .../StreamHttpClient/StreamHttpClient.ino     |  36 +++--
 .../src/ESP8266HTTPClient.cpp                 | 138 +++++++++---------
 2 files changed, 85 insertions(+), 89 deletions(-)

diff --git a/libraries/ESP8266HTTPClient/examples/StreamHttpClient/StreamHttpClient.ino b/libraries/ESP8266HTTPClient/examples/StreamHttpClient/StreamHttpClient.ino
index 626dbe7e3..7ed0cd529 100644
--- a/libraries/ESP8266HTTPClient/examples/StreamHttpClient/StreamHttpClient.ino
+++ b/libraries/ESP8266HTTPClient/examples/StreamHttpClient/StreamHttpClient.ino
@@ -63,31 +63,35 @@ void loop() {
         // create buffer for read
         uint8_t buff[128] = { 0 };
 
+#if 0
+        // with API
+        Serial.println(http.getString());
+#else
+        // or "by hand"
+
         // get tcp stream
         WiFiClient * stream = &client;
 
         // read all data from server
         while (http.connected() && (len > 0 || len == -1)) {
-          // get available data size
-          size_t size = stream->available();
-
-          if (size) {
-            // read up to 128 byte
-            int c = stream->readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size));
-
-            // write it to Serial
-            Serial.write(buff, c);
-
-            if (len > 0) {
-              len -= c;
-            }
+          // read up to 128 byte
+          int c = stream->readBytes(buff, std::min((size_t)len, sizeof(buff)));
+          Serial.printf("readBytes: %d\n", c);
+          if (!c) {
+            Serial.println("read timeout");
+          }
+
+          // write it to Serial
+          Serial.write(buff, c);
+
+          if (len > 0) {
+            len -= c;
           }
-          delay(1);
         }
+#endif
 
         Serial.println();
         Serial.print("[HTTP] connection closed or file end.\n");
-
       }
     } else {
       Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
@@ -96,5 +100,5 @@ void loop() {
     http.end();
   }
 
-  delay(10000);
+  delay(60000);
 }
diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp
index 2c9542cb0..8d7880e4e 100644
--- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp
+++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp
@@ -847,7 +847,7 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size)
         }
 
     } else {
-        DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] too less ram! need %d\n", HTTP_TCP_BUFFER_SIZE);
+        DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] not enough ram! need %d\n", HTTP_TCP_BUFFER_SIZE);
         return returnError(HTTPC_ERROR_TOO_LESS_RAM);
     }
 
@@ -1033,7 +1033,7 @@ String HTTPClient::errorToString(int error)
     case HTTPC_ERROR_NO_HTTP_SERVER:
         return F("no HTTP server");
     case HTTPC_ERROR_TOO_LESS_RAM:
-        return F("too less ram");
+        return F("not enough ram");
     case HTTPC_ERROR_ENCODING:
         return F("Transfer-Encoding not supported");
     case HTTPC_ERROR_STREAM_WRITE:
@@ -1346,7 +1346,7 @@ int HTTPClient::handleHeaderResponse()
 int HTTPClient::writeToStreamDataBlock(Stream * stream, int size)
 {
     int buff_size = HTTP_TCP_BUFFER_SIZE;
-    int len = size;
+    int len = size; // left size to read
     int bytesWritten = 0;
 
     // if possible create smaller buffer then HTTP_TCP_BUFFER_SIZE
@@ -1357,93 +1357,85 @@ int HTTPClient::writeToStreamDataBlock(Stream * stream, int size)
     // create buffer for read
     uint8_t * buff = (uint8_t *) malloc(buff_size);
 
-    if(buff) {
-        // read all data from server
-        while(connected() && (len > 0 || len == -1)) {
+    if(!buff) {
+        DEBUG_HTTPCLIENT("[HTTP-Client][writeToStreamDataBlock] not enough ram! need %d\n", HTTP_TCP_BUFFER_SIZE);
+        return HTTPC_ERROR_TOO_LESS_RAM;
+    }
 
-            // get available data size
-            size_t sizeAvailable = _client->available();
+    // read all data from server
+    while(connected() && (len > 0 || len == -1))
+    {
+        int readBytes = len;
 
-            if(sizeAvailable) {
+        // not read more the buffer can handle
+        if(readBytes > buff_size) {
+            readBytes = buff_size;
+        }
 
-                int readBytes = sizeAvailable;
+        // read data
+        int bytesRead = _client->readBytes(buff, readBytes);
+        if (!bytesRead)
+        {
+            DEBUG_HTTPCLIENT("[HTTP-Client][writeToStreamDataBlock] input stream timeout\n");
+            free(buff);
+            return HTTPC_ERROR_READ_TIMEOUT;
+        }
 
-                // read only the asked bytes
-                if(len > 0 && readBytes > len) {
-                    readBytes = len;
-                }
+        // write it to Stream
+        int bytesWrite = stream->write(buff, bytesRead);
+        bytesWritten += bytesWrite;
 
-                // not read more the buffer can handle
-                if(readBytes > buff_size) {
-                    readBytes = buff_size;
-                }
+        // are all Bytes a writen to stream ?
+        if(bytesWrite != bytesRead) {
+            DEBUG_HTTPCLIENT("[HTTP-Client][writeToStream] short write asked for %d but got %d retry...\n", bytesRead, bytesWrite);
 
-                // read data
-                int bytesRead = _client->readBytes(buff, readBytes);
+            // check for write error
+            if(stream->getWriteError()) {
+                DEBUG_HTTPCLIENT("[HTTP-Client][writeToStreamDataBlock] stream write error %d\n", stream->getWriteError());
 
-                // write it to Stream
-                int bytesWrite = stream->write(buff, bytesRead);
-                bytesWritten += bytesWrite;
+                //reset write error for retry
+                stream->clearWriteError();
+            }
 
-                // are all Bytes a writen to stream ?
-                if(bytesWrite != bytesRead) {
-                    DEBUG_HTTPCLIENT("[HTTP-Client][writeToStream] short write asked for %d but got %d retry...\n", bytesRead, bytesWrite);
+            // some time for the stream
+            delay(1);
 
-                    // check for write error
-                    if(stream->getWriteError()) {
-                        DEBUG_HTTPCLIENT("[HTTP-Client][writeToStreamDataBlock] stream write error %d\n", stream->getWriteError());
+            int leftBytes = (bytesRead - bytesWrite);
 
-                        //reset write error for retry
-                        stream->clearWriteError();
-                    }
+            // retry to send the missed bytes
+            bytesWrite = stream->write((buff + bytesWrite), leftBytes);
+            bytesWritten += bytesWrite;
 
-                    // some time for the stream
-                    delay(1);
-
-                    int leftBytes = (readBytes - bytesWrite);
-
-                    // retry to send the missed bytes
-                    bytesWrite = stream->write((buff + bytesWrite), leftBytes);
-                    bytesWritten += bytesWrite;
-
-                    if(bytesWrite != leftBytes) {
-                        // failed again
-                        DEBUG_HTTPCLIENT("[HTTP-Client][writeToStream] short write asked for %d but got %d failed.\n", leftBytes, bytesWrite);
-                        free(buff);
-                        return HTTPC_ERROR_STREAM_WRITE;
-                    }
-                }
-
-                // check for write error
-                if(stream->getWriteError()) {
-                    DEBUG_HTTPCLIENT("[HTTP-Client][writeToStreamDataBlock] stream write error %d\n", stream->getWriteError());
-                    free(buff);
-                    return HTTPC_ERROR_STREAM_WRITE;
-                }
-
-                // count bytes to read left
-                if(len > 0) {
-                    len -= readBytes;
-                }
-
-                delay(0);
-            } else {
-                delay(1);
+            if(bytesWrite != leftBytes) {
+                // failed again
+                DEBUG_HTTPCLIENT("[HTTP-Client][writeToStream] short write asked for %d but got %d failed.\n", leftBytes, bytesWrite);
+                free(buff);
+                return HTTPC_ERROR_STREAM_WRITE;
             }
         }
 
-        free(buff);
-
-        DEBUG_HTTPCLIENT("[HTTP-Client][writeToStreamDataBlock] connection closed or file end (written: %d).\n", bytesWritten);
-
-        if((size > 0) && (size != bytesWritten)) {
-            DEBUG_HTTPCLIENT("[HTTP-Client][writeToStreamDataBlock] bytesWritten %d and size %d mismatch!.\n", bytesWritten, size);
+        // check for write error
+        if(stream->getWriteError()) {
+            DEBUG_HTTPCLIENT("[HTTP-Client][writeToStreamDataBlock] stream write error %d\n", stream->getWriteError());
+            free(buff);
             return HTTPC_ERROR_STREAM_WRITE;
         }
 
-    } else {
-        DEBUG_HTTPCLIENT("[HTTP-Client][writeToStreamDataBlock] too less ram! need %d\n", HTTP_TCP_BUFFER_SIZE);
-        return HTTPC_ERROR_TOO_LESS_RAM;
+        // count bytes to read left
+        if(len > 0) {
+            len -= bytesRead;
+        }
+
+        delay(0);
+    }
+
+    free(buff);
+
+    DEBUG_HTTPCLIENT("[HTTP-Client][writeToStreamDataBlock] connection closed or file end (written: %d).\n", bytesWritten);
+
+    if((size > 0) && (size != bytesWritten)) {
+        DEBUG_HTTPCLIENT("[HTTP-Client][writeToStreamDataBlock] bytesWritten %d and size %d mismatch!.\n", bytesWritten, size);
+        return HTTPC_ERROR_STREAM_WRITE;
     }
 
     return bytesWritten;