From b3174a61c62290aa7db57db1848903ce1572f168 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 4 Feb 2016 00:50:11 +0300 Subject: [PATCH 1/6] Move ClientContext write behaviour into strategies --- .../ESP8266WebServer/src/ESP8266WebServer.h | 2 +- libraries/ESP8266WiFi/src/WiFiClient.cpp | 40 +-- libraries/ESP8266WiFi/src/WiFiClient.h | 48 +--- .../ESP8266WiFi/src/WiFiClientSecure.cpp | 6 +- .../ESP8266WiFi/src/include/ClientContext.h | 65 ++--- .../ESP8266WiFi/src/include/DataStrategy.h | 25 ++ .../src/include/DataStrategyImpl.h | 261 ++++++++++++++++++ 7 files changed, 350 insertions(+), 97 deletions(-) create mode 100644 libraries/ESP8266WiFi/src/include/DataStrategy.h create mode 100644 libraries/ESP8266WiFi/src/include/DataStrategyImpl.h diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.h b/libraries/ESP8266WebServer/src/ESP8266WebServer.h index 4e291e5fbc..935ca5cb84 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.h @@ -125,7 +125,7 @@ template size_t streamFile(T &file, const String& contentType){ sendHeader("Content-Encoding", "gzip"); } send(200, contentType, ""); - return _currentClient.write(file, HTTP_DOWNLOAD_UNIT_SIZE); + return _currentClient.write(file); } protected: diff --git a/libraries/ESP8266WiFi/src/WiFiClient.cpp b/libraries/ESP8266WiFi/src/WiFiClient.cpp index c2c6966f22..3799dbcc62 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClient.cpp @@ -40,6 +40,7 @@ extern "C" #include "lwip/netif.h" #include "include/ClientContext.h" #include "c_types.h" +#include "include/DataStrategyImpl.h" uint16_t WiFiClient::_localPort = 0; @@ -172,35 +173,34 @@ size_t WiFiClient::write(const uint8_t *buf, size_t size) { return 0; } + BufferStrategy strategy(buf, size, _timeout); + return _client->write(strategy); +} - return _client->write(reinterpret_cast(buf), size); +size_t WiFiClient::write(Stream& stream, size_t unused) +{ + return WiFiClient::write(stream); } -size_t WiFiClient::write_P(PGM_P buf, size_t size) +size_t WiFiClient::write(Stream& stream) { - if (!_client || !size) + if (!_client || !stream.available()) { return 0; } + ChunkedStrategy strategy(stream, stream.available(), _timeout); + return _client->write(strategy); +} - char chunkUnit[WIFICLIENT_MAX_PACKET_SIZE + 1]; - chunkUnit[WIFICLIENT_MAX_PACKET_SIZE] = '\0'; - size_t remaining_size = size; - - while (buf != NULL && remaining_size > 0) { - size_t chunkUnitLen = WIFICLIENT_MAX_PACKET_SIZE; - - if (remaining_size < WIFICLIENT_MAX_PACKET_SIZE) chunkUnitLen = remaining_size; - // due to the memcpy signature, lots of casts are needed - memcpy_P((void*)chunkUnit, (PGM_VOID_P)buf, chunkUnitLen); - - buf += chunkUnitLen; - remaining_size -= chunkUnitLen; - - // write is so overloaded, had to use the cast to get it pick the right one - _client->write((const char*)chunkUnit, chunkUnitLen); +size_t WiFiClient::write_P(PGM_P buf, size_t size) +{ + if (!_client || !size) + { + return 0; } - return size; + ProgmemSource ps(buf, size); + ChunkedStrategy strategy(ps, size, _timeout); + return _client->write(strategy); } int WiFiClient::available() diff --git a/libraries/ESP8266WiFi/src/WiFiClient.h b/libraries/ESP8266WiFi/src/WiFiClient.h index 340cd62b9f..dabd65f26e 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.h +++ b/libraries/ESP8266WiFi/src/WiFiClient.h @@ -49,8 +49,10 @@ class WiFiClient : public Client, public SList { virtual size_t write(uint8_t); virtual size_t write(const uint8_t *buf, size_t size); size_t write_P(PGM_P buf, size_t size); - template - size_t write(T& source, size_t unitSize); + size_t write(Stream& stream); + + // This one is deprecated, use write(Stream& instead) + size_t write(Stream& stream, size_t unitSize) __attribute__ ((deprecated)); virtual int available(); virtual int read(); @@ -73,28 +75,6 @@ class WiFiClient : public Client, public SList { void setNoDelay(bool nodelay); static void setLocalPortStart(uint16_t port) { _localPort = port; } - template size_t write(T &src){ - uint8_t obuf[WIFICLIENT_MAX_PACKET_SIZE]; - size_t doneLen = 0; - size_t sentLen; - int i; - - while (src.available() > WIFICLIENT_MAX_PACKET_SIZE){ - src.read(obuf, WIFICLIENT_MAX_PACKET_SIZE); - sentLen = write(obuf, WIFICLIENT_MAX_PACKET_SIZE); - doneLen = doneLen + sentLen; - if(sentLen != WIFICLIENT_MAX_PACKET_SIZE){ - return doneLen; - } - } - - uint16_t leftLen = src.available(); - src.read(obuf, leftLen); - sentLen = write(obuf, leftLen); - doneLen = doneLen + sentLen; - return doneLen; - } - friend class WiFiServer; using Print::write; @@ -114,24 +94,4 @@ class WiFiClient : public Client, public SList { static uint16_t _localPort; }; - -template -inline size_t WiFiClient::write(T& source, size_t unitSize) { - std::unique_ptr buffer(new uint8_t[unitSize]); - size_t size_sent = 0; - while(true) { - size_t left = source.available(); - if (!left) - break; - size_t will_send = (left < unitSize) ? left : unitSize; - source.read(buffer.get(), will_send); - size_t cb = write(buffer.get(), will_send); - size_sent += cb; - if (cb != will_send) { - break; - } - } - return size_sent; -} - #endif diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecure.cpp b/libraries/ESP8266WiFi/src/WiFiClientSecure.cpp index 505954bee0..21878a8a34 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecure.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClientSecure.cpp @@ -39,6 +39,7 @@ extern "C" #include "lwip/netif.h" #include "include/ClientContext.h" #include "c_types.h" +#include "include/DataStrategyImpl.h" #ifdef DEBUG_ESP_SSL #define DEBUG_SSL @@ -468,7 +469,10 @@ extern "C" int ax_port_write(int fd, uint8_t* buffer, size_t count) { errno = EIO; return -1; } - size_t cb = _client->write((const char*) buffer, count); + + // TODO: add a way to pass timeout here (e.g. through ClientContext?) + BufferStrategy strategy(buffer, count, 5000); + size_t cb = _client->write(strategy); if (cb != count) { errno = EAGAIN; } diff --git a/libraries/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index d725aa38f5..1635efcaf5 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -29,15 +29,18 @@ typedef void (*discard_cb_t)(void*, ClientContext*); extern "C" void esp_yield(); extern "C" void esp_schedule(); +#include "DataStrategy.h" + 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), _send_waiting(false) { + _pcb(pcb), _rx_buf(0), _rx_buf_offset(0), _discard_cb(discard_cb), _discard_cb_arg(discard_cb_arg), _refcnt(0), _next(0) { tcp_setprio(pcb, TCP_PRIO_MIN); tcp_arg(pcb, this); tcp_recv(pcb, (tcp_recv_fn) &_s_recv); tcp_sent(pcb, &_s_sent); tcp_err(pcb, &_s_error); + tcp_poll(pcb, &_s_poll, 1); } err_t abort(){ @@ -47,6 +50,7 @@ class ClientContext { tcp_sent(_pcb, NULL); tcp_recv(_pcb, NULL); tcp_err(_pcb, NULL); + tcp_poll(_pcb, NULL, 0); tcp_abort(_pcb); _pcb = 0; } @@ -61,6 +65,7 @@ class ClientContext { tcp_sent(_pcb, NULL); tcp_recv(_pcb, NULL); tcp_err(_pcb, NULL); + tcp_poll(_pcb, NULL, 0); err = tcp_close(_pcb); if(err != ERR_OK) { DEBUGV(":tc err %d\r\n", err); @@ -211,40 +216,38 @@ class ClientContext { return _pcb->state; } - size_t write(const char* data, size_t size) { - if(!_pcb) { - DEBUGV(":wr !_pcb\r\n"); + size_t getSendBufferSize() + { + if (!_pcb) { return 0; } + return tcp_sndbuf(_pcb); + } - if(size == 0) { - return 0; - } - - size_t room = tcp_sndbuf(_pcb); - size_t will_send = (room < size) ? room : size; - err_t err = tcp_write(_pcb, data, will_send, 0); - if(err != ERR_OK) { - DEBUGV(":wr !ERR_OK\r\n"); - return 0; + bool write(const uint8_t* data, size_t size) + { + if (!_pcb) { + return false; } + err_t err = tcp_write(_pcb, data, size, 0); + tcp_output(_pcb); + return err == ERR_OK; + } - _size_sent = will_send; - DEBUGV(":wr\r\n"); - tcp_output( _pcb ); - _send_waiting = true; - delay(5000); // max send timeout - _send_waiting = false; - DEBUGV(":ww\r\n"); - return will_send - _size_sent; + size_t write(DataStrategy& strategy) { + _strategy = &strategy; + auto ret = strategy.write(*this); + _strategy = nullptr; + return ret; } private: err_t _sent(tcp_pcb* pcb, uint16_t len) { DEBUGV(":sent %d\r\n", len); - _size_sent -= len; - if(_size_sent == 0 && _send_waiting) esp_schedule(); + if (_strategy) { + _strategy->on_sent(*this, len); + } return ERR_OK; } @@ -279,8 +282,6 @@ class ClientContext { } if(_rx_buf) { - // there is some unread data - // chain the new pbuf to the existing one DEBUGV(":rch %d, %d\r\n", _rx_buf->tot_len, pb->tot_len); pbuf_cat(_rx_buf, pb); } else { @@ -292,18 +293,21 @@ class ClientContext { } void _error(err_t err) { - DEBUGV(":er %d %d %d\r\n", err, _size_sent, _send_waiting); + DEBUGV(":er %d %08x\r\n", err, (uint32_t) _strategy); tcp_arg(_pcb, NULL); tcp_sent(_pcb, NULL); tcp_recv(_pcb, NULL); tcp_err(_pcb, NULL); _pcb = NULL; - if(_size_sent && _send_waiting) { - esp_schedule(); + if (_strategy) { + _strategy->on_error(*this); } } err_t _poll(tcp_pcb* pcb) { + if (_strategy) { + _strategy->on_poll(*this); + } return ERR_OK; } @@ -335,8 +339,7 @@ class ClientContext { int _refcnt; ClientContext* _next; - size_t _size_sent; - bool _send_waiting; + DataStrategy* _strategy = nullptr; }; #endif//CLIENTCONTEXT_H diff --git a/libraries/ESP8266WiFi/src/include/DataStrategy.h b/libraries/ESP8266WiFi/src/include/DataStrategy.h new file mode 100644 index 0000000000..a22164e100 --- /dev/null +++ b/libraries/ESP8266WiFi/src/include/DataStrategy.h @@ -0,0 +1,25 @@ +/* DataStrategy.h - interface for writing data to ClientContext + * Copyright (c) 2016 Ivan Grokhotkov. All rights reserved. + * This file is distributed under MIT license. + */ + +#ifndef DATASTRATEGY_H +#define DATASTRATEGY_H + + +#include "ClientContext.h" + +class DataStrategy +{ +public: + virtual ~DataStrategy() {} + virtual size_t write(ClientContext& ctx) = 0; + virtual void on_sent(ClientContext& ctx, size_t size) = 0; + virtual void on_error(ClientContext& ctx) = 0; + virtual void on_poll(ClientContext& ctx) = 0; +}; + +/// in DataStrategyImpl.h + + +#endif//DATASTRATEGY_H diff --git a/libraries/ESP8266WiFi/src/include/DataStrategyImpl.h b/libraries/ESP8266WiFi/src/include/DataStrategyImpl.h new file mode 100644 index 0000000000..86a1de5d41 --- /dev/null +++ b/libraries/ESP8266WiFi/src/include/DataStrategyImpl.h @@ -0,0 +1,261 @@ +/* DataStrategyImpl.h - a few strategies for writing data to ClientContext, + plus some helper classes + * Copyright (c) 2016 Ivan Grokhotkov. All rights reserved. + * This file is distributed under MIT license. + */ + +#ifndef DATASTRATEGYIMPL_H +#define DATASTRATEGYIMPL_H + +#include "DataStrategy.h" +#include + +class BufferStrategy : public DataStrategy +{ +public: + BufferStrategy(const uint8_t* buf, size_t size, uint32_t timeout) + : _buf(buf), + _size(size), + _timeout(timeout + millis()) + { + } + + size_t write(ClientContext& ctx) override + { + write_some(ctx); + while (!_done) { + esp_yield(); + } + return _written; + } + + void write_some(ClientContext& ctx) + { + size_t can_write = ctx.getSendBufferSize(); + size_t will_write = (can_write < _size) ? can_write : _size; + if (!ctx.write(_buf, will_write)) { + end(); + return; + } + _buf += will_write; + _size -= will_write; + _queued += will_write; + } + + void on_sent(ClientContext& ctx, size_t size) override + { + if (_size > 0) { + write_some(ctx); + } + _queued -= size; + _written += size; + if (_queued == 0) { + end(); + } + } + + void on_error(ClientContext& ctx) override + { + if (_queued > 0) { + end(); + } + } + + void on_poll(ClientContext& ctx) override + { + if (millis() > _timeout && _queued > 0) { + end(); + } + } + + void end() + { + if (!_done) { + _done = true; + esp_schedule(); + } + } + +protected: + const uint8_t * _buf; + size_t _size; + size_t _written = 0; + size_t _queued = 0; + uint32_t _timeout = 0; + bool _done = false; +}; + +class BufferLink +{ +public: + BufferLink(size_t size, BufferLink* prev) : + _size(size), + _data(new uint8_t[size]) + { + if (prev) { + prev->_next = this; + } + } + + ~BufferLink() + { + delete[] _data; + } + + uint8_t* data() const + { + return _data; + } + + size_t size() const + { + return _size; + } + + BufferLink* next() const + { + return _next; + } + +protected: + size_t _size; + uint8_t* _data; + BufferLink* _next = nullptr; +}; + +class ProgmemSource +{ +public: + ProgmemSource(PGM_P buf, size_t size) : + _buf(buf), + _left(size) + { + } + + size_t readBytes(char* dst, size_t size) + { + size_t will_read = (_left < size) ? _left : size; + memcpy_P((void*)dst, (PGM_VOID_P)_buf, will_read); + return will_read; + } + +protected: + PGM_P _buf; + size_t _left; +}; + +template +class ChunkedStrategy : public DataStrategy +{ +public: + ChunkedStrategy(TSource& source, size_t size, unsigned timeout) : + _source(source), + _size(size), + _timeout(timeout) + { + } + + ~ChunkedStrategy() override + { + while (_buffers_head) { + auto tmp = _buffers_head; + _buffers_head = _buffers_head->next(); + delete tmp; + } + } + + size_t write(ClientContext& ctx) override + { + write_some(ctx); + while (!_done) { + esp_yield(); + } + return _written; + } + + void write_some(ClientContext& ctx) + { + size_t can_write = ctx.getSendBufferSize(); + size_t will_write = (can_write < _size) ? can_write : _size; + + BufferLink* new_buf = new BufferLink(will_write, _buffers_tail); + if (!_buffers_head) { + _buffers_head = new_buf; + } + _buffers_tail = new_buf; + size_t cb = _source.readBytes((char*) new_buf->data(), will_write); + if (cb < will_write) { + end(); + return; + } + if (!ctx.write(new_buf->data(), will_write)) { + end(); + return; + } + _size -= will_write; + _last_write_time = millis(); + } + + void on_sent(ClientContext& ctx, size_t size) override + { + if (_size > 0) { + write_some(ctx); + } + auto size_to_remove = size; + while (size_to_remove) { + assert(_buffers_head != nullptr); + size_t buf_size = _buffers_head->size() - _offset; + if (buf_size > size_to_remove) { + _offset += size_to_remove; + break; + } + auto tmp = _buffers_head; + _buffers_head = _buffers_head->next(); + delete tmp; + _offset = 0; + size_to_remove -= buf_size; + } + _written += size; + if (_buffers_head == nullptr) { + end(); + } + } + + void on_error(ClientContext& ctx) override + { + if (_buffers_head != nullptr) { + end(); + } + } + + void on_poll(ClientContext& ctx) override + { + if (_last_write_time != 0 && + millis() - _last_write_time > _timeout && + _buffers_head != nullptr) { + end(); + } + } + + void end() + { + if (!_done) { + _done = true; + esp_schedule(); + } + } + +protected: + TSource& _source; + BufferLink* _buffers_head = nullptr; + BufferLink* _buffers_tail = nullptr; + size_t _size; + size_t _written = 0; + uint32_t _timeout = 0; + size_t _offset = 0; + bool _done = false; + uint32_t _last_write_time = 0; +}; + + +#endif//DATASTRATEGYIMPL_H From 25962b2ca901766fd0f59606dd15a7c0895b35a1 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 4 Feb 2016 14:17:31 +0300 Subject: [PATCH 2/6] Fix formatting --- .../ESP8266WiFi/src/include/ClientContext.h | 567 ++++++++++-------- 1 file changed, 315 insertions(+), 252 deletions(-) diff --git a/libraries/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index 1635efcaf5..b021e5f2ef 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -31,315 +31,378 @@ extern "C" void esp_schedule(); #include "DataStrategy.h" -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) { - tcp_setprio(pcb, TCP_PRIO_MIN); - tcp_arg(pcb, this); - tcp_recv(pcb, (tcp_recv_fn) &_s_recv); - tcp_sent(pcb, &_s_sent); - tcp_err(pcb, &_s_error); - tcp_poll(pcb, &_s_poll, 1); - } - - err_t abort(){ - if(_pcb) { - DEBUGV(":abort\r\n"); - tcp_arg(_pcb, NULL); - tcp_sent(_pcb, NULL); - tcp_recv(_pcb, NULL); - tcp_err(_pcb, NULL); - tcp_poll(_pcb, NULL, 0); - tcp_abort(_pcb); - _pcb = 0; - } - return ERR_ABRT; - } - - err_t close(){ - err_t err = ERR_OK; - if(_pcb) { - DEBUGV(":close\r\n"); - tcp_arg(_pcb, NULL); - tcp_sent(_pcb, NULL); - tcp_recv(_pcb, NULL); - tcp_err(_pcb, NULL); - tcp_poll(_pcb, NULL, 0); - err = tcp_close(_pcb); - if(err != ERR_OK) { - DEBUGV(":tc err %d\r\n", err); - tcp_abort(_pcb); - err = ERR_ABRT; - } - _pcb = 0; - } - return err; - } - - ~ClientContext() { - } - - ClientContext* next() const { - return _next; - } - - ClientContext* next(ClientContext* new_next) { - _next = new_next; - return _next; - } - - void ref() { - ++_refcnt; - DEBUGV(":ref %d\r\n", _refcnt); - } - - void unref() { - if(this != 0) { - DEBUGV(":ur %d\r\n", _refcnt); - if(--_refcnt == 0) { - flush(); - close(); - if(_discard_cb) - _discard_cb(_discard_cb_arg, this); - DEBUGV(":del\r\n"); - delete this; +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) + { + tcp_setprio(pcb, TCP_PRIO_MIN); + tcp_arg(pcb, this); + tcp_recv(pcb, (tcp_recv_fn) &_s_recv); + tcp_sent(pcb, &_s_sent); + tcp_err(pcb, &_s_error); + tcp_poll(pcb, &_s_poll, 1); + } + + err_t abort() + { + if(_pcb) { + DEBUGV(":abort\r\n"); + tcp_arg(_pcb, NULL); + tcp_sent(_pcb, NULL); + tcp_recv(_pcb, NULL); + tcp_err(_pcb, NULL); + tcp_poll(_pcb, NULL, 0); + tcp_abort(_pcb); + _pcb = 0; + } + return ERR_ABRT; + } + + err_t close() + { + err_t err = ERR_OK; + if(_pcb) { + DEBUGV(":close\r\n"); + tcp_arg(_pcb, NULL); + tcp_sent(_pcb, NULL); + tcp_recv(_pcb, NULL); + tcp_err(_pcb, NULL); + tcp_poll(_pcb, NULL, 0); + err = tcp_close(_pcb); + if(err != ERR_OK) { + DEBUGV(":tc err %d\r\n", err); + tcp_abort(_pcb); + err = ERR_ABRT; + } + _pcb = 0; + } + return err; + } + + ~ClientContext() + { + } + + ClientContext* next() const + { + return _next; + } + + ClientContext* next(ClientContext* new_next) + { + _next = new_next; + return _next; + } + + void ref() + { + ++_refcnt; + DEBUGV(":ref %d\r\n", _refcnt); + } + + void unref() + { + if(this != 0) { + DEBUGV(":ur %d\r\n", _refcnt); + if(--_refcnt == 0) { + flush(); + close(); + if(_discard_cb) { + _discard_cb(_discard_cb_arg, this); } + DEBUGV(":del\r\n"); + delete this; } } + } - void setNoDelay(bool nodelay){ - if(!_pcb) return; - if(nodelay) tcp_nagle_disable(_pcb); - else tcp_nagle_enable(_pcb); + void setNoDelay(bool nodelay) + { + if(!_pcb) { + return; } - - bool getNoDelay(){ - if(!_pcb) return false; - return tcp_nagle_disabled(_pcb); + if(nodelay) { + tcp_nagle_disable(_pcb); + } else { + tcp_nagle_enable(_pcb); } + } - uint32_t getRemoteAddress() { - if(!_pcb) return 0; - - return _pcb->remote_ip.addr; + bool getNoDelay() + { + if(!_pcb) { + return false; } + return tcp_nagle_disabled(_pcb); + } - uint16_t getRemotePort() { - if(!_pcb) return 0; - - return _pcb->remote_port; + uint32_t getRemoteAddress() + { + if(!_pcb) { + return 0; } - uint32_t getLocalAddress() { - if(!_pcb) return 0; + return _pcb->remote_ip.addr; + } - return _pcb->local_ip.addr; + uint16_t getRemotePort() + { + if(!_pcb) { + return 0; } - uint16_t getLocalPort() { - if(!_pcb) return 0; + return _pcb->remote_port; + } - return _pcb->local_port; + uint32_t getLocalAddress() + { + if(!_pcb) { + return 0; } - size_t getSize() const { - if(!_rx_buf) return 0; + return _pcb->local_ip.addr; + } - return _rx_buf->tot_len - _rx_buf_offset; + uint16_t getLocalPort() + { + if(!_pcb) { + return 0; } - char read() { - if(!_rx_buf) return 0; + return _pcb->local_port; + } - char c = reinterpret_cast(_rx_buf->payload)[_rx_buf_offset]; - _consume(1); - return c; + size_t getSize() const + { + if(!_rx_buf) { + return 0; } - size_t read(char* dst, size_t size) { - if(!_rx_buf) return 0; - - size_t max_size = _rx_buf->tot_len - _rx_buf_offset; - size = (size < max_size) ? size : max_size; - - DEBUGV(":rd %d, %d, %d\r\n", size, _rx_buf->tot_len, _rx_buf_offset); - size_t size_read = 0; - while(size) { - size_t buf_size = _rx_buf->len - _rx_buf_offset; - size_t copy_size = (size < buf_size) ? size : buf_size; - DEBUGV(":rdi %d, %d\r\n", buf_size, copy_size); - os_memcpy(dst, reinterpret_cast(_rx_buf->payload) + _rx_buf_offset, copy_size); - dst += copy_size; - _consume(copy_size); - size -= copy_size; - size_read += copy_size; - } - return size_read; + return _rx_buf->tot_len - _rx_buf_offset; + } + + char read() + { + if(!_rx_buf) { + return 0; } - char peek() { - if(!_rx_buf) return 0; + char c = reinterpret_cast(_rx_buf->payload)[_rx_buf_offset]; + _consume(1); + return c; + } - return reinterpret_cast(_rx_buf->payload)[_rx_buf_offset]; + size_t read(char* dst, size_t size) + { + if(!_rx_buf) { + return 0; } - size_t peekBytes(char *dst, size_t size) { - if(!_rx_buf) return 0; + size_t max_size = _rx_buf->tot_len - _rx_buf_offset; + size = (size < max_size) ? size : max_size; - size_t max_size = _rx_buf->tot_len - _rx_buf_offset; - size = (size < max_size) ? size : max_size; - - DEBUGV(":pd %d, %d, %d\r\n", size, _rx_buf->tot_len, _rx_buf_offset); + DEBUGV(":rd %d, %d, %d\r\n", size, _rx_buf->tot_len, _rx_buf_offset); + size_t size_read = 0; + while(size) { size_t buf_size = _rx_buf->len - _rx_buf_offset; size_t copy_size = (size < buf_size) ? size : buf_size; - DEBUGV(":rpi %d, %d\r\n", buf_size, copy_size); + DEBUGV(":rdi %d, %d\r\n", buf_size, copy_size); os_memcpy(dst, reinterpret_cast(_rx_buf->payload) + _rx_buf_offset, copy_size); - return copy_size; - } - - void flush() { - if(!_rx_buf) { - return; - } + dst += copy_size; + _consume(copy_size); + size -= copy_size; + size_read += copy_size; + } + return size_read; + } + + char peek() + { + if(!_rx_buf) { + return 0; + } + + return reinterpret_cast(_rx_buf->payload)[_rx_buf_offset]; + } + + size_t peekBytes(char *dst, size_t size) + { + if(!_rx_buf) { + return 0; + } + + size_t max_size = _rx_buf->tot_len - _rx_buf_offset; + size = (size < max_size) ? size : max_size; + + DEBUGV(":pd %d, %d, %d\r\n", size, _rx_buf->tot_len, _rx_buf_offset); + size_t buf_size = _rx_buf->len - _rx_buf_offset; + size_t copy_size = (size < buf_size) ? size : buf_size; + DEBUGV(":rpi %d, %d\r\n", buf_size, copy_size); + os_memcpy(dst, reinterpret_cast(_rx_buf->payload) + _rx_buf_offset, copy_size); + return copy_size; + } + + void flush() + { + if(!_rx_buf) { + return; + } + if(_pcb) { + tcp_recved(_pcb, (size_t) _rx_buf->tot_len); + } + pbuf_free(_rx_buf); + _rx_buf = 0; + _rx_buf_offset = 0; + } + + uint8_t state() const + { + if(!_pcb) { + return CLOSED; + } + + return _pcb->state; + } + + size_t getSendBufferSize() + { + if (!_pcb) { + return 0; + } + return tcp_sndbuf(_pcb); + } + + bool write(const uint8_t* data, size_t size) + { + if (!_pcb) { + return false; + } + err_t err = tcp_write(_pcb, data, size, 0); + tcp_output(_pcb); + return err == ERR_OK; + } + + size_t write(DataStrategy& strategy) + { + _strategy = &strategy; + auto ret = strategy.write(*this); + _strategy = nullptr; + return ret; + } + +private: + + err_t _sent(tcp_pcb* pcb, uint16_t len) + { + DEBUGV(":sent %d\r\n", len); + if (_strategy) { + _strategy->on_sent(*this, len); + } + return ERR_OK; + } + + void _consume(size_t 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, (size_t) _rx_buf->tot_len); + tcp_recved(_pcb, _rx_buf->len); } pbuf_free(_rx_buf); _rx_buf = 0; _rx_buf_offset = 0; - } - - uint8_t state() const { - if(!_pcb) return CLOSED; - - return _pcb->state; - } - - size_t getSendBufferSize() - { - if (!_pcb) { - return 0; - } - return tcp_sndbuf(_pcb); - } - - bool write(const uint8_t* data, size_t size) - { - if (!_pcb) { - return false; + } else { + DEBUGV(":c %d, %d, %d\r\n", size, _rx_buf->len, _rx_buf->tot_len); + auto head = _rx_buf; + _rx_buf = _rx_buf->next; + _rx_buf_offset = 0; + pbuf_ref(_rx_buf); + if(_pcb) { + tcp_recved(_pcb, head->len); } - err_t err = tcp_write(_pcb, data, size, 0); - tcp_output(_pcb); - return err == ERR_OK; - } - - size_t write(DataStrategy& strategy) { - _strategy = &strategy; - auto ret = strategy.write(*this); - _strategy = nullptr; - return ret; + pbuf_free(head); } + } - private: + int32_t _recv(tcp_pcb* pcb, pbuf* pb, err_t err) + { - err_t _sent(tcp_pcb* pcb, uint16_t len) { - DEBUGV(":sent %d\r\n", len); - if (_strategy) { - _strategy->on_sent(*this, len); - } - return ERR_OK; - } - - void _consume(size_t 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; - } else { - DEBUGV(":c %d, %d, %d\r\n", size, _rx_buf->len, _rx_buf->tot_len); - auto head = _rx_buf; - _rx_buf = _rx_buf->next; - _rx_buf_offset = 0; - pbuf_ref(_rx_buf); - if(_pcb) tcp_recved(_pcb, head->len); - pbuf_free(head); - } + if(pb == 0) { // connection closed + DEBUGV(":rcl\r\n"); + abort(); + return ERR_ABRT; } - int32_t _recv(tcp_pcb* pcb, pbuf* pb, err_t err) { - - if(pb == 0) // connection closed - { - DEBUGV(":rcl\r\n"); - abort(); - return ERR_ABRT; - } - - if(_rx_buf) { - DEBUGV(":rch %d, %d\r\n", _rx_buf->tot_len, pb->tot_len); - pbuf_cat(_rx_buf, pb); - } else { - DEBUGV(":rn %d\r\n", pb->tot_len); - _rx_buf = pb; - _rx_buf_offset = 0; - } - return ERR_OK; + if(_rx_buf) { + DEBUGV(":rch %d, %d\r\n", _rx_buf->tot_len, pb->tot_len); + pbuf_cat(_rx_buf, pb); + } else { + DEBUGV(":rn %d\r\n", pb->tot_len); + _rx_buf = pb; + _rx_buf_offset = 0; } + return ERR_OK; + } - void _error(err_t err) { - DEBUGV(":er %d %08x\r\n", err, (uint32_t) _strategy); - tcp_arg(_pcb, NULL); - tcp_sent(_pcb, NULL); - tcp_recv(_pcb, NULL); - tcp_err(_pcb, NULL); - _pcb = NULL; - if (_strategy) { - _strategy->on_error(*this); - } + void _error(err_t err) + { + DEBUGV(":er %d %08x\r\n", err, (uint32_t) _strategy); + tcp_arg(_pcb, NULL); + tcp_sent(_pcb, NULL); + tcp_recv(_pcb, NULL); + tcp_err(_pcb, NULL); + _pcb = NULL; + if (_strategy) { + _strategy->on_error(*this); } + } - err_t _poll(tcp_pcb* pcb) { - if (_strategy) { - _strategy->on_poll(*this); - } - return ERR_OK; + err_t _poll(tcp_pcb* pcb) + { + if (_strategy) { + _strategy->on_poll(*this); } + return ERR_OK; + } - static int32_t _s_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *pb, err_t err) { - return reinterpret_cast(arg)->_recv(tpcb, pb, err); - } + static int32_t _s_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *pb, err_t err) + { + return reinterpret_cast(arg)->_recv(tpcb, pb, err); + } - static void _s_error(void *arg, err_t err) { - reinterpret_cast(arg)->_error(err); - } + static void _s_error(void *arg, err_t err) + { + reinterpret_cast(arg)->_error(err); + } - static err_t _s_poll(void *arg, struct tcp_pcb *tpcb) { - return reinterpret_cast(arg)->_poll(tpcb); - } + static err_t _s_poll(void *arg, struct tcp_pcb *tpcb) + { + return reinterpret_cast(arg)->_poll(tpcb); + } - static err_t _s_sent(void *arg, struct tcp_pcb *tpcb, uint16_t len) { - return reinterpret_cast(arg)->_sent(tpcb, len); - } + static err_t _s_sent(void *arg, struct tcp_pcb *tpcb, uint16_t len) + { + return reinterpret_cast(arg)->_sent(tpcb, len); + } - private: - tcp_pcb* _pcb; +private: + tcp_pcb* _pcb; - pbuf* _rx_buf; - size_t _rx_buf_offset; + pbuf* _rx_buf; + size_t _rx_buf_offset; - discard_cb_t _discard_cb; - void* _discard_cb_arg; + discard_cb_t _discard_cb; + void* _discard_cb_arg; - int _refcnt; - ClientContext* _next; + int _refcnt; + ClientContext* _next; - DataStrategy* _strategy = nullptr; + DataStrategy* _strategy = nullptr; }; #endif//CLIENTCONTEXT_H From 2ebfed4cc9444d69dcba5e023b1095290b1a340b Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 4 Feb 2016 16:27:09 +0300 Subject: [PATCH 3/6] ESP8266WebServer: delegate writing to WiFiClient --- .../ESP8266WebServer/src/ESP8266WebServer.cpp | 58 +------------------ 1 file changed, 3 insertions(+), 55 deletions(-) diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp index a01abf4165..86566e0657 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp @@ -307,67 +307,15 @@ void ESP8266WebServer::send(int code, const String& content_type, const String& } void ESP8266WebServer::sendContent(const String& content) { - const size_t unit_size = HTTP_DOWNLOAD_UNIT_SIZE; - size_t size_to_send = content.length(); - const char* send_start = content.c_str(); - - while (size_to_send) { - size_t will_send = (size_to_send < unit_size) ? size_to_send : unit_size; - size_t sent = _currentClient.write(send_start, will_send); - if (sent == 0) { - break; - } - size_to_send -= sent; - send_start += sent; - } + _currentClient.write(content.c_str(), content.length()); } void ESP8266WebServer::sendContent_P(PGM_P content) { - char contentUnit[HTTP_DOWNLOAD_UNIT_SIZE + 1]; - - contentUnit[HTTP_DOWNLOAD_UNIT_SIZE] = '\0'; - - while (content != NULL) { - size_t contentUnitLen; - PGM_P contentNext; - - // due to the memccpy signature, lots of casts are needed - contentNext = (PGM_P)memccpy_P((void*)contentUnit, (PGM_VOID_P)content, 0, HTTP_DOWNLOAD_UNIT_SIZE); - - if (contentNext == NULL) { - // no terminator, more data available - content += HTTP_DOWNLOAD_UNIT_SIZE; - contentUnitLen = HTTP_DOWNLOAD_UNIT_SIZE; - } - else { - // reached terminator. Do not send the terminator - contentUnitLen = contentNext - contentUnit - 1; - content = NULL; - } - - // write is so overloaded, had to use the cast to get it pick the right one - _currentClient.write((const char*)contentUnit, contentUnitLen); - } + _currentClient.write_P(content, strlen_P(content)); } void ESP8266WebServer::sendContent_P(PGM_P content, size_t size) { - char contentUnit[HTTP_DOWNLOAD_UNIT_SIZE + 1]; - contentUnit[HTTP_DOWNLOAD_UNIT_SIZE] = '\0'; - size_t remaining_size = size; - - while (content != NULL && remaining_size > 0) { - size_t contentUnitLen = HTTP_DOWNLOAD_UNIT_SIZE; - - if (remaining_size < HTTP_DOWNLOAD_UNIT_SIZE) contentUnitLen = remaining_size; - // due to the memcpy signature, lots of casts are needed - memcpy_P((void*)contentUnit, (PGM_VOID_P)content, contentUnitLen); - - content += contentUnitLen; - remaining_size -= contentUnitLen; - - // write is so overloaded, had to use the cast to get it pick the right one - _currentClient.write((const char*)contentUnit, contentUnitLen); - } + _currentClient.write_P(content, size); } From 94b3f415f24e1de0f4196a9db0e23c84a66ca5d9 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 4 Feb 2016 16:35:21 +0300 Subject: [PATCH 4/6] ESP8266WebServer: set write timeout before sending content --- libraries/ESP8266WebServer/src/ESP8266WebServer.cpp | 3 +-- libraries/ESP8266WebServer/src/ESP8266WebServer.h | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp index 86566e0657..7ee49273ac 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp @@ -193,7 +193,7 @@ void ESP8266WebServer::handleClient() { _currentStatus = HC_NONE; return; } - + _currentClient.setTimeout(HTTP_MAX_SEND_WAIT); _contentLength = CONTENT_LENGTH_NOT_SET; _handleRequest(); @@ -270,7 +270,6 @@ void ESP8266WebServer::send(int code, const char* content_type, const String& co String header; _prepareHeader(header, code, content_type, content.length()); sendContent(header); - sendContent(content); } diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.h b/libraries/ESP8266WebServer/src/ESP8266WebServer.h index 935ca5cb84..58a73fd586 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.h @@ -34,6 +34,7 @@ enum HTTPClientStatus { HC_NONE, HC_WAIT_READ, HC_WAIT_CLOSE }; #define HTTP_DOWNLOAD_UNIT_SIZE 1460 #define HTTP_UPLOAD_BUFLEN 2048 #define HTTP_MAX_DATA_WAIT 1000 //ms to wait for the client to send the request +#define HTTP_MAX_SEND_WAIT 5000 //ms to wait for data chunk to be ACKed #define HTTP_MAX_CLOSE_WAIT 2000 //ms to wait for the client to close the connection #define CONTENT_LENGTH_UNKNOWN ((size_t) -1) From 7e1e61174afa1ee75edb633551e858bfafbcbbb4 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 4 Feb 2016 17:36:22 +0300 Subject: [PATCH 5/6] WiFiClient: fix progmem reads --- libraries/ESP8266WebServer/src/ESP8266WebServer.cpp | 3 +++ libraries/ESP8266WebServer/src/ESP8266WebServer.h | 2 +- libraries/ESP8266WiFi/src/include/DataStrategyImpl.h | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp index 7ee49273ac..c22c1f6806 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp @@ -241,6 +241,9 @@ void ESP8266WebServer::sendHeader(const String& name, const String& value, bool } } +void ESP8266WebServer::setContentLength(size_t contentLength) { + _contentLength = contentLength; +} void ESP8266WebServer::_prepareHeader(String& response, int code, const char* content_type, size_t contentLength) { response = "HTTP/1.1 "; diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.h b/libraries/ESP8266WebServer/src/ESP8266WebServer.h index 58a73fd586..7d210c795f 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.h @@ -112,7 +112,7 @@ class ESP8266WebServer void send_P(int code, PGM_P content_type, PGM_P content); void send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength); - void setContentLength(size_t contentLength) { _contentLength = contentLength; } + void setContentLength(size_t contentLength); void sendHeader(const String& name, const String& value, bool first = false); void sendContent(const String& content); void sendContent_P(PGM_P content); diff --git a/libraries/ESP8266WiFi/src/include/DataStrategyImpl.h b/libraries/ESP8266WiFi/src/include/DataStrategyImpl.h index 86a1de5d41..b9317eb414 100644 --- a/libraries/ESP8266WiFi/src/include/DataStrategyImpl.h +++ b/libraries/ESP8266WiFi/src/include/DataStrategyImpl.h @@ -136,6 +136,8 @@ class ProgmemSource { size_t will_read = (_left < size) ? _left : size; memcpy_P((void*)dst, (PGM_VOID_P)_buf, will_read); + _left -= will_read; + _buf += will_read; return will_read; } From b61c33de2ddb23fb0f257f9d51f95d2bd61035c8 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 4 Feb 2016 17:37:04 +0300 Subject: [PATCH 6/6] DataStrategyImpl: make some functions protected --- .../ESP8266WiFi/src/include/DataStrategy.h | 3 - .../src/include/DataStrategyImpl.h | 97 ++++++++++--------- 2 files changed, 49 insertions(+), 51 deletions(-) diff --git a/libraries/ESP8266WiFi/src/include/DataStrategy.h b/libraries/ESP8266WiFi/src/include/DataStrategy.h index a22164e100..622f190e08 100644 --- a/libraries/ESP8266WiFi/src/include/DataStrategy.h +++ b/libraries/ESP8266WiFi/src/include/DataStrategy.h @@ -19,7 +19,4 @@ class DataStrategy virtual void on_poll(ClientContext& ctx) = 0; }; -/// in DataStrategyImpl.h - - #endif//DATASTRATEGY_H diff --git a/libraries/ESP8266WiFi/src/include/DataStrategyImpl.h b/libraries/ESP8266WiFi/src/include/DataStrategyImpl.h index b9317eb414..008634a6fa 100644 --- a/libraries/ESP8266WiFi/src/include/DataStrategyImpl.h +++ b/libraries/ESP8266WiFi/src/include/DataStrategyImpl.h @@ -22,53 +22,54 @@ class BufferStrategy : public DataStrategy size_t write(ClientContext& ctx) override { - write_some(ctx); + _write_some(ctx); while (!_done) { esp_yield(); } return _written; } - void write_some(ClientContext& ctx) - { - size_t can_write = ctx.getSendBufferSize(); - size_t will_write = (can_write < _size) ? can_write : _size; - if (!ctx.write(_buf, will_write)) { - end(); - return; - } - _buf += will_write; - _size -= will_write; - _queued += will_write; - } - void on_sent(ClientContext& ctx, size_t size) override { if (_size > 0) { - write_some(ctx); + _write_some(ctx); } _queued -= size; _written += size; if (_queued == 0) { - end(); + _end(); } } void on_error(ClientContext& ctx) override { if (_queued > 0) { - end(); + _end(); } } void on_poll(ClientContext& ctx) override { if (millis() > _timeout && _queued > 0) { - end(); + _end(); + } + } + +protected: + void _write_some(ClientContext& ctx) + { + size_t can_write = ctx.getSendBufferSize(); + size_t will_write = (can_write < _size) ? can_write : _size; + if (!ctx.write(_buf, will_write)) { + _end(); + return; } + _buf += will_write; + _size -= will_write; + _queued += will_write; } - void end() + void _end() { if (!_done) { _done = true; @@ -168,40 +169,17 @@ class ChunkedStrategy : public DataStrategy size_t write(ClientContext& ctx) override { - write_some(ctx); + _write_some(ctx); while (!_done) { esp_yield(); } return _written; } - void write_some(ClientContext& ctx) - { - size_t can_write = ctx.getSendBufferSize(); - size_t will_write = (can_write < _size) ? can_write : _size; - - BufferLink* new_buf = new BufferLink(will_write, _buffers_tail); - if (!_buffers_head) { - _buffers_head = new_buf; - } - _buffers_tail = new_buf; - size_t cb = _source.readBytes((char*) new_buf->data(), will_write); - if (cb < will_write) { - end(); - return; - } - if (!ctx.write(new_buf->data(), will_write)) { - end(); - return; - } - _size -= will_write; - _last_write_time = millis(); - } - void on_sent(ClientContext& ctx, size_t size) override { if (_size > 0) { - write_some(ctx); + _write_some(ctx); } auto size_to_remove = size; while (size_to_remove) { @@ -219,14 +197,14 @@ class ChunkedStrategy : public DataStrategy } _written += size; if (_buffers_head == nullptr) { - end(); + _end(); } } void on_error(ClientContext& ctx) override { if (_buffers_head != nullptr) { - end(); + _end(); } } @@ -235,11 +213,34 @@ class ChunkedStrategy : public DataStrategy if (_last_write_time != 0 && millis() - _last_write_time > _timeout && _buffers_head != nullptr) { - end(); + _end(); } } - void end() +protected: + void _write_some(ClientContext& ctx) + { + size_t can_write = ctx.getSendBufferSize(); + size_t will_write = (can_write < _size) ? can_write : _size; + BufferLink* new_buf = new BufferLink(will_write, _buffers_tail); + if (!_buffers_head) { + _buffers_head = new_buf; + } + _buffers_tail = new_buf; + size_t cb = _source.readBytes((char*) new_buf->data(), will_write); + if (cb < will_write) { + _end(); + return; + } + if (!ctx.write(new_buf->data(), will_write)) { + _end(); + return; + } + _size -= will_write; + _last_write_time = millis(); + } + + void _end() { if (!_done) { _done = true;