1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-06-13 13:01:55 +03:00

ESP8266WebServer - Drop inactive connection when another is waiting to improve page load time (#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).
This commit is contained in:
aWZHY0yQH81uOYvH
2021-10-27 13:28:47 -07:00
committed by GitHub
parent ac4af38c09
commit f8de3fb464
6 changed files with 61 additions and 5 deletions

View File

@ -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 ()

View File

@ -343,11 +343,18 @@ void ESP8266WebServerTemplate<ServerType>::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;

View File

@ -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

View File

@ -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) {

View File

@ -82,6 +82,13 @@ public:
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);

View File

@ -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();
}