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:
@ -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 ()
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
Reference in New Issue
Block a user