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
|
.. code:: cpp
|
||||||
|
|
||||||
bool hasClient ()
|
bool hasClient ()
|
||||||
|
size_t hasClientData ()
|
||||||
|
bool hasMaxPendingClients ()
|
||||||
bool getNoDelay ()
|
bool getNoDelay ()
|
||||||
virtual size_t write (const uint8_t *buf, size_t size)
|
virtual size_t write (const uint8_t *buf, size_t size)
|
||||||
uint8_t status ()
|
uint8_t status ()
|
||||||
|
@ -343,11 +343,18 @@ void ESP8266WebServerTemplate<ServerType>::handleClient() {
|
|||||||
} // switch _parseRequest()
|
} // switch _parseRequest()
|
||||||
} else {
|
} else {
|
||||||
// !_currentClient.available(): waiting for more data
|
// !_currentClient.available(): waiting for more data
|
||||||
if (millis() - _statusChange <= HTTP_MAX_DATA_WAIT) {
|
unsigned long timeSinceChange = millis() - _statusChange;
|
||||||
keepCurrentClient = true;
|
// 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;
|
callYield = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -59,6 +59,7 @@ enum HTTPAuthMethod { BASIC_AUTH, DIGEST_AUTH };
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define HTTP_MAX_DATA_WAIT 5000 //ms to wait for the client to send the request
|
#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_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_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
|
#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;
|
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) {
|
WiFiClient WiFiServer::available(byte* status) {
|
||||||
(void) status;
|
(void) status;
|
||||||
if (_unclaimed) {
|
if (_unclaimed) {
|
||||||
|
@ -82,6 +82,13 @@ public:
|
|||||||
virtual ~WiFiServer() {}
|
virtual ~WiFiServer() {}
|
||||||
WiFiClient available(uint8_t* status = NULL);
|
WiFiClient available(uint8_t* status = NULL);
|
||||||
bool hasClient();
|
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();
|
||||||
void begin(uint16_t port);
|
void begin(uint16_t port);
|
||||||
void begin(uint16_t port, uint8_t backlog);
|
void begin(uint16_t port, uint8_t backlog);
|
||||||
|
@ -99,7 +99,7 @@ void WiFiServer::begin ()
|
|||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
server.sin_family = AF_INET;
|
server.sin_family = AF_INET;
|
||||||
server.sin_port = htons(mockport);
|
server.sin_port = htons(mockport);
|
||||||
server.sin_addr.s_addr = htonl(global_source_address);
|
server.sin_addr.s_addr = htonl(global_source_address);
|
||||||
if (bind(sock, (struct sockaddr*)&server, sizeof(server)) == -1)
|
if (bind(sock, (struct sockaddr*)&server, sizeof(server)) == -1)
|
||||||
@ -150,3 +150,23 @@ void WiFiServer::stop ()
|
|||||||
{
|
{
|
||||||
close();
|
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