From 99740f86d7ffc29cba0b7c772a3664bfdc5819b1 Mon Sep 17 00:00:00 2001
From: David Gauchard <gauchard@laas.fr>
Date: Wed, 4 Mar 2020 21:20:51 +0100
Subject: [PATCH 1/3] expose hidden WebServer's chunked API

---
 .../examples/FSBrowser/FSBrowser.ino          | 22 ++++++++++++++-----
 .../ESP8266WebServer/src/ESP8266WebServer.h   | 20 +++++++++++++++--
 2 files changed, 35 insertions(+), 7 deletions(-)

diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino
index fb5847d4b0..645d46b137 100644
--- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino
+++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino
@@ -186,12 +186,22 @@ void handleFileList() {
   Dir dir = filesystem->openDir(path);
   path.clear();
 
-  String output = "[";
+  // use chunked response to avoid building a huge temporary string
+  server.chunkedResponseModeStart(200, "text/json");
+  bool first = true;
+  // use the same string for every line
+  String output;
+  output.reserve(64);
   while (dir.next()) {
+
+    if (output.length())
+        // send string from previous iteration
+        // as an HTTP chunk
+        server.sendContent(output);
+
     File entry = dir.openFile("r");
-    if (output != "[") {
-      output += ',';
-    }
+    output = first? '[': ',';
+    first = false;
     bool isDir = false;
     output += "{\"type\":\"";
     output += (isDir) ? "dir" : "file";
@@ -205,8 +215,10 @@ void handleFileList() {
     entry.close();
   }
 
+  // send last string
   output += "]";
-  server.send(200, "text/json", output);
+  server.sendContent(output);
+  server.chunkedResponseFinalize();
 }
 
 void setup(void) {
diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.h b/libraries/ESP8266WebServer/src/ESP8266WebServer.h
index eefcba2ba3..b3e88a5029 100644
--- a/libraries/ESP8266WebServer/src/ESP8266WebServer.h
+++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.h
@@ -149,18 +149,34 @@ class ESP8266WebServerTemplate
   void sendContent(const char *content) { sendContent_P(content); }
   void sendContent(const char *content, size_t size) { sendContent_P(content, size); }
 
+  bool chunkedResponseModeStart (int code, const char* content_type) {
+    setContentLength(CONTENT_LENGTH_UNKNOWN);
+    send(code, content_type, emptyString);
+  }
+  bool chunkedResponseModeStart (int code, const String& content_type) {
+    setContentLength(CONTENT_LENGTH_UNKNOWN);
+    send(code, content_type, emptyString);
+  }
+  bool chunkedResponseModeStart (int code, PGM_P content_type) {
+    setContentLength(CONTENT_LENGTH_UNKNOWN);
+    send(code, content_type, emptyString);
+  }
+  void chunkedResponseFinalize () {
+    sendContent(emptyString);
+  }
+
   static String credentialHash(const String& username, const String& realm, const String& password);
 
   static String urlDecode(const String& text);
 
-  // Handle a GET request by sending a response header and stream file content to response body 
+  // Handle a GET request by sending a response header and stream file content to response body
   template<typename T>
   size_t streamFile(T &file, const String& contentType) {
     return streamFile(file, contentType, HTTP_GET);
   }
 
   // Implement GET and HEAD requests for files.
-  // Stream body on HTTP_GET but not on HTTP_HEAD requests. 
+  // Stream body on HTTP_GET but not on HTTP_HEAD requests.
   template<typename T>
   size_t streamFile(T &file, const String& contentType, HTTPMethod requestMethod) {
     size_t contentLength = 0;

From fee13076e59711ec8c75b66cd7a45c2455d2d8d6 Mon Sep 17 00:00:00 2001
From: david gauchard <gauchard@laas.fr>
Date: Wed, 4 Mar 2020 22:17:42 +0100
Subject: [PATCH 2/3] expose hidden WebServer's chunked API

---
 .../examples/FSBrowser/FSBrowser.ino           |  8 ++++++--
 .../ESP8266WebServer/src/ESP8266WebServer.h    | 18 ++++++++++--------
 2 files changed, 16 insertions(+), 10 deletions(-)

diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino
index 645d46b137..5503cb001f 100644
--- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino
+++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino
@@ -186,8 +186,12 @@ void handleFileList() {
   Dir dir = filesystem->openDir(path);
   path.clear();
 
-  // use chunked response to avoid building a huge temporary string
-  server.chunkedResponseModeStart(200, "text/json");
+  // use HTTP/1.1 Chunked response to avoid building a huge temporary string
+  if (!server.chunkedResponseModeStart(200, "text/json")) {
+    server.send(505, FPSTR("text/html"), FPSTR("HTTP1.1 required"));
+    return;
+  }
+
   bool first = true;
   // use the same string for every line
   String output;
diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.h b/libraries/ESP8266WebServer/src/ESP8266WebServer.h
index b3e88a5029..9e999acadc 100644
--- a/libraries/ESP8266WebServer/src/ESP8266WebServer.h
+++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.h
@@ -149,17 +149,19 @@ class ESP8266WebServerTemplate
   void sendContent(const char *content) { sendContent_P(content); }
   void sendContent(const char *content, size_t size) { sendContent_P(content, size); }
 
-  bool chunkedResponseModeStart (int code, const char* content_type) {
+  bool chunkedResponseModeStart_P (int code, PGM_P content_type) {
+    if (_currentVersion == 0)
+        // no chunk mode in HTTP/1.0
+        return false;
     setContentLength(CONTENT_LENGTH_UNKNOWN);
-    send(code, content_type, emptyString);
+    send_P(code, content_type, "");
+    return true;
   }
-  bool chunkedResponseModeStart (int code, const String& content_type) {
-    setContentLength(CONTENT_LENGTH_UNKNOWN);
-    send(code, content_type, emptyString);
+  bool chunkedResponseModeStart (int code, const char* content_type) {
+    return chunkedResponseModeStart_P(code, content_type);
   }
-  bool chunkedResponseModeStart (int code, PGM_P content_type) {
-    setContentLength(CONTENT_LENGTH_UNKNOWN);
-    send(code, content_type, emptyString);
+  bool chunkedResponseModeStart (int code, const String& content_type) {
+    return chunkedResponseModeStart_P(code, content_type.c_str());
   }
   void chunkedResponseFinalize () {
     sendContent(emptyString);

From cc3e01cc180bb86687089af50b449671f34581d6 Mon Sep 17 00:00:00 2001
From: david gauchard <gauchard@laas.fr>
Date: Wed, 4 Mar 2020 22:29:41 +0100
Subject: [PATCH 3/3] expose hidden WebServer's chunked API

---
 .../examples/FSBrowser/FSBrowser.ino              | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino
index 5503cb001f..bd25fad582 100644
--- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino
+++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino
@@ -192,20 +192,21 @@ void handleFileList() {
     return;
   }
 
-  bool first = true;
   // use the same string for every line
   String output;
   output.reserve(64);
   while (dir.next()) {
 
-    if (output.length())
-        // send string from previous iteration
-        // as an HTTP chunk
-        server.sendContent(output);
+    if (output.length()) {
+      // send string from previous iteration
+      // as an HTTP chunk
+      server.sendContent(output);
+      output = ',';
+    } else {
+      output = '[';
+    }
 
     File entry = dir.openFile("r");
-    output = first? '[': ',';
-    first = false;
     bool isDir = false;
     output += "{\"type\":\"";
     output += (isDir) ? "dir" : "file";