diff --git a/doc/esp8266wifi/server-class.rst b/doc/esp8266wifi/server-class.rst index 5603228378..f2f00dfdf5 100644 --- a/doc/esp8266wifi/server-class.rst +++ b/doc/esp8266wifi/server-class.rst @@ -49,6 +49,8 @@ Other Function Calls .. code:: cpp bool hasClient () + size_t hasClientData () + bool hasMaxPendingClients () bool getNoDelay () virtual size_t write (const uint8_t *buf, size_t size) uint8_t status () diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h b/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h index b9ca0fe535..4c900c9bcd 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h @@ -343,11 +343,18 @@ void ESP8266WebServerTemplate::handleClient() { } // switch _parseRequest() } else { // !_currentClient.available(): waiting for more data - if (millis() - _statusChange <= HTTP_MAX_DATA_WAIT) { - keepCurrentClient = true; + unsigned long timeSinceChange = millis() - _statusChange; + // Use faster connection drop timeout if any other client has data + // or the buffer of pending clients is full + if ((_server.hasClientData() || _server.hasMaxPendingClients()) + && timeSinceChange > HTTP_MAX_DATA_AVAILABLE_WAIT) + DBGWS("webserver: closing since there's another connection to read from\n"); + else { + if (timeSinceChange > HTTP_MAX_DATA_WAIT) + DBGWS("webserver: closing after read timeout\n"); + else + keepCurrentClient = true; } - else - DBGWS("webserver: closing after read timeout\n"); callYield = true; } break; diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.h b/libraries/ESP8266WebServer/src/ESP8266WebServer.h index 334612d30a..ae697f4b27 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.h @@ -59,6 +59,7 @@ enum HTTPAuthMethod { BASIC_AUTH, DIGEST_AUTH }; #endif #define HTTP_MAX_DATA_WAIT 5000 //ms to wait for the client to send the request +#define HTTP_MAX_DATA_AVAILABLE_WAIT 30 //ms to wait for the client to send the request when there is another client with data available #define HTTP_MAX_POST_WAIT 5000 //ms to wait for POST data to arrive #define HTTP_MAX_SEND_WAIT 5000 //ms to wait for data chunk to be ACKed #define HTTP_MAX_CLOSE_WAIT 2000 //ms to wait for the client to close the connection diff --git a/libraries/ESP8266WiFi/src/WiFiServer.cpp b/libraries/ESP8266WiFi/src/WiFiServer.cpp index 644f8b45d1..30205e7cc5 100644 --- a/libraries/ESP8266WiFi/src/WiFiServer.cpp +++ b/libraries/ESP8266WiFi/src/WiFiServer.cpp @@ -109,6 +109,25 @@ bool WiFiServer::hasClient() { return false; } +size_t WiFiServer::hasClientData() { + ClientContext *next = _unclaimed; + while (next) { + size_t s = next->getSize(); + // return the amount of data available from the first connection that has any + if (s) return s; + next = next->next(); + } + return 0; +} + +bool WiFiServer::hasMaxPendingClients() { +#if TCP_LISTEN_BACKLOG + return ((struct tcp_pcb_listen *)_listen_pcb)->accepts_pending >= MAX_PENDING_CLIENTS_PER_PORT; +#else + return false; +#endif +} + WiFiClient WiFiServer::available(byte* status) { (void) status; if (_unclaimed) { diff --git a/libraries/ESP8266WiFi/src/WiFiServer.h b/libraries/ESP8266WiFi/src/WiFiServer.h index f061938385..4c69868297 100644 --- a/libraries/ESP8266WiFi/src/WiFiServer.h +++ b/libraries/ESP8266WiFi/src/WiFiServer.h @@ -82,6 +82,13 @@ class WiFiServer : public Server { virtual ~WiFiServer() {} WiFiClient available(uint8_t* status = NULL); bool hasClient(); + // hasClientData(): + // returns the amount of data available from the first client + // or 0 if there is none + size_t hasClientData(); + // hasMaxPendingClients(): + // returns true if the queue of pending clients is full + bool hasMaxPendingClients(); void begin(); void begin(uint16_t port); void begin(uint16_t port, uint8_t backlog); diff --git a/tests/host/common/MockWiFiServerSocket.cpp b/tests/host/common/MockWiFiServerSocket.cpp index 13569b96a2..43b966d92c 100644 --- a/tests/host/common/MockWiFiServerSocket.cpp +++ b/tests/host/common/MockWiFiServerSocket.cpp @@ -99,7 +99,7 @@ void WiFiServer::begin () exit(EXIT_FAILURE); } - server.sin_family = AF_INET; + server.sin_family = AF_INET; server.sin_port = htons(mockport); server.sin_addr.s_addr = htonl(global_source_address); if (bind(sock, (struct sockaddr*)&server, sizeof(server)) == -1) @@ -150,3 +150,23 @@ void WiFiServer::stop () { close(); } + +size_t WiFiServer::hasClientData () +{ + // Trivial Mocking: + // There is no waiting list of clients in this trivial mocking code, + // so the code has to act as if the tcp backlog list is full, + // and nothing is known about potential further clients. + // It could be implemented by accepting new clients and store their data until the current one is closed. + return 0; +} + +bool WiFiServer::hasMaxPendingClients () +{ + // Mocking code does not consider the waiting client list, + // so it will return ::hasClient() here meaning: + // - our waiting client list does not exist + // - we consider pending number is max if a new client is waiting + // or not max if there's no new client. + return hasClient(); +}