diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..65c5ca8
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/README.md b/README.md
index 53328a1..4b1f78a 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,24 @@
# ESPAsyncTCP
-Lokálně udržovaná verze ESPAsyncTCP knihovny, používaná ve všech projektech s ESP8266
\ No newline at end of file
+Lokálně udržovaná verze ESPAsyncTCP knihovny, používaná ve všech projektech s ESP8266
+
+A fork of the [AsyncTCP](https://github.com/me-no-dev/AsyncTCP) library by [@me-no-dev](https://github.com/me-no-dev) for [ESPHome](https://esphome.io).
+
+### Async TCP Library for ESP8266 Arduino
+
+This is a fully asynchronous TCP library, aimed at enabling trouble-free, multi-connection network environment for Espressif's ESP8266 MCUs.
+
+This library is the base for [ESPAsyncWebServer](https://github.com/me-no-dev/ESPAsyncWebServer)
+
+## AsyncClient and AsyncServer
+The base classes on which everything else is built. They expose all possible scenarios, but are really raw and require more skills to use.
+
+## AsyncPrinter
+This class can be used to send data like any other ```Print``` interface (```Serial``` for example).
+The object then can be used outside of the Async callbacks (the loop) and receive asynchronously data using ```onData```. The object can be checked if the underlying ```AsyncClient```is connected, or hook to the ```onDisconnect``` callback.
+
+## AsyncTCPbuffer
+This class is really similar to the ```AsyncPrinter```, but it differs in the fact that it can buffer some of the incoming data.
+
+## SyncClient
+It is exactly what it sounds like. This is a standard, blocking TCP Client, similar to the one included in ```ESP8266WiFi```
diff --git a/examples/ClientServer/Client/Client.ino b/examples/ClientServer/Client/Client.ino
new file mode 100644
index 0000000..b30d791
--- /dev/null
+++ b/examples/ClientServer/Client/Client.ino
@@ -0,0 +1,62 @@
+#include
+#include
+
+extern "C" {
+#include
+#include
+}
+
+#include "config.h"
+
+static os_timer_t intervalTimer;
+
+static void replyToServer(void* arg) {
+ AsyncClient* client = reinterpret_cast(arg);
+
+ // send reply
+ if (client->space() > 32 && client->canSend()) {
+ char message[32];
+ sprintf(message, "this is from %s", WiFi.localIP().toString().c_str());
+ client->add(message, strlen(message));
+ client->send();
+ }
+}
+
+/* event callbacks */
+static void handleData(void* arg, AsyncClient* client, void *data, size_t len) {
+ Serial.printf("\n data received from %s \n", client->remoteIP().toString().c_str());
+ Serial.write((uint8_t*)data, len);
+
+ os_timer_arm(&intervalTimer, 2000, true); // schedule for reply to server at next 2s
+}
+
+void onConnect(void* arg, AsyncClient* client) {
+ Serial.printf("\n client has been connected to %s on port %d \n", SERVER_HOST_NAME, TCP_PORT);
+ replyToServer(client);
+}
+
+
+void setup() {
+ Serial.begin(115200);
+ delay(20);
+
+ // connects to access point
+ WiFi.mode(WIFI_STA);
+ WiFi.begin(SSID, PASSWORD);
+ while (WiFi.status() != WL_CONNECTED) {
+ Serial.print('.');
+ delay(500);
+ }
+
+ AsyncClient* client = new AsyncClient;
+ client->onData(&handleData, client);
+ client->onConnect(&onConnect, client);
+ client->connect(SERVER_HOST_NAME, TCP_PORT);
+
+ os_timer_disarm(&intervalTimer);
+ os_timer_setfn(&intervalTimer, &replyToServer, client);
+}
+
+void loop() {
+
+}
diff --git a/examples/ClientServer/Client/config.h b/examples/ClientServer/Client/config.h
new file mode 100644
index 0000000..cf51e91
--- /dev/null
+++ b/examples/ClientServer/Client/config.h
@@ -0,0 +1,23 @@
+#ifndef CONFIG_H
+#define CONFIG_H
+
+/*
+ * This example demonstrate how to use asynchronous client & server APIs
+ * in order to establish tcp socket connections in client server manner.
+ * server is running (on port 7050) on one ESP, acts as AP, and other clients running on
+ * remaining ESPs acts as STAs. after connection establishment between server and clients
+ * there is a simple message transfer in every 2s. clients connect to server via it's host name
+ * (in this case 'esp_server') with help of DNS service running on server side.
+ *
+ * Note: default MSS for ESPAsyncTCP is 536 byte and defualt ACK timeout is 5s.
+*/
+
+#define SSID "ESP-TEST"
+#define PASSWORD "123456789"
+
+#define SERVER_HOST_NAME "esp_server"
+
+#define TCP_PORT 7050
+#define DNS_PORT 53
+
+#endif // CONFIG_H
diff --git a/examples/ClientServer/Server/Server.ino b/examples/ClientServer/Server/Server.ino
new file mode 100644
index 0000000..c8c9b7f
--- /dev/null
+++ b/examples/ClientServer/Server/Server.ino
@@ -0,0 +1,73 @@
+#include
+#include
+#include
+#include
+
+#include "config.h"
+
+static DNSServer DNS;
+
+static std::vector clients; // a list to hold all clients
+
+ /* clients events */
+static void handleError(void* arg, AsyncClient* client, int8_t error) {
+ Serial.printf("\n connection error %s from client %s \n", client->errorToString(error), client->remoteIP().toString().c_str());
+}
+
+static void handleData(void* arg, AsyncClient* client, void *data, size_t len) {
+ Serial.printf("\n data received from client %s \n", client->remoteIP().toString().c_str());
+ Serial.write((uint8_t*)data, len);
+
+ // reply to client
+ if (client->space() > 32 && client->canSend()) {
+ char reply[32];
+ sprintf(reply, "this is from %s", SERVER_HOST_NAME);
+ client->add(reply, strlen(reply));
+ client->send();
+ }
+}
+
+static void handleDisconnect(void* arg, AsyncClient* client) {
+ Serial.printf("\n client %s disconnected \n", client->remoteIP().toString().c_str());
+}
+
+static void handleTimeOut(void* arg, AsyncClient* client, uint32_t time) {
+ Serial.printf("\n client ACK timeout ip: %s \n", client->remoteIP().toString().c_str());
+}
+
+
+/* server events */
+static void handleNewClient(void* arg, AsyncClient* client) {
+ Serial.printf("\n new client has been connected to server, ip: %s", client->remoteIP().toString().c_str());
+
+ // add to list
+ clients.push_back(client);
+
+ // register events
+ client->onData(&handleData, NULL);
+ client->onError(&handleError, NULL);
+ client->onDisconnect(&handleDisconnect, NULL);
+ client->onTimeout(&handleTimeOut, NULL);
+}
+
+void setup() {
+ Serial.begin(115200);
+ delay(20);
+
+ // create access point
+ while (!WiFi.softAP(SSID, PASSWORD, 6, false, 15)) {
+ delay(500);
+ }
+
+ // start dns server
+ if (!DNS.start(DNS_PORT, SERVER_HOST_NAME, WiFi.softAPIP()))
+ Serial.printf("\n failed to start dns service \n");
+
+ AsyncServer* server = new AsyncServer(TCP_PORT); // start listening on tcp port 7050
+ server->onClient(&handleNewClient, server);
+ server->begin();
+}
+
+void loop() {
+ DNS.processNextRequest();
+}
diff --git a/examples/ClientServer/Server/config.h b/examples/ClientServer/Server/config.h
new file mode 100644
index 0000000..cf51e91
--- /dev/null
+++ b/examples/ClientServer/Server/config.h
@@ -0,0 +1,23 @@
+#ifndef CONFIG_H
+#define CONFIG_H
+
+/*
+ * This example demonstrate how to use asynchronous client & server APIs
+ * in order to establish tcp socket connections in client server manner.
+ * server is running (on port 7050) on one ESP, acts as AP, and other clients running on
+ * remaining ESPs acts as STAs. after connection establishment between server and clients
+ * there is a simple message transfer in every 2s. clients connect to server via it's host name
+ * (in this case 'esp_server') with help of DNS service running on server side.
+ *
+ * Note: default MSS for ESPAsyncTCP is 536 byte and defualt ACK timeout is 5s.
+*/
+
+#define SSID "ESP-TEST"
+#define PASSWORD "123456789"
+
+#define SERVER_HOST_NAME "esp_server"
+
+#define TCP_PORT 7050
+#define DNS_PORT 53
+
+#endif // CONFIG_H
diff --git a/examples/SyncClient/.esp31b.skip b/examples/SyncClient/.esp31b.skip
new file mode 100644
index 0000000..e69de29
diff --git a/examples/SyncClient/SyncClient.ino b/examples/SyncClient/SyncClient.ino
new file mode 100644
index 0000000..6ecc525
--- /dev/null
+++ b/examples/SyncClient/SyncClient.ino
@@ -0,0 +1,54 @@
+#ifdef ESP8266
+#include
+#include
+#include
+#else
+#include
+#endif
+#include "ESPAsyncTCP.h"
+#include "SyncClient.h"
+
+const char* ssid = "**********";
+const char* password = "************";
+
+void setup(){
+ Serial.begin(115200);
+ WiFi.begin(ssid, password);
+ if (WiFi.waitForConnectResult() != WL_CONNECTED) {
+ Serial.printf("WiFi Failed!\n");
+ return;
+ }
+ Serial.printf("WiFi Connected!\n");
+ Serial.println(WiFi.localIP());
+#ifdef ESP8266
+ ArduinoOTA.begin();
+#endif
+
+ SyncClient client;
+ if(!client.connect("www.google.com", 80)){
+ Serial.println("Connect Failed");
+ return;
+ }
+ client.setTimeout(2);
+ if(client.printf("GET / HTTP/1.1\r\nHost: www.google.com\r\nConnection: close\r\n\r\n") > 0){
+ while(client.connected() && client.available() == 0){
+ delay(1);
+ }
+ while(client.available()){
+ Serial.write(client.read());
+ }
+ if(client.connected()){
+ client.stop();
+ }
+ } else {
+ client.stop();
+ Serial.println("Send Failed");
+ while(client.connected()) delay(0);
+ }
+}
+
+void loop(){
+#ifdef ESP8266
+ ArduinoOTA.handle();
+#endif
+}
diff --git a/library.json b/library.json
new file mode 100644
index 0000000..47c2e72
--- /dev/null
+++ b/library.json
@@ -0,0 +1,22 @@
+{
+ "name":"ESPAsyncTCP-esphome",
+ "description":"Asynchronous TCP Library for ESP8266",
+ "keywords":"async,tcp",
+ "authors":
+ {
+ "name": "Hristo Gochkov",
+ "maintainer": true
+ },
+ "repository":
+ {
+ "type": "git",
+ "url": "https://github.com/OttoWinter/ESPAsyncTCP.git"
+ },
+ "version": "1.2.2",
+ "license": "LGPL-3.0",
+ "frameworks": "arduino",
+ "platforms": "espressif8266",
+ "build": {
+ "libCompatMode": 2
+ }
+}
diff --git a/library.properties b/library.properties
new file mode 100644
index 0000000..c6b52e0
--- /dev/null
+++ b/library.properties
@@ -0,0 +1,9 @@
+name=ESPAsyncTCP-esphome
+version=1.2.2
+author=Me-No-Dev
+maintainer=Me-No-Dev
+sentence=Async TCP Library for ESP8266 and ESP31B
+paragraph=Async TCP Library for ESP8266 and ESP31B
+category=Other
+url=https://github.com/OttoWinter/ESPAsyncTCP
+architectures=*
diff --git a/src/AsyncPrinter.cpp b/src/AsyncPrinter.cpp
new file mode 100644
index 0000000..8a63f20
--- /dev/null
+++ b/src/AsyncPrinter.cpp
@@ -0,0 +1,214 @@
+/*
+ Asynchronous TCP library for Espressif MCUs
+
+ Copyright (c) 2016 Hristo Gochkov. 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
+*/
+
+#include "AsyncPrinter.h"
+
+AsyncPrinter::AsyncPrinter()
+ : _client(NULL)
+ , _data_cb(NULL)
+ , _data_arg(NULL)
+ , _close_cb(NULL)
+ , _close_arg(NULL)
+ , _tx_buffer(NULL)
+ , _tx_buffer_size(TCP_MSS)
+ , next(NULL)
+{}
+
+AsyncPrinter::AsyncPrinter(AsyncClient *client, size_t txBufLen)
+ : _client(client)
+ , _data_cb(NULL)
+ , _data_arg(NULL)
+ , _close_cb(NULL)
+ , _close_arg(NULL)
+ , _tx_buffer(NULL)
+ , _tx_buffer_size(txBufLen)
+ , next(NULL)
+{
+ _attachCallbacks();
+ _tx_buffer = new (std::nothrow) cbuf(_tx_buffer_size);
+ if(_tx_buffer == NULL) {
+ panic(); //What should we do?
+ }
+}
+
+AsyncPrinter::~AsyncPrinter(){
+ _on_close();
+}
+
+void AsyncPrinter::onData(ApDataHandler cb, void *arg){
+ _data_cb = cb;
+ _data_arg = arg;
+}
+
+void AsyncPrinter::onClose(ApCloseHandler cb, void *arg){
+ _close_cb = cb;
+ _close_arg = arg;
+}
+
+int AsyncPrinter::connect(IPAddress ip, uint16_t port){
+ if(_client != NULL && connected())
+ return 0;
+ _client = new (std::nothrow) AsyncClient();
+ if (_client == NULL) {
+ panic();
+ }
+
+ _client->onConnect([](void *obj, AsyncClient *c){ ((AsyncPrinter*)(obj))->_onConnect(c); }, this);
+ if(_client->connect(ip, port)){
+ while(_client && _client->state() < 4)
+ delay(1);
+ return connected();
+ }
+ return 0;
+}
+
+int AsyncPrinter::connect(const char *host, uint16_t port){
+ if(_client != NULL && connected())
+ return 0;
+ _client = new (std::nothrow) AsyncClient();
+ if (_client == NULL) {
+ panic();
+ }
+
+ _client->onConnect([](void *obj, AsyncClient *c){ ((AsyncPrinter*)(obj))->_onConnect(c); }, this);
+ if(_client->connect(host, port)){
+ while(_client && _client->state() < 4)
+ delay(1);
+ return connected();
+ }
+ return 0;
+}
+
+void AsyncPrinter::_onConnect(AsyncClient *c){
+ (void)c;
+ if(_tx_buffer != NULL){
+ cbuf *b = _tx_buffer;
+ _tx_buffer = NULL;
+ delete b;
+ }
+ _tx_buffer = new (std::nothrow) cbuf(_tx_buffer_size);
+ if(_tx_buffer) {
+ panic();
+ }
+
+ _attachCallbacks();
+}
+
+AsyncPrinter::operator bool(){ return connected(); }
+
+AsyncPrinter & AsyncPrinter::operator=(const AsyncPrinter &other){
+ if(_client != NULL){
+ _client->close(true);
+ _client = NULL;
+ }
+ _tx_buffer_size = other._tx_buffer_size;
+ if(_tx_buffer != NULL){
+ cbuf *b = _tx_buffer;
+ _tx_buffer = NULL;
+ delete b;
+ }
+ _tx_buffer = new (std::nothrow) cbuf(other._tx_buffer_size);
+ if(_tx_buffer == NULL) {
+ panic();
+ }
+
+ _client = other._client;
+ _attachCallbacks();
+ return *this;
+}
+
+size_t AsyncPrinter::write(uint8_t data){
+ return write(&data, 1);
+}
+
+size_t AsyncPrinter::write(const uint8_t *data, size_t len){
+ if(_tx_buffer == NULL || !connected())
+ return 0;
+ size_t toWrite = 0;
+ size_t toSend = len;
+ while(_tx_buffer->room() < toSend){
+ toWrite = _tx_buffer->room();
+ _tx_buffer->write((const char*)data, toWrite);
+ while(connected() && !_client->canSend())
+ delay(0);
+ if(!connected())
+ return 0; // or len - toSend;
+ _sendBuffer();
+ toSend -= toWrite;
+ }
+ _tx_buffer->write((const char*)(data+(len - toSend)), toSend);
+ while(connected() && !_client->canSend()) delay(0);
+ if(!connected()) return 0; // or len - toSend;
+ _sendBuffer();
+ return len;
+}
+
+bool AsyncPrinter::connected(){
+ return (_client != NULL && _client->connected());
+}
+
+void AsyncPrinter::close(){
+ if(_client != NULL)
+ _client->close(true);
+}
+
+size_t AsyncPrinter::_sendBuffer(){
+ size_t available = _tx_buffer->available();
+ if(!connected() || !_client->canSend() || available == 0)
+ return 0;
+ size_t sendable = _client->space();
+ if(sendable < available)
+ available= sendable;
+ char *out = new (std::nothrow) char[available];
+ if (out == NULL) {
+ panic(); // Connection should be aborted instead
+ }
+
+ _tx_buffer->read(out, available);
+ size_t sent = _client->write(out, available);
+ delete out;
+ return sent;
+}
+
+void AsyncPrinter::_onData(void *data, size_t len){
+ if(_data_cb)
+ _data_cb(_data_arg, this, (uint8_t*)data, len);
+}
+
+void AsyncPrinter::_on_close(){
+ if(_client != NULL){
+ _client = NULL;
+ }
+ if(_tx_buffer != NULL){
+ cbuf *b = _tx_buffer;
+ _tx_buffer = NULL;
+ delete b;
+ }
+ if(_close_cb)
+ _close_cb(_close_arg, this);
+}
+
+void AsyncPrinter::_attachCallbacks(){
+ _client->onPoll([](void *obj, AsyncClient* c){ (void)c; ((AsyncPrinter*)(obj))->_sendBuffer(); }, this);
+ _client->onAck([](void *obj, AsyncClient* c, size_t len, uint32_t time){ (void)c; (void)len; (void)time; ((AsyncPrinter*)(obj))->_sendBuffer(); }, this);
+ _client->onDisconnect([](void *obj, AsyncClient* c){ ((AsyncPrinter*)(obj))->_on_close(); delete c; }, this);
+ _client->onData([](void *obj, AsyncClient* c, void *data, size_t len){ (void)c; ((AsyncPrinter*)(obj))->_onData(data, len); }, this);
+}
diff --git a/src/AsyncPrinter.h b/src/AsyncPrinter.h
new file mode 100644
index 0000000..c3ebe3a
--- /dev/null
+++ b/src/AsyncPrinter.h
@@ -0,0 +1,73 @@
+/*
+ Asynchronous TCP library for Espressif MCUs
+
+ Copyright (c) 2016 Hristo Gochkov. 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 ASYNCPRINTER_H_
+#define ASYNCPRINTER_H_
+
+#include "Arduino.h"
+#include "ESPAsyncTCP.h"
+#include "cbuf.h"
+
+class AsyncPrinter;
+
+typedef std::function ApDataHandler;
+typedef std::function ApCloseHandler;
+
+class AsyncPrinter: public Print {
+ private:
+ AsyncClient *_client;
+ ApDataHandler _data_cb;
+ void *_data_arg;
+ ApCloseHandler _close_cb;
+ void *_close_arg;
+ cbuf *_tx_buffer;
+ size_t _tx_buffer_size;
+
+ void _onConnect(AsyncClient *c);
+ public:
+ AsyncPrinter *next;
+
+ AsyncPrinter();
+ AsyncPrinter(AsyncClient *client, size_t txBufLen = TCP_MSS);
+ virtual ~AsyncPrinter();
+
+ int connect(IPAddress ip, uint16_t port);
+ int connect(const char *host, uint16_t port);
+
+ void onData(ApDataHandler cb, void *arg);
+ void onClose(ApCloseHandler cb, void *arg);
+
+ operator bool();
+ AsyncPrinter & operator=(const AsyncPrinter &other);
+
+ size_t write(uint8_t data);
+ size_t write(const uint8_t *data, size_t len);
+
+ bool connected();
+ void close();
+
+ size_t _sendBuffer();
+ void _onData(void *data, size_t len);
+ void _on_close();
+ void _attachCallbacks();
+};
+
+#endif /* ASYNCPRINTER_H_ */
diff --git a/src/DebugPrintMacros.h b/src/DebugPrintMacros.h
new file mode 100644
index 0000000..29accaf
--- /dev/null
+++ b/src/DebugPrintMacros.h
@@ -0,0 +1,96 @@
+#ifndef _DEBUG_PRINT_MACROS_H
+#define _DEBUG_PRINT_MACROS_H
+// Some customizable print macros to suite the debug needs de jour.
+
+// Debug macros
+// #include
+// https://stackoverflow.com/questions/8487986/file-macro-shows-full-path
+// This value is resolved at compile time.
+#define _FILENAME_ strrchr("/" __FILE__, '/')
+
+// #define DEBUG_ESP_ASYNC_TCP 1
+// #define DEBUG_ESP_TCP_SSL 1
+// #define DEBUG_ESP_PORT Serial
+
+#if defined(DEBUG_ESP_PORT) && !defined(DEBUG_TIME_STAMP_FMT)
+#define DEBUG_TIME_STAMP_FMT "%06u.%03u "
+struct _DEBUG_TIME_STAMP {
+ unsigned dec;
+ unsigned whole;
+};
+inline struct _DEBUG_TIME_STAMP debugTimeStamp(void) {
+ struct _DEBUG_TIME_STAMP st;
+ unsigned now = millis() % 1000000000;
+ st.dec = now % 1000;
+ st.whole = now / 1000;
+ return st;
+}
+#endif
+
+#if defined(DEBUG_ESP_PORT) && !defined(DEBUG_GENERIC)
+ #define DEBUG_GENERIC( module, format, ... ) \
+ do { \
+ struct _DEBUG_TIME_STAMP st = debugTimeStamp(); \
+ DEBUG_ESP_PORT.printf( DEBUG_TIME_STAMP_FMT module " " format, st.whole, st.dec, ##__VA_ARGS__ ); \
+ } while(false)
+#endif
+#if defined(DEBUG_ESP_PORT) && !defined(DEBUG_GENERIC_P)
+ #define DEBUG_GENERIC_P( module, format, ... ) \
+ do { \
+ struct _DEBUG_TIME_STAMP st = debugTimeStamp(); \
+ DEBUG_ESP_PORT.printf_P(PSTR( DEBUG_TIME_STAMP_FMT module " " format ), st.whole, st.dec, ##__VA_ARGS__ ); \
+ } while(false)
+#endif
+
+#if defined(DEBUG_GENERIC) && !defined(ASSERT_GENERIC)
+#define ASSERT_GENERIC( a, module ) \
+ do { \
+ if ( !(a) ) { \
+ DEBUG_GENERIC( module, "%s:%s:%u: ASSERT("#a") failed!\n", __FILE__, __func__, __LINE__); \
+ DEBUG_ESP_PORT.flush(); \
+ } \
+ } while(false)
+#endif
+#if defined(DEBUG_GENERIC_P) && !defined(ASSERT_GENERIC_P)
+#define ASSERT_GENERIC_P( a, module ) \
+ do { \
+ if ( !(a) ) { \
+ DEBUG_GENERIC_P( module, "%s:%s:%u: ASSERT("#a") failed!\n", __FILE__, __func__, __LINE__); \
+ DEBUG_ESP_PORT.flush(); \
+ } \
+ } while(false)
+#endif
+
+#ifndef DEBUG_GENERIC
+#define DEBUG_GENERIC(...) do { (void)0;} while(false)
+#endif
+
+#ifndef DEBUG_GENERIC_P
+#define DEBUG_GENERIC_P(...) do { (void)0;} while(false)
+#endif
+
+#ifndef ASSERT_GENERIC
+#define ASSERT_GENERIC(...) do { (void)0;} while(false)
+#endif
+
+#ifndef ASSERT_GENERIC_P
+#define ASSERT_GENERIC_P(...) do { (void)0;} while(false)
+#endif
+
+#ifndef DEBUG_ESP_PRINTF
+#define DEBUG_ESP_PRINTF( format, ...) DEBUG_GENERIC_P("[%s]", format, &_FILENAME_[1], ##__VA_ARGS__)
+#endif
+
+#if defined(DEBUG_ESP_ASYNC_TCP) && !defined(ASYNC_TCP_DEBUG)
+#define ASYNC_TCP_DEBUG( format, ...) DEBUG_GENERIC_P("[ASYNC_TCP]", format, ##__VA_ARGS__)
+#endif
+
+#ifndef ASYNC_TCP_ASSERT
+#define ASYNC_TCP_ASSERT( a ) ASSERT_GENERIC_P( (a), "[ASYNC_TCP]")
+#endif
+
+#if defined(DEBUG_ESP_TCP_SSL) && !defined(TCP_SSL_DEBUG)
+#define TCP_SSL_DEBUG( format, ...) DEBUG_GENERIC_P("[TCP_SSL]", format, ##__VA_ARGS__)
+#endif
+
+#endif //_DEBUG_PRINT_MACROS_H
diff --git a/src/ESPAsyncTCP.cpp b/src/ESPAsyncTCP.cpp
new file mode 100644
index 0000000..0a51ed7
--- /dev/null
+++ b/src/ESPAsyncTCP.cpp
@@ -0,0 +1,1409 @@
+/*
+ Asynchronous TCP library for Espressif MCUs
+
+ Copyright (c) 2016 Hristo Gochkov. 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
+*/
+/*
+Changes for July 2019
+
+The operator "new ..." was changed to "new (std::nothrow) ...", which will
+return NULL when the heap is out of memory. Without the change "soft WDT"
+was the result, starting with Arduino ESP8266 Core 2.5.0. (Note, RE:"soft
+WDT" - the error reporting may improve with core 2.6.) With proir core
+versions the library appears to work fine.
+ref: https://github.com/esp8266/Arduino/issues/6269#issue-464978944
+
+To support newer lwIP versions and buffer models. All references to 1460
+were replaced with TCP_MSS. If TCP_MSS is not defined (exp. 1.4v lwIP)
+1460 is assumed.
+
+The ESPAsyncTCP library should build for Arduino ESP8266 Core releases:
+2.3.0, 2.4.1, 2.4.2, 2.5.1, 2.5.2. It may still build with core versions
+2.4.0 and 2.5.0. I did not do any regression testing with these, since
+they had too many issues and were quickly superseded.
+
+lwIP tcp_err() callback often resulted in crashes. The problem was a
+tcp_err() would come in, while processing a send or receive in the
+forground. The tcp_err() callback would be passed down to a client's
+registered disconnect CB. A common problem with SyncClient and other
+modules as well as some client code was: the freeing of ESPAsyncTCP
+AsyncClient objects via disconnect CB handlers while the library was
+waiting for an operstion to finished. Attempts to access bad pointers
+followed. For SyncClient this commonly occured during a call to delay().
+On return to SyncClient _client was invalid. Also the problem described by
+issue #94 also surfaced
+
+Use of tcp_abort() required some very special handling and was very
+challenging to make work without changing client API. ERR_ABRT can only be
+used once on a return to lwIP for a given connection and since the
+AsyncClient structure was sometimes deleted before returning to lwIP, the
+state tracking became tricky. While ugly, a global variable for this
+seemed to work; however, I abanded it when I saw a possible
+reentrancy/concurrency issue. After several approaches I settled the
+problem by creating "class ACErrorTracker" to manage the issue.
+
+
+Additional Async Client considerations:
+
+The client sketch must always test if the connection is still up at loop()
+entry and after the return of any function call, that may have done a
+delay() or yield() or any ESPAsyncTCP library family call. For example,
+the connection could be lost during a call to _client->write(...). Client
+sketches that delete _client as part of their onDisconnect() handler must
+be very careful as _client will become invalid after calls to delay(),
+yield(), etc.
+
+
+ */
+#include "Arduino.h"
+
+#include "ESPAsyncTCP.h"
+extern "C"{
+ #include "lwip/opt.h"
+ #include "lwip/tcp.h"
+ #include "lwip/inet.h"
+ #include "lwip/dns.h"
+ #include "lwip/init.h"
+}
+#include
+
+/*
+ Async Client Error Return Tracker
+*/
+// Assumption: callbacks are never called with err == ERR_ABRT; however,
+// they may return ERR_ABRT.
+
+ACErrorTracker::ACErrorTracker(AsyncClient *c):
+ _client(c)
+ , _close_error(ERR_OK)
+ , _errored(EE_OK)
+#ifdef DEBUG_MORE
+ , _error_event_cb(NULL)
+ , _error_event_cb_arg(NULL)
+#endif
+{}
+
+#ifdef DEBUG_MORE
+/**
+ * This is not necessary, but a start at gathering some statistics on
+ * errored out connections. Used from AsyncServer.
+ */
+void ACErrorTracker::onErrorEvent(AsNotifyHandler cb, void *arg) {
+ _error_event_cb = cb;
+ _error_event_cb_arg = arg;
+}
+#endif
+
+void ACErrorTracker::setCloseError(err_t e) {
+ if (e != ERR_OK)
+ ASYNC_TCP_DEBUG("setCloseError() to: %s(%ld)\n", _client->errorToString(e), e);
+ if(_errored == EE_OK)
+ _close_error = e;
+}
+/**
+ * Called mainly by callback routines, called when err is not ERR_OK.
+ * This prevents the possiblity of aborting an already errored out
+ * connection.
+ */
+void ACErrorTracker::setErrored(size_t errorEvent){
+ if(EE_OK == _errored)
+ _errored = errorEvent;
+#ifdef DEBUG_MORE
+ if (_error_event_cb)
+ _error_event_cb(_error_event_cb_arg, errorEvent);
+#endif
+}
+/**
+ * Used by callback functions only. Used for proper ERR_ABRT return value
+ * reporting. ERR_ABRT is only reported/returned once; thereafter ERR_OK
+ * is always returned.
+ */
+err_t ACErrorTracker::getCallbackCloseError(void){
+ if (EE_OK != _errored)
+ return ERR_OK;
+ if (ERR_ABRT == _close_error)
+ setErrored(EE_ABORTED);
+ return _close_error;
+}
+
+/*
+ Async TCP Client
+*/
+#if DEBUG_ESP_ASYNC_TCP
+static size_t _connectionCount=0;
+#endif
+
+#if ASYNC_TCP_SSL_ENABLED
+AsyncClient::AsyncClient(tcp_pcb* pcb, SSL_CTX * ssl_ctx):
+#else
+AsyncClient::AsyncClient(tcp_pcb* pcb):
+#endif
+ _connect_cb(0)
+ , _connect_cb_arg(0)
+ , _discard_cb(0)
+ , _discard_cb_arg(0)
+ , _sent_cb(0)
+ , _sent_cb_arg(0)
+ , _error_cb(0)
+ , _error_cb_arg(0)
+ , _recv_cb(0)
+ , _recv_cb_arg(0)
+ , _pb_cb(0)
+ , _pb_cb_arg(0)
+ , _timeout_cb(0)
+ , _timeout_cb_arg(0)
+ , _poll_cb(0)
+ , _poll_cb_arg(0)
+ , _pcb_busy(false)
+#if ASYNC_TCP_SSL_ENABLED
+ , _pcb_secure(false)
+ , _handshake_done(true)
+#endif
+ , _pcb_sent_at(0)
+ , _close_pcb(false)
+ , _ack_pcb(true)
+ , _tx_unacked_len(0)
+ , _tx_acked_len(0)
+ , _tx_unsent_len(0)
+ , _rx_ack_len(0)
+ , _rx_last_packet(0)
+ , _rx_since_timeout(0)
+ , _ack_timeout(ASYNC_MAX_ACK_TIME)
+ , _connect_port(0)
+ , _recv_pbuf_flags(0)
+ , _errorTracker(NULL)
+ , prev(NULL)
+ , next(NULL)
+{
+ _pcb = pcb;
+ if(_pcb){
+ _rx_last_packet = millis();
+ tcp_setprio(_pcb, TCP_PRIO_MIN);
+ tcp_arg(_pcb, this);
+ tcp_recv(_pcb, &_s_recv);
+ tcp_sent(_pcb, &_s_sent);
+ tcp_err(_pcb, &_s_error);
+ tcp_poll(_pcb, &_s_poll, 1);
+#if ASYNC_TCP_SSL_ENABLED
+ if(ssl_ctx){
+ if(tcp_ssl_new_server(_pcb, ssl_ctx) < 0){
+ _close();
+ return;
+ }
+ tcp_ssl_arg(_pcb, this);
+ tcp_ssl_data(_pcb, &_s_data);
+ tcp_ssl_handshake(_pcb, &_s_handshake);
+ tcp_ssl_err(_pcb, &_s_ssl_error);
+
+ _pcb_secure = true;
+ _handshake_done = false;
+ }
+#endif
+ }
+
+ _errorTracker = std::make_shared(this);
+#if DEBUG_ESP_ASYNC_TCP
+ _errorTracker->setConnectionId(++_connectionCount);
+#endif
+}
+
+AsyncClient::~AsyncClient(){
+ if(_pcb)
+ _close();
+
+ _errorTracker->clearClient();
+}
+
+inline void clearTcpCallbacks(tcp_pcb* pcb){
+ tcp_arg(pcb, NULL);
+ tcp_sent(pcb, NULL);
+ tcp_recv(pcb, NULL);
+ tcp_err(pcb, NULL);
+ tcp_poll(pcb, NULL, 0);
+}
+
+#if ASYNC_TCP_SSL_ENABLED
+bool AsyncClient::connect(IPAddress ip, uint16_t port, bool secure){
+#else
+bool AsyncClient::connect(IPAddress ip, uint16_t port){
+#endif
+ if (_pcb) //already connected
+ return false;
+ ip_addr_t addr;
+ addr.addr = ip;
+#if LWIP_VERSION_MAJOR == 1
+ netif* interface = ip_route(&addr);
+ if (!interface){ //no route to host
+ return false;
+ }
+#endif
+ tcp_pcb* pcb = tcp_new();
+ if (!pcb){ //could not allocate pcb
+ return false;
+ }
+
+ tcp_setprio(pcb, TCP_PRIO_MIN);
+#if ASYNC_TCP_SSL_ENABLED
+ _pcb_secure = secure;
+ _handshake_done = !secure;
+#endif
+ tcp_arg(pcb, this);
+ tcp_err(pcb, &_s_error);
+ size_t err = tcp_connect(pcb, &addr, port,(tcp_connected_fn)&_s_connected);
+ return (ERR_OK == err);
+}
+
+#if ASYNC_TCP_SSL_ENABLED
+bool AsyncClient::connect(const char* host, uint16_t port, bool secure){
+#else
+bool AsyncClient::connect(const char* host, uint16_t port){
+#endif
+ ip_addr_t addr;
+ err_t err = dns_gethostbyname(host, &addr, (dns_found_callback)&_s_dns_found, this);
+ if(err == ERR_OK) {
+#if ASYNC_TCP_SSL_ENABLED
+ return connect(IPAddress(addr.addr), port, secure);
+#else
+ return connect(IPAddress(addr.addr), port);
+#endif
+ } else if(err == ERR_INPROGRESS) {
+#if ASYNC_TCP_SSL_ENABLED
+ _pcb_secure = secure;
+ _handshake_done = !secure;
+#endif
+ _connect_port = port;
+ return true;
+ }
+ return false;
+}
+
+AsyncClient& AsyncClient::operator=(const AsyncClient& other){
+ if (_pcb) {
+ ASYNC_TCP_DEBUG("operator=[%u]: Abandoned _pcb(0x%" PRIXPTR ") forced close.\n", getConnectionId(), uintptr_t(_pcb));
+ _close();
+ }
+ _errorTracker = other._errorTracker;
+
+ // I am confused when "other._pcb" falls out of scope the destructor will
+ // close it? TODO: Look to see where this is used and how it might work.
+ _pcb = other._pcb;
+ if (_pcb) {
+ _rx_last_packet = millis();
+ tcp_setprio(_pcb, TCP_PRIO_MIN);
+ tcp_arg(_pcb, this);
+ tcp_recv(_pcb, &_s_recv);
+ tcp_sent(_pcb, &_s_sent);
+ tcp_err(_pcb, &_s_error);
+ tcp_poll(_pcb, &_s_poll, 1);
+#if ASYNC_TCP_SSL_ENABLED
+ if(tcp_ssl_has(_pcb)){
+ _pcb_secure = true;
+ _handshake_done = false;
+ tcp_ssl_arg(_pcb, this);
+ tcp_ssl_data(_pcb, &_s_data);
+ tcp_ssl_handshake(_pcb, &_s_handshake);
+ tcp_ssl_err(_pcb, &_s_ssl_error);
+ } else {
+ _pcb_secure = false;
+ _handshake_done = true;
+ }
+#endif
+ }
+ return *this;
+}
+
+bool AsyncClient::operator==(const AsyncClient &other) {
+ return (_pcb != NULL && other._pcb != NULL && (_pcb->remote_ip.addr == other._pcb->remote_ip.addr) && (_pcb->remote_port == other._pcb->remote_port));
+}
+
+void AsyncClient::abort(){
+ // Notes:
+ // 1) _pcb is set to NULL, so we cannot call tcp_abort() more than once.
+ // 2) setCloseError(ERR_ABRT) is only done here!
+ // 3) Using this abort() function guarantees only one tcp_abort() call is
+ // made and only one CB returns with ERR_ABORT.
+ // 4) After abort() is called from _close(), no callbacks with an err
+ // parameter will be called. eg. _recv(), _error(), _connected().
+ // _close() will reset there CB handlers before calling.
+ // 5) A callback to _error(), will set _pcb to NULL, thus avoiding the
+ // of a 2nd call to tcp_abort().
+ // 6) Callbacks to _recv() or _connected() with err set, will result in _pcb
+ // set to NULL. Thus, preventing possible calls later to tcp_abort().
+ if(_pcb) {
+ tcp_abort(_pcb);
+ _pcb = NULL;
+ setCloseError(ERR_ABRT);
+ }
+ return;
+}
+
+void AsyncClient::close(bool now){
+ if(_pcb)
+ tcp_recved(_pcb, _rx_ack_len);
+ if(now)
+ _close();
+ else
+ _close_pcb = true;
+}
+
+void AsyncClient::stop() {
+ close(false);
+}
+
+bool AsyncClient::free(){
+ if(!_pcb)
+ return true;
+ if(_pcb->state == 0 || _pcb->state > 4)
+ return true;
+ return false;
+}
+
+size_t AsyncClient::write(const char* data) {
+ if(data == NULL)
+ return 0;
+ return write(data, strlen(data));
+}
+
+size_t AsyncClient::write(const char* data, size_t size, uint8_t apiflags) {
+ size_t will_send = add(data, size, apiflags);
+
+ if(!will_send || !send())
+ return 0;
+ return will_send;
+}
+
+size_t AsyncClient::add(const char* data, size_t size, uint8_t apiflags) {
+ if(!_pcb || size == 0 || data == NULL)
+ return 0;
+ size_t room = space();
+ if(!room)
+ return 0;
+#if ASYNC_TCP_SSL_ENABLED
+ if(_pcb_secure){
+ int sent = tcp_ssl_write(_pcb, (uint8_t*)data, size);
+ if(sent >= 0){
+ _tx_unacked_len += sent;
+ return sent;
+ }
+ _close();
+ return 0;
+ }
+#endif
+ size_t will_send = (room < size) ? room : size;
+ err_t err = tcp_write(_pcb, data, will_send, apiflags);
+ if(err != ERR_OK) {
+ ASYNC_TCP_DEBUG("_add[%u]: tcp_write() returned err: %s(%ld)\n", getConnectionId(), errorToString(err), err);
+ return 0;
+ }
+ _tx_unsent_len += will_send;
+ return will_send;
+}
+
+bool AsyncClient::send(){
+#if ASYNC_TCP_SSL_ENABLED
+ if(_pcb_secure)
+ return true;
+#endif
+ err_t err = tcp_output(_pcb);
+ if(err == ERR_OK){
+ _pcb_busy = true;
+ _pcb_sent_at = millis();
+ _tx_unacked_len += _tx_unsent_len;
+ _tx_unsent_len = 0;
+ return true;
+ }
+
+ ASYNC_TCP_DEBUG("send[%u]: tcp_output() returned err: %s(%ld)", getConnectionId(), errorToString(err), err);
+ _tx_unsent_len = 0;
+ return false;
+}
+
+size_t AsyncClient::ack(size_t len){
+ if(len > _rx_ack_len)
+ len = _rx_ack_len;
+ if(len)
+ tcp_recved(_pcb, len);
+ _rx_ack_len -= len;
+ return len;
+}
+
+// Private Callbacks
+
+void AsyncClient::_connected(std::shared_ptr& errorTracker, void* pcb, err_t err){
+ //(void)err; // LWIP v1.4 appears to always call with ERR_OK
+ // Documentation for 2.1.0 also says:
+ // "err - An unused error code, always ERR_OK currently ;-)"
+ // https://www.nongnu.org/lwip/2_1_x/tcp_8h.html#a939867106bd492caf2d85852fb7f6ae8
+ // Based on that wording and emoji lets just handle it now.
+ // After all, the API does allow for an err != ERR_OK.
+ if(NULL == pcb || ERR_OK != err) {
+ ASYNC_TCP_DEBUG("_connected[%u]:%s err: %s(%ld)\n", errorTracker->getConnectionId(), ((NULL == pcb) ? " NULL == pcb!," : ""), errorToString(err), err);
+ errorTracker->setCloseError(err);
+ errorTracker->setErrored(EE_CONNECTED_CB);
+ _pcb = reinterpret_cast(pcb);
+ if (_pcb)
+ clearTcpCallbacks(_pcb);
+ _pcb = NULL;
+ _error(err);
+ return;
+ }
+
+ _pcb = reinterpret_cast(pcb);
+ if(_pcb){
+ _pcb_busy = false;
+ _rx_last_packet = millis();
+ tcp_setprio(_pcb, TCP_PRIO_MIN);
+ tcp_recv(_pcb, &_s_recv);
+ tcp_sent(_pcb, &_s_sent);
+ tcp_poll(_pcb, &_s_poll, 1);
+#if ASYNC_TCP_SSL_ENABLED
+ if(_pcb_secure){
+ if(tcp_ssl_new_client(_pcb) < 0){
+ _close();
+ return;
+ }
+ tcp_ssl_arg(_pcb, this);
+ tcp_ssl_data(_pcb, &_s_data);
+ tcp_ssl_handshake(_pcb, &_s_handshake);
+ tcp_ssl_err(_pcb, &_s_ssl_error);
+ }
+ }
+ if(!_pcb_secure && _connect_cb)
+#else
+ }
+ if(_connect_cb)
+#endif
+ _connect_cb(_connect_cb_arg, this);
+ return;
+}
+
+void AsyncClient::_close(){
+ if(_pcb) {
+#if ASYNC_TCP_SSL_ENABLED
+ if(_pcb_secure){
+ tcp_ssl_free(_pcb);
+ }
+#endif
+ clearTcpCallbacks(_pcb);
+ err_t err = tcp_close(_pcb);
+ if(ERR_OK == err) {
+ setCloseError(err);
+ } else {
+ ASYNC_TCP_DEBUG("_close[%u]: abort() called for AsyncClient 0x%" PRIXPTR "\n", getConnectionId(), uintptr_t(this));
+ abort();
+ }
+ _pcb = NULL;
+ if(_discard_cb)
+ _discard_cb(_discard_cb_arg, this);
+ }
+ return;
+}
+
+void AsyncClient::_error(err_t err) {
+ ASYNC_TCP_DEBUG("_error[%u]:%s err: %s(%ld)\n", getConnectionId(), ((NULL == _pcb) ? " NULL == _pcb!," : ""), errorToString(err), err);
+ if(_pcb){
+#if ASYNC_TCP_SSL_ENABLED
+ if(_pcb_secure){
+ tcp_ssl_free(_pcb);
+ }
+#endif
+ // At this callback _pcb is possible already freed. Thus, no calls are
+ // made to set to NULL other callbacks.
+ _pcb = NULL;
+ }
+ if(_error_cb)
+ _error_cb(_error_cb_arg, this, err);
+ if(_discard_cb)
+ _discard_cb(_discard_cb_arg, this);
+}
+
+#if ASYNC_TCP_SSL_ENABLED
+void AsyncClient::_ssl_error(int8_t err){
+ if(_error_cb)
+ _error_cb(_error_cb_arg, this, err+64);
+}
+#endif
+
+void AsyncClient::_sent(std::shared_ptr& errorTracker, tcp_pcb* pcb, uint16_t len) {
+ (void)pcb;
+#if ASYNC_TCP_SSL_ENABLED
+ if (_pcb_secure && !_handshake_done)
+ return;
+#endif
+ _rx_last_packet = millis();
+
+ // If add() is called, then send(), we have unacked len with data, and _pcb_busy true
+ // we have space so we call add with some more data, we call then canSend()
+ // That returns false, because _pcb_busy is true, so we dont call send().
+ // Then this function gets called, but not only with the bytes queued before the send() call
+ // also the data added with add() is send, and acked, now we have more acked data than expected
+ // if we don't check for this unackled overflows when substracting acked length and
+ // pcb_busy never goes false, if we keep checking canSend(), we never call send even if we did
+ // _pcb_busy will remain true.
+ if (len > _tx_unacked_len)
+ {
+ _tx_unacked_len += _tx_unsent_len;
+ _tx_unsent_len = 0;
+ }
+
+ _tx_unacked_len -= len;
+ _tx_acked_len += len;
+ ASYNC_TCP_DEBUG("_sent[%u]: %4u, unacked=%4u, acked=%4u, space=%4u\n", errorTracker->getConnectionId(), len, _tx_unacked_len, _tx_acked_len, space());
+ if(_tx_unacked_len == 0){
+ _pcb_busy = false;
+ errorTracker->setCloseError(ERR_OK);
+ if(_sent_cb) {
+ _sent_cb(_sent_cb_arg, this, _tx_acked_len, (millis() - _pcb_sent_at));
+ if(!errorTracker->hasClient())
+ return;
+ }
+ _tx_acked_len = 0;
+ }
+ return;
+}
+
+void AsyncClient::_recv(std::shared_ptr& errorTracker, tcp_pcb* pcb, pbuf* pb, err_t err) {
+ // While lwIP v1.4 appears to always call with ERR_OK, 2.x lwIP may present
+ // a non-ERR_OK value.
+ // https://www.nongnu.org/lwip/2_1_x/tcp_8h.html#a780cfac08b02c66948ab94ea974202e8
+ if(NULL == pcb || ERR_OK != err){
+ ASYNC_TCP_DEBUG("_recv[%u]:%s err: %s(%ld)\n", errorTracker->getConnectionId(), ((NULL == pcb) ? " NULL == pcb!," : ""), errorToString(err), err);
+ ASYNC_TCP_ASSERT(ERR_ABRT != err);
+ errorTracker->setCloseError(err);
+ errorTracker->setErrored(EE_RECV_CB);
+ _pcb = pcb;
+ if(_pcb)
+ clearTcpCallbacks(_pcb);
+ _pcb = NULL;
+ // I think we are safe from being called from an interrupt context.
+ // Best Hint that calling _error() is safe:
+ // https://www.nongnu.org/lwip/2_1_x/group__lwip__nosys.html
+ // "Feed incoming packets to netif->input(pbuf, netif) function from
+ // mainloop, not from interrupt context. You can allocate a Packet buffers
+ // (PBUF) in interrupt context and put them into a queue which is processed
+ // from mainloop."
+ // And the description of "Mainloop Mode" option 2:
+ // https://www.nongnu.org/lwip/2_1_x/pitfalls.html
+ // "2) Run lwIP in a mainloop. ... lwIP is ONLY called from mainloop
+ // callstacks here. The ethernet IRQ has to put received telegrams into a
+ // queue which is polled in the mainloop. Ensure lwIP is NEVER called from
+ // an interrupt, ...!"
+ // Based on these comments I am thinking tcp_recv_fn() is called
+ // from somebody's mainloop(), which could only have been reached from a
+ // delay like function or the Arduino sketch loop() function has returned.
+ // What I don't want is for the client sketch to delete the AsyncClient
+ // object via _error() while it is in the middle of using it. However,
+ // the client sketch must always test that the connection is still up
+ // at loop() entry and after the return of any function call, that may
+ // have done a delay() or yield().
+ _error(err);
+ return;
+ }
+
+ if(pb == NULL){
+ ASYNC_TCP_DEBUG("_recv[%u]: pb == NULL! Closing... %ld\n", errorTracker->getConnectionId(), err);
+ _close();
+ return;
+ }
+ _rx_last_packet = millis();
+ errorTracker->setCloseError(ERR_OK);
+#if ASYNC_TCP_SSL_ENABLED
+ if(_pcb_secure){
+ ASYNC_TCP_DEBUG("_recv[%u]: %d\n", getConnectionId(), pb->tot_len);
+ int read_bytes = tcp_ssl_read(pcb, pb);
+ if(read_bytes < 0){
+ if (read_bytes != SSL_CLOSE_NOTIFY) {
+ ASYNC_TCP_DEBUG("_recv[%u] err: %d\n", getConnectionId(), read_bytes);
+ _close();
+ }
+ }
+ return;
+ }
+#endif
+ while(pb != NULL){
+ // IF this callback function returns ERR_OK or ERR_ABRT
+ // then it is assummed we freed the pbufs.
+ // https://www.nongnu.org/lwip/2_1_x/group__tcp__raw.html#ga8afd0b316a87a5eeff4726dc95006ed0
+ if(!errorTracker->hasClient()){
+ while(pb != NULL){
+ pbuf *b = pb;
+ pb = b->next;
+ b->next = NULL;
+ pbuf_free(b);
+ }
+ return;
+ }
+ //we should not ack before we assimilate the data
+ _ack_pcb = true;
+ pbuf *b = pb;
+ pb = b->next;
+ b->next = NULL;
+ ASYNC_TCP_DEBUG("_recv[%u]: %d%s\n", errorTracker->getConnectionId(), b->len, (b->flags&PBUF_FLAG_PUSH)?", PBUF_FLAG_PUSH":"");
+ if(_pb_cb){
+ _pb_cb(_pb_cb_arg, this, b);
+ } else {
+ if(_recv_cb){
+ _recv_pbuf_flags = b->flags;
+ _recv_cb(_recv_cb_arg, this, b->payload, b->len);
+ }
+ if(errorTracker->hasClient()){
+ if(!_ack_pcb)
+ _rx_ack_len += b->len;
+ else
+ tcp_recved(pcb, b->len);
+ }
+ pbuf_free(b);
+ }
+ }
+ return;
+}
+
+void AsyncClient::_poll(std::shared_ptr& errorTracker, tcp_pcb* pcb){
+ (void)pcb;
+ errorTracker->setCloseError(ERR_OK);
+
+ // Close requested
+ if(_close_pcb){
+ _close_pcb = false;
+ _close();
+ return;
+ }
+ uint32_t now = millis();
+
+ // ACK Timeout
+ if(_pcb_busy && _ack_timeout && (now - _pcb_sent_at) >= _ack_timeout){
+ _pcb_busy = false;
+ if(_timeout_cb)
+ _timeout_cb(_timeout_cb_arg, this, (now - _pcb_sent_at));
+ return;
+ }
+ // RX Timeout
+ if(_rx_since_timeout && (now - _rx_last_packet) >= (_rx_since_timeout * 1000)){
+ _close();
+ return;
+ }
+#if ASYNC_TCP_SSL_ENABLED
+ // SSL Handshake Timeout
+ if(_pcb_secure && !_handshake_done && (now - _rx_last_packet) >= 2000){
+ _close();
+ return;
+ }
+#endif
+ // Everything is fine
+ if(_poll_cb)
+ _poll_cb(_poll_cb_arg, this);
+ return;
+}
+
+#if LWIP_VERSION_MAJOR == 1
+void AsyncClient::_dns_found(struct ip_addr *ipaddr){
+#else
+void AsyncClient::_dns_found(const ip_addr *ipaddr){
+#endif
+ if(ipaddr){
+#if ASYNC_TCP_SSL_ENABLED
+ connect(IPAddress(ipaddr->addr), _connect_port, _pcb_secure);
+#else
+ connect(IPAddress(ipaddr->addr), _connect_port);
+#endif
+ } else {
+ if(_error_cb)
+ _error_cb(_error_cb_arg, this, -55);
+ if(_discard_cb)
+ _discard_cb(_discard_cb_arg, this);
+ }
+}
+
+// lwIP Callbacks
+#if LWIP_VERSION_MAJOR == 1
+void AsyncClient::_s_dns_found(const char *name, ip_addr_t *ipaddr, void *arg){
+#else
+void AsyncClient::_s_dns_found(const char *name, const ip_addr *ipaddr, void *arg){
+#endif
+ (void)name;
+ reinterpret_cast(arg)->_dns_found(ipaddr);
+}
+
+err_t AsyncClient::_s_poll(void *arg, struct tcp_pcb *tpcb) {
+ AsyncClient *c = reinterpret_cast(arg);
+ std::shared_ptrerrorTracker = c->getACErrorTracker();
+ c->_poll(errorTracker, tpcb);
+ return errorTracker->getCallbackCloseError();
+}
+
+err_t AsyncClient::_s_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *pb, err_t err) {
+ AsyncClient *c = reinterpret_cast(arg);
+ auto errorTracker = c->getACErrorTracker();
+ c->_recv(errorTracker, tpcb, pb, err);
+ return errorTracker->getCallbackCloseError();
+}
+
+void AsyncClient::_s_error(void *arg, err_t err) {
+ AsyncClient *c = reinterpret_cast(arg);
+ auto errorTracker = c->getACErrorTracker();
+ errorTracker->setCloseError(err);
+ errorTracker->setErrored(EE_ERROR_CB);
+ c->_error(err);
+}
+
+err_t AsyncClient::_s_sent(void *arg, struct tcp_pcb *tpcb, uint16_t len) {
+ AsyncClient *c = reinterpret_cast(arg);
+ auto errorTracker = c->getACErrorTracker();
+ c->_sent(errorTracker, tpcb, len);
+ return errorTracker->getCallbackCloseError();
+}
+
+err_t AsyncClient::_s_connected(void* arg, void* tpcb, err_t err){
+ AsyncClient *c = reinterpret_cast(arg);
+ auto errorTracker = c->getACErrorTracker();
+ c->_connected(errorTracker, tpcb, err);
+ return errorTracker->getCallbackCloseError();
+}
+
+#if ASYNC_TCP_SSL_ENABLED
+void AsyncClient::_s_data(void *arg, struct tcp_pcb *tcp, uint8_t * data, size_t len){
+ AsyncClient *c = reinterpret_cast(arg);
+ if(c->_recv_cb)
+ c->_recv_cb(c->_recv_cb_arg, c, data, len);
+}
+
+void AsyncClient::_s_handshake(void *arg, struct tcp_pcb *tcp, SSL *ssl){
+ AsyncClient *c = reinterpret_cast(arg);
+ c->_handshake_done = true;
+ if(c->_connect_cb)
+ c->_connect_cb(c->_connect_cb_arg, c);
+}
+
+void AsyncClient::_s_ssl_error(void *arg, struct tcp_pcb *tcp, int8_t err){
+ reinterpret_cast(arg)->_ssl_error(err);
+}
+#endif
+
+// Operators
+
+AsyncClient & AsyncClient::operator+=(const AsyncClient &other) {
+ if(next == NULL){
+ next = (AsyncClient*)(&other);
+ next->prev = this;
+ } else {
+ AsyncClient *c = next;
+ while(c->next != NULL) c = c->next;
+ c->next =(AsyncClient*)(&other);
+ c->next->prev = c;
+ }
+ return *this;
+}
+
+void AsyncClient::setRxTimeout(uint32_t timeout){
+ _rx_since_timeout = timeout;
+}
+
+uint32_t AsyncClient::getRxTimeout(){
+ return _rx_since_timeout;
+}
+
+uint32_t AsyncClient::getAckTimeout(){
+ return _ack_timeout;
+}
+
+void AsyncClient::setAckTimeout(uint32_t timeout){
+ _ack_timeout = timeout;
+}
+
+void AsyncClient::setNoDelay(bool nodelay){
+ if(!_pcb)
+ return;
+ if(nodelay)
+ tcp_nagle_disable(_pcb);
+ else
+ tcp_nagle_enable(_pcb);
+}
+
+bool AsyncClient::getNoDelay(){
+ if(!_pcb)
+ return false;
+ return tcp_nagle_disabled(_pcb);
+}
+
+uint16_t AsyncClient::getMss(){
+ if(_pcb)
+ return tcp_mss(_pcb);
+ return 0;
+}
+
+uint32_t AsyncClient::getRemoteAddress() {
+ if(!_pcb)
+ return 0;
+ return _pcb->remote_ip.addr;
+}
+
+uint16_t AsyncClient::getRemotePort() {
+ if(!_pcb)
+ return 0;
+ return _pcb->remote_port;
+}
+
+uint32_t AsyncClient::getLocalAddress() {
+ if(!_pcb)
+ return 0;
+ return _pcb->local_ip.addr;
+}
+
+uint16_t AsyncClient::getLocalPort() {
+ if(!_pcb)
+ return 0;
+ return _pcb->local_port;
+}
+
+IPAddress AsyncClient::remoteIP() {
+ return IPAddress(getRemoteAddress());
+}
+
+uint16_t AsyncClient::remotePort() {
+ return getRemotePort();
+}
+
+IPAddress AsyncClient::localIP() {
+ return IPAddress(getLocalAddress());
+}
+
+uint16_t AsyncClient::localPort() {
+ return getLocalPort();
+}
+
+#if ASYNC_TCP_SSL_ENABLED
+SSL * AsyncClient::getSSL(){
+ if(_pcb && _pcb_secure){
+ return tcp_ssl_get_ssl(_pcb);
+ }
+ return NULL;
+}
+#endif
+
+uint8_t AsyncClient::state() {
+ if(!_pcb)
+ return 0;
+ return _pcb->state;
+}
+
+bool AsyncClient::connected(){
+ if (!_pcb)
+ return false;
+#if ASYNC_TCP_SSL_ENABLED
+ return _pcb->state == 4 && _handshake_done;
+#else
+ return _pcb->state == 4;
+#endif
+}
+
+bool AsyncClient::connecting(){
+ if (!_pcb)
+ return false;
+ return _pcb->state > 0 && _pcb->state < 4;
+}
+
+bool AsyncClient::disconnecting(){
+ if (!_pcb)
+ return false;
+ return _pcb->state > 4 && _pcb->state < 10;
+}
+
+bool AsyncClient::disconnected(){
+ if (!_pcb)
+ return true;
+ return _pcb->state == 0 || _pcb->state == 10;
+}
+
+bool AsyncClient::freeable(){
+ if (!_pcb)
+ return true;
+ return _pcb->state == 0 || _pcb->state > 4;
+}
+
+bool AsyncClient::canSend(){
+ return !_pcb_busy && (space() > 0);
+}
+
+
+// Callback Setters
+
+void AsyncClient::onConnect(AcConnectHandler cb, void* arg){
+ _connect_cb = cb;
+ _connect_cb_arg = arg;
+}
+
+void AsyncClient::onDisconnect(AcConnectHandler cb, void* arg){
+ _discard_cb = cb;
+ _discard_cb_arg = arg;
+}
+
+void AsyncClient::onAck(AcAckHandler cb, void* arg){
+ _sent_cb = cb;
+ _sent_cb_arg = arg;
+}
+
+void AsyncClient::onError(AcErrorHandler cb, void* arg){
+ _error_cb = cb;
+ _error_cb_arg = arg;
+}
+
+void AsyncClient::onData(AcDataHandler cb, void* arg){
+ _recv_cb = cb;
+ _recv_cb_arg = arg;
+}
+
+void AsyncClient::onPacket(AcPacketHandler cb, void* arg){
+ _pb_cb = cb;
+ _pb_cb_arg = arg;
+}
+
+void AsyncClient::onTimeout(AcTimeoutHandler cb, void* arg){
+ _timeout_cb = cb;
+ _timeout_cb_arg = arg;
+}
+
+void AsyncClient::onPoll(AcConnectHandler cb, void* arg){
+ _poll_cb = cb;
+ _poll_cb_arg = arg;
+}
+
+
+size_t AsyncClient::space(){
+#if ASYNC_TCP_SSL_ENABLED
+ if((_pcb != NULL) && (_pcb->state == 4) && _handshake_done){
+ uint16_t s = tcp_sndbuf(_pcb);
+ if(_pcb_secure){
+#ifdef AXTLS_2_0_0_SNDBUF
+ return tcp_ssl_sndbuf(_pcb);
+#else
+ if(s >= 128) //safe approach
+ return s - 128;
+ return 0;
+#endif
+ }
+ return s;
+ }
+#else // ASYNC_TCP_SSL_ENABLED
+ if((_pcb != NULL) && (_pcb->state == 4)){
+ return tcp_sndbuf(_pcb);
+ }
+#endif // ASYNC_TCP_SSL_ENABLED
+ return 0;
+}
+
+void AsyncClient::ackPacket(struct pbuf * pb){
+ if(!pb){
+ return;
+ }
+ tcp_recved(_pcb, pb->len);
+ pbuf_free(pb);
+}
+
+const char * AsyncClient::errorToString(err_t error) {
+ switch (error) {
+ case ERR_OK: return "No error, everything OK";
+ case ERR_MEM: return "Out of memory error";
+ case ERR_BUF: return "Buffer error";
+ case ERR_TIMEOUT: return "Timeout";
+ case ERR_RTE: return "Routing problem";
+ case ERR_INPROGRESS: return "Operation in progress";
+ case ERR_VAL: return "Illegal value";
+ case ERR_WOULDBLOCK: return "Operation would block";
+ case ERR_ABRT: return "Connection aborted";
+ case ERR_RST: return "Connection reset";
+ case ERR_CLSD: return "Connection closed";
+ case ERR_CONN: return "Not connected";
+ case ERR_ARG: return "Illegal argument";
+ case ERR_USE: return "Address in use";
+#if defined(LWIP_VERSION_MAJOR) && (LWIP_VERSION_MAJOR > 1)
+ case ERR_ALREADY: return "Already connectioning";
+#endif
+ case ERR_IF: return "Low-level netif error";
+ case ERR_ISCONN: return "Connection already established";
+ case -55: return "DNS failed";
+ default: return "Unknown error";
+ }
+}
+
+const char * AsyncClient::stateToString(){
+ switch(state()){
+ case 0: return "Closed";
+ case 1: return "Listen";
+ case 2: return "SYN Sent";
+ case 3: return "SYN Received";
+ case 4: return "Established";
+ case 5: return "FIN Wait 1";
+ case 6: return "FIN Wait 2";
+ case 7: return "Close Wait";
+ case 8: return "Closing";
+ case 9: return "Last ACK";
+ case 10: return "Time Wait";
+ default: return "UNKNOWN";
+ }
+}
+
+/*
+ Async TCP Server
+*/
+struct pending_pcb {
+ tcp_pcb* pcb;
+ pbuf *pb;
+ struct pending_pcb * next;
+};
+
+AsyncServer::AsyncServer(IPAddress addr, uint16_t port)
+ : _port(port)
+ , _addr(addr)
+ , _noDelay(false)
+ , _pcb(0)
+ , _connect_cb(0)
+ , _connect_cb_arg(0)
+#if ASYNC_TCP_SSL_ENABLED
+ , _pending(NULL)
+ , _ssl_ctx(NULL)
+ , _file_cb(0)
+ , _file_cb_arg(0)
+#endif
+{
+#ifdef DEBUG_MORE
+ for (size_t i=0; inext;
+ if(p->pb){
+ pbuf_free(p->pb);
+ }
+ free(p);
+ }
+ }
+ }
+#endif
+}
+
+void AsyncServer::setNoDelay(bool nodelay){
+ _noDelay = nodelay;
+}
+
+bool AsyncServer::getNoDelay(){
+ return _noDelay;
+}
+
+uint8_t AsyncServer::status(){
+ if (!_pcb)
+ return 0;
+ return _pcb->state;
+}
+
+err_t AsyncServer::_accept(tcp_pcb* pcb, err_t err){
+ //http://savannah.nongnu.org/bugs/?43739
+ if(NULL == pcb || ERR_OK != err){
+ // https://www.nongnu.org/lwip/2_1_x/tcp_8h.html#a00517abce6856d6c82f0efebdafb734d
+ // An error code if there has been an error accepting. Only return ERR_ABRT
+ // if you have called tcp_abort from within the callback function!
+ // eg. 2.1.0 could call with error on failure to allocate pcb.
+ ASYNC_TCP_DEBUG("_accept:%s err: %ld\n", ((NULL == pcb) ? " NULL == pcb!," : ""), err);
+ ASYNC_TCP_ASSERT(ERR_ABRT != err);
+#ifdef DEBUG_MORE
+ incEventCount(EE_ACCEPT_CB);
+#endif
+ return ERR_OK;
+ }
+
+ if(_connect_cb){
+#if ASYNC_TCP_SSL_ENABLED
+ if (_noDelay || _ssl_ctx)
+#else
+ if (_noDelay)
+#endif
+ tcp_nagle_disable(pcb);
+ else
+ tcp_nagle_enable(pcb);
+
+#if ASYNC_TCP_SSL_ENABLED
+ if(_ssl_ctx){
+ if(tcp_ssl_has_client() || _pending){
+ struct pending_pcb * new_item = (struct pending_pcb*)malloc(sizeof(struct pending_pcb));
+ if(!new_item){
+ ASYNC_TCP_DEBUG("### malloc new pending failed!\n");
+ if(tcp_close(pcb) != ERR_OK){
+ tcp_abort(pcb);
+ return ERR_ABRT;
+ }
+ return ERR_OK;
+ }
+ ASYNC_TCP_DEBUG("### put to wait: %d\n", _clients_waiting);
+ new_item->pcb = pcb;
+ new_item->pb = NULL;
+ new_item->next = NULL;
+ tcp_setprio(_pcb, TCP_PRIO_MIN);
+ tcp_arg(pcb, this);
+ tcp_poll(pcb, &_s_poll, 1);
+ tcp_recv(pcb, &_s_recv);
+
+ if(_pending == NULL){
+ _pending = new_item;
+ } else {
+ struct pending_pcb * p = _pending;
+ while(p->next != NULL)
+ p = p->next;
+ p->next = new_item;
+ }
+ } else {
+ AsyncClient *c = new (std::nothrow) AsyncClient(pcb, _ssl_ctx);
+ if(c){
+ ASYNC_TCP_DEBUG("_accept[%u]: SSL connected\n", c->getConnectionId());
+ c->onConnect([this](void * arg, AsyncClient *c){
+ _connect_cb(_connect_cb_arg, c);
+ }, this);
+ } else {
+ ASYNC_TCP_DEBUG("_accept[_ssl_ctx]: new AsyncClient() failed, connection aborted!\n");
+ if(tcp_close(pcb) != ERR_OK){
+ tcp_abort(pcb);
+ return ERR_ABRT;
+ }
+ }
+ }
+ return ERR_OK;
+ } else {
+ AsyncClient *c = new (std::nothrow) AsyncClient(pcb, NULL);
+#else
+ AsyncClient *c = new (std::nothrow) AsyncClient(pcb);
+#endif
+
+ if(c){
+ auto errorTracker = c->getACErrorTracker();
+#ifdef DEBUG_MORE
+ errorTracker->onErrorEvent(
+ [](void *obj, size_t ee){ ((AsyncServer*)(obj))->incEventCount(ee); },
+ this);
+#endif
+ ASYNC_TCP_DEBUG("_accept[%u]: connected\n", errorTracker->getConnectionId());
+ _connect_cb(_connect_cb_arg, c);
+ return errorTracker->getCallbackCloseError();
+ } else {
+ ASYNC_TCP_DEBUG("_accept: new AsyncClient() failed, connection aborted!\n");
+ if(tcp_close(pcb) != ERR_OK){
+ tcp_abort(pcb);
+ return ERR_ABRT;
+ }
+ }
+#if ASYNC_TCP_SSL_ENABLED
+ }
+#endif
+ }
+ if(tcp_close(pcb) != ERR_OK){
+ tcp_abort(pcb);
+ return ERR_ABRT;
+ }
+ return ERR_OK;
+}
+
+err_t AsyncServer::_s_accept(void *arg, tcp_pcb* pcb, err_t err){
+ return reinterpret_cast(arg)->_accept(pcb, err);
+}
+
+#if ASYNC_TCP_SSL_ENABLED
+err_t AsyncServer::_poll(tcp_pcb* pcb){
+ if(!tcp_ssl_has_client() && _pending){
+ struct pending_pcb * p = _pending;
+ if(p->pcb == pcb){
+ _pending = _pending->next;
+ } else {
+ while(p->next && p->next->pcb != pcb) p = p->next;
+ if(!p->next) return 0;
+ struct pending_pcb * b = p->next;
+ p->next = b->next;
+ p = b;
+ }
+ ASYNC_TCP_DEBUG("### remove from wait: %d\n", _clients_waiting);
+ AsyncClient *c = new (std::nothrow) AsyncClient(pcb, _ssl_ctx);
+ if(c){
+ c->onConnect([this](void * arg, AsyncClient *c){
+ _connect_cb(_connect_cb_arg, c);
+ }, this);
+ if(p->pb)
+ c->_recv(pcb, p->pb, 0);
+ }
+ // Should there be error handling for when "new AsynClient" fails??
+ free(p);
+ }
+ return ERR_OK;
+}
+
+err_t AsyncServer::_recv(struct tcp_pcb *pcb, struct pbuf *pb, err_t err){
+ if(!_pending)
+ return ERR_OK;
+
+ struct pending_pcb * p;
+
+ if(!pb){
+ ASYNC_TCP_DEBUG("### close from wait: %d\n", _clients_waiting);
+ p = _pending;
+ if(p->pcb == pcb){
+ _pending = _pending->next;
+ } else {
+ while(p->next && p->next->pcb != pcb) p = p->next;
+ if(!p->next) return 0;
+ struct pending_pcb * b = p->next;
+ p->next = b->next;
+ p = b;
+ }
+ if(p->pb){
+ pbuf_free(p->pb);
+ }
+ free(p);
+ size_t err = tcp_close(pcb);
+ if (err != ERR_OK) {
+ tcp_abort(pcb);
+ return ERR_ABRT;
+ }
+ } else {
+ ASYNC_TCP_DEBUG("### wait _recv: %u %d\n", pb->tot_len, _clients_waiting);
+ p = _pending;
+ while(p && p->pcb != pcb)
+ p = p->next;
+ if(p){
+ if(p->pb){
+ pbuf_chain(p->pb, pb);
+ } else {
+ p->pb = pb;
+ }
+ }
+ }
+ return ERR_OK;
+}
+
+int AsyncServer::_cert(const char *filename, uint8_t **buf){
+ if(_file_cb){
+ return _file_cb(_file_cb_arg, filename, buf);
+ }
+ *buf = 0;
+ return 0;
+}
+
+int AsyncServer::_s_cert(void *arg, const char *filename, uint8_t **buf){
+ return reinterpret_cast(arg)->_cert(filename, buf);
+}
+
+err_t AsyncServer::_s_poll(void *arg, struct tcp_pcb *pcb){
+ return reinterpret_cast(arg)->_poll(pcb);
+}
+
+err_t AsyncServer::_s_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *pb, err_t err){
+ return reinterpret_cast(arg)->_recv(pcb, pb, err);
+}
+#endif
diff --git a/src/ESPAsyncTCP.h b/src/ESPAsyncTCP.h
new file mode 100644
index 0000000..2d1f768
--- /dev/null
+++ b/src/ESPAsyncTCP.h
@@ -0,0 +1,327 @@
+/*
+ Asynchronous TCP library for Espressif MCUs
+
+ Copyright (c) 2016 Hristo Gochkov. 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 ASYNCTCP_H_
+#define ASYNCTCP_H_
+
+#include
+#include "IPAddress.h"
+#include
+#include
+
+extern "C" {
+ #include "lwip/init.h"
+ #include "lwip/err.h"
+ #include "lwip/pbuf.h"
+};
+
+class AsyncClient;
+class AsyncServer;
+class ACErrorTracker;
+
+#define ASYNC_MAX_ACK_TIME 5000
+#define ASYNC_WRITE_FLAG_COPY 0x01 //will allocate new buffer to hold the data while sending (else will hold reference to the data given)
+#define ASYNC_WRITE_FLAG_MORE 0x02 //will not send PSH flag, meaning that there should be more data to be sent before the application should react.
+
+struct tcp_pcb;
+struct ip_addr;
+#if ASYNC_TCP_SSL_ENABLED
+struct SSL_;
+typedef struct SSL_ SSL;
+struct SSL_CTX_;
+typedef struct SSL_CTX_ SSL_CTX;
+#endif
+
+typedef std::function AcConnectHandler;
+typedef std::function AcAckHandler;
+typedef std::function AcErrorHandler;
+typedef std::function AcDataHandler;
+typedef std::function AcPacketHandler;
+typedef std::function AcTimeoutHandler;
+typedef std::function AsNotifyHandler;
+
+enum error_events {
+ EE_OK = 0,
+ EE_ABORTED, // Callback or foreground aborted connections
+ EE_ERROR_CB, // Stack initiated aborts via error Callbacks.
+ EE_CONNECTED_CB,
+ EE_RECV_CB,
+ EE_ACCEPT_CB,
+ EE_MAX
+};
+// DEBUG_MORE is for gathering more information on which CBs close events are
+// occuring and count.
+// #define DEBUG_MORE 1
+class ACErrorTracker {
+ private:
+ AsyncClient *_client;
+ err_t _close_error;
+ int _errored;
+#if DEBUG_ESP_ASYNC_TCP
+ size_t _connectionId;
+#endif
+#ifdef DEBUG_MORE
+ AsNotifyHandler _error_event_cb;
+ void* _error_event_cb_arg;
+#endif
+
+ protected:
+ friend class AsyncClient;
+ friend class AsyncServer;
+#ifdef DEBUG_MORE
+ void onErrorEvent(AsNotifyHandler cb, void *arg);
+#endif
+#if DEBUG_ESP_ASYNC_TCP
+ void setConnectionId(size_t id) { _connectionId=id;}
+ size_t getConnectionId(void) { return _connectionId;}
+#endif
+ void setCloseError(err_t e);
+ void setErrored(size_t errorEvent);
+ err_t getCallbackCloseError(void);
+ void clearClient(void){ if (_client) _client = NULL;}
+
+ public:
+ err_t getCloseError(void) const { return _close_error;}
+ bool hasClient(void) const { return (_client != NULL);}
+ ACErrorTracker(AsyncClient *c);
+ ~ACErrorTracker() {}
+};
+
+class AsyncClient {
+ protected:
+ friend class AsyncTCPbuffer;
+ friend class AsyncServer;
+ tcp_pcb* _pcb;
+ AcConnectHandler _connect_cb;
+ void* _connect_cb_arg;
+ AcConnectHandler _discard_cb;
+ void* _discard_cb_arg;
+ AcAckHandler _sent_cb;
+ void* _sent_cb_arg;
+ AcErrorHandler _error_cb;
+ void* _error_cb_arg;
+ AcDataHandler _recv_cb;
+ void* _recv_cb_arg;
+ AcPacketHandler _pb_cb;
+ void* _pb_cb_arg;
+ AcTimeoutHandler _timeout_cb;
+ void* _timeout_cb_arg;
+ AcConnectHandler _poll_cb;
+ void* _poll_cb_arg;
+ bool _pcb_busy;
+#if ASYNC_TCP_SSL_ENABLED
+ bool _pcb_secure;
+ bool _handshake_done;
+#endif
+ uint32_t _pcb_sent_at;
+ bool _close_pcb;
+ bool _ack_pcb;
+ uint32_t _tx_unacked_len;
+ uint32_t _tx_acked_len;
+ uint32_t _tx_unsent_len;
+ uint32_t _rx_ack_len;
+ uint32_t _rx_last_packet;
+ uint32_t _rx_since_timeout;
+ uint32_t _ack_timeout;
+ uint16_t _connect_port;
+ u8_t _recv_pbuf_flags;
+ std::shared_ptr _errorTracker;
+
+ void _close();
+ void _connected(std::shared_ptr& closeAbort, void* pcb, err_t err);
+ void _error(err_t err);
+#if ASYNC_TCP_SSL_ENABLED
+ void _ssl_error(int8_t err);
+#endif
+ void _poll(std::shared_ptr& closeAbort, tcp_pcb* pcb);
+ void _sent(std::shared_ptr& closeAbort, tcp_pcb* pcb, uint16_t len);
+#if LWIP_VERSION_MAJOR == 1
+ void _dns_found(struct ip_addr *ipaddr);
+#else
+ void _dns_found(const ip_addr *ipaddr);
+#endif
+ static err_t _s_poll(void *arg, struct tcp_pcb *tpcb);
+ static err_t _s_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *pb, err_t err);
+ static void _s_error(void *arg, err_t err);
+ static err_t _s_sent(void *arg, struct tcp_pcb *tpcb, uint16_t len);
+ static err_t _s_connected(void* arg, void* tpcb, err_t err);
+#if LWIP_VERSION_MAJOR == 1
+ static void _s_dns_found(const char *name, struct ip_addr *ipaddr, void *arg);
+#else
+ static void _s_dns_found(const char *name, const ip_addr *ipaddr, void *arg);
+#endif
+#if ASYNC_TCP_SSL_ENABLED
+ static void _s_data(void *arg, struct tcp_pcb *tcp, uint8_t * data, size_t len);
+ static void _s_handshake(void *arg, struct tcp_pcb *tcp, SSL *ssl);
+ static void _s_ssl_error(void *arg, struct tcp_pcb *tcp, int8_t err);
+#endif
+ std::shared_ptr getACErrorTracker(void) const { return _errorTracker; };
+ void setCloseError(err_t e) const { _errorTracker->setCloseError(e);}
+
+ public:
+ AsyncClient* prev;
+ AsyncClient* next;
+
+#if ASYNC_TCP_SSL_ENABLED
+ AsyncClient(tcp_pcb* pcb = 0, SSL_CTX * ssl_ctx = NULL);
+#else
+ AsyncClient(tcp_pcb* pcb = 0);
+#endif
+ ~AsyncClient();
+
+ AsyncClient & operator=(const AsyncClient &other);
+ AsyncClient & operator+=(const AsyncClient &other);
+
+ bool operator==(const AsyncClient &other);
+
+ bool operator!=(const AsyncClient &other) {
+ return !(*this == other);
+ }
+#if ASYNC_TCP_SSL_ENABLED
+ bool connect(IPAddress ip, uint16_t port, bool secure=false);
+ bool connect(const char* host, uint16_t port, bool secure=false);
+#else
+ bool connect(IPAddress ip, uint16_t port);
+ bool connect(const char* host, uint16_t port);
+#endif
+ void close(bool now = false);
+ void stop();
+ void abort();
+ bool free();
+
+ bool canSend();//ack is not pending
+ size_t space();
+ size_t add(const char* data, size_t size, uint8_t apiflags=0);//add for sending
+ bool send();//send all data added with the method above
+ size_t ack(size_t len); //ack data that you have not acked using the method below
+ void ackLater(){ _ack_pcb = false; } //will not ack the current packet. Call from onData
+ bool isRecvPush(){ return !!(_recv_pbuf_flags & PBUF_FLAG_PUSH); }
+#if DEBUG_ESP_ASYNC_TCP
+ size_t getConnectionId(void) const { return _errorTracker->getConnectionId();}
+#endif
+#if ASYNC_TCP_SSL_ENABLED
+ SSL *getSSL();
+#endif
+
+ size_t write(const char* data);
+ size_t write(const char* data, size_t size, uint8_t apiflags=0); //only when canSend() == true
+
+ uint8_t state();
+ bool connecting();
+ bool connected();
+ bool disconnecting();
+ bool disconnected();
+ bool freeable();//disconnected or disconnecting
+
+ uint16_t getMss();
+ uint32_t getRxTimeout();
+ void setRxTimeout(uint32_t timeout);//no RX data timeout for the connection in seconds
+ uint32_t getAckTimeout();
+ void setAckTimeout(uint32_t timeout);//no ACK timeout for the last sent packet in milliseconds
+ void setNoDelay(bool nodelay);
+ bool getNoDelay();
+ uint32_t getRemoteAddress();
+ uint16_t getRemotePort();
+ uint32_t getLocalAddress();
+ uint16_t getLocalPort();
+
+ IPAddress remoteIP();
+ uint16_t remotePort();
+ IPAddress localIP();
+ uint16_t localPort();
+
+ void onConnect(AcConnectHandler cb, void* arg = 0); //on successful connect
+ void onDisconnect(AcConnectHandler cb, void* arg = 0); //disconnected
+ void onAck(AcAckHandler cb, void* arg = 0); //ack received
+ void onError(AcErrorHandler cb, void* arg = 0); //unsuccessful connect or error
+ void onData(AcDataHandler cb, void* arg = 0); //data received (called if onPacket is not used)
+ void onPacket(AcPacketHandler cb, void* arg = 0); //data received
+ void onTimeout(AcTimeoutHandler cb, void* arg = 0); //ack timeout
+ void onPoll(AcConnectHandler cb, void* arg = 0); //every 125ms when connected
+ void ackPacket(struct pbuf * pb);
+
+ const char * errorToString(err_t error);
+ const char * stateToString();
+
+ void _recv(std::shared_ptr& closeAbort, tcp_pcb* pcb, pbuf* pb, err_t err);
+ err_t getCloseError(void) const { return _errorTracker->getCloseError();}
+};
+
+#if ASYNC_TCP_SSL_ENABLED
+typedef std::function AcSSlFileHandler;
+struct pending_pcb;
+#endif
+
+
+class AsyncServer {
+ protected:
+ uint16_t _port;
+ IPAddress _addr;
+ bool _noDelay;
+ tcp_pcb* _pcb;
+ AcConnectHandler _connect_cb;
+ void* _connect_cb_arg;
+#if ASYNC_TCP_SSL_ENABLED
+ struct pending_pcb * _pending;
+ SSL_CTX * _ssl_ctx;
+ AcSSlFileHandler _file_cb;
+ void* _file_cb_arg;
+#endif
+#ifdef DEBUG_MORE
+ int _event_count[EE_MAX];
+#endif
+
+ public:
+
+ AsyncServer(IPAddress addr, uint16_t port);
+ AsyncServer(uint16_t port);
+ ~AsyncServer();
+ void onClient(AcConnectHandler cb, void* arg);
+#if ASYNC_TCP_SSL_ENABLED
+ void onSslFileRequest(AcSSlFileHandler cb, void* arg);
+ void beginSecure(const char *cert, const char *private_key_file, const char *password);
+#endif
+ void begin();
+ void end();
+ void setNoDelay(bool nodelay);
+ bool getNoDelay();
+ uint8_t status();
+#ifdef DEBUG_MORE
+ int getEventCount(size_t ee) const { return _event_count[ee];}
+#endif
+ protected:
+ err_t _accept(tcp_pcb* newpcb, err_t err);
+ static err_t _s_accept(void *arg, tcp_pcb* newpcb, err_t err);
+#ifdef DEBUG_MORE
+ int incEventCount(size_t ee) { return ++_event_count[ee];}
+#endif
+#if ASYNC_TCP_SSL_ENABLED
+ int _cert(const char *filename, uint8_t **buf);
+ err_t _poll(tcp_pcb* pcb);
+ err_t _recv(tcp_pcb *pcb, struct pbuf *pb, err_t err);
+ static int _s_cert(void *arg, const char *filename, uint8_t **buf);
+ static err_t _s_poll(void *arg, struct tcp_pcb *tpcb);
+ static err_t _s_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *pb, err_t err);
+#endif
+};
+
+
+#endif /* ASYNCTCP_H_ */
diff --git a/src/ESPAsyncTCPbuffer.cpp b/src/ESPAsyncTCPbuffer.cpp
new file mode 100644
index 0000000..d2261da
--- /dev/null
+++ b/src/ESPAsyncTCPbuffer.cpp
@@ -0,0 +1,555 @@
+/**
+ * @file ESPAsyncTCPbuffer.cpp
+ * @date 22.01.2016
+ * @author Markus Sattler
+ *
+ * Copyright (c) 2015 Markus Sattler. All rights reserved.
+ * This file is part of the Asynv TCP for ESP.
+ *
+ * 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
+ *
+ */
+
+
+#include
+#include
+
+#include "ESPAsyncTCPbuffer.h"
+
+
+AsyncTCPbuffer::AsyncTCPbuffer(AsyncClient* client) {
+ if(client == NULL) {
+ DEBUG_ASYNC_TCP("[A-TCP] client is null!!!\n");
+ panic();
+ }
+
+ _client = client;
+ _TXbufferWrite = new (std::nothrow) cbuf(TCP_MSS);
+ _TXbufferRead = _TXbufferWrite;
+ _RXbuffer = new (std::nothrow) cbuf(100);
+ _RXmode = ATB_RX_MODE_FREE;
+ _rxSize = 0;
+ _rxTerminator = 0x00;
+ _rxReadBytesPtr = NULL;
+ _rxReadStringPtr = NULL;
+ _cbDisconnect = NULL;
+
+ _cbRX = NULL;
+ _cbDone = NULL;
+ _attachCallbacks();
+}
+
+AsyncTCPbuffer::~AsyncTCPbuffer() {
+ if(_client) {
+ _client->close();
+ }
+
+ if(_RXbuffer) {
+ delete _RXbuffer;
+ _RXbuffer = NULL;
+ }
+
+ if(_TXbufferWrite) {
+ // will be deleted in _TXbufferRead chain
+ _TXbufferWrite = NULL;
+ }
+
+ if(_TXbufferRead) {
+ cbuf * next = _TXbufferRead->next;
+ delete _TXbufferRead;
+ while(next != NULL) {
+ _TXbufferRead = next;
+ next = _TXbufferRead->next;
+ delete _TXbufferRead;
+ }
+ _TXbufferRead = NULL;
+ }
+}
+
+size_t AsyncTCPbuffer::write(String & data) {
+ return write(data.c_str(), data.length());
+}
+
+size_t AsyncTCPbuffer::write(uint8_t data) {
+ return write(&data, 1);
+}
+
+size_t AsyncTCPbuffer::write(const char* data) {
+ return write((const uint8_t *) data, strlen(data));
+}
+
+size_t AsyncTCPbuffer::write(const char *data, size_t len) {
+ return write((const uint8_t *) data, len);
+}
+
+/**
+ * write data in to buffer and try to send the data
+ * @param data
+ * @param len
+ * @return
+ */
+size_t AsyncTCPbuffer::write(const uint8_t *data, size_t len) {
+ if(_TXbufferWrite == NULL || _client == NULL || !_client->connected() || data == NULL || len == 0) {
+ return 0;
+ }
+
+ size_t bytesLeft = len;
+ while(bytesLeft) {
+ size_t w = _TXbufferWrite->write((const char*) data, bytesLeft);
+ bytesLeft -= w;
+ data += w;
+ _sendBuffer();
+
+ // add new buffer since we have more data
+ if(_TXbufferWrite->full() && bytesLeft > 0) {
+
+ // to less ram!!!
+ if(ESP.getFreeHeap() < 4096) {
+ DEBUG_ASYNC_TCP("[A-TCP] run out of Heap can not send all Data!\n");
+ return (len - bytesLeft);
+ }
+
+ cbuf * next = new (std::nothrow) cbuf(TCP_MSS);
+ if(next == NULL) {
+ DEBUG_ASYNC_TCP("[A-TCP] run out of Heap!\n");
+ panic();
+ } else {
+ DEBUG_ASYNC_TCP("[A-TCP] new cbuf\n");
+ }
+
+ // add new buffer to chain (current cbuf)
+ _TXbufferWrite->next = next;
+
+ // move ptr for next data
+ _TXbufferWrite = next;
+ }
+ }
+
+ return len;
+
+}
+
+/**
+ * wait until all data has send out
+ */
+void AsyncTCPbuffer::flush() {
+ while(!_TXbufferWrite->empty()) {
+ while(connected() && !_client->canSend()) {
+ delay(0);
+ }
+ if(!connected())
+ return;
+ _sendBuffer();
+ }
+}
+
+void AsyncTCPbuffer::noCallback() {
+ _RXmode = ATB_RX_MODE_NONE;
+}
+
+void AsyncTCPbuffer::readStringUntil(char terminator, String * str, AsyncTCPbufferDoneCb done) {
+ if(_client == NULL) {
+ return;
+ }
+ DEBUG_ASYNC_TCP("[A-TCP] readStringUntil terminator: %02X\n", terminator);
+ _RXmode = ATB_RX_MODE_NONE;
+ _cbDone = done;
+ _rxReadStringPtr = str;
+ _rxTerminator = terminator;
+ _rxSize = 0;
+ _RXmode = ATB_RX_MODE_TERMINATOR_STRING;
+}
+
+/*
+ void AsyncTCPbuffer::readBytesUntil(char terminator, char *buffer, size_t length, AsyncTCPbufferDoneCb done) {
+ _RXmode = ATB_RX_MODE_NONE;
+ _cbDone = done;
+ _rxReadBytesPtr = (uint8_t *) buffer;
+ _rxTerminator = terminator;
+ _rxSize = length;
+ _RXmode = ATB_RX_MODE_TERMINATOR;
+ _handleRxBuffer(NULL, 0);
+ }
+
+ void AsyncTCPbuffer::readBytesUntil(char terminator, uint8_t *buffer, size_t length, AsyncTCPbufferDoneCb done) {
+ readBytesUntil(terminator, (char *) buffer, length, done);
+ }
+ */
+
+void AsyncTCPbuffer::readBytes(char *buffer, size_t length, AsyncTCPbufferDoneCb done) {
+ if(_client == NULL) {
+ return;
+ }
+ DEBUG_ASYNC_TCP("[A-TCP] readBytes length: %d\n", length);
+ _RXmode = ATB_RX_MODE_NONE;
+ _cbDone = done;
+ _rxReadBytesPtr = (uint8_t *) buffer;
+ _rxSize = length;
+ _RXmode = ATB_RX_MODE_READ_BYTES;
+}
+
+void AsyncTCPbuffer::readBytes(uint8_t *buffer, size_t length, AsyncTCPbufferDoneCb done) {
+ readBytes((char *) buffer, length, done);
+}
+
+void AsyncTCPbuffer::onData(AsyncTCPbufferDataCb cb) {
+ if(_client == NULL) {
+ return;
+ }
+ DEBUG_ASYNC_TCP("[A-TCP] onData\n");
+ _RXmode = ATB_RX_MODE_NONE;
+ _cbDone = NULL;
+ _cbRX = cb;
+ _RXmode = ATB_RX_MODE_FREE;
+}
+
+void AsyncTCPbuffer::onDisconnect(AsyncTCPbufferDisconnectCb cb) {
+ _cbDisconnect = cb;
+}
+
+IPAddress AsyncTCPbuffer::remoteIP() {
+ if(!_client) {
+ return IPAddress(0U);
+ }
+ return _client->remoteIP();
+}
+
+uint16_t AsyncTCPbuffer::remotePort() {
+ if(!_client) {
+ return 0;
+ }
+ return _client->remotePort();
+}
+
+bool AsyncTCPbuffer::connected() {
+ if(!_client) {
+ return false;
+ }
+ return _client->connected();
+}
+
+void AsyncTCPbuffer::stop() {
+
+ if(!_client) {
+ return;
+ }
+ _client->stop();
+ _client = NULL;
+
+ if(_cbDone) {
+ switch(_RXmode) {
+ case ATB_RX_MODE_READ_BYTES:
+ case ATB_RX_MODE_TERMINATOR:
+ case ATB_RX_MODE_TERMINATOR_STRING:
+ _RXmode = ATB_RX_MODE_NONE;
+ _cbDone(false, NULL);
+ break;
+ default:
+ break;
+ }
+ }
+ _RXmode = ATB_RX_MODE_NONE;
+}
+
+void AsyncTCPbuffer::close() {
+ stop();
+}
+
+
+///--------------------------------
+
+/**
+ * attachCallbacks to AsyncClient class
+ */
+void AsyncTCPbuffer::_attachCallbacks() {
+ if(!_client) {
+ return;
+ }
+ DEBUG_ASYNC_TCP("[A-TCP] attachCallbacks\n");
+
+ _client->onPoll([](void *obj, AsyncClient* c) {
+ (void)c;
+ AsyncTCPbuffer* b = ((AsyncTCPbuffer*)(obj));
+ if((b->_TXbufferRead != NULL) && !b->_TXbufferRead->empty()) {
+ b->_sendBuffer();
+ }
+ // if(!b->_RXbuffer->empty()) {
+ // b->_handleRxBuffer(NULL, 0);
+ // }
+ }, this);
+
+ _client->onAck([](void *obj, AsyncClient* c, size_t len, uint32_t time) {
+ (void)c;
+ (void)len;
+ (void)time;
+ DEBUG_ASYNC_TCP("[A-TCP] onAck\n");
+ ((AsyncTCPbuffer*)(obj))->_sendBuffer();
+ }, this);
+
+ _client->onDisconnect([](void *obj, AsyncClient* c) {
+ DEBUG_ASYNC_TCP("[A-TCP] onDisconnect\n");
+ AsyncTCPbuffer* b = ((AsyncTCPbuffer*)(obj));
+ b->_client = NULL;
+ bool del = true;
+ if(b->_cbDisconnect) {
+ del = b->_cbDisconnect(b);
+ }
+ delete c;
+ if(del) {
+ delete b;
+ }
+ }, this);
+
+ _client->onData([](void *obj, AsyncClient* c, void *buf, size_t len) {
+ (void)c;
+ AsyncTCPbuffer* b = ((AsyncTCPbuffer*)(obj));
+ b->_rxData((uint8_t *)buf, len);
+ }, this);
+
+ _client->onTimeout([](void *obj, AsyncClient* c, uint32_t time){
+ (void)obj;
+ (void)time;
+ DEBUG_ASYNC_TCP("[A-TCP] onTimeout\n");
+ c->close();
+ }, this);
+
+ DEBUG_ASYNC_TCP("[A-TCP] attachCallbacks Done.\n");
+}
+
+/**
+ * send TX buffer if possible
+ */
+void AsyncTCPbuffer::_sendBuffer() {
+ //DEBUG_ASYNC_TCP("[A-TCP] _sendBuffer...\n");
+ size_t available = _TXbufferRead->available();
+ if(available == 0 || _client == NULL || !_client->connected() || !_client->canSend()) {
+ return;
+ }
+
+ while(connected() && (_client->space() > 0) && (_TXbufferRead->available() > 0) && _client->canSend()) {
+
+ available = _TXbufferRead->available();
+
+ if(available > _client->space()) {
+ available = _client->space();
+ }
+
+ char *out = new (std::nothrow) char[available];
+ if(out == NULL) {
+ DEBUG_ASYNC_TCP("[A-TCP] to less heap, try later.\n");
+ return;
+ }
+
+ // read data from buffer
+ _TXbufferRead->peek(out, available);
+
+ // send data
+ size_t send = _client->write((const char*) out, available);
+ if(send != available) {
+ DEBUG_ASYNC_TCP("[A-TCP] write failed send: %d available: %d \n", send, available);
+ if(!connected()) {
+ DEBUG_ASYNC_TCP("[A-TCP] incomplete transfer, connection lost.\n");
+ }
+ }
+
+ // remove really send data from buffer
+ _TXbufferRead->remove(send);
+
+ // if buffer is empty and there is a other buffer in chain delete the empty one
+ if(_TXbufferRead->available() == 0 && _TXbufferRead->next != NULL) {
+ cbuf * old = _TXbufferRead;
+ _TXbufferRead = _TXbufferRead->next;
+ delete old;
+ DEBUG_ASYNC_TCP("[A-TCP] delete cbuf\n");
+ }
+
+ delete out;
+ }
+
+}
+
+/**
+ * called on incoming data
+ * @param buf
+ * @param len
+ */
+void AsyncTCPbuffer::_rxData(uint8_t *buf, size_t len) {
+ if(!_client || !_client->connected()) {
+ DEBUG_ASYNC_TCP("[A-TCP] not connected!\n");
+ return;
+ }
+ if(!_RXbuffer) {
+ DEBUG_ASYNC_TCP("[A-TCP] _rxData no _RXbuffer!\n");
+ return;
+ }
+ DEBUG_ASYNC_TCP("[A-TCP] _rxData len: %d RXmode: %d\n", len, _RXmode);
+
+ size_t handled = 0;
+
+ if(_RXmode != ATB_RX_MODE_NONE) {
+ handled = _handleRxBuffer((uint8_t *) buf, len);
+ buf += handled;
+ len -= handled;
+
+ // handle as much as possible before using the buffer
+ if(_RXbuffer->empty()) {
+ while(_RXmode != ATB_RX_MODE_NONE && handled != 0 && len > 0) {
+ handled = _handleRxBuffer(buf, len);
+ buf += handled;
+ len -= handled;
+ }
+ }
+ }
+
+ if(len > 0) {
+
+ if(_RXbuffer->room() < len) {
+ // to less space
+ DEBUG_ASYNC_TCP("[A-TCP] _rxData buffer full try resize\n");
+ _RXbuffer->resizeAdd((len + _RXbuffer->room()));
+
+ if(_RXbuffer->room() < len) {
+ DEBUG_ASYNC_TCP("[A-TCP] _rxData buffer to full can only handle %d!!!\n", _RXbuffer->room());
+ }
+ }
+
+ _RXbuffer->write((const char *) (buf), len);
+ }
+
+ if(!_RXbuffer->empty() && _RXmode != ATB_RX_MODE_NONE) {
+ // handle as much as possible data in buffer
+ handled = _handleRxBuffer(NULL, 0);
+ while(_RXmode != ATB_RX_MODE_NONE && handled != 0) {
+ handled = _handleRxBuffer(NULL, 0);
+ }
+ }
+
+ // clean up ram
+ if(_RXbuffer->empty() && _RXbuffer->room() != 100) {
+ _RXbuffer->resize(100);
+ }
+
+}
+
+/**
+ *
+ */
+size_t AsyncTCPbuffer::_handleRxBuffer(uint8_t *buf, size_t len) {
+ if(!_client || !_client->connected() || _RXbuffer == NULL) {
+ return 0;
+ }
+
+ DEBUG_ASYNC_TCP("[A-TCP] _handleRxBuffer len: %d RXmode: %d\n", len, _RXmode);
+
+ size_t BufferAvailable = _RXbuffer->available();
+ size_t r = 0;
+
+ if(_RXmode == ATB_RX_MODE_NONE) {
+ return 0;
+ } else if(_RXmode == ATB_RX_MODE_FREE) {
+ if(_cbRX == NULL) {
+ return 0;
+ }
+
+ if(BufferAvailable > 0) {
+ uint8_t * b = new (std::nothrow) uint8_t[BufferAvailable];
+ if(b == NULL){
+ panic(); //TODO: What action should this be ?
+ }
+ _RXbuffer->peek((char *) b, BufferAvailable);
+ r = _cbRX(b, BufferAvailable);
+ _RXbuffer->remove(r);
+ }
+
+ if(r == BufferAvailable && buf && (len > 0)) {
+ return _cbRX(buf, len);
+ } else {
+ return 0;
+ }
+
+ } else if(_RXmode == ATB_RX_MODE_READ_BYTES) {
+ if(_rxReadBytesPtr == NULL || _cbDone == NULL) {
+ return 0;
+ }
+
+ size_t newReadCount = 0;
+
+ if(BufferAvailable) {
+ r = _RXbuffer->read((char *) _rxReadBytesPtr, _rxSize);
+ _rxSize -= r;
+ _rxReadBytesPtr += r;
+ }
+
+ if(_RXbuffer->empty() && (len > 0) && buf) {
+ r = len;
+ if(r > _rxSize) {
+ r = _rxSize;
+ }
+ memcpy(_rxReadBytesPtr, buf, r);
+ _rxReadBytesPtr += r;
+ _rxSize -= r;
+ newReadCount += r;
+ }
+
+ if(_rxSize == 0) {
+ _RXmode = ATB_RX_MODE_NONE;
+ _cbDone(true, NULL);
+ }
+
+ // add left over bytes to Buffer
+ return newReadCount;
+
+ } else if(_RXmode == ATB_RX_MODE_TERMINATOR) {
+ // TODO implement read terminator non string
+
+ } else if(_RXmode == ATB_RX_MODE_TERMINATOR_STRING) {
+ if(_rxReadStringPtr == NULL || _cbDone == NULL) {
+ return 0;
+ }
+
+ // handle Buffer
+ if(BufferAvailable > 0) {
+ while(!_RXbuffer->empty()) {
+ char c = _RXbuffer->read();
+ if(c == _rxTerminator || c == 0x00) {
+ _RXmode = ATB_RX_MODE_NONE;
+ _cbDone(true, _rxReadStringPtr);
+ return 0;
+ } else {
+ (*_rxReadStringPtr) += c;
+ }
+ }
+ }
+
+ if(_RXbuffer->empty() && (len > 0) && buf) {
+ size_t newReadCount = 0;
+ while(newReadCount < len) {
+ char c = (char) *buf;
+ buf++;
+ newReadCount++;
+ if(c == _rxTerminator || c == 0x00) {
+ _RXmode = ATB_RX_MODE_NONE;
+ _cbDone(true, _rxReadStringPtr);
+ return newReadCount;
+ } else {
+ (*_rxReadStringPtr) += c;
+ }
+ }
+ return newReadCount;
+ }
+ }
+
+ return 0;
+}
diff --git a/src/ESPAsyncTCPbuffer.h b/src/ESPAsyncTCPbuffer.h
new file mode 100644
index 0000000..08a57c7
--- /dev/null
+++ b/src/ESPAsyncTCPbuffer.h
@@ -0,0 +1,118 @@
+/**
+ * @file ESPAsyncTCPbuffer.h
+ * @date 22.01.2016
+ * @author Markus Sattler
+ *
+ * Copyright (c) 2015 Markus Sattler. All rights reserved.
+ * This file is part of the Asynv TCP for ESP.
+ *
+ * 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 ESPASYNCTCPBUFFER_H_
+#define ESPASYNCTCPBUFFER_H_
+
+//#define DEBUG_ASYNC_TCP(...) while(((U0S >> USTXC) & 0x7F) != 0x00); os_printf( __VA_ARGS__ ); while(((U0S >> USTXC) & 0x7F) != 0x00)
+//#define DEBUG_ASYNC_TCP ASYNC_TCP_DEBUG
+#ifndef DEBUG_ASYNC_TCP
+#define DEBUG_ASYNC_TCP(...)
+#endif
+
+#include
+#include
+
+#include "ESPAsyncTCP.h"
+
+
+
+typedef enum {
+ ATB_RX_MODE_NONE,
+ ATB_RX_MODE_FREE,
+ ATB_RX_MODE_READ_BYTES,
+ ATB_RX_MODE_TERMINATOR,
+ ATB_RX_MODE_TERMINATOR_STRING
+} atbRxMode_t;
+
+class AsyncTCPbuffer: public Print {
+
+ public:
+
+ typedef std::function AsyncTCPbufferDataCb;
+ typedef std::function AsyncTCPbufferDoneCb;
+ typedef std::function AsyncTCPbufferDisconnectCb;
+
+ AsyncTCPbuffer(AsyncClient* c);
+ virtual ~AsyncTCPbuffer();
+
+ size_t write(String & data);
+ size_t write(uint8_t data);
+ size_t write(const char* data);
+ size_t write(const char *data, size_t len);
+ size_t write(const uint8_t *data, size_t len);
+
+ void flush();
+
+ void noCallback();
+
+ void readStringUntil(char terminator, String * str, AsyncTCPbufferDoneCb done);
+
+ // TODO implement read terminator non string
+ //void readBytesUntil(char terminator, char *buffer, size_t length, AsyncTCPbufferDoneCb done);
+ //void readBytesUntil(char terminator, uint8_t *buffer, size_t length, AsyncTCPbufferDoneCb done);
+
+ void readBytes(char *buffer, size_t length, AsyncTCPbufferDoneCb done);
+ void readBytes(uint8_t *buffer, size_t length, AsyncTCPbufferDoneCb done);
+
+ // TODO implement
+ // void setTimeout(size_t timeout);
+
+ void onData(AsyncTCPbufferDataCb cb);
+ void onDisconnect(AsyncTCPbufferDisconnectCb cb);
+
+ IPAddress remoteIP();
+ uint16_t remotePort();
+ IPAddress localIP();
+ uint16_t localPort();
+
+ bool connected();
+
+ void stop();
+ void close();
+
+ protected:
+ AsyncClient* _client;
+ cbuf * _TXbufferRead;
+ cbuf * _TXbufferWrite;
+ cbuf * _RXbuffer;
+ atbRxMode_t _RXmode;
+ size_t _rxSize;
+ char _rxTerminator;
+ uint8_t * _rxReadBytesPtr;
+ String * _rxReadStringPtr;
+
+ AsyncTCPbufferDataCb _cbRX;
+ AsyncTCPbufferDoneCb _cbDone;
+ AsyncTCPbufferDisconnectCb _cbDisconnect;
+
+ void _attachCallbacks();
+ void _sendBuffer();
+ void _on_close();
+ void _rxData(uint8_t *buf, size_t len);
+ size_t _handleRxBuffer(uint8_t *buf, size_t len);
+
+};
+
+#endif /* ESPASYNCTCPBUFFER_H_ */
diff --git a/src/SyncClient.cpp b/src/SyncClient.cpp
new file mode 100644
index 0000000..8335358
--- /dev/null
+++ b/src/SyncClient.cpp
@@ -0,0 +1,414 @@
+/*
+ Asynchronous TCP library for Espressif MCUs
+
+ Copyright (c) 2016 Hristo Gochkov. 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
+*/
+#include "Arduino.h"
+#include "SyncClient.h"
+#include "ESPAsyncTCP.h"
+#include "cbuf.h"
+#include
+
+#define DEBUG_ESP_SYNC_CLIENT
+#if defined(DEBUG_ESP_SYNC_CLIENT) && !defined(SYNC_CLIENT_DEBUG)
+#define SYNC_CLIENT_DEBUG( format, ...) DEBUG_GENERIC_P("[SYNC_CLIENT]", format, ##__VA_ARGS__)
+#endif
+#ifndef SYNC_CLIENT_DEBUG
+#define SYNC_CLIENT_DEBUG(...) do { (void)0;} while(false)
+#endif
+
+/*
+ Without LWIP_NETIF_TX_SINGLE_PBUF, all tcp_writes default to "no copy".
+ Referenced data must be preserved and free-ed from the specified tcp_sent()
+ callback. Alternative, tcp_writes need to use the TCP_WRITE_FLAG_COPY
+ attribute.
+*/
+static_assert(LWIP_NETIF_TX_SINGLE_PBUF, "Required, tcp_write() must always copy.");
+
+SyncClient::SyncClient(size_t txBufLen)
+ : _client(NULL)
+ , _tx_buffer(NULL)
+ , _tx_buffer_size(txBufLen)
+ , _rx_buffer(NULL)
+ , _ref(NULL)
+{
+ ref();
+}
+
+SyncClient::SyncClient(AsyncClient *client, size_t txBufLen)
+ : _client(client)
+ , _tx_buffer(new (std::nothrow) cbuf(txBufLen))
+ , _tx_buffer_size(txBufLen)
+ , _rx_buffer(NULL)
+ , _ref(NULL)
+{
+ if(ref() > 0 && _client != NULL)
+ _attachCallbacks();
+}
+
+SyncClient::~SyncClient(){
+ if (0 == unref())
+ _release();
+}
+
+void SyncClient::_release(){
+ if(_client != NULL){
+ _client->onData(NULL, NULL);
+ _client->onAck(NULL, NULL);
+ _client->onPoll(NULL, NULL);
+ _client->abort();
+ _client = NULL;
+ }
+ if(_tx_buffer != NULL){
+ cbuf *b = _tx_buffer;
+ _tx_buffer = NULL;
+ delete b;
+ }
+ while(_rx_buffer != NULL){
+ cbuf *b = _rx_buffer;
+ _rx_buffer = _rx_buffer->next;
+ delete b;
+ }
+}
+
+int SyncClient::ref(){
+ if(_ref == NULL){
+ _ref = new (std::nothrow) int;
+ if(_ref != NULL)
+ *_ref = 0;
+ else
+ return -1;
+ }
+ return (++*_ref);
+}
+
+int SyncClient::unref(){
+ int count = -1;
+ if (_ref != NULL) {
+ count = --*_ref;
+ if (0 == count) {
+ delete _ref;
+ _ref = NULL;
+ }
+ }
+ return count;
+}
+
+#if ASYNC_TCP_SSL_ENABLED
+int SyncClient::_connect(const IPAddress& ip, uint16_t port, bool secure){
+#else
+int SyncClient::_connect(const IPAddress& ip, uint16_t port){
+#endif
+ if(connected())
+ return 0;
+ if(_client != NULL)
+ delete _client;
+
+ _client = new (std::nothrow) AsyncClient();
+ if (_client == NULL)
+ return 0;
+
+ _client->onConnect([](void *obj, AsyncClient *c){ ((SyncClient*)(obj))->_onConnect(c); }, this);
+ _attachCallbacks_Disconnect();
+#if ASYNC_TCP_SSL_ENABLED
+ if(_client->connect(ip, port, secure)){
+#else
+ if(_client->connect(ip, port)){
+#endif
+ while(_client != NULL && !_client->connected() && !_client->disconnecting())
+ delay(1);
+ return connected();
+ }
+ return 0;
+}
+
+#if ASYNC_TCP_SSL_ENABLED
+int SyncClient::connect(const char *host, uint16_t port, bool secure){
+#else
+int SyncClient::connect(const char *host, uint16_t port){
+#endif
+ if(connected())
+ return 0;
+ if(_client != NULL)
+ delete _client;
+
+ _client = new (std::nothrow) AsyncClient();
+ if (_client == NULL)
+ return 0;
+
+ _client->onConnect([](void *obj, AsyncClient *c){ ((SyncClient*)(obj))->_onConnect(c); }, this);
+ _attachCallbacks_Disconnect();
+#if ASYNC_TCP_SSL_ENABLED
+ if(_client->connect(host, port, secure)){
+#else
+ if(_client->connect(host, port)){
+#endif
+ while(_client != NULL && !_client->connected() && !_client->disconnecting())
+ delay(1);
+ return connected();
+ }
+ return 0;
+}
+//#define SYNCCLIENT_NEW_OPERATOR_EQUAL
+#ifdef SYNCCLIENT_NEW_OPERATOR_EQUAL
+/*
+ New behavior for operator=
+
+ Allow for the object to be placed on a queue and transfered to a new container
+ with buffers still in tact. Avoiding receive data drops. Transfers rx and tx
+ buffers. Supports return by value.
+
+ Note, this is optional, the old behavior is the default.
+
+*/
+SyncClient & SyncClient::operator=(const SyncClient &other){
+ int *rhsref = other._ref;
+ ++*rhsref; // Just in case the left and right side are the same object with different containers
+ if (0 == unref())
+ _release();
+ _ref = other._ref;
+ ref();
+ --*rhsref;
+ // Why do I not test _tx_buffer for != NULL and free?
+ // I allow for the lh target container, to be a copy of an active
+ // connection. Thus we are just reusing the container.
+ // The above unref() handles releaseing the previous client of the container.
+ _tx_buffer_size = other._tx_buffer_size;
+ _tx_buffer = other._tx_buffer;
+ _client = other._client;
+ if (_client != NULL && _tx_buffer == NULL)
+ _tx_buffer = new (std::nothrow) cbuf(_tx_buffer_size);
+
+ _rx_buffer = other._rx_buffer;
+ if(_client)
+ _attachCallbacks();
+ return *this;
+}
+#else // ! SYNCCLIENT_NEW_OPERATOR_EQUAL
+// This is the origianl logic with null checks
+SyncClient & SyncClient::operator=(const SyncClient &other){
+ if(_client != NULL){
+ _client->abort();
+ _client->free();
+ _client = NULL;
+ }
+ _tx_buffer_size = other._tx_buffer_size;
+ if(_tx_buffer != NULL){
+ cbuf *b = _tx_buffer;
+ _tx_buffer = NULL;
+ delete b;
+ }
+ while(_rx_buffer != NULL){
+ cbuf *b = _rx_buffer;
+ _rx_buffer = b->next;
+ delete b;
+ }
+ if(other._client != NULL)
+ _tx_buffer = new (std::nothrow) cbuf(other._tx_buffer_size);
+
+ _client = other._client;
+ if(_client)
+ _attachCallbacks();
+
+ return *this;
+}
+#endif
+
+void SyncClient::setTimeout(uint32_t seconds){
+ if(_client != NULL)
+ _client->setRxTimeout(seconds);
+}
+
+uint8_t SyncClient::status(){
+ if(_client == NULL)
+ return 0;
+ return _client->state();
+}
+
+uint8_t SyncClient::connected(){
+ return (_client != NULL && _client->connected());
+}
+
+bool SyncClient::stop(unsigned int maxWaitMs){
+ (void)maxWaitMs;
+ if(_client != NULL)
+ _client->close(true);
+ return true;
+}
+
+size_t SyncClient::_sendBuffer(){
+ if(_client == NULL || _tx_buffer == NULL)
+ return 0;
+ size_t available = _tx_buffer->available();
+ if(!connected() || !_client->canSend() || available == 0)
+ return 0;
+ size_t sendable = _client->space();
+ if(sendable < available)
+ available= sendable;
+ char *out = new (std::nothrow) char[available];
+ if(out == NULL)
+ return 0;
+
+ _tx_buffer->read(out, available);
+ size_t sent = _client->write(out, available);
+ delete[] out;
+ return sent;
+}
+
+void SyncClient::_onData(void *data, size_t len){
+ _client->ackLater();
+ cbuf *b = new (std::nothrow) cbuf(len+1);
+ if(b != NULL){
+ b->write((const char *)data, len);
+ if(_rx_buffer == NULL)
+ _rx_buffer = b;
+ else {
+ cbuf *p = _rx_buffer;
+ while(p->next != NULL)
+ p = p->next;
+ p->next = b;
+ }
+ } else {
+ // We ran out of memory. This fail causes lost receive data.
+ // The connection should be closed in a manner that conveys something
+ // bad/abnormal has happened to the connection. Hence, we abort the
+ // connection to avoid possible data corruption.
+ // Note, callbacks maybe called.
+ _client->abort();
+ }
+}
+
+void SyncClient::_onDisconnect(){
+ if(_client != NULL){
+ _client = NULL;
+ }
+ if(_tx_buffer != NULL){
+ cbuf *b = _tx_buffer;
+ _tx_buffer = NULL;
+ delete b;
+ }
+}
+
+void SyncClient::_onConnect(AsyncClient *c){
+ _client = c;
+ if(_tx_buffer != NULL){
+ cbuf *b = _tx_buffer;
+ _tx_buffer = NULL;
+ delete b;
+ }
+ _tx_buffer = new (std::nothrow) cbuf(_tx_buffer_size);
+ _attachCallbacks_AfterConnected();
+}
+
+void SyncClient::_attachCallbacks(){
+ _attachCallbacks_Disconnect();
+ _attachCallbacks_AfterConnected();
+}
+
+void SyncClient::_attachCallbacks_AfterConnected(){
+ _client->onAck([](void *obj, AsyncClient* c, size_t len, uint32_t time){ (void)c; (void)len; (void)time; ((SyncClient*)(obj))->_sendBuffer(); }, this);
+ _client->onData([](void *obj, AsyncClient* c, void *data, size_t len){ (void)c; ((SyncClient*)(obj))->_onData(data, len); }, this);
+ _client->onTimeout([](void *obj, AsyncClient* c, uint32_t time){ (void)obj; (void)time; c->close(); }, this);
+}
+
+void SyncClient::_attachCallbacks_Disconnect(){
+ _client->onDisconnect([](void *obj, AsyncClient* c){ ((SyncClient*)(obj))->_onDisconnect(); delete c; }, this);
+}
+
+size_t SyncClient::write(uint8_t data){
+ return write(&data, 1);
+}
+
+size_t SyncClient::write(const uint8_t *data, size_t len){
+ if(_tx_buffer == NULL || !connected()){
+ return 0;
+ }
+ size_t toWrite = 0;
+ size_t toSend = len;
+ while(_tx_buffer->room() < toSend){
+ toWrite = _tx_buffer->room();
+ _tx_buffer->write((const char*)data, toWrite);
+ while(connected() && !_client->canSend())
+ delay(0);
+ if(!connected())
+ return 0;
+ _sendBuffer();
+ toSend -= toWrite;
+ }
+ _tx_buffer->write((const char*)(data+(len - toSend)), toSend);
+ if(connected() && _client->canSend())
+ _sendBuffer();
+ return len;
+}
+
+int SyncClient::available(){
+ if(_rx_buffer == NULL) return 0;
+ size_t a = 0;
+ cbuf *b = _rx_buffer;
+ while(b != NULL){
+ a += b->available();
+ b = b->next;
+ }
+ return a;
+}
+
+int SyncClient::peek(){
+ if(_rx_buffer == NULL) return -1;
+ return _rx_buffer->peek();
+}
+
+int SyncClient::read(uint8_t *data, size_t len){
+ if(_rx_buffer == NULL) return -1;
+
+ size_t readSoFar = 0;
+ while(_rx_buffer != NULL && (len - readSoFar) >= _rx_buffer->available()){
+ cbuf *b = _rx_buffer;
+ _rx_buffer = _rx_buffer->next;
+ size_t toRead = b->available();
+ readSoFar += b->read((char*)(data+readSoFar), toRead);
+ if(connected()){
+ _client->ack(b->size() - 1);
+ }
+ delete b;
+ }
+ if(_rx_buffer != NULL && readSoFar < len){
+ readSoFar += _rx_buffer->read((char*)(data+readSoFar), (len - readSoFar));
+ }
+ return readSoFar;
+}
+
+int SyncClient::read(){
+ uint8_t res = 0;
+ if(read(&res, 1) != 1)
+ return -1;
+ return res;
+}
+
+bool SyncClient::flush(unsigned int maxWaitMs){
+ (void)maxWaitMs;
+ if(_tx_buffer == NULL || !connected())
+ return false;
+ if(_tx_buffer->available()){
+ while(connected() && !_client->canSend())
+ delay(0);
+ if(_client == NULL || _tx_buffer == NULL)
+ return false;
+ _sendBuffer();
+ }
+ return true;
+}
diff --git a/src/SyncClient.h b/src/SyncClient.h
new file mode 100644
index 0000000..cb568de
--- /dev/null
+++ b/src/SyncClient.h
@@ -0,0 +1,109 @@
+/*
+ Asynchronous TCP library for Espressif MCUs
+
+ Copyright (c) 2016 Hristo Gochkov. 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 SYNCCLIENT_H_
+#define SYNCCLIENT_H_
+
+#include "Client.h"
+// Needed for Arduino core releases prior to 2.5.0, because of changes
+// made to accommodate Arduino core 2.5.0
+// CONST was 1st defined in Core 2.5.0 in IPAddress.h
+#ifndef CONST
+#define CONST
+#endif
+#include
+class cbuf;
+class AsyncClient;
+
+class SyncClient: public Client {
+ private:
+ AsyncClient *_client;
+ cbuf *_tx_buffer;
+ size_t _tx_buffer_size;
+ cbuf *_rx_buffer;
+ int *_ref;
+
+ size_t _sendBuffer();
+ void _onData(void *data, size_t len);
+ void _onConnect(AsyncClient *c);
+ void _onDisconnect();
+ void _attachCallbacks();
+ void _attachCallbacks_Disconnect();
+ void _attachCallbacks_AfterConnected();
+ void _release();
+
+ public:
+ SyncClient(size_t txBufLen = TCP_MSS);
+ SyncClient(AsyncClient *client, size_t txBufLen = TCP_MSS);
+ virtual ~SyncClient();
+
+ int ref();
+ int unref();
+ operator bool(){ return connected(); }
+ SyncClient & operator=(const SyncClient &other);
+
+#if ASYNC_TCP_SSL_ENABLED
+ int _connect(const IPAddress& ip, uint16_t port, bool secure);
+ int connect(CONST IPAddress& ip, uint16_t port, bool secure){
+ return _connect(ip, port, secure);
+ }
+ int connect(IPAddress ip, uint16_t port, bool secure){
+ return _connect(reinterpret_cast(ip), port, secure);
+ }
+ int connect(const char *host, uint16_t port, bool secure);
+ int connect(CONST IPAddress& ip, uint16_t port){
+ return _connect(ip, port, false);
+ }
+ int connect(IPAddress ip, uint16_t port){
+ return _connect(reinterpret_cast(ip), port, false);
+ }
+ int connect(const char *host, uint16_t port){
+ return connect(host, port, false);
+ }
+#else
+ int _connect(const IPAddress& ip, uint16_t port);
+ int connect(CONST IPAddress& ip, uint16_t port){
+ return _connect(ip, port);
+ }
+ int connect(IPAddress ip, uint16_t port){
+ return _connect(reinterpret_cast(ip), port);
+ }
+ int connect(const char *host, uint16_t port);
+#endif
+ void setTimeout(uint32_t seconds);
+
+ uint8_t status();
+ uint8_t connected();
+
+ bool stop(unsigned int maxWaitMs);
+ bool flush(unsigned int maxWaitMs);
+ void stop() { (void)stop(0);}
+ void flush() { (void)flush(0);}
+ size_t write(uint8_t data);
+ size_t write(const uint8_t *data, size_t len);
+
+ int available();
+ int peek();
+ int read();
+ int read(uint8_t *data, size_t len);
+};
+
+#endif /* SYNCCLIENT_H_ */
diff --git a/src/async_config.h b/src/async_config.h
new file mode 100644
index 0000000..ca6912f
--- /dev/null
+++ b/src/async_config.h
@@ -0,0 +1,38 @@
+#ifndef LIBRARIES_ESPASYNCTCP_SRC_ASYNC_CONFIG_H_
+#define LIBRARIES_ESPASYNCTCP_SRC_ASYNC_CONFIG_H_
+
+#ifndef ASYNC_TCP_SSL_ENABLED
+#define ASYNC_TCP_SSL_ENABLED 0
+#endif
+
+#ifndef TCP_MSS
+// May have been definded as a -DTCP_MSS option on the compile line or not.
+// Arduino core 2.3.0 or earlier does not do the -DTCP_MSS option.
+// Later versions may set this option with info from board.txt.
+// However, Core 2.4.0 and up board.txt does not define TCP_MSS for lwIP v1.4
+#define TCP_MSS (1460)
+#endif
+
+// #define ASYNC_TCP_DEBUG(...) ets_printf(__VA_ARGS__)
+// #define TCP_SSL_DEBUG(...) ets_printf(__VA_ARGS__)
+// #define ASYNC_TCP_ASSERT( a ) do{ if(!(a)){ets_printf("ASSERT: %s %u \n", __FILE__, __LINE__);}}while(0)
+
+// Starting with Arduino Core 2.4.0 and up the define of DEBUG_ESP_PORT
+// can be handled through the Arduino IDE Board options instead of here.
+// #define DEBUG_ESP_PORT Serial
+
+// #define DEBUG_ESP_ASYNC_TCP 1
+// #define DEBUG_ESP_TCP_SSL 1
+#include
+
+#ifndef ASYNC_TCP_ASSERT
+#define ASYNC_TCP_ASSERT(...) do { (void)0;} while(false)
+#endif
+#ifndef ASYNC_TCP_DEBUG
+#define ASYNC_TCP_DEBUG(...) do { (void)0;} while(false)
+#endif
+#ifndef TCP_SSL_DEBUG
+#define TCP_SSL_DEBUG(...) do { (void)0;} while(false)
+#endif
+
+#endif /* LIBRARIES_ESPASYNCTCP_SRC_ASYNC_CONFIG_H_ */
diff --git a/src/tcp_axtls.c b/src/tcp_axtls.c
new file mode 100644
index 0000000..cdbdf41
--- /dev/null
+++ b/src/tcp_axtls.c
@@ -0,0 +1,588 @@
+/*
+ Asynchronous TCP library for Espressif MCUs
+
+ Copyright (c) 2016 Hristo Gochkov. 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
+*/
+/*
+ * Compatibility for AxTLS with LWIP raw tcp mode (http://lwip.wikia.com/wiki/Raw/TCP)
+ * Original Code and Inspiration: Slavey Karadzhov
+ */
+#include
+#if ASYNC_TCP_SSL_ENABLED
+
+#include "lwip/opt.h"
+#include "lwip/tcp.h"
+#include "lwip/inet.h"
+#include
+#include
+#include
+#include
+#include
+
+uint8_t * default_private_key = NULL;
+uint16_t default_private_key_len = 0;
+
+uint8_t * default_certificate = NULL;
+uint16_t default_certificate_len = 0;
+
+static uint8_t _tcp_ssl_has_client = 0;
+
+SSL_CTX * tcp_ssl_new_server_ctx(const char *cert, const char *private_key_file, const char *password){
+ uint32_t options = SSL_CONNECT_IN_PARTS;
+ SSL_CTX *ssl_ctx;
+
+ if(private_key_file){
+ options |= SSL_NO_DEFAULT_KEY;
+ }
+
+ if ((ssl_ctx = ssl_ctx_new(options, SSL_DEFAULT_SVR_SESS)) == NULL){
+ TCP_SSL_DEBUG("tcp_ssl_new_server_ctx: failed to allocate context\n");
+ return NULL;
+ }
+
+ if (private_key_file){
+ int obj_type = SSL_OBJ_RSA_KEY;
+ if (strstr(private_key_file, ".p8"))
+ obj_type = SSL_OBJ_PKCS8;
+ else if (strstr(private_key_file, ".p12"))
+ obj_type = SSL_OBJ_PKCS12;
+
+ if (ssl_obj_load(ssl_ctx, obj_type, private_key_file, password)){
+ TCP_SSL_DEBUG("tcp_ssl_new_server_ctx: load private key '%s' failed\n", private_key_file);
+ return NULL;
+ }
+ }
+
+ if (cert){
+ if (ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CERT, cert, NULL)){
+ TCP_SSL_DEBUG("tcp_ssl_new_server_ctx: load certificate '%s' failed\n", cert);
+ return NULL;
+ }
+ }
+ return ssl_ctx;
+}
+
+struct tcp_ssl_pcb {
+ struct tcp_pcb *tcp;
+ int fd;
+ SSL_CTX* ssl_ctx;
+ SSL *ssl;
+ uint8_t type;
+ int handshake;
+ void * arg;
+ tcp_ssl_data_cb_t on_data;
+ tcp_ssl_handshake_cb_t on_handshake;
+ tcp_ssl_error_cb_t on_error;
+ int last_wr;
+ struct pbuf *tcp_pbuf;
+ int pbuf_offset;
+ struct tcp_ssl_pcb * next;
+};
+
+typedef struct tcp_ssl_pcb tcp_ssl_t;
+
+static tcp_ssl_t * tcp_ssl_array = NULL;
+static int tcp_ssl_next_fd = 0;
+
+uint8_t tcp_ssl_has_client(){
+ return _tcp_ssl_has_client;
+}
+
+tcp_ssl_t * tcp_ssl_new(struct tcp_pcb *tcp) {
+
+ if(tcp_ssl_next_fd < 0){
+ tcp_ssl_next_fd = 0;//overflow
+ }
+
+ tcp_ssl_t * new_item = (tcp_ssl_t*)malloc(sizeof(tcp_ssl_t));
+ if(!new_item){
+ TCP_SSL_DEBUG("tcp_ssl_new: failed to allocate tcp_ssl\n");
+ return NULL;
+ }
+
+ new_item->tcp = tcp;
+ new_item->handshake = SSL_NOT_OK;
+ new_item->arg = NULL;
+ new_item->on_data = NULL;
+ new_item->on_handshake = NULL;
+ new_item->on_error = NULL;
+ new_item->tcp_pbuf = NULL;
+ new_item->pbuf_offset = 0;
+ new_item->next = NULL;
+ new_item->ssl_ctx = NULL;
+ new_item->ssl = NULL;
+ new_item->type = TCP_SSL_TYPE_CLIENT;
+ new_item->fd = tcp_ssl_next_fd++;
+
+ if(tcp_ssl_array == NULL){
+ tcp_ssl_array = new_item;
+ } else {
+ tcp_ssl_t * item = tcp_ssl_array;
+ while(item->next != NULL)
+ item = item->next;
+ item->next = new_item;
+ }
+
+ TCP_SSL_DEBUG("tcp_ssl_new: %d\n", new_item->fd);
+ return new_item;
+}
+
+tcp_ssl_t* tcp_ssl_get(struct tcp_pcb *tcp) {
+ if(tcp == NULL) {
+ return NULL;
+ }
+ tcp_ssl_t * item = tcp_ssl_array;
+ while(item && item->tcp != tcp){
+ item = item->next;
+ }
+ return item;
+}
+
+int tcp_ssl_new_client(struct tcp_pcb *tcp){
+ SSL_CTX* ssl_ctx;
+ tcp_ssl_t * tcp_ssl;
+
+ if(tcp == NULL) {
+ return -1;
+ }
+
+ if(tcp_ssl_get(tcp) != NULL){
+ TCP_SSL_DEBUG("tcp_ssl_new_client: tcp_ssl already exists\n");
+ return -1;
+ }
+
+ ssl_ctx = ssl_ctx_new(SSL_CONNECT_IN_PARTS | SSL_SERVER_VERIFY_LATER, 1);
+ if(ssl_ctx == NULL){
+ TCP_SSL_DEBUG("tcp_ssl_new_client: failed to allocate ssl context\n");
+ return -1;
+ }
+
+ tcp_ssl = tcp_ssl_new(tcp);
+ if(tcp_ssl == NULL){
+ ssl_ctx_free(ssl_ctx);
+ return -1;
+ }
+
+ tcp_ssl->ssl_ctx = ssl_ctx;
+
+ tcp_ssl->ssl = ssl_client_new(ssl_ctx, tcp_ssl->fd, NULL, 0, NULL);
+ if(tcp_ssl->ssl == NULL){
+ TCP_SSL_DEBUG("tcp_ssl_new_client: failed to allocate ssl\n");
+ tcp_ssl_free(tcp);
+ return -1;
+ }
+
+ return tcp_ssl->fd;
+}
+
+int tcp_ssl_new_server(struct tcp_pcb *tcp, SSL_CTX* ssl_ctx){
+ tcp_ssl_t * tcp_ssl;
+
+ if(tcp == NULL) {
+ return -1;
+ }
+
+ if(ssl_ctx == NULL){
+ return -1;
+ }
+
+ if(tcp_ssl_get(tcp) != NULL){
+ TCP_SSL_DEBUG("tcp_ssl_new_server: tcp_ssl already exists\n");
+ return -1;
+ }
+
+ tcp_ssl = tcp_ssl_new(tcp);
+ if(tcp_ssl == NULL){
+ return -1;
+ }
+
+ tcp_ssl->type = TCP_SSL_TYPE_SERVER;
+ tcp_ssl->ssl_ctx = ssl_ctx;
+
+ _tcp_ssl_has_client = 1;
+ tcp_ssl->ssl = ssl_server_new(ssl_ctx, tcp_ssl->fd);
+ if(tcp_ssl->ssl == NULL){
+ TCP_SSL_DEBUG("tcp_ssl_new_server: failed to allocate ssl\n");
+ tcp_ssl_free(tcp);
+ return -1;
+ }
+
+ return tcp_ssl->fd;
+}
+
+int tcp_ssl_free(struct tcp_pcb *tcp) {
+
+ if(tcp == NULL) {
+ return -1;
+ }
+
+ tcp_ssl_t * item = tcp_ssl_array;
+
+ if(item->tcp == tcp){
+ tcp_ssl_array = tcp_ssl_array->next;
+ if(item->tcp_pbuf != NULL){
+ pbuf_free(item->tcp_pbuf);
+ }
+ TCP_SSL_DEBUG("tcp_ssl_free: %d\n", item->fd);
+ if(item->ssl)
+ ssl_free(item->ssl);
+ if(item->type == TCP_SSL_TYPE_CLIENT && item->ssl_ctx)
+ ssl_ctx_free(item->ssl_ctx);
+ if(item->type == TCP_SSL_TYPE_SERVER)
+ _tcp_ssl_has_client = 0;
+ free(item);
+ return 0;
+ }
+
+ while(item->next && item->next->tcp != tcp)
+ item = item->next;
+
+ if(item->next == NULL){
+ return ERR_TCP_SSL_INVALID_CLIENTFD_DATA;//item not found
+ }
+
+ tcp_ssl_t * i = item->next;
+ item->next = i->next;
+ if(i->tcp_pbuf != NULL){
+ pbuf_free(i->tcp_pbuf);
+ }
+ TCP_SSL_DEBUG("tcp_ssl_free: %d\n", i->fd);
+ if(i->ssl)
+ ssl_free(i->ssl);
+ if(i->type == TCP_SSL_TYPE_CLIENT && i->ssl_ctx)
+ ssl_ctx_free(i->ssl_ctx);
+ if(i->type == TCP_SSL_TYPE_SERVER)
+ _tcp_ssl_has_client = 0;
+ free(i);
+ return 0;
+}
+
+#ifdef AXTLS_2_0_0_SNDBUF
+int tcp_ssl_sndbuf(struct tcp_pcb *tcp){
+ int expected;
+ int available;
+ int result = -1;
+
+ if(tcp == NULL) {
+ return result;
+ }
+ tcp_ssl_t * tcp_ssl = tcp_ssl_get(tcp);
+ if(!tcp_ssl){
+ TCP_SSL_DEBUG("tcp_ssl_sndbuf: tcp_ssl is NULL\n");
+ return result;
+ }
+ available = tcp_sndbuf(tcp);
+ if(!available){
+ TCP_SSL_DEBUG("tcp_ssl_sndbuf: tcp_sndbuf is zero\n");
+ return 0;
+ }
+ result = available;
+ while((expected = ssl_calculate_write_length(tcp_ssl->ssl, result)) > available){
+ result -= (expected - available) + 4;
+ }
+
+ if(expected > 0){
+ //TCP_SSL_DEBUG("tcp_ssl_sndbuf: tcp_sndbuf is %d from %d\n", result, available);
+ return result;
+ }
+
+ return 0;
+}
+#endif
+
+int tcp_ssl_write(struct tcp_pcb *tcp, uint8_t *data, size_t len) {
+ if(tcp == NULL) {
+ return -1;
+ }
+ tcp_ssl_t * tcp_ssl = tcp_ssl_get(tcp);
+ if(!tcp_ssl){
+ TCP_SSL_DEBUG("tcp_ssl_write: tcp_ssl is NULL\n");
+ return 0;
+ }
+ tcp_ssl->last_wr = 0;
+
+#ifdef AXTLS_2_0_0_SNDBUF
+ int expected_len = ssl_calculate_write_length(tcp_ssl->ssl, len);
+ int available_len = tcp_sndbuf(tcp);
+ if(expected_len < 0 || expected_len > available_len){
+ TCP_SSL_DEBUG("tcp_ssl_write: data will not fit! %u < %d(%u)\r\n", available_len, expected_len, len);
+ return -1;
+ }
+#endif
+
+ int rc = ssl_write(tcp_ssl->ssl, data, len);
+
+ //TCP_SSL_DEBUG("tcp_ssl_write: %u -> %d (%d)\r\n", len, tcp_ssl->last_wr, rc);
+
+ if (rc < 0){
+ if(rc != SSL_CLOSE_NOTIFY) {
+ TCP_SSL_DEBUG("tcp_ssl_write error: %d\r\n", rc);
+ }
+ return rc;
+ }
+
+ return tcp_ssl->last_wr;
+}
+
+/**
+ * Reads data from the SSL over TCP stream. Returns decrypted data.
+ * @param tcp_pcb *tcp - pointer to the raw tcp object
+ * @param pbuf *p - pointer to the buffer with the TCP packet data
+ *
+ * @return int
+ * 0 - when everything is fine but there are no symbols to process yet
+ * < 0 - when there is an error
+ * > 0 - the length of the clear text characters that were read
+ */
+int tcp_ssl_read(struct tcp_pcb *tcp, struct pbuf *p) {
+ if(tcp == NULL) {
+ return -1;
+ }
+ tcp_ssl_t* fd_data = NULL;
+
+ int read_bytes = 0;
+ int total_bytes = 0;
+ uint8_t *read_buf;
+
+ fd_data = tcp_ssl_get(tcp);
+ if(fd_data == NULL) {
+ TCP_SSL_DEBUG("tcp_ssl_read: tcp_ssl is NULL\n");
+ return ERR_TCP_SSL_INVALID_CLIENTFD_DATA;
+ }
+
+ if(p == NULL) {
+ TCP_SSL_DEBUG("tcp_ssl_read:p == NULL\n");
+ return ERR_TCP_SSL_INVALID_DATA;
+ }
+
+ //TCP_SSL_DEBUG("READY TO READ SOME DATA\n");
+
+ fd_data->tcp_pbuf = p;
+ fd_data->pbuf_offset = 0;
+
+ do {
+ read_bytes = ssl_read(fd_data->ssl, &read_buf);
+ //TCP_SSL_DEBUG("tcp_ssl_ssl_read: %d\n", read_bytes);
+ if(read_bytes < SSL_OK) {
+ if(read_bytes != SSL_CLOSE_NOTIFY) {
+ TCP_SSL_DEBUG("tcp_ssl_read: read error: %d\n", read_bytes);
+ }
+ total_bytes = read_bytes;
+ break;
+ } else if(read_bytes > 0){
+ if(fd_data->on_data){
+ fd_data->on_data(fd_data->arg, tcp, read_buf, read_bytes);
+ }
+ total_bytes+= read_bytes;
+ } else {
+ if(fd_data->handshake != SSL_OK) {
+ fd_data->handshake = ssl_handshake_status(fd_data->ssl);
+ if(fd_data->handshake == SSL_OK){
+ //TCP_SSL_DEBUG("tcp_ssl_read: handshake OK\n");
+ if(fd_data->on_handshake)
+ fd_data->on_handshake(fd_data->arg, fd_data->tcp, fd_data->ssl);
+ } else if(fd_data->handshake != SSL_NOT_OK){
+ TCP_SSL_DEBUG("tcp_ssl_read: handshake error: %d\n", fd_data->handshake);
+ if(fd_data->on_error)
+ fd_data->on_error(fd_data->arg, fd_data->tcp, fd_data->handshake);
+ return fd_data->handshake;
+ }
+ }
+ }
+ } while (p->tot_len - fd_data->pbuf_offset > 0);
+
+ tcp_recved(tcp, p->tot_len);
+ fd_data->tcp_pbuf = NULL;
+ pbuf_free(p);
+
+ return total_bytes;
+}
+
+SSL * tcp_ssl_get_ssl(struct tcp_pcb *tcp){
+ tcp_ssl_t * tcp_ssl = tcp_ssl_get(tcp);
+ if(tcp_ssl){
+ return tcp_ssl->ssl;
+ }
+ return NULL;
+}
+
+bool tcp_ssl_has(struct tcp_pcb *tcp){
+ return tcp_ssl_get(tcp) != NULL;
+}
+
+int tcp_ssl_is_server(struct tcp_pcb *tcp){
+ tcp_ssl_t * tcp_ssl = tcp_ssl_get(tcp);
+ if(tcp_ssl){
+ return tcp_ssl->type;
+ }
+ return -1;
+}
+
+void tcp_ssl_arg(struct tcp_pcb *tcp, void * arg){
+ tcp_ssl_t * item = tcp_ssl_get(tcp);
+ if(item) {
+ item->arg = arg;
+ }
+}
+
+void tcp_ssl_data(struct tcp_pcb *tcp, tcp_ssl_data_cb_t arg){
+ tcp_ssl_t * item = tcp_ssl_get(tcp);
+ if(item) {
+ item->on_data = arg;
+ }
+}
+
+void tcp_ssl_handshake(struct tcp_pcb *tcp, tcp_ssl_handshake_cb_t arg){
+ tcp_ssl_t * item = tcp_ssl_get(tcp);
+ if(item) {
+ item->on_handshake = arg;
+ }
+}
+
+void tcp_ssl_err(struct tcp_pcb *tcp, tcp_ssl_error_cb_t arg){
+ tcp_ssl_t * item = tcp_ssl_get(tcp);
+ if(item) {
+ item->on_error = arg;
+ }
+}
+
+static tcp_ssl_file_cb_t _tcp_ssl_file_cb = NULL;
+static void * _tcp_ssl_file_arg = NULL;
+
+void tcp_ssl_file(tcp_ssl_file_cb_t cb, void * arg){
+ _tcp_ssl_file_cb = cb;
+ _tcp_ssl_file_arg = arg;
+}
+
+int ax_get_file(const char *filename, uint8_t **buf) {
+ //TCP_SSL_DEBUG("ax_get_file: %s\n", filename);
+ if(_tcp_ssl_file_cb){
+ return _tcp_ssl_file_cb(_tcp_ssl_file_arg, filename, buf);
+ }
+ *buf = 0;
+ return 0;
+}
+
+tcp_ssl_t* tcp_ssl_get_by_fd(int fd) {
+ tcp_ssl_t * item = tcp_ssl_array;
+ while(item && item->fd != fd){
+ item = item->next;
+ }
+ return item;
+}
+/*
+ * The LWIP tcp raw version of the SOCKET_WRITE(A, B, C)
+ */
+int ax_port_write(int fd, uint8_t *data, uint16_t len) {
+ tcp_ssl_t *fd_data = NULL;
+ int tcp_len = 0;
+ err_t err = ERR_OK;
+
+ //TCP_SSL_DEBUG("ax_port_write: %d, %d\n", fd, len);
+
+ fd_data = tcp_ssl_get_by_fd(fd);
+ if(fd_data == NULL) {
+ //TCP_SSL_DEBUG("ax_port_write: tcp_ssl[%d] is NULL\n", fd);
+ return ERR_MEM;
+ }
+
+ if (data == NULL || len == 0) {
+ return 0;
+ }
+
+ if (tcp_sndbuf(fd_data->tcp) < len) {
+ tcp_len = tcp_sndbuf(fd_data->tcp);
+ if(tcp_len == 0) {
+ TCP_SSL_DEBUG("ax_port_write: tcp_sndbuf is zero: %d\n", len);
+ return ERR_MEM;
+ }
+ } else {
+ tcp_len = len;
+ }
+
+ if (tcp_len > 2 * fd_data->tcp->mss) {
+ tcp_len = 2 * fd_data->tcp->mss;
+ }
+
+ err = tcp_write(fd_data->tcp, data, tcp_len, TCP_WRITE_FLAG_COPY);
+ if(err < ERR_OK) {
+ if (err == ERR_MEM) {
+ TCP_SSL_DEBUG("ax_port_write: No memory %d (%d)\n", tcp_len, len);
+ return err;
+ }
+ TCP_SSL_DEBUG("ax_port_write: tcp_write error: %d\n", err);
+ return err;
+ } else if (err == ERR_OK) {
+ //TCP_SSL_DEBUG("ax_port_write: tcp_output: %d / %d\n", tcp_len, len);
+ err = tcp_output(fd_data->tcp);
+ if(err != ERR_OK) {
+ TCP_SSL_DEBUG("ax_port_write: tcp_output err: %d\n", err);
+ return err;
+ }
+ }
+
+ fd_data->last_wr += tcp_len;
+
+ return tcp_len;
+}
+
+/*
+ * The LWIP tcp raw version of the SOCKET_READ(A, B, C)
+ */
+int ax_port_read(int fd, uint8_t *data, int len) {
+ tcp_ssl_t *fd_data = NULL;
+ uint8_t *read_buf = NULL;
+ uint8_t *pread_buf = NULL;
+ u16_t recv_len = 0;
+
+ //TCP_SSL_DEBUG("ax_port_read: %d, %d\n", fd, len);
+
+ fd_data = tcp_ssl_get_by_fd(fd);
+ if (fd_data == NULL) {
+ TCP_SSL_DEBUG("ax_port_read: tcp_ssl[%d] is NULL\n", fd);
+ return ERR_TCP_SSL_INVALID_CLIENTFD_DATA;
+ }
+
+ if(fd_data->tcp_pbuf == NULL || fd_data->tcp_pbuf->tot_len == 0) {
+ return 0;
+ }
+
+ read_buf =(uint8_t*)calloc(fd_data->tcp_pbuf->len + 1, sizeof(uint8_t));
+ pread_buf = read_buf;
+ if (pread_buf != NULL){
+ recv_len = pbuf_copy_partial(fd_data->tcp_pbuf, read_buf, len, fd_data->pbuf_offset);
+ fd_data->pbuf_offset += recv_len;
+ }
+
+ if (recv_len != 0) {
+ memcpy(data, read_buf, recv_len);
+ }
+
+ if(len < recv_len) {
+ TCP_SSL_DEBUG("ax_port_read: got %d bytes more than expected\n", recv_len - len);
+ }
+
+ free(pread_buf);
+ pread_buf = NULL;
+
+ return recv_len;
+}
+
+void ax_wdt_feed() {}
+
+#endif
diff --git a/src/tcp_axtls.h b/src/tcp_axtls.h
new file mode 100644
index 0000000..118e36f
--- /dev/null
+++ b/src/tcp_axtls.h
@@ -0,0 +1,98 @@
+/*
+ Asynchronous TCP library for Espressif MCUs
+
+ Copyright (c) 2016 Hristo Gochkov. 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
+*/
+/*
+ * Compatibility for AxTLS with LWIP raw tcp mode (http://lwip.wikia.com/wiki/Raw/TCP)
+ * Original Code and Inspiration: Slavey Karadzhov
+ */
+
+#ifndef LWIPR_COMPAT_H
+#define LWIPR_COMPAT_H
+
+#include
+
+#if ASYNC_TCP_SSL_ENABLED
+
+#include "lwipopts.h"
+/*
+ * All those functions will run only if LWIP tcp raw mode is used
+ */
+#if LWIP_RAW==1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include "include/ssl.h"
+
+#define ERR_TCP_SSL_INVALID_SSL -101
+#define ERR_TCP_SSL_INVALID_TCP -102
+#define ERR_TCP_SSL_INVALID_CLIENTFD -103
+#define ERR_TCP_SSL_INVALID_CLIENTFD_DATA -104
+#define ERR_TCP_SSL_INVALID_DATA -105
+
+#define TCP_SSL_TYPE_CLIENT 0
+#define TCP_SSL_TYPE_SERVER 1
+
+#define tcp_ssl_ssl_write(A, B, C) tcp_ssl_write(A, B, C)
+#define tcp_ssl_ssl_read(A, B) tcp_ssl_read(A, B)
+
+typedef void (* tcp_ssl_data_cb_t)(void *arg, struct tcp_pcb *tcp, uint8_t * data, size_t len);
+typedef void (* tcp_ssl_handshake_cb_t)(void *arg, struct tcp_pcb *tcp, SSL *ssl);
+typedef void (* tcp_ssl_error_cb_t)(void *arg, struct tcp_pcb *tcp, int8_t error);
+typedef int (* tcp_ssl_file_cb_t)(void *arg, const char *filename, uint8_t **buf);
+
+uint8_t tcp_ssl_has_client();
+
+int tcp_ssl_new_client(struct tcp_pcb *tcp);
+
+SSL_CTX * tcp_ssl_new_server_ctx(const char *cert, const char *private_key_file, const char *password);
+int tcp_ssl_new_server(struct tcp_pcb *tcp, SSL_CTX* ssl_ctx);
+int tcp_ssl_is_server(struct tcp_pcb *tcp);
+
+int tcp_ssl_free(struct tcp_pcb *tcp);
+int tcp_ssl_read(struct tcp_pcb *tcp, struct pbuf *p);
+
+#ifdef AXTLS_2_0_0_SNDBUF
+int tcp_ssl_sndbuf(struct tcp_pcb *tcp);
+#endif
+
+int tcp_ssl_write(struct tcp_pcb *tcp, uint8_t *data, size_t len);
+
+void tcp_ssl_file(tcp_ssl_file_cb_t cb, void * arg);
+
+void tcp_ssl_arg(struct tcp_pcb *tcp, void * arg);
+void tcp_ssl_data(struct tcp_pcb *tcp, tcp_ssl_data_cb_t arg);
+void tcp_ssl_handshake(struct tcp_pcb *tcp, tcp_ssl_handshake_cb_t arg);
+void tcp_ssl_err(struct tcp_pcb *tcp, tcp_ssl_error_cb_t arg);
+
+SSL * tcp_ssl_get_ssl(struct tcp_pcb *tcp);
+bool tcp_ssl_has(struct tcp_pcb *tcp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_RAW==1 */
+
+#endif /* ASYNC_TCP_SSL_ENABLED */
+
+#endif /* LWIPR_COMPAT_H */
diff --git a/ssl/gen_server_cert.sh b/ssl/gen_server_cert.sh
new file mode 100755
index 0000000..fd749ed
--- /dev/null
+++ b/ssl/gen_server_cert.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+cat > ca_cert.conf << EOF
+[ req ]
+distinguished_name = req_distinguished_name
+prompt = no
+
+[ req_distinguished_name ]
+ O = Espressif Systems
+EOF
+
+openssl genrsa -out axTLS.ca_key.pem 2048
+openssl req -new -config ./ca_cert.conf -key axTLS.ca_key.pem -out axTLS.ca_x509.req
+openssl x509 -req -sha1 -days 5000 -signkey axTLS.ca_key.pem -CAkey axTLS.ca_key.pem -in axTLS.ca_x509.req -out axTLS.ca_x509.pem
+
+cat > certs.conf << EOF
+[ req ]
+distinguished_name = req_distinguished_name
+prompt = no
+
+[ req_distinguished_name ]
+ O = axTLS on ESP8266
+ CN = esp8266.local
+EOF
+
+openssl genrsa -out axTLS.key_1024.pem 1024
+openssl req -new -config ./certs.conf -key axTLS.key_1024.pem -out axTLS.x509_1024.req
+openssl x509 -req -sha1 -CAcreateserial -days 5000 -CA axTLS.ca_x509.pem -CAkey axTLS.ca_key.pem -in axTLS.x509_1024.req -out axTLS.x509_1024.pem
+
+openssl rsa -outform DER -in axTLS.key_1024.pem -out axTLS.key_1024
+openssl x509 -outform DER -in axTLS.x509_1024.pem -out axTLS.x509_1024.cer
+
+cat axTLS.key_1024 > server.key
+cat axTLS.x509_1024.cer > server.cer
+
+rm axTLS.* ca_cert.conf certs.conf
diff --git a/ssl/server.cer b/ssl/server.cer
new file mode 100644
index 0000000..b5e5f24
Binary files /dev/null and b/ssl/server.cer differ
diff --git a/ssl/server.key b/ssl/server.key
new file mode 100644
index 0000000..1b7095f
Binary files /dev/null and b/ssl/server.key differ