From 474af1a36b2a89f0a052ef0e2f22845472260a8a Mon Sep 17 00:00:00 2001 From: Zakary Kamal Ismail Date: Mon, 29 Jun 2020 12:11:59 -0400 Subject: [PATCH 1/4] WebServer: Allow client to send many requests on the same connection --- .../ESP8266WebServer/src/ESP8266WebServer-impl.h | 16 +++++++++++++++- .../ESP8266WebServer/src/ESP8266WebServer.h | 8 ++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h b/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h index 0823a5ed47..80c9611490 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h @@ -49,6 +49,7 @@ ESP8266WebServerTemplate::ESP8266WebServerTemplate(IPAddress addr, i , _currentVersion(0) , _currentStatus(HC_NONE) , _statusChange(0) +, _keepAlive(false) , _currentHandler(nullptr) , _firstHandler(nullptr) , _lastHandler(nullptr) @@ -325,6 +326,10 @@ void ESP8266WebServerTemplate::handleClient() { bool callYield = false; if (_currentClient.connected() || _currentClient.available()) { + if (_currentClient.available() && _keepAlive) { + _currentStatus = HC_WAIT_READ; + } + switch (_currentStatus) { case HC_NONE: // No-op to avoid C++ compiler warning @@ -362,6 +367,7 @@ void ESP8266WebServerTemplate::handleClient() { if (!keepCurrentClient) { _currentClient = ClientType(); _currentStatus = HC_NONE; + _keepAlive = false; _currentUpload.reset(); } @@ -374,6 +380,7 @@ template void ESP8266WebServerTemplate::close() { _server.close(); _currentStatus = HC_NONE; + _keepAlive = false; if(!_headerKeysCount) collectHeaders(0, 0); } @@ -429,7 +436,14 @@ void ESP8266WebServerTemplate::_prepareHeader(String& response, int if (_corsEnabled) { sendHeader(String(F("Access-Control-Allow-Origin")), String("*")); } - sendHeader(String(F("Connection")), String(F("close"))); + + if (_keepAlive && _server.hasClient()) { // Disable keep alive if another client is waiting. + _keepAlive = false; + } + sendHeader(String(F("Connection")), String(_keepAlive ? F("keep-alive") : F("close"))); + if (_keepAlive) { + sendHeader(String(F("Keep-Alive")), String(F("timeout=")) + HTTP_MAX_CLOSE_WAIT); + } response += _responseHeaders; response += "\r\n"; diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.h b/libraries/ESP8266WebServer/src/ESP8266WebServer.h index 2ace5dc107..85aa8f69d5 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.h @@ -168,6 +168,13 @@ class ESP8266WebServerTemplate sendContent(emptyString); } + // Whether other requests should be accepted from the client on the + // same socket after a response is sent. + // This will automatically configure the "Connection" header. + // Defaults to false. + void keepAlive(bool keepAlive) { _keepAlive = keepAlive; } + bool keepAlive() { return _keepAlive; } + static String credentialHash(const String& username, const String& realm, const String& password); static String urlDecode(const String& text); @@ -224,6 +231,7 @@ class ESP8266WebServerTemplate uint8_t _currentVersion; HTTPClientStatus _currentStatus; unsigned long _statusChange; + bool _keepAlive; RequestHandlerType* _currentHandler; RequestHandlerType* _firstHandler; From 747b636921d716fc01b72c7854ef6a47a8cd2c05 Mon Sep 17 00:00:00 2001 From: Zakary Kamal Ismail Date: Fri, 10 Jul 2020 15:31:01 -0400 Subject: [PATCH 2/4] WebServer: Keep the connection alive with a client by default --- libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h b/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h index 80c9611490..3441654d66 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h @@ -320,6 +320,7 @@ void ESP8266WebServerTemplate::handleClient() { _currentClient = client; _currentStatus = HC_WAIT_READ; _statusChange = millis(); + _keepAlive = true; } bool keepCurrentClient = false; @@ -367,7 +368,6 @@ void ESP8266WebServerTemplate::handleClient() { if (!keepCurrentClient) { _currentClient = ClientType(); _currentStatus = HC_NONE; - _keepAlive = false; _currentUpload.reset(); } @@ -380,7 +380,6 @@ template void ESP8266WebServerTemplate::close() { _server.close(); _currentStatus = HC_NONE; - _keepAlive = false; if(!_headerKeysCount) collectHeaders(0, 0); } From 53b18523100f55fdd1ab39606610c31bc2eac3a1 Mon Sep 17 00:00:00 2001 From: Zakary Kamal Ismail Date: Fri, 10 Jul 2020 17:02:32 -0400 Subject: [PATCH 3/4] WebServer: Use the request's HTTP version and Connection header to set the default keep alive value --- libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h | 1 - libraries/ESP8266WebServer/src/ESP8266WebServer.h | 5 +++-- libraries/ESP8266WebServer/src/Parsing-impl.h | 7 +++++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h b/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h index 3441654d66..f064805d02 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h @@ -320,7 +320,6 @@ void ESP8266WebServerTemplate::handleClient() { _currentClient = client; _currentStatus = HC_WAIT_READ; _statusChange = millis(); - _keepAlive = true; } bool keepCurrentClient = false; diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.h b/libraries/ESP8266WebServer/src/ESP8266WebServer.h index 85aa8f69d5..c854dfe485 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.h @@ -170,8 +170,9 @@ class ESP8266WebServerTemplate // Whether other requests should be accepted from the client on the // same socket after a response is sent. - // This will automatically configure the "Connection" header. - // Defaults to false. + // This will automatically configure the "Connection" header of the response. + // Defaults to true when the client's HTTP version is 1.1 or above, otherwise it defaults to false. + // If the client sends the "Connection" header, the value given by the header is used. void keepAlive(bool keepAlive) { _keepAlive = keepAlive; } bool keepAlive() { return _keepAlive; } diff --git a/libraries/ESP8266WebServer/src/Parsing-impl.h b/libraries/ESP8266WebServer/src/Parsing-impl.h index 54bcd6c28e..0fd0e5827c 100644 --- a/libraries/ESP8266WebServer/src/Parsing-impl.h +++ b/libraries/ESP8266WebServer/src/Parsing-impl.h @@ -114,6 +114,9 @@ bool ESP8266WebServerTemplate::_parseRequest(ClientType& client) { } _currentMethod = method; + _keepAlive = _currentVersion > 0; // Keep the connection alive by default + // if the protocol version is greater than HTTP 1.0 + #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("method: "); DEBUG_OUTPUT.print(methodStr); @@ -177,6 +180,8 @@ bool ESP8266WebServerTemplate::_parseRequest(ClientType& client) { contentLength = headerValue.toInt(); } else if (headerName.equalsIgnoreCase(F("Host"))){ _hostHeader = headerValue; + } else if (headerName.equalsIgnoreCase(F("Connection"))){ + _keepAlive = headerValue.equalsIgnoreCase(F("keep-alive")); } } @@ -240,6 +245,8 @@ bool ESP8266WebServerTemplate::_parseRequest(ClientType& client) { if (headerName.equalsIgnoreCase(F("Host"))){ _hostHeader = headerValue; + } else if (headerName.equalsIgnoreCase(F("Connection"))){ + _keepAlive = headerValue.equalsIgnoreCase(F("keep-alive")); } } _parseArguments(searchStr); From afebe47685312c7a1b7a11e4a7f1d721f0ca0ff9 Mon Sep 17 00:00:00 2001 From: Zakary Kamal Ismail Date: Fri, 10 Jul 2020 17:03:03 -0400 Subject: [PATCH 4/4] Fix a typo in a comment --- libraries/ESP8266WebServer/src/Parsing-impl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ESP8266WebServer/src/Parsing-impl.h b/libraries/ESP8266WebServer/src/Parsing-impl.h index 0fd0e5827c..d5f8a4aeb2 100644 --- a/libraries/ESP8266WebServer/src/Parsing-impl.h +++ b/libraries/ESP8266WebServer/src/Parsing-impl.h @@ -147,7 +147,7 @@ bool ESP8266WebServerTemplate::_parseRequest(ClientType& client) { while(1){ req = client.readStringUntil('\r'); client.readStringUntil('\n'); - if (req.isEmpty()) break;//no moar headers + if (req.isEmpty()) break; //no more headers int headerDiv = req.indexOf(':'); if (headerDiv == -1){ break;