From 6c64759b44c41bac9dd1ad27109362381829e69f Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Wed, 22 Aug 2018 11:35:21 +0200 Subject: [PATCH 01/35] wip --- .../ESP8266WiFi/src/include/ClientContext.h | 47 +++++++++---------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/libraries/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index eb8a286d6d..b124026416 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -427,48 +427,47 @@ class ClientContext return false; } - size_t left = _datasource->available(); - size_t can_send = tcp_sndbuf(_pcb); - if (_pcb->snd_queuelen >= TCP_SND_QUEUELEN) { - can_send = 0; - } - size_t will_send = (can_send < left) ? can_send : left; - DEBUGV(":wr %d %d %d\r\n", will_send, left, _written); - bool need_output = false; - while( will_send && _datasource) { - size_t next_chunk = - will_send > _write_chunk_size ? _write_chunk_size : will_send; - const uint8_t* buf = _datasource->get_buffer(next_chunk); - if (state() == CLOSED) { - need_output = false; +// DEBUGV(":wr %d %d %d\r\n", will_send, left, _written); + bool has_written = false; + while (_datasource) { + if (state() == CLOSED) + return false; + + size_t next_chunk = tcp_sndbuf(_pcb); + if (next_chunk > _datasource->available()) + next_chunk = _datasource->available(); + if (!next_chunk) break; - } - err_t err = tcp_write(_pcb, buf, next_chunk, TCP_WRITE_FLAG_COPY); + const uint8_t* buf = _datasource->get_buffer(next_chunk); + err_t err = tcp_write(_pcb, buf, next_chunk, TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE); DEBUGV(":wrc %d %d %d\r\n", next_chunk, will_send, (int) err); if (err == ERR_OK) { _datasource->release_buffer(buf, next_chunk); _written += next_chunk; - need_output = true; + has_written = true; } else { // ERR_MEM(-1) is a valid error meaning // "come back later". It leaves state() opened break; } - will_send -= next_chunk; } - if( need_output ) { + + if (has_written) + // lwIP: "Find out what we can send and send it" tcp_output(_pcb); - return true; - } - return false; + + return has_written; } void _write_some_from_cb() { + // lwIP needs feeding + _write_some(); + if (_send_waiting == 1) { _send_waiting--; - esp_schedule(); } + esp_schedule(); } err_t _acked(tcp_pcb* pcb, uint16_t len) @@ -592,7 +591,7 @@ class ClientContext DataSource* _datasource = nullptr; size_t _written = 0; - size_t _write_chunk_size = 256; + //size_t _write_chunk_size = 256; uint32_t _timeout_ms = 5000; uint32_t _op_start_time = 0; uint8_t _send_waiting = 0; From 746ec81d7aba07509a8f362487d9fe1fbaffd6e8 Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Wed, 22 Aug 2018 15:28:10 +0200 Subject: [PATCH 02/35] cc wip --- .../ESP8266WiFi/src/include/ClientContext.h | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/libraries/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index b124026416..abe6db5193 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -427,23 +427,26 @@ class ClientContext return false; } -// DEBUGV(":wr %d %d %d\r\n", will_send, left, _written); + DEBUGV(":wr %d %d %d\r\n", will_send, left, _written); bool has_written = false; + while (_datasource) { if (state() == CLOSED) return false; - - size_t next_chunk = tcp_sndbuf(_pcb); - if (next_chunk > _datasource->available()) - next_chunk = _datasource->available(); - if (!next_chunk) + size_t next_chunk_size = std::min((size_t)tcp_sndbuf(_pcb), _datasource->available()); +#if LWIP_VERSION_MAJOR == 1 + if (next_chunk_size > 256) + // phy not quickly emptied by lwip1.4 (could be removed after tests) + next_chunk_size = 256; +#endif + if (!next_chunk_size) break; - const uint8_t* buf = _datasource->get_buffer(next_chunk); - err_t err = tcp_write(_pcb, buf, next_chunk, TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE); - DEBUGV(":wrc %d %d %d\r\n", next_chunk, will_send, (int) err); + const uint8_t* buf = _datasource->get_buffer(next_chunk_size); + err_t err = tcp_write(_pcb, buf, next_chunk_size, TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE); + DEBUGV(":wrc %d %d %d\r\n", next_chunk_size, will_send, (int)err); if (err == ERR_OK) { - _datasource->release_buffer(buf, next_chunk); - _written += next_chunk; + _datasource->release_buffer(buf, next_chunk_size); + _written += next_chunk_size; has_written = true; } else { // ERR_MEM(-1) is a valid error meaning @@ -452,7 +455,7 @@ class ClientContext } } - if (has_written) + if (/*nagle*/0 && has_written) // lwIP: "Find out what we can send and send it" tcp_output(_pcb); From 53c9222d3d80b3776f1d980bfa61e88d9dd54252 Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Wed, 22 Aug 2018 16:54:29 +0200 Subject: [PATCH 03/35] cc wip --- libraries/ESP8266WiFi/src/include/ClientContext.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index abe6db5193..6bf25ed7bf 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -455,7 +455,7 @@ class ClientContext } } - if (/*nagle*/0 && has_written) + if (tcp_nagle_disabled(_pcb) && has_written) // lwIP: "Find out what we can send and send it" tcp_output(_pcb); From 6de50d4c9c3588ee3f6910da64476bb58fc350f8 Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Wed, 22 Aug 2018 17:34:56 +0200 Subject: [PATCH 04/35] cc comments --- libraries/ESP8266WiFi/src/include/ClientContext.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/libraries/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index 6bf25ed7bf..804a25ff20 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -434,14 +434,11 @@ class ClientContext if (state() == CLOSED) return false; size_t next_chunk_size = std::min((size_t)tcp_sndbuf(_pcb), _datasource->available()); -#if LWIP_VERSION_MAJOR == 1 - if (next_chunk_size > 256) - // phy not quickly emptied by lwip1.4 (could be removed after tests) - next_chunk_size = 256; -#endif if (!next_chunk_size) break; const uint8_t* buf = _datasource->get_buffer(next_chunk_size); + // TCP_WRITE_FLAG_MORE to remove PUSH flag from packet + // TCP_WRITE_FLAG_MORE implicitely disables nagle (see lwIP's tcp_out.c) err_t err = tcp_write(_pcb, buf, next_chunk_size, TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE); DEBUGV(":wrc %d %d %d\r\n", next_chunk_size, will_send, (int)err); if (err == ERR_OK) { @@ -456,7 +453,8 @@ class ClientContext } if (tcp_nagle_disabled(_pcb) && has_written) - // lwIP: "Find out what we can send and send it" + // handle nagle manually because of TCP_WRITE_FLAG_MORE + // lwIP's tcp_output: "Find out what we can send and send it" tcp_output(_pcb); return has_written; From 2dd152d22325bebfc8574a1902419b3b2e021abc Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Fri, 24 Aug 2018 10:33:11 +0200 Subject: [PATCH 05/35] wip cc --- .../ESP8266WiFi/src/include/ClientContext.h | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/libraries/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index 804a25ff20..bb44c44bd9 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -437,9 +437,17 @@ class ClientContext if (!next_chunk_size) break; const uint8_t* buf = _datasource->get_buffer(next_chunk_size); - // TCP_WRITE_FLAG_MORE to remove PUSH flag from packet - // TCP_WRITE_FLAG_MORE implicitely disables nagle (see lwIP's tcp_out.c) - err_t err = tcp_write(_pcb, buf, next_chunk_size, TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE); + // TCP_WRITE_FLAG_MORE to remove PUSH flag from packet (lwIP's doc), implicitely disables nagle (see lwIP's tcp_out.c) + // Notes: + // PUSH is for peer, telling to give to user app as soon as received + // PUSH may be set when sender has finished sending a meaningful data block + // Nagle is for delaying local stack, to send less and bigger packets + uint8_t flags = TCP_WRITE_FLAG_COPY; + if (!tcp_nagle_disabled(_pcb)) + // nagle enabled, delayed, buffering, bigger packets + // (When should we use PUSH?) + flags |= TCP_WRITE_FLAG_MORE; + err_t err = tcp_write(_pcb, buf, next_chunk_size, flags); DEBUGV(":wrc %d %d %d\r\n", next_chunk_size, will_send, (int)err); if (err == ERR_OK) { _datasource->release_buffer(buf, next_chunk_size); @@ -452,7 +460,7 @@ class ClientContext } } - if (tcp_nagle_disabled(_pcb) && has_written) + if (has_written && tcp_nagle_disabled(_pcb)) // handle nagle manually because of TCP_WRITE_FLAG_MORE // lwIP's tcp_output: "Find out what we can send and send it" tcp_output(_pcb); @@ -482,14 +490,13 @@ class ClientContext void _consume(size_t size) { + if(_pcb) + tcp_recved(_pcb, size); ptrdiff_t left = _rx_buf->len - _rx_buf_offset - size; if(left > 0) { _rx_buf_offset += size; } else if(!_rx_buf->next) { DEBUGV(":c0 %d, %d\r\n", size, _rx_buf->tot_len); - if(_pcb) { - tcp_recved(_pcb, _rx_buf->len); - } pbuf_free(_rx_buf); _rx_buf = 0; _rx_buf_offset = 0; @@ -499,9 +506,6 @@ class ClientContext _rx_buf = _rx_buf->next; _rx_buf_offset = 0; pbuf_ref(_rx_buf); - if(_pcb) { - tcp_recved(_pcb, head->len); - } pbuf_free(head); } } From f87602af3403509d2bd1692075c462420be4ca55 Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Mon, 27 Aug 2018 19:07:07 +0200 Subject: [PATCH 06/35] +sync, get/set default nodelay, sync --- libraries/ESP8266WiFi/src/WiFiClient.cpp | 25 ++++++++- libraries/ESP8266WiFi/src/WiFiClient.h | 13 ++++- libraries/ESP8266WiFi/src/WiFiServer.cpp | 11 ++-- libraries/ESP8266WiFi/src/WiFiServer.h | 2 +- .../ESP8266WiFi/src/include/ClientContext.h | 54 ++++++++++++------- 5 files changed, 78 insertions(+), 27 deletions(-) diff --git a/libraries/ESP8266WiFi/src/WiFiClient.cpp b/libraries/ESP8266WiFi/src/WiFiClient.cpp index 6be20c1dcb..c6d09aed30 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClient.cpp @@ -42,6 +42,8 @@ extern "C" #include "c_types.h" uint16_t WiFiClient::_localPort = 0; +bool WiFiClient::_defaultNoDelay = false; +bool WiFiClient::_defaultSync = false; template<> WiFiClient* SList::_s_first = 0; @@ -60,6 +62,9 @@ WiFiClient::WiFiClient(ClientContext* client) _timeout = 5000; _client->ref(); WiFiClient::_add(this); + + setSync(_defaultSync); + setNoDelay(_defaultNoDelay); } WiFiClient::~WiFiClient() @@ -91,7 +96,6 @@ WiFiClient& WiFiClient::operator=(const WiFiClient& other) return *this; } - int WiFiClient::connect(const char* host, uint16_t port) { IPAddress remote_addr; @@ -147,6 +151,9 @@ int WiFiClient::connect(IPAddress ip, uint16_t port) return 0; } + setSync(_defaultSync); + setNoDelay(_defaultNoDelay); + return 1; } @@ -156,12 +163,26 @@ void WiFiClient::setNoDelay(bool nodelay) { _client->setNoDelay(nodelay); } -bool WiFiClient::getNoDelay() { +bool WiFiClient::getNoDelay() const { if (!_client) return false; return _client->getNoDelay(); } +void WiFiClient::setSync(bool sync) +{ + if(!_client) + return; + _client->setSync(sync); +} + +bool WiFiClient::getSync() const +{ + if(!_client) + return false; + return _client->getSync(); +} + size_t WiFiClient::availableForWrite () { return _client? _client->availableForWrite(): 0; diff --git a/libraries/ESP8266WiFi/src/WiFiClient.h b/libraries/ESP8266WiFi/src/WiFiClient.h index ac37b181ee..20b4174fe9 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.h +++ b/libraries/ESP8266WiFi/src/WiFiClient.h @@ -76,8 +76,11 @@ class WiFiClient : public Client, public SList { uint16_t remotePort(); IPAddress localIP(); uint16_t localPort(); - bool getNoDelay(); + bool getNoDelay() const; void setNoDelay(bool nodelay); + bool getSync() const; + void setSync(bool sync); + static void setLocalPortStart(uint16_t port) { _localPort = port; } size_t availableForWrite(); @@ -96,6 +99,11 @@ class WiFiClient : public Client, public SList { uint8_t getKeepAliveCount () const; void disableKeepAlive () { keepAlive(0, 0, 0); } + static void setDefaultNoDelay (bool noDelay) { _defaultNoDelay = noDelay; } + static void setDefaultSync (bool sync) { _defaultSync = sync; } + static bool getDefaultNoDelay () { return _defaultNoDelay; } + static bool getDefaultSync () { return _defaultSync; } + protected: static int8_t _s_connected(void* arg, void* tpcb, int8_t err); @@ -106,6 +114,9 @@ class WiFiClient : public Client, public SList { ClientContext* _client; static uint16_t _localPort; + + static bool _defaultNoDelay; + static bool _defaultSync; }; #endif diff --git a/libraries/ESP8266WiFi/src/WiFiServer.cpp b/libraries/ESP8266WiFi/src/WiFiServer.cpp index 16f5bcc754..d1178d52d9 100644 --- a/libraries/ESP8266WiFi/src/WiFiServer.cpp +++ b/libraries/ESP8266WiFi/src/WiFiServer.cpp @@ -88,11 +88,16 @@ void WiFiServer::begin(uint16_t port) { } void WiFiServer::setNoDelay(bool nodelay) { - _noDelay = nodelay; + _noDelay = nodelay? _ndTrue: _ndFalse; } bool WiFiServer::getNoDelay() { - return _noDelay; + switch (_noDelay) + { + case _ndFalse: return false; + case _ndTrue: return true; + default: return WiFiClient::getDefaultNoDelay(); + } } bool WiFiServer::hasClient() { @@ -106,7 +111,7 @@ WiFiClient WiFiServer::available(byte* status) { if (_unclaimed) { WiFiClient result(_unclaimed); _unclaimed = _unclaimed->next(); - result.setNoDelay(_noDelay); + result.setNoDelay(getNoDelay()); DEBUGV("WS:av\r\n"); return result; } diff --git a/libraries/ESP8266WiFi/src/WiFiServer.h b/libraries/ESP8266WiFi/src/WiFiServer.h index ecf251be5a..95e7e5a904 100644 --- a/libraries/ESP8266WiFi/src/WiFiServer.h +++ b/libraries/ESP8266WiFi/src/WiFiServer.h @@ -43,7 +43,7 @@ class WiFiServer : public Server { ClientContext* _unclaimed; ClientContext* _discarded; - bool _noDelay = false; + enum { _ndDefault, _ndFalse, _ndTrue } _noDelay = _ndDefault; public: WiFiServer(IPAddress addr, uint16_t port); diff --git a/libraries/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index bb44c44bd9..3d6c508e03 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -35,7 +35,7 @@ class ClientContext { public: ClientContext(tcp_pcb* pcb, discard_cb_t discard_cb, void* discard_cb_arg) : - _pcb(pcb), _rx_buf(0), _rx_buf_offset(0), _discard_cb(discard_cb), _discard_cb_arg(discard_cb_arg), _refcnt(0), _next(0) + _pcb(pcb), _rx_buf(0), _rx_buf_offset(0), _discard_cb(discard_cb), _discard_cb_arg(discard_cb_arg), _refcnt(0), _next(0), _sync(WiFiClient::getDefaultSync()) { tcp_setprio(pcb, TCP_PRIO_MIN); tcp_arg(pcb, this); @@ -44,7 +44,7 @@ class ClientContext tcp_err(pcb, &_s_error); tcp_poll(pcb, &_s_poll, 1); - // not enabled by default for 2.4.0 + // keep-alive not enabled by default //keepAlive(); } @@ -159,7 +159,7 @@ class ClientContext } } - bool getNoDelay() + bool getNoDelay() const { if(!_pcb) { return false; @@ -172,12 +172,12 @@ class ClientContext _timeout_ms = timeout_ms; } - int getTimeout() + int getTimeout() const { return _timeout_ms; } - uint32_t getRemoteAddress() + uint32_t getRemoteAddress() const { if(!_pcb) { return 0; @@ -186,7 +186,7 @@ class ClientContext return _pcb->remote_ip.addr; } - uint16_t getRemotePort() + uint16_t getRemotePort() const { if(!_pcb) { return 0; @@ -195,7 +195,7 @@ class ClientContext return _pcb->remote_port; } - uint32_t getLocalAddress() + uint32_t getLocalAddress() const { if(!_pcb) { return 0; @@ -204,7 +204,7 @@ class ClientContext return _pcb->local_ip.addr; } - uint16_t getLocalPort() + uint16_t getLocalPort() const { if(!_pcb) { return 0; @@ -257,7 +257,7 @@ class ClientContext return size_read; } - char peek() + char peek() const { if(!_rx_buf) { return 0; @@ -266,7 +266,7 @@ class ClientContext return reinterpret_cast(_rx_buf->payload)[_rx_buf_offset]; } - size_t peekBytes(char *dst, size_t size) + size_t peekBytes(char *dst, size_t size) const { if(!_rx_buf) { return 0; @@ -307,7 +307,7 @@ class ClientContext int tries = 1+ WAIT_TRIES_MS; while (state() == ESTABLISHED && tcp_sndbuf(_pcb) != TCP_SND_BUF && --tries) { - _write_some(); + //_write_some(); delay(1); // esp_ schedule+yield } } @@ -321,7 +321,6 @@ class ClientContext return _pcb->state; } - size_t write(const uint8_t* data, size_t size) { if (!_pcb) { @@ -379,6 +378,16 @@ class ClientContext return isKeepAliveEnabled()? _pcb->keep_cnt: 0; } + bool getSync () const + { + return _sync; + } + + void setSync (bool sync) + { + _sync = sync; + } + protected: bool _is_timeout() @@ -418,6 +427,10 @@ class ClientContext esp_yield(); } while(true); _send_waiting = 0; + + if (_sync) + wait_until_sent(); + return _written; } @@ -442,11 +455,9 @@ class ClientContext // PUSH is for peer, telling to give to user app as soon as received // PUSH may be set when sender has finished sending a meaningful data block // Nagle is for delaying local stack, to send less and bigger packets - uint8_t flags = TCP_WRITE_FLAG_COPY; - if (!tcp_nagle_disabled(_pcb)) - // nagle enabled, delayed, buffering, bigger packets - // (When should we use PUSH?) - flags |= TCP_WRITE_FLAG_MORE; + uint8_t flags = TCP_WRITE_FLAG_MORE; // do not tcp-PuSH (XXX always?) + if (!_sync) + flags |= TCP_WRITE_FLAG_COPY; err_t err = tcp_write(_pcb, buf, next_chunk_size, flags); DEBUGV(":wrc %d %d %d\r\n", next_chunk_size, will_send, (int)err); if (err == ERR_OK) { @@ -460,10 +471,12 @@ class ClientContext } } - if (has_written && tcp_nagle_disabled(_pcb)) + if (has_written && (_sync || tcp_nagle_disabled(_pcb))) + { // handle nagle manually because of TCP_WRITE_FLAG_MORE // lwIP's tcp_output: "Find out what we can send and send it" tcp_output(_pcb); + } return has_written; } @@ -472,7 +485,7 @@ class ClientContext { // lwIP needs feeding _write_some(); - + if (_send_waiting == 1) { _send_waiting--; } @@ -596,7 +609,6 @@ class ClientContext DataSource* _datasource = nullptr; size_t _written = 0; - //size_t _write_chunk_size = 256; uint32_t _timeout_ms = 5000; uint32_t _op_start_time = 0; uint8_t _send_waiting = 0; @@ -604,6 +616,8 @@ class ClientContext int8_t _refcnt; ClientContext* _next; + + bool _sync; }; #endif//CLIENTCONTEXT_H From 6be4b6f118126e2fafde243b044117d26c157da0 Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Tue, 28 Aug 2018 16:30:10 +0200 Subject: [PATCH 07/35] default nodelay=1 --- libraries/ESP8266WiFi/src/WiFiClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ESP8266WiFi/src/WiFiClient.cpp b/libraries/ESP8266WiFi/src/WiFiClient.cpp index c6d09aed30..bafc725605 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClient.cpp @@ -42,7 +42,7 @@ extern "C" #include "c_types.h" uint16_t WiFiClient::_localPort = 0; -bool WiFiClient::_defaultNoDelay = false; +bool WiFiClient::_defaultNoDelay = true; bool WiFiClient::_defaultSync = false; template<> From 8f7fb1e1ec9646af17752a044173ee88f3852e4d Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Tue, 28 Aug 2018 16:59:05 +0200 Subject: [PATCH 08/35] update flush() --- libraries/ESP8266WiFi/src/WiFiClient.cpp | 13 ++++++---- libraries/ESP8266WiFi/src/WiFiClient.h | 13 +++++++--- .../ESP8266WiFi/src/include/ClientContext.h | 26 +++++++++++++------ 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/libraries/ESP8266WiFi/src/WiFiClient.cpp b/libraries/ESP8266WiFi/src/WiFiClient.cpp index bafc725605..67b09aaf03 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClient.cpp @@ -285,18 +285,21 @@ size_t WiFiClient::peekBytes(uint8_t *buffer, size_t length) { return _client->peekBytes((char *)buffer, count); } -void WiFiClient::flush() +bool WiFiClient::flush(int maxWaitMs) { if (_client) - _client->wait_until_sent(); + return !_client || _client->wait_until_sent(maxWaitMs); + return true; } -void WiFiClient::stop() +bool WiFiClient::stop(int maxWaitMs) { if (!_client) - return; + return true; - _client->close(); + bool ok = _client->wait_until_sent(maxWaitMs); + ok &= _client->close() == ERR_OK; + return ok; } uint8_t WiFiClient::connected() diff --git a/libraries/ESP8266WiFi/src/WiFiClient.h b/libraries/ESP8266WiFi/src/WiFiClient.h index 20b4174fe9..aabe60c9c9 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.h +++ b/libraries/ESP8266WiFi/src/WiFiClient.h @@ -28,7 +28,12 @@ #include "IPAddress.h" #include "include/slist.h" -#define WIFICLIENT_MAX_PACKET_SIZE 1460 +#ifndef TCP_MSS +#define TCP_MSS 1460 // lwip1.4 +#endif + +#define WIFICLIENT_MAX_PACKET_SIZE TCP_MSS +#define WIFICLIENT_MAX_FLUSH_WAIT_MS 100 #define TCP_DEFAULT_KEEPALIVE_IDLE_SEC 7200 // 2 hours #define TCP_DEFAULT_KEEPALIVE_INTERVAL_SEC 75 // 75 sec @@ -67,8 +72,10 @@ class WiFiClient : public Client, public SList { size_t peekBytes(char *buffer, size_t length) { return peekBytes((uint8_t *) buffer, length); } - virtual void flush(); - virtual void stop(); + bool flush(int maxWaitMs); + bool stop(int maxWaitMs); + virtual void flush() { flush(WIFICLIENT_MAX_FLUSH_WAIT_MS); } + virtual void stop() { stop(WIFICLIENT_MAX_FLUSH_WAIT_MS); } virtual uint8_t connected(); virtual operator bool(); diff --git a/libraries/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index 3d6c508e03..91ee29ff52 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -296,20 +296,30 @@ class ClientContext _rx_buf_offset = 0; } - void wait_until_sent() + bool wait_until_sent(int max_wait_ms = WIFICLIENT_MAX_FLUSH_WAIT_MS) { - // fix option 1 in - // https://github.com/esp8266/Arduino/pull/3967#pullrequestreview-83451496 - // TODO: option 2 + if (!_pcb) + return; + tcp_output(_pcb); - #define WAIT_TRIES_MS 10 // at most 10ms + // https://github.com/esp8266/Arduino/pull/3967#pullrequestreview-83451496 + // option 1 done + // option 2 / _write_some() not necessary since _datasource is always nullptr here - int tries = 1+ WAIT_TRIES_MS; + max_wait_ms++; - while (state() == ESTABLISHED && tcp_sndbuf(_pcb) != TCP_SND_BUF && --tries) { - //_write_some(); + // wait for peer's acks flushing lwIP's output buffer + while (state() == ESTABLISHED && tcp_sndbuf(_pcb) != TCP_SND_BUF && --max_wait_ms) delay(1); // esp_ schedule+yield + + #ifdef DEBUGV + if (max_wait_ms == 0) { + // wait until sent: timeout + DEBUGV(":wustmo\n"); } + #endif + + return max_wait_ms > 0; } uint8_t state() const From c0bea2b6fde1fd225b09e5e6f87a971fd757eda8 Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Tue, 28 Aug 2018 17:02:14 +0200 Subject: [PATCH 09/35] fix return value --- libraries/ESP8266WiFi/src/include/ClientContext.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index 91ee29ff52..4824a0d51a 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -299,7 +299,8 @@ class ClientContext bool wait_until_sent(int max_wait_ms = WIFICLIENT_MAX_FLUSH_WAIT_MS) { if (!_pcb) - return; + return true; + tcp_output(_pcb); // https://github.com/esp8266/Arduino/pull/3967#pullrequestreview-83451496 From d00ac35123a8c9d9ff7caaf88546d44fdd3b677f Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Tue, 28 Aug 2018 17:34:21 +0200 Subject: [PATCH 10/35] ClientContext: put things together --- libraries/ESP8266WiFi/src/WiFiClient.cpp | 18 ++++----- libraries/ESP8266WiFi/src/WiFiClient.h | 6 +-- .../ESP8266WiFi/src/include/ClientContext.h | 39 +++++++++---------- 3 files changed, 28 insertions(+), 35 deletions(-) diff --git a/libraries/ESP8266WiFi/src/WiFiClient.cpp b/libraries/ESP8266WiFi/src/WiFiClient.cpp index 67b09aaf03..45b1c26561 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClient.cpp @@ -171,14 +171,14 @@ bool WiFiClient::getNoDelay() const { void WiFiClient::setSync(bool sync) { - if(!_client) + if (!_client) return; _client->setSync(sync); } bool WiFiClient::getSync() const { - if(!_client) + if (!_client) return false; return _client->getSync(); } @@ -285,21 +285,19 @@ size_t WiFiClient::peekBytes(uint8_t *buffer, size_t length) { return _client->peekBytes((char *)buffer, count); } -bool WiFiClient::flush(int maxWaitMs) +void WiFiClient::flush() { if (_client) - return !_client || _client->wait_until_sent(maxWaitMs); - return true; + _client->wait_until_sent(); } -bool WiFiClient::stop(int maxWaitMs) +void WiFiClient::stop() { if (!_client) - return true; + return; - bool ok = _client->wait_until_sent(maxWaitMs); - ok &= _client->close() == ERR_OK; - return ok; + _client->wait_until_sent(); + _client->close(); } uint8_t WiFiClient::connected() diff --git a/libraries/ESP8266WiFi/src/WiFiClient.h b/libraries/ESP8266WiFi/src/WiFiClient.h index aabe60c9c9..3f417e69a2 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.h +++ b/libraries/ESP8266WiFi/src/WiFiClient.h @@ -72,10 +72,8 @@ class WiFiClient : public Client, public SList { size_t peekBytes(char *buffer, size_t length) { return peekBytes((uint8_t *) buffer, length); } - bool flush(int maxWaitMs); - bool stop(int maxWaitMs); - virtual void flush() { flush(WIFICLIENT_MAX_FLUSH_WAIT_MS); } - virtual void stop() { stop(WIFICLIENT_MAX_FLUSH_WAIT_MS); } + virtual void flush(); + virtual void stop(); virtual uint8_t connected(); virtual operator bool(); diff --git a/libraries/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index 4824a0d51a..35262ca732 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -296,22 +296,22 @@ class ClientContext _rx_buf_offset = 0; } - bool wait_until_sent(int max_wait_ms = WIFICLIENT_MAX_FLUSH_WAIT_MS) + void wait_until_sent() { - if (!_pcb) - return true; - - tcp_output(_pcb); - // https://github.com/esp8266/Arduino/pull/3967#pullrequestreview-83451496 // option 1 done // option 2 / _write_some() not necessary since _datasource is always nullptr here - max_wait_ms++; + if (!_pcb) + return; + + tcp_output(_pcb); + + int max_wait_ms = WIFICLIENT_MAX_FLUSH_WAIT_MS + 1; // wait for peer's acks flushing lwIP's output buffer while (state() == ESTABLISHED && tcp_sndbuf(_pcb) != TCP_SND_BUF && --max_wait_ms) - delay(1); // esp_ schedule+yield + delay(1); // yields #ifdef DEBUGV if (max_wait_ms == 0) { @@ -319,8 +319,6 @@ class ClientContext DEBUGV(":wustmo\n"); } #endif - - return max_wait_ms > 0; } uint8_t state() const @@ -461,13 +459,15 @@ class ClientContext if (!next_chunk_size) break; const uint8_t* buf = _datasource->get_buffer(next_chunk_size); - // TCP_WRITE_FLAG_MORE to remove PUSH flag from packet (lwIP's doc), implicitely disables nagle (see lwIP's tcp_out.c) + // use TCP_WRITE_FLAG_MORE to remove PUSH flag from packet (lwIP's doc), + // because PUSH implicitely disables nagle (see lwIP's tcp_out.c) // Notes: - // PUSH is for peer, telling to give to user app as soon as received - // PUSH may be set when sender has finished sending a meaningful data block - // Nagle is for delaying local stack, to send less and bigger packets - uint8_t flags = TCP_WRITE_FLAG_MORE; // do not tcp-PuSH (XXX always?) + // PUSH is for peer, telling to give data to user app as soon as received + // PUSH "may be set" when sender has finished sending a meaningful data block + // Nagle is for delaying local data, to send less/bigger packets + uint8_t flags = TCP_WRITE_FLAG_MORE; // do not tcp-PuSH if (!_sync) + // user data will not stay in place when data are sent but not acknowledged flags |= TCP_WRITE_FLAG_COPY; err_t err = tcp_write(_pcb, buf, next_chunk_size, flags); DEBUGV(":wrc %d %d %d\r\n", next_chunk_size, will_send, (int)err); @@ -484,8 +484,8 @@ class ClientContext if (has_written && (_sync || tcp_nagle_disabled(_pcb))) { - // handle nagle manually because of TCP_WRITE_FLAG_MORE - // lwIP's tcp_output: "Find out what we can send and send it" + // handle no-Nagle manually because of TCP_WRITE_FLAG_MORE + // lwIP's tcp_output doc: "Find out what we can send and send it" tcp_output(_pcb); } @@ -494,13 +494,10 @@ class ClientContext void _write_some_from_cb() { - // lwIP needs feeding - _write_some(); - if (_send_waiting == 1) { _send_waiting--; + esp_schedule(); } - esp_schedule(); } err_t _acked(tcp_pcb* pcb, uint16_t len) From 53931c5f8c21e36ced72270a4a9514fd1d1dd64e Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Wed, 22 Aug 2018 11:35:21 +0200 Subject: [PATCH 11/35] wip --- .../ESP8266WiFi/src/include/ClientContext.h | 47 +++++++++---------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/libraries/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index eb8a286d6d..b124026416 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -427,48 +427,47 @@ class ClientContext return false; } - size_t left = _datasource->available(); - size_t can_send = tcp_sndbuf(_pcb); - if (_pcb->snd_queuelen >= TCP_SND_QUEUELEN) { - can_send = 0; - } - size_t will_send = (can_send < left) ? can_send : left; - DEBUGV(":wr %d %d %d\r\n", will_send, left, _written); - bool need_output = false; - while( will_send && _datasource) { - size_t next_chunk = - will_send > _write_chunk_size ? _write_chunk_size : will_send; - const uint8_t* buf = _datasource->get_buffer(next_chunk); - if (state() == CLOSED) { - need_output = false; +// DEBUGV(":wr %d %d %d\r\n", will_send, left, _written); + bool has_written = false; + while (_datasource) { + if (state() == CLOSED) + return false; + + size_t next_chunk = tcp_sndbuf(_pcb); + if (next_chunk > _datasource->available()) + next_chunk = _datasource->available(); + if (!next_chunk) break; - } - err_t err = tcp_write(_pcb, buf, next_chunk, TCP_WRITE_FLAG_COPY); + const uint8_t* buf = _datasource->get_buffer(next_chunk); + err_t err = tcp_write(_pcb, buf, next_chunk, TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE); DEBUGV(":wrc %d %d %d\r\n", next_chunk, will_send, (int) err); if (err == ERR_OK) { _datasource->release_buffer(buf, next_chunk); _written += next_chunk; - need_output = true; + has_written = true; } else { // ERR_MEM(-1) is a valid error meaning // "come back later". It leaves state() opened break; } - will_send -= next_chunk; } - if( need_output ) { + + if (has_written) + // lwIP: "Find out what we can send and send it" tcp_output(_pcb); - return true; - } - return false; + + return has_written; } void _write_some_from_cb() { + // lwIP needs feeding + _write_some(); + if (_send_waiting == 1) { _send_waiting--; - esp_schedule(); } + esp_schedule(); } err_t _acked(tcp_pcb* pcb, uint16_t len) @@ -592,7 +591,7 @@ class ClientContext DataSource* _datasource = nullptr; size_t _written = 0; - size_t _write_chunk_size = 256; + //size_t _write_chunk_size = 256; uint32_t _timeout_ms = 5000; uint32_t _op_start_time = 0; uint8_t _send_waiting = 0; From 81ec0f45621f820200d20fd08e9bded97f47e74b Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Wed, 22 Aug 2018 15:28:10 +0200 Subject: [PATCH 12/35] cc wip --- .../ESP8266WiFi/src/include/ClientContext.h | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/libraries/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index b124026416..abe6db5193 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -427,23 +427,26 @@ class ClientContext return false; } -// DEBUGV(":wr %d %d %d\r\n", will_send, left, _written); + DEBUGV(":wr %d %d %d\r\n", will_send, left, _written); bool has_written = false; + while (_datasource) { if (state() == CLOSED) return false; - - size_t next_chunk = tcp_sndbuf(_pcb); - if (next_chunk > _datasource->available()) - next_chunk = _datasource->available(); - if (!next_chunk) + size_t next_chunk_size = std::min((size_t)tcp_sndbuf(_pcb), _datasource->available()); +#if LWIP_VERSION_MAJOR == 1 + if (next_chunk_size > 256) + // phy not quickly emptied by lwip1.4 (could be removed after tests) + next_chunk_size = 256; +#endif + if (!next_chunk_size) break; - const uint8_t* buf = _datasource->get_buffer(next_chunk); - err_t err = tcp_write(_pcb, buf, next_chunk, TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE); - DEBUGV(":wrc %d %d %d\r\n", next_chunk, will_send, (int) err); + const uint8_t* buf = _datasource->get_buffer(next_chunk_size); + err_t err = tcp_write(_pcb, buf, next_chunk_size, TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE); + DEBUGV(":wrc %d %d %d\r\n", next_chunk_size, will_send, (int)err); if (err == ERR_OK) { - _datasource->release_buffer(buf, next_chunk); - _written += next_chunk; + _datasource->release_buffer(buf, next_chunk_size); + _written += next_chunk_size; has_written = true; } else { // ERR_MEM(-1) is a valid error meaning @@ -452,7 +455,7 @@ class ClientContext } } - if (has_written) + if (/*nagle*/0 && has_written) // lwIP: "Find out what we can send and send it" tcp_output(_pcb); From fbf1dd10b31210206dbbf34acf7191d7f461d920 Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Wed, 22 Aug 2018 16:54:29 +0200 Subject: [PATCH 13/35] cc wip --- libraries/ESP8266WiFi/src/include/ClientContext.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index abe6db5193..6bf25ed7bf 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -455,7 +455,7 @@ class ClientContext } } - if (/*nagle*/0 && has_written) + if (tcp_nagle_disabled(_pcb) && has_written) // lwIP: "Find out what we can send and send it" tcp_output(_pcb); From b4a918f55217787354d02ff7cdcee23c91195aa9 Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Wed, 22 Aug 2018 17:34:56 +0200 Subject: [PATCH 14/35] cc comments --- libraries/ESP8266WiFi/src/include/ClientContext.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/libraries/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index 6bf25ed7bf..804a25ff20 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -434,14 +434,11 @@ class ClientContext if (state() == CLOSED) return false; size_t next_chunk_size = std::min((size_t)tcp_sndbuf(_pcb), _datasource->available()); -#if LWIP_VERSION_MAJOR == 1 - if (next_chunk_size > 256) - // phy not quickly emptied by lwip1.4 (could be removed after tests) - next_chunk_size = 256; -#endif if (!next_chunk_size) break; const uint8_t* buf = _datasource->get_buffer(next_chunk_size); + // TCP_WRITE_FLAG_MORE to remove PUSH flag from packet + // TCP_WRITE_FLAG_MORE implicitely disables nagle (see lwIP's tcp_out.c) err_t err = tcp_write(_pcb, buf, next_chunk_size, TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE); DEBUGV(":wrc %d %d %d\r\n", next_chunk_size, will_send, (int)err); if (err == ERR_OK) { @@ -456,7 +453,8 @@ class ClientContext } if (tcp_nagle_disabled(_pcb) && has_written) - // lwIP: "Find out what we can send and send it" + // handle nagle manually because of TCP_WRITE_FLAG_MORE + // lwIP's tcp_output: "Find out what we can send and send it" tcp_output(_pcb); return has_written; From c14eeef1a3f4ad05803fa156aaa291ae02a63b0d Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Fri, 24 Aug 2018 10:33:11 +0200 Subject: [PATCH 15/35] wip cc --- .../ESP8266WiFi/src/include/ClientContext.h | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/libraries/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index 804a25ff20..bb44c44bd9 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -437,9 +437,17 @@ class ClientContext if (!next_chunk_size) break; const uint8_t* buf = _datasource->get_buffer(next_chunk_size); - // TCP_WRITE_FLAG_MORE to remove PUSH flag from packet - // TCP_WRITE_FLAG_MORE implicitely disables nagle (see lwIP's tcp_out.c) - err_t err = tcp_write(_pcb, buf, next_chunk_size, TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE); + // TCP_WRITE_FLAG_MORE to remove PUSH flag from packet (lwIP's doc), implicitely disables nagle (see lwIP's tcp_out.c) + // Notes: + // PUSH is for peer, telling to give to user app as soon as received + // PUSH may be set when sender has finished sending a meaningful data block + // Nagle is for delaying local stack, to send less and bigger packets + uint8_t flags = TCP_WRITE_FLAG_COPY; + if (!tcp_nagle_disabled(_pcb)) + // nagle enabled, delayed, buffering, bigger packets + // (When should we use PUSH?) + flags |= TCP_WRITE_FLAG_MORE; + err_t err = tcp_write(_pcb, buf, next_chunk_size, flags); DEBUGV(":wrc %d %d %d\r\n", next_chunk_size, will_send, (int)err); if (err == ERR_OK) { _datasource->release_buffer(buf, next_chunk_size); @@ -452,7 +460,7 @@ class ClientContext } } - if (tcp_nagle_disabled(_pcb) && has_written) + if (has_written && tcp_nagle_disabled(_pcb)) // handle nagle manually because of TCP_WRITE_FLAG_MORE // lwIP's tcp_output: "Find out what we can send and send it" tcp_output(_pcb); @@ -482,14 +490,13 @@ class ClientContext void _consume(size_t size) { + if(_pcb) + tcp_recved(_pcb, size); ptrdiff_t left = _rx_buf->len - _rx_buf_offset - size; if(left > 0) { _rx_buf_offset += size; } else if(!_rx_buf->next) { DEBUGV(":c0 %d, %d\r\n", size, _rx_buf->tot_len); - if(_pcb) { - tcp_recved(_pcb, _rx_buf->len); - } pbuf_free(_rx_buf); _rx_buf = 0; _rx_buf_offset = 0; @@ -499,9 +506,6 @@ class ClientContext _rx_buf = _rx_buf->next; _rx_buf_offset = 0; pbuf_ref(_rx_buf); - if(_pcb) { - tcp_recved(_pcb, head->len); - } pbuf_free(head); } } From 238610ee012caedfba0601ec138e6e3bd70b2fce Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Mon, 27 Aug 2018 19:07:07 +0200 Subject: [PATCH 16/35] +sync, get/set default nodelay, sync --- libraries/ESP8266WiFi/src/WiFiClient.cpp | 25 ++++++++- libraries/ESP8266WiFi/src/WiFiClient.h | 13 ++++- libraries/ESP8266WiFi/src/WiFiServer.cpp | 11 ++-- libraries/ESP8266WiFi/src/WiFiServer.h | 2 +- .../ESP8266WiFi/src/include/ClientContext.h | 54 ++++++++++++------- 5 files changed, 78 insertions(+), 27 deletions(-) diff --git a/libraries/ESP8266WiFi/src/WiFiClient.cpp b/libraries/ESP8266WiFi/src/WiFiClient.cpp index 6be20c1dcb..c6d09aed30 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClient.cpp @@ -42,6 +42,8 @@ extern "C" #include "c_types.h" uint16_t WiFiClient::_localPort = 0; +bool WiFiClient::_defaultNoDelay = false; +bool WiFiClient::_defaultSync = false; template<> WiFiClient* SList::_s_first = 0; @@ -60,6 +62,9 @@ WiFiClient::WiFiClient(ClientContext* client) _timeout = 5000; _client->ref(); WiFiClient::_add(this); + + setSync(_defaultSync); + setNoDelay(_defaultNoDelay); } WiFiClient::~WiFiClient() @@ -91,7 +96,6 @@ WiFiClient& WiFiClient::operator=(const WiFiClient& other) return *this; } - int WiFiClient::connect(const char* host, uint16_t port) { IPAddress remote_addr; @@ -147,6 +151,9 @@ int WiFiClient::connect(IPAddress ip, uint16_t port) return 0; } + setSync(_defaultSync); + setNoDelay(_defaultNoDelay); + return 1; } @@ -156,12 +163,26 @@ void WiFiClient::setNoDelay(bool nodelay) { _client->setNoDelay(nodelay); } -bool WiFiClient::getNoDelay() { +bool WiFiClient::getNoDelay() const { if (!_client) return false; return _client->getNoDelay(); } +void WiFiClient::setSync(bool sync) +{ + if(!_client) + return; + _client->setSync(sync); +} + +bool WiFiClient::getSync() const +{ + if(!_client) + return false; + return _client->getSync(); +} + size_t WiFiClient::availableForWrite () { return _client? _client->availableForWrite(): 0; diff --git a/libraries/ESP8266WiFi/src/WiFiClient.h b/libraries/ESP8266WiFi/src/WiFiClient.h index ac37b181ee..20b4174fe9 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.h +++ b/libraries/ESP8266WiFi/src/WiFiClient.h @@ -76,8 +76,11 @@ class WiFiClient : public Client, public SList { uint16_t remotePort(); IPAddress localIP(); uint16_t localPort(); - bool getNoDelay(); + bool getNoDelay() const; void setNoDelay(bool nodelay); + bool getSync() const; + void setSync(bool sync); + static void setLocalPortStart(uint16_t port) { _localPort = port; } size_t availableForWrite(); @@ -96,6 +99,11 @@ class WiFiClient : public Client, public SList { uint8_t getKeepAliveCount () const; void disableKeepAlive () { keepAlive(0, 0, 0); } + static void setDefaultNoDelay (bool noDelay) { _defaultNoDelay = noDelay; } + static void setDefaultSync (bool sync) { _defaultSync = sync; } + static bool getDefaultNoDelay () { return _defaultNoDelay; } + static bool getDefaultSync () { return _defaultSync; } + protected: static int8_t _s_connected(void* arg, void* tpcb, int8_t err); @@ -106,6 +114,9 @@ class WiFiClient : public Client, public SList { ClientContext* _client; static uint16_t _localPort; + + static bool _defaultNoDelay; + static bool _defaultSync; }; #endif diff --git a/libraries/ESP8266WiFi/src/WiFiServer.cpp b/libraries/ESP8266WiFi/src/WiFiServer.cpp index 16f5bcc754..d1178d52d9 100644 --- a/libraries/ESP8266WiFi/src/WiFiServer.cpp +++ b/libraries/ESP8266WiFi/src/WiFiServer.cpp @@ -88,11 +88,16 @@ void WiFiServer::begin(uint16_t port) { } void WiFiServer::setNoDelay(bool nodelay) { - _noDelay = nodelay; + _noDelay = nodelay? _ndTrue: _ndFalse; } bool WiFiServer::getNoDelay() { - return _noDelay; + switch (_noDelay) + { + case _ndFalse: return false; + case _ndTrue: return true; + default: return WiFiClient::getDefaultNoDelay(); + } } bool WiFiServer::hasClient() { @@ -106,7 +111,7 @@ WiFiClient WiFiServer::available(byte* status) { if (_unclaimed) { WiFiClient result(_unclaimed); _unclaimed = _unclaimed->next(); - result.setNoDelay(_noDelay); + result.setNoDelay(getNoDelay()); DEBUGV("WS:av\r\n"); return result; } diff --git a/libraries/ESP8266WiFi/src/WiFiServer.h b/libraries/ESP8266WiFi/src/WiFiServer.h index ecf251be5a..95e7e5a904 100644 --- a/libraries/ESP8266WiFi/src/WiFiServer.h +++ b/libraries/ESP8266WiFi/src/WiFiServer.h @@ -43,7 +43,7 @@ class WiFiServer : public Server { ClientContext* _unclaimed; ClientContext* _discarded; - bool _noDelay = false; + enum { _ndDefault, _ndFalse, _ndTrue } _noDelay = _ndDefault; public: WiFiServer(IPAddress addr, uint16_t port); diff --git a/libraries/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index bb44c44bd9..3d6c508e03 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -35,7 +35,7 @@ class ClientContext { public: ClientContext(tcp_pcb* pcb, discard_cb_t discard_cb, void* discard_cb_arg) : - _pcb(pcb), _rx_buf(0), _rx_buf_offset(0), _discard_cb(discard_cb), _discard_cb_arg(discard_cb_arg), _refcnt(0), _next(0) + _pcb(pcb), _rx_buf(0), _rx_buf_offset(0), _discard_cb(discard_cb), _discard_cb_arg(discard_cb_arg), _refcnt(0), _next(0), _sync(WiFiClient::getDefaultSync()) { tcp_setprio(pcb, TCP_PRIO_MIN); tcp_arg(pcb, this); @@ -44,7 +44,7 @@ class ClientContext tcp_err(pcb, &_s_error); tcp_poll(pcb, &_s_poll, 1); - // not enabled by default for 2.4.0 + // keep-alive not enabled by default //keepAlive(); } @@ -159,7 +159,7 @@ class ClientContext } } - bool getNoDelay() + bool getNoDelay() const { if(!_pcb) { return false; @@ -172,12 +172,12 @@ class ClientContext _timeout_ms = timeout_ms; } - int getTimeout() + int getTimeout() const { return _timeout_ms; } - uint32_t getRemoteAddress() + uint32_t getRemoteAddress() const { if(!_pcb) { return 0; @@ -186,7 +186,7 @@ class ClientContext return _pcb->remote_ip.addr; } - uint16_t getRemotePort() + uint16_t getRemotePort() const { if(!_pcb) { return 0; @@ -195,7 +195,7 @@ class ClientContext return _pcb->remote_port; } - uint32_t getLocalAddress() + uint32_t getLocalAddress() const { if(!_pcb) { return 0; @@ -204,7 +204,7 @@ class ClientContext return _pcb->local_ip.addr; } - uint16_t getLocalPort() + uint16_t getLocalPort() const { if(!_pcb) { return 0; @@ -257,7 +257,7 @@ class ClientContext return size_read; } - char peek() + char peek() const { if(!_rx_buf) { return 0; @@ -266,7 +266,7 @@ class ClientContext return reinterpret_cast(_rx_buf->payload)[_rx_buf_offset]; } - size_t peekBytes(char *dst, size_t size) + size_t peekBytes(char *dst, size_t size) const { if(!_rx_buf) { return 0; @@ -307,7 +307,7 @@ class ClientContext int tries = 1+ WAIT_TRIES_MS; while (state() == ESTABLISHED && tcp_sndbuf(_pcb) != TCP_SND_BUF && --tries) { - _write_some(); + //_write_some(); delay(1); // esp_ schedule+yield } } @@ -321,7 +321,6 @@ class ClientContext return _pcb->state; } - size_t write(const uint8_t* data, size_t size) { if (!_pcb) { @@ -379,6 +378,16 @@ class ClientContext return isKeepAliveEnabled()? _pcb->keep_cnt: 0; } + bool getSync () const + { + return _sync; + } + + void setSync (bool sync) + { + _sync = sync; + } + protected: bool _is_timeout() @@ -418,6 +427,10 @@ class ClientContext esp_yield(); } while(true); _send_waiting = 0; + + if (_sync) + wait_until_sent(); + return _written; } @@ -442,11 +455,9 @@ class ClientContext // PUSH is for peer, telling to give to user app as soon as received // PUSH may be set when sender has finished sending a meaningful data block // Nagle is for delaying local stack, to send less and bigger packets - uint8_t flags = TCP_WRITE_FLAG_COPY; - if (!tcp_nagle_disabled(_pcb)) - // nagle enabled, delayed, buffering, bigger packets - // (When should we use PUSH?) - flags |= TCP_WRITE_FLAG_MORE; + uint8_t flags = TCP_WRITE_FLAG_MORE; // do not tcp-PuSH (XXX always?) + if (!_sync) + flags |= TCP_WRITE_FLAG_COPY; err_t err = tcp_write(_pcb, buf, next_chunk_size, flags); DEBUGV(":wrc %d %d %d\r\n", next_chunk_size, will_send, (int)err); if (err == ERR_OK) { @@ -460,10 +471,12 @@ class ClientContext } } - if (has_written && tcp_nagle_disabled(_pcb)) + if (has_written && (_sync || tcp_nagle_disabled(_pcb))) + { // handle nagle manually because of TCP_WRITE_FLAG_MORE // lwIP's tcp_output: "Find out what we can send and send it" tcp_output(_pcb); + } return has_written; } @@ -472,7 +485,7 @@ class ClientContext { // lwIP needs feeding _write_some(); - + if (_send_waiting == 1) { _send_waiting--; } @@ -596,7 +609,6 @@ class ClientContext DataSource* _datasource = nullptr; size_t _written = 0; - //size_t _write_chunk_size = 256; uint32_t _timeout_ms = 5000; uint32_t _op_start_time = 0; uint8_t _send_waiting = 0; @@ -604,6 +616,8 @@ class ClientContext int8_t _refcnt; ClientContext* _next; + + bool _sync; }; #endif//CLIENTCONTEXT_H From d0036a167a520cbc66ea683a07f93aaa2ee2482f Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Tue, 28 Aug 2018 16:30:10 +0200 Subject: [PATCH 17/35] default nodelay=1 --- libraries/ESP8266WiFi/src/WiFiClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ESP8266WiFi/src/WiFiClient.cpp b/libraries/ESP8266WiFi/src/WiFiClient.cpp index c6d09aed30..bafc725605 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClient.cpp @@ -42,7 +42,7 @@ extern "C" #include "c_types.h" uint16_t WiFiClient::_localPort = 0; -bool WiFiClient::_defaultNoDelay = false; +bool WiFiClient::_defaultNoDelay = true; bool WiFiClient::_defaultSync = false; template<> From e4c187b78ddec87e4d9c496683d730543cacc860 Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Tue, 28 Aug 2018 16:59:05 +0200 Subject: [PATCH 18/35] update flush() --- libraries/ESP8266WiFi/src/WiFiClient.cpp | 13 ++++++---- libraries/ESP8266WiFi/src/WiFiClient.h | 13 +++++++--- .../ESP8266WiFi/src/include/ClientContext.h | 26 +++++++++++++------ 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/libraries/ESP8266WiFi/src/WiFiClient.cpp b/libraries/ESP8266WiFi/src/WiFiClient.cpp index bafc725605..67b09aaf03 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClient.cpp @@ -285,18 +285,21 @@ size_t WiFiClient::peekBytes(uint8_t *buffer, size_t length) { return _client->peekBytes((char *)buffer, count); } -void WiFiClient::flush() +bool WiFiClient::flush(int maxWaitMs) { if (_client) - _client->wait_until_sent(); + return !_client || _client->wait_until_sent(maxWaitMs); + return true; } -void WiFiClient::stop() +bool WiFiClient::stop(int maxWaitMs) { if (!_client) - return; + return true; - _client->close(); + bool ok = _client->wait_until_sent(maxWaitMs); + ok &= _client->close() == ERR_OK; + return ok; } uint8_t WiFiClient::connected() diff --git a/libraries/ESP8266WiFi/src/WiFiClient.h b/libraries/ESP8266WiFi/src/WiFiClient.h index 20b4174fe9..aabe60c9c9 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.h +++ b/libraries/ESP8266WiFi/src/WiFiClient.h @@ -28,7 +28,12 @@ #include "IPAddress.h" #include "include/slist.h" -#define WIFICLIENT_MAX_PACKET_SIZE 1460 +#ifndef TCP_MSS +#define TCP_MSS 1460 // lwip1.4 +#endif + +#define WIFICLIENT_MAX_PACKET_SIZE TCP_MSS +#define WIFICLIENT_MAX_FLUSH_WAIT_MS 100 #define TCP_DEFAULT_KEEPALIVE_IDLE_SEC 7200 // 2 hours #define TCP_DEFAULT_KEEPALIVE_INTERVAL_SEC 75 // 75 sec @@ -67,8 +72,10 @@ class WiFiClient : public Client, public SList { size_t peekBytes(char *buffer, size_t length) { return peekBytes((uint8_t *) buffer, length); } - virtual void flush(); - virtual void stop(); + bool flush(int maxWaitMs); + bool stop(int maxWaitMs); + virtual void flush() { flush(WIFICLIENT_MAX_FLUSH_WAIT_MS); } + virtual void stop() { stop(WIFICLIENT_MAX_FLUSH_WAIT_MS); } virtual uint8_t connected(); virtual operator bool(); diff --git a/libraries/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index 3d6c508e03..91ee29ff52 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -296,20 +296,30 @@ class ClientContext _rx_buf_offset = 0; } - void wait_until_sent() + bool wait_until_sent(int max_wait_ms = WIFICLIENT_MAX_FLUSH_WAIT_MS) { - // fix option 1 in - // https://github.com/esp8266/Arduino/pull/3967#pullrequestreview-83451496 - // TODO: option 2 + if (!_pcb) + return; + tcp_output(_pcb); - #define WAIT_TRIES_MS 10 // at most 10ms + // https://github.com/esp8266/Arduino/pull/3967#pullrequestreview-83451496 + // option 1 done + // option 2 / _write_some() not necessary since _datasource is always nullptr here - int tries = 1+ WAIT_TRIES_MS; + max_wait_ms++; - while (state() == ESTABLISHED && tcp_sndbuf(_pcb) != TCP_SND_BUF && --tries) { - //_write_some(); + // wait for peer's acks flushing lwIP's output buffer + while (state() == ESTABLISHED && tcp_sndbuf(_pcb) != TCP_SND_BUF && --max_wait_ms) delay(1); // esp_ schedule+yield + + #ifdef DEBUGV + if (max_wait_ms == 0) { + // wait until sent: timeout + DEBUGV(":wustmo\n"); } + #endif + + return max_wait_ms > 0; } uint8_t state() const From d31d6f2eca0d7b29b29febf9da0d87b535e4090c Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Tue, 28 Aug 2018 17:02:14 +0200 Subject: [PATCH 19/35] fix return value --- libraries/ESP8266WiFi/src/include/ClientContext.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index 91ee29ff52..4824a0d51a 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -299,7 +299,8 @@ class ClientContext bool wait_until_sent(int max_wait_ms = WIFICLIENT_MAX_FLUSH_WAIT_MS) { if (!_pcb) - return; + return true; + tcp_output(_pcb); // https://github.com/esp8266/Arduino/pull/3967#pullrequestreview-83451496 From 5cf60b6e9a8ed439ec9aa8162907ec0e501332b2 Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Tue, 28 Aug 2018 17:34:21 +0200 Subject: [PATCH 20/35] ClientContext: put things together --- libraries/ESP8266WiFi/src/WiFiClient.cpp | 18 ++++----- libraries/ESP8266WiFi/src/WiFiClient.h | 6 +-- .../ESP8266WiFi/src/include/ClientContext.h | 39 +++++++++---------- 3 files changed, 28 insertions(+), 35 deletions(-) diff --git a/libraries/ESP8266WiFi/src/WiFiClient.cpp b/libraries/ESP8266WiFi/src/WiFiClient.cpp index 67b09aaf03..45b1c26561 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClient.cpp @@ -171,14 +171,14 @@ bool WiFiClient::getNoDelay() const { void WiFiClient::setSync(bool sync) { - if(!_client) + if (!_client) return; _client->setSync(sync); } bool WiFiClient::getSync() const { - if(!_client) + if (!_client) return false; return _client->getSync(); } @@ -285,21 +285,19 @@ size_t WiFiClient::peekBytes(uint8_t *buffer, size_t length) { return _client->peekBytes((char *)buffer, count); } -bool WiFiClient::flush(int maxWaitMs) +void WiFiClient::flush() { if (_client) - return !_client || _client->wait_until_sent(maxWaitMs); - return true; + _client->wait_until_sent(); } -bool WiFiClient::stop(int maxWaitMs) +void WiFiClient::stop() { if (!_client) - return true; + return; - bool ok = _client->wait_until_sent(maxWaitMs); - ok &= _client->close() == ERR_OK; - return ok; + _client->wait_until_sent(); + _client->close(); } uint8_t WiFiClient::connected() diff --git a/libraries/ESP8266WiFi/src/WiFiClient.h b/libraries/ESP8266WiFi/src/WiFiClient.h index aabe60c9c9..3f417e69a2 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.h +++ b/libraries/ESP8266WiFi/src/WiFiClient.h @@ -72,10 +72,8 @@ class WiFiClient : public Client, public SList { size_t peekBytes(char *buffer, size_t length) { return peekBytes((uint8_t *) buffer, length); } - bool flush(int maxWaitMs); - bool stop(int maxWaitMs); - virtual void flush() { flush(WIFICLIENT_MAX_FLUSH_WAIT_MS); } - virtual void stop() { stop(WIFICLIENT_MAX_FLUSH_WAIT_MS); } + virtual void flush(); + virtual void stop(); virtual uint8_t connected(); virtual operator bool(); diff --git a/libraries/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index 4824a0d51a..35262ca732 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -296,22 +296,22 @@ class ClientContext _rx_buf_offset = 0; } - bool wait_until_sent(int max_wait_ms = WIFICLIENT_MAX_FLUSH_WAIT_MS) + void wait_until_sent() { - if (!_pcb) - return true; - - tcp_output(_pcb); - // https://github.com/esp8266/Arduino/pull/3967#pullrequestreview-83451496 // option 1 done // option 2 / _write_some() not necessary since _datasource is always nullptr here - max_wait_ms++; + if (!_pcb) + return; + + tcp_output(_pcb); + + int max_wait_ms = WIFICLIENT_MAX_FLUSH_WAIT_MS + 1; // wait for peer's acks flushing lwIP's output buffer while (state() == ESTABLISHED && tcp_sndbuf(_pcb) != TCP_SND_BUF && --max_wait_ms) - delay(1); // esp_ schedule+yield + delay(1); // yields #ifdef DEBUGV if (max_wait_ms == 0) { @@ -319,8 +319,6 @@ class ClientContext DEBUGV(":wustmo\n"); } #endif - - return max_wait_ms > 0; } uint8_t state() const @@ -461,13 +459,15 @@ class ClientContext if (!next_chunk_size) break; const uint8_t* buf = _datasource->get_buffer(next_chunk_size); - // TCP_WRITE_FLAG_MORE to remove PUSH flag from packet (lwIP's doc), implicitely disables nagle (see lwIP's tcp_out.c) + // use TCP_WRITE_FLAG_MORE to remove PUSH flag from packet (lwIP's doc), + // because PUSH implicitely disables nagle (see lwIP's tcp_out.c) // Notes: - // PUSH is for peer, telling to give to user app as soon as received - // PUSH may be set when sender has finished sending a meaningful data block - // Nagle is for delaying local stack, to send less and bigger packets - uint8_t flags = TCP_WRITE_FLAG_MORE; // do not tcp-PuSH (XXX always?) + // PUSH is for peer, telling to give data to user app as soon as received + // PUSH "may be set" when sender has finished sending a meaningful data block + // Nagle is for delaying local data, to send less/bigger packets + uint8_t flags = TCP_WRITE_FLAG_MORE; // do not tcp-PuSH if (!_sync) + // user data will not stay in place when data are sent but not acknowledged flags |= TCP_WRITE_FLAG_COPY; err_t err = tcp_write(_pcb, buf, next_chunk_size, flags); DEBUGV(":wrc %d %d %d\r\n", next_chunk_size, will_send, (int)err); @@ -484,8 +484,8 @@ class ClientContext if (has_written && (_sync || tcp_nagle_disabled(_pcb))) { - // handle nagle manually because of TCP_WRITE_FLAG_MORE - // lwIP's tcp_output: "Find out what we can send and send it" + // handle no-Nagle manually because of TCP_WRITE_FLAG_MORE + // lwIP's tcp_output doc: "Find out what we can send and send it" tcp_output(_pcb); } @@ -494,13 +494,10 @@ class ClientContext void _write_some_from_cb() { - // lwIP needs feeding - _write_some(); - if (_send_waiting == 1) { _send_waiting--; + esp_schedule(); } - esp_schedule(); } err_t _acked(tcp_pcb* pcb, uint16_t len) From a058506b17e770f479073860037122fb4831c1cc Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Sun, 16 Sep 2018 18:25:39 -0700 Subject: [PATCH 21/35] Move SSLContext to its own header (#5121) Simple refactor to make WiFiClientSecureAxTLS use an external header to define its SSLContext, just as it does for several other classes. Fixes #3648 --- .../ESP8266WiFi/src/WiFiClientSecureAxTLS.cpp | 411 +--------------- .../ESP8266WiFi/src/include/SSLContext.h | 440 ++++++++++++++++++ 2 files changed, 444 insertions(+), 407 deletions(-) create mode 100644 libraries/ESP8266WiFi/src/include/SSLContext.h diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureAxTLS.cpp b/libraries/ESP8266WiFi/src/WiFiClientSecureAxTLS.cpp index c8c083587c..d200d51e59 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureAxTLS.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureAxTLS.cpp @@ -22,24 +22,10 @@ #define LWIP_INTERNAL -extern "C" -{ -#include "osapi.h" -#include "ets_sys.h" -} -#include -#include #include "debug.h" #include "ESP8266WiFi.h" #include "WiFiClientSecure.h" #include "WiFiClient.h" -#include "lwip/opt.h" -#include "lwip/ip.h" -#include "lwip/tcp.h" -#include "lwip/inet.h" -#include "lwip/netif.h" -#include "include/ClientContext.h" -#include "c_types.h" #ifdef DEBUG_ESP_SSL #define DEBUG_SSL @@ -51,401 +37,12 @@ extern "C" #define SSL_DEBUG_OPTS 0 #endif -namespace axTLS { - - -typedef struct BufferItem -{ - BufferItem(const uint8_t* data_, size_t size_) - : size(size_), data(new uint8_t[size]) - { - if (data.get() != nullptr) { - memcpy(data.get(), data_, size); - } else { - DEBUGV(":wcs alloc %d failed\r\n", size_); - size = 0; - } - } - - size_t size; - std::unique_ptr data; -} BufferItem; - -typedef std::list BufferList; - -class SSLContext -{ -public: - SSLContext(bool isServer = false) - { - _isServer = isServer; - if (!_isServer) { - if (_ssl_client_ctx_refcnt == 0) { - _ssl_client_ctx = ssl_ctx_new(SSL_SERVER_VERIFY_LATER | SSL_DEBUG_OPTS | SSL_CONNECT_IN_PARTS | SSL_READ_BLOCKING | SSL_NO_DEFAULT_KEY, 0); - } - ++_ssl_client_ctx_refcnt; - } else { - if (_ssl_svr_ctx_refcnt == 0) { - _ssl_svr_ctx = ssl_ctx_new(SSL_SERVER_VERIFY_LATER | SSL_DEBUG_OPTS | SSL_CONNECT_IN_PARTS | SSL_READ_BLOCKING | SSL_NO_DEFAULT_KEY, 0); - } - ++_ssl_svr_ctx_refcnt; - } - } - - ~SSLContext() - { - if (io_ctx) { - io_ctx->unref(); - io_ctx = nullptr; - } - _ssl = nullptr; - if (!_isServer) { - --_ssl_client_ctx_refcnt; - if (_ssl_client_ctx_refcnt == 0) { - ssl_ctx_free(_ssl_client_ctx); - _ssl_client_ctx = nullptr; - } - } else { - --_ssl_svr_ctx_refcnt; - if (_ssl_svr_ctx_refcnt == 0) { - ssl_ctx_free(_ssl_svr_ctx); - _ssl_svr_ctx = nullptr; - } - } - } - - static void _delete_shared_SSL(SSL *_to_del) - { - ssl_free(_to_del); - } - - void connect(ClientContext* ctx, const char* hostName, uint32_t timeout_ms) - { - SSL_EXTENSIONS* ext = ssl_ext_new(); - ssl_ext_set_host_name(ext, hostName); - if (_ssl) { - /* Creating a new TLS session on top of a new TCP connection. - ssl_free will want to send a close notify alert, but the old TCP connection - is already gone at this point, so reset io_ctx. */ - io_ctx = nullptr; - _ssl = nullptr; - _available = 0; - _read_ptr = nullptr; - } - io_ctx = ctx; - ctx->ref(); - - // Wrap the new SSL with a smart pointer, custom deleter to call ssl_free - SSL *_new_ssl = ssl_client_new(_ssl_client_ctx, reinterpret_cast(this), nullptr, 0, ext); - std::shared_ptr _new_ssl_shared(_new_ssl, _delete_shared_SSL); - _ssl = _new_ssl_shared; - - uint32_t t = millis(); - - while (millis() - t < timeout_ms && ssl_handshake_status(_ssl.get()) != SSL_OK) { - uint8_t* data; - int rc = ssl_read(_ssl.get(), &data); - if (rc < SSL_OK) { - ssl_display_error(rc); - break; - } - } - } - - void connectServer(ClientContext *ctx, uint32_t timeout_ms) - { - io_ctx = ctx; - ctx->ref(); - - // Wrap the new SSL with a smart pointer, custom deleter to call ssl_free - SSL *_new_ssl = ssl_server_new(_ssl_svr_ctx, reinterpret_cast(this)); - std::shared_ptr _new_ssl_shared(_new_ssl, _delete_shared_SSL); - _ssl = _new_ssl_shared; - - uint32_t t = millis(); - - while (millis() - t < timeout_ms && ssl_handshake_status(_ssl.get()) != SSL_OK) { - uint8_t* data; - int rc = ssl_read(_ssl.get(), &data); - if (rc < SSL_OK) { - ssl_display_error(rc); - break; - } - } - } - void stop() - { - if (io_ctx) { - io_ctx->unref(); - } - io_ctx = nullptr; - } - - bool connected() - { - if (_isServer) { - return _ssl != nullptr; - } else { - return _ssl != nullptr && ssl_handshake_status(_ssl.get()) == SSL_OK; - } - } - - int read(uint8_t* dst, size_t size) - { - if (!_available) { - if (!_readAll()) { - return 0; - } - } - size_t will_copy = (_available < size) ? _available : size; - memcpy(dst, _read_ptr, will_copy); - _read_ptr += will_copy; - _available -= will_copy; - if (_available == 0) { - _read_ptr = nullptr; - /* Send pending outgoing data, if any */ - if (_hasWriteBuffers()) { - _writeBuffersSend(); - } - } - return will_copy; - } - - int read() - { - if (!_available) { - if (!_readAll()) { - return -1; - } - } - int result = _read_ptr[0]; - ++_read_ptr; - --_available; - if (_available == 0) { - _read_ptr = nullptr; - /* Send pending outgoing data, if any */ - if (_hasWriteBuffers()) { - _writeBuffersSend(); - } - } - return result; - } - - int write(const uint8_t* src, size_t size) - { - if (_isServer) { - return _write(src, size); - } else if (!_available) { - if (_hasWriteBuffers()) { - int rc = _writeBuffersSend(); - if (rc < 0) { - return rc; - } - } - return _write(src, size); - } - /* Some received data is still present in the axtls fragment buffer. - We can't call ssl_write now, as that will overwrite the contents of - the fragment buffer, corrupting the received data. - Save a copy of the outgoing data, and call ssl_write when all - recevied data has been consumed by the application. - */ - return _writeBufferAdd(src, size); - } - - int peek() - { - if (!_available) { - if (!_readAll()) { - return -1; - } - } - return _read_ptr[0]; - } - - size_t peekBytes(char *dst, size_t size) - { - if (!_available) { - if (!_readAll()) { - return -1; - } - } - - size_t will_copy = (_available < size) ? _available : size; - memcpy(dst, _read_ptr, will_copy); - return will_copy; - } - - int available() - { - auto cb = _available; - if (cb == 0) { - cb = _readAll(); - } else { - optimistic_yield(100); - } - return cb; - } - - // similar to available, but doesn't return exact size - bool hasData() - { - return _available > 0 || (io_ctx && io_ctx->getSize() > 0); - } - - bool loadObject(int type, Stream& stream, size_t size) - { - std::unique_ptr buf(new uint8_t[size]); - if (!buf.get()) { - DEBUGV("loadObject: failed to allocate memory\n"); - return false; - } - - size_t cb = stream.readBytes(buf.get(), size); - if (cb != size) { - DEBUGV("loadObject: reading %u bytes, got %u\n", size, cb); - return false; - } - - return loadObject(type, buf.get(), size); - } - - bool loadObject_P(int type, PGM_VOID_P data, size_t size) - { - std::unique_ptr buf(new uint8_t[size]); - memcpy_P(buf.get(),data, size); - return loadObject(type, buf.get(), size); - } - - bool loadObject(int type, const uint8_t* data, size_t size) - { - int rc = ssl_obj_memory_load(_isServer?_ssl_svr_ctx:_ssl_client_ctx, type, data, static_cast(size), nullptr); - if (rc != SSL_OK) { - DEBUGV("loadObject: ssl_obj_memory_load returned %d\n", rc); - return false; - } - return true; - } - - bool verifyCert() - { - int rc = ssl_verify_cert(_ssl.get()); - if (_allowSelfSignedCerts && rc == SSL_X509_ERROR(X509_VFY_ERROR_SELF_SIGNED)) { - DEBUGV("Allowing self-signed certificate\n"); - return true; - } else if (rc != SSL_OK) { - DEBUGV("ssl_verify_cert returned %d\n", rc); - ssl_display_error(rc); - return false; - } - return true; - } - - void allowSelfSignedCerts() - { - _allowSelfSignedCerts = true; - } - - operator SSL*() - { - return _ssl.get(); - } - - static ClientContext* getIOContext(int fd) - { - if (fd) { - SSLContext *thisSSL = reinterpret_cast(fd); - return thisSSL->io_ctx; - } - return nullptr; - } - -protected: - int _readAll() - { - if (!_ssl) { - return 0; - } - - optimistic_yield(100); - - uint8_t* data; - int rc = ssl_read(_ssl.get(), &data); - if (rc <= 0) { - if (rc < SSL_OK && rc != SSL_CLOSE_NOTIFY && rc != SSL_ERROR_CONN_LOST) { - _ssl = nullptr; - } - return 0; - } - DEBUGV(":wcs ra %d\r\n", rc); - _read_ptr = data; - _available = rc; - return _available; - } - - int _write(const uint8_t* src, size_t size) - { - if (!_ssl) { - return 0; - } - - int rc = ssl_write(_ssl.get(), src, size); - if (rc >= 0) { - return rc; - } - DEBUGV(":wcs write rc=%d\r\n", rc); - return rc; - } - - int _writeBufferAdd(const uint8_t* data, size_t size) - { - if (!_ssl) { - return 0; - } - - _writeBuffers.emplace_back(data, size); - if (_writeBuffers.back().data.get() == nullptr) { - _writeBuffers.pop_back(); - return 0; - } - return size; - } - - int _writeBuffersSend() - { - while (!_writeBuffers.empty()) { - auto& first = _writeBuffers.front(); - int rc = _write(first.data.get(), first.size); - _writeBuffers.pop_front(); - if (rc < 0) { - if (_hasWriteBuffers()) { - DEBUGV(":wcs _writeBuffersSend dropping unsent data\r\n"); - _writeBuffers.clear(); - } - return rc; - } - } - return 0; - } +// Pull in required classes +#include "include/SSLContext.h" +#include "include/ClientContext.h" - bool _hasWriteBuffers() - { - return !_writeBuffers.empty(); - } - - bool _isServer = false; - static SSL_CTX* _ssl_client_ctx; - static int _ssl_client_ctx_refcnt; - static SSL_CTX* _ssl_svr_ctx; - static int _ssl_svr_ctx_refcnt; - std::shared_ptr _ssl = nullptr; - const uint8_t* _read_ptr = nullptr; - size_t _available = 0; - BufferList _writeBuffers; - bool _allowSelfSignedCerts = false; - ClientContext* io_ctx = nullptr; -}; +namespace axTLS { SSL_CTX* SSLContext::_ssl_client_ctx = nullptr; int SSLContext::_ssl_client_ctx_refcnt = 0; diff --git a/libraries/ESP8266WiFi/src/include/SSLContext.h b/libraries/ESP8266WiFi/src/include/SSLContext.h new file mode 100644 index 0000000000..4e8cfe11f0 --- /dev/null +++ b/libraries/ESP8266WiFi/src/include/SSLContext.h @@ -0,0 +1,440 @@ +/* + SSLContext.h - Used by WiFiClientAxTLS + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef SSLCONTEXT_H +#define SSLCONTEXT_H + +#define LWIP_INTERNAL + +extern "C" +{ +#include "osapi.h" +#include "ets_sys.h" +} +#include +#include +#include "lwip/opt.h" +#include "lwip/ip.h" +#include "lwip/tcp.h" +#include "lwip/inet.h" +#include "lwip/netif.h" +#include "include/ClientContext.h" +#include "c_types.h" + +namespace axTLS { + +typedef struct BufferItem +{ + BufferItem(const uint8_t* data_, size_t size_) + : size(size_), data(new uint8_t[size]) + { + if (data.get() != nullptr) { + memcpy(data.get(), data_, size); + } else { + DEBUGV(":wcs alloc %d failed\r\n", size_); + size = 0; + } + } + + size_t size; + std::unique_ptr data; +} BufferItem; + +typedef std::list BufferList; + +class SSLContext +{ +public: + SSLContext(bool isServer = false) + { + _isServer = isServer; + if (!_isServer) { + if (_ssl_client_ctx_refcnt == 0) { + _ssl_client_ctx = ssl_ctx_new(SSL_SERVER_VERIFY_LATER | SSL_DEBUG_OPTS | SSL_CONNECT_IN_PARTS | SSL_READ_BLOCKING | SSL_NO_DEFAULT_KEY, 0); + } + ++_ssl_client_ctx_refcnt; + } else { + if (_ssl_svr_ctx_refcnt == 0) { + _ssl_svr_ctx = ssl_ctx_new(SSL_SERVER_VERIFY_LATER | SSL_DEBUG_OPTS | SSL_CONNECT_IN_PARTS | SSL_READ_BLOCKING | SSL_NO_DEFAULT_KEY, 0); + } + ++_ssl_svr_ctx_refcnt; + } + } + + ~SSLContext() + { + if (io_ctx) { + io_ctx->unref(); + io_ctx = nullptr; + } + _ssl = nullptr; + if (!_isServer) { + --_ssl_client_ctx_refcnt; + if (_ssl_client_ctx_refcnt == 0) { + ssl_ctx_free(_ssl_client_ctx); + _ssl_client_ctx = nullptr; + } + } else { + --_ssl_svr_ctx_refcnt; + if (_ssl_svr_ctx_refcnt == 0) { + ssl_ctx_free(_ssl_svr_ctx); + _ssl_svr_ctx = nullptr; + } + } + } + + static void _delete_shared_SSL(SSL *_to_del) + { + ssl_free(_to_del); + } + + void connect(ClientContext* ctx, const char* hostName, uint32_t timeout_ms) + { + SSL_EXTENSIONS* ext = ssl_ext_new(); + ssl_ext_set_host_name(ext, hostName); + if (_ssl) { + /* Creating a new TLS session on top of a new TCP connection. + ssl_free will want to send a close notify alert, but the old TCP connection + is already gone at this point, so reset io_ctx. */ + io_ctx = nullptr; + _ssl = nullptr; + _available = 0; + _read_ptr = nullptr; + } + io_ctx = ctx; + ctx->ref(); + + // Wrap the new SSL with a smart pointer, custom deleter to call ssl_free + SSL *_new_ssl = ssl_client_new(_ssl_client_ctx, reinterpret_cast(this), nullptr, 0, ext); + std::shared_ptr _new_ssl_shared(_new_ssl, _delete_shared_SSL); + _ssl = _new_ssl_shared; + + uint32_t t = millis(); + + while (millis() - t < timeout_ms && ssl_handshake_status(_ssl.get()) != SSL_OK) { + uint8_t* data; + int rc = ssl_read(_ssl.get(), &data); + if (rc < SSL_OK) { + ssl_display_error(rc); + break; + } + } + } + + void connectServer(ClientContext *ctx, uint32_t timeout_ms) + { + io_ctx = ctx; + ctx->ref(); + + // Wrap the new SSL with a smart pointer, custom deleter to call ssl_free + SSL *_new_ssl = ssl_server_new(_ssl_svr_ctx, reinterpret_cast(this)); + std::shared_ptr _new_ssl_shared(_new_ssl, _delete_shared_SSL); + _ssl = _new_ssl_shared; + + uint32_t t = millis(); + + while (millis() - t < timeout_ms && ssl_handshake_status(_ssl.get()) != SSL_OK) { + uint8_t* data; + int rc = ssl_read(_ssl.get(), &data); + if (rc < SSL_OK) { + ssl_display_error(rc); + break; + } + } + } + + void stop() + { + if (io_ctx) { + io_ctx->unref(); + } + io_ctx = nullptr; + } + + bool connected() + { + if (_isServer) { + return _ssl != nullptr; + } else { + return _ssl != nullptr && ssl_handshake_status(_ssl.get()) == SSL_OK; + } + } + + int read(uint8_t* dst, size_t size) + { + if (!_available) { + if (!_readAll()) { + return 0; + } + } + size_t will_copy = (_available < size) ? _available : size; + memcpy(dst, _read_ptr, will_copy); + _read_ptr += will_copy; + _available -= will_copy; + if (_available == 0) { + _read_ptr = nullptr; + /* Send pending outgoing data, if any */ + if (_hasWriteBuffers()) { + _writeBuffersSend(); + } + } + return will_copy; + } + + int read() + { + if (!_available) { + if (!_readAll()) { + return -1; + } + } + int result = _read_ptr[0]; + ++_read_ptr; + --_available; + if (_available == 0) { + _read_ptr = nullptr; + /* Send pending outgoing data, if any */ + if (_hasWriteBuffers()) { + _writeBuffersSend(); + } + } + return result; + } + + int write(const uint8_t* src, size_t size) + { + if (_isServer) { + return _write(src, size); + } else if (!_available) { + if (_hasWriteBuffers()) { + int rc = _writeBuffersSend(); + if (rc < 0) { + return rc; + } + } + return _write(src, size); + } + /* Some received data is still present in the axtls fragment buffer. + We can't call ssl_write now, as that will overwrite the contents of + the fragment buffer, corrupting the received data. + Save a copy of the outgoing data, and call ssl_write when all + recevied data has been consumed by the application. + */ + return _writeBufferAdd(src, size); + } + + int peek() + { + if (!_available) { + if (!_readAll()) { + return -1; + } + } + return _read_ptr[0]; + } + + size_t peekBytes(char *dst, size_t size) + { + if (!_available) { + if (!_readAll()) { + return -1; + } + } + + size_t will_copy = (_available < size) ? _available : size; + memcpy(dst, _read_ptr, will_copy); + return will_copy; + } + + int available() + { + auto cb = _available; + if (cb == 0) { + cb = _readAll(); + } else { + optimistic_yield(100); + } + return cb; + } + + // similar to available, but doesn't return exact size + bool hasData() + { + return _available > 0 || (io_ctx && io_ctx->getSize() > 0); + } + + bool loadObject(int type, Stream& stream, size_t size) + { + std::unique_ptr buf(new uint8_t[size]); + if (!buf.get()) { + DEBUGV("loadObject: failed to allocate memory\n"); + return false; + } + + size_t cb = stream.readBytes(buf.get(), size); + if (cb != size) { + DEBUGV("loadObject: reading %u bytes, got %u\n", size, cb); + return false; + } + + return loadObject(type, buf.get(), size); + } + + bool loadObject_P(int type, PGM_VOID_P data, size_t size) + { + std::unique_ptr buf(new uint8_t[size]); + memcpy_P(buf.get(),data, size); + return loadObject(type, buf.get(), size); + } + + bool loadObject(int type, const uint8_t* data, size_t size) + { + int rc = ssl_obj_memory_load(_isServer?_ssl_svr_ctx:_ssl_client_ctx, type, data, static_cast(size), nullptr); + if (rc != SSL_OK) { + DEBUGV("loadObject: ssl_obj_memory_load returned %d\n", rc); + return false; + } + return true; + } + + bool verifyCert() + { + int rc = ssl_verify_cert(_ssl.get()); + if (_allowSelfSignedCerts && rc == SSL_X509_ERROR(X509_VFY_ERROR_SELF_SIGNED)) { + DEBUGV("Allowing self-signed certificate\n"); + return true; + } else if (rc != SSL_OK) { + DEBUGV("ssl_verify_cert returned %d\n", rc); + ssl_display_error(rc); + return false; + } + return true; + } + + void allowSelfSignedCerts() + { + _allowSelfSignedCerts = true; + } + + operator SSL*() + { + return _ssl.get(); + } + + static ClientContext* getIOContext(int fd) + { + if (fd) { + SSLContext *thisSSL = reinterpret_cast(fd); + return thisSSL->io_ctx; + } + return nullptr; + } + +protected: + int _readAll() + { + if (!_ssl) { + return 0; + } + + optimistic_yield(100); + + uint8_t* data; + int rc = ssl_read(_ssl.get(), &data); + if (rc <= 0) { + if (rc < SSL_OK && rc != SSL_CLOSE_NOTIFY && rc != SSL_ERROR_CONN_LOST) { + _ssl = nullptr; + } + return 0; + } + DEBUGV(":wcs ra %d\r\n", rc); + _read_ptr = data; + _available = rc; + return _available; + } + + int _write(const uint8_t* src, size_t size) + { + if (!_ssl) { + return 0; + } + + int rc = ssl_write(_ssl.get(), src, size); + if (rc >= 0) { + return rc; + } + DEBUGV(":wcs write rc=%d\r\n", rc); + return rc; + } + + int _writeBufferAdd(const uint8_t* data, size_t size) + { + if (!_ssl) { + return 0; + } + + _writeBuffers.emplace_back(data, size); + if (_writeBuffers.back().data.get() == nullptr) { + _writeBuffers.pop_back(); + return 0; + } + return size; + } + + int _writeBuffersSend() + { + while (!_writeBuffers.empty()) { + auto& first = _writeBuffers.front(); + int rc = _write(first.data.get(), first.size); + _writeBuffers.pop_front(); + if (rc < 0) { + if (_hasWriteBuffers()) { + DEBUGV(":wcs _writeBuffersSend dropping unsent data\r\n"); + _writeBuffers.clear(); + } + return rc; + } + } + return 0; + } + + bool _hasWriteBuffers() + { + return !_writeBuffers.empty(); + } + + bool _isServer = false; + static SSL_CTX* _ssl_client_ctx; + static int _ssl_client_ctx_refcnt; + static SSL_CTX* _ssl_svr_ctx; + static int _ssl_svr_ctx_refcnt; + std::shared_ptr _ssl = nullptr; + const uint8_t* _read_ptr = nullptr; + size_t _available = 0; + BufferList _writeBuffers; + bool _allowSelfSignedCerts = false; + ClientContext* io_ctx = nullptr; +}; + +}; // End namespace axTLS + +#endif From a7a5959797e57a5c705618e916bdb63cc404124c Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Sun, 16 Sep 2018 19:24:42 -0700 Subject: [PATCH 22/35] Fix connection options and update github pubkey (#5120) As part of the "clear connection configuration for reused objects" patch, a ::stop would reset the self-signed, trust anchors, etc. WiFiClient, unfortunately, calls ::stop as part of the connection process, so all of these settings were lost. Now only clear the connection settings on ::stop if we've already been connected. Also update the github public key which changed yet again. Fixes #5086 --- .../BearSSL_Validation/BearSSL_Validation.ino | 14 +++++++------- .../ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp | 5 ++++- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino b/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino index bd2a31e3f9..18f72f14da 100644 --- a/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino +++ b/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino @@ -131,13 +131,13 @@ able to establish communications. // Extracted by: openssl x509 -pubkey -noout -in servercert.pem static const char pubkey[] PROGMEM = R"KEY( -----BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqcCPMEktuxLoDAxdHQgI -95FweH4Fa6+LslU2qmmUBF+pu4ZOUvpIQxVU5wqdWaxZauxG1nYUTrAWdPb1n0um -gLsGE7WYXJnQPJewIK4Qhua0LsrirIdHkcwHQ83NEYj+lswhg0fUQURt06Uta5ak -LovDdJPLqTuTS/nshOa76hR0ouWnrqucLL1szcvX/obB+Nsbmr58Mrg8prQfRoK6 -ibzlZysV88qPcCpc57lq6QBKQ2F9WgQMssQigXfTNm8lAAQ+L6gCZngd4KfHYPSJ -YA07oFWmuSOalgh00Wh8PUjuRGrcNxWpmgfALQHHFYgoDcD+a8+GoJk+GdJd3ong -ZQIDAQAB +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy+3Up8qBkIn/7S9AfWlH +Od8SdXmnWx+JCIHvnWzjFcLeLvQb2rMqqCDL5XDlvkyC5SZ8ZyLITemej5aJYuBv +zcKPzyZ0QfYZiskU9nzL2qBQj8alzJJ/Cc32AWuuWrPrzVxBmOEW9gRCGFCD3m0z +53y6GjcmBS2wcX7RagqbD7g2frEGko4G7kmW96H6dyh2j9Rou8TwAK6CnbiXPAM/ +5Q6dyfdYlHOCgP75F7hhdKB5gpprm9A/OnQsmZjUPzy4u0EKCxE8MfhBerZrZdod +88ZdDG3CvTgm050bc+lGlbsT+s09lp0dgxSZIeI8+syV2Owt4YF/PdjeeymtzQdI +wQIDAQAB -----END PUBLIC KEY----- )KEY"; BearSSL::WiFiClientSecure client; diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp index f946ae6ef1..45b7eec62c 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp @@ -179,7 +179,10 @@ void WiFiClientSecure::stop() { _client->abort(); } WiFiClient::stop(); - _clearAuthenticationSettings(); + // Only if we've already connected, clear the connection options + if (_handshake_done) { + _clearAuthenticationSettings(); + } _freeSSL(); } From d06cac24f46600c32ff01119a417bbaab72d7d16 Mon Sep 17 00:00:00 2001 From: David Gauchard Date: Tue, 18 Sep 2018 09:49:49 +0200 Subject: [PATCH 23/35] ClientContext: fix debugging messages --- libraries/ESP8266WiFi/src/include/ClientContext.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index 35262ca732..77eae267b0 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -449,7 +449,8 @@ class ClientContext return false; } - DEBUGV(":wr %d %d %d\r\n", will_send, left, _written); + DEBUGV(":wr %d %d\r\n", _datasource->available(), _written); + bool has_written = false; while (_datasource) { @@ -470,7 +471,7 @@ class ClientContext // user data will not stay in place when data are sent but not acknowledged flags |= TCP_WRITE_FLAG_COPY; err_t err = tcp_write(_pcb, buf, next_chunk_size, flags); - DEBUGV(":wrc %d %d %d\r\n", next_chunk_size, will_send, (int)err); + DEBUGV(":wrc %d %d %d\r\n", next_chunk_size, _datasource->available(), (int)err); if (err == ERR_OK) { _datasource->release_buffer(buf, next_chunk_size); _written += next_chunk_size; From f271b2ab13585f75733a513378a774cc82200c62 Mon Sep 17 00:00:00 2001 From: david gauchard Date: Wed, 19 Sep 2018 01:42:51 +0200 Subject: [PATCH 24/35] WiFiClient: move static members out of the class, add comments --- libraries/ESP8266WiFi/src/WiFiClient.cpp | 25 +++++++++++++++-- libraries/ESP8266WiFi/src/WiFiClient.h | 28 +++++++++++-------- .../ESP8266WiFi/src/include/ClientContext.h | 10 ++++--- 3 files changed, 46 insertions(+), 17 deletions(-) diff --git a/libraries/ESP8266WiFi/src/WiFiClient.cpp b/libraries/ESP8266WiFi/src/WiFiClient.cpp index 45b1c26561..6815e3fe81 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClient.cpp @@ -42,8 +42,29 @@ extern "C" #include "c_types.h" uint16_t WiFiClient::_localPort = 0; -bool WiFiClient::_defaultNoDelay = true; -bool WiFiClient::_defaultSync = false; + +static bool _defaultNoDelay = false; // false == Nagle enabled by default +static bool _defaultSync = false; + +void WiFiClient::setDefaultNoDelay (bool noDelay) +{ + _defaultNoDelay = noDelay; +} + +void WiFiClient::setDefaultSync (bool sync) +{ + _defaultSync = sync; +} + +bool WiFiClient::getDefaultNoDelay () +{ + return _defaultNoDelay; +} + +bool WiFiClient::getDefaultSync () +{ + return _defaultSync; +} template<> WiFiClient* SList::_s_first = 0; diff --git a/libraries/ESP8266WiFi/src/WiFiClient.h b/libraries/ESP8266WiFi/src/WiFiClient.h index 3f417e69a2..97ab33a42d 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.h +++ b/libraries/ESP8266WiFi/src/WiFiClient.h @@ -81,10 +81,6 @@ class WiFiClient : public Client, public SList { uint16_t remotePort(); IPAddress localIP(); uint16_t localPort(); - bool getNoDelay() const; - void setNoDelay(bool nodelay); - bool getSync() const; - void setSync(bool sync); static void setLocalPortStart(uint16_t port) { _localPort = port; } @@ -104,10 +100,23 @@ class WiFiClient : public Client, public SList { uint8_t getKeepAliveCount () const; void disableKeepAlive () { keepAlive(0, 0, 0); } - static void setDefaultNoDelay (bool noDelay) { _defaultNoDelay = noDelay; } - static void setDefaultSync (bool sync) { _defaultSync = sync; } - static bool getDefaultNoDelay () { return _defaultNoDelay; } - static bool getDefaultSync () { return _defaultSync; } + // default NoDelay=False (Nagle=True=!NoDelay) + // Nagle is for shortly delaying outgoing data, to send less/bigger packets + // Nagle should be disabled for telnet-like/interactive streams + // Nagle is meaningless/ignored when Sync=true + static void setDefaultNoDelay (bool noDelay); + static bool getDefaultNoDelay (); + bool getNoDelay() const; + void setNoDelay(bool nodelay); + + // default Sync=false + // When sync is true, all writes are automatically flushed. + // This is slower but also does not allocate + // temporary memory for sending data + static void setDefaultSync (bool sync); + static bool getDefaultSync (); + bool getSync() const; + void setSync(bool sync); protected: @@ -119,9 +128,6 @@ class WiFiClient : public Client, public SList { ClientContext* _client; static uint16_t _localPort; - - static bool _defaultNoDelay; - static bool _defaultSync; }; #endif diff --git a/libraries/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index 35262ca732..e0e7ed3792 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -460,14 +460,16 @@ class ClientContext break; const uint8_t* buf = _datasource->get_buffer(next_chunk_size); // use TCP_WRITE_FLAG_MORE to remove PUSH flag from packet (lwIP's doc), - // because PUSH implicitely disables nagle (see lwIP's tcp_out.c) + // because PUSH code implicitely disables Nagle code (see lwIP's tcp_out.c) // Notes: - // PUSH is for peer, telling to give data to user app as soon as received + // PUSH is meant for peer, telling to give data to user app as soon as received // PUSH "may be set" when sender has finished sending a meaningful data block - // Nagle is for delaying local data, to send less/bigger packets + // PUSH is quite unclear in its application + // Nagle is for shortly delaying outgoing data, to send less/bigger packets uint8_t flags = TCP_WRITE_FLAG_MORE; // do not tcp-PuSH if (!_sync) - // user data will not stay in place when data are sent but not acknowledged + // user data must be copied when data are sent but not yet acknowledged + // (with sync, we wait for acknowledgment before returning to user) flags |= TCP_WRITE_FLAG_COPY; err_t err = tcp_write(_pcb, buf, next_chunk_size, flags); DEBUGV(":wrc %d %d %d\r\n", next_chunk_size, will_send, (int)err); From 888bdb8c3b10b534e77495d3ea89396b04f8717c Mon Sep 17 00:00:00 2001 From: david gauchard Date: Wed, 19 Sep 2018 02:10:12 +0200 Subject: [PATCH 25/35] remove circular dependency --- libraries/ESP8266WiFi/src/WiFiClient.cpp | 27 ++++++++++--------- libraries/ESP8266WiFi/src/WiFiClient.h | 3 --- .../ESP8266WiFi/src/include/ClientContext.h | 7 +++-- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/libraries/ESP8266WiFi/src/WiFiClient.cpp b/libraries/ESP8266WiFi/src/WiFiClient.cpp index e3fb5a590c..3769229c58 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClient.cpp @@ -42,30 +42,33 @@ extern "C" #include "c_types.h" uint16_t WiFiClient::_localPort = 0; -bool WiFiClient::_defaultNoDelay = true; -bool WiFiClient::_defaultSync = false; -static bool _defaultNoDelay = false; // false == Nagle enabled by default -static bool _defaultSync = false; +static bool defaultNoDelay = false; // false == Nagle enabled by default +static bool defaultSync = false; + +bool getDefaultPrivateGlobalSyncValue () +{ + return defaultSync; +} void WiFiClient::setDefaultNoDelay (bool noDelay) { - _defaultNoDelay = noDelay; + defaultNoDelay = noDelay; } void WiFiClient::setDefaultSync (bool sync) { - _defaultSync = sync; + defaultSync = sync; } bool WiFiClient::getDefaultNoDelay () { - return _defaultNoDelay; + return defaultNoDelay; } bool WiFiClient::getDefaultSync () { - return _defaultSync; + return defaultSync; } template<> @@ -86,8 +89,8 @@ WiFiClient::WiFiClient(ClientContext* client) _client->ref(); WiFiClient::_add(this); - setSync(_defaultSync); - setNoDelay(_defaultNoDelay); + setSync(defaultSync); + setNoDelay(defaultNoDelay); } WiFiClient::~WiFiClient() @@ -174,8 +177,8 @@ int WiFiClient::connect(IPAddress ip, uint16_t port) return 0; } - setSync(_defaultSync); - setNoDelay(_defaultNoDelay); + setSync(defaultSync); + setNoDelay(defaultNoDelay); return 1; } diff --git a/libraries/ESP8266WiFi/src/WiFiClient.h b/libraries/ESP8266WiFi/src/WiFiClient.h index fb2f6b00cd..97ab33a42d 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.h +++ b/libraries/ESP8266WiFi/src/WiFiClient.h @@ -128,9 +128,6 @@ class WiFiClient : public Client, public SList { ClientContext* _client; static uint16_t _localPort; - - static bool _defaultNoDelay; - static bool _defaultSync; }; #endif diff --git a/libraries/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index e95f430326..81db7b1ac6 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -31,11 +31,14 @@ extern "C" void esp_schedule(); #include "DataSource.h" +bool getDefaultPrivateGlobalSyncValue (); + class ClientContext { public: ClientContext(tcp_pcb* pcb, discard_cb_t discard_cb, void* discard_cb_arg) : - _pcb(pcb), _rx_buf(0), _rx_buf_offset(0), _discard_cb(discard_cb), _discard_cb_arg(discard_cb_arg), _refcnt(0), _next(0), _sync(WiFiClient::getDefaultSync()) + _pcb(pcb), _rx_buf(0), _rx_buf_offset(0), _discard_cb(discard_cb), _discard_cb_arg(discard_cb_arg), _refcnt(0), _next(0), + _sync(::getDefaultPrivateGlobalSyncValue()) { tcp_setprio(pcb, TCP_PRIO_MIN); tcp_arg(pcb, this); @@ -473,7 +476,7 @@ class ClientContext // (with sync, we wait for acknowledgment before returning to user) flags |= TCP_WRITE_FLAG_COPY; err_t err = tcp_write(_pcb, buf, next_chunk_size, flags); - DEBUGV(":wrc %d %d %d\r\n", next_chunk_size, will_send, (int)err); + DEBUGV(":wrc %d %d %d\r\n", next_chunk_size, _datasource->available(), (int)err); if (err == ERR_OK) { _datasource->release_buffer(buf, next_chunk_size); _written += next_chunk_size; From c59a91fa6ef85dc708e951dffa75cf3076218a0f Mon Sep 17 00:00:00 2001 From: david gauchard Date: Sun, 23 Sep 2018 01:38:49 +0200 Subject: [PATCH 26/35] parameter and return value for Client::flush&stop, flush timeout raised to 300ms --- cores/esp8266/Client.h | 4 ++-- libraries/ESP8266WiFi/src/WiFiClient.cpp | 15 ++++++++------- libraries/ESP8266WiFi/src/WiFiClient.h | 6 +++--- .../ESP8266WiFi/src/WiFiClientSecureAxTLS.cpp | 4 ++-- libraries/ESP8266WiFi/src/WiFiClientSecureAxTLS.h | 2 +- .../ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp | 14 +++++--------- .../ESP8266WiFi/src/WiFiClientSecureBearSSL.h | 4 ++-- libraries/ESP8266WiFi/src/include/ClientContext.h | 9 +++++---- 8 files changed, 28 insertions(+), 30 deletions(-) diff --git a/cores/esp8266/Client.h b/cores/esp8266/Client.h index d776a2e16d..3cab9f2d69 100644 --- a/cores/esp8266/Client.h +++ b/cores/esp8266/Client.h @@ -34,8 +34,8 @@ class Client: public Stream { virtual int read() = 0; virtual int read(uint8_t *buf, size_t size) = 0; virtual int peek() = 0; - virtual void flush() = 0; - virtual void stop() = 0; + virtual bool flush(unsigned int maxWaitMs = 0) = 0; + virtual bool stop(unsigned int maxWaitMs = 0) = 0; virtual uint8_t connected() = 0; virtual operator bool() = 0; protected: diff --git a/libraries/ESP8266WiFi/src/WiFiClient.cpp b/libraries/ESP8266WiFi/src/WiFiClient.cpp index 3769229c58..347d068f15 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClient.cpp @@ -311,19 +311,20 @@ size_t WiFiClient::peekBytes(uint8_t *buffer, size_t length) { return _client->peekBytes((char *)buffer, count); } -void WiFiClient::flush() +bool WiFiClient::flush(unsigned int maxWaitMs) { - if (_client) - _client->wait_until_sent(); + return !_client || _client->wait_until_sent(maxWaitMs?: WIFICLIENT_MAX_FLUSH_WAIT_MS); } -void WiFiClient::stop() +bool WiFiClient::stop(unsigned int maxWaitMs) { if (!_client) - return; + return true; - _client->wait_until_sent(); - _client->close(); + bool ret = flush(maxWaitMs); // virtual, may be ssl's + if (_client->close() != ERR_OK) + ret = false; + return ret; } uint8_t WiFiClient::connected() diff --git a/libraries/ESP8266WiFi/src/WiFiClient.h b/libraries/ESP8266WiFi/src/WiFiClient.h index 97ab33a42d..7f9d02ee51 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.h +++ b/libraries/ESP8266WiFi/src/WiFiClient.h @@ -33,7 +33,7 @@ #endif #define WIFICLIENT_MAX_PACKET_SIZE TCP_MSS -#define WIFICLIENT_MAX_FLUSH_WAIT_MS 100 +#define WIFICLIENT_MAX_FLUSH_WAIT_MS 300 #define TCP_DEFAULT_KEEPALIVE_IDLE_SEC 7200 // 2 hours #define TCP_DEFAULT_KEEPALIVE_INTERVAL_SEC 75 // 75 sec @@ -72,8 +72,8 @@ class WiFiClient : public Client, public SList { size_t peekBytes(char *buffer, size_t length) { return peekBytes((uint8_t *) buffer, length); } - virtual void flush(); - virtual void stop(); + virtual bool flush(unsigned int maxWaitMs = 0); + virtual bool stop(unsigned int maxWaitMs = 0); virtual uint8_t connected(); virtual operator bool(); diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureAxTLS.cpp b/libraries/ESP8266WiFi/src/WiFiClientSecureAxTLS.cpp index d200d51e59..d275f2aacf 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureAxTLS.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureAxTLS.cpp @@ -271,12 +271,12 @@ uint8_t WiFiClientSecure::connected() return false; } -void WiFiClientSecure::stop() +bool WiFiClientSecure::stop(unsigned int maxWaitMs) { if (_ssl) { _ssl->stop(); } - WiFiClient::stop(); + return WiFiClient::stop(maxWaitMs); } static bool parseHexNibble(char pb, uint8_t* res) diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureAxTLS.h b/libraries/ESP8266WiFi/src/WiFiClientSecureAxTLS.h index e836d7bd5f..fabfb5e4ef 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureAxTLS.h +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureAxTLS.h @@ -51,7 +51,7 @@ class WiFiClientSecure : public WiFiClient { int read() override; int peek() override; size_t peekBytes(uint8_t *buffer, size_t length) override; - void stop() override; + bool stop(unsigned int maxWaitMs = 0) override; bool setCACert(const uint8_t* pk, size_t size); bool setCertificate(const uint8_t* pk, size_t size); diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp index 45b7eec62c..65bc833291 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp @@ -172,23 +172,19 @@ void WiFiClientSecure::setBufferSizes(int recv, int xmit) { _iobuf_out_size = xmit; } -void WiFiClientSecure::stop() { - flush(); - if (_client) { - _client->wait_until_sent(); - _client->abort(); - } - WiFiClient::stop(); +bool WiFiClientSecure::stop(unsigned int maxWaitMs) { + bool ret = WiFiClient::stop(maxWaitMs); // calls our virtual flush() // Only if we've already connected, clear the connection options if (_handshake_done) { _clearAuthenticationSettings(); } _freeSSL(); + return ret; } -void WiFiClientSecure::flush() { +bool WiFiClientSecure::flush(unsigned int maxWaitMs) { (void) _run_until(BR_SSL_SENDAPP); - WiFiClient::flush(); + return WiFiClient::flush(maxWaitMs); } int WiFiClientSecure::connect(IPAddress ip, uint16_t port) { diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h index 365cb9bd01..cd40ffaf68 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h @@ -54,8 +54,8 @@ class WiFiClientSecure : public WiFiClient { int read() override; int peek() override; size_t peekBytes(uint8_t *buffer, size_t length) override; - void stop() override; - void flush() override; + bool flush(unsigned int maxWaitMs = 0) override; + bool stop(unsigned int maxWaitMs = 0) override; // Don't validate the chain, just accept whatever is given. VERY INSECURE! void setInsecure() { diff --git a/libraries/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index 81db7b1ac6..cbf6326515 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -299,18 +299,17 @@ class ClientContext _rx_buf_offset = 0; } - void wait_until_sent() + bool wait_until_sent(int max_wait_ms = WIFICLIENT_MAX_FLUSH_WAIT_MS) { // https://github.com/esp8266/Arduino/pull/3967#pullrequestreview-83451496 // option 1 done // option 2 / _write_some() not necessary since _datasource is always nullptr here if (!_pcb) - return; + return true; tcp_output(_pcb); - - int max_wait_ms = WIFICLIENT_MAX_FLUSH_WAIT_MS + 1; + max_wait_ms++; // wait for peer's acks flushing lwIP's output buffer while (state() == ESTABLISHED && tcp_sndbuf(_pcb) != TCP_SND_BUF && --max_wait_ms) @@ -322,6 +321,8 @@ class ClientContext DEBUGV(":wustmo\n"); } #endif + + return max_wait_ms > 0; } uint8_t state() const From 8414e1751c6e604059ac31821e45156b812da2b8 Mon Sep 17 00:00:00 2001 From: david gauchard Date: Sun, 23 Sep 2018 22:47:00 +0200 Subject: [PATCH 27/35] tcp flush: restart timer on ack receive --- .../ESP8266WiFi/src/include/ClientContext.h | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/libraries/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index cbf6326515..86327dc403 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -170,7 +170,7 @@ class ClientContext return tcp_nagle_disabled(_pcb); } - void setTimeout(int timeout_ms) + void setTimeout(int timeout_ms) { _timeout_ms = timeout_ms; } @@ -308,15 +308,33 @@ class ClientContext if (!_pcb) return true; - tcp_output(_pcb); + int loop = -1; + int prevsndbuf = -1; max_wait_ms++; - // wait for peer's acks flushing lwIP's output buffer - while (state() == ESTABLISHED && tcp_sndbuf(_pcb) != TCP_SND_BUF && --max_wait_ms) - delay(1); // yields + // wait for peer's acks to flush lwIP's output buffer + + while (1) { + + // force lwIP to send what can be sent + tcp_output(_pcb); + + int sndbuf = tcp_sndbuf(_pcb); + if (sndbuf != prevsndbuf) { + // send buffer has changed (or first iteration) + // we received an ack: restart the loop counter + prevsndbuf = sndbuf; + loop = max_wait_ms; + } + + if (state() != ESTABLISHED || sndbuf == TCP_SND_BUF || --loop <= 0) + break; + + delay(1); + } #ifdef DEBUGV - if (max_wait_ms == 0) { + if (loop <= 0) { // wait until sent: timeout DEBUGV(":wustmo\n"); } From 4d6287906b68e1259f9837d2f9c5806062fdb25f Mon Sep 17 00:00:00 2001 From: david gauchard Date: Sun, 23 Sep 2018 23:16:12 +0200 Subject: [PATCH 28/35] OTA protocol needs setNoDelay(true) --- libraries/ArduinoOTA/ArduinoOTA.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/ArduinoOTA/ArduinoOTA.cpp b/libraries/ArduinoOTA/ArduinoOTA.cpp index 24be99d374..b6d1a915e6 100644 --- a/libraries/ArduinoOTA/ArduinoOTA.cpp +++ b/libraries/ArduinoOTA/ArduinoOTA.cpp @@ -290,6 +290,8 @@ void ArduinoOTAClass::_runUpdate() { } _state = OTA_IDLE; } + // OTA sends little packets + client.setNoDelay(true); uint32_t written, total = 0; while (!Update.isFinished() && client.connected()) { From 826b8b58a78f6a6a683d817f32b6ec4e8235c1ba Mon Sep 17 00:00:00 2001 From: david gauchard Date: Sun, 23 Sep 2018 23:24:06 +0200 Subject: [PATCH 29/35] fix Ethernet with Client changes --- libraries/Ethernet/src/EthernetClient.cpp | 20 +++++++++++++++----- libraries/Ethernet/src/EthernetClient.h | 4 ++-- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/libraries/Ethernet/src/EthernetClient.cpp b/libraries/Ethernet/src/EthernetClient.cpp index 1feed4c424..7e9dc9abbd 100644 --- a/libraries/Ethernet/src/EthernetClient.cpp +++ b/libraries/Ethernet/src/EthernetClient.cpp @@ -119,13 +119,15 @@ int EthernetClient::peek() { return b; } -void EthernetClient::flush() { +bool EthernetClient::flush(unsigned int maxWaitMs) { + (void)maxWaitMs; ::flush(_sock); + return true; } -void EthernetClient::stop() { +bool EthernetClient::stop(unsigned int maxWaitMs) { if (_sock == MAX_SOCK_NUM) - return; + return true; // attempt to close the connection gracefully (send a FIN to other side) disconnect(_sock); @@ -133,19 +135,27 @@ void EthernetClient::stop() { // wait up to a second for the connection to close uint8_t s; + if (maxWaitMs == 0) + maxWaitMs = 1000; do { s = status(); if (s == SnSR::CLOSED) break; // exit the loop delay(1); - } while (millis() - start < 1000); + } while (millis() - start < maxWaitMs); + + bool ret = true; // if it hasn't closed, close it forcefully - if (s != SnSR::CLOSED) + if (s != SnSR::CLOSED) { + ret = false; close(_sock); + } EthernetClass::_server_port[_sock] = 0; _sock = MAX_SOCK_NUM; + + return ret; } uint8_t EthernetClient::connected() { diff --git a/libraries/Ethernet/src/EthernetClient.h b/libraries/Ethernet/src/EthernetClient.h index 16e2500bc3..ff4198ff17 100644 --- a/libraries/Ethernet/src/EthernetClient.h +++ b/libraries/Ethernet/src/EthernetClient.h @@ -20,8 +20,8 @@ class EthernetClient : public Client { virtual int read(); virtual int read(uint8_t *buf, size_t size); virtual int peek(); - virtual void flush(); - virtual void stop(); + virtual bool flush(unsigned int maxWaitMs = 0); + virtual bool stop(unsigned int maxWaitMs = 0); virtual uint8_t connected(); virtual operator bool(); virtual bool operator==(const bool value) { return bool() == value; } From 3fa6a97acb63dca04d2c8af9cd26aad9c8add07d Mon Sep 17 00:00:00 2001 From: david gauchard Date: Sun, 23 Sep 2018 23:27:22 +0200 Subject: [PATCH 30/35] 1 line unredable -> 5 lines readable code --- libraries/ESP8266WiFi/src/WiFiClient.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/ESP8266WiFi/src/WiFiClient.cpp b/libraries/ESP8266WiFi/src/WiFiClient.cpp index 347d068f15..6bb79a4a9b 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClient.cpp @@ -313,7 +313,12 @@ size_t WiFiClient::peekBytes(uint8_t *buffer, size_t length) { bool WiFiClient::flush(unsigned int maxWaitMs) { - return !_client || _client->wait_until_sent(maxWaitMs?: WIFICLIENT_MAX_FLUSH_WAIT_MS); + if (!_client) + return true; + + if (maxWaitMs == 0) + maxWaitMs = WIFICLIENT_MAX_FLUSH_WAIT_MS; + return _client->wait_until_sent(maxWaitMs); } bool WiFiClient::stop(unsigned int maxWaitMs) From 7752c98510c701d215f487ee5fabcada8564722b Mon Sep 17 00:00:00 2001 From: david gauchard Date: Mon, 24 Sep 2018 22:44:00 +0200 Subject: [PATCH 31/35] doc --- doc/esp8266wifi/client-class.rst | 42 ++++++++++++++++++++++++++++++++ doc/esp8266wifi/server-class.rst | 4 +++ 2 files changed, 46 insertions(+) diff --git a/doc/esp8266wifi/client-class.rst b/doc/esp8266wifi/client-class.rst index 9ddd46b458..d53f6c1207 100644 --- a/doc/esp8266wifi/client-class.rst +++ b/doc/esp8266wifi/client-class.rst @@ -18,6 +18,14 @@ Methods documented for `Client `__ documentation. Before they are fully documented please refer to information below. +flush and stop +~~~~~~~~~~~~~~ + +`flush(timeoutMs)` and `stop(timeoutMs)` both have now an optional argument: timeout in millisecond and both return a boolean. +Default input value 0 means that effective value is left at the discretion of the implementer. +`flush()` returning `true` indicates that output data have effectively been sent, and `false` that a timeout has occurred. +`stop()` returns `false` in case of an issue closing the client. Depending on implementation, its parameter is given to `flush()` or ignored. + setNoDelay ~~~~~~~~~~ @@ -35,6 +43,33 @@ This algorithm is intended to reduce TCP/IP traffic of small packets sent over t client.setNoDelay(true); +setSync +~~~~~~~ + +This is an experimental API that will set the client in synchronized mode. +In this mode, every ``write()`` is flushed. It means that after a call to +``write()``, data are ensured to be received where they went sent to (that is +``flush`` semantic). + +In ``WiFiClient`` implementation, + +- It also allows to avoid a temporary copy of data that otherwise consumes + at most TCP_SND_BUF = (2*MSS) bytes per connection, + +- When set to true, it slows down transfers, and implicitely disable the + Nagle algorithm. + + +setDefaultNoDelay and setDefaultSync +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +These set the default value for both setSync and setNoDelay for every future +instance of ``WiFiClient`` (including those coming from ``WiFiServer.available()``). + +Default value are false for both ``NoDelay`` and ``Sync``. *This means that +Nagle is enabled by default for all new connections*. + + Other Function Calls ~~~~~~~~~~~~~~~~~~~~ @@ -54,7 +89,14 @@ Other Function Calls uint16_t remotePort () IPAddress localIP () uint16_t localPort () + bool setNoDelay (bool noDelay) bool getNoDelay () + bool setSync (bool sync) + bool getSync () + (static) bool setDefaultNoDelay (bool noDelay) + (static) bool getDefaultNoDelay () + (static) bool setDefaultSync (bool sync) + (static) bool getDefaultSync () Documentation for the above functions is not yet prepared. diff --git a/doc/esp8266wifi/server-class.rst b/doc/esp8266wifi/server-class.rst index 6b3512959c..55db28b728 100644 --- a/doc/esp8266wifi/server-class.rst +++ b/doc/esp8266wifi/server-class.rst @@ -32,6 +32,10 @@ This algorithm is intended to reduce TCP/IP traffic of small packets sent over t server.begin(); server.setNoDelay(true); +By default, NoDelay value will depends on global ``WiFiClient::getDefaultNoDelay()`` (currently false by default). + +However, a call to ``wiFiServer.setNoDelay()`` will override ``NoDelay`` for all new ``WiFiClient`` provided by the calling instance (``wiFiServer``). + Other Function Calls ~~~~~~~~~~~~~~~~~~~~ From c160d173069b268211f53fbb8407fe6e9245846c Mon Sep 17 00:00:00 2001 From: david gauchard Date: Mon, 24 Sep 2018 22:51:27 +0200 Subject: [PATCH 32/35] doc --- doc/esp8266wifi/client-class.rst | 9 ++++++--- doc/esp8266wifi/server-class.rst | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/doc/esp8266wifi/client-class.rst b/doc/esp8266wifi/client-class.rst index d53f6c1207..ad86639e54 100644 --- a/doc/esp8266wifi/client-class.rst +++ b/doc/esp8266wifi/client-class.rst @@ -21,10 +21,13 @@ Methods and properties described further down are specific to ESP8266. They are flush and stop ~~~~~~~~~~~~~~ -`flush(timeoutMs)` and `stop(timeoutMs)` both have now an optional argument: timeout in millisecond and both return a boolean. +``flush(timeoutMs)`` and ``stop(timeoutMs)`` both have now an optional argument: timeout in millisecond and both return a boolean. + Default input value 0 means that effective value is left at the discretion of the implementer. -`flush()` returning `true` indicates that output data have effectively been sent, and `false` that a timeout has occurred. -`stop()` returns `false` in case of an issue closing the client. Depending on implementation, its parameter is given to `flush()` or ignored. + +``flush()`` returning ``true`` indicates that output data have effectively been sent, and ``false`` that a timeout has occurred. + +``stop()`` returns ``false`` in case of an issue when closing the client (for instance a timed-out ``flush``). Depending on implementation, its parameter can be passed to ``flush()``. setNoDelay ~~~~~~~~~~ diff --git a/doc/esp8266wifi/server-class.rst b/doc/esp8266wifi/server-class.rst index 55db28b728..8855083ab9 100644 --- a/doc/esp8266wifi/server-class.rst +++ b/doc/esp8266wifi/server-class.rst @@ -32,7 +32,7 @@ This algorithm is intended to reduce TCP/IP traffic of small packets sent over t server.begin(); server.setNoDelay(true); -By default, NoDelay value will depends on global ``WiFiClient::getDefaultNoDelay()`` (currently false by default). +By default, ``nodelay`` value will depends on global ``WiFiClient::getDefaultNoDelay()`` (currently false by default). However, a call to ``wiFiServer.setNoDelay()`` will override ``NoDelay`` for all new ``WiFiClient`` provided by the calling instance (``wiFiServer``). From db8fe6c269141d7bedf102ff42e701fd94d70f58 Mon Sep 17 00:00:00 2001 From: david gauchard Date: Mon, 24 Sep 2018 22:57:05 +0200 Subject: [PATCH 33/35] doc --- doc/esp8266wifi/client-class.rst | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/doc/esp8266wifi/client-class.rst b/doc/esp8266wifi/client-class.rst index ad86639e54..8963282b96 100644 --- a/doc/esp8266wifi/client-class.rst +++ b/doc/esp8266wifi/client-class.rst @@ -21,7 +21,7 @@ Methods and properties described further down are specific to ESP8266. They are flush and stop ~~~~~~~~~~~~~~ -``flush(timeoutMs)`` and ``stop(timeoutMs)`` both have now an optional argument: timeout in millisecond and both return a boolean. +``flush(timeoutMs)`` and ``stop(timeoutMs)`` both have now an optional argument: ``timeout`` in millisecond, and both return a boolean. Default input value 0 means that effective value is left at the discretion of the implementer. @@ -54,23 +54,24 @@ In this mode, every ``write()`` is flushed. It means that after a call to ``write()``, data are ensured to be received where they went sent to (that is ``flush`` semantic). -In ``WiFiClient`` implementation, +When set to ``true`` in ``WiFiClient`` implementation, -- It also allows to avoid a temporary copy of data that otherwise consumes - at most TCP_SND_BUF = (2*MSS) bytes per connection, +- It slows down transfers, and implicitely disable the Nagle algorithm. -- When set to true, it slows down transfers, and implicitely disable the - Nagle algorithm. +- It also allows to avoid a temporary copy of data that otherwise consumes + at most ``TCP_SND_BUF`` = (2*``MSS``) bytes per connection, setDefaultNoDelay and setDefaultSync ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -These set the default value for both setSync and setNoDelay for every future -instance of ``WiFiClient`` (including those coming from ``WiFiServer.available()``). +These set the default value for both ``setSync`` and ``setNoDelay`` for +every future instance of ``WiFiClient`` (including those coming from +``WiFiServer.available()`` by default). + +Default values are false for both ``NoDelay`` and ``Sync``. -Default value are false for both ``NoDelay`` and ``Sync``. *This means that -Nagle is enabled by default for all new connections*. +This means that *Nagle is enabled by default* for all new connections. Other Function Calls From 8bb444d6626c4689d4be74fb0b391095a18f1da4 Mon Sep 17 00:00:00 2001 From: david gauchard Date: Mon, 24 Sep 2018 23:00:07 +0200 Subject: [PATCH 34/35] doc --- doc/esp8266wifi/client-class.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/esp8266wifi/client-class.rst b/doc/esp8266wifi/client-class.rst index 8963282b96..9ddaf402a4 100644 --- a/doc/esp8266wifi/client-class.rst +++ b/doc/esp8266wifi/client-class.rst @@ -59,7 +59,7 @@ When set to ``true`` in ``WiFiClient`` implementation, - It slows down transfers, and implicitely disable the Nagle algorithm. - It also allows to avoid a temporary copy of data that otherwise consumes - at most ``TCP_SND_BUF`` = (2*``MSS``) bytes per connection, + at most ``TCP_SND_BUF`` = (2 * ``MSS``) bytes per connection, setDefaultNoDelay and setDefaultSync @@ -71,7 +71,7 @@ every future instance of ``WiFiClient`` (including those coming from Default values are false for both ``NoDelay`` and ``Sync``. -This means that *Nagle is enabled by default* for all new connections. +This means that Nagle is enabled by default *for all new connections*. Other Function Calls From 33dee325e162ce003092bf237a3e268f00d68a7d Mon Sep 17 00:00:00 2001 From: Develo Date: Mon, 24 Sep 2018 18:21:33 -0300 Subject: [PATCH 35/35] Update client-class.rst Added details for getters --- doc/esp8266wifi/client-class.rst | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/doc/esp8266wifi/client-class.rst b/doc/esp8266wifi/client-class.rst index 9ddaf402a4..2f153b254d 100644 --- a/doc/esp8266wifi/client-class.rst +++ b/doc/esp8266wifi/client-class.rst @@ -46,6 +46,11 @@ This algorithm is intended to reduce TCP/IP traffic of small packets sent over t client.setNoDelay(true); +getNoDelay +~~~~~~~~~~ + +Returns whether NoDelay is enabled or not for the current connection. + setSync ~~~~~~~ @@ -61,6 +66,10 @@ When set to ``true`` in ``WiFiClient`` implementation, - It also allows to avoid a temporary copy of data that otherwise consumes at most ``TCP_SND_BUF`` = (2 * ``MSS``) bytes per connection, +getSync +~~~~~~~ + +Returns whether Sync is enabled or not for the current connection. setDefaultNoDelay and setDefaultSync ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -73,6 +82,10 @@ Default values are false for both ``NoDelay`` and ``Sync``. This means that Nagle is enabled by default *for all new connections*. +getDefaultNoDelay and getDefaultSync +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Return the values to be used as default for NoDelay and Sync for all future connections. Other Function Calls ~~~~~~~~~~~~~~~~~~~~ @@ -93,14 +106,6 @@ Other Function Calls uint16_t remotePort () IPAddress localIP () uint16_t localPort () - bool setNoDelay (bool noDelay) - bool getNoDelay () - bool setSync (bool sync) - bool getSync () - (static) bool setDefaultNoDelay (bool noDelay) - (static) bool getDefaultNoDelay () - (static) bool setDefaultSync (bool sync) - (static) bool getDefaultSync () Documentation for the above functions is not yet prepared.