Fix compilation for Async

Update library for ESP32 async
This commit is contained in:
Luc 2019-03-19 14:22:17 +01:00
parent 40c5156be5
commit df51e025ce
23 changed files with 753 additions and 323 deletions

View File

@ -12,8 +12,11 @@
"type": "git", "type": "git",
"url": "https://github.com/me-no-dev/AsyncTCP.git" "url": "https://github.com/me-no-dev/AsyncTCP.git"
}, },
"version": "1.0.0", "version": "1.0.3",
"license": "LGPL-3.0", "license": "LGPL-3.0",
"frameworks": "arduino", "frameworks": "arduino",
"platforms": ["espressif32", "espressif32_stage"] "platforms": "espressif32",
"build": {
"libCompatMode": 2
}
} }

View File

@ -1,5 +1,5 @@
name=AsyncTCP name=AsyncTCP
version=1.0.0 version=1.0.3
author=Me-No-Dev author=Me-No-Dev
maintainer=Me-No-Dev maintainer=Me-No-Dev
sentence=Async TCP Library for ESP32 sentence=Async TCP Library for ESP32

View File

@ -29,12 +29,18 @@ extern "C"{
#include "lwip/dns.h" #include "lwip/dns.h"
} }
#if CONFIG_FREERTOS_UNICORE
#define ASYNCTCP_RUNNING_CORE 0
#else
#define ASYNCTCP_RUNNING_CORE 1
#endif
/* /*
* TCP/IP Event Task * TCP/IP Event Task
* */ * */
typedef enum { typedef enum {
LWIP_TCP_SENT, LWIP_TCP_RECV, LWIP_TCP_ERROR, LWIP_TCP_POLL LWIP_TCP_SENT, LWIP_TCP_RECV, LWIP_TCP_ERROR, LWIP_TCP_POLL, LWIP_TCP_CLEAR
} lwip_event_t; } lwip_event_t;
typedef struct { typedef struct {
@ -74,13 +80,77 @@ typedef struct {
static xQueueHandle _async_queue; static xQueueHandle _async_queue;
static TaskHandle_t _async_service_task_handle = NULL; static TaskHandle_t _async_service_task_handle = NULL;
static void _handle_async_event(lwip_event_packet_t * e){ static inline bool _init_async_event_queue(){
if(!_async_queue){
_async_queue = xQueueCreate(32, sizeof(lwip_event_packet_t *));
if(!_async_queue){
return false;
}
}
return true;
}
if(e->event == LWIP_TCP_RECV){ static inline bool _send_async_event(lwip_event_packet_t ** e){
return _async_queue && xQueueSend(_async_queue, e, portMAX_DELAY) == pdPASS;
}
static inline bool _prepend_async_event(lwip_event_packet_t ** e){
return _async_queue && xQueueSendToFront(_async_queue, e, portMAX_DELAY) == pdPASS;
}
static inline bool _get_async_event(lwip_event_packet_t ** e){
return _async_queue && xQueueReceive(_async_queue, e, portMAX_DELAY) == pdPASS;
}
static bool _remove_events_with_arg(void * arg){
lwip_event_packet_t * first_packet = NULL;
lwip_event_packet_t * packet = NULL;
if(!_async_queue){
return false;
}
//figure out which is the first packet so we can keep the order
while(!first_packet){
if(xQueueReceive(_async_queue, &first_packet, 0) != pdPASS){
return false;
}
//discard packet if matching
if((int)first_packet->arg == (int)arg){
//ets_printf("X: 0x%08x\n", (uint32_t)first_packet->arg);
free(first_packet);
first_packet = NULL;
//return first packet to the back of the queue
} else if(xQueueSend(_async_queue, &first_packet, portMAX_DELAY) != pdPASS){
return false;
}
}
while(xQueuePeek(_async_queue, &packet, 0) == pdPASS && packet != first_packet){
if(xQueueReceive(_async_queue, &packet, 0) != pdPASS){
return false;
}
if((int)packet->arg == (int)arg){
//ets_printf("X: 0x%08x\n", (uint32_t)packet->arg);
free(packet);
packet = NULL;
} else if(xQueueSend(_async_queue, &packet, portMAX_DELAY) != pdPASS){
return false;
}
}
return true;
}
static void _handle_async_event(lwip_event_packet_t * e){
if(e->event == LWIP_TCP_CLEAR){
_remove_events_with_arg(e->arg);
} else if(e->event == LWIP_TCP_RECV){
//ets_printf("%c: 0x%08x 0x%08x\n", e->recv.pb?'R':'D', e->arg, e->recv.pcb);
AsyncClient::_s_recv(e->arg, e->recv.pcb, e->recv.pb, e->recv.err); AsyncClient::_s_recv(e->arg, e->recv.pcb, e->recv.pb, e->recv.err);
} else if(e->event == LWIP_TCP_SENT){ } else if(e->event == LWIP_TCP_SENT){
//ets_printf("S: 0x%08x 0x%08x\n", e->arg, e->sent.pcb);
AsyncClient::_s_sent(e->arg, e->sent.pcb, e->sent.len); AsyncClient::_s_sent(e->arg, e->sent.pcb, e->sent.len);
} else if(e->event == LWIP_TCP_POLL){ } else if(e->event == LWIP_TCP_POLL){
//ets_printf("P: 0x%08x 0x%08x\n", e->arg, e->poll.pcb);
AsyncClient::_s_poll(e->arg, e->poll.pcb); AsyncClient::_s_poll(e->arg, e->poll.pcb);
} else if(e->event == LWIP_TCP_ERROR){ } else if(e->event == LWIP_TCP_ERROR){
AsyncClient::_s_error(e->arg, e->error.err); AsyncClient::_s_error(e->arg, e->error.err);
@ -91,11 +161,8 @@ static void _handle_async_event(lwip_event_packet_t * e){
static void _async_service_task(void *pvParameters){ static void _async_service_task(void *pvParameters){
lwip_event_packet_t * packet = NULL; lwip_event_packet_t * packet = NULL;
for (;;) { for (;;) {
if(xQueueReceive(_async_queue, &packet, 0) == pdTRUE){ if(_get_async_event(&packet)){
//dispatch packet
_handle_async_event(packet); _handle_async_event(packet);
} else {
vTaskDelay(1);
} }
} }
vTaskDelete(NULL); vTaskDelete(NULL);
@ -110,14 +177,11 @@ static void _stop_async_task(){
} }
*/ */
static bool _start_async_task(){ static bool _start_async_task(){
if(!_async_queue){ if(!_init_async_event_queue()){
_async_queue = xQueueCreate(32, sizeof(lwip_event_packet_t *)); return false;
if(!_async_queue){
return false;
}
} }
if(!_async_service_task_handle){ if(!_async_service_task_handle){
xTaskCreatePinnedToCore(_async_service_task, "async_tcp", 8192, NULL, 3, &_async_service_task_handle, 1); xTaskCreatePinnedToCore(_async_service_task, "async_tcp", 8192, NULL, 3, &_async_service_task_handle, ASYNCTCP_RUNNING_CORE);
if(!_async_service_task_handle){ if(!_async_service_task_handle){
return false; return false;
} }
@ -129,60 +193,58 @@ static bool _start_async_task(){
* LwIP Callbacks * LwIP Callbacks
* */ * */
static int8_t _tcp_poll(void * arg, struct tcp_pcb * pcb) { static int8_t _tcp_clear_events(void * arg) {
if(!_async_queue){ lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t));
return ERR_OK; e->event = LWIP_TCP_CLEAR;
e->arg = arg;
if (!_prepend_async_event(&e)) {
free((void*)(e));
} }
return ERR_OK;
}
static int8_t _tcp_poll(void * arg, struct tcp_pcb * pcb) {
lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t));
e->event = LWIP_TCP_POLL; e->event = LWIP_TCP_POLL;
e->arg = arg; e->arg = arg;
e->poll.pcb = pcb; e->poll.pcb = pcb;
if (xQueueSend(_async_queue, &e, portMAX_DELAY) != pdPASS) { if (!_send_async_event(&e)) {
free((void*)(e)); free((void*)(e));
} }
return ERR_OK; return ERR_OK;
} }
static int8_t _tcp_recv(void * arg, struct tcp_pcb * pcb, struct pbuf *pb, int8_t err) { static int8_t _tcp_recv(void * arg, struct tcp_pcb * pcb, struct pbuf *pb, int8_t err) {
if(!_async_queue){
return ERR_OK;
}
lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t));
e->event = LWIP_TCP_RECV; e->event = LWIP_TCP_RECV;
e->arg = arg; e->arg = arg;
e->recv.pcb = pcb; e->recv.pcb = pcb;
e->recv.pb = pb; e->recv.pb = pb;
e->recv.err = err; e->recv.err = err;
if (xQueueSend(_async_queue, &e, portMAX_DELAY) != pdPASS) { if (!_send_async_event(&e)) {
free((void*)(e)); free((void*)(e));
} }
return ERR_OK; return ERR_OK;
} }
static int8_t _tcp_sent(void * arg, struct tcp_pcb * pcb, uint16_t len) { static int8_t _tcp_sent(void * arg, struct tcp_pcb * pcb, uint16_t len) {
if(!_async_queue){
return ERR_OK;
}
lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t));
e->event = LWIP_TCP_SENT; e->event = LWIP_TCP_SENT;
e->arg = arg; e->arg = arg;
e->sent.pcb = pcb; e->sent.pcb = pcb;
e->sent.len = len; e->sent.len = len;
if (xQueueSend(_async_queue, &e, portMAX_DELAY) != pdPASS) { if (!_send_async_event(&e)) {
free((void*)(e)); free((void*)(e));
} }
return ERR_OK; return ERR_OK;
} }
static void _tcp_error(void * arg, int8_t err) { static void _tcp_error(void * arg, int8_t err) {
if(!_async_queue){
return;
}
lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t));
e->event = LWIP_TCP_ERROR; e->event = LWIP_TCP_ERROR;
e->arg = arg; e->arg = arg;
e->error.err = err; e->error.err = err;
if (xQueueSend(_async_queue, &e, portMAX_DELAY) != pdPASS) { if (!_send_async_event(&e)) {
free((void*)(e)); free((void*)(e));
} }
} }
@ -194,7 +256,7 @@ static void _tcp_error(void * arg, int8_t err) {
#include "lwip/priv/tcpip_priv.h" #include "lwip/priv/tcpip_priv.h"
typedef struct { typedef struct {
struct tcpip_api_call call; struct tcpip_api_call_data call;
tcp_pcb * pcb; tcp_pcb * pcb;
int8_t err; int8_t err;
union { union {
@ -217,20 +279,24 @@ typedef struct {
}; };
} tcp_api_call_t; } tcp_api_call_t;
static err_t _tcp_output_api(struct tcpip_api_call *api_call_msg){ static err_t _tcp_output_api(struct tcpip_api_call_data *api_call_msg){
tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
msg->err = tcp_output(msg->pcb); if(msg->pcb){
msg->err = tcp_output(msg->pcb);
} else {
msg->err = 0;
}
return msg->err; return msg->err;
} }
static esp_err_t _tcp_output(tcp_pcb * pcb) { static esp_err_t _tcp_output(tcp_pcb * pcb) {
tcp_api_call_t msg; tcp_api_call_t msg;
msg.pcb = pcb; msg.pcb = pcb;
tcpip_api_call(_tcp_output_api, (struct tcpip_api_call*)&msg); tcpip_api_call(_tcp_output_api, (struct tcpip_api_call_data*)&msg);
return msg.err; return msg.err;
} }
static err_t _tcp_write_api(struct tcpip_api_call *api_call_msg){ static err_t _tcp_write_api(struct tcpip_api_call_data *api_call_msg){
tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
msg->err = tcp_write(msg->pcb, msg->write.data, msg->write.size, msg->write.apiflags); msg->err = tcp_write(msg->pcb, msg->write.data, msg->write.size, msg->write.apiflags);
return msg->err; return msg->err;
@ -242,11 +308,11 @@ static esp_err_t _tcp_write(tcp_pcb * pcb, const char* data, size_t size, uint8_
msg.write.data = data; msg.write.data = data;
msg.write.size = size; msg.write.size = size;
msg.write.apiflags = apiflags; msg.write.apiflags = apiflags;
tcpip_api_call(_tcp_write_api, (struct tcpip_api_call*)&msg); tcpip_api_call(_tcp_write_api, (struct tcpip_api_call_data*)&msg);
return msg.err; return msg.err;
} }
static err_t _tcp_recved_api(struct tcpip_api_call *api_call_msg){ static err_t _tcp_recved_api(struct tcpip_api_call_data *api_call_msg){
tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
msg->err = 0; msg->err = 0;
tcp_recved(msg->pcb, msg->received); tcp_recved(msg->pcb, msg->received);
@ -257,11 +323,11 @@ static esp_err_t _tcp_recved(tcp_pcb * pcb, size_t len) {
tcp_api_call_t msg; tcp_api_call_t msg;
msg.pcb = pcb; msg.pcb = pcb;
msg.received = len; msg.received = len;
tcpip_api_call(_tcp_recved_api, (struct tcpip_api_call*)&msg); tcpip_api_call(_tcp_recved_api, (struct tcpip_api_call_data*)&msg);
return msg.err; return msg.err;
} }
static err_t _tcp_connect_api(struct tcpip_api_call *api_call_msg){ static err_t _tcp_connect_api(struct tcpip_api_call_data *api_call_msg){
tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
msg->err = tcp_connect(msg->pcb, msg->connect.addr, msg->connect.port, msg->connect.cb); msg->err = tcp_connect(msg->pcb, msg->connect.addr, msg->connect.port, msg->connect.cb);
return msg->err; return msg->err;
@ -273,11 +339,11 @@ static esp_err_t _tcp_connect(tcp_pcb * pcb, ip_addr_t * addr, uint16_t port, tc
msg.connect.addr = addr; msg.connect.addr = addr;
msg.connect.port = port; msg.connect.port = port;
msg.connect.cb = cb; msg.connect.cb = cb;
tcpip_api_call(_tcp_connect_api, (struct tcpip_api_call*)&msg); tcpip_api_call(_tcp_connect_api, (struct tcpip_api_call_data*)&msg);
return msg.err; return msg.err;
} }
static err_t _tcp_close_api(struct tcpip_api_call *api_call_msg){ static err_t _tcp_close_api(struct tcpip_api_call_data *api_call_msg){
tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
msg->err = tcp_close(msg->pcb); msg->err = tcp_close(msg->pcb);
return msg->err; return msg->err;
@ -286,11 +352,12 @@ static err_t _tcp_close_api(struct tcpip_api_call *api_call_msg){
static esp_err_t _tcp_close(tcp_pcb * pcb) { static esp_err_t _tcp_close(tcp_pcb * pcb) {
tcp_api_call_t msg; tcp_api_call_t msg;
msg.pcb = pcb; msg.pcb = pcb;
tcpip_api_call(_tcp_close_api, (struct tcpip_api_call*)&msg); //ets_printf("close 0x%08x\n", (uint32_t)pcb);
tcpip_api_call(_tcp_close_api, (struct tcpip_api_call_data*)&msg);
return msg.err; return msg.err;
} }
static err_t _tcp_abort_api(struct tcpip_api_call *api_call_msg){ static err_t _tcp_abort_api(struct tcpip_api_call_data *api_call_msg){
tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
msg->err = 0; msg->err = 0;
tcp_abort(msg->pcb); tcp_abort(msg->pcb);
@ -300,11 +367,12 @@ static err_t _tcp_abort_api(struct tcpip_api_call *api_call_msg){
static esp_err_t _tcp_abort(tcp_pcb * pcb) { static esp_err_t _tcp_abort(tcp_pcb * pcb) {
tcp_api_call_t msg; tcp_api_call_t msg;
msg.pcb = pcb; msg.pcb = pcb;
tcpip_api_call(_tcp_abort_api, (struct tcpip_api_call*)&msg); //ets_printf("abort 0x%08x\n", (uint32_t)pcb);
tcpip_api_call(_tcp_abort_api, (struct tcpip_api_call_data*)&msg);
return msg.err; return msg.err;
} }
static err_t _tcp_bind_api(struct tcpip_api_call *api_call_msg){ static err_t _tcp_bind_api(struct tcpip_api_call_data *api_call_msg){
tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
msg->err = tcp_bind(msg->pcb, msg->bind.addr, msg->bind.port); msg->err = tcp_bind(msg->pcb, msg->bind.addr, msg->bind.port);
return msg->err; return msg->err;
@ -315,11 +383,11 @@ static esp_err_t _tcp_bind(tcp_pcb * pcb, ip_addr_t * addr, uint16_t port) {
msg.pcb = pcb; msg.pcb = pcb;
msg.bind.addr = addr; msg.bind.addr = addr;
msg.bind.port = port; msg.bind.port = port;
tcpip_api_call(_tcp_bind_api, (struct tcpip_api_call*)&msg); tcpip_api_call(_tcp_bind_api, (struct tcpip_api_call_data*)&msg);
return msg.err; return msg.err;
} }
static err_t _tcp_listen_api(struct tcpip_api_call *api_call_msg){ static err_t _tcp_listen_api(struct tcpip_api_call_data *api_call_msg){
tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg;
msg->err = 0; msg->err = 0;
msg->pcb = tcp_listen_with_backlog(msg->pcb, msg->backlog); msg->pcb = tcp_listen_with_backlog(msg->pcb, msg->backlog);
@ -330,7 +398,7 @@ static tcp_pcb * _tcp_listen_with_backlog(tcp_pcb * pcb, uint8_t backlog) {
tcp_api_call_t msg; tcp_api_call_t msg;
msg.pcb = pcb; msg.pcb = pcb;
msg.backlog = backlog?backlog:0xFF; msg.backlog = backlog?backlog:0xFF;
tcpip_api_call(_tcp_listen_api, (struct tcpip_api_call*)&msg); tcpip_api_call(_tcp_listen_api, (struct tcpip_api_call_data*)&msg);
return msg.pcb; return msg.pcb;
} }
#define _tcp_listen(p) _tcp_listen_with_backlog(p, 0xFF); #define _tcp_listen(p) _tcp_listen_with_backlog(p, 0xFF);
@ -352,6 +420,8 @@ AsyncClient::AsyncClient(tcp_pcb* pcb)
, _error_cb_arg(0) , _error_cb_arg(0)
, _recv_cb(0) , _recv_cb(0)
, _recv_cb_arg(0) , _recv_cb_arg(0)
, _pb_cb(0)
, _pb_cb_arg(0)
, _timeout_cb(0) , _timeout_cb(0)
, _timeout_cb_arg(0) , _timeout_cb_arg(0)
, _pcb_busy(false) , _pcb_busy(false)
@ -366,6 +436,8 @@ AsyncClient::AsyncClient(tcp_pcb* pcb)
, next(NULL) , next(NULL)
, _in_lwip_thread(false) , _in_lwip_thread(false)
{ {
//ets_printf("+: 0x%08x\n", (uint32_t)this);
_pcb = pcb; _pcb = pcb;
if(_pcb){ if(_pcb){
_rx_last_packet = millis(); _rx_last_packet = millis();
@ -374,12 +446,15 @@ AsyncClient::AsyncClient(tcp_pcb* pcb)
tcp_sent(_pcb, &_tcp_sent); tcp_sent(_pcb, &_tcp_sent);
tcp_err(_pcb, &_tcp_error); tcp_err(_pcb, &_tcp_error);
tcp_poll(_pcb, &_tcp_poll, 1); tcp_poll(_pcb, &_tcp_poll, 1);
//ets_printf("accept 0x%08x\n", (uint32_t)_pcb);
} }
} }
AsyncClient::~AsyncClient(){ AsyncClient::~AsyncClient(){
if(_pcb) if(_pcb)
_close(); _close();
//ets_printf("-: 0x%08x\n", (uint32_t)this);
} }
bool AsyncClient::connect(IPAddress ip, uint16_t port){ bool AsyncClient::connect(IPAddress ip, uint16_t port){
@ -445,6 +520,7 @@ int8_t AsyncClient::_connected(void* pcb, int8_t err){
} }
int8_t AsyncClient::_close(){ int8_t AsyncClient::_close(){
//ets_printf("X: 0x%08x\n", (uint32_t)this);
int8_t err = ERR_OK; int8_t err = ERR_OK;
if(_pcb) { if(_pcb) {
//log_i(""); //log_i("");
@ -453,6 +529,7 @@ int8_t AsyncClient::_close(){
tcp_recv(_pcb, NULL); tcp_recv(_pcb, NULL);
tcp_err(_pcb, NULL); tcp_err(_pcb, NULL);
tcp_poll(_pcb, NULL, 0); tcp_poll(_pcb, NULL, 0);
_tcp_clear_events(this);
if(_in_lwip_thread){ if(_in_lwip_thread){
err = tcp_close(_pcb); err = tcp_close(_pcb);
} else { } else {
@ -484,6 +561,7 @@ void AsyncClient::_error(int8_t err) {
} }
int8_t AsyncClient::_sent(tcp_pcb* pcb, uint16_t len) { int8_t AsyncClient::_sent(tcp_pcb* pcb, uint16_t len) {
_in_lwip_thread = false;
_rx_last_packet = millis(); _rx_last_packet = millis();
//log_i("%u", len); //log_i("%u", len);
_pcb_busy = false; _pcb_busy = false;
@ -493,6 +571,14 @@ int8_t AsyncClient::_sent(tcp_pcb* pcb, uint16_t len) {
} }
int8_t AsyncClient::_recv(tcp_pcb* pcb, pbuf* pb, int8_t err) { int8_t AsyncClient::_recv(tcp_pcb* pcb, pbuf* pb, int8_t err) {
if(!_pcb || pcb != _pcb){
log_e("0x%08x != 0x%08x", (uint32_t)pcb, (uint32_t)_pcb);
if(pb){
pbuf_free(pb);
}
return ERR_OK;
}
_in_lwip_thread = false;
if(pb == NULL){ if(pb == NULL){
return _close(); return _close();
} }
@ -504,20 +590,25 @@ int8_t AsyncClient::_recv(tcp_pcb* pcb, pbuf* pb, int8_t err) {
//Serial.write((const uint8_t *)pb->payload, pb->len); //Serial.write((const uint8_t *)pb->payload, pb->len);
_ack_pcb = true; _ack_pcb = true;
pbuf *b = pb; pbuf *b = pb;
if(_recv_cb)
_recv_cb(_recv_cb_arg, this, b->payload, b->len);
if(!_ack_pcb)
_rx_ack_len += b->len;
else
_tcp_recved(pcb, b->len);
pb = b->next; pb = b->next;
b->next = NULL; b->next = NULL;
pbuf_free(b); if(_pb_cb){
_pb_cb(_pb_cb_arg, this, b);
} else {
if(_recv_cb)
_recv_cb(_recv_cb_arg, this, b->payload, b->len);
if(!_ack_pcb)
_rx_ack_len += b->len;
else
_tcp_recved(pcb, b->len);
pbuf_free(b);
}
} }
return ERR_OK; return ERR_OK;
} }
int8_t AsyncClient::_poll(tcp_pcb* pcb){ int8_t AsyncClient::_poll(tcp_pcb* pcb){
_in_lwip_thread = false;
// Close requested // Close requested
if(_close_pcb){ if(_close_pcb){
_close_pcb = false; _close_pcb = false;
@ -546,7 +637,7 @@ int8_t AsyncClient::_poll(tcp_pcb* pcb){
return ERR_OK; return ERR_OK;
} }
void AsyncClient::_dns_found(ip_addr_t *ipaddr){ void AsyncClient::_dns_found(struct ip_addr *ipaddr){
_in_lwip_thread = true; _in_lwip_thread = true;
if(ipaddr){ if(ipaddr){
connect(IPAddress(ipaddr->u_addr.ip4.addr), _connect_port); connect(IPAddress(ipaddr->u_addr.ip4.addr), _connect_port);
@ -591,10 +682,12 @@ int8_t AsyncClient::abort(){
} }
void AsyncClient::close(bool now){ void AsyncClient::close(bool now){
if(_in_lwip_thread){ if(_pcb){
tcp_recved(_pcb, _rx_ack_len); if(_in_lwip_thread){
} else { tcp_recved(_pcb, _rx_ack_len);
_tcp_recved(_pcb, _rx_ack_len); } else {
_tcp_recved(_pcb, _rx_ack_len);
}
} }
if(now) if(now)
_close(); _close();
@ -814,6 +907,14 @@ bool AsyncClient::canSend(){
return space() > 0; return space() > 0;
} }
void AsyncClient::ackPacket(struct pbuf * pb){
if(!pb){
return;
}
_tcp_recved(_pcb, pb->len);
pbuf_free(pb);
}
// Callback Setters // Callback Setters
@ -842,6 +943,11 @@ void AsyncClient::onData(AcDataHandler cb, void* arg){
_recv_cb_arg = arg; _recv_cb_arg = arg;
} }
void AsyncClient::onPacket(AcPacketHandler cb, void* arg){
_pb_cb = cb;
_pb_cb_arg = arg;
}
void AsyncClient::onTimeout(AcTimeoutHandler cb, void* arg){ void AsyncClient::onTimeout(AcTimeoutHandler cb, void* arg){
_timeout_cb = cb; _timeout_cb = cb;
_timeout_cb_arg = arg; _timeout_cb_arg = arg;
@ -853,31 +959,58 @@ void AsyncClient::onPoll(AcConnectHandler cb, void* arg){
} }
void AsyncClient::_s_dns_found(const char * name, ip_addr_t * ipaddr, void * arg){ void AsyncClient::_s_dns_found(const char * name, struct ip_addr * ipaddr, void * arg){
reinterpret_cast<AsyncClient*>(arg)->_dns_found(ipaddr); if(arg){
reinterpret_cast<AsyncClient*>(arg)->_dns_found(ipaddr);
} else {
log_e("Bad Arg: 0x%08x", arg);
}
} }
int8_t AsyncClient::_s_poll(void * arg, struct tcp_pcb * pcb) { int8_t AsyncClient::_s_poll(void * arg, struct tcp_pcb * pcb) {
reinterpret_cast<AsyncClient*>(arg)->_poll(pcb); if(arg && pcb){
reinterpret_cast<AsyncClient*>(arg)->_poll(pcb);
} else {
log_e("Bad Args: 0x%08x 0x%08x", arg, pcb);
}
return ERR_OK; return ERR_OK;
} }
int8_t AsyncClient::_s_recv(void * arg, struct tcp_pcb * pcb, struct pbuf *pb, int8_t err) { int8_t AsyncClient::_s_recv(void * arg, struct tcp_pcb * pcb, struct pbuf *pb, int8_t err) {
reinterpret_cast<AsyncClient*>(arg)->_recv(pcb, pb, err); if(arg && pcb){
reinterpret_cast<AsyncClient*>(arg)->_recv(pcb, pb, err);
} else {
if(pb){
pbuf_free(pb);
}
log_e("Bad Args: 0x%08x 0x%08x", arg, pcb);
}
return ERR_OK; return ERR_OK;
} }
int8_t AsyncClient::_s_sent(void * arg, struct tcp_pcb * pcb, uint16_t len) { int8_t AsyncClient::_s_sent(void * arg, struct tcp_pcb * pcb, uint16_t len) {
reinterpret_cast<AsyncClient*>(arg)->_sent(pcb, len); if(arg && pcb){
reinterpret_cast<AsyncClient*>(arg)->_sent(pcb, len);
} else {
log_e("Bad Args: 0x%08x 0x%08x", arg, pcb);
}
return ERR_OK; return ERR_OK;
} }
void AsyncClient::_s_error(void * arg, int8_t err) { void AsyncClient::_s_error(void * arg, int8_t err) {
reinterpret_cast<AsyncClient*>(arg)->_error(err); if(arg){
reinterpret_cast<AsyncClient*>(arg)->_error(err);
} else {
log_e("Bad Arg: 0x%08x", arg);
}
} }
int8_t AsyncClient::_s_connected(void * arg, void * pcb, int8_t err){ int8_t AsyncClient::_s_connected(void * arg, void * pcb, int8_t err){
reinterpret_cast<AsyncClient*>(arg)->_connected(pcb, err); if(arg && pcb){
reinterpret_cast<AsyncClient*>(arg)->_connected(pcb, err);
} else {
log_e("Bad Args: 0x%08x 0x%08x", arg, pcb);
}
return ERR_OK; return ERR_OK;
} }
@ -1032,9 +1165,9 @@ void AsyncServer::end(){
tcp_arg(_pcb, NULL); tcp_arg(_pcb, NULL);
tcp_accept(_pcb, NULL); tcp_accept(_pcb, NULL);
if(_in_lwip_thread){ if(_in_lwip_thread){
tcp_abort(_pcb); tcp_close(_pcb);
} else { } else {
_tcp_abort(_pcb); _tcp_close(_pcb);
} }
_pcb = NULL; _pcb = NULL;
} }

View File

@ -25,7 +25,8 @@
#include "IPAddress.h" #include "IPAddress.h"
#include <functional> #include <functional>
extern "C" { extern "C" {
#include "freertos/semphr.h" #include "freertos/semphr.h"
#include "lwip/pbuf.h"
} }
class AsyncClient; class AsyncClient;
@ -38,11 +39,11 @@ 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*, size_t len, uint32_t time)> AcAckHandler;
typedef std::function<void(void*, AsyncClient*, int8_t error)> AcErrorHandler; typedef std::function<void(void*, AsyncClient*, int8_t error)> AcErrorHandler;
typedef std::function<void(void*, AsyncClient*, void *data, size_t len)> AcDataHandler; 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*, AsyncClient*, uint32_t time)> AcTimeoutHandler;
struct tcp_pcb; struct tcp_pcb;
struct pbuf; struct ip_addr;
struct _ip_addr;
class AsyncClient { class AsyncClient {
protected: protected:
@ -58,6 +59,8 @@ class AsyncClient {
void* _error_cb_arg; void* _error_cb_arg;
AcDataHandler _recv_cb; AcDataHandler _recv_cb;
void* _recv_cb_arg; void* _recv_cb_arg;
AcPacketHandler _pb_cb;
void* _pb_cb_arg;
AcTimeoutHandler _timeout_cb; AcTimeoutHandler _timeout_cb;
void* _timeout_cb_arg; void* _timeout_cb_arg;
AcConnectHandler _poll_cb; AcConnectHandler _poll_cb;
@ -78,7 +81,7 @@ class AsyncClient {
void _error(int8_t err); void _error(int8_t err);
int8_t _poll(tcp_pcb* pcb); int8_t _poll(tcp_pcb* pcb);
int8_t _sent(tcp_pcb* pcb, uint16_t len); int8_t _sent(tcp_pcb* pcb, uint16_t len);
void _dns_found(struct _ip_addr *ipaddr); void _dns_found(struct ip_addr *ipaddr);
public: public:
@ -105,13 +108,13 @@ class AsyncClient {
bool canSend();//ack is not pending bool canSend();//ack is not pending
size_t space(); size_t space();
size_t add(const char* data, size_t size, uint8_t apiflags=0);//add for sending size_t add(const char* data, size_t size, uint8_t apiflags=ASYNC_WRITE_FLAG_COPY);//add for sending
bool send();//send all data added with the method above 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 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 void ackLater(){ _ack_pcb = false; } //will not ack the current packet. Call from onData
size_t write(const char* data); size_t write(const char* data);
size_t write(const char* data, size_t size, uint8_t apiflags=0); //only when canSend() == true size_t write(const char* data, size_t size, uint8_t apiflags=ASYNC_WRITE_FLAG_COPY); //only when canSend() == true
uint8_t state(); uint8_t state();
bool connecting(); bool connecting();
@ -141,10 +144,13 @@ class AsyncClient {
void onDisconnect(AcConnectHandler cb, void* arg = 0); //disconnected void onDisconnect(AcConnectHandler cb, void* arg = 0); //disconnected
void onAck(AcAckHandler cb, void* arg = 0); //ack received void onAck(AcAckHandler cb, void* arg = 0); //ack received
void onError(AcErrorHandler cb, void* arg = 0); //unsuccessful connect or error void onError(AcErrorHandler cb, void* arg = 0); //unsuccessful connect or error
void onData(AcDataHandler cb, void* arg = 0); //data received 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 onTimeout(AcTimeoutHandler cb, void* arg = 0); //ack timeout
void onPoll(AcConnectHandler cb, void* arg = 0); //every 125ms when connected void onPoll(AcConnectHandler cb, void* arg = 0); //every 125ms when connected
void ackPacket(struct pbuf * pb);
const char * errorToString(int8_t error); const char * errorToString(int8_t error);
const char * stateToString(); const char * stateToString();
@ -155,7 +161,7 @@ class AsyncClient {
static void _s_error(void *arg, int8_t err); static void _s_error(void *arg, int8_t err);
static int8_t _s_sent(void *arg, struct tcp_pcb *tpcb, uint16_t len); static int8_t _s_sent(void *arg, struct tcp_pcb *tpcb, uint16_t len);
static int8_t _s_connected(void* arg, void* tpcb, int8_t err); static int8_t _s_connected(void* arg, void* tpcb, int8_t err);
static void _s_dns_found(const char *name, struct _ip_addr *ipaddr, void *arg); static void _s_dns_found(const char *name, struct ip_addr *ipaddr, void *arg);
bool _in_lwip_thread; bool _in_lwip_thread;
}; };

View File

@ -12,6 +12,8 @@ To use this library you might need to have the latest git versions of [ESP32](ht
## Table of contents ## Table of contents
- [ESPAsyncWebServer ](#espasyncwebserver-) - [ESPAsyncWebServer ](#espasyncwebserver-)
- [Installation](#installation)
- [Using PlatformIO](#using-platformio)
- [Why should you care](#why-should-you-care) - [Why should you care](#why-should-you-care)
- [Important things to remember](#important-things-to-remember) - [Important things to remember](#important-things-to-remember)
- [Principles of operation](#principles-of-operation) - [Principles of operation](#principles-of-operation)
@ -28,6 +30,7 @@ To use this library you might need to have the latest git versions of [ESP32](ht
- [GET, POST and FILE parameters](#get-post-and-file-parameters) - [GET, POST and FILE parameters](#get-post-and-file-parameters)
- [FILE Upload handling](#file-upload-handling) - [FILE Upload handling](#file-upload-handling)
- [Body data handling](#body-data-handling) - [Body data handling](#body-data-handling)
- [JSON body handling with ArduinoJson](#json-body-handling-with-arduinojson)
- [Responses](#responses) - [Responses](#responses)
- [Redirect to another URL](#redirect-to-another-url) - [Redirect to another URL](#redirect-to-another-url)
- [Basic response with HTTP Code](#basic-response-with-http-code) - [Basic response with HTTP Code](#basic-response-with-http-code)
@ -78,6 +81,33 @@ To use this library you might need to have the latest git versions of [ESP32](ht
- [Setup global and class functions as request handlers](#setup-global-and-class-functions-as-request-handlers) - [Setup global and class functions as request handlers](#setup-global-and-class-functions-as-request-handlers)
- [Methods for controlling websocket connections](#methods-for-controlling-websocket-connections) - [Methods for controlling websocket connections](#methods-for-controlling-websocket-connections)
- [Adding default headers to all responses](#adding-default-headers) - [Adding default headers to all responses](#adding-default-headers)
## Installation
### Using PlatformIO
[PlatformIO](http://platformio.org) is an open source ecosystem for IoT development with cross platform build system, library manager and full support for Espressif ESP8266/ESP32 development. It works on the popular host OS: Mac OS X, Windows, Linux 32/64, Linux ARM (like Raspberry Pi, BeagleBone, CubieBoard).
1. Install [PlatformIO IDE](http://platformio.org/platformio-ide)
2. Create new project using "PlatformIO Home > New Project"
3. Update dev/platform to staging version:
- [Instruction for Espressif 8266](http://docs.platformio.org/en/latest/platforms/espressif8266.html#using-arduino-framework-with-staging-version)
- [Instruction for Espressif 32](http://docs.platformio.org/en/latest/platforms/espressif32.html#using-arduino-framework-with-staging-version)
4. Add "ESP Async WebServer" to project using [Project Configuration File `platformio.ini`](http://docs.platformio.org/page/projectconf.html) and [lib_deps](http://docs.platformio.org/page/projectconf/section_env_library.html#lib-deps) option:
```ini
[env:myboard]
platform = espressif...
board = ...
framework = arduino
# using the latest stable version
lib_deps = ESP Async WebServer
# or using GIT Url (the latest development version)
lib_deps = https://github.com/me-no-dev/ESPAsyncWebServer.git
```
5. Happy coding with PlatformIO!
## Why should you care ## Why should you care
- Using asynchronous network means that you can handle more than one connection at the same time - Using asynchronous network means that you can handle more than one connection at the same time
- You are called once the request is ready and parsed - You are called once the request is ready and parsed
@ -284,6 +314,20 @@ void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_
} }
} }
``` ```
If needed, the `_tempObject` field on the request can be used to store a pointer to temporary data (e.g. from the body) associated with the request. If assigned, the pointer will automatically be freed along with the request.
### JSON body handling with ArduinoJson
Endpoints which consume JSON can use a special handler to get ready to use JSON data in the request callback:
```cpp
#include "AsyncJson.h"
#include "ArduinoJson.h"
AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/rest/endpoint", [](AsyncWebServerRequest *request, JsonVariant &json) {
JsonObject& jsonObj = json.as<JsonObject>();
// ...
});
server.addHandler(handler);
```
## Responses ## Responses
### Redirect to another URL ### Redirect to another URL
@ -688,7 +732,7 @@ This way of sending Json is great for when the result is below 4KB
#include "ArduinoJson.h" #include "ArduinoJson.h"
AsyncResponseStream *response = request->beginResponseStream("text/json"); AsyncResponseStream *response = request->beginResponseStream("application/json");
DynamicJsonBuffer jsonBuffer; DynamicJsonBuffer jsonBuffer;
JsonObject &root = jsonBuffer.createObject(); JsonObject &root = jsonBuffer.createObject();
root["heap"] = ESP.getFreeHeap(); root["heap"] = ESP.getFreeHeap();
@ -942,7 +986,7 @@ ws.printfAll(arguments...);
//printf_P to a client //printf_P to a client
ws.printf_P((uint32_t)client_id, PSTR(format), arguments...); ws.printf_P((uint32_t)client_id, PSTR(format), arguments...);
//printfAll_P to all clients //printfAll_P to all clients
ws.printf_P(PSTR(format), arguments...); ws.printfAll_P(PSTR(format), arguments...);
//send text to a client //send text to a client
ws.text((uint32_t)client_id, (char*)text); ws.text((uint32_t)client_id, (char*)text);
ws.text((uint32_t)client_id, (uint8_t*)text, (size_t)len); ws.text((uint32_t)client_id, (uint8_t*)text, (size_t)len);
@ -1097,7 +1141,7 @@ server.on("/scan", HTTP_GET, [](AsyncWebServerRequest *request){
} }
} }
json += "]"; json += "]";
request->send(200, "text/json", json); request->send(200, "application/json", json);
json = String(); json = String();
}); });
``` ```

View File

@ -1,23 +0,0 @@
{
"name":"ESPAsyncWebServer",
"description":"Asynchronous HTTP and WebSocket Server Library for ESP8266",
"keywords":"http,async,websocket,webserver",
"authors":
{
"name": "Hristo Gochkov",
"maintainer": true
},
"repository":
{
"type": "git",
"url": "https://github.com/me-no-dev/ESPAsyncWebServer.git"
},
"version": "1.1.0",
"license": "LGPL-3.0",
"frameworks": "arduino",
"platforms": ["espressif8266", "espressif8266_stage"],
"dependencies":
{
"name": "ESPAsyncTCP"
}
}

View File

@ -6,7 +6,7 @@
#include <ESPAsyncTCP.h> #include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <SPIFFSEditor.h> #include <SPIFFSEditor.h>
#include <ESP8266SSDP.h>
// SKETCH BEGIN // SKETCH BEGIN
AsyncWebServer server(80); AsyncWebServer server(80);
AsyncWebSocket ws("/ws"); AsyncWebSocket ws("/ws");

View File

@ -0,0 +1,72 @@
//
// A simple server implementation showing how to:
// * serve static messages
// * read GET and POST parameters
// * handle missing pages / 404s
//
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <Hash.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
AsyncWebServer server(80);
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";
const char* PARAM_MESSAGE = "message";
void notFound(AsyncWebServerRequest *request) {
request->send(404, "text/plain", "Not found");
}
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.printf("WiFi Failed!\n");
return;
}
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
Serial.print("Hostname: ");
Serial.println(WiFi.hostname());
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/plain", "Hello, world");
});
// Send a GET request to <IP>/get?message=<message>
server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) {
String message;
if (request->hasParam(PARAM_MESSAGE)) {
message = request->getParam(PARAM_MESSAGE)->value();
} else {
message = "No message sent";
}
request->send(200, "text/plain", "Hello, GET: " + message);
});
// Send a POST request to <IP>/post with a form field message set to <message>
server.on("/post", HTTP_POST, [](AsyncWebServerRequest *request){
String message;
if (request->hasParam(PARAM_MESSAGE, true)) {
message = request->getParam(PARAM_MESSAGE, true)->value();
} else {
message = "No message sent";
}
request->send(200, "text/plain", "Hello, POST: " + message);
});
server.onNotFound(notFound);
server.begin();
}
void loop() {
}

View File

@ -1,6 +1,6 @@
{ {
"name":"AsyncWebServer", "name":"ESP Async WebServer",
"description":"Asynchronous HTTP and WebSocket Server Library for ESP32", "description":"Asynchronous HTTP and WebSocket Server Library for ESP8266 and ESP32",
"keywords":"http,async,websocket,webserver", "keywords":"http,async,websocket,webserver",
"authors": "authors":
{ {
@ -12,12 +12,18 @@
"type": "git", "type": "git",
"url": "https://github.com/me-no-dev/ESPAsyncWebServer.git" "url": "https://github.com/me-no-dev/ESPAsyncWebServer.git"
}, },
"version": "1.1.0", "version": "1.2.0",
"license": "LGPL-3.0", "license": "LGPL-3.0",
"frameworks": "arduino", "frameworks": "arduino",
"platforms": ["espressif32", "espressif32_stage"], "platforms": ["espressif8266", "espressif32"],
"dependencies": "dependencies": [
{ {
"name": "AsyncTCP" "name": "ESPAsyncTCP",
} "platforms": "espressif8266"
},
{
"name": "AsyncTCP",
"platforms": "espressif32"
}
]
} }

View File

@ -1,5 +1,5 @@
name=ESP Async WebServer name=ESP Async WebServer
version=1.1.0 version=1.2.0
author=Me-No-Dev author=Me-No-Dev
maintainer=Me-No-Dev maintainer=Me-No-Dev
sentence=Async Web Server for ESP8266 and ESP31B sentence=Async Web Server for ESP8266 and ESP31B

View File

@ -1,9 +1,9 @@
// ESPasyncJson.h // AsyncJson.h
/* /*
Async Response to use with arduinoJson and asyncwebserver Async Response to use with ArduinoJson and AsyncWebServer
Written by Andrew Melvin (SticilFace) with help from me-no-dev and BBlanchon. Written by Andrew Melvin (SticilFace) with help from me-no-dev and BBlanchon.
example of callback in use Example of callback in use
server.on("/json", HTTP_ANY, [](AsyncWebServerRequest * request) { server.on("/json", HTTP_ANY, [](AsyncWebServerRequest * request) {
@ -17,11 +17,27 @@
request->send(response); request->send(response);
}); });
--------------------
Async Request to use with ArduinoJson and AsyncWebServer
Written by Arsène von Wyss (avonwyss)
Example
AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/rest/endpoint");
handler->onRequest([](AsyncWebServerRequest *request, JsonVariant &json) {
JsonObject& jsonObj = json.as<JsonObject>();
// ...
});
server.addHandler(handler);
*/ */
#ifndef ASYNC_JSON_H_ #ifndef ASYNC_JSON_H_
#define ASYNC_JSON_H_ #define ASYNC_JSON_H_
#include <ArduinoJson.h> #include <ArduinoJson.h>
constexpr char* JSON_MIMETYPE = "application/json";
/* /*
* Json Response * Json Response
* */ * */
@ -57,7 +73,7 @@ class AsyncJsonResponse: public AsyncAbstractResponse {
public: public:
AsyncJsonResponse(bool isArray=false): _isValid{false} { AsyncJsonResponse(bool isArray=false): _isValid{false} {
_code = 200; _code = 200;
_contentType = "text/json"; _contentType = JSON_MIMETYPE;
if(isArray) if(isArray)
_root = _jsonBuffer.createArray(); _root = _jsonBuffer.createArray();
else else
@ -80,4 +96,68 @@ class AsyncJsonResponse: public AsyncAbstractResponse {
return len; return len;
} }
}; };
typedef std::function<void(AsyncWebServerRequest *request, JsonVariant &json)> ArJsonRequestHandlerFunction;
class AsyncCallbackJsonWebHandler: public AsyncWebHandler {
private:
protected:
const String _uri;
WebRequestMethodComposite _method;
ArJsonRequestHandlerFunction _onRequest;
int _contentLength;
int _maxContentLength;
public:
AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest) : _uri(uri), _method(HTTP_POST|HTTP_PUT|HTTP_PATCH), _onRequest(onRequest), _maxContentLength(16384) {}
void setMethod(WebRequestMethodComposite method){ _method = method; }
void setMaxContentLength(int maxContentLength){ _maxContentLength = maxContentLength; }
void onRequest(ArJsonRequestHandlerFunction fn){ _onRequest = fn; }
virtual bool canHandle(AsyncWebServerRequest *request) override final{
if(!_onRequest)
return false;
if(!(_method & request->method()))
return false;
if(_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri+"/")))
return false;
if (!request->contentType().equalsIgnoreCase(JSON_MIMETYPE))
return false;
request->addInterestingHeader("ANY");
return true;
}
virtual void handleRequest(AsyncWebServerRequest *request) override final {
if(_onRequest) {
if (request->_tempObject != NULL) {
DynamicJsonBuffer jsonBuffer;
JsonVariant json = jsonBuffer.parse((uint8_t*)(request->_tempObject));
if (json.success()) {
_onRequest(request, json);
return;
}
}
request->send(_contentLength > _maxContentLength ? 413 : 400);
} else {
request->send(500);
}
}
virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final {
}
virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final {
if (_onRequest) {
_contentLength = total;
if (total > 0 && request->_tempObject == NULL && total < _maxContentLength) {
request->_tempObject = malloc(total);
}
if (request->_tempObject != NULL) {
memcpy((uint8_t*)(request->_tempObject) + index, data, len);
}
}
}
virtual bool isRequestHandlerTrivial() override final {return _onRequest ? false : true;}
};
#endif #endif

View File

@ -146,7 +146,7 @@ AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(uint8_t * data, size_t
return; return;
} }
_data = new uint8_t[size + 1]; _data = new uint8_t[_len + 1];
if (_data) { if (_data) {
memcpy(_data, data, _len); memcpy(_data, data, _len);
@ -224,10 +224,10 @@ bool AsyncWebSocketMessageBuffer::reserve(size_t size)
_data = nullptr; _data = nullptr;
} }
_data = new uint8_t[size]; _data = new uint8_t[_len + 1];
if (_data) { if (_data) {
_data[_len] = 0; _data[_len] = 0;
return true; return true;
} else { } else {
return false; return false;
@ -337,14 +337,31 @@ AsyncWebSocketBasicMessage::~AsyncWebSocketBasicMessage() {
_status = WS_MSG_SENT; _status = WS_MSG_SENT;
return 0; return 0;
} }
size_t window = webSocketSendFrameWindow(client); if(_sent > _len){
_status = WS_MSG_ERROR;
return 0;
}
size_t toSend = _len - _sent; size_t toSend = _len - _sent;
if(window < toSend) toSend = window; size_t window = webSocketSendFrameWindow(client);
bool final = ((toSend + _sent) == _len);
size_t sent = webSocketSendFrame(client, final, (_sent == 0)?_opcode:(int)WS_CONTINUATION, _mask, (uint8_t*)(_data+_sent), toSend); if(window < toSend) {
_sent += sent; toSend = window;
uint8_t headLen = ((sent < 126)?2:4)+(_mask*4); }
_ack += sent + headLen;
_sent += toSend;
_ack += toSend + ((toSend < 126)?2:4) + (_mask * 4);
bool final = (_sent == _len);
uint8_t* dPtr = (uint8_t*)(_data + (_sent - toSend));
uint8_t opCode = (toSend && _sent == toSend)?_opcode:(uint8_t)WS_CONTINUATION;
size_t sent = webSocketSendFrame(client, final, opCode, _mask, dPtr, toSend);
_status = WS_MSG_SENDING;
if(toSend && sent != toSend){
_sent -= (toSend - sent);
_ack -= (toSend - sent);
}
return sent; return sent;
} }
@ -384,6 +401,7 @@ AsyncWebSocketMultiMessage::AsyncWebSocketMultiMessage(AsyncWebSocketMessageBuff
_data = buffer->get(); _data = buffer->get();
_len = buffer->length(); _len = buffer->length();
_status = WS_MSG_SENDING; _status = WS_MSG_SENDING;
//ets_printf("M: %u\n", _len);
} else { } else {
_status = WS_MSG_ERROR; _status = WS_MSG_ERROR;
} }
@ -399,9 +417,10 @@ AsyncWebSocketMultiMessage::~AsyncWebSocketMultiMessage() {
void AsyncWebSocketMultiMessage::ack(size_t len, uint32_t time) { void AsyncWebSocketMultiMessage::ack(size_t len, uint32_t time) {
_acked += len; _acked += len;
if(_sent == _len && _acked == _ack){ if(_sent >= _len && _acked >= _ack){
_status = WS_MSG_SENT; _status = WS_MSG_SENT;
} }
//ets_printf("A: %u\n", len);
} }
size_t AsyncWebSocketMultiMessage::send(AsyncClient *client) { size_t AsyncWebSocketMultiMessage::send(AsyncClient *client) {
if(_status != WS_MSG_SENDING) if(_status != WS_MSG_SENDING)
@ -410,18 +429,39 @@ AsyncWebSocketMultiMessage::~AsyncWebSocketMultiMessage() {
return 0; return 0;
} }
if(_sent == _len){ if(_sent == _len){
if(_acked == _ack) _status = WS_MSG_SENT;
_status = WS_MSG_SENT;
return 0; return 0;
} }
size_t window = webSocketSendFrameWindow(client); if(_sent > _len){
_status = WS_MSG_ERROR;
//ets_printf("E: %u > %u\n", _sent, _len);
return 0;
}
size_t toSend = _len - _sent; size_t toSend = _len - _sent;
if(window < toSend) toSend = window; size_t window = webSocketSendFrameWindow(client);
bool final = ((toSend + _sent) == _len);
size_t sent = webSocketSendFrame(client, final, (_sent == 0)?_opcode:(int)WS_CONTINUATION, _mask, (uint8_t*)(_data+_sent), toSend); if(window < toSend) {
_sent += sent; toSend = window;
uint8_t headLen = ((sent < 126)?2:4)+(_mask*4); }
_ack += sent + headLen;
_sent += toSend;
_ack += toSend + ((toSend < 126)?2:4) + (_mask * 4);
//ets_printf("W: %u %u\n", _sent - toSend, toSend);
bool final = (_sent == _len);
uint8_t* dPtr = (uint8_t*)(_data + (_sent - toSend));
uint8_t opCode = (toSend && _sent == toSend)?_opcode:(uint8_t)WS_CONTINUATION;
size_t sent = webSocketSendFrame(client, final, opCode, _mask, dPtr, toSend);
_status = WS_MSG_SENDING;
if(toSend && sent != toSend){
//ets_printf("E: %u != %u\n", toSend, sent);
_sent -= (toSend - sent);
_ack -= (toSend - sent);
}
//ets_printf("S: %u %u\n", _sent, sent);
return sent; return sent;
} }
@ -511,7 +551,12 @@ void AsyncWebSocketClient::_queueMessage(AsyncWebSocketMessage *dataMessage){
delete dataMessage; delete dataMessage;
return; return;
} }
_messageQueue.add(dataMessage); if(_messageQueue.length() > WS_MAX_QUEUED_MESSAGES){
ets_printf("ERROR: Too many messages queued\n");
delete dataMessage;
} else {
_messageQueue.add(dataMessage);
}
if(_client->canSend()) if(_client->canSend())
_runQueue(); _runQueue();
} }
@ -565,83 +610,93 @@ void AsyncWebSocketClient::_onDisconnect(){
_server->_handleDisconnect(this); _server->_handleDisconnect(this);
} }
void AsyncWebSocketClient::_onData(void *buf, size_t plen){ void AsyncWebSocketClient::_onData(void *pbuf, size_t plen){
_lastMessageTime = millis(); _lastMessageTime = millis();
uint8_t *fdata = (uint8_t*)buf; uint8_t *data = (uint8_t*)pbuf;
uint8_t * data = fdata; while(plen > 0){
if(!_pstate){ if(!_pstate){
_pinfo.index = 0; const uint8_t *fdata = data;
_pinfo.final = (fdata[0] & 0x80) != 0; _pinfo.index = 0;
_pinfo.opcode = fdata[0] & 0x0F; _pinfo.final = (fdata[0] & 0x80) != 0;
_pinfo.masked = (fdata[1] & 0x80) != 0; _pinfo.opcode = fdata[0] & 0x0F;
_pinfo.len = fdata[1] & 0x7F; _pinfo.masked = (fdata[1] & 0x80) != 0;
data += 2; _pinfo.len = fdata[1] & 0x7F;
plen = plen - 2;
if(_pinfo.len == 126){
_pinfo.len = fdata[3] | (uint16_t)(fdata[2]) << 8;
data += 2; data += 2;
plen = plen - 2; plen -= 2;
} else if(_pinfo.len == 127){ if(_pinfo.len == 126){
_pinfo.len = fdata[9] | (uint16_t)(fdata[8]) << 8 | (uint32_t)(fdata[7]) << 16 | (uint32_t)(fdata[6]) << 24 | (uint64_t)(fdata[5]) << 32 | (uint64_t)(fdata[4]) << 40 | (uint64_t)(fdata[3]) << 48 | (uint64_t)(fdata[2]) << 56; _pinfo.len = fdata[3] | (uint16_t)(fdata[2]) << 8;
data += 8; data += 2;
plen = plen - 8; plen -= 2;
} else if(_pinfo.len == 127){
_pinfo.len = fdata[9] | (uint16_t)(fdata[8]) << 8 | (uint32_t)(fdata[7]) << 16 | (uint32_t)(fdata[6]) << 24 | (uint64_t)(fdata[5]) << 32 | (uint64_t)(fdata[4]) << 40 | (uint64_t)(fdata[3]) << 48 | (uint64_t)(fdata[2]) << 56;
data += 8;
plen -= 8;
}
if(_pinfo.masked){
memcpy(_pinfo.mask, data, 4);
data += 4;
plen -= 4;
}
} }
const size_t datalen = std::min((size_t)(_pinfo.len - _pinfo.index), plen);
const auto datalast = data[datalen];
if(_pinfo.masked){ if(_pinfo.masked){
memcpy(_pinfo.mask, data, 4); for(size_t i=0;i<datalen;i++)
data += 4; data[i] ^= _pinfo.mask[(_pinfo.index+i)%4];
plen = plen - 4;
size_t i;
for(i=0;i<plen;i++)
data[i] = data[i] ^ _pinfo.mask[(_pinfo.index+i)%4];
} }
} else {
if(_pinfo.masked){
size_t i;
for(i=0;i<plen;i++)
data[i] = data[i] ^ _pinfo.mask[(_pinfo.index+i)%4];
}
}
if((plen + _pinfo.index) < _pinfo.len){
_pstate = 1;
if(_pinfo.index == 0){ if((datalen + _pinfo.index) < _pinfo.len){
if(_pinfo.opcode){ _pstate = 1;
_pinfo.message_opcode = _pinfo.opcode;
_pinfo.num = 0;
} else _pinfo.num += 1;
}
_server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, (uint8_t*)data, plen);
_pinfo.index += plen; if(_pinfo.index == 0){
} else if((plen + _pinfo.index) == _pinfo.len){ if(_pinfo.opcode){
_pstate = 0; _pinfo.message_opcode = _pinfo.opcode;
if(_pinfo.opcode == WS_DISCONNECT){ _pinfo.num = 0;
if(plen){ } else _pinfo.num += 1;
uint16_t reasonCode = (uint16_t)(data[0] << 8) + data[1]; }
char * reasonString = (char*)(data+2); _server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, (uint8_t*)data, datalen);
if(reasonCode > 1001){
_server->_handleEvent(this, WS_EVT_ERROR, (void *)&reasonCode, (uint8_t*)reasonString, strlen(reasonString)); _pinfo.index += datalen;
} else if((datalen + _pinfo.index) == _pinfo.len){
_pstate = 0;
if(_pinfo.opcode == WS_DISCONNECT){
if(datalen){
uint16_t reasonCode = (uint16_t)(data[0] << 8) + data[1];
char * reasonString = (char*)(data+2);
if(reasonCode > 1001){
_server->_handleEvent(this, WS_EVT_ERROR, (void *)&reasonCode, (uint8_t*)reasonString, strlen(reasonString));
}
} }
if(_status == WS_DISCONNECTING){
_status = WS_DISCONNECTED;
_client->close(true);
} else {
_status = WS_DISCONNECTING;
_queueControl(new AsyncWebSocketControl(WS_DISCONNECT, data, datalen));
}
} else if(_pinfo.opcode == WS_PING){
_queueControl(new AsyncWebSocketControl(WS_PONG, data, datalen));
} else if(_pinfo.opcode == WS_PONG){
if(datalen != AWSC_PING_PAYLOAD_LEN || memcmp(AWSC_PING_PAYLOAD, data, AWSC_PING_PAYLOAD_LEN) != 0)
_server->_handleEvent(this, WS_EVT_PONG, NULL, data, datalen);
} else if(_pinfo.opcode < 8){//continuation or text/binary frame
_server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, data, datalen);
} }
if(_status == WS_DISCONNECTING){ } else {
_status = WS_DISCONNECTED; //os_printf("frame error: len: %u, index: %llu, total: %llu\n", datalen, _pinfo.index, _pinfo.len);
_client->close(true); //what should we do?
} else { break;
_status = WS_DISCONNECTING;
_queueControl(new AsyncWebSocketControl(WS_DISCONNECT, data, plen));
}
} else if(_pinfo.opcode == WS_PING){
_queueControl(new AsyncWebSocketControl(WS_PONG, data, plen));
} else if(_pinfo.opcode == WS_PONG){
if(plen != AWSC_PING_PAYLOAD_LEN || memcmp(AWSC_PING_PAYLOAD, data, AWSC_PING_PAYLOAD_LEN) != 0)
_server->_handleEvent(this, WS_EVT_PONG, NULL, (uint8_t*)data, plen);
} else if(_pinfo.opcode < 8){//continuation or text/binary frame
_server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, (uint8_t*)data, plen);
} }
} else {
//os_printf("frame error: len: %u, index: %llu, total: %llu\n", plen, _pinfo.index, _pinfo.len); // restore byte as _handleEvent may have added a null terminator i.e., data[len] = 0;
//what should we do? if (datalen > 0)
data[datalen] = datalast;
data += datalen;
plen -= datalen;
} }
} }
@ -873,8 +928,9 @@ void AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer * buffer){
if (!buffer) return; if (!buffer) return;
buffer->lock(); buffer->lock();
for(const auto& c: _clients){ for(const auto& c: _clients){
if(c->status() == WS_CONNECTED) if(c->status() == WS_CONNECTED){
c->text(buffer); c->text(buffer);
}
} }
buffer->unlock(); buffer->unlock();
_cleanBuffers(); _cleanBuffers();
@ -946,7 +1002,7 @@ size_t AsyncWebSocket::printfAll(const char *format, ...) {
va_end(arg); va_end(arg);
delete[] temp; delete[] temp;
AsyncWebSocketMessageBuffer * buffer = makeBuffer(len + 1); AsyncWebSocketMessageBuffer * buffer = makeBuffer(len);
if (!buffer) { if (!buffer) {
return 0; return 0;
} }
@ -1140,9 +1196,10 @@ AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(uint8_t * data, size_t
void AsyncWebSocket::_cleanBuffers() void AsyncWebSocket::_cleanBuffers()
{ {
for(const auto& c: _buffers){ for(AsyncWebSocketMessageBuffer * c: _buffers){
if(c->canDelete()) if(c && c->canDelete()){
_buffers.remove(c); _buffers.remove(c);
}
} }
} }

View File

@ -24,8 +24,10 @@
#include <Arduino.h> #include <Arduino.h>
#ifdef ESP32 #ifdef ESP32
#include <AsyncTCP.h> #include <AsyncTCP.h>
#define WS_MAX_QUEUED_MESSAGES 32
#else #else
#include <ESPAsyncTCP.h> #include <ESPAsyncTCP.h>
#define WS_MAX_QUEUED_MESSAGES 8
#endif #endif
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
@ -35,13 +37,26 @@ class AsyncWebSocketClient;
class AsyncWebSocketControl; class AsyncWebSocketControl;
typedef struct { typedef struct {
/** Message type as defined by enum AwsFrameType.
* Note: Applications will only see WS_TEXT and WS_BINARY.
* All other types are handled by the library. */
uint8_t message_opcode; uint8_t message_opcode;
/** Frame number of a fragmented message. */
uint32_t num; uint32_t num;
/** Is this the last frame in a fragmented message ?*/
uint8_t final; uint8_t final;
/** Is this frame masked? */
uint8_t masked; uint8_t masked;
/** Message type as defined by enum AwsFrameType.
* This value is the same as message_opcode for non-fragmented
* messages, but may also be WS_CONTINUATION in a fragmented message. */
uint8_t opcode; uint8_t opcode;
/** Length of the current frame.
* This equals the total length of the message if num == 0 && final == true */
uint64_t len; uint64_t len;
/** Mask key */
uint8_t mask[4]; uint8_t mask[4];
/** Offset of the data inside the current frame. */
uint64_t index; uint64_t index;
} AwsFrameInfo; } AwsFrameInfo;
@ -154,6 +169,8 @@ class AsyncWebSocketClient {
uint32_t id(){ return _clientId; } uint32_t id(){ return _clientId; }
AwsClientStatus status(){ return _status; } AwsClientStatus status(){ return _status; }
AsyncClient* client(){ return _client; } AsyncClient* client(){ return _client; }
AsyncWebSocket *server(){ return _server; }
AwsFrameInfo const &pinfo() const { return _pinfo; }
IPAddress remoteIP(); IPAddress remoteIP();
uint16_t remotePort(); uint16_t remotePort();
@ -199,7 +216,7 @@ class AsyncWebSocketClient {
void _onPoll(); void _onPoll();
void _onTimeout(uint32_t time); void _onTimeout(uint32_t time);
void _onDisconnect(); void _onDisconnect();
void _onData(void *buf, size_t plen); void _onData(void *pbuf, size_t plen);
}; };
typedef std::function<void(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len)> AwsEventHandler; typedef std::function<void(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len)> AwsEventHandler;

View File

@ -51,6 +51,7 @@ class AsyncStaticWebHandler;
class AsyncCallbackWebHandler; class AsyncCallbackWebHandler;
class AsyncResponseStream; class AsyncResponseStream;
#ifndef WEBSERVER_H
typedef enum { typedef enum {
HTTP_GET = 0b00000001, HTTP_GET = 0b00000001,
HTTP_POST = 0b00000010, HTTP_POST = 0b00000010,
@ -61,6 +62,11 @@ typedef enum {
HTTP_OPTIONS = 0b01000000, HTTP_OPTIONS = 0b01000000,
HTTP_ANY = 0b01111111, HTTP_ANY = 0b01111111,
} WebRequestMethod; } WebRequestMethod;
#endif
//if this value is returned when asked for data, packet will not be sent and you will be asked for data again
#define RESPONSE_TRY_AGAIN 0xFFFFFFFF
typedef uint8_t WebRequestMethodComposite; typedef uint8_t WebRequestMethodComposite;
typedef std::function<void(void)> ArDisconnectHandler; typedef std::function<void(void)> ArDisconnectHandler;
@ -130,6 +136,7 @@ class AsyncWebServerRequest {
AsyncWebServerResponse* _response; AsyncWebServerResponse* _response;
StringArray _interestingHeaders; StringArray _interestingHeaders;
ArDisconnectHandler _onDisconnectfn; ArDisconnectHandler _onDisconnectfn;
String _temp; String _temp;
uint8_t _parseState; uint8_t _parseState;
@ -204,6 +211,7 @@ class AsyncWebServerRequest {
RequestedConnectionType requestedConnType() const { return _reqconntype; } RequestedConnectionType requestedConnType() const { return _reqconntype; }
bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED); bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED);
void onDisconnect (ArDisconnectHandler fn); void onDisconnect (ArDisconnectHandler fn);
//hash is the string representation of: //hash is the string representation of:
// base64(user:pass) for basic or // base64(user:pass) for basic or
// user:realm:md5(user:realm:pass) for digest // user:realm:md5(user:realm:pass) for digest
@ -323,6 +331,7 @@ class AsyncWebHandler {
virtual void handleRequest(AsyncWebServerRequest *request __attribute__((unused))){} virtual void handleRequest(AsyncWebServerRequest *request __attribute__((unused))){}
virtual void handleUpload(AsyncWebServerRequest *request __attribute__((unused)), const String& filename __attribute__((unused)), size_t index __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), bool final __attribute__((unused))){} virtual void handleUpload(AsyncWebServerRequest *request __attribute__((unused)), const String& filename __attribute__((unused)), size_t index __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), bool final __attribute__((unused))){}
virtual void handleBody(AsyncWebServerRequest *request __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), size_t index __attribute__((unused)), size_t total __attribute__((unused))){} virtual void handleBody(AsyncWebServerRequest *request __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), size_t index __attribute__((unused)), size_t total __attribute__((unused))){}
virtual bool isRequestHandlerTrivial(){return true;}
}; };
/* /*

View File

@ -477,7 +477,7 @@ void SPIFFSEditor::handleRequest(AsyncWebServerRequest *request){
dir.close(); dir.close();
#endif #endif
output += "]"; output += "]";
request->send(200, "text/json", output); request->send(200, "application/json", output);
output = String(); output = String();
} }
else if(request->hasParam("edit") || request->hasParam("download")){ else if(request->hasParam("edit") || request->hasParam("download")){

View File

@ -18,6 +18,7 @@ class SPIFFSEditor: public AsyncWebHandler {
virtual bool canHandle(AsyncWebServerRequest *request) override final; virtual bool canHandle(AsyncWebServerRequest *request) override final;
virtual void handleRequest(AsyncWebServerRequest *request) override final; virtual void handleRequest(AsyncWebServerRequest *request) override final;
virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final; virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final;
virtual bool isRequestHandlerTrivial() override final {return false;}
}; };
#endif #endif

View File

@ -104,6 +104,7 @@ class AsyncCallbackWebHandler: public AsyncWebHandler {
if(_onBody) if(_onBody)
_onBody(request, data, len, index, total); _onBody(request, data, len, index, total);
} }
virtual bool isRequestHandlerTrivial() override final {return _onRequest ? false : true;}
}; };
#endif /* ASYNCWEBSERVERHANDLERIMPL_H_ */ #endif /* ASYNCWEBSERVERHANDLERIMPL_H_ */

View File

@ -127,12 +127,18 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len){
} }
} }
} else if(_parseState == PARSE_REQ_BODY){ } else if(_parseState == PARSE_REQ_BODY){
// A handler should be already attached at this point in _parseLine function.
// If handler does nothing (_onRequest is NULL), we don't need to really parse the body.
const bool needParse = _handler && !_handler->isRequestHandlerTrivial();
if(_isMultipart){ if(_isMultipart){
size_t i; if(needParse){
for(i=0; i<len; i++){ size_t i;
_parseMultipartPostByte(((uint8_t*)buf)[i], i == len - 1); for(i=0; i<len; i++){
_parsedLength++; _parseMultipartPostByte(((uint8_t*)buf)[i], i == len - 1);
} _parsedLength++;
}
} else
_parsedLength += len;
} else { } else {
if(_parsedLength == 0){ if(_parsedLength == 0){
if(_contentType.startsWith("application/x-www-form-urlencoded")){ if(_contentType.startsWith("application/x-www-form-urlencoded")){
@ -149,12 +155,14 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len){
//check if authenticated before calling the body //check if authenticated before calling the body
if(_handler) _handler->handleBody(this, (uint8_t*)buf, len, _parsedLength, _contentLength); if(_handler) _handler->handleBody(this, (uint8_t*)buf, len, _parsedLength, _contentLength);
_parsedLength += len; _parsedLength += len;
} else { } else if(needParse) {
size_t i; size_t i;
for(i=0; i<len; i++){ for(i=0; i<len; i++){
_parsedLength++; _parsedLength++;
_parsePlainPostChar(((uint8_t*)buf)[i]); _parsePlainPostChar(((uint8_t*)buf)[i]);
} }
} else {
_parsedLength += len;
} }
} }
if(_parsedLength == _contentLength){ if(_parsedLength == _contentLength){
@ -886,7 +894,7 @@ const String& AsyncWebServerRequest::arg(const __FlashStringHelper * data) const
size_t n = strlen_P(p); size_t n = strlen_P(p);
char * name = (char*) malloc(n+1); char * name = (char*) malloc(n+1);
if (name) { if (name) {
strcpy(name, p); strcpy_P(name, p);
const String & result = arg( String(name) ); const String & result = arg( String(name) );
free(name); free(name);
return result; return result;

View File

@ -42,6 +42,10 @@ class AsyncBasicResponse: public AsyncWebServerResponse {
class AsyncAbstractResponse: public AsyncWebServerResponse { class AsyncAbstractResponse: public AsyncWebServerResponse {
private: private:
String _head; String _head;
// Data is inserted into cache at begin().
// This is inefficient with vector, but if we use some other container,
// we won't be able to access it as contiguous array of bytes when reading from it,
// so by gaining performance in one place, we'll lose it in another.
std::vector<uint8_t> _cache; std::vector<uint8_t> _cache;
size_t _readDataFromCacheOrContent(uint8_t* data, const size_t len); size_t _readDataFromCacheOrContent(uint8_t* data, const size_t len);
size_t _fillBufferAndProcessTemplates(uint8_t* buf, size_t maxLen); size_t _fillBufferAndProcessTemplates(uint8_t* buf, size_t maxLen);
@ -55,7 +59,10 @@ class AsyncAbstractResponse: public AsyncWebServerResponse {
virtual size_t _fillBuffer(uint8_t *buf __attribute__((unused)), size_t maxLen __attribute__((unused))) { return 0; } virtual size_t _fillBuffer(uint8_t *buf __attribute__((unused)), size_t maxLen __attribute__((unused))) { return 0; }
}; };
#ifndef TEMPLATE_PLACEHOLDER
#define TEMPLATE_PLACEHOLDER '%' #define TEMPLATE_PLACEHOLDER '%'
#endif
#define TEMPLATE_PARAM_NAME_LENGTH 32 #define TEMPLATE_PARAM_NAME_LENGTH 32
class AsyncFileResponse: public AsyncAbstractResponse { class AsyncFileResponse: public AsyncAbstractResponse {
using File = fs::File; using File = fs::File;

View File

@ -302,9 +302,7 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
} }
if(headLen){ if(headLen){
//TODO: memcpy should be faster? memcpy(buf, _head.c_str(), _head.length());
sprintf((char*)buf, "%s", _head.c_str());
_head = String();
} }
size_t readLen = 0; size_t readLen = 0;
@ -313,6 +311,10 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
// HTTP 1.1 allows leading zeros in chunk length. Or spaces may be added. // HTTP 1.1 allows leading zeros in chunk length. Or spaces may be added.
// See RFC2616 sections 2, 3.6.1. // See RFC2616 sections 2, 3.6.1.
readLen = _fillBufferAndProcessTemplates(buf+headLen+6, outLen - 8); readLen = _fillBufferAndProcessTemplates(buf+headLen+6, outLen - 8);
if(readLen == RESPONSE_TRY_AGAIN){
free(buf);
return 0;
}
outLen = sprintf((char*)buf+headLen, "%x", readLen) + headLen; outLen = sprintf((char*)buf+headLen, "%x", readLen) + headLen;
while(outLen < headLen + 4) buf[outLen++] = ' '; while(outLen < headLen + 4) buf[outLen++] = ' ';
buf[outLen++] = '\r'; buf[outLen++] = '\r';
@ -321,16 +323,27 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
buf[outLen++] = '\r'; buf[outLen++] = '\r';
buf[outLen++] = '\n'; buf[outLen++] = '\n';
} else { } else {
outLen = _fillBufferAndProcessTemplates(buf+headLen, outLen) + headLen; readLen = _fillBufferAndProcessTemplates(buf+headLen, outLen);
if(readLen == RESPONSE_TRY_AGAIN){
free(buf);
return 0;
}
outLen = readLen + headLen;
} }
if(outLen) if(headLen){
_writtenLength += request->client()->write((const char*)buf, outLen); _head = String();
}
if(_chunked) if(outLen){
_sentLength += readLen; _writtenLength += request->client()->write((const char*)buf, outLen);
else }
_sentLength += outLen - headLen;
if(_chunked){
_sentLength += readLen;
} else {
_sentLength += outLen - headLen;
}
free(buf); free(buf);
@ -378,8 +391,6 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size
// temporary buffer to hold parameter name // temporary buffer to hold parameter name
uint8_t buf[TEMPLATE_PARAM_NAME_LENGTH + 1]; uint8_t buf[TEMPLATE_PARAM_NAME_LENGTH + 1];
String paramName; String paramName;
// cache position to insert remainder of template parameter value
std::vector<uint8_t>::iterator i = _cache.end();
// If closing placeholder is found: // If closing placeholder is found:
if(pTemplateEnd) { if(pTemplateEnd) {
// prepare argument to callback // prepare argument to callback
@ -403,16 +414,14 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size
// prepare argument to callback // prepare argument to callback
*pTemplateEnd = 0; *pTemplateEnd = 0;
paramName = String(reinterpret_cast<char*>(buf)); paramName = String(reinterpret_cast<char*>(buf));
// Copy remaining read-ahead data into cache (when std::vector::insert returning iterator will be available, these 3 lines can be simplified into 1) // Copy remaining read-ahead data into cache
const size_t pos = _cache.size(); _cache.insert(_cache.begin(), pTemplateEnd + 1, buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent);
_cache.insert(_cache.end(), pTemplateEnd + 1, buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent);
i = _cache.begin() + pos;
pTemplateEnd = &data[len - 1]; pTemplateEnd = &data[len - 1];
} }
else // closing placeholder not found in file data, store found percent symbol as is and advance to the next position else // closing placeholder not found in file data, store found percent symbol as is and advance to the next position
{ {
// but first, store read file data in cache // but first, store read file data in cache
_cache.insert(_cache.end(), buf + (&data[len - 1] - pTemplateStart), buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent); _cache.insert(_cache.begin(), buf + (&data[len - 1] - pTemplateStart), buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent);
++pTemplateStart; ++pTemplateStart;
} }
} }
@ -434,9 +443,7 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size
// make room for param value // make room for param value
// 1. move extra data to cache if parameter value is longer than placeholder AND if there is no room to store // 1. move extra data to cache if parameter value is longer than placeholder AND if there is no room to store
if((pTemplateEnd + 1 < pTemplateStart + numBytesCopied) && (originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1) < len)) { if((pTemplateEnd + 1 < pTemplateStart + numBytesCopied) && (originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1) < len)) {
size_t pos = i - _cache.begin(); _cache.insert(_cache.begin(), &data[originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1)], &data[len]);
_cache.insert(i, &data[originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1)], &data[len]);
i = _cache.begin() + pos;
//2. parameter value is longer than placeholder text, push the data after placeholder which not saved into cache further to the end //2. parameter value is longer than placeholder text, push the data after placeholder which not saved into cache further to the end
memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[originalLen] - pTemplateStart - numBytesCopied); memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[originalLen] - pTemplateStart - numBytesCopied);
} else if(pTemplateEnd + 1 != pTemplateStart + numBytesCopied) } else if(pTemplateEnd + 1 != pTemplateStart + numBytesCopied)
@ -447,7 +454,7 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size
memcpy(pTemplateStart, pvstr, numBytesCopied); memcpy(pTemplateStart, pvstr, numBytesCopied);
// If result is longer than buffer, copy the remainder into cache (this could happen only if placeholder text itself did not fit entirely in buffer) // If result is longer than buffer, copy the remainder into cache (this could happen only if placeholder text itself did not fit entirely in buffer)
if(numBytesCopied < pvlen) { if(numBytesCopied < pvlen) {
_cache.insert(i, pvstr + numBytesCopied, pvstr + pvlen); _cache.insert(_cache.begin(), pvstr + numBytesCopied, pvstr + pvlen);
} else if(pTemplateStart + numBytesCopied < pTemplateEnd + 1) { // result is copied fully; if result is shorter than placeholder text... } else if(pTemplateStart + numBytesCopied < pTemplateEnd + 1) { // result is copied fully; if result is shorter than placeholder text...
// there is some free room, fill it from cache // there is some free room, fill it from cache
const size_t roomFreed = pTemplateEnd + 1 - pTemplateStart - numBytesCopied; const size_t roomFreed = pTemplateEnd + 1 - pTemplateStart - numBytesCopied;
@ -476,7 +483,7 @@ void AsyncFileResponse::_setContentType(const String& path){
if (path.endsWith(".html")) _contentType = "text/html"; if (path.endsWith(".html")) _contentType = "text/html";
else if (path.endsWith(".htm")) _contentType = "text/html"; else if (path.endsWith(".htm")) _contentType = "text/html";
else if (path.endsWith(".css")) _contentType = "text/css"; else if (path.endsWith(".css")) _contentType = "text/css";
else if (path.endsWith(".json")) _contentType = "text/json"; else if (path.endsWith(".json")) _contentType = "application/json";
else if (path.endsWith(".js")) _contentType = "application/javascript"; else if (path.endsWith(".js")) _contentType = "application/javascript";
else if (path.endsWith(".png")) _contentType = "image/png"; else if (path.endsWith(".png")) _contentType = "image/png";
else if (path.endsWith(".gif")) _contentType = "image/gif"; else if (path.endsWith(".gif")) _contentType = "image/gif";
@ -599,7 +606,9 @@ AsyncCallbackResponse::AsyncCallbackResponse(const String& contentType, size_t l
size_t AsyncCallbackResponse::_fillBuffer(uint8_t *data, size_t len){ size_t AsyncCallbackResponse::_fillBuffer(uint8_t *data, size_t len){
size_t ret = _content(data, len, _filledLength); size_t ret = _content(data, len, _filledLength);
_filledLength += ret; if(ret != RESPONSE_TRY_AGAIN){
_filledLength += ret;
}
return ret; return ret;
} }
@ -619,7 +628,9 @@ AsyncChunkedResponse::AsyncChunkedResponse(const String& contentType, AwsRespons
size_t AsyncChunkedResponse::_fillBuffer(uint8_t *data, size_t len){ size_t AsyncChunkedResponse::_fillBuffer(uint8_t *data, size_t len){
size_t ret = _content(data, len, _filledLength); size_t ret = _content(data, len, _filledLength);
_filledLength += ret; if(ret != RESPONSE_TRY_AGAIN){
_filledLength += ret;
}
return ret; return ret;
} }

View File

@ -79,6 +79,7 @@ bool AsyncWebServer::removeHandler(AsyncWebHandler *handler){
} }
void AsyncWebServer::begin(){ void AsyncWebServer::begin(){
_server.setNoDelay(true);
_server.begin(); _server.begin();
} }

View File

@ -60,8 +60,9 @@
bool can_process_serial = true; bool can_process_serial = true;
extern bool deleteRecursive(String path); extern bool deleteRecursive(String path);
extern void CloseSerialUpload (bool iserror, String & filename); extern bool sendLine2Serial (String & line, int32_t linenb, int32_t * newlinenb);
extern bool sendLine2Serial (String & line); extern void CloseSerialUpload (bool iserror, String & filename , int32_t linenb);
extern bool purge_serial();
extern long id_connection; extern long id_connection;
const uint8_t PAGE_404 [] PROGMEM = "<HTML>\n<HEAD>\n<title>Redirecting...</title> \n</HEAD>\n<BODY>\n<CENTER>Unknown page : $QUERY$- you will be redirected...\n<BR><BR>\nif not redirected, <a href='http://$WEB_ADDRESS$'>click here</a>\n<BR><BR>\n<PROGRESS name='prg' id='prg'></PROGRESS>\n\n<script>\nvar i = 0; \nvar x = document.getElementById(\"prg\"); \nx.max=5; \nvar interval=setInterval(function(){\ni=i+1; \nvar x = document.getElementById(\"prg\"); \nx.value=i; \nif (i>5) \n{\nclearInterval(interval);\nwindow.location.href='/';\n}\n},1000);\n</script>\n</CENTER>\n</BODY>\n</HTML>\n\n"; const uint8_t PAGE_404 [] PROGMEM = "<HTML>\n<HEAD>\n<title>Redirecting...</title> \n</HEAD>\n<BODY>\n<CENTER>Unknown page : $QUERY$- you will be redirected...\n<BR><BR>\nif not redirected, <a href='http://$WEB_ADDRESS$'>click here</a>\n<BR><BR>\n<PROGRESS name='prg' id='prg'></PROGRESS>\n\n<script>\nvar i = 0; \nvar x = document.getElementById(\"prg\"); \nx.max=5; \nvar interval=setInterval(function(){\ni=i+1; \nvar x = document.getElementById(\"prg\"); \nx.value=i; \nif (i>5) \n{\nclearInterval(interval);\nwindow.location.href='/';\n}\n},1000);\n</script>\n</CENTER>\n</BODY>\n</HTML>\n\n";
@ -1092,6 +1093,7 @@ void SDFile_serial_upload (AsyncWebServerRequest *request, String filename, size
LOG ("Uploading: ") LOG ("Uploading: ")
LOG (filename) LOG (filename)
LOG ("\n") LOG ("\n")
static int32_t lineNb =-1;
static String current_line; static String current_line;
static bool is_comment = false; static bool is_comment = false;
static String current_filename; static String current_filename;
@ -1107,75 +1109,66 @@ void SDFile_serial_upload (AsyncWebServerRequest *request, String filename, size
//Upload start //Upload start
//************** //**************
if (!index) { if (!index) {
LOG ("Starting\n") LOG("Upload Start\r\n")
//need to lock serial out to avoid garbage in file String command = "M29";
(web_interface->blockserial) = true; String resetcmd = "M110 N0";
current_line = ""; if (CONFIG::GetFirmwareTarget() == SMOOTHIEWARE)resetcmd = "N0 M110";
//init flags lineNb=1;
is_comment = false; //close any ongoing upload and get current line number
current_filename = filename; if(!sendLine2Serial (command,1, &lineNb)){
web_interface->_upload_status = UPLOAD_STATUS_ONGOING; //it can failed for repetier
ESPCOM::println (F ("Uploading..."), PRINTER_PIPE); if ( ( CONFIG::GetFirmwareTarget() == REPETIER4DV) || (CONFIG::GetFirmwareTarget() == REPETIER) ) {
ESPCOM::flush (DEFAULT_PRINTER_PIPE); if(!sendLine2Serial (command,-1, NULL)){
LOG ("Clear Serial\r\n"); LOG("Start Upload failed")
if(ESPCOM::available(DEFAULT_PRINTER_PIPE)) { web_interface->_upload_status= UPLOAD_STATUS_FAILED;
ESPCOM::bridge();
CONFIG::wait(1);
}
//command to pritnter to start print
String command = "M28 " + filename;
LOG (command);
LOG ("\r\n");
ESPCOM::println (command, DEFAULT_PRINTER_PIPE);
ESPCOM::flush (DEFAULT_PRINTER_PIPE);
CONFIG::wait (500);
uint32_t timeout = millis();
bool done = false;
while (!done) { //time out is 2000ms
CONFIG::wdtFeed();
//if there is something in serial buffer
size_t len = ESPCOM::available(DEFAULT_PRINTER_PIPE);
//get size of buffer
if (len > 0) {
CONFIG::wdtFeed();
uint8_t * sbuf = (uint8_t *)malloc(len+1);
if(!sbuf){
web_interface->_upload_status = UPLOAD_STATUS_CANCELLED;
ESPCOM::println (F ("SD upload rejected"), PRINTER_PIPE);
LOG ("SD upload rejected\r\n");
request->client()->abort();
return ;
}
//read buffer
ESPCOM::readBytes (DEFAULT_PRINTER_PIPE, sbuf, len);
//convert buffer to zero end array
sbuf[len] = '\0';
//use string because easier to handle
response = (const char*) sbuf;
LOG (response);
//if there is a wait it means purge is done
if (response.indexOf ("wait") > -1) {
LOG ("Exit start writing\r\n");
done = true;
free(sbuf);
break;
}
//it is first command if it is failed no need to continue
//and resend command won't help
if (response.indexOf ("Resend") > -1 || response.indexOf ("failed") > -1) {
web_interface->blockserial = false;
LOG ("Error start writing\r\n");
web_interface->_upload_status = UPLOAD_STATUS_FAILED;
request->client()->abort();
free(sbuf);
return; return;
} }
free(sbuf); } else {
} LOG("Start Upload failed")
if ( (millis() - timeout) > SERIAL_CHECK_TIMEOUT) { web_interface->_upload_status= UPLOAD_STATUS_FAILED;
done = true; return;
} }
} }
//Mount SD card
command = "M21";
if(!sendLine2Serial (command,-1, NULL)){
LOG("Mounting SD failed")
web_interface->_upload_status= UPLOAD_STATUS_FAILED;
return;
}
//Reset line numbering
if(!sendLine2Serial (resetcmd,-1, NULL)){
LOG("Reset Numbering failed")
web_interface->_upload_status= UPLOAD_STATUS_FAILED;
return;
}
lineNb=1;
//need to lock serial out to avoid garbage in file
(web_interface->blockserial) = true;
current_line ="";
current_filename = filename;
is_comment = false;
String response;
ESPCOM::println (F ("Uploading..."), PRINTER_PIPE);
//Clear all serial
ESPCOM::flush (DEFAULT_PRINTER_PIPE);
purge_serial();
//besure nothing left again
purge_serial();
command = "M28 " + current_filename;
//send start upload
//no correction allowed because it means reset numbering was failed
if (sendLine2Serial(command, lineNb, NULL)){
CONFIG::wait(1200);
//additional purge, in case it is slow to answer
purge_serial();
web_interface->_upload_status= UPLOAD_STATUS_ONGOING;
LOG("Creation Ok\r\n")
} else {
web_interface->_upload_status= UPLOAD_STATUS_FAILED;
LOG("Creation failed\r\n");
}
} }
//Upload write //Upload write
//************** //**************
@ -1196,10 +1189,10 @@ void SDFile_serial_upload (AsyncWebServerRequest *request, String filename, size
if (current_line.length() < 126) { if (current_line.length() < 126) {
//do we have something in buffer ? //do we have something in buffer ?
if (current_line.length() > 0 ) { if (current_line.length() > 0 ) {
current_line += "\r\n"; lineNb++;
if (!sendLine2Serial (current_line) ) { if (!sendLine2Serial (current_line, lineNb, NULL) ) {
LOG ("Error over buffer\n") LOG ("Error sending line\n")
CloseSerialUpload (true, current_filename); CloseSerialUpload (true, current_filename,lineNb);
request->client()->abort(); request->client()->abort();
return; return;
} }
@ -1212,7 +1205,8 @@ void SDFile_serial_upload (AsyncWebServerRequest *request, String filename, size
} else { } else {
//error buffer overload //error buffer overload
LOG ("Error over buffer\n") LOG ("Error over buffer\n")
CloseSerialUpload (true, current_filename); lineNb++;
CloseSerialUpload (true, current_filename, lineNb);
request->client()->abort(); request->client()->abort();
return; return;
} }
@ -1221,7 +1215,8 @@ void SDFile_serial_upload (AsyncWebServerRequest *request, String filename, size
current_line += char (data[pos]); //copy current char to buffer to send/resend current_line += char (data[pos]); //copy current char to buffer to send/resend
} else { } else {
LOG ("Error over buffer\n") LOG ("Error over buffer\n")
CloseSerialUpload (true, current_filename); lineNb++;
CloseSerialUpload (true, current_filename, lineNb);
request->client()->abort(); request->client()->abort();
return; return;
} }
@ -1238,16 +1233,18 @@ void SDFile_serial_upload (AsyncWebServerRequest *request, String filename, size
LOG ("Final is reached\n") LOG ("Final is reached\n")
//if last part does not have '\n' //if last part does not have '\n'
if (current_line.length() > 0) { if (current_line.length() > 0) {
current_line += "\r\n"; lineNb++;
if (!sendLine2Serial (current_line) ) { if (!sendLine2Serial (current_line, lineNb, NULL) ) {
LOG ("Error sending buffer\n") LOG ("Error sending buffer\n")
CloseSerialUpload (true, current_filename); lineNb++;
CloseSerialUpload (true, current_filename, lineNb);
request->client()->abort(); request->client()->abort();
return; return;
} }
} }
LOG ("Upload finished "); LOG ("Upload finished ");
CloseSerialUpload (false, current_filename); lineNb++;
CloseSerialUpload (false, current_filename, lineNb);
} }
LOG ("Exit fn\n") LOG ("Exit fn\n")
} }

View File

@ -19,7 +19,7 @@
*/ */
//version and sources location //version and sources location
#define FW_VERSION "2.0.0.c24" #define FW_VERSION "2.0.0.c25"
#define REPOSITORY "https://github.com/luc-github/ESP3D" #define REPOSITORY "https://github.com/luc-github/ESP3D"
//Customize ESP3D //////////////////////////////////////////////////////////////////////// //Customize ESP3D ////////////////////////////////////////////////////////////////////////