Skip to content

Commit cf6ea03

Browse files
aWZHY0yQH81uOYvHhasenradball
authored andcommitted
ESP8266WebServer - Drop inactive connection when another is waiting to improve page load time (esp8266#8216)
* ESP8266WebServer - drop current HC_WAIT_READ connection sooner when another has data Safari sometimes opens two connections when loading a page and only sends a request over the second one, resulting in a 5 second wait (HTTP_MAX_DATA_WAIT) before the request is processed. This commit drops the current connection after 30ms (HTTP_MAX_DATA_AVAILABLE_WAIT) when there is a new connection with data available or the buffer of pending TCP clients is full (currently 5).
1 parent 7025cae commit cf6ea03

File tree

6 files changed

+61
-5
lines changed

6 files changed

+61
-5
lines changed

Diff for: doc/esp8266wifi/server-class.rst

+2
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ Other Function Calls
4949
.. code:: cpp
5050
5151
bool hasClient ()
52+
size_t hasClientData ()
53+
bool hasMaxPendingClients ()
5254
bool getNoDelay ()
5355
virtual size_t write (const uint8_t *buf, size_t size)
5456
uint8_t status ()

Diff for: libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h

+11-4
Original file line numberDiff line numberDiff line change
@@ -343,11 +343,18 @@ void ESP8266WebServerTemplate<ServerType>::handleClient() {
343343
} // switch _parseRequest()
344344
} else {
345345
// !_currentClient.available(): waiting for more data
346-
if (millis() - _statusChange <= HTTP_MAX_DATA_WAIT) {
347-
keepCurrentClient = true;
346+
unsigned long timeSinceChange = millis() - _statusChange;
347+
// Use faster connection drop timeout if any other client has data
348+
// or the buffer of pending clients is full
349+
if ((_server.hasClientData() || _server.hasMaxPendingClients())
350+
&& timeSinceChange > HTTP_MAX_DATA_AVAILABLE_WAIT)
351+
DBGWS("webserver: closing since there's another connection to read from\n");
352+
else {
353+
if (timeSinceChange > HTTP_MAX_DATA_WAIT)
354+
DBGWS("webserver: closing after read timeout\n");
355+
else
356+
keepCurrentClient = true;
348357
}
349-
else
350-
DBGWS("webserver: closing after read timeout\n");
351358
callYield = true;
352359
}
353360
break;

Diff for: libraries/ESP8266WebServer/src/ESP8266WebServer.h

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ enum HTTPAuthMethod { BASIC_AUTH, DIGEST_AUTH };
5959
#endif
6060

6161
#define HTTP_MAX_DATA_WAIT 5000 //ms to wait for the client to send the request
62+
#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
6263
#define HTTP_MAX_POST_WAIT 5000 //ms to wait for POST data to arrive
6364
#define HTTP_MAX_SEND_WAIT 5000 //ms to wait for data chunk to be ACKed
6465
#define HTTP_MAX_CLOSE_WAIT 2000 //ms to wait for the client to close the connection

Diff for: libraries/ESP8266WiFi/src/WiFiServer.cpp

+19
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,25 @@ bool WiFiServer::hasClient() {
109109
return false;
110110
}
111111

112+
size_t WiFiServer::hasClientData() {
113+
ClientContext *next = _unclaimed;
114+
while (next) {
115+
size_t s = next->getSize();
116+
// return the amount of data available from the first connection that has any
117+
if (s) return s;
118+
next = next->next();
119+
}
120+
return 0;
121+
}
122+
123+
bool WiFiServer::hasMaxPendingClients() {
124+
#if TCP_LISTEN_BACKLOG
125+
return ((struct tcp_pcb_listen *)_listen_pcb)->accepts_pending >= MAX_PENDING_CLIENTS_PER_PORT;
126+
#else
127+
return false;
128+
#endif
129+
}
130+
112131
WiFiClient WiFiServer::available(byte* status) {
113132
(void) status;
114133
if (_unclaimed) {

Diff for: libraries/ESP8266WiFi/src/WiFiServer.h

+7
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,13 @@ class WiFiServer : public Server {
8282
virtual ~WiFiServer() {}
8383
WiFiClient available(uint8_t* status = NULL);
8484
bool hasClient();
85+
// hasClientData():
86+
// returns the amount of data available from the first client
87+
// or 0 if there is none
88+
size_t hasClientData();
89+
// hasMaxPendingClients():
90+
// returns true if the queue of pending clients is full
91+
bool hasMaxPendingClients();
8592
void begin();
8693
void begin(uint16_t port);
8794
void begin(uint16_t port, uint8_t backlog);

Diff for: tests/host/common/MockWiFiServerSocket.cpp

+21-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ void WiFiServer::begin ()
9999
exit(EXIT_FAILURE);
100100
}
101101

102-
server.sin_family = AF_INET;
102+
server.sin_family = AF_INET;
103103
server.sin_port = htons(mockport);
104104
server.sin_addr.s_addr = htonl(global_source_address);
105105
if (bind(sock, (struct sockaddr*)&server, sizeof(server)) == -1)
@@ -150,3 +150,23 @@ void WiFiServer::stop ()
150150
{
151151
close();
152152
}
153+
154+
size_t WiFiServer::hasClientData ()
155+
{
156+
// Trivial Mocking:
157+
// There is no waiting list of clients in this trivial mocking code,
158+
// so the code has to act as if the tcp backlog list is full,
159+
// and nothing is known about potential further clients.
160+
// It could be implemented by accepting new clients and store their data until the current one is closed.
161+
return 0;
162+
}
163+
164+
bool WiFiServer::hasMaxPendingClients ()
165+
{
166+
// Mocking code does not consider the waiting client list,
167+
// so it will return ::hasClient() here meaning:
168+
// - our waiting client list does not exist
169+
// - we consider pending number is max if a new client is waiting
170+
// or not max if there's no new client.
171+
return hasClient();
172+
}

0 commit comments

Comments
 (0)