Verze s opravou unacked
This commit is contained in:
parent
e66221023b
commit
936727df60
165
LICENSE.txt
Normal file
165
LICENSE.txt
Normal file
@ -0,0 +1,165 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
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.
|
23
README.md
23
README.md
@ -1,3 +1,24 @@
|
||||
# ESPAsyncTCP
|
||||
|
||||
Lokálně udržovaná verze ESPAsyncTCP knihovny, používaná ve všech projektech s ESP8266
|
||||
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```
|
||||
|
62
examples/ClientServer/Client/Client.ino
Normal file
62
examples/ClientServer/Client/Client.ino
Normal file
@ -0,0 +1,62 @@
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
|
||||
extern "C" {
|
||||
#include <osapi.h>
|
||||
#include <os_type.h>
|
||||
}
|
||||
|
||||
#include "config.h"
|
||||
|
||||
static os_timer_t intervalTimer;
|
||||
|
||||
static void replyToServer(void* arg) {
|
||||
AsyncClient* client = reinterpret_cast<AsyncClient*>(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() {
|
||||
|
||||
}
|
23
examples/ClientServer/Client/config.h
Normal file
23
examples/ClientServer/Client/config.h
Normal file
@ -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
|
73
examples/ClientServer/Server/Server.ino
Normal file
73
examples/ClientServer/Server/Server.ino
Normal file
@ -0,0 +1,73 @@
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#include <DNSServer.h>
|
||||
#include <vector>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
static DNSServer DNS;
|
||||
|
||||
static std::vector<AsyncClient*> 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();
|
||||
}
|
23
examples/ClientServer/Server/config.h
Normal file
23
examples/ClientServer/Server/config.h
Normal file
@ -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
|
0
examples/SyncClient/.esp31b.skip
Normal file
0
examples/SyncClient/.esp31b.skip
Normal file
54
examples/SyncClient/SyncClient.ino
Normal file
54
examples/SyncClient/SyncClient.ino
Normal file
@ -0,0 +1,54 @@
|
||||
#ifdef ESP8266
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <ArduinoOTA.h>
|
||||
#else
|
||||
#include <ESP31BWiFi.h>
|
||||
#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
|
||||
}
|
22
library.json
Normal file
22
library.json
Normal file
@ -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
|
||||
}
|
||||
}
|
9
library.properties
Normal file
9
library.properties
Normal file
@ -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=*
|
214
src/AsyncPrinter.cpp
Normal file
214
src/AsyncPrinter.cpp
Normal file
@ -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);
|
||||
}
|
73
src/AsyncPrinter.h
Normal file
73
src/AsyncPrinter.h
Normal file
@ -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<void(void*, AsyncPrinter*, uint8_t*, size_t)> ApDataHandler;
|
||||
typedef std::function<void(void*, AsyncPrinter*)> 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_ */
|
96
src/DebugPrintMacros.h
Normal file
96
src/DebugPrintMacros.h
Normal file
@ -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 <pgmspace.h>
|
||||
// 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
|
1409
src/ESPAsyncTCP.cpp
Normal file
1409
src/ESPAsyncTCP.cpp
Normal file
File diff suppressed because it is too large
Load Diff
327
src/ESPAsyncTCP.h
Normal file
327
src/ESPAsyncTCP.h
Normal file
@ -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 <async_config.h>
|
||||
#include "IPAddress.h"
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
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<void(void*, AsyncClient*)> AcConnectHandler;
|
||||
typedef std::function<void(void*, AsyncClient*, size_t len, uint32_t time)> AcAckHandler;
|
||||
typedef std::function<void(void*, AsyncClient*, err_t error)> AcErrorHandler;
|
||||
typedef std::function<void(void*, AsyncClient*, void *data, size_t len)> AcDataHandler;
|
||||
typedef std::function<void(void*, AsyncClient*, struct pbuf *pb)> AcPacketHandler;
|
||||
typedef std::function<void(void*, AsyncClient*, uint32_t time)> AcTimeoutHandler;
|
||||
typedef std::function<void(void*, size_t event)> 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<ACErrorTracker> _errorTracker;
|
||||
|
||||
void _close();
|
||||
void _connected(std::shared_ptr<ACErrorTracker>& 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<ACErrorTracker>& closeAbort, tcp_pcb* pcb);
|
||||
void _sent(std::shared_ptr<ACErrorTracker>& 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<ACErrorTracker> 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<ACErrorTracker>& 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<int(void* arg, const char *filename, uint8_t **buf)> 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_ */
|
555
src/ESPAsyncTCPbuffer.cpp
Normal file
555
src/ESPAsyncTCPbuffer.cpp
Normal file
@ -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 <Arduino.h>
|
||||
#include <debug.h>
|
||||
|
||||
#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;
|
||||
}
|
118
src/ESPAsyncTCPbuffer.h
Normal file
118
src/ESPAsyncTCPbuffer.h
Normal file
@ -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 <Arduino.h>
|
||||
#include <cbuf.h>
|
||||
|
||||
#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<size_t(uint8_t * payload, size_t length)> AsyncTCPbufferDataCb;
|
||||
typedef std::function<void(bool ok, void * ret)> AsyncTCPbufferDoneCb;
|
||||
typedef std::function<bool(AsyncTCPbuffer * obj)> 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_ */
|
414
src/SyncClient.cpp
Normal file
414
src/SyncClient.cpp
Normal file
@ -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 <interrupts.h>
|
||||
|
||||
#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;
|
||||
}
|
109
src/SyncClient.h
Normal file
109
src/SyncClient.h
Normal file
@ -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 <async_config.h>
|
||||
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<const IPAddress&>(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<const IPAddress&>(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<const IPAddress&>(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_ */
|
38
src/async_config.h
Normal file
38
src/async_config.h
Normal file
@ -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 <DebugPrintMacros.h>
|
||||
|
||||
#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_ */
|
588
src/tcp_axtls.c
Normal file
588
src/tcp_axtls.c
Normal file
@ -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 <async_config.h>
|
||||
#if ASYNC_TCP_SSL_ENABLED
|
||||
|
||||
#include "lwip/opt.h"
|
||||
#include "lwip/tcp.h"
|
||||
#include "lwip/inet.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <tcp_axtls.h>
|
||||
|
||||
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
|
98
src/tcp_axtls.h
Normal file
98
src/tcp_axtls.h
Normal file
@ -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 <async_config.h>
|
||||
|
||||
#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 <stdbool.h>
|
||||
#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 */
|
36
ssl/gen_server_cert.sh
Executable file
36
ssl/gen_server_cert.sh
Executable file
@ -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
|
BIN
ssl/server.cer
Normal file
BIN
ssl/server.cer
Normal file
Binary file not shown.
BIN
ssl/server.key
Normal file
BIN
ssl/server.key
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user